Yurik has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/198181

Change subject: Added gl_perm & gl_updated columns to gather_list
......................................................................

Added gl_perm & gl_updated columns to gather_list

INCOMPLETE!  this patch is checked in so that we can
discuss DB schema changes. The tests are not passing yet.

* Two new fields for the lists table
* New list index by permission + updated TS
  This will allow api to iterate over public+last updated
* The original SQL schema has been updated but commented out
  for now until betalabs is updated. I plan to delete the
  GatherListPermissions.php file once all dev machines
  and betalabs are updated - will create it properly on
  production cluster without updating.

Change-Id: I6ab326f98b2fa4778f83f867507b8e52dcc38e62
---
M includes/api/ApiEditList.php
M includes/api/ApiQueryListPages.php
M includes/api/ApiQueryLists.php
A schema/GatherListPermissions.php
M schema/Updater.hooks.php
A schema/gather_list-perm-ts.sql
M schema/gather_list.sql
M schema/gather_list_item.sql
8 files changed, 172 insertions(+), 63 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Gather 
refs/changes/81/198181/1

diff --git a/includes/api/ApiEditList.php b/includes/api/ApiEditList.php
index 6af5668..d5cca7b 100644
--- a/includes/api/ApiEditList.php
+++ b/includes/api/ApiEditList.php
@@ -126,10 +126,7 @@
                        $listId = $this->createRow( $dbw, $user, $params, 
$isWatchlist );
                } else {
                        // Find existing list
-                       $row = $dbw->selectRow( 'gather_list',
-                               array( 'gl_id', 'gl_user', 'gl_label', 
'gl_info' ), array( 'gl_id' => $listId ),
-                               __METHOD__
-                       );
+                       $row = $this->getListRow( $dbw, array( 'gl_id' => 
$listId ) );
                        if ( $row === false ) {
                                // No database record with the given ID
                                $this->dieUsage( "List {$p}id was not found", 
'badid' );
@@ -230,13 +227,18 @@
        private function updateInfo( stdClass $v, array $params, $isWatchlist ) 
{
                $updated = false;
 
+               if ( $isWatchlist && $params['perm'] === 'public' ) {
+                       // Per team discussion, introducing artificial 
limitation for now
+                       // until we establish that making watchlist public 
would cause no harm.
+                       // This check can be deleted at any time since all 
other API code supports it.
+                       $this->dieUsage( 'Making watchlist public is not 
supported for security reasons',
+                               'publicwatchlist' );
+               }
+
                //
                // Set default
                if ( !property_exists( $v, 'description' ) ) {
                        $v->description = '';
-               }
-               if ( !property_exists( $v, 'perm' ) ) {
-                       $v->perm = 'private';
                }
                if ( !property_exists( $v, 'image' ) ) {
                        $v->image = '';
@@ -246,17 +248,6 @@
                // Update from api parameters
                if ( $params['description'] !== null && $v->description !== 
$params['description'] ) {
                        $v->description = $params['description'];
-                       $updated = true;
-               }
-               if ( $params['perm'] !== null && $v->perm !== $params['perm'] ) 
{
-                       if ( $isWatchlist && $params['perm'] !== 'private' ) {
-                               // Per team discussion, introducing artificial 
limitation for now
-                               // until we establish that making watchlist 
public would cause no harm.
-                               // This check can be deleted at any time since 
all other API code supports it.
-                               $this->dieUsage( 'Making watchlist public is 
not supported for security reasons',
-                                       'publicwatchlist' );
-                       }
-                       $v->perm = $params['perm'];
                        $updated = true;
                }
                if ( $params['image'] !== null && $v->image !== 
$params['image'] ) {
@@ -279,6 +270,7 @@
         * Check if the info is a public list
         * @param stdClass $info
         * @return bool
+        * @deprecated must use DB column instead (To be deleted once DB is 
updated)
         */
        public static function isPublic( $info ) {
                if ( $info && property_exists( $info, 'perm' ) ) {
@@ -308,6 +300,8 @@
                                'gl_user' => $user->getId(),
                                'gl_label' => $label,
                                'gl_info' => $info,
+                               'gl_perm' => $params['perm'] === 'public' ? 1 : 
0,
+                               'gl_updated' => $dbw->timestamp( 
wfTimestampNow() ),
                        ), __METHOD__, 'IGNORE' );
                        $id = $dbw->insertId();
                } else {
@@ -316,11 +310,9 @@
 
                if ( $id === 0 ) {
                        // List already exists, update instead, or might not 
need it
-                       $row = $dbw->selectRow( 'gather_list',
-                               array( 'gl_id', 'gl_user', 'gl_label', 
'gl_info' ),
-                               array( 'gl_user' => $user->getId(), 'gl_label' 
=> $label ),
-                               __METHOD__
-                       );
+                       $row = $this->getListRow( $dbw, array(
+                               'gl_user' => $user->getId(), 'gl_label' => 
$label
+                       ) );
                        if ( $row === false ) {
                                if ( $createRow ) {
                                        // If creation failed, the second query 
should have succeeded
@@ -355,6 +347,12 @@
                $update = array();
                if ( $params['label'] !== null && $row->gl_label !== 
$params['label'] ) {
                        $update['gl_label'] = $params['label'];
+               }
+               if ( $params['perm'] !== null ) {
+                       $perm = $params['perm'] === 'public' ? 1 : 0;
+                       if ( $row->gl_perm !== $perm ) {
+                               $update['gl_perm'] = $perm;
+                       }
                }
                $info = self::parseListInfo( $row->gl_info, $row->gl_id, true );
                $json = $this->updateInfo( $info, $params, $row->gl_label === 
'' );
@@ -522,4 +520,16 @@
                }
                return new stdClass();
        }
+
+       /**
+        * @param DatabaseBase $dbw
+        * @param array $conds
+        * @return bool|stdClass
+        */
+       private function getListRow( DatabaseBase $dbw, $conds ) {
+               return $dbw->selectRow( 'gather_list',
+                       array( 'gl_id', 'gl_user', 'gl_label', 'gl_perm', 
'gl_info' ),
+                       $conds,
+                       __METHOD__ );
+       }
 }
diff --git a/includes/api/ApiQueryListPages.php 
b/includes/api/ApiQueryListPages.php
index 7ecf1f9..dca0811 100644
--- a/includes/api/ApiQueryListPages.php
+++ b/includes/api/ApiQueryListPages.php
@@ -79,7 +79,7 @@
                        // Id was given, this could be public or private list, 
legacy watchlist or regular
                        // Allow access to any public list/watchlist, and to 
private with proper owner/self
                        $db = $this->getDB();
-                       $listRow = $db->selectRow( 'gather_list', array( 
'gl_label', 'gl_user', 'gl_info' ),
+                       $listRow = $db->selectRow( 'gather_list', array( 
'gl_label', 'gl_user' ),
                                array( 'gl_id' => $params['id'] ), __METHOD__ );
                        if ( $listRow === false ) {
                                $this->dieUsage( "List does not exist", 'badid' 
);
@@ -101,11 +101,8 @@
                        }
 
                        // Check if this is a public list (if required)
-                       if ( !$showPrivate ) {
-                               $info = ApiEditList::parseListInfo( 
$listRow->gl_info, $params['id'], false );
-                               if ( !ApiEditList::isPublic( $info ) ) {
-                                       $this->dieUsage( "You have no rights to 
see this list", 'badid' );
-                               }
+                       if ( !$showPrivate && $listRow->perm !== 1 ) {
+                               $this->dieUsage( "You have no rights to see 
this list", 'badid' );
                        }
 
                        if ( $listRow->gl_label === '' ) {
diff --git a/includes/api/ApiQueryLists.php b/includes/api/ApiQueryLists.php
index 17c3f9f..08ba289 100644
--- a/includes/api/ApiQueryLists.php
+++ b/includes/api/ApiQueryLists.php
@@ -98,11 +98,13 @@
                $this->addTables( 'gather_list' );
                $this->addFields( 'gl_id' );
                $this->addFieldsIf( 'gl_label', $fld_label || !$modeAll );
-               // won't know if private until later
-               $this->addFieldsIf( 'gl_user', $showPrivate === null || 
$modeAll );
+               $this->addFieldsIf( 'gl_user', $modeAll );
+               $this->addFieldsIf( 'gl_perm', $fld_public );
+
                if ( $owner ) {
                        $this->addWhereFld( 'gl_user', $owner->getId() );
                }
+
                if ( $ids || $findWatchlist ) {
                        $cond = array();
                        if ( $ids ) {
@@ -110,6 +112,14 @@
                        }
                        if ( $findWatchlist ) {
                                $cond['gl_label'] = '';
+                       }
+                       $this->addWhere( $db->makeList( $cond, LIST_OR ) );
+               }
+
+               if ( $showPrivate !== true ) {
+                       $cond = array( 'gl_perm' => 1 );
+                       if ( $showPrivate === null ) {
+                               $cond['gl_user'] = $this->getUser()->getId();
                        }
                        $this->addWhere( $db->makeList( $cond, LIST_OR ) );
                }
@@ -147,14 +157,11 @@
                        }
                }
 
-               // If we need it for privacy checks or we need to return a prop 
field
-               $useInfo = $showPrivate !== true || $fld_description || 
$fld_public || $fld_image;
-
+               $useInfo = $fld_description || $fld_image;
                $this->addFieldsIf( 'gl_info', $useInfo );
 
                $limit = $params['limit'];
-               // TODO: Disabling this until we stop skipping rows in 
processing
-               // $this->addOption( 'LIMIT', $limit + 1 );
+               $this->addOption( 'LIMIT', $limit + 1 );
 
                if ( $modeAll ) {
                        $this->addOption( 'ORDER BY', 'gl_id' );
@@ -164,12 +171,10 @@
 
                $count = 0;
                $path = array( 'query', $this->getModuleName() );
-               $currUserId = strval( $this->getUser()->getId() );
 
                // This closure will process one row, even if that row is fake 
watchlist
                $processRow = function( $row ) use ( &$count, $modeAll, $limit, 
 $useInfo, $title,
-                       $fld_label, $fld_description, $fld_public, $fld_image, 
$path, $showPrivate,
-                       $currUserId, $userId
+                       $fld_label, $fld_description, $fld_public, $fld_image, 
$path, $userId
                ) {
                        if ( $row === null ) {
                                // Fake watchlist row
@@ -179,21 +184,6 @@
                                        'gl_user' => false,
                                        'gl_info' => '',
                                );
-                       }
-
-                       $info = $useInfo ?
-                               ApiEditList::parseListInfo( $row->gl_info, 
$row->gl_id, false ) : null;
-
-                       // Determine if this gather_list row is viewable by the 
current user
-                       // TODO: this should be part of the SQL query once 
fields are cerated
-                       $show = $showPrivate;
-                       if ( $show === null && $row->gl_user === $currUserId ) {
-                               $show = true;
-                       } elseif ( $show !== true && ApiEditList::isPublic( 
$info ) ) {
-                               $show = true;
-                       }
-                       if ( !$show ) {
-                               return true;
                        }
 
                        $count++;
@@ -226,12 +216,13 @@
                                        $data['title'] = $row->isIn !== null;
                                }
                        }
+                       if ( $fld_public ) {
+                               $data['public'] = $row->gl_perm === 1;
+                       }
                        if ( $useInfo ) {
+                               $info = ApiEditList::parseListInfo( 
$row->gl_info, $row->gl_id, false );
                                if ( $fld_description ) {
                                        $data['description'] = property_exists( 
$info, 'description' ) ? $info->description : '';
-                               }
-                               if ( $fld_public ) {
-                                       $data['public'] = 
ApiEditList::isPublic( $info );
                                }
                                if ( $fld_image ) {
                                        if ( property_exists( $info, 'image' ) 
&& $info->image ) {
@@ -380,7 +371,7 @@
                        if ( !$this->getUser()->isLoggedIn() ) {
                                $showPrivate = false;
                        } else {
-                               $showPrivate = null; // check each id against 
$currUserId
+                               $showPrivate = null; // check each id against 
current user's ID
                        }
                }
                if ( $showPrivate !== false ) {
diff --git a/schema/GatherListPermissions.php b/schema/GatherListPermissions.php
new file mode 100644
index 0000000..3cb6b43
--- /dev/null
+++ b/schema/GatherListPermissions.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * @ingroup Maintenance
+ */
+
+namespace Gather;
+
+use FormatJson;
+use Gather\api\ApiEditList;
+use LoggedUpdateMaintenance;
+
+
+$IP = getenv( 'MW_INSTALL_PATH' );
+if ( $IP === false ) {
+       $IP = __DIR__ . '/../../..';
+}
+require_once( "$IP/maintenance/Maintenance.php" );
+
+/**
+ * @ingroup Maintenance
+ */
+class GatherListPermissions extends LoggedUpdateMaintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->mDescription = 'Fix permissions and timestamp fields on 
gather_list';
+       }
+
+       protected function getUpdateKey() {
+               return 'update gather list permissions';
+       }
+
+       protected function updateSkippedMessage() {
+               return 'gather list permissions already updated';
+       }
+
+       protected function doDBUpdates() {
+               $this->output( "Populating gl_perm column in gather_list 
table\n" );
+
+               $db = wfGetDB( DB_MASTER );
+
+               $totalCount = 0;
+               $batchSize = $count = $this->mBatchSize;
+
+               while ( $batchSize === $count ) {
+                       $count = 0;
+
+                       $res = $db->select(
+                               'gather_list',
+                               array( 'gl_id', 'gl_info', 'gl_label' ),
+                               array( 'gl_perm' => NULL ),
+                               __METHOD__,
+                               array( 'LIMIT' => $batchSize )
+                       );
+
+                       foreach ( $res as $row ) {
+                               $count++;
+
+                               $info = ApiEditList::parseListInfo( 
$row->gl_info, $row->gl_id, true );
+                               $perm = ( ApiEditList::isPublic( $info ) && 
$row->gl_label !== '' ) ? 1 : 0;
+                               unset( $info->perm );
+                               unset( $info->public );
+
+                               $db->update(
+                                       'gather_list',
+                                       array(
+                                               'gl_perm' => $perm,
+                                               'gl_info' => 
FormatJson::encode( $info, false, FormatJson::ALL_OK ),
+                                               'gl_updated' => $db->timestamp( 
wfTimestampNow() ),
+                                       ),
+                                       array( 'gl_id' => $row->gl_id ),
+                                       __METHOD__ );
+                       }
+
+                       $totalCount += $count;
+
+                       wfWaitForSlaves();
+               }
+
+               $this->output( "Done, $totalCount rows updated.\n" );
+               return true;
+       }
+}
+
+$maintClass = 'GatherListPermissions';
+require_once( RUN_MAINTENANCE_IF_MAIN );
diff --git a/schema/Updater.hooks.php b/schema/Updater.hooks.php
index a9ffd98..1e5b339 100644
--- a/schema/Updater.hooks.php
+++ b/schema/Updater.hooks.php
@@ -9,10 +9,12 @@
  */
 class UpdaterHooks {
        public static function onLoadExtensionSchemaUpdates( DatabaseUpdater 
$du ) {
-               $base = dirname( __FILE__ );
+               $du->addExtensionTable( 'gather_list', __DIR__ . 
"/gather_list.sql", true );
+               $du->addExtensionTable( 'gather_list_item', __DIR__ . 
"/gather_list_item.sql", true );
+               $du->addExtensionField( 'gather_list', 'gl_perm', __DIR__ . 
"/gather_list-perm-ts.sql" );
 
-               $du->addExtensionTable( 'gather_list', "$base/gather_list.sql", 
true );
-               $du->addExtensionTable( 'gather_list_item', 
"$base/gather_list_item.sql", true );
+               require_once __DIR__ . '/GatherListPermissions.php';
+               $du->addPostDatabaseUpdateMaintenance( 
'Gather\GatherListPermissions' );
 
                return true;
        }
diff --git a/schema/gather_list-perm-ts.sql b/schema/gather_list-perm-ts.sql
new file mode 100644
index 0000000..76d4b68
--- /dev/null
+++ b/schema/gather_list-perm-ts.sql
@@ -0,0 +1,13 @@
+-- Licence: GNU GPL v2+
+
+ALTER TABLE /*$wgDBprefix*/gather_list
+  -- The list permissions type (NULL=not migrated, GATHER_PRIVATE=0, 
GATHER_PUBLIC=1)
+  ADD gl_perm TINYINT UNSIGNED NULL DEFAULT NULL AFTER gl_label,
+
+  -- The timestamp is updated whenever the list's meta data is modified.
+  -- It is possible we might update this field when modifying watchlist / list 
pages
+  ADD gl_updated VARBINARY(14) NOT NULL DEFAULT '' AFTER gl_perm;
+
+
+-- Show all public lists, sorted by the last updated timestamp
+CREATE INDEX /*i*/gl_user_perm_updated ON /*_*/gather_list (gl_perm, 
gl_updated DESC);
diff --git a/schema/gather_list.sql b/schema/gather_list.sql
index cd4f745..de1f72f 100644
--- a/schema/gather_list.sql
+++ b/schema/gather_list.sql
@@ -16,6 +16,13 @@
   -- Must be unique per user - makes it easier for querying/looking up
   gl_label VARCHAR(255) BINARY NOT NULL,
 
+  -- The list permissions type (NULL=not migrated, GATHER_PRIVATE=0, 
GATHER_PUBLIC=1)
+  -- gl_perm TINYINT UNSIGNED NOT NULL DEFAULT 0,
+
+  -- The timestamp is updated whenever the list's meta data is modified.
+  -- It is possible we might update this field when modifying watchlist / list 
pages
+  -- gl_updated VARBINARY(14) NOT NULL DEFAULT '',
+
   -- All other values are stored here to allow for rapid design changes.
   -- At this point we do not foresee any value in using indexes
   -- thus blob is a perfect storage medium. Stored as JSON blob.
@@ -24,5 +31,8 @@
 ) /*$wgDBTableOptions*/;
 
 
--- Index to iterate by label
+-- Gets lists of a specific user, sorted by label, watchlist first
 CREATE UNIQUE INDEX /*i*/gl_user_label ON /*_*/gather_list (gl_user, gl_label);
+
+-- Show all public lists, sorted by the last updated timestamp
+-- CREATE INDEX /*i*/gl_user_perm_updated ON /*_*/gather_list (gl_perm, 
gl_updated DESC);
diff --git a/schema/gather_list_item.sql b/schema/gather_list_item.sql
index 7bdb7f5..f6b1358 100644
--- a/schema/gather_list_item.sql
+++ b/schema/gather_list_item.sql
@@ -17,13 +17,14 @@
 
   -- Sort order uses real to simplify item insertion
   -- without modifying other items
-  gli_order REAL NOT NULL DEFAULT 0
+  gli_order FLOAT NOT NULL DEFAULT 0
 
 ) /*$wgDBTableOptions*/;
 
 
 -- Define clustered index (must be first unique) -- most common operations 
will enumerate in order
 CREATE UNIQUE INDEX /*i*/gli_id_order_ns_title ON /*_*/gather_list_item 
(gli_gl_id, gli_order, gli_namespace, gli_title);
+
 -- In case all lists for a specific title need to be shown / updated
 -- Also, enforce one title per list uniqueness
 CREATE UNIQUE INDEX /*i*/gli_ns_title ON /*_*/gather_list_item (gli_namespace, 
gli_title, gli_gl_id);

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6ab326f98b2fa4778f83f867507b8e52dcc38e62
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Gather
Gerrit-Branch: master
Gerrit-Owner: Yurik <[email protected]>

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

Reply via email to