r93508 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r93507‎ | r93508 | r93509 >
Date:01:24, 30 July 2011
Author:jeroendedauw
Status:deferred
Tags:
Comment:
Tag for version 0.1.
Modified paths:
  • /tags/extensions/SemanticWatchlist/REL_0_1 (added) (history)

Diff [purge]

Index: tags/extensions/SemanticWatchlist/REL_0_1/SemanticWatchlist.i18n.alias.php
@@ -0,0 +1,36 @@
 2+<?php
 3+
 4+/**
 5+ * Aliases for the special pages of the Semantic Watchlist extension.
 6+ *
 7+ * @file SemanticWatchlist.i18n.alias.php
 8+ * @ingroup SemanticWatchlist
 9+ *
 10+ * @licence GNU GPL v3+
 11+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 12+ */
 13+
 14+$specialPageAliases = array();
 15+
 16+/** English (English) */
 17+$specialPageAliases['en'] = array(
 18+ 'SemanticWatchlist' => array( 'SemanticWatchlist' ),
 19+ 'WatchlistConditions' => array( 'WatchlistConditions' ),
 20+);
 21+
 22+/** Arabic (العربية) */
 23+$specialPageAliases['ar'] = array(
 24+ 'SemanticWatchlist' => array( 'قائمة_مراقبة_سيمانتيك' ),
 25+ 'WatchlistConditions' => array( 'شروط_قائمة_المراقبة' ),
 26+);
 27+
 28+/** Dutch (Nederlands) */
 29+$specialPageAliases['nl'] = array(
 30+ 'SemanticWatchlist' => array( 'SemantischeVolglijst' ),
 31+ 'WatchlistConditions' => array( 'Volglijstcondities' ),
 32+);
 33+
 34+/**
 35+ * For backwards compatibility with MediaWiki 1.15 and earlier.
 36+ */
 37+$aliases =& $specialPageAliases;
\ No newline at end of file
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/SemanticWatchlist.i18n.alias.php
___________________________________________________________________
Added: svn:eol-style
138 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/specials/ext.swl.watchlist.js
@@ -0,0 +1,13 @@
 2+/**
 3+ * JavasSript for Special:SemanticWatchlist in the Semantic Watchlist extension.
 4+ * @see http://www.mediawiki.org/wiki/Extension:Semantic_Watchlist
 5+ *
 6+ * @licence GNU GPL v3 or later
 7+ * @author Jeroen De Dauw <jeroendedauw at gmail dot com>
 8+ */
 9+
 10+(function($) { $( document ).ready( function() {
 11+
 12+
 13+
 14+} ); })(jQuery);
\ No newline at end of file
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/specials/ext.swl.watchlist.js
___________________________________________________________________
Added: svn:eol-style
115 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/specials/jquery.watchlistcondition.js
@@ -0,0 +1,238 @@
 2+/**
 3+ * JavasSript for Special:WatchlistConditions in the Semantic Watchlist extension.
 4+ * @see http://www.mediawiki.org/wiki/Extension:Semantic_Watchlist
 5+ *
 6+ * @licence GNU GPL v3 or later
 7+ * @author Jeroen De Dauw <jeroendedauw at gmail dot com>
 8+ */
 9+
 10+(function( $ ){ $.fn.watchlistcondition = function( group, options ) {
 11+
 12+ var self = this;
 13+ this.group = group;
 14+
 15+ this.buildHtml = function() {
 16+ this.html( $( '<legend />' ).text( group.name ) );
 17+
 18+ var table = $( '<table />' ).attr( { 'class': 'swltable' } );
 19+
 20+ propTd = $( '<td />' ).attr( {
 21+ 'rowspan': 2
 22+ } );
 23+
 24+ this.propsDiv = $( '<div />' );
 25+
 26+ var addPropInput = $( '<input />' ).attr( {
 27+ 'type': 'text',
 28+ 'value': '',
 29+ 'size': 30,
 30+ 'class': 'swl-group-add-prop'
 31+ } );
 32+
 33+ var addButton = $( '<input />' ).attr( {
 34+ 'type': 'button',
 35+ 'value': mediaWiki.msg( 'swl-group-add-property' )
 36+ } ).click( function() {
 37+ var propName = addPropInput.val();
 38+
 39+ if ( propName.trim() != '' ) {
 40+ self.addPropertyDiv( propName );
 41+ addPropInput.val( '' );
 42+ addPropInput.focus();
 43+ }
 44+ } );
 45+
 46+ addPropInput.keypress( function( event ) {
 47+ if ( event.which == '13' ) {
 48+ addButton.click();
 49+ }
 50+ } );
 51+
 52+ propTd.html( mediaWiki.msg( 'swl-group-properties' ) )
 53+ .append( this.propsDiv )
 54+ .append( $( '<div />' ).html( addPropInput ).append( '&nbsp;' ).append( addButton ) );
 55+
 56+ for ( i in group.properties ) {
 57+ this.addPropertyDiv( group.properties[i] );
 58+ }
 59+
 60+ this.nameInput = $( '<input />' ).attr( {
 61+ 'type': 'text',
 62+ 'value': group.name,
 63+ 'size': 30
 64+ } );
 65+
 66+ this.nameInput.keyup( function() {
 67+ self.find( 'legend' ).text( $( this ).val() );
 68+ } );
 69+
 70+ var nameTd = $( '<td />' ).html( $( '<p />' ).text( mediaWiki.msg( 'swl-group-name' ) + ' ' ).append( this.nameInput ) );
 71+ table.append( $( '<tr />' ).html( nameTd ).append( propTd ) );
 72+
 73+ var conditionValue, conditionType;
 74+
 75+ switch ( true ) {
 76+ case group.categories.length > 0:
 77+ conditionValue = group.categories[0];
 78+ conditionType = 'category';
 79+ break;
 80+ case group.namespaces.length > 0:
 81+ conditionValue = group.namespaces[0];
 82+ conditionType = 'namespace';
 83+ break;
 84+ case group.concepts.length > 0:
 85+ conditionValue = group.concepts[0];
 86+ conditionType = 'concept';
 87+ break;
 88+ }
 89+
 90+ this.conditionTypeInput = $( '<select />' );
 91+ var conditionTypes = [ 'category', 'namespace', 'concept' ];
 92+ var conditionTypeGroups = [ 'categories', 'namespaces', 'concepts' ];
 93+
 94+ for ( i in conditionTypes ) {
 95+ var optionElement = $( '<option />' )
 96+ .text( mediaWiki.msg( 'swl-group-' + conditionTypes[i] ) )
 97+ .attr( { 'value': conditionTypes[i], 'type': conditionTypeGroups[i] } );
 98+
 99+ if ( conditionType == conditionTypes[i] ) {
 100+ optionElement.attr( 'selected', 'selected' );
 101+ }
 102+
 103+ this.conditionTypeInput.append( optionElement );
 104+ }
 105+
 106+ this.conditionNameInput = $( '<input />' ).attr( {
 107+ 'type': 'text',
 108+ 'value': conditionValue,
 109+ 'size': 30
 110+ } );
 111+ var conditionTd = $( '<td />' ).html(
 112+ $( '<p />' ).text( mediaWiki.msg( 'swl-group-page-selection' ) + ' ' )
 113+ .append( this.conditionTypeInput )
 114+ .append( '&nbsp;' )
 115+ .append( this.conditionNameInput )
 116+ );
 117+
 118+ table.append( $( '<tr />' ).html( conditionTd ) );
 119+
 120+ this.append( table );
 121+
 122+ this.append(
 123+ $( '<input />' ).attr( {
 124+ 'type': 'button',
 125+ 'value': mediaWiki.msg( 'swl-group-save' ),
 126+ 'class': 'swl-save'
 127+ } ).click( function() {
 128+ this.disabled = true;
 129+ var button = this;
 130+
 131+ self.doSave( function( success ) {
 132+ if ( success ) {
 133+ // TODO: indicate success?
 134+ }
 135+ else {
 136+ alert( 'Could not update the watchlist group.' );
 137+ }
 138+
 139+ button.disabled = false;
 140+ } );
 141+ } )
 142+ );
 143+
 144+ this.append( '&nbsp;' );
 145+
 146+ this.append(
 147+ $( '<input />' ).attr( {
 148+ 'type': 'button',
 149+ 'value': mediaWiki.msg( 'swl-group-delete' )
 150+ } ).click( function() {
 151+ if ( confirm( mediaWiki.msg( 'swl-group-confirmdelete', self.nameInput.val() ) ) ) {
 152+ this.disabled = true;
 153+ var button = this;
 154+
 155+ self.doDelete( function( success ) {
 156+ if ( success ) {
 157+ self.slideUp( 'fast', function() { self.remove(); } );
 158+ }
 159+ else {
 160+ alert( 'Could not delete the watchlist group.' );
 161+ button.disabled = false;
 162+ }
 163+ } );
 164+ }
 165+ } )
 166+ );
 167+ };
 168+
 169+ this.addPropertyDiv = function( property ) {
 170+ var propDiv = $( '<div />' ).attr( 'class', 'propid' );
 171+
 172+ var propInput = $( '<input />' ).attr( {
 173+ 'type': 'text',
 174+ 'value': property,
 175+ 'size': 30,
 176+ 'class': 'swl-group-prop'
 177+ } );
 178+
 179+ var removeButton = $( '<input />' ).attr( {
 180+ 'type': 'button',
 181+ 'value': mediaWiki.msg( 'swl-group-remove-property' )
 182+ } );
 183+
 184+ removeButton.click( function() {
 185+ propDiv.remove();
 186+ } );
 187+
 188+ this.propsDiv.append( propDiv.html( propInput ).append( '&nbsp;' ).append( removeButton ) );
 189+ };
 190+
 191+ this.getProperties = function() {
 192+ var props = [];
 193+
 194+ this.find( '.swl-group-prop' ).each( function( index, domElement ) {
 195+ props.push( $( domElement ).val() );
 196+ } );
 197+
 198+ return props;
 199+ };
 200+
 201+ this.doSave = function( callback ) {
 202+ var args = {
 203+ 'action': 'editswlgroup',
 204+ 'format': 'json',
 205+ 'id': this.group.id,
 206+ 'name': this.nameInput.val(),
 207+ 'properties': this.getProperties().join( '|' )
 208+ };
 209+
 210+ args[this.conditionTypeInput.find( 'option:selected' ).attr( 'type' )] = this.conditionNameInput.val();
 211+
 212+ $.getJSON(
 213+ wgScriptPath + '/api.php',
 214+ args,
 215+ function( data ) {
 216+ callback( data.success );
 217+ }
 218+ );
 219+ };
 220+
 221+ this.doDelete = function( callback ) {
 222+ $.getJSON(
 223+ wgScriptPath + '/api.php',
 224+ {
 225+ 'action': 'deleteswlgroup',
 226+ 'format': 'json',
 227+ 'ids': this.group.id
 228+ },
 229+ function( data ) {
 230+ callback( data.success );
 231+ }
 232+ );
 233+ };
 234+
 235+ this.buildHtml();
 236+
 237+ return this;
 238+
 239+}; })( jQuery );
\ No newline at end of file
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/specials/jquery.watchlistcondition.js
___________________________________________________________________
Added: svn:eol-style
1240 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/specials/SpecialWatchlistConditions.php
@@ -0,0 +1,158 @@
 2+<?php
 3+
 4+/**
 5+ * Interface to modify the semantic watchlist groups.
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file SpecialWatchlistConditions.php
 10+ * @ingroup SemanticWatchlist
 11+ *
 12+ * @licence GNU GPL v3 or later
 13+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 14+ */
 15+class SpecialWatchlistConditions extends SpecialPage {
 16+
 17+ /**
 18+ * Constructor.
 19+ *
 20+ * @since 0.1
 21+ */
 22+ public function __construct() {
 23+ parent::__construct( 'WatchlistConditions', 'semanticwatchgroups' );
 24+ }
 25+
 26+ /**
 27+ * @see SpecialPage::getDescription
 28+ *
 29+ * @since 0.1
 30+ */
 31+ public function getDescription() {
 32+ return wfMsg( 'special-' . strtolower( $this->getName() ) );
 33+ }
 34+
 35+ /**
 36+ * Sets headers - this should be called from the execute() method of all derived classes!
 37+ *
 38+ * @since 0.1
 39+ */
 40+ public function setHeaders() {
 41+ global $wgOut;
 42+ $wgOut->setArticleRelated( false );
 43+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 44+ $wgOut->setPageTitle( $this->getDescription() );
 45+ }
 46+
 47+ /**
 48+ * Main method.
 49+ *
 50+ * @since 0.1
 51+ *
 52+ * @param string $arg
 53+ */
 54+ public function execute( $arg ) {
 55+ global $wgOut, $wgUser, $wgRequest;
 56+
 57+ $this->setHeaders();
 58+ $this->outputHeader();
 59+
 60+ // If the user is authorized, display the page, if not, show an error.
 61+ if ( !$this->userCanExecute( $wgUser ) ) {
 62+ $this->displayRestrictionError();
 63+ return;
 64+ }
 65+
 66+ $groupsHtml = array();
 67+
 68+ foreach ( SWLGroups::getAll() as $group ) {
 69+ $groupsHtml[] = $this->getGroupHtml( $group );
 70+ }
 71+
 72+ $wgOut->addHTML(
 73+ '<div id="swl-groups">' .
 74+ implode( '', $groupsHtml ) .
 75+ '</div>'
 76+ );
 77+
 78+ $wgOut->addHTML( Html::rawElement(
 79+ 'fieldset',
 80+ array(
 81+
 82+ ),
 83+ Html::element(
 84+ 'legend',
 85+ array(),
 86+ wfMsg( 'swl-group-add-new-group' )
 87+ ) .
 88+ Html::element(
 89+ 'span',
 90+ array(),
 91+ wfMsg( 'swl-group-name' )
 92+ ) . '&nbsp;' .
 93+ Html::element(
 94+ 'input',
 95+ array(
 96+ 'type' => 'text',
 97+ 'value' => '',
 98+ 'id' => 'swl-add-group-name'
 99+ )
 100+ ) . '&nbsp;' .
 101+ Html::element(
 102+ 'input',
 103+ array(
 104+ 'type' => 'button',
 105+ 'value' => wfMsg( 'swl-group-add-group' ),
 106+ 'id' => 'swl-add-group-button'
 107+ )
 108+ )
 109+ ) );
 110+
 111+ $wgOut->addHTML( Html::element(
 112+ 'input',
 113+ array(
 114+ 'type' => 'button',
 115+ 'value' => wfMsg( 'swl-group-save-all' ),
 116+ 'id' => 'swl-save-all'
 117+ )
 118+ ) );
 119+
 120+ $wgOut->addModules( 'ext.swl.watchlistconditions' );
 121+ }
 122+
 123+ /**
 124+ * Creates and returns the HTML for a single watchlist group.
 125+ *
 126+ * @since 0.1
 127+ *
 128+ * @param SWLGroup $group
 129+ *
 130+ * @return string
 131+ */
 132+ protected function getGroupHtml( SWLGroup $group ) {
 133+ $namespaces = $group->getNamespaces();
 134+
 135+ foreach ( $namespaces as &$ns ) {
 136+ $ns = $ns == 0 ? 'Main' : MWNamespace::getCanonicalName( $ns );
 137+ }
 138+
 139+ return Html::rawElement(
 140+ 'fieldset',
 141+ array(
 142+ 'id' => 'swl_group_' . $group->getId(),
 143+ 'groupid' => $group->getId(),
 144+ 'class' => 'swl_group',
 145+ 'groupname' => $group->getName(),
 146+ 'categories' => implode( '|', $group->getCategories() ),
 147+ 'namespaces' => implode( '|', $namespaces ),
 148+ 'properties' => implode( '|', $group->getProperties() ),
 149+ 'concepts' => implode( '|', $group->getConcepts() ),
 150+ ),
 151+ Html::element(
 152+ 'legend',
 153+ array(),
 154+ $group->getName()
 155+ )
 156+ );
 157+ }
 158+
 159+}
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/specials/SpecialWatchlistConditions.php
___________________________________________________________________
Added: svn:eol-style
1160 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/specials/ext.swl.watchlistconditions.css
@@ -0,0 +1,30 @@
 2+@CHARSET "UTF-8";
 3+
 4+/**
 5+ * CSS for Special:WatchlistConditions in the Semantic Watchlist extension.
 6+ * @see http://www.mediawiki.org/wiki/Extension:Semantic_Watchlist
 7+ *
 8+ * @licence GNU GPL v3 or later
 9+ * @author Jeroen De Dauw <jeroendedauw at gmail dot com>
 10+ */
 11+
 12+fieldset .swl_group {
 13+ width: 100%;
 14+ border: 1px solid black;
 15+ min-height: 60px;
 16+}
 17+
 18+/*table .swltable {
 19+ width: 100%;
 20+ border: 1px solid black;
 21+}
 22+
 23+*/div .propid {
 24+ width: 100%;
 25+ padding-bottom: 2px;
 26+ /*border: 1px solid black;*/
 27+}
 28+
 29+.swl-group-prop {
 30+
 31+}
\ No newline at end of file
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/specials/ext.swl.watchlistconditions.css
___________________________________________________________________
Added: svn:eol-style
132 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/specials/ext.swl.watchlistconditions.js
@@ -0,0 +1,102 @@
 2+/**
 3+ * JavasSript for Special:WatchlistConditions in the Semantic Watchlist extension.
 4+ * @see http://www.mediawiki.org/wiki/Extension:Semantic_Watchlist
 5+ *
 6+ * @licence GNU GPL v3 or later
 7+ * @author Jeroen De Dauw <jeroendedauw at gmail dot com>
 8+ */
 9+
 10+(function($) { $( document ).ready( function() {
 11+
 12+ function getSplitAttrValue( element, attribute, separator ) {
 13+ if ( typeof element.attr( attribute ) == 'undefined'
 14+ || element.attr( attribute ) == '' ) {
 15+ return [];
 16+ }
 17+
 18+ return element.attr( attribute ).split( separator );
 19+ }
 20+
 21+ function initGroupElement( element ) {
 22+ element.watchlistcondition(
 23+ {
 24+ name: element.attr( 'groupname' ),
 25+ id: element.attr( 'groupid' ),
 26+ categories: getSplitAttrValue( element, 'categories', '|' ),
 27+ namespaces: getSplitAttrValue( element, 'namespaces', '|' ),
 28+ properties: getSplitAttrValue( element, 'properties', '|' ),
 29+ concepts: getSplitAttrValue( element, 'concepts', '|' )
 30+ },
 31+ {}
 32+ );
 33+ }
 34+
 35+ $( '.swl_group' ).each(function( index, domElement ) {
 36+ initGroupElement( $( domElement ) );
 37+ });
 38+
 39+ $( '#swl-save-all' ).click( function() {
 40+ $( '.swl-save' ).click();
 41+ } );
 42+
 43+ function addGroupToDB( groupName, callback ) {
 44+ $.getJSON(
 45+ wgScriptPath + '/api.php',
 46+ {
 47+ 'action': 'addswlgroup',
 48+ 'format': 'json',
 49+ 'name': groupName,
 50+ 'properties': ''
 51+ },
 52+ function( data ) {
 53+ callback( data.success, data.group );
 54+ }
 55+ );
 56+ }
 57+
 58+ function addGroupToGUI( groupName, groupId ) {
 59+ var newGroup = $( '<fieldset />' ).attr( {
 60+ 'id': 'swl_group_' + groupId,
 61+ 'groupid': groupId,
 62+ 'class': 'swl_group',
 63+ 'groupname': groupName,
 64+ 'categories': '',
 65+ 'namespaces': '',
 66+ 'properties': '',
 67+ 'concepts': ''
 68+ } )
 69+ .html( $( '<legend />' ).text( groupName ) );
 70+
 71+ $( '#swl-groups' ).append( newGroup );
 72+
 73+ initGroupElement( newGroup );
 74+ }
 75+
 76+ $( '#swl-add-group-button' ).click( function() {
 77+ var input = $( '#swl-add-group-name' );
 78+ var button = this;
 79+
 80+ button.disabled = true;
 81+ input.disabled = true;
 82+
 83+ addGroupToDB( input.val(), function( success, group ) {
 84+ if ( success ) {
 85+ addGroupToGUI( group.name, group.id );
 86+ input.val( '' );
 87+ }
 88+ else {
 89+ alert( 'Could not add the group.' );
 90+ }
 91+
 92+ button.disabled = false;
 93+ input.disabled = false;
 94+ } );
 95+ } );
 96+
 97+ $( '#swl-add-group-name' ).keypress( function( event ) {
 98+ if ( event.which == '13' ) {
 99+ $( '#swl-add-group-button' ).click();
 100+ }
 101+ } );
 102+
 103+} ); })(jQuery);
\ No newline at end of file
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/specials/ext.swl.watchlistconditions.js
___________________________________________________________________
Added: svn:eol-style
1104 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/specials/ext.swl.watchlist.css
@@ -0,0 +1,17 @@
 2+@CHARSET "UTF-8";
 3+
 4+div .swl-prop-div {
 5+ padding-left: 15px;
 6+}
 7+
 8+.swl-watchlist-insertions {
 9+ display: inline;
 10+}
 11+
 12+.swl-watchlist-deletions {
 13+ display: inline;
 14+}
 15+
 16+.swl-watchlist-prop {
 17+ font-style: italic;
 18+}
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/specials/ext.swl.watchlist.css
___________________________________________________________________
Added: svn:eol-style
119 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/specials/SpecialSemanticWatchlist.php
@@ -0,0 +1,444 @@
 2+<?php
 3+
 4+/**
 5+ * Semantic watchlist page listing changes to watched properties.
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file SemanticWatchlist.php
 10+ * @ingroup SemanticWatchlist
 11+ *
 12+ * @licence GNU GPL v3 or later
 13+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 14+ */
 15+class SpecialSemanticWatchlist extends SpecialPage {
 16+
 17+ /**
 18+ * MediaWiki timestamp of when the watchlist was last viewed by the current user.
 19+ *
 20+ * @since 0.1
 21+ *
 22+ * @var integer
 23+ */
 24+ protected $lastViewed;
 25+
 26+ /**
 27+ * Constructor.
 28+ *
 29+ * @since 0.1
 30+ */
 31+ public function __construct() {
 32+ parent::__construct( 'SemanticWatchlist', 'semanticwatch' );
 33+ }
 34+
 35+ /**
 36+ * @see SpecialPage::getDescription
 37+ *
 38+ * @since 0.1
 39+ */
 40+ public function getDescription() {
 41+ return wfMsg( 'special-' . strtolower( $this->getName() ) );
 42+ }
 43+
 44+ /**
 45+ * Sets headers - this should be called from the execute() method of all derived classes!
 46+ *
 47+ * @since 0.1
 48+ */
 49+ public function setHeaders() {
 50+ global $wgOut;
 51+ $wgOut->setArticleRelated( false );
 52+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 53+ $wgOut->setPageTitle( $this->getDescription() );
 54+ }
 55+
 56+ /**
 57+ * Main method.
 58+ *
 59+ * @since 0.1
 60+ *
 61+ * @param string $arg
 62+ */
 63+ public function execute( $subPage ) {
 64+ global $wgOut, $wgUser;
 65+
 66+ $this->setHeaders();
 67+ $this->outputHeader();
 68+
 69+ // If the user is authorized, display the page, if not, show an error.
 70+ if ( !$this->userCanExecute( $wgUser ) ) {
 71+ $this->displayRestrictionError();
 72+ return;
 73+ }
 74+
 75+ $this->registerUserView( $wgUser );
 76+
 77+ $wgOut->addHTML( '<p>' );
 78+
 79+ if ( $wgUser->isAllowed( 'semanticwatchgroups' ) ) {
 80+ $wgOut->addHTML( wfMsgExt( 'swl-watchlist-can-mod-groups', 'parseinline', 'Special:WatchlistConditions' ) . '&#160' );
 81+ }
 82+
 83+ if ( $this->userHasWatchlistGroups( $wgUser ) ) {
 84+ $wgOut->addHTML( wfMsgExt( 'swl-watchlist-can-mod-prefs', 'parseinline', 'Special:Preferences#prefsection-swl' ) );
 85+ $wgOut->addHTML( '</p>' );
 86+
 87+ $this->getAndDisplaySets( $subPage );
 88+ }
 89+ else {
 90+ $wgOut->addHTML( wfMsgExt( 'swl-watchlist-no-groups', 'parseinline', 'Special:Preferences#prefsection-swl' ) );
 91+ $wgOut->addHTML( '</p>' );
 92+ }
 93+ }
 94+
 95+ /**
 96+ * Obtains the change sets and displays them by calling displayWatchlist.
 97+ * Also takes care of pagination and displaying appropriate message when there are no results.
 98+ *
 99+ * @since 0.1
 100+ *
 101+ * @param string $subPage
 102+ */
 103+ protected function getAndDisplaySets( $subPage ) {
 104+ global $wgRequest, $wgOut, $wgLang;
 105+
 106+ $limit = $wgRequest->getInt( 'limit', 20 );
 107+ $offset = $wgRequest->getInt( 'offset', 0 );
 108+ $continue = $wgRequest->getVal( 'continue' );
 109+
 110+ $changeSetData = $this->getChangeSetsData( $limit, $continue );
 111+
 112+ $sets = array();
 113+
 114+ foreach ( $changeSetData['sets'] as $set ) {
 115+ $sets[] = SWLChangeSet::newFromArray( $set );
 116+ }
 117+
 118+ $newContinue = false;
 119+
 120+ if ( array_key_exists( 'query-continue', $changeSetData ) ) {
 121+ $newContinue = $changeSetData['query-continue']['semanticwatchlist']['swcontinue'];
 122+ }
 123+
 124+ if ( $offset != 0 || count( $sets ) > 0 ) {
 125+ $wgOut->addHTML( '<p>' . wfMsgExt(
 126+ 'swl-watchlist-position',
 127+ array( 'parseinline' ),
 128+ $wgLang->formatNum( count( $sets ) ),
 129+ $wgLang->formatNum( $offset + 1 )
 130+ ) . '</p>' );
 131+
 132+ $wgOut->addHTML( $this->getPagingControlHTML( $limit, $continue, $subPage, $newContinue, $offset ) );
 133+ }
 134+
 135+ if ( count( $sets ) > 0 ) {
 136+ $this->displayWatchlist( $sets );
 137+ }
 138+ else {
 139+ $wgOut->addWikiMsg( 'swl-watchlist-no-items' );
 140+ }
 141+ }
 142+
 143+ /**
 144+ * Register the user viewed the watchlist,
 145+ * so we know that following chnages should
 146+ * result into notification emails is desired.
 147+ *
 148+ * @since 0.1
 149+ *
 150+ * @param User $user
 151+ */
 152+ protected function registerUserView( User $user ) {
 153+ $this->lastViewed = $user->getOption( 'swl_last_view' );
 154+
 155+ if ( is_null( $this->lastViewed ) ) {
 156+ $this->lastViewed = wfTimestampNow();
 157+ }
 158+
 159+ $user->setOption( 'swl_last_view', wfTimestampNow() );
 160+ $user->setOption( 'swl_mail_count',0 );
 161+ $user->saveSettings();
 162+ }
 163+
 164+ /**
 165+ * @since 0.1
 166+ *
 167+ * @return string
 168+ */
 169+ protected function getPagingControlHTML( $limit, $currentContinue, $subPage, $newContinue, $offset ) {
 170+ global $wgLang;
 171+
 172+ $nextMsg = wfMsgExt( 'nextn', array( 'parsemag', 'escape' ), $limit );
 173+ $firstMsg = wfMsgExt( 'swl-watchlist-firstn', array( 'parsemag', 'escape' ), $limit );
 174+
 175+ if ( $newContinue === false ) {
 176+ $nextLink = $nextMsg;
 177+ }
 178+ else {
 179+ $nextLink = Html::element(
 180+ 'a',
 181+ array(
 182+ 'href' => $this->getTitle( $subPage )->getLocalURL( wfArrayToCGI( array(
 183+ 'limit' => $limit,
 184+ 'continue' => $newContinue,
 185+ 'offset' => $offset + $limit
 186+ ) ) ),
 187+ 'title' => wfMsgExt( 'nextn-title', array( 'parsemag', 'escape' ), $limit ),
 188+ 'class' => 'mw-nextlink'
 189+ ),
 190+ $nextMsg
 191+ );
 192+ }
 193+
 194+ $limitLinks = array();
 195+ $limitLinkArgs = array();
 196+
 197+ if ( $currentContinue == '' ) {
 198+ $firstLink = $firstMsg;
 199+ }
 200+ else {
 201+ $limitLinkArgs['continue'] = $currentContinue;
 202+
 203+ $firstLink = Html::element(
 204+ 'a',
 205+ array(
 206+ 'href' => $this->getTitle( $subPage )->getLocalURL( wfArrayToCGI( array( 'limit' => $limit ) ) ),
 207+ 'title' => wfMsgExt( 'swl-watchlist-firstn-title', array( 'parsemag', 'escape' ), $limit )
 208+ ),
 209+ $firstMsg
 210+ );
 211+ }
 212+
 213+ foreach ( array( 20, 50, 100, 250, 500 ) as $limitValue ) {
 214+ $limitLinkArgs['limit'] = $limitValue;
 215+ if ( $offset != 0 ) {
 216+ $limitLinkArgs['offset'] = $offset;
 217+ }
 218+
 219+ $limitLinks[] = Html::element(
 220+ 'a',
 221+ array(
 222+ 'href' => $this->getTitle( $subPage )->getLocalURL( wfArrayToCGI( $limitLinkArgs ) ),
 223+ 'title' => wfMsgExt( 'shown-title', array( 'parsemag', 'escape' ), $limitValue )
 224+ ),
 225+ $wgLang->formatNum( $limitValue )
 226+ );
 227+ }
 228+
 229+ return Html::rawElement(
 230+ 'p',
 231+ array(),
 232+ wfMsgHtml( 'swl-watchlist-pagincontrol', $wgLang->pipeList( array( $firstLink, $nextLink ) ), $wgLang->pipeList( $limitLinks ) )
 233+ );
 234+ }
 235+
 236+ /**
 237+ * Displays the watchlist.
 238+ *
 239+ * @since 0.1
 240+ *
 241+ * @param array of SWLChangeSet $sets
 242+ */
 243+ protected function displayWatchlist( array $sets ) {
 244+ global $wgOut, $wgLang;
 245+
 246+ $changeSetsHTML = array();
 247+
 248+ foreach ( $sets as /* SWLChangeSet */ $set ) {
 249+ $dayKey = substr( $set->getEdit()->getTime(), 0, 8 ); // Get the YYYYMMDD part.
 250+
 251+ if ( !array_key_exists( $dayKey, $changeSetsHTML ) ) {
 252+ $changeSetsHTML[$dayKey] = array();
 253+ }
 254+
 255+ $changeSetsHTML[$dayKey][] = $this->getChangeSetHTML( $set );
 256+ }
 257+
 258+ krsort( $changeSetsHTML );
 259+
 260+ foreach ( $changeSetsHTML as $dayKey => $daySets ) {
 261+ $wgOut->addHTML( Html::element(
 262+ 'h4',
 263+ array(),
 264+ $wgLang->date( str_pad( $dayKey, 14, '0' ) )
 265+ ) );
 266+
 267+ $wgOut->addHTML( '<ul>' );
 268+
 269+ foreach ( $daySets as $setHTML ) {
 270+ $wgOut->addHTML( $setHTML );
 271+ }
 272+
 273+ $wgOut->addHTML( '</ul>' );
 274+ }
 275+
 276+ SMWOutputs::commitToOutputPage( $wgOut );
 277+
 278+ $wgOut->addModules( 'ext.swl.watchlist' );
 279+ }
 280+
 281+ /**
 282+ * Returns the response of an internal request to the API semanticwatchlist query module.
 283+ *
 284+ * @since 0.1
 285+ *
 286+ * @param integer $limit
 287+ * @param string $continue
 288+ *
 289+ * @return array
 290+ */
 291+ protected function getChangeSetsData( $limit, $continue ) {
 292+ $requestData = array(
 293+ 'action' => 'query',
 294+ 'list' => 'semanticwatchlist',
 295+ 'format' => 'json',
 296+ 'swuserid' => $GLOBALS['wgUser']->getId(),
 297+ 'swlimit' => $limit,
 298+ 'swcontinue' => $continue,
 299+ 'swmerge' => '1'
 300+ );
 301+
 302+ $api = new ApiMain( new FauxRequest( $requestData, true ), true );
 303+ $api->execute();
 304+ return $api->getResultData();
 305+ }
 306+
 307+ /**
 308+ * Gets the HTML for a single change set (edit).
 309+ *
 310+ * @since 0.1
 311+ *
 312+ * @param SWLChangeSet $changeSet
 313+ *
 314+ * @return string
 315+ */
 316+ protected function getChangeSetHTML( SWLChangeSet $changeSet ) {
 317+ global $wgLang;
 318+
 319+ $edit = $changeSet->getEdit();
 320+
 321+ $html = '';
 322+
 323+ $html .= '<li>';
 324+
 325+ $html .=
 326+ '<p>' .
 327+ $wgLang->time( $edit->getTime(), true ) . ' ' .
 328+ Html::element(
 329+ 'a',
 330+ array( 'href' => $edit->getTitle()->getLocalURL() ),
 331+ $edit->getTitle()->getText()
 332+ ) . ' (' .
 333+ Html::element(
 334+ 'a',
 335+ array( 'href' => $edit->getTitle()->getLocalURL( 'action=history' ) ),
 336+ wfMsg( 'hist' )
 337+ ) . ') . . ' .
 338+ Html::element(
 339+ 'a',
 340+ array( 'href' => $edit->getUser()->getUserPage()->getLocalURL() ),
 341+ $edit->getUser()->getName()
 342+ ) . ' (' .
 343+ Html::element(
 344+ 'a',
 345+ array( 'href' => $edit->getUser()->getTalkPage()->getLocalURL() ),
 346+ wfMsg( 'talkpagelinktext' )
 347+ ) . ' | ' .
 348+ ( $edit->getUser()->isAnon() ? '' :
 349+ Html::element(
 350+ 'a',
 351+ array( 'href' => SpecialPage::getTitleFor( 'Contributions', $edit->getUser()->getName() )->getLocalURL() ),
 352+ wfMsg( 'contribslink' )
 353+ ) . ' | '
 354+ ) .
 355+ Html::element(
 356+ 'a',
 357+ array( 'href' => SpecialPage::getTitleFor( 'Block', $edit->getUser()->getName() )->getLocalURL() ),
 358+ wfMsg( 'blocklink' )
 359+ ) . ')' .
 360+ ( $edit->getTime() > $this->lastViewed ? ' [NEW]' : '' ) .
 361+ '</p>'
 362+ ;
 363+
 364+ $propertyHTML= array();
 365+
 366+ foreach ( $changeSet->getAllProperties() as /* SMWDIProperty */ $property ) {
 367+ $propertyHTML[] = $this->getPropertyHTML( $property, $changeSet->getAllPropertyChanges( $property ) );
 368+ }
 369+
 370+ $html .= implode( '', $propertyHTML );
 371+
 372+ $html .= '</li>';
 373+
 374+ return $html;
 375+ }
 376+
 377+ /**
 378+ * Returns the HTML for the changes to a single propety.
 379+ *
 380+ * @param SMWDIProperty $property
 381+ * @param array of SWLPropertyChange $changes
 382+ *
 383+ * @return string
 384+ */
 385+ protected function getPropertyHTML( SMWDIProperty $property, array $changes ) {
 386+ $insertions = array();
 387+ $deletions = array();
 388+
 389+ // Convert the changes into a list of insertions and a list of deletions.
 390+ foreach ( $changes as /* SWLPropertyChange */ $change ) {
 391+ if ( !is_null( $change->getOldValue() ) ) {
 392+ $deletions[] = SMWDataValueFactory::newDataItemValue( $change->getOldValue(), $property )->getLongHTMLText();
 393+ }
 394+ if ( !is_null( $change->getNewValue() ) ) {
 395+ $insertions[] = SMWDataValueFactory::newDataItemValue( $change->getNewValue(), $property )->getLongHTMLText();
 396+ }
 397+ }
 398+
 399+ $lines = array();
 400+
 401+ if ( count( $deletions ) > 0 ) {
 402+ $lines[] = Html::element( 'div', array( 'class' => 'swl-watchlist-deletions' ), wfMsg( 'swl-watchlist-deletions' ) ) . ' ' . implode( ', ', $deletions );
 403+ }
 404+
 405+ if ( count( $insertions ) > 0 ) {
 406+ $lines[] = Html::element( 'div', array( 'class' => 'swl-watchlist-insertions' ), wfMsg( 'swl-watchlist-insertions' ) ) . ' ' . implode( ', ', $insertions );
 407+ }
 408+
 409+ $html = Html::element( 'span', array( 'class' => 'swl-watchlist-prop' ), $property->getLabel() );
 410+
 411+ $html .= Html::rawElement(
 412+ 'div',
 413+ array( 'class' => 'swl-prop-div' ),
 414+ implode( '<br />', $lines )
 415+ );
 416+
 417+ return $html;
 418+ }
 419+
 420+ /**
 421+ * Rteurns if the specified user has any watchlist groups in his notification list.
 422+ *
 423+ * @since 0.1
 424+ *
 425+ * @param User $user
 426+ *
 427+ * @return boolean
 428+ */
 429+ protected function userHasWatchlistGroups( User $user ) {
 430+ if ( !$user->isLoggedIn() ) {
 431+ return false;
 432+ }
 433+
 434+ $dbr = wfGetDB( DB_SLAVE );
 435+
 436+ $group = $dbr->selectRow(
 437+ 'swl_users_per_group',
 438+ array( 'upg_group_id' ),
 439+ array( 'upg_user_id' => $user->getId() )
 440+ );
 441+
 442+ return $group !== false;
 443+ }
 444+
 445+}
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/specials/SpecialSemanticWatchlist.php
___________________________________________________________________
Added: svn:eol-style
1446 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/SemanticWatchlist.sql
@@ -0,0 +1,58 @@
 2+-- MySQL version of the database schema for the Semantic Watchlist extension.
 3+-- Licence: GNU GPL v3+
 4+-- Author: Jeroen De Dauw < jeroendedauw@gmail.com >
 5+
 6+-- Watchlist groups
 7+CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/swl_groups (
 8+ group_id SMALLINT unsigned NOT NULL auto_increment PRIMARY KEY,
 9+ group_name VARCHAR(255) NOT NULL,
 10+ -- No need to have this stuff relational, so keep it simple.
 11+ -- These fields keep multiple values, | separated.
 12+ group_categories BLOB NOT NULL, -- Category names
 13+ group_namespaces BLOB NOT NULL, -- Namespace IDs
 14+ group_properties BLOB NOT NULL, -- Property names
 15+ group_concepts BLOB NOT NULL -- Concept names
 16+) /*$wgDBTableOptions*/;
 17+
 18+-- Single value changes to a property.
 19+CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/swl_changes (
 20+ change_id INT(10) unsigned NOT NULL auto_increment PRIMARY KEY,
 21+ change_set_id INT(10) unsigned NOT NULL, -- Foreign key: swl_sets.set_id
 22+ change_property VARCHAR(255) NOT NULL, -- Name of the property of which a value was changed
 23+ change_old_value BLOB NULL, -- The old value of the property (null for an adittion)
 24+ change_new_value BLOB NULL -- The new value of the property (null for a deletion)
 25+) /*$wgDBTableOptions*/;
 26+
 27+-- Individual edits to pages.
 28+CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/swl_edits (
 29+ edit_id SMALLINT unsigned NOT NULL auto_increment PRIMARY KEY,
 30+ edit_user_name VARCHAR(255) NOT NULL, -- The person that made the modification (account name or ip)
 31+ edit_page_id INT(10) unsigned NOT NULL, -- The id of the page the modification was on
 32+ edit_time CHAR(14) binary NOT NULL default '' -- The time the chages where made
 33+) /*$wgDBTableOptions*/;
 34+
 35+-- Sets of changes. There can be many such sets for one edit, with overlapping changes.
 36+CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/swl_sets (
 37+ set_id INT(10) unsigned NOT NULL auto_increment PRIMARY KEY
 38+) /*$wgDBTableOptions*/;
 39+
 40+-- Links change sets their edits.
 41+CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/swl_sets_per_edit (
 42+ spe_set_id SMALLINT unsigned NOT NULL, -- Foreign key: swl_sets.set_id
 43+ spe_edit_id INT(10) unsigned NOT NULL, -- Edit ID
 44+ PRIMARY KEY (spe_set_id,spe_edit_id)
 45+) /*$wgDBTableOptions*/;
 46+
 47+-- Links change sets to watchlist groups.
 48+CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/swl_sets_per_group (
 49+ spg_group_id SMALLINT unsigned NOT NULL, -- Foreign key: swl_groups.group_id
 50+ spg_set_id INT(10) unsigned NOT NULL, -- Foreign key: swl_sets.set_id
 51+ PRIMARY KEY (spg_group_id,spg_set_id)
 52+) /*$wgDBTableOptions*/;
 53+
 54+-- Links users to watchlist groups.
 55+CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/swl_users_per_group (
 56+ upg_group_id SMALLINT unsigned NOT NULL, -- Foreign key: swl_groups.group_id
 57+ upg_user_id INT(10) unsigned NOT NULL, -- Foreign key: user.user_id
 58+ PRIMARY KEY (upg_group_id,upg_user_id)
 59+) /*$wgDBTableOptions*/;
\ No newline at end of file
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/SemanticWatchlist.sql
___________________________________________________________________
Added: svn:eol-style
160 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/INSTALL
@@ -0,0 +1,44 @@
 2+These is the install file for the Semantic Watchlist extension.
 3+
 4+Extension page on mediawiki.org: http://www.mediawiki.org/wiki/Extension:SemanticWatchlist
 5+Latest version of the install file: http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/SemanticWatchlist/INSTALL?view=co
 6+
 7+
 8+== Requirements ==
 9+
 10+Semantic Watchlist requires:
 11+
 12+* MediaWiki 1.17 or above
 13+* Semantic MediaWiki 1.6 or above
 14+* PHP 5.2 or above
 15+
 16+== Download ==
 17+
 18+You can find the current version of Semantic Watchlist on the [https://code.google.com/p/semanticwatchlist/downloads/list Google Code download page],
 19+as well as a [https://code.google.com/p/semanticwatchlist/downloads/list?can=1 list of legacy downloads].
 20+
 21+You can also get the code directly from SVN. Tags can be obtained via
 22+
 23+ svn checkout <nowiki>http://svn.wikimedia.org/svnroot/mediawiki/tags/extensions/SemanticWatchlist/REL_version</nowiki>
 24+
 25+Where 'version' is the version number of the tag, such as 0_1 (see the [http://svn.wikimedia.org/svnroot/mediawiki/tags/extensions/SemanticWatchlist/ available tags]).
 26+The latest code can be obtained from trunk:
 27+
 28+ svn checkout <nowiki>http://svn.wikimedia.org/svnroot/mediawiki/trunk/extensions/SemanticWatchlist/</nowiki>
 29+
 30+== Installation ==
 31+
 32+Once you have downloaded the code, place the ''SemanticWatchlist'' directory within your MediaWiki
 33+'extensions' directory. Then add the following code to your [[Manual:LocalSettings.php|LocalSettings.php]] file:
 34+
 35+# Semantic Watchlist
 36+require_once( "$IP/extensions/SemanticWatchlist/SemanticWatchlist.php" );
 37+
 38+== Configuration ==
 39+
 40+Configuration of Semantic Watchlist is done by adding simple PHP statements to your
 41+[[Manual:LocalSettings.php|LocalSettings.php]] file. These statements need to be placed
 42+AFTER the inclusion of Semantic Watchlist. The options are listed below and their default
 43+is set in the [http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/SemanticWatchlist/SemanticWatchlist.settings.php?view=markup Semantic Watchlist settings file].
 44+You should NOT modify the settings file, but can have a look at it to get an idea of
 45+how to use the settings, in case the below descriptions do not suffice.
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/INSTALL
___________________________________________________________________
Added: svn:eol-style
146 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/RELEASE-NOTES
@@ -0,0 +1,18 @@
 2+These are the release notes for the Semantic Watchlist extension.
 3+
 4+Extension page on mediawiki.org: http://www.mediawiki.org/wiki/Extension:SemanticWatchlist
 5+Latest version of the release notes: http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/SemanticWatchlist/RELEASE-NOTES?view=co
 6+
 7+
 8+=== Version 0.1 ===
 9+2011-07-30
 10+
 11+Initial release with these features:
 12+
 13+* Special:SemanticWatchlist showing changes to properties watched by the user.
 14+* Per-user optional email notification per edit that changes properties.
 15+* Integeration with user preferences to allow users to specify which watchlist
 16+ groups they want to follow, and if they want to recieve emails on changes.
 17+* Special:WatchListConditions as administration interface for watchlist groups.
 18+* API module to query property changes grouped by edit for a single user.
 19+* API modules to add, modify and delete the watchlist groups.
\ No newline at end of file
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/RELEASE-NOTES
___________________________________________________________________
Added: svn:eol-style
120 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_Edit.php
@@ -0,0 +1,241 @@
 2+<?php
 3+
 4+/**
 5+ *
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file SWL_Edit.php
 10+ * @ingroup SemanticWatchlist
 11+ *
 12+ * @licence GNU GPL v3 or later
 13+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 14+ */
 15+class SWLEdit {
 16+
 17+ /**
 18+ * The ID of the page the edit was made to.
 19+ *
 20+ * @var integer
 21+ */
 22+ protected $pageId;
 23+
 24+ /**
 25+ * The name of the user that made the edit.
 26+ *
 27+ * @var string
 28+ */
 29+ protected $userName;
 30+
 31+ /**
 32+ * The user that made the changes.
 33+ *
 34+ * @var User or false
 35+ */
 36+ protected $user = false;
 37+
 38+ /**
 39+ * The time on which the edit was made.
 40+ *
 41+ * @var integer
 42+ */
 43+ protected $time;
 44+
 45+ /**
 46+ * DB ID of the edit (swl_edits.edit_id).
 47+ *
 48+ * @var integer
 49+ */
 50+ protected $id;
 51+
 52+ /**
 53+ * Creates and returns a new instance of SWLEdit by getting it's info from the database.
 54+ *
 55+ * @since 0.1
 56+ *
 57+ * @param integer $id
 58+ *
 59+ * @return SWLEdit
 60+ */
 61+ public static function newFromId( $id ) {
 62+ $dbr = wfGetDB( DB_SLAVE );
 63+
 64+ return self::newFromDBResult( $dbr->select(
 65+ 'swl_edits',
 66+ array(
 67+ 'edit_id',
 68+ 'edit_user_name',
 69+ 'edit_page_id',
 70+ 'edit_time'
 71+ ),
 72+ array( 'edit_id' => $id )
 73+ ) );
 74+ }
 75+
 76+ /**
 77+ * Creates and returns a new instance of SWLEdit from a database result.
 78+ *
 79+ * @since 0.1
 80+ *
 81+ * @param ResultWrapper $edit
 82+ *
 83+ * @return SWLEdit
 84+ */
 85+ public static function newFromDBResult( $edit ) {
 86+ return new self(
 87+ $edit->edit_page_id,
 88+ $edit->edit_user_name,
 89+ $edit->edit_time,
 90+ $edit->edit_id
 91+ );
 92+ }
 93+
 94+ /**
 95+ * Constructor.
 96+ *
 97+ * @since 0.1
 98+ */
 99+ public function __construct( $pageId, $userName, $time, $id = null ) {
 100+ $this->pageId = $pageId;
 101+ $this->userName = $userName;
 102+ $this->time = $time;
 103+ $this->id = $id;
 104+ }
 105+
 106+ /**
 107+ * Writes the edit to the database, either updating it
 108+ * when it already exists, or inserting it when it doesn't.
 109+ *
 110+ * @since 0.1
 111+ *
 112+ * @return boolean Success indicator
 113+ */
 114+ public function writeToDB() {
 115+ if ( is_null( $this->id ) ) {
 116+ return $this->insertIntoDB();
 117+ }
 118+ else {
 119+ return $this->updateInDB();
 120+ }
 121+ }
 122+
 123+ /**
 124+ * Updates the group in the database.
 125+ *
 126+ * @since 0.1
 127+ *
 128+ * @return boolean Success indicator
 129+ */
 130+ protected function updateInDB() {
 131+ $dbr = wfGetDB( DB_MASTER );
 132+
 133+ return $dbr->update(
 134+ 'swl_edits',
 135+ array(
 136+ 'edit_user_name' => $this->userName,
 137+ 'edit_page_id' => $this->pageId,
 138+ 'edit_time' => $this->time
 139+ ),
 140+ array( 'edit_id' => $this->id )
 141+ );
 142+ }
 143+
 144+ /**
 145+ * Inserts the group into the database.
 146+ *
 147+ * @since 0.1
 148+ *
 149+ * @return boolean Success indicator
 150+ */
 151+ protected function insertIntoDB() {
 152+ wfRunHooks( 'SWLBeforeEditInsert', array( &$this ) );
 153+
 154+ $dbr = wfGetDB( DB_MASTER );
 155+
 156+ $result = $dbr->insert(
 157+ 'swl_edits',
 158+ array(
 159+ 'edit_user_name' => $this->userName,
 160+ 'edit_page_id' => $this->pageId,
 161+ 'edit_time' => $this->time
 162+ )
 163+ );
 164+
 165+ $this->id = $dbr->insertId();
 166+
 167+ wfRunHooks( 'SWLAfterEditInsert', array( &$this ) );
 168+
 169+ return $result;
 170+ }
 171+
 172+ /**
 173+ * Returns the edit database id (swl_edits.edit_id).
 174+ *
 175+ * @since 0.1
 176+ *
 177+ * @return integer
 178+ */
 179+ public function getId() {
 180+ return $this->id;
 181+ }
 182+
 183+ /**
 184+ * Returns the ID of the page the edit was made to.
 185+ *
 186+ * @since 0.1
 187+ *
 188+ * @return integer
 189+ */
 190+ public function getPageId() {
 191+ return $this->pageId;
 192+ }
 193+
 194+ /**
 195+ * Gets the title of the page these changes belong to.
 196+ *
 197+ * @since 0.1
 198+ *
 199+ * @return Title
 200+ */
 201+ public function getTitle() {
 202+ return Title::newFromID( $this->pageId );
 203+ }
 204+
 205+ /**
 206+ * Gets the name of the user that made the changes.
 207+ *
 208+ * @since 0.1
 209+ *
 210+ * @return string
 211+ */
 212+ public function getUserName() {
 213+ return $this->userName;
 214+ }
 215+
 216+ /**
 217+ * Gets the user that made the changes.
 218+ *
 219+ * @since 0.1
 220+ *
 221+ * @return User
 222+ */
 223+ public function getUser() {
 224+ if ( $this->user === false ) {
 225+ $this->user = User::newFromName( $this->userName, false );
 226+ }
 227+
 228+ return $this->user;
 229+ }
 230+
 231+ /**
 232+ * Gets the time on which the changes where made.
 233+ *
 234+ * @since 0.1
 235+ *
 236+ * @return integer
 237+ */
 238+ public function getTime() {
 239+ return $this->time;
 240+ }
 241+
 242+}
\ No newline at end of file
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_Edit.php
___________________________________________________________________
Added: svn:eol-style
1243 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_PropertyChange.php
@@ -0,0 +1,110 @@
 2+<?php
 3+
 4+/**
 5+ * Represents a change to a semantic property.
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file SWL_PropertyChange.php
 10+ * @ingroup SemanticWatchlist
 11+ *
 12+ * @licence GNU GPL v3 or later
 13+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 14+ */
 15+class SWLPropertyChange {
 16+
 17+ const TYPE_INSERT = 0;
 18+ const TYPE_UPDATE = 1;
 19+ const TYPE_DELETE = 2;
 20+
 21+ /**
 22+ * The old value.
 23+ *
 24+ * @var SMWDataItem or null
 25+ */
 26+ protected $oldValue;
 27+
 28+ /**
 29+ * The new value.
 30+ *
 31+ * @var SMWDataItem or null
 32+ */
 33+ protected $newValue;
 34+
 35+ /**
 36+ * Creates and returns a new SWLPropertyChange instance from a serialization.
 37+ *
 38+ * @param string|null $oldValue
 39+ * @param string|null $newValue
 40+ *
 41+ * @return SWLPropertyChange
 42+ */
 43+ public static function newFromSerialization( SMWDIProperty $property, $oldValue, $newValue ) {
 44+ $diType = SMWDataValueFactory::getDataItemId( $property->findPropertyTypeID() );
 45+ //var_dump($property);
 46+ //if($diType!=7) {throw new Exception();exit;}
 47+ return new self(
 48+ is_null( $oldValue ) ? null : SMWDataItem::newFromSerialization( $diType, $oldValue ),
 49+ is_null( $newValue ) ? null : SMWDataItem::newFromSerialization( $diType, $newValue )
 50+ );
 51+ }
 52+
 53+ /**
 54+ * Create a new SWLPropertyChange.
 55+ *
 56+ * @param SMWDataItem $oldValue
 57+ * @param SMWDataItem $newValue
 58+ */
 59+ public function __construct( /* SMWDataItem */ $oldValue, /* SMWDataItem */ $newValue ) {
 60+ $this->oldValue = $oldValue;
 61+ $this->newValue = $newValue;
 62+ }
 63+
 64+ /**
 65+ * Retruns the old value, or null if there is none.
 66+ *
 67+ * @return SMWDataItem or null
 68+ */
 69+ public function getOldValue() {
 70+ return $this->oldValue;
 71+ }
 72+
 73+
 74+ /**
 75+ * returns the new value, or null if there is none.
 76+ *
 77+ * @return SMWDataItem or null
 78+ */
 79+ public function getNewValue() {
 80+ return $this->newValue;
 81+ }
 82+
 83+ /**
 84+ * Returns the type of the change.
 85+ *
 86+ * @return element of the SWLPropertyChange::TYPE_ enum
 87+ */
 88+ public function getType() {
 89+ if ( is_null( $this->oldValue ) ) {
 90+ return self::TYPE_INSERT;
 91+ }
 92+ elseif ( is_null( $this->newValue ) ) {
 93+ return self::TYPE_DELETE;
 94+ }
 95+ else {
 96+ return self::TYPE_UPDATE;
 97+ }
 98+ }
 99+
 100+ /**
 101+ * Returns a serialized version of the change, suitable to
 102+ * do equal comparisions but not to unserialize.
 103+ *
 104+ * @return string
 105+ */
 106+ public function getSerialization() {
 107+ return is_null( $this->oldValue ) ? '' : $this->oldValue->getSerialization() . '|' .
 108+ is_null( $this->newValue ) ? '' : $this->newValue->getSerialization();
 109+ }
 110+
 111+}
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_PropertyChange.php
___________________________________________________________________
Added: svn:eol-style
1112 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_Group.php
@@ -0,0 +1,457 @@
 2+<?php
 3+
 4+/**
 5+ * Static class with functions interact with watchlist groups.
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file SWL_Groups.php
 10+ * @ingroup SemanticWatchlist
 11+ *
 12+ * @licence GNU GPL v3 or later
 13+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 14+ */
 15+class SWLGroup {
 16+
 17+ /**
 18+ * The ID of the group; the group_id field in swl_groups.
 19+ * When creating a new group, this will be null, and
 20+ * automatically set after writing the group to the DB.
 21+ *
 22+ * @since 0.1
 23+ *
 24+ * @var integer or null
 25+ */
 26+ protected $id;
 27+
 28+ /**
 29+ * Name of the group.
 30+ *
 31+ * @since 0.1
 32+ *
 33+ * @var string
 34+ */
 35+ protected $name;
 36+
 37+ /**
 38+ * List of categories this group covers.
 39+ *
 40+ * @since 0.1
 41+ *
 42+ * @var array of string
 43+ */
 44+ protected $categories;
 45+
 46+ /**
 47+ * List of namespaces IDs of namespaces this group covers.
 48+ *
 49+ * @since 0.1
 50+ *
 51+ * @var array of integer
 52+ */
 53+ protected $namespaces = array();
 54+
 55+ /**
 56+ * List of SMW properties this group covers.
 57+ *
 58+ * @since 0.1
 59+ *
 60+ * @var array of string
 61+ */
 62+ protected $properties;
 63+
 64+ /**
 65+ * List of SMW concepts this group covers.
 66+ *
 67+ * @since 0.1
 68+ *
 69+ * @var array of string
 70+ */
 71+ protected $concepts;
 72+
 73+ /**
 74+ * Cached list of IDs of users that are watching this group,
 75+ * or false if this data has not been obtained yet.
 76+ *
 77+ * @since 0.1
 78+ *
 79+ * @var array of integer or false
 80+ */
 81+ protected $watchingUsers = false;
 82+
 83+ /**
 84+ * Creates a new instance of SWLGroup from a DB result.
 85+ *
 86+ * @since 0.1
 87+ *
 88+ * @param $group
 89+ *
 90+ * @return SWLGroup
 91+ */
 92+ public static function newFromDBResult( $group ) {
 93+ return new SWLGroup(
 94+ $group->group_id,
 95+ $group->group_name,
 96+ $group->group_categories == '' ? array() : explode( '|', $group->group_categories ),
 97+ $group->group_namespaces == '' ? array() : explode( '|', $group->group_namespaces ),
 98+ $group->group_properties == '' ? array() : explode( '|', $group->group_properties ),
 99+ $group->group_concepts == '' ? array() : explode( '|', $group->group_group_concepts )
 100+ );
 101+ }
 102+
 103+ /**
 104+ * Constructor.
 105+ *
 106+ * @since 0.1
 107+ *
 108+ * @param integer $id Set to null when creating a new group.
 109+ * @param string $name
 110+ * @param array $categories List of category names
 111+ * @param array $namespaces List of namespace names or IDs
 112+ * @param array $properties List of property names
 113+ * @param array $concepts List of concept names
 114+ */
 115+ public function __construct( $id, $name, array $categories, array $namespaces, array $properties, array $concepts ) {
 116+ $this->id = $id;
 117+ $this->name = $name;
 118+ $this->categories = $categories;
 119+ $this->properties = $properties;
 120+ $this->concepts = $concepts;
 121+
 122+ foreach ( $namespaces as $ns ) {
 123+ if ( preg_match( "/^-?([0-9])+$/", $ns ) ) {
 124+ $this->namespaces[] = $ns;
 125+ }
 126+ elseif ( $ns == '' || strtolower( $ns ) == 'main' ) {
 127+ $this->namespaces[] = 0;
 128+ }
 129+ else {
 130+ $ns = MWNamespace::getCanonicalIndex( strtolower( $ns ) );
 131+
 132+ if ( !is_null( $ns ) ) {
 133+ $this->namespaces[] = $ns;
 134+ }
 135+ }
 136+ }
 137+ }
 138+
 139+ /**
 140+ * Writes the group to the database, either updating it
 141+ * when it already exists, or inserting it when it doesn't.
 142+ *
 143+ * @since 0.1
 144+ *
 145+ * @return boolean Success indicator
 146+ */
 147+ public function writeToDB() {
 148+ if ( is_null( $this->id ) ) {
 149+ return $this->insertIntoDB();
 150+ }
 151+ else {
 152+ return $this->updateInDB();
 153+ }
 154+ }
 155+
 156+ /**
 157+ * Updates the group in the database.
 158+ *
 159+ * @since 0.1
 160+ *
 161+ * @return boolean Success indicator
 162+ */
 163+ protected function updateInDB() {
 164+ $dbr = wfGetDB( DB_MASTER );
 165+
 166+ return $dbr->update(
 167+ 'swl_groups',
 168+ array(
 169+ 'group_name' => $this->name,
 170+ 'group_properties' => implode( '|', $this->properties ),
 171+ 'group_categories' => implode( '|', $this->categories ),
 172+ 'group_namespaces' => implode( '|', $this->namespaces ),
 173+ 'group_concepts' => implode( '|', $this->concepts ),
 174+ ),
 175+ array( 'group_id' => $this->id )
 176+ );
 177+ }
 178+
 179+ /**
 180+ * Inserts the group into the database.
 181+ *
 182+ * @since 0.1
 183+ *
 184+ * @return boolean Success indicator
 185+ */
 186+ protected function insertIntoDB() {
 187+ $dbr = wfGetDB( DB_MASTER );
 188+
 189+ $result = $dbr->insert(
 190+ 'swl_groups',
 191+ array(
 192+ 'group_name' => $this->name,
 193+ 'group_properties' => $this->properties,
 194+ 'group_categories' => $this->categories,
 195+ 'group_namespaces' => $this->namespaces,
 196+ 'group_concepts' => $this->concepts,
 197+ )
 198+ );
 199+
 200+ $this->id = $dbr->insertId();
 201+
 202+ return $result;
 203+ }
 204+
 205+ /**
 206+ * Returns the categories specified by the group.
 207+ *
 208+ * @since 0.1
 209+ *
 210+ * @return array[string]
 211+ */
 212+ public function getCategories() {
 213+ return $this->categories;
 214+ }
 215+
 216+ /**
 217+ * Returns the namespaces specified by the group.
 218+ *
 219+ * @since 0.1
 220+ *
 221+ * @return array[integer]
 222+ */
 223+ public function getNamespaces() {
 224+ return $this->namespaces;
 225+ }
 226+
 227+ /**
 228+ * Returns the properties specified by the group as strings (serializations of SMWDIProperty).
 229+ *
 230+ * @since 0.1
 231+ *
 232+ * @return array[string]
 233+ */
 234+ public function getProperties() {
 235+ return $this->properties;
 236+ }
 237+
 238+ /**
 239+ * Returns the properties specified by the group as SMWDIProperty objects.
 240+ *
 241+ * @since 0.1
 242+ *
 243+ * @return array[SMWDIProperty]
 244+ */
 245+ public function getPropertyObjects() {
 246+ $properties = array();
 247+
 248+ foreach ( $this->properties as $property ) {
 249+ $properties[] = SMWDIProperty::newFromSerialization( $property );
 250+ }
 251+
 252+ return $properties;
 253+ }
 254+
 255+ /**
 256+ * Returns the concepts specified by the group.
 257+ *
 258+ * @since 0.1
 259+ *
 260+ * @return array[string]
 261+ */
 262+ public function getConcepts() {
 263+ return $this->concepts;
 264+ }
 265+
 266+ /**
 267+ * Returns the group database id.
 268+ *
 269+ * @since 0.1
 270+ *
 271+ * @return integer
 272+ */
 273+ public function getId() {
 274+ return $this->id;
 275+ }
 276+
 277+ /**
 278+ * Returns the group name.
 279+ *
 280+ * @since 0.1
 281+ *
 282+ * @return string
 283+ */
 284+ public function getName() {
 285+ return $this->name;
 286+ }
 287+
 288+ /**
 289+ * Returns whether the group contains the specified page.
 290+ *
 291+ * @since 0.1
 292+ *
 293+ * @param Title $title
 294+ *
 295+ * @return boolean
 296+ */
 297+ public function coversPage( Title $title ) {
 298+ return $this->categoriesCoverPage( $title )
 299+ && $this->namespacesCoversPage( $title )
 300+ && $this->conceptsCoverPage( $title );
 301+ }
 302+
 303+ /**
 304+ * Returns whether the namespaces of the group cover the specified page.
 305+ *
 306+ * @since 0.1
 307+ *
 308+ * @param Title $title
 309+ *
 310+ * @return boolean
 311+ */
 312+ public function namespacesCoversPage( Title $title ) {
 313+ if ( count( $this->namespaces ) > 0 ) {
 314+ if ( !in_array( $title->getNamespace(), $this->namespaces ) ) {
 315+ return false;
 316+ }
 317+ }
 318+
 319+ return true;
 320+ }
 321+
 322+ /**
 323+ * Returns whether the catgeories of the group cover the specified page.
 324+ *
 325+ * @since 0.1
 326+ *
 327+ * @param Title $title
 328+ *
 329+ * @return boolean
 330+ */
 331+ public function categoriesCoverPage( Title $title ) {
 332+ if ( count( $this->categories ) == 0 ) {
 333+ return true;
 334+ }
 335+
 336+ $foundMatch = false;
 337+
 338+ $cats = array_keys( $title->getParentCategories() );
 339+
 340+ if ( count( $cats ) == 0 ) {
 341+ return false;
 342+ }
 343+
 344+ global $wgContLang;
 345+ $catPrefix = $wgContLang->getNSText( NS_CATEGORY ) . ':';
 346+
 347+ foreach ( $this->categories as $groupCategory ) {
 348+ $foundMatch = in_array( $catPrefix . $groupCategory, $cats );
 349+
 350+ if ( $foundMatch ) {
 351+ break;
 352+ }
 353+ }
 354+
 355+ return $foundMatch;
 356+ }
 357+
 358+ /**
 359+ * Returns whether the concepts of the group cover the specified page.
 360+ *
 361+ * @since 0.1
 362+ *
 363+ * @param Title $title
 364+ *
 365+ * @return boolean
 366+ */
 367+ public function conceptsCoverPage( Title $title ) {
 368+ if ( count( $this->concepts ) == 0 ) {
 369+ return true;
 370+ }
 371+
 372+ $foundMatch = false;
 373+
 374+ foreach ( $this->concepts as $groupConcept ) {
 375+ $queryDescription = new SMWConjunction();
 376+
 377+ $conceptTitle = Title::newFromText( $groupConcept, SMW_NS_CONCEPT );
 378+ $queryDescription->addDescription( new SMWConceptDescription( SMWDIWikiPage::newFromTitle( $conceptTitle ) ) );
 379+ $queryDescription->addDescription( new SMWValueDescription( SMWDIWikiPage::newFromTitle( $title ) ) );
 380+
 381+ $query = new SMWQuery( $queryDescription );
 382+ $query->querymode = SMWQuery::MODE_COUNT;
 383+
 384+ /* SMWQueryResult */ $result = smwfGetStore()->getQueryResult( $query );
 385+ $foundMatch = $result->getCount() > 0;
 386+
 387+ if ( $foundMatch ) {
 388+ break;
 389+ }
 390+ }
 391+
 392+ return $foundMatch;
 393+ }
 394+
 395+ /**
 396+ * Returns the IDs of the users watching the group.
 397+ *
 398+ * @since 0.1
 399+ *
 400+ * @return array of integer
 401+ */
 402+ public function getWatchingUsers() {
 403+ if ( $this->watchingUsers == false ) {
 404+ $dbr = wfGetDB( DB_SLAVE );
 405+
 406+ $users = $dbr->select(
 407+ 'swl_users_per_group',
 408+ array(
 409+ 'upg_user_id'
 410+ ),
 411+ array(
 412+ 'upg_group_id' => $this->getId()
 413+ )
 414+ );
 415+
 416+ $userIds = array();
 417+
 418+ foreach ( $users as $user ) {
 419+ $userIds[] = $user->upg_user_id;
 420+ }
 421+
 422+ $this->watchingUsers = $userIds;
 423+ }
 424+
 425+ return $this->watchingUsers;
 426+ }
 427+
 428+ /**
 429+ * Returns if the group is watched by the specified user or not.
 430+ *
 431+ * @since 0.1
 432+ *
 433+ * @param User $user
 434+ *
 435+ * @return boolean
 436+ */
 437+ public function isWatchedByUser( User $user ) {
 438+ return in_array( $user->getId(), $this->getWatchingUsers() );
 439+ }
 440+
 441+ /**
 442+ * Gets all the watching users and passes them, together with the specified
 443+ * changes and the group object itself, to the SWLGroupNotify hook.
 444+ *
 445+ * @since 0.1
 446+ *
 447+ * @param SMWChangeSet $changes
 448+ */
 449+ public function notifyWatchingUsers( SWLChangeSet $changes ) {
 450+ $users = $this->getWatchingUsers();
 451+
 452+ if ( $changes->hasChanges( true ) ) {
 453+ wfRunHooks( 'SWLGroupNotify', array( $this, $users, $changes ) );
 454+ }
 455+ }
 456+
 457+}
 458+
\ No newline at end of file
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_Group.php
___________________________________________________________________
Added: svn:eol-style
1459 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_PropertyChanges.php
@@ -0,0 +1,178 @@
 2+<?php
 3+
 4+/**
 5+ * A collection of semantic properties and changes changes made to them.
 6+ * This class is based on SMWSemanticData and can be seen as a simplified
 7+ * version with SWLPropertyChange objects, each holding 2 SMWDataItem objects,
 8+ * instead of SMWDataItem objects.
 9+ *
 10+ * @since 0.1
 11+ *
 12+ * @file SWL_PropertyChange.php
 13+ * @ingroup SemanticWatchlist
 14+ *
 15+ * @licence GNU GPL v3 or later
 16+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 17+ */
 18+class SWLPropertyChanges implements Iterator {
 19+
 20+ protected $pos = 0;
 21+ protected $currentRow = null;
 22+
 23+ /**
 24+ * Cache for the localized version of the namespace prefix "Property:".
 25+ *
 26+ * @var string
 27+ */
 28+ static protected $propertyPrefix = '';
 29+
 30+ /**
 31+ * Array mapping property keys (string) to arrays of SWLPropertyChange.
 32+ *
 33+ * @var array of SWLPropertyChange
 34+ */
 35+ protected $changes = array();
 36+
 37+ /**
 38+ * Array mapping property keys (string) to SMWDIProperty objects.
 39+ *
 40+ * @var array of SMWDIProperty
 41+ */
 42+ protected $properties = array();
 43+
 44+ /**
 45+ * Indicates if there are changes in the list.
 46+ *
 47+ * @var boolean
 48+ */
 49+ protected $hasChanges = false;
 50+
 51+ /**
 52+ * Get the array of all properties that have changes.
 53+ *
 54+ * @return array of SMWDIProperty
 55+ */
 56+ public function getProperties() {
 57+ return $this->properties;
 58+ }
 59+
 60+ /**
 61+ * Returns if the list contains any changes.
 62+ * This info is cached, so the call is cheaper then doing a count.
 63+ *
 64+ * @return boolean
 65+ */
 66+ public function hasChanges() {
 67+ return $this->hasChanges;
 68+ }
 69+
 70+ /**
 71+ * Get the array of all stored values for some property.
 72+ *
 73+ * @param $property SMWDIProperty
 74+ *
 75+ * @return array of SWLPropertyChange
 76+ */
 77+ public function getPropertyChanges( SMWDIProperty $property ) {
 78+ if ( array_key_exists( $property->getKey(), $this->changes ) ) {
 79+ return $this->changes[$property->getKey()];
 80+ } else {
 81+ return array();
 82+ }
 83+ }
 84+
 85+ /**
 86+ * Store a value for a property identified by its SMWDataItem object.
 87+ *
 88+ * @note There is no check whether the type of the given data item
 89+ * agrees with the type of the property. Since property types can
 90+ * change, all parts of SMW are prepared to handle mismatched data item
 91+ * types anyway.
 92+ *
 93+ * @param SMWDIProperty $property
 94+ * @param SWLPropertyChange $change
 95+ */
 96+ public function addPropertyObjectChange( SMWDIProperty $property, SWLPropertyChange $change ) {
 97+ if ( $property->isInverse() ) { // inverse properties cannot be used for annotation
 98+ return;
 99+ }
 100+
 101+ if ( !array_key_exists( $property->getKey(), $this->changes ) ) {
 102+ $this->changes[$property->getKey()] = array();
 103+ $this->properties[$property->getKey()] = $property;
 104+ }
 105+
 106+ $this->changes[$property->getKey()][] = $change;
 107+
 108+ $this->hasChanges = true;
 109+ }
 110+
 111+ /**
 112+ * Store a value for a given property identified by its text label
 113+ * (without namespace prefix).
 114+ *
 115+ * @param string $propertyName
 116+ * @param SWLPropertyChange $change
 117+ */
 118+ public function addPropertyChange( $propertyName, SWLPropertyChange $change ) {
 119+ $propertyKey = smwfNormalTitleDBKey( $propertyName );
 120+
 121+ if ( array_key_exists( $propertyKey, $this->properties ) ) {
 122+ $property = $this->properties[$propertyKey];
 123+ } else {
 124+ if ( self::$propertyPrefix == '' ) {
 125+ global $wgContLang;
 126+ self::$propertyPrefix = $wgContLang->getNsText( SMW_NS_PROPERTY ) . ':';
 127+ } // explicitly use prefix to cope with things like [[Property:User:Stupid::somevalue]]
 128+
 129+ $propertyDV = SMWPropertyValue::makeUserProperty( self::$propertyPrefix . $propertyName );
 130+
 131+ if ( !$propertyDV->isValid() ) { // error, maybe illegal title text
 132+ return;
 133+ }
 134+
 135+ $property = $propertyDV->getDataItem();
 136+ }
 137+
 138+ $this->addPropertyObjectChange( $property, $change );
 139+ }
 140+
 141+ /**
 142+ * Removes all changes for a certian property.
 143+ *
 144+ * @param SMWDIProperty $property
 145+ */
 146+ public function removeChangesForProperty( SMWDIProperty $property ) {
 147+ if ( array_key_exists( $property->getKey(), $this->changes ) ) {
 148+ unset( $this->changes[$property->getKey()] );
 149+ unset( $this->properties[$property->getKey()] );
 150+ }
 151+ }
 152+
 153+ function rewind() {
 154+ $this->pos = 0;
 155+ $this->currentRow = null;
 156+ }
 157+
 158+ function current() {
 159+ if ( is_null( $this->currentRow ) ) {
 160+ $this->next();
 161+ }
 162+ return $this->currentRow;
 163+ }
 164+
 165+ function key() {
 166+ return $this->pos;
 167+ }
 168+
 169+ function next() {
 170+ $this->pos++;
 171+ $this->currentRow = array_key_exists( $this->pos, $this->changes ) ? $this->changes[$this->pos] : false;
 172+ return $this->currentRow;
 173+ }
 174+
 175+ function valid() {
 176+ return $this->current() !== false;
 177+ }
 178+
 179+}
\ No newline at end of file
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_PropertyChanges.php
___________________________________________________________________
Added: svn:eol-style
1180 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_Emailer.php
@@ -0,0 +1,135 @@
 2+<?php
 3+
 4+/**
 5+ * Static class holding functions for sending emails.
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file SWL_Emailer.php
 10+ * @ingroup SemanticWatchlist
 11+ *
 12+ * @licence GNU GPL v3 or later
 13+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 14+ */
 15+final class SWLEmailer {
 16+
 17+ /**
 18+ * Notifies a single user of the changes made to properties in a single edit.
 19+ *
 20+ * @since 0.1
 21+ *
 22+ * @param SWLGroup $group
 23+ * @param User $user
 24+ * @param SWLChangeSet $changeSet
 25+ * @param boolean $describeChanges
 26+ *
 27+ * @return Status
 28+ */
 29+ public static function notifyUser( SWLGroup $group, User $user, SWLChangeSet $changeSet, $describeChanges ) {
 30+ global $wgLang, $wgPasswordSender, $wgPasswordSenderName;
 31+
 32+ $emailText = wfMsgExt(
 33+ 'swl-email-propschanged-long',
 34+ 'parse',
 35+ $GLOBALS['wgSitename'],
 36+ $changeSet->getEdit()->getUser()->getName(),
 37+ SpecialPage::getTitleFor( 'SemanticWatchlist' )->getFullURL(),
 38+ $wgLang->time( $changeSet->getEdit()->getTime() ),
 39+ $wgLang->date( $changeSet->getEdit()->getTime() )
 40+ );
 41+
 42+ if ( $describeChanges ) {
 43+ $emailText .= '<h3> ' . wfMsgExt(
 44+ 'swl-email-changes',
 45+ 'parse',
 46+ $changeSet->getEdit()->getTitle()->getFullText(),
 47+ $changeSet->getEdit()->getTitle()->getFullURL()
 48+ ) . ' </h3>';
 49+
 50+ $emailText .= self::getChangeListHTML( $changeSet );
 51+ }
 52+
 53+ $title = wfMsgReal(
 54+ 'swl-email-propschanged',
 55+ array( $changeSet->getEdit()->getTitle()->getFullText() ),
 56+ true,
 57+ $user->getOption( 'language' )
 58+ );
 59+
 60+ wfRunHooks( 'SWLBeforeEmailNotify', array( $group, $user, $changeSet, $describeChanges, &$title, &$emailText ) );
 61+
 62+ return UserMailer::send(
 63+ new MailAddress( $user ),
 64+ new MailAddress( $wgPasswordSender, $wgPasswordSenderName ),
 65+ $title,
 66+ $emailText,
 67+ null,
 68+ 'text/html; charset=ISO-8859-1'
 69+ );
 70+ }
 71+
 72+ /**
 73+ * Creates and returns the HTML representatation of the change set.
 74+ *
 75+ * @since 0.1
 76+ *
 77+ * @param SWLChangeSet $changeSet
 78+ *
 79+ * @return string
 80+ */
 81+ protected static function getChangeListHTML( SWLChangeSet $changeSet ) {
 82+ $propertyHTML = array();
 83+
 84+ foreach ( $changeSet->getAllProperties() as /* SMWDIProperty */ $property ) {
 85+ $propertyHTML[] = self::getPropertyHTML( $property, $changeSet->getAllPropertyChanges( $property ) );
 86+ }
 87+
 88+ return implode( '', $propertyHTML );
 89+ }
 90+
 91+ /**
 92+ * Creates and returns the HTML representatation of the property and it's changes.
 93+ *
 94+ * @since 0.1
 95+ *
 96+ * @param SMWDIProperty $property
 97+ * @param array $changes
 98+ *
 99+ * @return string
 100+ */
 101+ protected static function getPropertyHTML( SMWDIProperty $property, array $changes ) {
 102+ $insertions = array();
 103+ $deletions = array();
 104+
 105+ // Convert the changes into a list of insertions and a list of deletions.
 106+ foreach ( $changes as /* SWLPropertyChange */ $change ) {
 107+ if ( !is_null( $change->getOldValue() ) ) {
 108+ $deletions[] = SMWDataValueFactory::newDataItemValue( $change->getOldValue(), $property )->getShortHTMLText();
 109+ }
 110+ if ( !is_null( $change->getNewValue() ) ) {
 111+ $insertions[] = SMWDataValueFactory::newDataItemValue( $change->getNewValue(), $property )->getShortHTMLText();
 112+ }
 113+ }
 114+
 115+ $lines = array();
 116+
 117+ if ( count( $insertions ) > 0 ) {
 118+ $lines[] = Html::element( 'span', array(), wfMsg( 'swl-watchlist-insertions' ) ) . ' ' . implode( ', ', $insertions );
 119+ }
 120+
 121+ if ( count( $deletions ) > 0 ) {
 122+ $lines[] = Html::element( 'span', array(), wfMsg( 'swl-watchlist-deletions' ) ) . ' ' . implode( ', ', $deletions );
 123+ }
 124+
 125+ $html = Html::element( 'b', array(), $property->getLabel() );
 126+
 127+ $html .= Html::rawElement(
 128+ 'div',
 129+ array( 'class' => 'swl-prop-div' ),
 130+ implode( '<br />', $lines )
 131+ );
 132+
 133+ return $html;
 134+ }
 135+
 136+}
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_Emailer.php
___________________________________________________________________
Added: svn:eol-style
1137 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_Groups.php
@@ -0,0 +1,95 @@
 2+<?php
 3+
 4+/**
 5+ * Static class with functions interact with watchlist groups.
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file SWL_Groups.php
 10+ * @ingroup SemanticWatchlist
 11+ *
 12+ * @licence GNU GPL v3 or later
 13+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 14+ */
 15+final class SWLGroups {
 16+
 17+ /**
 18+ * Cached list of all watchlist groups.
 19+ *
 20+ * @var array of SWLGroup
 21+ */
 22+ protected static $groups = false;
 23+
 24+ /**
 25+ * Returns all watchlist groups.
 26+ *
 27+ * @since 0.1
 28+ *
 29+ * @return array of SWLGroup
 30+ */
 31+ public static function getAll() {
 32+ if ( self::$groups === false ) {
 33+ self::$groups = array();
 34+
 35+ $dbr = wfGetDB( DB_SLAVE );
 36+
 37+ $groups = $dbr->select( 'swl_groups', array(
 38+ 'group_id',
 39+ 'group_name',
 40+ 'group_categories',
 41+ 'group_namespaces',
 42+ 'group_properties',
 43+ 'group_concepts'
 44+ ) );
 45+
 46+ foreach ( $groups as $group ) {
 47+ self::$groups[] = SWLGroup::newFromDBResult( $group );
 48+ }
 49+ }
 50+
 51+ return self::$groups;
 52+ }
 53+
 54+ /**
 55+ * Returns all watchlist groups that watch the specified page.
 56+ *
 57+ * @since 0.1
 58+ *
 59+ * @param Title $title
 60+ *
 61+ * @return array of SWLGroup
 62+ */
 63+ public static function getMatchingWatchGroups( Title $title ) {
 64+ $matchingGroups = array();
 65+
 66+ foreach ( self::getAll() as /* SWLGroup */ $group ) {
 67+ if ( $group->coversPage( $title ) ) {
 68+ $matchingGroups[] = $group;
 69+ }
 70+ }
 71+
 72+ return $matchingGroups;
 73+ }
 74+
 75+ /**
 76+ * Returns all watchlist groups that are watched by the specified user.
 77+ *
 78+ * @since 0.1
 79+ *
 80+ * @param User $user
 81+ *
 82+ * @return array of SWLGroup
 83+ */
 84+ public static function getGroupsForUser( User $user ) {
 85+ $matchingGroups = array();
 86+
 87+ foreach ( self::getAll() as /* SWLGroup */ $group ) {
 88+ if ( $group->isWatchedByUser( $user ) ) {
 89+ $matchingGroups[] = $group;
 90+ }
 91+ }
 92+
 93+ return $matchingGroups;
 94+ }
 95+
 96+}
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_Groups.php
___________________________________________________________________
Added: svn:eol-style
197 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_ChangeSet.php
@@ -0,0 +1,753 @@
 2+<?php
 3+
 4+/**
 5+ *
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file SWL_ChangeSet.php
 10+ * @ingroup SemanticWatchlist
 11+ *
 12+ * @licence GNU GPL v3 or later
 13+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 14+ */
 15+class SWLChangeSet {
 16+
 17+ /**
 18+ * The subject the changes apply to.
 19+ *
 20+ * @var SMWDIWikiPage
 21+ */
 22+ protected $subject;
 23+
 24+ /**
 25+ * Object holding semantic data that got inserted.
 26+ *
 27+ * @var SMWSemanticData
 28+ */
 29+ protected $insertions;
 30+
 31+ /**
 32+ * Object holding semantic data that got deleted.
 33+ *
 34+ * @var SMWSemanticData
 35+ */
 36+ protected $deletions;
 37+
 38+ /**
 39+ * List of all changes(, not including insertions and deletions).
 40+ *
 41+ * @var SWLPropertyChanges
 42+ */
 43+ protected $changes;
 44+
 45+ /**
 46+ * DB ID of the change set (swl_sets.set_id).
 47+ *
 48+ * @var integer
 49+ */
 50+ protected $id;
 51+
 52+ /**
 53+ * The title of the page the changeset holds changes for.
 54+ * The title will be constructed from the subject of the SMWChangeSet
 55+ * the first time getTitle is called, so it should be accessed via this
 56+ * method.
 57+ *
 58+ * @var Title or false
 59+ */
 60+ protected $title = false;
 61+
 62+ /**
 63+ * The edit this set of changes belongs to.
 64+ *
 65+ * @var SWLEdit
 66+ */
 67+ protected $edit;
 68+
 69+ /**
 70+ * Creates and returns a new SWLChangeSet instance from a database result
 71+ * obtained by doing a select on swl_sets.
 72+ *
 73+ * @since 0.1
 74+ *
 75+ * @param $set
 76+ *
 77+ * @return SWLChangeSet
 78+ */
 79+ public static function newFromDBResult( $set ) {
 80+ $changeSet = new SWLChangeSet(
 81+ SMWDIWikiPage::newFromTitle( Title::newFromID( $set->edit_page_id ) ),
 82+ null,
 83+ null,
 84+ null,
 85+ $set->spe_set_id,
 86+ new SWLEdit(
 87+ $set->edit_page_id,
 88+ $set->edit_user_name,
 89+ $set->edit_time,
 90+ $set->edit_id
 91+ )
 92+ );
 93+
 94+ $dbr = wfGetDb( DB_SLAVE );
 95+
 96+ $changes = $dbr->select(
 97+ 'swl_changes',
 98+ array(
 99+ 'change_id',
 100+ 'change_property',
 101+ 'change_old_value',
 102+ 'change_new_value'
 103+ ),
 104+ array(
 105+ 'change_set_id' => $set->spe_set_id
 106+ )
 107+ );
 108+
 109+ foreach ( $changes as $change ) {
 110+ $property = SMWDIProperty::doUnserialize( $change->change_property, '__pro' );
 111+
 112+ $changeSet->addChange(
 113+ $property,
 114+ SWLPropertyChange::newFromSerialization( $property, $change->change_old_value, $change->change_new_value )
 115+ );
 116+ }
 117+
 118+ return $changeSet;
 119+ }
 120+
 121+ /**
 122+ * Creates and returns a new SWLChangeSet instance from a database result
 123+ * obtained by doing a select on swl_sets.
 124+ *
 125+ * @since 0.1
 126+ *
 127+ * @param array $changeSetArray
 128+ *
 129+ * @return SWLChangeSet
 130+ */
 131+ public static function newFromArray( array $changeSetArray ) {
 132+ $changeSet = new SWLChangeSet(
 133+ SMWDIWikiPage::newFromTitle( Title::newFromID( $changeSetArray['page_id'] ) ),
 134+ null,
 135+ null,
 136+ null,
 137+ $changeSetArray['id'],
 138+ new SWLEdit(
 139+ $changeSetArray['page_id'],
 140+ $changeSetArray['user_name'],
 141+ $changeSetArray['time'],
 142+ $changeSetArray['editid']
 143+ )
 144+ );
 145+
 146+ foreach ( $changeSetArray['changes'] as $propName => $changes ) {
 147+ $property = SMWDIProperty::doUnserialize( $propName, '__pro' );
 148+
 149+ foreach ( $changes as $change ) {
 150+ $changeSet->addChange(
 151+ $property,
 152+ SWLPropertyChange::newFromSerialization(
 153+ $property,
 154+ array_key_exists( 'old', $change ) ? $change['old'] : null,
 155+ array_key_exists( 'new', $change ) ? $change['new'] : null
 156+ )
 157+ );
 158+ }
 159+ }
 160+
 161+ return $changeSet;
 162+ }
 163+
 164+ /**
 165+ * Creates and returns a new SMWChangeSet from 2 SMWSemanticData objects.
 166+ *
 167+ * @param SMWSemanticData $old
 168+ * @param SMWSemanticData $new
 169+ * @param array $filterProperties Optional list of properties (string serializations) to filter on. Null for no filtering.
 170+ *
 171+ * @return SMWChangeSet
 172+ */
 173+ public static function newFromSemanticData( SMWSemanticData $old, SMWSemanticData $new, array $filterProperties = null ) {
 174+ $subject = $old->getSubject();
 175+
 176+ if ( $subject != $new->getSubject() ) {
 177+ return new self( $subject );
 178+ }
 179+
 180+ $changes = new SWLPropertyChanges();
 181+ $insertions = new SMWSemanticData( $subject );
 182+ $deletions = new SMWSemanticData( $subject );
 183+
 184+ $oldProperties = array();
 185+ $newProperties = array();
 186+
 187+ foreach ( $old->getProperties() as $property ) {
 188+ if ( is_null( $filterProperties ) || in_array( $property->getLabel(), $filterProperties ) ) {
 189+ $oldProperties[] = $property;
 190+ }
 191+ }
 192+
 193+ foreach ( $new->getProperties() as $property ) {
 194+ if ( is_null( $filterProperties ) || in_array( $property->getLabel(), $filterProperties ) ) {
 195+ $newProperties[] = $property;
 196+ }
 197+ }
 198+
 199+ // Find the deletions.
 200+ self::findSingleDirectionChanges( $deletions, $oldProperties, $old, $newProperties, $filterProperties );
 201+
 202+ // Find the insertions.
 203+ self::findSingleDirectionChanges( $insertions, $newProperties, $new, $oldProperties, $filterProperties );
 204+
 205+ foreach ( $oldProperties as $propertyKey => /* SMWDIProperty */ $diProperty ) {
 206+ $oldDataItems = array();
 207+ $newDataItems = array();
 208+
 209+ // Populate the data item arrays using keys that are their hash, so matches can be found.
 210+ // Note: this code assumes there are no duplicates.
 211+ foreach ( $old->getPropertyValues( $diProperty ) as /* SMWDataItem */ $dataItem ) {
 212+ $oldDataItems[$dataItem->getHash()] = $dataItem;
 213+ }
 214+ foreach ( $new->getPropertyValues( $diProperty ) as /* SMWDataItem */ $dataItem ) {
 215+ $newDataItems[$dataItem->getHash()] = $dataItem;
 216+ }
 217+
 218+ $foundMatches = array();
 219+
 220+ // Find values that are both in the old and new version.
 221+ foreach ( array_keys( $oldDataItems ) as $hash ) {
 222+ if ( array_key_exists( $hash, $newDataItems ) ) {
 223+ $foundMatches[] = $hash;
 224+ }
 225+ }
 226+
 227+ // Remove the values occuring in both sets, so only changes remain.
 228+ foreach ( $foundMatches as $foundMatch ) {
 229+ unset( $oldDataItems[$foundMatch] );
 230+ unset( $newDataItems[$foundMatch] );
 231+ }
 232+
 233+ // Find which group is biggest, so it's easy to loop over all values of the smallest.
 234+ $oldIsBigger = count( $oldDataItems ) > count ( $newDataItems );
 235+ $bigGroup = $oldIsBigger ? $oldDataItems : $newDataItems;
 236+ $smallGroup = $oldIsBigger ? $newDataItems : $oldDataItems;
 237+
 238+ // Add all one-to-one changes.
 239+ while ( $dataItem = array_shift( $smallGroup ) ) {
 240+ $changes->addPropertyObjectChange( $diProperty, new SWLPropertyChange( $dataItem, array_shift( $bigGroup ) ) );
 241+ }
 242+
 243+ // If the bigger group is not-equal to the smaller one, items will be left,
 244+ // that are either insertions or deletions, depending on the group.
 245+ if ( count( $bigGroup > 0 ) ) {
 246+ $semanticData = $oldIsBigger ? $deletions : $insertions;
 247+
 248+ foreach ( $bigGroup as /* SMWDataItem */ $dataItem ) {
 249+ $semanticData->addPropertyObjectValue( $diProperty, $dataItem );
 250+ }
 251+ }
 252+ }
 253+
 254+ return new self( $subject, $changes, $insertions, $deletions );
 255+ }
 256+
 257+ /**
 258+ * Finds the inserts or deletions and adds them to the passed SMWSemanticData object.
 259+ * These values will also be removed from the first list of properties and their values,
 260+ * so it can be used for one-to-one change finding later on.
 261+ *
 262+ * @param SMWSemanticData $changeSet
 263+ * @param array $oldProperties
 264+ * @param SMWSemanticData $oldData
 265+ * @param array $newProperties
 266+ */
 267+ protected static function findSingleDirectionChanges( SMWSemanticData &$changeSet,
 268+ array &$oldProperties, SMWSemanticData $oldData, array $newProperties ) {
 269+
 270+ $deletionKeys = array();
 271+
 272+ foreach ( $oldProperties as $propertyKey => /* SMWDIProperty */ $diProperty ) {
 273+ if ( !array_key_exists( $propertyKey, $newProperties ) ) {
 274+ foreach ( $oldData->getPropertyValues( $diProperty ) as /* SMWDataItem */ $dataItem ) {
 275+ $changeSet->addPropertyObjectValue( $diProperty, $dataItem );
 276+ }
 277+ $deletionKeys[] = $propertyKey;
 278+ }
 279+ }
 280+
 281+ foreach ( $deletionKeys as $key ) {
 282+ unset( $oldProperties[$propertyKey] );
 283+ }
 284+ }
 285+
 286+ /**
 287+ * Create a new instance of a change set.
 288+ *
 289+ * @param SMWDIWikiPage $subject
 290+ * @param SWLPropertyChanges $changes Can be null
 291+ * @param SMWSemanticData $insertions Can be null
 292+ * @param SMWSemanticData $deletions Can be null
 293+ * @param integer $id Can be null
 294+ * @param SWLEdit $edit Can be null
 295+ */
 296+ public function __construct( SMWDIWikiPage $subject, /* SWLPropertyChanges */ $changes = null,
 297+ /* SMWSemanticData */ $insertions = null, /* SMWSemanticData */ $deletions = null,
 298+ $id = null, /* SWLEdit */ $edit = null ) {
 299+
 300+ $this->subject = $subject;
 301+ $this->changes = is_null( $changes ) ? new SWLPropertyChanges() : $changes;
 302+ $this->insertions = is_null( $insertions ) ? new SMWSemanticData( $subject ): $insertions;
 303+ $this->deletions = is_null( $deletions ) ? new SMWSemanticData( $subject ): $deletions;
 304+
 305+ $this->id = $id;
 306+ $this->edit = $edit;
 307+ }
 308+
 309+ /**
 310+ * Rteurns if the change set contains (changes for) user defined properties.
 311+ *
 312+ * @since 0.1
 313+ *
 314+ * @return boolean
 315+ */
 316+ public function hasUserDefinedProperties() {
 317+ $properties = array();
 318+
 319+ foreach ( $this->getAllProperties() as /* SMWDIProperty */ $property ) {
 320+ if ( $property->isUserDefined() ) {
 321+ $properties[] = $property;
 322+ }
 323+ }
 324+
 325+ return count( $properties ) != 0;
 326+ }
 327+
 328+ /**
 329+ * Returns whether the set contains any changes.
 330+ *
 331+ * @since 0.1
 332+ *
 333+ * @return boolean
 334+ */
 335+ public function hasChanges() {
 336+ return $this->changes->hasChanges()
 337+ || $this->insertions->hasVisibleProperties()
 338+ || $this->deletions->hasVisibleProperties();
 339+ }
 340+
 341+ /**
 342+ * Returns a SMWSemanticData object holding all inserted SMWDataItem objects.
 343+ *
 344+ * @return SMWSemanticData
 345+ */
 346+ public function getInsertions() {
 347+ return $this->insertions;
 348+ }
 349+
 350+ /**
 351+ * Returns a SMWSemanticData object holding all deleted SMWDataItem objects.
 352+ *
 353+ * @return SMWSemanticData
 354+ */
 355+ public function getDeletions() {
 356+ return $this->deletions;
 357+ }
 358+
 359+ /**
 360+ * Returns a SWLPropertyChanges object holding all SWLPropertyChange objects.
 361+ *
 362+ * @return SWLPropertyChanges
 363+ */
 364+ public function getChanges() {
 365+ return $this->changes;
 366+ }
 367+
 368+ /**
 369+ * Returns the subject these changes apply to.
 370+ *
 371+ * @return SMWDIWikiPage
 372+ */
 373+ public function getSubject() {
 374+ return $this->subject;
 375+ }
 376+
 377+ /**
 378+ * Adds a SWLPropertyChange to the set for the specified SMWDIProperty.
 379+ *
 380+ * @since 0.1
 381+ *
 382+ * @param SMWDIProperty $property
 383+ * @param SWLPropertyChange $change
 384+ */
 385+ public function addChange( SMWDIProperty $property, SWLPropertyChange $change ) {
 386+ switch ( $change->getType() ) {
 387+ case SWLPropertyChange::TYPE_UPDATE:
 388+ $this->changes->addPropertyObjectChange( $property, $change );
 389+ break;
 390+ case SWLPropertyChange::TYPE_INSERT:
 391+ $this->addInsertion( $property, $change->getNewValue() );
 392+ break;
 393+ case SWLPropertyChange::TYPE_DELETE:
 394+ $this->addDeletion( $property, $change->getOldValue() );
 395+ break;
 396+ }
 397+ }
 398+
 399+ /**
 400+ * Adds a SMWDataItem representing an insertion to the set for the specified SMWDIProperty.
 401+ *
 402+ * @since 0.1
 403+ *
 404+ * @param SMWDIProperty $property
 405+ * @param SMWDataItem $dataItem
 406+ */
 407+ public function addInsertion( SMWDIProperty $property, SMWDataItem $dataItem ) {
 408+ $this->insertions->addPropertyObjectValue( $property, $dataItem );
 409+ }
 410+
 411+ /**
 412+ * Adds a SMWDataItem representing a deletion to the set for the specified SMWDIProperty.
 413+ *
 414+ * @since 0.1
 415+ *
 416+ * @param SMWDIProperty $property
 417+ * @param SMWDataItem $dataItem
 418+ */
 419+ public function addDeletion( SMWDIProperty $property, SMWDataItem $dataItem ) {
 420+ $this->deletions->addPropertyObjectValue( $property, $dataItem );
 421+ }
 422+
 423+ /**
 424+ * Returns a list of all properties.
 425+ *
 426+ * @return array of SMWDIProperty
 427+ */
 428+ public function getAllProperties() {
 429+ return array_merge(
 430+ $this->getChanges()->getProperties(),
 431+ $this->getInsertions()->getProperties(),
 432+ $this->getDeletions()->getProperties()
 433+ );
 434+ }
 435+
 436+ /**
 437+ * Removes all changes for a certian property.
 438+ *
 439+ * @param SMWDIProperty $property
 440+ */
 441+ public function removeChangesForProperty( SMWDIProperty $property ) {
 442+ $this->getChanges()->removeChangesForProperty( $property );
 443+ $this->getInsertions()->removeDataForProperty( $property );
 444+ $this->getDeletions()->removeDataForProperty( $property );
 445+ }
 446+
 447+ /**
 448+ * Returns a list of ALL changes, including isertions and deletions.
 449+ *
 450+ * @param SMWDIProperty $proprety
 451+ *
 452+ * @return array of SWLPropertyChange
 453+ */
 454+ public function getAllPropertyChanges( SMWDIProperty $property ) {
 455+ $changes = array();
 456+
 457+ foreach ( $this->changes->getPropertyChanges( $property ) as /* SWLPropertyChange */ $change ) {
 458+ $changes[] = $change;
 459+ }
 460+
 461+ foreach ( $this->insertions->getPropertyValues( $property ) as /* SMWDataItem */ $dataItem ) {
 462+ $changes[] = new SWLPropertyChange( null, $dataItem );
 463+ }
 464+
 465+ foreach ( $this->deletions->getPropertyValues( $property ) as /* SMWDataItem */ $dataItem ) {
 466+ $changes[] = new SWLPropertyChange( $dataItem, null );
 467+ }
 468+
 469+ return $changes;
 470+ }
 471+
 472+ /**
 473+ * Serializes the object as an associative array, which can be passed
 474+ * to newFromArray to create a new instance.
 475+ *
 476+ * @since 0.1
 477+ *
 478+ * @return array
 479+ */
 480+ public function toArray() {
 481+ $changeSet = array(
 482+ 'id' => $this->id,
 483+ 'user_name' => $this->edit->getUserName(),
 484+ 'page_id' => $this->edit->getPageID(),
 485+ 'time' => $this->edit->getTime(),
 486+ 'editid' => $this->edit->getId(),
 487+ 'changes' => array()
 488+ );
 489+
 490+ foreach ( $this->getAllProperties() as /* SMWDIProperty */ $property ) {
 491+ $propChanges = array();
 492+
 493+ foreach ( $this->getAllPropertyChanges( $property ) as /* SWLPropertyChange */ $change ) {
 494+ $propChange = array();
 495+
 496+ if ( is_object( $change->getOldValue() ) ) {
 497+ $propChange['old'] = $change->getOldValue()->getSerialization();
 498+ }
 499+
 500+ if ( is_object( $change->getNewValue() ) ) {
 501+ $propChange['new'] = $change->getNewValue()->getSerialization();
 502+ }
 503+
 504+ $propChanges[] = $propChange;
 505+ }
 506+
 507+ $changeSet['changes'][$property->getSerialization()] = $propChanges;
 508+ }
 509+
 510+ return $changeSet;
 511+ }
 512+
 513+ /**
 514+ * Save the change set to the database.
 515+ *
 516+ * @since 0.1
 517+ *
 518+ * @param array of SWLGroup
 519+ * @param integer $editId
 520+ *
 521+ * @return integer ID of the inserted row (0 if nothing was inserted).
 522+ */
 523+ public function writeToStore( array $groupsToAssociate, $editId ) {
 524+ if ( !$this->hasUserDefinedProperties() ) {
 525+ return 0;
 526+ }
 527+
 528+ wfRunHooks( 'SWLBeforeChangeSetInsert', array( &$this, &$groupsToAssociate, &$editId ) );
 529+
 530+ $dbw = wfGetDB( DB_MASTER );
 531+
 532+ $dbw->insert(
 533+ 'swl_sets',
 534+ array( 'set_id' => null )
 535+ );
 536+
 537+ $id = $dbw->insertId();
 538+
 539+ $dbw->insert(
 540+ 'swl_sets_per_edit',
 541+ array(
 542+ 'spe_set_id' => $id,
 543+ 'spe_edit_id' => $editId
 544+ )
 545+ );
 546+
 547+ $changes = array();
 548+
 549+ foreach ( $this->getAllProperties() as /* SMWDIProperty */ $property ) {
 550+ if ( $property->isUserDefined() ) {
 551+ $propSerialization = $property->getSerialization();
 552+
 553+ foreach ( $this->getChanges()->getPropertyChanges( $property ) as /* SWLPropertyChange */ $change ) {
 554+ $changes[] = array(
 555+ 'property' => $propSerialization,
 556+ 'old' => $change->getOldValue()->getSerialization(),
 557+ 'new' => $change->getNewValue()->getSerialization()
 558+ );
 559+ }
 560+
 561+ foreach ( $this->getInsertions()->getPropertyValues( $property ) as /* SMWDataItem */ $dataItem ) {
 562+ $changes[] = array(
 563+ 'property' => $propSerialization,
 564+ 'old' => null,
 565+ 'new' => $dataItem->getSerialization()
 566+ );
 567+ }
 568+
 569+ foreach ( $this->getDeletions()->getPropertyValues( $property ) as /* SMWDataItem */ $dataItem ) {
 570+ $changes[] = array(
 571+ 'property' => $propSerialization,
 572+ 'old' => $dataItem->getSerialization(),
 573+ 'new' => null
 574+ );
 575+ }
 576+ }
 577+ }
 578+
 579+ $dbw->begin();
 580+
 581+ foreach ( $changes as $change ) {
 582+ if ( $change['property'] == '' ) {
 583+ // When removing the last value for a property of a page,
 584+ // for some reason it gets inserted for a property without
 585+ // name, so skip that. Better to fix higher up though.
 586+ continue;
 587+ }
 588+
 589+ $dbw->insert(
 590+ 'swl_changes',
 591+ array(
 592+ 'change_set_id' => $id,
 593+ 'change_property' => $change['property'],
 594+ 'change_old_value' => $change['old'],
 595+ 'change_new_value' => $change['new']
 596+ )
 597+ );
 598+ }
 599+
 600+ foreach ( $groupsToAssociate as /* SWLGroup */ $group ) {
 601+ $dbw->insert(
 602+ 'swl_sets_per_group',
 603+ array(
 604+ 'spg_group_id' => $group->getId(),
 605+ 'spg_set_id' => $id
 606+ )
 607+ );
 608+ }
 609+
 610+ $dbw->commit();
 611+
 612+ wfRunHooks( 'SWLAfterChangeSetInsert', array( &$this, $groupsToAssociate, $editId ) );
 613+
 614+ return $id;
 615+ }
 616+
 617+ /**
 618+ * Gets the title of the page these changes belong to.
 619+ *
 620+ * @since 0.1
 621+ *
 622+ * @return Title
 623+ */
 624+ public function getTitle() {
 625+ if ( $this->title === false ) {
 626+ $this->title = Title::makeTitle( $this->getSubject()->getNamespace(), $this->getSubject()->getDBkey() );
 627+ }
 628+
 629+ return $this->title;
 630+ }
 631+
 632+ /**
 633+ * Gets the edit this set of changes belong to.
 634+ *
 635+ * @since 0.1
 636+ *
 637+ * @return SWLEdit
 638+ */
 639+ public function getEdit() {
 640+ return $this->edit;
 641+ }
 642+
 643+ /**
 644+ * Sets the edit this set of changes belong to.
 645+ *
 646+ * @since 0.1
 647+ *
 648+ * @param SWLEdit $edit
 649+ */
 650+ public function setEdit( SWLEdit $edit ) {
 651+ $this->edit = $edit;
 652+ }
 653+
 654+ /**
 655+ * Returns if a certain insertion is present in the set of changes.
 656+ *
 657+ * @since 0.1
 658+ *
 659+ * @param SMWDIProperty $property
 660+ * @param string $value
 661+ *
 662+ * @return boolean
 663+ */
 664+ public function hasInsertion( SMWDIProperty $property, $value ) {
 665+ $has = false;
 666+
 667+ foreach ( $this->insertions->getPropertyValues( $property ) as /* SMWDataItem */ $insertion ) {
 668+ if ( $insertion->getSerialization() == $value ) {
 669+ $has = true;
 670+ break;
 671+ }
 672+ }
 673+
 674+ return $has;
 675+ }
 676+
 677+ /**
 678+ * Returns if a certain insertion is present in the set of changes.
 679+ *
 680+ * @since 0.1
 681+ *
 682+ * @param SMWDIProperty $property
 683+ * @param string $value
 684+ *
 685+ * @return boolean
 686+ */
 687+ public function hasDeletion( SMWDIProperty $property, $value ) {
 688+ $has = false;
 689+
 690+ foreach ( $this->deletions->getPropertyValues( $property ) as /* SMWDataItem */ $deletion ) {
 691+ if ( $deletion->getSerialization() == $value ) {
 692+ $has = true;
 693+ break;
 694+ }
 695+ }
 696+
 697+ return $has;
 698+ }
 699+
 700+ /**
 701+ * Returns if a certain change is present in the set of changes.
 702+ *
 703+ * @since 0.1
 704+ *
 705+ * @param SMWDIProperty $property
 706+ * @param SWLPropertyChange $change
 707+ *
 708+ * @return boolean
 709+ */
 710+ public function hasChange( SMWDIProperty $property, SWLPropertyChange $change ) {
 711+ $has = false;
 712+
 713+ foreach ( $this->changes->getPropertyChanges( $property ) as /* SWLPropertyChange */ $propChange ) {
 714+ if ( $propChange->getSerialization() == $change->getSerialization() ) {
 715+ $has = true;
 716+ break;
 717+ }
 718+ }
 719+
 720+ return $has;
 721+ }
 722+
 723+ /**
 724+ * Merges in the changes of another change set.
 725+ * Duplicate changes are detected and only kept as a single change.
 726+ * This is usefull for merging sets with (possibly overlapping) changes belonging to a single edit.
 727+ *
 728+ * @since 0.1
 729+ *
 730+ * @param SWLChangeSet $set
 731+ */
 732+ public function mergeInChangeSet( SWLChangeSet $set ) {
 733+ foreach ( $set->getAllProperties() as $property ) {
 734+ foreach ( $set->getChanges()->getPropertyChanges( $property ) as /* SWLPropertyChange */ $change ) {
 735+ if ( !$this->hasChange( $property, $change ) ) {
 736+ $this->addChange( $property, $change );
 737+ }
 738+ }
 739+
 740+ foreach ( $set->getInsertions()->getPropertyValues( $property ) as /* SMWDataItem */ $dataItem ) {
 741+ if ( !$this->hasInsertion( $property, $dataItem ) ) {
 742+ $this->addInsertion( $property, $dataItem );
 743+ }
 744+ }
 745+
 746+ foreach ( $set->getDeletions()->getPropertyValues( $property ) as /* SMWDataItem */ $dataItem ) {
 747+ if ( !$this->hasInsertion( $property, $dataItem ) ) {
 748+ $this->addDeletion( $property, $dataItem );
 749+ }
 750+ }
 751+ }
 752+ }
 753+
 754+}
\ No newline at end of file
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/includes/SWL_ChangeSet.php
___________________________________________________________________
Added: svn:eol-style
1755 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/COPYING
@@ -0,0 +1,682 @@
 2+The license text below "----" applies to all files within this distribution, other
 3+than those that are in a directory which contains files named "LICENSE" or
 4+"COPYING", or a subdirectory thereof. For those files, the license text contained in
 5+said file overrides any license information contained in directories of smaller depth.
 6+Alternative licenses are typically used for software that is provided by external
 7+parties, and merely packaged with this software for convenience.
 8+----
 9+
 10+ GNU GENERAL PUBLIC LICENSE
 11+ Version 3, 29 June 2007
 12+
 13+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 14+ Everyone is permitted to copy and distribute verbatim copies
 15+ of this license document, but changing it is not allowed.
 16+
 17+ Preamble
 18+
 19+ The GNU General Public License is a free, copyleft license for
 20+software and other kinds of works.
 21+
 22+ The licenses for most software and other practical works are designed
 23+to take away your freedom to share and change the works. By contrast,
 24+the GNU General Public License is intended to guarantee your freedom to
 25+share and change all versions of a program--to make sure it remains free
 26+software for all its users. We, the Free Software Foundation, use the
 27+GNU General Public License for most of our software; it applies also to
 28+any other work released this way by its authors. You can apply it to
 29+your programs, too.
 30+
 31+ When we speak of free software, we are referring to freedom, not
 32+price. Our General Public Licenses are designed to make sure that you
 33+have the freedom to distribute copies of free software (and charge for
 34+them if you wish), that you receive source code or can get it if you
 35+want it, that you can change the software or use pieces of it in new
 36+free programs, and that you know you can do these things.
 37+
 38+ To protect your rights, we need to prevent others from denying you
 39+these rights or asking you to surrender the rights. Therefore, you have
 40+certain responsibilities if you distribute copies of the software, or if
 41+you modify it: responsibilities to respect the freedom of others.
 42+
 43+ For example, if you distribute copies of such a program, whether
 44+gratis or for a fee, you must pass on to the recipients the same
 45+freedoms that you received. You must make sure that they, too, receive
 46+or can get the source code. And you must show them these terms so they
 47+know their rights.
 48+
 49+ Developers that use the GNU GPL protect your rights with two steps:
 50+(1) assert copyright on the software, and (2) offer you this License
 51+giving you legal permission to copy, distribute and/or modify it.
 52+
 53+ For the developers' and authors' protection, the GPL clearly explains
 54+that there is no warranty for this free software. For both users' and
 55+authors' sake, the GPL requires that modified versions be marked as
 56+changed, so that their problems will not be attributed erroneously to
 57+authors of previous versions.
 58+
 59+ Some devices are designed to deny users access to install or run
 60+modified versions of the software inside them, although the manufacturer
 61+can do so. This is fundamentally incompatible with the aim of
 62+protecting users' freedom to change the software. The systematic
 63+pattern of such abuse occurs in the area of products for individuals to
 64+use, which is precisely where it is most unacceptable. Therefore, we
 65+have designed this version of the GPL to prohibit the practice for those
 66+products. If such problems arise substantially in other domains, we
 67+stand ready to extend this provision to those domains in future versions
 68+of the GPL, as needed to protect the freedom of users.
 69+
 70+ Finally, every program is threatened constantly by software patents.
 71+States should not allow patents to restrict development and use of
 72+software on general-purpose computers, but in those that do, we wish to
 73+avoid the special danger that patents applied to a free program could
 74+make it effectively proprietary. To prevent this, the GPL assures that
 75+patents cannot be used to render the program non-free.
 76+
 77+ The precise terms and conditions for copying, distribution and
 78+modification follow.
 79+
 80+ TERMS AND CONDITIONS
 81+
 82+ 0. Definitions.
 83+
 84+ "This License" refers to version 3 of the GNU General Public License.
 85+
 86+ "Copyright" also means copyright-like laws that apply to other kinds of
 87+works, such as semiconductor masks.
 88+
 89+ "The Program" refers to any copyrightable work licensed under this
 90+License. Each licensee is addressed as "you". "Licensees" and
 91+"recipients" may be individuals or organizations.
 92+
 93+ To "modify" a work means to copy from or adapt all or part of the work
 94+in a fashion requiring copyright permission, other than the making of an
 95+exact copy. The resulting work is called a "modified version" of the
 96+earlier work or a work "based on" the earlier work.
 97+
 98+ A "covered work" means either the unmodified Program or a work based
 99+on the Program.
 100+
 101+ To "propagate" a work means to do anything with it that, without
 102+permission, would make you directly or secondarily liable for
 103+infringement under applicable copyright law, except executing it on a
 104+computer or modifying a private copy. Propagation includes copying,
 105+distribution (with or without modification), making available to the
 106+public, and in some countries other activities as well.
 107+
 108+ To "convey" a work means any kind of propagation that enables other
 109+parties to make or receive copies. Mere interaction with a user through
 110+a computer network, with no transfer of a copy, is not conveying.
 111+
 112+ An interactive user interface displays "Appropriate Legal Notices"
 113+to the extent that it includes a convenient and prominently visible
 114+feature that (1) displays an appropriate copyright notice, and (2)
 115+tells the user that there is no warranty for the work (except to the
 116+extent that warranties are provided), that licensees may convey the
 117+work under this License, and how to view a copy of this License. If
 118+the interface presents a list of user commands or options, such as a
 119+menu, a prominent item in the list meets this criterion.
 120+
 121+ 1. Source Code.
 122+
 123+ The "source code" for a work means the preferred form of the work
 124+for making modifications to it. "Object code" means any non-source
 125+form of a work.
 126+
 127+ A "Standard Interface" means an interface that either is an official
 128+standard defined by a recognized standards body, or, in the case of
 129+interfaces specified for a particular programming language, one that
 130+is widely used among developers working in that language.
 131+
 132+ The "System Libraries" of an executable work include anything, other
 133+than the work as a whole, that (a) is included in the normal form of
 134+packaging a Major Component, but which is not part of that Major
 135+Component, and (b) serves only to enable use of the work with that
 136+Major Component, or to implement a Standard Interface for which an
 137+implementation is available to the public in source code form. A
 138+"Major Component", in this context, means a major essential component
 139+(kernel, window system, and so on) of the specific operating system
 140+(if any) on which the executable work runs, or a compiler used to
 141+produce the work, or an object code interpreter used to run it.
 142+
 143+ The "Corresponding Source" for a work in object code form means all
 144+the source code needed to generate, install, and (for an executable
 145+work) run the object code and to modify the work, including scripts to
 146+control those activities. However, it does not include the work's
 147+System Libraries, or general-purpose tools or generally available free
 148+programs which are used unmodified in performing those activities but
 149+which are not part of the work. For example, Corresponding Source
 150+includes interface definition files associated with source files for
 151+the work, and the source code for shared libraries and dynamically
 152+linked subprograms that the work is specifically designed to require,
 153+such as by intimate data communication or control flow between those
 154+subprograms and other parts of the work.
 155+
 156+ The Corresponding Source need not include anything that users
 157+can regenerate automatically from other parts of the Corresponding
 158+Source.
 159+
 160+ The Corresponding Source for a work in source code form is that
 161+same work.
 162+
 163+ 2. Basic Permissions.
 164+
 165+ All rights granted under this License are granted for the term of
 166+copyright on the Program, and are irrevocable provided the stated
 167+conditions are met. This License explicitly affirms your unlimited
 168+permission to run the unmodified Program. The output from running a
 169+covered work is covered by this License only if the output, given its
 170+content, constitutes a covered work. This License acknowledges your
 171+rights of fair use or other equivalent, as provided by copyright law.
 172+
 173+ You may make, run and propagate covered works that you do not
 174+convey, without conditions so long as your license otherwise remains
 175+in force. You may convey covered works to others for the sole purpose
 176+of having them make modifications exclusively for you, or provide you
 177+with facilities for running those works, provided that you comply with
 178+the terms of this License in conveying all material for which you do
 179+not control copyright. Those thus making or running the covered works
 180+for you must do so exclusively on your behalf, under your direction
 181+and control, on terms that prohibit them from making any copies of
 182+your copyrighted material outside their relationship with you.
 183+
 184+ Conveying under any other circumstances is permitted solely under
 185+the conditions stated below. Sublicensing is not allowed; section 10
 186+makes it unnecessary.
 187+
 188+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
 189+
 190+ No covered work shall be deemed part of an effective technological
 191+measure under any applicable law fulfilling obligations under article
 192+11 of the WIPO copyright treaty adopted on 20 December 1996, or
 193+similar laws prohibiting or restricting circumvention of such
 194+measures.
 195+
 196+ When you convey a covered work, you waive any legal power to forbid
 197+circumvention of technological measures to the extent such circumvention
 198+is effected by exercising rights under this License with respect to
 199+the covered work, and you disclaim any intention to limit operation or
 200+modification of the work as a means of enforcing, against the work's
 201+users, your or third parties' legal rights to forbid circumvention of
 202+technological measures.
 203+
 204+ 4. Conveying Verbatim Copies.
 205+
 206+ You may convey verbatim copies of the Program's source code as you
 207+receive it, in any medium, provided that you conspicuously and
 208+appropriately publish on each copy an appropriate copyright notice;
 209+keep intact all notices stating that this License and any
 210+non-permissive terms added in accord with section 7 apply to the code;
 211+keep intact all notices of the absence of any warranty; and give all
 212+recipients a copy of this License along with the Program.
 213+
 214+ You may charge any price or no price for each copy that you convey,
 215+and you may offer support or warranty protection for a fee.
 216+
 217+ 5. Conveying Modified Source Versions.
 218+
 219+ You may convey a work based on the Program, or the modifications to
 220+produce it from the Program, in the form of source code under the
 221+terms of section 4, provided that you also meet all of these conditions:
 222+
 223+ a) The work must carry prominent notices stating that you modified
 224+ it, and giving a relevant date.
 225+
 226+ b) The work must carry prominent notices stating that it is
 227+ released under this License and any conditions added under section
 228+ 7. This requirement modifies the requirement in section 4 to
 229+ "keep intact all notices".
 230+
 231+ c) You must license the entire work, as a whole, under this
 232+ License to anyone who comes into possession of a copy. This
 233+ License will therefore apply, along with any applicable section 7
 234+ additional terms, to the whole of the work, and all its parts,
 235+ regardless of how they are packaged. This License gives no
 236+ permission to license the work in any other way, but it does not
 237+ invalidate such permission if you have separately received it.
 238+
 239+ d) If the work has interactive user interfaces, each must display
 240+ Appropriate Legal Notices; however, if the Program has interactive
 241+ interfaces that do not display Appropriate Legal Notices, your
 242+ work need not make them do so.
 243+
 244+ A compilation of a covered work with other separate and independent
 245+works, which are not by their nature extensions of the covered work,
 246+and which are not combined with it such as to form a larger program,
 247+in or on a volume of a storage or distribution medium, is called an
 248+"aggregate" if the compilation and its resulting copyright are not
 249+used to limit the access or legal rights of the compilation's users
 250+beyond what the individual works permit. Inclusion of a covered work
 251+in an aggregate does not cause this License to apply to the other
 252+parts of the aggregate.
 253+
 254+ 6. Conveying Non-Source Forms.
 255+
 256+ You may convey a covered work in object code form under the terms
 257+of sections 4 and 5, provided that you also convey the
 258+machine-readable Corresponding Source under the terms of this License,
 259+in one of these ways:
 260+
 261+ a) Convey the object code in, or embodied in, a physical product
 262+ (including a physical distribution medium), accompanied by the
 263+ Corresponding Source fixed on a durable physical medium
 264+ customarily used for software interchange.
 265+
 266+ b) Convey the object code in, or embodied in, a physical product
 267+ (including a physical distribution medium), accompanied by a
 268+ written offer, valid for at least three years and valid for as
 269+ long as you offer spare parts or customer support for that product
 270+ model, to give anyone who possesses the object code either (1) a
 271+ copy of the Corresponding Source for all the software in the
 272+ product that is covered by this License, on a durable physical
 273+ medium customarily used for software interchange, for a price no
 274+ more than your reasonable cost of physically performing this
 275+ conveying of source, or (2) access to copy the
 276+ Corresponding Source from a network server at no charge.
 277+
 278+ c) Convey individual copies of the object code with a copy of the
 279+ written offer to provide the Corresponding Source. This
 280+ alternative is allowed only occasionally and noncommercially, and
 281+ only if you received the object code with such an offer, in accord
 282+ with subsection 6b.
 283+
 284+ d) Convey the object code by offering access from a designated
 285+ place (gratis or for a charge), and offer equivalent access to the
 286+ Corresponding Source in the same way through the same place at no
 287+ further charge. You need not require recipients to copy the
 288+ Corresponding Source along with the object code. If the place to
 289+ copy the object code is a network server, the Corresponding Source
 290+ may be on a different server (operated by you or a third party)
 291+ that supports equivalent copying facilities, provided you maintain
 292+ clear directions next to the object code saying where to find the
 293+ Corresponding Source. Regardless of what server hosts the
 294+ Corresponding Source, you remain obligated to ensure that it is
 295+ available for as long as needed to satisfy these requirements.
 296+
 297+ e) Convey the object code using peer-to-peer transmission, provided
 298+ you inform other peers where the object code and Corresponding
 299+ Source of the work are being offered to the general public at no
 300+ charge under subsection 6d.
 301+
 302+ A separable portion of the object code, whose source code is excluded
 303+from the Corresponding Source as a System Library, need not be
 304+included in conveying the object code work.
 305+
 306+ A "User Product" is either (1) a "consumer product", which means any
 307+tangible personal property which is normally used for personal, family,
 308+or household purposes, or (2) anything designed or sold for incorporation
 309+into a dwelling. In determining whether a product is a consumer product,
 310+doubtful cases shall be resolved in favor of coverage. For a particular
 311+product received by a particular user, "normally used" refers to a
 312+typical or common use of that class of product, regardless of the status
 313+of the particular user or of the way in which the particular user
 314+actually uses, or expects or is expected to use, the product. A product
 315+is a consumer product regardless of whether the product has substantial
 316+commercial, industrial or non-consumer uses, unless such uses represent
 317+the only significant mode of use of the product.
 318+
 319+ "Installation Information" for a User Product means any methods,
 320+procedures, authorization keys, or other information required to install
 321+and execute modified versions of a covered work in that User Product from
 322+a modified version of its Corresponding Source. The information must
 323+suffice to ensure that the continued functioning of the modified object
 324+code is in no case prevented or interfered with solely because
 325+modification has been made.
 326+
 327+ If you convey an object code work under this section in, or with, or
 328+specifically for use in, a User Product, and the conveying occurs as
 329+part of a transaction in which the right of possession and use of the
 330+User Product is transferred to the recipient in perpetuity or for a
 331+fixed term (regardless of how the transaction is characterized), the
 332+Corresponding Source conveyed under this section must be accompanied
 333+by the Installation Information. But this requirement does not apply
 334+if neither you nor any third party retains the ability to install
 335+modified object code on the User Product (for example, the work has
 336+been installed in ROM).
 337+
 338+ The requirement to provide Installation Information does not include a
 339+requirement to continue to provide support service, warranty, or updates
 340+for a work that has been modified or installed by the recipient, or for
 341+the User Product in which it has been modified or installed. Access to a
 342+network may be denied when the modification itself materially and
 343+adversely affects the operation of the network or violates the rules and
 344+protocols for communication across the network.
 345+
 346+ Corresponding Source conveyed, and Installation Information provided,
 347+in accord with this section must be in a format that is publicly
 348+documented (and with an implementation available to the public in
 349+source code form), and must require no special password or key for
 350+unpacking, reading or copying.
 351+
 352+ 7. Additional Terms.
 353+
 354+ "Additional permissions" are terms that supplement the terms of this
 355+License by making exceptions from one or more of its conditions.
 356+Additional permissions that are applicable to the entire Program shall
 357+be treated as though they were included in this License, to the extent
 358+that they are valid under applicable law. If additional permissions
 359+apply only to part of the Program, that part may be used separately
 360+under those permissions, but the entire Program remains governed by
 361+this License without regard to the additional permissions.
 362+
 363+ When you convey a copy of a covered work, you may at your option
 364+remove any additional permissions from that copy, or from any part of
 365+it. (Additional permissions may be written to require their own
 366+removal in certain cases when you modify the work.) You may place
 367+additional permissions on material, added by you to a covered work,
 368+for which you have or can give appropriate copyright permission.
 369+
 370+ Notwithstanding any other provision of this License, for material you
 371+add to a covered work, you may (if authorized by the copyright holders of
 372+that material) supplement the terms of this License with terms:
 373+
 374+ a) Disclaiming warranty or limiting liability differently from the
 375+ terms of sections 15 and 16 of this License; or
 376+
 377+ b) Requiring preservation of specified reasonable legal notices or
 378+ author attributions in that material or in the Appropriate Legal
 379+ Notices displayed by works containing it; or
 380+
 381+ c) Prohibiting misrepresentation of the origin of that material, or
 382+ requiring that modified versions of such material be marked in
 383+ reasonable ways as different from the original version; or
 384+
 385+ d) Limiting the use for publicity purposes of names of licensors or
 386+ authors of the material; or
 387+
 388+ e) Declining to grant rights under trademark law for use of some
 389+ trade names, trademarks, or service marks; or
 390+
 391+ f) Requiring indemnification of licensors and authors of that
 392+ material by anyone who conveys the material (or modified versions of
 393+ it) with contractual assumptions of liability to the recipient, for
 394+ any liability that these contractual assumptions directly impose on
 395+ those licensors and authors.
 396+
 397+ All other non-permissive additional terms are considered "further
 398+restrictions" within the meaning of section 10. If the Program as you
 399+received it, or any part of it, contains a notice stating that it is
 400+governed by this License along with a term that is a further
 401+restriction, you may remove that term. If a license document contains
 402+a further restriction but permits relicensing or conveying under this
 403+License, you may add to a covered work material governed by the terms
 404+of that license document, provided that the further restriction does
 405+not survive such relicensing or conveying.
 406+
 407+ If you add terms to a covered work in accord with this section, you
 408+must place, in the relevant source files, a statement of the
 409+additional terms that apply to those files, or a notice indicating
 410+where to find the applicable terms.
 411+
 412+ Additional terms, permissive or non-permissive, may be stated in the
 413+form of a separately written license, or stated as exceptions;
 414+the above requirements apply either way.
 415+
 416+ 8. Termination.
 417+
 418+ You may not propagate or modify a covered work except as expressly
 419+provided under this License. Any attempt otherwise to propagate or
 420+modify it is void, and will automatically terminate your rights under
 421+this License (including any patent licenses granted under the third
 422+paragraph of section 11).
 423+
 424+ However, if you cease all violation of this License, then your
 425+license from a particular copyright holder is reinstated (a)
 426+provisionally, unless and until the copyright holder explicitly and
 427+finally terminates your license, and (b) permanently, if the copyright
 428+holder fails to notify you of the violation by some reasonable means
 429+prior to 60 days after the cessation.
 430+
 431+ Moreover, your license from a particular copyright holder is
 432+reinstated permanently if the copyright holder notifies you of the
 433+violation by some reasonable means, this is the first time you have
 434+received notice of violation of this License (for any work) from that
 435+copyright holder, and you cure the violation prior to 30 days after
 436+your receipt of the notice.
 437+
 438+ Termination of your rights under this section does not terminate the
 439+licenses of parties who have received copies or rights from you under
 440+this License. If your rights have been terminated and not permanently
 441+reinstated, you do not qualify to receive new licenses for the same
 442+material under section 10.
 443+
 444+ 9. Acceptance Not Required for Having Copies.
 445+
 446+ You are not required to accept this License in order to receive or
 447+run a copy of the Program. Ancillary propagation of a covered work
 448+occurring solely as a consequence of using peer-to-peer transmission
 449+to receive a copy likewise does not require acceptance. However,
 450+nothing other than this License grants you permission to propagate or
 451+modify any covered work. These actions infringe copyright if you do
 452+not accept this License. Therefore, by modifying or propagating a
 453+covered work, you indicate your acceptance of this License to do so.
 454+
 455+ 10. Automatic Licensing of Downstream Recipients.
 456+
 457+ Each time you convey a covered work, the recipient automatically
 458+receives a license from the original licensors, to run, modify and
 459+propagate that work, subject to this License. You are not responsible
 460+for enforcing compliance by third parties with this License.
 461+
 462+ An "entity transaction" is a transaction transferring control of an
 463+organization, or substantially all assets of one, or subdividing an
 464+organization, or merging organizations. If propagation of a covered
 465+work results from an entity transaction, each party to that
 466+transaction who receives a copy of the work also receives whatever
 467+licenses to the work the party's predecessor in interest had or could
 468+give under the previous paragraph, plus a right to possession of the
 469+Corresponding Source of the work from the predecessor in interest, if
 470+the predecessor has it or can get it with reasonable efforts.
 471+
 472+ You may not impose any further restrictions on the exercise of the
 473+rights granted or affirmed under this License. For example, you may
 474+not impose a license fee, royalty, or other charge for exercise of
 475+rights granted under this License, and you may not initiate litigation
 476+(including a cross-claim or counterclaim in a lawsuit) alleging that
 477+any patent claim is infringed by making, using, selling, offering for
 478+sale, or importing the Program or any portion of it.
 479+
 480+ 11. Patents.
 481+
 482+ A "contributor" is a copyright holder who authorizes use under this
 483+License of the Program or a work on which the Program is based. The
 484+work thus licensed is called the contributor's "contributor version".
 485+
 486+ A contributor's "essential patent claims" are all patent claims
 487+owned or controlled by the contributor, whether already acquired or
 488+hereafter acquired, that would be infringed by some manner, permitted
 489+by this License, of making, using, or selling its contributor version,
 490+but do not include claims that would be infringed only as a
 491+consequence of further modification of the contributor version. For
 492+purposes of this definition, "control" includes the right to grant
 493+patent sublicenses in a manner consistent with the requirements of
 494+this License.
 495+
 496+ Each contributor grants you a non-exclusive, worldwide, royalty-free
 497+patent license under the contributor's essential patent claims, to
 498+make, use, sell, offer for sale, import and otherwise run, modify and
 499+propagate the contents of its contributor version.
 500+
 501+ In the following three paragraphs, a "patent license" is any express
 502+agreement or commitment, however denominated, not to enforce a patent
 503+(such as an express permission to practice a patent or covenant not to
 504+sue for patent infringement). To "grant" such a patent license to a
 505+party means to make such an agreement or commitment not to enforce a
 506+patent against the party.
 507+
 508+ If you convey a covered work, knowingly relying on a patent license,
 509+and the Corresponding Source of the work is not available for anyone
 510+to copy, free of charge and under the terms of this License, through a
 511+publicly available network server or other readily accessible means,
 512+then you must either (1) cause the Corresponding Source to be so
 513+available, or (2) arrange to deprive yourself of the benefit of the
 514+patent license for this particular work, or (3) arrange, in a manner
 515+consistent with the requirements of this License, to extend the patent
 516+license to downstream recipients. "Knowingly relying" means you have
 517+actual knowledge that, but for the patent license, your conveying the
 518+covered work in a country, or your recipient's use of the covered work
 519+in a country, would infringe one or more identifiable patents in that
 520+country that you have reason to believe are valid.
 521+
 522+ If, pursuant to or in connection with a single transaction or
 523+arrangement, you convey, or propagate by procuring conveyance of, a
 524+covered work, and grant a patent license to some of the parties
 525+receiving the covered work authorizing them to use, propagate, modify
 526+or convey a specific copy of the covered work, then the patent license
 527+you grant is automatically extended to all recipients of the covered
 528+work and works based on it.
 529+
 530+ A patent license is "discriminatory" if it does not include within
 531+the scope of its coverage, prohibits the exercise of, or is
 532+conditioned on the non-exercise of one or more of the rights that are
 533+specifically granted under this License. You may not convey a covered
 534+work if you are a party to an arrangement with a third party that is
 535+in the business of distributing software, under which you make payment
 536+to the third party based on the extent of your activity of conveying
 537+the work, and under which the third party grants, to any of the
 538+parties who would receive the covered work from you, a discriminatory
 539+patent license (a) in connection with copies of the covered work
 540+conveyed by you (or copies made from those copies), or (b) primarily
 541+for and in connection with specific products or compilations that
 542+contain the covered work, unless you entered into that arrangement,
 543+or that patent license was granted, prior to 28 March 2007.
 544+
 545+ Nothing in this License shall be construed as excluding or limiting
 546+any implied license or other defenses to infringement that may
 547+otherwise be available to you under applicable patent law.
 548+
 549+ 12. No Surrender of Others' Freedom.
 550+
 551+ If conditions are imposed on you (whether by court order, agreement or
 552+otherwise) that contradict the conditions of this License, they do not
 553+excuse you from the conditions of this License. If you cannot convey a
 554+covered work so as to satisfy simultaneously your obligations under this
 555+License and any other pertinent obligations, then as a consequence you may
 556+not convey it at all. For example, if you agree to terms that obligate you
 557+to collect a royalty for further conveying from those to whom you convey
 558+the Program, the only way you could satisfy both those terms and this
 559+License would be to refrain entirely from conveying the Program.
 560+
 561+ 13. Use with the GNU Affero General Public License.
 562+
 563+ Notwithstanding any other provision of this License, you have
 564+permission to link or combine any covered work with a work licensed
 565+under version 3 of the GNU Affero General Public License into a single
 566+combined work, and to convey the resulting work. The terms of this
 567+License will continue to apply to the part which is the covered work,
 568+but the special requirements of the GNU Affero General Public License,
 569+section 13, concerning interaction through a network will apply to the
 570+combination as such.
 571+
 572+ 14. Revised Versions of this License.
 573+
 574+ The Free Software Foundation may publish revised and/or new versions of
 575+the GNU General Public License from time to time. Such new versions will
 576+be similar in spirit to the present version, but may differ in detail to
 577+address new problems or concerns.
 578+
 579+ Each version is given a distinguishing version number. If the
 580+Program specifies that a certain numbered version of the GNU General
 581+Public License "or any later version" applies to it, you have the
 582+option of following the terms and conditions either of that numbered
 583+version or of any later version published by the Free Software
 584+Foundation. If the Program does not specify a version number of the
 585+GNU General Public License, you may choose any version ever published
 586+by the Free Software Foundation.
 587+
 588+ If the Program specifies that a proxy can decide which future
 589+versions of the GNU General Public License can be used, that proxy's
 590+public statement of acceptance of a version permanently authorizes you
 591+to choose that version for the Program.
 592+
 593+ Later license versions may give you additional or different
 594+permissions. However, no additional obligations are imposed on any
 595+author or copyright holder as a result of your choosing to follow a
 596+later version.
 597+
 598+ 15. Disclaimer of Warranty.
 599+
 600+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
 601+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
 602+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
 603+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
 604+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 605+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
 606+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
 607+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
 608+
 609+ 16. Limitation of Liability.
 610+
 611+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
 612+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
 613+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
 614+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
 615+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
 616+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
 617+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
 618+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
 619+SUCH DAMAGES.
 620+
 621+ 17. Interpretation of Sections 15 and 16.
 622+
 623+ If the disclaimer of warranty and limitation of liability provided
 624+above cannot be given local legal effect according to their terms,
 625+reviewing courts shall apply local law that most closely approximates
 626+an absolute waiver of all civil liability in connection with the
 627+Program, unless a warranty or assumption of liability accompanies a
 628+copy of the Program in return for a fee.
 629+
 630+ END OF TERMS AND CONDITIONS
 631+
 632+ How to Apply These Terms to Your New Programs
 633+
 634+ If you develop a new program, and you want it to be of the greatest
 635+possible use to the public, the best way to achieve this is to make it
 636+free software which everyone can redistribute and change under these terms.
 637+
 638+ To do so, attach the following notices to the program. It is safest
 639+to attach them to the start of each source file to most effectively
 640+state the exclusion of warranty; and each file should have at least
 641+the "copyright" line and a pointer to where the full notice is found.
 642+
 643+ <one line to give the program's name and a brief idea of what it does.>
 644+ Copyright (C) <year> <name of author>
 645+
 646+ This program is free software: you can redistribute it and/or modify
 647+ it under the terms of the GNU General Public License as published by
 648+ the Free Software Foundation, either version 3 of the License, or
 649+ (at your option) any later version.
 650+
 651+ This program is distributed in the hope that it will be useful,
 652+ but WITHOUT ANY WARRANTY; without even the implied warranty of
 653+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 654+ GNU General Public License for more details.
 655+
 656+ You should have received a copy of the GNU General Public License
 657+ along with this program. If not, see <http://www.gnu.org/licenses/>.
 658+
 659+Also add information on how to contact you by electronic and paper mail.
 660+
 661+ If the program does terminal interaction, make it output a short
 662+notice like this when it starts in an interactive mode:
 663+
 664+ <program> Copyright (C) <year> <name of author>
 665+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 666+ This is free software, and you are welcome to redistribute it
 667+ under certain conditions; type `show c' for details.
 668+
 669+The hypothetical commands `show w' and `show c' should show the appropriate
 670+parts of the General Public License. Of course, your program's commands
 671+might be different; for a GUI interface, you would use an "about box".
 672+
 673+ You should also get your employer (if you work as a programmer) or school,
 674+if any, to sign a "copyright disclaimer" for the program, if necessary.
 675+For more information on this, and how to apply and follow the GNU GPL, see
 676+<http://www.gnu.org/licenses/>.
 677+
 678+ The GNU General Public License does not permit incorporating your program
 679+into proprietary programs. If your program is a subroutine library, you
 680+may consider it more useful to permit linking proprietary applications with
 681+the library. If this is what you want to do, use the GNU Lesser General
 682+Public License instead of this License. But first, please read
 683+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
\ No newline at end of file
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/COPYING
___________________________________________________________________
Added: svn:eol-style
1684 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/api/ApiDeleteWatchlistGroup.php
@@ -0,0 +1,217 @@
 2+<?php
 3+
 4+/**
 5+ * API module to delete semantic watchlist groups.
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file ApiDeleteWatchlistGroup.php
 10+ * @ingroup SemanticWatchlist
 11+ * @ingroup API
 12+ *
 13+ * @licence GNU GPL v3+
 14+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 15+ *
 16+ * TODO: delete changes
 17+ */
 18+class ApiDeleteWatchlistGroup extends ApiBase {
 19+
 20+ public function __construct( $main, $action ) {
 21+ parent::__construct( $main, $action );
 22+ }
 23+
 24+ public function execute() {
 25+ global $wgUser;
 26+
 27+ if ( !$wgUser->isAllowed( 'semanticwatchgroups' ) || $wgUser->isBlocked() ) {
 28+ $this->dieUsageMsg( array( 'badaccess-groups' ) );
 29+ }
 30+
 31+ $params = $this->extractRequestParams();
 32+
 33+ $everythingOk = true;
 34+
 35+ foreach ( $params['ids'] as $id ) {
 36+ $everythingOk = $this->deleteGroup( $id ) && $everythingOk;
 37+ }
 38+
 39+ $this->getResult()->addValue(
 40+ null,
 41+ 'success',
 42+ $everythingOk
 43+ );
 44+ }
 45+
 46+ /**
 47+ * Delete the group with specified ID, and
 48+ * all linked data not used by other groups.
 49+ *
 50+ * @since 0.1
 51+ *
 52+ * @param integer $groupId
 53+ *
 54+ * @return boolean Sucess indicator
 55+ */
 56+ protected function deleteGroup( $groupId ) {
 57+ $everythingOk = true;
 58+
 59+ $dbr = wfGetDB( DB_SLAVE );
 60+
 61+ $setsForGroup = $dbr->select(
 62+ 'swl_sets_per_group',
 63+ array( 'spg_set_id' ),
 64+ array(
 65+ 'spg_group_id' => $groupId,
 66+ )
 67+ );
 68+
 69+ foreach ( $setsForGroup as $set ) {
 70+ $changes = $dbr->select(
 71+ 'swl_changes',
 72+ array( 'change_id' ),
 73+ array( 'change_set_id' => $set->spg_set_id )
 74+ );
 75+
 76+ foreach ( $changes as $change ) {
 77+ $dbr->select(
 78+ 'swl_changes_per_set',
 79+ array( 'change_id' ),
 80+ array( 'change_set_id' => $set->spg_set_id )
 81+ );
 82+ }
 83+ }
 84+
 85+ // Find all edits linked to this group.
 86+ $editsForGroup = $dbr->select(
 87+ array( 'swl_sets_per_group', 'swl_sets_per_edit' ),
 88+ array( 'spe_edit_id' ),
 89+ array(
 90+ 'spg_group_id' => $groupId,
 91+ ),
 92+ '',
 93+ array(),
 94+ array(
 95+ 'swl_sets_per_edit' => array( 'INNER JOIN', array( 'spe_set_id=spg_set_id' ) ),
 96+ )
 97+ );
 98+
 99+ $editsToDelete = array();
 100+
 101+ // For each linked edit, find all linked groups, and save those with only one (this one).
 102+ foreach ( $editsForGroup as $edit ) {
 103+ $groupsForEdit = $dbr->select(
 104+ array( 'swl_sets_per_edit', 'swl_sets_per_group', 'swl_groups' ),
 105+ array( 'spg_group_id' ),
 106+ array(
 107+ 'spe_edit_id' => $edit->spe_edit_id,
 108+ ),
 109+ '',
 110+ array(),
 111+ array(
 112+ 'swl_sets_per_group' => array( 'INNER JOIN', array( 'spg_set_id=spe_set_id' ) ),
 113+ 'swl_groups' => array( 'INNER JOIN', array( 'group_id=spg_group_id' ) ),
 114+ )
 115+ );
 116+
 117+ if ( $dbr->numRows( $groupsForEdit ) < 2 ) {
 118+ $editsToDelete[] = $edit->spe_edit_id;
 119+ }
 120+ }
 121+
 122+ $dbw = wfGetDB( DB_MASTER );
 123+ $dbw->begin();
 124+
 125+ // Delete all edits and sets per edits only linked to this group.
 126+ foreach ( $editsToDelete as $editId ) {
 127+ $dbw->delete(
 128+ 'swl_edits',
 129+ array( 'edit_id' => $editId )
 130+ );
 131+
 132+ $dbw->delete(
 133+ 'swl_sets_per_edit',
 134+ array( 'spe_edit_id' => $editId )
 135+ );
 136+ }
 137+
 138+ foreach ( $setsForGroup as $set ) {
 139+ $dbw->delete(
 140+ 'swl_sets',
 141+ array( 'set_id' => $set->spg_set_id )
 142+ );
 143+ }
 144+
 145+ // Delete sets per group links for this group.
 146+ $result = $dbw->delete(
 147+ 'swl_sets_per_group',
 148+ array( 'spg_group_id' => $groupId )
 149+ );
 150+
 151+ if ( $result === false ) {
 152+ $everythingOk = false;
 153+ }
 154+
 155+ // Delete users per group links for this group.
 156+ $result = $dbw->delete(
 157+ 'swl_users_per_group',
 158+ array( 'upg_group_id' => $groupId )
 159+ );
 160+
 161+ if ( $result === false ) {
 162+ $everythingOk = false;
 163+ }
 164+
 165+ // Delete the actual group.
 166+ $result = $dbw->delete(
 167+ 'swl_groups',
 168+ array( 'group_id' => $groupId )
 169+ );
 170+
 171+ if ( $result === false ) {
 172+ $everythingOk = false;
 173+ }
 174+
 175+ $dbw->commit();
 176+
 177+ return $everythingOk;
 178+ }
 179+
 180+ public function getAllowedParams() {
 181+ return array(
 182+ 'ids' => array(
 183+ ApiBase::PARAM_TYPE => 'integer',
 184+ ApiBase::PARAM_REQUIRED => true,
 185+ ApiBase::PARAM_ISMULTI => true,
 186+ ),
 187+ );
 188+ }
 189+
 190+ public function getParamDescription() {
 191+ return array(
 192+ 'ids' => 'The IDs of the watchlist groups to delete'
 193+ );
 194+ }
 195+
 196+ public function getDescription() {
 197+ return array(
 198+ 'API module to delete semantic watchlist groups.'
 199+ );
 200+ }
 201+
 202+ public function getPossibleErrors() {
 203+ return array_merge( parent::getPossibleErrors(), array(
 204+ array( 'missingparam', 'ids' ),
 205+ ) );
 206+ }
 207+
 208+ protected function getExamples() {
 209+ return array(
 210+ 'api.php?action=deleteswlgroup&ids=42|34',
 211+ );
 212+ }
 213+
 214+ public function getVersion() {
 215+ return __CLASS__ . ': $Id$';
 216+ }
 217+
 218+}
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/api/ApiDeleteWatchlistGroup.php
___________________________________________________________________
Added: svn:keywords
1219 + Id
Added: svn:eol-style
2220 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/api/ApiEditWatchlistGroup.php
@@ -0,0 +1,111 @@
 2+<?php
 3+
 4+/**
 5+ * API module to modify semantic watchlist groups.
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file ApiEditWatchlistGroup.php
 10+ * @ingroup SemanticWatchlist
 11+ * @ingroup API
 12+ *
 13+ * @licence GNU GPL v3+
 14+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 15+ */
 16+class ApiEditWatchlistGroup extends ApiBase {
 17+
 18+ public function __construct( $main, $action ) {
 19+ parent::__construct( $main, $action );
 20+ }
 21+
 22+ public function execute() {
 23+ global $wgUser;
 24+
 25+ if ( !$wgUser->isAllowed( 'semanticwatchgroups' ) || $wgUser->isBlocked() ) {
 26+ $this->dieUsageMsg( array( 'badaccess-groups' ) );
 27+ }
 28+
 29+ $params = $this->extractRequestParams();
 30+
 31+ $group = new SWLGroup(
 32+ $params['id'],
 33+ $params['name'],
 34+ $params['categories'],
 35+ $params['namespaces'],
 36+ $params['properties'],
 37+ $params['concepts']
 38+ );
 39+
 40+ $this->getResult()->addValue(
 41+ null,
 42+ 'success',
 43+ $group->writeToDB()
 44+ );
 45+ }
 46+
 47+ public function getAllowedParams() {
 48+ return array(
 49+ 'id' => array(
 50+ ApiBase::PARAM_TYPE => 'integer',
 51+ ApiBase::PARAM_REQUIRED => true,
 52+ ),
 53+ 'name' => array(
 54+ ApiBase::PARAM_TYPE => 'string',
 55+ ApiBase::PARAM_REQUIRED => true,
 56+ ),
 57+ 'properties' => array(
 58+ ApiBase::PARAM_TYPE => 'string',
 59+ ApiBase::PARAM_ISMULTI => true,
 60+ ApiBase::PARAM_REQUIRED => true,
 61+ ),
 62+ 'categories' => array(
 63+ ApiBase::PARAM_TYPE => 'string',
 64+ ApiBase::PARAM_ISMULTI => true,
 65+ ApiBase::PARAM_DFLT => '',
 66+ ),
 67+ 'namespaces' => array(
 68+ ApiBase::PARAM_TYPE => 'string',
 69+ ApiBase::PARAM_ISMULTI => true,
 70+ ApiBase::PARAM_DFLT => '',
 71+ ),
 72+ 'concepts' => array(
 73+ ApiBase::PARAM_TYPE => 'string',
 74+ ApiBase::PARAM_ISMULTI => true,
 75+ ApiBase::PARAM_DFLT => '',
 76+ ),
 77+ );
 78+ }
 79+
 80+ public function getParamDescription() {
 81+ return array(
 82+ 'id' => 'The ID of the watchlist group to edit',
 83+ 'name' => 'The name of the group, used for display in the user preferences',
 84+ 'properties' => 'The properties this watchlist group covers',
 85+ 'categories' => 'The categories this watchlist group covers',
 86+ 'namespaces' => 'The namespaces this watchlist group covers',
 87+ 'concepts' => 'The concepts this watchlist group covers',
 88+ );
 89+ }
 90+
 91+ public function getDescription() {
 92+ return array(
 93+ 'API module to modify semantic watchlist groups.'
 94+ );
 95+ }
 96+
 97+ public function getPossibleErrors() {
 98+ return array_merge( parent::getPossibleErrors(), array(
 99+ ) );
 100+ }
 101+
 102+ protected function getExamples() {
 103+ return array(
 104+ 'api.php?action=editswlgroup&id=42&name=My group of awesome&properties=Has awesomeness|Has epicness&categories=Awesome stuff',
 105+ );
 106+ }
 107+
 108+ public function getVersion() {
 109+ return __CLASS__ . ': $Id$';
 110+ }
 111+
 112+}
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/api/ApiEditWatchlistGroup.php
___________________________________________________________________
Added: svn:keywords
1113 + Id
Added: svn:eol-style
2114 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/api/ApiAddWatchlistGroup.php
@@ -0,0 +1,118 @@
 2+<?php
 3+
 4+/**
 5+ * API module to add semantic watchlist groups.
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file ApiAddWatchlistGroup.php
 10+ * @ingroup SemanticWatchlist
 11+ * @ingroup API
 12+ *
 13+ * @licence GNU GPL v3+
 14+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 15+ */
 16+class ApiAddWatchlistGroup extends ApiBase {
 17+
 18+ public function __construct( $main, $action ) {
 19+ parent::__construct( $main, $action );
 20+ }
 21+
 22+ public function execute() {
 23+ global $wgUser;
 24+
 25+ if ( !$wgUser->isAllowed( 'semanticwatchgroups' ) || $wgUser->isBlocked() ) {
 26+ $this->dieUsageMsg( array( 'badaccess-groups' ) );
 27+ }
 28+
 29+ $params = $this->extractRequestParams();
 30+
 31+ $group = new SWLGroup(
 32+ null,
 33+ $params['name'],
 34+ $params['categories'],
 35+ $params['namespaces'],
 36+ $params['properties'],
 37+ $params['concepts']
 38+ );
 39+
 40+ $this->getResult()->addValue(
 41+ null,
 42+ 'success',
 43+ $group->writeToDB()
 44+ );
 45+
 46+ $this->getResult()->addValue(
 47+ 'group',
 48+ 'id',
 49+ $group->getId()
 50+ );
 51+
 52+ $this->getResult()->addValue(
 53+ 'group',
 54+ 'name',
 55+ $group->getName()
 56+ );
 57+ }
 58+
 59+ public function getAllowedParams() {
 60+ return array(
 61+ 'name' => array(
 62+ ApiBase::PARAM_TYPE => 'string',
 63+ ApiBase::PARAM_REQUIRED => true,
 64+ ),
 65+ 'properties' => array(
 66+ ApiBase::PARAM_TYPE => 'string',
 67+ ApiBase::PARAM_ISMULTI => true,
 68+ ApiBase::PARAM_REQUIRED => true,
 69+ ),
 70+ 'categories' => array(
 71+ ApiBase::PARAM_TYPE => 'string',
 72+ ApiBase::PARAM_ISMULTI => true,
 73+ ApiBase::PARAM_DFLT => '',
 74+ ),
 75+ 'namespaces' => array(
 76+ ApiBase::PARAM_TYPE => 'string',
 77+ ApiBase::PARAM_ISMULTI => true,
 78+ ApiBase::PARAM_DFLT => '',
 79+ ),
 80+ 'concepts' => array(
 81+ ApiBase::PARAM_TYPE => 'string',
 82+ ApiBase::PARAM_ISMULTI => true,
 83+ ApiBase::PARAM_DFLT => '',
 84+ ),
 85+ );
 86+ }
 87+
 88+ public function getParamDescription() {
 89+ return array(
 90+ 'name' => 'The name of the group, used for display in the user preferences',
 91+ 'properties' => 'The properties this watchlist group covers',
 92+ 'categories' => 'The categories this watchlist group covers',
 93+ 'namespaces' => 'The namespaces this watchlist group covers',
 94+ 'concepts' => 'The concepts this watchlist group covers',
 95+ );
 96+ }
 97+
 98+ public function getDescription() {
 99+ return array(
 100+ 'API module to add semantic watchlist groups.'
 101+ );
 102+ }
 103+
 104+ public function getPossibleErrors() {
 105+ return array_merge( parent::getPossibleErrors(), array(
 106+ ) );
 107+ }
 108+
 109+ protected function getExamples() {
 110+ return array(
 111+ 'api.php?action=addswlgroup&name=My group of awesome&properties=Has awesomeness|Has epicness&categories=Awesome stuff',
 112+ );
 113+ }
 114+
 115+ public function getVersion() {
 116+ return __CLASS__ . ': $Id$';
 117+ }
 118+
 119+}
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/api/ApiAddWatchlistGroup.php
___________________________________________________________________
Added: svn:keywords
1120 + Id
Added: svn:eol-style
2121 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/api/ApiQuerySemanticWatchlist.php
@@ -0,0 +1,249 @@
 2+<?php
 3+
 4+/**
 5+ * API module to get a list of modified properties per page for a persons semantic watchlist.
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file ApiQuerySemanticWatchlist.php
 10+ * @ingroup SemanticWatchlist
 11+ * @ingroup API
 12+ *
 13+ * @licence GNU GPL v3+
 14+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 15+ */
 16+class ApiQuerySemanticWatchlist extends ApiQueryBase {
 17+ public function __construct( $main, $action ) {
 18+ parent :: __construct( $main, $action, 'sw' );
 19+ }
 20+
 21+ /**
 22+ * Retrieve the specil words from the database.
 23+ */
 24+ public function execute() {
 25+ // Get the requests parameters.
 26+ $params = $this->extractRequestParams();
 27+
 28+ if ( !( isset( $params['userid'] ) XOR isset( $params['groupids'] ) ) ) {
 29+ $this->dieUsage( wfMsgExt( 'swl-err-userid-xor-groupids' ), 'userid-xor-groupids' );
 30+ }
 31+
 32+ $isUserFilter = isset( $params['userid'] );
 33+ $filter = $isUserFilter ? $params['userid'] : $params['groupids'];
 34+
 35+ $this->setupChangeSetQuery( $filter, $isUserFilter, $params['limit'], $params['continue'] );
 36+
 37+ $sets = $this->select( __METHOD__ );
 38+ $count = 0;
 39+ $resultSets = array();
 40+
 41+ foreach ( $sets as $set ) {
 42+ if ( ++$count > $params['limit'] ) {
 43+ // We've reached the one extra which shows that
 44+ // there are additional pages to be had. Stop here...
 45+ $this->setContinueEnumParameter( 'continue', $set->edit_time . '-' . $set->spe_set_id );
 46+ break;
 47+ }
 48+
 49+ $resultSets[] = SWLChangeSet::newFromDBResult( $set );
 50+ }
 51+
 52+ if ( $params['merge'] ) {
 53+ $this->mergeSets( $resultSets );
 54+ }
 55+
 56+ foreach ( $resultSets as &$set ) {
 57+ $set = $set->toArray();
 58+
 59+ foreach ( $set['changes'] as $propName => $changes ) {
 60+ $this->getResult()->setIndexedTagName( $set['changes'][$propName], 'change' );
 61+ }
 62+ }
 63+
 64+ $this->getResult()->setIndexedTagName( $resultSets, 'set' );
 65+
 66+ $this->getResult()->addValue(
 67+ null,
 68+ 'sets',
 69+ $resultSets
 70+ );
 71+ }
 72+
 73+ /**
 74+ * Gets a list of change sets belonging to any of the watchlist groups
 75+ * watched by the user, newest first.
 76+ *
 77+ * @param mixed $filter User ID or array of group IDs
 78+ * @param boolean $isUserFilter
 79+ * @param integer $limit
 80+ * @param string $continue
 81+ */
 82+ protected function setupChangeSetQuery( $filter, $isUserFilter, $limit, $continue ) {
 83+ $tables = array( 'swl_edits', 'swl_sets_per_edit', 'swl_sets_per_group' );
 84+
 85+ if ( $isUserFilter ) {
 86+ $tables[] = 'swl_users_per_group';
 87+ }
 88+
 89+ $this->addTables( $tables );
 90+
 91+ $this->addJoinConds( array(
 92+ 'swl_sets_per_edit' => array( 'INNER JOIN', array( 'edit_id=spe_edit_id' ) ),
 93+ 'swl_sets_per_group' => array( 'INNER JOIN', array( 'spe_set_id=spg_set_id' ) ),
 94+ ) );
 95+
 96+ if ( $isUserFilter ) {
 97+ $this->addJoinConds( array(
 98+ 'swl_users_per_group' => array( 'INNER JOIN', array( 'spg_group_id=upg_group_id' ) ),
 99+ ) );
 100+ }
 101+
 102+ $this->addFields( array(
 103+ 'spe_set_id',
 104+ 'edit_user_name',
 105+ 'edit_page_id',
 106+ 'edit_time',
 107+ 'edit_id'
 108+ ) );
 109+
 110+ $this->addWhere( array(
 111+ ( $isUserFilter ? 'upg_user_id' : 'spg_group_id' ) => $filter
 112+ ) );
 113+
 114+ $this->addOption( 'DISTINCT' );
 115+ $this->addOption( 'LIMIT', $limit + 1 );
 116+ $this->addOption( 'ORDER BY', 'edit_time DESC, spe_set_id DESC' );
 117+
 118+ if ( !is_null( $continue ) ) {
 119+ $continueParams = explode( '-', $continue );
 120+
 121+ if ( count( $continueParams ) == 2 ) {
 122+ $dbr = wfGetDB( DB_SLAVE );
 123+ $this->addWhere( 'edit_time <= ' . $dbr->addQuotes( $continueParams[0] ) );
 124+ $this->addWhere( 'spe_set_id <= ' . $dbr->addQuotes( $continueParams[1] ) );
 125+ }
 126+ else {
 127+ // TODO: error
 128+ }
 129+ }
 130+ }
 131+
 132+ /**
 133+ * Merge change sets belonging to the same edit into one sinlge change set.
 134+ *
 135+ * @since 0.1
 136+ *
 137+ * @param array $sets
 138+ */
 139+ protected function mergeSets( array &$sets ) {
 140+ if ( count( $sets ) > 1 ) {
 141+ $setsPerEdits = array();
 142+
 143+ // List the sets per edit.
 144+ foreach ( $sets as $set ) {
 145+ if ( !array_key_exists( $set->getEdit()->getId(), $setsPerEdits ) ) {
 146+ $setsPerEdits[$set->getEdit()->getId()] = array();
 147+ }
 148+
 149+ $setsPerEdits[$set->getEdit()->getId()][] = $set;
 150+ }
 151+
 152+ $mergedSets = array();
 153+
 154+ // For all edits with more then one set, merge all sets in the first one,
 155+ // and add it to the $mergedSets list.
 156+ foreach ( $setsPerEdits as $setsForEdit ) {
 157+ $setCount = count( $setsForEdit );
 158+
 159+ if ( $setCount > 1 ) {
 160+ for ( $i = 1; $i < $setCount; $i++ ) {
 161+ $setsForEdit[0]->mergeInChangeSet( $setsForEdit[$i] );
 162+ }
 163+ }
 164+
 165+ $mergedSets[] = $setsForEdit[0];
 166+ }
 167+
 168+ $sets = $mergedSets;
 169+ }
 170+ }
 171+
 172+ /**
 173+ * (non-PHPdoc)
 174+ * @see includes/api/ApiBase#getAllowedParams()
 175+ */
 176+ public function getAllowedParams() {
 177+ return array (
 178+ 'userid' => array(
 179+ ApiBase::PARAM_TYPE => 'integer',
 180+ ),
 181+ 'groupids' => array(
 182+ ApiBase::PARAM_TYPE => 'integer',
 183+ ApiBase::PARAM_ISMULTI => true,
 184+ ),
 185+ 'merge' => array(
 186+ ApiBase::PARAM_TYPE => 'boolean',
 187+ ApiBase::PARAM_DFLT => false,
 188+ ),
 189+ 'limit' => array(
 190+ ApiBase :: PARAM_DFLT => 20,
 191+ ApiBase :: PARAM_TYPE => 'limit',
 192+ ApiBase :: PARAM_MIN => 1,
 193+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
 194+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
 195+ ),
 196+ 'continue' => null,
 197+ );
 198+
 199+ }
 200+
 201+ /**
 202+ * (non-PHPdoc)
 203+ * @see includes/api/ApiBase#getParamDescription()
 204+ */
 205+ public function getParamDescription() {
 206+ return array (
 207+ 'userid' => 'The ID of the user for which to return semantic watchlist data.',
 208+ 'groupids' => 'The IDs of the groups for which to return semantic watchlist data.',
 209+ 'merge' => 'Merge sets of changes that belong to the same edit?',
 210+ 'continue' => 'Offset number from where to continue the query',
 211+ 'limit' => 'Max amount of words to return',
 212+ );
 213+ }
 214+
 215+ /**
 216+ * (non-PHPdoc)
 217+ * @see includes/api/ApiBase#getDescription()
 218+ */
 219+ public function getDescription() {
 220+ return 'Returns a list of sets of changes for the either specified user of specified group(s).';
 221+ }
 222+
 223+ /**
 224+ * (non-PHPdoc)
 225+ * @see includes/api/ApiBase#getPossibleErrors()
 226+ */
 227+ public function getPossibleErrors() {
 228+ return array_merge( parent::getPossibleErrors(), array(
 229+
 230+ ) );
 231+ }
 232+
 233+ /**
 234+ * (non-PHPdoc)
 235+ * @see includes/api/ApiBase#getExamples()
 236+ */
 237+ protected function getExamples() {
 238+ return array (
 239+ 'api.php?action=query&list=semanticwatchlist&swuserid=1',
 240+ 'api.php?action=query&list=semanticwatchlist&swuserid=1&swlimit=42&swcontinue=20110514143957-9001',
 241+ 'api.php?action=query&list=semanticwatchlist&swgroupids=1',
 242+ 'api.php?action=query&list=semanticwatchlist&swgroupids=1|42&swlimit=34',
 243+ );
 244+ }
 245+
 246+ public function getVersion() {
 247+ return __CLASS__ . ': $Id$';
 248+ }
 249+
 250+}
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/api/ApiQuerySemanticWatchlist.php
___________________________________________________________________
Added: svn:keywords
1251 + Id
Added: svn:eol-style
2252 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/SemanticWatchlist.i18n.php
@@ -0,0 +1,598 @@
 2+<?php
 3+
 4+/**
 5+ * Internationalization file for the Semantic Watchlist extension.
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file SemanticWatchlist.i18n.php
 10+ * @ingroup SemanticWatchlist
 11+ *
 12+ * @licence GNU GPL v3+
 13+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 14+ */
 15+
 16+$messages = array();
 17+
 18+/** English
 19+ * @author Jeroen De Dauw
 20+ */
 21+$messages['en'] = array(
 22+ 'semanticwatchlist-desc' => 'Lets users be notified of specific changes to Semantic MediaWiki data',
 23+
 24+ 'right-semanticwatch' => 'Use semantic watchlist',
 25+ 'right-semanticwatchgroups' => '[[Special:WatchlistConditions|Modify]] the semantic watchlist groups',
 26+
 27+ 'special-semanticwatchlist' => 'Semantic Watchlist',
 28+ 'special-watchlistconditions' => 'Semantic watchlist conditions',
 29+
 30+ 'group-swladmins' => 'Semantic Watchlist admins',
 31+ 'group-swladmins-member' => 'Semantic Watchlist admin',
 32+ 'grouppage-swladmins' => 'Project:Semantic_Watchlist_admins',
 33+
 34+ // Special:WatchlistConditions
 35+ 'swl-group-name' => 'Group name:',
 36+ 'swl-group-properties' => 'Properties covered by this group:',
 37+ 'swl-group-remove-property' => 'Remove property',
 38+ 'swl-group-add-property' => 'Add property',
 39+ 'swl-group-page-selection' => 'Pages in',
 40+ 'swl-group-save' => 'Save',
 41+ 'swl-group-delete' => 'Delete',
 42+ 'swl-group-category' => 'category',
 43+ 'swl-group-namespace' => 'namespace',
 44+ 'swl-group-concept' => 'concept',
 45+ 'swl-group-confirmdelete' => 'Are you sure you want to delete the "$1" watchlist group?',
 46+ 'swl-group-save-all' => 'Save all',
 47+ 'swl-group-add-new-group' => 'Add a new group',
 48+ 'swl-group-add-group' => 'Add group',
 49+
 50+ // Special:SemanticWatchlist
 51+ 'swl-watchlist-position' => "Showing '''$1''' of the last {{PLURAL:$1|change|changes}} starting with '''#$2'''.",
 52+ 'swl-watchlist-insertions' => 'New:',
 53+ 'swl-watchlist-deletions' => 'Old:',
 54+ 'swl-watchlist-pagincontrol' => 'View ($1) ($2)',
 55+ 'swl-watchlist-firstn' => 'first $1',
 56+ 'swl-watchlist-firstn-title' => 'First $1 {{PLURAL:$1|result|results}}',
 57+ 'swl-watchlist-no-items' => 'You have no items on your watchlist.',
 58+ 'swl-watchlist-can-mod-groups' => 'You can [[$1|modify the watchlist groups]].',
 59+ 'swl-watchlist-can-mod-prefs' => 'You can [[$1|modify your watchlist preferences]], including setting which properties to watch.',
 60+ 'swl-watchlist-no-groups' => 'You are not yet watching any watchlist groups. [[$1|Modify your watchlist preferences]].',
 61+
 62+ // Email
 63+ 'swl-email-propschanged' => 'Properties have changed at $1',
 64+ 'swl-email-propschanged-long' => "One or more properties you watch at '''$1''' have been changed by user '''$2''' at $4 on $5. You can view these and other changes on [$3 your semantic watchlist].",
 65+ 'swl-email-changes' => 'Property changes on [$2 $1]:',
 66+
 67+ // Preferences
 68+ 'prefs-swl' => 'Semantic watchlist',
 69+ 'prefs-swlgroup' => 'Groups to watch',
 70+ 'prefs-swlglobal' => 'General options',
 71+ 'swl-prefs-emailnofity' => 'E-mail me on changes to properties I am watching',
 72+ 'swl-prefs-watchlisttoplink' => 'Show a link to the Semantic Watchlist on the top of the page',
 73+ 'swl-prefs-category-label' => "'''$1''': {{PLURAL:$2|property|properties}} $3 from category ''$4''",
 74+ 'swl-prefs-namespace-label' => "'''$1''': {{PLURAL:$2|property|properties}} $3 from namespace ''$4''",
 75+ 'swl-prefs-concept-label' => "'''$1''': {{PLURAL:$2|property|properties}} $3 from concept ''$4''",
 76+
 77+ // API
 78+ 'swl-err-userid-xor-groupids' => 'Either the userid or the groupids parameter needs to be specified, but not both.',
 79+);
 80+
 81+/** Message documentation (Message documentation)
 82+ * @author Jeroen De Dauw
 83+ * @author Nemo bis
 84+ */
 85+$messages['qqq'] = array(
 86+ 'semanticwatchlist-desc' => '{{desc}}',
 87+ 'right-semanticwatch' => '{{doc-right|semanticwatch}}',
 88+ 'right-semanticwatchgroups' => '{{doc-right|semanticwatchgroups}}',
 89+ 'swl-watchlist-position' => "The message explains how many changes are displayed in the special page ($1) and what's the number of the first one shown ($2): the special page provides results in paginated format.",
 90+ 'swl-email-propschanged-long' => '$1: wiki name, $2: user name, $3: url, $4: time, $5: date',
 91+);
 92+
 93+/** Bulgarian (Български)
 94+ * @author DCLXVI
 95+ */
 96+$messages['bg'] = array(
 97+ 'swl-group-save' => 'Съхраняване',
 98+ 'swl-group-delete' => 'Изтриване',
 99+);
 100+
 101+/** Breton (Brezhoneg)
 102+ * @author Y-M D
 103+ */
 104+$messages['br'] = array(
 105+ 'swl-group-save' => 'Enrollañ',
 106+ 'swl-group-delete' => 'Dilemel',
 107+ 'swl-group-category' => 'rummad',
 108+ 'swl-group-namespace' => 'esaouenn anv',
 109+ 'swl-group-save-all' => 'Enrollañ pep tra',
 110+ 'swl-group-add-new-group' => 'Ouzhpennañ ur strollad nevez',
 111+ 'swl-group-add-group' => 'Ouzhpennañ ur strollad',
 112+ 'swl-watchlist-insertions' => 'Ouzhpennet :',
 113+);
 114+
 115+/** German (Deutsch)
 116+ * @author Kghbln
 117+ * @author Purodha
 118+ */
 119+$messages['de'] = array(
 120+ 'semanticwatchlist-desc' => 'Ermöglicht die Benachrichtigung von Benutzern zu bestimmten Änderungen an semantischen Daten',
 121+ 'right-semanticwatch' => 'Semantische Beobachtungslisten verwenden',
 122+ 'right-semanticwatchgroups' => 'Semantische Beobachtungslisten [[Special:WatchlistConditions|anpassen]]',
 123+ 'special-semanticwatchlist' => 'Semantische Beobachtungsliste',
 124+ 'special-watchlistconditions' => 'Einstellungen zu semantischen Beobachtungslisten',
 125+ 'group-swladmins' => 'SWL-Administratoren',
 126+ 'group-swladmins-member' => 'SWL-Administrator',
 127+ 'grouppage-swladmins' => 'Project:SWL-Administratoren',
 128+ 'swl-group-name' => 'Gruppenname:',
 129+ 'swl-group-properties' => 'Attribute zu dieser Gruppe:',
 130+ 'swl-group-remove-property' => 'Attribut entfernen',
 131+ 'swl-group-add-property' => 'Attribut hinzufügen',
 132+ 'swl-group-page-selection' => 'Seiten in',
 133+ 'swl-group-save' => 'Speichern',
 134+ 'swl-group-delete' => 'Löschen',
 135+ 'swl-group-category' => 'Kategorie',
 136+ 'swl-group-namespace' => 'Namensraum',
 137+ 'swl-group-concept' => 'Konzept',
 138+ 'swl-group-confirmdelete' => 'Soll die Beobachtungsliste "$1" tatsächlich gelöscht werden?',
 139+ 'swl-group-save-all' => 'Alle speichern',
 140+ 'swl-group-add-new-group' => 'Eine neue Gruppe hinzufügen',
 141+ 'swl-group-add-group' => 'Eine Gruppe hinzufügen',
 142+ 'swl-watchlist-position' => "Anzeige der letzten '''$1''' Änderungen beginnend mit '''#$2'''.",
 143+ 'swl-watchlist-insertions' => 'Hinzugefügt:',
 144+ 'swl-watchlist-deletions' => 'Alt:',
 145+ 'swl-watchlist-pagincontrol' => 'Zeige ($1) ($2)',
 146+ 'swl-watchlist-firstn' => 'erstes $1',
 147+ 'swl-watchlist-firstn-title' => '{{PLURAL:$1|Das erste Ergebnis|Die ersten $1 Ergebnisse}}',
 148+ 'swl-watchlist-no-items' => 'Es befinden sich keine Einträge auf deiner Beobachtungsliste.',
 149+ 'swl-watchlist-can-mod-groups' => 'Du kannst [[$1|die Gruppen]] anpassen.',
 150+ 'swl-watchlist-can-mod-prefs' => 'Du kannst [[$1|die Einstellungen der semantischen Beobachtungsliste]], einschließlich der zu beobachtenden Attribute, anpassen.',
 151+ 'swl-watchlist-no-groups' => 'Du beobachtest bislang noch keine Gruppen. [[$1|Pass deine Einstellungen an]].',
 152+ 'swl-email-propschanged' => 'Attribute wurden auf $1 geändert',
 153+ 'swl-email-propschanged-long' => "Eines oder mehrere der auf '''$1''' beobachteten Attribute wurden von Benutzer '''$2''' am $4 um $5 Uhr geändert. Diese und andere Änderungen werden auf [$3 dieser semantischen Beobachtungsliste] angezeigt.",
 154+ 'swl-email-changes' => 'Attributänderungen auf [$2 $1]:',
 155+ 'prefs-swl' => 'Semantische Beobachtungsliste',
 156+ 'prefs-swlgroup' => 'Zu beobachtende Gruppen',
 157+ 'prefs-swlglobal' => 'Allgemeine Optionen',
 158+ 'swl-prefs-emailnofity' => 'Bei Änderungen an beobachteten Attributen E-Mails senden',
 159+ 'swl-prefs-watchlisttoplink' => 'Einen Link zur semantischen Beobachtungsliste oben auf der Seite im Benutzermenü anzeigen',
 160+ 'swl-prefs-category-label' => "'''$1''': {{PLURAL:$2|Attribut|Attribute}} $3 in Kategorie ''$4''",
 161+ 'swl-prefs-namespace-label' => "'''$1''': {{PLURAL:$2|Attribut|Attribute}} $3 im Namensraum ''$4''",
 162+ 'swl-prefs-concept-label' => "'''$1''': {{PLURAL:$2|Attribut|Attribute}} $3 im Konzept ''$4''",
 163+ 'swl-err-userid-xor-groupids' => 'Es muss entweder der Parameter für die Benutzerkennung oder für die Gruppenkennung angegeben werden, jedoch nicht beide gleichzeitig.',
 164+);
 165+
 166+/** German (formal address) (‪Deutsch (Sie-Form)‬)
 167+ * @author Kghbln
 168+ */
 169+$messages['de-formal'] = array(
 170+ 'swl-watchlist-no-items' => 'Es befinden sich keine Einträge auf Ihrer Beobachtungsliste.',
 171+ 'swl-watchlist-can-mod-groups' => 'Sie können [[$1|die Gruppen]] anpassen.',
 172+ 'swl-watchlist-can-mod-prefs' => 'Sie können [[$1|die Einstellungen der semantischen Beobachtungsliste]], einschließlich der zu beobachtenden Attribute, anpassen.',
 173+ 'swl-watchlist-no-groups' => 'Sie beobachten bislang noch keine Gruppen. [[$1|Passen Sie Ihre Einstellungen an]].',
 174+);
 175+
 176+/** Spanish (Español)
 177+ * @author Mor
 178+ */
 179+$messages['es'] = array(
 180+ 'swl-group-name' => 'Nombre del grupo:',
 181+ 'swl-group-remove-property' => 'Quitar propiedad',
 182+ 'swl-group-add-property' => 'Añadir propiedad',
 183+ 'swl-group-save' => 'Guardar',
 184+ 'swl-group-delete' => 'Borrar',
 185+ 'swl-group-category' => 'categoría',
 186+ 'swl-watchlist-insertions' => 'Añadido:',
 187+ 'swl-watchlist-deletions' => 'Borrado:',
 188+ 'swl-prefs-emailnofity' => 'Enviarme un mensaje de correo electrónico sobre los cambios en las propiedades que estoy vigilando',
 189+);
 190+
 191+/** French (Français)
 192+ * @author IAlex
 193+ * @author Sherbrooke
 194+ */
 195+$messages['fr'] = array(
 196+ 'semanticwatchlist-desc' => 'Permet de définir des groupes de propriétés sémantiques pour une ou plusieurs catégories / espaces de noms qui peuvent ensuite être inscrits sur la liste de suivi sémantique',
 197+ 'right-semanticwatch' => 'Utiliser la liste de suivi sémantique',
 198+ 'right-semanticwatchgroups' => '[[Special:WatchlistConditions|Modifier]] les groupes de la liste de suivi sémantique',
 199+ 'special-semanticwatchlist' => 'Liste de suivi sémantique',
 200+ 'special-watchlistconditions' => 'Paramètres de la liste de suivi sémantique',
 201+ 'swl-group-name' => 'Nom du groupe:',
 202+ 'swl-group-properties' => 'Propriétés de ce groupe :',
 203+ 'swl-group-remove-property' => 'Retirez la propriété',
 204+ 'swl-group-add-property' => 'Ajouter une propriété',
 205+ 'swl-group-page-selection' => 'Pages dans la',
 206+ 'swl-group-save' => 'Enregistrer',
 207+ 'swl-group-delete' => 'Supprimer',
 208+ 'swl-group-category' => 'catégorie',
 209+ 'swl-group-namespace' => 'espace de noms',
 210+ 'swl-group-concept' => 'concept',
 211+ 'swl-group-confirmdelete' => 'Etes-vous certain de vouloir supprimer le groupe de liste d\'alerte "$1" ?',
 212+ 'swl-group-save-all' => 'Enregistrer tout',
 213+ 'swl-group-add-new-group' => 'Ajouter un nouveau groupe',
 214+ 'swl-group-add-group' => 'Ajouter un groupe',
 215+ 'swl-watchlist-position' => "'''Afficher $1''' des derniers changements en commençant par '''#$2'''.",
 216+ 'swl-watchlist-insertions' => 'Ajouté :',
 217+ 'swl-watchlist-deletions' => 'Supprimé :',
 218+ 'swl-watchlist-pagincontrol' => 'Voir ($1) ($2)',
 219+ 'swl-watchlist-firstn' => '$1 premiers',
 220+ 'swl-watchlist-firstn-title' => '$1 {{PLURAL:$1|permier résultat|premiers résultats}}',
 221+ 'swl-email-propschanged' => 'Les propriétés ont changé à $1',
 222+ 'swl-email-propschanged-long' => "Une ou plusieurs propriétés que vous suivez à '''$1'' ont été modifiées par l'utilisateur '''$2''' à $4 sur $5 . Vous pouvez visualiser ces modifications et d'autres sur [$3 votre liste de suivi sémantique].",
 223+ 'swl-email-changes' => 'Changements de propriétés sur [$2 $1] :',
 224+ 'prefs-swl' => 'Liste de suivi sémantique',
 225+ 'prefs-swlgroup' => 'Groupes à suivre',
 226+ 'swl-prefs-emailnofity' => "Envoyez-moi un courriel sur les modifications apportées aux propriétés que j'ai en liste de suivi",
 227+ 'swl-prefs-category-label' => "'''$1''' : {{PLURAL:$2| propriété|propriétés}} $3 de la catégorie ''$4''",
 228+ 'swl-prefs-namespace-label' => "'''$1''' : {{PLURAL:$2|propriété|propriétés}} $3 de l'espace de noms ''$4''",
 229+ 'swl-prefs-concept-label' => "'''$1''' : {{PLURAL:$2|propriété|propriétés}} $3 du concept ''$4''",
 230+ 'swl-err-userid-xor-groupids' => 'Il faut spécifier <code>userid</code> ou <code>groupid</code>, mais pas les deux en même temps.',
 231+);
 232+
 233+/** Franco-Provençal (Arpetan)
 234+ * @author ChrisPtDe
 235+ */
 236+$messages['frp'] = array(
 237+ 'right-semanticwatch' => 'Utilisar la lista de survelyence sèmantica',
 238+ 'right-semanticwatchgroups' => '[[Special:WatchlistConditions|Changiér]] les tropes de la lista de survelyence sèmantica',
 239+ 'special-semanticwatchlist' => 'Lista de survelyence sèmantica',
 240+ 'special-watchlistconditions' => 'Paramètres de la lista de survelyence sèmantica',
 241+ 'swl-group-name' => 'Nom de la tropa :',
 242+ 'swl-group-properties' => 'Propriètâts de ceta tropa :',
 243+ 'swl-group-remove-property' => 'Enlevar una propriètât',
 244+ 'swl-group-add-property' => 'Apondre una propriètât',
 245+ 'swl-group-page-selection' => 'Pâges dens la',
 246+ 'swl-group-save' => 'Encartar',
 247+ 'swl-group-delete' => 'Suprimar',
 248+ 'swl-group-category' => 'catègorie',
 249+ 'swl-group-namespace' => 'èspâço de noms',
 250+ 'swl-group-concept' => 'concèpte',
 251+ 'swl-group-confirmdelete' => 'Éte-vos de sûr de volêr suprimar la tropa de la lista de survelyence « $1 » ?',
 252+ 'swl-group-save-all' => 'Encartar tot',
 253+ 'swl-group-add-new-group' => 'Apondre una tropa novèla',
 254+ 'swl-group-add-group' => 'Apondre una tropa',
 255+ 'swl-watchlist-position' => "Fâre vêre '''$1''' des dèrriérs changements en comencient per '''#$2'''.",
 256+ 'swl-watchlist-insertions' => 'Apondu :',
 257+ 'swl-watchlist-deletions' => 'Suprimâ :',
 258+ 'swl-watchlist-pagincontrol' => 'Vêre ($1) ($2)',
 259+ 'swl-watchlist-firstn' => '$1 premiérs',
 260+ 'swl-watchlist-firstn-title' => '$1 {{PLURAL:$1|premiér rèsultat|premiérs rèsultats}}',
 261+ 'swl-email-propschanged' => 'Les propriètâts ont changiês a $1',
 262+ 'swl-email-changes' => 'Changements de propriètâts sur [$2 $1] :',
 263+ 'prefs-swl' => 'Lista de survelyence sèmantica',
 264+ 'prefs-swlgroup' => 'Tropes a siuvre',
 265+ 'swl-prefs-category-label' => "'''$1''' : propriètât{{PLURAL:$2||s}} $3 de la catègorie ''$4''",
 266+ 'swl-prefs-namespace-label' => "'''$1''' : propriètât{{PLURAL:$2||s}} $3 de l’èspâço de noms ''$4''",
 267+ 'swl-prefs-concept-label' => "'''$1''' : propriètât{{PLURAL:$2||s}} $3 du concèpte ''$4''",
 268+);
 269+
 270+/** Galician (Galego)
 271+ * @author Toliño
 272+ */
 273+$messages['gl'] = array(
 274+ 'semanticwatchlist-desc' => 'Permite especificar grupos de propiedades semánticas para unha ou varias categorías ou espazos de nomes que poden ser vixiados despois',
 275+ 'right-semanticwatch' => 'Empregar a lista de vixilancia semántica',
 276+ 'right-semanticwatchgroups' => '[[Special:WatchlistConditions|Modificar]] os grupos da lista de vixilancia semántica',
 277+ 'special-semanticwatchlist' => 'Lista de vixilancia semántica',
 278+ 'special-watchlistconditions' => 'Condicións da lista de vixilancia semántica',
 279+ 'swl-group-name' => 'Nome do grupo:',
 280+ 'swl-group-properties' => 'Propiedades cubertas por este grupo:',
 281+ 'swl-group-remove-property' => 'Eliminar a propiedade',
 282+ 'swl-group-add-property' => 'Engadir a propiedade',
 283+ 'swl-group-page-selection' => 'Páxinas en',
 284+ 'swl-group-save' => 'Gardar',
 285+ 'swl-group-delete' => 'Borrar',
 286+ 'swl-group-category' => 'categoría',
 287+ 'swl-group-namespace' => 'espazo de nomes',
 288+ 'swl-group-concept' => 'concepto',
 289+ 'swl-group-confirmdelete' => 'Está seguro de querer borrar o grupo da lista de vixilancia "$1"?',
 290+ 'swl-group-save-all' => 'Gardar todos',
 291+ 'swl-group-add-new-group' => 'Engadir un novo grupo',
 292+ 'swl-group-add-group' => 'Engadir o grupo',
 293+ 'swl-watchlist-position' => "Mostrando '''$1''' dos últimos cambios, comezando polo '''nº $2'''.",
 294+ 'swl-watchlist-insertions' => 'Engadido:',
 295+ 'swl-watchlist-deletions' => 'Borrado:',
 296+ 'swl-watchlist-pagincontrol' => 'Ver ($1) ($2)',
 297+ 'swl-watchlist-firstn' => '$1 primeiras',
 298+ 'swl-watchlist-firstn-title' => '{{PLURAL:$1|Primeiro resultado|Primeiros $1 resultados}}',
 299+ 'swl-email-propschanged' => 'As propiedades cambiaron ás $1',
 300+ 'swl-email-propschanged-long' => "O usuario '''$2''' modificou unha ou máis propiedades que vixía en '''$1''' o $5 ás $4. Pode ollar estas e outras modificacións [$3 na súa lista de vixilancia semántica].",
 301+ 'swl-email-changes' => 'Cambio nas propiedades en [$2 $1]:',
 302+ 'prefs-swl' => 'Lista de vixilancia semántica',
 303+ 'prefs-swlgroup' => 'Grupos a vixiar',
 304+ 'swl-prefs-emailnofity' => 'Enviádeme un correo electrónico se hai cambios nas propiedades que vixío',
 305+ 'swl-prefs-category-label' => "'''$1:''' {{PLURAL:$2|propiedade|propiedades}} $3 da categoría ''$4''",
 306+ 'swl-prefs-namespace-label' => "'''$1:''' {{PLURAL:$2|propiedade|propiedades}} $3 do espazo de nomes ''$4''",
 307+ 'swl-prefs-concept-label' => "'''$1:''' {{PLURAL:$2|propiedade|propiedades}} $3 do concepto ''$4''",
 308+ 'swl-err-userid-xor-groupids' => 'Cómpre especificar ou ben o parámetro de identificación de usuario ou o parámetro de identificación de grupo, pero non os dous.',
 309+);
 310+
 311+/** Hebrew (עברית)
 312+ * @author Amire80
 313+ */
 314+$messages['he'] = array(
 315+ 'semanticwatchlist-desc' => 'מאפשרת למשתמשים לקבל הודעה על שינויים מסוימים בנתונים של מדיה־ויקי סמנטית',
 316+ 'right-semanticwatch' => 'שימוש ברשימת מעקב סמנטית',
 317+ 'right-semanticwatchgroups' => '[[Special:WatchlistConditions|שינוי]] קבוצות רשימת מעקב סמנטית',
 318+ 'special-semanticwatchlist' => 'רשימת מעקב סמנטית',
 319+);
 320+
 321+/** Interlingua (Interlingua)
 322+ * @author McDutchie
 323+ */
 324+$messages['ia'] = array(
 325+ 'semanticwatchlist-desc' => 'Permitte specificar gruppos de proprietates semantic pro un o plus categorias o spatios de nomines, le quales pote alora esser observate pro modificationes',
 326+ 'right-semanticwatch' => 'Usar observatorio semantic',
 327+ 'right-semanticwatchgroups' => '[[Special:WatchlistConditions|Modificar]] le gruppos del observatorio semantic',
 328+ 'special-semanticwatchlist' => 'Observatorio semantic',
 329+ 'special-watchlistconditions' => 'Conditiones del observatorio semantic',
 330+ 'swl-group-name' => 'Nomine del gruppo:',
 331+ 'swl-group-properties' => 'Proprietates coperite per iste gruppo:',
 332+ 'swl-group-remove-property' => 'Remover proprietate',
 333+ 'swl-group-add-property' => 'Adder proprietate',
 334+ 'swl-group-page-selection' => 'Paginas in',
 335+ 'swl-group-save' => 'Salveguardar',
 336+ 'swl-group-delete' => 'Deler',
 337+ 'swl-group-category' => 'categoria',
 338+ 'swl-group-namespace' => 'spatio de nomines',
 339+ 'swl-group-concept' => 'concepto',
 340+ 'swl-group-confirmdelete' => 'Es tu secur de voler deler le gruppo "$1" del observatorio?',
 341+ 'swl-group-save-all' => 'Salveguardar totes',
 342+ 'swl-group-add-new-group' => 'Adder un nove gruppo',
 343+ 'swl-group-add-group' => 'Adder gruppo',
 344+ 'swl-watchlist-position' => "Presenta '''$1''' del ultime modificationes a partir del '''№ $2'''.",
 345+ 'swl-watchlist-insertions' => 'Addite:',
 346+ 'swl-watchlist-deletions' => 'Delite:',
 347+ 'swl-watchlist-pagincontrol' => 'Vider ($1) ($2)',
 348+ 'swl-watchlist-firstn' => 'prime $1',
 349+ 'swl-watchlist-firstn-title' => 'Le prime {{PLURAL:$1|resultato|$1 resultatos}}',
 350+ 'swl-email-propschanged' => 'Proprietates ha cambiate a $1',
 351+ 'swl-email-propschanged-long' => "Un o plus proprietates que tu observa a '''$1''' ha essite cambiate per le usator '''$2''' le $5 a $4. Tu pote vider iste e altere cambios in [$3 tu observatorio semantic].",
 352+ 'swl-email-changes' => 'Cambios de proprietate in [$2 $1]:',
 353+ 'prefs-swl' => 'Observatorio semantic',
 354+ 'prefs-swlgroup' => 'Gruppos a observar',
 355+ 'swl-prefs-emailnofity' => 'Inviar me e-mail in caso de modificationes in proprietates que io observa',
 356+ 'swl-prefs-category-label' => "'''$1''': {{PLURAL:$2|proprietate|proprietates}} $3 del categoria ''$4''",
 357+ 'swl-prefs-namespace-label' => "'''$1''': {{PLURAL:$2|proprietate|proprietates}} $3 del spatio de nomines ''$4''",
 358+ 'swl-prefs-concept-label' => "'''$1''': {{PLURAL:$2|proprietate|proprietates}} $3 del concepto ''$4''",
 359+ 'swl-err-userid-xor-groupids' => 'O le parametro "userid" o le "groupids" debe esser specificate, ma non ambes.',
 360+);
 361+
 362+/** Indonesian (Bahasa Indonesia)
 363+ * @author IvanLanin
 364+ */
 365+$messages['id'] = array(
 366+ 'semanticwatchlist-desc' => 'Memungkinkan penetapan kelompok properti semantik untuk satu atau lebih kategori/ruang nama yang kemudian dapat dipantau perubahannya',
 367+ 'right-semanticwatch' => 'Menggunakan daftar pantauan semantik',
 368+ 'right-semanticwatchgroups' => '[[Special:WatchlistConditions|Mengubah]] kelompok daftar pantauan semantik',
 369+ 'special-semanticwatchlist' => 'Daftar Pantau Semantik',
 370+ 'special-watchlistconditions' => 'Kriteria daftar pantau semantik',
 371+ 'swl-group-name' => 'Nama kelompok:',
 372+ 'swl-group-properties' => 'Properti yang dicakup oleh kelompok ini:',
 373+ 'swl-group-remove-property' => 'Hapus properti',
 374+ 'swl-group-page-selection' => 'Halaman dalam',
 375+);
 376+
 377+/** Colognian (Ripoarisch)
 378+ * @author Purodha
 379+ */
 380+$messages['ksh'] = array(
 381+ 'semanticwatchlist-desc' => 'Määt et müjjelesch, Jroppe vun semantesche Eijeschaffte aanzjävve, för Saachjroppe un Appachtemangs, di dann op en Oppaßleß kumme un bewach wääde, för der Fall, dat se jeändert wääde.',
 382+ 'right-semanticwatch' => 'De semantesche Oppaßleß verwände',
 383+ 'right-semanticwatchgroups' => '[[Special:WatchlistConditions|Jroppe ändere]] för de semantesche Oppaßleßte',
 384+ 'special-semanticwatchlist' => 'Semantesch Oppaßleß',
 385+ 'special-watchlistconditions' => 'Enshtällonge för de semantesche Oppaßleßte',
 386+);
 387+
 388+/** Kurdish (Latin) (Kurdî (Latin))
 389+ * @author George Animal
 390+ */
 391+$messages['ku-latn'] = array(
 392+ 'swl-group-save' => 'Tomar bike',
 393+ 'swl-group-category' => 'kategorî',
 394+);
 395+
 396+/** Luxembourgish (Lëtzebuergesch)
 397+ * @author Robby
 398+ */
 399+$messages['lb'] = array(
 400+ 'right-semanticwatch' => 'Semantesch Iwwerwaachungslëscht benotzen',
 401+ 'special-semanticwatchlist' => 'Semantesch Iwwerwaachungslëscht',
 402+ 'special-watchlistconditions' => 'Astellunge vun der semantescher Iwwerwaachnugslëscht',
 403+ 'group-swladmins' => 'SWL-Administrateuren',
 404+ 'group-swladmins-member' => 'SWL-Administrateur',
 405+ 'grouppage-swladmins' => 'Project:SWL-Administrateuren',
 406+ 'swl-group-name' => 'Numm vum Grupp:',
 407+ 'swl-group-page-selection' => 'Säiten a(n)',
 408+ 'swl-group-save' => 'Späicheren',
 409+ 'swl-group-delete' => 'Läschen',
 410+ 'swl-group-category' => 'Kategorie',
 411+ 'swl-group-namespace' => 'Nummraum',
 412+ 'swl-group-concept' => 'Konzept',
 413+ 'swl-group-confirmdelete' => 'Sidd Dir sécher datt Dir de Grupp vun der Iwwerwaachungslëscht "$1" läsche wëllt?',
 414+ 'swl-group-save-all' => 'All späicheren',
 415+ 'swl-group-add-new-group' => 'Eng nei Grupp derbäisetzen',
 416+ 'swl-group-add-group' => 'Grupp derbäisetzen',
 417+ 'swl-watchlist-insertions' => 'Derbäigesat:',
 418+ 'swl-watchlist-deletions' => 'Al:',
 419+ 'swl-watchlist-pagincontrol' => '($1) ($2) weisen',
 420+ 'swl-watchlist-firstn' => 'éischt $1',
 421+ 'swl-watchlist-firstn-title' => '{{PLURAL:$1|Dat éischt Resultat|Déi éischt $1 Resultater}}',
 422+ 'swl-watchlist-no-items' => 'Dir hutt keng Objeten op Ärer Iwwerwaachungslëscht.',
 423+ 'swl-email-propschanged' => "D'Eegeschafte goufen op $1 geännert",
 424+ 'prefs-swl' => 'Semantesch Iwwerwaachungslëscht',
 425+ 'prefs-swlgroup' => "Gruppe fir z'iwwerwaachen",
 426+ 'prefs-swlglobal' => 'Allgemeng Optiounen',
 427+);
 428+
 429+/** Macedonian (Македонски)
 430+ * @author Bjankuloski06
 431+ */
 432+$messages['mk'] = array(
 433+ 'semanticwatchlist-desc' => 'Овозможува известување на корисниците за одредени измени во податоците на Семантички МедијаВики',
 434+ 'right-semanticwatch' => 'Користење на семантички список на набљудувања',
 435+ 'right-semanticwatchgroups' => '[[Special:WatchlistConditions|Менување]] на групи од семантички списоци на набљудувања',
 436+ 'special-semanticwatchlist' => 'Семантички список на набљудувања',
 437+ 'special-watchlistconditions' => 'Услови за семантичкиот список на набљудувања',
 438+ 'group-swladmins' => 'Администратори на Семантичкиот список на набљудувања',
 439+ 'group-swladmins-member' => 'Администратор на Семантичкиот список на набљудувања',
 440+ 'grouppage-swladmins' => 'Project:Админи_на_Семантичкиот_список_на_набљудувања',
 441+ 'swl-group-name' => 'Име на групата:',
 442+ 'swl-group-properties' => 'Својства покриени со оваа група:',
 443+ 'swl-group-remove-property' => 'Отстрани својство',
 444+ 'swl-group-add-property' => 'Додај својство',
 445+ 'swl-group-page-selection' => 'Страници во',
 446+ 'swl-group-save' => 'Зачувај',
 447+ 'swl-group-delete' => 'Избриши',
 448+ 'swl-group-category' => 'категорија',
 449+ 'swl-group-namespace' => 'именски простор',
 450+ 'swl-group-concept' => 'концепт',
 451+ 'swl-group-confirmdelete' => 'Дали сте сигурни дека сакате да ја избришете групата „$1“ од списокот на набљудувања?',
 452+ 'swl-group-save-all' => 'Зачувај сè',
 453+ 'swl-group-add-new-group' => 'Додај нова група',
 454+ 'swl-group-add-group' => 'Додај група',
 455+ 'swl-watchlist-position' => "Приказ на '''$1''' од последните промени, почнувајќи од '''бр. $2'''.",
 456+ 'swl-watchlist-insertions' => 'Додадено:',
 457+ 'swl-watchlist-deletions' => 'Стари:',
 458+ 'swl-watchlist-pagincontrol' => 'Видете ($1) ($2)',
 459+ 'swl-watchlist-firstn' => 'први $1',
 460+ 'swl-watchlist-firstn-title' => '{{PLURAL:$1|Прв $1 резултат|Први $1 резултати}}',
 461+ 'swl-watchlist-no-items' => 'Немате ништо во списокот на набљудувања.',
 462+ 'swl-watchlist-can-mod-groups' => 'Можете да ги [[$1|измените групите на набљудувања]].',
 463+ 'swl-watchlist-can-mod-prefs' => 'Можете да ги [[$1|измените вашите нагодувања за набљудување]]., вклучувајќи кои својства да се набљудуваат.',
 464+ 'swl-watchlist-no-groups' => 'Сè уште не набљудувате ниедна група со списоци на набљудувања. [[$1|Измени нагодувања]].',
 465+ 'swl-email-propschanged' => 'Својствата на $1 се имаат изменето',
 466+ 'swl-email-propschanged-long' => "Едно или повеќе својства на '''$1''' што ги набљудувате се изменети од корисникот '''$2''' на $4 во $5 ч.. Можете да ги погледате овие и други промени на [$3 вашиот семантички список на набљудувања].",
 467+ 'swl-email-changes' => 'Измени во својства на [$2 $1]:',
 468+ 'prefs-swl' => 'Семантички список на набљудувања',
 469+ 'prefs-swlgroup' => 'Групи за набљудување',
 470+ 'prefs-swlglobal' => 'Општи наагодувања',
 471+ 'swl-prefs-emailnofity' => 'Испрати ми е-пошта кога ќе се изменат својствата што ги набљудувам',
 472+ 'swl-prefs-watchlisttoplink' => 'Прикажувај врска до Семантичкиот список на набљудувања најгоре на страницата',
 473+ 'swl-prefs-category-label' => "'''$1''': {{PLURAL:$2|својство|својства}} $3 од категоријата ''$4''",
 474+ 'swl-prefs-namespace-label' => "'''$1''': {{PLURAL:$2|својство|својства}} $3 од именскиот простор ''$4''",
 475+ 'swl-prefs-concept-label' => "'''$1''': {{PLURAL:$2|својство|својства}} $3 од концептот ''$4''",
 476+ 'swl-err-userid-xor-groupids' => 'Треба да ја наведете назнаката на корисникот или назнаките на групата (но не обете).',
 477+);
 478+
 479+/** Malay (Bahasa Melayu)
 480+ * @author Anakmalaysia
 481+ */
 482+$messages['ms'] = array(
 483+ 'semanticwatchlist-desc' => 'Membolehkan penenetuan kumpulan-kumpulan sifat semantik untuk satu atau lebih kategori/ruang nama yang kemudiannya boleh dipantau untuk perubahan',
 484+ 'right-semanticwatch' => 'Menggunakan senarai pantau semantik',
 485+ 'right-semanticwatchgroups' => '[[Special:WatchlistConditions|Mengubah suai]] kumpulan senarai pantau semantik',
 486+ 'special-semanticwatchlist' => 'Senarai Pantau Semantik',
 487+ 'special-watchlistconditions' => 'Syarat-syarat senarai pantau semantik',
 488+);
 489+
 490+/** Dutch (Nederlands)
 491+ * @author SPQRobin
 492+ * @author Siebrand
 493+ */
 494+$messages['nl'] = array(
 495+ 'semanticwatchlist-desc' => 'Maakt het mogelijk groepen van semantische eigenschappen aan te geven voor een of meer categorieën of naamruimten, zodat wijzigingen kunnen worden weergegeven',
 496+ 'right-semanticwatch' => 'Semantische volglijst gebruiken',
 497+ 'right-semanticwatchgroups' => 'De semantische volglijstgroepen [[Special:WatchlistConditions|aanpassen]]',
 498+ 'special-semanticwatchlist' => 'Semantische volglijst',
 499+ 'special-watchlistconditions' => 'Voorwaarden voor semantische volglijst',
 500+ 'swl-group-name' => 'Groepsnaam:',
 501+ 'swl-group-properties' => 'Eigenschappen die onder deze groep vallen:',
 502+ 'swl-group-remove-property' => 'Eigenschap verwijderen',
 503+ 'swl-group-add-property' => 'Eigenschap toevoegen',
 504+ 'swl-group-page-selection' => "Pagina's in",
 505+ 'swl-group-save' => 'Opslaan',
 506+ 'swl-group-delete' => 'Verwijderen',
 507+ 'swl-group-category' => 'categorie',
 508+ 'swl-group-namespace' => 'naamruimte',
 509+ 'swl-group-concept' => 'concept',
 510+ 'swl-group-confirmdelete' => 'Weet u zeker dat u de volglijstgroep "$1" wilt verwijderen?',
 511+ 'swl-group-save-all' => 'Allemaal opslaan',
 512+ 'swl-group-add-new-group' => 'Een nieuwe groep toevoegen',
 513+ 'swl-group-add-group' => 'Groep toevoegen',
 514+ 'swl-watchlist-position' => "Resultaat '''$1''' van de laatste wijzigingen beginnend met '''#$2'''.",
 515+ 'swl-watchlist-insertions' => 'Toegevoegd:',
 516+ 'swl-watchlist-deletions' => 'Verwijderd:',
 517+ 'swl-watchlist-pagincontrol' => 'Bekijken ($1) ($2)',
 518+ 'swl-watchlist-firstn' => 'eerste $1',
 519+ 'swl-watchlist-firstn-title' => 'Eerste $1 {{PLURAL:$1|resultaat|resultaten}}',
 520+ 'swl-email-propschanged' => 'Eigenschappen zijn veranderd op $1',
 521+ 'swl-email-propschanged-long' => "Een of meer eigenschappen die u volgt op '''$1''' zijn gewijzigd door gebruiker '''$2''' om $4 op $5. U kunt deze en andere wijzigingen bekijken op [$3 uw semantische volglijst].",
 522+ 'swl-email-changes' => 'Wijzigingen in eigenschappen op [$2 $1]:',
 523+ 'prefs-swl' => 'Semantische Volglijst',
 524+ 'prefs-swlgroup' => 'Te volgen groepen',
 525+ 'swl-prefs-emailnofity' => 'Mij e-mailen bij wijzigingen in eigenschappen die ik volg',
 526+ 'swl-prefs-category-label' => "'''$1''': {{PLURAL:$2|eigenschap|eigenschappen}} $3 van categorie ''$4''",
 527+ 'swl-prefs-namespace-label' => "'''$1''': {{PLURAL:$2|eigenschap|eigenschappen}} $3 van naamruimte ''$4''",
 528+ 'swl-prefs-concept-label' => "'''$1''': {{PLURAL:$2|eigenschap|eigenschappen}} $3 van concept ''$4''",
 529+ 'swl-err-userid-xor-groupids' => 'Geef de parameter "userid" of "groupids" op, maar niet beide.',
 530+);
 531+
 532+/** Portuguese (Português)
 533+ * @author Hamilton Abreu
 534+ */
 535+$messages['pt'] = array(
 536+ 'semanticwatchlist-desc' => 'Permite que os utilizadores sejam notificados de alterações específicas aos dados do MediaWiki Semântico',
 537+ 'right-semanticwatch' => 'Usar a lista de propriedades semânticas vigiadas',
 538+ 'right-semanticwatchgroups' => '[[Special:WatchlistConditions|Alterar]] os grupos de propriedades semânticas vigiadas',
 539+ 'special-semanticwatchlist' => 'Lista das Propriedades Semânticas Vigiadas',
 540+ 'special-watchlistconditions' => 'Condições da lista das propriedades semânticas vigiadas',
 541+ 'group-swladmins' => 'Administradores das Propriedades Semânticas Vigiadas',
 542+ 'group-swladmins-member' => 'Administrador das Propriedades Semânticas Vigiadas',
 543+ 'grouppage-swladmins' => 'Project:Administradores_das_Propriedades_Semânticas_Vigiadas',
 544+ 'swl-group-name' => 'Nome de grupo:',
 545+ 'swl-group-properties' => 'Propriedades abrangidas por este grupo:',
 546+ 'swl-group-remove-property' => 'Remover propriedade',
 547+ 'swl-group-add-property' => 'Adicionar propriedade',
 548+ 'swl-group-page-selection' => 'Páginas em',
 549+ 'swl-group-save' => 'Gravar',
 550+ 'swl-group-delete' => 'Eliminar',
 551+ 'swl-group-category' => 'categoria',
 552+ 'swl-group-namespace' => 'espaço nominal',
 553+ 'swl-group-concept' => 'conceito',
 554+ 'swl-group-confirmdelete' => 'Tem a certeza de que pretende eliminar o grupo de propriedades semântica vigiadas "$1"?',
 555+ 'swl-group-save-all' => 'Gravar todos',
 556+ 'swl-group-add-new-group' => 'Adicionar um grupo novo',
 557+ 'swl-group-add-group' => 'Adicionar grupo',
 558+ 'swl-watchlist-position' => "A mostrar '''$1''' das últimas alterações, começando pela '''$2ª'''.",
 559+ 'swl-watchlist-insertions' => 'Adições:',
 560+ 'swl-watchlist-deletions' => 'Antigas:',
 561+ 'swl-watchlist-pagincontrol' => 'Ver ($1) ($2)',
 562+ 'swl-watchlist-firstn' => 'primeiras $1',
 563+ 'swl-watchlist-firstn-title' => '{{PLURAL:$1|Primeiro resultado|Primeiros $1 results}}',
 564+ 'swl-watchlist-no-items' => 'A sua lista de propriedades semânticas vigiadas está vazia.',
 565+ 'swl-watchlist-can-mod-groups' => 'Pode [[$1|alterar os grupos de propriedades semânticas vigiadas]].',
 566+ 'swl-watchlist-can-mod-prefs' => 'Pode [[$1|alterar as suas preferências das propriedades semânticas vigiadas]], incluindo definir quais as propriedades que pretende vigiar.',
 567+ 'swl-watchlist-no-groups' => 'Ainda não está a vigiar nenhum grupo de propriedades semânticas vigiadas. [[$1|Alterar as suas preferências das propriedades semânticas vigiadas]].',
 568+ 'swl-email-propschanged' => 'Propriedades alteradas na $1',
 569+ 'swl-email-propschanged-long' => "Uma ou mais propriedades que está a vigiar na '''$1''' foram alteradas pelo utilizador '''$2''' às $4 de $5. Pode ver estas e outras alterações na sua [$3 lista de propriedades semânticas vigiadas].",
 570+ 'swl-email-changes' => 'Alterações de propriedades em [$2 $1]:',
 571+ 'prefs-swl' => 'Lista das propriedades semânticas vigiadas',
 572+ 'prefs-swlgroup' => 'Grupos para vigiar',
 573+ 'prefs-swlglobal' => 'Opções gerais',
 574+ 'swl-prefs-emailnofity' => 'Notificar-me por correio electrónico das alterações de propriedades que estou a vigiar',
 575+ 'swl-prefs-watchlisttoplink' => 'Mostrar um link para a lista de propriedades semânticas vigiadas no topo da página',
 576+ 'swl-prefs-category-label' => "'''$1''': {{PLURAL:$2|Propriedade|Propriedades}} $3 da categoria ''$4''",
 577+ 'swl-prefs-namespace-label' => "'''$1''': {{PLURAL:$2|Propriedade|Propriedades}} $3 do espaço nominal ''$4''",
 578+ 'swl-prefs-concept-label' => "'''$1''': {{PLURAL:$2|propriedade|propriedades}} $3 do conceito ''$4''",
 579+ 'swl-err-userid-xor-groupids' => 'Deve ser especificado, ou o parâmetro de identificação do utilizador, ou o parâmetro de identificações de grupos, mas não ambos.',
 580+);
 581+
 582+/** Somali (Soomaaliga)
 583+ * @author Maax
 584+ */
 585+$messages['so'] = array(
 586+ 'swl-group-category' => 'qeyb',
 587+);
 588+
 589+/** Tagalog (Tagalog)
 590+ * @author AnakngAraw
 591+ */
 592+$messages['tl'] = array(
 593+ 'semanticwatchlist-desc' => 'Nagpapahintulot ng pagtutukoy ng mga pangkat ng mga pag-aaring semantiko para sa isa o mas marami pang mga kategorya/mga puwang na pampangalan na maaari namang bantayan para sa mga pagbabago',
 594+ 'right-semanticwatch' => 'Gamitin ang semantikong talaan ng binabantayan',
 595+ 'right-semanticwatchgroups' => '[[Special:WatchlistConditions|Baguhin]] ang semantikong mga pangkat ng talaan ng binabantayan',
 596+ 'special-semanticwatchlist' => 'Semantikong Talaan ng Binabantayan',
 597+ 'special-watchlistconditions' => 'Mga kalagayan ng semantikong talaan ng binabantayan',
 598+);
 599+
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/SemanticWatchlist.i18n.php
___________________________________________________________________
Added: svn:eol-style
1600 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/SemanticWatchlist.settings.php
@@ -0,0 +1,54 @@
 2+<?php
 3+
 4+/**
 5+ * File defining the settings for the Semantic Watchlist extension.
 6+ * More info can be found at http://www.mediawiki.org/wiki/Extension:Semantic_Watchlist#Settings
 7+ *
 8+ * NOTICE:
 9+ * Changing one of these settings can be done by copying or cutting it,
 10+ * and placing it in LocalSettings.php, AFTER the inclusion of this extension.
 11+ *
 12+ * @file SemanticWatchlist.settings.php
 13+ * @ingroup SemanticWatchlist
 14+ *
 15+ * @licence GNU GPL v3+
 16+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 17+ */
 18+
 19+if ( !defined( 'MEDIAWIKI' ) ) {
 20+ die( 'Not an entry point.' );
 21+}
 22+
 23+# Users that can use the semantic watchlist.
 24+$wgGroupPermissions['*' ]['semanticwatch'] = false;
 25+$wgGroupPermissions['user' ]['semanticwatch'] = true;
 26+$wgGroupPermissions['autoconfirmed']['semanticwatch'] = true;
 27+$wgGroupPermissions['bot' ]['semanticwatch'] = false;
 28+$wgGroupPermissions['sysop' ]['semanticwatch'] = true;
 29+
 30+# Users that can modify the watchlist groups via Special:WatchlistConditions
 31+$wgGroupPermissions['*' ]['semanticwatchgroups'] = false;
 32+$wgGroupPermissions['user' ]['semanticwatchgroups'] = false;
 33+$wgGroupPermissions['autoconfirmed']['semanticwatchgroups'] = false;
 34+$wgGroupPermissions['bot' ]['semanticwatchgroups'] = false;
 35+$wgGroupPermissions['sysop' ]['semanticwatchgroups'] = true;
 36+$wgGroupPermissions['swladmins' ]['semanticwatchgroups'] = true;
 37+
 38+# Enable email notification or not?
 39+$egSWLEnableEmailNotify = true;
 40+
 41+# Send an email for every change (as opossed to a "something changed email" for the first $egSWLMaxMails changes)?
 42+$egSWLMailPerChange = true;
 43+
 44+# The maximum amount of generic emails to send about changes untill the user actually checks his semantic watchlist.
 45+$egSWLMaxMails = 1;
 46+
 47+# The default value for the user preference to send email notifications.
 48+$wgDefaultUserOptions['swl_email'] = true;
 49+
 50+# The default value for the user preference to display a top link to the semantic watchlist.
 51+$wgDefaultUserOptions['swl_watchlisttoplink'] = true;
 52+
 53+# Enable displaying a top link to the semantic watchlist?
 54+$egSWLEnableTopLink = true;
 55+
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/SemanticWatchlist.settings.php
___________________________________________________________________
Added: svn:eol-style
156 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/SemanticWatchlist.php
@@ -0,0 +1,140 @@
 2+<?php
 3+
 4+/**
 5+ * Initialization file for the Semantic Watchlist extension.
 6+ *
 7+ * Documentation: http://www.mediawiki.org/wiki/Extension:Semantic_Watchlist
 8+ * Support http://www.mediawiki.org/wiki/Extension_talk:Semantic_Watchlist
 9+ * Source code: http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/SemanticWatchlist
 10+ *
 11+ * @file SemanticWatchlist.php
 12+ * @ingroup SemanticWatchlist
 13+ *
 14+ * @licence GNU GPL v3+
 15+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 16+ */
 17+
 18+/**
 19+ * This documenation group collects source code files belonging to Semantic Watchlist.
 20+ *
 21+ * @defgroup SemanticWatchlist Semantic Watchlist
 22+ */
 23+
 24+if ( !defined( 'MEDIAWIKI' ) ) {
 25+ die( 'Not an entry point.' );
 26+}
 27+
 28+if ( version_compare( $wgVersion, '1.17', '<' ) ) {
 29+ die( '<b>Error:</b> Semantic Watchlist requires MediaWiki 1.17 or above.' );
 30+}
 31+
 32+// Show a warning if Semantic MediaWiki is not loaded.
 33+if ( ! defined( 'SMW_VERSION' ) ) {
 34+ die( '<b>Error:</b> You need to have <a href="http://semantic-mediawiki.org/wiki/Semantic_MediaWiki">Semantic MediaWiki</a> installed in order to use Semantic Watchlist.' );
 35+}
 36+
 37+if ( version_compare( SMW_VERSION, '1.6 alpha', '<' ) ) {
 38+ die( '<b>Error:</b> Semantic Watchlist requires Semantic MediaWiki 1.6 or above.' );
 39+}
 40+
 41+define( 'SemanticWatchlist_VERSION', '0.1' );
 42+
 43+$wgExtensionCredits[defined( 'SEMANTIC_EXTENSION_TYPE' ) ? 'semantic' : 'other'][] = array(
 44+ 'path' => __FILE__,
 45+ 'name' => 'Semantic Watchlist',
 46+ 'version' => SemanticWatchlist_VERSION,
 47+ 'author' => array(
 48+ '[http://www.mediawiki.org/wiki/User:Jeroen_De_Dauw Jeroen De Dauw] for [http://www.wikiworks.com/ WikiWorks]',
 49+ ),
 50+ 'url' => 'http://www.mediawiki.org/wiki/Extension:Semantic_Watchlist',
 51+ 'descriptionmsg' => 'semanticwatchlist-desc'
 52+);
 53+
 54+$egSWLScriptPath = $wgExtensionAssetsPath === false ? $wgScriptPath . '/extensions/SemanticWatchlist' : $wgExtensionAssetsPath . '/SemanticWatchlist';
 55+
 56+$wgExtensionMessagesFiles['SemanticWatchlist'] = dirname( __FILE__ ) . '/SemanticWatchlist.i18n.php';
 57+$wgExtensionAliasesFiles['SemanticWatchlist'] = dirname( __FILE__ ) . '/SemanticWatchlist.i18n.alias.php';
 58+
 59+$wgAutoloadClasses['SWLHooks'] = dirname( __FILE__ ) . '/SemanticWatchlist.hooks.php';
 60+
 61+$wgAutoloadClasses['ApiAddWatchlistGroup'] = dirname( __FILE__ ) . '/api/ApiAddWatchlistGroup.php';
 62+$wgAutoloadClasses['ApiDeleteWatchlistGroup'] = dirname( __FILE__ ) . '/api/ApiDeleteWatchlistGroup.php';
 63+$wgAutoloadClasses['ApiEditWatchlistGroup'] = dirname( __FILE__ ) . '/api/ApiEditWatchlistGroup.php';
 64+$wgAutoloadClasses['ApiQuerySemanticWatchlist'] = dirname( __FILE__ ) . '/api/ApiQuerySemanticWatchlist.php';
 65+
 66+$wgAutoloadClasses['SWLChangeSet'] = dirname( __FILE__ ) . '/includes/SWL_ChangeSet.php';
 67+$wgAutoloadClasses['SWLEdit'] = dirname( __FILE__ ) . '/includes/SWL_Edit.php';
 68+$wgAutoloadClasses['SWLEmailer'] = dirname( __FILE__ ) . '/includes/SWL_Emailer.php';
 69+$wgAutoloadClasses['SWLGroup'] = dirname( __FILE__ ) . '/includes/SWL_Group.php';
 70+$wgAutoloadClasses['SWLGroups'] = dirname( __FILE__ ) . '/includes/SWL_Groups.php';
 71+$wgAutoloadClasses['SWLPropertyChange'] = dirname( __FILE__ ) . '/includes/SWL_PropertyChange.php';
 72+$wgAutoloadClasses['SWLPropertyChanges'] = dirname( __FILE__ ) . '/includes/SWL_PropertyChanges.php';
 73+
 74+$wgAutoloadClasses['SpecialSemanticWatchlist'] = dirname( __FILE__ ) . '/specials/SpecialSemanticWatchlist.php';
 75+$wgAutoloadClasses['SpecialWatchlistConditions'] = dirname( __FILE__ ) . '/specials/SpecialWatchlistConditions.php';
 76+
 77+$wgSpecialPages['SemanticWatchlist'] = 'SpecialSemanticWatchlist';
 78+$wgSpecialPageGroups['SemanticWatchlist'] = 'changes';
 79+
 80+$wgSpecialPages['WatchlistConditions'] = 'SpecialWatchlistConditions';
 81+$wgSpecialPageGroups['WatchlistConditions'] = 'changes';
 82+
 83+$wgAPIModules['addswlgroup'] = 'ApiAddWatchlistGroup';
 84+$wgAPIModules['deleteswlgroup'] = 'ApiDeleteWatchlistGroup';
 85+$wgAPIModules['editswlgroup'] = 'ApiEditWatchlistGroup';
 86+$wgAPIListModules['semanticwatchlist'] = 'ApiQuerySemanticWatchlist';
 87+
 88+$wgHooks['LoadExtensionSchemaUpdates'][] = 'SWLHooks::onSchemaUpdate';
 89+$wgHooks['SMWStore::updateDataBefore'][] = 'SWLHooks::onDataUpdate';
 90+$wgHooks['GetPreferences'][] = 'SWLHooks::onGetPreferences';
 91+$wgHooks['UserSaveOptions'][] = 'SWLHooks::onUserSaveOptions';
 92+$wgHooks['AdminLinks'][] = 'SWLHooks::addToAdminLinks';
 93+$wgHooks['PersonalUrls'][] = 'SWLHooks::onPersonalUrls';
 94+
 95+$moduleTemplate = array(
 96+ 'localBasePath' => dirname( __FILE__ ),
 97+ 'remoteBasePath' => $egSWLScriptPath
 98+);
 99+
 100+$wgResourceModules['ext.swl.watchlist'] = $moduleTemplate + array(
 101+ 'styles' => array( 'specials/ext.swl.watchlist.css' ),
 102+ 'scripts' => array(
 103+ ),
 104+ 'dependencies' => array(),
 105+ 'messages' => array(
 106+ )
 107+);
 108+
 109+$wgResourceModules['ext.swl.watchlistconditions'] = $moduleTemplate + array(
 110+ 'styles' => array( 'specials/ext.swl.watchlistconditions.css' ),
 111+ 'scripts' => array(
 112+ 'specials/jquery.watchlistcondition.js',
 113+ 'specials/ext.swl.watchlistconditions.js'
 114+ ),
 115+ 'dependencies' => array(),
 116+ 'messages' => array(
 117+ 'swl-group-name',
 118+ 'swl-group-properties',
 119+ 'swl-group-remove-property',
 120+ 'swl-group-add-property',
 121+ 'swl-group-page-selection',
 122+ 'swl-group-save',
 123+ 'swl-group-delete',
 124+ 'swl-group-category',
 125+ 'swl-group-namespace',
 126+ 'swl-group-concept',
 127+ 'swl-group-confirmdelete',
 128+ )
 129+);
 130+
 131+require_once 'SemanticWatchlist.settings.php';
 132+
 133+// This overrides the default value for the setting in SMW, as the behaviour it enables is used by this extension.
 134+$smwgCheckChangesBeforeUpdate = true;
 135+
 136+$wgAvailableRights[] = 'semanticwatch';
 137+$wgAvailableRights[] = 'semanticwatchgroups';
 138+
 139+if ( $egSWLEnableEmailNotify ) {
 140+ $wgHooks['SWLGroupNotify'][] = 'SWLHooks::onGroupNotify';
 141+}
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/SemanticWatchlist.php
___________________________________________________________________
Added: svn:eol-style
1142 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/README
@@ -0,0 +1,23 @@
 2+These is the readme file for the Semantic Watchlist extension.
 3+
 4+Extension page on mediawiki.org: http://www.mediawiki.org/wiki/Extension:SemanticWatchlist
 5+Latest version of the readme file: http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/SemanticWatchlist/README?view=co
 6+
 7+== About ==
 8+
 9+Semantic Watchlist enables users to watch semantic properties by adding a new
 10+watchlist page (Special:SemanticWatchlist) that lists changes to these properties.
 11+Users can choose to follow one or more watchlist groups, which are administrator
 12+defined, and cover a set of properties and a set of pages (category, namespace,
 13+or SMW concept). Notification of changes to watched properties is also possible
 14+via email.
 15+
 16+=== Feature overview ===
 17+
 18+* A watchlist page (Special:SemanticWatchlist) listing changes to properties watched by the user.
 19+* Per-user optional email notification per edit that changes properties.
 20+* Integration with user preferences to allow users to specify which watchlist
 21+ groups they want to follow, and if they want to receive emails on changes.
 22+* Special:WatchListConditions as administration interface for watchlist groups.
 23+* API module to query property changes grouped by edit for a single user.
 24+* API modules to add, modify and delete the watchlist groups.
\ No newline at end of file
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/README
___________________________________________________________________
Added: svn:eol-style
125 + native
Index: tags/extensions/SemanticWatchlist/REL_0_1/SemanticWatchlist.hooks.php
@@ -0,0 +1,324 @@
 2+<?php
 3+
 4+/**
 5+ * Static class for hooks handled by the Semantic Watchlist extension.
 6+ *
 7+ * @since 0.1
 8+ *
 9+ * @file SemanticWatchlist.hooks.php
 10+ * @ingroup SemanticWatchlist
 11+ *
 12+ * @licence GNU GPL v3+
 13+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 14+ */
 15+final class SWLHooks {
 16+
 17+ /**
 18+ * Handle the onDataChanged hook of SMW >1.6, which gets called
 19+ * every time the value of a propery changes somewhere.
 20+ *
 21+ * @since 0.1
 22+ *
 23+ * @param SMWStore $store
 24+ * @param SMWChangeSet $changes
 25+ *
 26+ * @return true
 27+ */
 28+ public static function onDataUpdate( SMWStore $store, SMWSemanticData $newData ) {
 29+ $subject = $newData->getSubject();
 30+ $oldData = $store->getSemanticData( $subject );
 31+ $title = Title::makeTitle( $subject->getNamespace(), $subject->getDBkey() );
 32+
 33+ $groups = SWLGroups::getMatchingWatchGroups( $title );
 34+
 35+ $edit = false;
 36+
 37+ foreach ( $groups as /* SWLGroup */ $group ) {
 38+ $changeSet = SWLChangeSet::newFromSemanticData( $oldData, $newData, $group->getProperties() );
 39+
 40+ if ( $changeSet->hasUserDefinedProperties() ) {
 41+ if ( $edit === false ) {
 42+ $edit = new SWLEdit(
 43+ $title->getArticleID(),
 44+ $GLOBALS['wgUser']->getName(),
 45+ wfTimestampNow()
 46+ );
 47+
 48+ $edit->writeToDB();
 49+ }
 50+
 51+ $changeSet->setEdit( $edit );
 52+ $setId = $changeSet->writeToStore( $groups, $edit->getId() );
 53+
 54+ if ( $setId != 0 ) {
 55+ $group->notifyWatchingUsers( $changeSet );
 56+ }
 57+ }
 58+ }
 59+
 60+ return true;
 61+ }
 62+
 63+ /**
 64+ * Handles group notification.
 65+ *
 66+ * @since 0.1
 67+ *
 68+ * @param SWLGroup $group
 69+ * @param array $userIDs
 70+ * @param SMWChangeSet $changes
 71+ *
 72+ * @return true
 73+ */
 74+ public static function onGroupNotify( SWLGroup $group, array $userIDs, SWLChangeSet $changes ) {
 75+ global $egSWLMailPerChange;
 76+
 77+ foreach ( $userIDs as $userID ) {
 78+ $user = User::newFromId( $userID );
 79+
 80+ if ( $user->getOption( 'swl_email', false ) ) {
 81+ if ( !method_exists( 'Sanitizer', 'validateEmail' ) || Sanitizer::validateEmail( $user->getEmail() ) ) {
 82+ $lastNotify = $user->getOption( 'swl_last_notify' );
 83+ $lastWatch = $user->getOption( 'swl_last_watch' );
 84+
 85+ if ( is_null( $lastNotify ) || is_null( $lastWatch ) || $lastNotify < $lastWatch ) {
 86+ $mailCount = $user->getOption( 'swl_mail_count', 0 );
 87+
 88+ if ( $egSWLMailPerChange || $mailCount < $egSWLMaxMails ) {
 89+ SWLEmailer::notifyUser( $group, $user, $changes, $egSWLMailPerChange );
 90+ $user->setOption( 'swl_last_notify', wfTimestampNow() );
 91+ $user->setOption( 'swl_mail_count', $mailCount + 1 );
 92+ $user->saveSettings();
 93+ }
 94+ }
 95+ }
 96+ }
 97+ }
 98+
 99+ return true;
 100+ }
 101+
 102+ /**
 103+ * Adds the preferences of Semantic Watchlist to the list of available ones.
 104+ *
 105+ * @since 0.1
 106+ *
 107+ * @param User $user
 108+ * @param array $preferences
 109+ *
 110+ * @return true
 111+ */
 112+ public static function onGetPreferences( User $user, array &$preferences ) {
 113+ $groups = SWLGroups::getAll();
 114+
 115+ // Only show the email notification preference when email notifications are enabled.
 116+ if ( $GLOBALS['egSWLEnableEmailNotify'] ) {
 117+ $preferences['swl_email'] = array(
 118+ 'type' => 'toggle',
 119+ 'label-message' => 'swl-prefs-emailnofity',
 120+ 'section' => 'swl/swlglobal',
 121+ );
 122+ }
 123+
 124+ // Only show the top link preference when it's enabled.
 125+ if ( $GLOBALS['egSWLEnableTopLink'] ) {
 126+ $preferences['swl_watchlisttoplink'] = array(
 127+ 'type' => 'toggle',
 128+ 'label-message' => 'swl-prefs-watchlisttoplink',
 129+ 'section' => 'swl/swlglobal',
 130+ );
 131+ }
 132+
 133+ foreach ( $groups as /* SWLGroup */ $group ) {
 134+ if ( count( $group->getProperties() ) == 0 ) {
 135+ continue;
 136+ }
 137+
 138+ switch ( true ) {
 139+ case count( $group->getCategories() ) > 0 :
 140+ $type = 'category';
 141+ $name = $group->getCategories();
 142+ $name = $name[0];
 143+ break;
 144+ case count( $group->getNamespaces() ) > 0 :
 145+ $type = 'namespace';
 146+ $name = $group->getNamespaces();
 147+ $name = $name[0] == 0 ? wfMsg( 'main' ) : MWNamespace::getCanonicalName( $name[0] );
 148+ break;
 149+ case count( $group->getConcepts() ) > 0 :
 150+ $type = 'concept';
 151+ $name = $group->getConcepts();
 152+ $name = $item[0];
 153+ break;
 154+ }
 155+
 156+ $properties = $group->getProperties();
 157+
 158+ foreach ( $properties as &$property ) {
 159+ $property = "''$property''";
 160+ }
 161+
 162+ $preferences['swl_watchgroup_' . $group->getId()] = array(
 163+ 'type' => 'toggle',
 164+ 'label' => wfMsgExt(
 165+ "swl-prefs-$type-label",
 166+ 'parseinline',
 167+ $group->getName(),
 168+ count( $group->getProperties() ),
 169+ $GLOBALS['wgLang']->listToText( $properties ),
 170+ $name
 171+ ),
 172+ 'section' => 'swl/swlgroup',
 173+ );
 174+ }
 175+
 176+ return true;
 177+ }
 178+
 179+ /**
 180+ * Called just before saving user preferences/options.
 181+ * Find the watchlist groups the user watches, and update the swl_users_per_group table.
 182+ *
 183+ * @since 0.1
 184+ *
 185+ * @param User $user
 186+ * @param array $options
 187+ *
 188+ * @return true
 189+ */
 190+ public static function onUserSaveOptions( User $user, array &$options ) {
 191+ $dbw = wfGetDB( DB_MASTER );
 192+
 193+ $dbw->begin();
 194+
 195+ $dbw->delete(
 196+ 'swl_users_per_group',
 197+ array( 'upg_user_id' => $user->getId() )
 198+ );
 199+
 200+ foreach ( $options as $name => $value ) {
 201+ if ( strpos( $name, 'swl_watchgroup_' ) === 0 && $value ) {
 202+ $dbw->insert(
 203+ 'swl_users_per_group',
 204+ array(
 205+ 'upg_user_id' => $user->getId(),
 206+ 'upg_group_id' => (int)substr( $name, strrpos( $name, '_' ) + 1 )
 207+ )
 208+ );
 209+ }
 210+ }
 211+
 212+ $dbw->commit();
 213+
 214+ return true;
 215+ }
 216+
 217+ /**
 218+ * Schema update to set up the needed database tables.
 219+ *
 220+ * @since 0.1
 221+ *
 222+ * @param DatabaseUpdater $updater
 223+ *
 224+ * @return true
 225+ */
 226+ public static function onSchemaUpdate( /* DatabaseUpdater */ $updater = null ) {
 227+ global $wgDBtype;
 228+
 229+ if ( $wgDBtype == 'mysql' ) {
 230+ $updater->addExtensionUpdate( array(
 231+ 'addTable',
 232+ 'swl_groups',
 233+ dirname( __FILE__ ) . '/SemanticWatchlist.sql',
 234+ true
 235+ ) );
 236+ $updater->addExtensionUpdate( array(
 237+ 'addTable',
 238+ 'swl_changes',
 239+ dirname( __FILE__ ) . '/SemanticWatchlist.sql',
 240+ true
 241+ ) );
 242+ $updater->addExtensionUpdate( array(
 243+ 'addTable',
 244+ 'swl_sets',
 245+ dirname( __FILE__ ) . '/SemanticWatchlist.sql',
 246+ true
 247+ ) );
 248+ $updater->addExtensionUpdate( array(
 249+ 'addTable',
 250+ 'swl_edits_per_group',
 251+ dirname( __FILE__ ) . '/SemanticWatchlist.sql',
 252+ true
 253+ ) );
 254+ $updater->addExtensionUpdate( array(
 255+ 'addTable',
 256+ 'swl_sets_per_group',
 257+ dirname( __FILE__ ) . '/SemanticWatchlist.sql',
 258+ true
 259+ ) );
 260+ $updater->addExtensionUpdate( array(
 261+ 'addTable',
 262+ 'swl_users_per_group',
 263+ dirname( __FILE__ ) . '/SemanticWatchlist.sql',
 264+ true
 265+ ) );
 266+ }
 267+
 268+ return true;
 269+ }
 270+
 271+ /**
 272+ * Adds a link to Admin Links page.
 273+ *
 274+ * @since 0.1
 275+ *
 276+ * @return true
 277+ */
 278+ public static function addToAdminLinks( &$admin_links_tree ) {
 279+ $displaying_data_section = $admin_links_tree->getSection( wfMsg( 'adminlinks_browsesearch' ) );
 280+
 281+ // Escape if SMW hasn't added links.
 282+ if ( is_null( $displaying_data_section ) ) return true;
 283+ $smw_docu_row = $displaying_data_section->getRow( 'smw' );
 284+
 285+ $smw_docu_row->addItem( AlItem::newFromSpecialPage( 'WatchlistConditions' ) );
 286+
 287+ return true;
 288+ }
 289+
 290+ /**
 291+ * Called after the personal URLs have been set up, before they are shown.
 292+ * https://secure.wikimedia.org/wikipedia/mediawiki/wiki/Manual:Hooks/PersonalUrls
 293+ *
 294+ * @since 0.1
 295+ *
 296+ * @param array $personal_urls
 297+ * @param Title $title
 298+ *
 299+ * @return true
 300+ */
 301+ public static function onPersonalUrls( array &$personal_urls, Title &$title ) {
 302+ if ( $GLOBALS['egSWLEnableTopLink'] ) {
 303+ global $wgUser;
 304+
 305+ // Find the watchlist item and replace it by itself and the semantic watchlist.
 306+ if ( $wgUser->isLoggedIn() && $wgUser->getOption( 'swl_watchlisttoplink' ) ) {
 307+ $keys = array_keys( $personal_urls );
 308+ $watchListLocation = array_search( 'watchlist', $keys );
 309+ $watchListItem = $personal_urls[$keys[$watchListLocation]];
 310+
 311+ $url = SpecialPage::getTitleFor( 'SemanticWatchlist' )->getLinkUrl();
 312+ $semanticWatchlist = array(
 313+ 'text' => wfMsg( 'prefs-swl' ),
 314+ 'href' => $url,
 315+ 'active' => ( $url == $title->getLinkUrl() )
 316+ );
 317+
 318+ array_splice( $personal_urls, $watchListLocation, 1, array( $watchListItem, $semanticWatchlist ) );
 319+ }
 320+ }
 321+
 322+ return true;
 323+ }
 324+
 325+}
Property changes on: tags/extensions/SemanticWatchlist/REL_0_1/SemanticWatchlist.hooks.php
___________________________________________________________________
Added: svn:eol-style
1326 + native

Status & tagging log