Jump to content

Extension:LifeMarks: Difference between revisions

From mediawiki.org
Content deleted Content added
Bakerq (talk | contribs)
LifeMarks Extension released
 
Bakerq (talk | contribs)
m Added the category tag
Line 926: Line 926:
I tried to stay organized with this, but the bigger it became, the harder it was to keep the bloat and sprawl away. I've probably written a lot of spaghetti here, so I ask for comment from anyone brave enough to dive in. I'm fairly satisfied with my first attempt at an extension, and I hope that some of you find it useful. Feel free to use the Talk page for comments or requests.<br/>
I tried to stay organized with this, but the bigger it became, the harder it was to keep the bloat and sprawl away. I've probably written a lot of spaghetti here, so I ask for comment from anyone brave enough to dive in. I'm fairly satisfied with my first attempt at an extension, and I hope that some of you find it useful. Feel free to use the Talk page for comments or requests.<br/>
--[[User:Bakerq|Bakerq]] 19:57, 9 September 2006 (UTC)
--[[User:Bakerq|Bakerq]] 19:57, 9 September 2006 (UTC)

[[Category:Extensions|ExpandCss]]

Revision as of 19:57, 9 September 2006

MediaWiki extensions manual
LifeMarks Extension
Release status: unknown
Description Mark notable dates and events for collection and display
Author(s) User:Bakerq
Latest version 0.1.0 (2006-09-9)
MediaWiki developed on 1.6.8
Licence No licence specified
Download Copy from this page

LifeMarks is an extension for MediaWiki that will allows you to add simple tags to articles consisting of a date and a description, with the goal of marking a date with an event; these dates could be collected into a timeline of events. This is my first extension written for MediaWiki, developed for version 1.6.8. I created it because the Events Extension, while very close to my needs, lacked some of the capability I needed. Also, I became somewhat enamored with the idea of filling out a wiki and (once a goodly number of events are recorded) using the events list to fill out a timeline.

Introduction

This extension was jumpstarted by borrowing code from the Events Extension and Tasks Extension, notably the database interface. I corrected the bug that resulted from a double-call of the parse() function (this would clobber any other extensions on the page and render them into UNIQ tags).

This extension introduces two new tags for use in an article. The first, <lifemark> is used to set an event marker on a date. The second, <lifemarklist> will render a list of all lifemarkers across the wiki that matches given criteria.

In its most simple form:

<lifemark>1533-09-07: Queen Elizabeth I born</lifemark>

This would create an event marker on the date of September 7, 1533, described as "Queen Elizabeth I born".

LifeMarks can further be given category tags for easy lookup later, such as listing all categories tagged with the word "births":

<lifemark tags="births">1533-09-07: [[w:Elizabeth I of England|Queen Elizabeth I]] born</lifemark>

All lifemarks can be listed in either a table or straight list by calling the <lifemarklist> tag. Following the above example, to display all LifeMarks tagged with the word "births":

<lifemarklist tags="births">List of Births</lifemarklist>

As you can see, the text in between the <lifemarklist> tags is the title of the list returned. Also, all texts provided (desciptions and titles) can include wiki-markup.

Using <LifeMarks>

LifeMarks can be set anywhere in any article. By default, they produce no output, so they're safe to use in the middle of a paragraph or block of text where appropriate.

LifeMark Syntax

LifeMarks can be called simply in the form of (which by default produces no output):

<lifemark>yyyy-mm-dd: Description or Title</lifemark>

Or with optional display and organizational parameters:

<lifemark display="display_def" tags="tagging_text">yyyy-mm-dd: Description or Title</lifemark>

Finally, multiple lifemarks can be set within a single tag. In this form, all lifemarks will receive the same display styling and tagging text as defined in the <lifemark>:

<lifemark display="display_def" tags="tagging_text">
yyyy-mm-dd: Description
yyyy-mm-dd: Description
yyyy-mm-dd: Description
yyyy-mm-dd: Description
</lifemark>

Customizing LifeMarks Display

What it looks like What you type

The default display keyword is "none". This produces no output as shown in a simple lifemark:


The following keywords are also available:

The default display keyword is "none".  This produces
no output as shown in a simple lifemark:

<lifemark>1999-9-9: Simple Lifemark</lifemark>

The following keywords are also available:
date

September 9th, 1990

; date : 
<lifemark display="date">
1990-9-9: Lifemark with display keyword: "date"
</lifemark>
slashdate

09/09/91

<nowiki>; slashdate : 
<lifemark display="slashdate">
1991-9-9: Lifemark with "slashdatedate" display keyword: "slashdate"
</lifemark>
meddate

Sep 9, 1993

; meddate : 
<lifemark display="meddate">
1993-9-9: Lifemark with "meddate" display keyword: "meddate"
</lifemark>
longdate

September 9th, 1992

; longdate : 
<lifemark display="longdate">
1992-9-9: Lifemark with "longdate" display keyword: "longdate"
</lifemark>

Notice that the date-based keywords do not contain line-breaks. This allows them to be used inside a paragraph and contribute to its composition. In the following line of text, the date is generated by a lifemark tag with the display set to "longdate":

The Y2K bug came and went on January 1st, 2000 with little tragedy.

Notice that the date-based keywords 
do '''not''' contain line-breaks.  
This allows them to be used inside 
a paragraph and contribute to its 
composition.  In the following line 
of text, the date is generated by a 
lifemark tag with the display set 
to "longdate":

The Y2K bug came and went on 
<lifemark display="longdate">2000-01-01: Y2K non-event</lifemark>
with little tragedy.

Event-based dispay keywords contain the desciption or title text given with the tag:

event

09-09-1994: Lifemark with display keyword: "event"

Event-based dispay keywords contain the desciption or
title text given with the tag:
; event :
<lifemark display="event">
1994-9-9: Lifemark with display keyword: "event"
</lifemark>
longevent

September 9th, 1996: Lifemark with display keyword: "longevent"

; longevent :
<lifemark display="longevent">
1996-9-9: Lifemark with display keyword: "longevent"
</lifemark>
medevent

Sep 9, 1998: Lifemark with display keyword: "medevent"

; medevent :
<lifemark display="medevent">
1998-9-9: Lifemark with display keyword: "medevent"
</lifemark>
slashevent

09/09/00: Lifemark with display keyword: "slashevent"

; slashevent :
<lifemark display="slashevent">
2000-9-9: Lifemark with display keyword: "slashevent"
</lifemark>

For convenience, each of the event-type display keywords also contain a plural form: events, longevents, medevents, and slashevents. Each of these display keywords end with a line-break, allowing lists to be formatted easily for multiple lifemarks.

events

January 20th, 1989: George H. W. Bush inaugurated
January 20th, 1993: Bill Clinton inaugurated
January 20th, 2001: George W. Bush inaugurated

For convenience, each of the event-type display keywords
also contain a plural form: '''events''', '''longevents''',
'''medevents''', and '''slashevents'''.  Each of these
display keywords end with a line-break, allowing lists
to be formatted easily for multiple lifemarks.

; events :
<lifemark display="longevents">
1989-1-20: George H. W. Bush inaugurated
1993-1-20: Bill Clinton inaugurated
2001-1-20: George W. Bush inaugurated
</lifemark>

Finally, if none of these keywords fit, you can define your own. You can construct a display tag with the variables %year%, %month%, %day% and %description%.

New Years Day (01-01-2006)

Finally, if none of these keywords fit, you can
define your own. You can construct a display tag with
the variables '''%year%''', '''%month%''', '''%day%'''
and '''%description%'''.

<lifemark display="%description% (%month%-%day%-%year%)">
2006-01-01: New Years Day
</lifemark>

If you're familiar with the PHP function date() then you may recognize a few of these display variables.

  • %year% is the same as %Y%, a 4-digit year.
  • %month% is the same as %m%, a 2-digit month.
  • %day% is the same as %d%, a 2-digit day.

1990-05-05

If you're familiar with the PHP function <code>date()</code>
then you may recognize a few of these display variables,
wrapped with '''%''' marks.

*'''%year%''' is the same as '''%Y%''', a 4-digit year.
*'''%month%''' is the same as '''%m%''', a 2-digit month.
*'''%day%''' is the same as '''%d%''', a 2-digit day.

<lifemark display="%Y%-%m%-%d%">1990-05-05: Cinco de Mayo!</lifemark>

Other date() variables that can be used are:

  • %y% is a 2-digit year.
  • %j% is the day with no leading zeros.
  • %n% is the month with no leading zeros.

7/4/76 Declaration of Independence

Lastly there are the names of the month in both long and short form, and a suffix for the day.

  • %F% is the full month name, like "January".
  • %M% is the abbreviated month name, "Jan"
  • %S% is the "st" in "1st", the "nd" in "2nd"
    the "rd" in "3rd" and the "th" in every other day.

Valentines Day fell on Feb 14th of 2004

Other <code>date()</code> variables that can be used are:
*'''%y%''' is a 2-digit year.
*'''%j%''' is the day with no leading zeros.
*'''%n%''' is the month with no leading zeros.

<lifemark display="%n%/%j%/%y% %description%">
1776-07-04: Declaration of Independence
</lifemark>

Lastly there are the names of the month in both
long and short form, and a suffix for the day.
*'''%F%''' is the full month name, like "January".
*'''%M%''' is the abbreviated month name, "Jan"
*'''%S%''' is the "st" in "1st", the "nd" in "2nd"
*:the "rd" in "3rd" and the "th" in every other day.

<lifemark display="%description% fell on %M% %j%%S% of %Y%">
2004-02-14: Valentines Day
</lifemark>


Using <LifeMarkList>

In the course of demonstrating the above, we've created a goodly number of lifemarks. Setting dates and events would be useless if there was no way to collect them and display them, which is where the <lifemarklist> comes in. LifeMarkLists, by default, are not returned in tables. They come out as straight text with a title, a hard line break, and then a list of lifemarkers that each use the events display keyword. (For tables, see below) The text in between the <lifemarklist> tags will become the title of the list. Just as with the <lifemark> tag, there are a number of display parameters and filtering parameters you can pass.

LifeMarkList Syntax

Generating a LifeMarkList of all LifeMarkers in the wiki is possible simply by using:

<nowiki><lifemarklist>List of LifeMarks</lifemarklist>

You can even remove the title by simply calling: <lifemarklist/> for a streamlined, no-options-at-all list of LifeMarks. There are, however, a great number of parameters that can be passed to the list to control its display and behavior.

<lifemaklist year="year_filter" month="month_filter" day="day_filter"
page="source_page_filter" limit="number_of_markers" offset="skipped_markers"
display="display_template" format="list|table" orderby="sort_order" sourcelinktext="link_word"
border="table_border_size" cellpadding="padding_size" cellspacing="spacing_size" style="css_style" 
class="css_class">Title Goes here</lifemarklist>

Customizing LifeMarkList Display

To limit the number returned, simply specify the criteria in the tag. For example:

<lifemarklist year="1993">Events in 1993</lifemarklist>

would list all lifemarkers in 1993. You can limit by year, month, day, source page_id, or by tags. For the first three, you can even specify a range, such as 1990-1992. Thus, the following:

<lifemarklist year="1992-2006" month="12" day="25" tags="happy">Happy Christmas dates in Vermont</lifemarklist>

would show all events that occured on December 25, during the years 1992 to 2006 and tagged with the word "happy". Listing all lifemarks from one page in the Wiki requires knowledge of that article's page_id. Users will rarely know or care about an article's page ID, however, by using the word "current", you can list all LifeMarkers defined on the current page.

<lifemarklist page="current">All events found on this page</lifemarklist>

Further ways to limit the display would be to set a limit on the number of events returned (limit="5"), or specifying the offset (offset=12).

You can change the order of events displayed as well. By default, the events are ordered chronologically starting with the earliest date. To list the events by month, you can use orderby="month", or by years in reverse order, "orderby=!year". The available sort methods are year, month, day, desccription, tags, page_id. Each method can be reversed by placing a ! in front, so !desc would list LifeMarkers by description in reverse alphabetical order.

Finally, you can set a display which is nearly identical to the display parameter on the <lifemarklist> tag, with a few notable exceptions. Firstly, it supports a display variable %source% which is replaced with a link to the page that has the LifeMarker in question. To extend this even further, you can pass another parameter, sourcelinktext which will become the link text. By default, this is "(src)". If you want the link to be the title of the origin page, use sourcelinktext="title".

The list can easily be returned in the form of a table as well. If you specify a border, cellpadding, cellspacing, style or class, or if you specify format="table", a table will be returned. (To apply a CSS class or style to a list, use format="list" to force a list-based output, but it's not guaranteed that the style will be applied properly throughout.) The table returned will place the table in the top row (colspan=3) and have columns for the date, source link, and description. When table output is used, the display parameter applies only to the date field. Placing the variable %description% in the display paramter for a table output would display the description twice: once in the description column and once crammed into the date column.

Special Page

A SpecialPage called LifeMarks will allow for collecting all LifeMarks and displaying them in order. They can also be filtered by year, month, day, source pages, description or (most likely) tag texts. (Although not through a web interface. That's a feature for the next version or an intrepid hacker.)

The SpecialPage differs from the usage of a <lifemarklist> tag in that it has links at the top of the table which can change the sort order immediately via up and down arrows next to the date, source, and description headers.

The source page definitions are included in the LifeMarks.php file, so no extra files are needed to be installed in other directories.

Future plans (beyond the simple filtering form fields) include a rudementary duplicate search.

Global Settings

The following Global Settings can be set in your LocalSettings.php file to alter the basic options of the LifeMarkers Extension.

$wgLifeMarkDefaultDisplay
If you require a prefix before your MySQL table names, include it with this setting. This string will be prefixed before all SQL calls.
$wgLifeMarkDefaultDisplay
Defaults to "none". By default, the <lifemark> tag produces no output. If you would like to define a default display, place it here.
$wgLifeMarkDefaultListDisplay
Defaults to "events". Similarly, the <lifemarklist> tag, when in list mode, display by default according to the events keyword. Change its default here.
$wgLifeMarkDefaultOrderBy =
Defaults to: 'year ASC, month ASC, day ASC, description ASC'. If you would like to change the default order that LifeMarkLists are displayed in, place it here. Please note that this is the SQL order which is appended directly to the end of the MySQL SELECT statement. This does not support the OrderBy keywords above.

Ideas

One possible further parament to the <lifemark> tag, fuzziness, would be a number of days (or more likely weeks) with which the date is fuzzy. Namily, if you have an event that took place in the Summer of 1985, you could set the date as July-1-1975 with a fuzziness of 8 weeks.


Installation

This extension saves all of its information in a MySQL table, which means you must run the following SQL command to create the space for its data. Read the note below if you use table prefixes

CREATE TABLE lifemarks (
   lmid MEDIUMINT(9) AUTO_INCREMENT NOT NULL PRIMARY KEY,
   page_id INT(8) UNSIGNED NOT NULL DEFAULT 0,
   year INT(4) UNSIGNED NOT NULL DEFAULT 0,
   month INT(2) UNSIGNED NOT NULL DEFAULT 0,
   day INT(2) UNSIGNED NOT NULL DEFAULT 0,
   description MEDIUMTEXT NOT NULL,
   tags MEDIUMTEXT NOT NULL
);


Place the source code below into a file called LifeMarks.php in your extensions directory. Then add the following line to your LocalSettings.php file:

require_once("extensions/LifeMarks.php");

If you require a SQL table prefix, alter the SQL code above to include it in the CREATE line. You must also set up the global setting $wgLifeMarkDBPrefix in your LocalSettings.php.

Source:

<?php
/*
 LifeMarks MediaWiki extension

SQL:

The following SQL must be executed prior to running this extension.  Please note that this extension does not yet
support database prefixes.

CREATE TABLE lifemarks (
    lmid MEDIUMINT(9) AUTO_INCREMENT NOT NULL PRIMARY KEY,
    page_id INT(8) UNSIGNED NOT NULL DEFAULT 0,
    year INT(4) UNSIGNED NOT NULL DEFAULT 0,
    month INT(2) UNSIGNED NOT NULL DEFAULT 0,
    day INT(2) UNSIGNED NOT NULL DEFAULT 0,
    description MEDIUMTEXT NOT NULL,
    tags MEDIUMTEXT NOT NULL
);



The purpose of this extension is to log lifemarks throughout a Wiki for collection and display.
A LifeMark is essentially an event in a life, or mark on a timeline, important enough for note.

USAGE:

You can simply call the tag with a date and a description:
 <lifemark>1998-12-31: Party like it's 1999</lifemark>

The above example produces no output and can be used easily inline text:

It wasn't until the the server crashed <lifemark>2003-5-12: Server crashed with all data</lifemark> that 
we decided to invest in a backup solution that was more reliable.

Lifemarks can be told to display themselves with either keywords or variables based on PHP Date() format:
<lifemark display="event">1999-12-31: Y2K Event</lifemark>
    produces (with wikimarkup): '''1999-12-31''': Y2K Event
<lifemark display="'''%F %j, %Y''': %description%">1999-12-31: Y2K Event</lifemark>
    produces:  '''December 31, 1999''': Y2K Event
Keywords are as follows (as applied to the above example):
    event:    '''1999-12-31''': Y2K Event

Lifemarks can also be tagged by text for categories:
<lifemark tag="Parties">2003-12-25: 2003 Company Christmas Party</lifemark>

Finally, multiple lifemarks can be tagged within a single block (tags and display styles apply to all):
<lifemark tag="Parties" display="event">
 1999-12-25: 1999 Company Christmas Party
 2001-04-30: Bob's 30th birthday party
 2003-07-04: Angela's [[Fourth of July] bash
 2005-02-14: Tina hosts her [[Valentine's Day|Valentine]] bash
</lifemark>

Also illustrated above is that wiki markup and links can be used in the descriptions.

TODONOW: Prevent duplicates, possibly low-level maint on special page

TODONOW: explain: Why not use timestamps?  Ambiguous dates!  QDateStamps!  Something!

*/

$extensionname = '[http://meta.wikimedia.org/wiki/LifeMarks LifeMarks]';
$extensiondesc = 'LifeMarks are notable events in a timeline with a description, (approximate) date and tags';
$extensionauthor = '[[w:User:Bakerq|Quentin Baker]]';
$extensionversion = '0.1.0';
if (!defined('MEDIAWIKI')) die();
$wgExtensionFunctions[] = 'wfLifeMarksExtension';
$wgHooks['ArticleSaveComplete'][] = 'saveLifeMarks';
$wgExtensionCredits['parserhook'][] = array(
        'name' => $extensionname,
        'description' => $extensiondesc,
        'author' => $extensionauthor,
        'version' => $extensionversion
);
$wgExtensionFunctions[] = 'wfSpecialLifeMarks' ;
$wgExtensionCredits['specialpage'][] = array(
        'name' => $extensionname,
        'description' => 'A Special Page for listing all LifeMarks on a wiki and organizing them',
        'author' => $extensionauthor,
        'version' => $extensionversion
);

//GLOBAL EXTENSION VARIABLES - THESE SHOULD BE SET IN LocalSettings.php
$wgLifeMarkDBPrefix = $wgLifeMarkDBPrefix ? $wgLifeMarkDBPrefix : null;
$wgLifeMarkDefaultDisplay = $wgLifeMarkDefaultDisplay ? $wgLifeMarkDefaultDisplay : "none";
$wgLifeMarkDefaultListDisplay = $wgLifeMarkDefaultListDisplay ? $wgLifeMarkDefaultListDisplay : "events";
$wgLifeMarkDefaultOrderBy = $wgLifemarkDefaultOrderBy ? $wgLifeMarkDefaultOrderBy : 'year ASC, month ASC, day ASC, description ASC';

function wfLifeMarksExtension() {
    global $wgParser;
    $wgParser->setHook( "lifemark", "renderLifeMarks" );
    $wgParser->setHook( "lifemarks", "renderLifeMarks" );
    $wgParser->setHook( "lifemarklist", "renderLifeMarksList" );
    $wgParser->setHook( "lifemarkslist", "renderLifeMarksList" );
}

function renderLifeMarks( $input, $argv) {
    global $wgLifeMarkDefaultDisplay;
    // COLLECT PARAMETERS
    $tagstext = $argv["tags"];
    $disptext = $argv["display"];
    # displaytemplate is grabbed from wgLifeMarkDefaultDispay first, then overwritten by disp parameter
    if (isset($disptext)) { 
        $displaytemplate = $disptext;
    } else {
        $displaytemplate = $wgLifeMarkDefaultDisplay;
    }
    # tags are collected from the parameter "tags"
    $tagstext = $argv["tags"];

    $foundlifemarks = getLifeMarks($input,$tagstext);

    //render, rinse, repeat
    $output = "";
    if ($foundlifemarks) {
        foreach($foundlifemarks as $lm)
            $output.=formatLifeMark($displaytemplate,$lm["year"],$lm["month"],$lm["day"],$lm["description"]);
        return($output);
    } else {
        // no lifemarks found
        return;
    }
}




function getLifeMarks($input,$tagstext) {
    // GET INFO FROM BODY OF TAG
    # now search the body for lifemark events (this code based on code from the Events extension)
    $bodymarks = preg_split("/[\n\r]+/", $input);
    foreach ($bodymarks as $lifemark_bodyline) {
    $exp = '/^\s*([0-9]{4}-[0-9]{1,2}-[0-9]{1,2}):\s*(.+)$/';
        if (preg_match($exp,$lifemark_bodyline,$matches)) {
            $date = $matches[1];
            $description = $matches[2];
            $datebits = explode("-",$date);
            $date_year  = $datebits[0];
            $date_month = $datebits[1];
            $date_day   = $datebits[2];
            $foundmarks[] = array(
                'year'    => $date_year,
                'month'    => $date_month,
                'day'    => $date_day,
                'description' => $description,
                'tags' => $tagstext
            );
        }
    }
    return($foundmarks);
}



# GRATEFULLY BORROWED FROM TASKS EXTENSION
function saveLifeMarks( $article, $user, $text ) {
    global $wgLifeMarkDBPrefix;
        $lifemarks_buffer = null;
        $page_id = $article->getID();
        $dbr =& wfGetDB( DB_MASTER );
        # Delete all tasks for this page.
        $dbr->delete(
                $wgLifeMarkDBPrefix . 'lifemarks',
                array( 'page_id' => $page_id )
        );
        $matches = array();
        $elements = array('lifemarks', 'lifemark');
        $text = Parser::extractTagsAndParams( $elements, $text, $matches );
        foreach( $matches as $marker => $data ) {
                list( $element, $content, $params, $tag ) = $data;
                $found = getLifeMarks($content,$params["tags"]);
                foreach($found as $datum)
                    $lifemarks_buffer[] = $datum;
        }
        # Re-insert all lifemarks that were created when parsing this page.
        foreach ($lifemarks_buffer as $lm) {
                $lm['page_id'] = $page_id;
                $dbr->insert(
                    $wgLifeMarkDBPrefix.'lifemarks',
                    $lm
                );
        }
        return 1;
}


function formatLifeMark($displaytemplate, $year, $month, $day, $desc, 
            $tags=null, $source=null, $parse_wiki=true) {
    global $wgLifeMarkDefaultDisplay, $wgTitle, $wgOut;

    // let's create some numerics
    $year = intval($year);
    $month = intval($month);
    $day = intval($day);

    // Process display keywords into something we can use
    switch ($displaytemplate) {
        case "date" :       $displaytemplate = "%F% %j%%S%, %Y%"; break;  
        case "slashdate" :  $displaytemplate = "%m%/%d%/%y%"; break;  
        case "dashdate" :   $displaytemplate = "%m%-%d%-%y%"; break;  
        case "longdate" :   $displaytemplate = "%F% %j%%S%, %Y%"; break;  
        case "meddate" :    $displaytemplate = "%M% %j%, %Y%"; break;  
        case "event" :      $displaytemplate = "'''%m%-%d%-%Y%''': %source% %description%"; break;
        case "events" :     $displaytemplate = "'''%m%-%d%-%Y%''': %source% %description%<br />"; break;
        case "longevent" :  $displaytemplate = "'''%F% %j%%S%, %Y%''': %source% %description%"; break;
        case "longevents":  $displaytemplate = "'''%F% %j%%S%, %Y%''': %source% %description%<br />"; break;
        case "medevent" :   $displaytemplate = "'''%M% %j%, %Y%''': %source% %description%"; break;
        case "medevents" :  $displaytemplate = "'''%M% %j%, %Y%''': %source% %description%<br />"; break;
        case "slashevent" : $displaytemplate = "'''%m%/%d%/%y%''': %source% %description%"; break;
        case "slashevents": $displaytemplate = "'''%m%/%d%/%y%''': %source% %description%<br />"; break;
        case "dashevent" :  $displaytemplate = "'''%m%-%d%-%y%''': %source% %description%"; break;
        case "dashevents":  $displaytemplate = "'''%m%-%d%-%y%''': %source% %description%<br />"; break;
        case "image" :      $displaytemplate = "IMG"; break;  //TODO: allow an image with a title tag, like a pin
        case "none" :       $displaytemplate = ""; break;
    }

    $localParser = new Parser();
    $output = "";
    $thisline = $displaytemplate;
    # simplestuff
    $thisline = str_replace("%year%", "%Y%", $thisline);  // convert big friendly names to stuff we use below
    $thisline = str_replace("%month%", "%m%", $thisline);
    $thisline = str_replace("%day%", "%d%", $thisline);
    $thisline = str_replace("%description%", $desc, $thisline);
    $thisline = str_replace("%source%", $source, $thisline);
    # TOUGH STUFF:    (see the big commented block below - I was planning to use date() for all this, but well...
    $thisline = str_replace("%d%", ((strlen(strval($day))<2) ? ("0".$day) : ($day)), $thisline); // make $day 2 digits
    $thisline = str_replace("%m%", ((strlen(strval($month))<2) ? ("0".$month) : ($month)), $thisline); // 2digit month
    $thisline = str_replace("%y%", ((strlen(strval($year))>2) ? (substr($year,-2,2)) : ($year)), $thisline); // 2digit year
    $thisline = str_replace("%j%", $day, $thisline); //day, no leading zeros
    $thisline = str_replace("%n%", $month, $thisline); //month, no leading zeros
    $thisline = str_replace("%Y%", $year, $thisline);  //year 4 digits
    # NOW FOR ENGLISH MONTH NAMES.  TODO, LET'S INTERNATIONALIZE THIS, EH?
    $monthname[1]  = array( 's'=>'Jan', 'l' => 'January' );
    $monthname[2]  = array( 's'=>'Feb', 'l' => 'February' );
    $monthname[3]  = array( 's'=>'Mar', 'l' => 'March' );
    $monthname[4]  = array( 's'=>'Apr', 'l' => 'April' );
    $monthname[5]  = array( 's'=>'May', 'l' => 'May' );
    $monthname[6]  = array( 's'=>'Jun', 'l' => 'June' );
    $monthname[7]  = array( 's'=>'Jul', 'l' => 'July' );
    $monthname[8]  = array( 's'=>'Aug', 'l' => 'August' );
    $monthname[9]  = array( 's'=>'Sep', 'l' => 'September' );
    $monthname[10] = array( 's'=>'Oct', 'l' => 'October' );
    $monthname[11] = array( 's'=>'Nov', 'l' => 'November' );
    $monthname[12] = array( 's'=>'Dec', 'l' => 'December' );
    $thisline = str_replace("%F%", $monthname[$month]['l'], $thisline); //long month name
    $thisline = str_replace("%M%", $monthname[$month]['s'], $thisline); //short month name
    switch (substr($day,-1,1)) {
        case "1" : $thisline = str_replace("%S%", "st", $thisline); break;
        case "2" : $thisline = str_replace("%S%", "nd", $thisline); break;
        case "3" : $thisline = str_replace("%S%", "rd", $thisline); break;
        default  : $thisline = str_replace("%S%", "th", $thisline); break;
    }
    $thisline = str_replace("%y", $year, $thisline);

    $processedline = $thisline;
    if ($parse_wiki) {  // IF THIS IS GOING TO BE PUT IN A  WIKI TABLE, DON'T PARSE NOW, THAT WILL COME LATER
        $temp_output = $localParser->parse($processedline, $wgTitle, $wgOut->mParserOptions, false);
        $output = $temp_output->getText();
    } else {
        $output = $processedline;
    }
    return($output);
}




function renderLifeMarksList( $input, $argv) {
    global $wgOut, $wgTitle, $wgParser, $wgLifeMarkDefaultOrderBy, $wgLifeMarkDefaultListDisplay,$wgLifeMarkDBPrefix;
    $page_titles = array();
    $dbr =& wfGetDB( DB_MASTER );
    // COLLECT FILTER PARAMETERS
    $yeartext = $argv["year"];
    $monthtext = $argv["month"];
    $daytext = $argv["day"];
    $tagtext = $argv["tags"];
    $disptext = $argv["display"];
    $formattext = $argv["format"];
    $limittext = $argv["limit"];
    $offsettext = $argv["offset"];
    $pagetext = $argv["page"];  
    // DISPLAY PARAMETERS
    $orderbytext = $argv["orderby"]; 
    $sourcelinktext = $argv["sourcelinktext"];
    // COLLECT HEADER PARAM (should only be called from Special Page)
    $headertext = $argv["header"];  // this will be filled by the Special page and *should* mirror the orderbytext
    // COLLECT TABLE PARAMS
    $tabledefstext = $argv["tabledefs"];
    $bordertext = $argv["border"];
    $cellpaddingtext = $argv["cellpadding"];
    $cellspacingtext = $argv["cellspacing"];
    $styletext = $argv["style"];
    $classtext = $argv["class"];

    #Set up SQL options
    $defaultorderby = $wgLifeMarkDefaultOrderBy;
    $orderby = null;
    switch ($orderbytext) {
        case 'year' :   $orderby = "year ASC"; break;
        case '!year' :  $orderby = "year DESC"; break;
        case 'month' :  $orderby = "month ASC"; break;
        case '!month' : $orderby = "month DESC"; break;
        case 'day' :    $orderby = "day ASC"; break;
        case '!day' :   $orderby = "day DESC"; break;
        case 'desc' :   $orderby = "description ASC"; break;
        case '!desc' :  $orderby = "description DESC"; break;
        case 'tags' :   $orderby = "tags ASC"; break;
        case '!tags' :  $orderby = "tags DESC"; break;
        case 'page' :   $orderby = "page_id ASC"; break;
        case '!page' :  $orderby = "page_id DESC"; break;
        default :       if ($orderbytext) { $orderbytext.=", "; } 
                        // if it doesn't match any of the others, try straight SQL
                        $orderby = $orderbytext . " $defaultorderby";
    }
    $options = array( 'ORDER BY'=>$orderby );  //default order

    if ($limittext) {
        if ($offsettext) { $limittext = "$offsettext, $limittext"; }
        $options['LIMIT'] = $limittext;
    }
                        
    # create the WHERE clause
    $where = array();
    if ($yeartext) {  //did they provide a year filter?
        if (preg_match('/([0-9]{4})-([0-9]{4})/',$yeartext,$matches)) {
            $year1 = $matches[1];
            $year2 = $matches[2];
            if ($year2<$year1) { list($year1,$year2) = array($year2,$year1);  } //swap 'em!
            $where[] = 'year>="' . $year1 .'"'; // the lower year
            $where[] = 'year<="' . $year2 .'"'; // the higher year
            // TODO: This entire extension completely ignores any years set for BC.
        } else {
            $where[] = 'year="' . $yeartext.'"'; 
        }
    }
    if ($monthtext) { // how about a month filter?
        if (preg_match('/([0-9]{1,2})-([0-9]{1,2})/',$monthtext,$matches)) {
            $month1 = $matches[1];
            $month2 = $matches[2];
            if ($month2<$month1) { list($month1,$month2) = array($month2,$month1);  } //swap 'em!
            $where[] = 'month>="' . $month1 .'"'; // the lower month
            $where[] = 'month<="' . $month2 .'"'; // the higher month
        } else {
            $where[] = 'month="' . $monthtext.'"'; 
        }
    }
    if ($daytext) { // perhaps a day filter?
        if (preg_match('/([0-9]{1,2})-([0-9]{1,2})/',$daytext,$matches)) {
            $day1 = $matches[1];
            $day2 = $matches[2];
            if ($day2<$day1) { list($day1,$day2) = array($day2,$day1);  } //swap 'em!
            $where[] = 'day>="' . $day1 .'"'; // the lower day
            $where[] = 'day<="' . $day2 .'"'; // the higher day
        } else {
            $where[] = 'day="' . $daytext . '"'; 
        }
    }
    if ($tagtext) { // this is a short tag filter
        $where[] = 'tags like "%' . $tagtext . '%"';    
    }
    if ($pagetext) { // and finally, a page_id filter
        if ($pagetext=="current") { //page="current" means list only marks on this page.
            $pageid = $wgTitle->getArticleID();
            $where[] = 'page_id="' . $pageid . '"';
        } elseif (is_numeric($pagetext)) {
            $where[] = 'page_id="' . $pagetext . '"';
        } else { # it's neither  the word "current" nor a numeric page_id, it must be the title
            //TODO:set up a way to take a page title and convert it to pageid.
            // I have yet to find a standard method of determining the pageid of an article by title string
        }
    }
    #set up a few display and count variables
    $sourcetag = "src";  //set up default sourcetag
    if ($sourcelinktext=="none") {
        $sourcetag = null;
    } else {
        if ($sourcelinktext) $sourcetag = $sourcelinktext;
    }
    $count = 0;
    $title = $input;
    # put together the output table definitions
    # THE POINT HERE IS TO SET UP A STRING VARIABLE CALLED $outputshell WHICH CONTAINS
    #  THE TEXT "%%CONTENT%%".  THIS TAGWILL BE REPLACED BY THE LIST OF FORMATTED LIFEMARKS
    $tabledefs = "$tabledefstext";
    if ($bordertext) $tabledefs.=" border=\"$bordertext\"";
    if ($cellpaddingtext) $tabledefs.=" cellpadding=\"$cellpaddingtext\"";
    if ($cellspacingtext) $tabledefs.=" cellspacing=\"$cellspacingtext\"";
    if ($styletext) $tabledefs.=" style=\"$styletext\"";
    if ($classtext) $tabledefs.=" class=\"$classtext\"";

    #do we want a list or a table?  First check the $format variable.  Failing that, check for any $tabledefs
    #if there are no tabledefs, then just return a list.  Otherwise, wrap it in a table.
    $tablemode = false;  // by default assume that just a list is wanted to be returned    
    if ($formattext=="list") {
        $tablemode = false;
    } elseif (($formattext=="table") or ($tabledefs)) {
        $tablemode = true;
    }

    # let's make the table look pretty
    if ($tablemode) {
        if ($title) { $tabletitle = "\n! align=\"center\" colspan=\"3\" | $input"; }
        if ($headertext) { 
            $url=$wgTitle->getPrefixedText();
            $dateasc  = "[[" . $url . "/order=year|↓]]";
            $datedesc = "[[" . $url . "/order=!year|↑]]";
            $srcasc   = "[[" . $url . "/order=page|↓]]";
            $srcdesc  = "[[" . $url . "/order=!page|↑]]";
            $descasc  = "[[" . $url . "/order=desc|↓]]";
            $descdesc = "[[" . $url . "/order=!desc|↑]]";
            $header = "\n|- align=\"center\" \n! Date $dateasc $datedesc !! Source $srcasc $srcdesc !! align=\"left\" | Description $descasc $descdesc\n";
        } else {
            $header = null;
        }                        
        $outputshell = "\n{| $tabledefs $tabletitle\n$header%%CONTENT%%\n|}";
        $tablemode = true;
        if (!$disptext) // no display mode was picked for table mode, make our own, date only since it's in a table
            $disptext = "'''%Y%-%m%-%d%'''";
        if ($tablemode) { // since we're in table mode, the DISPLAY parameter applies to the date
            $disptext = "\n|-\n|| " . $disptext . "||%source%\n||%description%";
        }
    } else {
    # looks like we're doing a list.
        if (!$disptext) // no display mode was picked for list mode, so let's use a default
            $disptext = $wgLifeMarkDefaultListDisplay;
        if ($title) { $title = "$input<br />"; }
        if ($styletext) $styledefs ="style=\"$styletext\"";
        if ($classtext) $classdefs ="class=\"$classtext\"";
        $outputshell = "<div $styledefs $classdefs>$title" . "%%CONTENT%%</div>";
    }


    #execute the SQL
    $res = $dbr->select(
                $wgLifeMarkDBPrefix.'lifemarks',
                array('page_id','year','month','day','description','tags'),
                $where, 'Database::select',
                $options
            );
    if (!$res) { return(0); }

    #loop through the SQL and find events
    while ($lm = $dbr->fetchRow($res)) {
        $page_id = $lm['page_id'];
        $year = $lm['year'];
        $month = $lm['month'];
        $day = $lm['day'];
        $description = $lm['description'];
        $tags = $lm['tags'];
        $page_title = $page_titles[$page_id]; // this keeps a temp cache of page titles
        if (!$page_title) { 
            $page_titles[$page_id] = Title::nameOf($page_id);
            $page_title = $page_titles[$page_id];
        }
        if ($sourcetag=="title") { 
            $pagesource = "[[$page_title]]";  
        } elseif ($sourcetag) { //anything besides the word "name" becomes the linktext
            $pagesource = "([[:$page_title|$sourcetag]])"; 
        } else {
            $pagesource = "";
        }

        $output.=formatLifeMark(
                $disptext,
                $year,
                $month,
                $day,
                $description,
                $tags,
                $pagesource,
                false//true //DO NOT PERFORM WIKI MARKUP, WE'LL DO THAT OURSELVES SHORTLY.
            );
        $count++;
    }

    $dbr->freeResult( $res );

    if (!$count) {
        $output.="No lifemarks found!";
    }


    $complete_output  = str_replace("%%CONTENT%%", $output, $outputshell);

    # now process the $outputshell which will convert any wikimarkup
    $localParser = new Parser();
    $tempoutput = $localParser->parse($complete_output, $wgTitle, $wgOut->mParserOptions, false);
    $output = $tempoutput->getText();


    return($output);
}




function wfSpecialLifeMarks() {
        require_once('SpecialPage.php');

        # complete the messages that will be used :
        global $wgMessageCache ;
                $wgMessageCache->addMessages(array(
                'lifemarks' => 'LifeMarks' ,
        ));
                
        class SpecialPage_LifeMarks extends SpecialPage
        {
                # constructor
                function SpecialPage_LifeMarks($restriction = '') {
                        SpecialPage::SpecialPage('LifeMarks', $restriction ) ;
                }
                
                # override of the abstract execute() function, manages the output
                function execute($args_string = '') {
                        global $wgOut, $wgTitle, $wgParser;
                        $dbr =& wfGetDB( DB_MASTER );
                        
                        # Parse arguments.
                        $args = array();
                        foreach (explode('/',$args_string) as $pair) {
                                $pair = explode('=',$pair);
                                $args[$pair[0]] = $pair[1];
                        }

                        $title = "LifeMarks";
                        $args['header'] = 1;  // Make sure that a header is displayed
                        $args['format'] = "table"; //The Special Page will always list in table format
                        $args['style'] = "border: 1px solid black;";
                        $args['sourcelinktext'] = "title";

                        # parse the parameters
                        if ($args['order'])    $args['orderby'] = $args['order'];
                        if ($args['year']) {
                                $title.= ' in year(s) ' . $args['year'];
                        }
                        if ($args['month']) {
                                $title.= ' in month(s) ' . $args['month'];
                        }
                        if ($args['day']) {
                                $title.= ' on day(s) ' . $args['day'];
                        }
                        if ($args['tags']) {
                                $title.= ' whose tags include "' . $args['tags'] .'"';
                        }
                        if ($args['page']) {
                                $page_title = Title::nameOf($args['page']);
                                $title.= ' found on page ' . $page_title;
                        }


                        $wgOut->setPageTitle($title);
                        $wgOut->addWikiText("This page will list all LifeMarks found in the Wiki.  You can arrange them by date, source page or description.  Technically, you can also list them by tags but I haven't coded that in yet.  I'd also like to allow you to limit the selection to just one namespace, but I'm beginning to think that my method of passing parameters to this page (in the form of url parameters like <code>/year=1999/order=!month</code>) is a bit too obtuse for that.  I'm looking for comments for improvement.");
                        $wgOut->addWikiText("Next on the list of Things To Do™ would be to put a form on this page allowing you to type in a month or year to filter the list.");
                        $output = renderLifeMarksList($title,$args);
                        $wgOut->addHTML($output);                
                        
                        # http://meta.wikimedia.org/wiki/Talk:Permissions (Error message with MediaWiki v1.6.5)
                        # explains line below
                        $wgOut->setArticleFlag( false );
                }
        }
        
        SpecialPage::addPage ( new SpecialPage_LifeMarks() ) ;
}
?>


Personal Thoughts

I tried to stay organized with this, but the bigger it became, the harder it was to keep the bloat and sprawl away. I've probably written a lot of spaghetti here, so I ask for comment from anyone brave enough to dive in. I'm fairly satisfied with my first attempt at an extension, and I hope that some of you find it useful. Feel free to use the Talk page for comments or requests.
--Bakerq 19:57, 9 September 2006 (UTC)