summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/PropertyChangePropagationNotifier.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/PropertyChangePropagationNotifier.php')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyChangePropagationNotifier.php252
1 files changed, 252 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyChangePropagationNotifier.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyChangePropagationNotifier.php
new file mode 100644
index 00000000..a0834810
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyChangePropagationNotifier.php
@@ -0,0 +1,252 @@
+<?php
+
+namespace SMW;
+
+use SMW\MediaWiki\Jobs\ChangePropagationDispatchJob;
+use SMWDataItem;
+use SMWDIBlob as DIBlob;
+
+/**
+ * Before a new set of data (type, constraints etc.) is stored about a property
+ * the class tries to compare old and new specifications (values about that property)
+ * and notifies a dispatcher about a change.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class PropertyChangePropagationNotifier {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var SerializerFactory
+ */
+ private $serializerFactory;
+
+ /**
+ * @var array
+ */
+ private $propertyList = [];
+
+ /**
+ * @var boolean
+ */
+ private $hasDiff = false;
+
+ /**
+ * @var boolean
+ */
+ private $isTypePropagation = false;
+
+ /**
+ * @var boolean
+ */
+ private $isCommandLineMode = false;
+
+ /**
+ * @since 1.9
+ *
+ * @param Store $store
+ * @param SerializerFactory $serializerFactory
+ */
+ public function __construct( Store $store, SerializerFactory $serializerFactory ) {
+ $this->store = $store;
+ $this->serializerFactory = $serializerFactory;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $propertyList
+ */
+ public function setPropertyList( array $propertyList ) {
+ $this->propertyList = $propertyList;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgCommandLineMode
+ * Indicates whether MW is running in command-line mode.
+ *
+ * @since 3.0
+ *
+ * @param boolean $isCommandLineMode
+ */
+ public function isCommandLineMode( $isCommandLineMode ) {
+ $this->isCommandLineMode = $isCommandLineMode;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function hasDiff() {
+ return $this->hasDiff;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIWikiPage $subject
+ */
+ public function notify( DIWikiPage $subject ) {
+
+ $namespace = $subject->getNamespace();
+
+ if ( !$this->hasDiff() || ( $namespace !== SMW_NS_PROPERTY && $namespace !== NS_CATEGORY ) ) {
+ return false;
+ }
+
+ $params = [];
+
+ if ( $this->isTypePropagation ) {
+ $params['isTypePropagation'] = true;
+ }
+
+ return ChangePropagationDispatchJob::planAsJob( $subject, $params );
+ }
+
+ /**
+ * Compare and detect differences between the invoked semantic data
+ * and the current stored data
+ *
+ * @note Compare on extra properties from `smwgChangePropagationWatchlist`
+ * (e.g '_PLIST') to find a possible specification change
+ *
+ * @since 1.9
+ */
+ public function checkAndNotify( SemanticData &$semanticData ) {
+
+ $namespace = $semanticData->getSubject()->getNamespace();
+
+ if ( $namespace !== SMW_NS_PROPERTY && $namespace !== NS_CATEGORY ) {
+ return;
+ }
+
+ $this->hasDiff = false;
+
+ // Check the type first
+ $propertyList = array_merge(
+ [
+ '_TYPE',
+ '_CONV',
+ '_UNIT',
+ '_REDI'
+ ],
+ $this->propertyList
+ );
+
+ foreach ( $propertyList as $key ) {
+
+ // No need to keep comparing once a diff has been
+ // detected
+ if ( $this->hasDiff() ) {
+ break;
+ }
+
+ $this->doCompare( $semanticData, $key );
+ }
+
+ $this->doNotifyAndPostpone( $semanticData );
+ }
+
+ private function doCompare( $semanticData, $key ) {
+
+ $property = new DIProperty( $key );
+
+ $newValues = $semanticData->getPropertyValues( $property );
+
+ $oldValues = $this->store->getPropertyValues(
+ $semanticData->getSubject(),
+ $property
+ );
+
+ $this->setDiff( !$this->isEqual( $oldValues, $newValues ), $key );
+ }
+
+ private function setDiff( $hasDiff = true, $key ) {
+
+ if ( !$hasDiff || $this->hasDiff ) {
+ return;
+ }
+
+ $this->hasDiff = true;
+ $this->isTypePropagation = $key === '_TYPE';
+ }
+
+ /**
+ * Helper function that compares two arrays of data values to check whether
+ * they contain the same content. Returns true if the two arrays contain the
+ * same data values (irrespective of their order), false otherwise.
+ *
+ * @param SMWDataItem[] $oldDataValue
+ * @param SMWDataItem[] $newDataValue
+ *
+ * @return boolean
+ */
+ private function isEqual( array $oldDataValue, array $newDataValue ) {
+
+ // The hashes of all values of both arrays are taken, then sorted
+ // and finally concatenated, thus creating one long hash out of each
+ // of the data value arrays. These are compared.
+ $values = [];
+ foreach ( $oldDataValue as $v ) {
+ $values[] = $v->getHash();
+ }
+
+ sort( $values );
+ $oldDataValueHash = implode( '___', $values );
+
+ $values = [];
+ foreach ( $newDataValue as $v ) {
+ $values[] = $v->getHash();
+ }
+
+ sort( $values );
+ $newDataValueHash = implode( '___', $values );
+
+ return $oldDataValueHash == $newDataValueHash;
+ }
+
+ private function doNotifyAndPostpone( SemanticData &$semanticData ) {
+
+ if ( !$this->hasDiff() ) {
+ return;
+ }
+
+ $this->notify( $semanticData->getSubject() );
+
+ // If executed from the commandLine (cronJob etc.), do not
+ // suspend the update
+ if ( $this->isCommandLineMode === true ) {
+ return;
+ }
+
+ $previous = $this->store->getSemanticData(
+ $semanticData->getSubject()
+ );
+
+ $semanticDataSerializer = $this->serializerFactory->newSemanticDataSerializer();
+
+ $new = $semanticDataSerializer->serialize(
+ $semanticData
+ );
+
+ // Encode and store the new version of the SemanticData and suspend
+ // the update until ChangePropagationDispatchJob was able to select
+ // all connected entities
+ $previous->addPropertyObjectValue(
+ new DIProperty( DIProperty::TYPE_CHANGE_PROP ),
+ new DIBlob( json_encode( $new ) )
+ );
+
+ $semanticData = $previous;
+ }
+
+}