Index: trunk/extensions/DataTransclusion/DataTransclusion.i18n.magic.php |
— | — | @@ -15,6 +15,3 @@ |
16 | 16 | 'record' => array( 0, 'record' ), |
17 | 17 | ); |
18 | 18 | |
19 | | -$magicWords['ar'] = array( |
20 | | - 'record' => array( '0', 'تسجيل', 'record' ), |
21 | | -); |
\ No newline at end of file |
Index: trunk/extensions/DataTransclusion/DataTransclusionHandler.php |
— | — | @@ -9,314 +9,275 @@ |
10 | 10 | * @licence GNU General Public Licence 2.0 or later |
11 | 11 | */ |
12 | 12 | |
13 | | -class DataTransclusionHandler { |
14 | | - static function buildAssociativeArguments ( $params ) { |
15 | | - // build associative arguments from flat parameter list |
16 | | - $argv = array(); |
17 | | - $i = 1; |
18 | | - foreach ( $params as $p ) { |
19 | | - if ( preg_match( '/^\s*(\S.*?)\s*=\s*(.*?)\s*$/', $p, $m ) ) { |
20 | | - $k = $m[1]; |
21 | | - $v = preg_replace( '/^"\s*(.*?)\s*"$/', '$1', $m[2] ); // strip any quotes enclosing the value |
22 | | - } else { |
23 | | - $v = trim( $p ); |
24 | | - $k = $i; |
25 | | - $i += 1; |
26 | | - } |
| 13 | +if( !defined( 'MEDIAWIKI' ) ) { |
| 14 | + echo( "Not a valid entry point.\n" ); |
| 15 | + die( 1 ); |
| 16 | +} |
27 | 17 | |
28 | | - $argv[$k] = $v; |
| 18 | +class DataTransclusionHandler { |
| 19 | + static function buildAssociativeArguments ( $params ) { |
| 20 | + // build associative arguments from flat parameter list |
| 21 | + $argv = array(); |
| 22 | + $i = 1; |
| 23 | + foreach ( $params as $p ) { |
| 24 | + if ( preg_match( '/^\s*(\S.*?)\s*=\s*(.*?)\s*$/', $p, $m ) ) { |
| 25 | + $k = $m[1]; |
| 26 | + $v = preg_replace( '/^"\s*(.*?)\s*"$/', '$1', $m[2] ); // strip any quotes enclosing the value |
29 | 27 | } |
30 | | - |
31 | | - return $argv; |
32 | | - } |
33 | | - |
34 | | - /** |
35 | | - * Entry point for the {{#record}} parser function. |
36 | | - * This is a wrapper around handleRecordTag |
37 | | - */ |
38 | | - static function handleRecordFunction ( $parser ) { |
39 | | - $params = func_get_args(); |
40 | | - array_shift( $params ); // first is &$parser, strip it |
41 | | - |
42 | | - // first user-supplied parameter must be category name |
43 | | - if ( !$params ) { |
44 | | - return ''; // no category specified, return nothing |
| 28 | + else { |
| 29 | + $v = trim( $p ); |
| 30 | + $k = $i; |
| 31 | + $i += 1; |
45 | 32 | } |
46 | 33 | |
47 | | - // build associative arguments from flat parameter list |
48 | | - $argv = DataTransclusionHandler::buildAssociativeArguments( $params ); |
49 | | - |
50 | | - // FIXME: error messages contining special blocks like <nowiki> don't get re-substitutet correctly. |
51 | | - $text = DataTransclusionHandler::handleRecordTransclusion( null, $argv, $parser, false ); |
52 | | - |
53 | | - return array( $text, 'noparse' => false, 'isHTML' => false ); |
| 34 | + $argv[$k] = $v; |
54 | 35 | } |
55 | 36 | |
56 | | - static function errorMessage( $key, $asHTML ) { |
57 | | - $params = func_get_args(); |
58 | | - array_shift( $params ); // $key |
59 | | - array_shift( $params ); // $asHTML |
| 37 | + return $argv; |
| 38 | + } |
60 | 39 | |
61 | | - if ( $asHTML ) { |
62 | | - $mode = 'parseinline'; |
63 | | - } else { |
64 | | - $mode = 'parsemag'; |
65 | | - } |
| 40 | + /** |
| 41 | + * Entry point for the {{#record}} parser function. |
| 42 | + * This is a wrapper around handleRecordTag |
| 43 | + */ |
| 44 | + static function handleRecordFunction ( $parser ) { |
| 45 | + $params = func_get_args(); |
| 46 | + array_shift( $params ); // first is &$parser, strip it |
66 | 47 | |
67 | | - $m = wfMsgExt( $key, $mode, $params ); |
| 48 | + // first user-supplied parameter must be category name |
| 49 | + if ( !$params ) { |
| 50 | + return ''; // no category specified, return nothing |
| 51 | + } |
68 | 52 | |
69 | | - return "<span class=\"error\">$m</span>"; |
70 | | - } |
| 53 | + // build associative arguments from flat parameter list |
| 54 | + $argv = DataTransclusionHandler::buildAssociativeArguments( $params ); |
71 | 55 | |
72 | | - /** |
73 | | - * Entry point for the <record> tag parser hook. Delegates to handleRecordTransclusion. |
74 | | - */ |
75 | | - static function handleRecordTag( $key, $argv, $parser ) { |
76 | | - DataTransclusionHandler::handleRecordTransclusion( $key, $argv, $parser, true ); |
77 | | - } |
| 56 | + //FIXME: error messages contining special blocks like <nowiki> don't get re-substitutet correctly. |
| 57 | + $text = DataTransclusionHandler::handleRecordTransclusion( null, $argv, $parser, false ); |
| 58 | + return array( $text, 'noparse' => false, 'isHTML' => false ); |
| 59 | + } |
78 | 60 | |
79 | | - /** |
80 | | - * Fetches a records and renders it, according to the given array of parameters. |
81 | | - * Common implementation for parser tag and parser function. |
82 | | - */ |
83 | | - static function handleRecordTransclusion( $key, $argv, $parser, $asHTML ) { |
84 | | - // find out which data source to use... |
85 | | - if ( empty( $argv['source'] ) ) { |
86 | | - if ( empty( $argv[1] ) ) { |
87 | | - return DataTransclusionHandler::errorMessage( 'datatransclusion-missing-source', $asHTML ); // TESTME |
88 | | - } else { |
89 | | - $sourceName = $argv[1]; |
90 | | - } |
91 | | - } else { |
92 | | - $sourceName = $argv['source']; |
93 | | - } |
| 61 | + static function errorMessage( $key, $asHTML ) { |
| 62 | + $params = func_get_args(); |
| 63 | + array_shift( $params ); // $key |
| 64 | + array_shift( $params ); // $asHTML |
94 | 65 | |
95 | | - $source = DataTransclusionHandler::getDataSource( $sourceName ); |
96 | | - if ( empty( $source ) ) { |
97 | | - return DataTransclusionHandler::errorMessage( 'datatransclusion-unknown-source', $asHTML, $sourceName ); // TESTME |
98 | | - } |
| 66 | + if ( $asHTML ) $mode = 'parseinline'; |
| 67 | + else $mode = 'parsemag'; |
99 | 68 | |
100 | | - // find out how to find the desired record |
101 | | - if ( empty( $argv['by'] ) ) { |
102 | | - $by = $source->getDefaultKey(); |
103 | | - } else { |
104 | | - $by = $argv['by']; |
105 | | - } |
| 69 | + $m = wfMsgExt( $key, $mode, $params ); |
| 70 | + return "<span class=\"error\">$m</span>"; |
| 71 | + } |
106 | 72 | |
107 | | - $keyFields = $source->getKeyFields(); |
108 | | - if ( ! in_array( $by, $keyFields ) ) { |
109 | | - return DataTransclusionHandler::errorMessage( 'datatransclusion-bad-argument-by', $asHTML, $sourceName, $by, join( ', ', $keyFields ) ); // TESTME |
110 | | - } |
| 73 | + /** |
| 74 | + * Entry point for the <record> tag parser hook. Delegates to handleRecordTransclusion. |
| 75 | + */ |
| 76 | + static function handleRecordTag( $key, $argv, $parser ) { |
| 77 | + DataTransclusionHandler::handleRecordTransclusion( $key, $argv, $parser, true ); |
| 78 | + } |
111 | 79 | |
112 | | - if ( !empty( $argv['key'] ) ) { |
113 | | - $key = $argv['key']; |
114 | | - } else if ( $key === null || $key === false ) { |
115 | | - if ( empty( $argv[2] ) ) { |
116 | | - return DataTransclusionHandler::errorMessage( 'datatransclusion-missing-argument-key', $asHTML ); // TESTME |
117 | | - } else { |
118 | | - $key = $argv[2]; |
119 | | - } |
120 | | - } |
| 80 | + /** |
| 81 | + * Fetches a records and renders it, according to the given array of parameters. |
| 82 | + * Common implementation for parser tag and parser function. |
| 83 | + */ |
| 84 | + static function handleRecordTransclusion( $key, $argv, $parser, $asHTML ) { |
| 85 | + //find out which data source to use... |
| 86 | + if ( empty( $argv['source'] ) ) { |
| 87 | + if ( empty( $argv[1] ) ) return DataTransclusionHandler::errorMessage( 'datatransclusion-missing-source', $asHTML ); //TESTME |
| 88 | + else $sourceName = $argv[1]; |
| 89 | + } else { |
| 90 | + $sourceName = $argv['source']; |
| 91 | + } |
121 | 92 | |
122 | | - // find out how to render the record |
123 | | - if ( empty( $argv['template'] ) ) { |
124 | | - if ( empty( $argv[3] ) ) { |
125 | | - return DataTransclusionHandler::errorMessage( 'datatransclusion-missing-argument-template', $asHTML ); // TESTME |
126 | | - } else { |
127 | | - $template = $argv[3]; |
128 | | - } |
129 | | - } else { |
130 | | - $template = $argv['template']; |
131 | | - } |
| 93 | + $source = DataTransclusionHandler::getDataSource( $sourceName ); |
| 94 | + if ( empty( $source ) ) return DataTransclusionHandler::errorMessage( 'datatransclusion-unknown-source', $asHTML, $sourceName ); //TESTME |
132 | 95 | |
133 | | - // load the record |
134 | | - $record = $source->fetchRecord( $by, $key ); |
135 | | - if ( empty( $record ) ) return DataTransclusionHandler::errorMessage( 'datatransclusion-record-not-found', $asHTML, $sourceName, $by, $key ); // TESTME |
| 96 | + //find out how to find the desired record |
| 97 | + if ( empty( $argv['by'] ) ) $by = $source->getDefaultKey(); |
| 98 | + else $by = $argv['by']; |
136 | 99 | |
137 | | - // render the record into wiki text |
138 | | - $t = Title::newFromText( $template, NS_TEMPLATE ); |
139 | | - if ( empty( $t ) ) { |
140 | | - return DataTransclusionHandler::errorMessage( 'datatransclusion-bad-template-name', $asHTML, $template ); // TESTME |
141 | | - } |
| 100 | + $keyFields = $source->getKeyFields(); |
| 101 | + if ( ! in_array( $by, $keyFields ) ) return DataTransclusionHandler::errorMessage( 'datatransclusion-bad-argument-by', $asHTML, $sourceName, $by, join(', ', $keyFields) ); //TESTME |
142 | 102 | |
143 | | - // FIXME: log the template we used into the parser output, like regular template use |
144 | | - // (including templates used by the template, etc) |
| 103 | + if ( !empty( $argv['key'] ) ) $key = $argv['key']; |
| 104 | + else if ( $key === null || $key === false ) { |
| 105 | + if ( empty( $argv[2] ) ) return DataTransclusionHandler::errorMessage( 'datatransclusion-missing-argument-key', $asHTML ); //TESTME |
| 106 | + else $key = $argv[2]; |
| 107 | + } |
145 | 108 | |
146 | | - $handler = new DataTransclusionHandler( $parser, $source, $t ); |
| 109 | + //find out how to render the record |
| 110 | + if ( empty( $argv['template'] ) ) { |
| 111 | + if ( empty( $argv[3] ) ) return DataTransclusionHandler::errorMessage( 'datatransclusion-missing-argument-template', $asHTML ); //TESTME |
| 112 | + else $template = $argv[3]; |
| 113 | + } else { |
| 114 | + $template = $argv['template']; |
| 115 | + } |
147 | 116 | |
148 | | - $record = $handler->normalizeRecord( $record ); |
149 | | - $text = $handler->render( $record ); |
| 117 | + //load the record |
| 118 | + $record = $source->fetchRecord( $by, $key ); |
| 119 | + if ( empty( $record ) ) return DataTransclusionHandler::errorMessage( 'datatransclusion-record-not-found', $asHTML, $sourceName, $by, $key ); //TESTME |
150 | 120 | |
151 | | - if ( $text === false ) { |
152 | | - return DataTransclusionHandler::errorMessage( 'datatransclusion-unknown-template', $asHTML, $template ); // TESTME |
153 | | - } |
| 121 | + //render the record into wiki text |
| 122 | + $t = Title::newFromText( $template, NS_TEMPLATE ); |
| 123 | + if ( empty( $t ) ) return DataTransclusionHandler::errorMessage( 'datatransclusion-bad-template-name', $asHTML, $template ); //TESTME |
154 | 124 | |
155 | | - // set parser output expiry |
156 | | - $expire = $source->getCacheDuration(); |
157 | | - if ( $expire !== false && $expire !== null ) { |
158 | | - $parser->getOutput()->updateCacheExpiry( $expire ); // NOTE: this works only since r67185 //TESTME |
159 | | - } |
| 125 | + //FIXME: log the template we used into the parser output, like regular template use |
| 126 | + // (including templates used by the template, etc) |
160 | 127 | |
161 | | - if ( $asHTML && $parser ) { // render into HTML if desired |
162 | | - $html = $parser->recursiveTagParse( $text ); |
163 | | - return $html; // TESTME |
164 | | - } else { |
165 | | - return $text; // TESTME |
166 | | - } |
167 | | - } |
| 128 | + $handler = new DataTransclusionHandler( $parser, $source, $t ); |
168 | 129 | |
169 | | - function __construct( $parser, $source, $template, $templateText = null ) { |
170 | | - $this->template = $template; |
171 | | - $this->source = $source; |
172 | | - $this->parser = $parser; |
173 | | - $this->templateText = $templateText; |
174 | | - } |
| 130 | + $record = $handler->normalizeRecord( $record ); |
| 131 | + $text = $handler->render( $record ); |
175 | 132 | |
176 | | - function render( $record ) { |
177 | | - // XXX: use cached & preparsed template. $template doesn't have the right type, it seems |
178 | | - /* |
179 | | - list( $text, $this->template ) = $this->parser->getTemplateDom( $this->template ); |
180 | | - $frame = $this->parser->getPreprocessor()->newCustomFrame( $record ); |
181 | | - $text = $frame->expand( $template ); |
182 | | - */ |
| 133 | + if ( $text === false ) return DataTransclusionHandler::errorMessage( 'datatransclusion-unknown-template', $asHTML, $template ); //TESTME |
183 | 134 | |
184 | | - // XXX: trying another way. but $piece['parts'] needs to be a PPNode. how to do that? |
185 | | - /* |
186 | | - $frame = $this->parser->getPreprocessor()->newCustomFrame( $record ); |
| 135 | + //set parser output expiry |
| 136 | + $expire = $source->getCacheDuration(); |
| 137 | + if ( $expire !== false && $expire !== null ) { |
| 138 | + $parser->getOutput()->updateCacheExpiry( $expire ); //NOTE: this works only since r67185 //TESTME |
| 139 | + } |
187 | 140 | |
188 | | - $piece = array(); |
| 141 | + if ( $asHTML && $parser ) { //render into HTML if desired |
| 142 | + $html = $parser->recursiveTagParse( $text ); |
| 143 | + return $html; //TESTME |
| 144 | + } else { |
| 145 | + return $text; //TESTME |
| 146 | + } |
| 147 | + } |
189 | 148 | |
190 | | - if ( $this->template->getNamespace() == NS_TEMPLATE ) $n = ""; |
191 | | - else $n = $this->template->getNsText() . ":"; |
| 149 | + function __construct( $parser, $source, $template, $templateText = null ) { |
| 150 | + $this->template = $template; |
| 151 | + $this->source = $source; |
| 152 | + $this->parser = $parser; |
| 153 | + $this->templateText = $templateText; |
| 154 | + } |
192 | 155 | |
193 | | - $piece ['title'] = $n . $this->template->getText(); |
194 | | - $piece['parts'] = $record; |
195 | | - $piece['lineStart'] = false; //XXX: ugly. can't know here whether the brace was at the start of a line |
| 156 | + function render( $record ) { |
| 157 | + //XXX: use cached & preparsed template. $template doesn't have the right type, it seems |
| 158 | + /* |
| 159 | + list( $text, $this->template ) = $this->parser->getTemplateDom( $this->template ); |
| 160 | + $frame = $this->parser->getPreprocessor()->newCustomFrame( $record ); |
| 161 | + $text = $frame->expand( $template ); |
| 162 | + */ |
196 | 163 | |
197 | | - $ret = $this->parser->braceSubstitution( $piece, $frame ); |
198 | | - $text = $ret[ 'text' ]; |
199 | | - */ |
| 164 | + //XXX: trying another way. but $piece['parts'] needs to be a PPNode. how to do that? |
| 165 | + /* |
| 166 | + $frame = $this->parser->getPreprocessor()->newCustomFrame( $record ); |
200 | 167 | |
201 | | - // dumb and slow, but works |
202 | | - if ( $this->templateText ) { |
203 | | - if ( is_string( $this->templateText ) ) { |
204 | | - $text = $this->templateText; |
205 | | - } else { |
206 | | - $text = $this->templateText->getContent(); |
207 | | - } |
208 | | - } else { |
209 | | - $article = new Article( $this->template ); |
210 | | - if ( !$article->exists() ) { |
211 | | - return false; // TESTME |
212 | | - } |
| 168 | + $piece = array(); |
213 | 169 | |
214 | | - $text = $article->getContent(); |
215 | | - } |
| 170 | + if ( $this->template->getNamespace() == NS_TEMPLATE ) $n = ""; |
| 171 | + else $n = $this->template->getNsText() . ":"; |
216 | 172 | |
217 | | - $text = $this->parser->replaceVariables( $text, $record, true ); |
| 173 | + $piece ['title'] = $n . $this->template->getText(); |
| 174 | + $piece['parts'] = $record; |
| 175 | + $piece['lineStart'] = false; //XXX: ugly. can't know here whether the brace was at the start of a line |
218 | 176 | |
219 | | - return $text; |
220 | | - } |
| 177 | + $ret = $this->parser->braceSubstitution( $piece, $frame ); |
| 178 | + $text = $ret[ 'text' ]; |
| 179 | + */ |
221 | 180 | |
222 | | - function normalizeRecord( $record ) { |
223 | | - $rec = array(); |
| 181 | + //dumb and slow, but works |
| 182 | + if ( $this->templateText ) { |
| 183 | + if ( is_string( $this->templateText ) ) $text = $this->templateText; |
| 184 | + else $text = $this->templateText->getContent(); |
| 185 | + } else { |
| 186 | + $article = new Article( $this->template ); |
| 187 | + if ( !$article->exists() ) return false; //TESTME |
224 | 188 | |
225 | | - // keep record fields, add missing values |
226 | | - $fields = $this->source->getFieldNames(); |
227 | | - foreach ( $fields as $f ) { |
228 | | - if ( isset( $record[ $f ] ) ) { |
229 | | - $v = $record[ $f ]; |
230 | | - } else { |
231 | | - $v = ''; |
232 | | - } |
| 189 | + $text = $article->getContent(); |
| 190 | + } |
233 | 191 | |
234 | | - $rec[ $f ] = $this->sanitizeValue( $v ); // TESTME |
235 | | - } |
| 192 | + $text = $this->parser->replaceVariables( $text, $record, true ); |
236 | 193 | |
237 | | - // add source meta info, so we can render links back to the source, |
238 | | - // provide license info, etc |
239 | | - $info = $this->source->getSourceInfo(); // TESTME |
240 | | - foreach ( $info as $f => $v ) { |
241 | | - if ( is_array( $v ) || is_object( $v ) || is_resource( $v ) ) { |
242 | | - continue; |
243 | | - } |
| 194 | + return $text; |
| 195 | + } |
244 | 196 | |
245 | | - $rec[ "source.$f" ] = $this->sanitizeValue( $v ); |
246 | | - } |
| 197 | + function normalizeRecord( $record ) { |
| 198 | + $rec = array(); |
247 | 199 | |
248 | | - return $rec; |
249 | | - } |
| 200 | + //keep record fields, add missing values |
| 201 | + $fields = $this->source->getFieldNames(); |
| 202 | + foreach ( $fields as $f ) { |
| 203 | + if ( isset( $record[ $f ] ) ) $v = $record[ $f ]; |
| 204 | + else $v = ''; |
250 | 205 | |
251 | | - protected static $sanitizerSubstitution = array( |
252 | | - # '!&!' => '&', #breaks URLs. not really needed when parsed as wiki-text... |
253 | | - '!&(#?x?[\w\d]+);!' => '&$1;', |
254 | | - '!<!' => '<', |
255 | | - '!>!' => '>', |
256 | | - '!\[!' => '[', |
257 | | - '!\]!' => ']', |
258 | | - '!\{!' => '{', |
259 | | - '!\}!' => '}', |
260 | | - '!\'!' => ''', |
261 | | - '!\|!' => '|', |
262 | | - '!^\*!m' => '*', |
263 | | - '!^#!m' => '#', |
264 | | - '!^:!m' => ':', |
265 | | - '!^;!m' => ';', |
266 | | - '![\r\n]!' => ' ', |
267 | | - '!^ !m' => ' ', |
268 | | - ); |
| 206 | + $rec[ $f ] = $this->sanitizeValue( $v ); //TESTME |
| 207 | + } |
269 | 208 | |
270 | | - static function sanitizeValue( $v ) { |
271 | | - $find = array_keys( self::$sanitizerSubstitution ); |
272 | | - $subst = array_values( self::$sanitizerSubstitution ); |
| 209 | + //add source meta info, so we can render links back to the source, |
| 210 | + //provide license info, etc |
| 211 | + $info = $this->source->getSourceInfo(); //TESTME |
| 212 | + foreach ( $info as $f => $v ) { |
| 213 | + if ( is_array( $v ) || is_object( $v ) || is_resource( $v ) ) continue; |
| 214 | + $rec[ "source.$f" ] = $this->sanitizeValue( $v ); |
| 215 | + } |
273 | 216 | |
274 | | - $v = preg_replace( $find, $subst, $v ); |
| 217 | + return $rec; |
| 218 | + } |
275 | 219 | |
276 | | - return $v; |
277 | | - } |
| 220 | + protected static $sanitizerSubstitution = array( |
| 221 | + # '!&!' => '&', #breaks URLs. not really needed when parsed as wiki-text... |
| 222 | + '!&(#?x?[\w\d]+);!' => '&$1;', |
| 223 | + '!<!' => '<', |
| 224 | + '!>!' => '>', |
| 225 | + '!\[!' => '[', |
| 226 | + '!\]!' => ']', |
| 227 | + '!\{!' => '{', |
| 228 | + '!\}!' => '}', |
| 229 | + '!\'!' => ''', |
| 230 | + '!\|!' => '|', |
| 231 | + '!^\*!m' => '*', |
| 232 | + '!^#!m' => '#', |
| 233 | + '!^:!m' => ':', |
| 234 | + '!^;!m' => ';', |
| 235 | + '![\r\n]!' => ' ', |
| 236 | + '!^ !m' => ' ', |
| 237 | + ); |
278 | 238 | |
279 | | - static function getDataSource( $name ) { |
280 | | - global $wgDataTransclusionSources; |
281 | | - if ( empty( $wgDataTransclusionSources[ $name ] ) ) { |
282 | | - return false; |
283 | | - } |
| 239 | + static function sanitizeValue( $v ) { |
| 240 | + $find = array_keys( self::$sanitizerSubstitution ); |
| 241 | + $subst = array_values( self::$sanitizerSubstitution ); |
284 | 242 | |
285 | | - $source = $wgDataTransclusionSources[ $name ]; |
| 243 | + $v = preg_replace( $find, $subst, $v ); |
| 244 | + return $v; |
| 245 | + } |
286 | 246 | |
287 | | - if ( is_array( $source ) ) { // if the source is an array, use it to instantiate the sourece object |
288 | | - $spec = $source; |
289 | | - $spec[ 'name' ] = $name; |
| 247 | + static function getDataSource( $name ) { |
| 248 | + global $wgDataTransclusionSources; |
| 249 | + if ( empty( $wgDataTransclusionSources[ $name ] ) ) return false; |
290 | 250 | |
291 | | - if ( !isset( $spec[ 'class' ] ) ) { |
292 | | - throw new MWException( "\$wgDataTransclusionSources['$name'] must specifying a class name in the 'class' field." ); |
293 | | - } |
| 251 | + $source = $wgDataTransclusionSources[ $name ]; |
294 | 252 | |
295 | | - $c = $spec[ 'class' ]; |
296 | | - $obj = new $c( $spec ); // pass spec array as constructor argument |
297 | | - if ( !$obj ) { |
298 | | - throw new MWException( "failed to instantiate \$wgDataTransclusionSources['$name'] as new $c." ); |
299 | | - } |
| 253 | + if ( is_array( $source ) ) { //if the source is an array, use it to instantiate the sourece object |
| 254 | + $spec = $source; |
| 255 | + $spec[ 'name' ] = $name; |
300 | 256 | |
301 | | - $source = $obj; |
| 257 | + if ( !isset( $spec[ 'class' ] ) ) throw new MWException( "\$wgDataTransclusionSources['$name'] must specifying a class name in the 'class' field." ); |
302 | 258 | |
303 | | - if ( isset( $spec[ 'cache' ] ) ) { // check if a cache should be used |
304 | | - $c = $spec[ 'cache' ]; |
305 | | - if ( !is_object( $c ) ) { // cache may be specified as a string |
306 | | - $c = wfGetCache( $c ); // $c should be one of the CACHE_* constants |
307 | | - } |
| 259 | + $c = $spec[ 'class' ]; |
| 260 | + $obj = new $c( $spec ); //pass spec array as constructor argument |
| 261 | + if ( !$obj ) throw new MWException( "failed to instantiate \$wgDataTransclusionSources['$name'] as new $c." ); |
308 | 262 | |
309 | | - $source = new CachingDataTransclusionSource( $obj, $c, @$spec['cache-duration'] ); // apply caching wrapper |
310 | | - } |
| 263 | + $source = $obj; |
311 | 264 | |
312 | | - $wgDataTransclusionSources[ $name ] = $source; // replace spec array by actual object, for later re-use |
313 | | - } |
| 265 | + if ( isset( $spec[ 'cache' ] ) ) { //check if a cache should be used |
| 266 | + $c = $spec[ 'cache' ]; |
| 267 | + if ( !is_object( $c ) ) { //cache may be specified as a string |
| 268 | + $c = wfGetCache( $c ); // $c should be one of the CACHE_* constants |
| 269 | + } |
314 | 270 | |
315 | | - if ( !is_object( $source ) ) { |
316 | | - if ( !isset( $source[ 'class' ] ) ) { |
317 | | - throw new MWException( "\$wgDataTransclusionSources['$name'] must be an array or an object." ); |
318 | | - } |
| 271 | + $source = new CachingDataTransclusionSource( $obj, $c, @$spec['cache-duration'] ); //apply caching wrapper |
319 | 272 | } |
320 | 273 | |
321 | | - return $source; |
322 | | - } |
| 274 | + $wgDataTransclusionSources[ $name ] = $source; //replace spec array by actual object, for later re-use |
| 275 | + } |
| 276 | + |
| 277 | + if ( !is_object( $source ) ) { |
| 278 | + if ( !isset( $source[ 'class' ] ) ) throw new MWException( "\$wgDataTransclusionSources['$name'] must be an array or an object." ); |
| 279 | + } |
| 280 | + |
| 281 | + return $source; |
| 282 | + } |
| 283 | + |
323 | 284 | } |
Index: trunk/extensions/DataTransclusion/tests/DataTransclusionTest.php |
— | — | @@ -11,7 +11,7 @@ |
12 | 12 | else $IP = $dir; |
13 | 13 | } |
14 | 14 | |
15 | | -if ( isset( $GET_ ) ) { |
| 15 | +if( isset( $GET_ ) ) { |
16 | 16 | echo( "This file cannot be run from the web.\n" ); |
17 | 17 | die( 1 ); |
18 | 18 | } |
— | — | @@ -21,16 +21,19 @@ |
22 | 22 | // requires PHPUnit 3.4 |
23 | 23 | require_once 'PHPUnit/Framework.php'; |
24 | 24 | |
25 | | -error_reporting( E_ALL ); |
| 25 | +error_reporting(E_ALL); |
26 | 26 | |
27 | 27 | class DataTransclusionTest extends PHPUnit_Framework_TestCase { |
28 | | - function setUp() { |
| 28 | + |
| 29 | + function setUp() |
| 30 | + { |
29 | 31 | global $wgTitle; |
30 | 32 | |
31 | 33 | $wgTitle = Title::newFromText( "Test" ); |
32 | 34 | } |
33 | | - |
34 | | - function runTest() { |
| 35 | + |
| 36 | + function runTest() |
| 37 | + { |
35 | 38 | $this->testErrorMessage(); |
36 | 39 | $this->testSanitizeValue(); |
37 | 40 | $this->testNormalizeRecord(); |
— | — | @@ -43,23 +46,23 @@ |
44 | 47 | } |
45 | 48 | |
46 | 49 | function testErrorMessage() { |
47 | | - $m = DataTransclusionHandler::errorMessage( 'datatransclusion-test-wikitext', false ); |
48 | | - $this->assertEquals( $m, '<span class="error">some <span class="test">html</span> and \'\'markup\'\'.</span>' ); |
| 50 | + $m = DataTransclusionHandler::errorMessage('datatransclusion-test-wikitext', false); |
| 51 | + $this->assertEquals( $m, '<span class="error">some <span class="test">html</span> and \'\'markup\'\'.</span>' ); |
49 | 52 | |
50 | | - $m = DataTransclusionHandler::errorMessage( 'datatransclusion-test-evil-html', false ); |
51 | | - $this->assertEquals( $m, '<span class="error">some <object>evil</object> html.</span>' ); |
| 53 | + $m = DataTransclusionHandler::errorMessage('datatransclusion-test-evil-html', false); |
| 54 | + $this->assertEquals( $m, '<span class="error">some <object>evil</object> html.</span>' ); |
52 | 55 | |
53 | | - $m = DataTransclusionHandler::errorMessage( 'datatransclusion-test-nowiki', false ); |
54 | | - $this->assertEquals( $m, '<span class="error">some <nowiki>{{nowiki}}</nowiki> code.</span>' ); |
| 56 | + $m = DataTransclusionHandler::errorMessage('datatransclusion-test-nowiki', false); |
| 57 | + $this->assertEquals( $m, '<span class="error">some <nowiki>{{nowiki}}</nowiki> code.</span>' ); |
55 | 58 | |
56 | | - $m = DataTransclusionHandler::errorMessage( 'datatransclusion-test-wikitext', true ); |
57 | | - $this->assertEquals( $m, '<span class="error">some <span class="test">html</span> and <i>markup</i>.</span>' ); |
| 59 | + $m = DataTransclusionHandler::errorMessage('datatransclusion-test-wikitext', true); |
| 60 | + $this->assertEquals( $m, '<span class="error">some <span class="test">html</span> and <i>markup</i>.</span>' ); |
58 | 61 | |
59 | | - $m = DataTransclusionHandler::errorMessage( 'datatransclusion-test-evil-html', true ); |
60 | | - $this->assertEquals( $m, '<span class="error">some <object>evil</object> html.</span>' ); |
| 62 | + $m = DataTransclusionHandler::errorMessage('datatransclusion-test-evil-html', true); |
| 63 | + $this->assertEquals( $m, '<span class="error">some <object>evil</object> html.</span>' ); |
61 | 64 | |
62 | | - $m = DataTransclusionHandler::errorMessage( 'datatransclusion-test-nowiki', true ); |
63 | | - $this->assertEquals( $m, '<span class="error">some {{nowiki}} code.</span>' ); |
| 65 | + $m = DataTransclusionHandler::errorMessage('datatransclusion-test-nowiki', true); |
| 66 | + $this->assertEquals( $m, '<span class="error">some {{nowiki}} code.</span>' ); |
64 | 67 | } |
65 | 68 | |
66 | 69 | function testSanitizeValue() { |
— | — | @@ -84,17 +87,17 @@ |
85 | 88 | } |
86 | 89 | |
87 | 90 | function testNormalizeRecord() { |
88 | | - // TODO... |
| 91 | + //TODO... |
89 | 92 | } |
90 | 93 | |
91 | 94 | function testBuildAssociativeArguments() { |
92 | 95 | $args = array( "foo bar", "x=y", " ah = \"be\" ", "blubber bla" ); |
93 | 96 | $assoc = DataTransclusionhandler::buildAssociativeArguments( $args ); |
94 | 97 | |
95 | | - $this->asserttrue( !isset( $assoc[0] ) ); |
96 | | - $this->asserttrue( !isset( $assoc[3] ) ); |
97 | | - $this->asserttrue( !isset( $assoc['foo'] ) ); |
98 | | - $this->asserttrue( !isset( $assoc['foo bar'] ) ); |
| 98 | + $this->asserttrue( !isset($assoc[0]) ); |
| 99 | + $this->asserttrue( !isset($assoc[3]) ); |
| 100 | + $this->asserttrue( !isset($assoc['foo']) ); |
| 101 | + $this->asserttrue( !isset($assoc['foo bar']) ); |
99 | 102 | $this->assertEquals( $assoc[1], 'foo bar' ); |
100 | 103 | $this->assertEquals( $assoc[2], 'blubber bla' ); |
101 | 104 | $this->assertEquals( $assoc['x'], 'y' ); |
— | — | @@ -105,24 +108,24 @@ |
106 | 109 | global $wgDataTransclusionSources; |
107 | 110 | |
108 | 111 | $spec = array( 'name' => 'FOO', 'keyFields' => 'name,id', 'fieldNames' => 'id,name,info' ); |
109 | | - $data[] = array( "name" => "foo", "id" => 3, "info" => 'test 1' ); |
110 | | - $data[] = array( "name" => "bar", "id" => 5, "info" => 'test 2' ); |
| 112 | + $data[] = array( "name" => "foo", "id" => 3, "info" => 'test 1'); |
| 113 | + $data[] = array( "name" => "bar", "id" => 5, "info" => 'test 2'); |
111 | 114 | $wgDataTransclusionSources[ 'FOO' ] = new FakeDataTransclusionSource( $spec, $data ); |
112 | 115 | |
113 | 116 | $src = DataTransclusionHandler::getDataSource( 'FOO' ); |
114 | 117 | $this->assertTrue( $src instanceof FakeDataTransclusionSource ); |
115 | | - |
| 118 | + |
116 | 119 | $rec = $src->fetchRecord( 'id', 3 ); |
117 | 120 | $this->assertEquals( $rec['id'], 3 ); |
118 | 121 | $this->assertEquals( $rec['name'], 'foo' ); |
119 | 122 | $this->assertEquals( $rec['info'], 'test 1' ); |
120 | | - |
| 123 | + |
121 | 124 | $rec = $src->fetchRecord( 'name', 'bar' ); |
122 | 125 | $this->assertEquals( $rec['id'], 5 ); |
123 | 126 | $this->assertEquals( $rec['name'], 'bar' ); |
124 | 127 | $this->assertEquals( $rec['info'], 'test 2' ); |
125 | 128 | |
126 | | - // ///////////////////////////////////////////////////////////////////////////// |
| 129 | + /////////////////////////////////////////////////////////////////////////////// |
127 | 130 | $spec[ 'class' ] = 'FakeDataTransclusionSource'; |
128 | 131 | $spec[ 'data' ] = $data; |
129 | 132 | |
— | — | @@ -131,12 +134,12 @@ |
132 | 135 | $src = DataTransclusionHandler::getdataSource( 'BAR' ); |
133 | 136 | $this->assertTrue( $src instanceof FakeDataTransclusionSource ); |
134 | 137 | $this->assertEquals( $src->getName(), 'BAR' ); |
135 | | - |
| 138 | + |
136 | 139 | $rec = $src->fetchRecord( 'id', 3 ); |
137 | 140 | $this->assertEquals( $rec['id'], 3 ); |
138 | 141 | $this->assertEquals( $rec['name'], 'foo' ); |
139 | 142 | $this->assertEquals( $rec['info'], 'test 1' ); |
140 | | - |
| 143 | + |
141 | 144 | $rec = $src->fetchRecord( 'name', 'bar' ); |
142 | 145 | $this->assertEquals( $rec['id'], 5 ); |
143 | 146 | $this->assertEquals( $rec['name'], 'bar' ); |
— | — | @@ -157,7 +160,7 @@ |
158 | 161 | |
159 | 162 | $source = null; |
160 | 163 | $title = Title::newFromText( "Template:Thing" ); |
161 | | - $rec = array( "name" => "foo", "id" => 3, "info" => 'test X' ); |
| 164 | + $rec = array( "name" => "foo", "id" => 3, "info" => 'test X'); |
162 | 165 | $template = "{{{id}}}|{{{name}}}|{{{info}}}"; |
163 | 166 | |
164 | 167 | $handler = new DataTransclusionHandler( $wgParser, $source, $title, $template ); |
— | — | @@ -169,39 +172,39 @@ |
170 | 173 | function testCachedFetchRecord() { |
171 | 174 | global $wgDataTransclusionSources; |
172 | 175 | |
173 | | - $data[] = array( "name" => "foo", "id" => 3, "info" => 'test 1' ); |
174 | | - $data[] = array( "name" => "bar", "id" => 5, "info" => 'test 2' ); |
175 | | - $spec = array( |
176 | | - 'class' => 'FakeDataTransclusionSource', |
| 176 | + $data[] = array( "name" => "foo", "id" => 3, "info" => 'test 1'); |
| 177 | + $data[] = array( "name" => "bar", "id" => 5, "info" => 'test 2'); |
| 178 | + $spec = array( |
| 179 | + 'class' => 'FakeDataTransclusionSource', |
177 | 180 | 'data' => $data, |
178 | | - 'keyFields' => 'name,id', |
| 181 | + 'keyFields' => 'name,id', |
179 | 182 | 'fieldNames' => 'id,name,info', |
180 | | - 'cacheDuration' => 2, |
181 | | - 'cache' => new HashBagOStuff(), |
| 183 | + 'cacheDuration' => 2, |
| 184 | + 'cache' => new HashBagOStuff(), |
182 | 185 | ); |
183 | | - |
| 186 | + |
184 | 187 | $wgDataTransclusionSources[ 'FOO' ] = $spec; |
185 | 188 | |
186 | 189 | $src = DataTransclusionHandler::getDataSource( 'FOO' ); |
187 | 190 | $this->assertTrue( $src instanceof CachingDataTransclusionSource ); |
188 | 191 | |
189 | | - // get original version |
| 192 | + //get original version |
190 | 193 | $rec = $src->fetchRecord( 'id', 3 ); |
191 | 194 | $this->assertEquals( $rec['id'], 3 ); |
192 | 195 | $this->assertEquals( $rec['name'], 'foo' ); |
193 | 196 | $this->assertEquals( $rec['info'], 'test 1' ); |
194 | 197 | |
195 | | - // change record |
196 | | - $rec = array( "name" => "foo", "id" => 3, "info" => 'test X' ); |
| 198 | + //change record |
| 199 | + $rec = array( "name" => "foo", "id" => 3, "info" => 'test X'); |
197 | 200 | $src->source->putRecord( $rec ); |
198 | 201 | |
199 | | - // fetch record - should be the cached version |
| 202 | + //fetch record - should be the cached version |
200 | 203 | $rec = $src->fetchRecord( 'id', 3 ); |
201 | 204 | $this->assertEquals( $rec['info'], 'test 1' ); |
202 | 205 | |
203 | | - sleep( 3 ); |
| 206 | + sleep(3); |
204 | 207 | |
205 | | - // fetch record - cached version should have expired |
| 208 | + //fetch record - cached version should have expired |
206 | 209 | $rec = $src->fetchRecord( 'id', 3 ); |
207 | 210 | $this->assertEquals( $rec['info'], 'test X' ); |
208 | 211 | } |
— | — | @@ -224,3 +227,4 @@ |
225 | 228 | $t->runTest(); |
226 | 229 | |
227 | 230 | echo "OK.\n"; |
| 231 | +?> |
\ No newline at end of file |
Index: trunk/extensions/DataTransclusion/WebDataTransclusionSource.php |
— | — | @@ -9,170 +9,133 @@ |
10 | 10 | * @licence GNU General Public Licence 2.0 or later |
11 | 11 | */ |
12 | 12 | |
13 | | -if ( !defined( 'MEDIAWIKI' ) ) { |
| 13 | +if( !defined( 'MEDIAWIKI' ) ) { |
14 | 14 | echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" ); |
15 | 15 | die( 1 ); |
16 | 16 | } |
17 | 17 | |
18 | 18 | /** |
19 | | - * Implementations of DataTransclusionSource, fetching data records via HTTP, |
20 | | - * usually from an web API. |
21 | | - * |
22 | | - * In addition to the options supported by the DataTransclusionSource base class, |
23 | | - * WebDataTransclusionSource accepts some additional options |
24 | | - * |
25 | | - * * $spec['url']: base URL for building urls for retrieving individual records. |
26 | | - * The key/value pair is appended to the URL as a regular URL |
27 | | - * parameter (preceeded by ? or &, as appropriate). For more |
28 | | - * complex rules for building the url, override getRecordURL(). REQUIRED. |
29 | | - * * $spec['dataFormat']: Serialization format returned from the web service. |
30 | | - * Supported values are 'php' for PHP serialization format, 'json' |
31 | | - * for JavaScript syntax, and 'wddx' for XML-based list/dicts. |
32 | | - * To support more formats, override decodeData(). Default is 'php'. |
33 | | - * * $spec['dataPath']: "path" to the actual data in the structure returned from the |
34 | | - * HTTP request. The response data is assumed to consit of nested arrays. Each entry |
35 | | - * In the path navigates one step in this structure. Each entry can be either a |
36 | | - * string (for a lookup in an associative array), and int (an index in a list), or |
37 | | - * a "meta-key" of the form @@N, where N is an integer. A meta-key refers to the |
38 | | - * Nth entry in an associative array: @1 would be "bar" in array( 'x' => "foo", 'y' => "bar" ). |
39 | | - * For more complex retrieval of the record, override extractRecord(). REQUIRED. |
40 | | - * * $spec['errorPath']: "path" to error messages in the structure returned from the |
41 | | - * HTTP request. The path is evaluated as deswcribed for $spec['dataPath']. If an |
42 | | - * entry is found at the given position in the response structure, the request |
43 | | - * is assumed to have failed. For more complex detection of errors, override |
44 | | - * extractError(). REQUIRED. |
45 | | - * * $spec['httpOptions']: array of options to pass to Http::get. For details, see Http::request. |
46 | | - * * $spec['timeout']: seconds before the request times out. If not given, |
47 | | - * $spec['httpOptions']['timeout'] is used. If both are not givern, 5 seconds are assumed. |
48 | | - * |
49 | | - * For more information on options supported by DataTransclusionSource, see the class-level |
50 | | - * documentation there. |
51 | | - */ |
| 19 | +* Implementations of DataTransclusionSource, fetching data records via HTTP, |
| 20 | +* usually from an web API. |
| 21 | +* |
| 22 | +* In addition to the options supported by the DataTransclusionSource base class, |
| 23 | +* WebDataTransclusionSource accepts some additional options |
| 24 | +* |
| 25 | +* * $spec['url']: base URL for building urls for retrieving individual records. |
| 26 | +* The key/value pair is appended to the URL as a regular URL |
| 27 | +* parameter (preceeded by ? or &, as appropriate). For more |
| 28 | +* complex rules for building the url, override getRecordURL(). REQUIRED. |
| 29 | +* * $spec['dataFormat']: Serialization format returned from the web service. |
| 30 | +* Supported values are 'php' for PHP serialization format, 'json' |
| 31 | +* for JavaScript syntax, and 'wddx' for XML-based list/dicts. |
| 32 | +* To support more formats, override decodeData(). Default is 'php'. |
| 33 | +* * $spec['dataPath']: "path" to the actual data in the structure returned from the |
| 34 | +* HTTP request. The response data is assumed to consit of nested arrays. Each entry |
| 35 | +* In the path navigates one step in this structure. Each entry can be either a |
| 36 | +* string (for a lookup in an associative array), and int (an index in a list), or |
| 37 | +* a "meta-key" of the form @@N, where N is an integer. A meta-key refers to the |
| 38 | +* Nth entry in an associative array: @1 would be "bar" in array( 'x' => "foo", 'y' => "bar" ). |
| 39 | +* For more complex retrieval of the record, override extractRecord(). REQUIRED. |
| 40 | +* * $spec['errorPath']: "path" to error messages in the structure returned from the |
| 41 | +* HTTP request. The path is evaluated as deswcribed for $spec['dataPath']. If an |
| 42 | +* entry is found at the given position in the response structure, the request |
| 43 | +* is assumed to have failed. For more complex detection of errors, override |
| 44 | +* extractError(). REQUIRED. |
| 45 | +* * $spec['httpOptions']: array of options to pass to Http::get. For details, see Http::request. |
| 46 | +* * $spec['timeout']: seconds before the request times out. If not given, |
| 47 | +* $spec['httpOptions']['timeout'] is used. If both are not givern, 5 seconds are assumed. |
| 48 | +* |
| 49 | +* For more information on options supported by DataTransclusionSource, see the class-level |
| 50 | +* documentation there. |
| 51 | +*/ |
52 | 52 | class WebDataTransclusionSource extends DataTransclusionSource { |
53 | | - function __construct( $spec ) { |
54 | | - DataTransclusionSource::__construct( $spec ); |
55 | 53 | |
56 | | - $this->url = $spec[ 'url' ]; |
57 | | - $this->dataFormat = @$spec[ 'dataFormat' ]; |
58 | | - $this->dataPath = DataTransclusionSource::splitList( @$spec[ 'dataPath' ] ); |
59 | | - $this->errorPath = DataTransclusionSource::splitList( @$spec[ 'errorPath' ] ); |
60 | | - $this->httpOptions = @$spec[ 'httpOptions' ]; |
61 | | - $this->timeout = @$spec[ 'timeout' ]; |
| 54 | + function __construct( $spec ) { |
| 55 | + DataTransclusionSource::__construct( $spec ); |
62 | 56 | |
63 | | - if ( !$this->dataFormat ) { |
64 | | - $this->dataFormat = 'php'; |
65 | | - } |
| 57 | + $this->url = $spec[ 'url' ]; |
| 58 | + $this->dataFormat = @$spec[ 'dataFormat' ]; |
| 59 | + $this->dataPath = DataTransclusionSource::splitList( @$spec[ 'dataPath' ] ); |
| 60 | + $this->errorPath = DataTransclusionSource::splitList( @$spec[ 'errorPath' ] ); |
| 61 | + $this->httpOptions = @$spec[ 'httpOptions' ]; |
| 62 | + $this->timeout = @$spec[ 'timeout' ]; |
66 | 63 | |
67 | | - if ( !$this->timeout ) { |
68 | | - $this->timeout = &$this->httpOptions[ 'timeout' ]; |
69 | | - } |
| 64 | + if ( !$this->dataFormat ) $this->dataFormat = 'php'; |
| 65 | + if ( !$this->timeout ) $this->timeout = &$this->httpOptions[ 'timeout' ]; |
| 66 | + if ( !$this->timeout ) $this->timeout = 5; |
| 67 | + } |
70 | 68 | |
71 | | - if ( !$this->timeout ) { |
72 | | - $this->timeout = 5; |
73 | | - } |
74 | | - } |
| 69 | + public function fetchRecord( $field, $value ) { |
| 70 | + $raw = $this->loadRecordData( $field, $value ); //TESTME |
| 71 | + if ( !$raw ) return false; //TODO: log error? |
75 | 72 | |
76 | | - public function fetchRecord( $field, $value ) { |
77 | | - $raw = $this->loadRecordData( $field, $value ); // TESTME |
78 | | - if ( !$raw ) { |
79 | | - return false; // TODO: log error? |
80 | | - } |
| 73 | + $data = $this->decodeData( $raw, $this->dataFormat ); //TESTME |
| 74 | + if ( !$data ) return false; //TODO: log error? |
81 | 75 | |
82 | | - $data = $this->decodeData( $raw, $this->dataFormat ); // TESTME |
83 | | - if ( !$data ) { |
84 | | - return false; // TODO: log error? |
85 | | - } |
| 76 | + $err = $this->extractError( $data ); //TESTME |
| 77 | + if ( $err ) return false; //TODO: log error? |
| 78 | + |
| 79 | + $rec = $this->extractRecord( $data ); //TESTME |
| 80 | + if ( !$rec ) return false; //TODO: log error? |
86 | 81 | |
87 | | - $err = $this->extractError( $data ); // TESTME |
88 | | - if ( $err ) { |
89 | | - return false; // TODO: log error? |
90 | | - } |
| 82 | + return $rec; |
| 83 | + } |
91 | 84 | |
92 | | - $rec = $this->extractRecord( $data ); // TESTME |
93 | | - if ( !$rec ) { |
94 | | - return false; // TODO: log error? |
95 | | - } |
| 85 | + protected function getRecordURL( $field, $value ) { |
| 86 | + $u = $this->url; |
96 | 87 | |
97 | | - return $rec; |
98 | | - } |
| 88 | + if ( strpos( $u, '?' ) === false ) $u .= '?'; |
| 89 | + else $u .= '&'; |
99 | 90 | |
100 | | - protected function getRecordURL( $field, $value ) { |
101 | | - $u = $this->url; |
| 91 | + $u .= $field; |
| 92 | + $u .= '='; |
| 93 | + $u .= urlencode( $value ); |
102 | 94 | |
103 | | - if ( strpos( $u, '?' ) === false ) { |
104 | | - $u .= '?'; |
105 | | - } else { |
106 | | - $u .= '&'; |
107 | | - } |
| 95 | + return $u; |
| 96 | + } |
108 | 97 | |
109 | | - $u .= $field; |
110 | | - $u .= '='; |
111 | | - $u .= urlencode( $value ); |
| 98 | + protected function loadRecordData( $field, $value ) { |
| 99 | + $u = $this->getRecordURL( $field, $value ); |
112 | 100 | |
113 | | - return $u; |
114 | | - } |
| 101 | + $raw = Http::get( $u, $this->timeout, $this->httpOptions ); |
| 102 | + return $raw; |
| 103 | + } |
115 | 104 | |
116 | | - protected function loadRecordData( $field, $value ) { |
117 | | - $u = $this->getRecordURL( $field, $value ); |
| 105 | + protected function decodeData( $raw, $format = 'php' ) { |
| 106 | + if ( $format == 'json' ) return FormatJson::decode( $raw, true ); //TESTME |
| 107 | + if ( $format == 'wddx' ) return wddx_unserialize( $raw ); //TESTME |
| 108 | + if ( $format == 'php' ) return unserialize( $raw ); //TESTME |
118 | 109 | |
119 | | - $raw = Http::get( $u, $this->timeout, $this->httpOptions ); |
| 110 | + return false; |
| 111 | + } |
120 | 112 | |
121 | | - return $raw; |
122 | | - } |
| 113 | + protected function extractError( $data ) { |
| 114 | + return $this->extractField( $data, $this->errorPath ); |
| 115 | + } |
123 | 116 | |
124 | | - protected function decodeData( $raw, $format = 'php' ) { |
125 | | - if ( $format == 'json' ) { |
126 | | - return FormatJson::decode( $raw, true ); // TESTME |
127 | | - } |
| 117 | + protected function extractRecord( $data ) { |
| 118 | + return $this->extractField( $data, $this->dataPath ); |
| 119 | + } |
128 | 120 | |
129 | | - if ( $format == 'wddx' ) { |
130 | | - return wddx_unserialize( $raw ); // TESTME |
131 | | - } |
| 121 | + protected function extractField( $data, $path ) { |
| 122 | + if ( $path == null ) return $data; |
| 123 | + if ( is_string( $path ) ) return @$data[ $path ]; |
132 | 124 | |
133 | | - if ( $format == 'php' ) { |
134 | | - return unserialize( $raw ); // TESTME |
135 | | - } |
| 125 | + foreach ( $path as $p ) { |
| 126 | + if ( is_object( $data ) ) $data = wfObjectToArray( $data ); |
136 | 127 | |
137 | | - return false; |
138 | | - } |
| 128 | + // meta-key: index in the list of array-keys. |
| 129 | + // e.g. use @0 to grab the first value from an assoc array. |
| 130 | + if ( is_string( $p ) && preg_match( '/^@(\d+)$/', $p, $m ) ) { |
| 131 | + $i = (int)$m[1]; |
| 132 | + $k = array_keys( $data ); |
| 133 | + $p = $k[ $i ]; |
| 134 | + } |
139 | 135 | |
140 | | - protected function extractError( $data ) { |
141 | | - return $this->extractField( $data, $this->errorPath ); |
142 | | - } |
| 136 | + if ( !isset( $data[ $p ] ) ) return false; |
| 137 | + $data = $data[ $p ]; |
| 138 | + } |
143 | 139 | |
144 | | - protected function extractRecord( $data ) { |
145 | | - return $this->extractField( $data, $this->dataPath ); |
146 | | - } |
147 | | - |
148 | | - protected function extractField( $data, $path ) { |
149 | | - if ( $path == null ) { |
150 | | - return $data; |
151 | | - } |
152 | | - |
153 | | - if ( is_string( $path ) ) { |
154 | | - return @$data[ $path ]; |
155 | | - } |
156 | | - |
157 | | - foreach ( $path as $p ) { |
158 | | - if ( is_object( $data ) ) { |
159 | | - $data = wfObjectToArray( $data ); |
160 | | - } |
161 | | - |
162 | | - // meta-key: index in the list of array-keys. |
163 | | - // e.g. use @0 to grab the first value from an assoc array. |
164 | | - if ( is_string( $p ) && preg_match( '/^@(\d+)$/', $p, $m ) ) { |
165 | | - $i = (int)$m[1]; |
166 | | - $k = array_keys( $data ); |
167 | | - $p = $k[ $i ]; |
168 | | - } |
169 | | - |
170 | | - if ( !isset( $data[ $p ] ) ) { |
171 | | - return false; |
172 | | - } |
173 | | - |
174 | | - $data = $data[ $p ]; |
175 | | - } |
176 | | - |
177 | | - return $data; |
178 | | - } |
| 140 | + return $data; |
| 141 | + } |
179 | 142 | } |
Index: trunk/extensions/DataTransclusion/DataTransclusion.php |
— | — | @@ -9,7 +9,7 @@ |
10 | 10 | * @licence GNU General Public Licence 2.0 or later |
11 | 11 | */ |
12 | 12 | |
13 | | -if ( !defined( 'MEDIAWIKI' ) ) { |
| 13 | +if( !defined( 'MEDIAWIKI' ) ) { |
14 | 14 | echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" ); |
15 | 15 | die( 1 ); |
16 | 16 | } |
— | — | @@ -22,26 +22,26 @@ |
23 | 23 | 'descriptionmsg' => 'datatransclusion-desc', |
24 | 24 | ); |
25 | 25 | |
26 | | -$dir = dirname( __FILE__ ) . '/'; |
| 26 | +$dir = dirname(__FILE__) . '/'; |
27 | 27 | $wgExtensionMessagesFiles['DataTransclusion'] = $dir . 'DataTransclusion.i18n.php'; |
28 | 28 | $wgExtensionMessagesFiles['DataTransclusionMagic'] = $dir . 'DataTransclusion.i18n.magic.php'; |
29 | 29 | |
30 | | -$wgAutoloadClasses['DataTransclusionRenderer'] = $dir . 'DataTransclusionRenderer.php'; |
31 | | -$wgAutoloadClasses['DataTransclusionHandler'] = $dir . 'DataTransclusionHandler.php'; |
32 | | -$wgAutoloadClasses['DataTransclusionSource'] = $dir . 'DataTransclusionSource.php'; |
33 | | -$wgAutoloadClasses['CachingDataTransclusionSource'] = $dir . 'DataTransclusionSource.php'; |
34 | | -$wgAutoloadClasses['FakeDataTransclusionSource'] = $dir . 'DataTransclusionSource.php'; |
35 | | -$wgAutoloadClasses['DBDataTransclusionSource'] = $dir . 'DBDataTransclusionSource.php'; |
36 | | -$wgAutoloadClasses['WebDataTransclusionSource'] = $dir . 'WebDataTransclusionSource.php'; |
| 30 | +$wgAutoloadClasses['DataTransclusionRenderer'] = $dir. 'DataTransclusionRenderer.php'; |
| 31 | +$wgAutoloadClasses['DataTransclusionHandler'] = $dir. 'DataTransclusionHandler.php'; |
| 32 | +$wgAutoloadClasses['DataTransclusionSource'] = $dir. 'DataTransclusionSource.php'; |
| 33 | +$wgAutoloadClasses['CachingDataTransclusionSource'] = $dir. 'DataTransclusionSource.php'; |
| 34 | +$wgAutoloadClasses['FakeDataTransclusionSource'] = $dir. 'DataTransclusionSource.php'; |
| 35 | +$wgAutoloadClasses['DBDataTransclusionSource'] = $dir. 'DBDataTransclusionSource.php'; |
| 36 | +$wgAutoloadClasses['WebDataTransclusionSource'] = $dir. 'WebDataTransclusionSource.php'; |
37 | 37 | |
38 | 38 | $wgHooks['ParserFirstCallInit'][] = 'efDataTransclusionSetHooks'; |
39 | 39 | |
40 | | -// TODO: Special Page for displaying all configured data sources |
| 40 | +//TODO: Special Page for displaying all configured data sources |
41 | 41 | |
42 | 42 | $wgDataTransclusionSources = array(); |
43 | 43 | |
44 | 44 | function efDataTransclusionSetHooks( $parser ) { |
45 | 45 | $parser->setHook( 'record' , 'DataTransclusionHandler::handleRecordTag' ); |
46 | | - $parser->setFunctionHook( 'record' , 'DataTransclusionHandler::handleRecordFunction' ); |
| 46 | + $parser->setFunctionHook( 'record' , 'DataTransclusionHandler::handleRecordFunction' ); |
47 | 47 | return true; |
48 | 48 | } |
Index: trunk/extensions/DataTransclusion/DataTransclusionSource.php |
— | — | @@ -9,232 +9,215 @@ |
10 | 10 | * @licence GNU General Public Licence 2.0 or later |
11 | 11 | */ |
12 | 12 | |
13 | | -if ( !defined( 'MEDIAWIKI' ) ) { |
| 13 | +if( !defined( 'MEDIAWIKI' ) ) { |
14 | 14 | echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" ); |
15 | 15 | die( 1 ); |
16 | 16 | } |
17 | 17 | |
18 | 18 | /** |
19 | | - * Baseclass representing a source of data transclusion. All logic for addressing, fetching, decoding and filtering |
20 | | - * data is encapsulated by a subclass of DataTransclusionSource. Instances of DataTransclusionSource are instantiated |
21 | | - * by DataTransclusionHandler, and initialized by passing an associative array of options to the constructor. This array |
22 | | - * is taken from the $wgDataTransclusionSources configuration variable. |
23 | | - * |
24 | | - * Below is a list of options for the $spec array, as handled by the DataTransclusionSource |
25 | | - * base class (sublcasses may handle additional options): |
26 | | - * |
27 | | - * * $spec['name']: the source's name, used to specify it in wiki text. |
28 | | - * Set automatically by DataTransclusionHandler. REQUIRED. |
29 | | - * * $spec['keyFields']: list of fields that can be used as the key |
30 | | - * for fetching a record. REQUIRED. |
31 | | - * * $spec['fieldNames']: names of all fields present in each record. |
32 | | - * Fields not listed here will not be available on the wiki, |
33 | | - * even if they are returned by the data source. REQUIRED. |
34 | | - * * $spec['defaultKey']: default key to select records. If not specified, |
35 | | - * the first entry in $spec['keyFields'] is used. |
36 | | - * * $spec['cacheDuration']: the number of seconds a result from this source |
37 | | - * may be cached for. If not set, results are assumed to be cacheable |
38 | | - * indefinitely. This setting determines the expiry time of the parser |
39 | | - * cache entry for pages that show data from this source. If $spec['cache'], |
40 | | - * i.e. if this DataTransclusionSource is wrapped by an instance of |
41 | | - * CachingDataTransclusionSource, $spec['cacheDuration'] also determines |
42 | | - * the expiry time of ObjectCache entries for records from this source. |
43 | | - * * $spec['sourceInfo']: associative array of information about the data source |
44 | | - * that should be made available on the wiki. This information will be |
45 | | - * present in the record arrays, with they keys prefixed by "source.". |
46 | | - * This is intended to allow information about source, license, etc to be |
47 | | - * shown on the wiki. Note that DataTransclusionSource implementations may |
48 | | - * provide extra information in the source info on their own: This base |
49 | | - * class forces $spec['sourceInfo']['name'] = $spec['name'] and |
50 | | - * $spec['sourceInfo']['defaultKey'] = $spec['defaultKey']. |
51 | | - * |
52 | | - * Options used by DataTransclusionHandler but ignored by DataTransclusionSource: |
53 | | - * * $spec['class']: see documentation if $wgDataTransclusionSources in DataTransclusion. |
54 | | - * * $spec['cache']: see documentation if $wgDataTransclusionSources in DataTransclusion. |
55 | | - * |
56 | | - * Lists may be given as arrays or strings with items separated by [,;|]. |
57 | | - */ |
| 19 | +* Baseclass representing a source of data transclusion. All logic for addressing, fetching, decoding and filtering |
| 20 | +* data is encapsulated by a subclass of DataTransclusionSource. Instances of DataTransclusionSource are instantiated |
| 21 | +* by DataTransclusionHandler, and initialized by passing an associative array of options to the constructor. This array |
| 22 | +* is taken from the $wgDataTransclusionSources configuration variable. |
| 23 | +* |
| 24 | +* Below is a list of options for the $spec array, as handled by the DataTransclusionSource |
| 25 | +* base class (sublcasses may handle additional options): |
| 26 | +* |
| 27 | +* * $spec['name']: the source's name, used to specify it in wiki text. |
| 28 | +* Set automatically by DataTransclusionHandler. REQUIRED. |
| 29 | +* * $spec['keyFields']: list of fields that can be used as the key |
| 30 | +* for fetching a record. REQUIRED. |
| 31 | +* * $spec['fieldNames']: names of all fields present in each record. |
| 32 | +* Fields not listed here will not be available on the wiki, |
| 33 | +* even if they are returned by the data source. REQUIRED. |
| 34 | +* * $spec['defaultKey']: default key to select records. If not specified, |
| 35 | +* the first entry in $spec['keyFields'] is used. |
| 36 | +* * $spec['cacheDuration']: the number of seconds a result from this source |
| 37 | +* may be cached for. If not set, results are assumed to be cacheable |
| 38 | +* indefinitely. This setting determines the expiry time of the parser |
| 39 | +* cache entry for pages that show data from this source. If $spec['cache'], |
| 40 | +* i.e. if this DataTransclusionSource is wrapped by an instance of |
| 41 | +* CachingDataTransclusionSource, $spec['cacheDuration'] also determines |
| 42 | +* the expiry time of ObjectCache entries for records from this source. |
| 43 | +* * $spec['sourceInfo']: associative array of information about the data source |
| 44 | +* that should be made available on the wiki. This information will be |
| 45 | +* present in the record arrays, with they keys prefixed by "source.". |
| 46 | +* This is intended to allow information about source, license, etc to be |
| 47 | +* shown on the wiki. Note that DataTransclusionSource implementations may |
| 48 | +* provide extra information in the source info on their own: This base |
| 49 | +* class forces $spec['sourceInfo']['name'] = $spec['name'] and |
| 50 | +* $spec['sourceInfo']['defaultKey'] = $spec['defaultKey']. |
| 51 | +* |
| 52 | +* Options used by DataTransclusionHandler but ignored by DataTransclusionSource: |
| 53 | +* * $spec['class']: see documentation if $wgDataTransclusionSources in DataTransclusion. |
| 54 | +* * $spec['cache']: see documentation if $wgDataTransclusionSources in DataTransclusion. |
| 55 | +* |
| 56 | +* Lists may be given as arrays or strings with items separated by [,;|]. |
| 57 | +*/ |
58 | 58 | class DataTransclusionSource { |
59 | | - static function splitList( $s ) { |
60 | | - if ( $s === null || $s === false ) { |
61 | | - return $s; |
62 | | - } |
| 59 | + static function splitList( $s ) { |
| 60 | + if ( $s === null || $s === false ) return $s; |
| 61 | + if ( !is_string( $s ) ) return $s; |
| 62 | + |
| 63 | + $list = preg_split( '!\s*[,;|]\s*!', $s ); |
| 64 | + return $list; |
| 65 | + } |
63 | 66 | |
64 | | - if ( !is_string( $s ) ) { |
65 | | - return $s; |
66 | | - } |
| 67 | + /** |
| 68 | + * Initializes the DataTransclusionSource from the given parameter array. |
| 69 | + * @param $spec associative array of options. See class-level documentation for details. |
| 70 | + */ |
| 71 | + function __construct( $spec ) { |
| 72 | + $this->name = $spec[ 'name' ]; |
67 | 73 | |
68 | | - $list = preg_split( '!\s*[,;|]\s*!', $s ); |
| 74 | + $this->keyFields = self::splitList( $spec[ 'keyFields' ] ); |
| 75 | + $this->fieldNames = self::splitList( $spec[ 'fieldNames' ] ); |
69 | 76 | |
70 | | - return $list; |
71 | | - } |
| 77 | + if ( !empty( $spec[ 'defaultKey' ] ) ) $this->defaultKey = $spec[ 'defaultKey' ]; |
| 78 | + else $this->defaultKey = $this->keyFields[ 0 ]; |
72 | 79 | |
73 | | - /** |
74 | | - * Initializes the DataTransclusionSource from the given parameter array. |
75 | | - * @param $spec associative array of options. See class-level documentation for details. |
76 | | - */ |
77 | | - function __construct( $spec ) { |
78 | | - $this->name = $spec[ 'name' ]; |
| 80 | + if ( !empty( $spec[ 'cacheDuration' ] ) ) $this->cacheDuration = (int)$spec[ 'cacheDuration' ]; |
| 81 | + else $this->cacheDuration = null; |
79 | 82 | |
80 | | - $this->keyFields = self::splitList( $spec[ 'keyFields' ] ); |
81 | | - $this->fieldNames = self::splitList( $spec[ 'fieldNames' ] ); |
| 83 | + $this->sourceInfo = array(); |
82 | 84 | |
83 | | - if ( !empty( $spec[ 'defaultKey' ] ) ) { |
84 | | - $this->defaultKey = $spec[ 'defaultKey' ]; |
85 | | - } else { |
86 | | - $this->defaultKey = $this->keyFields[ 0 ]; |
87 | | - } |
88 | | - |
89 | | - if ( !empty( $spec[ 'cacheDuration' ] ) ) { |
90 | | - $this->cacheDuration = (int)$spec[ 'cacheDuration' ]; |
91 | | - } else { |
92 | | - $this->cacheDuration = null; |
93 | | - } |
94 | | - |
95 | | - $this->sourceInfo = array(); |
96 | | - |
97 | | - if ( !empty( $spec[ 'sourceInfo' ] ) ) { |
98 | | - foreach ( $spec[ 'sourceInfo' ] as $k => $v ) { |
99 | | - $this->sourceInfo[ $k ] = $v; |
100 | | - } |
101 | | - } |
102 | | - |
103 | | - $this->sourceInfo[ 'name' ] = $this->name; // force this one |
104 | | - $this->sourceInfo[ 'defaultKey' ] = $this->name; // force this one |
| 85 | + if ( !empty( $spec[ 'sourceInfo' ] ) ) { |
| 86 | + foreach ( $spec[ 'sourceInfo' ] as $k => $v ) { |
| 87 | + $this->sourceInfo[ $k ] = $v; |
105 | 88 | } |
| 89 | + } |
106 | 90 | |
107 | | - public function getName() { |
108 | | - return $this->name; |
109 | | - } |
| 91 | + $this->sourceInfo[ 'name' ] = $this->name; //force this one |
| 92 | + $this->sourceInfo[ 'defaultKey' ] = $this->name; //force this one |
| 93 | + } |
110 | 94 | |
111 | | - public function getDefaultKey() { |
112 | | - return $this->defaultKey; |
113 | | - } |
| 95 | + public function getName() { |
| 96 | + return $this->name; |
| 97 | + } |
114 | 98 | |
115 | | - public function getSourceInfo() { |
116 | | - return $this->sourceInfo; |
117 | | - } |
| 99 | + public function getDefaultKey() { |
| 100 | + return $this->defaultKey; |
| 101 | + } |
118 | 102 | |
119 | | - public function getKeyFields() { |
120 | | - return $this->keyFields; |
121 | | - } |
| 103 | + public function getSourceInfo() { |
| 104 | + return $this->sourceInfo; |
| 105 | + } |
122 | 106 | |
123 | | - public function getFieldNames() { |
124 | | - return $this->fieldNames; |
125 | | - } |
| 107 | + public function getKeyFields() { |
| 108 | + return $this->keyFields; |
| 109 | + } |
126 | 110 | |
127 | | - public function getCacheDuration() { |
128 | | - return $this->cacheDuration; |
129 | | - } |
| 111 | + public function getFieldNames() { |
| 112 | + return $this->fieldNames; |
| 113 | + } |
130 | 114 | |
131 | | - public function fetchRecord( $key, $value ) { |
132 | | - throw new MWException( "override fetchRecord()" ); |
133 | | - } |
| 115 | + public function getCacheDuration() { |
| 116 | + return $this->cacheDuration; |
| 117 | + } |
| 118 | + |
| 119 | + public function fetchRecord( $key, $value ) { |
| 120 | + throw new MWException( "override fetchRecord()" ); |
| 121 | + } |
134 | 122 | } |
135 | 123 | |
136 | 124 | /** |
137 | | - * Implementation of DataTransclusionSource that wraps another DataTransclusionSource and applies caching in an |
138 | | - * ObjectCache. All methods delegate to the underlieing data source, fetchRecord adds logic for caching. |
139 | | - */ |
| 125 | +* Implementation of DataTransclusionSource that wraps another DataTransclusionSource and applies caching in an |
| 126 | +* ObjectCache. All methods delegate to the underlieing data source, fetchRecord adds logic for caching. |
| 127 | +*/ |
140 | 128 | class CachingDataTransclusionSource extends DataTransclusionSource { |
141 | | - /** |
142 | | - * Initializes the CachingDataTransclusionSource |
143 | | - * |
144 | | - * @param $source a DataTransclusionSource instance for fetching data records. |
145 | | - * @param $cache an ObjectCache instance |
146 | | - * @param $duration number of seconds for which records may be cached |
147 | | - */ |
148 | | - function __construct( $source, $cache, $duration ) { |
149 | | - $this->source = $source; |
150 | | - $this->cache = $cache; |
151 | | - $this->duration = $duration; |
152 | | - } |
153 | 129 | |
154 | | - public function getName() { |
155 | | - return $this->source->getName(); |
156 | | - } |
| 130 | + /** |
| 131 | + * Initializes the CachingDataTransclusionSource |
| 132 | + * |
| 133 | + * @param $source a DataTransclusionSource instance for fetching data records. |
| 134 | + * @param $cache an ObjectCache instance |
| 135 | + * @param $duration number of seconds for which records may be cached |
| 136 | + */ |
| 137 | + function __construct( $source, $cache, $duration ) { |
| 138 | + $this->source = $source; |
| 139 | + $this->cache = $cache; |
| 140 | + $this->duration = $duration; |
| 141 | + } |
157 | 142 | |
158 | | - public function getDefaultTemplate() { |
159 | | - return $this->source->getDefaultTemplate(); |
160 | | - } |
| 143 | + public function getName() { |
| 144 | + return $this->source->getName(); |
| 145 | + } |
161 | 146 | |
162 | | - public function getSourceInfo() { |
163 | | - return $this->source->getSourceInfo(); |
164 | | - } |
| 147 | + public function getDefaultTemplate() { |
| 148 | + return $this->source->getDefaultTemplate(); |
| 149 | + } |
165 | 150 | |
166 | | - public function getKeyFields() { |
167 | | - return $this->source->getKeyFields(); |
168 | | - } |
| 151 | + public function getSourceInfo() { |
| 152 | + return $this->source->getSourceInfo(); |
| 153 | + } |
169 | 154 | |
170 | | - public function getFieldNames() { |
171 | | - return $this->source->getFieldNames(); |
172 | | - } |
| 155 | + public function getKeyFields() { |
| 156 | + return $this->source->getKeyFields(); |
| 157 | + } |
173 | 158 | |
174 | | - public function getCacheDuration() { |
175 | | - return $this->source->getCacheDuration(); |
176 | | - } |
| 159 | + public function getFieldNames() { |
| 160 | + return $this->source->getFieldNames(); |
| 161 | + } |
177 | 162 | |
178 | | - public function fetchRecord( $key, $value ) { |
179 | | - global $wgDBname, $wgUser; |
| 163 | + public function getCacheDuration() { |
| 164 | + return $this->source->getCacheDuration(); |
| 165 | + } |
180 | 166 | |
181 | | - $cacheKey = "$wgDBname:DataTransclusion(" . $this->getName() . ":$key=$value)"; |
| 167 | + public function fetchRecord( $key, $value ) { |
| 168 | + global $wgDBname, $wgUser; |
182 | 169 | |
183 | | - $rec = $this->cache->get( $cacheKey ); |
| 170 | + $cacheKey = "$wgDBname:DataTransclusion(" . $this->getName() . ":$key=$value)"; |
| 171 | + |
| 172 | + $rec = $this->cache->get( $cacheKey ); |
184 | 173 | |
185 | | - if ( !$rec ) { |
186 | | - $rec = $this->source->fetchRecord( $key, $value ); |
187 | | - if ( $rec ) { |
188 | | - $this->cache->set( $cacheKey, $rec, $this->getCacheDuration() ) ; // XXX: also cache negatives?? |
189 | | - } |
190 | | - } |
| 174 | + if ( !$rec ) { |
| 175 | + $rec = $this->source->fetchRecord( $key, $value ); |
| 176 | + if ( $rec ) $this->cache->set( $cacheKey, $rec, $this->getCacheDuration() ) ; //XXX: also cache negatives?? |
| 177 | + } |
191 | 178 | |
192 | | - return $rec; |
193 | | - } |
| 179 | + return $rec; |
| 180 | + } |
194 | 181 | } |
195 | 182 | |
196 | 183 | /** |
197 | | - * Implementations of DataTransclusionSource which simply fetches data from an array. This is |
198 | | - * intended mainly for testing and debugging. |
199 | | - */ |
| 184 | +* Implementations of DataTransclusionSource which simply fetches data from an array. This is |
| 185 | +* intended mainly for testing and debugging. |
| 186 | +*/ |
200 | 187 | class FakeDataTransclusionSource extends DataTransclusionSource { |
201 | | - /** |
202 | | - * Initializes the CachingDataTransclusionSource |
203 | | - * |
204 | | - * @param $spec an associative array of options. See class-level |
205 | | - * documentation of DataTransclusionSource for details. |
206 | | - * |
207 | | - * @param $data an array containing a list of records. Records from |
208 | | - * this list can be accessed via fetchRecord() using the key fields specified |
209 | | - * by $spec['keyFields']. If $data is not given, $spec['data'] must contain the data array. |
210 | | - */ |
211 | | - function __construct( $spec, $data = null ) { |
212 | | - DataTransclusionSource::__construct( $spec ); |
213 | 188 | |
214 | | - if ( $data === null ) { |
215 | | - $data = $spec[ 'data' ]; |
216 | | - } |
| 189 | + /** |
| 190 | + * Initializes the CachingDataTransclusionSource |
| 191 | + * |
| 192 | + * @param $spec an associative array of options. See class-level |
| 193 | + * documentation of DataTransclusionSource for details. |
| 194 | + * |
| 195 | + * @param $data an array containing a list of records. Records from |
| 196 | + * this list can be accessed via fetchRecord() using the key fields specified |
| 197 | + * by $spec['keyFields']. If $data is not given, $spec['data'] must contain the data array. |
| 198 | + */ |
| 199 | + function __construct( $spec, $data = null ) { |
| 200 | + DataTransclusionSource::__construct( $spec ); |
217 | 201 | |
218 | | - $this->lookup = array(); |
| 202 | + if ( $data === null ) $data = $spec[ 'data' ]; |
219 | 203 | |
220 | | - foreach ( $data as $rec ) { |
221 | | - $this->putRecord( $rec ); |
222 | | - } |
223 | | - } |
| 204 | + $this->lookup = array(); |
224 | 205 | |
225 | | - public function putRecord( $record ) { |
226 | | - $fields = $this->getKeyFields(); |
227 | | - foreach ( $fields as $f ) { |
228 | | - $k = $record[ $f ]; |
229 | | - |
230 | | - if ( !isset( $this->lookup[ $f ] ) ) { |
231 | | - $this->lookup[ $f ] = array(); |
232 | | - } |
233 | | - |
234 | | - $this->lookup[ $f ][ $k ] = $record; |
235 | | - } |
| 206 | + foreach ( $data as $rec ) { |
| 207 | + $this->putRecord( $rec ); |
236 | 208 | } |
| 209 | + } |
237 | 210 | |
238 | | - public function fetchRecord( $key, $value ) { |
239 | | - return @$this->lookup[ $key ][ $value ]; |
| 211 | + public function putRecord( $record ) { |
| 212 | + $fields = $this->getKeyFields(); |
| 213 | + foreach ( $fields as $f ) { |
| 214 | + $k = $record[ $f ]; |
| 215 | + |
| 216 | + if ( !isset( $this->lookup[ $f ] ) ) $this->lookup[ $f ] = array(); |
| 217 | + $this->lookup[ $f ][ $k ] = $record; |
240 | 218 | } |
| 219 | + } |
| 220 | + |
| 221 | + public function fetchRecord( $key, $value ) { |
| 222 | + return @$this->lookup[ $key ][ $value ]; |
| 223 | + } |
241 | 224 | } |
Index: trunk/extensions/DataTransclusion/DBDataTransclusionSource.php |
— | — | @@ -9,110 +9,88 @@ |
10 | 10 | * @licence GNU General Public Licence 2.0 or later |
11 | 11 | */ |
12 | 12 | |
13 | | -if ( !defined( 'MEDIAWIKI' ) ) { |
| 13 | +if( !defined( 'MEDIAWIKI' ) ) { |
14 | 14 | echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" ); |
15 | 15 | die( 1 ); |
16 | 16 | } |
17 | 17 | |
18 | 18 | /** |
19 | | - Implementations of DataTransclusionSource, fetching data records from an SQL database. |
20 | | - * |
21 | | - * In addition to the options supported by the DataTransclusionSource base class, |
22 | | - * DBDataTransclusionSource accepts some additional options |
23 | | - * |
24 | | - * * $spec['query']: the SQL query for fetching records. May not contain a |
25 | | - * GROUP or LIMIT clause (use $spec['querySuffix'] for that). The |
26 | | - * WHERE clause is automatically generated from the requested key/value pair. |
27 | | - * If $spec['query'] already contains a WHERE clause, the condition for |
28 | | - * the desired key/value pair is appended using AND. Note that subqueries are |
29 | | - * not supported reliably. REQUIRED. |
30 | | - * * $spec['querySuffix']: additional clauses to be added after the WHERE clause. |
31 | | - * Useful mostly to specify GROUP BY (or ORDER BY or LIMIT). |
32 | | - * * $spec['keyTypes']: associative arrays specifying the data types for the key fields. |
33 | | - * Array keys are the field names, the associated values specify the type |
34 | | - * as 'int' for integers, 'float' or 'decimal' for decimals, or 'string' |
35 | | - * for string fields. |
36 | | - * * $spec['keyFields']: like for DataTransclusionSource, this is list of fields |
37 | | - * that can be used as the key for fetching a record. However, it's not required |
38 | | - * for DBDataTransclusionSource: if not provided, array_keys( $spec['keyTypes'] ) |
39 | | - * will be used. REQUIRED. |
40 | | - * |
41 | | - * For more information on options supported by DataTransclusionSource, see the class-level |
42 | | - * documentation there. |
43 | | - */ |
| 19 | +* Implementations of DataTransclusionSource, fetching data records from an SQL database. |
| 20 | +* |
| 21 | +* In addition to the options supported by the DataTransclusionSource base class, |
| 22 | +* DBDataTransclusionSource accepts some additional options |
| 23 | +* |
| 24 | +* * $spec['query']: the SQL query for fetching records. May not contain a |
| 25 | +* GROUP or LIMIT clause (use $spec['querySuffix'] for that). The |
| 26 | +* WHERE clause is automatically generated from the requested key/value pair. |
| 27 | +* If $spec['query'] already contains a WHERE clause, the condition for |
| 28 | +* the desired key/value pair is appended using AND. Note that subqueries are |
| 29 | +* not supported reliably. REQUIRED. |
| 30 | +* * $spec['querySuffix']: additional clauses to be added after the WHERE clause. |
| 31 | +* Useful mostly to specify GROUP BY (or ORDER BY or LIMIT). |
| 32 | +* * $spec['keyTypes']: associative arrays specifying the data types for the key fields. |
| 33 | +* Array keys are the field names, the associated values specify the type |
| 34 | +* as 'int' for integers, 'float' or 'decimal' for decimals, or 'string' |
| 35 | +* for string fields. |
| 36 | +* * $spec['keyFields']: like for DataTransclusionSource, this is list of fields |
| 37 | +* that can be used as the key for fetching a record. However, it's not required |
| 38 | +* for DBDataTransclusionSource: if not provided, array_keys( $spec['keyTypes'] ) |
| 39 | +* will be used. REQUIRED. |
| 40 | +* |
| 41 | +* For more information on options supported by DataTransclusionSource, see the class-level |
| 42 | +* documentation there. |
| 43 | +*/ |
44 | 44 | class DBDataTransclusionSource extends DataTransclusionSource { |
45 | | - /** |
46 | | - * Initializes the DBDataTransclusionSource from the given parameter array. |
47 | | - * @param $spec associative array of options. See class-level documentation for details. |
48 | | - */ |
49 | | - function __construct( $spec ) { |
50 | | - if ( !isset( $spec[ 'keyFields' ] ) && isset( $spec[ 'keyTypes' ] ) ) { |
51 | | - $spec[ 'keyFields' ] = array_keys( $spec[ 'keyTypes' ] ); |
52 | | - } |
53 | 45 | |
54 | | - DataTransclusionSource::__construct( $spec ); |
| 46 | + /** |
| 47 | + * Initializes the DBDataTransclusionSource from the given parameter array. |
| 48 | + * @param $spec associative array of options. See class-level documentation for details. |
| 49 | + */ |
| 50 | + function __construct( $spec ) { |
| 51 | + if ( !isset( $spec[ 'keyFields' ] ) && isset( $spec[ 'keyTypes' ] ) ) $spec[ 'keyFields' ] = array_keys( $spec[ 'keyTypes' ] ); |
55 | 52 | |
56 | | - $this->query = $spec[ 'query' ]; |
57 | | - $this->querySuffix = @$spec[ 'querySuffix' ]; |
| 53 | + DataTransclusionSource::__construct( $spec ); |
58 | 54 | |
59 | | - if ( isset( $spec[ 'keyTypes' ] ) ) { |
60 | | - $this->keyTypes = $spec[ 'keyTypes' ]; |
61 | | - } else { |
62 | | - $this->keyTypes = null; |
63 | | - } |
64 | | - } |
| 55 | + $this->query = $spec[ 'query' ]; |
| 56 | + $this->querySuffix = @$spec[ 'querySuffix' ]; |
65 | 57 | |
66 | | - public function convertKey( $key, $value ) { |
67 | | - if ( !isset( $this->keyTypes[ $key ] ) ) { |
68 | | - return (string)$value; |
69 | | - } |
| 58 | + if ( isset( $spec[ 'keyTypes' ] ) ) $this->keyTypes = $spec[ 'keyTypes' ]; |
| 59 | + else $this->keyTypes = null; |
| 60 | + } |
70 | 61 | |
71 | | - $t = strtolower( trim( $this->keyTypes[ $key ] ) ); |
| 62 | + public function convertKey( $key, $value ) { |
| 63 | + if ( !isset( $this->keyTypes[ $key ] ) ) return (string)$value; |
72 | 64 | |
73 | | - if ( $t == 'int' ) { |
74 | | - return (int)$value; |
75 | | - } else if ( $t == 'decimal' || $t == 'float' ) { |
76 | | - return (float)$value; |
77 | | - } else { |
78 | | - return (string)$value; |
79 | | - } |
80 | | - } |
| 65 | + $t = strtolower( trim( $this->keyTypes[ $key ] ) ); |
| 66 | + |
| 67 | + if ( $t == 'int' ) return (int)$value; |
| 68 | + else if ( $t == 'decimal' || $t == 'float' ) return (float)$value; |
| 69 | + else return (string)$value; |
| 70 | + } |
81 | 71 | |
82 | | - public function fetchRecord( $field, $value ) { |
83 | | - $db = wfGetDB( DB_SLAVE ); |
| 72 | + public function fetchRecord( $field, $value ) { |
| 73 | + $db = wfGetDB( DB_SLAVE ); |
84 | 74 | |
85 | | - if ( !preg_match( '/\w+[\w\d]+/', $field ) ) { |
86 | | - return false; // redundant, but make extra sure we don't get anythign evil here //TESTME |
87 | | - } |
| 75 | + if ( !preg_match( '/\w+[\w\d]+/', $field ) ) return false; // redundant, but make extra sure we don't get anythign evil here //TESTME |
88 | 76 | |
89 | | - $value = $this->convertKey( $field, $value ); // TESTME |
| 77 | + $value = $this->convertKey( $field, $value ); //TESTME |
90 | 78 | |
91 | | - if ( is_string( $value ) ) { |
92 | | - $v = $db->addQuotes( $value ); // TESTME |
93 | | - } else { |
94 | | - $v = $value; |
95 | | - } |
| 79 | + if ( is_string( $value ) ) $v = $db->addQuotes( $value ); //TESTME |
| 80 | + else $v = $value; |
96 | 81 | |
97 | | - $where = "( " . $field . " = " . $v . " )"; |
| 82 | + $where = "( " . $field . " = " . $v . " )"; |
98 | 83 | |
99 | | - if ( preg_match( '/[)\s]WHERE[\s(]/is', $this->query ) ) { |
100 | | - $sql = $this->query . " AND " . $where; |
101 | | - } else { |
102 | | - $sql = $this->query . " WHERE " . $where; |
103 | | - } |
| 84 | + if ( preg_match('/[)\s]WHERE[\s(]/is', $this->query ) ) $sql = $this->query . " AND " . $where; |
| 85 | + else $sql = $this->query . " WHERE " . $where; |
104 | 86 | |
105 | | - if ( $this->querySuffix ) { |
106 | | - $sql = $sql . ' ' . $this->querySuffix; |
107 | | - } |
| 87 | + if ( $this->querySuffix ) $sql = $sql . ' ' . $this->querySuffix; |
108 | 88 | |
109 | | - $rs = $db->query( $sql, "DBDataTransclusionSource(" . $this->getName() . ")::fetchRecord" ); |
110 | | - if ( !$rs ) { |
111 | | - return false; |
112 | | - } |
| 89 | + $rs = $db->query( $sql, "DBDataTransclusionSource(" . $this->getName() . ")::fetchRecord" ); |
| 90 | + if ( !$rs ) return false; |
113 | 91 | |
114 | | - $rec = $db->fetchRow( $rs ); |
| 92 | + $rec = $db->fetchRow( $rs ); |
115 | 93 | |
116 | | - $db->freeResult( $rs ); |
117 | | - return $rec; |
118 | | - } |
| 94 | + $db->freeResult( $rs ); |
| 95 | + return $rec; |
| 96 | + } |
119 | 97 | } |