jenkins-bot has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/369624 )

Change subject: New Wikidata Build - 2017-08-02T10:00:01+0000
......................................................................


New Wikidata Build - 2017-08-02T10:00:01+0000

Change-Id: I8c81465b8105cd632c37512b0a134ad0a8e4f44a
---
M composer.lock
M extensions/ArticlePlaceholder/i18n/nn.json
M extensions/Constraints/api/CheckConstraints.php
M extensions/Constraints/i18n/gl.json
M extensions/Constraints/i18n/he.json
M extensions/Constraints/i18n/hi.json
M extensions/Constraints/i18n/zh-hans.json
M extensions/Constraints/includes/ConstraintCheck/Helper/TypeCheckerHelper.php
M extensions/Constraints/package.json
M 
extensions/Constraints/tests/phpunit/Checker/TypeChecker/TypeCheckerHelperTest.php
M extensions/PropertySuggester/package.json
M extensions/Quality/package.json
M extensions/Wikibase/client/i18n/din.json
M extensions/Wikibase/client/includes/Changes/ChangeHandler.php
M extensions/Wikibase/client/includes/Changes/PageUpdater.php
M extensions/Wikibase/client/includes/Changes/WikiPageUpdater.php
M extensions/Wikibase/client/includes/WikibaseClient.php
M 
extensions/Wikibase/client/tests/phpunit/includes/Changes/ChangeHandlerTest.php
M extensions/Wikibase/client/tests/phpunit/includes/Changes/MockPageUpdater.php
M 
extensions/Wikibase/client/tests/phpunit/includes/Changes/WikiPageUpdaterTest.php
M extensions/Wikibase/repo/includes/EditEntity.php
M extensions/Wikibase/repo/includes/EditEntityFactory.php
M extensions/Wikibase/repo/includes/Store/EntityPermissionChecker.php
M 
extensions/Wikibase/repo/includes/Store/WikiPageEntityStorePermissionChecker.php
M extensions/Wikibase/repo/includes/WikibaseRepo.php
M extensions/Wikibase/repo/maintenance/dispatchChanges.php
M extensions/Wikibase/repo/tests/phpunit/includes/EditEntityTest.php
M 
extensions/Wikibase/repo/tests/phpunit/includes/Store/WikiPageEntityStorePermissionCheckerTest.php
M extensions/Wikibase/view/resources/wikibase/resources.php
R extensions/Wikibase/view/resources/wikibase/wikibase.less
M package.json
M vendor/composer/installed.json
32 files changed, 348 insertions(+), 608 deletions(-)

Approvals:
  Aude: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/composer.lock b/composer.lock
index 259e513..310f86f 100644
--- a/composer.lock
+++ b/composer.lock
@@ -756,12 +756,12 @@
             "source": {
                 "type": "git",
                 "url": 
"https://github.com/wikimedia/mediawiki-extensions-ArticlePlaceholder.git";,
-                "reference": "f1e8a1a35716239f9a78eebca09f1135935c4f17"
+                "reference": "9fde45a4bb0c062e9004c38a34f99f1c8dcab012"
             },
             "dist": {
                 "type": "zip",
-                "url": 
"https://api.github.com/repos/wikimedia/mediawiki-extensions-ArticlePlaceholder/zipball/f1e8a1a35716239f9a78eebca09f1135935c4f17";,
-                "reference": "f1e8a1a35716239f9a78eebca09f1135935c4f17",
+                "url": 
"https://api.github.com/repos/wikimedia/mediawiki-extensions-ArticlePlaceholder/zipball/9fde45a4bb0c062e9004c38a34f99f1c8dcab012";,
+                "reference": "9fde45a4bb0c062e9004c38a34f99f1c8dcab012",
                 "shasum": ""
             },
             "require": {
@@ -790,7 +790,7 @@
             ],
             "description": "Provides a special page with Wikibase information 
about a certain topic, with invitation to create an article for the topic",
             "homepage": 
"https://www.mediawiki.org/wiki/Extension:ArticlePlaceholder";,
-            "time": "2017-07-31 10:42:11"
+            "time": "2017-08-01 20:36:04"
         },
         {
             "name": "propertysuggester/property-suggester",
@@ -798,7 +798,7 @@
             "source": {
                 "type": "git",
                 "url": 
"https://gerrit.wikimedia.org/r/mediawiki/extensions/PropertySuggester";,
-                "reference": "ee4c46b4a335c87855e80147b2bc85766ae15b20"
+                "reference": "283a7070fd970cfe991f9b16f47b10cb04d1c4ce"
             },
             "require": {
                 "php": ">=5.5.9",
@@ -849,7 +849,7 @@
                 "wikibase",
                 "wikidata"
             ],
-            "time": "2017-07-26 09:44:33"
+            "time": "2017-08-01 08:45:42"
         },
         {
             "name": "serialization/serialization",
@@ -966,7 +966,7 @@
             "source": {
                 "type": "git",
                 "url": 
"https://gerrit.wikimedia.org/r/mediawiki/extensions/WikibaseQualityConstraints";,
-                "reference": "8614148918fc44218c82182610aff43fdd07f081"
+                "reference": "8c90ed7f4bc35d3b36b3823150404b8b41abfda8"
             },
             "require": {
                 "php": ">=5.5.9",
@@ -1027,7 +1027,7 @@
             "support": {
                 "issues": 
"https://phabricator.wikimedia.org/project/profile/1202/";
             },
-            "time": "2017-07-31 21:06:39"
+            "time": "2017-08-02 09:14:11"
         },
         {
             "name": "wikibase/data-model",
@@ -1432,7 +1432,7 @@
             "source": {
                 "type": "git",
                 "url": 
"https://gerrit.wikimedia.org/r/mediawiki/extensions/WikibaseQuality";,
-                "reference": "22aafec778ad7605863090758753c21e79ed44df"
+                "reference": "9f25f6f316e962aa4117e0922e0c041eab895b79"
             },
             "require": {
                 "php": ">=5.5.9",
@@ -1491,7 +1491,7 @@
             "support": {
                 "issues": 
"https://phabricator.wikimedia.org/project/profile/989/";
             },
-            "time": "2017-07-28 20:48:10"
+            "time": "2017-08-01 11:18:15"
         },
         {
             "name": "wikibase/serialization-javascript",
@@ -1540,12 +1540,12 @@
             "source": {
                 "type": "git",
                 "url": 
"https://github.com/wikimedia/mediawiki-extensions-Wikibase.git";,
-                "reference": "aa7c10c4531cca80af5f81b817fd5b578e94942e"
+                "reference": "4331d2cd0e171070329f5af6a465bd582d5fab2c"
             },
             "dist": {
                 "type": "zip",
-                "url": 
"https://api.github.com/repos/wikimedia/mediawiki-extensions-Wikibase/zipball/aa7c10c4531cca80af5f81b817fd5b578e94942e";,
-                "reference": "aa7c10c4531cca80af5f81b817fd5b578e94942e",
+                "url": 
"https://api.github.com/repos/wikimedia/mediawiki-extensions-Wikibase/zipball/4331d2cd0e171070329f5af6a465bd582d5fab2c";,
+                "reference": "4331d2cd0e171070329f5af6a465bd582d5fab2c",
                 "shasum": ""
             },
             "require": {
@@ -1621,7 +1621,7 @@
                 "wikibaserepo",
                 "wikidata"
             ],
-            "time": "2017-08-01 08:03:31"
+            "time": "2017-08-02 08:09:10"
         },
         {
             "name": "wikibase/wikimedia-badges",
diff --git a/extensions/ArticlePlaceholder/i18n/nn.json 
b/extensions/ArticlePlaceholder/i18n/nn.json
index 4de7475..051d0b5 100644
--- a/extensions/ArticlePlaceholder/i18n/nn.json
+++ b/extensions/ArticlePlaceholder/i18n/nn.json
@@ -4,13 +4,18 @@
                        "Njardarlogar"
                ]
        },
-       "articleplaceholder-abouttopic-create-article": "Opprett ein ny 
artikkel med tittelen under",
+       "createtopicpage": "Opprett side",
+       "articleplaceholder-abouttopic-create-article-title": "Opprett ein ny 
artikkel",
        "articleplaceholder-abouttopic-create-article-label": "Artikkeltittel",
        "articleplaceholder-abouttopic-create-article-mandatory": "Tittelen til 
artikkelen er påkravd",
        "articleplaceholder-abouttopic-create-article-button": "Lag ein 
artikkel",
+       "articleplaceholder-abouttopic-create-emtpy-article-button": "Skriv frå 
botnen av",
+       "articleplaceholder-abouttopic-translate-article-label": "Kjeldespråk:",
+       "articleplaceholder-abouttopic-translate-article-button": "Set om 
artikkel",
        "articleplaceholder-abouttopic-create-article-submit-button": "Send",
        "articleplaceholder-abouttopic-article-exists-error": "Det finst alt 
ein artikkel med dette namnet",
        "articleplaceholder-abouttopic-lua-reference": "Kjelder",
        "articleplaceholder-abouttopic-lua-identifier": "Eksterne ressursar",
-       "articleplaceholder-search-header": "Finn data om emnet"
+       "articleplaceholder-search-header": "Finn data om emnet",
+       "articleplaceholder-createpage-title": "Opprett $1"
 }
diff --git a/extensions/Constraints/api/CheckConstraints.php 
b/extensions/Constraints/api/CheckConstraints.php
index 49f7f57..0155a21 100644
--- a/extensions/Constraints/api/CheckConstraints.php
+++ b/extensions/Constraints/api/CheckConstraints.php
@@ -351,7 +351,10 @@
                        } catch ( StatementGuidParsingException $e ) {
                                // constraint template on talk page
                                $typeLabel = htmlspecialchars( $typeItemId );
-                               $link = $title->getTalkPage()->getFullUrl();
+                               if ( !$title->canHaveTalkPage() ) {
+                                       throw new MWException( 'Properties 
namespace must define a talk namespace' );
+                               }
+                               $link = 
$title->getTalkPageIfDefined()->getFullUrl();
                        }
 
                        $result = [
diff --git a/extensions/Constraints/i18n/gl.json 
b/extensions/Constraints/i18n/gl.json
index 677ee7b..476f2ee 100644
--- a/extensions/Constraints/i18n/gl.json
+++ b/extensions/Constraints/i18n/gl.json
@@ -22,6 +22,8 @@
        "wbqc-constraintreport-status-exception": "Excepción",
        "wbqc-constraintreport-status-todo": "Pendente",
        "wbqc-constraintreport-status-bad-parameters": "Parámetros incorrectos",
+       "wbqc-constraintreport-status-deprecated": "Obsoleto",
+       "wbqc-constraintreport-status-warning": "Aviso",
        "wbqc-constraintreport-result-table-header-status": "Estado",
        "wbqc-constraintreport-result-table-header-claim": "Afirmación",
        "wbqc-constraintreport-result-table-header-constraint": "Restrición",
@@ -31,6 +33,8 @@
        "wbqc-potentialissues-short": "Problemas potenciais",
        "wbqc-potentialissues-long": "Esta instrucciónten problemas 
potenciais.",
        "wbqc-badparameters-short": "Parámetros incorrectos",
+       "wbqc-parameterissues-short": "Problemas complexos",
+       "wbqc-problems-short": "Problemas",
        "apihelp-wbcheckconstraints-param-constraintid": "Filtro opcional para 
devolver só as constantes que teñen o ID de constante especificado",
        "apihelp-wbcheckconstraints-example-1": "Verificar tódalas constantes 
sobre os elementos Q5 e Q42.",
        "apihelp-wbcheckconstraints-example-2": "Verificar tódalas constantes 
nunha soa instrucción.",
diff --git a/extensions/Constraints/i18n/he.json 
b/extensions/Constraints/i18n/he.json
index fb9f59b..c15c4ec 100644
--- a/extensions/Constraints/i18n/he.json
+++ b/extensions/Constraints/i18n/he.json
@@ -36,6 +36,7 @@
        "wbqc-parameterissues-long": "הסוגיות האלו הן בעיות עם הגדרת האילוצים 
על המאפיין, לא עם הקביעה הזאת.",
        "wbqc-problems-short": "בעיות",
        "wbqc-problems-long": "בקביעה הזאת יש בעיות מסוימות.",
+       "wbqc-constrainttypehelp-long": "דף עזרה עבור סוג האילוץ הזה",
        "apihelp-wbcheckconstraints-description": "ביצוע בדיקות אילוצים על ישות 
כלשהי והחזרת התוצאה.",
        "apihelp-wbcheckconstraints-summary": "ביצוע בדיקת אילוצים על כל ישות 
והחזרת התוצאה.",
        "apihelp-wbcheckconstraints-param-id": "רשימת מזהי ישויות שמהן צריך 
לקבל נתונים. יש להפריד את הערכים בתו '|' או חלופות.",
@@ -103,5 +104,6 @@
        "wbqc-violation-message-valueType-instance": "ערכים של קביעות $1 אמורים 
להיות מופעים של {{PLURAL:$3|1=$5|2=$5 או $6|אחת מהמחלקות הבאות}} (או של 
{{PLURAL:$3|1=תת־מחלקה שלה|2=תת־מחלקה שלהן|אחת מהתת־מחלקות שלהן}}), אבל כעת 
{{PLURAL:$3|$2 1=אינה כזאת.|2=אינה כזאת.|אינה כזאת: $4}}",
        "wbqc-violation-message-valueType-subclass": "ערכים של קביעות $1 אמורים 
להיות תת־מחלקות של {{PLURAL:$3|1=$5|2=$5 או $6|אחת מהמחלקות הבאות}} (או של 
{{PLURAL:$3|1=תת־מחלקה שלה|2=תת־מחלקה שלהן|אחת מהתת־מחלקות שלהן}}), אבל כעת 
{{PLURAL:$3|$2 1=אינה כזאת.|2=אינה כזאת.|אינה כזאת: $4}}",
        "wbqc-violation-message-target-required-claim": "ל{{GRAMMAR:תחילית|$1}} 
אמורה להיות {{PLURAL:$3|0=קביעה $2.|1=קביעה $2 $5.הקביעה עבור $2 עם אחד מהערכים 
הבאים: $4}}",
-       "wbqc-violation-message-unique-value": "ערך המאפיין הזה צריך לא להיות 
נוכח בשום פריט אחר, אבל הוא 
נוכח{{PLURAL:$1|1=ב{{GRAMMAR:תחילית|$3}}.|2=ב{{GRAMMAR:תחילית|$3}} 
וב{{GRAMMAR:תחילית|$4}}|בפריטים הבאים: $2}}"
+       "wbqc-violation-message-unique-value": "ערך המאפיין הזה צריך לא להיות 
נוכח בשום פריט אחר, אבל הוא 
נוכח{{PLURAL:$1|1=ב{{GRAMMAR:תחילית|$3}}.|2=ב{{GRAMMAR:תחילית|$3}} 
וב{{GRAMMAR:תחילית|$4}}|בפריטים הבאים: $2}}",
+       "wbqc-exception-message": "הישות הזאת היא חריגה ידועה עבור האילוץ הזה 
והיא סומנה בתור כזאת."
 }
diff --git a/extensions/Constraints/i18n/hi.json 
b/extensions/Constraints/i18n/hi.json
index 20f9f29..d63b6a1 100644
--- a/extensions/Constraints/i18n/hi.json
+++ b/extensions/Constraints/i18n/hi.json
@@ -5,5 +5,6 @@
                        "Vishwanath"
                ]
        },
+       "wbqc-constraintreport-status-warning": "चेतावनी",
        "wbqc-violation-message-parameter-string": "\"$1\" पैरामीटर का मान एक 
स्ट्रिंग होना चाहिए, \"$2\" नहीं होना चाहिए।"
 }
diff --git a/extensions/Constraints/i18n/zh-hans.json 
b/extensions/Constraints/i18n/zh-hans.json
index 6d4b7a4..d7a1735 100644
--- a/extensions/Constraints/i18n/zh-hans.json
+++ b/extensions/Constraints/i18n/zh-hans.json
@@ -36,6 +36,7 @@
        "wbqc-parameterissues-long": "这些问题是属性上的约束定义问题,而不是此声明的问题。",
        "wbqc-problems-short": "问题",
        "wbqc-problems-long": "此声明有一些问题。",
+       "wbqc-constrainttypehelp-long": "此约束类型的帮助页面",
        "apihelp-wbcheckconstraints-description": "在任何您希望执行检查的实体上,执行约束检查并返回结果。",
        "apihelp-wbcheckconstraints-summary": "在任何您希望执行的实体上执行约束检查,并返回结果。",
        "apihelp-wbcheckconstraints-param-id": "要获取数据的实体ID列表。使用“|”或替代字符分隔值。",
@@ -110,5 +111,6 @@
        "wbqc-violation-message-valueType-instance": 
"$1声明的值性质应为{{PLURAL:$3|1=$5|2=$5或$6|以下类之一}}(或为{{PLURAL:$3|其子类之一}}),但$2当前{{PLURAL:$3|1=并非如此。|2=并非如此。|并非如此:$4}}",
        "wbqc-violation-message-valueType-subclass": 
"$1声明的值的上级分类应为{{PLURAL:$3|1=$5|2=$5或$6|以下类之一}}(或为{{PLURAL:$3|其子类之一}}),但$2当前{{PLURAL:$3|1=并非如此。|2=并非如此。|并非如此:$4}}",
        "wbqc-violation-message-target-required-claim": 
"$1应拥有{{PLURAL:$3|0=声明$2。|1=声明$2 $5。|声明$2,并使用以下值之一:$4}}",
-       "wbqc-violation-message-unique-value": 
"此属性的值不得出现在其他任何项中,但却也出现在了{{PLURAL:$1|1=$3上。|2=$3和$4上。|以下项中:$2}}"
+       "wbqc-violation-message-unique-value": 
"此属性的值不得出现在其他任何项中,但却也出现在了{{PLURAL:$1|1=$3上。|2=$3和$4上。|以下项中:$2}}",
+       "wbqc-exception-message": "此实体是该约束的已知例外,并已如此标记。"
 }
diff --git 
a/extensions/Constraints/includes/ConstraintCheck/Helper/TypeCheckerHelper.php 
b/extensions/Constraints/includes/ConstraintCheck/Helper/TypeCheckerHelper.php
index d7cb13a..b3b698a 100644
--- 
a/extensions/Constraints/includes/ConstraintCheck/Helper/TypeCheckerHelper.php
+++ 
b/extensions/Constraints/includes/ConstraintCheck/Helper/TypeCheckerHelper.php
@@ -4,6 +4,7 @@
 
 use Config;
 use MediaWiki\MediaWikiServices;
+use OverflowException;
 use Wikibase\DataModel\Entity\EntityId;
 use Wikibase\DataModel\Entity\EntityIdValue;
 use Wikibase\DataModel\Entity\PropertyId;
@@ -68,31 +69,19 @@
         * of one of the item ID serializations in $classesToCheck.
         * If the class hierarchy is not exhausted before
         * the configured limit (WBQualityConstraintsTypeCheckMaxEntities) is 
reached,
-        * the injected {@link SparqlHelper} is consulted if present,
-        * otherwise the check aborts and returns false.
+        * an OverflowException is thrown.
         *
         * @param EntityId $comparativeClass
         * @param string[] $classesToCheck
         * @param int &$entitiesChecked
         *
         * @return bool
-        *
-        * @throws SparqlHelperException if SPARQL is used and the query times 
out or some other error occurs
+        * @throws OverflowException if $entitiesChecked exceeds the configured 
limit
         */
-       public function isSubclassOf( EntityId $comparativeClass, array 
$classesToCheck, &$entitiesChecked = 0 ) {
+       private function isSubclassOf( EntityId $comparativeClass, array 
$classesToCheck, &$entitiesChecked = 0 ) {
                $maxEntities = $this->config->get( 
'WBQualityConstraintsTypeCheckMaxEntities' );
                if ( ++$entitiesChecked > $maxEntities ) {
-                       if ( $entitiesChecked === $maxEntities + 1 && 
$this->sparqlHelper !== null ) {
-                               
MediaWikiServices::getInstance()->getStatsdDataFactory()
-                                       ->increment( 
'wikibase.quality.constraints.sparql.typeFallback' );
-                               return $this->sparqlHelper->hasType(
-                                       $comparativeClass->getSerialization(),
-                                       $classesToCheck,
-                                       /* withInstance = */ false
-                               );
-                       } else {
-                               return false;
-                       }
+                       throw new OverflowException( 'Too many entities to 
check' );
                }
 
                $item = $this->entityLookup->getEntity( $comparativeClass );
@@ -127,6 +116,39 @@
        }
 
        /**
+        * Checks if $comparativeClass is a subclass
+        * of one of the item ID serializations in $classesToCheck.
+        * If isSubclassOf() aborts due to hitting the configured limit,
+        * the injected {@link SparqlHelper} is consulted if present,
+        * otherwise the check returns false.
+        *
+        * @param EntityId $comparativeClass
+        * @param string[] $classesToCheck
+        * @param int &$entitiesChecked
+        *
+        * @return bool
+        *
+        * @throws SparqlHelperException if SPARQL is used and the query times 
out or some other error occurs
+        */
+       public function isSubclassOfWithSparqlFallback( EntityId 
$comparativeClass, array $classesToCheck ) {
+               try {
+                       return $this->isSubclassOf( $comparativeClass, 
$classesToCheck );
+               } catch ( OverflowException $e ) {
+                       if ( $this->sparqlHelper !== null ) {
+                               
MediaWikiServices::getInstance()->getStatsdDataFactory()
+                                       ->increment( 
'wikibase.quality.constraints.sparql.typeFallback' );
+                               return $this->sparqlHelper->hasType(
+                                       $comparativeClass->getSerialization(),
+                                       $classesToCheck,
+                                       /* withInstance = */ false
+                               );
+                       } else {
+                               return false;
+                       }
+               }
+       }
+
+       /**
         * Checks, if one of the itemId serializations in $classesToCheck
         * is contained in the list of $statements
         * via property $relationId or if it is a subclass of
@@ -158,7 +180,7 @@
                                return true;
                        }
 
-                       if ( $this->isSubclassOf( $comparativeClass, 
$classesToCheck ) ) {
+                       if ( $this->isSubclassOfWithSparqlFallback( 
$comparativeClass, $classesToCheck ) ) {
                                return true;
                        }
                }
diff --git a/extensions/Constraints/package.json 
b/extensions/Constraints/package.json
index b85a888..8f94116 100644
--- a/extensions/Constraints/package.json
+++ b/extensions/Constraints/package.json
@@ -13,7 +13,7 @@
        "devDependencies": {
                "eslint-config-wikimedia": "0.4.0",
                "grunt": "1.0.1",
-               "grunt-banana-checker": "0.3.0",
+               "grunt-banana-checker": "0.6.0",
                "grunt-eslint": "19.0.0",
                "grunt-jsonlint": "1.1.0",
                "grunt-stylelint": "0.6.0",
diff --git 
a/extensions/Constraints/tests/phpunit/Checker/TypeChecker/TypeCheckerHelperTest.php
 
b/extensions/Constraints/tests/phpunit/Checker/TypeChecker/TypeCheckerHelperTest.php
index c4173a6..746cc8e 100644
--- 
a/extensions/Constraints/tests/phpunit/Checker/TypeChecker/TypeCheckerHelperTest.php
+++ 
b/extensions/Constraints/tests/phpunit/Checker/TypeChecker/TypeCheckerHelperTest.php
@@ -4,12 +4,15 @@
 
 use PHPUnit_Framework_TestCase;
 use Wikibase\DataModel\Services\Lookup\EntityLookup;
+use Wikibase\DataModel\Services\Lookup\InMemoryEntityLookup;
 use Wikibase\DataModel\Statement\Statement;
 use Wikibase\DataModel\Snak\PropertyValueSnak;
 use Wikibase\DataModel\Entity\EntityIdValue;
 use Wikibase\DataModel\Entity\ItemId;
 use Wikibase\DataModel\Entity\PropertyId;
 use Wikibase\DataModel\Statement\StatementList;
+use Wikibase\Repo\Tests\NewItem;
+use Wikibase\Repo\Tests\NewStatement;
 use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\SparqlHelper;
 use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\TypeCheckerHelper;
 use WikibaseQuality\ConstraintReport\Tests\ConstraintParameters;
@@ -46,11 +49,14 @@
        }
 
        /**
+        * @param EntityLookup $lookup The backing lookup of the mock (defaults 
to JsonFileEntityLookup).
         * @return EntityLookup Expects that getEntity is called
         * exactly WBQualityConstraintsTypeCheckMaxEntities times.
         */
-       private function getMaxEntitiesLookup() {
-               $lookup = new JsonFileEntityLookup( __DIR__ );
+       private function getMaxEntitiesLookup( EntityLookup $lookup = null ) {
+               if ( $lookup === null ) {
+                       $lookup = new JsonFileEntityLookup( __DIR__ );
+               }
                $maxEntities = $this->getDefaultConfig()->get( 
'WBQualityConstraintsTypeCheckMaxEntities' );
 
                $spy = $this->getMock( EntityLookup::class );
@@ -63,15 +69,20 @@
 
        /**
         * @param boolean $return
+        * @param array|null $arguments
         * @return SparqlHelper expects that {@link SparqlHelper::hasType}
         * is called exactly once and returns $return.
         */
-       private function getSparqlHelper( $return ) {
+       private function getSparqlHelper( $return, array $arguments = null ) {
+               if ( $arguments === null ) {
+                       $arguments = [ $this->anything(), $this->anything(), 
$this->anything() ];
+               }
                $mock = $this->getMockBuilder( SparqlHelper::class )
                          ->disableOriginalConstructor()
                          ->getMock();
                $mock->expects( $this->once() )
                        ->method( 'hasType' )
+                       ->withConsecutive( $arguments )
                        ->willReturn( $return );
                return $mock;
        }
@@ -98,31 +109,81 @@
        }
 
        public function testCheckIsSubclassOfValidWithIndirection() {
-               $this->assertTrue( $this->getHelper()->isSubclassOf( new 
ItemId( 'Q6' ), [ 'Q100', 'Q101' ] ) );
+               $this->assertTrue( 
$this->getHelper()->isSubclassOfWithSparqlFallback( new ItemId( 'Q6' ), [ 
'Q100', 'Q101' ] ) );
        }
 
        public function testCheckIsSubclassOfInvalid() {
-               $this->assertFalse( $this->getHelper()->isSubclassOf( new 
ItemId( 'Q6' ), [ 'Q200', 'Q201' ] ) );
+               $this->assertFalse( 
$this->getHelper()->isSubclassOfWithSparqlFallback( new ItemId( 'Q6' ), [ 
'Q200', 'Q201' ] ) );
        }
 
        public function testCheckIsSubclassCyclic() {
                $helper = $this->getHelper( $this->getMaxEntitiesLookup() );
-               $this->assertFalse( $helper->isSubclassOf( new ItemId( 'Q7' ), 
[ 'Q100', 'Q101' ] ) );
+               $this->assertFalse( $helper->isSubclassOfWithSparqlFallback( 
new ItemId( 'Q7' ), [ 'Q100', 'Q101' ] ) );
        }
 
        public function testCheckIsSubclassCyclicWide() {
                $helper = $this->getHelper( $this->getMaxEntitiesLookup() );
-               $this->assertFalse( $helper->isSubclassOf( new ItemId( 'Q9' ), 
[ 'Q100', 'Q101' ] ) );
+               $this->assertFalse( $helper->isSubclassOfWithSparqlFallback( 
new ItemId( 'Q9' ), [ 'Q100', 'Q101' ] ) );
        }
 
        public function testCheckIsSubclassCyclicWideWithSparqlTrue() {
                $helper = $this->getHelper( $this->getMaxEntitiesLookup(), 
$this->getSparqlHelper( true ) );
-               $this->assertTrue( $helper->isSubclassOf( new ItemId( 'Q9' ), [ 
'Q100', 'Q101' ] ) );
+               $this->assertTrue( $helper->isSubclassOfWithSparqlFallback( new 
ItemId( 'Q9' ), [ 'Q100', 'Q101' ] ) );
        }
 
        public function testCheckIsSubclassCyclicWideWithSparqlFalse() {
                $helper = $this->getHelper( $this->getMaxEntitiesLookup(), 
$this->getSparqlHelper( false ) );
-               $this->assertFalse( $helper->isSubclassOf( new ItemId( 'Q9' ), 
[ 'Q100', 'Q101' ] ) );
+               $this->assertFalse( $helper->isSubclassOfWithSparqlFallback( 
new ItemId( 'Q9' ), [ 'Q100', 'Q101' ] ) );
+       }
+
+       public function testCheckIsSubclassTree() {
+               $lookup = new InMemoryEntityLookup();
+               $subclassPid = $this->getDefaultConfig()->get( 
'WBQualityConstraintsSubclassOfId' );
+
+               $q1 = NewItem::withId( 'Q1' )
+                       ->andStatement(
+                               NewStatement::forProperty( $subclassPid )
+                                       ->withValue( new ItemId( 'Q2' ) )
+                       )
+                       ->build();
+               $lookup->addEntity( $q1 );
+               $q2 = NewItem::withId( 'Q2' )
+                       ->andStatement(
+                               NewStatement::forProperty( $subclassPid )
+                                       ->withValue( new ItemId( 'Q3' ) )
+                       )
+                       ->andStatement(
+                               NewStatement::forProperty( $subclassPid )
+                                       ->withValue( new ItemId( 'Q5' ) )
+                       )
+                       ->build();
+               $lookup->addEntity( $q2 );
+               $q3 = NewItem::withId( 'Q3' )
+                       ->andStatement(
+                               NewStatement::forProperty( $subclassPid )
+                                       ->withValue( new ItemId( 'Q4' ) )
+                       )
+                       ->build();
+               $lookup->addEntity( $q3 );
+               $q4 = NewItem::withId( 'Q4' )
+                       ->andStatement(
+                               NewStatement::forProperty( $subclassPid )
+                                       ->withValue( new ItemId( 'Q3' ) )
+                       )
+                       ->build();
+               $lookup->addEntity( $q4 );
+
+               $sparqlHelper = $this->getSparqlHelper(
+                       true,
+                       [
+                               $this->identicalTo( 'Q1' ),
+                               $this->identicalTo( [ 'Q5' ] ),
+                               $this->identicalTo( false )
+                       ]
+               );
+               $helper = $this->getHelper( $this->getMaxEntitiesLookup( 
$lookup ), $sparqlHelper );
+
+               $this->assertTrue( $helper->isSubclassOfWithSparqlFallback( new 
ItemId( 'Q1' ), [ 'Q5' ] ) );
        }
 
 }
diff --git a/extensions/PropertySuggester/package.json 
b/extensions/PropertySuggester/package.json
index bc9deeb..c0f24f3 100644
--- a/extensions/PropertySuggester/package.json
+++ b/extensions/PropertySuggester/package.json
@@ -12,7 +12,7 @@
        "license": "GPL-2.0+",
        "devDependencies": {
                "eslint-config-wikimedia": "0.4.0",
-               "grunt": "^1.0.1",
+               "grunt": "1.0.1",
                "grunt-banana-checker": "0.5.0",
                "grunt-eslint": "19.0.0",
                "grunt-jsonlint": "1.0.7"
diff --git a/extensions/Quality/package.json b/extensions/Quality/package.json
index c8ba0d5..8e78752 100644
--- a/extensions/Quality/package.json
+++ b/extensions/Quality/package.json
@@ -11,12 +11,13 @@
   "author": "BP2014N1",
   "license": "GPL-2.0+",
   "devDependencies": {
-     "eslint-config-wikimedia": "0.4.0",
+    "eslint-config-wikimedia": "0.4.0",
     "grunt": "1.0.1",
     "grunt-banana-checker": "0.4.0",
     "grunt-eslint": "19.0.0",
     "grunt-jsonlint": "1.0.7",
     "grunt-stylelint": "0.6.0",
+    "stylelint": "7.8.0",
     "stylelint-config-wikimedia": "0.4.1"
   }
 }
diff --git a/extensions/Wikibase/client/i18n/din.json 
b/extensions/Wikibase/client/i18n/din.json
index 109b122..a6ec9d3 100644
--- a/extensions/Wikibase/client/i18n/din.json
+++ b/extensions/Wikibase/client/i18n/din.json
@@ -10,6 +10,7 @@
        "wikibase-dataitem": "{{WBREPONAME}} këdäŋ",
        "wikibase-editlinks": "Lathë anuɛ̈tic",
        "wikibase-editlinkstitle": "Lathë anuɛ̈t ë kaamëtokic",
+       "wikibase-linkitem-addlinks": "Mätë anuɛ̈t",
        "wikibase-rc-hide-wikidata": "$1 {{WBREPONAME}}",
        "wikibase-rc-hide-wikidata-hide": "Thaan",
        "wikibase-rc-hide-wikidata-show": "Nyooth",
diff --git a/extensions/Wikibase/client/includes/Changes/ChangeHandler.php 
b/extensions/Wikibase/client/includes/Changes/ChangeHandler.php
index 884c30e..d30344a 100644
--- a/extensions/Wikibase/client/includes/Changes/ChangeHandler.php
+++ b/extensions/Wikibase/client/includes/Changes/ChangeHandler.php
@@ -8,6 +8,7 @@
 use MWException;
 use SiteLookup;
 use Title;
+use Traversable;
 use Wikibase\Change;
 use Wikibase\Client\Store\TitleFactory;
 use Wikibase\Client\Usage\EntityUsage;
@@ -24,32 +25,6 @@
  * @author Daniel Kinzler
  */
 class ChangeHandler {
-
-       /**
-        * The change requites any rendered version of the page to be purged 
from the parser cache.
-        */
-       const PARSER_PURGE_ACTION = 'parser';
-
-       /**
-        * The change requites a LinksUpdate job to be scheduled to update any 
links
-        * associated with the page.
-        */
-       const LINKS_UPDATE_ACTION = 'links';
-
-       /**
-        * The change requites any HTML output generated from the page to be 
purged from web cached.
-        */
-       const WEB_PURGE_ACTION = 'web';
-
-       /**
-        * The change requites an entry to be injected into the recentchanges 
table.
-        */
-       const RC_ENTRY_ACTION = 'rc';
-
-       /**
-        * The change requites an entry to be injected into the revision table.
-        */
-       const HISTORY_ENTRY_ACTION = 'history';
 
        /**
         * @var AffectedPagesFinder
@@ -157,117 +132,26 @@
                wfDebugLog( __CLASS__, __FUNCTION__ . ': updating ' . count( 
$usagesPerPage )
                        . " page(s) for change #$changeId." );
 
-               $actionBuckets = [];
+               // Run all updates on all affected pages
+               $titlesToUpdate = $this->getTitlesForUsages( $usagesPerPage );
 
-               /** @var PageEntityUsages $usages */
-               foreach ( $usagesPerPage as $usages ) {
-                       $actions = $this->getUpdateActions( 
$usages->getAspects() );
-                       $this->updateActionBuckets( $actionBuckets, 
$usages->getPageId(), $actions );
-               }
-
-               foreach ( $actionBuckets as $action => $bucket ) {
-                       $this->applyUpdateAction( $action, $bucket, $change );
-               }
+               $this->updater->purgeWebCache( $titlesToUpdate );
+               $this->updater->scheduleRefreshLinks( $titlesToUpdate );
+               $this->updater->injectRCRecords( $titlesToUpdate, $change );
+               // TODO: inject dummy revisions
        }
 
        /**
-        * @param string[] $aspects List of usage aspects (without modifiers), 
as defined by the
-        * EntityUsage::..._USAGE constants.
-        *
-        * @return string[] List of actions, as defined by the 
ChangeHandler::..._ACTION constants.
-        */
-       public function getUpdateActions( array $aspects ) {
-               $actions = [];
-               $aspects = array_flip( $aspects );
-
-               $all = isset( $aspects[EntityUsage::ALL_USAGE] );
-
-               if ( isset( $aspects[EntityUsage::SITELINK_USAGE] ) || $all ) {
-                       $actions[self::LINKS_UPDATE_ACTION] = true;
-
-                       // TODO: introduce an update action that updates just 
the metadata
-                       // in the cached ParserOutput, without re-parsing the 
page!
-                       $actions[self::PARSER_PURGE_ACTION] = true;
-               }
-
-               if ( isset( $aspects[EntityUsage::LABEL_USAGE] ) || $all ) {
-                       $actions[self::PARSER_PURGE_ACTION] = true;
-               }
-
-               if ( isset( $aspects[EntityUsage::TITLE_USAGE] ) || $all ) {
-                       $actions[self::PARSER_PURGE_ACTION] = true;
-               }
-
-               if ( isset( $aspects[EntityUsage::OTHER_USAGE] ) || $all ) {
-                       $actions[self::PARSER_PURGE_ACTION] = true;
-               }
-
-               // Purge caches and inject log entries if we have reason
-               // to update the cached ParserOutput object in some way.
-               if ( isset( $actions[self::PARSER_PURGE_ACTION] ) || isset( 
$actions[self::LINKS_UPDATE_ACTION] ) ) {
-                       $actions[self::WEB_PURGE_ACTION] = true;
-                       $actions[self::RC_ENTRY_ACTION] = true;
-                       $actions[self::HISTORY_ENTRY_ACTION] = true;
-               }
-
-               return array_keys( $actions );
-       }
-
-       /**
-        * @param array[] &$buckets Map of action names to lists of page IDs. 
To be updated.
-        * @param int $pageId The page ID
-        * @param string[] $actions Actions to perform on the page
-        */
-       private function updateActionBuckets( array &$buckets, $pageId, array 
$actions ) {
-               foreach ( $actions as $action ) {
-                       $buckets[$action][] = $pageId;
-               }
-       }
-
-       /**
-        * @param string $action
-        * @param int[] $pageIds
-        * @param EntityChange $change
-        */
-       private function applyUpdateAction( $action, array $pageIds, 
EntityChange $change ) {
-               $titlesToUpdate = $this->getTitlesForPageIds( $pageIds );
-
-               switch ( $action ) {
-                       case self::PARSER_PURGE_ACTION:
-                               $this->updater->purgeParserCache( 
$titlesToUpdate );
-                               break;
-
-                       case self::WEB_PURGE_ACTION:
-                               $this->updater->purgeWebCache( $titlesToUpdate 
);
-                               break;
-
-                       case self::LINKS_UPDATE_ACTION:
-                               $this->updater->scheduleRefreshLinks( 
$titlesToUpdate );
-                               break;
-
-                       case self::RC_ENTRY_ACTION:
-                               if ( $this->injectRecentChanges ) {
-                                       $this->updater->injectRCRecords( 
$titlesToUpdate, $change );
-                               }
-
-                               break;
-
-                       //TODO: handling for self::HISTORY_ENTRY_ACTION goes 
here.
-                       //      should probably be 
$this->updater->injectHistoryRecords() or some such.
-               }
-       }
-
-       /**
-        * @param int[] $pageIds
+        * @param Traversable $usagesPerPage A sequence of PageEntityUsages 
objects
         *
         * @return Title[]
         */
-       private function getTitlesForPageIds( array $pageIds ) {
+       private function getTitlesForUsages( $usagesPerPage ) {
                $titles = [];
 
-               foreach ( $pageIds as $id ) {
+               foreach ( $usagesPerPage as $usages ) {
                        try {
-                               $title = $this->titleFactory->newFromID( $id );
+                               $title = $this->titleFactory->newFromID( 
$usages->getPageId() );
                                $titles[] = $title;
                        } catch ( Exception $ex ) {
                                // No title for that ID, maybe the page got 
deleted just now.
diff --git a/extensions/Wikibase/client/includes/Changes/PageUpdater.php 
b/extensions/Wikibase/client/includes/Changes/PageUpdater.php
index ec74351..391f122 100644
--- a/extensions/Wikibase/client/includes/Changes/PageUpdater.php
+++ b/extensions/Wikibase/client/includes/Changes/PageUpdater.php
@@ -17,13 +17,6 @@
 interface PageUpdater {
 
        /**
-        * Invalidates local cached of the given pages.
-        *
-        * @param Title[] $titles The Titles of the pages to update
-        */
-       public function purgeParserCache( array $titles );
-
-       /**
         * Invalidates external web cached of the given pages.
         *
         * @param Title[] $titles The Titles of the pages to update
diff --git a/extensions/Wikibase/client/includes/Changes/WikiPageUpdater.php 
b/extensions/Wikibase/client/includes/Changes/WikiPageUpdater.php
index 3ab41b1..2ab62a5 100644
--- a/extensions/Wikibase/client/includes/Changes/WikiPageUpdater.php
+++ b/extensions/Wikibase/client/includes/Changes/WikiPageUpdater.php
@@ -2,7 +2,7 @@
 
 namespace Wikibase\Client\Changes;
 
-use Job;
+use HTMLCacheUpdateJob;
 use JobQueueGroup;
 use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
 use RefreshLinksJob;
@@ -95,33 +95,37 @@
        }
 
        /**
-        * Invalidates local cached of the given pages.
-        *
-        * @param Title[] $titles The Titles of the pages to update
-        */
-       public function purgeParserCache( array $titles ) {
-               /* @var Title $title */
-               foreach ( $titles as $title ) {
-                       wfDebugLog( __CLASS__, __FUNCTION__ . ": purging page " 
. $title->getText() );
-
-                       // TODO: This queues a database update for each title 
separately. Batch it.
-                       $title->invalidateCache();
-               }
-               $this->incrementStats( 'ParserCache', count( $titles ) );
-       }
-
-       /**
         * Invalidates external web cached of the given pages.
         *
         * @param Title[] $titles The Titles of the pages to update
         */
        public function purgeWebCache( array $titles ) {
-               /* @var Title $title */
-               foreach ( $titles as $title ) {
-                       wfDebugLog( __CLASS__, __FUNCTION__ . ": purging web 
cache for " . $title->getText() );
-                       $title->purgeSquid();
+               if ( $titles === [] ) {
+                       return;
                }
-               $this->incrementStats( 'WebCache', count( $titles ) );
+
+               $jobs = [];
+               $titleBatches = array_chunk( $titles, $this->dbBatchSize );
+
+               /* @var Title[] $batch */
+               foreach ( $titleBatches as $batch ) {
+                       wfDebugLog( __CLASS__, __FUNCTION__ . ": scheduling 
HTMLCacheUpdateJob for "
+                                            . count( $batch ) . " titles" );
+
+                       $dummyTitle = Title::makeTitle( NS_SPECIAL, 'Badtitle/' 
. __CLASS__ );
+
+                       $jobs[] = new HTMLCacheUpdateJob(
+                               $dummyTitle, // the title will be ignored 
because the 'pages' parameter is set.
+                               [
+                                       'pages' => 
$this->getPageParamForRefreshLinksJob( $batch ),
+                                       'rootJobTimestamp' => wfTimestampNow()
+                               ]
+                       );
+               }
+
+               $this->jobQueueGroup->lazyPush( $jobs );
+               $this->incrementStats( 'WebCache.jobs', count( $jobs ) );
+               $this->incrementStats( 'WebCache.titles', count( $titles ) );
        }
 
        /**
@@ -130,22 +134,51 @@
         * @param Title[] $titles The Titles of the pages to update
         */
        public function scheduleRefreshLinks( array $titles ) {
-               /* @var Title $title */
-               foreach ( $titles as $title ) {
-                       wfDebugLog( __CLASS__, __FUNCTION__ . ": scheduling 
refresh links for "
-                               . $title->getText() );
-
-                       $job = new RefreshLinksJob(
-                               $title,
-                               Job::newRootJobParams(
-                                       $title->getPrefixedDBkey()
-                               )
-                       );
-
-                       $this->jobQueueGroup->push( $job );
-                       $this->jobQueueGroup->deduplicateRootJob( $job );
+               if ( $titles === [] ) {
+                       return;
                }
-               $this->incrementStats( 'RefreshLinksJob', count( $titles ) );
+
+               $jobs = [];
+               $titleBatches = array_chunk( $titles, $this->dbBatchSize );
+
+               /* @var Title[] $batch */
+               foreach ( $titleBatches as $batch ) {
+                       wfDebugLog( __CLASS__, __FUNCTION__ . ": scheduling 
refresh links for "
+                               . count( $batch ) . " titles" );
+
+                       $dummyTitle = Title::makeTitle( NS_SPECIAL, 'Badtitle/' 
. __CLASS__ );
+
+                       $jobs[] = new RefreshLinksJob(
+                               $dummyTitle, // the title will be ignored 
because the 'pages' parameter is set.
+                               [
+                                       'pages' => 
$this->getPageParamForRefreshLinksJob( $batch ),
+                                       'rootJobTimestamp' => wfTimestampNow(),
+                               ]
+                       );
+               }
+
+               $this->jobQueueGroup->lazyPush( $jobs );
+               $this->incrementStats( 'RefreshLinks.jobs', count( $jobs ) );
+               $this->incrementStats( 'RefreshLinks.titles', count( $titles ) 
);
+       }
+
+       /**
+        * @param Title[] $titles
+        *
+        * @returns array[] string $pageId => [ int $namespace, string $dbKey ]
+        */
+       private function getPageParamForRefreshLinksJob( array $titles ) {
+               $pages = [];
+
+               foreach ( $titles as $t ) {
+                       $id = $t->getArticleID();
+                       $pages[$id] = [
+                               $t->getNamespace(),
+                               $t->getDBkey()
+                       ];
+               }
+
+               return $pages;
        }
 
        /**
diff --git a/extensions/Wikibase/client/includes/WikibaseClient.php 
b/extensions/Wikibase/client/includes/WikibaseClient.php
index 6b2d353..9a0ba9d 100644
--- a/extensions/Wikibase/client/includes/WikibaseClient.php
+++ b/extensions/Wikibase/client/includes/WikibaseClient.php
@@ -224,9 +224,19 @@
        private $repositoryDefinitions;
 
        /**
-        * @var PrefetchingTermLookup|null
+        * @var TermLookup|null
         */
        private $termLookup = null;
+
+       /**
+        * @var TermBuffer|null
+        */
+       private $termBuffer = null;
+
+       /**
+        * @var PrefetchingTermLookup|null
+        */
+       private $prefetchingTermLookup = null;
 
        /**
         * @var EntityNamespaceLookup|null
@@ -423,25 +433,34 @@
         * @return TermBuffer
         */
        public function getTermBuffer() {
-               return $this->getPrefetchingTermLookup();
+               if ( !$this->termBuffer ) {
+                       $this->termBuffer = $this->getPrefetchingTermLookup();
+               }
+
+               return $this->termBuffer;
        }
 
        /**
         * @return TermLookup
         */
        public function getTermLookup() {
-               return $this->getPrefetchingTermLookup();
+               if ( !$this->termLookup ) {
+                       $this->termLookup = $this->getPrefetchingTermLookup();
+               }
+
+               return $this->termLookup;
        }
 
        /**
         * @return PrefetchingTermLookup
         */
        private function getPrefetchingTermLookup() {
-               if ( !$this->termLookup ) {
-                       $this->termLookup = 
$this->getEntityDataRetrievalServiceFactory()->getTermBuffer();
+               if ( !$this->prefetchingTermLookup ) {
+                       // TODO: This should not assume the TermBuffer instance 
to be a PrefetchingTermLookup
+                       $this->prefetchingTermLookup = 
$this->getEntityDataRetrievalServiceFactory()->getTermBuffer();
                }
 
-               return $this->termLookup;
+               return $this->prefetchingTermLookup;
        }
 
        /**
diff --git 
a/extensions/Wikibase/client/tests/phpunit/includes/Changes/ChangeHandlerTest.php
 
b/extensions/Wikibase/client/tests/phpunit/includes/Changes/ChangeHandlerTest.php
index ed5d00c..a3dc146 100644
--- 
a/extensions/Wikibase/client/tests/phpunit/includes/Changes/ChangeHandlerTest.php
+++ 
b/extensions/Wikibase/client/tests/phpunit/includes/Changes/ChangeHandlerTest.php
@@ -181,66 +181,6 @@
                $this->assertEquals( 1, $spy->handleChangesCallCount );
        }
 
-       public function provideGetUpdateActions() {
-               return [
-                       'empty' => [
-                               [],
-                               [],
-                       ],
-                       'sitelink usage' => [
-                               [ EntityUsage::SITELINK_USAGE ],
-                               [ ChangeHandler::LINKS_UPDATE_ACTION, 
ChangeHandler::PARSER_PURGE_ACTION,
-                                       ChangeHandler::WEB_PURGE_ACTION, 
ChangeHandler::RC_ENTRY_ACTION ],
-                       ],
-                       'label usage' => [
-                               [ EntityUsage::LABEL_USAGE ],
-                               [ ChangeHandler::PARSER_PURGE_ACTION, 
ChangeHandler::WEB_PURGE_ACTION,
-                                       ChangeHandler::RC_ENTRY_ACTION ],
-                               [ ChangeHandler::LINKS_UPDATE_ACTION ]
-                       ],
-                       'title usage' => [
-                               [ EntityUsage::TITLE_USAGE ],
-                               [ ChangeHandler::PARSER_PURGE_ACTION, 
ChangeHandler::WEB_PURGE_ACTION,
-                                       ChangeHandler::RC_ENTRY_ACTION ],
-                               [ ChangeHandler::LINKS_UPDATE_ACTION ]
-                       ],
-                       'other usage' => [
-                               [ EntityUsage::OTHER_USAGE ],
-                               [ ChangeHandler::PARSER_PURGE_ACTION, 
ChangeHandler::WEB_PURGE_ACTION,
-                                       ChangeHandler::RC_ENTRY_ACTION ],
-                               [ ChangeHandler::LINKS_UPDATE_ACTION ]
-                       ],
-                       'all usage' => [
-                               [ EntityUsage::ALL_USAGE ],
-                               [ ChangeHandler::PARSER_PURGE_ACTION, 
ChangeHandler::WEB_PURGE_ACTION,
-                                       ChangeHandler::RC_ENTRY_ACTION ],
-                       ],
-                       'sitelink and other usage (does links update)' => [
-                               [ EntityUsage::SITELINK_USAGE, 
EntityUsage::OTHER_USAGE ],
-                               [ ChangeHandler::LINKS_UPDATE_ACTION, 
ChangeHandler::PARSER_PURGE_ACTION,
-                                       ChangeHandler::WEB_PURGE_ACTION, 
ChangeHandler::RC_ENTRY_ACTION ],
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideGetUpdateActions
-        */
-       public function testGetUpdateActions( array $aspects, array $expected, 
array $not = [] ) {
-               $handler = $this->getChangeHandler();
-               $actions = $handler->getUpdateActions( $aspects );
-
-               sort( $expected );
-               sort( $actions );
-
-               // check that $actions contains AT LEAST $expected
-               $actual = array_intersect( $actions, $expected );
-               $this->assertEquals( array_values( $expected ), array_values( 
$actual ), 'expected actions' );
-
-               $unexpected = array_intersect( $actions, $not );
-               $this->assertEmpty( array_values( $unexpected ), 'unexpected 
actions: ' . implode( '|', $unexpected ) );
-       }
-
        /**
         * Returns a map of fake local page IDs to the corresponding local page 
names.
         * The fake page IDs are the IDs of the items that have a sitelink to 
the
@@ -390,42 +330,36 @@
                $userEmmy2 = Title::newFromText( 'User:Emmy2' 
)->getPrefixedText();
 
                $empty = [
-                       'purgeParserCache' => [],
                        'scheduleRefreshLinks' => [],
                        'purgeWebCache' => [],
                        'injectRCRecord' => [],
                ];
 
                $emmy2PurgeParser = [
-                       'purgeParserCache' => [ 'Emmy2' => true ],
-                       'scheduleRefreshLinks' => [],
+                       'scheduleRefreshLinks' => [ 'Emmy2' => true ],
                        'purgeWebCache' => [ 'Emmy2' => true ],
                        'injectRCRecord' => [ 'Emmy2' => true ],
                ];
 
                $userEmmy2PurgeParser = [
-                       'purgeParserCache' => [ $userEmmy2 => true ],
-                       'scheduleRefreshLinks' => [],
+                       'scheduleRefreshLinks' => [ $userEmmy2 => true ],
                        'purgeWebCache' => [ $userEmmy2 => true ],
                        'injectRCRecord' => [ $userEmmy2 => true ],
                ];
 
                $emmyUpdateLinks = [
-                       'purgeParserCache' => [ 'Emmy' => true ],
                        'scheduleRefreshLinks' => [ 'Emmy' => true ],
                        'purgeWebCache' => [ 'Emmy' => true ],
                        'injectRCRecord' => [ 'Emmy' => true ],
                ];
 
                $emmy2UpdateLinks = [
-                       'purgeParserCache' => [ 'Emmy2' => true ],
                        'scheduleRefreshLinks' => [ 'Emmy2' => true ],
                        'purgeWebCache' => [ 'Emmy2' => true ],
                        'injectRCRecord' => [ 'Emmy2' => true ],
                ];
 
                $emmy2UpdateAll = [
-                       'purgeParserCache' => [ 'Emmy2' => true ],
                        'scheduleRefreshLinks' => [ 'Emmy2' => true ],
                        'purgeWebCache' => [ 'Emmy2' => true ],
                        'injectRCRecord' => [ 'Emmy2' => true ],
@@ -516,7 +450,6 @@
                                $changes['change-enwiki-sitelink'],
                                [ 'q100' => [ 'enwiki' => 'Emmy' ], 'q200' => [ 
'enwiki' => 'Emmy2' ] ],
                                [
-                                       'purgeParserCache' => [ 'Emmy' => true, 
'Emmy2' => true ],
                                        'scheduleRefreshLinks' => [ 'Emmy' => 
true, 'Emmy2' => true ],
                                        'purgeWebCache' => [ 'Emmy' => true, 
'Emmy2' => true ],
                                        'injectRCRecord' => [ 'Emmy' => true, 
'Emmy2' => true ],
diff --git 
a/extensions/Wikibase/client/tests/phpunit/includes/Changes/MockPageUpdater.php 
b/extensions/Wikibase/client/tests/phpunit/includes/Changes/MockPageUpdater.php
index 34f7047..02a7028 100644
--- 
a/extensions/Wikibase/client/tests/phpunit/includes/Changes/MockPageUpdater.php
+++ 
b/extensions/Wikibase/client/tests/phpunit/includes/Changes/MockPageUpdater.php
@@ -18,21 +18,10 @@
 class MockPageUpdater implements PageUpdater {
 
        private $updates = [
-               'purgeParserCache' => [],
                'purgeWebCache' => [],
                'scheduleRefreshLinks' => [],
                'injectRCRecord' => [],
        ];
-
-       /**
-        * @param Title[] $titles
-        */
-       public function purgeParserCache( array $titles ) {
-               foreach ( $titles as $title ) {
-                       $key = $title->getPrefixedDBkey();
-                       $this->updates['purgeParserCache'][ $key ] = $title;
-               }
-       }
 
        /**
         * @param Title[] $titles
diff --git 
a/extensions/Wikibase/client/tests/phpunit/includes/Changes/WikiPageUpdaterTest.php
 
b/extensions/Wikibase/client/tests/phpunit/includes/Changes/WikiPageUpdaterTest.php
index 2be8d28..5315a06 100644
--- 
a/extensions/Wikibase/client/tests/phpunit/includes/Changes/WikiPageUpdaterTest.php
+++ 
b/extensions/Wikibase/client/tests/phpunit/includes/Changes/WikiPageUpdaterTest.php
@@ -3,9 +3,11 @@
 namespace Wikibase\Client\Tests\Changes;
 
 use IJobSpecification;
+use HTMLCacheUpdateJob;
 use Job;
 use JobQueueGroup;
 use PHPUnit_Framework_MockObject_MockObject;
+use MediaWiki\MediaWikiServices;
 use RefreshLinksJob;
 use Title;
 use Wikibase\Client\Changes\WikiPageUpdater;
@@ -135,77 +137,78 @@
                return $LBFactory;
        }
 
-       public function testPurgeParserCache() {
-               $updater = new WikiPageUpdater(
-                       $this->getJobQueueGroupMock(),
-                       $this->getRCFactoryMock(),
-                       $this->getLBFactoryMock(),
-                       $this->getRCDupeDetectorMock()
-               );
-
-               $title = $this->getTitleMock( 'Foo' );
-
-               $title->expects( $this->once() )
-                       ->method( 'invalidateCache' );
-
-               $updater->purgeParserCache( [
-                       $title
-               ] );
-       }
-
        public function testPurgeWebCache() {
-               $updater = new WikiPageUpdater(
-                       $this->getJobQueueGroupMock(),
-                       $this->getRCFactoryMock(),
-                       $this->getLBFactoryMock(),
-                       $this->getRCDupeDetectorMock()
-               );
-
-               $title = $this->getTitleMock( 'Foo' );
-               $title->expects( $this->once() )
-                       ->method( 'purgeSquid' );
-
-               $updater->purgeWebCache( [
-                       $title
-               ] );
-       }
-
-       public function testScheduleRefreshLinks() {
-               $title = $this->getTitleMock( 'Foo' );
+               $titleFoo = $this->getTitleMock( 'Foo', 21 );
+               $titleBar = $this->getTitleMock( 'Bar', 22 );
+               $titleCuzz = $this->getTitleMock( 'Cuzz', 23 );
 
                $jobQueueGroup = $this->getJobQueueGroupMock();
 
-               $jobMatcher = function( RefreshLinksJob $job ) {
-                       $this->assertSame( 'Foo', 
$job->getTitle()->getPrefixedDBkey() );
-
-                       $expectedSignature = Job::newRootJobParams( 'Foo' );
-                       $actualSignature = $job->getRootJobParams();
-                       $this->assertSame(
-                               $expectedSignature['rootJobSignature'],
-                               $actualSignature['rootJobSignature']
-                       );
-
-                       return true;
-               };
-
-               $jobQueueGroup->expects( $this->any() )
-                       ->method( 'push' )
-                       ->with( $this->callback( $jobMatcher ) );
-
-               $jobQueueGroup->expects( $this->any() )
-                       ->method( 'deduplicateRootJob' )
-                       ->with( $this->callback( $jobMatcher ) );
+               $pages = [];
+               $jobQueueGroup->expects( $this->atLeastOnce() )
+                       ->method( 'lazyPush' )
+                       ->will( $this->returnCallback( function( array $jobs ) 
use ( &$pages ) {
+                               /** @var Job $job */
+                               foreach ( $jobs as $job ) {
+                                       $this->assertInstanceOf( 
HTMLCacheUpdateJob::class, $job );
+                                       $params = $job->getParams();
+                                       $this->assertArrayHasKey( 'pages', 
$params, '$params["pages"]' );
+                                       $pages += $params['pages']; // addition 
uses keys, array_merge does not
+                               }
+                       } ) );
 
                $updater = new WikiPageUpdater(
                        $jobQueueGroup,
                        $this->getRCFactoryMock(),
-                       $this->getLBFactoryMock(),
+                       
MediaWikiServices::getInstance()->getDBLoadBalancerFactory(),
+                       $this->getRCDupeDetectorMock()
+               );
+
+               $updater->purgeWebCache( [
+                       $titleFoo, $titleBar, $titleCuzz,
+               ] );
+
+               $this->assertEquals( [ 21, 22, 23 ], array_keys( $pages ) );
+               $this->assertEquals( [ 0, 'Foo' ], $pages[21], '$pages[21]' );
+               $this->assertEquals( [ 0, 'Bar' ], $pages[22], '$pages[22]' );
+               $this->assertEquals( [ 0, 'Cuzz' ], $pages[23], '$pages[23]' );
+       }
+
+       public function testScheduleRefreshLinks() {
+               $titleFoo = $this->getTitleMock( 'Foo', 21 );
+               $titleBar = $this->getTitleMock( 'Bar', 22 );
+               $titleCuzz = $this->getTitleMock( 'Cuzz', 23 );
+
+               $jobQueueGroup = $this->getJobQueueGroupMock();
+
+               $pages = [];
+               $jobQueueGroup->expects( $this->atLeastOnce() )
+                       ->method( 'lazyPush' )
+                       ->will( $this->returnCallback( function( array $jobs ) 
use ( &$pages ) {
+                               /** @var Job $job */
+                               foreach ( $jobs as $job ) {
+                                       $this->assertInstanceOf( 
RefreshLinksJob::class, $job );
+                                       $params = $job->getParams();
+                                       $this->assertArrayHasKey( 'pages', 
$params, '$params["pages"]' );
+                                       $pages += $params['pages']; // addition 
uses keys, array_merge does not
+                               }
+                       } ) );
+
+               $updater = new WikiPageUpdater(
+                       $jobQueueGroup,
+                       $this->getRCFactoryMock(),
+                       
MediaWikiServices::getInstance()->getDBLoadBalancerFactory(),
                        $this->getRCDupeDetectorMock()
                );
 
                $updater->scheduleRefreshLinks( [
-                       $title
+                       $titleFoo, $titleBar, $titleCuzz,
                ] );
+
+               $this->assertEquals( [ 21, 22, 23 ], array_keys( $pages ) );
+               $this->assertEquals( [ 0, 'Foo' ], $pages[21], '$pages[21]' );
+               $this->assertEquals( [ 0, 'Bar' ], $pages[22], '$pages[22]' );
+               $this->assertEquals( [ 0, 'Cuzz' ], $pages[23], '$pages[23]' );
        }
 
        public function testInjectRCRecords() {
diff --git a/extensions/Wikibase/repo/includes/EditEntity.php 
b/extensions/Wikibase/repo/includes/EditEntity.php
index 6fe10e3..30250e8 100644
--- a/extensions/Wikibase/repo/includes/EditEntity.php
+++ b/extensions/Wikibase/repo/includes/EditEntity.php
@@ -106,11 +106,6 @@
        private $title = null;
 
        /**
-        * @var IContextSource
-        */
-       private $context;
-
-       /**
         * @var EditFilterHookRunner
         */
        private $editFilterHookRunner;
@@ -178,10 +173,6 @@
         *        This will detect "late" edit conflicts, i.e. someone 
squeezing in an edit
         *        just before the actual database transaction for saving beings.
         *        The empty string and 0 are both treated as `false`, disabling 
conflict checks.
-        * @param IContextSource|null $context the context to use while 
processing
-        *        the edit; defaults to RequestContext::getMain().
-        *
-        * @throws InvalidArgumentException
         */
        public function __construct(
                EntityTitleStoreLookup $titleLookup,
@@ -193,8 +184,7 @@
                EntityDocument $newEntity,
                User $user,
                EditFilterHookRunner $editFilterHookRunner,
-               $baseRevId = false,
-               IContextSource $context = null
+               $baseRevId = false
        ) {
                $this->newEntity = $newEntity;
 
@@ -211,12 +201,6 @@
 
                $this->errorType = 0;
                $this->status = Status::newGood();
-
-               if ( $context === null ) {
-                       $context = RequestContext::getMain();
-               }
-
-               $this->context = $context;
 
                $this->titleLookup = $titleLookup;
                $this->entityRevisionLookup = $entityLookup;
@@ -678,56 +662,6 @@
         */
        public function doesCheckForEditConflicts() {
                return $this->getBaseRevisionId() !== false;
-       }
-
-       /**
-        * Shows an error page showing the errors that occurred during 
attemptSave(), if any.
-        *
-        * If $titleMessage is set it is made an assumption that the page is 
still the original
-        * one, and there should be no link back from a special error page.
-        *
-        * @param string|null $titleMessage Message key for the page title.
-        *
-        * @return bool true if an error page was shown, false if there were no 
errors to show.
-        */
-       public function showErrorPage( $titleMessage = null ) {
-               $out = $this->context->getOutput();
-
-               if ( $this->status === null || $this->status->isOK() ) {
-                       return false;
-               }
-
-               if ( $titleMessage === null ) {
-                       $out->prepareErrorPage( wfMessage( 'errorpagetitle' ) );
-               } else {
-                       $out->prepareErrorPage( wfMessage( $titleMessage ), 
wfMessage( 'errorpagetitle' ) );
-               }
-
-               $this->showStatus();
-
-               if ( !isset( $titleMessage ) ) {
-                       $out->returnToMain( '', $this->getTitle() );
-               }
-
-               return true;
-       }
-
-       /**
-        * Shows any errors or warnings from attemptSave().
-        *
-        * @return bool true if any message was shown, false if there were no 
errors to show.
-        */
-       private function showStatus() {
-               if ( $this->status === null || $this->status->isGood() ) {
-                       return false;
-               }
-
-               $out = $this->context->getOutput();
-               $text = $this->status->getHTML();
-
-               $out->addHTML( Html::rawElement( 'div', [ 'class' => 'error' ], 
$text ) );
-
-               return true;
        }
 
        /**
diff --git a/extensions/Wikibase/repo/includes/EditEntityFactory.php 
b/extensions/Wikibase/repo/includes/EditEntityFactory.php
index d88a6c5..b81e94b 100644
--- a/extensions/Wikibase/repo/includes/EditEntityFactory.php
+++ b/extensions/Wikibase/repo/includes/EditEntityFactory.php
@@ -55,11 +55,6 @@
        private $editFilterHookRunner;
 
        /**
-        * @var IContextSource|null
-        */
-       private $context;
-
-       /**
         * @param EntityTitleStoreLookup $titleLookup
         * @param EntityRevisionLookup $entityLookup
         * @param EntityStore $entityStore
@@ -67,7 +62,6 @@
         * @param EntityDiffer $entityDiffer
         * @param EntityPatcher $entityPatcher
         * @param EditFilterHookRunner $editFilterHookRunner
-        * @param IContextSource|null $context
         */
        public function __construct(
                EntityTitleStoreLookup $titleLookup,
@@ -76,8 +70,7 @@
                EntityPermissionChecker $permissionChecker,
                EntityDiffer $entityDiffer,
                EntityPatcher $entityPatcher,
-               EditFilterHookRunner $editFilterHookRunner,
-               IContextSource $context = null
+               EditFilterHookRunner $editFilterHookRunner
        ) {
                $this->titleLookup = $titleLookup;
                $this->entityRevisionLookup = $entityLookup;
@@ -86,7 +79,6 @@
                $this->entityDiffer = $entityDiffer;
                $this->entityPatcher = $entityPatcher;
                $this->editFilterHookRunner = $editFilterHookRunner;
-               $this->context = $context;
        }
 
        /**
@@ -112,8 +104,7 @@
                        $entity,
                        $user,
                        $this->editFilterHookRunner,
-                       $baseRevId,
-                       $this->context
+                       $baseRevId
                );
        }
 
diff --git 
a/extensions/Wikibase/repo/includes/Store/EntityPermissionChecker.php 
b/extensions/Wikibase/repo/includes/Store/EntityPermissionChecker.php
index e197c8f..2242f88 100644
--- a/extensions/Wikibase/repo/includes/Store/EntityPermissionChecker.php
+++ b/extensions/Wikibase/repo/includes/Store/EntityPermissionChecker.php
@@ -20,8 +20,6 @@
 
        const ACTION_EDIT = 'edit';
 
-       const ACTION_CREATE = 'create';
-
        const ACTION_EDIT_TERMS = 'term';
 
        const ACTION_MERGE = 'merge';
diff --git 
a/extensions/Wikibase/repo/includes/Store/WikiPageEntityStorePermissionChecker.php
 
b/extensions/Wikibase/repo/includes/Store/WikiPageEntityStorePermissionChecker.php
index ec93fd7..710501b 100644
--- 
a/extensions/Wikibase/repo/includes/Store/WikiPageEntityStorePermissionChecker.php
+++ 
b/extensions/Wikibase/repo/includes/Store/WikiPageEntityStorePermissionChecker.php
@@ -18,6 +18,8 @@
  */
 class WikiPageEntityStorePermissionChecker implements EntityPermissionChecker {
 
+       const ACTION_MW_CREATE = 'create';
+
        /**
         * @var EntityNamespaceLookup
         */
@@ -71,7 +73,7 @@
                if ( $id === null ) {
                        return $this->getPermissionStatusForEntityType(
                                $user,
-                               [ $action, self::ACTION_CREATE ],
+                               [ $action, self::ACTION_MW_CREATE ],
                                $entity->getType(),
                                $quick
                        );
@@ -103,7 +105,7 @@
                if ( $title === null || !$title->exists() ) {
                        return $this->getPermissionStatusForEntityType(
                                $user,
-                               [ $action, self::ACTION_CREATE ],
+                               [ $action, self::ACTION_MW_CREATE ],
                                $entityId->getEntityType(),
                                $quick
                        );
@@ -178,7 +180,7 @@
        }
 
        private function getMediaWikiPermissionsToCheck( $action, $entityType ) 
{
-               if ( $action === EntityPermissionChecker::ACTION_CREATE ) {
+               if ( $action === self::ACTION_MW_CREATE ) {
                        $entityTypeSpecificCreatePermission = $entityType . 
'-create';
 
                        $permissions = [ 'read', 'edit', 'createpage' ];
diff --git a/extensions/Wikibase/repo/includes/WikibaseRepo.php 
b/extensions/Wikibase/repo/includes/WikibaseRepo.php
index a2258a3..025bd48 100644
--- a/extensions/Wikibase/repo/includes/WikibaseRepo.php
+++ b/extensions/Wikibase/repo/includes/WikibaseRepo.php
@@ -1647,10 +1647,6 @@
         * @return EditEntityFactory
         */
        public function newEditEntityFactory( IContextSource $context = null ) {
-               if ( $context === null ) {
-                       $context = RequestContext::getMain();
-               }
-
                return new EditEntityFactory(
                        $this->getEntityTitleLookup(),
                        $this->getEntityRevisionLookup( 'uncached' ),
@@ -1658,8 +1654,7 @@
                        $this->getEntityPermissionChecker(),
                        $this->getEntityDiffer(),
                        $this->getEntityPatcher(),
-                       $this->newEditFilterHookRunner( $context ),
-                       $context
+                       $this->newEditFilterHookRunner( $context ?: 
RequestContext::getMain() )
                );
        }
 
diff --git a/extensions/Wikibase/repo/maintenance/dispatchChanges.php 
b/extensions/Wikibase/repo/maintenance/dispatchChanges.php
index 7189bda..1ca5612 100644
--- a/extensions/Wikibase/repo/maintenance/dispatchChanges.php
+++ b/extensions/Wikibase/repo/maintenance/dispatchChanges.php
@@ -232,6 +232,8 @@
                                if ( $wikiState ) {
                                        $dispatchedChanges = 
$dispatcher->dispatchTo( $wikiState );
                                        $stats->updateCount( 
'wikibase.repo.dispatchChanges.changes', $dispatchedChanges );
+                                       $stats->updateCount( 
'wikibase.repo.dispatchChanges.changes-per-client.'
+                                                                               
        . $wikiState['chd_site'], $dispatchedChanges );
                                } else {
                                        $stats->increment( 
'wikibase.repo.dispatchChanges.noclient' );
                                        // Try again later, unless we have 
already reached the limit.
diff --git a/extensions/Wikibase/repo/tests/phpunit/includes/EditEntityTest.php 
b/extensions/Wikibase/repo/tests/phpunit/includes/EditEntityTest.php
index abf7bcc..803aa45 100644
--- a/extensions/Wikibase/repo/tests/phpunit/includes/EditEntityTest.php
+++ b/extensions/Wikibase/repo/tests/phpunit/includes/EditEntityTest.php
@@ -142,9 +142,6 @@
                array $permissions = null,
                $editFilterHookRunner = null
        ) {
-               $context = new RequestContext();
-               $context->setRequest( new FauxRequest() );
-
                if ( $user === null ) {
                        $user = User::newFromName( 'EditEntityTestUser' );
                }
@@ -164,8 +161,7 @@
                        $entity,
                        $user,
                        $editFilterHookRunner,
-                       $baseRevId,
-                       $context
+                       $baseRevId
                );
        }
 
@@ -410,36 +406,6 @@
 
                $this->assertEquals( $expectedConflict, $status->hasMessage( 
'edit-conflict' ),
                        "Saving should have failed late if and only if a base 
rev was provided.\n$statusMessage" );
-
-               $this->assertEquals( $expectedConflict, 
$editEntity->showErrorPage(),
-                       "If and only if there was an error, an error page 
should be shown.\n$statusMessage" );
-       }
-
-       public function testErrorPage_DoesNotDoubleEscapeHtmlCharacters() {
-               $repo = $this->getMockRepository();
-               $permissions = [];
-               $context = new RequestContext();
-               // Cannot reuse makeEditEntity because we need the access the 
context
-               $editEntity = new EditEntity(
-                       $this->getEntityTitleLookup(),
-                       $repo,
-                       $repo,
-                       $this->getEntityPermissionChecker( $permissions ),
-                       new EntityDiffer(),
-                       new EntityPatcher(),
-                       new Item(),
-                       $this->getUser( 'EditEntityTestUser' ),
-                       $this->getMockEditFitlerHookRunner(),
-                       false,
-                       $context
-               );
-
-               $editEntity->checkEditPermissions();
-               $editEntity->showErrorPage();
-               $html = $context->getOutput()->getHTML();
-
-               $this->assertContains( '<li>', $html, 'Unescaped HTML' );
-               $this->assertNotContains( '&amp;lt;', $html, 'No double 
escaping' );
        }
 
        public function dataCheckEditPermissions() {
@@ -724,7 +690,6 @@
 
                $this->assertEquals( $shouldWork, $edit->getStatus()->isOK() );
                $this->assertNotEquals( $shouldWork, $edit->hasError( 
EditEntity::TOKEN_ERROR ) );
-               $this->assertNotEquals( $shouldWork, $edit->showErrorPage() );
        }
 
        public function provideAttemptSaveWatch() {
diff --git 
a/extensions/Wikibase/repo/tests/phpunit/includes/Store/WikiPageEntityStorePermissionCheckerTest.php
 
b/extensions/Wikibase/repo/tests/phpunit/includes/Store/WikiPageEntityStorePermissionCheckerTest.php
index 1dd8701..91d6893 100644
--- 
a/extensions/Wikibase/repo/tests/phpunit/includes/Store/WikiPageEntityStorePermissionCheckerTest.php
+++ 
b/extensions/Wikibase/repo/tests/phpunit/includes/Store/WikiPageEntityStorePermissionCheckerTest.php
@@ -184,110 +184,6 @@
        }
 
        /**
-        * @dataProvider provideNonExistingEntitiesAndPermissionsThatAllowEdit
-        */
-       public function 
testAllRequiredPermissionsAreNeededToCreateNonExistingEntity(
-               EntityDocument $nonExistentEntity,
-               array $groupPermissionsAllowingCreation
-       ) {
-               $this->anyUserHasPermissions( $groupPermissionsAllowingCreation 
);
-
-               $this->assertUserIsAllowedTo( 
EntityPermissionChecker::ACTION_CREATE, $nonExistentEntity );
-       }
-
-       /**
-        * @dataProvider 
provideNonExistingEntitiesAndPermissionsThatDisallowEdit
-        */
-       public function 
testAllRequiredPermissionsAreNeededToCreateNonExistingEntity_failures(
-               EntityDocument $nonExistingEntity,
-               array $groupPermissions
-       ) {
-               $this->anyUserHasPermissions( $groupPermissions );
-
-               $this->assertItIsForbiddenForUserTo( 
EntityPermissionChecker::ACTION_CREATE, $nonExistingEntity );
-       }
-
-       /**
-        * TODO: should this be even tested? Is this correct behaviour? Or 
should it rather refuse on PERMISSION_CREATE
-        * and the existing entity?
-        *
-        * @dataProvider provideExistingEntitiesAndPermissionsThatAllowCreating
-        */
-       public function 
testAllRequiredPermissionsAreNeededToCreateExistingEntity(
-               EntityDocument $existingEntity,
-               array $groupPermissions
-       ) {
-               $this->anyUserHasPermissions( $groupPermissions );
-
-               $this->assertUserIsAllowedTo( 
EntityPermissionChecker::ACTION_CREATE, $existingEntity );
-       }
-
-       public function 
provideExistingEntitiesAndPermissionsThatAllowCreating() {
-               return [
-                       'existing item, createpage permission' => [
-                               $this->getExistingItem(),
-                               [ 'createpage' => true ]
-                       ],
-                       'existing item, createpage and property-create 
permission' => [
-                               $this->getExistingProperty(),
-                               [ 'createpage' => true, 'property-create' => 
true, ]
-                       ],
-               ];
-       }
-
-       /**
-        * TODO: should this be even tested? Is this correct behaviour? Or 
should it rather refuse on PERMISSION_CREATE
-        * and the existing entity?
-        *
-        * @dataProvider 
provideExistingEntitiesAndPermissionsThatDisallowCreating
-        */
-       public function 
testAllRequiredPermissionsAreNeededToCreateExistingEntity_failures(
-               EntityDocument $existingEntity,
-               array $groupPermissions
-       ) {
-               $this->anyUserHasPermissions( $groupPermissions );
-
-               $this->assertItIsForbiddenForUserTo( 
EntityPermissionChecker::ACTION_CREATE, $existingEntity );
-       }
-
-       public function 
provideExistingEntitiesAndPermissionsThatDisallowCreating() {
-               return [
-                       'existing item, no createpage permission' => [
-                               $this->getExistingItem(),
-                               [ 'createpage' => false ]
-                       ],
-                       'existing item, no edit permission' => [
-                               $this->getExistingItem(),
-                               [ 'edit' => false, 'createpage' => true, ]
-                       ],
-                       'existing item, no read permission' => [
-                               $this->getExistingItem(),
-                               [ 'read' => false, 'edit' => true, 'createpage' 
=> true, ]
-                       ],
-                       'existing property, no property-create permission' => [
-                               $this->getExistingProperty(),
-                               [ 'createpage' => true, 'property-create' => 
false, ]
-                       ],
-                       'existing property, no createpage permission' => [
-                               $this->getExistingProperty(),
-                               [ 'createpage' => false, 'property-create' => 
true, ]
-                       ],
-                       'existing property, no createpage nor property-create 
permission' => [
-                               $this->getExistingProperty(),
-                               [ 'createpage' => false, 'property-create' => 
false, ]
-                       ],
-                       'existing property, no edit permission' => [
-                               $this->getExistingProperty(),
-                               [ 'edit' => false, 'createpage' => true, 
'property-create' => true, ]
-                       ],
-                       'existing property, no read permission' => [
-                               $this->getExistingProperty(),
-                               [ 'read' => false, 'edit' => true, 'createpage' 
=> true, 'property-create' => true, ]
-                       ],
-               ];
-       }
-
-       /**
         * @dataProvider provideAllEntities
         */
        public function testReadPermissionsAreNeededToReadAnEntity( 
EntityDocument $entity ) {
diff --git a/extensions/Wikibase/view/resources/wikibase/resources.php 
b/extensions/Wikibase/view/resources/wikibase/resources.php
index 253e564..07889bf 100644
--- a/extensions/Wikibase/view/resources/wikibase/resources.php
+++ b/extensions/Wikibase/view/resources/wikibase/resources.php
@@ -23,7 +23,7 @@
                        'position' => 'top',
                        'styles' => [
                                // Order must be hierarchical, do not order 
alphabetically
-                               'wikibase.css',
+                               'wikibase.less',
                                
'../jquery/wikibase/themes/default/jquery.wikibase.aliasesview.css',
                                
'../jquery/wikibase/themes/default/jquery.wikibase.descriptionview.css',
                                
'../jquery/wikibase/themes/default/jquery.wikibase.entityview.css',
diff --git a/extensions/Wikibase/view/resources/wikibase/wikibase.css 
b/extensions/Wikibase/view/resources/wikibase/wikibase.less
similarity index 100%
rename from extensions/Wikibase/view/resources/wikibase/wikibase.css
rename to extensions/Wikibase/view/resources/wikibase/wikibase.less
diff --git a/package.json b/package.json
index 465d91b..0724a32 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,8 @@
     "test": "grunt test"
   },
   "devDependencies": {
-    "grunt": "1.0.1",
+    "grunt": "0.4.5",
+    "grunt-cli": "0.1.13",
     "grunt-contrib-clean": "0.7.0",
     "grunt-contrib-jshint": "0.12.0",
     "grunt-exec": "0.4.6",
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 8dbd8a9..41065a9 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -122,12 +122,12 @@
         "source": {
             "type": "git",
             "url": 
"https://github.com/wikimedia/mediawiki-extensions-ArticlePlaceholder.git";,
-            "reference": "f1e8a1a35716239f9a78eebca09f1135935c4f17"
+            "reference": "9fde45a4bb0c062e9004c38a34f99f1c8dcab012"
         },
         "dist": {
             "type": "zip",
-            "url": 
"https://api.github.com/repos/wikimedia/mediawiki-extensions-ArticlePlaceholder/zipball/f1e8a1a35716239f9a78eebca09f1135935c4f17";,
-            "reference": "f1e8a1a35716239f9a78eebca09f1135935c4f17",
+            "url": 
"https://api.github.com/repos/wikimedia/mediawiki-extensions-ArticlePlaceholder/zipball/9fde45a4bb0c062e9004c38a34f99f1c8dcab012";,
+            "reference": "9fde45a4bb0c062e9004c38a34f99f1c8dcab012",
             "shasum": ""
         },
         "require": {
@@ -138,7 +138,7 @@
             "jakub-onderka/php-parallel-lint": "0.9.2",
             "wikibase/wikibase-codesniffer": "^0.1.0"
         },
-        "time": "2017-07-31 10:42:11",
+        "time": "2017-08-01 20:36:04",
         "type": "mediawiki-extension",
         "installation-source": "dist",
         "autoload": {
@@ -1380,12 +1380,12 @@
         "source": {
             "type": "git",
             "url": 
"https://github.com/wikimedia/mediawiki-extensions-Wikibase.git";,
-            "reference": "aa7c10c4531cca80af5f81b817fd5b578e94942e"
+            "reference": "4331d2cd0e171070329f5af6a465bd582d5fab2c"
         },
         "dist": {
             "type": "zip",
-            "url": 
"https://api.github.com/repos/wikimedia/mediawiki-extensions-Wikibase/zipball/aa7c10c4531cca80af5f81b817fd5b578e94942e";,
-            "reference": "aa7c10c4531cca80af5f81b817fd5b578e94942e",
+            "url": 
"https://api.github.com/repos/wikimedia/mediawiki-extensions-Wikibase/zipball/4331d2cd0e171070329f5af6a465bd582d5fab2c";,
+            "reference": "4331d2cd0e171070329f5af6a465bd582d5fab2c",
             "shasum": ""
         },
         "require": {
@@ -1418,7 +1418,7 @@
             "jakub-onderka/php-parallel-lint": ">=0.3 <0.10",
             "wikibase/wikibase-codesniffer": "^0.1.0"
         },
-        "time": "2017-08-01 08:03:31",
+        "time": "2017-08-02 08:09:10",
         "type": "mediawiki-extension",
         "installation-source": "dist",
         "autoload": {
@@ -1638,7 +1638,7 @@
         "source": {
             "type": "git",
             "url": 
"https://gerrit.wikimedia.org/r/mediawiki/extensions/PropertySuggester";,
-            "reference": "ee4c46b4a335c87855e80147b2bc85766ae15b20"
+            "reference": "283a7070fd970cfe991f9b16f47b10cb04d1c4ce"
         },
         "require": {
             "php": ">=5.5.9",
@@ -1653,7 +1653,7 @@
             "wikibase/wikibase-codesniffer": "^0.1.0",
             "wikimedia/testing-access-wrapper": "~1.0"
         },
-        "time": "2017-05-29 17:15:29",
+        "time": "2017-08-01 08:45:42",
         "type": "mediawiki-extension",
         "installation-source": "source",
         "autoload": {
@@ -1699,7 +1699,7 @@
         "source": {
             "type": "git",
             "url": 
"https://gerrit.wikimedia.org/r/mediawiki/extensions/WikibaseQuality";,
-            "reference": "22aafec778ad7605863090758753c21e79ed44df"
+            "reference": "9f25f6f316e962aa4117e0922e0c041eab895b79"
         },
         "require": {
             "php": ">=5.5.9",
@@ -1719,7 +1719,7 @@
             "wikibase/data-model-serialization": ">=0.1 <3.0",
             "wikibase/wikibase-codesniffer": "^0.1.0"
         },
-        "time": "2017-07-28 20:48:10",
+        "time": "2017-08-01 11:18:15",
         "type": "mediawiki-extension",
         "installation-source": "source",
         "autoload": {
@@ -1768,7 +1768,7 @@
         "source": {
             "type": "git",
             "url": 
"https://gerrit.wikimedia.org/r/mediawiki/extensions/WikibaseQualityConstraints";,
-            "reference": "8614148918fc44218c82182610aff43fdd07f081"
+            "reference": "8c90ed7f4bc35d3b36b3823150404b8b41abfda8"
         },
         "require": {
             "php": ">=5.5.9",
@@ -1784,7 +1784,7 @@
             "satooshi/php-coveralls": "master-dev",
             "wikibase/wikibase-codesniffer": "^0.1.0"
         },
-        "time": "2017-07-31 21:06:39",
+        "time": "2017-08-02 09:14:11",
         "type": "mediawiki-extension",
         "installation-source": "source",
         "autoload": {

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I8c81465b8105cd632c37512b0a134ad0a8e4f44a
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Wikidata
Gerrit-Branch: master
Gerrit-Owner: WikidataBuilder <[email protected]>
Gerrit-Reviewer: Aude <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

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

Reply via email to