diff --git a/2022/cli/export.php b/2022/cli/export.php new file mode 100644 index 0000000..e337228 --- /dev/null +++ b/2022/cli/export.php @@ -0,0 +1,37 @@ +#!/usr/bin/php +. + +/** + * This script import contents from Meta-wiki + */ + +require 'load.php'; + +// query this Conference +$conference = ( new QueryConference() ) + ->whereConferenceUID( THIS_CONFERENCE_UID ) + ->queryRow(); + +// no Conference no party +if( !$conference ) { + echo "missing conference\n"; + exit; +} + +// current Confererence ID +$conference_ID = $conference->getConferenceID(); diff --git a/2022/cli/import.php b/2022/cli/import.php new file mode 100755 index 0000000..0513a34 --- /dev/null +++ b/2022/cli/import.php @@ -0,0 +1,486 @@ +#!/usr/bin/php +. + +/** + * This script import contents from Meta-wiki + */ + +require 'load.php'; + +// query this Conference +$conference = ( new QueryConference() ) + ->whereConferenceUID( THIS_CONFERENCE_UID ) + ->queryRow(); + +// no Conference no party +if( !$conference ) { + echo "missing conference\n"; + exit; +} + +// current Confererence ID +$conference_ID = $conference->getConferenceID(); + +// room number to Room ID +$ROOMS = [ + '1' => 36, + '2' => 37, + '1 e 2' => 36, + 'caccia al tesoro' => 38, + 'ospiti' => 39, +]; + +// template arguments +TemplateArg::add( 'titolo' ); +TemplateArg::add( 'abstract' ); +TemplateArg::add( 'descrizione' ); +TemplateArg::add( 'note' ); +TemplateArg::add( 'chi' ); +TemplateArg::add( 'giorno' ); +TemplateArg::add( 'ora inizio' ); +TemplateArg::add( 'ora fine' ); +TemplateArg::add( 'moderatori' ); +TemplateArg::add( 'traccia', function ( $track ) use ( $ROOMS ) { + + $track = $ROOMS[ $track ] ?? null; + if( !$track ) { + error( "bad room $track" ); + } + + return $track; +} ); + +// wikimedia meta +$meta = \wm\MetaWiki::instance(); + +// months and related number +$MONTHS = [ + 'gennaio' => '01', + 'febbraio' => '02', + 'marzo' => '03', + 'aprile' => '04', + 'maggio' => '05', + 'giugno' => '06', + 'luglio' => '07', + 'agosto' => '08', + 'settembre' => '09', + 'ottobre' => '10', + 'novembre' => '11', + 'dicembre' => '12', +]; + +// API query category members +$queries = + $meta->createQuery( [ + // generator of category members + 'action' => 'query', + 'generator' => 'categorymembers', + 'gcmtitle' => 'Category:ItWikiCon 2020 - Programma', + + // for each page retrieved from the generator ask the revision + 'prop' => 'revisions', + 'rvslots' => 'main', + 'rvprop' => [ + 'ids', + 'timestamp', + + // we can ask the content and manually parse it with regexes, or just get the id and call API parse + // 'content', + ], + ] ); + +// for each API query request +foreach( $queries as $query ) { + + // for each page found during this request + foreach( $query->query->pages as $page ) { + + // page ID + $pageid = $page->pageid; + + // page title + $page_title = $page->title; + + // Meta-wiki page URL + $page_url = 'https://meta.wikimedia.org/wiki/' . urlencode( str_replace( ' ', '_', $page_title ) ); + + // revisions is an array of just one element + $revisions = $page->revisions ?? []; + foreach( $revisions as $revision ) { + + // revision ID + $revid = $revision->revid; + + // parse the templates + // https://www.mediawiki.org/w/api.php?action=help&modules=parse + $parse_request = + $meta->fetch( [ + 'action' => 'parse', + 'oldid' => $revid, + 'prop' => 'parsetree', + ] ); + + // DOM tree as XML + $tree = $parse_request->parse->parsetree->{'*'} ?? null; + if( $tree ) { + $reader = simplexml_load_string( $tree ); + + $template = $reader->template; + $template_title = trim( $template->title ); + if( $template_title === 'ItWikiCon/2020/Session' ) { + + $template_values = []; + + // template arguments + foreach( $template->part as $template_part ) { + + // argument title and its value ( | name = value ) + $part_name = trim( $template_part->name ); + $part_value = trim( $template_part->value ); + + // find the template value + $template_value = TemplateArg::createValue( $part_name, $part_value ); + if( $template_value ) { + $template_values[] = $template_value; + } + } + + // finally extract the template parameters + // $pageid + $event_title = TemplateArgValue::findAndGetValue( $template_values, 'titolo' ); + $event_who = TemplateArgValue::findAndGetValue( $template_values, 'chi' ); + $event_moderators = TemplateArgValue::findAndGetValue( $template_values, 'moderatori' ); + $abstract = TemplateArgValue::findAndGetValue( $template_values, 'abstract' ); + $note = TemplateArgValue::findAndGetValue( $template_values, 'note' ); + $giorno = TemplateArgValue::findAndGetValue( $template_values, 'giorno' ); + $ora_inizio = TemplateArgValue::findAndGetValue( $template_values, 'ora inizio' ); + $ora_fine = TemplateArgValue::findAndGetValue( $template_values, 'ora fine' ); + $event_description = TemplateArgValue::findAndGetValue( $template_values, 'descrizione' ); + $lingua = TemplateArgValue::findAndGetValue( $template_values, 'lingua', 'it' ); + $room_ID = TemplateArgValue::findAndGetValue( $template_values, 'traccia' ); + + // parse the wikitext + $event_description = from_wikitext_to_html( $meta, $event_description ); + $abstract = from_wikitext_to_html( $meta, $abstract ); + + // 24 ottobre + $giorno_dd_mm = explode( ' ', $giorno ); + if( count( $giorno_dd_mm ) !== 2 ) { + echo "Skip $giorno\n"; + continue; + } + + $giorno_dd_mm[1] = $MONTHS[ $giorno_dd_mm[1] ]; // ottobre -> 10 + $event_start = sprintf( + '%s-%s-%s %s:00', + date( 'Y' ), + $giorno_dd_mm[1], + $giorno_dd_mm[0], + $ora_inizio + ); + $event_end = sprintf( + '%s-%s-%s %s:00', + date( 'Y' ), + $giorno_dd_mm[1], + $giorno_dd_mm[0], + $ora_fine + ); + + // event UID + $event_uid = generate_slug( $event_title ); + + // option used to remember the Meta pageid -> Event ID + $option_name_meta_pageid = "event-from-meta-pageid-$pageid"; + + // basic data to be always updated + $basic_data = [ + 'event_title' => $event_title, + 'event_url' => $page_url, + 'event_uid' => $event_uid, + "event_description_$lingua" => $event_description, + "event_abstract_$lingua" => $abstract, + "event_note_$lingua" => $note, + 'event_language' => $lingua, + 'event_start' => $event_start, + 'event_end' => $event_end, + 'room_ID' => $room_ID, + ]; + + // start a MariaDB transaction + query( 'START TRANSACTION' ); + + // check if this page is new + $event_ID = get_option( $option_name_meta_pageid ); + if( $event_ID ) { + + // update + echo "Updating $event_title\n"; + + ( new QueryEvent() ) + ->whereEventID( $event_ID ) + ->update( $basic_data ); + + } else { + + echo "Inserting $event_title\n"; + + query( 'START TRANSACTION' ); + + // insert + ( new QueryEvent() ) + ->insertRow( array_merge( $basic_data, [ + 'conference_ID' => $conference_ID, + 'event_uid' => $event_uid, + ] ) ); + + $event_ID = last_inserted_ID(); + + set_option( $option_name_meta_pageid, $event_ID, false ); + + query( 'COMMIT' ); + } + + // wiki usernames + $wiki_usernames = [ + 'speaker' => suck_wiki_usernames( $event_who ), + 'moderator' => suck_wiki_usernames( $event_moderators ), + ]; + + foreach( $wiki_usernames as $role => $users ) { + foreach( $users as $wiki_username ) { + + // check if an user associated to this Meta-wiki nick exists + $user_talk = + ( new QueryUser() ) + ->whereMetaUsername( $wiki_username ) + ->queryRow(); + + if( $user_talk ) { + + $user_talk_ID = $user_talk->getUserID(); + + + } else { + + // eventually guess the user surname + $name_parts = explode( ' ', $wiki_username, 2 ); + $user_name = $name_parts[0] ?? $wiki_username; + $user_surname = $name_parts[1] ?? ''; + + // eventually strip "(ASD)" from surname + if( $user_surname ) { + $user_surname_parts = explode( ' (', 2 ); + $user_surname = $user_surname_parts[0] ?? $user_surname; + } + + // create the User + ( new QueryUser() ) + ->insertRow( [ + User::UID => generate_slug( $wiki_username ), + User::NAME => $user_name, + User::SURNAME => $user_surname, + User::META_WIKI => $wiki_username, + User::IS_PUBLIC => 1, + User::IS_ACTIVE => 0, + USER::ROLE => 'user', + ] ); + + echo "Creating $wiki_username\n"; + $user_talk_ID = last_inserted_ID(); + } + + // check if that User is related to this Event + $user_talk_relation = + ( new Query() ) + ->from( 'event_user' ) + ->whereInt( User::ID, $user_talk_ID ) + ->whereInt( Event::ID, $event_ID ) + ->queryRow(); + + // relate the User to this Event + if( !$user_talk_relation ) { + echo "Connecting $role to event...\n"; + insert_row( EventUser::T, [ + User::ID => $user_talk_ID, + Event::ID => $event_ID, + EventUser::ROLE => $role, + ] ); + } + } + } + + // commit the MariaDB transaction + query( 'COMMIT' ); + } + } + } + } +} + +/** + * A very dummy function to suck some usernames from the wikitext + */ +function suck_wiki_usernames( $wikitext ) { + + $users = []; + + // strange cases: + // [[:it:s:Utente:|OrbiliusMagister]] damn ORBILIUS! asd + $founds = preg_match_all( '/\[\[:?User:(.+?)([\|\]])/i', $wikitext, $matches ); + for( $i = 0; $i < $founds; $i++ ) { + $user = trim( $matches[1][$i] ); + $user = str_replace( '_', ' ', $user ); + + // AAAAAAAAAAAASD DAMN MARCO CHEMELLO + if( $user === 'Marco Chemello (WMIT)' ) { + $user = 'Marcok'; + } + + $users[] = $user; + } + + array_unique( $users ); + + return $users; +} + +function from_wikitext_to_html( $wiki, $wikitext ) { + + // no wikitext no party + if( !$wikitext ) { + return $wikitext; + } + + $request = + $wiki->fetch( [ + 'action' => 'parse', + 'contentmodel' => 'wikitext', + 'text' => $wikitext, + 'disablelimitreport' => 1, + ] ); + + $html = $request->parse->text->{'*'} ?? ''; + + $html = str_replace( 'href="/wiki/', 'https://meta.wikimedia.org/wiki/', $html ); + + return $html; +} + +class TemplateArg { + + private $name; + + private $callback; + + public static $args = []; + + public function __construct( $name, $callback ) { + $this->name = $name; + $this->callback = $callback; + } + + public function getName() { + return $this->name; + } + + public function normalizeValue( $value ) { + if( $this->callback ) { + $value = call_user_func( $this->callback, $value ); + } + return $value; + } + + /** + * Add a template argument in the known list of arguments + */ + public static function add( $name, $callback = null ) { + self::$args[] = new TemplateArg( $name, $callback ); + } + + public static function createValue( $name, $value ) { + + $arg = self::find( $name ); + if( $arg ) { + return new TemplateArgValue( $arg, $value ); + } + + return false; + } + + public static function find( $name ) { + + foreach( self::$args as $arg ) { + if( $arg->getName() === $name ) { + return $arg; + } + } + + return false; + } + +} + +class TemplateArgValue { + + private $arg; + + private $value; + + public function __construct( TemplateArg $arg, $value ) { + $this->arg = $arg; + $this->value = $value; + } + + public function getValue( $default_value = null ) { + $value = $this->value; + $value = $this->arg->normalizeValue( $value ); + if( !$value ) { + $value = $default_value; + } + return $value; + } + + public function getArg() { + return $this->arg; + } + + public function getName() { + return $this->getArg()->getName(); + } + + public static function find( $all, $name ) { + foreach( $all as $one ) { + if( $one->getName() === $name ) { + return $one; + } + } + return false; + } + + public static function findAndGetValue( $all, $name, $default_value = null ) { + + $value = $default_value; + + $one = self::find( $all, $name ); + if( $one ) { + $value = $one->getValue( $default_value ); + } + + return $value; + } +} diff --git a/2022/cli/load.php b/2022/cli/load.php new file mode 100644 index 0000000..a4ad86e --- /dev/null +++ b/2022/cli/load.php @@ -0,0 +1,5 @@ +. + +/** + * Require a certain page from the template directory + * + * @param $name string page name (to be sanitized) + * @param $args mixed arguments to be passed to the page scope + */ +function template_2022( $template_name, $template_args = [] ) { + extract( $template_args, EXTR_SKIP ); + return require ABSPATH . "/2022/template/$template_name.php"; +} + +/** + * Spawn a 2022 Event + * + * @param int $id Event ID + */ +function event_2022( $id ) { + + $event = ( new QueryEvent() ) + ->whereEventID( $id ) + ->select( Conference::fields() ) + ->select( Event::fields() ) + ->select( Room::fields() ) + ->select( Track::fields() ) + ->select( Room::fields() ) + ->select( Chapter::fields() ) + ->selectEventHasVideo() + ->selectEventHasDocument() + ->joinConference() + ->joinTrack( 'LEFT' ) + ->joinRoom( 'LEFT' ) + ->joinChapter( 'LEFT' ) + ->queryRow(); + + template_2022( 'event-brief', [ + 'event' => $event, + ] ); +} + +/** + * Print an icon for the 2022 website + * + * See https://materializecss.com/icons.html + * + * @param string $name Icon name + * @param string $classes CSS classes + * @return string + */ +function icon_2022( $name, $classes = null ) { + $classes = $classes ? " $classes" : ''; + return sprintf( + '%s', + $classes, + $name + ); +} + +/** + * Query some Event Users + * + * @param Event $event + * @param string $role Choose 'speaker' or 'moderator' + * @return Generator + */ +function event_users_2022( $event, $role ) { + + $users = + ( new QueryEventUser() ) + ->joinUser() + ->whereEvent( $event ) + ->whereEventUserRole( $role ) + ->orderByEventUserOrder() + ->queryGenerator(); + + return $users; +} + +/** + * Link to an User of the itWikiCon 2022 + * + * @param User $user + * @return string HTML firm + */ +function user_link_2022( $user ) { + + $name = esc_html( $user->getUserDisplayName() ); + + // print the Meta-wiki permalink + if( $user->has( User::META_WIKI ) ) { + $name = HTML::a( $user->getUserMetaWikiURL(), $name ); + } + + return $name; +} + +/** + * Print the URL to a Sharable + * + * @param Event $event + * @param Sharable $sharable + * @param string $text + * @return string HTML + */ +function sharable_edit( $event, $sharable = null, $text = null ) { + + $s = ''; + + // no editable, no party + if( $event->isEventEditable() ) { + + // displayed text + if( !$text ) { + $text = $sharable ? __( "modifica" ) : __( "aggiungi" ); + } + + // edit or creation URL + $url = $sharable + ? $sharable->getSharableEditURL() + : Sharable::editURL( [ + 'event_ID' => $event->getEventID(), + ] ); + + // create link + $s = HTML::a( $url, esc_html( $text ) ); + } + + // [edit] + if( $s ) { + $s = "[$s]"; + } + + return $s; +} diff --git a/2022/load.php b/2022/load.php new file mode 100644 index 0000000..7ff45c1 --- /dev/null +++ b/2022/load.php @@ -0,0 +1,41 @@ +. + +/* + * This file is called after before your unversioned 'load.php' and + * load some default configurations. + * + * You can override most of them from your 'load.php'. Example: + * + * define( 'SOMETHING', 'value' ); + */ + +// define latest conference (may be different to the one of the current site) +define( 'THIS_CONFERENCE_UID', 'itwikicon-2022' ); + +// room permalink for this conference +define( 'ROOM_PERMALINK', 'room.php/%2$s' ); + +// do not put ?l=it in URLs +define( 'NO_LANGUAGE_IN_URLS', true ); + +// require the upstream generic configuration file shared for all the conferences +require __DIR__ . '/../load.php'; + +// require some custom 2020 stuff +require __DIR__ . '/include/functions.php'; diff --git a/2022/template/alert.php b/2022/template/alert.php new file mode 100644 index 0000000..7408add --- /dev/null +++ b/2022/template/alert.php @@ -0,0 +1,21 @@ +. + +?> + + diff --git a/2022/template/day-one.php b/2022/template/day-one.php new file mode 100644 index 0000000..0928bd6 --- /dev/null +++ b/2022/template/day-one.php @@ -0,0 +1,23 @@ +. + +/** + * Events of the day 1 of the itWikiCon 2022 + */ +?> + + diff --git a/2022/template/day-three.php b/2022/template/day-three.php new file mode 100644 index 0000000..f4156d7 --- /dev/null +++ b/2022/template/day-three.php @@ -0,0 +1,23 @@ +. + +/** + * Events of the day 3 of the itWikiCon 2022 + */ +?> + + diff --git a/2022/template/day-two.php b/2022/template/day-two.php new file mode 100644 index 0000000..8b1076e --- /dev/null +++ b/2022/template/day-two.php @@ -0,0 +1,23 @@ +. + +/** + * Events of the day 2 of the itWikiCon 2022 + */ +?> + + diff --git a/2022/template/event-brief.php b/2022/template/event-brief.php new file mode 100644 index 0000000..fe432ba --- /dev/null +++ b/2022/template/event-brief.php @@ -0,0 +1,133 @@ +. + +/** + * Template used to display a single event briefly + * + * Variables that should be available: + * + * $event Event: current Event + * Note that the Event should have these additional attributes: + * event_has_video + * event_has_document + */ + +// query all the Users maintaining this Event +$users = + ( new QueryEventUser() ) + ->joinUser() + ->whereEvent( $event ) + ->whereEventUserIsSpeaker() + ->orderByEventUserOrder() + ->queryGenerator(); +?> + +
+ isEventAborted() ): ?>
+ = esc_html( $event->getEventTitle() ) ?>
+ = __( "sessione annullata" ) ?>
+
+ = esc_html( $event->getEventTitle() ) ?>
+
+
+
= HTML::a( + $event->getRoomURL(), + $event->getRoomName(). + 'play_arrow', + null, + 'btn white blue-text waves-effect' + ) ?> +
++ + = icon_2022( 'play_arrow', 'left' ) ?> + = __( "Rivedi" ) ?> + +
+ + + + + get( 'event_has_document' ) ): ?> ++ + = icon_2022( 'attachment', 'left' ) ?> + = __( "Materiali" ) ?> + +
+ + + + isEventEditable() ): ?> + = HTML::a( + $event->getEventEditURL(), + '[edit]' + ) ?> + + +