diff --git a/.gitignore b/.gitignore index db9f334..49ad46f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ -# Backup files -*.swp -*~ +# syntax: https://www.atlassian.com/git/tutorials/saving-changes/gitignore -# Composer & npm -/node_modules -/vendor -/composer.lock +# Private files +Captcha.json +TODO.php + +# temporary log files +*-report.txt diff --git a/ApiADEPULGroupMember.php b/ApiADEPULGroupMember.php deleted file mode 100644 index e966c31..0000000 --- a/ApiADEPULGroupMember.php +++ /dev/null @@ -1,141 +0,0 @@ -extractRequestParams(); - $apiResult = $this->getResult(); - - $code_action = $params['code_action']; - $membres = $params['membres'] ?: $params['membre']; - $cle = $params['cle']; - - if( $wgMGWikiSecretKeyADEPUL === null || !hash_equals( $wgMGWikiSecretKeyADEPUL, $cle ) ) { - $this->dieWithError( 'mgwiki-wrong-secret-key' ); - } - - // Get ADEPUL group page - $groupTitle = MGWiki::getADEPULGroup( $code_action ); - if( $groupTitle === null ) { - $this->dieWithError( 'mgwiki-unknown-group' ); - } - $groupArticle = WikiPage::factory( $groupTitle ); - $groupContent = $groupArticle->getContent(); - if( $groupContent === null || $groupContent->getModel() !== CONTENT_MODEL_WIKITEXT ) { - $this->dieWithError( 'mgwiki-bad-group' ); - } - $groupWikitext = $groupContent->getText(); - if( !preg_match( '/\{\{Groupe[\n |](?:[^}]+\})+\}/', $groupWikitext, $groupTemplate, PREG_OFFSET_CAPTURE ) ) { - $this->dieWithError( 'mgwiki-bad-adepul-group' ); - } - if( !preg_match( '/^\| *' . $wgMGWikiUserProperties['moderator'] . ' *= *([^|]*)/m', $groupTemplate[0][0], $formateurUsername ) ) { - $this->dieWithError( 'mgwiki-bad-adepul-group' ); - } - if( !preg_match( '/^\| *' . $wgMGWikiUserProperties['membersList'] . ' *= *([^|]*)/m', $groupTemplate[0][0], $membersList, PREG_OFFSET_CAPTURE ) ) { - $this->dieWithError( 'mgwiki-bad-adepul-group' ); - } - $formateurUsername = trim( $formateurUsername[1] ); - $formateurUser = User::newFromName( $formateurUsername ); - - // Get existing members - $existingMembers = explode( ',', $membersList[1][0] ); - $existingMembers = array_map( function($s) { return trim($s); }, $existingMembers ); - if( count( $existingMembers ) === 1 && $existingMembers[0] === '' ) { - $existingMembers = []; - } - - // Retrieve members to be added - $membres = explode( ',', $membres ); - $errorsMembers = []; - $listMembers = []; - foreach( $membres as $membre ) { - try { - $memberUser = MGWiki::getUserByADEPUL( $membre, $formateurUsername ); - $memberUser = $memberUser->getName(); - if( !in_array( $memberUser, $existingMembers ) ) { - $listMembers[] = $memberUser; - } - } catch( Exception $e ) { - $errorsMembers[] = $membre; - } - } - if( count( $listMembers ) === 0 ) { - $apiResult->addValue( null, "result", "success" ); - return; - } - - $newWikitext = substr( $groupWikitext, 0, $groupTemplate[0][1] ) . // Before template Groupe - substr( $groupTemplate[0][0], 0, $membersList[1][1] ) . // In template Groupe, before the parameter 'Membres' and including this parameter name until its '=' - implode( ', ', $listMembers ) . ( count( $existingMembers ) && $existingMembers[0] ? ', ' : '' ) . // Add our new members - substr( $groupTemplate[0][0], $membersList[1][1] ) . // In template Groupe, just after the parameter 'Membres' - substr( $groupWikitext, $groupTemplate[0][1] + strlen( $groupTemplate[0][0] ) ); // After template Groupe - - $newGroupContent = new WikitextContent( $newWikitext ); - - $summary = wfMessage( 'mgwiki-add-member-grouppage' )->inContentLanguage()->text(); - $groupArticle->doEditContent( $newGroupContent, $summary, 0, false, $formateurUser ); - - $apiResult->addValue( null, "result", "success" ); - } - - public function mustBePosted() { - return true; - } - - public function isReadMode() { - return false; - } - - public function isWriteMode() { - return true; - } - - public function getAllowedParams() { - return [ - 'code_action' => [ - ApiBase::PARAM_TYPE => 'string', - ], - 'membre' => [ - ApiBase::PARAM_TYPE => 'string', - ], - 'membres' => [ - ApiBase::PARAM_TYPE => 'string', - ], - 'cle' => [ - ApiBase::PARAM_TYPE => 'string', - ], - ]; - } - - public function needsToken() { - return false; - } -} diff --git a/ApiNewADEPULGroup.php b/ApiNewADEPULGroup.php deleted file mode 100644 index edeeb3a..0000000 --- a/ApiNewADEPULGroup.php +++ /dev/null @@ -1,115 +0,0 @@ -extractRequestParams(); - $apiResult = $this->getResult(); - - $code_action = $params['code_action']; - $intitule_ac = $params['intitule_ac']; - $intitule_long_ac = $params['intitule_long_ac']; - $formateur = $params['formateur']; - $cle = $params['cle']; - - if( $wgMGWikiSecretKeyADEPUL === null || !hash_equals( $wgMGWikiSecretKeyADEPUL, $cle ) ) { - $this->dieWithError( 'mgwiki-wrong-secret-key' ); - } - - $annee = intval( preg_replace( '/^[A-Z]([0-9]{2}).*/', '$1', $code_action ) ); - $annee = $annee ? '20' . $annee : ''; - - try { - $formateurUser = MGWiki::getUserByADEPUL( $formateur ); - $formateurUsername = $formateurUser->getName(); - } catch( Exception $e ) { - $this->dieWithError( 'mgwiki-wrong-secret-key' ); - } - - $title = "DPC ADEPUL $annee - $intitule_ac"; - $wikitext = "{{Groupe -|Archivé=Non -|Description=$intitule_long_ac -|Institution de rattachement=ADEPUL -|Tuteur ou modérateur=$formateurUsername -|Membres= -|Type de groupe=DPC -|Titre de la formation=$intitule_ac -|Année=$annee -|Code action=$code_action -}} -{{Groupe2}}"; - $groupTitle = Title::newFromText( $title, NS_MAIN ); - $groupArticle = WikiPage::factory( $groupTitle ); - $content = new WikitextContent( $wikitext ); - $summary = wfMessage( 'mgwiki-create-grouppage' )->inContentLanguage()->text(); - $flags = EDIT_NEW; - $groupArticle->doEditContent( $content, $summary, $flags, false, $formateurUser ); - - $apiResult->addValue( null, "result", "success" ); - } - - public function mustBePosted() { - return true; - } - - public function isReadMode() { - return false; - } - - public function isWriteMode() { - return true; - } - - public function getAllowedParams() { - return [ - 'code_action' => [ - ApiBase::PARAM_TYPE => 'string', - ], - 'intitule_ac' => [ - ApiBase::PARAM_TYPE => 'string', - ], - 'intitule_long_ac' => [ - ApiBase::PARAM_TYPE => 'string', - ], - 'formateur' => [ - ApiBase::PARAM_TYPE => 'string', - ], - 'cle' => [ - ApiBase::PARAM_TYPE => 'string', - ], - ]; - } - - public function needsToken() { - return false; - } -} diff --git a/EmailTokenAuthenticationRequest.php b/EmailTokenAuthenticationRequest.php deleted file mode 100644 index fc733fd..0000000 --- a/EmailTokenAuthenticationRequest.php +++ /dev/null @@ -1,42 +0,0 @@ - - * @license GPL-3.0+ - */ - -namespace MediaWiki\Auth; - -/** - * This represents the field emailtoken aiming at connecting the user after - * s/he is redirected through Special:Invitation. - * - * @ingroup Auth - */ -class EmailTokenAuthenticationRequest extends AuthenticationRequest { - /** @var string|null Email address token */ - public $emailtoken; - - /** - * Return the field required for this authentication type. - * - * @return array[] The fields with their properties. - */ - public function getFieldInfo() { - $config = \ConfigFactory::getDefaultInstance()->makeConfig( 'main' ); - $ret = [ - 'emailtoken' => [ - 'type' => 'hidden', - 'label' => wfMessage( 'authmanager-emailtoken-label' ), - 'help' => wfMessage( 'authmanager-emailtoken-help' ), - 'optional' => true, - ], - ]; - - if ( !$config->get( 'EnableEmail' ) ) { - unset( $ret['emailtoken'] ); - } - - return $ret; - } -} diff --git a/EmailTokenPrimaryAuthenticationProvider.php b/EmailTokenPrimaryAuthenticationProvider.php deleted file mode 100644 index 61e5280..0000000 --- a/EmailTokenPrimaryAuthenticationProvider.php +++ /dev/null @@ -1,134 +0,0 @@ - - * @license GPL-3.0+ - */ - -namespace MediaWiki\Auth; - -use Config; -use User; - -class EmailTokenPrimaryAuthenticationProvider - extends AbstractPrimaryAuthenticationProvider -{ - /** @var bool */ - protected $sendConfirmationEmail; - - /** - * Construct this authentication provider. - * - * @param array $params - * - sendConfirmationEmail: (bool) send an email asking the user to confirm their email - * address to finalize its account creation - */ - public function __construct( $params = [] ) { - if ( isset( $params['sendConfirmationEmail'] ) ) { - $this->sendConfirmationEmail = (bool)$params['sendConfirmationEmail']; - } - } - - /** - * Set the internal configuration: check if email is activated. - * - * @param Config $config Global MediaWiki configuration. - * @return void - */ - public function setConfig( Config $config ) { - parent::setConfig( $config ); - - if ( $this->sendConfirmationEmail === null ) { - $this->sendConfirmationEmail = $this->config->get( 'EnableEmail' ) - && $this->config->get( 'EmailAuthentication' ); - } - } - - /** - * Return the authentication requests for this specific authentication type. - * - * @param string $action Authentication action, one of AuthManager::ACTION_*. - * @param array $options Options (unused here). - * @return AuthenticationRequest[] The email authentication request for login only. - */ - public function getAuthenticationRequests( $action, array $options ) { - switch ( $action ) { - case AuthManager::ACTION_LOGIN: - case AuthManager::ACTION_LOGIN_CONTINUE: - return [ new EmailTokenAuthenticationRequest() ]; - - default: - return []; - } - } - - public function beginPrimaryAuthentication( array $reqs ) { - $req = AuthenticationRequest::getRequestByClass( $reqs, EmailTokenAuthenticationRequest::class ); - if ( !$req || !$req->emailtoken ) { - return AuthenticationResponse::newAbstain(); - } - - $user = User::newFromConfirmationCode( $req->emailtoken, User::READ_LATEST ); - if ( !( $user instanceof User ) || !$user->getId() ) { - return AuthenticationResponse::newFail( wfMessage( 'mgwiki-bad-email-token' ) ); - } - $username = $user->getName(); - $user->setEmail( $user->getEmail() ); - $user->confirmEmail(); - return AuthenticationResponse::newPass( $username ); - } - - public function beginPrimaryAccountCreation( $user, $creator, array $reqs ) { - /*if ( - $this->sendConfirmationEmail - && $user->getEmail() - && !$this->manager->getAuthenticationSessionData( 'no-email' ) - ) { - $status = $user->sendConfirmationMail(); - $user->saveSettings(); - if ( $status->isGood() ) { - // TODO show 'confirmemail_oncreate' success message - } else { - // TODO show 'confirmemail_sendfailed' error message - $this->logger->warning( 'Could not send confirmation email: ' . - $status->getWikiText( false, false, 'en' ) ); - } - }*/ - - return AuthenticationResponse::newPass(); - } - - public function accountCreationType() { - return self::TYPE_NONE; - } - - public function testUserExists( $username, $flags = User::READ_NORMAL ) { - $username = User::getCanonicalName( $username, 'usable' ); - if ( $username === false ) { - return false; - } - - list( $db, $options ) = \DBAccessObjectUtils::getDBOptions( $flags ); - return (bool)wfGetDB( $db )->selectField( - [ 'user' ], - [ 'user_id' ], - [ 'user_name' => $username ], - __METHOD__, - $options - ); - } - - public function testUserCanAuthenticate( $username ) { - $user = User::newFromName( $username ); - return $user->isEmailConfirmationPending(); - } - - public function providerAllowsAuthenticationDataChange( AuthenticationRequest $req, - $checkData = true - ) { - return \StatusValue::newGood( 'ignored' ); - } - - public function providerChangeAuthenticationData( AuthenticationRequest $req ) { - } -} diff --git a/Hooks.php b/Hooks.php new file mode 100644 index 0000000..7451261 --- /dev/null +++ b/Hooks.php @@ -0,0 +1,187 @@ + + * @author Alexandre Brulet + * @license GPL-3.0+ + * @package MediaWiki-extension-MGWikiDev + */ +class MGWikiDevHooks { + + /** + * Chargement de l'extension + */ + public static function onExtensionLoad() { + + define("MGW_DB_UNSET", 0); + define("MGW_DB_UNCHANGED", 1); + define("MGW_DB_INSERTED", 2); + define("MGW_DB_UPDATED", 3); + define("MGW_DB_DROPED", 4); + define("MGW_DB_ERROR", 5); + define("MGW_PAGE_MISSING", 0); + + // définition des variables de type string + global $wgMgwStringVars; + $wgMgwStringVars = [ + 'utilisateur_nom', + 'utilisateur_prenom', + 'institution_nom', + 'groupe_type_nom' + ]; + + // niveaux de permissions + global $wgMgwLevels; + $wgMgwLevels = [ + 0 => 'U0', + 1 => 'U1', + 2 => 'U2', + 3 => 'U3', + 4 => 'sysop' + ]; + + // duration + function wfMgwDuration( int $int ) { + switch ( $int ) { + case 0: + return 'indéfini'; + break; + case 1: + return '6 mois'; + break; + case 2: + return '1 an'; + break; + default: + return ( $int / 2 ) . ' ans'; + break; + } + } + } + + /** + * Chargement du module MGWikiDev + */ + public static function onBeforePageDisplay( OutputPage $out, Skin $skin ) { + + //Modules pour toutes les pages + $out->addModules('ext.mgwiki-dev'); + } + + public static function onSpecialPageBeforeExecute ( $specialPage, $sub ) + { + // rediriger dans certains cas d'usage + + # pages ne devant pas être renommées manuellement + if ( $specialPage->getName() == 'Movepage') { + $url = $specialPage->getRequest()->getGlobalRequestURL(); + + // sous-pages MGWiki:Types_de_groupes/Xxx + if ( preg_match( '/MGWiki:(Types_de_groupes\/.*)$/', $url, $matches ) > 0 ) { + $title = Title::newFromText( $matches[1], NS_PROJECT ); + $screen = DbF::select_clean( + 'groupe_type', + [ 'id', 'page_id' ], + 'page_id = ' . $title->getArticleID() + ); + if ( !is_null( $screen ) ) { + $returnto = $specialPage->getTitleFor( 'specialadmingrouptypes' )->getLinkURL( [ + "action" => "edit", + "id" => $screen[0]['id'] + ] ); + $specialPage->getOutput()->redirect( $returnto ); + } + } + + } + + } + + /** + * ! CUSTOM HOOK ! (cf readme -> ApiMain.php changes) + * Autoriser l'API getjson quelque soit l'utilisateur + */ + public static function onApiAllow( $module, $user ) + { + global $wgRequest; + if ( $wgRequest->getText( 'action' ) == 'getjson' ) + { + return true; + } + return false; + } + + // Register any render callbacks with the parser + public static function onParserFirstCallInit( Parser $parser ) { + + $parser->setFunctionHook( 'mgw-onclick', [ Parsers::class, 'onclickSpan' ] ); + $parser->setFunctionHook( 'mgw-groupe-type', [ Parsers::class, 'mgw_groupe_type' ] ); + $parser->setFunctionHook( 'mgw-display', [ Parsers::class, 'mgw_display' ] ); + } + + /** + * Hook: LoadExtensionSchemaUpdates + * + * fonction exécutée par maintenance/Update.php + * + * @param DatabaseUpdater $updater + */ + public static function onLoadExtensionSchemaUpdates( DatabaseUpdater $updater ) { + + $dir = __DIR__ . '/sql'; + $tables = array( + 'mgw_utilisateur', + 'mgw_archive_utilisateur', + 'mgw_institution', + 'mgw_archive_institution', + 'mgw_groupe', + 'mgw_archive_groupe', + 'mgw_groupe_membre', + 'mgw_archive_groupe_membre', + 'mgw_groupe_type', + 'mgw_archive_groupe_type', + 'mgw_institution_groupe' + ); + + foreach( $tables as $table ) { + $tableSQLFile = "$dir/addTable-" . $table . ".sql"; + $indexSQLfile = "$dir/addIndex-" . $table . "_lookup.sql"; + $updater->addExtensionTable( 'mgw_' . $table, $tableSQLFile ); + if ( file_exists($indexSQLfile) ) { + $updater->addExtensionIndex( $table, $table.'_lookup', $indexSQLfile ); + } + } + } + + /** + * login avec mail + authentification insensible à la casse + */ + public static function onAuthChangeFormFields( $requests, $fieldInfo, &$formDescriptor, $action ) { + global $wgRequest; + // uniquement pour le formulaire de login + if ( $wgRequest->getGlobalRequestURL() == '/wiki/index.php/Sp%C3%A9cial:Connexion' ) + { + $formDescriptor['username']['label'] = 'E-mail ou nom d\'utilisateur'; + $formDescriptor['username']['filter-callback'] = function ( $val, $array ) { + $val = htmlspecialchars($val, ENT_QUOTES ); + + // si mail valide on récupère le nom d'utilisateur correspondant + if ( preg_match( '/@/', $val ) > 0 ) + $r = UserF::emailExists( $val ); + + // on corrige les erreurs de casse + else $r = UserF::userExists( $val, false ); + + if ( !$r ) return htmlspecialchars_decode( $val, ENT_QUOTES ); // = utilisateur inconnu + else return $r; + }; + } + } +} diff --git a/MGWiki.magic.php b/MGWiki.magic.php deleted file mode 100644 index 6dc50e1..0000000 --- a/MGWiki.magic.php +++ /dev/null @@ -1,14 +0,0 @@ - array( 0, 'isusersysop' ), -); diff --git a/MGWiki.php b/MGWiki.php deleted file mode 100644 index 48af016..0000000 --- a/MGWiki.php +++ /dev/null @@ -1,819 +0,0 @@ - - * @license GPL-3.0+ - * @package MediaWiki-extension-MGWiki - */ - -declare(strict_types=1); - -use MediaWiki\Auth\AuthManager; -use MediaWiki\Auth\AuthenticationRequest; -use MediaWiki\Auth\AuthenticationResponse; - -class MGWiki { - - /** - * Check permissions for actions. - * - * @param Title $title Title of the page - * @param User $user User about to do the action - * @param string $action Requested action - * @param bool|mixed $result True or false to authorize or deny the action - */ - public static function onuserCan( Title &$title, User &$user, $action, &$result ) { - - global $wgMGWikiForms; - - # Only edit permission is checked - if ( $action != 'edit' ) { - return true; - } - - # Check permissions for forms - $titleEnglish = ''; - $ns = MWNamespace::getCanonicalName( $title->getNamespace() ); - if ( $ns ) $titleEnglish .= $ns . ':'; - $titleEnglish .= $title->getText(); - foreach ( $wgMGWikiForms as $form => $params ) { - # For each registered form type is associated a resulting page name, check the permissions on this page, not the form itself - if ( array_key_exists( 'RegexPageName', $params ) && preg_match( $params['RegexPageName'], $titleEnglish ) ) { - if ( $title->getNamespace() == NS_USER && $title->getText() == $user->getName() && array_key_exists( 'EditOwnUserpage', $params ) && $params['EditOwnUserpage'] === true ) { - return true; - } - if ( array_key_exists( 'RequiredRight', $params ) && is_string( $params['RequiredRight'] && !$user->isAllowed( $params['RequiredRight'] ) ) ) { - # Unauthorised user, and all further permissions hooks must be skipped since this result is authoritative - $result = false; - return false; - } - } - } - - return true; - } - - /** - * Display a warning if the user account and user page don’t together exist or are missing. - * - * @param Title $targetTitle Page title - * @param string $pre_form_html String displayed just before the form - * @return true - */ - public static function onsfHTMLBeforeForm( $targetTitle, &$pre_form_html ) { - - # Only use this hook on user pages - if ( empty( $targetTitle ) || $targetTitle->getNamespace() != NS_USER ) return; - - # Get the user account - $user = User::newFromName( $targetTitle->getText() )->getId(); - - if ( $targetTitle->exists() xor $user ) { - - $pre_form_html = '
'; - if ( $targetTitle->exists() ) $pre_form_html .= wfMessage( 'mgwiki-userpage-without-useraccount' )->escaped(); - else $pre_form_html .= wfMessage( 'mgwiki-useraccount-without-userpage' )->escaped(); - $pre_form_html .= "
\n"; - } - - return true; - } - - public static function onPrefsEmailAudit( $user, $oldaddr, $newaddr ) { - - global $wgMGWikiUserProperties; - - # Normalise value - $emailProperty = $wgMGWikiUserProperties['email']; - - # Get the wiki page - $title = Title::newFromText( $user->getName(), NS_USER ); - if ( $title->getArticleID() == -1 ) return; - $article = WikiPage::factory( $title ); - $summary = wfMessage( 'mgwiki-changed-email' )->inContentLanguage()->text(); - - # Get the content - $oldContent = $article->getContent(); - if( $oldContent->getModel() != CONTENT_MODEL_WIKITEXT ) { - return; - } - - # Update the content - $content = new WikitextContent( preg_replace( '/\| *'.preg_quote($emailProperty,'/').' *=[a-zA-Z0-9@._+-]+/', "|$emailProperty = $newaddr\n", $oldContent->getNativeData() ) ); - - # And edit - $flags = EDIT_MINOR | EDIT_SUPPRESS_RC | EDIT_UPDATE; - $status = $article->doEditContent( $content, $summary, $flags, false, $wgUser ); - if( !$status->isOK() ) { - return; - } - } - - /** - * When a user page is modified by SemanticMediaWiki, create the corresponding MediaWiki user or update the email - * - * Only the user or 'admins' with the right 'mgwikimanagelevel1' can report the email address in the user preferences. - * If $wgNewUserLog is true (default), add an entry in the 'newusers' log when a user is created. - * - * @param SMWSQLStore3 $store SemanticMediaWiki store - * @param SMWSemanticData $semanticData Semantic data - * @param SMW\SQLStore\CompositePropertyTableDiffIterator $compositePropertyTableDiffIterator Differences on property values - * @return bool|void - */ - public static function onSMW_SQLStore_AfterDataUpdateComplete( SMWSQLStore3 $store, SMWSemanticData $semanticData, SMW\SQLStore\CompositePropertyTableDiffIterator $compositePropertyTableDiffIterator ) { - - global $wgMGWikiForms; - - # Get title with namespace in English - $title = $semanticData->getSubject()->getTitle(); - $titleEnglish = ''; - $ns = MWNamespace::getCanonicalName( $title->getNamespace() ); - if ( $ns ) $titleEnglish .= $ns . ':'; - $titleEnglish .= $title->getText(); - - # Get the user who made the change - # It is executed as a job, so $wgUser is not the real user who made the change - $statements = self::collectSemanticData( [ '_LEDT' ], $semanticData, $complete ); - if ( !array_key_exists( '_LEDT', $statements ) || !$statements['_LEDT'] instanceof Title || $statements['_LEDT']->getNamespace() != 2 ) - return; - $editor = User::newFromName( $statements['_LEDT']->getText() ); - if( !$editor ) { - return false; - } - $editor->load(); - - # Search the form - foreach ( $wgMGWikiForms as $form => $params ) { - if ( array_key_exists( 'RegexPageName', $params ) && preg_match( $params['RegexPageName'], $titleEnglish ) ) { - - self::synchroniseMediaWikiGroups( $title, $editor, $form, $params, $store, $semanticData, $compositePropertyTableDiffIterator ); - - # Delete the page if requested - if ( array_key_exists( 'EphemeralPage', $params ) && $params['EphemeralPage'] ) { - $page = WikiPage::factory( $title ); - $deleteStatus = $page->doDeleteArticleReal( wfMessage( 'mgwiki-ephemeral-page-deleted' )->inContentLanguage()->text() ); - } - - break; - } - } - - return true; - } - - /** - * Update the property on the userpage “last-date-edited-by-user-her/himself” - * - * @param WikiPage $wikiPage The WikiPage (object) being saved. - * @param User $user The User (object) saving the article. - * @param Content $content The new article content, as a Content object. - * @param string $summary The article summary (comment). - * @param integer $isMinor Minor flag. - * @param null $isWatch Watch flag (not used, aka always null). - * @param null $section Section number (not used, aka always null). - * @param integer $flags See WikiPage::doEditContent documentation for flags' definition. - * @param Status $status Status (object). - */ - public static function onPageContentSave( &$wikiPage, &$user, &$content, &$summary, $isMinor, $isWatch, $section, &$flags, &$status ) { - - global $wgMGWikiUserProperties; - - # If not the userpage, not in the scope of this hook - if( !$user->getUserPage()->equals( $wikiPage->getTitle() ) ) { - return true; - } - - # Always update the “last-date-edited-by-user-her/himself” - if( preg_match( "/(^|\r?\n) *\| *" . preg_quote( $wgMGWikiUserProperties['timestamp'], '/' ) . " *=.*(\r?\n|$)/", $content->getNativeData() ) ) { - $content = new WikitextContent( preg_replace( "/(^|\r?\n) *\| *" . preg_quote( $wgMGWikiUserProperties['timestamp'], '/' ) . " *=.*(\r?\n|$)/", - '$1|' . $wgMGWikiUserProperties['timestamp'] . '=' . wfTimestamp() . '$2', - $content->getNativeData() ) ); - } else { - $content = new WikitextContent( preg_replace( "/((^|\r?\n) *\| *" . preg_quote( $wgMGWikiUserProperties['email'], '/' ) . " *=.*)(\r?\n|$)/", - "\$1\n|" . $wgMGWikiUserProperties['timestamp'] . '=' . wfTimestamp() . '$3', - $content->getNativeData() ) ); - } - } - - /** - * Redirect the user just after login if her/his semantic property says - * s/he should update her/his informations. - */ - static function onPostLoginRedirect( &$returnTo, &$returnToQuery, &$type ) { - - global $wgUser; - global $wgMGWikiUserProperties; - - $store = &smwfGetStore(); - $complete = null; - - $update = self::collectSemanticData( [ $wgMGWikiUserProperties['requiredUserUpdate'] ], $store->getSemanticData( SMW\DIWikiPage::newFromTitle( $wgUser->getUserPage() ) ), $complete ); - - if( count( $update ) == 1 && $update[$wgMGWikiUserProperties['requiredUserUpdate']] ) { - $returnTo = $wgUser->getUserPage()->getPrefixedText(); - $returnToQuery = [ 'action' => 'formedit' ]; - $type = 'successredirect'; - } - - return true; - } - - /** - * - * - * @param SpecialPage $specialPage - * @param string|null $subpage - */ - static function onSpecialPageAfterExecute( $specialPage, $subpage ) { - - global $wgUser, $wgOut; - - # After the user has changed her/his password, send her/him to her/his userpage in form-edition to confirm her/his data - if( $specialPage->getName() == 'ChangeCredentials' && $specialPage->getRequest()->wasPosted() ) { - - $wgOut->redirect( $wgUser->getUserPage()->getFullURL( [ 'action' => 'formedit' ] ) ); - $wgOut->output(); - } - - return true; - } - - /** - * Synchronise the requested groups from the semantic form with MediaWiki groups. - * - * @param Title $title Title of the subject page. - * @param User $editor Last editor of the page. - * @param string $form Name of this form. - * @param array $paramsForm Parameters for this form type. - * @param SMWSQLStore3 $store SemanticMediaWiki store. - * @param SMWSemanticData $semanticData Semantic data. - * @param SMW\SQLStore\CompositePropertyTableDiffIterator $compositePropertyTableDiffIterator Differences on property values. - * @return true - */ - private static function synchroniseMediaWikiGroups( Title $title, User $editor, $form, array $paramsForm, $store, $semanticData, $compositePropertyTableDiffIterator ) { - - global $wgMGWikiFieldsGroups, $wgMGWikiUserProperties, $wgMGWikiGroups; - global $wgUser; - - # Default groups to be added - $groups = []; - $editOwnUserpage = false; - $complete = null; - - # Check if the user edits her/his own userpage - if ( $title->getNamespace() === NS_USER ) { - if( strpos( $title->getDBkey(), '/' ) !== false ) { - return; - } - $user = User::newFromName( $title->getDBkey() ); - if( $user instanceof User ) { - $user->load(); - if ( $editor->isLoggedIn() && $editor->getId() == $user->getId() ) - $editOwnUserpage = array_key_exists( 'EditOwnUserpage', $paramsForm ) && $paramsForm['EditOwnUserpage']; - } - } - - # Check permissions - if ( !$editor->isAllowed( $paramsForm['RequiredRight'] ) && !$editOwnUserpage ) - return; - - # Iterate over the fields groups - $defaultGroups = self::searchFieldsGroups( $title, $editor, $semanticData, $editOwnUserpage ); - - # Get moderator’s institution if defined - $institution = []; - if( array_key_exists( 'InstitutionFromModerator', $paramsForm ) && $paramsForm['InstitutionFromModerator'] ) { - $moderator = self::collectSemanticData( [ $wgMGWikiUserProperties['moderator'] ], $semanticData, $complete ); - if( count( $moderator ) == 1 ) { - $institution = self::collectSemanticData( [ $wgMGWikiUserProperties['institution'] ], - $store->getSemanticData( SMW\DIWikiPage::newFromTitle( $moderator[$wgMGWikiUserProperties['moderator']] ) ), - $complete ); - if( count( $institution ) != 1 ) { - $institution = []; - } - $institution[$wgMGWikiUserProperties['referrer']] = $moderator[$wgMGWikiUserProperties['moderator']]->getText(); - } - } - elseif( array_key_exists( 'InstitutionFromCreator', $paramsForm ) && $paramsForm['InstitutionFromCreator'] ) { - $creator = self::collectSemanticData( [ '_LEDT' ], $semanticData, $complete ); - if( count( $creator ) == 1 ) { - $institution = self::collectSemanticData( [ $wgMGWikiUserProperties['institution'] ], - $store->getSemanticData( SMW\DIWikiPage::newFromTitle( $creator['_LEDT'] ) ), - $complete ); - if( count( $institution ) != 1 ) { - $institution = []; - } - $institution[$wgMGWikiUserProperties['referrer']] = $creator['_LEDT']->getText(); - } - } - - # Iterate over the subobjects - $content = ''; - $templates = []; - if ( array_key_exists( 'SubObjects', $paramsForm ) && $paramsForm['SubObjects'] ) { - if ( $semanticData->hasSubSemanticData() ) { - $subSemanticData = $semanticData->getSubSemanticData(); - $createdUsers = []; - if ( array_key_exists( 'MergeNewUsers', $paramsForm ) && is_array( $paramsForm['MergeNewUsers'] ) ) { - $templates = $paramsForm['MergeNewUsers']; - $article = WikiPage::factory( $title ); - # Get the content - $contentObject = $article->getContent(); - if( $contentObject->getModel() == CONTENT_MODEL_WIKITEXT ) { - $content = $contentObject->getNativeData(); - } - } - foreach ( $subSemanticData as $user => $userSemanticData ) { - - # Create users - $propertiesToBeSearched = array_values( $wgMGWikiUserProperties ); - $userData = self::collectSemanticData( $propertiesToBeSearched, $userSemanticData, $complete ); - $userData = array_merge( $institution, $userData ); - if ( array_key_exists( $wgMGWikiUserProperties['firstname'], $userData ) && array_key_exists( $wgMGWikiUserProperties['lastname'], $userData ) ) { - # Iterate over the fields groups - $userGroups = self::searchFieldsGroups( null, $editor, $userSemanticData, $editOwnUserpage ); - $groups = array_merge( $defaultGroups, $userGroups ); - #echo "userData = ";var_dump($userData); - #echo "defaultGroups = ";var_dump($defaultGroups); - #echo "userGroups = ";var_dump($userGroups); - - # If there is a person status, override add it - #foreach ( $wgMGWikiFieldsGroups as $k => $v ) { - # if ( array_key_exists( $k, $groups ) ) { - # $userData[$k] = array_flip( $v['MapFromProperty'] )[$groups[$k]]; - # } - #} - #echo "userData (bis) = ";var_dump($userData); - - $username = $userData[$wgMGWikiUserProperties['firstname']].' '.$userData[$wgMGWikiUserProperties['lastname']]; - self::createUser( $username, $userData ); - $createdUsers[] = $userData; - - # User groups - self::addMediaWikiGroups( $username, $groups, $editOwnUserpage ); - - # Replace templates with lists - if( $content ) { - foreach( $templates as $template => $list ) { - $content = preg_replace( '/\{\{ *' . $wgMGWikiUserProperties[$template] . "[ \|\n].*?\}\}\n?/s", '', $content ); - if( !preg_match( '/\| *' . $wgMGWikiUserProperties[$list] . " *=(.*?) *([\|\n])/", $content ) ) { - $content = preg_replace( '/\| *' . $wgMGWikiUserProperties['moderator'] . " *=(?:.*?) *\n/", "$0|" . $wgMGWikiUserProperties[$list] . ' = ' . $username . "\n", $content ); - } else { - $content = preg_replace( '/\| *' . $wgMGWikiUserProperties[$list] . " *= *(.*?) *([\|\n])/", '|' . $wgMGWikiUserProperties[$list] . ' = $1, ' . $username . '$2', $content ); - } - } - } - } - } - } - - # Save - if ( $content && count( $templates ) ) { - - # Update the content - $contentObject = new WikitextContent( $content ); - - # And edit - $flags = EDIT_MINOR | EDIT_UPDATE; - $summary = wfMessage( 'mgwiki-summary-rewrite-grouppage' )->inContentLanguage()->text(); - $status = $article->doEditContent( $contentObject, $summary, $flags, false, $wgUser ); - if( !$status->isOK() ) { - # Error - } - } - } - - # Standalone form (Personne) - elseif ( $title->getNamespace() == NS_USER ) { - # Search if there is an email property - $email = ''; - $statements = self::collectSemanticData( [ $wgMGWikiUserProperties['email'] ], $semanticData, $complete ); - if ( array_key_exists( $wgMGWikiUserProperties['email'], $statements ) ) - $email = $statements[$wgMGWikiUserProperties['email']]; - - # If the user doesn’t exist, create it - if ( self::createUser( $title->getText(), [ $wgMGWikiUserProperties['email'] => $email ] ) ); - - # Or just update the email - elseif ( $email && $user->getEmail() != $email ) { - $user->setEmail( $email ); - $user->saveSettings(); - } - - # And update the user groups - self::addMediaWikiGroups( $user, $defaultGroups, $editOwnUserpage ); - } - } - - private static function searchFieldsGroups( $title, $editor, $semanticData, $editOwnUserpage ) { - - global $wgMGWikiFieldsGroups; - - $groups = []; - $complete = null; - - foreach( $wgMGWikiFieldsGroups as $property => $paramsProperty ) { - - # Get data - $statements = self::collectSemanticData( [ $property ], $semanticData, $complete ); - #echo "\$statements ($property) = ";var_dump($statements); - - # Check permissions - $canEditOwnUserpage = array_key_exists( 'EditOwnUserpage', $paramsProperty ) && $paramsProperty['EditOwnUserpage']; - if ( !$editor->isAllowed( $paramsProperty['RequiredRight'] ) && !($editOwnUserpage && $canEditOwnUserpage) ) - continue; - - # Get the group to be added - if ( array_key_exists( $property, $statements ) ) { - $value = $statements[$property]; - // If the property is not explicitely defined (and has the type Page) - if( $value instanceof Title ) { - $value = $value->getText(); - } - if( ! is_string( $value ) ) { // Mainly boolean - $value = (string) $value; - } - $groups[$property] = $paramsProperty['MapFromProperty'][$value]; - } elseif ( $title && array_key_exists( 'MapFromTitle', $paramsProperty ) ) { - #echo "Title = ".$title->getText()." MapFromTitle ($property) =";var_dump($paramsProperty['MapFromTitle']); - foreach( $paramsProperty['MapFromTitle'] as $regex => $group ) { - if ( preg_match( $regex, $title->getText() ) ) - $groups[$property] = $group; - } - } - if ( !array_key_exists( $property, $groups ) && in_array( '', $paramsProperty['Groups'] ) ) - $groups[$property] = ''; - #echo "\$groups ($property) = ";var_dump($groups); - } - - return $groups; - } - - private static function addMediaWikiGroups( $user, $groups, $editOwnUserpage ) { - - global $wgMGWikiFieldsGroups; - - if ( is_string( $user ) ) - $user = User::newFromName( $user ); - - foreach( $groups as $property => $valueProperty ) { - - # Check permissions - if ( !$user->isAllowed( $wgMGWikiFieldsGroups[$property]['RequiredRight'] ) && !( array_key_exists( 'EditOwnUserpage', $wgMGWikiFieldsGroups[$property] ) && $wgMGWikiFieldsGroups[$property]['EditOwnUserpage'] === true && $editOwnUserpage ) ) { - continue; - } - - # Collect currently subscribed groups - $uniqueGroup = null; - $effectiveGroups = []; - foreach( $wgMGWikiFieldsGroups[$property]['Groups'] as $g ) { - $effectiveGroupe[$g] = false; - if ( $g && in_array( $g, $user->getGroups() ) ) { - $effectiveGroups[$g] = true; - if ( $uniqueGroup === null ) $uniqueGroup = $g; - else $uniqueGroup = false; - } - } - if ( in_array( '', $wgMGWikiFieldsGroups[$property]['Groups'] ) && $uniqueGroup === null ) { - $effectiveGroups[''] = true; - $uniqueGroup = ''; - } - - # Is it what we want? If so, continue - if ( $uniqueGroup === $valueProperty ) { - continue; - } - - # Else remove the user from the groups - $removedGroups = []; - foreach( $effectiveGroups as $g => $v ) { - if ( $g && $v ) { - $user->removeGroup( $g ); - $removedGroups[] = $g; - } - } - - # If a group is wanted, add it - if ( !$valueProperty ) - continue; - $user->addGroup( $valueProperty ); - } - } - - /** - * Collect the requested data. - * - * @param string[] $fields Field names - * @param SMW\SemanticData $semanticData Semantic data - * @param bool $complete Is set to true or false depending if all fields were found in the data - * @return array Requested data - */ - protected static function collectSemanticData( array $fields, SMW\SemanticData $semanticData, &$complete ) { - - # Init - $userData = array(); - $count = 0; - - # Retrieve values - $properties = $semanticData->getProperties(); - - # Normalise keys - $mapNormalisation = []; - foreach( $fields as $field ) - $mapNormalisation[str_replace( ' ', '_', $field )] = $field; - #echo "mapNormalisation = ";var_dump($mapNormalisation); - - # Iterate over existing properties and search requested properties - foreach( $properties as $key => $diProperty ) { - $values = $semanticData->getPropertyValues( $diProperty ); - #echo "property ".$diProperty->getKey()." found with ".count( $values )." values and type ".current( $values )->getDIType()."\n"; - if ( !in_array( $diProperty->getKey(), array_keys( $mapNormalisation ) ) ) - continue; - #echo "property ".$diProperty->getKey()." (".$mapNormalisation[$diProperty->getKey()].") found with ".count( $values )." values and type ".current( $values )->getDIType()."\n"; - if ( count( $values ) == 1 && current( $values )->getDIType() == SMWDataItem::TYPE_BLOB ) { - #echo "property ".$diProperty->getKey()." (".$mapNormalisation[$diProperty->getKey()].") = ".current( $values )->getString()."\n"; - $userData[$mapNormalisation[$diProperty->getKey()]] = current( $values )->getString(); - $count++; - } - elseif ( count( $values ) == 1 && current( $values )->getDIType() == SMWDataItem::TYPE_WIKIPAGE ) { - #echo "property ".$diProperty->getKey()." (".$mapNormalisation[$diProperty->getKey()].") = ".current( $values )->getTitle()."\n"; - $userData[$mapNormalisation[$diProperty->getKey()]] = current( $values )->getTitle(); - $count++; - } - elseif ( count( $values ) == 1 && current( $values )->getDIType() == SMWDataItem::TYPE_BOOLEAN ) { - #echo "property ".$diProperty->getKey()." (".$mapNormalisation[$diProperty->getKey()].") = ".(current( $values )->getBoolean()?'true':'false')."\n"; - $userData[$mapNormalisation[$diProperty->getKey()]] = current( $values )->getBoolean(); - $count++; - } - } - - # Check if we have all mandatory values - $complete = false; - if ( $count == count( $fields ) ) $complete = true; - - return $userData; - } - - /** - * Create a user. - * - * @param string $username Username - * @param string|null $email E-mail - * @param array $groups Groups - * @return bool The user was created - */ - public static function createUser( string $username, $userData = [], array $groups = [] ) { - - global $wgUser, $wgNewUserLog, $wgVersion; - global $wgMGWikiUserProperties; - - $username = User::getCanonicalName( $username, 'creatable' ); - $user = User::newFromName( $username ); - if ( $user->getId() ) - return false; - - $properties = []; - if ( array_key_exists( $wgMGWikiUserProperties['email'], $userData ) && is_string( $userData[$wgMGWikiUserProperties['email']] ) ) - $properties['email'] = $userData[$wgMGWikiUserProperties['email']]; - - # Create the user and add log entry - if ( false && version_compare( $wgVersion, '1.27.0' ) >= 0 ) { - //$data = $properties; // Not to send the confirmation email through AuthManager since I want to customise it - $data = []; - $data['username'] = $username; - $data['password'] = ''; - $data['retype'] = ''; - - # This comes from AuthManagerAuthPlugin - $reqs = AuthManager::singleton()->getAuthenticationRequests( AuthManager::ACTION_CREATE ); - $reqs = AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data ); - $res = AuthManager::singleton()->beginAccountCreation( $wgUser, $reqs, 'null:' ); - switch ( $res->status ) { - case AuthenticationResponse::PASS: - return true; - case AuthenticationResponse::FAIL: - // Hope it's not a PreAuthenticationProvider that failed... - $msg = $res->message instanceof \Message ? $res->message : new \Message( $res->message ); - return false; - default: - throw new \BadMethodCallException( - 'AuthManager does not support such simplified account creation' - ); - } - - } else { - $user = User::createNew( $username, $properties ); - if ( !$user instanceof User ) - return false; - if ( $wgNewUserLog ) { - $logEntry = new ManualLogEntry( 'newusers', 'create2' ); - $logEntry->setPerformer( $wgUser ); - $logEntry->setTarget( $user->getUserPage() ); - $logEntry->setParameters( array( '4::userid' => $user->getId() ) ); - $logid = $logEntry->insert(); - $logEntry->publish( $logid ); - } - } - - # Add template on userpage - $userTitle = Title::newFromText( $username, NS_USER ); - $userArticle = WikiPage::factory( $userTitle ); - $summary = wfMessage( 'mgwiki-create-userpage' )->inContentLanguage()->text(); - $email = array_key_exists( $wgMGWikiUserProperties['email'], $userData ) ? $userData[$wgMGWikiUserProperties['email']] : ''; - $statutPers = array_key_exists( $wgMGWikiUserProperties['statutPersonne'], $userData ) ? $userData[$wgMGWikiUserProperties['statutPersonne']] : ''; - $statutAddPers = array_key_exists( $wgMGWikiUserProperties['statutAdditionnelPersonne'], $userData ) ? $userData[$wgMGWikiUserProperties['statutAdditionnelPersonne']] : ''; - $institution = array_key_exists( $wgMGWikiUserProperties['institution'], $userData ) ? $userData[$wgMGWikiUserProperties['institution']]->getPrefixedText() : ''; - $referrer = array_key_exists( $wgMGWikiUserProperties['referrer'], $userData ) ? $userData[$wgMGWikiUserProperties['referrer']] : ''; - $content = new WikitextContent( wfMessage( 'mgwiki-template-new-userpage', - $username, $userData[$wgMGWikiUserProperties['firstname']], $userData[$wgMGWikiUserProperties['lastname']], - $email, $statutPers, $statutAddPers, $institution, $referrer - )->inContentLanguage()->plain() ); - $flags = EDIT_NEW; - $userArticle->doEditContent( $content, $summary, $flags, false, $wgUser ); - - # Send email - $user->sendConfirmationMail( 'created_by_mgwiki' ); - - return true; - } - - /** - * Get the user from the official ADEPUL database. - * - * @param string $code_adepul ADEPUL id. - * @return object|false|null Object with the various data describing the ADEPUL user, or false if non-existing ADEPUL user, or null if error. - */ - public static function getUserFromOfficialADEPUL( $code_adepul ) { - global $wgMGWikiUserEndpointADEPUL, $wgMGWikiSecretKeyADEPUL; - $infoADEPUL = file_get_contents( $wgMGWikiUserEndpointADEPUL . '?t_idf=' . $code_adepul . '&cle=' . $wgMGWikiSecretKeyADEPUL ); - if( !$infoADEPUL ) { - return null; - } - $infoADEPUL = json_decode( $infoADEPUL ); - if( $infoADEPUL === null ) { - return null; - } - if( $infoADEPUL->existe === 'NON' ) { - return false; - } - return $infoADEPUL; - } - - /** - * Get (or create) the user corresponding to an ADEPUL id. - * - * @param string $code_adepul ADEPUL id. - * @param string|null $creator Username creating a new user, or $wgMGWikiDefaultCreatorNewAccounts if null. - * @return User|null MediaWiki user, possibly just created with the specific MGWiki process. - */ - public static function getUserByADEPUL( $code_adepul, $creator = null ) { - global $wgUser; - global $wgMGWikiUserProperties, $wgMGWikiDefaultCreatorNewAccounts; - - $codeAdepulTitle = Title::newFromText( 'Property:' . $wgMGWikiUserProperties['codeAdepul'] ); - $codeAdepul = $codeAdepulTitle->getDBkey(); - - // Create property instance - $property = new SMWDIProperty( $codeAdepul ); - $property->setPropertyTypeId( SMW\DataValues\StringValue::TYPE_ID ); - $dataItem = new SMWDIBlob( $code_adepul ); - $dataValue = SMW\DataValueFactory::getInstance()->newDataValueByItem( - $dataItem, - $property - ); - - // Create a description that represents the condition - $descriptionFactory = new SMW\Query\DescriptionFactory(); - $namespaceDescription = $descriptionFactory->newNamespaceDescription( - NS_USER - ); - $descriptionAdepul = $descriptionFactory->newSomeProperty( - $property, - $descriptionFactory->newValueDescription( $dataItem ) - ); - $description = $descriptionFactory->newConjunction( array( - $namespaceDescription, - $descriptionAdepul - ) ); - $propertyValue = SMW\DataValueFactory::getInstance()->newPropertyValueByLabel( - $codeAdepul - ); - - $description->addPrintRequest( - new SMW\Query\PrintRequest( SMW\Query\PrintRequest::PRINT_PROP, null, $propertyValue ) - ); - - // Create query object - $query = new SMWQuery( - $description - ); - - $query->querymode = SMWQuery::MODE_INSTANCES; - - // Try to match condition against the store - $queryResult = SMW\ApplicationFactory::getInstance()->getStore()->getQueryResult( $query ); - - if( $queryResult->getCount() === 0 ) { - # Create user - $backupWgUser = $wgUser; - $creator = $creator ?: $wgMGWikiDefaultCreatorNewAccounts; - $wgUser = User::newFromName( $creator ); - if( !$wgUser || $wgUser->getId() === 0 ) { - if( $creator !== $wgMGWikiDefaultCreatorNewAccounts ) { - $wgUser = User::newFromName( $wgMGWikiDefaultCreatorNewAccounts ); - } else { - throw new Exception(); // TODO improve - } - } - $adhAdepul = self::getUserFromOfficialADEPUL( $code_adepul ); - if( !$adhAdepul ) { - throw new Exception(); // TODO improve - } - $prenom = $adhAdepul->prenom; - $nom = $adhAdepul->nom; - $mail = $adhAdepul->mail; - $profession = $adhAdepul->profession; - $specialite = $adhAdepul->specialite; - $username = $prenom . ' ' . strtoupper( $nom ); - $userData = []; - $userData[$wgMGWikiUserProperties['firstname']] = $prenom; - $userData[$wgMGWikiUserProperties['lastname']] = $nom; - $userData[$wgMGWikiUserProperties['institution']] = Title::newFromText( 'ADEPUL', NS_PROJECT ); - $userData[$wgMGWikiUserProperties['codeAdepul']] = $code_adepul; - $userData[$wgMGWikiUserProperties['email']] = $mail; - MGWiki::createUser( $username, $userData ); - $wgUser = $backupWgUser; - $user = User::newFromName( $username ); - return $user; - } elseif( $queryResult->getCount() > 1 ) { - throw new Exception(); // TODO improve - } elseif( $queryResult->getCount() === 1 ) { - $userValue = $queryResult->getResults()[0]; - $username = $userValue->getDBkey(); - $user = User::newFromName( $username ); - return $user; - } - - return null; - } - - /** - * Get an ADEPUL group. - * - * @param string $code_action ADEPUL action id. - * @return Title|null MediaWiki page of the ADEPUL group. - */ - public static function getADEPULGroup( $code_action ) { - global $wgUser; - global $wgMGWikiUserProperties; - - $codeActionTitle = Title::newFromText( 'Property:' . $wgMGWikiUserProperties['codeActionAdepul'] ); - $codeAction = $codeActionTitle->getDBkey(); - - // Create property instance - $property = new SMWDIProperty( $codeAction ); - $property->setPropertyTypeId( SMW\DataValues\StringValue::TYPE_ID ); - $dataItem = new SMWDIBlob( $code_action ); - $dataValue = SMW\DataValueFactory::getInstance()->newDataValueByItem( - $dataItem, - $property - ); - - // Create a description that represents the condition - $descriptionFactory = new SMW\Query\DescriptionFactory(); - $description = $descriptionFactory->newSomeProperty( - $property, - $descriptionFactory->newValueDescription( $dataItem ) - ); - $propertyValue = SMW\DataValueFactory::getInstance()->newPropertyValueByLabel( - $codeAction - ); - - $description->addPrintRequest( - new SMW\Query\PrintRequest( SMW\Query\PrintRequest::PRINT_PROP, null, $propertyValue ) - ); - - // Create query object - $query = new SMWQuery( - $description - ); - - $query->querymode = SMWQuery::MODE_INSTANCES; - - // Try to match condition against the store - $queryResult = SMW\ApplicationFactory::getInstance()->getStore()->getQueryResult( $query ); - - if( $queryResult->getCount() === 0 ) { - return null; - } elseif( $queryResult->getCount() > 1 ) { - throw new Exception(); // TODO improve - } elseif( $queryResult->getCount() === 1 ) { - $groupValue = $queryResult->getResults()[0]; - $group = Title::newFromText( $groupValue->getDBkey() ); - return $group; - } - - return null; - } -} diff --git a/MGWiki.alias.php b/MGWikiDev.alias.php similarity index 62% rename from MGWiki.alias.php rename to MGWikiDev.alias.php index 8aa16c0..d04bd06 100644 --- a/MGWiki.alias.php +++ b/MGWikiDev.alias.php @@ -9,12 +9,13 @@ $specialPageAliases = array(); + /** English (English) */ $specialPageAliases['en'] = array( - 'Invitation' => array( 'Invitation' ), + 'SpecialAccountRequest' => array( 'Account Request' ), ); /** French (français) */ -$specialPageAliases['ff'] = array( - 'Invitation' => array( 'Invitation' ), +$specialPageAliases['fr'] = array( + 'SpecialAccountRequest' => array( 'Demande de création de compte' ), ); diff --git a/MGWikiDev.i18n.php b/MGWikiDev.i18n.php new file mode 100644 index 0000000..264be61 --- /dev/null +++ b/MGWikiDev.i18n.php @@ -0,0 +1,24 @@ + [ 0, 'mgw-onclick' ], + 'mgw-groupe-type' => [ 0, 'mgw-groupe-type' ], + 'mgw-display' => [ 0, 'mgw-display' ] +]; + +/** French (français) */ +$magicWords['fr'] = [ + 'mgw-onclick' => [ 0, 'mgw-onclick' ], + 'mgw-groupe-type' => [ 0, 'mgw-groupe-type' ], + 'mgw-display' => [ 0, 'mgw-display' ] +]; diff --git a/MGWikiMarguerite.php b/MGWikiMarguerite.php deleted file mode 100644 index ab5fc3a..0000000 --- a/MGWikiMarguerite.php +++ /dev/null @@ -1,30 +0,0 @@ - - * @license GPL-3.0+ - * @package MediaWiki-extension-MGWiki - */ - -/** - * Class for the “marguerite” (ox-eye daisy) feature. - */ -class MGWikiMarguerite { - - /** - * Hook function for MediaWiki’s hook "BeforePageDisplay". - * - * This hook simply adds the JavaScript module ext.mgwiki.edit. - * - * @param OutputPage $out - * @param Skin $skin - * @return true - */ - static function onBeforePageDisplay( OutputPage &$out, Skin &$skin ) { - - $out->addModules( 'ext.mgwiki' ); - - return true; - } -} diff --git a/MGWikiParserFunctions.php b/MGWikiParserFunctions.php deleted file mode 100644 index ac3c6d9..0000000 --- a/MGWikiParserFunctions.php +++ /dev/null @@ -1,63 +0,0 @@ - - * @license GPL-3.0+ - * @package MediaWiki-extension-MGWiki - */ - -/** - * Class for some parser functions. - */ -class MGWikiParserFunctions { - - /** - * Hook function for MediaWiki’s hook "ParserFirstCallInit". - * - * @param Parser $parser Parser object where will be registered the new parser functions. - * @return true - */ - public static function onParserFirstCallInit( &$parser ) { - - $parser->setFunctionHook( 'isusersysop', 'MGWikiParserFunctions::pfuncIsUserSysop', Parser::SFH_OBJECT_ARGS ); - - return true; - } - - /** - * Parser function returning 1 if the given user is a sysop, else an empty string. - * - * This result is compatible with SemanticMediaWiki: you can directly use the result to set some semantic - * property, and hence you can get the adminness as a data source in SemanticMediaWiki. - * - * In case of error, returns an HTML red warning, which can be caught by #iferror from ParserFunctions. - * - * @param Parser $parser Parser Parser object where is registered the parser function. - * @param PPFrame $frame - * @param array $args - * @return string Result of the parser function. - */ - public static function pfuncIsUserSysop( $parser, $frame, $args ) { - - try { - $username = isset( $args[0] ) ? trim( $frame->expand( $args[0] ) ) : ''; - if( !$username ) { - return '' . wfMessage( 'mgwiki-isusersysop-nousername' )->text() . ''; - } - if( User::isIP( $username ) ) { - return '0'; - } - $user = User::newFromName( $username ); - if( !$user ) { - return '' . wfMessage( 'mgwiki-isusersysop-badusername' )->text() . ''; - } - if( in_array( 'sysop', $user->getGroups() ) ) { - return '1'; - } - } catch( Exception $e ) { - return '' . wfMessage( 'mgwiki-parserfunction-internalerror' )->text() . ''; - } - return '0'; - } -} diff --git a/README.md b/README.md index 872d68e..fb01895 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,37 @@ -[English below] +Extension MGWiki - module de développement +========================================== -Personnalisations pour MGWiki -============================= +màj: 17/11/2020 Cette extension permet de personnaliser le site [MGWiki](https://mgwiki.univ-lyon1.fr), wiki privé dédié au partage d’expérience entre internes en médecine et médecins. -Les personnalisations concernent : - -* la gestion des droits entre différents niveaux d’utilisateurs ; -* la création d’utilisateurs au moyen de formulaires Semantic MediaWiki ; -* l’envoi d’une invitation aux nouveaux inscrits, invitation qui les connecte directement et les redirigent vers le formulaire de leur profil utilisateur ; -* la synchronisation entre les formulaires des profils utilisateur et MediaWiki (groupes utilisateur et adresse de courriel). - -Cette extension (et son versant Semantic MediaWiki) a été réalisée par l’entreprise [Wiki Valley](http://wiki-valley.com) pour MGWiki, et est publiée sous licence GPL-3.0+. Il est peu probable qu’elle serve directement à d’autres réutilisateurs, étant très spécialisée, mais elle est publiée dans l’espoir que certaines parties puissent aider d’autres développeurs ayant des besoins similaires. - -MGWiki customisations -===================== - -This extension customise the website [MGWiki](https://mgwiki.univ-lyon1.fr), private wiki dedicated to experience sharing between medecine interns and physicians. - -These customisations are: - -* the rights management between different user levels; -* the creation of users by means of Semantic MediaWiki forms; -* the sending of an invitation to new users, which logs them directly in and redirects them to the user profile form; -* the synchronisation between the user profile forms and MediaWiki (user groups and email address). - -This extension (and its counterpart Semantic MediaWiki) was realised by the company [Wiki Valley](http://wiki-valley.com) for MGWiki, and is published under the GPL-3.0+ licence. It is unlikely it will be directely reused by other people, being very specialised, but it is published in the hope some parts can help other developers with similar needs. +Modules inclus: +* Pages spéciales: + - includes/SpecialAccountRequest.php : page spéciale formulaire de demande d'inscription accessible au publique +* Parsers: + - {{#mgw-onclick:|
|}} +* API: + - includes/Api/ApiGetJson: API (?action=getjson) pour récupérer un contenu .json, accès publique autorisé +* Classes php: + - includes/Utilities/GetJsonPage.php: classe pour l'obtention et la manipulation de données au format JSON depuis une page du wiki + permet la gestion de données d'interface directement depuis le wiki: + - MediaWiki:Specialaccountrequest.json : définition du formulaire Special:Account_Request + - MediaWiki:MGWiki-messages.json : messages d'interface + - includes/Utilities/JsonToForm : classe pour la création de formulaires depuis une description au format .json +* Autres: + - maintenance/CheckHooks.php: + - vérification de l'existence des Hooks utilisés par l'extension + - insertion de Hooks customizés si nécessaire (onApiAllow) + - skinning: + - resources/ext.mgwiki-dev.js & css + - images/php + - login avec email proposé ( Hooks::onAuthChangeFormFields ) + +Todo: +* /var/www/html/wiki/includes/preferences/DefaultPreferencesFactory.php : modif automatique +* /var/www/html/wiki/includes/specials/SpecialChangeEmail.php : modif automatique +* includes/SpecialAccount.php: use WebResponse::setCookie() instead of setcookie() when upgrading Mediawiki >= 1.35 + +Core changes: +* /includes/api/ApiMain.php l.1419 : + ++ && !Hooks::run('ApiAllow', [ $module, $user ] ) diff --git a/SpecialInvitation.php b/SpecialInvitation.php deleted file mode 100644 index 12b0b45..0000000 --- a/SpecialInvitation.php +++ /dev/null @@ -1,160 +0,0 @@ - - * @license GPL-3.0+ - */ - -use MediaWiki\Auth\AuthManager; -use MediaWiki\Auth\AuthenticationResponse; -use MediaWiki\Auth\CreatedAccountAuthenticationRequest; - -/** - * Special page allows users to request email confirmation message, and handles - * processing of the confirmation code when the link in the email is followed - * - * Derived from SpecialConfirmemail.php with heavy customisations. - * @author Brion Vibber - * @author Rob Church - */ -#class Invitation extends AuthManagerSpecialPage { -class Invitation extends LoginSignupSpecialPage { - public function __construct() { - parent::__construct( 'Invitation' ); - $this->setListed( false ); - } - - public function doesWrites() { - return true; - } - - /** - * Get the default action for this special page, if none is given via URL/POST data. - * Subclasses should override this (or override loadAuth() so this is never called). - * @param string $subPage Subpage of the special page. - * @return string an AuthManager::ACTION_* constant. - */ - protected function getDefaultAction( $subPage ) { - return AuthManager::ACTION_LOGIN_CONTINUE; - } - - protected function getLoginSecurityLevel() { - return false; - } - - protected function isSignup() { - return false; - } - - /** - * @param bool $direct True if the action was successful just now; false if that happened - * pre-redirection (so this handler was called already) - * @param StatusValue|null $extraMessages - * @return void - */ - protected function successfulAction( $direct = false, $extraMessages = null ) { - } - - /** - * Logs to the authmanager-stats channel. - * @param bool $success - * @param string|null $status Error message key - */ - protected function logAuthResult( $success, $status = null ) { - } - - protected function beforeExecute( $subPage ) { - $data = []; - $data['emailtoken'] = $subPage; - $data[$this->getTokenName()] = $this->getToken()->toString(); - $this->setRequest( $data, true ); - parent::beforeExecute( $subPage ); - } - - /** - * Main execution point - * - * @param null|string $code Confirmation code passed to the page - * @throws PermissionsError - * @throws ReadOnlyError - * @throws UserNotLoggedIn - */ - function execute( $code ) { - // Ignore things like master queries/connections on GET requests. - // It's very convenient to just allow formless link usage. - Profiler::instance()->getTransactionProfiler()->resetExpectations(); - #$authManager = AuthManager::singleton(); - - $this->setHeaders(); - - $this->checkReadOnly(); - $this->checkPermissions(); - - # Check the code has a good format - if( !$code || !preg_match( '/^[0-9a-f]{32}$/', $code ) ) { - $this->getOutput()->addWikiMsg( 'mgwiki-bad-email-token' ); - $this->getOutput()->returnToMain(); - return; - } - // This could also let someone check the current email address, so - // require both permissions. - #if ( !$this->getUser()->isAllowed( 'viewmyprivateinfo' ) ) { - # throw new PermissionsError( 'viewmyprivateinfo' ); - #} - - $this->loadAuth( '', AuthManager::ACTION_LOGIN ); - #var_dump($this->authAction); - #var_dump($this->authRequests); - $status = $this->trySubmit(); - #var_dump($this->authRequests); - - $response = $status->getValue(); - #var_dump($status); - #var_dump($response); - switch( $response->status ) { - case AuthenticationResponse::PASS: - # Update session data to immediately connect the user - $this->setSessionUserForCurrentRequest(); - - # Confirm the email (it was the authentication token) - $user = User::newFromName( $response->username ); - $user->confirmEmail(); - $user->saveSettings(); - - $this->successfulAction( true ); - $this->getOutput()->redirect( SpecialPage::getSafeTitleFor( 'ChangePassword' )->getFullURL() ); - break; - case AuthenticationResponse::FAIL: - $this->getOutput()->addWikiMsg( 'mgwiki-bad-email-token' ); - $this->getOutput()->returnToMain(); - break; - default: - throw new LogicException( 'invalid AuthenticationResponse' ); - } - #if( $response->status == AuthenticationResponse::PASS ) { - #foreach( $this->authRequests as &$req ) { - # if( $req instanceof MediaWiki\Auth\EmailTokenAuthenticationRequest ) { - # $req->username = $response->username; - # } - #} - #$returnToUrl = $this->getPageTitle( 'return' ) - # ->getFullURL( $this->getPreservedParams( true ), false, PROTO_HTTPS ); - #$name = $response->username; - #$id = User::newFromName( $name ); - #$reqCreation = new CreatedAccountAuthenticationRequest( $id, $name ); - #var_dump($this->authRequests); - #$response2 = $authManager->beginAuthentication( $this->authRequests, $returnToUrl ); - /*if( $response2->status == AuthenticationResponse::PASS ) - var_dump('aull right');*/ - # $this->setSessionUserForCurrentRequest(); - #} - #parent::execute( $code ); - - #if ( $code === null || $code === '' ) { - # $this->requireLogin( 'confirmemail_needlogin' ); - #} else { - # $this->attemptConfirm( $code ); - #} - } -} diff --git a/composer.json b/composer.json index cbecdaf..d2b4d16 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,11 @@ { - "description": "Customisations for MGWiki", + "name": "MGWikiDev", + "type": "mediawiki-extension", + "description": "Customisations for MGWiki - development extension", "keywords": [ "Semantic MediaWiki", - "MediaWiki" + "MediaWiki", + "MGWiki" ], "homepage": "https://mgwiki.univ-lyon1.fr", "license": "GPL-3.0+", @@ -11,29 +14,20 @@ "name": "Sébastien Beyou", "homepage": "https://www.seb35.fr", "role": "Original author" + }, + { + "name": "Alexandre Bulet", + "role": "Co-author" } ], "support": { - "issues": "https://github.com/WikiValley/MGWiki/issues", - "source": "https://github.com/WikiValley/MGWiki" + "issues": "https://github.com/Looxloox/MGWiki-dev/issues", + "source": "https://github.com/Looxloox/MGWiki-dev" }, "require": { "php": ">=7.0.0" }, "require-dev": { - "jakub-onderka/php-parallel-lint": "0.9.2", "mediawiki/mediawiki-codesniffer": "0.7.2" - }, - "suggest": { - "mediawiki/semantic-forms": "Easy creation of forms to add semantic data" - }, - "scripts": { - "test": [ - "parallel-lint . --exclude vendor", - "phpcs -p -s" - ], - "fix": [ - "phpcbf" - ] } } diff --git a/config.schema.json b/config.schema.json deleted file mode 100644 index 19572a1..0000000 --- a/config.schema.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema#", - "description": "Schema for MGWiki extension configuration parameters.", - "type": "object", - "properties": { - "MGWikiInitialDelayBeforeInactive": { - "type": "number", - "description": "Number of seconds after which an account is considered inactive." - }, - "MGWikiForms": { - "type": "object", - "description": "Registration of the semantic forms: target pages, rights, and how to read the values.", - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9 ]+$": { - "type": "object", - "description": "Configuration for one specific form.", - "additionalProperties": false, - "required": ["RegexPageName"], - "properties": { - "RegexPageName": { - "type": "string", - "description": "Regular expression (must be understood by PHP, beginning and ending with delimiters) of the target page of the form (see {{{info|page name=}}} in the SemanticForms). Must be normalised with the canonical namespace (in English) if any." - }, - "RequiredRight": { - "type": "string", - "description": "Required right to edit the target page (apart for the user edit her/his own userpage if EditOwnUserpage is true); this right should be registered in \"AvailableRights\" in extension.json. No required right if not provided." - }, - "InstitutionFromModerator": - "type": "boolean", - "description": "Read the property 'institution' defined for the moderator (defined by the property 'moderator') and apply it to created accounts.", - "default": false - }, - "InstitutionFromCreator": - "type": "boolean", - "description": "Read the property 'institution' defined for the account creator and apply it to created accounts.", - "default": false - }, - "EditOwnUserpage": { - "type": "boolean", - "description": "Exceptional right for a user editing her/his own userpage if s/he don’t have the required right.", - "default": false - }, - "SubObjects": { - "type": "boolean", - "description": "Read subobjects in the form to create users accordingly.", - "default": false - }, - "EphemeralPage": { - "type": "boolean", - "description": "Delete the page created just after treatment and redirect to main page.", - "default": false - } - } - } - } - }, - "MGWikiFieldsGroups": { - "type": "object", - "description": "Registration of the mappings between semantic properties and MediaWiki groups: rights, groups names, and how to get the right group given criteria.", - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9 ]+$": { - "type": "object", - "description": "Name of the semantic property to be synchronised with MediaWiki groups. At least one property between MapFromProperty and MapFromTitle should be provided.", - "additionalProperties": false, - "required": ["Groups"], - "properties": { - "RequiredRight": { - "type": "string", - "description": "Required right to synchronise the the semantic property with MediaWiki groups (apart for the user editing her/his own userpage if EditOwnUserpage is true); without it, and if the user is authorised to edit the page, change this property will have no effect in the MediaWiki groups; this right should be registered in \"AvailableRights\" in extension.json. In addition to this setting, note that for usability matters it would be better to disable the property in SemanticForms itself ({{{field|restricted}}}) to avoid desynchronising data sources. No required right if not provided." - }, - "EditOwnUserpage": { - "type": "boolean", - "description": "Exceptional right for a user to synchronise the semantic property on her/his own userpage with MediaWiki groups; without it, and if the user is authorised to edit the page, change this property will have no effect in the MGWiki extension realm.", - "default": false - }, - "Groups": { - "type": "array", - "description": "MediaWiki groups which are managed by this semantic property." - }, - "MapFromProperty": { - "type": "object", - "description": "Mapping between semantic property values and their corresponding MediaWiki groups.", - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9]*$": { - "type": "string", - "description": "Name of the MediaWiki group when the semantic property has the value given by the key." - } - } - }, - "MapFromTitle": { - "type": "object", - "description": "Mapping between the target page title and their corresponding MediaWiki groups.", - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9/$^]+$": { - "type": "string", - "description": "Name of the MediaWiki group when the target page has a title matching the regular expression given by the key." - } - } - } - } - } - } - }, - "MGWikiUserProperties": { - "type": "object", - "description": "Mapping between internal identifiers and real semantic property names.", - "additionalProperties": false, - "required": ["email", "firstname", "lastname", "statutPersonne", "statutAdditionnelPersonne"], - "patternProperties": { - "email": { - "type": "string", - "description": "Name of the email property, must be unique accross all semantic forms else they will be unrecongnised." - }, - "firstname": { - "type": "string", - "description": "Name of the firstname property, must be unique accross all semantic forms else they will be unrecongnised." - }, - "lastname": { - "type": "string", - "description": "Name of the lastname property, must be unique accross all semantic forms else they will be unrecongnised." - }, - "statutPersonne": { - "type": "string", - "description": "Name of the 'statutPersonne' property, must be unique accross all semantic forms else they will be unrecongnised." - }, - "statutAdditionnelPersonne": { - "type": "string", - "description": "Name of the 'statutAdditionnelPersonne' property, must be unique accross all semantic forms else they will be unrecongnised." - } - } - } - } -} diff --git a/extension.json b/extension.json index d258909..1c4f524 100644 --- a/extension.json +++ b/extension.json @@ -1,266 +1,88 @@ { "@note": "When updating this file please also update MGWiki.php with the same changes.", - "name": "MGWiki", + "name": "MGWikiDev", "version": "0.1", "author": [ - "Sébastien Beyou" + "Alexandre BRULET" ], "url": "https://mgwiki.univ-lyon1.fr", - "descriptionmsg": "mgwiki-desc", + "descriptionmsg": "mgwiki-dev-desc", "license-name": "GPL-3.0+", "type": "other", - "AvailableRights": [ - "mgwikimanagelevel1", - "mgwikimanagelevel2" - ], - "config": { - "MGWikiSecretKeyADEPUL": null, - "MGWikiInitialDelayBeforeInactive": 1209600, - "MGWikiDefaultCreatorNewAccounts": "Alexandre BRULET", - "MGWikiForms": { - "Personne": { - "RegexPageName": "/^User:[^\\/]+$/", - "RequiredRight": "mgwikimanagelevel1", - "EditOwnUserpage": true, - "SubObjects": false, - "Fields": ["Statut personne", "Statut additionnel personne"] - }, - "Nouveaux utilisateurs": { - "RegexPageName": "/^Nouveaux utilisateurs/", - "RequiredRight": "mgwikimanagelevel1", - "InstitutionFromCreator": true, - "SubObjects": true, - "EphemeralPage": true - }, - "Groupe": { - "RegexPageName": "/^(GEP|GAPP)/", - "RequiredRight": "mgwikimanagelevel1", - "InstitutionFromCreator": true, - "SubObjects": true, - "MergeNewUsers": { - "participantTemplate": "membersList" - } - }, - "GEP": { - "RegexPageName": "/^Groupe:GEP/", - "RequiredRight": "mgwikimanagelevel1", - "InstitutionFromCreator": true, - "SubObjects": true, - "MergeNewUsers": { - "participantTemplate": "membersList" - } - }, - "GAPP": { - "RegexPageName": "/^Groupe:GAPP/", - "RequiredRight": "mgwikimanagelevel1", - "InstitutionFromCreator": true, - "SubObjects": true, - "MergeNewUsers": { - "participantTemplate": "membersList" - } - }, - "Stage praticien": { - "RegexPageName": "/^Groupe:Stage praticien/", - "RequiredRight": "mgwikimanagelevel1", - "InstitutionFromCreator": true, - "SubObjects": true, - "MergeNewUsers": { - "participantTemplate": "membersList" - } - }, - "DPC": { - "RegexPageName": "/^Groupe:DPC/", - "RequiredRight": "mgwikimanagelevel1", - "InstitutionFromCreator": true, - "SubObjects": true, - "MergeNewUsers": { - "participantTemplate": "membersList" - } - }, - "Groupe de niveau 2": { - "RegexPageName": "/^(Tuteurs|Modérateurs)/", - "RequiredRight": "mgwikimanagelevel2", - "InstitutionFromCreator": true, - "SubObjects": true, - "MergeNewUsers": { - "participantTemplate": "membersList" - } - }, - "Nouveaux tuteurs ou modérateurs": { - "RegexPageName": "/^Nouveaux tuteurs ou modérateurs/", - "RequiredRight": "mgwikimanagelevel2", - "InstitutionFromCreator": true, - "SubObjects": true, - "EphemeralPage": true - } - }, - "MGWikiFieldsGroups": { - "Statut personne": { - "RequiredRight": "mgwikimanagelevel1", - "EditOwnUserpage": true, - "Groups": ["interne", "médecin", "scientifique"], - "MapFromProperty": { - "Visiteur": "visiteur", - "Interne": "interne", - "Médecin": "médecin", - "Scientifique": "scientifique" - }, - "MapFromTitle": { - "/^GEP/": "interne", - "/^GAPP/": "médecin" - } - }, - "Statut additionnel personne": { - "RequiredRight": "mgwikimanagelevel2", - "Groups": ["", "U2"], - "MapFromProperty": { - "": "", - "Tuteur": "U2", - "Modérateur": "U2", - "Formateur": "U2", - "MSU": "U2" - }, - "MapFromTitle": { - "/^Tuteurs/": "U2", - "/^Modérateurs/": "U2" - } - }, - "Groupe U2": { - "RequiredRight": "mgwikimanagelevel2", - "Groups": ["", "U2"], - "MapFromProperty": { - "": "", - "No": "", - "Non": "", - "1": "U2", - "Yes": "U2", - "Oui": "U2" - } - } - }, - "MGWikiUserProperties": { - "email": "Courriel", - "firstname": "Prénom", - "lastname": "Nom", - "statutPersonne": "Statut personne", - "statutAdditionnelPersonne": "Statut additionnel personne", - "timestamp": "Dernière modification", - "institution": "Institution de rattachement", - "referrer": "Responsable référent", - "moderator": "Tuteur ou modérateur", - "requiredUserUpdate": "Mise à jour requise", - "participantTemplate": "Participant Groupe", - "membersList": "Membres", - "codeAdepul": "Code ADEPUL", - "codeActionAdepul": "Code action" - } - }, - "SpecialPages": { - "Invitation": "Invitation" - }, - "GroupPermissions": { - "interne": {}, - "médecin": {}, - "scientifique": {}, - "U2": { - "mgwikimanagelevel1": true - }, - "sysop": { - "mgwikimanagelevel1": true, - "mgwikimanagelevel2": true - } - }, "MessagesDirs": { - "MGWiki": [ + "MGWikiDev": [ "i18n" ] }, - "ExtensionMessagesFiles": { - "MGWikiAlias": "MGWiki.alias.php", - "MGWikiMagic": "MGWiki.magic.php" + "AutoloadNamespaces": { + "MediaWiki\\Extension\\MGWikiDev\\": "includes/", + "MediaWiki\\Extension\\MGWikiDev\\Utilities\\": "includes/Utilities/", + "MediaWiki\\Extension\\MGWikiDev\\Classes\\": "includes/Classes/", + "MediaWiki\\Extension\\MGWikiDev\\Api\\": "includes/Api/", + "MediaWiki\\Extension\\MGWikiDev\\Foreign\\": "includes/Foreign/" }, "AutoloadClasses": { - "MGWiki": "MGWiki.php", - "MGWikiParserFunctions": "MGWikiParserFunctions.php", - "MGWikiMarguerite": "MGWikiMarguerite.php", - "Invitation": "SpecialInvitation.php", - "ApiNewADEPULGroup": "ApiNewADEPULGroup.php", - "ApiADEPULGroupMember": "ApiADEPULGroupMember.php", - "MediaWiki\\Auth\\EmailTokenAuthenticationRequest": "EmailTokenAuthenticationRequest.php", - "MediaWiki\\Auth\\EmailTokenPrimaryAuthenticationProvider": "EmailTokenPrimaryAuthenticationProvider.php" + "MGWikiDevHooks": "Hooks.php" + }, + "SpecialPages": { + "SpecialAccountRequest": "MediaWiki\\Extension\\MGWikiDev\\SpecialAccountRequest", + "SpecialCheckAccounts": "MediaWiki\\Extension\\MGWikiDev\\SpecialCheckAccounts", + "SpecialCheckGroups": "MediaWiki\\Extension\\MGWikiDev\\SpecialCheckGroups", + "SpecialMgwikiTest": "MediaWiki\\Extension\\MGWikiDev\\SpecialMgwikiTest", + "SpecialAdminGroupTypes": "MediaWiki\\Extension\\MGWikiDev\\SpecialAdminGroupTypes" }, "Hooks": { - "userCan": [ - "MGWiki::onuserCan" - ], - "sfHTMLBeforeForm": [ - "MGWiki::onsfHTMLBeforeForm" - ], - "PrefsEmailAudit": [ - "MGWiki::onPrefsEmailAudit" - ], - "SMW::SQLStore::AfterDataUpdateComplete": [ - "MGWiki::onSMW_SQLStore_AfterDataUpdateComplete" - ], - "PageContentSave": [ - "MGWiki::onPageContentSave" - ], - "PostLoginRedirect": [ - "MGWiki::onPostLoginRedirect" - ], - "SpecialPageAfterExecute": [ - "MGWiki::onSpecialPageAfterExecute" - ], - "ParserFirstCallInit": [ - "MGWikiParserFunctions::onParserFirstCallInit" - ], - "BeforePageDisplay": [ - "MGWikiMarguerite::onBeforePageDisplay" - ] + "BeforePageDisplay": "MGWikiDevHooks::onBeforePageDisplay", + "ApiAllow": "MGWikiDevHooks::onApiAllow", + "ParserFirstCallInit": "MGWikiDevHooks::onParserFirstCallInit", + "LoadExtensionSchemaUpdates": "MGWikiDevHooks::onLoadExtensionSchemaUpdates", + "AuthChangeFormFields": "MGWikiDevHooks::onAuthChangeFormFields", + "SpecialPageBeforeExecute": "MGWikiDevHooks::onSpecialPageBeforeExecute" }, - "AuthManagerAutoConfig": { - "primaryauth": { - "MediaWiki\\Auth\\EmailTokenPrimaryAuthenticationProvider": { - "class": "MediaWiki\\Auth\\EmailTokenPrimaryAuthenticationProvider", - "args": [ - { - "authoritative": false - } - ], - "sort": 0 - } - } + "callback": "MGWikiDevHooks::onExtensionLoad", + "APIModules": { + "getjson" : "MediaWiki\\Extension\\MGWikiDev\\Api\\ApiGetJson" }, "ResourceFileModulePaths": { "localBasePath": "resources", - "remoteExtPath": "MGWiki/resources" + "remoteExtPath": "MGWikiDev/resources" }, "ResourceModules": { - "ext.mgwiki": { - "scripts": [ - "ext.mgwiki.js" - ], - "dependencies": [ - "ext.mgwiki.edit" - ] + "ext.mgwiki-dev": { + "packageFiles": [ "ext.mgwiki-dev.js", "jquery.tancolor.js" ], + "styles": [ "ext.mgwiki-dev.css" ] }, - "ext.mgwiki.edit": { - "scripts": [ - "ext.mgwiki.edit.js" - ], + "ext.mgwiki-jsonform": { + "packageFiles": [ "jsonform.js" ], + "styles": [ "jsonform.css" ] + }, + "ext.mgwiki-specialaccountrequest": { + "packageFiles": [ "specialaccountrequest.js" ] + }, + "ext.mgwiki-specialadmin": { + "packageFiles": [ "ext.mgwiki-specialadmin.js" ], + "styles": [ "ext.mgwiki-specialadmin.css" ] + }, + "ext.mgwiki-specialcheckaccounts": { + "packageFiles": [ "specialcheckaccounts.js" ], + "styles": [ "specialcheckaccounts.css" ] + }, + "ext.mgwiki-specialcheckgroups": { + "packageFiles": [ "specialcheckgroups.js" ], + "styles": [ "specialcheckgroups.css" ] + }, + "ext.mgwiki-specialadmingrouptypes": { "dependencies": [ - "mediawiki.api", - "mediawiki.Title" + "oojs-ui-core", + "oojs-ui-windows" ], - "targets": [ - "desktop", - "mobile" - ] + "packageFiles": [ "specialadmingrouptypes.js" ], + "styles": [ "specialadmingrouptypes.css" ] } }, - "APIModules": { - "nouveau-groupe-adepul": "ApiNewADEPULGroup", - "membre-groupe-adepul": "ApiADEPULGroupMember" + "ExtensionMessagesFiles": { + "MGWikiDevAlias": "MGWikiDev.alias.php", + "MGWikiDevMagic": "MGWikiDev.i18n.php" }, "manifest_version": 1 } diff --git a/i18n/en.json b/i18n/en.json index d60a8a4..e3ada1d 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1,41 +1,17 @@ { "@metadata": { "authors": [ - "Sébastien Beyou" + "Sébastien Beyou", + "Alexandre Brulet" ] }, - "mgwiki-desc": "Specific hooks for MGWiki", - "invitation": "Invitation", - "right-mgwikimanagelevel1": "Manage MGWiki level-1 users (interns, physicians, scientists)", - "right-mgwikimanagelevel2": "Manage MGWiki level-2 users (moderators)", - "group-interne": "Interns", - "group-médecin": "Physicians", - "group-scientifique": "Scientists", - "group-U2": "U2", - "group-interne-member": "{{GENDER:$1|intern}}", - "group-médecin-member": "{{GENDER:$1|physician}}", - "group-scientifique-member": "{{GENDER:$1|scientist}}", - "group-U2-member": "{{GENDER:$1|U2|U2}}", - "grouppage-interne": "{{ns:project}}:Internes", - "grouppage-médecin": "{{ns:project}}:Médecins", - "grouppage-scientifique": "{{ns:project}}:Scientifiques", - "grouppage-U2": "{{ns:project}}:U2", - "authmanager-emailtoken-label": "Email token", - "authmanager-emailtoken-help": "Email token of the user", - "confirmemail_body_created_by_mgwiki": "MGWiki <{{SERVER}}> is a website for medecine students\nand physicians aiming at exchanging their practices and knownledges. A moderator\nfrom MGWiki created this account \"$2\" for you with this email address.\n\nTo log in and confirm this account, open this link in your browser:\n\n{{SERVER}}/Special:Invitation/{{#titleparts:$3||-1}}\n\nThis confirmation code will expire at $4.", - "mgwiki-userpage-without-useraccount": "The userpage exists but not the MediaWiki user account.", - "mgwiki-useraccount-without-userpage": "The user account exists but not the userpage.", - "mgwiki-create-userpage": "Userpage initialization", - "mgwiki-create-grouppage": "Group page initialization", - "mgwiki-template-new-userpage": "{{Personne\n|Titre=\n|Prénom=$2\n|Nom=$3\n|E-mail=$4\n|Date de dernière modification=\n|Statut personne=$5\n|Statut additionnel personne=$6\n|Spécialité ou profession=\n|Institution de rattachement=$7\n|Responsable référent=$8\n|Année de promotion=\n|Année de thèse=\n|Présentation=\n}}\n{{Personne2\n|Rapports et conflits d'intérêts=\n}}", - "mgwiki-bad-email-token": "The initial connection link seems to be broken (bad email token). Please check the link was not truncated by copying and pasting in your browser the whole link from your email.", - "mgwiki-changed-email": "Change email address", - "mgwiki-summary-rewrite-grouppage": "Convert new users to group members", - "mgwiki-ephemeral-page-deleted": "Ephemeral page", - "mgwiki-isusersysop-nousername": "No username given", - "mgwiki-isusersysop-badusername": "Bad username given", - "mgwiki-parserfunction-internalerror": "PHP code internal error", - "mgwiki-wrong-secret-key": "Wrong secret key", - "mgwiki-bad-adepul-group": "Badly recognised ADEPUL group", - "mgwiki-add-member-grouppage": "Add a new member in the group" + "mgwiki-dev-desc": "{{desc|name=MGWikiDev|url=https://mgwiki.univ-lyon1.fr}}", + "specialpages-group-mgwiki": "MGWiki", + "specialaccountrequest": "Demande de création de compte", + "specialcheckaccounts": "Maintenance : utilisateurs", + "specialcheckgroups": "Maintenance : groupes", + "specialadmingrouptypes": "Maintenance: types de groupes", + "specialmgwikilogin": "Login", + "specialmgwikilogin-unknownuser": "utilisateur inconnu", + "mgw-cannot-move-page": "Vous ne pouvez pas renommer cette page" } diff --git a/i18n/fr.json b/i18n/fr.json index 548e378..c8faed0 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -1,40 +1,21 @@ { "@metadata": { "authors": [ - "Sébastien Beyou" + "Sébastien Beyou", + "Alexandre Brulet" ] }, - "mgwiki-desc": "Accroches spécifiques à MGWiki", - "invitation": "Invitation", - "right-mgwikimanagelevel1": "Gérer les utilisateurs MGWiki de niveau 1 (internes, médecins, scientifiques)", - "right-mgwikimanagelevel2": "Gérer les utilisateurs MGWiki de niveau 2 (tuteurs, modérateurs)", - "group-interne": "Internes", - "group-médecin": "Médecins", - "group-scientifique": "Scientifiques", - "group-U2": "U2", - "group-interne-member": "{{GENDER:$1|interne}}", - "group-médecin-member": "{{GENDER:$1|médecin}}", - "group-scientifique-member": "{{GENDER:$1|scientifique}}", - "group-U2-member": "{{GENDER:$1|U2|U2}}", - "grouppage-interne": "{{ns:project}}:Internes", - "grouppage-médecin": "{{ns:project}}:Médecins", - "grouppage-scientifique": "{{ns:project}}:Scientifiques", - "grouppage-U2": "{{ns:project}}:U2", - "authmanager-emailtoken-label": "Jeton de courriel", - "authmanager-emailtoken-help": "Jeton de courriel de l’utilisateur", - "confirmemail_body_created_by_mgwiki": "MGWiki <{{SERVER}}> est un site permettant aux internes\nen médecine et médecins d’échanger sur leurs pratiques. Un tuteur de MGWiki\nvous a créé un compte « $2 » avec cette adresse de courriel.\n\nPour vous connecter et confirmer ce compte, veuillez suivre ce lien dans votre navigateur :\n\n{{SERVER}}/Special:Invitation/{{#titleparts:$3||-1}}\n\nSi ce vous n’avez pas sollicité de tel compte, n’ouvrez pas ce lien.\n\nCe code de confirmation expirera le $4.", - "mgwiki-userpage-without-useraccount": "La page utilisateur existe, mais pas le compte utilisateur MediaWiki.", - "mgwiki-useraccount-without-userpage": "Le compte utilisateur existe, mais pas la page utilisateur.", - "mgwiki-create-userpage": "Initialisation de la page utilisateur", - "mgwiki-create-grouppage": "Initialisation de la page du groupe", - "mgwiki-template-new-userpage": "{{Personne\n|Titre=\n|Prénom=$2\n|Nom=$3\n|E-mail=$4\n|Date de dernière modification=\n|Statut personne=$5\n|Statut additionnel personne=$6\n|Spécialité ou profession=\n|Institution de rattachement=$7\n|Responsable référent=$8\n|Année de promotion=\n|Année de thèse=\n|Présentation=\n}}\n{{Personne2\n|Rapports et conflits d'intérêts=\n}}", - "mgwiki-bad-email-token": "Le lien de connexion initiale que vous avez suivi semble ne pas être valide (mauvais jeton de courriel).\n\nVeuillez vérifier que le lien n’a pas été tronqué en copiant et collant dans votre navigateur l’ensemble du lien du courriel.", - "mgwiki-changed-email": "Changement d’adresse de courriel", - "mgwiki-summary-rewrite-grouppage": "Conversion des nouveaux utilisateurs en membres du groupe", - "mgwiki-ephemeral-page-deleted": "Page éphémère", - "mgwiki-isusersysop-nousername": "Pas de nom d’utilisateur indiqué", - "mgwiki-isusersysop-badusername": "Mauvais nom d’utilisateur", - "mgwiki-parserfunction-internalerror": "Erreur interne au code PHP", - "mgwiki-bad-adepul-group": "Groupe ADEPUL mal reconnu", - "mgwiki-add-member-grouppage": "Ajout d’un nouveau membre au groupe" + "mgwiki-dev-desc": "{{desc|name=MGWikiDev|url=https://mgwiki.univ-lyon1.fr}}", + "specialpages-group-mgwiki": "MGWiki", + "specialaccountrequest": "Demande de création de compte", + "specialcheckaccounts": "Maintenance : utilisateurs", + "specialcheckgroups": "Maintenance : groupes", + "specialadmingrouptypes": "Maintenance: types de groupes", + "apihelp-getjson-summary": "Permet de récupérer des données json", + "apihelp-getjson-param-service": "'messages' -> messages d'interface pour MGWiki
'specialAccountRequest' -> choix d'institutions pour la page Special:Demande_de_création_de_compte", + "usermerge-badoldid": "user_id de l'ancien utilisateur inexistant", + "usermerge-badnewuserid": "user_id du nouvel utilisateur inexistant", + "specialmgwikilogin": "Login", + "specialmgwikilogin-unknownuser": "utilisateur inconnu", + "mgw-cannot-move-page": "Vous ne pouvez pas renommer cette page" } diff --git a/i18n/qqq.json b/i18n/qqq.json index 9539105..e3ada1d 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -1,39 +1,17 @@ { "@metadata": { "authors": [ - "Sébastien Beyou" + "Sébastien Beyou", + "Alexandre Brulet" ] }, - "mgwiki-desc": "{{desc|name=MGWiki|url=https://mgwiki.univ-lyon1.fr}}", - "invitation": "Title of the [[Special:Invitation]] special page", - "right-mgwikimanagelevel1": "{{doc-right|right-mgwikimanagelevel1}}Right to manage basic users", - "right-mgwikimanagelevel2": "{{doc-right|right-mgwikimanagelevel2}}Right to manage advanced users (moderators)", - "group-interne": "{{doc-group|interne}}", - "group-médecin": "{{doc-group|médecin}}", - "group-scientifique": "{{doc-group|scientifique}}", - "group-U2": "{{doc-group|U2}}", - "group-interne-member": "{{doc-group|interne|member}}", - "group-médecin-member": "{{doc-group|médecin|member}}", - "group-scientifique-member": "{{doc-group|scientifique|member}}", - "group-U2-member": "{{doc-group|U2|member}}", - "grouppage-interne": "{{doc-group|interne|page}}", - "grouppage-médecin": "{{doc-group|médecin|page}}", - "grouppage-scientifique": "{{doc-group|scientifique|page}}", - "grouppage-U2": "{{doc-group|U2|page}}", - "authmanager-emailtoken-label": "Jeton de courriel", - "authmanager-emailtoken-help": "Jeton de courriel de l’utilisateur", - "confirmemail_body_created_by_mgwiki": "Body of the email sent to user when their account is created by a MGWiki moderator.\n\nParameters:\n* $1 - the IP address of the user that created or confirmed the email address\n* $2 - the name of the user\n* $3 - a URL to [[Special:ConfirmEmail]]\n* $4 - a time and date (duplicated by $6 and $7)\n* $5 - a URL to [[Special:InvalidateEmail]]\n* $6 - (Optional) a date\n* $7 - (Optional) a time\n{{Related|Confirmemail body}}", - "mgwiki-userpage-without-useraccount": "Warning message on top of a form", - "mgwiki-useraccount-without-userpage": "Warning message on top of a form", - "mgwiki-create-userpage": "Summary when a userpage is created", - "mgwiki-template-new-userpage": "Wikitext template when a userpage is initialised.\nParameters:\n* $1 - username\n* $2 - first name\n* $3 - family name\n* $4 - email\n* $5 - status (interne/médecin/scientifique)\n* $6 additional status (tuteur/modérateur)\n* $7 Institution\n* $8 Referrer", - "mgwiki-bad-email-token": "Warning message when a user has a bad token when trying to connect with", - "mgwiki-summary-rewrite-grouppage": "Summary when group page is edited to convert new users to group members", - "mgwiki-ephemeral-page-deleted": "Log message when an ephemeral page is deleted", - "mgwiki-isusersysop-nousername": "Error message for #isusersysop", - "mgwiki-isusersysop-badusername": "Error message for #isusersysop", - "mgwiki-parserfunction-internalerror": "Error message for parser functions", - "mgwiki-wrong-secret-key": "Error message in API", - "mgwiki-bad-adepul-group": "Error message in API", - "mgwiki-add-member-grouppage": "Summary" + "mgwiki-dev-desc": "{{desc|name=MGWikiDev|url=https://mgwiki.univ-lyon1.fr}}", + "specialpages-group-mgwiki": "MGWiki", + "specialaccountrequest": "Demande de création de compte", + "specialcheckaccounts": "Maintenance : utilisateurs", + "specialcheckgroups": "Maintenance : groupes", + "specialadmingrouptypes": "Maintenance: types de groupes", + "specialmgwikilogin": "Login", + "specialmgwikilogin-unknownuser": "utilisateur inconnu", + "mgw-cannot-move-page": "Vous ne pouvez pas renommer cette page" } diff --git a/images/php/ImageFunctions.php b/images/php/ImageFunctions.php new file mode 100644 index 0000000..cf6f674 --- /dev/null +++ b/images/php/ImageFunctions.php @@ -0,0 +1,39 @@ + diff --git a/images/php/MenuListBorder.php b/images/php/MenuListBorder.php new file mode 100644 index 0000000..1b44c28 --- /dev/null +++ b/images/php/MenuListBorder.php @@ -0,0 +1,20 @@ + diff --git a/images/php/menu-list-background.png b/images/php/menu-list-background.png new file mode 100644 index 0000000..daac499 Binary files /dev/null and b/images/php/menu-list-background.png differ diff --git a/includes/Api/ApiGetJson.php b/includes/Api/ApiGetJson.php new file mode 100644 index 0000000..40f2ff4 --- /dev/null +++ b/includes/Api/ApiGetJson.php @@ -0,0 +1,73 @@ +extractRequestParams(); + $r = $this->getResult(); + + try { + if ( !isset( $params['service'] ) ) { + throw new \Exception("Erreur : service non défini."); + } + + $Json = new GetJsonPage($params['service']); + + # arguments 'key' et 'subkeys': retourne la 1ère valeur trouvée + if ( isset( $params['key'] ) && isset( $params['parents'] ) ) { + $data = $Json->getSubData( $params['key'], $params['parents'] ); + } + elseif ( isset( $params['key'] ) ) { + $data = $Json->getSubData( $params['key'] ); + } + + # arguments 'merge' et 'parents': aggrégation de toutes les valeurs trouvées + elseif ( isset( $params['merge'] ) && isset( $params['parents'] ) ) { + $data = $Json->mergeSubData( $params['merge'], $params['parents'] ); + } + elseif ( isset( $params['merge'] ) ) { + $data = $Json->mergeSubData( $params['merge'] ); + } + + # absence d'argument: retourne la totalité du contenu + else { + $data = $Json->getFullData(); + } + + # retour des valeurs + if ( !is_null($data) ) { + foreach ( $data as $key => $value ) { + $r->addValue( null, $key, $value ); + } + } + } catch (\Exception $e) { + $r->addValue( null, "erreur", $e ); + } + } + + protected function getAllowedParams() { + return [ + 'service' => [ + ApiBase::PARAM_TYPE => 'string' + ], + 'key' => [ + ApiBase::PARAM_TYPE => 'string' + ], + 'merge' => [ + ApiBase::PARAM_TYPE => 'string' + ], + 'parents' => [ + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_ISMULTI => true, + ] + ]; + } +} diff --git a/includes/Classes/MGWSpecialAdmin.php b/includes/Classes/MGWSpecialAdmin.php new file mode 100644 index 0000000..fcc684e --- /dev/null +++ b/includes/Classes/MGWSpecialAdmin.php @@ -0,0 +1,403 @@ +triOptions = []; + * $this->filtreOptions = []; + * $this->filtreOptionsTitles = []; + * $this->headActions = [ 'action' => 'label' ] + * $this->headHiddenFields = [ 'name' => 'value' ] + */ + public function __construct( ) + { + parent::__construct( 'SpecialAdmin', 'permission' ); // ADAPTER + + $out = $this->getOutput(); + $out->addModules('ext.mgwiki-specialadmin'); + } + + + /******************* + ** TO OVERRIDE ** + *******************/ + + /** + * TODO OVERRIDE + */ + public function execute( $sub ) + { + # les fonctions doivent être appelées dans l'ordre suivant: + $reqData = $this->set_reqData(); // TO OVERRIDE + $this->set_select( $reqData ); + $this->set_check( $reqData ); + + // (...) + + $out->addHTML( $this->makeHeadForm() ); + + $out->addHTML( $this->makeHeadTable() ); // TO OVERRIDE (optionnel) + + // construction des données + if ( isset( $reqData['action'] ) && $reqData['action'] == 'show' ) { + $showArray = $this->getShowArray( ); + } + + // définition des actions (...) + + $this->sortShowArray( $showArray, $actionInfo = [] ); + + $out->addHTML( $this->displayShowArray( $showArray ) ); // TO OVERRIDE + + $out->addHTML( $this->displayFooterActions() ); // TO OVERRIDE (optionnel) + } + + /** + * TODO OVERRIDE + */ + private function set_reqData() { + return $this->getRequest()->getPostValues(); + } + + + /** + * @param array $reqData + */ + private function set_select( &$reqData ) + { + global $tri; + $select = &$this->select; + + $tri[1] = ( isset( $reqData['tri1'] ) ) + ? $reqData['tri1'] : 'user_id'; + + $tri[2] = ( isset( $reqData['tri2'] ) ) + ? $reqData['tri2'] : ''; + + foreach ( $this->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]['...'] = ''; + } + } + + return $tri; + } + + /** + * @param array $reqData + */ + private function set_check( &$reqData ) + { + $check = &$this->check; + + foreach ( $this->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'] + ] ) + ) { + $check[$value]['view'] = 'checked'; + } + } + } + + + /** + * TODO OVERRIDE + */ + private function makeHeadTable () {} + + /** + * TODO OVERRIDE + * @param array $row + * @return string + */ + private function displayShowArray ( &$row ) { + $content = ''; + foreach( $showArray as $row ) { + $table = ' (...) '; + $class = 'active'; // 'active'|'inactive'|'deleted' + $content .= $this->tagRowTable( $table, $class ); + // (...) + } + return $this->tagShowArray( $content ); + } + + /** + * TODO OVERRIDE + * @return array $showArray + */ + private function getShowArray () {} + + /** + * TODO OVERRIDE + */ + private function displayFooterActions() {} + + + /*********************** + ** TO USE fuctions ** + ***********************/ + /** + * TODO USE in $this->displayShowArray() + * @param array $hidden [ 'name' => 'value' ] + * @param string $ctrlFn 'mw.maFonction()' + */ + // => HtmlFunctions.php + private function actionButton ( + $action, + $label, + $check = [ 'add' => 'false', 'del' => 'false' ], + $ctrlFn = '', + $hidden = [] ) + { + $extra = ' extra="' . json_encode( $hidden ) . '"'; + return ''; + } + + /** + * TODO USE in $this->displayShowArray() + */ + private function tagShowArray( $content ) { + return '
' . $content . '
'; + } + + /** + * TODO USE in $this->displayShowArray() + * @param string $content ' (...) ' + * @param string $class 'active'|'inactive'|'deleted' + */ + private function tagRowTable( $content, $class ) { + return '' . $content . '
'; + } + + /** + * TODO USE in $this->displayShowArray() + */ + private function addBox ( $value ) { + return ''; + } + + /** + * TODO USE in $this->displayShowArray() + */ + private function delBox ( $value ) { + return ''; + } + + /** + * TODO USE in $this->displayShowArray() + */ + private function addAllBox ( $label ) { + return ' + '; + } + + /** + * TODO USE in $this->displayShowArray() + */ + private function delAllBox ( $label ) { + return ' + '; + } + + /** + * @param string $msg_prefix + * le préfixe des messages à récupérer + * + * @return string + */ + private function makeHeadForm ( $msg_prefix ) + { + $select = &$this->select; + $check = &$this->check; + + $return = ' +
'; + + // TRI + $return .= ' +
Tri + + + + + +
+ + + + + +
+
'; + // FILTRES + $return .= ' +
Filtres + + + + + + + '; + $writeTitle = false; + foreach ( $this->filtreOptions as $filtreOption ) { + foreach ( $this->filtreOptionsTitles as $title => $titleOptions ) { + if ( in_array( $filtreOption, $titleOptions ) && !$writeTitle ){ + $return .= ''; + $writeTitle = true; + } + elseif ( !in_array( $filtreOption, $titleOptions ) ) + $writeTitle = false; + } + $return .= ' + + + + + + '; + } + $return .= ' +
seul
' . Msg::get( $msg_prefix.'-'.$title ) . '
' . Msg::get( $msg_prefix.'-'.$filtreOption ) . '
+
'; + + if ( isset( $this->headActions ) ) { + foreach ( $this->headActions as $action => $label ) { + $return .= $this->actionButton( $action, $label ); + } + } + + if ( isset( $this->headHiddenFields ) ) { + foreach ( $this->headHiddenFields as $field => $value ) { + $return .= ''; + } + } + + $return .= ' + + + + +
'; + return $return; + } + + /** + * @param array $showArray + * @param array $actionInfo + * @return void + */ + private function sortShowArray( &$showArray, $actionInfo = [] ) { + if ( isset( $showArray ) ) { + // tri d'un tableau à 2 dimentions + uasort ( $showArray , 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 $showArray = []; + + if ( count( $actionInfo ) > 0 ) { + $showArray = array_merge( $actionInfo, $showArray ); + } + } + + /** + * @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/Classes/MGWStatus.php b/includes/Classes/MGWStatus.php new file mode 100644 index 0000000..0a2bb56 --- /dev/null +++ b/includes/Classes/MGWStatus.php @@ -0,0 +1,67 @@ +set_done( $done ); + $status->set_message( $message ); + if ( !is_null( $extra ) ) $status->set_extra( $extra ); + return $status; + } + + public static function newDone( $message, $extra = null ) { + return MGWStatus::new( true, $message, $extra ); + } + + public static function newFailed( $message, $extra = null ) { + return MGWStatus::new( false, $message, $extra ); + } + + public function set_done( $bool ) { + $this->done = $bool; + } + + public function set_message( $string ) { + $this->message = $string; + } + + public function set_extra( $extra ) { + $this->extra = $extra; + } + + public function done() { + return $this->done; + } + + public function mess() { + return $this->message; + } + + public function extra() { + return $this->extra; + } +} diff --git a/includes/Classes/MGWUser.php b/includes/Classes/MGWUser.php new file mode 100644 index 0000000..af684ce --- /dev/null +++ b/includes/Classes/MGWUser.php @@ -0,0 +1,593 @@ +set_user_id( $user_id ); + $r = $MGWuser->userExists( $user_id ); + if ( !$r ) + return null; + + // données de la table user + $MGWuser->set_mw_exists( true ); + $MGWuser->retrieveBaseData(); + + // données de la table mgw_utilisateurs + $r = $MGWuser->mgwUserExists( 'user_id', $user_id ); + if ( !$r ) $MGWuser->set_mgw_exists( false ); + else { + $MGWuser->set_mgw_exists( true ); + $MGWuser->retrieveMGWData(); + } + + // données de la page utilisateur + $r = PageF::getPageFromTitleText( $MGWuser->data['user_name'], NS_USER, true ); + if ( is_null( $r ) ) $MGWuser->set_page_exists( false ); + else { + $MGWuser->set_page_exists( true ); + $MGWuser->retrievePageData( $r ); + } + + // définition des variables principales + $MGWuser->set_nom(); + $MGWuser->set_prenom(); + $MGWuser->set_email(); + + return $MGWuser; + } + + ######################### + ## Fonctions publiques ## + ######################### + + // Setters + public function set_user_id( $user_id ) { + $this->user_id = $user_id; + } + + public function set_level( $level ) { + $this->level = $level; + } + + public function set_nom( $nom = null ) { + + if ( is_null( $nom ) && isset( $this->data['utilisateur_nom'] ) ) + $this->nom = $this->data['utilisateur_nom']; + + elseif ( is_null( $nom ) && isset( $this->data['page_template_nom'] ) ) + $this->nom = $this->data['page_template_nom']; + + elseif ( !is_null( $nom ) ) + $this->nom = $nom; + + else $this->nom = null; + } + + public function set_prenom( $prenom = null ) { + + if ( is_null( $prenom ) && isset( $this->data['utilisateur_prenom'] ) ) + $this->prenom = $this->data['utilisateur_prenom']; + + elseif ( is_null( $prenom ) && isset( $this->data['page_template_prenom'] ) ) + $this->prenom = $this->data['page_template_prenom']; + + elseif ( !is_null( $prenom ) ) + $this->prenom = $prenom; + + else $this->prenom = null; + } + + public function set_email( $email = null ) { + + if ( is_null( $email ) && isset( $this->data['user_email'] ) ) + $this->email = $this->data['user_email']; + + elseif ( is_null( $email ) && isset( $this->data['page_template_email'] ) ) + $this->email = $this->data['page_template_email']; + + elseif ( !is_null( $email ) ) + $this->email = $email; + + else $this->email = null; + } + + public function set_mw_exists( $mw_exists ) { + $this->mw_exists = $mw_exists; + } + + public function set_mgw_exists( $mgw_exists ) { + $this->mgw_exists = $mgw_exists; + } + + public function set_page_exists( $page_exists ) { + $this->page_exists = $page_exists; + } + + // getters + public function get_user_id( ) { + return $this->user_id; + } + + public function get_level( ) { + return $this->level; + } + + public function get_nom( ) { + return $this->nom; + } + + public function get_prenom( ) { + return $this->prenom; + } + + public function get_email( ) { + return $this->email; + } + + public function get_mw_exists( ) { + return $this->mw_exists; + } + + public function get_mgw_exists( ) { + return $this->mgw_exists; + } + + public function get_page_exists( ) { + return $this->page_exists; + } + + public function get_data( ) { + return $this->data; + } + + /** + * @return string 'Prénom NOM' + * construit à partir de $this->prenom et $this->nom + */ + public function make_name() { + if ( isset( $this->prenom ) && isset( $this->nom ) ) { + return $this->prenom . ' ' . $this->nom; + } + return false; + } + + public function same_names() { + + if ( !$this->mw_exists || !$this->page_exists ) + return false; + + if ( !$this->data['page_template'] || + is_null( $this->data['page_template_nom'] ) || + is_null( $this->data['page_template_prenom'] ) ) + return false; + + if ( $this->data['user_name'] != $this->data['page_template_prenom'] . ' ' . $this->data['page_template_nom'] ) + return false; + + return true; + } + + public function same_email() { + + if ( !$this->mw_exists || !$this->page_exists ) + return false; + + if ( !$this->data['page_template'] || is_null( $this->data['page_template_email'] ) ) + return false; + + if ( $this->data['user_email'] != $this->data['page_template_email'] ) + return false; + + return true; + } + + /** + * définit : $user_email = $page_template_email + * envoie un mail de confirmation + */ + public function update_email() { + $user = $this->getUserFromId( $this->user_id ); + $user->setEmail( $this->data['page_template_email'] ); + $user->saveSettings(); + $user->sendConfirmationMail( 'changed' ); + } + + /** + * définit : $user_email = null + */ + public function delete_email() { + $user = $this->getUserFromId( $this->user_id ); + $user->setEmail( null ); + $user->saveSettings(); + } + + + /** + * définit : $user_groups = null + * @return string message de retour + */ + public function delete_all_groups() { + $user = $this->getUserFromId( $this->user_id ); + $deleted = []; + $kept = []; + foreach ( $user->getGroups() as $group ) { + if ( $group != 'sysop' ){ + $user->removeGroup( $group ); + $deleted[] = $group; + } + else { + $kept[] = $group; + } + } + $user->saveSettings(); + + if ( count( $deleted ) > 0 ){ + $mess = 'Groupes supprimés: ' . implode( ',', $deleted ); + } + else { + $mess = 'Aucun groupe n\'a été supprimé'; + } + + if ( count( $kept ) > 0 ){ + $mess .= '
Groupes conservés: ' . implode( ',', $kept ); + } + else { + $mess .= '
Aucun groupe n\'a été conservé.'; + } + + return $mess; + } + + public function same_real_name() { + if ( $this->data['user_real_name'] != $this->data['user_name'] ) { + return false; + } + return true; + } + + /** + * définit : $user_real_name = $user_name + */ + public function update_real_name() { + $user = $this->getUserFromId( $this->user_id ); + $user->setRealName( $this->data['user_name'] ); + $user->saveSettings(); + } + + /** + * définit : $user_real_name = null + */ + public function delete_real_name() { + $user = $this->getUserFromId( $this->user_id ); + $user->setRealName( null ); + $user->saveSettings(); + } + + public function sanitize_nom() { + if ( !isset( $this->nom ) ) { + return false; + } + $this->nom = strtoupper( $this->nom ); + return true; + } + + public function sanitize_prenom() { + if ( !isset( $this->prenom ) ) { + return false; + } + $space = false; + $str = $this->prenom; + if ( preg_match('/ /', $str ) > 0 ) { + $str = str_replace(' ','-',$str); + $space = true; + } + + $str = explode( '-', $str ); + foreach ( $str as $key => $substr ) { + $str[$key] = ucfirst( strtolower( $substr ) ); + } + + $this->prenom = ( $space ) ? implode( ' ', $str ) : implode( '-', $str ); + + return true; + } + + /** + * FONCTION TEMPORAIRE + * @param string $summary le résumé de modification de page + * @return MGWStatus + */ + public function update_page_template_names( $summary ) { + + $mess = ''; + + if ( $this->nom != $this->data['page_template_nom'] ) { + $status = $this->update_page_template_info( 'Nom', $this->nom, $summary ); + $mess .= $status->mess() . '
'; + $done = $status->done(); + } + + if ( $this->prenom != $this->data['page_template_prenom'] ) { + $status = $this->update_page_template_info( 'Prénom', $this->prenom, Msg::get('mgwuser-sanitize-names') ); + $mess .= $status->mess() . '
'; + if ( isset( $done ) && $done ) $done = $status->done(); + elseif ( !isset( $done ) ) $done = $status->done(); + } + + return Status::new( $done, $mess ); + } + + /** + * Renomme l'utilisateur à partir de $this->prenom et $this->nom + * @param string $summary + * @param bool $contentsReplace + * @param bool $nsAll recherche et remplace le texte dans toutes les pages + * @param string $n liste numérique des NS séparés par des virgules + * @return MGWStatus + */ + public function rename( $summary, $contentsReplace = false, $nsAll = false, $ns = null, $user = null ) { + + if ( is_null( $user ) ) { + global $wgUser; + $user = $wgUser; + } + + if ( !$this->mw_exists ) { + return Status::newFailed( 'Utilisateur inexistant' ); + } + if ( !isset( $this->nom ) ) { + return Status::newFailed( 'Variable non définie : "NOM"' ); + } + if ( !isset( $this->prenom ) ) { + return Status::newFailed( 'Variable non définie : "Prénom"' ); + } + + $oldName = $this->data['user_name']; + $newName = $this->make_name(); + if ( $oldName == $newName ) { + return Status::newFailed( 'Nom d\'utilisateur inchangé : valeurs identiques.' ); + } + + $status = Status::newDone( 'processing...' ); + + // on renomme l'utilisateur + $rename = MGWRenameuser::execute( + $oldName, // user_name actuel + $newName, // user_name de destination + true, // renommer toutes les pages + false, // supprimer les redirections + $summary, + $user + ); + if ( !$rename->done() ) $status->set_done( false ); + $status->extra[] = $rename; + + if ( $contentsReplace ) { + // on remplace l'ancien nom par le nouveau dans le contenu de toutes les pages + $replace = new MGWReplaceText( [ + "target" => $oldName, + "replace" => $newName, + "regex" => false, + "nsall" => $nsAll, + "ns" => $ns, + "summary" => $summary, + "user" => $user->getName() + ] ); + $replace = $replace->execute(); + + if ( !$replace->done() ) $status->set_done( false ); + $status->extra = $replace; + } + + if ( !$status->done() ) { + $status->set_message( 'Certaines étapes du renommage ont échoué (voir détails)' ); + } + else $status->set_message( 'Renommage réussi (voir détails)' ); + + return $status; + } + + /** + * Mise à jour de la table mgw_utilisateurs + * insert ou update sur les données $this->user_id, $this->nom, $this->prenom + * @return MGWStatus + */ + public function db_update( $updater_id = null ) { + + if ( !$this->mw_exists ) { + return Status::newFailed("Erreur: un utilisateur MGWiki ne peut être créé que s'il existe déjà ". + "en tant qu'utilisateur MediaWiki"); + } + if ( is_null( $this->nom ) || is_null( $this->prenom ) ) { + return Status::newFailed("Erreur: un utilisateur MGWiki ne peut être créé que si nom et prénom ". + "sont définis (null renvoyé)"); + } + + if ( is_null($updater_id) ) { + global $wgUser; + $updater_id = $wgUser->getId(); + } + + return DbF::update_or_insert( + 'utilisateur', + [ 'user_id' => $this->user_id ], + [ 'nom' => $this->nom, 'prenom' => $this->prenom ], + $updater_id + ); + } + + ######################## + ## Fonctions privées ## + ######################## + private function retrieveBaseData() { + + if ( ! $this->mw_exists ) + return false; + + $lb = MediaWikiServices::getInstance()->getDBLoadBalancer(); + $dbr = $lb->getConnectionRef( DB_REPLICA ); + $res = $dbr->select( + 'user', + [ + 'user_id', + 'user_name', + 'user_real_name', + 'user_email' + ], + 'user_id = ' . $this->user_id + ); + + if ( $res->numRows() > 0 ) { + $r = $res->fetchRow(); + foreach ( $r as $field => $value ) { + if ( is_string($field) ) $this->data[ $field ] = $value; + } + return true; + } + return false; + } + + private function retrieveMGWData() { + + if ( ! $this->mgw_exists ) return false; + + $lb = MediaWikiServices::getInstance()->getDBLoadBalancer(); + $dbr = $lb->getConnectionRef( DB_REPLICA ); + $res = $dbr->select( + 'mgw_utilisateur', + [ + 'utilisateur_nom', + 'utilisateur_prenom', + 'utilisateur_level', + 'utilisateur_updater_id', + 'utilisateur_update_time' + ], + 'utilisateur_user_id = ' . $this->user_id + ); + + if ( $res->numRows() > 0 ) { + $r = $res->fetchRow(); + foreach ( $r as $field => $value ) { + if ( is_string($field) ) $this->data[ $field ] = $value; + } + return true; + } + + return false; + } + + private function retrievePageData( $wikiPage ) { + + if ( ! $this->page_exists ) return false; + + $this->data['page_title'] = $wikiPage->getTitle()->getFullText(); + $this->data['page_url'] = $wikiPage->getTitle()->getFullURL(); + + $redirect = PageF::getPageRedirect ( $wikiPage, 'title' ); + if ( !is_null( $redirect ) ) { + $this->data['page_redirect'] = true; + $this->data['page_redirect_title'] = $redirect->getFullText(); + $this->data['page_redirect_url'] = $redirect->getFullURL(); + } else { + $this->data['page_redirect'] = false; + } + + $template = PageF::getPageTemplateInfos( $wikiPage, 'Personne', [ 'Nom', 'Prénom', 'E-mail' ] ); + if ( !is_null( $template ) ) { + $this->data['page_template'] = true; + $this->data['page_template_nom'] = $template['Nom']; + $this->data['page_template_prenom'] = $template['Prénom']; + $this->data['page_template_email'] = $template['E-mail']; + } else { + $this->data['page_template'] = false; + } + + return true; + } + + /** + * @return MGWStatus + */ + private function update_page_template_info( $field, $value, $summary ) { + + if ( !$this->mw_exists ) { + return Status::newFailed( 'Utilisateur inexistant' ); + } + if ( !$this->page_exists ) { + return Status::newFailed( 'Page utilisateur inexistante' ); + } + if ( !$this->data['page_template'] ) { + return Status::newFailed( 'Modèle {{Personne}} inexistant' ); + } + + $userpage = PageF::getPageFromTitleText( $this->data['user_name'], NS_USER, true ); + $oldtext = $userpage->getContent()->getNativeData(); + $regexp = '/(\|' . $field . '=)([^\|\n]+)/'; + if ( preg_match( $regexp, $oldtext ) < 1 ) { + return Status::newFailed( 'Champs "' . $field . '" inexistant.' ); + } + $newtext = preg_replace( $regexp, '$1' . $value , $oldtext ); + if ( $newtext != $oldtext ) { + try { + $edit_summary = $summary; + $newcontent = new WikitextContent( $newtext ); + $userpage->doEditContent( $newcontent, $edit_summary, EDIT_AUTOSUMMARY ); + return Status::newDone( 'Champs "' . $field . '" mis à jour avec succès.' ); + } + catch (\Exception $e) { + return Status::newFailed( $e ); + } + } + return Status::newFailed( 'Champs "' . $field . '" inchangé.' ); + } +} diff --git a/includes/Foreign/MGWRenameuser.php b/includes/Foreign/MGWRenameuser.php new file mode 100644 index 0000000..46b8430 --- /dev/null +++ b/includes/Foreign/MGWRenameuser.php @@ -0,0 +1,209 @@ + bool, 'message' => bool, ] + */ + public function execute( $oldname, $newname, $movepages, $suppressRedirect, $reason, $user = null ) { + global $wgContLang, $wgCapitalLinks, $wgUser; + + // ! on AUTORISE les utilisateurs à renommer leur propre compte + if ( is_null( $user ) ) { + $user = $wgUser; + } + + if ( !( $user->isAllowed( 'renameuser' ) || $user->getName() != $oldname ) ) { + throw new PermissionsError( 'renameuser' ); + } + if ( $user->isBlocked() ) { + throw new UserBlockedError( $wgUser->mBlock ); + } + + $oldusername = Title::makeTitle( NS_USER, $oldname ); + $newusername = Title::makeTitleSafe( NS_USER, $wgContLang->ucfirst( $newname ) ); + + $oun = is_object( $oldusername ) ? $oldusername->getText() : ''; + $nun = is_object( $newusername ) ? $newusername->getText() : ''; + $token = $user->getEditToken(); + + if ( $oun == '' ) { + $message = str_replace( '$1', $oldname, wfMessage( 'renameusererrorinvalid' )->text() ); + return Status::newFailed( $message ); + } + if ( $nun == '' ) { + $message = str_replace( '$1', $newname, wfMessage( 'renameusererrorinvalid' )->text() ); + return Status::newFailed( $message ); + } + + // Suppress username validation of old username + $olduser = User::newFromName( $oldusername->getText(), false ); + $newuser = User::newFromName( $newusername->getText(), 'creatable' ); + + // It won't be an object if for instance "|" is supplied as a value + if ( !is_object( $olduser ) ) { + $message = str_replace( '$1', $oldname, wfMessage( 'renameusererrorinvalid' )->text() ); + return Status::newFailed( $message ); + } + if ( !is_object( $newuser ) || !User::isCreatableName( $newuser->getName() ) ) { + $message = str_replace( '$1', $newname, wfMessage( 'renameusererrorinvalid' )->text() ); + return Status::newFailed( $message ); + } + + // Check for the existence of lowercase oldusername in database. + // Until r19631 it was possible to rename a user to a name with first character as lowercase + if ( $oldusername->getText() !== $wgContLang->ucfirst( $oldusername->getText() ) ) { + // oldusername was entered as lowercase -> check for existence in table 'user' + $dbr = wfGetDB( DB_REPLICA ); + $uid = $dbr->selectField( 'user', 'user_id', + [ 'user_name' => $oldusername->getText() ], + __METHOD__ ); + if ( $uid === false ) { + if ( !$wgCapitalLinks ) { + $uid = 0; // We are on a lowercase wiki but lowercase username does not exists + } else { + // We are on a standard uppercase wiki, use normal + $uid = $olduser->idForName(); + $oldusername = Title::makeTitleSafe( NS_USER, $olduser->getName() ); + } + } + } else { + // oldusername was entered as upperase -> standard procedure + $uid = $olduser->idForName(); + } + + if ( $uid === 0 ) { + $message = str_replace( '$1', $oldname, wfMessage( 'renameusererrordoesnotexist' )->text() ); + return Status::newFailed( $message ); + } + + if ( $newuser->idForName() !== 0 ) { + $message = str_replace( + ['$1', '{{GENDER:$1|utilisateur|utilisatrice}}'], + [ $oldname,'utilisateur.ice' ], + wfMessage( 'renameusererrorexists' )->text() ); + return Status::newFailed( $message ); + } + + // Do the heavy lifting... + $rename = new RenameuserSQL( + $oldusername->getText(), + $newusername->getText(), + $uid, + $wgUser, + [ 'reason' => $reason ] + ); + if ( !$rename->rename() ) { + return [ 'done' => false, 'message' => 'User '.$oldname.' does not exist, bailing out' ]; + } + + // If this user is renaming his/herself, make sure that MovePage::move() + // doesn't make a bunch of null move edits under the old name! + if ( $user->getId() === $uid ) { + $user->setName( $newusername->getText() ); + } + + // Move any user pages + if ( $movepages ) { + $dbr = wfGetDB( DB_REPLICA ); + + $pages = $dbr->select( + 'page', + [ 'page_namespace', 'page_title' ], + [ + 'page_namespace' => [ NS_USER, NS_USER_TALK ], + $dbr->makeList( [ + 'page_title ' . $dbr->buildLike( $oldusername->getDBkey() . '/', $dbr->anyString() ), + 'page_title = ' . $dbr->addQuotes( $oldusername->getDBkey() ), + ], LIST_OR ), + ], + __METHOD__ + ); + + $output = ''; + $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); + foreach ( $pages as $row ) { + $oldPage = Title::makeTitleSafe( $row->page_namespace, $row->page_title ); + $newPage = Title::makeTitleSafe( $row->page_namespace, + preg_replace( '!^[^/]+!', $newusername->getDBkey(), $row->page_title ) ); + + $movePage = new MovePage( $oldPage, $newPage ); + $validMoveStatus = $movePage->isValidMove(); + + # Do not autodelete or anything, title must not exist + if ( $newPage->exists() && !$validMoveStatus->isOK() ) { + $link = $linkRenderer->makeKnownLink( $newPage ); + $output .= Html::rawElement( + 'li', + [ 'class' => 'mw-renameuser-pe' ], + wfMessage( 'renameuser-page-exists' )->rawParams( $link )->escaped() + ); + } else { + $logReason = wfMessage( + 'renameuser-move-log', $oldusername->getText(), $newusername->getText() + )->inContentLanguage()->text(); + + $moveStatus = $movePage->move( $user, $logReason, !$suppressRedirect ); + + if ( $moveStatus->isOK() ) { + # oldPage is not known in case of redirect suppression + $oldLink = $linkRenderer->makeLink( $oldPage, null, [], [ 'redirect' => 'no' ] ); + + # newPage is always known because the move was successful + $newLink = $linkRenderer->makeKnownLink( $newPage ); + + $output .= Html::rawElement( + 'li', + [ 'class' => 'mw-renameuser-pm' ], + wfMessage( 'renameuser-page-moved' )->rawParams( $oldLink, $newLink )->escaped() + ); + } else { + $oldLink = $linkRenderer->makeKnownLink( $oldPage ); + $newLink = $linkRenderer->makeLink( $newPage ); + $output .= Html::rawElement( + 'li', [ 'class' => 'mw-renameuser-pu' ], + wfMessage( 'renameuser-page-unmoved' )->rawParams( $oldLink, $newLink )->escaped() + ); + } + } + } + } + + // Output success message stuff :) + + $message = str_replace( + ['$1', '$2', '{{GENDER:$1|utilisateur|utilisatrice}}', '{{GENDER:$1|renommé|renommée}}'], + [ $oldname, $newname, 'utilisateur.ice', 'renomé.e' ], + wfMessage( 'renameusersuccess' )->text() ); + if ( $output ) $message .= $output . 'ATTENTION: la prise en compte des modifications peut prendre du temps.'; + + return Status::newDone( $message ); + } +} diff --git a/includes/Foreign/MGWReplaceText.php b/includes/Foreign/MGWReplaceText.php new file mode 100644 index 0000000..01d9273 --- /dev/null +++ b/includes/Foreign/MGWReplaceText.php @@ -0,0 +1,264 @@ + ReplaceTextSearch et ReplaceTextJob + * + * TODO: boucle en cas de nombre de pages max atteint (250) + * + * https://www.mediawiki.org/wiki/Extension:Replace_Text + */ + +namespace MediaWiki\Extension\MGWikiDev\Foreign; + +use User; +use MWNamespace; +use ReplaceTextSearch; +use ReplaceTextJob; +use TitleArrayFromResult; +use MediaWiki\Extension\MGWikiDev\Classes\MGWStatus as Status; + +class MGWReplaceText { + private $user; + private $target; + private $replacement; + private $namespaces; + private $category; + private $prefix; + private $useRegex; + private $doAnnounce; + private $rename; + + private $nsall; + private $ns; + private $summary; + private $status; + + /** + * @param $args list of arrays: + * [ "target" => string ] MANDATORY Target text to find. + * [ "replace" => string ] MANDATORY Text to replace. + * [ "regex" => bool ] This is a regex (false). + * [ "user" => string ] The user to attribute this to (uid 1). + * [ "summary" => string ] Alternate edit summary (%r is where to place the replacement text, %f the text to look for.) + * [ "nsall" => bool ] Search all canonical namespaces (false) If true, this option overrides the ns option. + * [ "ns" => array ] Comma separated namespaces to search in (Main). + * [ 'rename' => bool ] Rename page titles instead of replacing contents. + * [ "no-announce" => bool ] Do not announce edits on Special:RecentChanges or watchlists. + * + * test: + * MGWReplaceText::do( [ "target" => "interne TEST1", "target" => "anonyme", "regex" => false, "nsall" => true, "summary" => "MGW replacetext test.", "user" => "Webmaster" ] ); + */ + + + /** + * @param array $args + * @return MGWStatus + */ + public static function do ( $args ) { + $replace = new MGWReplaceText( $args ); + return $replace->execute(); + } + + + /** + * @param array $args + * @return MGWReplaceText + */ + public function __construct( $args ) { + + $this->category = null; + $this->prefix = null; + + foreach ($args as $arg => $value ) { + switch ( $arg ) { + + case "user": + $user = is_numeric( $value ) ? + User::newFromId( $value ) : + User::newFromName( $value ); + + if ( get_class( $user ) !== 'User' ) { + $this->status = Status::newFailed( 'Couldn\'t translate '.$value.' to a user.'); + } + $this->user = $user; + break; + + case "target": + $this->target = $value; + break; + + case "replace": + $this->replacement = $value; + break; + + case "summary": + $this->summary = $value; + break; + + case "nsall": + $this->nsall = $value; + break; + + case "ns": + $this->ns = $value; + break; + + case "regex": + $this->useRegex = $value; + break; + + case "rename": + $this->rename = $value; + break; + + case "no-announce": + $this->doAnnounce = !($value); + + default: + $this->status = Status::newFailed( "Paramètre [ '" . $arg . + "' => '" . $value . "' ] inconnu." ); + } + } + + if ( is_null( $this->target ) ) { + $this->status = Status::newFailed( "You have to specify a target." ); + } + + if ( is_null( $this->replacement ) ) { + $this->status = Status::newFailed( "You have to specify replacement text." ); + } + + if ( is_null( $this->rename ) ) { + $this->rename = false; + } + + if ( is_null( $this->nsall ) && is_null( $this->ns ) ) { + $namespaces = [ NS_MAIN ]; + } else { + $canonical = MWNamespace::getCanonicalNamespaces(); + $canonical[NS_MAIN] = "_"; + $namespaces = array_flip( $canonical ); + if ( is_null( $this->nsall ) ) { + $namespaces = array_map( + function ( $n ) use ( $canonical, $namespaces ) { + if ( is_numeric( $n ) ) { + if ( isset( $canonical[ $n ] ) ) { + return intval( $n ); + } + } else { + if ( isset( $namespaces[ $n ] ) ) { + return $namespaces[ $n ]; + } + } + return null; + }, explode( ",", $this->ns ) ); + $namespaces = array_filter( + $namespaces, + function ( $val ) { + return $val !== null; + } ); + } + } + $this->namespaces = $namespaces; + if ( $this->namespaces === [] ) { + $this->status = Status::newFailed( "No matching namespaces." ); + } + + if ( is_null( $this->doAnnounce ) ) { + $this->doAnnounce = true; + } + + if ( is_null( $this->status ) ) { + $this->status = Status::newDone( 'ReplaceText object construction done.'); + } + } + + private function getSummary( $target, $replacement ) { + $msg = wfMessage( 'replacetext_editsummary', $target, $replacement )->plain(); + if ( isset( $this->summary ) ) { + $msg = str_replace( [ '%f', '%r' ], [ $this->target, $this->replacement ], $this->summary ); + } + return $msg; + } + + private function replaceTitles( $titles, $target, $replacement, $useRegex, $rename ) { + foreach ( $titles as $title ) { + $params = [ + 'target_str' => $target, + 'replacement_str' => $replacement, + 'use_regex' => $useRegex, + 'user_id' => $this->user->getId(), + 'edit_summary' => $this->getSummary( $target, $replacement ), + 'doAnnounce' => $this->doAnnounce + ]; + + if ( $rename ) { + $params[ 'move_page' ] = true; + $params[ 'create_redirect' ] = true; + $params[ 'watch_page' ] = false; + } + + $job = new ReplaceTextJob( $title, $params ); + if ( $job->run() !== true ) { + $this->status->extra[] = [ + "title" => $title, + "done" => false, + "message" => "Trouble replacing on the page '$title'." + ]; + } + else $this->status->extra[] = [ + "title" => $title, + "done" => true, + "message" => "Replacing on '$title' done." + ]; + } + } + + + /** + * @return MGWStatus + */ + public function execute() { + + if ( !$this->status->done() ) { + return $this->status; + } + + if ( $this->rename ) { + $res = ReplaceTextSearch::getMatchingTitles( + $this->target, + $this->namespaces, + $this->category, + $this->prefix, + $this->useRegex + ); + } else { + $res = ReplaceTextSearch::doSearchQuery( + $this->target, + $this->namespaces, + $this->category, + $this->prefix, + $this->useRegex + ); + } + + $titles = new TitleArrayFromResult( $res ); + + if ( count( $titles ) === 0 ) { + $this->status->set_done( false ); + $this->status->set_message( 'No targets found to replace.' ); + return $this->status; + } + + $this->replaceTitles( + $titles, $this->target, $this->replacement, $this->useRegex, $this->rename + ); + $this->status->set_message( 'ReplaceText done (see details).' ); + + return $this->status; + } +} diff --git a/includes/Foreign/MGWUserMerge.php b/includes/Foreign/MGWUserMerge.php new file mode 100644 index 0000000..95d3fdd --- /dev/null +++ b/includes/Foreign/MGWUserMerge.php @@ -0,0 +1,80 @@ + utiliser seulement la fusion avec l'option delete + * + * ! la trace de l'utilisateur peut persister dans le contenu des pages + * + * https://www.mediawiki.org/wiki/Extension:UserMerge + */ + +namespace MediaWiki\Extension\MGWikiDev\Foreign; + +use User; +use MediaWiki\MediaWikiServices; +use MergeUser; // extension UserMerge +use UserMergeLogger; // extension UserMerge +use MediaWiki\Extension\MGWikiDev\Classes\MGWStatus as Status; + +class MGWUserMerge { + + /** + * @param int $old_id + * @param int|null $new_id # !! null NE FONCTIONNE PAS + * @param bool $delete + * @param callable $msg Function that returns a Message object + * @return Status + */ + public function execute( $old_id, $new_id, $delete, /* callable */ $msg ) { + global $wgUser; + + // Validate old user + $oldUser = User::newFromId( $old_id ); + if ( !$oldUser ) { + return Status::newFailed( wfMessage('usermerge-badoldid')->text() ); + } + if ( $wgUser->getId() === $old_id ) { + return Status::newFailed( wfMessage('usermerge-noselfdelete', $wgUser->getName() )->parse() ); + } + $protectedGroups = MediaWikiServices::getInstance()->getMainConfig()->get( 'UserMergeProtectedGroups' ); + if ( count( array_intersect( $oldUser->getGroups(), $protectedGroups ) ) ) { + return Status::newFailed( wfMessage( 'usermerge-protectedgroup', $oldUser->getName() )->parse() ); + } + + // validate new user + if ( $new_id !== null ) { + $newUser = User::newFromId( $new_id ); + if ( !$newUser ) { + return Status::newFailed( wfMessage('usermerge-badnewuserid')->text() ); + } + } + // Handle "Anonymous" as a special case for user deletion + if ( $new_id === null ) { + $newUser = User::newFromName( 'Anonymous' ); + $newUser->mId = 0; + } + + if ( $new_id !== null && $newUser->getName() === $oldUser->getName() ) { + return Status::newFailed( wfMessage('usermerge-same-old-and-new-user')->text() ); + } + + // Validation passed, let's merge the user now. + $message = ''; + $um = new MergeUser( $oldUser, $newUser, new UserMergeLogger() ); + $um->merge( $wgUser, __METHOD__ ); + + $message .= wfMessage( 'usermerge-success' )->rawParams( + $oldUser->getName(), $old_id, + $newUser->getName(), $new_id )->parse(); + + if ( $delete ) { + $failed = $um->delete( $wgUser, $msg ); + $message .= wfMessage( 'usermerge-userdeleted')->rawParams( $oldUser->getName(), $old_id )->escaped() ; + if ( $failed ) return Status::newFailed( wfMessage('usermerge-page-unmoved')->text() ); + } + return Status::newDone( $message ); + } +} diff --git a/includes/Parsers.php b/includes/Parsers.php new file mode 100644 index 0000000..0558edc --- /dev/null +++ b/includes/Parsers.php @@ -0,0 +1,94 @@ +' . $include . ''; + + if ($target == ''){ + $output = $include; + } + + return [ $output, 'isHTML' => true ];//, 'noparse' => true, + } + + // retrieve data from mgw_groupe_type table + // must be called from a' MGWiki:Types_de_groupes/' subpage + public static function mgw_groupe_type( $parser, $field = '' ) { + + $title = $parser->getTitle(); + $fields = [ 'admin_level', 'user_level', 'default_duration' ]; + + if ( $title->getNamespace() != NS_PROJECT || + preg_match( '/Types de groupes\/(.*)$/', $title->getText() ) < 1 ) + { + $output = HtmlF::parseError('La balise {{#mgw-groupe-type:[arg]}} doit être employée sur une sous-page '. + '"MGWiki:Types_de_groupes/..."' ); + return [ $output, 'noparse' => true, 'isHTML' => true ]; + } + + if ( empty( $field ) ) { + $output = HtmlF::parseError('La balise {{#mgw-groupe-type:[arg]}} doit comporter un argument' ); + return [ $output, 'noparse' => true, 'isHTML' => true ]; + } + + if ( !in_array( $field, $fields ) ) { + $output = HtmlF::parseError('{{#mgw-groupe-type:[arg]}} : mauvais argument => "admin_level" | "user_level" | "default_duration"' ); + return [ $output, 'noparse' => true, 'isHTML' => true ]; + } + + $screen = DbF::select_clean( 'groupe_type', ['page_id', $field ], 'page_id = ' . $title->getArticleID() ); + if ( is_null( $screen ) ) { + $output = HtmlF::parseError('{{#mgw-groupe-type:' . $field . + '}} : cette page n\'est pas connue dans la table mgw_groupe_type.' ); + return [ $output, 'noparse' => true, 'isHTML' => true ]; + } + else { + return $screen[0][$field]; + } + } + + // retrieve data from mgw_groupe_type table + // must be called from a' MGWiki:Types_de_groupes/' 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 :