Subsections


5.1 The $S Object

The $S object contains all of Scoop's data and methods. In quite a few cases, the data is accessed via a method rather than directly. All of the data is drawn from the database, but in most cases you will not have to access the database directly as Scoop does some aggressive (and transparent) data caching.

5.1.1 Data

Below is a list of some of the more useful bits of data that you access directly, followed by a list of data that you want to access using Scoop's methods.

All of this data is loaded into $S in the initialization stages of each request, from the cache if possible and from the database if necessary.

$S->{UI}->{VARS}
Each of Scoop's site controls (variables, or vars) can be accessed using $S->{UI}->{VARS}->{variable_name} for use in testing whether or not a feature is on or off, or for getting or setting values. If your code will break horribly if a variable is zero or blank then make sure to put a sensible default value in your code that will be used if the variable in question is empty. Administrators can change the variables through the Site Controls Admin Tool (A.6). For ease of use, the site controls are usually assigned to a variable local to that subroutine or box if they will be used more than once in that part of the code, then used as the much shorter and easier to type variable from then on.

$S->{UI}->{BLOCKS}
Each of Scoop's blocks can be accessed using $S->{UI}->{BLOCKS}->{block_name} for use in perl code. This method of accessing blocks should only be used if you need to process the block in question in a special way, such as handling special keys (4.15); if you want to simply include one block in another one, place the included block's name between vertical pipes (|block_name|) where its content should be placed in the other block. If you are using perl code to handle special keys in a particular block, whether in a box or in the code, in the substitution command the key should be between double percent signs (%%key_name%%) as that's how keys are stored in the database. When processing the page just before sending it to the user's computer, Scoop does all the normal block interpolation automatically, so you don't have to worry about substituting keys for blocks that exist. Administrators can change the blocks through the Blocks Admin Tool (A.7).

$S->{SECTION_DATA}
The information associated with each of Scoop's sections can be accessed using $S->{SECTION_DATA}->{sectionid}. The information available is the section's title, description, and icon. For example:
my $title = $S->{SECTION_DATA}->{$section}->{title};
my $section_slogan = $S->{SECTION_DATA}->{$section}->{description};
my $headline = qq@$title ~ $section_slogan@;
where $section is set earlier in the code to something appropriate to the page being processed.

$S->{TOPIC_DATA}
The information associated with each of Scoop's topics can be accessed using $S->{SECTION_DATA}->{topicid}. The information available is the topic's image, alt text (or text version), image width, and image height. For example:
my $img = $S->{TOPIC_DATA}->{$tid}->{image};
my $alt = $S->{TOPIC_DATA}->{$tid}->{alttext};
my $topic_img = qq~<IMG src="|imagedir||topics|/$img" alt="$alt">~;
where $tid is set earlier in the code to something appropriate to the page being processed.

$S->{NICK}
The nickname of the user requesting the current page. This is mainly used for customization of the display, greeting the visitor by name, etc.

$S->{UID}
The user ID of the user requesting the current page. This is mainly used for getting other information by or about the current user, since all information particular to a user is identified by the UID.

$S->{GID}
The group ID of the user requesting the current page. This is mainly used for checking permissions, as the purpose of groups is mainly to give and restrict permissions. It is used as a default group ID if no group ID is specified when using the $S->have_perm() function.

$S->cgi->param()
The param method provides access to all of the form variables sent by either GET or POST to Scoop. Pass the name of the form variable to the method as the argument; the return value is the value of that variable.
$S->apache()
This is the apache request object, stuffed in $S so it's always available. This gives you access to methods which get or set things like the incoming or outgoing headers, the connection information, the configuration, and so on. For information on what functions the apache request object supports, read the pod for the Apache perl modules. $S->apache corresponds to the $r used in the module documentation.

5.1.2 Database access

To get information into and out of the database, Scoop provides an interface to the database that allows you to build your query in code then retreive it in the form of a series of hashes. The methods provide caching where appropriate, to reduce the number of requests sent to the database.

The select, update, insert, and delete methods described below all return two values, $rv and $sth. $rv is the return value; false if the query was not successful. $sth contains the data and all of the other information about the query. It's a DBI object and has several methods of its own. For information on what functions $sth supports, read the pod for DBI, 'perldoc DBI' and see below.

$sth->fetchrow_hashref()
Note: this method is not an $S method. Each time it is run it fetches the next row of results from the database query you just ran until it runs out of results, after which it returns a false value. This is generally used in a while loop:
while( my $tmp = $sth->fetchrow_hashref() ) {
   # perl code here processing $tmp->{fieldname} value(s)
}

$sth->fetchrow_array()
Note: this method is not an $S method. Each time it is run it fetches the next row of results from the database query you just ran until it runs out of results, after which it returns a false value. This is generally used in a while loop:
while( my ($foo, $bar) = $sth->fetchrow_array() ) {
   # perl code here processing $foo and $bar
}
fetchrow_array is extremely useful when you are fetching only a few columns, because you can assign the returned values into normal scalar variables instead of using hashref notation.

$S->db_select()
This method takes an anonymous hash as its arguments, with keys WHAT, FROM, WHERE, GROUP_BY, ORDER_BY, LIMIT, DEBUG, and DISTINCT. The values of these correspond to the SQL statements by the same name, excepting WHAT and DEBUG; WHAT is the list of fields to fetch from the database, and DEBUG determines whether or not Scoop will print debugging information for that particular query. All values default to blank if not used, so things like DEBUG and DISTINCT don't have to be explicitly turned off.
my ($rv, $sth) = $S->db_select({
	WHAT => 'title,sid',
	FROM => 'stories',
	WHERE => qq~aid = "$S->{UID}" AND displaystatus = '0'~,
	ORDER_BY => 'time DESC',
	LIMIT => '10'
});
The above code will fetch the titles and story IDs (sid) of the 10 most recent front-page posted stories by the current user. A display status of 0 means posted to front page. 1 means posted to section only, and negative display status values are for unpublished (hidden or queued) stories. See the first item in this list for getting the data returned by this query from $sth.

$S->db_update()
This method takes an anonymous hash as its arguments, with keys WHAT, SET, WHERE, and DEBUG. The values of these correspond to the SQL statements by the same name, excepting WHAT and DEBUG; WHAT is the name of the table to update, and DEBUG determines whether or not Scoop will print debugging information for that particular query. All values default to blank if not used, so things like DEBUG don't have to be explicitly turned off.
my ($rv, $sth) = $S->db_update({
	WHAT => 'comments',
	SET => 'subject = "[wiped]", comment = "[wiped]"',
	WHERE => qq~sid = "$sid" AND cid = "$cid"~
});
The above code will wipe the subject and body of a particular comment, while leaving it in place so as not to disrupt the threading of a discussion. The variables $sid and $cid (story ID and comment ID, respectively) would be set earlier in the code, and together uniquely identify the comment.

$S->db_insert()
This method takes an anonymous hash as its arguments, with keys INTO, COLS, VALUES, and DEBUG. The values of these correspond to the SQL statements by the same name, excepting DEBUG, which determines whether or not Scoop will print debugging information for that particular query. All values default to blank if not used, so things like DEBUG don't have to be explicitly turned off.
my ($rv, $sth) = $S->db_insert({
	INTO => 'pollanswers',
	COLS => 'qid, aid, answer, votes',
	VALUES => qq~$qid, $aid, $answer, '0'~,
});
The above code will insert a new poll answer with 0 votes into an existing poll (basically allowing write-in answers to be added to the poll). The variable $qid is the poll (question) ID, $aid is the answer ID, and $answer is the text of the answer. $aid would need to be discovered before the insert; a simple count of the number of answers + 1 via a db_select statement would provide the right value to use in the insert. $answer would be supplied by the user. (Don't forget to quote() it first! See below...)

$S->db_delete()
This method takes an anonymous hash as its arguments, with keys FROM, WHERE, and DEBUG. The values of these correspond to the SQL statements by the same name, excepting DEBUG, which determines whether or not Scoop will print debugging information for that particular query. All values default to blank if not used, so things like DEBUG don't have to be explicitly turned off.
my ($rv, $sth) = $S->db_delete({
	FROM => 'users',
	WHERE => qq~creation_time < DATE_SUB(NOW(), INTERVAL 1 month)
		AND is_new_account = '1'~,
});
The above code will delete any accounts which have not been confirmed by one month after their creation. Since unconfirmed accounts cannot have anything in the database attached to them, deleting them holds no risk of causing data integrity errors. Accounts are confirmed by logging in with the username and password emailed to the user when they create the account.

$S->dbh->quote()
This method takes a string as its argument and returns a string properly quoted for use in the database. Use this any time you are passing any string to the database that is not hardcoded right in your perl code; even hidden form fields can be changed.

This isn't only a security measure to stop malicious users. Everything going into the database or used in a query should always be quoted, even administrative stuff that only trusted administrators will use, because a misplaced quote--or even an unexpected apostrophe--can cause database errors when not quoted.

5.1.3 User-related Scoop Methods

$S->user_data()
This method takes a numeric user ID and returns a hashref containing all user info, user preferences and subscription information if subscriptions are being used.

The user info, such as the user's homepage, bio, fake email, and so on, are stored as $user->{homepage} and so on; the hash keys are the names of the fields in the users table.

The user preferences, such as the user's time zone, how many titles they want displayed on the front and section pages, whether or not they're listed in the ``who's online'' box, and so on, are stored as $user->{prefs}->{time_zone} and so on; the hash keys are the names of the preferences in the `prefname' field of the userprefs table.

The subscription information, such as when it was created, when it expires, whether or not it is active, and so on, are stored as $user->{sub}->{created} and so on; the hash keys are the names of the fields in the subscription_info table.

If you pass user_data() an array of UIDs, it will fetch all of the user data for all of the UIDs and save them to the user data cache, and return nothing. If you want the user data for a large number of users, this is the best way to do it because it guarantees only one hit to the database; calling user_data with single UIDs after this will fetch the information from the cache. If you skip the step of sending user_data an array of UIDs, it could potentially mean a number of hits to the database equal to the number of UIDs you want information for.

$S->get_nick_from_uid()
This method takes a numeric user ID and returns the corresponding nickname. If the user doesn't exist, it returns an empty string.

$S->get_uid_from_nick()
This method takes a nickname and returns the corresponding numeric user ID. If the user doesn't exist, it returns an undefined value.

$S->have_perm()
This method takes the name of a permission as seen in the Groups Admin Tool (A.12) and returns a true value if the current user has the permission. If an (optional) group ID is given as a second argument, it returns a true value if the group named has the permission. This is typically used in conditional blocks to permit or deny actions depending on permissions:
if ( $S->have_perm('comment_rate') ) {
	$out = "Hey you! Rate comments!";
}

$S->have_section_perm()
This method takes the name of a section permission and the name of a section as arguments and returns a true value if the user has the permission. This is used differently than $S->have_perm() because if a user has the section perm `hide_read_stories' then the user should not be allowed to read the story. This is because section perms are not a simple yes/no switch the way regular perms are; they distinguish between allowing, hiding, and denying permission. See section 4.16.3 for more details on section permissions and how to use them.
if ( $S->have_section_perm('hide_read_stories', 'news') ) {
	$out = "Never heard of it";
} elsif ( $S->have_section_perm('deny_read_stories', 'news') ) {
	$out = "You're not allowed in here";
} else {
	$out = "Welcome to this section";
}

$S->pref()
This method provides a way to get and set the user preferences for user requesting the current page.

To get a preference, pass the name of the user preference as the only argument; the return value is the value of that user preference.

To set a single preference, pass the name of the user preference and the new value as the only two arguments; the return value is a database error string if the update failed.

To set multiple preferences, pass a single variable that is a hashref of the preferences you wish to set, with prefnames as the keys and the new values as the hash values; the return value is a database error string if the update failed.

5.1.4 Story-related Scoop Methods

$S->displaystory()
This method formats the full story: header, introtext, and bodytext. It takes the story ID as the first parameter and returns two variables: a hashref containing all story data and the formatted story. The print_story box available on the Scoop Box Exchange (B) uses this to format the story with no comments.

$S->getstories()
This method gets story data with a wide variety of options. This should be used instead of accessing the database directly if at all possible, because among other things, this takes into account all group and section permissions for you.

It takes one argument, a hashref containing all of its options, with the option name (listed below) as the hash key. It returns an arrayref which contains one hashref for each story returned, with all of the data requested for that story in the hashref.

The options understood by getstories() are: (note that the dash is part of the key)

-type
This can be one of summaries, fullstory, titlesonly, titlesonly-section, or section. Each of those returns a list of stories and their data as described in their name, subject to the options set in the other parameters. This is the only option that is required.
-topic
This can be the internal name of any of the topics you have configured. (The topic ID, not the alttext.) Setting a -topic value when you are using a -type of either titlesonly or section means that you will get back only stories in the topic you named.
-user
This can be the nickname of any user on the system. Setting a -user value when you are using a -type of section means that you will get back only stories (and diaries) written by the user you named.
-section
This can be the internal name of any of the sections you have configured. Setting a -section value when you are using a -type of either titlesonly or section means that you will get back only stories in the section you named. If you want the `Everything' pseudo-section, use the section name `__all__'. If you want all sections except a particular section, use the name of the section you want to exclude prefixed with an exclamation mark (!).
-sid
This can be any valid story ID (sid) in the system. Setting a -sid value when you are using a -type of summaries, fullstory, or titlesonly means that you will get back only the story with the story ID you named.
-dispstatus
This can be any of the internally used display statuses: 1 for published to section only, 0 for published to front page, -1 for hidden, -2 for in the voting queue, and -3 for in the edit queue. Setting a -dispstatus value when you are using a -type of summaries means that you will get back only stories with the displaystatus you named. If you do not set a -dispstatus value, Scoop uses 0 (front page stories only).
-where
This can be any SQL `where' clause. Setting a -where value when you are using a -type of either summaries or titlesonly means that whatever SQL you put here will be appended to the SQL `where' clause.
-from
This can be any SQL `from' clause. Setting a -from value when you are using a -type of summaries means that whatever SQL you put here will be appended to the SQL `from' clause.
-perm_override
This is generally either a 1 or not set, as it is used as a true/false test. Setting a -perm_override value to anything true when you are using a -type of fullstory means that story viewing permissions are ignored and the user can see even hidden stories.
-page
This can be any positive number, and represents the `page number' of a story index page. Setting a -page value when you are using a -type of either summaries or section means that you will get a list of stories offset by that number of pages.

5.1.5 Comment-related Scoop Methods

$S->display_comments()
This method takes up to four arguments and returns formatted comments as a string. The four arguments are, in order, sid, pid, mode, and cid (story ID, parent comment ID, display mode, and comment ID). sid and cid together uniquely identify every comment. A pid of 0 indicates top level comments (you can think of the story as comment #0). The mode can be `alone' (do not display any replies) or `collapsed' (show only the subject and author as a link to the full comment); if blank, replies to the comment named in sid and cid are displayed and formatted according to the user's comment display preferences.
$S->_commentcount()
This method takes a sid (story ID) as its argument and returns the total number of comments, both topical and editorial, attached to that sid.
$S->_comment_highest()
This method takes a sid (story ID) as its argument and returns the cid (comment ID) of the highest numbered comment attached to that sid.

5.1.6 Other Useful Scoop Methods

$S->interpolate()
This method does all of the work of substituting the value of blocks into the keys before sending the page out. It can also be used to substitute the special keys in particular blocks. It takes a string containing the block you want interpolated and a hashref containing the data for the keys as arguments, and returns a string containing the block with the keys substituted.

Generally to use this function, you would build up a hashref with the data - the keys of the hashref must be the keys you want substituted in the block - then have the function interpolate all the special keys you handle in one go. Any other, non-special keys will not be touched at this time, and will be substituted when Scoop does its final page preparation.

$S->mail()
This method sends an email from the address you set in the site control local_email. The arguments it takes are, in order, the recipient's email address, the email subject, and the email content. If there is a problem with sending the email, it will return an error message.

$S->admin_alert()
This method sends an email from the address you set in the site control local_email to the email(s) listed in the site control admin_alert. It takes one argument, the warning about the event that triggered the email. Information on the current user (the user who triggered the email) is automatically included.

$S->urlify()
This method takes a string as its only argument and returns that string with any non-URL-safe characters URL-encoded. This method should be used on any string that will be used in a URL.

5.1.7 Debugging

If you look at the code, you will see the variable $DEBUG scattered all over, always near a warn() command. All of those debugging messages, if Scoop is in debugging mode, are printed in apache's error log. You can turn debugging on for each perl module by setting the value of $DEBUG to 1 at the top of the file.

Most debugging statements look like this:

warn "some error text" if $DEBUG;
They should include the function name, but not all of them do. The error or status text you include is completely up to you, and will depend on what behaviour you are trying to track down.

Whatever you do, don't forget the `if $DEBUG' part of the statement, and don't forget to turn $DEBUG off before you submit your patch. Debugging output in distributed code is not something most people like.

The only warn statements that should ever be used without the `if $DEBUG' are for actual errors or events that the admin must be able to look up in the logs. If the admin must be notified of something immediately (such as when a member tries to break things) you can use $S->admin_alert(), which sends an email to the site admins.


janra
2004-03-26