' subpage
+ public static function mgw_display( $parser, $value = '', $field = '' ) {
+
+ $fields = [ 'level', 'duration' ];
+
+ if ( $value === '' ) {
+ $output = HtmlF::parseError('{{#mgw-display:[value]|[field]}} : [value] absent.' );
+ return [ $output, 'noparse' => true, 'isHTML' => true ];
+ }
+
+ if ( !in_array( $field, $fields ) ) {
+ $output = HtmlF::parseError('{{#mgw-display:[value]|[field]}} : Mauvais argument '.
+ '[field] => "'. implode('" | "', $fields) .'"' );
+ return [ $output, 'noparse' => true, 'isHTML' => true ];
+ }
+
+ switch ( $field ) {
+ case 'level':
+ global $wgMgwLevels;
+ return $wgMgwLevels[ (int)$value ];
+ break;
+ case 'duration':
+ return wfMgwDuration( (int)$value );
+ break;
+ }
+ }
+}
diff --git a/includes/SpecialAccountRequest.php b/includes/SpecialAccountRequest.php
new file mode 100644
index 0000000..226a315
--- /dev/null
+++ b/includes/SpecialAccountRequest.php
@@ -0,0 +1,141 @@
+getRequest()->getPostValues(); // récupération des variables POST si elles existent
+ $this->isPosted = ( sizeof($postData) > 0 );
+ $this->JsonForm = new JsonToForm(
+ 'specialaccountrequest',
+ $postData
+ );
+
+ # récupération des cookies
+ self::getCookies();
+ }
+
+ /**
+ * Shows the page to the user.
+ * @param string $sub The subpage string argument (if any).
+ */
+ public function execute( $sub ) {
+
+ $messHTML = "";
+ $done = false;
+
+ /* Traitement de la réponse au captcha */
+ if ( $this->JsonForm->isCaptchaPosted() )
+ {
+ # demande déjà finalisée
+ if ( $this->cookieData['done'] )
+ {
+ $messHTML = $this->messageHTML( 'done', Msg::get('specialaccountrequest-mess-alreadydone') );
+ $done = true;
+ }
+ # envoi du mail si réponse valide
+ elseif ( $this->JsonForm->isCaptchaValid() )
+ {
+ $this->JsonForm->sendEmail();
+ //Todo: use WebResponse::setCookie() when upgrade Mediawiki > 1.35
+ setcookie( $this->cookieData['label'], "Account request sent.", time()+3600 );
+ $messHTML = $this->messageHTML( 'done', Msg::get('specialaccountrequest-mess-confirm') );
+ $done = true;
+ }
+
+ # captcha invalide, nombre d'essais dépassés
+ elseif ( $this->cookieData['attempt'] > 4 ){
+ $messHTML = $this->messageHTML( 'done', Msg::get('specialaccountrequest-mess-attemptslimit') );
+ $done = true;
+ }
+
+ # captcha invalide, nouvel essai
+ else
+ {
+ $this->cookieData['attempt'] += 1;
+ $messHTML = $this->messageHTML( 'captchaError', Msg::get('specialaccountrequest-mess-captchaerror') );
+ setcookie('mgw_accountRequestAttempt', $this->cookieData['attempt'], time()+60*60);
+ }
+ }
+
+ /* Construction de la page */
+ $out = $this->getOutput();
+ $out->addModules('ext.mgwiki-jsonform');
+ $out->addModules('ext.mgwiki-specialaccountrequest');
+ $out->setPageTitle( Msg::get('specialaccountrequest-title') );
+
+ if (!$done){
+ $form = $this->JsonForm->makeForm( $messHTML );
+ foreach ( $form as $outKey => $outValue ) {
+ switch ( $outValue['type'] ) {
+ case 'html':
+ $out->addHTML( $outValue['content'] );
+ break;
+ case 'wiki':
+ $out->addWikiTextAsContent( $outValue['content'] );
+ break;
+ }
+ }
+ }
+ else {
+ $out->addHTML($messHTML);
+ }
+ }
+
+ protected function messageHTML($option, $mess)
+ {
+ switch ($option) {
+
+ case 'done':
+ return '' . $mess . '
+ ';
+ break;
+
+ case 'captchaError':
+ return '
' . $mess . '';
+ break;
+
+ default:
+ throw new \Exception("Error: use of SpecialAccountRequest::messageHTML with invalid option argument", 1);
+ }
+ }
+
+ /**
+ * récupération des cookies s'ils existent
+ */
+ private function getCookies ()
+ {
+ global $_COOKIE; // NB: l'appel de $this->getRequest()->getCookie() provoque une erreur: Warning: require(): Could not call the sapi_header_callback in /var/www/html/wiki/extensions/MGWikiDev/includes/SpecialAccountRequest.php
+ if ( $this->isPosted ){
+ $this->cookieData['label'] = str_replace(array('=', ',' ,';' ,'\t' ,'\r' ,'\n' ,'\013' ,'\014'), '', "mgw_" . $this->JsonForm->getHash() );
+ $this->cookieData['done'] = isset($_COOKIE[$this->cookieData['label']]);
+ $this->cookieData['attempt'] = isset($_COOKIE['mgw_accountRequestAttempt']) ? $_COOKIE['mgw_accountRequestAttempt'] : 0;
+ if ( isset( $this->cookieData[ 'attempt'] ) && !is_int( $this->cookieData[ 'attempt' ] ) ) { $this->cookieData[ 'attempt' ] = 5; }
+ }
+ }
+
+ /**
+ * groupe auquel se rapporte cette page spéciale
+ */
+ protected function getGroupName() {
+ return 'mgwiki';
+ }
+}
diff --git a/includes/SpecialAdminGroupTypes.php b/includes/SpecialAdminGroupTypes.php
new file mode 100644
index 0000000..39a5481
--- /dev/null
+++ b/includes/SpecialAdminGroupTypes.php
@@ -0,0 +1,804 @@
+headInfos['triOptions'] = ['nom', 'admin_level', 'user_level', 'default_duration', 'update_time', 'updater_id'];
+ $this->headInfos['filtreOptions'] = [ 'archive' ];
+ $this->headInfos['filtreOptionsTitles'] = [];
+ $this->headInfos['headActions'] = [ 'refresh' => 'rafraîchir' ];
+ $this->headInfos['headHiddenFields'] = [];
+ }
+
+ public function execute( $sub ) {
+
+ // récupération et contrôle des entrées
+ $reqData = $this->set_reqData();
+ HtmlF::set_select( $reqData, $this->headInfos['triOptions'], $this->tri, $this->select );
+ HtmlF::set_check( $reqData, $this->headInfos['filtreOptions'], $this->check );
+
+ // préparation de l'affichage
+ $this->setHeaders();
+ $out = $this->getOutput();
+ $out->addModules('ext.mgwiki-specialadmin');
+ $out->addModules('ext.mgwiki-specialadmingrouptypes');
+ $out->enableOOUI();
+
+ // sélection du mode d'affichage
+ $edit = ( $reqData['action'] == 'edit' );
+ $history = ( $reqData['action'] == 'history' && !is_null( $reqData['id'] ));
+ $show = ( ! $edit && ! $history );
+
+ // SUBMIT
+ if ( isset( $reqData['submit'] ) ) {
+ $status = $this->formCallback( $reqData, $edit, $show );
+ $done = ( $status->done() ) ? 'true' : 'false';
+ $out->addHTML( HtmlF::alertMessage( $status ) );
+ }
+
+ // ACTIONS
+ # undo
+ if ( $reqData['action'] == 'undo' && isset( $reqData['id'] ) && isset( $reqData['archive'] ) ) {
+ $undo = $this->undo( $reqData['id'], $reqData['archive'] );
+ $out->addHTML( HtmlF::alertMessage( $undo ) );
+ $history = true;
+ $show = false;
+ }
+
+ # delete
+ if ( $reqData['action'] == 'delete' && isset( $reqData['id'] ) ) {
+ $delete = $this->delete( $reqData['id'], 'Suppression de type de groupe.' );
+ $out->addHTML( HtmlF::alertMessage( $delete ) );
+ $history = false;
+ $show = true;
+ }
+
+ # undelete
+ if ( $reqData['action'] == 'undelete' && isset( $reqData['archive'] ) ) {
+ $undelete = $this->undelete( $reqData['archive'] );
+ $out->addHTML( HtmlF::alertMessage( $undelete ) );
+ $history = true;
+ $show = false;
+ }
+
+ // VUES
+ # history
+ if ( $history ) {
+
+ $show_array = $this->getHistoryShowArray( $reqData['id'] );
+
+ $out->setPageTitle( Msg::get('specialadmingrouptypes-title-history', [ $show_array[0]['nom'] ] ) );
+ $out->addHTML( $this->makeView( $show_array ) );
+ $out->addHTML( '
' .
+ (string)new \OOUI\ButtonWidget( [ 'href' => $this->selfURL(), 'label' => 'retour' ] )
+ );
+ }
+
+ # show
+ if ( $show ) {
+
+ $show_array = $this->getShowArray( '', [ 'ORDER BY' => 'nom' ] );
+
+ if ( count( $show_array ) > 0 ) {
+ HtmlF::sort_show_array( $show_array, $this->tri );
+ }
+
+ $out->setPageTitle( Msg::get( 'specialadmingrouptypes-title' ) );
+ $out->addHTML( HtmlF::makeHeadForm( $this->selfURL(), $this->select, $this->check, $this->tri, $this->headInfos, 'specialadmingrouptypes' ) );
+ $out->addHTML( $this->makeView( $show_array ) );
+ $out->addHTML( '
' .
+ (string)new \OOUI\ButtonWidget( [
+ 'href' => $this->selfURL( [ 'action' => 'edit' ] ),
+ 'label' => 'nouveau',
+ 'flags' => [
+ 'primary',
+ 'progressive'
+ ]
+ ] )
+ );
+ }
+
+ # edit
+ if ( $edit ) {
+ $this->setData( $reqData );
+ if ( !empty( $this->nom ) ) {
+ $out->setPageTitle( Msg::get('specialadmingrouptypes-title-edit', [ $this->nom ] ) );
+ }
+ else {
+ $out->setPageTitle( Msg::get('specialadmingrouptypes-title-edit-new' ) );
+ }
+ $out->addHTML( $this->makeOouiForm( $reqData ) );
+ }
+ }
+
+ private function set_reqData() {
+ $reqData = $this->getRequest()->getValues();
+ PhpF::empty( $reqData['action'] );
+ PhpF::empty( $reqData['id'] );
+ PhpF::int( $reqData['id'] );
+ PhpF::empty( $reqData['reverse-a'] );
+ PhpF::empty( $reqData['reverse-b'] );
+ return $reqData;
+ }
+
+
+ /**
+ * @param int $id
+ * @param int $archive_id
+ */
+ private function undo( $id, $archive_id ) {
+
+ global $wgUser;
+
+ $old = DbF::select_clean(
+ 'archive_groupe_type',
+ [ 'id', 'nom', 'page_id', 'admin_level', 'user_level', 'default_duration' ],
+ 'archive_id = ' . $archive_id
+ )[0];
+
+ $actual = DbF::select_clean(
+ 'groupe_type',
+ [ 'id', 'nom', 'page_id' ],
+ 'id = ' . $id
+ )[0];
+
+ $currentNames = DbF::select_clean(
+ 'groupe_type',
+ [ 'id', 'nom' ]
+ );
+
+ // on interdit la création de doublon
+ foreach ( $currentNames as $row ) {
+ if ( $row['id'] != $old['id'] &&
+ strtolower( $row['nom'] ) == strtolower( $old['nom'] ) )
+ {
+ return Status::newFailed( 'Le nom '.$old['nom'] .
+ ' correspond à un autre type de groupe en cours d\'utilisation.'
+ .' Veuillez rétablir les modifications manuellement avec un autre nom.' );
+ }
+ }
+
+ // on vérifie l'existence de la page
+ $page_id = PageF::pageID( 'MGWiki:Types de groupes/' . $actual['nom'] );
+ if ( $page_id < 1 ) {
+ $actualPage = PageF::newPage(
+ 'Types de groupes/' . $actual['nom'],
+ NS_PROJECT,
+ '{{Modèle:Types de groupes}}',
+ Msg::get( 'specialadmingrouptypes-newpage-summary' ),
+ $wgUser
+ );
+ if( !$actualPage->done() ) {
+ return Status::newFailed( 'Action annulée: la page MGWiki:Types de groupes/' .
+ $actual['nom'] . ' n\'a pas pu être créée. (' . $actualPage->mess() . ')' );
+ }
+ $old['page_id'] = $actualPage->extra();
+ }
+ elseif ( $old['page_id'] != $page_id ) {
+ $old['page_id'] != $page_id;
+ }
+
+ // rename si besoin
+ if ( $old['nom'] != $actual['nom'] ) {
+ $rename = PageF::renamePage(
+ 'Types de groupes/' . $actual['nom'],
+ NS_PROJECT,
+ 'Types de groupes/' . $old['nom'],
+ NS_PROJECT,
+ 'Ancienne version restaurée.',
+ $wgUser
+ );
+ if( !$rename->done() ) {
+ return Status::newFailed( 'Action annulée: la page MGWiki:Types de groupes/' .
+ $actual['nom'] . ' n\'a pas pu être renomée.' );
+ }
+ $old['page_id'] = $rename->extra();
+ }
+
+ // màj des tables
+ $update = DbF::update( 'groupe_type', [ 'id' => $id ], $old, $wgUser->getId() );
+ if ( ! $update->done() ) {
+ return $update;
+ }
+
+ // refreshPage
+ $refresh = PageF::refreshPage(
+ $old['page_id'],
+ Msg::get( 'specialadmingrouptypes-update-summary' ),
+ $wgUser
+ );
+ if ( ! $refresh->done() ) {
+ return Status::newFailed( 'Les modifications ont été enregistrées mais la page MGWiki:Types de groupes/'
+ . $old['nom'] . ' n\'a pas pu être réactualisée.' );
+ }
+
+ // succès
+ return Status::newDone( 'L\'ancienne version de '.$old['nom'].' a été rétablie.');
+ }
+
+ /**
+ * @param int $id mgw_groupe_type_id
+ * @param string $summary
+ */
+ private function delete( $id, $summary ) {
+ global $wgUser;
+ // suppression dans les tables
+ $dbDelete = DbF::delete( 'groupe_type', [ 'id' => $id ], $wgUser->getId() );
+ if ( !$dbDelete->done() ){
+ return $dbDelete;
+ }
+ // suppression de la page
+ $pageDelete = PageF::lightDelete( $dbDelete->extra()['groupe_type_page_id'], $summary );
+ if ( ! $pageDelete->done() ) {
+ return Status::newFailed( '' . $dbDelete->extra()['groupe_type_nom'] . ' a été supprimé mais'.
+ ' la page "MGWiki:Types_de_groupes/' . $dbDelete->extra()['groupe_type_nom'] .
+ '" existe toujours ( ' . $dbDelete->mess() . ' )' );
+ }
+ // succès
+ return Status::newDone( ''.$dbDelete->extra()['groupe_type_nom'].' a été supprimé.');
+ }
+
+
+ /**
+ * @param int $archive_id
+ */
+ private function undelete( $archive_id ) {
+ global $wgUser;
+
+ // requêtes
+ $old = DbF::select_clean(
+ 'archive_groupe_type',
+ [ 'id', 'nom', 'page_id', 'admin_level', 'user_level', 'default_duration' ],
+ 'archive_id = ' . $archive_id
+ )[0];
+
+ $names = DbF::select_clean(
+ 'groupe_type',
+ [ 'id', 'nom' ]
+ );
+
+ // on vérifie l'absence de doublons
+ foreach ( $names as $row ) {
+ if ( strtolower( $row['nom'] ) == strtolower( $old['nom'] ) ) {
+ return Status::newFailed( 'Le nom '.$old['nom'] .
+ ' correspond à un autre type de groupe en cours d\'utilisation.'
+ .' Veuillez rétablir le groupe supprimé manuellement avec un autre nom.' );
+ }
+ }
+
+ // on re-crée la page
+ if ( PageF::pageArchiveExists( 'MGWiki:Types de groupes/' . $old['nom'] ) ) {
+ $undelete = PageF::undeletePage(
+ 'MGWiki:Types de groupes/' . $old['nom'],
+ 'Restauration du type de groupe.',
+ $wgUser
+ );
+ if( !$undelete->done() ) {
+ return Status::newFailed( 'Action annulée: la page MGWiki:Types de groupes/' .
+ $old['nom'] . ' n\'a pas pu être restaurée.' );
+ }
+ $old['page_id'] = $undelete->extra();
+ }
+ else {
+ $newPage = PageF::newPage(
+ 'Types de groupes/' . $old['nom'],
+ NS_PROJECT,
+ '{{Modèle:Types de groupes}}',
+ Msg::get( 'specialadmingrouptypes-newpage-summary' ),
+ $wgUser
+ );
+ if( !$newPage->done() ) {
+ return Status::newFailed( 'Action annulée: la page MGWiki:Types de groupes/' .
+ $old['nom'] . ' n\'a pas pu être créée. (' . $newPage->mess() . ')' );
+ }
+ $old['page_id'] = $newPage->extra();
+ }
+
+ // on rétablit l'ancienne version
+ $status = DbF::insert( 'groupe_type', $old, $wgUser->getId() );
+ if ( ! $status->done() ) {
+ return $status;
+ }
+
+ $refresh = PageF::refreshPage(
+ $old['page_id'],
+ Msg::get( 'specialadmingrouptypes-update-summary' ),
+ $wgUser
+ );
+ if ( ! $refresh->done() ) {
+ return Status::newFailed( 'Les modifications ont été enregistrées mais la page MGWiki:Types de groupes/'
+ . $old['nom'] . ' n\'a pas pu être réactualisée.' );
+ }
+
+ // succès
+ return Status::newDone( $old['nom'].' a été rétabli.');
+ }
+
+
+ private function getShowArray( $select = '', $opts = [] ) {
+
+ $groupTypes = [];
+ if ( $this->check['archive']['only'] != 'checked' ) {
+ $groupTypes = DbF::select_clean(
+ 'groupe_type',
+ [ 'id', 'nom', 'page_id', 'admin_level', 'user_level', 'default_duration', 'update_time', 'updater_id' ],
+ $select,
+ $opts
+ );
+ }
+
+ if ( $this->check['archive']['view'] == 'checked' || $this->check['archive']['only'] == 'checked' ) {
+ if ( !empty( $select ) ) {
+ $select = implode( ' AND ', [ $select, 'drop_time IS NOT NULL'] );
+ }
+ else {
+ $select = 'drop_time IS NOT NULL';
+ }
+ $droped = DbF::select_clean(
+ 'archive_groupe_type',
+ [ 'archive_id', 'id', 'nom', 'page_id', 'admin_level', 'user_level', 'default_duration', 'update_time', 'updater_id', 'drop_time', 'drop_updater_id' ],
+ $select,
+ $opts
+ );
+ $groupTypes = array_merge( $groupTypes, $droped );
+ }
+
+ foreach ( $groupTypes as $key => $row ) {
+ PhpF::null( $groupTypes[$key]['drop_time'] );
+ PhpF::null( $groupTypes[$key]['archive_id'] );
+ $groupTypes[$key]['droped'] = !is_null( $groupTypes[$key]['drop_time'] );
+ $groupTypes[$key]['undroped'] = false;
+ if ( $groupTypes[$key]['droped'] ) {
+ // on le retire s'il a été rétabli depuis
+ $screen = DbF::select_clean(
+ 'groupe_type',
+ [ 'id', 'update_time' ],
+ 'id = ' . $groupTypes[$key]['id'] . ' AND groupe_type_update_time > ' . $groupTypes[$key]['update_time']
+ );
+ if ( !is_null( $screen ) ) {
+ unset( $groupTypes[$key] );
+ }
+ // on n'affiche que la dernière suppression si plusieurs
+ if ( isset( $groupTypes[$key] ) ) {
+ $screen = DbF::select_clean(
+ 'archive_groupe_type',
+ [ 'id', 'drop_time' ],
+ 'id = ' . $groupTypes[$key]['id'] . ' AND groupe_type_drop_time > ' . $groupTypes[$key]['drop_time']
+ );
+ if ( !is_null( $screen ) ) {
+ unset( $groupTypes[$key] );
+ }
+ }
+ }
+ }
+ return $groupTypes;
+ }
+
+ private function getHistoryShowArray( $id ) {
+
+ $actu = DbF::select_clean(
+ 'groupe_type',
+ [ 'id', 'nom', 'page_id', 'admin_level', 'user_level', 'default_duration', 'update_time', 'updater_id' ],
+ 'id = ' . $id
+ );
+
+ $past = DbF::select_clean(
+ 'archive_groupe_type',
+ [ 'archive_id', 'id', 'nom', 'page_id', 'admin_level', 'user_level', 'default_duration',
+ 'update_time', 'updater_id', 'drop_time', 'drop_updater_id' ],
+ 'id = ' . $id,
+ [ 'ORDER BY' => 'update_time DESC' ]
+ );
+
+ if ( is_null( $actu ) ) {
+ $actu = [];
+ }
+ if ( is_null( $past ) ) {
+ $past = [];
+ }
+
+ $return = array_merge( $actu, $past );
+
+ foreach ( $return as $key => $row ) {
+ PhpF::null( $return[$key]['drop_time'] );
+ PhpF::null( $return[$key]['archive_id'] );
+ $return[$key]['droped'] = !is_null( $return[$key]['drop_time'] );
+ $return[$key]['undroped'] = isset( $return[$key - 1] );
+ }
+
+ return $return;
+ }
+
+ private function makeView( $show_array ) {
+ if ( $show_array == [] ) {
+ return HtmlF::tagShowArray('
Aucun type de groupe ne correspond à la sélection');
+ }
+ global $wgMgwLevels;
+
+ # préparation des variables
+ foreach ( $show_array as $row ) {
+
+ $duration = wfMgwDuration( $row['default_duration'] );
+
+ if ( $row['droped'] && !$row['undroped'] ) {
+ $buttons = HtmlF::onclickButton(
+ 'rétablir',
+ $this->selfURL( [ 'action' => 'undelete', 'id' => $row['id'], 'archive' => $row['archive_id'] ] )
+ ) . 'historique' ;
+ }
+ elseif ( !is_null( $row['archive_id'] ) ) {
+ $buttons = HtmlF::onclickButton(
+ 'rétablir',
+ $this->selfURL( [ 'action'=>'undo', 'id'=>$row['id'], 'archive'=>$row['archive_id'] ] )
+ );
+ }
+ else {
+ $buttons = HtmlF::onclickButton(
+ 'modifier',
+ $this->selfURL( [ 'action'=>'edit', 'id'=>$row['id'] ] )
+ ) . '
+ historique
+ supprimer' ;
+ }
+
+ if ( $row['droped'] ) {
+ $class = 'deleted';
+ }
+ elseif ( !is_null( $row['archive_id'] ) ) {
+ $class = 'inactive';
+ }
+ else {
+ $class = 'active';
+ }
+
+ if ( $row['droped'] ) {
+ $update_time = wfTimestamp( TS_DB, $row['drop_time'] );
+ $updater = \User::newFromId ( $row['drop_updater_id'] );
+ $updater_link = '' . $updater->getName() . '';
+ $maj = 'Supprimé le ' . $update_time . ' par ' . $updater_link;
+ }
+ else {
+ $update_time = wfTimestamp( TS_DB, $row['update_time'] );
+ $updater = \User::newFromId ( $row['updater_id'] );
+ $updater_link = '' . $updater->getName() . '';
+ $maj = 'Màj le ' . $update_time . ' par ' . $updater_link ;
+ }
+
+ #intégration
+ $rowTable = '
+
+ | ' . HtmlF::linkPageId( $row['nom'], $row['page_id'] ) . ' |
+ |
+ ' . $buttons . ' |
+
+
+ | Admin : ' . $wgMgwLevels[ $row['admin_level'] ] . ' |
+ User : ' . $wgMgwLevels[ $row['user_level'] ] . ' |
+ Durée : ' . $duration . ' |
+
+
+ |
+ ' . $maj . ' |
+
';
+
+ $print[] = HtmlF::tagRowTable( $rowTable, $class );
+ }
+ $print = HtmlF::tagShowArray( implode( '', $print ) );
+ return $print;
+ }
+
+ ///////////////
+ // FORMULAIRE
+
+ private function setData( $reqData ){
+ // valeurs par défaut
+ if ( isset( $reqData['submit'] ) ) {
+ $this->id = $reqData['id'];
+ $this->nom = $reqData['nom'];
+ $this->admin_level = $reqData['admin_level'];
+ $this->user_level = $reqData['user_level'];
+ $this->duration = $reqData['duration'];
+ }
+ elseif ( !empty( $reqData['id'] ) ) {
+ $row = $this->getShowArray(
+ 'id = ' . $reqData['id'],
+ [ 'ORDER BY' => 'nom' ]
+ )[0];
+
+ $this->id = $row['id'];
+ $this->nom = $row['nom'];
+ $this->admin_level = $row['admin_level'];
+ $this->user_level = $row['user_level'];
+ $this->duration = $row['default_duration'];
+ }
+ else {
+ $this->id = '';
+ $this->nom = '';
+ $this->admin_level = 2;
+ $this->user_level = 1;
+ $this->duration = 0;
+ }
+ }
+
+ private function makeOouiForm( $reqData ) {
+ $html = new \OOUI\FormLayout( [
+ 'method' => 'POST',
+ 'action' => $this->selfURL(),
+ 'items' => [
+ new \OOUI\FieldsetLayout( [
+ 'items' => [
+ new \OOUI\FieldLayout(
+ new \OOUI\HiddenInputWidget( [
+ 'name' => 'id',
+ 'value' => $this->id
+ ] )
+ ),
+ new \OOUI\FieldLayout(
+ new \OOUI\TextInputWidget( [
+ 'name' => 'nom',
+ 'type' => 'text',
+ 'value' => $this->nom,
+ 'required' => true,
+ 'autofocus' => true
+ ] ),
+ [
+ 'label' => 'Nom du type de groupe',
+ 'align' => 'top',
+ ]
+ ),
+ new \OOUI\FieldLayout(
+ new \OOUI\DropdownInputWidget( [
+ 'name' => 'admin_level',
+ 'value' => $this->admin_level,
+ 'options' => [
+ [ 'data' => 1, 'label' => 'U1' ],
+ [ 'data' => 2, 'label' => 'U2' ],
+ [ 'data' => 3, 'label' => 'U3' ],
+ [ 'data' => 4, 'label' => 'sysop' ],
+ ]
+ ] ),
+ [
+ 'label' => 'Niveau des responsables',
+ 'align' => 'top',
+ ]
+ ),
+ new \OOUI\FieldLayout(
+ new \OOUI\DropdownInputWidget( [
+ 'name' => 'user_level',
+ 'value' => $this->user_level,
+ 'options' => [
+ [ 'data' => 1, 'label' => 'U1' ],
+ [ 'data' => 2, 'label' => 'U2' ],
+ [ 'data' => 3, 'label' => 'U3' ],
+ [ 'data' => 4, 'label' => 'sysop' ],
+ ]
+ ] ),
+ [
+ 'label' => 'Niveau des utilisateurs',
+ 'align' => 'top',
+ ]
+ ),
+ new \OOUI\FieldLayout(
+ new \OOUI\DropdownInputWidget( [
+ 'name' => 'duration',
+ 'value' => $this->duration,
+ 'options' => [
+ [ 'data' => 0, 'label' => 'indéfini' ],
+ [ 'data' => 1, 'label' => '6 mois' ],
+ [ 'data' => 2, 'label' => '1 an' ],
+ [ 'data' => 4, 'label' => '2 ans' ],
+ [ 'data' => 6, 'label' => '3 ans' ],
+ [ 'data' => 8, 'label' => '4 ans' ],
+ [ 'data' => 10, 'label' => '5 ans' ]
+ ]
+ ] ),
+ [
+ 'label' => 'Durée par défaut',
+ 'align' => 'top',
+ ]
+ ),
+ new \OOUI\FieldLayout(
+ new \OOUI\ButtonInputWidget( [
+ 'name' => 'submit',
+ 'label' => 'Valider',
+ 'type' => 'submit',
+ 'flags' => [
+ 'primary',
+ 'progressive'
+ ]
+ ] ),
+ [
+ 'label' => null,
+ 'align' => 'top',
+ ]
+ )
+ ] // items [...]
+ ] ) // FieldsetLayout( [...] )
+ ] // items [...]
+ ] ); // FormLayout( [...] )
+ $html .= '
' . (string)new \OOUI\ButtonWidget( [
+ 'href' => $this->selfURL(),
+ 'label' => 'annuler'
+ ] );
+ return $html;
+ }
+
+ public static function formCallback( &$reqData, &$edit, &$show ) {
+ global $wgUser;
+ PhpF::html( $reqData['nom'] );
+ PhpF::int( $reqData['admin_level'] );
+ PhpF::int( $reqData['user_level'] );
+ PhpF::int( $reqData['duration'] );
+
+ // on interdit les doublons sur le champs 'nom'
+ $check = DbF::select_clean( 'groupe_type', [ 'id', 'nom' ] );
+ foreach ( $check as $row ) {
+ if ( strtolower( $row['nom'] ) == strtolower( $reqData['nom'] )
+ && $row['id'] != $reqData['id'] ) {
+ $edit = true; // on ré-affiche le formulaire
+ $show = false;
+ return Status::newFailed( 'Le nom demandé existe déjà. Vous pouvez:
+ ' );
+ }
+ }
+
+ // préparation de la reqête
+ if ( is_null( $reqData['id'] ) ) {
+ $select = [ 'nom' => ucfirst( $reqData['nom'] ) ];
+ $data = [
+ 'admin_level' => $reqData['admin_level'],
+ 'user_level' => $reqData['user_level'],
+ 'default_duration' => $reqData['duration']
+ ];
+ $new = true;
+ }
+ else {
+ $select = [ 'id' => $reqData['id'] ];
+ $data = [
+ 'nom' => ucfirst( $reqData['nom'] ),
+ 'admin_level' => $reqData['admin_level'],
+ 'user_level' => $reqData['user_level'],
+ 'default_duration' => $reqData['duration']
+ ];
+ $new = false;
+ }
+
+ // gestion des pages du wiki
+ if ( $new ) {
+
+ # on crée une nouvelle page
+ $status = PageF::newPage(
+ 'Types de groupes/' . $select['nom'],
+ NS_PROJECT,
+ '{{Modèle:Types de groupes}}',
+ Msg::get( 'specialadmingrouptypes-newpage-summary' ),
+ $wgUser
+ );
+ if( !$status->done() ) {
+ return Status::newFailed( 'Action annulée: la page MGWiki:Types de groupes/' .
+ $reqData['nom'] . ' n\'a pas pu être créée. (' . $status->mess() . ')' );
+ }
+ $data['page_id'] = $status->extra();
+ }
+ else {
+ $oldData = DbF::select_clean( 'groupe_type', ['id', 'nom', 'page_id' ], 'id = ' . $reqData['id'] )[0];
+
+ # on vérifie l'existence de la page et l'actualité de la base
+ $page_id = PageF::pageID( 'MGWiki:Types de groupes/' . $oldData['nom'] );
+ if ( $page_id < 1 ) {
+ $actualPage = PageF::newPage(
+ 'Types de groupes/' . $oldData['nom'],
+ NS_PROJECT,
+ '{{Modèle:Types de groupes}}',
+ Msg::get( 'specialadmingrouptypes-newpage-summary' ),
+ $wgUser
+ );
+ if ( !$actualPage->done() ) {
+ return Status::newFailed( 'Action annulée: la page MGWiki:Types de groupes/' .
+ $oldData['nom'] . ' n\'a pas pu être créée. (' . $actualPage->mess() . ')' );
+ }
+ $oldData['page_id'] = $actualPage->extra();
+ }
+ elseif ( $oldData['page_id'] != $page_id ) {
+ $oldData['page_id'] = $page_id;
+ }
+
+ # on renomme la page si le nom est modifié
+ if ( $oldData['nom'] != $data['nom'] ) {
+ $status = PageF::renamePage(
+ 'Types de groupes/' . $oldData['nom'],
+ NS_PROJECT,
+ 'Types de groupes/' . $data['nom'],
+ NS_PROJECT,
+ 'Mise à jour automatisée suite à modification de nom',
+ $wgUser
+ );
+ if( !$status->done() ) {
+ return Status::newFailed( 'Action annulée: la page MGWiki:Types de groupes/' .
+ $reqData['nom'] . ' n\'a pas pu être renomée.' );
+ }
+ $data['page_id'] = $status->extra();
+ }
+ else {
+ $data['page_id'] = $oldData['page_id'];
+ }
+ }
+
+ // écriture des tables
+ $write = DbF::update_or_insert( 'groupe_type', $select, $data, $wgUser->getId() );
+ if ( ! $write->done() ) {
+ $edit = true;
+ return $write;
+ }
+ $edit = false;
+
+ // réactualisation de la page
+ $refresh = PageF::refreshPage(
+ $data['page_id'],
+ Msg::get( 'specialadmingrouptypes-update-summary' ),
+ $wgUser
+ );
+
+ if ( ! $refresh->done() ) {
+ return Status::newFailed( 'Les modifications ont été enregistrées mais la page MGWiki:Types de groupes/'
+ . $reqData['nom'] . ' n\'a pas pu être réactualisée.' );
+ }
+
+ return Status::newDone( 'Les modifications de ' . $reqData['nom'] . ' ont été enregistrées.' );
+ }
+
+ /**
+ * @param array $get associative array of GET request parameters
+ * [key => value]
+ */
+ private function selfURL( array $get = [] ) {
+ return SpecialPage::getTitleFor( 'specialadmingrouptypes' )->getLinkURL( $get );
+ }
+
+ protected function getGroupName() {
+ return 'mgwiki';
+ }
+}
diff --git a/includes/SpecialCheckAccounts.php b/includes/SpecialCheckAccounts.php
new file mode 100644
index 0000000..a1920ec
--- /dev/null
+++ b/includes/SpecialCheckAccounts.php
@@ -0,0 +1,905 @@
+triOptions = ['user_id', 'user_name', 'page_template_nom', 'page_template_prenom', 'user_email'];
+ $this->filtreOptions = [
+ 'page',
+ 'page_redirect',
+ 'page_template',
+ 'same_names',
+ 'user_email',
+ 'same_email',
+ 'user_real_name',
+ 'same_real_name',
+ 'mgw_exists',
+ 'sysop',
+ 'bureaucrat',
+ 'U3',
+ 'U2',
+ 'U1',
+ 'U0'
+ ];
+ $this->filtreOptionsTitles = [
+ 'user_groups' => [ 'sysop', 'bureaucrat', 'U3', 'U2', 'U1', 'U0' ]
+ ];
+ }
+
+ /**
+ * @param string $sub The subpage string argument (if any).
+ */
+ public function execute( $sub )
+ {
+ global $wgUser;
+ global $tri;
+
+ $postData = $this->getRequest()->getPostValues();
+
+ // définition de $tri et $select[]
+ $tri = $this->set_select( $postData );
+
+ // définition de $check[]
+ $this->set_check( $postData );
+
+ // affichage du formulaire d'entête
+ $this->setHeaders();
+ $out = $this->getOutput();
+ $out->addModules('ext.mgwiki-specialcheckaccounts');
+ $out->setPageTitle( Msg::get('specialcheckaccounts-title') );
+ $out->addHTML( $this->makeHeadForm() );
+
+ // ACTIONS
+ $empty = [
+ 'user_id' => '',
+ 'user_name' => '',
+ 'page_url' => ''
+ ];
+
+ # affichage brut
+ if ( isset( $postData['action'] ) && $postData['action'] == 'show' ) {
+ $users = $this->getUsers( );
+ }
+
+ # emails en doublon uniquement
+ if ( isset( $postData['action'] ) && $postData['action'] == 'emailduplicates' ) {
+
+ $user_ids = $this->getEmailDuplicates();
+ $users = $this->getUsers( implode( ',', $user_ids ) );
+
+ if ( count( $users ) == 0 ) {
+ $empty['user_id'] = '0';
+ $empty['message'] = 'Aucun mail en doublon n\'a été trouvé.';
+ $actionInfo[] = $empty;
+ }
+ }
+
+ # utilisateurs cochés uniquement
+ if ( isset( $postData['action'] ) && $postData['action'] == 'select' ) {
+ $users = $this->getUsers( $postData['addusers'] );
+ }
+
+ # harmonisation de la casse 'Prénom NOM' sur les pages utilisateurs
+ if ( isset( $postData['action'] ) && $postData['action'] == 'sanitize' ) {
+
+ $actionInfo = [];
+ $list = explode( ',', $postData['addusers'] );
+
+ foreach ( $list as $user_id ) {
+
+ $MGWuser = MGWUser::newFromUserId( (int)$user_id );
+ if ( $MGWuser->sanitize_nom() && $MGWuser->sanitize_prenom() ) {
+ $status = $MGWuser->update_page_template_names(
+ Msg::get('specialcheckaccount-sanitize-summary')
+ );
+ }
+ else
+ $status = Status::newFailed( 'Nom ou Prénom non défini' );
+
+ $mess = ( $status->done() ) ? 'SUCCES : ' : 'ECHEC : ';
+ $mess .= $status->mess();
+
+ $return = array(
+ 'user_id' => $user_id,
+ 'user_name' => $MGWuser->get_data()['user_name'],
+ 'page_url' => $MGWuser->get_data()['page_url'],
+ 'message' => $mess
+ );
+
+ $actionInfo[] = $return;
+ }
+ $users = $this->getUsers( $postData['addusers'] );
+ }
+
+ # harmoniser les comptes utilisateurs sur la base du formulaire Personne
+ if ( isset( $postData['action'] ) && $postData['action'] == 'harmonize' ) {
+ if ( $postData['harmonize-replace'] == 'oui' ) {
+ $replace = true;
+ } else {
+ $replace = false;
+ }
+ $list = explode( ',', $postData['addusers'] );
+ $actionInfo = [];
+ foreach ( $list as $user_id ) {
+ $MGWuser = MGWUser::newFromUserId( $user_id );
+ if ( !$MGWuser->same_names() ){
+ $status = $MGWuser->rename(
+ Msg::get('specialcheckaccount-rename-summary'),
+ $replace,
+ $nsAll = true,
+ $ns = null,
+ $wgUser
+ );
+ $details = '';
+ if ( is_array( $status->extra() ) && count( $status->extra() ) > 0 ) {
+ foreach ( $status->extra() as $detail ) {
+ $details .= ' / ' . $detail['message'];
+ }
+ }
+ $mess = ( $status->done() ) ? 'SUCCES : ' : 'ECHEC : ';
+ $mess .= $status->mess() . $details ;
+ } else {
+ $mess = '"user_name" est à jour.';
+ }
+ if ( !$MGWuser->same_real_name() ) {
+ $MGWuser->update_real_name();
+ $mess .= '
"user_real_name" a été mis à jour';
+ }
+ if ( !$MGWuser->same_email() ) {
+ $realname = $MGWuser->update_email();
+ $mess .= '
"user_email" a été mis à jour';
+ }
+ $return = array(
+ 'user_id' => $user_id,
+ 'user_name' => $MGWuser->get_data()['user_name'],
+ 'page_url' => $MGWuser->get_data()['page_url'],
+ 'message' => $mess
+ );
+ $actionInfo[] = $return;
+ }
+ $users = $this->getUsers( $postData['addusers'] );
+ }
+
+ # vider le compte utilisateur de ses infos (user_email, user_real_name et groupes )
+ if ( isset( $postData['action'] ) && $postData['action'] == 'empty' ) {
+ $list = explode( ',', $postData['deleteusers'] );
+ $actionInfo = [];
+ foreach ( $list as $user_id ) {
+ $MGWuser = MGWUser::newFromUserId( $user_id );
+ $info = $MGWuser->delete_all_groups();
+ if ( !is_null( $MGWuser->get_email() ) ) {
+ $MGWuser->delete_email();
+ $info .= '
E-mail effacé.';
+ }
+ if ( is_string( $MGWuser->get_data()['user_real_name'] ) ) {
+ $MGWuser->delete_real_name();
+ $info .= '
real_name effacé.';
+ }
+ $page_url = ( is_string( $MGWuser->get_data()['page_url'] ) ) ? $MGWuser->get_data()['page_url'] : null;
+ $return = array(
+ 'user_id' => $user_id,
+ 'user_name' => $MGWuser->get_data()['user_name'],
+ 'page_url' => $page_url,
+ 'message' => $info
+ );
+ $actionInfo[] = $return;
+ }
+ $users = $this->getUsers( $postData['deleteusers'] );
+ }
+
+ # effacer les utilisateurs
+ ## contrôle des entrées
+ $deleteAsked = isset( $postData['action'] ) && $postData['action'] == 'delete';
+ $checkDelete = true;
+ if ( $deleteAsked && count( explode( ',', $postData['addusers'] ) ) > 1 ) {
+ $empty['message'] = '"in" :: un seul utilisateur de destination doit être coché.';
+ $actionInfo[] = $empty;
+ if ( isset( $postData['deleteusers'] ) && $postData['deleteusers'] != '' ) {
+ $postData['addusers'] .= ',' . $postData['deleteusers'];
+ }
+ $users = $this->getUsers( $postData['addusers'] );
+ $checkDelete = false;
+ }
+ if ( $deleteAsked && ( !isset( $postData['addusers'] ) || $postData['addusers'] == '' ) ) {
+ $empty['message'] = '"in" :: un utilisateur de destination doit être coché.';
+ $actionInfo[] = $empty;
+ if ( isset( $postData['deleteusers'] ) && $postData['deleteusers'] != '' ) {
+ $users = $this->getUsers( $postData['deleteusers'] );
+ }
+ $checkDelete = false;
+ }
+ if ( $deleteAsked && ( !isset( $postData['deleteusers'] ) || $postData['deleteusers'] == '' ) ) {
+ $empty['message'] = 'Au moins un utilisateur à supprimer doit être coché :: "out"';
+ $actionInfo[] = $empty;
+ if ( isset( $postData['deleteusers'] ) && $postData['deleteusers'] != '' ) {
+ $users = $this->getUsers( $postData['deleteusers'] );
+ }
+ $checkDelete = false;
+ }
+ ## délétion en tant que telle
+ if ( $deleteAsked && $checkDelete ) {
+ $targetUser = $this->getUsers( $postData['addusers'] );
+ $deleteUsers = $this->getUsers( $postData['deleteusers'] );
+ foreach ( $deleteUsers as $key => $deleteUser ) {
+ // merge en effançant l'ancien utilisateur
+ // implique suppression de compte et de page
+ $merge = MGWUserMerge::execute(
+ $deleteUser['user_id'],
+ $targetUser[0]['user_id'],
+ true,
+ [ $this, 'msg' ]
+ );
+ if ( $merge->done() && $postData['delete-replace'] == 'oui' ) {
+ // on remplace l'ancien nom par le nouveau dans le contenu de toutes les pages
+ $replace = new MGWReplaceText( [
+ "target" => $deleteUser['user_name'],
+ "replace" => $targetUser[0]['user_name'],
+ "regex" => false,
+ "nsall" => true,
+ "ns" => null,
+ "summary" => 'Tache automatisée lors de la fusion des utilisateurs.',
+ "user" => $wgUser->getName()
+ ] );
+ $replace = $replace->execute();
+ }
+ if ( !$merge->done() ) {
+ $message = 'ECHEC :' . $deleteUser['user_name'] .
+ ' n\'a pas pu être supprimé (' . $merge->mess() . ')';
+ }
+ elseif ( $merge->done() || !$replace->done() ) {
+ $details = '';
+ if ( is_array( $replace->extra() ) && count( $replace->extra() ) > 0 ) {
+ foreach ( $replace->extra() as $detail ) {
+ $details .= ' / ' . $detail['message'];
+ }
+ }
+ $message = 'SUCCES PARTIEL :
' .
+ '- ' . $deleteUser['user_name'] . ' a bien été supprimé (' . $merge->mess() . ')
' .
+ '- Son nom n\'a pas été supprimé dans les pages (' . $replace->mess() . ')' . $details . '
';
+ }
+ else {
+ $message = 'SUCCES :
' .
+ '- ' . $deleteUser['user_name'] . 'a bien été supprimé (' . $merge->mess() . ')
';
+ if ( isset( $replace ) ) {
+ $details = '';
+ foreach ( $replace->extra() as $detail ) {
+ $details .= ' / ' . $detail['message'];
+ }
+ $message .= '- Son nom a bien été supprimé dans les pages (' . $replace->mess() . ')' . $details . '
';
+ }
+ $message .= '
';
+ }
+ $deleteUser['message'] = $message;
+ $actionInfo[] = $deleteUser;
+ }
+ $users = $this->getUsers( $postData['addusers'] );
+ }
+
+ # mise à jour de la table mgw_utilisateurs
+ if ( isset( $postData['action'] ) && $postData['action'] == 'db_update' ) {
+ $list = explode( ',', $postData['addusers'] );
+ $actionInfo = [];
+ foreach ( $list as $user_id ) {
+ $MGWuser = MGWUser::newFromUserId( (int)$user_id );
+ $status = $MGWuser->db_update();
+ if ( $status->done() ) {
+ $info = 'SUCCES: ' . $status->mess();
+ } else {
+ $info = 'ECHEC: ' . $status->mess();
+ }
+ $actionInfo[] = [
+ 'user_id' => $user_id,
+ 'user_name' => $MGWuser->get_data()['user_name'],
+ 'page_url' => $MGWuser->get_data()['page_url'],
+ 'message' => $info
+ ];
+ }
+ $users = $this->getUsers( $postData['addusers'] );
+ }
+
+ // TRI
+ if ( isset( $users ) ) {
+ // tri d'un tableau à 2 dimentions
+ uasort ( $users , function ($a, $b) {
+ global $tri;
+ if ( $a[$tri[1]] == $b[$tri[1]] ) {
+ if ( $tri[2] != '' ) {
+ if ( $a[$tri[2]] == $b[$tri[2]] ) return 0;
+ return ( $a[$tri[2]] < $b[$tri[2]] ) ? -1 : 1;
+ }
+ return 0;
+ }
+ return ( $a[$tri[1]] < $b[$tri[1]] ) ? -1 : 1;
+ });
+ }
+ else $users = [];
+
+ if ( isset( $actionInfo ) ) {
+ $users = array_merge( $actionInfo, $users );
+ }
+
+ if ( sizeof( $users ) < 1 ) {
+ $users[] = array(
+ 'user_id' => '0',
+ 'user_name' => '',
+ 'page_url' => '',
+ 'message' => 'Aucun utilisateur ne correspond à la requête.'
+ );
+ }
+
+ // AFFICHAGE DES UTILISATEURS
+ $out->addHTML('');
+ foreach( $users as $row ) {
+ $out->addHTML( $this->displayUser( $row ) );
+ }
+ $out->addHTML('
');
+ $out->addHTML( $this->displayActions() );
+ }
+
+
+ /**
+ * @param array $postData
+ */
+ private function set_select( &$postData )
+ {
+ $select = &$this->select;
+
+ $tri[1] = ( isset( $postData['tri1'] ) )
+ ? $postData['tri1'] : 'user_id';
+
+ $tri[2] = ( isset( $postData['tri2'] ) )
+ ? $postData['tri2'] : '';
+
+ foreach ( $this->triOptions as $value ) {
+
+ if ( isset($postData['tri1'] ) && $postData['tri1'] == $value ) {
+ $select[1][$value] = 'selected';
+ } else {
+ $select[1][$value] = '';
+ }
+
+ if ( isset($postData['tri2'] ) && $postData['tri2'] == $value ) {
+ $select[2][$value] = 'selected';
+ } else {
+ $select[2][$value] = '';
+ }
+
+ if ( !in_array( 'selected', $select[2] ) ) {
+ $select[2]['...'] = 'selected';
+ } else {
+ $select[2]['...'] = '';
+ }
+ }
+
+ return $tri;
+ }
+
+ /**
+ * @param array $postData
+ */
+ private function set_check( &$postData )
+ {
+ $check = &$this->check;
+
+ foreach ( $this->filtreOptions as $value ) {
+
+ $check[$value]['view'] =
+ ( isset( $postData[$value] ) && $postData[$value] == 'view' )
+ ? 'checked' : '';
+
+ $check[$value]['hide'] =
+ ( isset( $postData[$value] ) && $postData[$value] == 'hide' )
+ ? 'checked' : '';
+
+ $check[$value]['only'] =
+ ( isset( $postData[$value] ) && $postData[$value] == 'only' )
+ ? 'checked' : '';
+
+ if ( !in_array(
+ 'checked',
+ [
+ $check[$value]['view'],
+ $check[$value]['hide'],
+ $check[$value]['only']
+ ] )
+ ) {
+ $check[$value]['view'] = 'checked';
+ }
+ }
+ }
+
+ /**
+ * @param string $option { 'tri' | 'filtres' }
+ * @return mixed (string ou false)
+ */
+ private function makeHeadForm ( )
+ {
+ global $_SERVER;
+ $select = &$this->select;
+ $check = &$this->check;
+
+ $return = '
+
+ ';
+ return $return;
+ }
+
+
+ /**
+ * @param array $row
+ * @return mixed (string HTML ou false)
+ */
+ private function displayUser ( &$row ) {
+ // si l'entrée correspond à un message on l'affiche...
+ if ( isset( $row['message'] ) ) {
+ $userName = ( !isset( $row['page_url'] ) || is_null( $row['page_url'] ) )
+ ? $row['user_name'] : '' . $row['user_name'] . '';
+ return '
+
+ '.$row['user_id'].' ' . $userName . ' |
+ ' . $row['message'] . ' |
+
+
';
+ }
+
+ // sinon on affiche l'utilisateur:
+ $class = ( $row['mgw_exists'] ) ? 'mgw-specialcheckaccount-mgw-in' : 'mgw-specialcheckaccount-mgw-out';
+ $userName = $row['user_name'];
+ $pageInfo = 'page utilisateur inexistante';
+
+ if ( $row['page'] ) {
+ $userName = '' . $row['user_name'] . '';
+
+ if ( $row['page_redirect'] ) {
+ $pageInfo = '#redirection: '
+ . $row['page_redirect_title'] . '';
+ }
+ elseif ( !$row['page_template'] ) {
+ $pageInfo = '! {{Personne}}';
+ }
+ else {
+ $pageInfo = '';
+ }
+ }
+
+ $return = '';
+ return $return;
+ }
+
+ /**
+ * @return string HTML
+ */
+ private function displayActions() {
+ global $_SERVER;
+ return '';
+ }
+
+
+ /**
+ * Requête la base et les pages utilisateur
+ *
+ * @param string $list user_id values in comma-separated string
+ * @param string $filter ( 'valid'|'page_template'|'page'|'page_redirect' )
+ * @param mixed $where string ou false (ex. : 'cat_pages > 0')
+ * @param mixed $options array ou false (ex. : array( 'ORDER BY' => 'cat_title ASC' ))
+ * @return array|null
+ */
+ private function getUsers ( $list = '', $where = '', $options = false ) {
+
+ // Construction de la liste des utilisateurs
+ if ( empty( $list ) ) {
+
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbr = $lb->getConnectionRef( DB_REPLICA );
+ $colnames = array( 'user_id' );
+
+ if ( !$options ) {
+ $res = $dbr->select( 'user', $colnames, $where );
+ }
+ elseif ( is_array($options) ) {
+ $res = $dbr->select( 'user', $colnames, $where, __METHOD__, $options );
+ }
+ else throw new \Exception("Erreur SpecialCheckAccounts::getUsers() : arguments invalides", 1);
+
+ foreach( $res as $row ) {
+ if ( empty( $list ) ) {
+ $list = $row->user_id;
+ }
+ else {
+ $list .= ',' . $row->user_id;
+ }
+ }
+ }
+
+ // construction des données
+ $list = explode( ',', $list );
+ $return = [];
+ foreach( $list as $user_id ) {
+ $user_id = (int)$user_id;
+
+ $MGWuser = MGWUser::newFromUserId( $user_id );
+ $MWuser = UserF::getUserFromId( $user_id );
+
+ $data = $MGWuser->get_data();
+ PhpF::false( $data['user_real_name'] );
+ PhpF::false( $data['user_email'] );
+ $data['user_groups'] = $MWuser->getGroups();
+ $data['user_registration'] = $MWuser->getRegistration();
+ PhpF::empty( $data['user_real_name'] );
+ PhpF::empty( $data['user_email'] );
+ $data['mgw_exists'] = $MGWuser->get_mgw_exists();
+ $data['page'] = $MGWuser->get_page_exists();
+ PhpF::false( $data['page_redirect'] );
+ PhpF::false( $data['page_template'] );
+ PhpF::empty( $data['user_'] );
+ PhpF::empty( $data['page_template_prenom'] );
+ PhpF::empty( $data['page_template_nom'] );
+ PhpF::empty( $data['page_template_email'] );
+ $data['same_email'] = $MGWuser->same_email();
+ $data['same_names'] = $MGWuser->same_names();
+
+ foreach ( $data['user_groups'] as $group ) {
+ $data[ $group ] = true;
+ }
+ foreach ( $this->filtreOptionsTitles['user_groups'] as $group ) {
+ PhpF::false( $data[ $group ] );
+ }
+
+ // contrôle de la sortie selon les filtres
+ $display = true;
+ foreach ( $this->check as $item => $control ) {
+
+ if ( array_key_exists( 'hide', $control )
+ && $control[ 'hide' ] == 'checked'
+ && ( !is_bool( $data[ $item ] ) || $data[ $item ] )
+ ) {
+ $display = false;
+ }
+
+ if ( array_key_exists( 'only', $control )
+ && $control[ 'only' ] == 'checked'
+ && !$data[ $item ]
+ ) {
+ $display = false;
+ }
+ }
+
+ // sortie
+ if ( $display ) {
+ $return[] = $data;
+ }
+ }
+ return $return;
+ }
+
+ /**
+ * Recherche les adresses mail en doublon
+ *
+ * @return mixed (array ou null)
+ */
+ private function getEmailDuplicates ( ) {
+ $mails = [];
+
+ // Requête sur les mails
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbr = $lb->getConnectionRef( DB_REPLICA );
+ $colnames = array( 'user_email' );
+ $where = 'user_email IS NOT NULL';
+ $options = array(
+ "GROUP BY" => "user_email",
+ "HAVING" => "COUNT(user_email) > 1"
+ );
+ $res = $dbr->select( 'user', $colnames, $where, __METHOD__, $options );
+
+ foreach( $res as $row ) {
+ $mails[] = $row->user_email;
+ }
+
+ // on récupère les utilisateurs correspondants
+ $user_ids = [];
+ foreach ( $mails as $mail ) {
+ $colnames = array( 'user_id' );
+ $where = 'user_email = \'' . $mail . '\'';
+ $res = $dbr->select( 'user', $colnames, $where );
+ foreach( $res as $row ) {
+ $user_ids[] = $row->user_id;
+ }
+ }
+ return $user_ids;
+ }
+
+ /**
+ * @param array $idList : liste des id utilisateurs
+ * @return array $return : utilisateurs et messages
+ */
+ private function sanitizeNomPrenom ( $idList ) {
+ $return = [];
+ foreach ( $idList as $id ) {
+ $user = UserF::getUserFromId( $id );
+ $userpage = PageF::getPageFromTitleText( $user->getName(), NS_USER, true );
+ $oldtext = $userpage->getContent()->getNativeData();
+
+ // Prénom
+ $newtext = preg_replace_callback(
+ '/(\|Prénom=)([^\|]+)/',
+ function ($text) {
+ $space = false;
+ $str = $text[2];
+ if ( preg_match('/ /', $text[2]) > 0 ) {
+ $str = str_replace(' ','-',$str);
+ $space = true;
+ }
+ $str = explode( '-', $str);
+ foreach ( $str as $key => $substr ) {
+ $str[$key] = ucfirst( strtolower( $substr ) );
+ }
+ $text[2] = ( $space ) ? implode( ' ', $str ) : implode( '-', $str );
+ return $text[1] . $text[2] ;
+ },
+ $oldtext );
+
+ // Nom
+ $newtext = preg_replace_callback(
+ '/(\|Nom=)([^\|]+)/',
+ function ($text) {
+ return $text[1] . strtoupper($text[2]);
+ },
+ $newtext );
+
+ // If there's at least one replacement, modify the page
+ if ( $newtext != $oldtext ) {
+ $edit_summary = Msg::get('specialcheckaccount-sanitize-summary');
+ $newcontent = new WikitextContent( $newtext );
+ $userpage->doEditContent( $newcontent, $edit_summary, EDIT_AUTOSUMMARY );
+ }
+ else {
+ $return[] = array(
+ 'user_id' => $id,
+ 'user_name' => $userpage->getTitle()->getFullText(),
+ 'page_url' => $userpage->getTitle()->getFullURL(),
+ 'message' => 'Aucune modification n\'a été faite.'
+ );
+ }
+ }
+ return $return;
+ }
+
+ /**
+ * @param string $oldName
+ * @param string $newName
+ * @return true|string $e
+ */
+ private function userPageRedirect( $oldName, $newName ) {
+
+ $oldUserPage = PageF::getPageFromTitleText( $oldName, NS_USER );
+
+ if ( is_null( $oldUserPage ) ) {
+ return Status::newFailed( "Redirection impossible: 'Utilisateur:$oldName' doit être une page existante." );
+ }
+
+ $newtext = '#REDIRECTION [[Utilisateur:' . $newName . ']]';
+ $edit_summary = Msg::get('specialcheckaccount-merge-summary', [ $newName ] );
+ $flags = EDIT_AUTOSUMMARY;
+ $newcontent = new WikitextContent( $newtext );
+
+ $oldUserPage->doEditContent( $newcontent, $edit_summary, $flags );
+
+ return Status::newDone( "La page 'Utilisateur:$oldName' a bien été redirigée vers 'Utilisateur:$newName'.");
+ }
+
+ private function userEmptyAccount( $userName ) {
+
+ $oldUser = UserF::getUserFromNames( $oldName, null, true );
+
+ if ( is_null($oldUser) ) {
+ return Status::newFailed( 'Nettoyage du compte utilisateur: $oldUser doit être un utilisateur valide.' );
+ }
+
+ $oldUser->setEmail('');
+ foreach ( $oldUser->getGroups() as $group ) {
+ $oldUser->removeGroup( $group );
+ }
+ $oldUser->saveSettings();
+
+ return Status::newDone( "L'e-mail et les groupes de l'utilisateur $userName ont été effacés.");
+ }
+
+ protected function getGroupName() {
+ return 'mgwiki';
+ }
+}
diff --git a/includes/SpecialCheckGroups.php b/includes/SpecialCheckGroups.php
new file mode 100644
index 0000000..678a93a
--- /dev/null
+++ b/includes/SpecialCheckGroups.php
@@ -0,0 +1,487 @@
+triOptions = ['page_id', 'page_title', 'referent', 'year'];
+ $this->filtreOptions = [
+ 'archive',
+ 'nogrouptemplate',
+ 'redirect'
+ ];
+ $this->filtreOptionsTitles = [
+ ];
+ }
+
+ /**
+ * @param string $sub The subpage string argument (if any).
+ */
+
+ public function execute( $sub )
+ {
+ global $tri;
+ $postData = $this->getRequest()->getPostValues();
+
+ // définition de $select[] et $check[]
+ $this->setOptions( $postData );
+
+ // affichage du formulaire d'entête
+ $this->setHeaders();
+ $out = $this->getOutput();
+ $out->addModules('ext.mgwiki-specialcheckgroups');
+ $out->setPageTitle( Msg::get('specialcheckgroups-title') );
+ $out->addHTML( $this->makeHeadForm() );
+
+ // ACTIONS
+
+ # affichage brut
+ if ( isset( $postData['afficher'] ) ) {
+ $groups = $this->getGroups( false );
+ }
+
+ # pages groupe cochés uniquement
+ if ( isset( $postData['select'] ) ) {
+ $groups = self::getGroups( false, 'page_id IN(' . $postData['addgroups'] . ')' );
+ }
+
+ # affichage des groupes valides uniquement
+ if ( isset( $postData['validgroups'] ) ) {
+ $groups = self::getGroups( true );
+ }
+
+ # mise à jour des tables mgw_group et mgw_membres
+ if ( isset( $postData['populate'] ) ) {
+ $groups = self::getGroups( true, 'user_id IN(' . $postData['addgroups'] . ')' );
+ foreach ( $groups as $validGroup ) {
+ /*
+ $r = UserF::setMGWUser( $validUser['id'], $validUser['nom'], $validUser['prenom'] );
+ $mess = ( $r['done'] ) ? 'SUCCES : ' : 'ECHEC : ';
+ $validUser['message'] = $mess . $r['message'];
+ $actionInfo[] = $validUser;
+ */
+ }
+ }
+
+ // TRI
+ if ( isset($groups) ) {
+ // tri d'un tableau à 2 dimentions
+ uasort ( $groups , function ($a, $b) {
+ global $tri;
+ if ( $a[$tri[1]] == $b[$tri[1]] ) {
+ if ( $tri[2] != '' ) {
+ if ( $a[$tri[2]] == $b[$tri[2]] ) return 0;
+ return ( $a[$tri[2]] < $b[$tri[2]] ) ? -1 : 1;
+ }
+ return 0;
+ }
+ return ( $a[$tri[1]] < $b[$tri[1]] ) ? -1 : 1;
+ });
+ }
+ else $groups = [];
+ if ( isset( $actionInfo ) ) {
+ $groups = array_merge( $actionInfo, $groups );
+ }
+ if ( sizeof( $groups ) < 1 ) {
+ $groups[] = array(
+ 'page_id' => '0',
+ 'page_title' => '',
+ 'page_url' => '',
+ 'message' => 'Aucun utilisateur ne correspond à la requête.'
+ );
+ }
+
+ // AFFICHAGE DES GROUPES
+ $out->addHTML('');
+ $select = &$this->select;
+ $check = &$this->check;
+ foreach( $groups as $row ) {
+ /*
+ filtres:
+ 'archive',
+ 'nogrouptemplate',
+ 'redirect'
+ champs:
+ 'archive'
+ 'template'
+ 'page_id'
+ 'page_title'
+ 'page_url'
+ 'type'
+ 'institution'
+ 'referent'
+ 'membres'
+ 'year'
+ 'redirect'
+ 'redirect_url'
+ */
+ $test = true;
+ //'nouserpage', 'nousertemplate', 'sysop', 'bureaucrat', 'U2', 'nouseremail', 'emailduplicate'
+ if ( ( $check['archive']['hide'] == 'checked' && $row['archive'] == 'Oui' ) ||
+ ( $check['archive']['only'] == 'checked' && $row['archive'] != 'Oui' )
+ ) $test = false;
+ if ( ( $check['nogrouptemplate']['hide'] == 'checked' && !$row['template'] ) ||
+ ( $check['nogrouptemplate']['only'] == 'checked' && $row['template'] )
+ ) $test = false;
+ if ( ( $check['redirect']['hide'] == 'checked' && !is_null($row['redirect']) ) ||
+ ( $check['redirect']['only'] == 'checked' && is_null($row['redirect']) )
+ ) $test = false;
+
+ if ( isset( $row['message'] ) ) $test = true;
+
+ if ( $test ) {
+ $out->addHTML( $this->displayGroup( $row ) );
+ }
+ }
+ $out->addHTML('
');
+ $out->addHTML( $this->displayActions() );
+ }
+
+
+ /**
+ * @param array $postData
+ */
+ private function setOptions( &$postData )
+ {
+ global $tri;
+ $select = &$this->select;
+ $check = &$this->check;
+
+ $tri[1] = ( isset( $postData['tri1'] ) ) ? $postData['tri1'] : 'id';
+ $tri[2] = ( isset( $postData['tri2'] ) ) ? $postData['tri2'] : '';
+
+ foreach ( $this->triOptions as $value ) {
+ if ( isset($postData['tri1'] ) && $postData['tri1'] == $value ) $select[1][$value] = 'selected';
+ else $select[1][$value] = '';
+ if ( isset($postData['tri2'] ) && $postData['tri2'] == $value ) $select[2][$value] = 'selected';
+ else $select[2][$value] = '';
+ if ( !in_array( 'selected', $select[2] ) ) $select[2]['...'] = 'selected';
+ else $select[2]['...'] = '';
+ }
+
+ foreach ( $this->filtreOptions as $value ) {
+ $check[$value]['view'] = ( isset( $postData[$value] ) && $postData[$value] == 'view' ) ? 'checked' : '';
+ $check[$value]['hide'] = ( isset( $postData[$value] ) && $postData[$value] == 'hide' ) ? 'checked' : '';
+ $check[$value]['only'] = ( isset( $postData[$value] ) && $postData[$value] == 'only' ) ? 'checked' : '';
+ if ( !in_array('checked', [ $check[$value]['view'], $check[$value]['hide'], $check[$value]['only'] ] ) )
+ $check[$value]['view'] = 'checked';
+ }
+ }
+
+
+ /**
+ * @param string $option { 'tri' | 'filtres' }
+ * @return mixed (string ou false)
+ */
+ private function makeHeadForm ( )
+ {
+ global $_SERVER;
+ $select = &$this->select;
+ $check = &$this->check;
+
+ $return = '';
+ return $return;
+ }
+
+
+ /**
+ * @param array $row
+ * @return mixed (string HTML ou false)
+ */
+ private function displayGroup ( &$row ) {
+ // si l'entrée correspond à un d'erreur on l'affiche...
+ if ( isset( $row['message'] ) ) {
+ return '
  '.$row['page_id'].' ' . $row['page_title'] . ' :
+   ' . $row['message'] . '
';
+ }
+ // ... sinon on affiche l'utilisateur
+ $return = '';
+ return $return;
+ }
+
+ /**
+ * @return string HTML
+ */
+ private function displayActions() {
+ global $_SERVER;
+ return '
+
+ ';
+ }
+
+ /**
+ * Requête la base et les pages groupe
+ *
+ * @param bool $isValid
+ * @param mixed $where string ou null
+ * @param mixed $options array ou false (ex. : array( 'ORDER BY' => 'cat_title ASC' ))
+ * @return array|null
+ */
+ private function getGroups ( $isValid, $where = null, $options = false ) {
+
+ $return = [];
+
+ /* Construction de la liste des groupes depuis les pages */
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbr = $lb->getConnectionRef( DB_REPLICA );
+ $colnames = array( 'page_id', 'page_title' );
+ $cond = 'page_namespace = 730';
+ if ( !is_null( $where ) ) $cond .= ' AND ' . $where;
+
+ if ( !$options ) {
+ $res = $dbr->select( 'page', $colnames, $cond );
+ }
+ elseif ( is_array($options) ) {
+ $res = $dbr->select( 'page', $colnames, $cond, __METHOD__, $options );
+ }
+ else throw new \Exception("Erreur SpecialCheckGroups::getGroups() : arguments invalides", 1);
+
+ ///////// PATCH TEMPORAIRE /////////////
+ foreach( $res as $row ) {
+ $group = [
+ 'page_id' => $row->page_id,
+ 'page_title' => $row->page_title,
+ 'page_url' => null,
+ 'template' => false,
+ 'archive' => null,
+ 'type' => null,
+ 'institution' => null,
+ 'referent' => null,
+ 'membres' => null,
+ 'year' => null,
+ 'redirect' => null,
+ 'redirect_url' => null,
+ ];
+ $valid = false;
+
+ $page = PageF::getPageFromId( $row->page_id );
+ if ( !is_null( $page ) ) {
+ $group['page_url'] = $page->getTitle()->getFullURL();
+ $infos = PageF::getPageTemplateInfos(
+ $page,
+ 'Groupe',
+ [
+ 'Archivé',
+ 'Type de groupe',
+ 'Institution de rattachement',
+ 'Tuteur ou modérateur',
+ 'Membres',
+ 'Année'
+ ]
+ );
+ if ( !is_null( $infos ) ) {
+ $group['archive'] = $infos['Archivé'];
+ $group['type'] = $infos['Type de groupe'];
+ $group['institution'] = $infos['Institution de rattachement'];
+ $group['referent'] = $infos['Tuteur ou modérateur'];
+ $group['membres'] = $infos['Membres'];
+ $group['year'] = $infos['Année'];
+ $group['template'] = true;
+ $valid = true;
+ }
+ else {
+ $group['redirect'] = PageF::getPageRedirect( $page );
+ if ( !is_null( $group['redirect'] ) ) {
+ // vérification de la validité de la redirection
+ $screen = preg_match( '/^Groupe:(.*)$/', $group['redirect'], $matches );
+ if ( $screen > 0 ) {
+ $title = PageF::getTitleFromText( $matches[1], NS_GROUP, true );
+ if ( !is_null( $title ) ) $group['redirect_url'] = $title->getFullURL();
+ }
+ }
+ }
+ }
+ if ( !$isValid ) $valid = true;
+ /////////////////////////////////////////////
+ if ( $valid ) {
+ $return[] = $group;
+ }
+ }
+ return $return;
+ }
+
+ protected function getGroupName() {
+ return 'mgwiki';
+ }
+}
diff --git a/includes/SpecialMgwikiTest.php b/includes/SpecialMgwikiTest.php
new file mode 100644
index 0000000..2f0a195
--- /dev/null
+++ b/includes/SpecialMgwikiTest.php
@@ -0,0 +1,188 @@
+setHeaders();
+ $postData = $this->getRequest()->getPostValues();
+ $select = '';
+ $opts = [
+ 'ORDER BY' => 'nom',
+ ];
+
+$rep = DbF::select( 'groupe_type', ['id', 'nom', 'admin_level' ] );
+
+
+var_dump($rep);
+
+/*
+ $out = $this->getOutput();
+ $out->enableOOUI();
+ $out->addHTML( new \OOUI\FormLayout( [
+ 'method' => 'POST',
+ 'action' => $_SERVER['PHP_SELF'],
+ 'items' => [
+ new \OOUI\FieldsetLayout( [
+ 'label' => 'Form layout',
+ 'items' => [
+ new \OOUI\FieldLayout(
+ new \OOUI\DropdownInputWidget(
+ [
+ 'name' => 'choix',
+ 'value' => 2,
+ 'options' => [
+ [ 'data' => 1, 'label' => 'choix1' ],
+ [ 'data' => 2, 'label' => 'choix2' ],
+ [ 'data' => 3, 'label' => 'choix3' ],
+ [ 'data' => 4, 'label' => 'choix4' ],
+ ],
+ ] ),
+ [
+ 'label' => 'Choisissez',
+ 'align' => 'top',
+ ]
+ ),
+ new \OOUI\FieldLayout(
+ new \OOUI\TextInputWidget( [
+ 'name' => 'password',
+ 'type' => 'text',
+ 'value' => 'blablabla'
+ ] ),
+ [
+ 'label' => 'Password',
+ 'align' => 'top',
+ ]
+ ),
+ new \OOUI\FieldLayout(
+ new \OOUI\CheckboxInputWidget( [
+ 'name' => 'rememberme',
+ 'selected' => true,
+ ] ),
+ [
+ 'label' => 'Remember me',
+ 'align' => 'inline',
+ ]
+ ),
+ new \OOUI\FieldLayout(
+ new \OOUI\ButtonInputWidget( [
+ 'name' => 'login',
+ 'label' => 'Log in',
+ 'type' => 'submit',
+ 'flags' => [ 'primary', 'progressive' ],
+ 'icon' => 'check',
+ ] ),
+ [
+ 'label' => null,
+ 'align' => 'top',
+ ]
+ ),
+ ]
+ ] )
+ ]
+ ] ) );
+ */
+/*
+ // formDescriptor Array to tell HTMLForm what to build
+ $formDescriptor = [
+ 'user_id' => [
+ 'type' => 'int',
+ 'label' => 'user_id',
+ 'required' => true,
+ //'filter-callback' => function ( $val, $array ) { return 'hahahaha'; },
+ ],
+ 'nom' => [
+ 'type' => 'text',
+ 'label' => 'nom',
+ 'required' => true,
+ //'filter-callback' => function ( $val, $array ) { return 'hahahaha'; },
+ ],
+ 'prenom' => [
+ 'type' => 'text',
+ 'label' => 'prenom',
+ 'required' => true,
+ //'filter-callback' => function ( $val, $array ) { return 'hahahaha'; },
+ ],
+ ];
+
+ // Build the HTMLForm object
+ $htmlForm = HTMLForm::factory( 'vform', $formDescriptor, $this->getContext() );
+ $htmlForm->setSubmitText( 'Soumettre' );
+ $htmlForm->setSubmitCallback( [ $this, 'processInput' ] );
+ $htmlForm->show(); // Display the form
+*/
+
+ /*
+ if ( isset($postData['wpkey'] ) ){
+
+ ///////// TIMESTAMP & DB:
+
+ $user = UserF::getUserFromId((int)$postData['wpkey']);
+ var_dump( wfTimestamp( TS_DB, $user->getRegistration() ) );
+ var_dump( wfTimestamp(TS_MW) );
+
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbw = $lb->getConnectionRef( DB_MASTER );
+ var_dump( $dbw->timestamp( ) ); //
+
+ var_dump( 'MGW_DB_ROW_DROPED : ' . MGW_DB_DROPED );
+
+ ///////// MGWuser :
+ $r = MGWUser::newFromUserId( (int)$postData['wpkey'] );
+ var_dump($r); */
+ /*
+ $replace = new MGWReplaceText( [
+ "target" => "interne TEST2",
+ "replace" => "anonyme",
+ "regex" => false,
+ "nsall" => true,
+ "summary" => "MGW replacetext test.",
+ "user" => "Webmaster"
+ ] );
+ $status = $replace->execute();
+ var_dump($status);
+ */
+ }
+
+ // Callback function
+ // OnSubmit Callback, here we do all the logic we want to do…
+ public static function processInput( $formData ) {
+
+ global $wgUser;
+ $table = 'utilisateur';
+ $select = [ 'user_id' => $formData['user_id'] ];
+ $data = [
+ 'nom' => $formData['nom'],
+ 'prenom' => $formData['prenom']
+ ];
+
+ $updater_id = $wgUser->getId();
+
+ $dbUpdate = DbF::update_or_insert( $table, $select, $data, $updater_id );
+
+ return $dbUpdate->mess();
+ }
+
+ protected function getGroupName() {
+ return 'mgwiki';
+ }
+}
diff --git a/includes/Utilities/Captcha.php b/includes/Utilities/Captcha.php
new file mode 100644
index 0000000..c9108ac
--- /dev/null
+++ b/includes/Utilities/Captcha.php
@@ -0,0 +1,34 @@
+answers = json_decode(file_get_contents($IP . "/extensions/MGWikiDev/data/Private/Captcha.json"), true);
+ }
+
+ public function getRandomKey()
+ {
+ return array_rand($this->answers);
+ }
+
+ public function isValid($key, $response)
+ {
+ return $this->getAnswer($key) === $this->sanitize($response);
+ }
+
+ private function getAnswer($key)
+ {
+ return isset($this->answers[$key]) ? htmlspecialchars($this->answers[$key]) : false;
+ }
+
+ private function sanitize($response)
+ {
+ return strtolower(str_replace('.',',',$response));
+ }
+}
diff --git a/includes/Utilities/GetJsonPage.php b/includes/Utilities/GetJsonPage.php
new file mode 100644
index 0000000..8aa70a5
--- /dev/null
+++ b/includes/Utilities/GetJsonPage.php
@@ -0,0 +1,123 @@
+jsonData = self::retrieveData( $service );
+ }
+
+ /**
+ * récupérer les données en usage statique, sans constructeur
+ * @param string $service
+ */
+ public static function getData( $service )
+ {
+ return self::retrieveData( $service );
+ }
+
+ /**
+ * récupérer l'url de la page
+ * @param string $service
+ */
+ public static function getLink( $service )
+ {
+ return self::getFullURL( $service );
+ }
+
+ public function getFullData()
+ {
+ return $this->jsonData;
+ }
+
+ /**
+ * Retrieve the first mached Json data from a key (recursive search)
+ * @param string $key
+ * @param array $parents (optionnal): a list of parent keys to be searched (in tree order)
+ * @return mixed value or array
+ */
+ public function getSubData( $key, $parents = [] )
+ {
+ $ret = $this->jsonData;
+ if ( !is_array( $parents ) ) {
+ throw new \Exception("Erreur GetJsonPage::getSubData(" . $key . ", " . strval($subkeys) . ") : le deuxième argument doit être un tableau.", 1);
+ }
+ if ( sizeof( $parents ) > 0 ) {
+ foreach ($parents as $index => $parent) {
+ $ret = PhpF::recursiveArrayKey( $parent, $ret );
+ }
+ }
+ $ret = PhpF::recursiveArrayKey( $key, $ret );
+ return $ret;
+ }
+
+ /**
+ * Merge all Json data corresponding to a same key (recursive search)
+ * @param string $key : values to be merged are direct children of this key
+ * @param array $parents (optionnal) : a list of parent keys to be the searched (out of any tree order)
+ * @return array
+ */
+ public function mergeSubData( $key, $parents = [] )
+ {
+ if ( !is_array( $parents ) ) {
+ throw new \Exception("Erreur GetJsonPage::mergeSubData(" . $key . ", " . strval($parents) . ") : le deuxième argument doit être un tableau.", 1);
+ }
+ if ( sizeof( $parents ) > 0 ) {
+ foreach ($parents as $index => $parent) {
+ $temp = $this->getSubData($parent);
+ if ( isset( $ret ) ) {
+ $ret = array_merge( $ret, PhpF::recursiveArrayKeyMerge( $key, $temp ) );
+ }
+ else {
+ $ret = PhpF::recursiveArrayKeyMerge( $key, $temp );
+ }
+ }
+ }
+ else {
+ $ret = PhpF::recursiveArrayKeyMerge( $key, $this->jsonData );
+ }
+ return $ret;
+ }
+
+ private function retrieveData( $service )
+ {
+ global $wgMGWikiJsonPages;
+ if (!isset($wgMGWikiJsonPages[$service])) throw new \Exception("La cible '" . $service . "' n'existe pas.", 1);
+
+ $title = Title::newFromText( $wgMGWikiJsonPages[$service]['title'], $wgMGWikiJsonPages[$service]['namespace'] );
+ if ( $title->getArticleID() == -1 ) {
+ throw new \Exception("La page ".$wgMGWikiJsonPages['title']." (NS: {$wgMGWikiJsonPages['namespace']}) n'existe pas.", 1);
+ }
+ $page = WikiPage::factory( $title );
+
+ return json_decode( $page->getContent()->getNativeData(), true );
+ }
+
+ private function getFullURL( $service )
+ {
+ global $wgMGWikiJsonPages;
+ if (!isset($wgMGWikiJsonPages[$service])) throw new \Exception("La cible '" . $service . "' n'existe pas.", 1);
+
+ $title = Title::newFromText( $wgMGWikiJsonPages[$service]['title'], $wgMGWikiJsonPages[$service]['namespace'] );
+ if ( $title->getArticleID() == -1 ) {
+ throw new \Exception("La page ".$wgMGWikiJsonPages['title']." (NS: {$wgMGWikiJsonPages['namespace']}) n'existe pas.", 1);
+ }
+ return $title->getFullURL();
+ }
+}
diff --git a/includes/Utilities/GetMessage.php b/includes/Utilities/GetMessage.php
new file mode 100644
index 0000000..aa06841
--- /dev/null
+++ b/includes/Utilities/GetMessage.php
@@ -0,0 +1,41 @@
+ 0 ) {
+ foreach ( $args as $key => $arg ) {
+ $needle = '$' . ($key + 1);
+ $out = str_replace( $needle, $arg, $out );
+ }
+ }
+ return $out;
+ }
+
+ // le message n'existe pas
+ if ( $html ) {
+ $link = GetJsonPage::getLink('messages');
+ return '<' . $mess . '>';
+ }
+ else return '<' . $mess . '>';
+ }
+}
diff --git a/includes/Utilities/HtmlFunctions.php b/includes/Utilities/HtmlFunctions.php
new file mode 100644
index 0000000..0d2bc0e
--- /dev/null
+++ b/includes/Utilities/HtmlFunctions.php
@@ -0,0 +1,286 @@
+' . $text . '';
+ }
+
+ /**
+ * @param MGWStatus $status
+ */
+ public static function alertMessage( $status ) {
+ $icon = ( $status->done() )
+ ? ''
+ : '' ;
+ $backgroundColor = ( $status->done() ) ? '#d5fdf4' : '#fef6e7' ;
+ $borderColor = ( $status->done() ) ? '#14866d' : '#ffcc33' ;
+ $outcome = ( $status->done() ) ? 'done' : 'failed' ;
+ return '
+ ' . $icon . '' . $status->mess() . '
' ;
+ }
+
+ public static function onclickButton( $value, $href ) {
+ return '';
+ }
+
+ /**
+ * @param array $hidden [ 'name' => 'value' ]
+ * @param string $ctrlFn 'mw.maFonction()'
+ */
+ public static function actionButton (
+ $action,
+ $label,
+ $check = [ 'add' => 'false', 'del' => 'false' ],
+ $ctrlFn = '',
+ $hidden = [] )
+ {
+ $extra = ' extra="' . json_encode( $hidden ) . '"';
+ return '';
+ }
+
+ /**
+ * @param int $a
+ */
+ public static function reverseCheck( $a, $checked = '' ) {
+ return '
+ ';
+ }
+
+ /**
+ */
+ public static function tagShowArray( $content ) {
+ return '' . $content . '
';
+ }
+
+ /**
+ * @param string $content '| (...) |
'
+ * @param string $class 'active'|'inactive'|'deleted'
+ */
+ public static function tagRowTable( $content, $class ) {
+ return '';
+ }
+
+
+ /**
+ * @param string $text le texte du lien
+ * @param int $id page_id
+ */
+ public static function linkPageId( $text, $id ) {
+ $title = Title::newFromID( $id );
+ if ( !is_null( $title ) ) {
+ return '' . $text . '';
+ }
+ else {
+ return $text;
+ }
+ }
+
+
+ /**
+ * @param array $reqData
+ */
+ public static function set_select( $reqData, $triOptions, &$tri, &$select )
+ {
+ $tri[1]['rev'] = ( empty($reqData['reverse-a']) ) ? '' : 'checked';
+ $tri[2]['rev'] = ( empty($reqData['reverse-b']) ) ? '' : 'checked';
+
+ $tri[1]['val'] = ( isset( $reqData['tri1'] ) )
+ ? $reqData['tri1'] : 'nom';
+
+ $tri[2]['val'] = ( isset( $reqData['tri2'] ) )
+ ? $reqData['tri2'] : '' ;
+
+ foreach ( $triOptions as $value ) {
+
+ if ( isset($reqData['tri1'] ) && $reqData['tri1'] == $value ) {
+ $select[1][$value] = 'selected';
+ } else {
+ $select[1][$value] = '';
+ }
+
+ if ( isset($reqData['tri2'] ) && $reqData['tri2'] == $value ) {
+ $select[2][$value] = 'selected';
+ } else {
+ $select[2][$value] = '';
+ }
+
+ if ( !in_array( 'selected', $select[2] ) ) {
+ $select[2]['...'] = 'selected';
+ } else {
+ $select[2]['...'] = '';
+ }
+ }
+ }
+
+ /**
+ * @param array $reqData
+ */
+ public static function set_check( &$reqData, $filtreOptions, &$check )
+ {
+ foreach ( $filtreOptions as $value ) {
+
+ PhpF::empty( $reqData[$value] );
+
+ $check[$value]['view'] =
+ ( $reqData[$value] == 'view' )
+ ? 'checked' : '';
+
+ $check[$value]['hide'] =
+ ( $reqData[$value] == 'hide' )
+ ? 'checked' : '';
+
+ $check[$value]['only'] =
+ ( $reqData[$value] == 'only' )
+ ? 'checked' : '';
+
+ if ( !in_array(
+ 'checked',
+ [
+ $check[$value]['view'],
+ $check[$value]['hide'],
+ $check[$value]['only']
+ ] )
+ ) {
+ if ( $value == 'archive' ) {
+ $check[$value]['hide'] = 'checked';
+ }
+ else {
+ $check[$value]['view'] = 'checked';
+ }
+ }
+ }
+ }
+
+ /**
+ * @param string $url
+ * @param array $select
+ * @param array $check
+ * @param string $msg_prefix préfixe des messages à récupérer
+ *
+ * @return string
+ */
+ public static function makeHeadForm ( $url, $select, $check, $tri, $headInfos, $msg_prefix )
+ {
+ $return = '
+ ';
+ return $return;
+ }
+
+ /**
+ * @param array $showArray
+ * @param array $actionInfo
+ * @return void
+ */
+ public static function sort_show_array( &$show_array, $triRef ) {
+ // tri d'un tableau à 2 dimentions
+ global $tri;
+ $tri = $triRef;
+ uasort ( $show_array , function ( $a, $b ) {
+ global $tri;
+ $ltA = ( $tri[1]['rev'] == 'checked' ) ? 1 : -1 ;
+ $gtA = ( $tri[1]['rev'] == 'checked' ) ? -1 : 1 ;
+ $ltB = ( $tri[2]['rev'] == 'checked' ) ? 1 : -1 ;
+ $gtB = ( $tri[2]['rev'] == 'checked' ) ? -1 : 1 ;
+
+ if ( $a[$tri[1]['val']] == $b[$tri[1]['val']] ) {
+ if ( !empty( $tri[2]['val'] ) ) {
+ if ( $a[$tri[2]['val']] == $b[$tri[2]['val']] ) return 0;
+ return ( $a[$tri[2]['val']] < $b[$tri[2]['val']] ) ? $ltB : $gtB;
+ }
+ return 0;
+ }
+
+ return ( $a[$tri[1]['val']] < $b[$tri[1]['val']] ) ? $ltA : $gtA;
+ });
+ }
+}
diff --git a/includes/Utilities/JsonToForm.php b/includes/Utilities/JsonToForm.php
new file mode 100644
index 0000000..7f63f66
--- /dev/null
+++ b/includes/Utilities/JsonToForm.php
@@ -0,0 +1,406 @@
+makeForm( $message = '', $displayCaptcha = false )
+ * Captcha
+ * JsonToForm->isCaptchaPosted()
+ * JsonToForm->isCaptchaValid()
+ * Email
+ * JsonToForm->sendEmail()
+ * Onclick
+ * form:*:type:radio
+ * template
+ * Divers
+ * JsonToForm->getHash()
+ *
+ * TODO: le module onclick n'est implémenté que sur les
+ */
+class JsonToForm
+{
+ /**
+ * @var string
+ */
+ private $formName;
+
+ /**
+ * @var string
+ */
+ private $prefix;
+
+ /**
+ * @var GetJsonPage
+ */
+ private $Json;
+
+ /**
+ * @var Captcha
+ */
+ private $Captcha;
+
+ /**
+ * @var array issued from WebRequest::getPostValues()
+ */
+ private $postData;
+
+ /**
+ * @var array [ ['type' => 'html'/'wiki', 'value' => (string) ] ]
+ */
+ private $output;
+
+ /**
+ * @param string $jsonService: doit correspondre à une clé de $wgMGWikiJsonPages (LocalSettings.php)
+ * @return array $this->output
+ */
+ public function __construct( $jsonService, &$postData )
+ {
+ global $wgMGWikiJsonPages;
+
+ $this->formName = $jsonService;
+ $this->prefix = 'mgw-' . $this->formName;
+
+ $this->Json = new GetJsonPage( $jsonService );
+ if ( is_null( $this->Json->getSubData( 'show', ['structure'] ) ) )
+ throw new \Exception('Erreur à la construction de JsonToForm depuis la page ' .
+ \MWNamespace::getCanonicalName( $wgMGWikiJsonPages[$jsonService]['namespace'] ) . ':' .
+ $wgMGWikiJsonPages[$jsonService]['title'] . ' : { "structure" : "show" } = null.', 1)
+ ;
+
+ self::hydratePostData($postData);
+
+ $this->Captcha = new Captcha();
+ }
+
+ private function hydratePostData( &$postData )
+ {
+ if ( isset( $postData[$this->formName] ) ) {
+ foreach ( $postData as $itemKey => $itemData ) {
+ $this->postData[$itemKey] = htmlspecialchars( $postData[$itemKey] );
+ }
+ }
+ }
+
+ /**
+ * @param string $message: message passé au format HTML
+ * @param bool $displayCaptcha : si vrai l'élément 'captcha' doit prendre une de ces valeurs : ['beforesubmit', 'aftersubmit']
+ * @return array $this->output
+ */
+ public function makeForm( $message = '' )
+ {
+ # variables
+ global $_SERVER;
+ global $wgMGWikiJsonPages;
+
+ # vérification des constructeurs
+ if ( !is_null( $this->Json->getSubData( 'captcha' ) ) &&
+ !in_array( $this->Json->getSubData( 'captcha' ), ['beforesubmit', 'aftersubmit'] ) )
+ {
+ throw new \Exception('Erreur à la construction de JsonToForm depuis la page ' .
+ \MWNamespace::getCanonicalName( $wgMGWikiJsonPages[$jsonService]['namespace'] ) . ':' .
+ $wgMGWikiJsonPages[$jsonService]['title'] . ' : { [...] "captcha" : "' . $displayCaptcha .
+ '" } : ! in_array( "' . $displayCaptcha . '", ["beforesubmit", "aftersubmit"])', 1) ;
+ }
+ $controllers = $this->Json->getSubData( 'controllers' );
+ if ( !is_null( $controllers ) ) {
+ if ( !is_array( $controllers ) )
+ throw new \Exception('Erreur à la construction de JsonToForm depuis la page ' .
+ \MWNamespace::getCanonicalName( $wgMGWikiJsonPages[$jsonService]['namespace'] ) . ':' .
+ $wgMGWikiJsonPages[$jsonService]['title'] . ' : { "controllers" : } doit contenir un tableau', 1) ;
+ foreach ( $controllers as $controllerKey => $controllerArray ) {
+ if ( !is_array( $controllerArray ) )
+ throw new \Exception('Erreur à la construction de JsonToForm depuis la page ' .
+ \MWNamespace::getCanonicalName( $wgMGWikiJsonPages[$jsonService]['namespace'] ) . ':' .
+ $wgMGWikiJsonPages[$jsonService]['title'] . ' : { "controllers" : "' . $controllerKey . '" : } doit contenir un tableau', 1);
+ }
+ }
+
+ # construction
+ $this->htmlOut(
+ '' );
+ return $this->output;
+ }
+
+ private function makeFormElement( $elementValue, $hide )
+ {
+ $class = ($hide) ? ' class="mgw-hidden"' : '';
+ $style = ($hide) ? ' style="display:none"' : '';
+ $this->htmlOut( '' );
+ }
+
+ private function makeTemplateElement( $elementValue, $hide)
+ {
+ foreach ( $elementValue as $id => $string ) {
+
+ $hideClass = ($hide) ? " mgw-hidden" : '';
+ $hideStyle = ($hide) ? ' style="display:none" ' : '';
+
+ if ( preg_match('/\$/', $string ) != 1 ) {
+ $this->htmlOut( '' );
+ $this->wikiOut( '{{' . $string . '}}' );
+ $this->htmlOut( '' );
+ }
+ else {
+ $string = explode( "$", $string );
+ $cases = $this->Json->getSubData( 'values', [ $string[1] ] );
+ foreach ( $cases as $caseKey => $caseValue )
+ {
+ $this->htmlOut( '' ) ;
+ $this->wikiOut( '{{' . $string[0] . $caseKey . '}}' );
+ $this->htmlOut( '' );
+ }
+ }
+ }
+ }
+
+ private function makeCaptchaElement( $hide )
+ {
+ $class = ($hide) ? ' mgw-hidden' : '';
+ $style = ($hide) ? ' style="display:none"' : '';
+ $captchaKey = $this->Captcha->getRandomKey();
+ $this->htmlOut( '
+ ' );
+ }
+
+ private function htmlOut( $content )
+ {
+ $this->output[] = [ 'type' => 'html', 'content' => $content ];
+ }
+
+ private function wikiOut( $content )
+ {
+ $this->output[] = [ 'type' => 'wiki', 'content' => $content ];
+ }
+
+ private function onclick( $element )
+ {
+ if ( !is_null( $this->Json->getSubData( $element, ['onclick'] ) ) ) {
+ foreach ( $this->Json->getSubData( 'onclick' ) as $key => $value ) {
+ if ( array_key_exists( $element, $value ) )
+ return 'onclick="mw.' . $this->formName . '()" '; //
+ }
+ }
+ else return '';
+ }
+
+ public function sendEmail()
+ {
+ $mailer = new \UserMailer();
+ $mail_to = $this->mailAdress( 'mailto' );
+ $mail_from = $this->mailAdress( 'mailfrom' );
+ $body = $this->composeEmail();
+ $mailer->send(
+ array($mail_to, $mail_from), //to
+ $mail_to, //from
+ Msg::get( $this->formName . '-email-subject' ), //subject
+ $body, //body
+ array( //options
+ 'replyTo' => $mail_from,
+ 'contentType' => 'text/html; charset=UTF-8')
+ );
+ }
+
+ private function mailAdress( $dest )
+ {
+ $controller = $this->Json->getSubData( $dest, ['sendmail'] );
+ if ( isset( $controller['getvalue'] ) ) {
+ $email = $this->postData[$controller['getvalue']];
+ }
+ elseif ( isset( $controller['setonvalue'] ) ) {
+ $email = $this->Json->getSubData( 'setmail', [ $this->postData[$controller['setonvalue']] ] );
+ }
+ switch ( $controller['type'] ) {
+ case 'user':
+ $user = \User::newFromName( $email );
+ return \MailAddress::newFromUser( $user );
+ break;
+ case 'email':
+ return new \MailAddress( $email );
+ break;
+ }
+ }
+
+ private function composeEmail()
+ {
+ $body = '
+
+ ' . Msg::get( $this->formName . '-email-intro') . '
+ Votre message :
+
+ | Date: | ' . date('Y-m-d H:i:s') . ' |
' ;
+
+ $fields = [];
+ # tous les champs "show"
+ foreach ( $this->Json->getSubData( 'form', ['show'] ) as $fieldKey => $fieldValue ) {
+ $fields[$fieldKey] = $fieldValue;
+ }
+ # les champs "hide" selon les valeurs de "changefields"
+ if ( !is_null( $this->Json->getSubData( 'changeform' ) ) ) {
+ foreach ( $this->Json->getSubData('changeform' ) as $key => $value ) {
+ if ( $value['onfield'] == 'any' || in_array( $this->postData[ $value['onfield'] ], $value['onvalues'] ) ) {
+ foreach ( $this->Json->getSubData( $this->postData[$key], ['changefields', '$'.$key ] ) as $fieldKey => $fieldValue ) {
+ $fields[$fieldKey] = $fieldValue;
+ }
+ }
+ }
+ }
+
+ foreach ( $fields as $fieldKey => $fieldValue ) {
+ if ( is_null( $this->Json->getSubData( 'hidden', [ $fieldKey ] ) ) && $fieldKey != 'submit' )
+ $body .= '| ' . $fieldValue[ 'label' ] . ': |
+ ' . $this->postData[ $fieldKey ] . ' |
';
+ }
+
+ $body .= '
+
+ ' . Msg::get( $this->formName . '-email-end' ) . '
+ ';
+
+ return $body;
+ }
+
+ public function isCaptchaPosted() {
+ return isset( $this->postData['captchaResponse'] );
+ }
+
+ public function isCaptchaValid()
+ {
+ if ( isset($this->postData['captchaKey']) && isset($this->postData['captchaResponse']) ){
+ return $this->Captcha->isValid($this->postData['captchaKey'], $this->postData['captchaResponse']);
+ }
+ else return false;
+ }
+
+ /**
+ * gives a unique key from the posted data
+ * @return string
+ */
+ public function getHash()
+ {
+ $string = '';
+ $i = 0;
+ foreach ($this->postData as $key => $value) {
+ $string .= $value ;
+ $i++;
+ if ($i > 2) { break; }
+ }
+ return md5( $string );
+ }
+}
diff --git a/includes/Utilities/MgwDataFunctions.php b/includes/Utilities/MgwDataFunctions.php
new file mode 100644
index 0000000..1c07542
--- /dev/null
+++ b/includes/Utilities/MgwDataFunctions.php
@@ -0,0 +1,336 @@
+ 'valeur', 'page_id' => 5, etc. ]
+ * @param int $updater_id
+ * @return MGWStatus
+ */
+ public static function update_or_insert( $table, $select, $data, $updater_id ) {
+
+ $update = self::update( $table, $select, $data, $updater_id );
+
+ if ( $update->done() ) {
+ return $update;
+ }
+
+ if ( $update->extra == MGW_DB_UNSET ) {
+ $data = array_merge( $data, $select );
+ return self::insert( $table, $data, $updater_id );
+ }
+
+ else {
+ return $update;
+ }
+ }
+
+ /**
+ * @param string $table // nom de la table sans préfixe ('utilisateur', 'groupe_membre', etc.)
+ * @param array $data // sous la forme: [ 'champs SANS PREFIXE' => 'valeur', 'page_id' => 5, etc. ]
+ * @param int $updater_id
+ * @return MGWStatus
+ */
+ public static function insert( $table, $data, $updater_id ) {
+
+ try {
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbw = $lb->getConnectionRef( DB_MASTER );
+
+ $insert = [];
+ foreach ( $data as $field => $value ) {
+ $insert[ $table . '_' . $field ] = $value;
+ }
+ $insert[ $table . '_updater_id' ] = $updater_id;
+ $insert[ $table . '_update_time' ] = $dbw->timestamp( date("Y-m-d H:i:s") );
+
+ $dbw->insert(
+ 'mgw_' . $table,
+ $insert
+ );
+ } catch (\Exception $e) {
+ return Status::newFailed( $e, MGW_DB_ERROR );
+ }
+ return Status::newDone( 'Entrée ajoutée à la table mgw_' . $table, MGW_DB_INSERTED );
+ }
+
+ /**
+ * @param string $table // nom de la table sans préfixe ('utilisateur', 'groupe_membre', etc.)
+ * @param array $select // sous la forme: [ 'champs SANS PREFIXE' => 'valeur' ]
+ * @param array $data // sous la forme: [ 'champs SANS PREFIXE' => 'valeur', 'page_id' => 5, etc. ]
+ * @param int $updater_id
+ * @return MGWStatus
+ */
+ public static function update( $table, $select, $data, $updater_id ) {
+
+ global $wgMgwStringVars;
+
+ $sel = array_keys( $select )[0];
+ if ( in_array( $table . '_' . $sel, $wgMgwStringVars ) ) {
+ $select[$sel] = "'" . $select[$sel] . "'";
+ }
+
+ try {
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbw = $lb->getConnectionRef( DB_MASTER );
+ $update = [];
+
+ // $data = [] -> màj update_time et updater_id seulement
+ if ( count( $data ) > 0 ) {
+
+ // 1. on récupère les données actuelles
+ $res = $dbw->select(
+ 'mgw_' . $table,
+ [
+ '*'
+ ],
+ $table . '_' . $sel .' = ' . $select[$sel]
+ );
+ if ( $res->result->num_rows < 1 ) {
+ return Status::newFailed(
+ 'Aucune ligne ne correspond à la requête ' . $table . '_' . $sel . ' = ' . $select[$sel] .
+ ' dans la table mgw_' . $table,
+ MGW_DB_UNSET
+ );
+ }
+
+ // 2. on vérifie la nouveauté des valeurs proposées
+ $oldData = self::extractResData( $res );
+ $oldData = $oldData[0];
+ $same_data = true;
+
+ foreach ( $data as $field => $value ) {
+ if ( $oldData[ $table . '_' . $field ] != $value ) {
+ $same_data = false;
+ break;
+ }
+ }
+ if ( $same_data ) {
+ return Status::newDone(
+ 'Table mgw_' . $table . ' : ' . $sel . ' = ' . $select[$sel] . ' déjà à jour.',
+ MGW_DB_UNCHANGED
+ );
+ }
+
+ // 3. on archive les données actuelles
+ $archive = self::archive( $table, $oldData, false, $updater_id );
+ if ( ! $archive->done() ) {
+ return $archive;
+ }
+
+ // 4. on met à jour la table
+ $update = [];
+ foreach ( $data as $field => $value ) {
+ $update[ $table . '_' . $field ] = $value;
+ }
+ }
+ $update[ $table . '_updater_id' ] = $updater_id;
+ $update[ $table . '_update_time' ] = $dbw->timestamp( date("Y-m-d H:i:s") );
+
+ foreach ( $update as $field => $value ) {
+ if ( is_string( $value ) ) {
+ $value = "'" . $value . "'";
+ }
+ $array[] = $field . ' = ' . $value;
+ }
+ $array = implode( ',', $array );
+
+ $sql = 'UPDATE MGW_mgw_' . $table . ' SET ' . $array .
+ ' WHERE ' . $table . '_' . $sel . ' = ' . $select[$sel] . ';';
+
+ $dbw->query( $sql );
+
+ } catch (\Exception $e) {
+ return Status::newFailed( $e, MGW_DB_ERROR );
+ }
+ return Status::newDone( 'La table mgw_' . $table . ' a été mise à jour.', MGW_DB_UPDATED );
+ }
+
+ /**
+ * @param string $table // nom de la table sans préfixe ('utilisateur', 'groupe_membre', etc.)
+ * @param array $select // sous la forme: [ 'champs SANS PREFIXE' => 'valeur' ]
+ * @param int $updater_id
+ * @return MGWStatus
+ */
+ public static function delete( $table, $select, $updater_id ) {
+
+ global $wgMgwStringVars;
+
+ $sel = array_keys( $select )[0];
+ if ( in_array( $table . '_' . $sel, $wgMgwStringVars ) ) {
+ $select[$sel] = "'" . $select[$sel] . "'";
+ }
+
+ try {
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbw = $lb->getConnectionRef( DB_MASTER );
+ $update = [];
+
+ // 1. on récupère les données actuelles
+ $res = $dbw->select(
+ 'mgw_' . $table,
+ [
+ '*'
+ ],
+ $table . '_' . $sel .' = ' . $select[$sel]
+ );
+ if ( $res->result->num_rows < 1 ) {
+ return Status::newFailed(
+ 'Aucune ligne ne correspond à la requête ' . $table . '_' . $sel .' = ' . $select[$sel] .
+ ' dans la table mgw_' . $table,
+ MGW_DB_UNSET
+ );
+ }
+
+ // 2. on archive les données actuelles
+ $row = self::extractResData( $res );
+ $row = $row[0];
+ $archive = self::archive( $table, $row, true, $updater_id );
+ if ( ! $archive->done() ) {
+ return $archive;
+ }
+
+ // 4. on supprime la ligne de la table
+ $dbw->delete( 'mgw_' . $table, $table . '_' . $sel . ' = ' . $select[$sel] );
+
+ } catch (\Exception $e) {
+ return Status::newFailed( $e, MGW_DB_ERROR );
+ }
+ return Status::newDone( 'La suppression a été effectuée dans la table mgw_' . $table, $row );
+ }
+
+
+ /**
+ * @param string $table // nom de la table sans préfixe ('utilisateur', 'groupe_membre', etc.)
+ * @param array $rowData // résultat d'une ligne de la requête SELECT * FROM mgw_$table
+ * @param bool $droped // si l'archive conçerne une délétion
+ * @param int $updater_id // obligatoire si l'archive conçerne une délétion
+ * @return MGWStatus
+ */
+ public static function archive( $table, $rowData, $droped = false, $updater_id ) {
+
+ try {
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbw = $lb->getConnectionRef( DB_MASTER );
+
+ if ( $droped && is_null( $updater_id ) ) {
+ throw new \Exception('Erreur: $updater_id = null alors que $droped = true', 1);
+ }
+
+ if ( $droped ) {
+ $rowData[ $table . '_drop_updater_id' ] = $updater_id;
+ $rowData[ $table . '_drop_time' ] = $dbw->timestamp( date("Y-m-d H:i:s") );
+ }
+
+ $dbw->insert(
+ 'mgw_archive_' . $table,
+ $rowData
+ );
+ } catch (\Exception $e) {
+ return Status::newFailed( $e, MGW_DB_ERROR );
+ }
+ return Status::newDone( 'Table mgw_archive' . $table . 'mise à jour.', MGW_DB_INSERTED );
+ }
+
+ /**
+ * Extraire la liste des résultats d'un objet IResultWrapper
+ * Normaliser le format (string|int)
+ * @param IResultWrapper $res
+ * @return array
+ */
+ public static function extractResData( &$res, $prefix = '' ) {
+
+ if ( $res->result->num_rows == 0 ) {
+ return null;
+ }
+
+ global $wgMgwStringVars;
+ $ret = [];
+ $i = 0;
+ while ( $i < $res->result->num_rows ) {
+ $row = $res->fetchRow();
+ $row = array_filter(
+ $row,
+ function( $k ) {
+ return is_string( $k );
+ },
+ ARRAY_FILTER_USE_KEY
+ );
+ # on rétablit string vs/int
+ foreach ( $row as $field => $value ) {
+ if ( !in_array( $field, $wgMgwStringVars ) ) {
+ $row[$field] = (int)$value;
+ }
+ }
+
+ # on supprime le préfixe si demandé
+ if ( !empty( $prefix ) ) {
+ $prefix = str_replace( 'archive_', '', $prefix );
+ foreach ( $row as $field => $value ) {
+ if ( preg_match( '/'.$prefix.'/', $field ) > 0 ){
+ $newfield = str_replace( $prefix . '_', '', $field );
+ $row[$newfield] = $value;
+ unset( $row[$field] );
+ }
+ }
+ }
+
+ $ret[] = $row;
+ $i++;
+ }
+ return $ret;
+ }
+
+ /**
+ * Requêtes dans les tables mgw.
+ * !!! les noms sont donnés sans les préfixes.
+ * @param string $table ex.: 'utilisateurs'
+ * @param array $columns ex.: [ 'id', 'user_id', etc. ]
+ * @param string $select ex.: 'nom = "Foo"'
+ * @param array $opts ex.: [ 'ORDER BY' => 'nom DESC' ]
+ *
+ * @return array
+ */
+ public static function select_clean( $table, $columns, $select = '', $opts = [] ) {
+
+ $prefix = str_replace( 'archive_', '', $table );
+
+ // réimplémentation des préfixes
+ foreach ( $columns as $key => $value ) {
+ if ( $value != 'archive_id' ) {
+ if ( preg_match( '/archive_id/', $select ) < 1 ) {
+ $select = preg_replace( '/^' . $value . '/', $prefix . '_' . $value, $select );
+ }
+ foreach ( $opts as $kkey => $vvalue ) {
+ $opts[$kkey] = preg_replace( '/^' . $value . '/', $prefix . '_' . $value, $vvalue );
+ }
+ $columns[$key] = $prefix . '_' . $value;
+ }
+ }
+ $table_full = 'mgw_' . $table;
+
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbr = $lb->getConnectionRef( DB_REPLICA );
+ $res = $dbr->select(
+ $table_full,
+ $columns,
+ $select,
+ __METHOD__,
+ $opts
+ );
+
+ return self::extractResData( $res, $table );
+ }
+
+}
diff --git a/includes/Utilities/PagesFunctions.php b/includes/Utilities/PagesFunctions.php
new file mode 100644
index 0000000..3e07995
--- /dev/null
+++ b/includes/Utilities/PagesFunctions.php
@@ -0,0 +1,308 @@
+getArticleID();
+ }
+
+ /**
+ * @param string $page_name
+ * @return bool
+ */
+ public static function pageArchiveExists( $page_name ) {
+ $title = Title::newFromText( $page_name );
+ return $title->isDeletedQuick();
+ }
+
+ /**
+ * @param string $pagename : titre de la page
+ * @param int $namespace : constante de l'espace de nom de la page (ex.: 'NS_MAIN')
+ * @param bool $check = false (return null if title does not exist)
+ *
+ * @return Title|null
+ */
+ public function getTitleFromText ( $pagename, $namespace = NS_MAIN, $check = false ) {
+ $title = Title::newFromText( $pagename, $namespace );
+ if ( $check && $title->getArticleID() <= 0 ) {
+ return null;
+ } else return $title;
+ }
+
+ /**
+ * @param Title $title
+ * @param bool $check = false (return null if page does not exist)
+ *
+ * @return WikiPage|null
+ */
+ public function getPageFromTitle ( Title $title, $check = false ) {
+ if ( $check ) {
+ if ( $title->getArticleID() <= 0 ) return null;
+ }
+ return WikiPage::factory( $title );
+ }
+
+ /**
+ * @param int $id
+ *
+ * @return WikiPage|null
+ */
+ public function getPageFromId ( $id ) {
+ return WikiPage::newFromID( $id );
+ }
+
+ /**
+ * @param string $pagename : titre de la page
+ * @param int $namespace : constante de l'espace de nom de la page (ex.: 'NS_MAIN')
+ * @param bool $check = false (return null if title or page does not exist)
+ *
+ * @return WikiPage|null
+ */
+ public function getPageFromTitleText ( $pagename, $namespace = NS_MAIN, $check = false ) {
+ $title = self::getTitleFromText( $pagename, $namespace, $check );
+ if ( is_null( $title ) ) {
+ return null;
+ }
+ return self::getPageFromTitle( $title, $check );
+ }
+
+ /**
+ * @param string $pagename : titre de la page
+ * @param int $namespace : constante de l'espace de nom de la page (ex.: 'NS_MAIN')
+ *
+ * @return string|null
+ */
+ public function getPageContentFromTitleText ( $pagename, $namespace ) {
+ $page = self::getPageFromTitleText( $pagename, $namespace, true );
+ if ( is_null( $page ) ) {
+ return null;
+ }
+ return $page->getContent()->getNativeData();
+ }
+
+ /**
+ * recherche l'existence d'une redirection
+ * renvoie le texte du titre de la page redirigée
+ *
+ * @param WikiPage $page : titre de la page
+ * @param string $output 'title'|'string'
+ * @return Title|string|false
+ */
+ public function getPageRedirect ( $page, $output = 'string' ) {
+ $return = [];
+ $content = $page->getContent()->getNativeData();
+ $screen = preg_match( '/^\#REDIRECTION \[\[(.*)\]\]/', $content, $matches );
+ if ( $screen > 0 ) {
+ switch ( $output ) {
+ case 'title':
+ return Title::newFromText( $matches[1] );
+ break;
+
+ default:
+ return $matches[1];
+ break;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * recherche la valeur des paramètres d'un modèle inclus
+ *
+ * @param WikiPage $page : titre de la page
+ * @param string $template : nom du modèle
+ * @param array $fields : champs recherchés
+ *
+ * @return array( "field" => data, ... )|null
+ */
+ public function getPageTemplateInfos ( $page, $template, $fields ) {
+ $return = [];
+ $content = $page->getContent()->getNativeData();
+ //$content = str_replace( '}}','',$content );
+ $content = explode('{{', $content );
+ foreach ( $content as $key => $string ) {
+ $screen = preg_match( '/^' . $template . '[\s\|]/', $string);
+ if ( $screen > 0 ) {
+ $data = explode( '|', $string );
+ foreach ( $fields as $kkey => $field ) {
+ foreach ( $data as $kkkey => $dat ) {
+ $screen = preg_match('/^'.$field.'[ ]*=(.+)[\s\|]/', $dat, $matches );
+ if ( isset( $matches[1] ) ) {
+ $return[$field] = $matches[1];
+ }
+ }
+ if ( !isset($return[$field]) ) {
+ $return[$field] = null;
+ }
+ }
+ }
+ }
+ if ( sizeof( $return ) == 0 ) $return = null;
+ return $return;
+ }
+
+ /**
+ * écriture du contenu d'une page
+ *
+ * @param WikiPage $page
+ * @param string $newtext
+ * @param string $edit_summary
+ * @param int $flags
+ *
+ * @return bool
+ */
+ public function writeContent ( $page, $newtext, $edit_summary, $flags = 0 ) {
+ global $wgUser;
+ $newcontent = new WikitextContent( $newtext );
+ // cf: docs/pageupdater.txt
+ $updater = $page->newPageUpdater( $wgUser );
+ $updater->setContent( 'main', $newcontent ); // SlotRecord::MAIN = 'main'
+ $updater->setRcPatrolStatus( 1 ); // RecentChange::PRC_PATROLLED = 1
+ $comment = CommentStoreComment::newUnsavedComment( $edit_summary );
+ $newRev = $updater->saveRevision( $comment, $flags );
+
+ return ( !is_null( $newRev ) && $updater->wasSuccessful() );
+ }
+
+ /**
+ * @param string $titletext
+ * @param int $namespace
+ * @param string $wikitextContent
+ * @param string $summary
+ * @param User $user
+ *
+ * @return MGWStatus
+ */
+ public static function newPage( $titletext, $namespace, $wikitext, $summary, $user ) {
+
+ global $wgNamespaceAliases;
+
+ $title = Title::newFromText( $titletext, $namespace );
+ $article = WikiPage::factory( $title );
+ $content = new WikitextContent( $wikitext );
+ $flags = EDIT_NEW;
+ $status = $article->doEditContent( $content, $summary, $flags, false, $user );
+ if ( !$status->isOK() ) {
+ return Status::newFailed( $status->getMessage() );
+ }
+ else {
+ return Status::newDone( 'La page "' . array_search ( $namespace, $wgNamespaceAliases ) .
+ ":" . $titletext . '" a été créée.', $article->getId() );
+ }
+ }
+
+ /**
+ * @param string $oldTitletext
+ * @param int $oldNamespace
+ * @param string $newTitletext
+ * @param int $newNamespace
+ * @param string $summary
+ * @param User $user
+ *
+ * @return MGWStatus
+ */
+ public static function renamePage( $oldTitletext, $oldNamespace, $newTitleText, $newNamespace, $summary, $user ) {
+
+ $oldTitle = Title::newFromText( $oldTitletext, $oldNamespace );
+ $newTitle = Title::newFromText( $newTitleText, $newNamespace );
+ $movePage = new MovePage( $oldTitle, $newTitle );
+ if ( $movePage->isValidMove() ) {
+ $movePage->move( $user, $summary, $createRedirect = true );
+ return Status::newDone( 'La page a été renommée', $newTitle->getArticleID() );
+ }
+ else {
+ return Status::newFailed( 'Impossible de renomer la page' );
+ }
+ }
+
+ /**
+ * @param int $page_id
+ * @param string $summary
+ * @param User $user
+ *
+ * @return MGWStatus
+ */
+ public static function refreshPage( $page_id, $summary, $user ) {
+
+ $title = Title::newFromID( $page_id );
+ if ( is_null( $title ) ) {
+ return Status::newFailed( 'La page n\'existe pas.', MGW_PAGE_MISSING );
+ }
+ $article = WikiPage::factory( $title );
+ $content = $article->getContent(); // enregistrement sans modification pour actualiser les parsers
+ $flags = EDIT_MINOR;
+ $ret = $article->doEditContent( $content, $summary, $flags, false, $user );
+
+ if ( ! $ret->isOK() ) {
+ return Status::newFailed( 'La page n\'a pas pu être rafraîchie ( ' .
+ $ret->getMessage() .' )' );
+ }
+ else {
+ return Status::newDone( 'La page a été rafraîchie' );
+ }
+ }
+
+ /**
+ * @param int $page_id
+ * @param string $summary
+ *
+ * @return MGWStatus
+ */
+ public static function lightDelete( $page_id, $reason ) {
+
+ $title = Title::newFromID( $page_id );
+ if ( !is_null( $title ) ) {
+ $article = WikiPage::factory( $title );
+ $delete = $article->doDeleteArticleReal( $reason );
+ if ( ! $delete->isOK() ) {
+ return Status::newFailed( 'La page n\'a pas pu être supprimée ( ' .
+ $delete->getMessage() .' )' );
+ }
+ }
+ return Status::newDone( 'La page a été supprimée' );
+ }
+
+
+ /**
+ * @param int $page_id
+ * @param string $summary
+ * @param User $user
+ *
+ * @return MGWStatus
+ */
+ public static function undeletePage( $page_name, $summary, $user ) {
+
+ $title = Title::newFromText( $page_name );
+ if ( !$title ) {
+ return Status::newFailed( "Invalid title" );
+ }
+
+ $archive = new PageArchive( $title, \RequestContext::getMain()->getConfig() );
+ $archive->undeleteAsUser( [], $user, $reason );
+ $page_id = $title->getArticleID();
+
+ if ( $page_id < 1 ) {
+ return Status::newFailed( 'La page n\'a pas pu être restaurée' );
+ }
+ else {
+ return Status::newDone( 'La page a été restaurée', $page_id );
+ }
+ }
+}
diff --git a/includes/Utilities/PhpFunctions.php b/includes/Utilities/PhpFunctions.php
new file mode 100644
index 0000000..4d17e20
--- /dev/null
+++ b/includes/Utilities/PhpFunctions.php
@@ -0,0 +1,181 @@
+ $value ) {
+ if ( $key === $needle ) {
+ return $value;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * recherche récursivement une paire 'clé' => 'valeur'
+ * @return bool
+ */
+ public function recursiveArrayKeyValue ( $key, $value, $array )
+ {
+ $recursive = self::recursiveIterator( $array );
+ foreach ( $recursive as $kkey => $vvalue ) {
+ if ( $key === $kkey && $value === $vvalue ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * recherche récursivement une clé, fusionne le résultat si plusieurs occurences
+ * @return mixed (valeur, array ou array_merge)
+ */
+ public function recursiveArrayKeyMerge ( $needle, $array )
+ {
+ $recursive = self::recursiveIterator( $array );
+ $ret = [];
+ foreach ($recursive as $key => $value) {
+ if ($key === $needle) {
+ $ret[] = $value;
+ }
+ }
+ switch ( sizeof( $ret ) ) {
+
+ case 0:
+ return null;
+ break;
+
+ case 1:
+ return $ret[0];
+ break;
+
+ default:
+ $merge = [];
+ foreach ( $ret as $key => $value ) {
+ if ( is_array( $value ) ) {
+ foreach ( $value as $kkey => $vvalue ) {
+ $merge[$kkey] = $vvalue;
+ }
+ }
+ else {
+ $merge[] = $value;
+ }
+ }
+ return $merge;
+ break;
+ }
+ }
+
+ /**
+ * recherche récursivement une clé, fusionne le résultat si plusieurs occurences
+ * @return mixed (valeur, array ou array_merge)
+ */
+ public function recursiveArrayKeyValueCount ( $key, $value, $array )
+ {
+ $recursive = self::recursiveIterator( $array );
+ $ret = 0;
+ foreach ($recursive as $kkey => $vvalue) {
+ if ( $key === $kkey && $value == $vvalue ) {
+ $ret++;
+ }
+ }
+ return $ret;
+ }
+
+
+ private function recursiveIterator( $array ) {
+ $iterator = new \RecursiveArrayIterator( $array );
+ $recursive = new \RecursiveIteratorIterator(
+ $iterator,
+ \RecursiveIteratorIterator::SELF_FIRST
+ );
+ return $recursive;
+ }
+
+ /**
+ * recherche les doublons dans un tableau
+ * @return mixed array ou null
+ */
+ public function array_doublons( $array ) {
+ if ( !is_array( $array ) ) return false;
+ $r_valeur = Array();
+ $array_unique = array_unique($array);
+
+ if ( count( $array ) - count( $array_unique ) ) {
+ for ( $i=0; $i< count( $array ); $i++ ) {
+ if ( !array_key_exists( $i, $array_unique ) )
+ $r_valeur[] = $array[$i];
+ }
+ }
+ return $r_valeur;
+ }
+
+ /**
+ * sets $var to false if null, empty or undefined
+ * @param mixed &$var
+ */
+ public function false ( &$var ) {
+ if ( !isset( $var ) || is_null( $var ) || empty( $var ) ) {
+ $var = false;
+ }
+ }
+
+ /**
+ * sets $var to null if empty, false or undefined
+ * @param mixed &$var
+ */
+ public function null ( &$var ) {
+ if ( !isset( $var ) || empty( $var ) || !$var ) {
+ $var = null;
+ }
+ }
+
+ /**
+ * sets $var to empty if null, undefined or false
+ * @param mixed &$var
+ */
+ public function empty ( &$var ) {
+ if ( !isset( $var ) || is_null( $var ) || !$var ) {
+ $var = '';
+ }
+ }
+
+ /**
+ * sets $var to empty if null, undefined or false
+ * @param mixed &$var
+ */
+ public function int ( &$var ) {
+ $int = (int)$var;
+ if ( 'test'.$int == 'test'.$var ) {
+ $var = $int;
+ }
+ else {
+ $var = null;
+ }
+ }
+
+ /**
+ * @param mixed &$var
+ * @param bool $decode
+ * @return void
+ */
+ public function html ( &$var, $decode = false ) {
+ if ( $decode ) {
+ $var = htmlspecialchars_decode( $var );
+ }
+ else {
+ $var = htmlspecialchars( $var );
+ }
+ }
+}
diff --git a/includes/Utilities/UsersFunctions.php b/includes/Utilities/UsersFunctions.php
new file mode 100644
index 0000000..570b38f
--- /dev/null
+++ b/includes/Utilities/UsersFunctions.php
@@ -0,0 +1,436 @@
+getId() <= 0 ) return null;
+ else return $user;
+ }
+
+ /**
+ * @param int $id
+ *
+ * @return User object
+ */
+ public function getUserFromId ( $id ) {
+ return User::newFromId ( $id );
+ }
+
+
+ /**
+ * Vérification de l'existence de l'utilisateur dans la base MediaWiki
+ * retourne le nom d'utilisateur s'il existe
+ *
+ * @param string|int $val user_name|user_id
+ * @param bool $case_sensitive
+ * @return string|bool user_name|false
+ */
+ public function userExists( $val, $case_sensitive = false ) {
+
+ if ( is_int( $val ) || preg_match( '/^[0-9]+$/', $val ) > 0 ) {
+ if ( !is_int( $val ) ) $val = (int)$val;
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbr = $lb->getConnectionRef( DB_REPLICA );
+ $res = $dbr->select( 'user', [ 'user_name' ], 'user_id = ' . $val );
+ if ( $res->numRows() > 0 ) {
+ $row = $res->fetchRow();
+ return $row['user_name'];
+ }
+ return false;
+ }
+
+ if ( $case_sensitive ) {
+ $user = User::newFromName ( $val );
+ if ( $user->getId() > 0 ) return $val;
+ else return false;
+ }
+
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbr = $lb->getConnectionRef( DB_REPLICA );
+ $res = $dbr->select( 'user', [ 'user_name' ] );
+ if ( $res->numRows() > 0 ) {
+ foreach ( $res as $row ) {
+ if ( strtolower( $val ) == strtolower( $row->user_name ) )
+ return $row->user_name;
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Vérification de l'existence de l'utilisateur dans les tables mgw
+ * retourne 'Prénom NOM' s'il existe
+ *
+ * @param array|string|int $val nom,prenom|user_name|user_id
+ * @param bool $case_sensitive
+ * @return string|bool user_name|false
+ */
+ public function mgwUserExists( $mode, $val, $case_sensitive = false ) {
+
+ switch ( $mode ) {
+ case 'user_id':
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbr = $lb->getConnectionRef( DB_REPLICA );
+ $res = $dbr->select( 'mgw_utilisateur', [ 'utilisateur_id' ], 'utilisateur_user_id = ' . $val );
+ if ( $res->numRows() > 0 ) {
+ return true;
+ }
+ return false;
+ break;
+
+ default :
+ return false;
+ break;
+ }
+ }
+
+
+ /**
+ * Vérification de l'existence du mail
+ *
+ * @param string $email
+ * @return string|bool $user_name|false
+ */
+ public function emailExists( $email ) {
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbw = $lb->getConnectionRef( DB_MASTER );
+ $res = $dbw->select( 'user', ['user_name', 'user_email'] );
+ foreach ( $res as $row ) {
+ if ( $row->user_email == $email ) {
+ return $row->user_name ;
+ }
+ }
+ return false;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // FONCTIONS A SUPPRIMER (-> MgwDataFonctions )
+ // ...
+
+ /**
+ * Cas d'usage: demande de suppression de compte.
+ * $fullDeletion = true implique un effaçement complet de l'utilisateur
+ * y compris dans les tables d'archive.
+ *
+ * @param int $user_id
+ * @param bool $fullDeletion
+ * @param int updater_id
+ * @return array [ 'done' => bool, 'message' => string ]
+ */
+ public function deleteMGWUser( $user_id, $fullDeletion = false, $updater_id = null ) {
+
+ if ( is_null($updater_id) ) {
+ global $wgUser;
+ $updater_id = $wgUser->getId();
+ }
+
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbw = $lb->getConnectionRef( DB_MASTER );
+ $res = $dbw->select(
+ 'mgw_utilisateur',
+ [
+ 'utilisateur_user_id'
+ ],
+ 'utilisateur_user_id = ' . $user_id
+ );
+
+ if ( sizeof( $res < 1 ) ) {
+ return [
+ 'done' => false,
+ 'message' => 'Suppression impossible: l\'utilisateur ' . $user_id .
+ ' n\'existe pas dans la table utilisateurs de mgwiki.'
+ ];
+ }
+ elseif ( sizeof( $res > 1 ) ) {
+ return [
+ 'done' => false,
+ 'message' => 'Suppression impossible: : l\'utilisateur ' . $user_id . ' figure ' .
+ sizeof( $res ) . ' fois dans la table mgw_utilisateur.
Veuillez contacter le responsable technique.'
+ ];
+ }
+
+ # suppression simple: on archive avec les champs
+ # utilisateur_drop_updater_id et utilisateur_drop_time renseignés
+ if ( !$fullDeletion ) {
+ $archive = self::archiveMGWUser(
+ $res[0]->utilisateur_id,
+ $res[0]->utilisateur_user_id,
+ $res[0]->utilisateur_nom,
+ $res[0]->utilisateur_prenom,
+ $res[0]->utilisateur_level,
+ $res[0]->utilisateur_updater_id,
+ $res[0]->utilisateur_update_time,
+ $updater_id,
+ date('Y-m-d H:i:s')
+ );
+ }
+ # suppression complète: on vide les archives de toutes les lignes mentionnant l'utilisateur
+ else $archive = self::dropUserFromArchive( $res[0]->utilisateur_id, $user_id, $updater_id );
+
+ $drop = self::dropMGWUser( $user_id );
+
+ $username = $res[0]->utilisateur_prenom . ' ' . $res[0]->utilisateur_nom;
+
+ if ( is_bool( $drop ) && is_bool( $archive ) && !$fullDeletion ) {
+ $done = $drop;
+ $message = $username . 'a été retiré des utilisateurs. Les archives n\'ont pas été effaçées.';
+ }
+ elseif ( is_bool( $drop ) && is_bool( $archive ) && $fullDeletion ) {
+ $done = $drop;
+ $message = $username . 'a été retiré des utilisateurs ainsi que des archives.';
+ }
+ else {
+ $done = false;
+ $message = 'Erreur à la suppression de ' . $username . ' :';
+ if ( !is_bool( $archive ) ) $message .= '
' . $archive;
+ if ( !is_bool( $update ) ) $message .= '
' . $archive;
+ }
+ return [ 'done' => $done, 'message' => $message ];
+ }
+
+ /**
+ * @param int $id
+ * @param string $user_nom
+ * @param string $user_prenom
+ * @param int $updater_id
+ * @return MGWStatus
+ */
+ public static function insertMGWUser( $user_id, $user_nom, $user_prenom, $updater_id ) {
+ try {
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbw = $lb->getConnectionRef( DB_MASTER );
+ $dbw->insert(
+ 'mgw_utilisateur',
+ [
+ 'utilisateur_user_id' => $user_id,
+ 'utilisateur_nom' => $user_nom,
+ 'utilisateur_prenom' => $user_prenom,
+ 'utilisateur_updater_id' => $updater_id,
+ 'utilisateur_update_time' => $dbw->timestamp( date("Y-m-d H:i:s") )
+ ]
+ );
+ } catch (\Exception $e) {
+ return Status::newFailed( $e );
+ }
+ return Status::newDone( $user_prenom . ' ' . $user_nom . ' a été ajouté à la table mgw_utilisateur.');
+ }
+
+ /**
+ * @param int $id
+ * @param array $data
+ * @return bool
+ */
+ public static function updateMGWUser( $user_id, $user_nom, $user_prenom, $updater_id ) {
+ try {
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbw = $lb->getConnectionRef( DB_MASTER );
+
+ $res = $dbw->select(
+ 'mgw_utilisateur',
+ [
+ '*'
+ ],
+ 'utilisateur_user_id = ' . $user_id
+ );
+
+ if ( $res->result->num_rows < 1 ) {
+ return Status::newFailed( 'Utilisateur inconnu dans la table mgw_utilisateur');
+ }
+
+ $row = $res->fetchRow();
+
+ if ( $row['utilisateur_nom'] == $user_nom && $row['utilisateur_prenom'] == $user_prenom ) {
+ return Status::newFailed( 'Table mgw_utilisateur déjà à jour.');
+ }
+
+ $archive = self::archiveMGWUser(
+ $row['utilisateur_id'],
+ $row['utilisateur_user_id'],
+ $row['utilisateur_nom'],
+ $row['utilisateur_prenom'],
+ $row['utilisateur_level'],
+ $row['utilisateur_updater_id'],
+ $row['utilisateur_update_time']
+ );
+
+ if ( ! $archive->done() ) {
+ return $archive;
+ }
+
+ $dbw->update(
+ 'mgw_utilisateur',
+ [
+ 'utilisateur_nom' => $user_nom,
+ 'utilisateur_prenom' => $user_prenom,
+ 'utilisateur_updater_id' => $updater_id,
+ 'utilisateur_update_time' => $dbw->timestamp( date("Y-m-d H:i:s") )
+ ],
+ 'utilisateur_user_id = ' . $user_id
+ );
+ } catch (\Exception $e) {
+ return Status::newFailed( $e );
+ }
+ return Status::newDone( $user_prenom . ' ' . $user_nom . ' a été mis à jour dans la table mgw_utilisateur.');
+ }
+
+
+ /**
+ * @param int $id
+ * @param string $user_nom
+ * @param string $user_prenom
+ * @param int $updater_id
+ * @return bool|string
+ */
+ public static function archiveMGWUser(
+ $utilisateur_id,
+ $utilisateur_user_id,
+ $utilisateur_nom,
+ $utilisateur_prenom,
+ $utilisateur_level,
+ $utilisateur_updater_id,
+ $utilisateur_update_time,
+ $utilisateur_drop_updater_id = null
+ ) {
+ try {
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbw = $lb->getConnectionRef( DB_MASTER );
+
+ if ( !is_null( $utilisateur_drop_updater_id ) ) {
+ $utilisateur_drop_time = $dbw->timestamp( date("Y-m-d H:i:s") );
+ } else {
+ $utilisateur_drop_time = null;
+ }
+ $dbw->insert(
+ 'mgw_archive_utilisateurs',
+ [
+ 'utilisateur_id' => $utilisateur_id,
+ 'utilisateur_user_id' => $utilisateur_user_id,
+ 'utilisateur_nom' => $utilisateur_nom,
+ 'utilisateur_prenom' => $utilisateur_prenom,
+ 'utilisateur_level' => $utilisateur_level,
+ 'utilisateur_updater_id' => $utilisateur_updater_id,
+ 'utilisateur_update_time' => $utilisateur_update_time,
+ 'utilisateur_drop_updater_id' => $utilisateur_drop_updater_id,
+ 'utilisateur_drop_time' => $utilisateur_drop_time
+ ]
+ );
+ } catch (\Exception $e) {
+ return Status::newFailed( $e );
+ }
+ return Status::newDone( 'Archive: ok' );
+ }
+
+
+ /**
+ * @param int $id
+ * @return bool
+ */
+ public static function dropMGWUser( $user_id, $updater_id ) {
+ try {
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbw = $lb->getConnectionRef( DB_MASTER );
+
+ $res = $dbw->select(
+ 'mgw_utilisateur',
+ [
+ '*'
+ ],
+ 'utilisateur_user_id = ' . $user_id
+ );
+
+ if ( $res->result->num_rows < 1 ) {
+ return Status::newFailed( 'Utilisateur inconnu dans la table mgw_utilisateur');
+ }
+
+ $row = $res->fetchRow();
+ $archive = self::archiveMGWUser(
+ $row['utilisateur_id'],
+ $row['utilisateur_user_id'],
+ $row['utilisateur_nom'],
+ $row['utilisateur_prenom'],
+ $row['utilisateur_level'],
+ $row['utilisateur_updater_id'],
+ $row['utilisateur_update_time'],
+ $updater_id
+ );
+
+ if ( ! $archive->done() ) {
+ return $archive;
+ }
+
+ $dbw->delete(
+ 'mgw_utilisateur',
+ 'utilisateur_user_id = ' . $user_id
+ );
+ } catch (\Exception $e) {
+ return Status::newFailed( $e );
+ }
+ return Status::newDone( 'l\'utilisateur' . $user_id . ' a été supprimé.' );
+ }
+
+ /**
+ * Suppression complète: on ne laisse que la trace de la suppression elle-même.
+ *
+ * @param int $utilisateur_id,
+ * @param int $utilisateur_user_id,
+ * @param int $utilisateur_updater_id
+ * @return bool
+ */
+ private function dropUserFromArchive( $utilisateur_id, $utilisateur_user_id, $utilisateur_updater_id ) {
+ try {
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $dbw = $lb->getConnectionRef( DB_MASTER );
+ $dbw->delete(
+ 'mgw_archive_utilisateurs',
+ 'utilisateur_user_id = ' . $utilisateur_user_id
+ );
+ $dbw->insert(
+ 'mgw_archive_utilisateurs',
+ [
+ 'utilisateur_id' => $utilisateur_id,
+ 'utilisateur_user_id' => $utilisateur_user_id,
+ 'utilisateur_nom' => 'compte supprimé',
+ 'utilisateur_prenom' => 'comte supprimé',
+ 'utilisateur_level' => null,
+ 'utilisateur_updater_id' => $utilisateur_updater_id,
+ 'utilisateur_update_time' => date('Y-m-d H:i:s'),
+ 'utilisateur_drop_updater_id' => $utilisateur_updater_id,
+ 'utilisateur_drop_time' => date('Y-m-d H:i:s')
+ ]
+ );
+ } catch (\Exception $e) {
+ return $e;
+ }
+ return true;
+ }
+
+ /**
+ * @param int $id
+ * @return int
+ */
+ public function getLevel( $id ) {
+ }
+}
diff --git a/maintenance/CheckHooks.php b/maintenance/CheckHooks.php
new file mode 100644
index 0000000..729a5d1
--- /dev/null
+++ b/maintenance/CheckHooks.php
@@ -0,0 +1,91 @@
+ [
+ "fileIdentifier" => 'class ApiMain extends ApiBase', // chaîne de caractères unique permettant d'identifier le fichier (ici: includes/api/ApiMain.php)
+ "stringIdentifier" => '!$user->isAllowed( \'read\' )', // chaîne de caractères unique permettant d'identifier le lieu d'insertion (ici : l.1419)
+ "customCode" => '&& !Hooks::run( \'ApiAllow\', [ $module, $user ] )' // code à insérer après stringIdentifier
+ ]
+];
+
+function checkHooks( $customHooks )
+{
+ $info = json_decode(file_get_contents( getPath('extension') . '/extension.json' ), true );
+ $endL = '
+';
+ $report = fopen('maintenance-report.txt', 'a');
+ fputs($report, $endL . '
+--------------------------------
+CheckHooks - ' . date('Y-m-d H:i:s') . '
+--------------------------------' . $endL );
+
+ foreach ($info['Hooks'] as $key => $value) {
+ $shell = ' && stdbuf -oL grep -r -n -E "Hooks::run(WithoutAbort)?\( \'' . $key . '\'" | head -n1';
+ $grep = shell_exec( 'cd ' . getPath('base') . '/includes' . $shell );
+ if ( is_null( $grep ) ) { $grep = shell_exec( 'cd ' . getPath('base') . '/maintenance' . $shell ); }
+ if ( is_null( $grep ) ) { $grep = shell_exec( 'cd ' . getPath('base') . '/skins' . $shell ); }
+ if ( is_null( $grep ) ) { $grep = shell_exec( 'cd ' . getPath('base') . '/extensions' . $shell ); }
+ if ( is_null( $grep ) ) {
+ if ( array_key_exists( $key, $customHooks ) ) {
+ $add = addHook( $customHooks[$key] );
+ fputs($report, $key . $add . $endL);
+ echo $key . $add . $endL;
+ }
+ else {
+ fputs($report, $key . ' : ECHEC' . $endL);
+ echo $key . ' : ECHEC' . $endL;
+ }
+ }
+ else {
+ fputs($report, $key . ' : OK' . $endL);
+ echo $key . ' : OK' . $endL ;
+ }
+ }
+ echo $endL . '
+ ==================================
+ => voir: maintenance-report.txt <=
+ ==================================
+ ';
+ fclose($report);
+}
+
+# insère le hook customisé dans MediaWiki-core
+# @param array $hook
+# @return bool
+function addHook( $hook )
+{
+ $grep = shell_exec( 'cd ' . getPath('base') . ' && grep -r -n -E "' . $hook[ 'fileIdentifier' ] . '"' );
+ if ( is_null( $grep ) ) {
+ $mess = ' : ECHEC (hook customisé) : occurence "' . $hook[ 'fileIdentifier' ] . '" introuvable dans les fichiers';
+ }
+ else {
+ $grep = explode( ':', $grep );
+ $file = getPath( 'base' ) . '/' . $grep[0];
+ $code = file_get_contents( $file );
+ $ret = substr_count($code, $hook['stringIdentifier']);
+ switch ( $ret ) {
+ case 0:
+ $mess = ' : ECHEC (MGW-custom) : '. $file .' : "' . $hook[ 'stringIdentifier' ] . '" introuvable';
+ break;
+ case 1: // ajout du code
+ $code = str_replace( $hook['stringIdentifier'], $hook['stringIdentifier'] . ' ' . $hook['customCode'], $code );
+ file_put_contents( $file, $code);
+ $mess = ' : OK (customized)';
+ break;
+ default:
+ $mess = ' : ECHEC (MGW-custom) : '. $file .' : "' . $hook[ 'stringIdentifier' ] . '" se trouve plusieurs fois.
+ "' . $hook['customCode'] . '" doit être ajouté manuellement.';
+ break;
+ }
+ }
+ return $mess;
+}
+
+checkHooks( $customHooks );
+?>
diff --git a/maintenance/MediawikiUpdate.php b/maintenance/MediawikiUpdate.php
new file mode 100644
index 0000000..e24302a
--- /dev/null
+++ b/maintenance/MediawikiUpdate.php
@@ -0,0 +1,53 @@
+ [
+ "fileIdentifier" => 'class ApiMain extends ApiBase', // chaîne de caractères unique permettant d'identifier le fichier (ici: includes/api/ApiMain.php)
+ "stringIdentifier" => '!$user->isAllowed( \'read\' )', // chaîne de caractères unique permettant d'identifier le lieu d'insertion (ici : l.1419)
+ "customCode" => '&& !Hooks::run( \'ApiAllow\', [ $module, $user ] )' // code à insérer après stringIdentifier
+ ]
+];
+
+/**
+ * Liste des versions de MediaWiki conçernées par cette màj
+ */
+$mw_releases = array(
+ '34' => '1',
+ '35' => '1');
+
+/**
+ * déclaration des chaînes de caractères à rechercher dans les releases notes
+ */
+$searchInCode = array(
+ [
+ 'type' => 'classe',
+ 'regexp' => '/^use ([a-zA-Z]+\\\)*([a-zA-Z]+)[ ]?;/',
+ 'match' => 2 ],
+ [
+ 'type' => 'classe',
+ 'regexp' => '/extends (\\\)?([a-zA-Z]+) /',
+ 'match' => 2 ],
+ [
+ 'type' => 'skin',
+ 'regexp' => '/wfLoadSkin\( \'([a-zA-Z]+)\' \)/',
+ 'match' => 1 ],
+ [
+ 'type' => 'extension',
+ 'regexp' => '/wfLoadExtension\( \'([a-zA-Z]+)\' \)/',
+ 'match' => 1 ],
+ [
+ 'type' => 'variable globale',
+ 'regexp' => '/wg[a-zA-Z]+/',
+ 'match' => 0 ]
+);
+
+include ('Tools.php');
+$includeTools = true;
+
+include ('CheckHooks.php');
+include ('ScreenReleaseNotes.php');
+
+?>
diff --git a/maintenance/ScreenReleaseNotes.php b/maintenance/ScreenReleaseNotes.php
new file mode 100644
index 0000000..d028e93
--- /dev/null
+++ b/maintenance/ScreenReleaseNotes.php
@@ -0,0 +1,278 @@
+ '1',
+ '35' => '1');
+
+if (!isset( $searchInCode ) ) $searchInCode = array(
+ [
+ 'type' => 'classe',
+ 'regexp' => '/^use ([a-zA-Z]+\\\)*([a-zA-Z]+)[ ]?;/',
+ 'match' => 2 ],
+ [
+ 'type' => 'classe',
+ 'regexp' => '/extends (\\\)?([a-zA-Z]+) /',
+ 'match' => 2 ],
+ [
+ 'type' => 'skin',
+ 'regexp' => '/wfLoadSkin\( \'([a-zA-Z]+)\' \)/',
+ 'match' => 1 ],
+ [
+ 'type' => 'extension',
+ 'regexp' => '/wfLoadExtension\( \'([a-zA-Z]+)\' \)/',
+ 'match' => 1 ],
+ [
+ 'type' => 'variable globale',
+ 'regexp' => '/wg[a-zA-Z]+/',
+ 'match' => 0 ]
+);
+
+/**
+ * Recherche et extraction des commentaires de versions
+ */
+function extractReleaseNotes ( $relN, $relV ) {
+ $file = "RELEASE-NOTES-$relN.$relV.txt";
+
+ $ofile = fopen( $file, 'a');
+ $url = 'https://phabricator.wikimedia.org/source/mediawiki/browse/REL'.$relN.'_'.$relV.'/RELEASE-NOTES-'.$relN.'.'.$relV;
+ $curl = "
+";
+ $curl = shell_exec( "curl $url" );
+ fputs($ofile, $curl);
+ fclose($ofile);
+ // lecture du fichier html
+ $ofile = fopen($file, 'r');
+ $out = '';
+ $endL = '
+';
+ while(! feof( $ofile ) ) {
+ $line = fgets( $ofile );
+ preg_match('/^.*(.*)/', $line, $matchL);
+ preg_match( '//', $line, $matchN );
+ if ( isset($matchL[1]) ){
+ $out .= htmlspecialchars_decode($matchL[1],ENT_QUOTES) . '@ligne@' . $matchN[1] . $endL;
+ }
+ }
+ fclose($ofile);
+
+ // réécriture sans les balises html
+ $ofile = fopen($file, 'w');
+ fwrite ( $ofile , $out) ;
+ fclose ( $ofile ) ;
+
+ // lecture des données
+ $ofile = fopen($file, 'r');
+ $data = [];
+ $h1 = '';
+ $h2 = '';
+ $h3 = '';
+ $h4 = '';
+ $l = '1';
+ $text = null;
+ $plain = false;
+ while(! feof( $ofile ) ) {
+ $line = fgets( $ofile );
+ $line = explode( '@ligne@', $line );
+ if ( isset( $line[1] ) ) {
+ preg_match('/[0-9]+/', $line[1], $matches);
+ $line[1] = $matches[0];
+ }
+ $done = false;
+ $title = false;
+ $screen = preg_match('/^(= )(.*)( =)/', $line[0], $matches);
+ if ( isset($screen) && $screen > 0 ) {
+ $new_h1 = $matches[2];
+ $new_h2 = '';
+ $new_h3 = '';
+ $new_h4 = '';
+ $new_l = $line[1];
+ $done = true;
+ $title = true;
+ }
+ $screen = preg_match('/^(== )(.*)( ==)/', $line[0], $matches);
+ if ( isset($screen) && $screen > 0 ) {
+ $new_h2 = $matches[2];
+ $new_h3 = '';
+ $new_h4 = '';
+ $new_l = $line[1];
+ $done = true;
+ $title = true;
+ }
+ $screen = preg_match('/^(=== )(.*)( ===)/', $line[0], $matches);
+ if ( isset($screen) && $screen > 0 ) {
+ $new_h3 = $matches[2];
+ $new_h4 = '';
+ $new_l = $line[1];
+ $done = true;
+ $title = true;
+ }
+ $screen = preg_match('/^(==== )(.*)( ====)/', $line[0], $matches);
+ if ( isset($screen) && $screen > 0 ) {
+ $new_h4 = $matches[2];
+ $new_l = $line[1];
+ $done = true;
+ $title = true;
+ }
+ $screen = preg_match('/^[a-zA-Z0-9]/', $line[0], $matches);
+ if ( isset($screen) && $screen < 1 ) {
+ $screen2 = preg_match('/^\*/', $line[0]);
+ if ( isset($screen2) && $screen2 > 0 ) {
+ $done = true;
+ $new_l = $line[1];
+ }
+ } else $plain = true;
+
+ if ($title) $plain = false;
+ $empty = ["","
+"];
+ if ( $done && !$plain && !is_null($text) && !in_array($text,$empty) ) {
+ $data[] = [
+ 'h1' => $h1,
+ 'h2' => $h2,
+ 'h3' => $h3,
+ 'h4' => $h4,
+ 'l' => $l,
+ 'url' => $url,
+ 'text' => $text
+ ];
+ if ( isset( $new_h1 ) ) $h1 = $new_h1;
+ if ( isset( $new_h2 ) ) $h2 = $new_h2;
+ if ( isset( $new_h3 ) ) $h3 = $new_h3;
+ if ( isset( $new_h4 ) ) $h4 = $new_h4;
+ if ( isset( $new_l ) ) $l = $new_l;
+ $text = null;
+ }
+
+ if ( !$title ) {
+ if ( is_null( $text ) ) $text = $line[0];
+ else $text .= $endL . $line[0];
+ }
+ }
+ fclose( $ofile );
+ unlink( $file );
+ return( $data );
+}
+
+/**
+ * Recherche et extraction des classes, skins et $wg dans le code MGWiki ...
+ */
+function screenCode ( $searchInCode, $self ) {
+ $return = [];
+ $endL = '
+';
+ $mgw_phpfiles = listDir( getPath( 'extension' ), 'php' ); // recherche dans tous les fichiers .php de l'extension
+ $mgw_phpfiles[] = getPath( 'base' ) . '/LocalSettings.php'; // on ajoute LocalSettings
+ foreach ( $mgw_phpfiles as $key => $file ) {
+ $ofile = fopen($file, 'r');
+ while(! feof( $ofile ) ) {
+ $line = fgets( $ofile );
+ $done = false;
+ foreach( $searchInCode as $num => $exp ) {
+ preg_match( $exp['regexp'], $line, $matches );
+ $m = preg_match('/'.$self.'/', $line );
+ $n = $exp['match'];
+ if ( isset( $matches[ $n ] ) && $m < 1 ){
+ if ( sizeof( $return ) > 0 ) {
+ foreach ( $return as $key => $value ) {
+ if ( $return[$key]['string'] == $matches[ $n ] && in_array( $file, $return[$key]['files'])) {
+ $done = true;
+ }
+ if ( $return[$key]['string'] == $matches[ $n ] && !in_array( $file, $return[$key]['files'])) {
+ $return[$key]['files'][] = $file;
+ $done = true;
+ }
+ }
+ }
+ if ( !$done ) {
+ $return[] = [
+ 'string' => $matches[ $n ],
+ 'files' => [ $file ],
+ 'type' => $exp['type']
+ ];
+ }
+ }
+ }
+ }
+ fclose($ofile);
+ }
+ return $return;
+}
+
+// import des releases notes au format html
+$releaseNotes = [];
+foreach ( $mw_releases as $relV => $relN ) {
+ $data = extractReleaseNotes( $relN, $relV );
+ $releaseNotes = array_merge( $releaseNotes, $data );
+}
+
+// recherche des classes utilisées dans les releases notes
+$includedClasses = screenCode( $searchInCode, $self );
+$endL = '
+';
+$out = '
+----------------------------------------
+ScreenReleaseNotes - ' . date('Y-m-d H:i:s') . '
+----------------------------------------
+Les modifications suivantes peuvent impacter le fonctionnement de MGWiki.
+Sources :
+';
+foreach ( $mw_releases as $relV => $relN ) {
+ $out .= ' https://phabricator.wikimedia.org/source/mediawiki/browse/REL'.$relN.'_'.$relV.'/RELEASE-NOTES-'.$relN.'.'.$relV.$endL;
+}
+
+try
+{
+ $out1 = null;
+ foreach ( $includedClasses as $classKey => $classArray ) {
+ $out1 = $endL . ' -- ' . $classArray['string'] . ' ('. $classArray['type'] .') --' . $endL;
+ $out1 .= ' ' . implode( $endL . ' ', $classArray['files'] ) . $endL . $endL;
+ $out2 = null;
+ $url = null;
+ foreach ( $releaseNotes as $noteKey => $noteArray ) {
+ if ( preg_match( '/'.$classArray['string'].'/', $noteArray['text'] ) > 0 ) {
+ if ( is_null( $out2 ) ) $out2 = '';
+ if ( $noteArray['url'] != $url ) {
+ $url = $noteArray['url'];
+ $out2 .= ' ' . strtoupper( $noteArray['h1'] ) . $endL . $endL;
+ //$out2 .= $url . $endL . $endL;
+ }
+ if ( $noteArray['h2'] != '' ) $chapter[] = $noteArray['h2'];
+ if ( $noteArray['h3'] != '' ) $chapter[] = $noteArray['h3'];
+ if ( $noteArray['h4'] != '' ) $chapter[] = $noteArray['h4'];
+ if ( isset( $chapter ) ) {
+ $out2 .= '(l.'. $noteArray['l'] .') >> ' . implode( ' > ', $chapter) . ' :' . $endL;
+ $out2 .= $noteArray['text'] . $endL . $endL;
+ unset($chapter);
+ }
+ }
+ }
+ if ( !is_null( $out2 ) ) $out1 .= $out2;
+ else $out1 = null;
+ if ( !is_null( $out1 ) ) {
+ $out .= $out1;
+ $out1 = null;
+ }
+ }
+} catch (\Exception $e) {
+ $out = $e;
+}
+
+$report = fopen('maintenance-report.txt', 'a');
+fputs($report, $endL . $out);
+fclose($report);
+echo $out . $endL . '
+==================================
+=> voir: maintenance-report.txt <=
+==================================
+';
+?>
diff --git a/maintenance/Tests/bacasable.php b/maintenance/Tests/bacasable.php
new file mode 100644
index 0000000..ea5196e
--- /dev/null
+++ b/maintenance/Tests/bacasable.php
@@ -0,0 +1,4 @@
+
diff --git a/maintenance/UpdateSQLfiles.php b/maintenance/UpdateSQLfiles.php
new file mode 100644
index 0000000..6f4798a
--- /dev/null
+++ b/maintenance/UpdateSQLfiles.php
@@ -0,0 +1,35 @@
+ 0 ) {
+ $file = '../sql/addTable-' . $matches[2] . '.sql';
+ file_put_contents( $file, $matches[1] );
+ $tables = $matches[3];
+ }
+ else break;
+}
+
+$indexes = $main;
+while ( $continue ) {
+ $test = preg_match( '/(CREATE INDEX \/\*i\*\/([a-z_]+) [^;]+;)(.*)/', $indexes, $matches );
+ if ( $test > 0 ) {
+ $file = '../sql/addIndex-' . $matches[2] . '.sql';
+ file_put_contents( $file, $matches[1] );
+ $indexes = $matches[3];
+ }
+ else break;
+}
+
+echo 'OK
+';
diff --git a/mgwiki.sql b/mgwiki.sql
new file mode 100644
index 0000000..0a860ac
--- /dev/null
+++ b/mgwiki.sql
@@ -0,0 +1,153 @@
+-- Database schema for MGWiki (only for MYSQL database)
+--
+-- en cas de modif: màj des fichiers sql/*.sql avec la commande 'php UpdateSQLfiles.php'
+-- ! pas de commentaires dans les lignes de commande
+-- booléens traités en int: 1|0
+
+CREATE TABLE IF NOT EXISTS /*_*/mgw_utilisateur (
+ utilisateur_id int unsigned not null auto_increment,
+ utilisateur_user_id int not null,
+ utilisateur_nom varchar(64) not null,
+ utilisateur_prenom varchar(64) not null,
+ utilisateur_level smallint default 0,
+ utilisateur_update_time varbinary(14) not null,
+ utilisateur_updater_id int not null,
+ PRIMARY KEY (utilisateur_id)
+) /*$wgDBTableOptions*/;
+
+CREATE INDEX /*i*/mgw_utilisateur_lookup ON /*_*/mgw_utilisateur (utilisateur_user_id, utilisateur_nom, utilisateur_prenom, utilisateur_level);
+
+CREATE TABLE IF NOT EXISTS /*_*/mgw_utilisateur_archive (
+ utilisateur_archive_id int unsigned not null auto_increment,
+ utilisateur_id int unsigned not null,
+ utilisateur_user_id int not null,
+ utilisateur_nom varchar(64) not null,
+ utilisateur_prenom varchar(64) not null,
+ utilisateur_level smallint not null,
+ utilisateur_update_time varbinary(14) not null,
+ utilisateur_updater_id int not null,
+ utilisateur_drop_time varbinary(14) default null,
+ utilisateur_drop_updater_id int default null,
+ PRIMARY KEY (utilisateur_archive_id)
+) /*$wgDBTableOptions*/;
+
+CREATE INDEX /*i*/mgw_utilisateur_archive_lookup ON /*_*/mgw_utilisateur_archive (utilisateur_user_id, utilisateur_nom, utilisateur_prenom, utilisateur_level);
+
+CREATE TABLE IF NOT EXISTS /*_*/mgw_institution (
+ institution_id int unsigned auto_increment not null,
+ institution_page_id int not null,
+ institution_nom varchar(64) not null,
+ institution_update_time varbinary(14) not null,
+ institution_updater_id int not null,
+ PRIMARY KEY (institution_id)
+) /*$wgDBTableOptions*/;
+
+CREATE TABLE IF NOT EXISTS /*_*/mgw_institution_archive (
+ institution_archive_id int unsigned not null auto_increment,
+ institution_id int unsigned not null,
+ institution_page_id int not null,
+ institution_nom varchar(64) not null,
+ institution_update_time varbinary(14) not null,
+ institution_updater_id int not null,
+ institution_drop_time varbinary(14) default null,
+ institution_drop_updater_id int default null,
+ PRIMARY KEY (institution_archive_id)
+) /*$wgDBTableOptions*/;
+
+CREATE TABLE IF NOT EXISTS /*_*/mgw_groupe (
+ groupe_id int unsigned auto_increment not null,
+ groupe_institution_id int unsigned not null,
+ groupe_page_id int,
+ groupe_frame_id int unsigned,
+ groupe_start_time varbinary(14),
+ groupe_end_time varbinary(14),
+ groupe_actif smallint default 1,
+ groupe_updater_id int not null,
+ groupe_update_time varbinary(14) not null,
+ PRIMARY KEY (groupe_id)
+) /*$wgDBTableOptions*/;
+
+CREATE INDEX /*i*/mgw_groupe_lookup ON /*_*/mgw_groupe (groupe_institution_id, groupe_page_id, groupe_frame_id, groupe_actif);
+
+ -- pas de cas d'usage drop pour les groupes
+CREATE TABLE IF NOT EXISTS /*_*/mgw_groupe_archive (
+ groupe_archive_id int unsigned not null auto_increment,
+ groupe_id int unsigned not null,
+ groupe_institution_id int unsigned not null,
+ groupe_page_id int,
+ groupe_frame_id int unsigned,
+ groupe_start_time varbinary(14),
+ groupe_end_time varbinary(14),
+ groupe_actif smallint,
+ groupe_update_time varbinary(14) not null,
+ groupe_updater_id int not null,
+ PRIMARY KEY (groupe_archive_id)
+) /*$wgDBTableOptions*/;
+
+CREATE INDEX /*i*/mgw_groupe_archive_lookup ON /*_*/mgw_groupe_archive (groupe_institution_id, groupe_page_id, groupe_frame_id, groupe_actif);
+
+CREATE TABLE IF NOT EXISTS /*_*/mgw_membre (
+ membre_id int unsigned auto_increment not null,
+ membre_groupe_id int unsigned not null,
+ membre_user_id int not null,
+ membre_isadmin smallint default 0,
+ membre_update_time varbinary(14) not null,
+ membre_updater_id int not null,
+ PRIMARY KEY (membre_id)
+) /*$wgDBTableOptions*/;
+
+CREATE INDEX /*i*/mgw_membre_lookup ON /*_*/mgw_membre (membre_groupe_id, membre_user_id, membre_isadmin);
+
+CREATE TABLE IF NOT EXISTS /*_*/mgw_membre_archive (
+ membre_archive_id int unsigned not null auto_increment,
+ membre_id int unsigned not null,
+ membre_groupe_id int unsigned not null,
+ membre_user_id int not null,
+ membre_isadmin smallint,
+ membre_update_time varbinary(14) not null,
+ membre_updater_id int not null,
+ membre_drop_time varbinary(14) default null,
+ membre_drop_updater_id int default null,
+ PRIMARY KEY (membre_archive_id)
+) /*$wgDBTableOptions*/;
+
+CREATE INDEX /*i*/mgw_membre_archive_lookup ON /*_*/mgw_membre_archive (membre_groupe_id, membre_user_id, membre_isadmin);
+
+CREATE TABLE IF NOT EXISTS /*_*/mgw_frame (
+ frame_id int unsigned auto_increment not null,
+ frame_nom varchar(64) not null,
+ frame_page_id int,
+ frame_admin_level smallint not null,
+ frame_user_level smallint not null,
+ frame_default_duration int default null,
+ frame_update_time varbinary(14) not null,
+ frame_updater_id int not null,
+ PRIMARY KEY (frame_id)
+) /*$wgDBTableOptions*/;
+
+CREATE INDEX /*i*/mgw_frame_lookup ON /*_*/mgw_frame (frame_nom, frame_page_id, frame_user_level, frame_admin_level);
+
+CREATE TABLE IF NOT EXISTS /*_*/mgw_frame_archive (
+ frame_archive_id int unsigned not null auto_increment,
+ frame_id int unsigned not null,
+ frame_nom varchar(64) not null,
+ frame_page_id int,
+ frame_admin_level smallint not null,
+ frame_user_level smallint not null,
+ frame_default_duration int,
+ frame_update_time varbinary(14) not null,
+ frame_updater_id int not null,
+ frame_drop_time varbinary(14) default null,
+ frame_drop_updater_id int default null,
+ PRIMARY KEY (frame_archive_id)
+) /*$wgDBTableOptions*/;
+
+CREATE INDEX /*i*/mgw_frame_archive_lookup ON /*_*/mgw_frame_archive (frame_nom, frame_page_id, frame_user_level, frame_admin_level);
+
+-- table ne nécessitant pas d'archive
+CREATE TABLE IF NOT EXISTS /*_*/mgw_instit_allow (
+ instit_allow_id int unsigned not null auto_increment,
+ instit_allow_institution_id int unsigned not null,
+ instit_allow_frame_id int unsigned not null,
+ PRIMARY KEY (institution_groupe_id)
+) /*$wgDBTableOptions*/;
diff --git a/phpcs.xml b/phpcs.xml
deleted file mode 100644
index d81a292..0000000
--- a/phpcs.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
- .
-
-
- vendor
-
diff --git a/resources/ext.mgwiki-dev.css b/resources/ext.mgwiki-dev.css
new file mode 100644
index 0000000..91ba6ec
--- /dev/null
+++ b/resources/ext.mgwiki-dev.css
@@ -0,0 +1,79 @@
+
+/***** MGW CUSTOM STYLES *****/
+.mgw-toggle-show {
+ color:white;
+ cursor:pointer;
+ float: right;
+ background: #447ff5;
+ border-radius: 3px;
+ padding-left:5px;
+ padding-right:5px;
+}
+
+.mgw-toggle-hide {
+ color:white;
+ cursor:pointer;
+ float: right;
+ background: #447ff5;
+ border-radius: 3px 3px 0 0;
+ padding-left:5px;
+ padding-right:5px;
+}
+
+.mgw-toggle-content {
+ color:black;
+ border: 1px solid #447ff5;
+ background: white;
+ padding:8px;
+ float: right;
+ border-radius: 5px 0 5px 5px;
+}
+
+/* Tooltip container */
+.mgw-tooltip {
+ position: relative;
+ display: inline-block;
+}
+
+/* Tooltip text */
+.mgw-tooltip .mgw-tooltiptext {
+ visibility: hidden;
+ width: 120px;
+ background-color: #555;
+ color: #fff;
+ text-align: center;
+ padding: 5px 0;
+ border-radius: 6px;
+
+ /* Position the tooltip text */
+ position: absolute;
+ z-index: 1;
+ bottom: 125%;
+ left: 50%;
+ margin-left: -60px;
+
+ /* Fade in tooltip */
+ opacity: 0;
+ transition: opacity 0.3s;
+}
+
+/* Show the tooltip text when you mouse over the tooltip container */
+.mgw-tooltip:hover .mgw-tooltiptext {
+ visibility: visible;
+ opacity: 1;
+}
+
+.mgw-delete-link{
+ color:red;
+ cursor:pointer;
+}
+
+.mgw-link{
+ text-decoration: none;
+}
+
+.mgw-parse-error{
+ font-style:italic;
+ font-weight:bold;
+ color:red;
+}
diff --git a/resources/ext.mgwiki-dev.js b/resources/ext.mgwiki-dev.js
new file mode 100644
index 0000000..b9166d6
--- /dev/null
+++ b/resources/ext.mgwiki-dev.js
@@ -0,0 +1,90 @@
+
+/**
+ * MGW customization
+ */
+
+( function ( mw, $ ) {
+ //liens donnés par le modèle {{mgw-link}}
+ mw.mgwLink = function () {
+ $(".mgw-link-self").each(function(){
+ let url = $(this).children('.mgw-link-url').text();
+ $(this).attr("onClick", "location.href='" + url + "'");
+ $(this).find('a').attr('href', url);
+ });
+ $(".mgw-link-blank").each(function(){
+ let url = $(this).children('.mgw-link-url').text();
+ $(this).attr("onClick", "parent.open('" + url + "')");
+ $childlink = $(this).children('a');
+ $childlink.replaceWith($(' ' + $childlink.html() + ' '));
+ });
+ }
+
+ //change menu border color
+ mw.mgwBorderColor = function() {
+ if ( $(".mgw-border-color").attr('style') !== undefined ) {
+ let color = $(".mgw-border-color").attr('style').match(/(#[a-z0-9]{6});/)[1];
+ let col = color.substring(1, 7);
+ $('#content').css('border', '1px solid' + color);
+ $('.vectorTabs,.vectorTabs span,.vectorTabs ul').css('background-image',
+ 'url(http://localhost/wiki/extensions/MGWikiDev/images/php/MenuListBorder.php?color='+col+')');
+ $('.vectorTabs li:not(.selected)').css('background-image',
+ 'url(http://localhost/wiki/extensions/MGWikiDev/images/php/MenuListBackground.php?color='+col+')');
+ }
+ }
+
+ //logo aide:
+ //override image link tooltip
+ mw.mgwImgTooltip = function () {
+ $(".mgw-help-img").each(function () {
+ title = $( this ).attr('title');
+ $( this ).children("a").attr('title',title);
+ });
+ }
+
+ mw.mgwToggleCreateUserSubPage = function () {
+ $icon = $("#mgw-toggle-createUserSubPage-icon");
+ $div = $("#mgw-toggle-createUserSubPage");
+ if ($icon.attr("class") == "mgw-show"){
+ $div.after('\
+ \
+ \
+ ');
+ $icon.html(' ▲ ');
+ $icon.attr('class','mgw-hide');
+ $div.attr('class','mgw-toggle-hide');
+ }
+ else {
+ $icon.html(' ▼ ');
+ $('.mgw-toggle-content').remove();
+ $icon.attr('class','mgw-show');
+ $div.attr('class','mgw-toggle-show');
+ }
+ }
+
+ // faire disparare les messages de succès après 10 secondes
+ mw.mgwAlertMessage = function () {
+ setTimeout(() => { $('.mgw-alert[status=done]').hide(); }, 2000);
+ }
+
+ $( function () {
+ mw.mgwAlertMessage();
+ mw.mgwBorderColor();
+ mw.mgwImgTooltip();
+ mw.mgwLink();
+ $('.mgw-tooltiptext').css('display','');
+ $("#mgw-toggle-createUserSubPage-icon").html(' ▼ ');
+ $("#mgw-toggle-createUserSubPage").attr("onclick","mw.mgwToggleCreateUserSubPage()");
+ $("#wpName1, #wpPassword1").attr("placeholder","");
+ $( ".mgw-delete-link" ).click( function() {
+ let link = $(this).attr('href');
+ let elmt = $(this).attr('elmt');
+ if ( confirm( 'Vous êtes sur le point de supprimer: ' + elmt +
+ '\nConfirmez-vous ?' ) ) {
+ document.location.href= link;
+ }
+ });
+ });
+}( mediaWiki, jQuery ) );
diff --git a/resources/ext.mgwiki-specialadmin.css b/resources/ext.mgwiki-specialadmin.css
new file mode 100644
index 0000000..3dea3c3
--- /dev/null
+++ b/resources/ext.mgwiki-specialadmin.css
@@ -0,0 +1,71 @@
+#content{
+}
+
+#mgw-admin-form{
+ display:flex;
+ justify-content: space-around;
+}
+
+#mgw-fieldset-tri, #mgw-fieldset-filtre{
+ border:none;
+ font-size:small;
+}
+
+legend{
+ font-weight:bold;
+}
+
+#mgw-admin-buttons{
+ align-self: flex-end;
+}
+.mgw-radio{
+ display:block;
+ margin:auto;
+}
+
+.mgw-action-button{
+ margin-left:50px;
+ margin-bottom:20px;
+ background-color: #848484;
+ border-radius: 4px;
+ color: #eeeeee;
+ text-align: center;
+ font-size: 18px;
+ cursor: pointer;
+ padding-left:10px;
+ padding-right:10px;
+
+ float:right;
+}
+
+.mgw-show-array{
+ background:#f6f6f6;
+ border: 1px solid black;
+ min-height:150px;
+ max-height:500px;
+ resize:vertical;
+ overflow: auto;
+}
+
+.mgw-row-table{
+ border-radius:10px;
+ margin:15px;
+ width:95%;
+ line-height:1.3;
+}
+
+.mgw-active{
+ background:#fadfaa;
+}
+
+.mgw-inactive{
+ background:#f6f6f6;
+}
+
+.mgw-deleted{
+ background:#ffcece;
+}
+
+.mgw-history-link{
+ margin-left:5px;
+}
diff --git a/resources/ext.mgwiki-specialadmin.js b/resources/ext.mgwiki-specialadmin.js
new file mode 100644
index 0000000..390092e
--- /dev/null
+++ b/resources/ext.mgwiki-specialadmin.js
@@ -0,0 +1,95 @@
+( function ( mw, $ ) {
+
+ mw.mgwFiltresShow = function() {
+ $('.mgw-view').attr('checked', true );
+ }
+
+ mw.mgwFiltresHide = function() {
+ $('.mgw-hide').attr('checked', true );
+ }
+
+ mw.mgwAddAll = function() {
+ let inputs = document.getElementById('mgw-check-add-all');
+ if ( inputs.checked ) {
+ $('.mgw-check-add').each( function(){
+ let id = $(this).val();
+ if ( ! $('.mgw-check-del[value='+id+']').is(':checked') ) {
+ $(this).attr('checked', true );
+ }
+ });
+ }
+ if ( ! inputs.checked ) {
+ $('.mgw-check-add').attr('checked', false );
+ }
+ }
+
+ mw.mgwDelAll = function() {
+ let inputs = document.getElementById('mgw-check-del-all');
+ if ( inputs.checked ) {
+ $('.mgw-check-del').each( function(){
+ let id = $(this).val();
+ if ( ! $('.mgw-check-add[value='+id+']').is(':checked') ) {
+ $(this).attr('checked', true );
+ }
+ });
+ }
+ if ( ! inputs.checked ) {
+ $('.mgw-check-del').attr('checked', false );
+ }
+ }
+
+ mw.mgwCheckAddConcat = function(){
+ var arr = [];
+ $('.mgw-check-add:checked').each( function(){ arr.push( $(this).val() ); });
+ $('#mgw-hidden-field-add').val( arr.join(',') );
+ }
+
+ mw.mgwCheckDelConcat = function(){
+ var arr = [];
+ $('.mgw-check-del:checked').each( function(){ arr.push( $(this).val() ); });
+ $('#mgw-hidden-field-del').val( arr.join(',') );
+ }
+
+ $( function () {
+
+ // valider le formulaire en définissant l'action
+ $('.mgw-action-button').click( function(){
+ let action = $(this).attr('action');
+ if ( $(this).attr('checkadd') == 'true' ) {
+ mw.mgwCheckAddConcat();
+ }
+ if ( $(this).attr('checkdel') == 'true' ) {
+ mw.mgwCheckDelConcat();
+ }
+ let hidden = JSON.parse( $(this).attr('hidden') );
+ Object.entries( hidden ).forEach(entry => {
+ const [ field, value ] = entry;
+ $('input[name='+field+']').val( value );
+ });
+ if ( $(this).attr('control') != '' ) {
+ $(this).attr('control');
+ }
+ $('input[name=action]').val( action )
+ });
+
+ // si check-add décocher check-del
+ $('.mgw-check-add').click( function(){
+ let id = $(this).val();
+ if( $(this).is(':checked') ){
+ if ( $('.mgw-check-del[value='+id+']').is(':checked') ) {
+ $('.mgw-check-del[value='+id+']').removeAttr('checked');
+ }
+ }
+ });
+
+ $('.mgw-check-del').click( function(){
+ let id = $(this).val();
+ if ( $(this).is(':checked') ){
+ if ( $('.mgw-check-add[value='+id+']').is(':checked') ){
+ $('.mgw-check-add[value='+id+']').removeAttr('checked');
+ }
+ }
+ });
+ });
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/ext.mgwiki.edit.js b/resources/ext.mgwiki.edit.js
deleted file mode 100644
index 0b81c2c..0000000
--- a/resources/ext.mgwiki.edit.js
+++ /dev/null
@@ -1,196 +0,0 @@
-/**
- * @class mw.Api.plugin.edit
- *
- * This is directly backported from MediaWiki 1.28, only the names were changed
- * to avoid future naming conflicts.
- */
-( function ( mw, $ ) {
-
- $.extend( mw.Api.prototype, {
-
- /**
- * Post to API with csrf token. If we have no token, get one and try to post.
- * If we have a cached token try using that, and if it fails, blank out the
- * cached token and start over.
- *
- * @param {Object} params API parameters
- * @param {Object} [ajaxOptions]
- * @return {jQuery.Promise} See #post
- */
- mgwikiPostWithEditToken: function ( params, ajaxOptions ) {
- return this.postWithToken( 'csrf', params, ajaxOptions );
- },
-
- /**
- * API helper to grab a csrf token.
- *
- * @return {jQuery.Promise} Received token.
- */
- mgwikiGetEditToken: function () {
- return this.getToken( 'csrf' );
- },
-
- /**
- * Create a new page.
- *
- * Example:
- *
- * new mw.Api().create( 'Sandbox',
- * { summary: 'Load sand particles.' },
- * 'Sand.'
- * );
- *
- * @since 1.28
- * @param {mw.Title|string} title Page title
- * @param {Object} params Edit API parameters
- * @param {string} params.summary Edit summary
- * @param {string} content
- * @return {jQuery.Promise} API response
- */
- mgwikiCreate: function ( title, params, content ) {
- return this.mgwikiPostWithEditToken( $.extend( {
- action: 'edit',
- title: String( title ),
- text: content,
- formatversion: '2',
-
- // Protect against errors and conflicts
- assert: mw.user.isAnon() ? undefined : 'user',
- createonly: true
- }, params ) ).then( function ( data ) {
- return data.edit;
- } );
- },
-
- /**
- * Edit an existing page.
- *
- * To create a new page, use #create() instead.
- *
- * Simple transformation:
- *
- * new mw.Api()
- * .edit( 'Sandbox', function ( revision ) {
- * return revision.content.replace( 'foo', 'bar' );
- * } )
- * .then( function () {
- * console.log( 'Saved! ');
- * } );
- *
- * Set save parameters by returning an object instead of a string:
- *
- * new mw.Api().edit(
- * 'Sandbox',
- * function ( revision ) {
- * return {
- * text: revision.content.replace( 'foo', 'bar' ),
- * summary: 'Replace "foo" with "bar".',
- * assert: 'bot',
- * minor: true
- * };
- * }
- * )
- * .then( function () {
- * console.log( 'Saved! ');
- * } );
- *
- * Transform asynchronously by returning a promise.
- *
- * new mw.Api()
- * .edit( 'Sandbox', function ( revision ) {
- * return Spelling
- * .corrections( revision.content )
- * .then( function ( report ) {
- * return {
- * text: report.output,
- * summary: report.changelog
- * };
- * } );
- * } )
- * .then( function () {
- * console.log( 'Saved! ');
- * } );
- *
- * @since 1.28
- * @param {mw.Title|string} title Page title
- * @param {Function} transform Callback that prepares the edit
- * @param {Object} transform.revision Current revision
- * @param {string} transform.revision.content Current revision content
- * @param {string|Object|jQuery.Promise} transform.return New content, object with edit
- * API parameters, or promise providing one of those.
- * @return {jQuery.Promise} Edit API response
- */
- mgwikiEdit: function ( title, transform ) {
- var basetimestamp, curtimestamp,
- api = this;
- return api.get( {
- action: 'query',
- prop: 'revisions',
- rvprop: [ 'content', 'timestamp' ],
- titles: String( title ),
- formatversion: '2',
- curtimestamp: true
- } )
- .then( function ( data ) {
- var page, revision;
- if ( !data.query || !data.query.pages ) {
- return $.Deferred().reject( 'unknown' );
- }
- page = data.query.pages[ 0 ];
- if ( !page || page.missing ) {
- return $.Deferred().reject( 'nocreate-missing' );
- }
- revision = page.revisions[ 0 ];
- basetimestamp = revision.timestamp;
- curtimestamp = data.curtimestamp;
- return transform( {
- timestamp: revision.timestamp,
- content: revision.content
- } );
- } )
- .then( function ( params ) {
- var editParams = typeof params === 'object' ? params : { text: String( params ) };
- return api.mgwikiPostWithEditToken( $.extend( {
- action: 'edit',
- title: title,
- formatversion: '2',
-
- // Protect against errors and conflicts
- assert: mw.user.isAnon() ? undefined : 'user',
- basetimestamp: basetimestamp,
- starttimestamp: curtimestamp,
- nocreate: true
- }, editParams ) );
- } )
- .then( function ( data ) {
- return data.edit;
- } );
- },
-
- /**
- * Post a new section to the page.
- *
- * @see #postWithEditToken
- * @param {mw.Title|string} title Target page
- * @param {string} header
- * @param {string} message wikitext message
- * @param {Object} [additionalParams] Additional API parameters, e.g. `{ redirect: true }`
- * @return {jQuery.Promise}
- */
- mgwikiNewSection: function ( title, header, message, additionalParams ) {
- return this.mgwikiPostWithEditToken( $.extend( {
- action: 'edit',
- section: 'new',
- title: String( title ),
- summary: header,
- text: message
- }, additionalParams ) );
- }
- } );
-
- /**
- * @class mw.Api
- * @mixins mw.Api.plugin.edit
- */
-
-}( mediaWiki, jQuery ) );
diff --git a/resources/ext.mgwiki.js b/resources/ext.mgwiki.js
deleted file mode 100644
index fe33790..0000000
--- a/resources/ext.mgwiki.js
+++ /dev/null
@@ -1,97 +0,0 @@
-( function ( mw, $ ) {
-
- /**
- * Modify in background the competency on the current page.
- *
- * The edition is done by the current user without edit summary.
- * The competence can be any string followed by '=Oui' or '=Non'
- * (boolean values in a template). If no specific value is given
- * in the second argument, the boolean value is toggled.
- *
- * @param {string} Competence name
- * @param {string|boolean} [Value]
- * @return void
- */
- mw.mgwikiModifyCompetence = function ( name, value ) {
-
- new mw.Api()
- .mgwikiEdit( mw.config.get( 'wgPageName' ), function ( revision ) {
- if ( !revision.content.match( new RegExp( '\\| *' + name + ' *= *([Oo]ui|[Nn]on)' ) ) ) {
- return revision.content;
- }
- if ( value === undefined ) {
- value = 'Oui';
- if ( revision.content.match( new RegExp( '\\| *' + name + ' *= *[Oo]ui' ) ) ) {
- value = 'Non';
- }
- } else if ( value == 'Non' || value == 'non' || !value ) {
- value = 'Non';
- } else {
- value = 'Oui';
- }
- return revision.content.replace(
- new RegExp( '\\| *' + name + ' *= *([Oo]ui|[Nn]on)' ),
- '|' + name + '=' + value );
- } );
- }
-
- /**
- * On some images, add clickable areas editing the page.
- *
- * More specifically, the image must be included in a ,
- * and this div must also contain a description of the clickable areas redacted in
- * HTML with (only |