part of the ArsDigita Community System by Mark Dalrymple

The Big Idea

People like to get free stuff. The contest module allows publishers to create a doorprize like contest and allows registered users to participate in the contest. Contest can be a simple "visit this page to get entered" or a more complex "please give us some information while entering" page. The publisher can then choose an arbitrary number of winners from the pool of contestants.

Publishing Decisions

Contests can be simple entry buttons, or they can be forms which ask for information. When you're creating a contest, the admin pages provide a customization feature where you can specify any extra data to be collected (such as a yes/no question, or a text field for free-form commentary). You can either use the automatically generated contest entry page (which looks at your customizations and generates the HTML <form> elements) or you can create your own page and include some hidden values which are used in entry processing.

Choosing Winners

When it's time to choose winners, go to the contest management page. You can specify the time range to choose entrants from (say from the last month), and the number of unique winners to return. The contest module will grovel through the database and return a unique set of results, which you can then do what you wish. (send them email, deliver a box of chocolate)

Under the Hood

There are two primary tables which hold the contest information, and a new table is created for each contest to hold any customized entry fields.

contest_domains is the table that holds general information about each contest:

create table contest_domains (
	domain_id		integer not null primary key,
	domain			varchar(21) not null unique,
	-- the unique constraint creates an index for us
	entrants_table_name	varchar(30),
	pretty_name		varchar(100) not null,
	-- where the contest starts
	home_url	varchar(200),
	-- arbitrary HTML text that goes at the top of 
	-- the auto-generated entry form
	blather		varchar(4000),
	-- where to send users after they enter
	-- (if blank, we use a generated form)
	post_entry_url	varchar(200),
	maintainer	not null references users(user_id),
	notify_of_additions_p	char(1) default 'f' check (notify_of_additions_p in ('t', 'f')),  -- send email when a person enters
	us_only_p		char(1) default 'f' check (us_only_p in ('t', 'f')),
	start_date		date,	-- these are optional
	end_date		date
In earlier versions of this module, the domain column was the primary key. It has been changed to an integer (domain_id) because of performance enhancements to the site-wide search. There is some backwards-compatibility code in the contest module that uses the domain column if there is no domain_id provided in the form data.

When a new contest is created, a new row is added to contest_domains, and a new table called contest_entrants_$domain (where $domain is the value of the domain column). This new entrants table looks like this:

create table contest_entrants_whatever (
	entry_date	date not null,
	user_id		not null references users
We don't really care how many times they enter. We'll do a "distinct" query when choosing the winners. For contests that allow extra information to be provided by the user, we may want them to be able to enter multiple times.

Now, how is this extra information handled? When you add a custom column to a contest, a row gets added to the table contest_extra_columns:

create table contest_extra_columns (
	domain_id		not null references contest_domains,
	column_pretty_name	varchar(30),
	column_actual_name	varchar(200) not null,
	column_type		varchar(200) not null,	-- things like 'boolean' or 'text'
	column_extra_sql	varchar(200)	-- things like 'not null' or 'default 5'
The column_pretty_name is what is displayed to the user, while column_actual_name> is the name of the column in the contest specific contest_entrants_$domain table. These new columns get added to the entrants table as the contest gets customized. (e.g. at any time)