Template:AddEventForm/doc

From Cyraxx Wiki - lolcow.city
Jump to navigation Jump to search

Extension: Special:AddEventForm

This extension provides a way to add and delete events on the GlobalEvents page using special forms.

Installation

1. Download and extract the extension files into the `extensions/SpecialAddEventForm` directory. 2. Add the following to your `LocalSettings.php` file:

   wfLoadExtension('SpecialAddEventForm');

Configuration

Make sure the extension files are set up correctly in the `extension.json`.

{
    "name": "SpecialAddEventForm",
    "type": "specialpage",
    "AutoloadClasses": {
        "SpecialAddEventForm": "SpecialAddEventForm.class.php",
        "SpecialGlobalEvents": "SpecialGlobalEvents.php",
        "SpecialDeleteEventForm": "SpecialDeleteEventForm.php",
        "SpecialAddEventFormHelper": "SpecialAddEventFormHelper.php"
    },
    "SpecialPages": {
        "AddEventForm": "SpecialAddEventForm",
        "GlobalEvents": "SpecialGlobalEvents",
        "DeleteEventForm": "SpecialDeleteEventForm"
    },
    "MessagesDirs": {
        "SpecialAddEventForm": ["i18n"]
    },
    "manifest_version": 1
}

Special Pages

This extension creates two special pages:

  • AddEventForm: Allows users to add new events.
  • DeleteEventForm: Allows users to delete existing events.

Usage

To Add an Event:

  1. Navigate to `Special:AddEventForm`.
  2. Fill in the date, title, and description.
  3. Click the "Submit" button.

To Delete an Event:

  1. Navigate to `Special:DeleteEventForm`.
  2. Select the event from the dropdown list.
  3. Click the "Delete" button.

SpecialAddEventForm.class.php

<?php

use MediaWiki\MediaWikiServices;

class SpecialAddEventForm extends SpecialPage {

    public function __construct() {
        parent::__construct('AddEventForm');
    }

    public function execute($subPage) {
        $this->setHeaders();
        $this->outputHeader();

        // Prettier title and description
        $out = $this->getOutput();
        $out->setPageTitle('Add New Event');
        $out->addHTML('<p>Use this form to add a new event to the GlobalEvents page. Please ensure that the date and title are correctly formatted.</p>');
        $out->addHTML('<p>To delete an event, please use the <a href="' . SpecialPage::getTitleFor('DeleteEventForm')->getLocalURL() . '">Delete Event Form</a>.</p>');

        // Display the add event form
        $formDescriptor = [
            'date' => [
                'type' => 'text',
                'label-message' => 'Date (YYYY-MM-DD)',
                'required' => true,
            ],
            'title' => [
                'type' => 'text',
                'label-message' => 'Event Title',
                'required' => true,
            ],
            'description' => [
                'type' => 'textarea',
                'label-message' => 'Event Description',
                'rows' => 5,
            ],
        ];

        $htmlForm = HTMLForm::factory('ooui', $formDescriptor, $this->getContext(), 'addeventform')
            ->setMethod('post')
            ->setAction($this->getPageTitle()->getLocalURL())
            ->setSubmitText('Submit')
            ->setWrapperLegend('Add New Event')
            ->setSubmitCallback([$this, 'onSubmit'])
            ->show();
    }

    public function onSubmit($formData) {
        $date = $formData['date'];
        $title = $formData['title'];
        $description = $formData['description'] ?? '';

        // Validate date format
        if (!SpecialAddEventFormHelper::validateDateFormat($date)) {
            return $this->getOutput()->addHTML('<p>Error: Invalid date format. Please use YYYY-MM-DD.</p>');
        }

        // Sanitize inputs
        $title = SpecialAddEventFormHelper::sanitizeInput($title);
        $description = SpecialAddEventFormHelper::sanitizeInput($description);

        // Check if sanitization removed forbidden characters
        if ($title === false || $description === false) {
            return $this->getOutput()->addHTML('<p>Error: Invalid characters in input.</p>');
        }

        if (!$date || !$title) {
            return $this->getOutput()->addHTML('<p>Date and title are mandatory fields.</p>');
        }

        $newEvent = [
            'date' => $date,
            'title' => $title,
            'description' => $description
        ];

        if (SpecialAddEventFormHelper::addEvent($newEvent)) {
            // Update the GlobalEvents page
            $this->updateGlobalEventsPage();
            return $this->getOutput()->addHTML('<p>Event added successfully!</p>');
        } else {
            return $this->getOutput()->addHTML('<p>Error: Could not add event.</p>');
        }
    }

    private function updateGlobalEventsPage() {
        $titleObj = Title::newFromText('GlobalEvents');
        if (!$titleObj) {
            throw new Exception('Could not create title object.');
        }
        $page = MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle($titleObj);
        if (!$page) {
            throw new Exception('Could not create WikiPage object.');
        }

        $events = SpecialAddEventFormHelper::readEventData();
        $contentText = SpecialAddEventFormHelper::displayEvents($events);

        $contentObj = ContentHandler::makeContent($contentText, $titleObj);

        // Use WikiPage::doUserEditContent to handle the edit
        $page->doUserEditContent(
            $contentObj,
            $this->getUser(),
            'Updated GlobalEvents page with new event.'
        );
    }
}

SpecialDeleteEventForm.php

<?php

use MediaWiki\MediaWikiServices;

class SpecialDeleteEventForm extends SpecialPage {

    public function __construct() {
        parent::__construct('DeleteEventForm');
    }

    public function execute($subPage) {
        $this->setHeaders();
        $this->outputHeader();

        // Prettier title and description
        $out = $this->getOutput();
        $out->setPageTitle('Delete Event');
        $out->addHTML('<p>Use this form to delete an event from the GlobalEvents page. Select the event from the dropdown list below.</p>');
        $out->addHTML('<p>To add a new event, please use the <a href="' . SpecialPage::getTitleFor('AddEventForm')->getLocalURL() . '">Add Event Form</a>.</p>');

        $events = SpecialAddEventFormHelper::readEventData();
        $eventOptions = $this->getEventOptions($events);

        $formDescriptor = [
            'event' => [
                'type' => 'select',
                'label-message' => 'Select Event to Delete',
                'options' => $eventOptions,
                'required' => true,
            ],
        ];

        $htmlForm = HTMLForm::factory('ooui', $formDescriptor, $this->getContext(), 'deleteeventform')
            ->setMethod('post')
            ->setAction($this->getPageTitle()->getLocalURL())
            ->setSubmitText('Delete')
            ->setWrapperLegend('Delete Event')
            ->setSubmitCallback([$this, 'onDelete'])
            ->show();
    }

    private function getEventOptions($events) {
        $options = [];
        foreach ($events as $section) {
            foreach ($section['events'] as $event) {
                $eventLabel = "{$event['date']} - {$event['title']}";
                $options[$eventLabel] = json_encode(['title' => $section['title'], 'date' => $event['date'], 'title' => $event['title']]);
            }
        }
        return $options;
    }

    public function onDelete($formData) {
        $event = json_decode($formData['event'], true);
        if (SpecialAddEventFormHelper::deleteEvent($event['title'], $event['date'])) {
            // Update the GlobalEvents page
            $this->updateGlobalEventsPage();
            return $this->getOutput()->addHTML('<p>Event deleted successfully!</p>');
        } else {
            return $this->getOutput()->addHTML('<p>Error: Could not delete event.</p>');
        }
    }

    private function updateGlobalEventsPage() {
        $titleObj = Title::newFromText('GlobalEvents');
        if (!$titleObj) {
            throw new Exception('Could not create title object.');
        }
        $page = MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle($titleObj);
        if (!$page) {
            throw new Exception('Could not create WikiPage object.');
        }

        $events = SpecialAddEventFormHelper::readEventData();
        $contentText = SpecialAddEventFormHelper::displayEvents($events);

        $contentObj = ContentHandler::makeContent($contentText, $titleObj);

        // Use WikiPage::doUserEditContent to handle the edit
        $page->doUserEditContent(
            $contentObj,
            $this->getUser(),
            'Updated GlobalEvents page after deleting an event.'
        );
    }
}

SpecialAddEventFormHelper.php

<?php

class SpecialAddEventFormHelper {

    private static $jsonFile = __DIR__ . '/events.json';

    public static function readEventData() {
        $jsonData = file_get_contents(self::$jsonFile);
        return json_decode($jsonData, true);
    }

    public static function saveEventData($events) {
        file_put_contents(self::$jsonFile, json_encode($events, JSON_PRETTY_PRINT));
    }

    public static function displayEvents($events) {
        // Sort sections by newest to oldest
        usort($events, function($a, $b) {
            return strtotime(end($b['events'])['date']) - strtotime(end($a['events'])['date']);
        });

        $output = "''Note: Please use the [[Special:AddEventForm|Add Event Form]] to add new events and the [[Special:DeleteEventForm|Delete Event Form]] to delete events.''\n\n";
        foreach ($events as $section) {
            $output .= "{{EventSection\n";
            $output .= "| title = {$section['title']}\n";
            $output .= "| dateHeader = Date\n";
            $output .= "| titleHeader = Event Name\n";
            $output .= "| descriptionHeader = Description\n";
            $output .= "| newsHeader = Description\n";
            $output .= "| tableStyle = width: 100%;\n";
            $output .= "| tableClass = wikitable\n";
            $output .= "| events =\n\n";
            foreach ($section['events'] as $event) {
                $output .= "{{EventRow\n";
                $output .= "| date = {$event['date']}\n";
                $output .= "| title = {$event['title']}\n";
                $output .= "| description = {$event['description']}\n";
                $output .= "}}\n";
            }
            $output .= "}}\n\n";
        }
        return $output;
    }

    public static function addEvent($newEvent) {
        $events = self::readEventData();
        $monthYear = date('F Y', strtotime($newEvent['date']));

        foreach ($events as &$section) {
            if ($section['title'] === $monthYear) {
                $section['events'][] = $newEvent;
                usort($section['events'], function($a, $b) {
                    return strtotime($b['date']) - strtotime($a['date']);
                });
                self::saveEventData($events);
                return true;
            }
        }

        // If the section does not exist, create a new one
        $events[] = [
            'title' => $monthYear,
            'events' => [$newEvent]
        ];
        usort($events, function($a, $b) {
            return strtotime(end($b['events'])['date']) - strtotime(end($a['events'])['date']);
        });
        self::saveEventData($events);

        return true;
    }

    public static function deleteEvent($title, $date) {
        $events = self::readEventData();
        foreach ($events as $sectionIndex => &$section) {
            if ($section['title'] === date('F Y', strtotime($date))) {
                foreach ($section['events'] as $eventIndex => $event) {
                    if ($event['title'] === $title && $event['date'] === $date) {
                        array_splice($section['events'], $eventIndex, 1);
                        if (empty($section['events'])) {
                            array_splice($events, $sectionIndex, 1);
                        }
                        self::saveEventData($events);
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public static function validateDateFormat($date) {
        return preg_match('/^\d{4}-\d{2}-\d{2}$/', $date);
    }

    public static function sanitizeInput($input) {
        // Remove any table-related characters not within [[ ]]
        $input = preg_replace_callback(
            '/\[\[.*?\]\]/',
            function ($matches) {
                $link = $matches[0];
                if (substr_count($link, '|') > 1) {
                    return false; // More than one pipe inside [[ ]]
                }
                return str_replace('|', '{{PIPE}}', $link);
            },
            $input
        );

        if ($input === false) {
            return false; // Invalid input due to multiple pipes in [[ ]]
        }

        // Remove all occurrences of |- and other table characters outside of [[ ]]
        if (preg_match('/(\|-|\{\||\|\}|\|\+|[^\[\]]\|[^\[\]])/', $input)) {
            return false;
        }

        // Restore the pipes within links
        $input = str_replace('{{PIPE}}', '|', $input);
        return $input;
    }
}

SpecialAddEventForm.php

This file defines and registers the special page `AddEventForm` with MediaWiki.

<?php

if (function_exists('wfLoadExtension')) {
    wfLoadExtension('SpecialAddEventForm');
    // Keep i18n globals so mergeMessageFileList.php doesn't break
    $wgMessagesDirs['SpecialAddEventForm'] = __DIR__ . '/i18n';
    $wgExtensionMessagesFiles['SpecialAddEventFormAlias'] = __DIR__ . '/SpecialAddEventForm.alias.php';
    wfWarn(
        'Deprecated PHP entry point used for the SpecialAddEventForm extension. Please use wfLoadExtension instead, ' .
        'see https://www.mediawiki.org/wiki/Extension_registration for more details.'
    );
    return true;
} else {
    die('This version of the SpecialAddEventForm extension requires MediaWiki 1.25+');
}

SpecialAddEventForm.alias.php

Note: Not implemented in this wiki

<?php

$specialPageAliases = [];

$specialPageAliases['en'] = [
    'AddEventForm' => [ 'AddEventForm' ],
    'DeleteEventForm' => [ 'DeleteEventForm' ],
];

return true;

LocalSettings.php Configuration

Add the following line to your `LocalSettings.php` file to enable the extension:

wfLoadExtension('SpecialAddEventForm');

JSON File Documentation

The extension uses a JSON file to store event data. This file is read and written to by the `SpecialAddEventFormHelper` class.

The JSON file is located in the `extensions/SpecialAddEventForm` directory and is named `events.json`.

events.json

The `events.json` file stores the event data in the following format:

[
    {
        "title": "February 2023",
        "events": [
            {
                "date": "2023-02-25",
                "title": "Chance Dances for Marty",
                "description": "[[MusicBizMarty]] made a deal with Cyraxx to dance in order to get his [[Xbox Saga|Xbox]] mailed back to him. Believing he was dancing to his own music, Cyraxx performed to \"Debbie Deb - Lookout Weekend,\" \"Stacy Q - Two of Hearts,\" and \"Aqua - Barbie Girl\""
            }
        ]
    },
    {
        "title": "June 2023",
        "events": [
            {
                "date": "2023-06-25",
                "title": "[[June 25th, 2023 Arrest]]",
                "description": "On June 25th, 2023, Chance Wilkins was detained by Akron Police for assaulting a troll with a bat. The incident was recorded by the troll, capturing Cyraxx's arrest while his step-grandfather, Ed, attempted to confront the individual filming the event."
            },
            {
                "date": "2023-06-26",
                "title": "Cyraxx Bailed Out",
                "description": "On June 26th, 2023, Cyraxx was bonded out of jail following his [[June 25th, 2023 Arrest]], the day prior."
            }
        ]
    }
]

Handling the JSON File

    • Reading the JSON file:**

The `readEventData()` function in `SpecialAddEventFormHelper.php` reads the event data from the `events.json` file.

public static function readEventData() {
    $jsonData = file_get_contents(self::$jsonFile);
    return json_decode($jsonData, true);
}
    • Writing to the JSON file:**

The `saveEventData()` function in `SpecialAddEventFormHelper.php` writes the event data to the `events.json` file.

public static function saveEventData($events) {
    file_put_contents(self::$jsonFile, json_encode($events, JSON_PRETTY_PRINT));
}

Adding Permissions

By default, the `AddEventForm` and `DeleteEventForm` special pages can be accessed by any user who can view special pages. To restrict these actions to specific user groups, such as administrators, you need to add permission checks.

Step 1: Define Permissions

Add custom permissions for adding and deleting events in your `LocalSettings.php` file:

$wgGroupPermissions['sysop']['add-event'] = true;
$wgGroupPermissions['sysop']['delete-event'] = true;

This code grants the `add-event` and `delete-event` permissions to the `sysop` group (administrators). You can adjust the permissions to other user groups as needed.

Step 2: Update Special Pages to Check Permissions

Modify the `SpecialAddEventForm.class.php` and `SpecialDeleteEventForm.php` files to include permission checks.

SpecialAddEventForm.class.php
<?php

use MediaWiki\MediaWikiServices;

class SpecialAddEventForm extends SpecialPage {

    public function __construct() {
        parent::__construct('AddEventForm');
    }

    public function execute($subPage) {
        // Check if the user has the appropriate permission
        $this->checkPermissions();

        $this->setHeaders();
        $this->outputHeader();

        // Prettier title and description
        $out = $this->getOutput();
        $out->setPageTitle('Add New Event');
        $out->addHTML('<p>Use this form to add a new event to the GlobalEvents page. Please ensure that the date and title are correctly formatted.</p>');
        $out->addHTML('<p>To delete an event, please use the <a href="' . SpecialPage::getTitleFor('DeleteEventForm')->getLocalURL() . '">Delete Event Form</a>.</p>');

        // Display the add event form
        $formDescriptor = [
            'date' => [
                'type' => 'text',
                'label-message' => 'Date (YYYY-MM-DD)',
                'required' => true,
            ],
            'title' => [
                'type' => 'text',
                'label-message' => 'Event Title',
                'required' => true,
            ],
            'description' => [
                'type' => 'textarea',
                'label-message' => 'Event Description',
                'rows' => 5,
            ],
        ];

        $htmlForm = HTMLForm::factory('ooui', $formDescriptor, $this->getContext(), 'addeventform')
            ->setMethod('post')
            ->setAction($this->getPageTitle()->getLocalURL())
            ->setSubmitText('Submit')
            ->setWrapperLegend('Add New Event')
            ->setSubmitCallback([$this, 'onSubmit'])
            ->show();
    }

    protected function checkPermissions() {
        if (!$this->getUser()->isAllowed('add-event')) {
            throw new PermissionsError('add-event');
        }
    }

    public function onSubmit($formData) {
        $date = $formData['date'];
        $title = $formData['title'];
        $description = $formData['description'] ?? '';

        // Validate date format
        if (!SpecialAddEventFormHelper::validateDateFormat($date)) {
            return $this->getOutput()->addHTML('<p>Error: Invalid date format. Please use YYYY-MM-DD.</p>');
        }

        // Sanitize inputs
        $title = SpecialAddEventFormHelper::sanitizeInput($title);
        $description = SpecialAddEventFormHelper::sanitizeInput($description);

        // Check if sanitization removed forbidden characters
        if ($title === false || $description === false) {
            return $this->getOutput()->addHTML('<p>Error: Invalid characters in input.</p>');
        }

        if (!$date || !$title) {
            return $this->getOutput()->addHTML('<p>Date and title are mandatory fields.</p>');
        }

        $newEvent = [
            'date' => $date,
            'title' => $title,
            'description' => $description
        ];

        if (SpecialAddEventFormHelper::addEvent($newEvent)) {
            // Update the GlobalEvents page
            $this->updateGlobalEventsPage();
            return $this->getOutput()->addHTML('<p>Event added successfully!</p>');
        } else {
            return $this->getOutput()->addHTML('<p>Error: Could not add event.</p>');
        }
    }

    private function updateGlobalEventsPage() {
        $titleObj = Title::newFromText('GlobalEvents');
        if (!$titleObj) {
            throw new Exception('Could not create title object.');
        }
        $page = MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle($titleObj);
        if (!$page) {
            throw new Exception('Could not create WikiPage object.');
        }

        $events = SpecialAddEventFormHelper::readEventData();
        $contentText = SpecialAddEventFormHelper::displayEvents($events);

        $contentObj = ContentHandler::makeContent($contentText, $titleObj);

        // Use WikiPage::doUserEditContent to handle the edit
        $page->doUserEditContent(
            $contentObj,
            $this->getUser(),
            'Updated GlobalEvents page with new event.'
        );
    }
}
SpecialDeleteEventForm.php
<?php

use MediaWiki\MediaWikiServices;

class SpecialDeleteEventForm extends SpecialPage {

    public function __construct() {
        parent::__construct('DeleteEventForm');
    }

    public function execute($subPage) {
        // Check if the user has the appropriate permission
        $this->checkPermissions();

        $this->setHeaders();
        $this->outputHeader();

        // Prettier title and description
        $out = $this->getOutput();
        $out->setPageTitle('Delete Event');
        $out->addHTML('<p>Use this form to delete an event from the GlobalEvents page. Select the event from the dropdown list below.</p>');
        $out->addHTML('<p>To add a new event, please use the <a href="' . SpecialPage::getTitleFor('AddEventForm')->getLocalURL() . '">Add Event Form</a>.</p>');

        $events = SpecialAddEventFormHelper::readEventData();
        $eventOptions = $this->getEventOptions($events);

        $formDescriptor = [
            'event' => [
                'type' => 'select',
                'label-message' => 'Select Event to Delete',
                'options' => $eventOptions,
                'required' => true,
            ],
        ];

        $htmlForm = HTMLForm::factory('ooui', $formDescriptor, $this->getContext(), 'deleteeventform')
            ->setMethod('post')
            ->setAction($this->getPageTitle()->getLocalURL())
            ->setSubmitText('Delete')
            ->setWrapperLegend('Delete Event')
            ->setSubmitCallback([$this, 'onDelete'])
            ->show();
    }

    protected function checkPermissions() {
        if (!$this->getUser()->isAllowed('delete-event')) {
            throw new PermissionsError('delete-event');
        }
    }

    private function getEventOptions($events) {
        $options = [];
        foreach ($events as $section) {
            foreach ($section['events'] as $event) {
                $eventLabel = "{$event['date']} - {$event['title']}";
                $options[$eventLabel] = json_encode(['title' => $section['title'], 'date' => $event['date'], 'title' => $event['title']]);
            }
        }
        return $options;
    }

    public function onDelete($formData) {
        $event = json_decode($formData['event'], true);
        if (SpecialAddEventFormHelper::deleteEvent($event['title'], $event['date'])) {
            // Update the GlobalEvents page
            $this->updateGlobalEventsPage();
            return $this->getOutput()->addHTML('<p>Event deleted successfully!</p>');
        } else {
            return $this->getOutput()->addHTML('<p>Error: Could not delete event.</p>');
        }
    }

    private function updateGlobalEventsPage() {
        $titleObj = Title::newFromText('GlobalEvents');
        if (!$titleObj) {
            throw new Exception('Could not create title object.');
        }
        $page = MediaWiki\MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle($titleObj);
        if (!$page) {
            throw new Exception('Could not create WikiPage object.');
        }

        $events = SpecialAddEventFormHelper::readEventData();
        $contentText = SpecialAddEventFormHelper::displayEvents($events);

        $contentObj = ContentHandler::makeContent($contentText, $titleObj);

        // Use WikiPage::doUserEditContent to handle the edit
        $page->doUserEditContent(
            $contentObj,
            $this->getUser(),
            'Updated GlobalEvents page after deleting an event.'
        );
    }
}