MZMcBride has submitted this change and it was merged.

Change subject: Parse the input list using a parser function
......................................................................


Parse the input list using a parser function

Users would use {{#target:User talk:Example|domain}} to add a subscription.
The domain part is optional and will result in a local delivery if not provided.

$wgNamepsacesToExtractLinksFor was renamed to $wgNamespacesToPostIn, and now 
controls
which namespaces we can post in. This checking is now done when the job 
executes so each
wiki can control their own settings. Basic duplicate checking is still done 
when parsing
the spamlist.

A dependency is added upon the SiteMatrix extension for any global deliveries.

Change-Id: I6cf785a1e89cad6f30b6abf7b00d5a9845e66e4b
---
M MassMessage.body.php
A MassMessage.hooks.php
A MassMessage.i18n.magic.php
M MassMessage.i18n.php
M MassMessage.php
M MassMessageJob.php
M SpecialMassMessage.php
7 files changed, 225 insertions(+), 17 deletions(-)

Approvals:
  Legoktm: Looks good to me, but someone else must approve
  MZMcBride: Verified; Looks good to me, approved
  jenkins-bot: Checked



diff --git a/MassMessage.body.php b/MassMessage.body.php
index 1cb462f..899a247 100644
--- a/MassMessage.body.php
+++ b/MassMessage.body.php
@@ -13,6 +13,13 @@
 
 
 class MassMessage {
+
+       /**
+        * A mapping of hostname to database name
+        * @var array
+        */
+       protected $dbnames = array();
+
        /**
         * Function to follow redirects
         *
@@ -68,4 +75,79 @@
 
                return $user;
        }
+       /**
+        * Returns the basic hostname and port using wfParseUrl
+        * @param  string $url URL to parse
+        * @return string
+        */
+       public static function getBaseUrl( $url ) {
+               $parse = wfParseUrl( $url );
+               $site = $parse['host'];
+               if ( isset( $parse['port'] ) ) {
+                       $site .= ':' . $parse['port'];
+               }
+               return $site;
+       }
+
+       /**
+        * Get database name from URL hostname
+        * Requires Extension:SiteMatrix. If not available will return null.
+        * @param  string $host
+        * @return string
+        */
+       public static function getDBName( $host ) {
+               if ( isset( $this->dbnames[$host] ) ) {
+                       return $this->dbnames[$host];
+               }
+               if ( !class_exists( 'SiteMatrix' ) ) {
+                       return null;
+               }
+               $matrix = new SiteMatrix();
+               foreach ( $matrix->hosts as $dbname => $url ) {
+                       $parse = wfParseUrl( $url );
+                       if ( $parse['host'] == $host ) {
+                               $this->dbnames[$host] = $dbname; // Store it 
for later
+                               return $dbname;
+                       }
+               }
+               return null; // Couldn't find anything
+       }
+
+       /**
+        * Normalizes an array of page/site combos
+        * Also removes some dupes
+        * @param  array $pages
+        * @param  bool $isLocal
+        * @return array
+        * @fixme Follow redirects on other sites
+        */
+       public static function normalizeSpamList( $pages, $isLocal ) {
+               global $wgDBname;
+               $data = array();
+               foreach ( $pages as $page ) {
+                       if ( $isLocal ) {
+                               $title = Title::newFromText( $page['title'] );
+                               $title = self::followRedirect( $title );
+                               if ( $title == null ) {
+                                       continue; // Interwiki redirect
+                               }
+                               $page['title'] = $title->getFullText();
+                       }
+                       if ( !isset( $page['dbname'] ) ) {
+                               $dbname = self::getDBName( $page['site'] );
+                               if ( $dbname == null ) { // Not in the site 
matrix?
+                                       continue;
+                               }
+                               $page['dbname'] = $dbname;
+                       }
+                       // Use an assoc array to clear dupes
+                       if ( $page['dbname'] == $wgDBname || !$isLocal ) {
+                               // If the delivery is local, only allow 
requests on the same site.
+                               $data[$page['title'] . $page['site']] = $page;
+                       }
+
+               }
+
+               return $data;
+       }
 }
diff --git a/MassMessage.hooks.php b/MassMessage.hooks.php
new file mode 100644
index 0000000..c94526c
--- /dev/null
+++ b/MassMessage.hooks.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * Hooks!
+ */
+
+class MassMessageHooks {
+
+       /**
+        * Hook to load our parser function
+        * @param  Parser $parser
+        * @return bool
+        */
+       public static function onParserFirstCallInit( Parser &$parser ) {
+               $parser->setFunctionHook( 'target', 
'MassMessageHooks::ParserFunction' );
+               return true;
+       }
+
+       /**
+        * Parser function for {{#target:User talk:Example|en.wikipedia.org}}
+        * Hostname is optional for local delivery
+        * @param Parser $parser
+        * @param string $site
+        * @param string $page
+        * @return array
+        */
+       public static function ParserFunction( $parser, $page, $site = '' ) {
+               global $wgScript;
+               $data = array( 'site' => $site, 'title' => $page );
+               if ( trim( $site ) === '' ) {
+                       // Assume it's a local delivery
+                       global $wgServer, $wgDBname;
+                       $site = MassMessage::getBaseUrl( $wgServer );
+                       $data['site'] = $site;
+                       $data['dbname'] = $wgDBname;
+               }
+               // Use a message so wikis can customize the output
+               $msg = wfMessage( 'massmessage-target' )->params( $site, 
$wgScript, $page )->plain();
+               $output = $parser->getOutput();
+
+               // Store the data in case we're parsing it manually
+               if ( defined( 'MASSMESSAGE_PARSE' ) ) {
+                       if ( !$output->getProperty( 'massmessage-targets' ) ) {
+                               $output->setProperty( 'massmessage-targets', 
array( $data ) );
+                       } else {
+                               $output->setProperty( 'massmessage-targets' , 
array_merge( $output->getProperty( 'massmessage-targets' ),  array( $data ) ) );
+                       }
+               }
+
+               return array( $msg, 'noparse' => false );
+       }
+}
diff --git a/MassMessage.i18n.magic.php b/MassMessage.i18n.magic.php
new file mode 100644
index 0000000..4f3d0ee
--- /dev/null
+++ b/MassMessage.i18n.magic.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Internationalization file.
+ */
+
+$magicWords = array();
+
+$magicWords['en'] = array(
+   'target' => array( 0, 'target' ),
+);
diff --git a/MassMessage.i18n.php b/MassMessage.i18n.php
index c3de09f..22278dc 100644
--- a/MassMessage.i18n.php
+++ b/MassMessage.i18n.php
@@ -30,6 +30,7 @@
        'massmessage-empty-subject' => 'The subject line is empty.',
        'massmessage-empty-message' => 'The message body is empty.',
        'massmessage-form-header' => 'Use the form below to send messages to a 
specified list. All fields are required.',
+       'massmessage-target' => '[//$1$2?title={{urlencode:$3|WIKI}} $3]',
        'right-massmessage' => 'Send a message to multiple users at once',
        'action-massmessage' => 'send a message to multiple users at once',
        'right-massmessage-global' => 'Send a message to multiple users on 
different wikis at once',
@@ -69,6 +70,10 @@
        'massmessage-empty-subject' => 'Error message the user sees if the 
"subject" field is empty.',
        'massmessage-empty-message' => 'Error message the user sees if the 
"message" field is empty.',
        'massmessage-form-header' => 'Introduction text at the top of the 
form.',
+       'massmessage-target' => 'Used to display the {{#target}} parserfunction.
+* $1 is the domain (example: "en.wikipedia.org")
+* $2 is <code>$wgScriptPath</code> (example: "/w/index.php")
+* $3 the page name (example: "User talk:Example")',
        'right-massmessage' => '{{doc-right|massmessage}}
 See also:
 * {{msg-mw|Right-massmessage-global}}',
diff --git a/MassMessage.php b/MassMessage.php
index 93b7808..fcf0445 100644
--- a/MassMessage.php
+++ b/MassMessage.php
@@ -16,20 +16,21 @@
 }
 
 /*
- * Namespaces to extract links for
+ * Namespaces to post in
  *
- * From your spamlist, only links to these
- * domains will be checked.
- * Only applies to local messages.
+ * Only let the bot post in these namespaces regardless
+ * of what the user specificed in the input list. This is checked
+ * after $wgNamespacesToConvert is applied.
+ * Applies to both local and global messages.
  */
-$wgNamespacesToExtractLinksFor = array( NS_PROJECT, NS_USER, NS_USER_TALK );
+$wgNamespacesToPostIn = array( NS_PROJECT, NS_USER_TALK );
 
 /*
  * Namespaces to convert
  *
  * If you want users to be able to provide a link to a User: page,
  * but have the bot post on their User talk: page you can define that here.
- * Only applies to local messages.
+ * Applies to both local and global messages.
  */
 $wgNamespacesToConvert = array( NS_USER => NS_USER_TALK );
 
@@ -54,11 +55,15 @@
 $wgSpecialPages[ 'MassMessage' ] = 'SpecialMassMessage';
 $wgExtensionMessagesFiles['MassMessage'] = "$dir/MassMessage.i18n.php";
 $wgExtensionMessagesFiles['MassMessageAlias'] = "$dir/MassMessage.alias.php";
+$wgExtensionMessagesFiles['MassMessageMagic'] = 
"$dir/MassMessage.i18n.magic.php";
 $wgAutoloadClasses['MassMessage'] = "$dir/MassMessage.body.php";
+$wgAutoloadClasses['MassMessageHooks'] = "$dir/MassMessage.hooks.php";
 $wgAutoloadClasses['SpecialMassMessage'] = "$dir/SpecialMassMessage.php";
 $wgAutoloadClasses['MassMessageJob'] = "$dir/MassMessageJob.php";
 $wgJobClasses['massmessageJob'] = 'MassMessageJob';
 
+$wgHooks['ParserFirstCallInit'][] = 'MassMessageHooks::onParserFirstCallInit';
+
 $wgResourceModules['ext.MassMessage.special'] = array(
        'scripts' => 'ext.MassMessage.special.js',
        'dependencies' => array(
diff --git a/MassMessageJob.php b/MassMessageJob.php
index 3e26d8e..20de7b4 100644
--- a/MassMessageJob.php
+++ b/MassMessageJob.php
@@ -30,6 +30,21 @@
 
                return true;
        }
+       /**
+        * Normalizes the title according to $wgNamespacesToConvert and 
$wgNamespacesToPostIn
+        * @param  Title $title
+        * @return Title|null null if we shouldn't post on that title
+        */
+       function normalizeTitle( $title ) {
+               global $wgNamespacesToPostIn, $wgNamespacesToConvert;
+               if ( isset( $wgNamespacesToConvert[$title->getNamespace()] ) ) {
+                       $title = Title::makeTitle( 
$wgNamespacesToConvert[$title->getNamespace()], $title->getText() );
+               }
+               if ( !in_array( $title->getNamespace(), $wgNamespacesToPostIn ) 
) {
+                       $title = null;
+               }
+               return $title;
+       }
 
        /**
         * Log any message failures on the submission site.
@@ -60,9 +75,14 @@
         * @return bool
         */
        function sendLocalMessage() {
+               $title = $this->normalizeTitle( $this->title );
+               if ( $title === null ) {
+                       return true; // Skip it
+               }
+
                $text = "== " . $this->params['subject'] . " ==\n\n" . 
$this->params['message'];
 
-               $talkPage = WikiPage::factory( $this->title );
+               $talkPage = WikiPage::factory( $title );
                $flags = $talkPage->checkFlags( 0 );
                if ( $flags & EDIT_UPDATE ) {
                        $content = $talkPage->getContent( Revision::RAW );
@@ -78,8 +98,8 @@
 
                // If we're sending to a User talk: page, make sure the user 
exists.
                // Redirects are automatically followed in getLocalTargets
-               if ( $this->title->getNamespace() == NS_USER_TALK ) {
-                       $user = User::newFromName( $this->title->getBaseText() 
);
+               if ( $title->getNamespace() == NS_USER_TALK ) {
+                       $user = User::newFromName( $title->getBaseText() );
                        if ( !$user->getId() ) { // Does not exist
                                return true; // Should we log anything here?
                        }
diff --git a/SpecialMassMessage.php b/SpecialMassMessage.php
index 6a5fac2..84ccd98 100644
--- a/SpecialMassMessage.php
+++ b/SpecialMassMessage.php
@@ -123,6 +123,39 @@
 
                return $m;
        }
+       /**
+        * Get an array of targets via the #target parser function
+        * @param  Title $spamlist
+        * @return array
+        */
+       function getParserFunctionTargets( $spamlist ) {
+               $page = WikiPage::factory( $spamlist );
+               $content = $page->getContent( Revision::RAW );
+               if ( $content instanceof TextContent ) {
+                       $text = $content->getNativeData();
+               } else {
+                       // Spamlist input isn't a text page
+                       $this->status->fatal( 
'massmessage-spamlist-doesnotexist' );
+                       return array();
+               }
+
+               // Prep the parser
+               define( 'MASSMESSAGE_PARSE', true );
+               $article = Article::newFromTitle( $spamlist, 
$this->getContext() );
+               $parserOptions = $article->makeParserOptions( 
$article->getContext() );
+               $parser = new Parser();
+
+               // Parse
+               $output = $parser->parse( $text, $spamlist, $parserOptions );
+               $data = $output->getProperty( 'massmessage-targets' );
+
+               if ( $data ) {
+                       return $data;
+               } else {
+                       return array();  // No parser functions on page
+               }
+
+       }
 
        /**
         * Get a list of pages to spam
@@ -232,7 +265,7 @@
         */
        function verifyData( $data ) {
 
-               $global = isset( $data['global'] ) && $data['global']; // If 
the message delivery is global
+               $this->isGlobal = isset( $data['global'] ) && $data['global']; 
// If the message delivery is global
 
                $spamlist = $this->getLocalSpamlist( $data['spamlist'] );
                if ( !( $spamlist instanceof Title ) ) {
@@ -241,8 +274,8 @@
 
                // Check that our account hasn't been blocked.
                $user = MassMessage::getMessengerUser();
-               if ( !$global && $user->isBlocked() ) {
-                       // If our delivery is global, it doesnt matter if our 
local account is blocked
+               if ( !$this->isGlobal && $user->isBlocked() ) {
+                       // If our delivery is global, it doesn't matter if our 
local account is blocked
                        $this->status->fatal( 'massmessage-account-blocked' );
                }
 
@@ -308,17 +341,18 @@
         * @return Status
         */
        function submit( $data ) {
-
                $spamlist = $this->getLocalSpamlist( $data['spamlist'] );
 
                // Log it.
                $this->logToWiki( $spamlist, $data['subject'] );
 
                // Insert it into the job queue.
-               $pages = $this->getLocalTargets( $spamlist );
-               foreach ( $pages as /*$title => */$page ) {
-                       $job = new MassMessageJob( $page, $data );
-                       JobQueueGroup::singleton()->push( $job );
+               $pages = $this->getParserFunctionTargets( $spamlist );
+               $pages = MassMessage::normalizeSpamList( $pages, 
!$this->isGlobal );
+               foreach ( $pages as $page ) {
+                       $title = Title::newFromText( $page['title'] );
+                       $job = new MassMessageJob( $title, $data );
+                       JobQueueGroup::singleton( $dbname )->push( $job );
                }
                return $this->status;
        }

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I6cf785a1e89cad6f30b6abf7b00d5a9845e66e4b
Gerrit-PatchSet: 7
Gerrit-Project: mediawiki/extensions/MassMessage
Gerrit-Branch: master
Gerrit-Owner: Legoktm <[email protected]>
Gerrit-Reviewer: Legoktm <[email protected]>
Gerrit-Reviewer: MZMcBride <[email protected]>
Gerrit-Reviewer: Matmarex <[email protected]>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to