Cicalese has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/366780 )

Change subject: Adds request/approve account special pages and hooks.
......................................................................


Adds request/approve account special pages and hooks.

T173378
Change-Id: I97c2c995eb0eeea760824468d3b4453f6e7ccb09
---
M extension.json
M i18n/en.json
M i18n/qqq.json
A includes/EchoEAPresentationModel.php
R includes/EmailAuthorization.alias.php
A includes/EmailAuthorizationApprove.php
M includes/EmailAuthorizationConfig.php
M includes/EmailAuthorizationHooks.php
A includes/EmailAuthorizationRequest.php
R sql/EmailAuth.sql
A sql/EmailRequest.sql
11 files changed, 768 insertions(+), 17 deletions(-)

Approvals:
  Cicalese: Verified; Looks good to me, approved
  jenkins-bot: Checked



diff --git a/extension.json b/extension.json
index 0f991aa..4c72bc2 100644
--- a/extension.json
+++ b/extension.json
@@ -1,6 +1,6 @@
 {
        "name": "Email Authorization",
-       "version": "1.2",
+       "version": "1.3",
        "author": [
                "[https://www.mediawiki.org/wiki/User:Cindy.cicalese Cindy 
Cicalese]"
        ],
@@ -9,7 +9,9 @@
        "license-name": "MIT",
        "type": "other",
        "SpecialPages": {
-               "EmailAuthorizationConfig": "EmailAuthorizationConfig"
+               "EmailAuthorizationConfig": "EmailAuthorizationConfig",
+               "EmailAuthorizationRequest": "EmailAuthorizationRequest",
+               "EmailAuthorizationApprove": "EmailAuthorizationApprove"
        },
        "MessagesDirs": {
                "EmailAuthorization": [
@@ -17,7 +19,7 @@
                ]
        },
        "ExtensionMessagesFiles": {
-               "EmailAuthorizationAlias": 
"includes/EmailAuthorizationConfig.alias.php"
+               "EmailAuthorizationAlias": 
"includes/EmailAuthorization.alias.php"
        },
        "ResourceModules": {
                "ext.EmailAuthorization": {
@@ -37,13 +39,22 @@
        "AutoloadClasses": {
                "EmailAuthorization": "includes/EmailAuthorization.php",
                "EmailAuthorizationHooks": 
"includes/EmailAuthorizationHooks.php",
-               "EmailAuthorizationConfig": 
"includes/EmailAuthorizationConfig.php"
+               "EmailAuthorizationConfig": 
"includes/EmailAuthorizationConfig.php",
+               "EmailAuthorizationRequest": 
"includes/EmailAuthorizationRequest.php",
+               "EmailAuthorizationApprove": 
"includes/EmailAuthorizationApprove.php",
+               "EchoEAPresentationModel": 
"includes/EchoEAPresentationModel.php"
        },
        "Hooks": {
                "PluggableAuthUserAuthorization": [ 
"EmailAuthorizationHooks::authorize" ],
                "LoadExtensionSchemaUpdates": [
                        "EmailAuthorizationHooks::loadExtensionSchemaUpdates"
-               ]
+               ],
+               "BeforeCreateEchoEvent": 
"EmailAuthorizationHooks::onBeforeCreateEchoEvent"
+       },
+       "callback": "EmailAuthorizationHooks::onRegistration",
+       "config": {
+               "EmailAuthorization_EnableRequests": false,
+               "EmailAuthorization_RequestFields": []
        },
        "manifest_version": 1
 }
diff --git a/i18n/en.json b/i18n/en.json
index 3535d27..1e58e35 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -5,7 +5,7 @@
                ]
        },
        "emailauthorization-desc": "Authorize users by email address",
-       "emailauthorizationconfig": "Configure Email Authorization",
+       "emailauthorizationconfig": "Email Authorization Dashboard",
        "emailauthorization-config-instructions": "To add or revoke all email 
addresses in a domain use ''@domain'', where ''domain'' is the email domain. 
This will not affect other email addresses explicitly added in that domain.",
        "emailauthorization-config-authorized": "''$1'' is authorized.",
        "emailauthorization-config-notauthorized": "''$1'' is not authorized.",
@@ -15,7 +15,7 @@
        "emailauthorization-config-noauthfound": "No authorized email addresses 
or domains found.",
        "emailauthorization-config-nousersfound": "No users found.",
        "emailauthorization-config-invalidemail": "''$1'' is not a valid email 
address or domain.",
-       "emailauthorization-config-label-email": "Email",
+       "emailauthorization-config-label-email": "Email Address",
        "emailauthorization-config-label-username": "Username",
        "emailauthorization-config-label-realname": "Real Name",
        "emailauthorization-config-label-userpage": "User Page",
@@ -34,6 +34,29 @@
        "emailauthorization-config-button-showauth": "Show authorized email 
addresses and domains",
        "emailauthorization-config-button-showall": "Show all wiki users",
        "right-emailauthorizationconfig": "Configure user authorization by 
email address",
-       "action-emailauthorizationconfig": "configure email authorization"
-
+       "action-emailauthorizationconfig": "configure email authorization",
+       "emailauthorizationrequest": "Request an Account",
+       "emailauthorization-request-instructions": "Fill in fields and submit 
to request an account. Fields marked with * are mandatory.",
+       "emailauthorization-request-label-email": "Email Address",
+       "emailauthorization-request-button-submit": "Submit",
+       "emailauthorization-request-requested": "An account has been requested 
for email address $1.",
+       "emailauthorization-request-missingmandatory": "Missing mandatory field 
value(s).",
+       "emailauthorization-request-invalidemail": "Invalid email address: $1",
+       "emailauthorization-request-error": "An error has occurred requesting 
an account for $1.",
+       "echo-category-title-emailauthorization-notification-category": "New 
account requests",
+       "notification-header-emailauthorization-account-request": "A request 
has been submitted for a new account.",
+       "notification-subject-emailauthorization-account-request": "New account 
request.",
+       "notification-body-emailauthorization-account-request": "$1 has 
requested an account.",
+       "notification-link-label-emailauthorization-account-request": "Visit 
account approval page",
+       "emailauthorizationapprove": "Approve Account Requests",
+       "emailauthorization-approve-label-email": "Email Address",
+       "emailauthorization-approve-label-extra": "Extra Fields",
+       "emailauthorization-approve-label-action": "Actions",
+       "emailauthorization-approve-button-approve": "Approve",
+       "emailauthorization-approve-button-reject": "Reject",
+       "emailauthorization-approve-button-next": "Next",
+       "emailauthorization-approve-button-previous": "Previous",
+       "emailauthorization-approve-norequestsfound": "No account requests 
found.",
+       "emailauthorization-approve-approved": "Approved $1.",
+       "emailauthorization-approve-rejected": "Rejected $1."
 }
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 2ea7d30..7c18561 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -8,6 +8,7 @@
        },
        "emailauthorization-desc": "{{desc|name=Email 
Authorization|url=https://www.mediawiki.org/wiki/Extension:Email_Authorization}}";,
        "emailauthorizationconfig": "Special page name",
+       "emailauthorizationapprove": "Special page name",
        "emailauthorization-config-instructions": "Instructions",
        "emailauthorization-config-authorized": "Status message",
        "emailauthorization-config-notauthorized": "Status message",
@@ -36,5 +37,28 @@
        "emailauthorization-config-button-showauth": "Button text",
        "emailauthorization-config-button-showall": "Button text",
        "right-emailauthorizationconfig": 
"{{doc-right|emailauthorizationconfig}}",
-       "action-emailauthorizationconfig": 
"{{doc-action|emailauthorizationconfig}}"
+       "action-emailauthorizationconfig": 
"{{doc-action|emailauthorizationconfig}}",
+       "emailauthorizationrequest": "Special page name",
+       "emailauthorization-request-instructions": "Instructions",
+       "emailauthorization-request-label-email": "Field label",
+       "emailauthorization-request-button-submit": "Button text",
+       "emailauthorization-request-requested": "Status message",
+       "emailauthorization-request-missingmandatory": "Error message",
+       "emailauthorization-request-invalidemail": "Error message",
+       "emailauthorization-request-error": "Error message",
+       "echo-category-title-emailauthorization-notification-category": "Name 
of category on Prefences/Notifications page for EmailAuthorization 
notifications",
+       "notification-header-emailauthorization-account-requested": 
"Flyout-specific format for displaying notification header of a new account 
request.\n\nParameters:\n* $1 - email address of requested account.",
+       "notification-subject-emailauthorization-account-requested": "Echo 
email subject.",
+       "notification-body-emailauthorization-reply-on-watched-page": "Echo 
email body.\n\nParameters:\n*$1 - email address of requested account.",
+       "notification-link-label-emailauthorization-account-requested": "Label",
+       "emailauthorization-approve-label-email": "Table column 
label\n{{Identical|E-mail}}",
+       "emailauthorization-approve-label-extra": "Table Column Label",
+       "emailauthorization-approve-label-action": "Table Column Label",
+       "emailauthorization-approve-button-approve": "Button 
text\n{{Identical|Approve}}",
+       "emailauthorization-approve-button-reject": "Button 
text\n{{Identical|Reject}}",
+       "emailauthorization-approve-button-next": "Button 
text\n{{Identical|Next}}",
+       "emailauthorization-approve-button-previous": "Button 
text\n{{Identical|Previous}}",
+       "emailauthorization-approve-norequestsfound": "Status message",
+       "emailauthorization-approve-approved": "Status message",
+       "emailauthorization-approve-rejected": "Status message"
 }
diff --git a/includes/EchoEAPresentationModel.php 
b/includes/EchoEAPresentationModel.php
new file mode 100644
index 0000000..436b6ce
--- /dev/null
+++ b/includes/EchoEAPresentationModel.php
@@ -0,0 +1,76 @@
+<?php
+/*
+ * Copyright (c) 2017 The MITRE Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+class EchoEAPresentationModel extends EchoEventPresentationModel {
+
+       /**
+        * @return string The symbolic icon name as defined in 
$wgEchoNotificationIcons
+        */
+       public function getIconType() {
+               return 'user-rights';
+       }
+
+       /**
+        * Array of primary link details, with possibly-relative URL & label.
+        *
+        * @return array|bool Array of link data, or false for no link:
+        *                    ['url' => (string) url, 'label' => (string) link 
text (non-escaped)]
+        */
+       public function getPrimaryLink() {
+               return [
+                       'url' => Title::newFromText( 
'Special:EmailAuthorizationApprove' )->getFullURL(),
+                       'label' => $this->msg( 
"notification-link-label-{$this->type}" )
+               ];
+       }
+
+       /**
+        * Get a message object and add the performer's name as
+        * a parameter. It is expected that subclasses will override
+        * this.
+        *
+        * @return Message
+        */
+       public function getHeaderMessage() {
+               $msg = wfMessage( "notification-header-{$this->type}" );
+               $msg->params( $this->event->getExtraParam(
+                       'email' ) );
+               return $msg;
+       }
+
+       public function getBodyMessage() {
+               $msg = wfMessage( "notification-body-{$this->type}" );
+               $msg->params( $this->event->getExtraParam(
+                       'email' ) );
+               return $msg;
+       }
+
+       /**
+        * If this function returns false, no other methods will be called
+        * on the object.
+        *
+        * @return bool
+        */
+       public function canRender() {
+               return !is_null( $this->event->getTitle() );
+       }
+}
diff --git a/includes/EmailAuthorizationConfig.alias.php 
b/includes/EmailAuthorization.alias.php
similarity index 100%
rename from includes/EmailAuthorizationConfig.alias.php
rename to includes/EmailAuthorization.alias.php
diff --git a/includes/EmailAuthorizationApprove.php 
b/includes/EmailAuthorizationApprove.php
new file mode 100644
index 0000000..25630e9
--- /dev/null
+++ b/includes/EmailAuthorizationApprove.php
@@ -0,0 +1,296 @@
+<?php
+
+/*
+ * Copyright (c) 2017 The MITRE Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ t copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+class EmailAuthorizationApprove extends SpecialPage {
+
+       function __construct() {
+               parent::__construct( 'EmailAuthorizationApprove',
+                       'emailauthorizationconfig' );
+       }
+
+       function execute( $par ) {
+               if ( !$this->userCanExecute( $this->getUser() ) ) {
+                       $this->displayRestrictionError();
+                       return;
+               }
+
+               $request = $this->getRequest();
+               $this->setHeaders();
+               $this->getOutput()->addModuleStyles( 'ext.EmailAuthorization' );
+
+               $title = Title::newFromText( 'Special:' . __CLASS__ );
+               $url = $title->getFullURL();
+
+               $approve_email =  $request->getText( 'approve-email' );
+               if ( !is_null( $approve_email ) && strlen( $approve_email ) ) {
+                       $fields = self::getRequestFields( $approve_email );
+                       self::insertEmail( $approve_email );
+                       self::deleteRequest( $approve_email );
+                       $this->displayMessage(
+                               wfMessage( 
'emailauthorization-approve-approved', $approve_email )
+                       );
+                       wfRunHooks( 'EmailAuthorizationApprove', [ 
$approve_email, $fields, $this->getUser() ] );
+               }
+
+               $reject_email =  $request->getText( 'reject-email' );
+               if ( !is_null( $reject_email ) && strlen( $reject_email ) ) {
+                       $fields = self::getRequestFields( $reject_email );
+                       self::deleteRequest( $reject_email );
+                       $this->displayMessage(
+                               wfMessage( 
'emailauthorization-approve-rejected', $reject_email )
+                       );
+                       wfRunHooks( 'EmailAuthorizationReject', [ 
$reject_email, $fields, $this->getUser() ] );
+               }
+
+               $offset =  $request->getText( 'offset' );
+
+               if ( is_null( $offset ) || strlen( $offset ) === 0 ||
+                       !is_numeric( $offset ) || $offset < 0 ) {
+                       $offset = 0;
+               }
+
+               $limit = 20;
+
+               $requests = self::getRequests( $limit + 1, $offset );
+               $next = false;
+
+               if ( !$requests->valid() ) {
+                       $offset = 0;
+                       $requests = self::getRequests( $limit + 1, $offset );
+                       if ( !$requests->valid() ) {
+                               $this->displayMessage(
+                                       wfMessage( 
'emailauthorization-approve-norequestsfound' )
+                               );
+                               return;
+                       }
+               }
+
+               $html = Html::openElement( 'table', [
+                               'class' => 'wikitable emailauth-wikitable'
+                       ] )
+                       . Html::openElement( 'tr' )
+                       . Html::openElement( 'th' )
+                       . wfMessage( 'emailauthorization-approve-label-email' )
+                       . Html::closeElement( 'th' )
+                       . Html::openElement( 'th' )
+                       . wfMessage( 'emailauthorization-approve-label-extra' )
+                       . Html::closeElement( 'th' )
+                       . Html::openElement( 'th' )
+                       . wfMessage( 'emailauthorization-approve-label-action' )
+                       . Html::closeElement( 'th' )
+                       . Html::closeElement( 'tr' );
+
+               $index = 0;
+               $more = false;
+               foreach ( $requests as $request ) {
+                       if ( $index < $limit ) {
+                               $email = htmlspecialchars( $request->email, 
ENT_QUOTES );
+                               $html .= Html::openElement( 'tr' )
+                                       . Html::openElement( 'td' )
+                                       . $email
+                                       . Html::closeElement( 'td' )
+                                       . Html::openElement( 'td' );
+                               $json = $request->request;
+                               $data = json_decode( $json );
+                               foreach ( $data as $field => $value ) {
+                                       $html .=
+                                               Html::openElement( 'b' ) .
+                                               htmlspecialchars( $field ) .
+                                               Html::closeElement( 'b' ) .
+                                                ': ' .
+                                               htmlspecialchars( $value ) .
+                                               Html::element( 'br' );
+                               }
+                               $html .=
+                                       Html::closeElement( 'td' )
+                                       . Html::openElement( 'td', [
+                                                       'style' => 'text-align: 
center;'
+                                               ] )
+                                       . $this->createApproveButton( $url, 
$email )
+                                       . $this->createRejectButton( $url, 
$email )
+                                       . Html::closeElement( 'td' )
+                                       . Html::closeElement( 'tr' );
+                               $index ++;
+                       } else {
+                               $more = true;
+                       }
+               }
+
+               $html .= Html::closeElement( 'table' );
+               $this->getOutput()->addHtml( $html );
+
+               if ( $offset > 0 || $more ) {
+                       $this->addTableNavigation( $offset, $more, $limit, 
'offset' );
+               }
+       }
+
+       private function createApproveButton( $url, $email ) {
+               $html = Html::openElement( 'form', [
+                               'method' => 'post',
+                               'action' => $url,
+                               'style' => 'display: inline-block;'
+                       ] )
+                       . Html::hidden( 'approve-email', $email )
+                       . Xml::submitButton(
+                               wfMessage( 
'emailauthorization-approve-button-approve' ),
+                               [ 'class' => 'emailauth-button' ] )
+                       . Html::closeElement( 'form' );
+               return $html;
+       }
+
+       private function createRejectButton( $url, $email ) {
+               $html = Html::openElement( 'form', [
+                               'method' => 'post',
+                               'action' => $url,
+                               'style' => 'display: inline-block;'
+                       ] )
+                       . Html::hidden( 'reject-email', $email )
+                       . Xml::submitButton(
+                               wfMessage( 
'emailauthorization-approve-button-reject' ),
+                               [ 'class' => 'emailauth-button' ] )
+                       . Html::closeElement( 'form' );
+               return $html;
+       }
+
+       private function addTableNavigation( $offset, $more, $limit, $paramname 
) {
+
+               $title = Title::newFromText( 
'Special:EmailAuthorizationApprove' );
+               $url = $title->getFullURL();
+
+               $html = Html::openElement( 'table', [
+                               'class' => 'emailauth-navigationtable'
+                       ] )
+                       . Html::openElement( 'tr' )
+                       . Html::openElement( 'td' );
+
+               if ( $offset > 0 ) {
+                       $prevurl = $url . '?' . $paramname . '=' . ( $offset - 
$limit );
+                       $html .= Html::openElement( 'a', [
+                                       'href' => $prevurl,
+                                       'class' => 'emailauth-button'
+                               ] )
+                               . wfMessage( 
'emailauthorization-approve-button-previous' )
+                               . Html::closeElement( 'a' );
+               }
+
+               $html .= Html::closeElement( 'td' )
+                       . Html::openElement( 'td', [
+                               'style' => 'text-align:right;'
+                       ] );
+
+               if ( $more ) {
+                       $nexturl = $url . '?' . $paramname . '=' . ( $offset + 
$limit );
+                       $html .= Html::openElement( 'a', [
+                                       'href' => $nexturl,
+                                       'class' => 'emailauth-button'
+                               ] )
+                               . wfMessage( 
'emailauthorization-approve-button-next' )
+                               . Html::closeElement( 'a' );
+               }
+
+               $html .= Html::closeElement( 'td' )
+                       . Html::closeElement( 'tr' )
+                       . Html::closeElement( 'table' );
+               $this->getOutput()->addHtml( $html );
+       }
+
+       private function displayMessage( $message ) {
+               $html = Html::openElement( 'p', [
+                               'class' => 'emailauth-message'
+                       ] )
+                       . $message
+                       . Html::closeElement( 'p' );
+               $this->getOutput()->addHtml( $html );
+       }
+
+       private static function getRequests( $limit, $offset ) {
+               $dbr = wfGetDB( DB_SLAVE );
+               $requests = $dbr->select(
+                       'emailrequest',
+                       [
+                               'email',
+                               'request',
+                       ],
+                       [],
+                       __METHOD__,
+                       [
+                               'ORDER BY' => 'email',
+                               'LIMIT' => $limit,
+                               'OFFSET' => $offset
+                       ]
+               );
+               return $requests;
+       }
+
+       private static function getRequestFields( $email ) {
+               $dbr = wfGetDB( DB_SLAVE );
+               $request = $dbr->selectRow(
+                       'emailrequest',
+                       [
+                               'request',
+                       ],
+                       [
+                               'email' => $email
+                       ],
+                       __METHOD__
+               );
+               if ( $request === false ) {
+                       return '';
+               }
+               return json_decode( $request->request );
+       }
+
+       private static function insertEmail( $email ) {
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->upsert(
+                       'emailauth',
+                       [
+                               'email' => $email
+                       ],
+                       [
+                               'email' => $email
+                       ],
+                       [
+                               'email' => $email
+                       ],
+                       __METHOD__
+               );
+               if ( $dbw->affectedRows() === 1 ) {
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+
+       private static function deleteRequest( $email ) {
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->delete(
+                       'emailrequest',
+                       [
+                               'email' => $email
+                       ],
+                       __METHOD__
+               );
+       }
+}
diff --git a/includes/EmailAuthorizationConfig.php 
b/includes/EmailAuthorizationConfig.php
index 23e0b00..02971fb 100644
--- a/includes/EmailAuthorizationConfig.php
+++ b/includes/EmailAuthorizationConfig.php
@@ -404,8 +404,8 @@
                        . Html::element( 'legend', null,
                                wfMessage( 
'emailauthorization-config-legend-search' ) . ':' );
                list( $label, $input ) =
-                       Xml::inputLabelSep( 'Email address:', 'searchemail', 
'searchemail',
-                       50 );
+                       Xml::inputLabelSep( wfMessage( 
'emailauthorization-config-label-email' ) . ':',
+                               'searchemail', 'searchemail', 50 );
                $html .= $label . ' ' . $input . ' '
                        . Xml::submitButton(
                                wfMessage( 
'emailauthorization-config-button-search' ),
@@ -425,8 +425,8 @@
                        . Html::element( 'legend', null,
                                wfMessage( 
'emailauthorization-config-legend-add' ) . ':' );
                list( $label, $input ) =
-                       Xml::inputLabelSep( 'Email address:', 'addemail', 
'addemail', 50,
-                       $default );
+                       Xml::inputLabelSep( wfMessage( 
'emailauthorization-config-label-email' ) . ':',
+                               'addemail', 'addemail', 50, $default );
                $html .= $label . ' ' . $input . ' '
                        . Xml::submitButton(
                                wfMessage( 
'emailauthorization-config-button-add' ),
@@ -446,8 +446,8 @@
                        . Html::element( 'legend', null,
                                wfMessage( 
'emailauthorization-config-legend-revoke' ) . ':' );
                list( $label, $input ) =
-                       Xml::inputLabelSep( 'Email address:', 'revokeemail', 
'revokeemail',
-                       50, $default );
+                       Xml::inputLabelSep( wfMessage( 
'emailauthorization-config-label-email' ) . ':',
+                               'revokeemail', 'revokeemail', 50, $default );
                $html .= $label . ' ' . $input . ' '
                        . Xml::submitButton(
                                wfMessage( 
'emailauthorization-config-button-revoke' ),
diff --git a/includes/EmailAuthorizationHooks.php 
b/includes/EmailAuthorizationHooks.php
index 2b70624..f5bc345 100644
--- a/includes/EmailAuthorizationHooks.php
+++ b/includes/EmailAuthorizationHooks.php
@@ -28,7 +28,9 @@
                $dir = $GLOBALS['wgExtensionDirectory'] . DIRECTORY_SEPARATOR .
                        'EmailAuthorization' . DIRECTORY_SEPARATOR . 'sql' . 
DIRECTORY_SEPARATOR;
                $updater->addExtensionTable( 'emailauth',
-                       $dir . 'EmailAuthorization.sql', true );
+                       $dir . 'EmailAuth.sql', true );
+               $updater->addExtensionTable( 'emailrequest',
+                       $dir . 'EmailRequest.sql', true );
                return true;
        }
 
@@ -36,4 +38,50 @@
                $authorized = EmailAuthorization::isEmailAuthorized( 
$user->mEmail );
                return $authorized;
        }
+
+       public static function onRegistration() {
+               $GLOBALS['wgHooks']['SpecialPage_initList'][] = function ( 
&$list ) {
+                       if ( !$GLOBALS['wgEmailAuthorization_EnableRequests'] ) 
{
+                               unset( $list['EmailAuthorizationRequest'] );
+                       }
+               };
+       }
+
+       public static function onBeforeCreateEchoEvent( &$notifications,
+               &$notificationCategories, &$icons ) {
+
+               
$notificationCategories['emailauthorization-notification-category'] = [
+                       'priority' => 3
+               ];
+
+               $notifications['emailauthorization-account-request'] = [
+                       'category' => 
'emailauthorization-notification-category',
+                       'group' => 'positive',
+                       'section' => 'alert',
+                       'presentation-model' => EchoEAPresentationModel::class,
+                       'user-locators' => [ 
'EmailAuthorizationHooks::locateBureaucrats' ]
+               ];
+       }
+
+       public static function locateBureaucrats( $event ) {
+               $db = wfGetDB( DB_REPLICA );
+               $res = $db->select(
+                       [ 'user_groups', 'user' ],
+                       [ 'ug_user', 'ug_expiry' ],
+                       [ 'ug_group' => 'bureaucrat' ],
+                       __METHOD__,
+                       [],
+                       [ 'user' => [ 'INNER JOIN', [ 'ug_user = user_id' ] ] ]
+               );
+               $users = [];
+               foreach ( $res as $row ) {
+                       $id = $row->ug_user;
+                       $user = User::newFromId( $id );
+                       $expiry = $row->ug_expiry;
+                       if ( !$expiry || wfTimestampNow() < $expiry ) {
+                               $users[$id] = $user;
+                       }
+               }
+               return $users;
+       }
 }
diff --git a/includes/EmailAuthorizationRequest.php 
b/includes/EmailAuthorizationRequest.php
new file mode 100644
index 0000000..a520835
--- /dev/null
+++ b/includes/EmailAuthorizationRequest.php
@@ -0,0 +1,268 @@
+<?php
+
+/*
+ * Copyright (c) 2017 The MITRE Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+class EmailAuthorizationRequest extends SpecialPage {
+
+       function __construct() {
+               parent::__construct( 'EmailAuthorizationRequest' );
+       }
+
+       function execute( $par ) {
+               $request = $this->getRequest();
+               $this->setHeaders();
+               $this->getOutput()->addModuleStyles( 'ext.EmailAuthorization' );
+
+               $emailLabel =
+                       wfMessage( 'emailauthorization-request-label-email' 
)->text();
+               $emailfield = [
+                       'label' => $emailLabel,
+                       'mandatory' => true
+               ];
+               $fields = array_merge( [ $emailfield ],
+                       $GLOBALS['wgEmailAuthorization_RequestFields'] );
+
+               $showform = true;
+
+               $submitted = $request->getBool(
+                       'emailauthorization-request-field-submitted' );
+
+               if ( $submitted ) {
+
+                       $showform = self::processRequest( $request, $fields );
+
+               }
+
+               if ( $showform ) {
+
+                       $html = Html::openElement( 'p' )
+                               . Html::openElement( 'b' )
+                               . wfMessage( 
'emailauthorization-request-instructions' )->parse()
+                               . Html::closeElement( 'b' )
+                               . Html::closeElement( 'p' )
+                               . Html::element( 'br' );
+                       $this->getOutput()->addHtml( $html );
+
+                       $title = Title::newFromText( 'Special:' . __CLASS__ );
+                       $url = $title->getFullURL();
+
+                       $html = Html::openElement( 'form', [
+                                       'method' => 'post',
+                                       'action' => $url,
+                                       'id' => 'RequestEmail'
+                               ] );
+                       $id = 'emailauthorization-request-field-submitted';
+                       $html .= Html::hidden( $id, true );
+                       $i = 0;
+                       foreach ( $fields as $field ) {
+                               $id = 'emailauthorization-request-field-' . $i;
+                               $i++;
+                               if ( isset( $field['label'] ) ) {
+                                       $html .= Xml::label( $field['label'] . 
': ', $id );
+                                       $mandatory = false;
+                                       if ( isset( $field['mandatory'] ) && 
$field['mandatory'] ) {
+                                               $html .= '* ';
+                                               $mandatory = true;
+                                       }
+                                       $attribs = [ 'id' => $id ];
+                                       if ( $submitted && $mandatory && 
$request->getText( $id ) === '' ) {
+                                               $attribs['style'] = 
"border-color:red;";
+                                       }
+                                       if ( isset( $field['values'] ) ) {
+                                               $attribs['name'] = $id;
+                                               $html .= Xml::openElement( 
'select', $attribs );
+                                               foreach ( $field['values'] as 
$value ) {
+                                                       $html .= Xml::option( 
$value, $value );
+                                               }
+                                               $html .= Xml::closeElement( 
'select' );
+                                               $html .= Html::element( 'br' );
+                                       } elseif ( isset( $field['rows'] ) ) {
+                                               $rows = $field['rows'];
+                                               $columns = 50;
+                                               if ( isset( $field['columns'] ) 
) {
+                                                       $columns = 
$field['columns'];
+                                               }
+                                               $value = '';
+                                               if ( $submitted ) {
+                                                       $value = 
$request->getText( $id );
+                                               }
+                                               $input = Xml::textarea( $id, 
$value, $columns, $rows, $attribs );
+                                               $html .= $input;
+                                       } else {
+                                               $columns = 50;
+                                               if ( isset( $field['columns'] ) 
) {
+                                                       $columns = 
$field['columns'];
+                                               }
+                                               $value = '';
+                                               if ( $submitted ) {
+                                                       $value = 
$request->getText( $id );
+                                               }
+                                               $input = Xml::input( $id, 
$columns, $value, $attribs );
+                                               $html .= $input;
+                                               $html .= Html::element( 'br' );
+                                       }
+                                       $html .= Html::element( 'br' );
+                               }
+                       }
+                       $html .= Xml::submitButton(
+                               wfMessage( 
'emailauthorization-request-button-submit' ),
+                               [ 'class' => 'emailauth-button' ] )
+                               . Html::closeElement( 'form' );
+                       $this->getOutput()->addHtml( $html );
+               }
+       }
+
+       private function displayMessage( $message ) {
+               $html = Html::openElement( 'p', [
+                               'class' => 'emailauth-message'
+                       ] )
+                       . $message
+                       . Html::closeElement( 'p' );
+               $this->getOutput()->addHtml( $html );
+       }
+
+       private function validateEmail( $email ) {
+               if ( is_null( $email ) || strlen( $email ) < 1 ) {
+                       return false;
+               }
+               $email = mb_strtolower( htmlspecialchars( trim( $email ), 
ENT_QUOTES ) );
+               if ( filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
+                       return $email;
+               }
+               return false;
+       }
+
+       private function processRequest( $request, $fields ) {
+               $i = 0;
+               foreach ( $fields as $field ) {
+                       $id = 'emailauthorization-request-field-' . $i;
+                       $i++;
+                       if ( isset( $field['label'] ) ) {
+                               if ( isset( $field['mandatory'] ) && 
$field['mandatory'] &&
+                                       $request->getText( $id ) === '' ) {
+                                       $this->displayMessage(
+                                               wfMessage( 
'emailauthorization-request-missingmandatory' ) );
+                                       return true;
+                               }
+                       }
+               }
+               $email = $request->getText( 
'emailauthorization-request-field-0' );
+               $validatedemail = $this->validateEmail( $email );
+               if ( $validatedemail === false ) {
+                       $this->displayMessage(
+                               wfMessage( 
'emailauthorization-request-invalidemail', $email )
+                       );
+                       return true;
+               }
+               $email = $validatedemail;
+               if ( self::checkEmail( $email ) ) {
+                       if ( self::insertRequest( $email, $request ) ) {
+                               if ( class_exists( 'EchoEvent' ) ) {
+                                       $extra = [
+                                               'email' => $validatedemail,
+                                               'notifyAgent' => true
+                                       ];
+                                       EchoEvent::create( [
+                                               'type' => 
'emailauthorization-account-request',
+                                               'extra' => $extra
+                                       ] );
+                               }
+                       } else {
+                               $this->displayMessage(
+                                       wfMessage( 
'emailauthorization-request-error', $validatedemail )
+                               );
+                               return true;
+                       }
+               }
+               $this->displayMessage(
+                       wfMessage( 'emailauthorization-request-requested', 
$validatedemail )
+               );
+               return false;
+       }
+
+       private static function checkEmail( $email ) {
+               $dbr = wfGetDB( DB_SLAVE );
+               $users = $dbr->select(
+                       'user',
+                       [
+                               'user_email'
+                       ],
+                       [
+                               'user_email' => $email
+                       ],
+                       __METHOD__
+               );
+               if ( $users->valid() ) {
+                       $users = $dbr->select(
+                               'emailauth',
+                               [
+                                       'email'
+                               ],
+                               [
+                                       'email' => $email
+                               ],
+                               __METHOD__
+                       );
+                       if ( $users->valid() ) {
+                               return false;
+                       }
+                       return true;
+               }
+                       return true;
+               return true;
+       }
+       private static function insertRequest( $email, $request ) {
+               $i = 1;
+               $data = [];
+               foreach ( $GLOBALS['wgEmailAuthorization_RequestFields'] as 
$field ) {
+                       $id = 'emailauthorization-request-field-' . $i;
+                       $i++;
+                       $value = $request->getText( $id ) ;
+                       if ( $value ) {
+                               $data[$field['label']] = $value;
+                       }
+               }
+               $json = json_encode($data);
+               $dbw = wfGetDB( DB_MASTER );
+               $res = $dbw->upsert(
+                       'emailrequest',
+                       [
+                               'email' => $email,
+                               'request' => $json,
+                       ],
+                       [
+                               'email' => $email
+                       ],
+                       [
+                               'request' => $json,
+                       ],
+                       __METHOD__
+               );
+               if ( $res ) {
+                       wfRunHooks( 'EmailAuthorizationRequest', [ $email, 
$data ] );
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+}
diff --git a/sql/EmailAuthorization.sql b/sql/EmailAuth.sql
similarity index 100%
rename from sql/EmailAuthorization.sql
rename to sql/EmailAuth.sql
diff --git a/sql/EmailRequest.sql b/sql/EmailRequest.sql
new file mode 100644
index 0000000..b2d8062
--- /dev/null
+++ b/sql/EmailRequest.sql
@@ -0,0 +1,5 @@
+CREATE TABLE `emailrequest` (
+  `email` tinyblob NOT NULL,
+  `request` blob NOT NULL,
+  PRIMARY KEY (`email`(50))
+) ENGINE=InnoDB DEFAULT CHARSET=binary;

-- 
To view, visit https://gerrit.wikimedia.org/r/366780
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I97c2c995eb0eeea760824468d3b4453f6e7ccb09
Gerrit-PatchSet: 18
Gerrit-Project: mediawiki/extensions/EmailAuthorization
Gerrit-Branch: master
Gerrit-Owner: Cicalese <cin...@gmail.com>
Gerrit-Reviewer: Cicalese <cin...@gmail.com>
Gerrit-Reviewer: Matthew-a-thompson <mathomp...@mitre.org>
Gerrit-Reviewer: Siebrand <siebr...@kitano.nl>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to