Netbrain has submitted this change and it was merged.

Change subject: Initial import of code
......................................................................


Initial import of code

Change-Id: Iee080eec7c33b355433b3d13ddec35e4b857295c
---
A SemanticSifter.i18n.magic.php
A SemanticSifter.i18n.php
A SemanticSifter.php
A resources/main/css/ext.semanticsifter.css
A resources/main/js/ext.semanticsifter.js
A src/main/SemanticSifterHooks.php
A src/main/api/API.php
A src/main/components/SemanticSifterComponent.php
A src/main/model/FilterStorageHTTPQuery.php
A src/main/parserfunction/Sift.php
A src/main/parserfunction/SiftLink.php
A src/test/model/FilterStorageHTTPQueryTest.php
12 files changed, 842 insertions(+), 0 deletions(-)

Approvals:
  Netbrain: Verified; Looks good to me, approved



diff --git a/SemanticSifter.i18n.magic.php b/SemanticSifter.i18n.magic.php
new file mode 100644
index 0000000..1f529e0
--- /dev/null
+++ b/SemanticSifter.i18n.magic.php
@@ -0,0 +1,6 @@
+<?php
+$magicWords = array();
+$magicWords['en'] = array(
+       'sift' => array( 0, 'sift' ),
+       'siftlink' => array( 0, 'siftlink' ),
+);
\ No newline at end of file
diff --git a/SemanticSifter.i18n.php b/SemanticSifter.i18n.php
new file mode 100644
index 0000000..1250c09
--- /dev/null
+++ b/SemanticSifter.i18n.php
@@ -0,0 +1,17 @@
+<?php
+$messages = array();
+
+$messages['en'] = array(
+       'semanticsifter-desc' => 'An extension which allows you to sift through 
semantic data',
+       'semanticsifter-button-apply-filter' => 'Apply filter',
+       'semanticsifter-message-no-results' => 'No results',
+       'semanticsifter-message-siftlink-params-error' => 'Missing parameters 
to siftlink parser function.'
+);
+
+$messages['qqq'] = array(
+       'semanticsifter-desc' => 'Extension description',
+       'semanticsifter-button-apply-filter' => 'Button which applies filter',
+       'semanticsifter-message-no-results' => 'Message shown when no results 
yielded from filtering.',
+       'semanticsifter-message-siftlink-params-error' => 'Message displayed  
when error occurs in parameter handling of #siftlink parser function'
+
+);
\ No newline at end of file
diff --git a/SemanticSifter.php b/SemanticSifter.php
new file mode 100644
index 0000000..682cf9d
--- /dev/null
+++ b/SemanticSifter.php
@@ -0,0 +1,80 @@
+<?php
+if ( !defined( 'MEDIAWIKI' ) ) {
+       echo( "This file is an extension to the MediaWiki software and cannot 
be used standalone.\n" );
+       die( 1 );
+}
+
+if ( !defined( 'SMW_VERSION' ) ) {
+    echo( "SemanticSifter extension requires SemanticMediaWiki\n" );
+    die( 1 );
+}
+
+if ( !defined( 'ParamProcessor_VERSION' ) ) {
+       echo( "SemanticSifter extension requires ParamProcessor\n" );
+       die( 1 );
+}
+
+function endsWith($haystack, $needle){
+       return $needle === "" || substr($haystack, -strlen($needle)) === 
$needle;
+}
+
+spl_autoload_register( function ( $className ) {
+       $className = ltrim( $className, '\\' );
+       $fileName = '';
+       $namespace = '';
+       $lastNsPos = strripos( $className, '\\');
+
+       if ( $lastNsPos ) {
+               $namespace = substr( $className, 0, $lastNsPos );
+               $className = substr( $className, $lastNsPos + 1 );
+               $fileName  = str_replace( '\\', '/', $namespace ) . '/';
+       }
+
+       $fileName .= str_replace( '_', '/', $className ) . '.php';
+
+       $namespaceSegments = explode( '\\', $namespace );
+
+       if ( array_shift($namespaceSegments) === 'SemanticSifter' ) {
+               $fileName = substr($fileName,$lastNsPos);
+               $namespaceSegments = array_map('strtolower', 
$namespaceSegments);
+
+               if(endsWith($fileName,'Test.php')){
+                       require_once (__DIR__ . '/src/test/' . 
join('/',$namespaceSegments) . $fileName);
+               }else{
+                       require_once (__DIR__ . '/src/main/' . 
join('/',$namespaceSegments) . $fileName);
+               }
+       }
+} );
+
+call_user_func( function() {
+       global 
$wgExtensionCredits,$wgHooks,$wgExtensionMessagesFiles,$wgResourceModules,$wgAjaxExportList;
+
+       //credits
+       $wgExtensionCredits['parserhook'][] = array(
+               'path' => __FILE__,
+               'name' => 'Semantic Sifter',
+               'descriptionmsg' => 'semanticsifter-desc',
+               'version' => '0.1',
+               'author' => 'Kim Eik',
+       );
+
+       //ajax
+       $wgAjaxExportList[] = 'SemanticSifter\API\API::filter';
+
+       //hooks
+       $wgHooks['ParserFirstCallInit'][] = 
'SemanticSifter\SemanticSifterHooks::parserFunctionInit';
+       $wgHooks['UnitTestsList'][] = 
'SemanticSifter\SemanticSifterHooks::unitTestsInit';
+
+       //i18n
+       $wgExtensionMessagesFiles['SemanticSifter'] = dirname( __FILE__ ) . 
'/SemanticSifter.i18n.php';
+       $wgExtensionMessagesFiles['SemanticSifterMagic'] = dirname( __FILE__ ) 
. '/SemanticSifter.i18n.magic.php';
+
+       //resource modules
+       $wgResourceModules['ext.semanticsifter'] = array(
+               'localBasePath' => dirname( __FILE__ ),
+               'remoteExtPath' => 'SemanticSifter',
+               'dependencies' => array( 'jquery.chosen','mediawiki.Uri' ),
+               'scripts' => '/resources/main/js/ext.semanticsifter.js',
+               'styles' => '/resources/main/css/ext.semanticsifter.css'
+       );
+});
\ No newline at end of file
diff --git a/resources/main/css/ext.semanticsifter.css 
b/resources/main/css/ext.semanticsifter.css
new file mode 100644
index 0000000..a204913
--- /dev/null
+++ b/resources/main/css/ext.semanticsifter.css
@@ -0,0 +1,22 @@
+.ss-noresults {
+    background-color: #f9f9f9;
+    text-align: center;
+    font-weight: bold;
+    padding: 3px;
+    border: 1px solid #aaa;
+    margin: 1em 0px 0px 0px;
+}
+
+.ss-propertyfilter{
+    display: inline-block;
+    width: 20%;
+}
+
+.ss-propertyfilter li.search-field{
+    width: 100%;
+}
+
+.ss-propertyfilter li.search-field input.default{
+    width: auto !important;
+}
+
diff --git a/resources/main/js/ext.semanticsifter.js 
b/resources/main/js/ext.semanticsifter.js
new file mode 100644
index 0000000..23eb313
--- /dev/null
+++ b/resources/main/js/ext.semanticsifter.js
@@ -0,0 +1,55 @@
+(function ($, mw) {
+
+
+    function serializeObject(obj) {
+        var o = {};
+        var a = obj.serializeArray();
+        $.each(a, function () {
+            if (o[this.name] !== undefined) {
+                if (!o[this.name].push) {
+                    o[this.name] = [o[this.name]];
+                }
+                o[this.name].push(this.value || '');
+            } else {
+                o[this.name] = this.value || '';
+            }
+        });
+        return o;
+    }
+
+    $(document).ready(function () {
+        var container = $('.ss-container');
+        var form = $('.ss-filteringform');
+
+        form.each(function(){
+            $(this).submit(function () {
+                var filters = serializeObject(form)
+                var data = $.get(
+                    mw.util.wikiScript(),
+                    {
+                        action: 'ajax',
+                        rs: 'SemanticSifter\\API\\API::filter',
+                        rsargs: [JSON.stringify(filters)]
+                    }
+                ).done(function (response) {
+                        var uri = new mw.Uri();
+                        uri.extend( { filter: response } );
+                        window.location.href = uri;
+                });
+
+                return false;
+            });
+        });
+
+
+        container.find('.ss-propertyfilter > select').chosen({width:'100%'});
+
+        for(var key in window.SemanticSifter){
+            var config = window.SemanticSifter[key];
+            
$('#'+key).find('.ss-propertyfilter').width(config['filterbox-width']);
+
+        }
+
+        container.fadeIn();
+    });
+})($, mw);
\ No newline at end of file
diff --git a/src/main/SemanticSifterHooks.php b/src/main/SemanticSifterHooks.php
new file mode 100644
index 0000000..93fefb9
--- /dev/null
+++ b/src/main/SemanticSifterHooks.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace SemanticSifter;
+
+class SemanticSifterHooks {
+
+       public static function unitTestsInit( &$files ) {
+               $testDir = __DIR__ . '/src/test';
+               $files = array_merge( $files, glob( "$testDir/*Test.php" ) );
+               return true;
+       }
+
+       public static function parserFunctionInit( \Parser &$parser ) {
+               $parser->setFunctionHook( 'sift', 
'SemanticSifter\ParserFunction\Sift::parserHook' );
+               $parser->setFunctionHook( 'siftlink', 
'SemanticSifter\ParserFunction\SiftLink::parserHook' );
+               return true;
+       }
+}
\ No newline at end of file
diff --git a/src/main/api/API.php b/src/main/api/API.php
new file mode 100644
index 0000000..b8f8e6c
--- /dev/null
+++ b/src/main/api/API.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SemanticSifter\API;
+
+use SemanticSifter\Model\FilterStorageHTTPQuery;
+
+class API {
+
+       public static function filter($filters){
+               $filterStorage = new FilterStorageHTTPQuery(false);
+               $filters = json_decode($filters);
+               foreach($filters as $property => $values){
+                       if(!is_array($values)){
+                               $values = array($values);
+                       }
+
+                       foreach($values as $value){
+                               $filterStorage->addFilter($property,$value);
+                       }
+               }
+               return $filterStorage->getFiltersAsQueryString(false);
+       }
+} 
\ No newline at end of file
diff --git a/src/main/components/SemanticSifterComponent.php 
b/src/main/components/SemanticSifterComponent.php
new file mode 100644
index 0000000..ec0afcd
--- /dev/null
+++ b/src/main/components/SemanticSifterComponent.php
@@ -0,0 +1,153 @@
+<?php
+/**
+ * @author: Kim Eik
+ */
+
+namespace SemanticSifter\Components;
+
+use \SMW\StoreFactory;
+use SemanticSifter\Model\FilterStorageHTTPQuery;
+/**
+ * Class SemanticSifterComponent
+ * @package SemanticSifter
+ */
+class SemanticSifterComponent {
+
+       /**
+        * HTTP Query storage system for filter selection
+        * @var FilterStorageHTTPQuery
+        */
+       protected $filterStorage;
+
+       /**
+        * @var \Parser
+        */
+       protected $parser;
+
+       /**
+        * The title of which the component will be created
+        * @var \Title
+        */
+       protected $title;
+
+
+
+       /**
+        * @param $parser \Parser
+        * @param $filterStorage FilterStorageHTTPQuery
+        */
+       function __construct(&$parser, $filterStorage = null) {
+               if(is_null($filterStorage)){
+                       $filterStorage = new FilterStorageHTTPQuery();
+               }
+               $this->filterStorage = $filterStorage;
+               $this->parser = $parser;
+               $this->title = $parser->getTitle();
+       }
+
+       /**
+        * @param SMW\Store $smwStore
+        * @param SMW\DIProperty $property
+        * @return array
+        * @throws \Exception
+        */
+       protected  function getPropertyValue( $smwStore, $property ) {
+               $wikiPages = $smwStore->getAllPropertySubjects( $property );
+               $v = array();
+               foreach ( $wikiPages as $wp ) {
+                       foreach ( $smwStore->getPropertyValues( $wp, $property 
) as $value ) {
+                               if ( $value instanceof \SMWDataItem ) {
+                                       switch ( $value->getDIType() ) {
+                                               case 
\SMWDataItem::TYPE_WIKIPAGE:
+                                                       $title = 
$value->getTitle()->getFullText();
+                                                       break;
+                                               default:
+                                                       $title = 
$value->getSerialization();
+                                                       break;
+                                       }
+                                       if ( !array_key_exists( $title, $v ) ) {
+                                               $v[$title] = null;
+                                       }
+                                       continue;
+                               }
+                               throw new \Exception( "Unknown value type, 
expected: SMWDataItem but got: " . get_class( $value ) );
+                       }
+               }
+               return array_keys($v);
+       }
+
+       public function getComponents($query,$properties,$smwParams = 
array(),$smwProperties = array()){
+               //create smw query
+               if(empty($smwProperties)){
+                       $smwProperties = $properties;
+               }
+               $tableComponent = $this->createSMWQueryComponent($query, 
$smwProperties, $smwParams);
+
+               //get smw property information and generate filterering 
component
+               $filteringComponent = 
$this->createFilteringComponent($properties);
+
+               return array($filteringComponent,$tableComponent);
+       }
+
+       protected function createSMWQueryFilter() {
+               $filters = $this->filterStorage->getFilters();
+               $tableFilters = '';
+               if ( !empty( $filters ) ) {
+                       foreach ($filters as $property => $values ) {
+                               $c = 0;
+                               $tableFilters .= "[[$property:: ";
+                               foreach ( $values as $value ) {
+                                       if ( $c > 0 ) {
+                                               $tableFilters .= '||';
+                                       }
+                                       $tableFilters .= $value;
+                                       $c++;
+                               }
+                               $tableFilters .= ']]';
+                       }
+               }
+               return $tableFilters;
+       }
+
+       public function createSMWQueryComponent($query, $properties, $params = 
array() ) {
+               $smwParams = array(
+                       'format' => 'broadtable',
+                       'default' => '<div 
class="ss-noresults">'.wfMessage('semanticsifter-message-no-results')->text().'</div>'
+               );
+               $smwParams = array_merge($smwParams,$params);
+
+               $tableFilters = $this->createSMWQueryFilter();
+               $tableQuery = "{{#ask: $query $tableFilters";
+               foreach ( $properties as $property => $value ) {
+                       $tableQuery .= '|?'.(is_null($value) ? $property : 
"$property=$value");
+               }
+
+               foreach($smwParams as $name => $value){
+                       $tableQuery .= "|$name=$value";
+               }
+               $tableQuery .= '}}';
+
+               return $tableQuery;
+       }
+
+       protected function createFilteringComponent($properties) {
+               $smwStore = StoreFactory::getStore();
+               $output = '<form class="ss-filteringform">';
+               foreach ( $properties as $p => $v ) {
+                       $displayValue = is_null($v) ? $p : $v;
+                       $property = \SMWDIProperty::newFromUserLabel( $p );
+                       $pValues = $this->getPropertyValue( $smwStore, 
$property );
+                       $output .= "<div class=\"ss-propertyfilter\">";
+                       $output .= "<select name=\"$p\" 
class=\"ss-property-select\" data-placeholder=\"$displayValue\" 
title=\"$displayValue\" multiple>";
+                       foreach($pValues as $pValue){
+                               $selected = 
$this->filterStorage->filterExists($p,$pValue) ? 'selected' : '';
+                               $output .= "<option value=\"$pValue\" 
$selected>$pValue</option>";
+                       }
+                       $output .= "</select>";
+                       $output .= "</div>";
+               }
+               $output .= '<div><button 
type="submit">'.wfMessage('semanticsifter-button-apply-filter')->text().'</button></div>';
+               $output .= '</form>';
+               return $output;
+       }
+}
\ No newline at end of file
diff --git a/src/main/model/FilterStorageHTTPQuery.php 
b/src/main/model/FilterStorageHTTPQuery.php
new file mode 100644
index 0000000..fcfb440
--- /dev/null
+++ b/src/main/model/FilterStorageHTTPQuery.php
@@ -0,0 +1,185 @@
+<?php
+/**
+ * @author: Kim Eik
+ */
+
+namespace SemanticSifter\Model;
+
+/**
+ * Class FilterStorageHTTPQuery
+ * @package SemanticSifter
+ */
+class FilterStorageHTTPQuery{
+       const QUERY_KEY = 'filter';
+       const FILTER_SEPARATOR = ";";
+       const PROPERTY_VALUE_SEPARATOR = "::";
+
+
+       private $filters = array();
+
+       /**
+        * @param bool $loadFromGET
+        */
+       function __construct( $loadFromGET = true ) {
+               $loadFromGET = $loadFromGET && 
array_key_exists(self::QUERY_KEY,$_GET);
+               if($loadFromGET){
+                       $filters = 
explode(self::FILTER_SEPARATOR,urldecode($_GET[self::QUERY_KEY]));
+                       foreach($filters as $filter){
+                               if(!empty($filter)){
+                                       list($property, $value) = 
$this->getDecodedFilterString($filter);
+                                       $this->addFilter($property,$value);
+                               }
+                       }
+               }
+       }
+
+       /**
+        * @param $property
+        * @param $value
+        * @return $this
+        */
+       public function addFilter($property,$value){
+               $this->filters[$property][$value] = null;
+               return $this;
+       }
+
+       /**
+        * @param $property
+        * @param $value
+        * @return $this
+        */
+       public function removeFilter($property,$value = null){
+               if(is_null($value)){
+                       unset($this->filters[$property]);
+               }else{
+                       unset($this->filters[$property][$value]);
+                       if(count($this->filters[$property]) === 0){
+                               $this->removeFilter($property);
+                       }
+               }
+               return $this;
+       }
+
+       public function toggleFilter($property,$value){
+               if(array_key_exists($property,$this->filters) && 
array_key_exists($value,$this->filters[$property])){
+                       $this->removeFilter($property,$value);
+               }else{
+                       $this->addFilter($property,$value);
+               }
+               return $this;
+       }
+
+       /**
+        * @return array
+        */
+       public function getFilters() {
+               $filters = $this->filters;
+               foreach(array_keys($filters) as $key){
+                       $filters[$key] = array_keys($filters[$key]);
+               }
+               return $filters;
+       }
+
+       /**
+        * @return string
+        */
+       public function getFiltersAsQueryString($prefixQueryKey = true) {
+               $filters = $this->getFilters();
+               $queryArray = array();
+               foreach($filters as $property => $values){
+                       foreach($values as $value){
+                               $queryArray[] = 
$this->getEncodedFilterString($property,$value);
+                       }
+               }
+               if(count($queryArray) > 0){
+                       $queryStr = implode(self::FILTER_SEPARATOR,$queryArray);
+                       if($prefixQueryKey){
+                               $queryStr = self::QUERY_KEY.'='.$queryStr;
+                       }
+                       return $queryStr;
+               }
+               return '';
+       }
+
+       /**
+        * @return string
+        */
+       public function getFiltersAsSeparatedString() {
+               $filters = $this->getFilters();
+               $queryArray = array();
+               foreach($filters as $property => $values){
+                       foreach($values as $value){
+                               $queryArray[] = 
$this->getFilterString($property,$value);
+                       }
+               }
+               $queryStr = implode(self::FILTER_SEPARATOR,$queryArray);
+               return $queryStr;
+       }
+
+
+       /**
+        * @param $filters
+        * @throws \InvalidArgumentException
+        * @return $this
+        */
+       public function setFiltersFromSeparatedString($filters) {
+               if(!empty($filters)){
+                       $filters = explode(self::FILTER_SEPARATOR,$filters);
+                       foreach($filters as $filter){
+                               
if(substr_count($filter,self::PROPERTY_VALUE_SEPARATOR) !== 1){
+                                       throw new 
\InvalidArgumentException("'$filter'' does not match the expected format 
<property>".
+                                       
self::PROPERTY_VALUE_SEPARATOR."<value>".
+                                       self::FILTER_SEPARATOR."[<property>".
+                                       
self::PROPERTY_VALUE_SEPARATOR."<value>]...");
+                               }
+                               list($property,$value) = 
explode(self::PROPERTY_VALUE_SEPARATOR,$filter);
+                               $this->addFilter($property,$value);
+                       }
+               }
+               return $this;
+       }
+
+       /**
+        * @return int
+        */
+       public function size(){
+               return count($this->filters);
+       }
+
+       public function filterExists($property,$value){
+               return array_key_exists($property,$this->filters) && 
array_key_exists($value,$this->filters[$property]);
+       }
+
+       /**
+        * @param $property
+        * @param $value
+        * @return string
+        */
+       private function getEncodedFilterString($property,$value){
+               return base64_encode($this->getFilterString($property,$value));
+       }
+
+       /**
+        * @param $property
+        * @param $value
+        * @return string
+        */
+       private function getFilterString($property, $value){
+               return $property.self::PROPERTY_VALUE_SEPARATOR.$value;
+       }
+
+       /**
+        * @param $input
+        * @return array
+        * @throws \InvalidArgumentException
+        */
+       private function getDecodedFilterString($input){
+               $filter = base64_decode($input,true);
+               if($filter !== false && 
substr_count($filter,self::PROPERTY_VALUE_SEPARATOR) === 1){
+                       $filterParts = 
explode(self::PROPERTY_VALUE_SEPARATOR,$filter);
+                       return $filterParts;
+               }
+               throw new \InvalidArgumentException("'$input ($filter)' does 
not match the expected format 
<property>".self::PROPERTY_VALUE_SEPARATOR."<value>");
+       }
+
+}
\ No newline at end of file
diff --git a/src/main/parserfunction/Sift.php b/src/main/parserfunction/Sift.php
new file mode 100644
index 0000000..f2dea9c
--- /dev/null
+++ b/src/main/parserfunction/Sift.php
@@ -0,0 +1,133 @@
+<?php
+
+namespace SemanticSifter\ParserFunction;
+
+
+use ParamProcessor\ProcessedParam;
+use ParamProcessor\ProcessingResult;
+use ParamProcessor\Processor;
+use SemanticSifter\Components\SemanticSifterComponent;
+
+class Sift {
+
+       private $component;
+
+       public function __construct(\Parser &$parser){
+               $this->component = new SemanticSifterComponent($parser);
+       }
+
+       public static function parserHook(\Parser &$parser){
+               $parser->getOutput()->addModules( 'ext.semanticsifter' );
+               $parser->disableCache();
+
+               try{
+                       //get parameters
+                       $args = func_get_args();
+                       array_shift($args);
+                       $args = self::getParameters( $args );
+                       $parameters = $args->getParameters();
+
+                       if(!$parameters){
+                               throw new \InvalidArgumentException("Error with 
parameters.");
+                       }
+
+                       $properties = array();
+                       $smwProperties = array();
+                       $smwParams = array();
+                       foreach($parameters as $param){
+                               if(strpos($param->getName(),'?') === 0){
+                                       
$properties[substr($param->getName(),1)] = $param->getValue();
+                               }elseif(strpos($param->getName(),'!?') === 0){
+                                       
$smwProperties[substr($param->getName(),2)] = $param->getValue();
+                               }elseif(strpos($param->getName(),'!') === 0){
+                                       $smwParams[substr($param->getName(),1)] 
= $param->getValue();
+                               }
+                       }
+
+                       $component = new SemanticSifterComponent($parser);
+                       list($filteringComponent,$tableComponent) = 
$component->getComponents(
+                               $parameters['query']->getValue(),
+                               $properties,
+                               $smwParams,
+                               $smwProperties
+                       );
+
+                       //create final html output
+                       $id = uniqid();
+                       $jsonParams = array(
+                               'filterbox-width' => 
$parameters['filterbox-width']->getValue(),
+                       );
+                       $jsonParams = json_encode($jsonParams);
+                       $output = <<<EOT
+                               <script type="text/javascript">
+                                       if(window.SemanticSifter === undefined){
+                                               window.SemanticSifter = {};
+                                       }
+                                       window.SemanticSifter['$id'] = 
$jsonParams;
+                               </script>
+                               <div id="$id" class="ss-container" 
style="display: none;">
+                                               {$filteringComponent}
+                                               <div style="overflow:auto">
+                                                       
{$parser->recursiveTagParse($tableComponent)}
+                                               </div>
+                               </div>
+EOT;
+               }catch (\Exception $e){
+                       //TODO add a more user friendly error
+                       return $e;
+               }
+               $output = 
str_replace(array("\r","\n","\t"),array('','',''),$output);
+               return array($output, 'noparse' => true, 'isHTML' => true );
+       }
+
+
+       private static function getParameters( array $args = array() ){
+               //Hackish use of ParamProcessor to make input parameters 
similar to #ask parser function input
+               $properties = array();
+               foreach($args as $key => &$arg){
+                       $arg = trim($arg);
+                       switch(substr($arg,0,1)){
+                               case '?':
+                               case '!':
+                                       list($property,$display) = 
array_pad(explode('=',$arg,2), 2, null);
+                                       $properties[$property] = new 
ProcessedParam($property,$display,false);
+                                       unset($args[$key]);
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+
+               $parameterDefs = array(
+                       array(
+                               'name' => 'query',
+                               'default' => '',
+                               'message' => 'semanticsifter-param-query',
+                       ),
+                       array(
+                               'name' => 'filterbox-width',
+                               'default' => '20%',
+                               'message' => 
'semanticsifter-param-filterbox-width',
+                       )
+               );
+
+               $defaultParams = array(
+                       array(
+                               0 => 'query',
+                               1 => Processor::PARAM_UNNAMED,
+                       )
+               );
+
+
+               $processor = Processor::newDefault();
+               
$processor->setFunctionParams($args,$parameterDefs,$defaultParams);
+               $processedParams = $processor->processParameters();
+
+               //Postprocess params
+               $processedParams = new 
ProcessingResult(array_merge($processedParams->getParameters(),$properties),$processedParams->getErrors());
+               if($processedParams->hasFatal()){
+                       return false;
+               }
+               return $processedParams;
+       }
+} 
\ No newline at end of file
diff --git a/src/main/parserfunction/SiftLink.php 
b/src/main/parserfunction/SiftLink.php
new file mode 100644
index 0000000..e091dd2
--- /dev/null
+++ b/src/main/parserfunction/SiftLink.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SemanticSifter\ParserFunction;
+
+use SemanticSifter\Model\FilterStorageHTTPQuery;
+
+class SiftLink {
+
+       public static function parserHook(\Parser &$parser,$title, $filters, 
$text){
+               if(is_null($title) || is_null($filters) || is_null($text)){
+                       return 
wfMessage('semanticsifter-message-siftlink-params-error');
+               }
+
+               try{
+                       $filterStorage = new FilterStorageHTTPQuery(false);
+                       $filterStorage->setFiltersFromSeparatedString($filters);
+
+                       $title = \Title::newFromText($title);
+
+                       $filters = $filterStorage->size() > 0 ? array(
+                               FilterStorageHTTPQuery::QUERY_KEY => 
$filterStorage->getFiltersAsQueryString(false)
+                       ) : array();
+
+                       $output = \Linker::link($title,$text,array('title' => 
null),$filters);
+
+                       return array($output, 'noparse' => true, 'isHTML' => 
true );
+               }catch (\Exception $e){
+                       //TODO add a more user friendly error
+                       return $e;
+               }
+       }
+
+} 
\ No newline at end of file
diff --git a/src/test/model/FilterStorageHTTPQueryTest.php 
b/src/test/model/FilterStorageHTTPQueryTest.php
new file mode 100644
index 0000000..6a61c99
--- /dev/null
+++ b/src/test/model/FilterStorageHTTPQueryTest.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * @author: Kim Eik
+ */
+
+namespace SemanticSifter;
+
+
+class FilterStorageHTTPQueryTest extends \MediaWikiTestCase {
+       /**
+        * @var FilterStorageHTTPQuery
+        */
+       private  $filterStorage;
+
+       protected function setUp() {
+               parent::setUp();
+               $this->filterStorage = new FilterStorageHTTPQuery();
+       }
+
+       protected function tearDown() {
+               unset( $this->filterStorage );
+               unset($_GET['filter']);
+               parent::tearDown();
+       }
+
+       public function testGetFiltersFromGET() {
+               $_GET['filter'] = base64_encode( 'Property::Value' );
+               $this->filterStorage = new FilterStorageHTTPQuery();
+               $this->assertArrayEquals( array(
+                       'Property' => array( 'Value' )
+               ), $this->filterStorage->getFilters() );
+       }
+
+       public function testGetFiltersFromGETWhenInvalidFormat() {
+               $this->setExpectedException('InvalidArgumentException');
+               $_GET['filter'] = 'INVALID';
+               $this->filterStorage = new FilterStorageHTTPQuery();
+       }
+
+       public function testAddFilter() {
+               
$this->assertType('SemanticSifter\FilterStorageHTTPQuery',$this->filterStorage->addFilter('Property','Value'));
+               $this->assertArrayEquals( array(
+                       'Property' => array( 'Value' )
+               ), $this->filterStorage->getFilters() );
+       }
+
+       public function testAddManyFilterByChaining() {
+               
$this->filterStorage->addFilter('Property','Value')->addFilter('Property','Value2')->addFilter('Property2','Value');
+               $this->assertArrayEquals( array(
+                       'Property' => array( 'Value','Value2' ),
+                       'Property2' => array( 'Value' )
+               ), $this->filterStorage->getFilters() );
+       }
+
+       public function testRemoveFilter() {
+               $this->filterStorage->addFilter('Property','Value');
+               $this->filterStorage->addFilter('Property','Value2');
+               $this->filterStorage->removeFilter('Property','Value');
+               $this->assertArrayEquals( array(
+                       'Property' => array( 'Value2' )
+               ), $this->filterStorage->getFilters() );
+       }
+
+       public function testRemoveAllFiltersByProperty() {
+               $this->filterStorage->addFilter('Property','Value');
+               $this->filterStorage->addFilter('Property','Value2');
+               $this->filterStorage->removeFilter('Property');
+               $this->assertArrayEquals( array(), 
$this->filterStorage->getFilters() );
+       }
+
+       public function testGetFiltersAsHTTPQueryString() {
+               $expected = 'filter=';
+               $expected .= base64_encode('Property::Value');
+               $expected .= ';';
+               $expected .= base64_encode('Property::Value2');
+
+               $this->filterStorage->addFilter('Property','Value');
+               $this->filterStorage->addFilter('Property','Value2');
+               $this->assertEquals( $expected, 
$this->filterStorage->getFiltersAsQueryString() );
+       }
+
+       public function testGetFiltersAsSeparatedString() {
+               $expected = 'Property::Value';
+               $expected .= ';';
+               $expected .= 'Property::Value2';
+
+               $this->filterStorage->addFilter('Property','Value');
+               $this->filterStorage->addFilter('Property','Value2');
+               $this->assertEquals( $expected, 
$this->filterStorage->getFiltersAsSeparatedString() );
+       }
+
+       public function testToggleFilter(){
+               $this->filterStorage->toggleFilter('Property','Value');
+               $this->assertArrayEquals( array(
+                       'Property' => array('Value')
+               ), $this->filterStorage->getFilters() );
+               $this->filterStorage->toggleFilter('Property','Value');
+               $this->assertArrayEquals( array(), 
$this->filterStorage->getFilters() );
+       }
+
+       public function testSetFiltersFromSeparatedString(){
+               $string = 'Property::Value;Property::Value2';
+               $this->assertArrayEquals(array(
+                       'Property' => array(
+                               'Value',
+                               'Value2'
+                       )
+               
),$this->filterStorage->setFiltersFromSeparatedString($string)->getFilters());
+       }
+
+
+       public function testSetFiltersFromSeparatedStringThatIsEmpty(){
+               $string = '';
+               
$this->assertArrayEquals(array(),$this->filterStorage->setFiltersFromSeparatedString($string)->getFilters());
+       }
+
+}
\ No newline at end of file

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

Gerrit-MessageType: merged
Gerrit-Change-Id: Iee080eec7c33b355433b3d13ddec35e4b857295c
Gerrit-PatchSet: 16
Gerrit-Project: mediawiki/extensions/SemanticSifter
Gerrit-Branch: master
Gerrit-Owner: Netbrain <[email protected]>
Gerrit-Reviewer: Netbrain <[email protected]>

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

Reply via email to