Netbrain has uploaded a new change for review. https://gerrit.wikimedia.org/r/89815
Change subject: Initial import of code ...................................................................... Initial import of code Change-Id: Iee080eec7c33b355433b3d13ddec35e4b857295c --- A SemanticSifter.hooks.php A SemanticSifter.i18n.magic.php A SemanticSifter.i18n.php A SemanticSifter.php A css/ext.semanticsifter.css A images/remove-filter.png A js/ext.semanticsifter.js 7 files changed, 319 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/SemanticSifter refs/changes/15/89815/1 diff --git a/SemanticSifter.hooks.php b/SemanticSifter.hooks.php new file mode 100644 index 0000000..ab41664 --- /dev/null +++ b/SemanticSifter.hooks.php @@ -0,0 +1,225 @@ +<?php + +class SemanticSifterHooks { + public static function parserFunctionInit(Parser &$parser){ + $parser->setFunctionHook( 'sift', 'SemanticSifterHooks::parserFunction' ); + return true; + } + + public static function parserFunction(Parser &$parser){ + $parser->getOutput()->addModules( 'ext.semanticsifter' ); + $parser->disableCache(); + + //get parameters + list($category, $properties) = self::getArguments(func_get_args()); + + //reconstruct filters + $filters = self::extractFilterParameter(); + + //create smw query filter + $tableFilters = self::createSMWQueryFilter($filters); + + //create smw query + $tableComponent = self::createSMWQuery($parser, $category, $tableFilters, $properties); + + //get smw property information and generate filterering component + $filteringComponent = self::createFilteringComponent($parser, $properties, $filters); + + //create filters control + $filtersControl = self::createFiltersControl($parser, $filters); + + //create final html output + $output = <<<EOT + <div class="ss-container" style="display: none;"> + $filtersControl + $filteringComponent + <div style="overflow:auto"> + $tableComponent + </div> + </div> +EOT; + + return array( $output, 'noparse' => true, 'isHTML' => true ); + + } + + /** + * @param $args array + * @return array + */ + private static function getArguments(array $args = array()) + { + $category = $args[1]; + $properties = array(); + for ($i = 2; $i < count($args); $i++) { + $properties[] = $args[$i]; + } + return array($category, $properties); + } + + /** + * @return array + */ + private static function extractFilterParameter() + { + $filters = array(); + if (isset($_GET['filter'])) { + $rawFilters = explode(';', $_GET['filter']); + for ($x = 0; $x < sizeof($rawFilters); $x++) { + $parts = explode('::', base64_decode($rawFilters[$x])); + if (sizeof($parts) !== 2) { + continue; //malformed query string + } + $property = $parts[0]; + $value = $parts[1]; + $filters[$property][$value] = null; + } + } + return $filters; + } + + /** + * @param $filters + * @return array + */ + private static function createSMWQueryFilter($filters) + { + $tableFilters = ''; + if (!empty($filters)) { + foreach (array_keys($filters) as $property) { + $c = 0; + $tableFilters .= "[[$property::"; + foreach (array_keys($filters[$property]) as $value) { + if ($c > 0) { + $tableFilters .= '||'; + } + $tableFilters .= $value; + $c++; + } + $tableFilters .= ']]'; + } + } + return $tableFilters; + } + + /** + * @param Parser $parser + * @param $category + * @param $tableFilters + * @param $properties + * @return array + */ + public static function createSMWQuery(Parser &$parser, $category, $tableFilters, $properties) + { + $noResults = '<div class="ss-noresults">No results</div>'; + $tableQuery = "{{#ask: [[Category:$category]] $tableFilters|format=table|default=$noResults"; + foreach ($properties as $p) { + $tableQuery .= "|?$p"; + } + $tableQuery .= "}}"; + return $parser->recursiveTagParse($tableQuery); + } + + /** + * @param $smwStore + * @param $property + * @return array + */ + private static function getPropertyHits($smwStore, $property) + { + $wikiPages = $smwStore->getAllPropertySubjects($property); + $hits = array(); + foreach ($wikiPages as $wp) { + foreach ($smwStore->getPropertyValues($wp, $property) as $value) { + $title = $value->getSerialization(); + if (!array_key_exists($title, $hits)) { + $hits[$title] = 0; + } + $hits[$title]++; + } + } + return $hits; + } + + /** + * @param Parser $parser + * @param $filters + * @return string + */ + public static function createFiltersControl(Parser &$parser, $filters) + { + $filtersControl = ''; + foreach (array_keys($filters) as $property) { + foreach (array_keys($filters[$property]) as $value) { + //remove the filter from the query param + $replace = base64_encode("$property::$value"); + $filterArg = trim(str_replace($replace, '', $_GET['filter']), ';'); + if (!empty($filterArg)) { + $filterArg = "filter=$filterArg"; + } + $filtersControl .= '<a href="' . $parser->recursiveTagParse("{{localurl:{{FULLPAGENAME}}|$filterArg}}") . '"><span class="ss-filterControl">' . $value . '</span></a>'; + } + } + + if(!empty($filtersControl)){ + $clearButtonUrl = $parser->getTitle()->getLinkURL(); + $filtersControl = <<<EOT + <div class="ss-filters"> + <div class="ss-filterControls">Filters: $filtersControl</div> + <div> + <button onclick="window.location.href='$clearButtonUrl'">Clear all</button> + </div> + </div> +EOT; + } + return $filtersControl; + } + + /** + * @param Parser $parser + * @param $properties + * @param $filters + * @return String + */ + public static function createFilteringComponent(Parser &$parser, $properties, $filters) + { + $smwStore = \SMW\StoreFactory::getStore(); + $filterTree = array(); + foreach ($properties as $p) { + $filterTree[$p] = array( + "value" => "@$p", + "children" => array() + ); + + $property = SMWDIProperty::newFromUserLabel($p); + $hits = self::getPropertyHits($smwStore, $property); + + foreach ($hits as $title => $hit) { + $filterLink = $filters; + if (array_key_exists($p, $filterLink) && array_key_exists($title, $filterLink[$p])) { + continue; + } + + $filterLink[$p][$title] = null; + $filterArg = 'filter='; + foreach (array_keys($filterLink) as $property) { + foreach (array_keys($filterLink[$property]) as $value) { + $filterArg .= base64_encode("$property::$value"); + $filterArg .= ';'; + } + } + $filterArg = substr($filterArg, 0, -1); //remove last ; + $filterTree[$p]["children"][] = "* <span class=\"plainlinks\">[{{fullurl:{{FULLPAGENAME}}|$filterArg}} $title ($hit)]</span>"; + } + if (sizeof($filterTree[$p]["children"]) > 0) { + $children = join("\n", $filterTree[$p]["children"]); + $filterTree[$p] = "{$filterTree[$p]["value"]}\n$children"; + } else { + unset($filterTree[$p]); + } + } + $filterTree = join("\n", $filterTree); + $filteringComponent = $parser->recursivePreprocess("<sidebarmenu parser.menuitem.expanded=false editlink=off>Filter by property \n$filterTree</sidebarmenu>"); + return $filteringComponent; + } +} \ No newline at end of file diff --git a/SemanticSifter.i18n.magic.php b/SemanticSifter.i18n.magic.php new file mode 100644 index 0000000..3ebb026 --- /dev/null +++ b/SemanticSifter.i18n.magic.php @@ -0,0 +1,5 @@ +<?php +$magicWords = array(); +$magicWords['en'] = array( + 'sift' => array( 0, 'sift' ), +); \ No newline at end of file diff --git a/SemanticSifter.i18n.php b/SemanticSifter.i18n.php new file mode 100644 index 0000000..c83e427 --- /dev/null +++ b/SemanticSifter.i18n.php @@ -0,0 +1,4 @@ +<?php +$messages = array(); +$messages['en'] = array(); +$messages['qqq'] = array(); \ No newline at end of file diff --git a/SemanticSifter.php b/SemanticSifter.php new file mode 100644 index 0000000..f14a55c --- /dev/null +++ b/SemanticSifter.php @@ -0,0 +1,36 @@ +<?php +if ( !defined( 'MEDIAWIKI' ) ) { + echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" ); + die( 1 ); +} + +//credits +$wgExtensionCredits['parserhook'][] = array( + 'path' => __FILE__, + 'name' => 'Semantic Sifter', + 'description' => 'An extension which allows you to sift through semantic data', + 'descriptionmsg' => 'semanticsifter-desc', + 'version' => '0.1', + 'author' => 'Kim Eik', +); + +//autoload +$wgAutoloadClasses['SemanticSifterHooks'] = dirname( __FILE__ ) . '/SemanticSifter.hooks.php'; + +//hooks +$wgHooks['ParserFirstCallInit'][] = 'SemanticSifterHooks::parserFunctionInit'; + +//i18n +$wgExtensionMessagesFiles['SemanticSifter'] = dirname( __FILE__ ) . '/SemanticSifter.i18n.php'; +$wgExtensionMessagesFiles['SemanticSifterMagic'] = dirname( __FILE__ ) . '/SemanticSifter.i18n.magic.php'; + +//ajax +$wgAjaxExportList[] = 'SemanticSifterAjax::filter'; + +//resource modules +$wgResourceModules['ext.semanticsifter'] = array( + 'localBasePath' => dirname( __FILE__ ), + 'remoteExtPath' => 'SemanticSifter', + 'scripts' => '/js/ext.semanticsifter.js', + 'styles' => '/css/ext.semanticsifter.css' +); \ No newline at end of file diff --git a/css/ext.semanticsifter.css b/css/ext.semanticsifter.css new file mode 100644 index 0000000..e71d275 --- /dev/null +++ b/css/ext.semanticsifter.css @@ -0,0 +1,44 @@ +.ss-filterControl { + margin: 3px; + padding: 3px; + padding-right: 25px; + border: 1px solid grey; + border-radius: 3px; + background-image: url('../images/remove-filter.png'); + background-repeat:no-repeat; + background-position:right; + background-color: #f2f2f2; + display: inline-block; +} + +.ss-filterControls a:active, .ss-filterControls a, ss-filterControls a:hover, ss-filterControls a:visited { + color:#0645ad; + text-decoration: none; +} + +.ss-container .sidebar-menu-container a:visited,.ss-container .sidebar-menu-container a { + color:#0645ad; +} + +.ss-container .sidebar-menu-container { + margin: 1em 5px 1em 0px; +} + +.ss-container .sidebar-menu-container .sidebar-menu-item:first-child { + margin-top: 0px; +} + +.ss-filters { + border: 1px solid #aaa; + background-color: #f9f9f9; + padding: 10px; +} + +.ss-noresults { + background-color: #f9f9f9; + text-align: center; + font-weight: bold; + padding: 3px; + border: 1px solid #aaa; + margin: 1em 0px 0px 0px; +} diff --git a/images/remove-filter.png b/images/remove-filter.png new file mode 100644 index 0000000..8a9d1ff --- /dev/null +++ b/images/remove-filter.png Binary files differ diff --git a/js/ext.semanticsifter.js b/js/ext.semanticsifter.js new file mode 100644 index 0000000..f6de9ed --- /dev/null +++ b/js/ext.semanticsifter.js @@ -0,0 +1,5 @@ +( function ( $, mw ) { + $(document).ready(function(){ + $('.ss-container').fadeIn(); + }); +})( $, mw ); \ 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: newchange Gerrit-Change-Id: Iee080eec7c33b355433b3d13ddec35e4b857295c Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/SemanticSifter Gerrit-Branch: master Gerrit-Owner: Netbrain <k...@heldig.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits