Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea/
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# SemanticDummyEditor
The SemanticDummyEditor extension for Semantic MediaWiki updates semantic property values that are dependent on another page's property values. See https://www.mediawiki.org/wiki/Extension:SemanticDummyEditor for more details.
The SemanticDummyEditor extension for Semantic MediaWiki updates semantic property values that are dependent on another page's property values.

See https://www.mediawiki.org/wiki/Extension:SemanticDummyEditor for more details.
152 changes: 99 additions & 53 deletions SemanticDummyEditor.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,14 @@
die( "ERROR: Semantic MediaWiki must be installed for Semantic Dummy Editor to run!" );
}

define( 'SDE_VERSION', '1.0.0' );
define( 'SDE_VERSION', '1.1.0' );

$wgExtensionCredits[defined( 'SEMANTIC_EXTENSION_TYPE' ) ? 'semantic' : 'other'][] = array(
'name' => 'SemanticDummyEditor',
'author' => array( '[https://www.mediawiki.org/wiki/User:Rcdeboer Remco C. de Boer]' ),
'author' => array(
'[https://www.mediawiki.org/wiki/User:Rcdeboer Remco C. de Boer]',
'[https://www.mediawiki.org/wiki/User:Fannon Simon Heimler]',
),
'url' => 'http://www.mediawiki.org/wiki/Extension:SemanticDummyEditor',
'description' => 'Monitors changes in semantic dependencies and propagates them through null edits on the dependent pages.',
'version' => SDE_VERSION,
Expand All @@ -54,58 +57,69 @@
$wgJobClasses[ 'DummyEditJob' ] = 'DummyEditJob';

$wgSDEUseJobQueue = false;
$wgSDERecursive = false;
$wgSDERelations = array();

function setupDummyEditor() {
global $wgHooks;

$wgHooks['ArticleSaveComplete'][] = 'sdeSaveHook';
$wgHooks['ArticleUndelete'][] = 'sdeUndeleteHook';
$wgHooks['SMW::SQLStore::AfterDataUpdateComplete'][] = 'SemanticDummyEditor::onAfterDataUpdateComplete';
}

function sdeSaveHook( &$article, &$user, $text, $summary, $minoredit, $watchthis, $sectionanchor, &$flags, $revision, &$status, $baseRevId ) {
$nullEdit = false;
if( $revision == null ) {
wfDebugLog( 'SemanticDummyEditor', "SemanticDummyEditor::sdeSaveHook detected null edit for " . $article->getTitle() );
$nullEdit = true;
}
SemanticDummyEditor::articleSavedComplete( $article->getTitle(), $nullEdit );
class SemanticDummyEditor {

return true; // always return true, in order not to stop MW's hook processing!
}
function sdeUndeleteHook( &$title, $create ) {
SemanticDummyEditor::articleSavedComplete( $title );
return true; // always return true, in order not to stop MW's hook processing!
}
public static function onAfterDataUpdateComplete( SMWStore $store, SMWSemanticData $newData, $compositePropertyTableDiffIterator ) {

global $wgSDERelations;
global $wgSDERecursive;

class SemanticDummyEditor {
$subject = $newData->getSubject();
$title = Title::makeTitle( $subject->getNamespace(), $subject->getDBkey() );
$hasSDERelations = array();
$properties = $newData->getProperties();
$diffTable = $compositePropertyTableDiffIterator->getOrderedDiffByTable();

/**
* Finds all pages dependent on a particular page, by examining the relations from $wgSDERelations.
* @param unknown_type $title the title of the page.
*/
private static function findDependencies( $title) {
global $wgSDERelations;
wfDebugLog('SemanticDummyEditor', "SDE::onAfterDataUpdateComplete: ---> " . $title);

// find all dependency relations on this page
wfDebugLog('SemanticDummyEditor', "SemanticDummyEditor::findDependencies for $title");
$dependencies = array();
foreach($wgSDERelations as $relation) {
wfDebugLog('SemanticDummyEditor', "SemanticDummyEditor::findDependencies for relation $relation");
$dependencies = array_merge( $dependencies, SemanticDummyEditor::dependsOn($title, $relation) );

// FIRST CHECK: Does the page data contain at least one of the $wgSDERelations ?

foreach($wgSDERelations as $relation) {

$relationSearchString = (string) $relation;

$relationSearchString = str_replace(' ', '_', $relationSearchString);
$relationSearchString = ltrim($relationSearchString, '-');
if (isset($properties[$relationSearchString])) {
$hasSDERelations[] = $relation;
}
}

if (count($hasSDERelations) === 0) {
wfDebugLog('SemanticDummyEditor', "SDE::onAfterDataUpdateComplete: No SDE relations found");
return true;
} else {
wfDebugLog('SemanticDummyEditor', "SDE::onAfterDataUpdateComplete: Found SDE relations: " . join(", ", $hasSDERelations));
}


// SECOND CHECK: Have there been actual changes in the data? (Ignore internal SMW data!)
// Only count property changes in "smw_di_wikipage" and not SMWs internal properties "smw_fpt_mdat"
// TODO: Introduce an explicit list of Semantic Properties to watch ?

if (!isset($diffTable['smw_di_wikipage'])) {
wfDebugLog('SemanticDummyEditor', "SDE::onAfterDataUpdateComplete: No semantic data changes detected");
return true;
} else {
wfDebugLog('SemanticDummyEditor', "SDE::onAfterDataUpdateComplete: Data changes detected: " . count($diffTable['smw_di_wikipage']));
}
return $dependencies;
}

public static function articleSavedComplete( $title, $nullEdit = false ) {
wfDebugLog('SemanticDummyEditor', "SemanticDummyEditor::articleSavedComplete article saved complete: $title ");

if( !$nullEdit ) { // only actual edits trigger null edits on all dependencies.
$dependencies = SemanticDummyEditor::findDependencies( $title, $hasSDERelations );

$dependencies = SemanticDummyEditor::findDependencies( $title );
if ($wgSDERecursive) {
for( $i = 0; $i < count($dependencies); $i++) {
$dependency = $dependencies[$i];

wfDebugLog( 'SemanticDummyEditor', "Testing dependency $dependency for additional dependencies.") ;
$additionalDependencies = SemanticDummyEditor::findDependencies( Title::newFromText( $dependency ) );
foreach( $additionalDependencies as $additionalDependency ) {
Expand All @@ -118,30 +132,59 @@ public static function articleSavedComplete( $title, $nullEdit = false ) {
}
}
}
}

wfDebugLog( 'SemanticDummyEditor', "SemanticDummyEditor::articleSavedComplete dependencies: " . implode( ',', $dependencies ) );
wfDebugLog( 'SemanticDummyEditor', "SDE::onAfterDataUpdateComplete dependencies: " . implode( '; ', $dependencies ) );

// refresh the dependent pages, since their dependent values will have changed
// refresh has to be done through a dummy edit
foreach( $dependencies as $changed ) {
$changedTitle = Title::newFromText( $changed );
SemanticDummyEditor::dummyEdit( $changedTitle, $title );
}
// refresh the dependent pages, since their dependent values will have changed
// refresh has to be done through a dummy edit
foreach( $dependencies as $changed ) {
$changedTitle = Title::newFromText( $changed );
SemanticDummyEditor::dummyEdit( $changedTitle );
}


return true;
}

/**
* Finds all pages dependent on a particular page, by examining the relations from $wgSDERelations.
* @param $title the title of the page.
* @param $hasSDERelations array of detected SDE relations
*
* @return array
*/
private static function findDependencies( $title, $hasSDERelations) {

if (!$hasSDERelations) {
global $wgSDERelations;
$hasSDERelations = $wgSDERelations;
}

// find all dependency relations on this page
$dependencies = array();
foreach($hasSDERelations as $relation) {
wfDebugLog('SemanticDummyEditor', "SDE::findDependencies for page " . $title->getPrefixedText() . " through relation $relation");
$dependencies = array_merge( $dependencies, SemanticDummyEditor::dependsOn($title, $relation) );
}

return $dependencies;
}

public static function dummyEdit( $title, $changedTitle ) {
/**
* Save a null revision in the page's history to propagate the update
*
* @param Title $title
*/
public static function dummyEdit( $title ) {
global $wgSDEUseJobQueue;
wfDebugLog( 'SemanticDummyEditor', "SemanticDummyEditor::dummyEdit performing dummy edit on $title" );
$dbw = wfGetDB( DB_MASTER );
# Save a null revision in the page's history to propagate the update

if( $wgSDEUseJobQueue ) {
wfDebugLog( 'SemanticDummyEditor', "SemanticDummyEditor::dummyEdit adding job to queue" );
wfDebugLog( 'SemanticDummyEditor', "SDE::dummyEdit adding job to queue: $title");
$job = new DummyEditJob( $title );
$job->insert();
} else {
wfDebugLog( 'SemanticDummyEditor', "SemanticDummyEditor::dummyEdit bypassing jobqueue" );
wfDebugLog( 'SemanticDummyEditor', "SDE::dummyEdit bypassing jobqueue: $title");
$page = WikiPage::newFromID( $title->getArticleId() );
if ( $page ) { // prevent NPE when page not found
$text = $page->getText( Revision::RAW );
Expand All @@ -157,21 +200,24 @@ public static function dummyEdit( $title, $changedTitle ) {
*/
private static function dependsOn( $title, $relation ) {

$titleText = $title->getPrefixedText();

$store = smwfGetStore();

$params = array();
$params[ 'limit' ] = 10000; // $smwgQMaxLimit
$processedParams = SMWQueryProcessor::getProcessedParams( $params );
$query = SMWQueryProcessor::createQuery( "[[$relation::$title]]", $processedParams, SMWQueryProcessor::SPECIAL_PAGE );
$query = SMWQueryProcessor::createQuery( "[[$relation::$titleText]]", $processedParams, SMWQueryProcessor::SPECIAL_PAGE );
$result = $store->getQueryResult( $query ); // SMWQueryResult
$pages = $result->getResults(); // array of SMWWikiPageValues

$relatedElements = array();
foreach($pages as $page) {
$relatedElements[] = $page->getTitle()->getText();
wfDebugLog( 'SemanticDummyEditor', "SemanticDummyEditor::dependsOn " . $page->getTitle()->getText() );
$relatedElements[] = $page->getTitle()->getPrefixedText();
}

wfDebugLog( 'SemanticDummyEditor', "SDE::dependsOn: " . implode( '; ', $relatedElements ) );

return $relatedElements;
}

Expand Down