Index: trunk/extensions/RDF/extensions/MwRdf.php |
— | — | @@ -22,14 +22,16 @@ |
23 | 23 | * @subpackage Extensions |
24 | 24 | */ |
25 | 25 | if (defined('MEDIAWIKI')) { |
| 26 | + require_once('GlobalFunctions.php'); |
26 | 27 | if (!defined('RDFAPI_INCLUDE_DIR')) { |
27 | 28 | wfDebugDieBacktrace("MwRdf: you must install RAP (RDF API for PHP) " . |
28 | 29 | "and define 'RDFAPI_INCLUDE_DIR' in LocalSettings.php"); |
29 | 30 | } |
30 | 31 | require_once(RDFAPI_INCLUDE_DIR . "RdfAPI.php"); |
31 | 32 | require_once(RDFAPI_INCLUDE_DIR . PACKAGE_VOCABULARY); |
32 | | - |
33 | | - define('MWRDF_VERSION', '0.3'); |
| 33 | + require_once('SpecialPage.php'); |
| 34 | + |
| 35 | + define('MWRDF_VERSION', '0.4'); |
34 | 36 | define('MWRDF_XML_TYPE_PREFS', |
35 | 37 | "application/rdf+xml,text/xml;q=0.7," . |
36 | 38 | "application/xml;q=0.5,text/rdf;q=0.1"); |
— | — | @@ -72,13 +74,14 @@ |
73 | 75 | 'ntriples' => MwRdfOutputNtriples); |
74 | 76 | |
75 | 77 | $wgRdfNamespaces = array('cc' => CC_NS); |
76 | | - |
| 78 | + $wgRdfCacheExpiry = 86400; |
| 79 | + |
77 | 80 | /* Config end */ |
78 | 81 | |
79 | 82 | $wgExtensionFunctions[] = 'setupMwRdf'; |
80 | 83 | |
81 | 84 | function setupMwRdf() { |
82 | | - global $wgParser, $wgMessageCache, $wgRequest, $wgOut; |
| 85 | + global $wgParser, $wgMessageCache, $wgRequest, $wgOut, $wgHooks; |
83 | 86 | |
84 | 87 | $wgMessageCache->addMessages(array('rdf' => 'Rdf', |
85 | 88 | 'rdf-inpage' => "Embedded In-page Turtle", |
— | — | @@ -128,7 +131,8 @@ |
129 | 132 | if (isset($title) && mb_strlen($title) > 0) { |
130 | 133 | $nt =& Title::newFromText($title); |
131 | 134 | |
132 | | - if ($action == 'view' && |
| 135 | + if (isset($nt) && |
| 136 | + $action == 'view' && |
133 | 137 | $nt->getNamespace() != NS_SPECIAL) |
134 | 138 | { |
135 | 139 | $rdft =& Title::makeTitle(NS_SPECIAL, "Rdf"); |
— | — | @@ -139,6 +143,13 @@ |
140 | 144 | $wgOut->addMetadataLink($linkdata); |
141 | 145 | } |
142 | 146 | } |
| 147 | + |
| 148 | + # We set some hooks for invalidating the cache |
| 149 | + |
| 150 | + $wgHooks['ArticleSave'][] = 'MwRdfOnArticleSave'; |
| 151 | + $wgHooks['ArticleSaveComplete'][] = 'MwRdfOnArticleSaveComplete'; |
| 152 | + $wgHooks['TitleMoveComplete'][] = 'MwRdfOnTitleMoveComplete'; |
| 153 | + $wgHooks['ArticleDeleteComplete'][] = 'MwRdfOnArticleDeleteComplete'; |
143 | 154 | } |
144 | 155 | |
145 | 156 | function wfSpecialRdf($par) { |
— | — | @@ -283,8 +294,9 @@ |
284 | 295 | } |
285 | 296 | |
286 | 297 | $fullModel = ModelFactory::getDefaultModel(); |
| 298 | + $title = $article->mTitle; |
287 | 299 | |
288 | | - $uri = $article->mTitle->getFullURL(); |
| 300 | + $uri = $title->getFullURL(); |
289 | 301 | $fullModel->setBaseURI($uri); |
290 | 302 | |
291 | 303 | foreach ($modelnames as $modelname) { |
— | — | @@ -296,11 +308,19 @@ |
297 | 309 | " for model '$modelname'."); |
298 | 310 | } |
299 | 311 | |
300 | | - $model = $modelfunc($article); |
301 | | - if ($model != null) { |
| 312 | + # Check the cache... |
| 313 | + |
| 314 | + $model = MwRdfGetCache($title, $modelname); |
| 315 | + |
| 316 | + # If it's not there, regenerate. |
| 317 | + |
| 318 | + if (!isset($model) || !$model) { |
| 319 | + $model = $modelfunc($article); |
| 320 | + MwRdfSetCache($title, $modelname, $model); |
| 321 | + } |
| 322 | + |
| 323 | + if (isset($model)) { |
302 | 324 | $fullModel->addModel($model); |
303 | | - # free the memory |
304 | | - $model->close(); |
305 | 325 | } |
306 | 326 | } |
307 | 327 | |
— | — | @@ -366,7 +386,7 @@ |
367 | 387 | $parser = new N3Parser(); |
368 | 388 | $parser->baseURI = $article->mTitle->getFullURL(); |
369 | 389 | |
370 | | - $prefixes = array_merge($default_prefixes, MwRdfNamespacePrefixes(), $wgRdfNamespaces); |
| 390 | + $prefixes = array_merge($default_prefixes, $wgRdfNamespaces, MwRdfNamespacePrefixes()); |
371 | 391 | |
372 | 392 | $prelude = ""; |
373 | 393 | |
— | — | @@ -383,7 +403,7 @@ |
384 | 404 | $RDFS_comment, |
385 | 405 | MwRdfLiteral("Error parsing in-page RDF: " |
386 | 406 | . $parser->errors[0] . |
387 | | - "\n code here: \n '" . $rdf . "'", null, |
| 407 | + "\n code here: \n '" . $prelude . $rdf . "'" , null, |
388 | 408 | "en"))); |
389 | 409 | } |
390 | 410 | } |
— | — | @@ -664,20 +684,26 @@ |
665 | 685 | # Find prefixed links |
666 | 686 | preg_match_all("/\[\[([^|\]]+:[^|\]]+)(\|.*)?\]\]/", $text, $matches); |
667 | 687 | |
668 | | - foreach ($matches[1] as $linktext) { |
669 | | - $iwlink = Title::newFromText($linktext); |
670 | | - $pfx = $iwlink->getInterwiki(); |
671 | | - if (mb_strlen($pfx) > 0) { |
672 | | - $iwr = MwRdfTitleResource($iwlink); |
673 | | - # XXX: Wikitravel uses some 4+ prefixes for sister site links |
674 | | - if ($wgContLang->getLanguageName($pfx) && mb_strlen($pfx) < 4) { |
675 | | - $model->add(new Statement($tr, $DCTERM['hasVersion'], $iwr)); |
676 | | - $model->add(new Statement($iwr, $DCMES['language'], |
677 | | - MwRdfLanguage($pfx))); |
678 | | - } else { |
679 | | - # XXX: Express the "sister site" relationship better |
680 | | - $model->add(new Statement($tr, $RDFS_seeAlso, |
681 | | - $iwr)); |
| 688 | + # XXX: this fails for Category: namespace; why? |
| 689 | + |
| 690 | + if (isset($matches)) { |
| 691 | + foreach ($matches[1] as $linktext) { |
| 692 | + $iwlink = Title::newFromText($linktext); |
| 693 | + if (isset($iwlink)) { |
| 694 | + $pfx = $iwlink->getInterwiki(); |
| 695 | + if (mb_strlen($pfx) > 0) { |
| 696 | + $iwr = MwRdfTitleResource($iwlink); |
| 697 | + # XXX: Wikitravel uses some 4+ prefixes for sister site links |
| 698 | + if ($wgContLang->getLanguageName($pfx) && mb_strlen($pfx) < 4) { |
| 699 | + $model->add(new Statement($tr, $DCTERM['hasVersion'], $iwr)); |
| 700 | + $model->add(new Statement($iwr, $DCMES['language'], |
| 701 | + MwRdfLanguage($pfx))); |
| 702 | + } else { |
| 703 | + # XXX: Express the "sister site" relationship better |
| 704 | + $model->add(new Statement($tr, $RDFS_seeAlso, |
| 705 | + $iwr)); |
| 706 | + } |
| 707 | + } |
682 | 708 | } |
683 | 709 | } |
684 | 710 | } |
— | — | @@ -844,16 +870,132 @@ |
845 | 871 | $prefixes = array(); |
846 | 872 | $spaces = $wgLang->getNamespaces(); |
847 | 873 | foreach ($spaces as $code => $text) { |
848 | | - $prefix = str_replace(' ', '_', $text); |
849 | | - # XXX: figure out a less sneaky way to do this |
850 | | - # XXX: won't work if article title isn't at the end of the URL |
851 | | - $title = Title::makeTitle($code, ''); |
852 | | - $uri = $title->getFullURL(); |
853 | | - $prefixes[$prefix] = $uri; |
| 874 | + $prefix = urlencode(str_replace(' ', '_', $text)); |
| 875 | + # FIXME: this is a hack |
| 876 | + if (strpos($prefix, '%') === false) { |
| 877 | + # XXX: figure out a less sneaky way to do this |
| 878 | + # XXX: won't work if article title isn't at the end of the URL |
| 879 | + $title = Title::makeTitle($code, ''); |
| 880 | + $uri = $title->getFullURL(); |
| 881 | + $prefixes[$prefix] = $uri; |
| 882 | + } |
854 | 883 | } |
855 | 884 | } |
856 | 885 | return $prefixes; |
857 | 886 | } |
| 887 | + |
| 888 | + function MwRdfGetCache($title, $modelname) { |
| 889 | + global $wgMemc; |
| 890 | + if (!isset($wgMemc)) { |
| 891 | + return false; |
| 892 | + } else { |
| 893 | + $ntrip = $wgMemc->get(MwRdfCacheKey($title, $modelname)); |
| 894 | + if (isset($ntrip) && $ntrip) { |
| 895 | + return MwRdfNTriplesToModel($ntrip); |
| 896 | + } else { |
| 897 | + return null; |
| 898 | + } |
| 899 | + } |
| 900 | + } |
| 901 | + |
| 902 | + function MwRdfClearCache($title, $modelname) { |
| 903 | + global $wgMemc; |
| 904 | + if (!isset($wgMemc)) { |
| 905 | + return false; |
| 906 | + } else { |
| 907 | + return $wgMemc->delete(MwRdfCacheKey($title, $modelname)); |
| 908 | + } |
| 909 | + } |
| 910 | + |
| 911 | + function MwRdfClearCacheAll($title) { |
| 912 | + global $wgRdfModelFunctions; |
| 913 | + $nt = $title; |
| 914 | + foreach (array_keys($wgRdfModelFunctions) as $modelname) { |
| 915 | + MwRdfClearCache($nt, $modelname); |
| 916 | + } |
| 917 | + } |
| 918 | + |
| 919 | + function MwRdfSetCache($title, $modelname, $model) { |
| 920 | + global $wgMemc, $wgRdfCacheExpiry; |
| 921 | + if (!isset($wgMemc)) { |
| 922 | + return false; |
| 923 | + } else { |
| 924 | + return $wgMemc->set(MwRdfCacheKey($title, $modelname), |
| 925 | + MwRdfModelToNTriples($model), |
| 926 | + $wgRdfCacheExpiry); |
| 927 | + } |
| 928 | + } |
| 929 | + |
| 930 | + function MwRdfCacheKey($title, $modelname) { |
| 931 | + if (!isset($title)) { |
| 932 | + return null; |
| 933 | + } else { |
| 934 | + global $wgDBname; |
| 935 | + $dbkey = $title->getDBkey(); |
| 936 | + $ns = $title->getNamespace(); |
| 937 | + return "$wgDBname:rdf:$ns:$dbkey:$modelname"; |
| 938 | + } |
| 939 | + } |
| 940 | + |
| 941 | + # Before saving, we clear the cache for articles this article links to |
| 942 | + |
| 943 | + function MwRdfOnArticleSave($article, $dc1, $dc2, $dc3, $dc4, $dc5, $dc6) { |
| 944 | + $id = $article->mTitle->getArticleID(); |
| 945 | + if ($id != 0) { |
| 946 | + $dbr =& wfGetDB(DB_SLAVE); |
| 947 | + $res = $dbr->select(array('cur', 'links'), |
| 948 | + array('cur_namespace', 'cur_title'), |
| 949 | + array('cur_id = l_to', |
| 950 | + 'l_from = ' . $id), |
| 951 | + 'MwRdfOnArticleSave', |
| 952 | + array('ORDER BY' => 'cur_namespace, cur_title')); |
| 953 | + while ($res && $row = $dbr->fetchObject($res)) { |
| 954 | + $lt = Title::makeTitle($row->cur_namespace, $row->cur_title); |
| 955 | + MwRdfClearCache($lt, 'linksto'); |
| 956 | + } |
| 957 | + } |
| 958 | + return true; |
| 959 | + } |
| 960 | + |
| 961 | + # Clear the cache when the article is saved |
| 962 | + |
| 963 | + function MwRdfOnArticleSaveComplete($article, $dc1, $dc2, $dc3, $dc4, $dc5, $dc6) { |
| 964 | + MwRdfClearCacheAll($article->mTitle); |
| 965 | + return true; |
| 966 | + } |
| 967 | + |
| 968 | + # Clear the cache when an article is moved |
| 969 | + |
| 970 | + function MwRdfOnTitleMoveComplete($oldt, $newt, $user, $oldid, $newid) { |
| 971 | + MwRdfClearCacheAll($newt); |
| 972 | + MwRdfClearCacheAll($oldt); |
| 973 | + return true; |
| 974 | + } |
| 975 | + |
| 976 | + # Clear the cache when an article is deleted |
| 977 | + |
| 978 | + function MwRdfOnArticleDeleteComplete($article, $user, $reason) { |
| 979 | + MwRdfClearCacheAll($article->mTitle); |
| 980 | + return true; |
| 981 | + } |
| 982 | + |
| 983 | + function MwRdfNTriplesToModel($ntrip) { |
| 984 | + require_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_N3); |
| 985 | + $parser = new N3Parser(); |
| 986 | + $model = $parser->parse2model($ntrip); |
| 987 | + return $model; |
| 988 | + } |
| 989 | + |
| 990 | + function MwRdfModelToNTriples($model) { |
| 991 | + # Make sure serializer is loaded |
| 992 | + if (!isset($model) || $model->size() == 0) { |
| 993 | + return ''; |
| 994 | + } else { |
| 995 | + require_once(RDFAPI_INCLUDE_DIR . PACKAGE_SYNTAX_N3); |
| 996 | + $ser = new NTripleSerializer(); |
| 997 | + return $ser->serialize($model); |
| 998 | + } |
| 999 | + } |
858 | 1000 | } |
859 | 1001 | |
860 | | -?> |
\ No newline at end of file |
| 1002 | +?> |
Index: trunk/extensions/RDF/README.RDF.txt |
— | — | @@ -1,7 +1,7 @@ |
2 | 2 | MediaWiki RDF extension |
3 | 3 | |
4 | | -version 0.3 |
5 | | -16 November 2005 |
| 4 | +version 0.4 |
| 5 | +24 January 2006 |
6 | 6 | |
7 | 7 | This is the README file for the RDF extension for MediaWiki |
8 | 8 | software. The extension is only useful if you've got a MediaWiki |
— | — | @@ -19,7 +19,7 @@ |
20 | 20 | |
21 | 21 | == License == |
22 | 22 | |
23 | | -Copyright 2005 Evan Prodromou <evan@wikitravel.org> |
| 23 | +Copyright 2005, 2006 Evan Prodromou <evan@wikitravel.org>. |
24 | 24 | |
25 | 25 | This program is free software; you can redistribute it and/or modify |
26 | 26 | it under the terms of the GNU General Public License as published by |
— | — | @@ -255,7 +255,8 @@ |
256 | 256 | $wgRdfOutputFunctions -- A map of output format to functions that |
257 | 257 | generate that output. You can add new output |
258 | 258 | formats by adding to this array. |
259 | | - |
| 259 | +$wgRdfCacheExpiry -- time in seconds to expire cached items |
| 260 | + |
260 | 261 | == Extending == |
261 | 262 | |
262 | 263 | You can add new RDF models to the framework by creating a model |
— | — | @@ -300,7 +301,7 @@ |
301 | 302 | |
302 | 303 | * Store statements in DB: statements could be stored in the database |
303 | 304 | when the page is saved and retrieved when needed. This would make it |
304 | | - to do extended queries based on information about *all* pages. |
| 305 | + possible to do extended queries based on information about *all* pages. |
305 | 306 | * Performance: there wasn't much performance tuning and there are |
306 | 307 | probably way too many DB hits and reads and such. |
307 | 308 | * Semantic tuning: I'd like to make sure that the statements in the |