Skip to content

Commit

Permalink
Add smw-pageedit to wgRestrictionLevels, refs 2232 (#2249)
Browse files Browse the repository at this point in the history
  • Loading branch information
mwjames committed Feb 11, 2017
1 parent 3abc677 commit 80e892c
Show file tree
Hide file tree
Showing 20 changed files with 1,396 additions and 39 deletions.
3 changes: 3 additions & 0 deletions i18n/en.json
Expand Up @@ -505,8 +505,11 @@
"smw-property-req-violation-missing-fields": "Property \"$1\" is missing declaration details for the annotated \"$2\" type by failing to define the <code>Has fields</code> property.",
"smw-property-req-violation-missing-formatter-uri": "Property \"$1\" is missing declaration details for the annotated type by failing to define the <code>External formatter URI</code> property.",
"smw-property-req-violation-predefined-type": "Property \"$1\" as predefined property contains a \"$2\" type declaration that is incompatible with the default type of this property.",
"protect-level-smw-pageedit":"Allow only users with page edit permission (Semantic MediaWiki)",
"smw-edit-protection": "This page is [[Property:Is edit protected|protected]] to prevent accidental data modification and therefore can only be edited by users with the appropriate \"$1\" [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups right].",
"smw-edit-protection-disabled": "The edit protection has been disabled therefore \"$1\" cannot be used to protect entity pages from unauthorized editing.",
"smw-edit-protection-auto-update":"Semantic MediaWiki has updated the protection status according to the `Is edit protected` property.",
"smw-edit-protection-enabled":"Edit protection (Semantic MediaWiki)",
"smw-patternedit-protection": "This page is protected and can only be edited by users with the appropriate <code>smw-patternedit</code> [https://www.semantic-mediawiki.org/wiki/Help:Permissions permission].",
"smw-pa-property-predefined_edip": "\"$1\" is a predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] to indicate whether editing is protected or not.",
"smw-pa-property-predefined-long_edip": "While any user is qualified to add this property to a subject, only a user with a dedicated permission can edit or revoke the protection to an entity after it has been added."
Expand Down
5 changes: 5 additions & 0 deletions includes/Setup.php
Expand Up @@ -255,6 +255,11 @@ private function registerPermissions() {
if ( !isset( $this->globalVars['wgGroupPermissions']['smwadministrator']['smw-admin'] ) ) {
$this->globalVars['wgGroupPermissions']['smwadministrator']['smw-admin'] = true;
}

// Add an additional protection level restricting edit/move/etc
if ( ( $editProtectionRight = $this->applicationFactory->getSettings()->get( 'smwgEditProtectionRight' ) ) !== false ) {
$this->globalVars['wgRestrictionLevels'][] = $editProtectionRight;
}
}

/**
Expand Down
19 changes: 19 additions & 0 deletions includes/StoreUpdater.php
Expand Up @@ -131,6 +131,13 @@ private function doPerformUpdate() {

$this->addFinalAnnotations( $title, $wikiPage, $revision, $user );

// In case of a restricted update, only the protection update is required
// hence the process bails-out early to avoid unnecessary DB connections
// or updates
if ( $this->doUpdateEditProtection( $wikiPage, $user ) === true ) {
return true;
}

$this->inspectPropertySpecification();
$this->doRealUpdate();
}
Expand Down Expand Up @@ -163,6 +170,18 @@ private function addFinalAnnotations( Title $title, WikiPage $wikiPage, $revisio
$propertyAnnotator->addAnnotation();
}

private function doUpdateEditProtection( $wikiPage, $user ) {

$editProtectionUpdater = $this->applicationFactory->create( 'EditProtectionUpdater',
$wikiPage,
$user
);

$editProtectionUpdater->doUpdateFrom( $this->semanticData );

return $editProtectionUpdater->isRestrictedUpdate();
}

/**
* @note Comparison must happen *before* the storage update;
* even finding uses of a property fails after its type changed.
Expand Down
145 changes: 145 additions & 0 deletions src/MediaWiki/Hooks/ArticleProtectComplete.php
@@ -0,0 +1,145 @@
<?php

namespace SMW\MediaWiki\Hooks;

use SMW\MediaWiki\EditInfoProvider;
use SMW\Message;
use Title;
use SMW\ApplicationFactory;
use SMW\PropertyAnnotators\EditProtectedPropertyAnnotator;

/**
* Occurs after the protect article request has been processed
*
* @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleProtectComplete
*
* @license GNU GPL v2+
* @since 2.5
*
* @author mwjames
*/
class ArticleProtectComplete extends HookHandler {

/**
* Whether the update should be restricted or not. Which means that when
* no other change is required then categorize the update as restricted
* to avoid unnecessary cascading updates.
*/
const RESTRICTED_UPDATE = 'articleprotectcomplete.restricted.update';

/**
* @var Title
*/
private $title;

/**
* @var EditInfoProvider
*/
private $editInfoProvider;

/**
* @var boolean|string
*/
private $editProtectionRight = false;

/**
* @since 2.5
*
* @param Title $title
* @param EditInfoProvider $editInfoProvider
*/
public function __construct( Title $title, EditInfoProvider $editInfoProvider ) {
parent::__construct();
$this->title = $title;
$this->editInfoProvider = $editInfoProvider;
}

/**
* @since 2.5
*
* @param string|boolean $editProtectionRight
*/
public function setEditProtectionRight( $editProtectionRight ) {
$this->editProtectionRight = $editProtectionRight;
}

/**
* @since 2.5
*
* @param array $protections
* @param string $reason
*/
public function process( $protections, $reason ) {

if ( Message::get( 'smw-edit-protection-auto-update' ) === $reason ) {
return $this->log( __METHOD__ . ' No changes required, invoked by own process!' );
}

$this->editInfoProvider->fetchEditInfo();

$output = $this->editInfoProvider->getOutput();

if ( $output === null ) {
return $this->log( __METHOD__ . ' Missing ParserOutput!' );
}

$parserData = ApplicationFactory::getInstance()->newParserData(
$this->title,
$output
);

$this->doPrepareData( $protections, $parserData );
$parserData->setOrigin( 'ArticleProtectComplete' );

$parserData->updateStore(
true
);
}

private function doPrepareData( $protections, $parserData ) {

$isRestrictedUpdate = true;
$isAnnotationBySystem = false;

$property = $this->dataItemFactory->newDIProperty( '_EDIP' );

$dataItems = $parserData->getSemanticData()->getPropertyValues( $property );
$dataItem = end( $dataItems );

if ( $dataItem ) {
$isAnnotationBySystem = $dataItem->getOption( EditProtectedPropertyAnnotator::SYSTEM_ANNOTATION );
}

// No _EDIP annotation but a selected protection matches the
// `EditProtectionRight` setting
if ( !$dataItem && isset( $protections['edit'] ) && $protections['edit'] === $this->editProtectionRight ) {
$this->log( 'ArticleProtectComplete addProperty `Is edit protected`' );

$isRestrictedUpdate = false;
$parserData->getSemanticData()->addPropertyObjectValue(
$property,
$this->dataItemFactory->newDIBoolean( true )
);
}

// _EDIP exists and was set by the EditProtectedPropertyAnnotator (which
// means that is has been set by the system and is not a "human" added
// annotation) but since the selected protection doesn't match the
// `EditProtectionRight` setting, remove the annotation
if ( $dataItem && $isAnnotationBySystem && isset( $protections['edit'] ) && $protections['edit'] !== $this->editProtectionRight ) {
$this->log( 'ArticleProtectComplete removeProperty `Is edit protected`' );

$isRestrictedUpdate = false;
$parserData->getSemanticData()->removePropertyObjectValue(
$property,
$this->dataItemFactory->newDIBoolean( true )
);
}

$parserData->getSemanticData()->setOption(
self::RESTRICTED_UPDATE,
$isRestrictedUpdate
);
}

}
31 changes: 31 additions & 0 deletions src/MediaWiki/Hooks/HookRegistry.php
Expand Up @@ -242,6 +242,37 @@ private function addCallbackHandlers( $basePath, $globalVars ) {
return $newRevisionFromEditComplete->process();
};

/**
* Hook: Occurs after the protect article request has been processed
*
* @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleProtectComplete
*/
$this->handlers['ArticleProtectComplete'] = function ( &$wikiPage, &$user, $protections, $reason ) use ( $applicationFactory ) {

$editInfoProvider = new \SMW\MediaWiki\EditInfoProvider(
$wikiPage,
$wikiPage->getRevision(),
$user
);

$articleProtectComplete = new ArticleProtectComplete(
$wikiPage->getTitle(),
$editInfoProvider
);

$articleProtectComplete->setEditProtectionRight(
$applicationFactory->getSettings()->get( 'smwgEditProtectionRight' )
);

$articleProtectComplete->setLogger(
$applicationFactory->getMediaWikiLogger()
);

$articleProtectComplete->process( $protections, $reason );

return true;
};

/**
* Hook: TitleMoveComplete occurs whenever a request to move an article
* is completed
Expand Down
11 changes: 11 additions & 0 deletions src/MediaWiki/Hooks/ParserAfterTidy.php
Expand Up @@ -60,6 +60,7 @@ protected function canPerformUpdate() {
// @see ParserData::setSemanticDataStateToParserOutputProperty
if ( $this->parser->getOutput()->getProperty( 'smw-semanticdata-status' ) ||
$this->parser->getOutput()->getProperty( 'displaytitle' ) ||
$this->parser->getTitle()->isProtected( 'edit' ) ||
$this->parser->getOutput()->getCategoryLinks() ||
$this->parser->getDefaultSort() ) {
return true;
Expand Down Expand Up @@ -104,6 +105,16 @@ private function updateAnnotationsForAfterParse( $propertyAnnotatorFactory, $sem
$propertyAnnotator
);

$propertyAnnotator = $propertyAnnotatorFactory->newEditProtectedPropertyAnnotator(
$propertyAnnotator,
$this->parser->getTitle()
);

// Special case! belongs to the EditProtectedPropertyAnnotator instance
$propertyAnnotator->addTopIndicatorTo(
$this->parser->getOutput()
);

$propertyAnnotator = $propertyAnnotatorFactory->newDisplayTitlePropertyAnnotator(
$propertyAnnotator,
$this->parser->getOutput()->getProperty( 'displaytitle' ),
Expand Down
4 changes: 2 additions & 2 deletions src/PermissionPthValidator.php
Expand Up @@ -4,7 +4,7 @@

use Title;
use User;
use SMW\MediaWiki\PageProtectionManager;
use SMW\Protection\EditProtectionValidator;

/**
* @license GNU GPL v2+
Expand Down Expand Up @@ -68,7 +68,7 @@ public function checkQuickPermissionOn( Title &$title, User $user, $action, &$er
*/
public function checkUserPermissionOn( Title &$title, User $user, $action, &$errors ) {

if ( $action !== 'edit' && $action !== 'delete' && $action !== 'move' ) {
if ( $action !== 'edit' && $action !== 'delete' && $action !== 'move' && $action !== 'upload' ) {
return true;
}

Expand Down
24 changes: 24 additions & 0 deletions src/PropertyAnnotatorFactory.php
Expand Up @@ -10,7 +10,9 @@
use SMW\PropertyAnnotators\PredefinedPropertyAnnotator;
use SMW\PropertyAnnotators\RedirectPropertyAnnotator;
use SMW\PropertyAnnotators\SortKeyPropertyAnnotator;
use SMW\PropertyAnnotators\EditProtectedPropertyAnnotator;
use SMW\Store;
use Title;

/**
* @license GNU GPL v2+
Expand Down Expand Up @@ -68,6 +70,28 @@ public function newPredefinedPropertyAnnotator( PropertyAnnotator $propertyAnnot
return $predefinedPropertyAnnotator;
}

/**
* @since 2.5
*
* @param SemanticData $semanticData
* @param Title $title
*
* @return EditProtectedPropertyAnnotator
*/
public function newEditProtectedPropertyAnnotator( PropertyAnnotator $propertyAnnotator, Title $title ) {

$editProtectedPropertyAnnotator = new EditProtectedPropertyAnnotator(
$propertyAnnotator,
$title
);

$editProtectedPropertyAnnotator->setEditProtectionRight(
ApplicationFactory::getInstance()->getSettings()->get( 'smwgEditProtectionRight' )
);

return $editProtectedPropertyAnnotator;
}

/**
* @since 2.0
*
Expand Down

0 comments on commit 80e892c

Please sign in to comment.