Thursday, August 8, 2013

Cascade Pages... And Modules!

So Arena's been able to Cascade Permissions on a bunch of things for a while, but what's been missing the entire time has been Module permissions when cascading Page permissions. Well, this sproc update will change that:

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[secu_sp_cascadePagePermissions]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[secu_sp_cascadePagePermissions]
GO

CREATE PROC [dbo].[secu_sp_cascadePagePermissions]
@parent INT,
@OrganizationID int

AS

DECLARE @page INT

DELETE FROM dbo.secu_permission
WHERE object_type=2 AND object_key in (SELECT module_instance_id FROM port_module_instance WHERE page_id=@parent AND organization_id=@OrganizationID) and organization_id = @OrganizationID

INSERT INTO dbo.secu_permission (created_by, modified_by, object_type, object_key, operation_type, subject_type, subject_key, organization_id)
SELECT DISTINCT 'AS-IP', 'AS-IP',2, module_instance_id, operation_type, subject_type, subject_key, @OrganizationID
FROM dbo.secu_permission AS sp
JOIN dbo.port_module_instance AS pmi ON pmi.page_id=@parent AND sp.object_type=1 AND pmi.organization_id=sp.organization_id
WHERE sp.object_key=@parent
AND sp.organization_id = @OrganizationID

DELETE FROM dbo.secu_permission
WHERE object_type = 2 AND object_key IN (SELECT child.module_instance_id FROM port_module_instance AS child JOIN port_module_instance AS parent ON parent.module_instance_id=child.parent_module_instance_id AND parent.organization_id = child.organization_id WHERE parent.page_id = @parent AND parent.organization_id =@OrganizationID) AND organization_id = @OrganizationID

INSERT INTO dbo.secu_permission (created_by, modified_by, object_type, object_key, operation_type, subject_type, subject_key, organization_id)
SELECT DISTINCT 'AS-IP', 'AS-IP',2, child.module_instance_id, operation_type, subject_type, subject_key, @OrganizationID
FROM dbo.secu_permission AS sp
JOIN dbo.port_module_instance AS pmi ON pmi.page_id=@parent AND sp.object_type=1 AND pmi.organization_id=sp.organization_id
JOIN dbo.port_module_instance AS child ON child.parent_module_instance_id=pmi.module_instance_id AND child.organization_id=pmi.organization_id
WHERE sp.object_key=@parent
AND sp.organization_id = @OrganizationID

DECLARE PageCursor CURSOR READ_ONLY FOR
WITH pageTree (page_id) AS (
SELECT page_id
FROM port_portal_page
WHERE organization_id=@OrganizationID
AND parent_page_id=@parent
UNION ALL
SELECT p.page_id
FROM port_portal_page AS p
JOIN pageTree AS t ON t.page_id=p.parent_page_id
WHERE p.organization_id=@OrganizationID)
SELECT page_id FROM pageTree;

OPEN PageCursor

FETCH NEXT FROM PageCursor
INTO @page

WHILE (@@FETCH_STATUS <> -1)
BEGIN

 IF (@@FETCH_STATUS = 0)
 BEGIN

  DELETE FROM dbo.secu_permission
  WHERE object_type=1 AND object_key=@page and organization_id = @OrganizationID

  INSERT INTO dbo.secu_permission (created_by, modified_by, object_type, object_key, operation_type, subject_type, subject_key, organization_id)
  SELECT 'AS-IP', 'AS-IP',object_type, @page, operation_type, subject_type, subject_key, @OrganizationID
  FROM dbo.secu_permission
  WHERE object_type=1
  AND object_key=@parent
  AND organization_id = @OrganizationID

  DELETE FROM dbo.secu_permission
  WHERE object_type=2 AND object_key in (SELECT module_instance_id FROM port_module_instance WHERE page_id=@page AND organization_id=@OrganizationID) and organization_id = @OrganizationID

  INSERT INTO dbo.secu_permission (created_by, modified_by, object_type, object_key, operation_type, subject_type, subject_key, organization_id)
  SELECT DISTINCT 'AS-IP', 'AS-IP',2, module_instance_id, operation_type, subject_type, subject_key, @OrganizationID
  FROM dbo.secu_permission AS sp
  JOIN dbo.port_module_instance AS pmi ON pmi.page_id=@page AND sp.object_type=1 AND pmi.organization_id=sp.organization_id
  WHERE sp.object_key=@parent
  AND sp.organization_id = @OrganizationID

  DELETE FROM dbo.secu_permission
  WHERE object_type = 2 AND object_key IN (SELECT child.module_instance_id FROM port_module_instance AS child JOIN port_module_instance AS parent ON parent.module_instance_id=child.parent_module_instance_id AND parent.organization_id=child.organization_id WHERE parent.page_id=@page AND parent.organization_id = @OrganizationID) AND organization_id = @OrganizationID

  INSERT INTO dbo.secu_permission (created_by, modified_by, object_type, object_key, operation_type, subject_type, subject_key, organization_id)
  SELECT DISTINCT 'AS-IP', 'AS-IP',2, child.module_instance_id, operation_type, subject_type, subject_key, @OrganizationID
  FROM dbo.secu_permission AS sp
  JOIN dbo.port_module_instance AS pmi ON pmi.page_id=@page AND sp.object_type=1 AND pmi.organization_id=sp.organization_id
  JOIN dbo.port_module_instance AS child ON child.parent_module_instance_id=pmi.module_instance_id AND child.organization_id=pmi.organization_id
  WHERE sp.object_key=@parent
  AND sp.organization_id = @OrganizationID

 END

 FETCH NEXT FROM PageCursor
 INTO @page

END

CLOSE PageCursor
DEALLOCATE PageCursor

Thursday, July 25, 2013

Don't go hunting with Reflection


So, apparently .NET's reflection doesn't like Properties that are marked Private. Oops. That's three hours of my life and a few forehead-shaped indentations in my desk I'd like back. Just thought I'd share. Carry on.

Monday, August 15, 2011

What about *last* Sunday?

An earlier (much earlier) post covered how to detect the next upcoming date that is a certain day of the week, and I just had a similar request for the last date it was a certain day of the week and cooked up this:

CREATE FUNCTION dbo.cust_funct_last_weekday
(@weekday INT)
RETURNS DATETIME
AS
BEGIN
DECLARE @date DATETIME
SET @date=DATEADD(day,DATEDIFF(day,0,GETDATE()),0)

IF DATEPART(dw,@date)=@weekday
SET @date=DATEADD(day,-1,@date)
WHILE DATEPART(dw,@date)&lt;&gt;@weekday
SET @date=DATEADD(day,-1,@DATE)

RETURN @date
END

Note that this function accepts an integer and returns a date, so you specify the numeric day of the week (e.g. 1 = Sunday, 2 = Monday...7 = Saturday, although this is actually just the US default for a SQL setting called @@DATEFIRST (MSDN page)), and it returns the last Date that was that day of the week. If you specify the same day of the week as today, it returns the same day of the week from last week (i.e. it will not return today). Also, note that it returns the date with a midnight (00:00:00) time component, so you might need to massage your comparison to remove specific times, e.g. "WHERE core_occurrence.occurrence_start_time = dbo.cust_funct_last_weekday(1)" becomes "WHERE DATEADD(day,DATEDIFF(day,0,core_occurrence.occurrence_start_time),0) = dbo.cust_funct_last_weekday(1)".

This can be useful for some automated reporting, such as "I want a report in my inbox Monday morning showing our attendance numbers for Sunday's services" or "How many kids attended the Wednesday night service?", and a host of other "last N-day" questions (possibly even in lists...).

Friday, July 22, 2011

Security on a Portal

So the question came up for how to view the entire security model for the Pages and Module Instances of a Portal, and I realized that while we have a handful of security reports for other things, we don't have a generic query pulling security on those items. So I spent an hour and a half (probably shouldn't have taken me that long, but I'm a bit out of practice) and what I came up with shows the page structure (in order, just like in Pages) with the permissions for the page, and within each page it shows the modules on that page (in their template frames and in order within those frames) and the permissions on those module instances. It shows permissions as "Roles | Persons" in case you have person-level security on anything, and each of those is detailed as "Name (permissions)" where the permissions are outlined as “V”=View, “E”=Edit, ”S”=Edit Security, ”P”=Edit People, “M”=Edit Modules, “N”=Edit Notes, “A”=Approve, and “R”=Edit Registrations.



DECLARE @portalID INT,@a INT
SET @portalID=1

-- Pages
DECLARE @pagetable TABLE (page_id INT, page_name VARCHAR(500),permission_list VARCHAR(MAX),ordering INT)

;WITH pagetree (
Root
, Parent
, Name
, Level
, ID
, Branch
) AS (
SELECT
ppp.page_name
, CONVERT(VARCHAR(100),'')
, ppp.page_name
, 0
, ppp.page_id
, CONVERT(VARCHAR(MAX),REPLACE(STR(CONVERT(VARCHAR,ISNULL(NULLIF(ppp.page_order,2147483647),9999)), 5, 0), ' ', '0')+'.'+CONVERT(VARCHAR,ppp.page_id))
FROM
port_portal_page AS ppp
WHERE
ppp.page_id=(SELECT TOP 1 default_page_id FROM port_portal WHERE portal_id=@portalID)
UNION ALL
SELECT
tree.Root
, tree.Name
, CONVERT(VARCHAR(100),(replicate('-',tree.level+1))+ppp.page_name)
, tree.Level + 1
, ppp.page_id
, CONVERT(VARCHAR(MAX),tree.Branch + '.' + REPLACE(STR(CONVERT(VARCHAR,ISNULL(NULLIF(ppp.page_order,2147483647),9999)), 5, 0), ' ', '0')+'.'+CONVERT(VARCHAR,ppp.page_id))
FROM
port_portal_page AS ppp
JOIN pagetree AS tree ON ppp.parent_page_id = tree.ID
)

INSERT INTO @pagetable
SELECT
pt.ID AS page_id
,pt.Name
,ISNULL(SUBSTRING(
(SELECT DISTINCT
', '+sr.role_name+' ('
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=0 AND subject_key=sp.subject_key AND operation_type=0) THEN 'V' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=0 AND subject_key=sp.subject_key AND operation_type=1) THEN 'E' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=0 AND subject_key=sp.subject_key AND operation_type=2) THEN 'S' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=0 AND subject_key=sp.subject_key AND operation_type=5) THEN 'M' ELSE '' END)
+')'
FROM
secu_permission AS sp WITH(NOLOCK)
JOIN secu_role AS sr WITH(NOLOCK) ON sr.role_id=sp.subject_key AND sp.subject_type=0
WHERE
sp.object_type=1
AND sp.object_key = pt.ID
FOR XML PATH(''))
,3,5000),'')
+ISNULL(' | '+SUBSTRING(
(SELECT DISTINCT
', '+cp.first_name+' '+cp.last_name+' ('
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=1 AND subject_key=sp.subject_key AND operation_type=0) THEN 'V' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=1 AND subject_key=sp.subject_key AND operation_type=1) THEN 'E' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=1 AND subject_key=sp.subject_key AND operation_type=2) THEN 'S' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=1 AND subject_key=sp.subject_key AND operation_type=5) THEN 'M' ELSE '' END)
+')'
FROM
secu_permission AS sp WITH(NOLOCK)
JOIN core_person AS cp WITH(NOLOCK) ON cp.person_id=sp.subject_key AND sp.subject_type=1
WHERE
sp.object_type=1
AND sp.object_key = pt.ID
FOR XML PATH(''))
,3,5000),'')
,ROW_NUMBER() OVER (ORDER BY @a )
FROM
pagetree AS pt
ORDER BY
Root
, branch


-- Modules
DECLARE @moduletable TABLE (page_id INT, module_instance_id INT,module_title VARCHAR(500),module_type VARCHAR(500), module_url VARCHAR(500),template_frame_name VARCHAR(500),module_order INT, permission_list VARCHAR(MAX))

INSERT INTO @moduletable
SELECT
pmi.page_id
,pmi.module_instance_id
,pmi.module_title
,pm.module_name
,pm.module_url
,pmi.template_frame_name
,pmi.template_frame_order
,ISNULL(SUBSTRING(
(SELECT DISTINCT
', '+sr.role_name+' ('
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=0 AND subject_key=sp.subject_key AND operation_type=0) THEN 'V' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=0 AND subject_key=sp.subject_key AND operation_type=1) THEN 'E' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=0 AND subject_key=sp.subject_key AND operation_type=2) THEN 'S' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=0 AND subject_key=sp.subject_key AND operation_type=4) THEN 'P' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=0 AND subject_key=sp.subject_key AND operation_type=6) THEN 'N' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=0 AND subject_key=sp.subject_key AND operation_type=7) THEN 'A' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=0 AND subject_key=sp.subject_key AND operation_type=8) THEN 'R' ELSE '' END)
+')'
FROM
secu_permission AS sp WITH(NOLOCK)
JOIN secu_role AS sr WITH(NOLOCK) ON sr.role_id=sp.subject_key AND sp.subject_type=0
WHERE
sp.object_type=2
AND sp.object_key = pmi.module_instance_id
FOR XML PATH(''))
,3,5000),'')
+ISNULL(' | '+SUBSTRING(
(SELECT DISTINCT
', '+cp.first_name+' '+cp.last_name+' ('
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=1 AND subject_key=sp.subject_key AND operation_type=0) THEN 'V' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=1 AND subject_key=sp.subject_key AND operation_type=1) THEN 'E' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=1 AND subject_key=sp.subject_key AND operation_type=2) THEN 'S' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=1 AND subject_key=sp.subject_key AND operation_type=4) THEN 'P' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=1 AND subject_key=sp.subject_key AND operation_type=6) THEN 'N' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=1 AND subject_key=sp.subject_key AND operation_type=7) THEN 'A' ELSE '' END)
+(CASE WHEN EXISTS(SELECT TOP 1 1 FROM secu_permission WITH(NOLOCK) WHERE object_type=sp.object_type AND object_key=sp.object_key AND subject_type=1 AND subject_key=sp.subject_key AND operation_type=8) THEN 'R' ELSE '' END)
+')'
FROM
secu_permission AS sp WITH(NOLOCK)
JOIN core_person AS cp WITH(NOLOCK) ON cp.person_id=sp.subject_key AND sp.subject_type=1
WHERE
sp.object_type=2
AND sp.object_key = pmi.module_instance_id
FOR XML PATH(''))
,3,5000),'')
FROM port_module_instance AS pmi WITH(NOLOCK)
JOIN port_module AS pm WITH(NOLOCK) ON pm.module_id=pmi.module_id
WHERE page_id IN (SELECT page_id FROM @pagetable)

DECLARE @finaltable TABLE (ordering varchar(500), page_id INT,page_name VARCHAR(500),template_frame_name VARCHAR(500),module_instance_id INT, module_title VARCHAR(500),module_type VARCHAR(500), module_url VARCHAR(500),permission_list VARCHAR(MAX))
INSERT INTO @finaltable
SELECT REPLACE(STR(CONVERT(VARCHAR,pt.ordering), 9, 0), ' ', '0'),pt.page_id,pt.page_name,NULL AS template_frame_name,NULL AS module_instance_id, NULL AS module_title, NULL AS module_type, NULL AS module_url,pt.permission_list FROM @pagetable pt
UNION
SELECT (SELECT REPLACE(STR(CONVERT(VARCHAR,ordering), 9, 0), ' ', '0') FROM @pagetable WHERE page_id=mt.page_id)+CONVERT(VARCHAR,mt.module_order),mt.page_id,'zzzzzzzzzzzzz' AS page_name,mt.template_frame_name,mt.module_instance_id,mt.module_title,mt.module_type,mt.module_url,mt.permission_list FROM @moduletable mt

SELECT
page_id
,REPLACE(page_name,'zzzzzzzzzzzzz','') AS page_name
,template_frame_name
,module_instance_id
,module_title
,module_type
,module_url
,permission_list
FROM @finaltable
ORDER BY ordering

Tuesday, June 28, 2011

Event Discount Codes

On the same theme of rapidly reporting data from an Event, here's a sproc to report the Discount Codes that have been used in the current event. You can deploy it with a Report Grid From Query on the Event Details page (see Event Costs And Fees for how to set up a RGFQ on the Events page, just make sure you set Public Site = True since you dont need all the extras for this grid) with a single parameter of "profile=;".

CREATE PROCEDURE dbo.cust_sp_rgfq_evnt_applied_discount_codes
@profile VARCHAR(20)
AS
SELECT DISTINCT
cp.person_id
,cp.last_name+', '+cp.first_name AS Person_Name
,edc.code_value AS [Discount Code Applied]
,(CASE WHEN ereg.date_created<ereg.date_modified THEN ereg.date_modified ELSE ereg.date_created END) AS [Date Last Modified]
FROM
evnt_registrant AS er
JOIN evnt_discount_code AS edc ON edc.discount_code_id=er.discount_code_applied
JOIN core_person AS cp ON cp.person_id=er.person_id
JOIN evnt_registration AS ereg ON ereg.registration_id=er.registration_id
WHERE er.profile_id=@profile
ORDER BY [Discount Code Applied],Person_Name

Monday, March 14, 2011

Event Costs and Fees

Just cooked this up for one of our trainers, but it’s something I think a lot of admins may end up wanting once they start to see it, so here’s the stuff needed for it.

CREATE PROCEDURE dbo.cust_sp_evnt_costs_and_fees
@profile INT
AS
SELECT
'Cost: '+ec.title AS [Cost or Fee Name]
,COUNT(*) AS [Number of Paid Registrants]
FROM
evnt_registrant_cost AS erc
JOIN evnt_registrant_cost_payment AS ercp ON ercp.registrant_cost_id=erc.registrant_cost_id
JOIN evnt_cost AS ec ON ec.cost_id=erc.cost_id
WHERE erc.profile_id = @profile
AND erc.paid=1
GROUP BY ec.title
UNION
SELECT
'Fee: '+ef.title+ISNULL(': '+NULLIF(erf.selected_value,''),'') AS [Cost or Fee Name]
,SUM(erf.quantity) AS [Number of Paid Registrants]
FROM
evnt_registrant_fee AS erf
JOIN evnt_registrant_fee_payment AS erfp ON erfp.registrant_fee_id=erf.registrant_fee_id
JOIN evnt_fee AS ef ON ef.fee_id=erf.fee_id
WHERE erf.profile_id = @profile
AND erf.paid=1
GROUP BY ef.title,erf.selected_value

This is the setup in Pages (explanation below):

Explanation:
On the Event Tags page's Main Content tab, add a new module.
Per the instructions in the image, drag the new module up one spot and back down (to force the program to recognize that it should be at the bottom of the page)
Open the new module and change the type to Report Grid From Query (it's in the Reporting section).
Set the Parameter and Query options as shown in the image (don't forget the semi-colon at the end of the parameter).
Refresh the cache and you're good to go.

What this does is use a Report Grid From Query at the bottom of the Event details page showing a count of how many of each cost and fee (it separates out fees by selected values, so if you’re doing T-shirts for $10 each, you’ll see something like “Fee: T-Shirt: X-Large……3”) have been selected, restricted to the current event (it does not roll up sub-events). It’s just an aggregate count, and it only includes Paid costs & fees, but gives you a quick count of how many vegetarian meals to order, or whatnot.

Thursday, February 10, 2011

Facebook: I Like Arena

Facebook: 500 million users, decent API, social media at its best, and one of the fastest communications tools out there for allowing your Membership to quickly reach each other with information about your Ministry. So the first step in getting into that world is the ubiquitous "Like" button.

To implement this, you'll need to do two things. First, you'll need to register your "app" (Arena-powered website) with Facebook by logging in to your church's Facebook account (using your personal account makes it your app, not the organization's) and going to: http://developers.facebook.com/setup/.

Once you've registered your app and have your AppID, add an Advanced HTML Text module to any page of your site, and copy this code into it, replacing the 'YOUR APP ID HERE' with... you guessed it: your app id (but keep the single-quotes around the ID, they're important).
<div id="fb-root"></div>
<script src="http://connect.facebook.net/en_US/all.js" ></script>
<script>
window.fbAsyncInit = function () {
FB.init({ appId: 'YOUR APP ID HERE', status: true, cookie: true,xfbml: true});
};
(function () {
var e = document.createElement('script');
e.type = 'text/javascript';
e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
e.async = true;
document.getElementById('fb-root').appendChild(e);
} ());
</script>
<fb:like layout="button_count" width="90"></fb:like>

And there's your Like button!
You can add this code directly to your template (or you might want to just add a specific content area to the template and add an Adv. HTML Text module to that content area in the Template inside Arena so you can manage security on it), since it intelligently "likes" the current dynamic URL (right down to the query string, so it can even "like" events or promotions).