tosfos has uploaded a new change for review.
https://gerrit.wikimedia.org/r/169315
Change subject: initial commit
......................................................................
initial commit
Change-Id: I08280bfa5bf5f6e76be19c52faece90f98e5a2f2
---
A .gitignore
A ContributionsList.class.php
A ContributionsList.hooks.php
A ContributionsList.magic.php
A ContributionsList.php
A i18n/en.json
A i18n/qqq.json
7 files changed, 492 insertions(+), 0 deletions(-)
git pull
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/ContributionsList
refs/changes/15/169315/1
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ee920b3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.jshintrc
diff --git a/ContributionsList.class.php b/ContributionsList.class.php
new file mode 100644
index 0000000..f319867
--- /dev/null
+++ b/ContributionsList.class.php
@@ -0,0 +1,358 @@
+<?php
+
+/**
+ * ContributionsList - generates a list of contributions
+ *
+ * @author Ike Hecht
+ */
+class ContributionsList extends ContextSource {
+ /**
+ * Username of target
+ *
+ * @var string
+ */
+ private $user;
+
+ /**
+ * Category name to target
+ *
+ * @var string
+ */
+ private $category;
+
+ /**
+ * The type of contributions to show.
+ * Can be either "createonly" to only show contributions that created
the page
+ * or "notcreate" to show only contributions that modified an existing
page.
+ * Any other value will show all contributions.
+ *
+ * @var string
+ */
+ private $type;
+
+ /**
+ * Only show contributions that were on or after this date.
+ *
+ * @var string
+ */
+ private $dateFrom;
+
+ /**
+ * Only show contributions that were on or before this date.
+ *
+ * @var string
+ */
+ private $dateTo;
+
+ /**
+ * A read-only database object
+ *
+ * @var DatabaseBase
+ */
+ private $db;
+
+ /**
+ * The index to actually be used for ordering. This is a single column,
+ * for one ordering, even if multiple orderings are supported.
+ *
+ * @var string
+ */
+ protected $indexField = 'rev_timestamp';
+
+ /**
+ * Result object for the query. Warning: seek before use.
+ *
+ * @var ResultWrapper
+ */
+ public $result;
+
+ /**
+ * Get all parameters required for the query
+ *
+ * @param string $user
+ * @param string $category
+ * @param string $type
+ * @param string $dateFrom
+ * @param string $dateTo
+ * @param IContextSource $context
+ */
+ public function __construct( $user, $category = null, $type = null,
$dateFrom = null,
+ $dateTo = null, IContextSource $context = null ) {
+ if ( $context ) {
+ $this->setContext( $context );
+ }
+
+ $this->db = wfGetDB( DB_SLAVE, 'contributionslist' );
+
+ $this->user = $user;
+
+ if ( $category ) {
+ $this->category = Title::newFromText( $category,
NS_CATEGORY );
+ }
+
+ if ( $type ) {
+ $this->type = $type;
+ }
+
+ if ( $dateFrom ) {
+ $this->dateFrom = wfTimestamp( TS_MW, strtotime(
$dateFrom ) );
+ }
+ if ( $dateTo ) {
+ // We want the end of the $dateTo, not the beginning.
+ $this->dateTo = wfTimestamp( TS_MW, strtotime(
"tomorrow", strtotime( $dateTo ) ) - 1 );
+ }
+
+ $this->doQuery();
+ }
+
+ /**
+ * Perform the db query
+ * The query code is based on ContribsPager
+ */
+ public function doQuery() {
+ # Use the child class name for profiling
+ $fname = __METHOD__ . ' (' . get_class( $this ) . ')';
+ wfProfileIn( $fname );
+
+ list( $tables, $fields, $conds, $fname, $options, $join_conds )
= $this->buildQueryInfo();
+ $this->result = $this->db->select(
+ $tables, $fields, $conds, $fname, $options, $join_conds
+ );
+
+ $this->result->rewind(); // Paranoia
+
+ wfProfileOut( $fname );
+ }
+
+ /**
+ * @return string
+ */
+ function getSqlComment() {
+ return get_class( $this );
+ }
+
+ /**
+ * Generate an array to be turned into the full and final query.
+ *
+ * @return array
+ */
+ protected function buildQueryInfo() {
+ $fname = __METHOD__ . ' (' . $this->getSqlComment() . ')';
+ $info = $this->getQueryInfo();
+ $tables = $info['tables'];
+ $fields = $info['fields'];
+ $conds = isset( $info['conds'] ) ? $info['conds'] : array();
+ $options = isset( $info['options'] ) ? $info['options'] :
array();
+ $join_conds = isset( $info['join_conds'] ) ?
$info['join_conds'] : array();
+
+ $options['ORDER BY'] = $this->indexField . ' DESC';
+
+ if ( $this->dateFrom ) {
+ $conds[] = $this->indexField .
+ '>=' . $this->db->addQuotes( $this->dateFrom );
+ }
+ if ( $this->dateTo ) {
+ $conds[] = $this->indexField .
+ '<=' . $this->db->addQuotes( $this->dateTo );
+ }
+
+ return array( $tables, $fields, $conds, $fname, $options,
$join_conds );
+ }
+
+ /**
+ * Generate an array of basic query info.
+ *
+ * @return array
+ */
+ function getQueryInfo() {
+ list( $tables, $index, $userCond, $join_cond ) =
$this->getUserCond();
+
+ if ( $this->category instanceof Title ) {
+ $tables[] = 'categorylinks';
+ $conds = array_merge( $userCond, array( 'cl_to' =>
$this->category->getDBkey() ) );
+ $join_cond['categorylinks'] = array( 'INNER JOIN',
'cl_from = page_id' );
+ } else {
+ $conds = $userCond;
+ }
+
+ $user = $this->getUser();
+ // Paranoia: avoid brute force searches (bug 17342)
+ if ( !$user->isAllowed( 'deletedhistory' ) ) {
+ $conds[] = $this->db->bitAnd( 'rev_deleted',
Revision::DELETED_USER ) . ' = 0';
+ } elseif ( !$user->isAllowed( 'suppressrevision' ) ) {
+ $conds[] = $this->db->bitAnd( 'rev_deleted',
Revision::SUPPRESSED_USER ) .
+ ' != ' . Revision::SUPPRESSED_USER;
+ }
+
+ # Don't include orphaned revisions
+ $join_cond['page'] = Revision::pageJoinCond();
+ # Get the current user name for accounts
+ $join_cond['user'] = Revision::userJoinCond();
+
+ $options = array();
+ if ( $index ) {
+ $options['USE INDEX'] = array( 'revision' => $index );
+ }
+ $queryInfo = array(
+ 'tables' => $tables,
+ 'fields' => array_merge(
+ Revision::selectFields(),
Revision::selectUserFields(),
+ array( 'page_namespace', 'page_title',
'page_is_new',
+ 'page_latest', 'page_is_redirect', 'page_len' )
+ ),
+ 'conds' => $conds,
+ 'options' => $options,
+ 'join_conds' => $join_cond
+ );
+
+ return $queryInfo;
+ }
+
+ /**
+ * Generate the condition that will fetch revisions of a specific user
+ *
+ * @return array
+ */
+ function getUserCond() {
+ $condition = array();
+ $join_conds = array();
+ $tables = array( 'revision', 'page', 'user' );
+ $index = false;
+
+ $uid = User::idFromName( $this->user );
+ if ( $uid ) {
+ $condition['rev_user'] = $uid;
+ $index = 'user_timestamp';
+ } else {
+ $condition['rev_user_text'] = $this->user;
+ $index = 'usertext_timestamp';
+ }
+
+ if ( $this->type == 'createonly' ) {
+ $condition[] = 'rev_parent_id = 0';
+ } elseif ( $this->type == 'notcreate' ) {
+ $condition[] = 'rev_parent_id != 0';
+ }
+
+ return array( $tables, $index, $condition, $join_conds );
+ }
+
+ function getIndexField() {
+ return 'rev_timestamp';
+ }
+
+ /**
+ * Generate the HTML for a valid page title that links to its page
+ *
+ * @param stdClass $row Object databse row
+ * @return string HTML link to a page title
+ */
+ function getLinkedTitle( $row ) {
+ get_class( $row );
+ wfProfileIn( __METHOD__ );
+
+ $classes = array();
+
+ /*
+ * There may be more than just revision rows. To make sure that
we'll only be processing
+ * revisions here, let's _try_ to build a revision out of our
row (without displaying
+ * notices though) and then trying to grab data from the built
object. If we succeed,
+ * we're definitely dealing with revision data and we may
proceed, if not, we'll leave it
+ * to extensions to subscribe to the hook to parse the row.
+ */
+ wfSuppressWarnings();
+ try {
+ $rev = new Revision( $row );
+ $validRevision = (bool) $rev->getId();
+ } catch ( MWException $e ) {
+ $validRevision = false;
+ }
+ wfRestoreWarnings();
+
+ if ( $validRevision ) {
+ $classes = array();
+
+ $page = Title::newFromRow( $row );
+ $link = Linker::link(
+ $page, htmlspecialchars(
$page->getPrefixedText() ),
+ array( 'class' =>
'contributionslist-title' ),
+ $page->isRedirect() ? array( 'redirect'
=> 'no' ) : array()
+ );
+ }
+
+ wfProfileOut( __METHOD__ );
+
+ return $link;
+ }
+
+ /**
+ * Get a list of contributions in the specified format
+ *
+ * @param string $format A valid format
+ * @return string HTML list
+ */
+ public function getContributionsList( $format ) {
+ if ( !in_array( strtolower( $format ), self::getValidFormats()
) ) {
+ $format = 'ul';
+ }
+ return call_user_func( array( $this, 'getContributionsList_' .
$format ) );
+ }
+
+ /**
+ * A list of valid list formats
+ *
+ * @return array
+ */
+ public static function getValidFormats() {
+ return array( 'ol', 'ul', 'plain' );
+ }
+
+ /**
+ * Get an ordered list of contributions
+ *
+ * @return string HTML ordered list
+ */
+ public function getContributionsList_ol() {
+ return $this->getContributionsListList( 'ol' );
+ }
+
+ /**
+ * Get an unordered list of contributions
+ *
+ * @return string HTML unordered list
+ */
+ public function getContributionsList_ul() {
+ return $this->getContributionsListList( 'ul' );
+ }
+
+ /**
+ * Get a list of contributions
+ *
+ * @param string $type An HTML list type: 'ol' or 'ul'
+ * @return string HTML list
+ */
+ public function getContributionsListList( $type ) {
+ $html = Html::openElement( $type, array( 'class' =>
'contributionslist' ) );
+ while ( $row = $this->result->fetchObject() ) {
+ $html .= Html::rawElement( 'li', array(),
$this->getLinkedTitle( $row ) );
+ }
+ $html .= Html::closeElement( $type );
+
+ return $html;
+ }
+
+ /**
+ * Get a plain comma-separated list of contributions with an "and"
before the final list item
+ *
+ * @return string HTML plain list
+ */
+ public function getContributionsList_plain() {
+ $links = array();
+ while ( $row = $this->result->fetchObject() ) {
+ $links[] = $this->getLinkedTitle( $row );
+ }
+ $lang = $this->getLanguage();
+ $html = $lang->listToText( $links );
+ return $html;
+ }
+}
diff --git a/ContributionsList.hooks.php b/ContributionsList.hooks.php
new file mode 100644
index 0000000..9a62290
--- /dev/null
+++ b/ContributionsList.hooks.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * Hooks for ContributionsList extension
+ *
+ * @file
+ * @ingroup Extensions
+ */
+class ContributionsListHooks {
+
+ /**
+ * Set up the #contributionslist parser function
+ *
+ * @param Parser $parser
+ * @return boolean
+ */
+ public static function setupParserFunction( Parser &$parser ) {
+ $parser->setFunctionHook( 'contributionslist', __CLASS__ .
'::contributionslistParserFunction',
+ SFH_OBJECT_ARGS );
+
+ return true;
+ }
+
+ /**
+ *
+ * The parser function is called with the form:
+ * {{#contributionslist:
+ * user=username | category=categoryname |
type=createonly/notcreate/all |
+ * format=plain/ol/ul | datefrom=fromdate | dateto=todate }}
+ *
+ * @param Parser $parser
+ * @param PPFrame $frame
+ * @param array $args
+ * @return string
+ */
+ public static function contributionslistParserFunction(
+ Parser $parser, PPFrame $frame, array $args ) {
+ $params = self::extractOptions( $args, $frame );
+
+ $user = isset( $params['user'] ) ? $params['user'] : '';
+ $category = isset( $params['category'] ) ? $params['category']
: '';
+ $type = isset( $params['type'] ) ? $params['type'] : '';
+ $format = isset( $params['format'] ) ? $params['format'] : '';
+ $dateFrom = isset( $params['datefrom'] ) ? $params['datefrom']
: '';
+ $dateTo = isset( $params['dateto'] ) ? $params['dateto'] : '';
+
+ $contributionsList = new ContributionsList( $user, $category,
$type, $dateFrom, $dateTo );
+ $output = $contributionsList->getContributionsList( $format );
+ return array( $output, 'noparse' => true, 'isHTML' => true );
+ }
+
+ /**
+ * Converts an array of values in form [0] => "name=value" into a real
+ * associative array in form [name] => value
+ *
+ * @param array $options
+ * @param PPFrame $frame
+ * @return array
+ */
+ public static function extractOptions( array $options, PPFrame $frame )
{
+ $results = array();
+
+ foreach ( $options as $option ) {
+ $pair = explode( '=', $frame->expand( $option ), 2 );
+ if ( count( $pair ) == 2 ) {
+ $name = strtolower( trim( $pair[0] ) );
+ $value = trim( $pair[1] );
+ $results[$name] = $value;
+ }
+ }
+
+ return $results;
+ }
+}
diff --git a/ContributionsList.magic.php b/ContributionsList.magic.php
new file mode 100644
index 0000000..63ababf
--- /dev/null
+++ b/ContributionsList.magic.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * Magic words
+ *
+ * @author Ike Hecht
+ */
+$magicWords = array();
+
+/** English
+ * @author Ike Hecht
+ */
+$magicWords['en'] = array(
+ 'contributionslist' => array( 0, 'contributionslist' ),
+);
diff --git a/ContributionsList.php b/ContributionsList.php
new file mode 100644
index 0000000..659d324
--- /dev/null
+++ b/ContributionsList.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * ContributionsList extension
+ *
+ * For more info see http://mediawiki.org/wiki/Extension:ContributionsList
+ *
+ * @file
+ * @ingroup Extensions
+ * @author Ike Hecht, 2014
+ * @license GNU General Public Licence 2.0 or later
+ */
+$wgExtensionCredits['other'][] = array(
+ 'path' => __FILE__,
+ 'name' => 'ContributionsList',
+ 'author' => array(
+ 'Ike Hecht',
+ ),
+ 'version' => '0.1.0',
+ 'url' => 'https://www.mediawiki.org/wiki/Extension:ContributionsList',
+ 'descriptionmsg' => 'contributionslist-desc',
+);
+
+$wgAutoloadClasses['ContributionsListHooks'] = __DIR__ .
'/ContributionsList.hooks.php';
+$wgAutoloadClasses['ContributionsList'] = __DIR__ .
'/ContributionsList.class.php';
+
+$wgMessagesDirs['ContributionsList'] = __DIR__ . '/i18n';
+$wgExtensionMessagesFiles['ContributionsListMagic'] = __DIR__ .
'/ContributionsList.magic.php';
+
+$wgHooks['ParserFirstCallInit'][] =
'ContributionsListHooks::setupParserFunction';
diff --git a/i18n/en.json b/i18n/en.json
new file mode 100644
index 0000000..46c87ff
--- /dev/null
+++ b/i18n/en.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ike Hecht"
+ ]
+ },
+ "contributionslist-desc": "Generates a list of user contributions using
a parser function"
+}
diff --git a/i18n/qqq.json b/i18n/qqq.json
new file mode 100644
index 0000000..6b721cd
--- /dev/null
+++ b/i18n/qqq.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ike Hecht"
+ ]
+ },
+ "contributionslist-desc":
"{{desc|name=ContributionsList|url=http://www.mediawiki.org/wiki/Extension:ContributionsList}}"
+}
--
To view, visit https://gerrit.wikimedia.org/r/169315
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I08280bfa5bf5f6e76be19c52faece90f98e5a2f2
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/ContributionsList
Gerrit-Branch: master
Gerrit-Owner: tosfos <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits