Twitter Widget Pro - Version 2.0.0

Version Description

  • Completely rewitten to use the new widget class introduced in WordPress 2.8
  • Now uses the json class that will be included in WordPress 2.9 for anyone on PHP < 5.2
  • The "Show Link to Twitter Widget Pro" is now off by default to comply with the latest decisions regarding the plugin repository
Download this release

Release Info

Developer aaroncampbell
Plugin Icon wp plugin Twitter Widget Pro
Version 2.0.0
Comparing to
See all releases

Code changes from version 1.5.1 to 2.0.0

class-json.php ADDED
@@ -0,0 +1,860 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+ /**
4
+ * Converts to and from JSON format.
5
+ *
6
+ * JSON (JavaScript Object Notation) is a lightweight data-interchange
7
+ * format. It is easy for humans to read and write. It is easy for machines
8
+ * to parse and generate. It is based on a subset of the JavaScript
9
+ * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
10
+ * This feature can also be found in Python. JSON is a text format that is
11
+ * completely language independent but uses conventions that are familiar
12
+ * to programmers of the C-family of languages, including C, C++, C#, Java,
13
+ * JavaScript, Perl, TCL, and many others. These properties make JSON an
14
+ * ideal data-interchange language.
15
+ *
16
+ * This package provides a simple encoder and decoder for JSON notation. It
17
+ * is intended for use with client-side Javascript applications that make
18
+ * use of HTTPRequest to perform server communication functions - data can
19
+ * be encoded into JSON notation for use in a client-side javascript, or
20
+ * decoded from incoming Javascript requests. JSON format is native to
21
+ * Javascript, and can be directly eval()'ed with no further parsing
22
+ * overhead
23
+ *
24
+ * All strings should be in ASCII or UTF-8 format!
25
+ *
26
+ * LICENSE: Redistribution and use in source and binary forms, with or
27
+ * without modification, are permitted provided that the following
28
+ * conditions are met: Redistributions of source code must retain the
29
+ * above copyright notice, this list of conditions and the following
30
+ * disclaimer. Redistributions in binary form must reproduce the above
31
+ * copyright notice, this list of conditions and the following disclaimer
32
+ * in the documentation and/or other materials provided with the
33
+ * distribution.
34
+ *
35
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
36
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
37
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
38
+ * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
39
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
40
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
41
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
42
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
43
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
44
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
45
+ * DAMAGE.
46
+ *
47
+ * @category
48
+ * @package Services_JSON
49
+ * @author Michal Migurski <mike-json@teczno.com>
50
+ * @author Matt Knapp <mdknapp[at]gmail[dot]com>
51
+ * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
52
+ * @copyright 2005 Michal Migurski
53
+ * @version CVS: $Id: JSON.php,v 1.3 2009/05/22 23:51:00 alan_k Exp $
54
+ * @license http://www.opensource.org/licenses/bsd-license.php
55
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
56
+ */
57
+
58
+ /**
59
+ * Marker constant for Services_JSON::decode(), used to flag stack state
60
+ */
61
+ define('SERVICES_JSON_SLICE', 1);
62
+
63
+ /**
64
+ * Marker constant for Services_JSON::decode(), used to flag stack state
65
+ */
66
+ define('SERVICES_JSON_IN_STR', 2);
67
+
68
+ /**
69
+ * Marker constant for Services_JSON::decode(), used to flag stack state
70
+ */
71
+ define('SERVICES_JSON_IN_ARR', 3);
72
+
73
+ /**
74
+ * Marker constant for Services_JSON::decode(), used to flag stack state
75
+ */
76
+ define('SERVICES_JSON_IN_OBJ', 4);
77
+
78
+ /**
79
+ * Marker constant for Services_JSON::decode(), used to flag stack state
80
+ */
81
+ define('SERVICES_JSON_IN_CMT', 5);
82
+
83
+ /**
84
+ * Behavior switch for Services_JSON::decode()
85
+ */
86
+ define('SERVICES_JSON_LOOSE_TYPE', 16);
87
+
88
+ /**
89
+ * Behavior switch for Services_JSON::decode()
90
+ */
91
+ define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
92
+
93
+ /**
94
+ * Converts to and from JSON format.
95
+ *
96
+ * Brief example of use:
97
+ *
98
+ * <code>
99
+ * // create a new instance of Services_JSON
100
+ * $json = new Services_JSON();
101
+ *
102
+ * // convert a complexe value to JSON notation, and send it to the browser
103
+ * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
104
+ * $output = $json->encode($value);
105
+ *
106
+ * print($output);
107
+ * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
108
+ *
109
+ * // accept incoming POST data, assumed to be in JSON notation
110
+ * $input = file_get_contents('php://input', 1000000);
111
+ * $value = $json->decode($input);
112
+ * </code>
113
+ */
114
+ class Services_JSON
115
+ {
116
+ /**
117
+ * constructs a new JSON instance
118
+ *
119
+ * @param int $use object behavior flags; combine with boolean-OR
120
+ *
121
+ * possible values:
122
+ * - SERVICES_JSON_LOOSE_TYPE: loose typing.
123
+ * "{...}" syntax creates associative arrays
124
+ * instead of objects in decode().
125
+ * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
126
+ * Values which can't be encoded (e.g. resources)
127
+ * appear as NULL instead of throwing errors.
128
+ * By default, a deeply-nested resource will
129
+ * bubble up with an error, so all return values
130
+ * from encode() should be checked with isError()
131
+ */
132
+ function Services_JSON($use = 0)
133
+ {
134
+ $this->use = $use;
135
+ }
136
+
137
+ /**
138
+ * convert a string from one UTF-16 char to one UTF-8 char
139
+ *
140
+ * Normally should be handled by mb_convert_encoding, but
141
+ * provides a slower PHP-only method for installations
142
+ * that lack the multibye string extension.
143
+ *
144
+ * @param string $utf16 UTF-16 character
145
+ * @return string UTF-8 character
146
+ * @access private
147
+ */
148
+ function utf162utf8($utf16)
149
+ {
150
+ // oh please oh please oh please oh please oh please
151
+ if(function_exists('mb_convert_encoding')) {
152
+ return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
153
+ }
154
+
155
+ $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
156
+
157
+ switch(true) {
158
+ case ((0x7F & $bytes) == $bytes):
159
+ // this case should never be reached, because we are in ASCII range
160
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
161
+ return chr(0x7F & $bytes);
162
+
163
+ case (0x07FF & $bytes) == $bytes:
164
+ // return a 2-byte UTF-8 character
165
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
166
+ return chr(0xC0 | (($bytes >> 6) & 0x1F))
167
+ . chr(0x80 | ($bytes & 0x3F));
168
+
169
+ case (0xFFFF & $bytes) == $bytes:
170
+ // return a 3-byte UTF-8 character
171
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
172
+ return chr(0xE0 | (($bytes >> 12) & 0x0F))
173
+ . chr(0x80 | (($bytes >> 6) & 0x3F))
174
+ . chr(0x80 | ($bytes & 0x3F));
175
+ }
176
+
177
+ // ignoring UTF-32 for now, sorry
178
+ return '';
179
+ }
180
+
181
+ /**
182
+ * convert a string from one UTF-8 char to one UTF-16 char
183
+ *
184
+ * Normally should be handled by mb_convert_encoding, but
185
+ * provides a slower PHP-only method for installations
186
+ * that lack the multibye string extension.
187
+ *
188
+ * @param string $utf8 UTF-8 character
189
+ * @return string UTF-16 character
190
+ * @access private
191
+ */
192
+ function utf82utf16($utf8)
193
+ {
194
+ // oh please oh please oh please oh please oh please
195
+ if(function_exists('mb_convert_encoding')) {
196
+ return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
197
+ }
198
+
199
+ switch(strlen($utf8)) {
200
+ case 1:
201
+ // this case should never be reached, because we are in ASCII range
202
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
203
+ return $utf8;
204
+
205
+ case 2:
206
+ // return a UTF-16 character from a 2-byte UTF-8 char
207
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
208
+ return chr(0x07 & (ord($utf8{0}) >> 2))
209
+ . chr((0xC0 & (ord($utf8{0}) << 6))
210
+ | (0x3F & ord($utf8{1})));
211
+
212
+ case 3:
213
+ // return a UTF-16 character from a 3-byte UTF-8 char
214
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
215
+ return chr((0xF0 & (ord($utf8{0}) << 4))
216
+ | (0x0F & (ord($utf8{1}) >> 2)))
217
+ . chr((0xC0 & (ord($utf8{1}) << 6))
218
+ | (0x7F & ord($utf8{2})));
219
+ }
220
+
221
+ // ignoring UTF-32 for now, sorry
222
+ return '';
223
+ }
224
+
225
+ /**
226
+ * encodes an arbitrary variable into JSON format (and sends JSON Header)
227
+ *
228
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
229
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
230
+ * if var is a strng, note that encode() always expects it
231
+ * to be in ASCII or UTF-8 format!
232
+ *
233
+ * @return mixed JSON string representation of input var or an error if a problem occurs
234
+ * @access public
235
+ */
236
+ function encode($var)
237
+ {
238
+ header('Content-type: application/x-javascript');
239
+ return $this->_encode($var);
240
+ }
241
+ /**
242
+ * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow CSS!!!!)
243
+ *
244
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
245
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
246
+ * if var is a strng, note that encode() always expects it
247
+ * to be in ASCII or UTF-8 format!
248
+ *
249
+ * @return mixed JSON string representation of input var or an error if a problem occurs
250
+ * @access public
251
+ */
252
+ function encodeUnsafe($var)
253
+ {
254
+ return $this->_encode($var);
255
+ }
256
+ /**
257
+ * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format
258
+ *
259
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
260
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
261
+ * if var is a strng, note that encode() always expects it
262
+ * to be in ASCII or UTF-8 format!
263
+ *
264
+ * @return mixed JSON string representation of input var or an error if a problem occurs
265
+ * @access public
266
+ */
267
+ function _encode($var)
268
+ {
269
+
270
+ switch (gettype($var)) {
271
+ case 'boolean':
272
+ return $var ? 'true' : 'false';
273
+
274
+ case 'NULL':
275
+ return 'null';
276
+
277
+ case 'integer':
278
+ return (int) $var;
279
+
280
+ case 'double':
281
+ case 'float':
282
+ return (float) $var;
283
+
284
+ case 'string':
285
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
286
+ $ascii = '';
287
+ $strlen_var = strlen($var);
288
+
289
+ /*
290
+ * Iterate over every character in the string,
291
+ * escaping with a slash or encoding to UTF-8 where necessary
292
+ */
293
+ for ($c = 0; $c < $strlen_var; ++$c) {
294
+
295
+ $ord_var_c = ord($var{$c});
296
+
297
+ switch (true) {
298
+ case $ord_var_c == 0x08:
299
+ $ascii .= '\b';
300
+ break;
301
+ case $ord_var_c == 0x09:
302
+ $ascii .= '\t';
303
+ break;
304
+ case $ord_var_c == 0x0A:
305
+ $ascii .= '\n';
306
+ break;
307
+ case $ord_var_c == 0x0C:
308
+ $ascii .= '\f';
309
+ break;
310
+ case $ord_var_c == 0x0D:
311
+ $ascii .= '\r';
312
+ break;
313
+
314
+ case $ord_var_c == 0x22:
315
+ case $ord_var_c == 0x2F:
316
+ case $ord_var_c == 0x5C:
317
+ // double quote, slash, slosh
318
+ $ascii .= '\\'.$var{$c};
319
+ break;
320
+
321
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
322
+ // characters U-00000000 - U-0000007F (same as ASCII)
323
+ $ascii .= $var{$c};
324
+ break;
325
+
326
+ case (($ord_var_c & 0xE0) == 0xC0):
327
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
328
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
329
+ if ($c+1 >= $strlen_var) {
330
+ $c += 1;
331
+ $ascii .= '?';
332
+ break;
333
+ }
334
+
335
+ $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
336
+ $c += 1;
337
+ $utf16 = $this->utf82utf16($char);
338
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
339
+ break;
340
+
341
+ case (($ord_var_c & 0xF0) == 0xE0):
342
+ if ($c+2 >= $strlen_var) {
343
+ $c += 2;
344
+ $ascii .= '?';
345
+ break;
346
+ }
347
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
348
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
349
+ $char = pack('C*', $ord_var_c,
350
+ @ord($var{$c + 1}),
351
+ @ord($var{$c + 2}));
352
+ $c += 2;
353
+ $utf16 = $this->utf82utf16($char);
354
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
355
+ break;
356
+
357
+ case (($ord_var_c & 0xF8) == 0xF0):
358
+ if ($c+3 >= $strlen_var) {
359
+ $c += 3;
360
+ $ascii .= '?';
361
+ break;
362
+ }
363
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
364
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
365
+ $char = pack('C*', $ord_var_c,
366
+ ord($var{$c + 1}),
367
+ ord($var{$c + 2}),
368
+ ord($var{$c + 3}));
369
+ $c += 3;
370
+ $utf16 = $this->utf82utf16($char);
371
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
372
+ break;
373
+
374
+ case (($ord_var_c & 0xFC) == 0xF8):
375
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
376
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
377
+ if ($c+4 >= $strlen_var) {
378
+ $c += 4;
379
+ $ascii .= '?';
380
+ break;
381
+ }
382
+ $char = pack('C*', $ord_var_c,
383
+ ord($var{$c + 1}),
384
+ ord($var{$c + 2}),
385
+ ord($var{$c + 3}),
386
+ ord($var{$c + 4}));
387
+ $c += 4;
388
+ $utf16 = $this->utf82utf16($char);
389
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
390
+ break;
391
+
392
+ case (($ord_var_c & 0xFE) == 0xFC):
393
+ if ($c+5 >= $strlen_var) {
394
+ $c += 5;
395
+ $ascii .= '?';
396
+ break;
397
+ }
398
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
399
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
400
+ $char = pack('C*', $ord_var_c,
401
+ ord($var{$c + 1}),
402
+ ord($var{$c + 2}),
403
+ ord($var{$c + 3}),
404
+ ord($var{$c + 4}),
405
+ ord($var{$c + 5}));
406
+ $c += 5;
407
+ $utf16 = $this->utf82utf16($char);
408
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
409
+ break;
410
+ }
411
+ }
412
+ return '"'.$ascii.'"';
413
+
414
+ case 'array':
415
+ /*
416
+ * As per JSON spec if any array key is not an integer
417
+ * we must treat the the whole array as an object. We
418
+ * also try to catch a sparsely populated associative
419
+ * array with numeric keys here because some JS engines
420
+ * will create an array with empty indexes up to
421
+ * max_index which can cause memory issues and because
422
+ * the keys, which may be relevant, will be remapped
423
+ * otherwise.
424
+ *
425
+ * As per the ECMA and JSON specification an object may
426
+ * have any string as a property. Unfortunately due to
427
+ * a hole in the ECMA specification if the key is a
428
+ * ECMA reserved word or starts with a digit the
429
+ * parameter is only accessible using ECMAScript's
430
+ * bracket notation.
431
+ */
432
+
433
+ // treat as a JSON object
434
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
435
+ $properties = array_map(array($this, 'name_value'),
436
+ array_keys($var),
437
+ array_values($var));
438
+
439
+ foreach($properties as $property) {
440
+ if(Services_JSON::isError($property)) {
441
+ return $property;
442
+ }
443
+ }
444
+
445
+ return '{' . join(',', $properties) . '}';
446
+ }
447
+
448
+ // treat it like a regular array
449
+ $elements = array_map(array($this, '_encode'), $var);
450
+
451
+ foreach($elements as $element) {
452
+ if(Services_JSON::isError($element)) {
453
+ return $element;
454
+ }
455
+ }
456
+
457
+ return '[' . join(',', $elements) . ']';
458
+
459
+ case 'object':
460
+ $vars = get_object_vars($var);
461
+
462
+ $properties = array_map(array($this, 'name_value'),
463
+ array_keys($vars),
464
+ array_values($vars));
465
+
466
+ foreach($properties as $property) {
467
+ if(Services_JSON::isError($property)) {
468
+ return $property;
469
+ }
470
+ }
471
+
472
+ return '{' . join(',', $properties) . '}';
473
+
474
+ default:
475
+ return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
476
+ ? 'null'
477
+ : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
478
+ }
479
+ }
480
+
481
+ /**
482
+ * array-walking function for use in generating JSON-formatted name-value pairs
483
+ *
484
+ * @param string $name name of key to use
485
+ * @param mixed $value reference to an array element to be encoded
486
+ *
487
+ * @return string JSON-formatted name-value pair, like '"name":value'
488
+ * @access private
489
+ */
490
+ function name_value($name, $value)
491
+ {
492
+ $encoded_value = $this->_encode($value);
493
+
494
+ if(Services_JSON::isError($encoded_value)) {
495
+ return $encoded_value;
496
+ }
497
+
498
+ return $this->_encode(strval($name)) . ':' . $encoded_value;
499
+ }
500
+
501
+ /**
502
+ * reduce a string by removing leading and trailing comments and whitespace
503
+ *
504
+ * @param $str string string value to strip of comments and whitespace
505
+ *
506
+ * @return string string value stripped of comments and whitespace
507
+ * @access private
508
+ */
509
+ function reduce_string($str)
510
+ {
511
+ $str = preg_replace(array(
512
+
513
+ // eliminate single line comments in '// ...' form
514
+ '#^\s*//(.+)$#m',
515
+
516
+ // eliminate multi-line comments in '/* ... */' form, at start of string
517
+ '#^\s*/\*(.+)\*/#Us',
518
+
519
+ // eliminate multi-line comments in '/* ... */' form, at end of string
520
+ '#/\*(.+)\*/\s*$#Us'
521
+
522
+ ), '', $str);
523
+
524
+ // eliminate extraneous space
525
+ return trim($str);
526
+ }
527
+
528
+ /**
529
+ * decodes a JSON string into appropriate variable
530
+ *
531
+ * @param string $str JSON-formatted string
532
+ *
533
+ * @return mixed number, boolean, string, array, or object
534
+ * corresponding to given JSON input string.
535
+ * See argument 1 to Services_JSON() above for object-output behavior.
536
+ * Note that decode() always returns strings
537
+ * in ASCII or UTF-8 format!
538
+ * @access public
539
+ */
540
+ function decode($str)
541
+ {
542
+ $str = $this->reduce_string($str);
543
+
544
+ switch (strtolower($str)) {
545
+ case 'true':
546
+ return true;
547
+
548
+ case 'false':
549
+ return false;
550
+
551
+ case 'null':
552
+ return null;
553
+
554
+ default:
555
+ $m = array();
556
+
557
+ if (is_numeric($str)) {
558
+ // Lookie-loo, it's a number
559
+
560
+ // This would work on its own, but I'm trying to be
561
+ // good about returning integers where appropriate:
562
+ // return (float)$str;
563
+
564
+ // Return float or int, as appropriate
565
+ return ((float)$str == (integer)$str)
566
+ ? (integer)$str
567
+ : (float)$str;
568
+
569
+ } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
570
+ // STRINGS RETURNED IN UTF-8 FORMAT
571
+ $delim = substr($str, 0, 1);
572
+ $chrs = substr($str, 1, -1);
573
+ $utf8 = '';
574
+ $strlen_chrs = strlen($chrs);
575
+
576
+ for ($c = 0; $c < $strlen_chrs; ++$c) {
577
+
578
+ $substr_chrs_c_2 = substr($chrs, $c, 2);
579
+ $ord_chrs_c = ord($chrs{$c});
580
+
581
+ switch (true) {
582
+ case $substr_chrs_c_2 == '\b':
583
+ $utf8 .= chr(0x08);
584
+ ++$c;
585
+ break;
586
+ case $substr_chrs_c_2 == '\t':
587
+ $utf8 .= chr(0x09);
588
+ ++$c;
589
+ break;
590
+ case $substr_chrs_c_2 == '\n':
591
+ $utf8 .= chr(0x0A);
592
+ ++$c;
593
+ break;
594
+ case $substr_chrs_c_2 == '\f':
595
+ $utf8 .= chr(0x0C);
596
+ ++$c;
597
+ break;
598
+ case $substr_chrs_c_2 == '\r':
599
+ $utf8 .= chr(0x0D);
600
+ ++$c;
601
+ break;
602
+
603
+ case $substr_chrs_c_2 == '\\"':
604
+ case $substr_chrs_c_2 == '\\\'':
605
+ case $substr_chrs_c_2 == '\\\\':
606
+ case $substr_chrs_c_2 == '\\/':
607
+ if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
608
+ ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
609
+ $utf8 .= $chrs{++$c};
610
+ }
611
+ break;
612
+
613
+ case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
614
+ // single, escaped unicode character
615
+ $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
616
+ . chr(hexdec(substr($chrs, ($c + 4), 2)));
617
+ $utf8 .= $this->utf162utf8($utf16);
618
+ $c += 5;
619
+ break;
620
+
621
+ case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
622
+ $utf8 .= $chrs{$c};
623
+ break;
624
+
625
+ case ($ord_chrs_c & 0xE0) == 0xC0:
626
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
627
+ //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
628
+ $utf8 .= substr($chrs, $c, 2);
629
+ ++$c;
630
+ break;
631
+
632
+ case ($ord_chrs_c & 0xF0) == 0xE0:
633
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
634
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
635
+ $utf8 .= substr($chrs, $c, 3);
636
+ $c += 2;
637
+ break;
638
+
639
+ case ($ord_chrs_c & 0xF8) == 0xF0:
640
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
641
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
642
+ $utf8 .= substr($chrs, $c, 4);
643
+ $c += 3;
644
+ break;
645
+
646
+ case ($ord_chrs_c & 0xFC) == 0xF8:
647
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
648
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
649
+ $utf8 .= substr($chrs, $c, 5);
650
+ $c += 4;
651
+ break;
652
+
653
+ case ($ord_chrs_c & 0xFE) == 0xFC:
654
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
655
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
656
+ $utf8 .= substr($chrs, $c, 6);
657
+ $c += 5;
658
+ break;
659
+
660
+ }
661
+
662
+ }
663
+
664
+ return $utf8;
665
+
666
+ } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
667
+ // array, or object notation
668
+
669
+ if ($str{0} == '[') {
670
+ $stk = array(SERVICES_JSON_IN_ARR);
671
+ $arr = array();
672
+ } else {
673
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
674
+ $stk = array(SERVICES_JSON_IN_OBJ);
675
+ $obj = array();
676
+ } else {
677
+ $stk = array(SERVICES_JSON_IN_OBJ);
678
+ $obj = new stdClass();
679
+ }
680
+ }
681
+
682
+ array_push($stk, array('what' => SERVICES_JSON_SLICE,
683
+ 'where' => 0,
684
+ 'delim' => false));
685
+
686
+ $chrs = substr($str, 1, -1);
687
+ $chrs = $this->reduce_string($chrs);
688
+
689
+ if ($chrs == '') {
690
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
691
+ return $arr;
692
+
693
+ } else {
694
+ return $obj;
695
+
696
+ }
697
+ }
698
+
699
+ //print("\nparsing {$chrs}\n");
700
+
701
+ $strlen_chrs = strlen($chrs);
702
+
703
+ for ($c = 0; $c <= $strlen_chrs; ++$c) {
704
+
705
+ $top = end($stk);
706
+ $substr_chrs_c_2 = substr($chrs, $c, 2);
707
+
708
+ if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
709
+ // found a comma that is not inside a string, array, etc.,
710
+ // OR we've reached the end of the character list
711
+ $slice = substr($chrs, $top['where'], ($c - $top['where']));
712
+ array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
713
+ //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
714
+
715
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
716
+ // we are in an array, so just push an element onto the stack
717
+ array_push($arr, $this->decode($slice));
718
+
719
+ } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
720
+ // we are in an object, so figure
721
+ // out the property name and set an
722
+ // element in an associative array,
723
+ // for now
724
+ $parts = array();
725
+
726
+ if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
727
+ // "name":value pair
728
+ $key = $this->decode($parts[1]);
729
+ $val = $this->decode($parts[2]);
730
+
731
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
732
+ $obj[$key] = $val;
733
+ } else {
734
+ $obj->$key = $val;
735
+ }
736
+ } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
737
+ // name:value pair, where name is unquoted
738
+ $key = $parts[1];
739
+ $val = $this->decode($parts[2]);
740
+
741
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
742
+ $obj[$key] = $val;
743
+ } else {
744
+ $obj->$key = $val;
745
+ }
746
+ }
747
+
748
+ }
749
+
750
+ } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
751
+ // found a quote, and we are not inside a string
752
+ array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
753
+ //print("Found start of string at {$c}\n");
754
+
755
+ } elseif (($chrs{$c} == $top['delim']) &&
756
+ ($top['what'] == SERVICES_JSON_IN_STR) &&
757
+ ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
758
+ // found a quote, we're in a string, and it's not escaped
759
+ // we know that it's not escaped becase there is _not_ an
760
+ // odd number of backslashes at the end of the string so far
761
+ array_pop($stk);
762
+ //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
763
+
764
+ } elseif (($chrs{$c} == '[') &&
765
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
766
+ // found a left-bracket, and we are in an array, object, or slice
767
+ array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
768
+ //print("Found start of array at {$c}\n");
769
+
770
+ } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
771
+ // found a right-bracket, and we're in an array
772
+ array_pop($stk);
773
+ //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
774
+
775
+ } elseif (($chrs{$c} == '{') &&
776
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
777
+ // found a left-brace, and we are in an array, object, or slice
778
+ array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
779
+ //print("Found start of object at {$c}\n");
780
+
781
+ } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
782
+ // found a right-brace, and we're in an object
783
+ array_pop($stk);
784
+ //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
785
+
786
+ } elseif (($substr_chrs_c_2 == '/*') &&
787
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
788
+ // found a comment start, and we are in an array, object, or slice
789
+ array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
790
+ $c++;
791
+ //print("Found start of comment at {$c}\n");
792
+
793
+ } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
794
+ // found a comment end, and we're in one now
795
+ array_pop($stk);
796
+ $c++;
797
+
798
+ for ($i = $top['where']; $i <= $c; ++$i)
799
+ $chrs = substr_replace($chrs, ' ', $i, 1);
800
+
801
+ //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
802
+
803
+ }
804
+
805
+ }
806
+
807
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
808
+ return $arr;
809
+
810
+ } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
811
+ return $obj;
812
+
813
+ }
814
+
815
+ }
816
+ }
817
+ }
818
+
819
+ /**
820
+ * @todo Ultimately, this should just call PEAR::isError()
821
+ */
822
+ function isError($data, $code = null)
823
+ {
824
+ if (class_exists('pear')) {
825
+ return PEAR::isError($data, $code);
826
+ } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
827
+ is_subclass_of($data, 'services_json_error'))) {
828
+ return true;
829
+ }
830
+
831
+ return false;
832
+ }
833
+ }
834
+
835
+ if (class_exists('PEAR_Error')) {
836
+
837
+ class Services_JSON_Error extends PEAR_Error
838
+ {
839
+ function Services_JSON_Error($message = 'unknown error', $code = null,
840
+ $mode = null, $options = null, $userinfo = null)
841
+ {
842
+ parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
843
+ }
844
+ }
845
+
846
+ } else {
847
+
848
+ /**
849
+ * @todo Ultimately, this class shall be descended from PEAR_Error
850
+ */
851
+ class Services_JSON_Error
852
+ {
853
+ function Services_JSON_Error($message = 'unknown error', $code = null,
854
+ $mode = null, $options = null, $userinfo = null)
855
+ {
856
+
857
+ }
858
+ }
859
+
860
+ }
json_decode.php DELETED
@@ -1,392 +0,0 @@
1
- <?php
2
- class Zend_Json_Decoder
3
- {
4
- /**
5
- * Parse tokens used to decode the JSON object. These are not
6
- * for public consumption, they are just used internally to the
7
- * class.
8
- */
9
- const EOF = 0;
10
- const DATUM = 1;
11
- const LBRACE = 2;
12
- const LBRACKET = 3;
13
- const RBRACE = 4;
14
- const RBRACKET = 5;
15
- const COMMA = 6;
16
- const COLON = 7;
17
-
18
- /**
19
- * Use to maintain a "pointer" to the source being decoded
20
- *
21
- * @var string
22
- */
23
- protected $_source;
24
-
25
- /**
26
- * Caches the source length
27
- *
28
- * @var int
29
- */
30
- protected $_sourceLength;
31
-
32
- /**
33
- * The offset within the souce being decoded
34
- *
35
- * @var int
36
- *
37
- */
38
- protected $_offset;
39
-
40
- /**
41
- * The current token being considered in the parser cycle
42
- *
43
- * @var int
44
- */
45
- protected $_token;
46
-
47
- /**
48
- * Constructor
49
- *
50
- * @param string $source String source to decode
51
- * @param int $decodeType How objects should be decoded -- see
52
- * {@link Zend_Json::TYPE_ARRAY} and {@link Zend_Json::TYPE_OBJECT} for
53
- * valid values
54
- * @return void
55
- */
56
- protected function __construct($source)
57
- {
58
- // Set defaults
59
- $this->_source = $source;
60
- $this->_sourceLength = strlen($source);
61
- $this->_token = self::EOF;
62
- $this->_offset = 0;
63
-
64
- // Set pointer at first token
65
- $this->_getNextToken();
66
- }
67
-
68
- /**
69
- * Decode a JSON source string
70
- *
71
- * Decodes a JSON encoded string. The value returned will be one of the
72
- * following:
73
- * - integer
74
- * - float
75
- * - boolean
76
- * - null
77
- * - StdClass
78
- * - array
79
- * - array of one or more of the above types
80
- *
81
- * By default, decoded objects will be returned as associative arrays; to
82
- * return a StdClass object instead, pass {@link Zend_Json::TYPE_OBJECT} to
83
- * the $objectDecodeType parameter.
84
- *
85
- * @static
86
- * @access public
87
- * @param string $source String to be decoded
88
- * @param int $objectDecodeType How objects should be decoded; should be
89
- * either or {@link Zend_Json::TYPE_ARRAY} or
90
- * {@link Zend_Json::TYPE_OBJECT}; defaults to TYPE_ARRAY
91
- * @return mixed
92
- * @throws Zend_Json_Exception
93
- */
94
- public static function decode($source = null)
95
- {
96
- if (null === $source || !is_string($source)) {
97
- return false;
98
- }
99
-
100
- $decoder = new self($source);
101
-
102
- return $decoder->_decodeValue();
103
- }
104
-
105
-
106
- /**
107
- * Recursive driving rountine for supported toplevel tops
108
- *
109
- * @return mixed
110
- */
111
- protected function _decodeValue()
112
- {
113
- switch ($this->_token) {
114
- case self::DATUM:
115
- $result = $this->_tokenValue;
116
- $this->_getNextToken();
117
- return($result);
118
- break;
119
- case self::LBRACE:
120
- return($this->_decodeObject());
121
- break;
122
- case self::LBRACKET:
123
- return($this->_decodeArray());
124
- break;
125
- default:
126
- return null;
127
- break;
128
- }
129
- }
130
-
131
- /**
132
- * Decodes an object of the form:
133
- * { "attribute: value, "attribute2" : value,...}
134
- *
135
- * If ZJsonEnoder or ZJAjax was used to encode the original object
136
- * then a special attribute called __className which specifies a class
137
- * name that should wrap the data contained within the encoded source.
138
- *
139
- * Decodes to either an array or StdClass object, based on the value of
140
- * {@link $_decodeType}. If invalid $_decodeType present, returns as an
141
- * array.
142
- *
143
- * @return array|StdClass
144
- */
145
- protected function _decodeObject()
146
- {
147
- $result = new StdClass();
148
- $members = array();
149
- $tok = $this->_getNextToken();
150
-
151
- while ($tok && $tok != self::RBRACE) {
152
- if ($tok != self::DATUM || ! is_string($this->_tokenValue)) {
153
- return $result;
154
- }
155
-
156
- $key = $this->_tokenValue;
157
- $tok = $this->_getNextToken();
158
-
159
- if ($tok != self::COLON) {
160
- return $result;
161
- }
162
-
163
- $tok = $this->_getNextToken();
164
- $members[$key] = $this->_decodeValue();
165
- $tok = $this->_token;
166
-
167
- if ($tok == self::RBRACE) {
168
- break;
169
- }
170
-
171
- if ($tok != self::COMMA) {
172
- return $result;
173
- }
174
-
175
- $tok = $this->_getNextToken();
176
- }
177
-
178
- // Create new StdClass and populate with $members
179
- foreach ($members as $key => $value) {
180
- $result->$key = $value;
181
- }
182
-
183
- $this->_getNextToken();
184
- return $result;
185
- }
186
-
187
- /**
188
- * Decodes a JSON array format:
189
- * [element, element2,...,elementN]
190
- *
191
- * @return array
192
- */
193
- protected function _decodeArray()
194
- {
195
- $result = array();
196
- $starttok = $tok = $this->_getNextToken(); // Move past the '['
197
- $index = 0;
198
-
199
- while ($tok && $tok != self::RBRACKET) {
200
- $result[$index++] = $this->_decodeValue();
201
-
202
- $tok = $this->_token;
203
-
204
- if ($tok == self::RBRACKET || !$tok) {
205
- break;
206
- }
207
-
208
- if ($tok != self::COMMA) {
209
- return $result;
210
- }
211
-
212
- $tok = $this->_getNextToken();
213
- }
214
-
215
- $this->_getNextToken();
216
- return($result);
217
- }
218
-
219
-
220
- /**
221
- * Removes whitepsace characters from the source input
222
- */
223
- protected function _eatWhitespace()
224
- {
225
- if (preg_match(
226
- '/([\t\b\f\n\r ])*/s',
227
- $this->_source,
228
- $matches,
229
- PREG_OFFSET_CAPTURE,
230
- $this->_offset)
231
- && $matches[0][1] == $this->_offset)
232
- {
233
- $this->_offset += strlen($matches[0][0]);
234
- }
235
- }
236
-
237
-
238
- /**
239
- * Retrieves the next token from the source stream
240
- *
241
- * @return int Token constant value specified in class definition
242
- */
243
- protected function _getNextToken()
244
- {
245
- $this->_token = self::EOF;
246
- $this->_tokenValue = null;
247
- $this->_eatWhitespace();
248
-
249
- if ($this->_offset >= $this->_sourceLength) {
250
- return(self::EOF);
251
- }
252
-
253
- $str = $this->_source;
254
- $str_length = $this->_sourceLength;
255
- $i = $this->_offset;
256
- $start = $i;
257
-
258
- switch ($str{$i}) {
259
- case '{':
260
- $this->_token = self::LBRACE;
261
- break;
262
- case '}':
263
- $this->_token = self::RBRACE;
264
- break;
265
- case '[':
266
- $this->_token = self::LBRACKET;
267
- break;
268
- case ']':
269
- $this->_token = self::RBRACKET;
270
- break;
271
- case ',':
272
- $this->_token = self::COMMA;
273
- break;
274
- case ':':
275
- $this->_token = self::COLON;
276
- break;
277
- case '"':
278
- $result = '';
279
- do {
280
- $i++;
281
- if ($i >= $str_length) {
282
- break;
283
- }
284
-
285
- $chr = $str{$i};
286
- if ($chr == '\\') {
287
- $i++;
288
- if ($i >= $str_length) {
289
- break;
290
- }
291
- $chr = $str{$i};
292
- switch ($chr) {
293
- case '"' :
294
- $result .= '"';
295
- break;
296
- case '\\':
297
- $result .= '\\';
298
- break;
299
- case '/' :
300
- $result .= '/';
301
- break;
302
- case 'b' :
303
- $result .= chr(8);
304
- break;
305
- case 'f' :
306
- $result .= chr(12);
307
- break;
308
- case 'n' :
309
- $result .= chr(10);
310
- break;
311
- case 'r' :
312
- $result .= chr(13);
313
- break;
314
- case 't' :
315
- $result .= chr(9);
316
- break;
317
- case '\'' :
318
- $result .= '\'';
319
- break;
320
- default:
321
- throw new Exception("Illegal escape "
322
- . "sequence '" . $chr . "'");
323
- }
324
- } elseif ($chr == '"') {
325
- break;
326
- } else {
327
- $result .= $chr;
328
- }
329
- } while ($i < $str_length);
330
-
331
- $this->_token = self::DATUM;
332
- //$this->_tokenValue = substr($str, $start + 1, $i - $start - 1);
333
- $this->_tokenValue = $result;
334
- break;
335
- case 't':
336
- if (($i+ 3) < $str_length && substr($str, $start, 4) == "true") {
337
- $this->_token = self::DATUM;
338
- }
339
- $this->_tokenValue = true;
340
- $i += 3;
341
- break;
342
- case 'f':
343
- if (($i+ 4) < $str_length && substr($str, $start, 5) == "false") {
344
- $this->_token = self::DATUM;
345
- }
346
- $this->_tokenValue = false;
347
- $i += 4;
348
- break;
349
- case 'n':
350
- if (($i+ 3) < $str_length && substr($str, $start, 4) == "null") {
351
- $this->_token = self::DATUM;
352
- }
353
- $this->_tokenValue = NULL;
354
- $i += 3;
355
- break;
356
- }
357
-
358
- if ($this->_token != self::EOF) {
359
- $this->_offset = $i + 1; // Consume the last token character
360
- return($this->_token);
361
- }
362
-
363
- $chr = $str{$i};
364
- if ($chr == '-' || $chr == '.' || ($chr >= '0' && $chr <= '9')) {
365
- if (preg_match('/-?([0-9])*(\.[0-9]*)?((e|E)((-|\+)?)[0-9]+)?/s',
366
- $str, $matches, PREG_OFFSET_CAPTURE, $start) && $matches[0][1] == $start) {
367
-
368
- $datum = $matches[0][0];
369
-
370
- if (is_numeric($datum)) {
371
- if (preg_match('/^0\d+$/', $datum)) {
372
- return;
373
- } else {
374
- $val = intval($datum);
375
- $fVal = floatval($datum);
376
- $this->_tokenValue = ($val == $fVal ? $val : $fVal);
377
- }
378
- } else {
379
- return;
380
- }
381
-
382
- $this->_token = self::DATUM;
383
- $this->_offset = $start + strlen($datum);
384
- }
385
- } else {
386
- return;
387
- }
388
-
389
- return($this->_token);
390
- }
391
- }
392
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
languages/twitter-widget-pro.pot CHANGED
@@ -7,7 +7,7 @@ msgid ""
7
  msgstr ""
8
  "Project-Id-Version: PACKAGE VERSION\n"
9
  "Report-Msgid-Bugs-To: http://wordpress.org/tag/twitter-widget-pro\n"
10
- "POT-Creation-Date: 2009-07-24 16:48+0000\n"
11
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,192 +16,212 @@ msgstr ""
16
  "Content-Transfer-Encoding: 8bit\n"
17
  "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
18
 
19
- #: wp-twitter-widget.php:74 wp-twitter-widget.php:491
20
- msgid "Twitter Widget Pro"
21
- msgstr ""
22
-
23
- #: wp-twitter-widget.php:90
24
- msgid "Twitter Widget Pro Options"
25
- msgstr ""
26
-
27
- #: wp-twitter-widget.php:96
28
- msgid "Click for Help!"
29
  msgstr ""
30
 
31
- #: wp-twitter-widget.php:97
32
- msgid "System Information:"
33
  msgstr ""
34
 
35
- #: wp-twitter-widget.php:102
36
- msgid "I agree to send anonymous system information"
37
  msgstr ""
38
 
39
- #: wp-twitter-widget.php:104
40
- msgid ""
41
- "You can help by sending anonymous system information that will help Xavisys "
42
- "make better decisions about new features."
43
  msgstr ""
44
 
45
- #: wp-twitter-widget.php:105
46
- msgid ""
47
- "The information will be sent anonymously, but a unique identifier will be "
48
- "sent to prevent duplicate entries from the same installation."
49
  msgstr ""
50
 
51
- #: wp-twitter-widget.php:111
52
- msgid "Update Options &raquo;"
53
  msgstr ""
54
 
55
- #: wp-twitter-widget.php:147
56
- msgid "Could not connect to Twitter"
57
  msgstr ""
58
 
59
- #: wp-twitter-widget.php:311
60
- msgid "Syndicate this content"
61
  msgstr ""
62
 
63
- #: wp-twitter-widget.php:329
64
- msgid "No Tweets Available"
65
  msgstr ""
66
 
67
- #: wp-twitter-widget.php:346
68
- #, php-format
69
- msgid "from %s"
70
  msgstr ""
71
 
72
- #: wp-twitter-widget.php:350
73
- #, php-format
74
- msgid "in reply to %s"
75
  msgstr ""
76
 
77
- #: wp-twitter-widget.php:374
78
- #, php-format
79
- msgid ""
80
- "Powered by <a href=\"%s\" title=\"Get Twitter Widget for your WordPress site"
81
- "\">WordPress Twitter Widget Pro</a>"
82
  msgstr ""
83
 
84
- #: wp-twitter-widget.php:489
85
- msgid "Follow a Twitter Feed"
86
  msgstr ""
87
 
88
- #: wp-twitter-widget.php:534
89
- msgid "Twitter username:"
90
  msgstr ""
91
 
92
- #: wp-twitter-widget.php:538
93
- msgid "Give the feed a title (optional):"
94
  msgstr ""
95
 
96
- #: wp-twitter-widget.php:542
97
- msgid "How many items would you like to display?"
98
  msgstr ""
99
 
100
- #: wp-twitter-widget.php:552
101
- msgid "Hide @replies"
102
  msgstr ""
103
 
104
- #: wp-twitter-widget.php:555
105
- msgid "What to display when Twitter is down (optional):"
106
  msgstr ""
107
 
108
- #: wp-twitter-widget.php:559
109
- msgid "Number of seconds to wait for a response from Twitter (default 2):"
110
  msgstr ""
111
 
112
- #: wp-twitter-widget.php:563
113
- msgid "Show date/time of Tweet (rather than 2 ____ ago):"
114
  msgstr ""
115
 
116
- #: wp-twitter-widget.php:565
117
- msgid "Always"
118
  msgstr ""
119
 
120
- #: wp-twitter-widget.php:566
121
- msgid "If over an hour old"
122
  msgstr ""
123
 
124
- #: wp-twitter-widget.php:567
125
- msgid "If over a day old"
126
  msgstr ""
127
 
128
- #: wp-twitter-widget.php:568
129
- msgid "If over a week old"
130
  msgstr ""
131
 
132
- #: wp-twitter-widget.php:569
133
- msgid "If over a month old"
134
  msgstr ""
135
 
136
- #: wp-twitter-widget.php:570
137
- msgid "If over a year old"
 
138
  msgstr ""
139
 
140
- #: wp-twitter-widget.php:571
141
- msgid "Never"
 
142
  msgstr ""
143
 
144
- #: wp-twitter-widget.php:575
145
- msgid "Hide RSS Icon and Link"
 
 
 
146
  msgstr ""
147
 
148
- #: wp-twitter-widget.php:578
149
- msgid "Show Profile Image"
150
  msgstr ""
151
 
152
- #: wp-twitter-widget.php:582
153
- msgid "Show Link to Twitter Widget Pro"
154
  msgstr ""
155
 
156
- #: wp-twitter-widget.php:623
157
  #, php-format
158
  msgid "about %s year ago"
159
  msgid_plural "about %s years ago"
160
  msgstr[0] ""
161
  msgstr[1] ""
162
 
163
- #: wp-twitter-widget.php:624
164
  #, php-format
165
  msgid "about %s month ago"
166
  msgid_plural "about %s months ago"
167
  msgstr[0] ""
168
  msgstr[1] ""
169
 
170
- #: wp-twitter-widget.php:625
171
  #, php-format
172
  msgid "about %s week ago"
173
  msgid_plural "about %s weeks ago"
174
  msgstr[0] ""
175
  msgstr[1] ""
176
 
177
- #: wp-twitter-widget.php:626
178
  #, php-format
179
  msgid "about %s day ago"
180
  msgid_plural "about %s days ago"
181
  msgstr[0] ""
182
  msgstr[1] ""
183
 
184
- #: wp-twitter-widget.php:627
185
  #, php-format
186
  msgid "about %s hour ago"
187
  msgid_plural "about %s hours ago"
188
  msgstr[0] ""
189
  msgstr[1] ""
190
 
191
- #: wp-twitter-widget.php:628
192
  #, php-format
193
  msgid "about %s minute ago"
194
  msgid_plural "about %s minutes ago"
195
  msgstr[0] ""
196
  msgstr[1] ""
197
 
198
- #: wp-twitter-widget.php:629
199
  #, php-format
200
  msgid "about %s second ago"
201
  msgid_plural "about %s seconds ago"
202
  msgstr[0] ""
203
  msgstr[1] ""
204
 
205
- #: wp-twitter-widget.php:698
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  msgid "Settings"
207
  msgstr ""
7
  msgstr ""
8
  "Project-Id-Version: PACKAGE VERSION\n"
9
  "Report-Msgid-Bugs-To: http://wordpress.org/tag/twitter-widget-pro\n"
10
+ "POT-Creation-Date: 2009-09-27 22:49+0000\n"
11
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
  "Language-Team: LANGUAGE <LL@li.org>\n"
16
  "Content-Transfer-Encoding: 8bit\n"
17
  "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
18
 
19
+ #: wp-twitter-widget.php:39
20
+ msgid "Follow a Twitter Feed"
 
 
 
 
 
 
 
 
21
  msgstr ""
22
 
23
+ #: wp-twitter-widget.php:46 wp-twitter-widget.php:449
24
+ msgid "Twitter Widget Pro"
25
  msgstr ""
26
 
27
+ #: wp-twitter-widget.php:71
28
+ msgid "Twitter username:"
29
  msgstr ""
30
 
31
+ #: wp-twitter-widget.php:72
32
+ msgid "username"
 
 
33
  msgstr ""
34
 
35
+ #: wp-twitter-widget.php:75
36
+ msgid "Give the feed a title (optional):"
 
 
37
  msgstr ""
38
 
39
+ #: wp-twitter-widget.php:76
40
+ msgid "title"
41
  msgstr ""
42
 
43
+ #: wp-twitter-widget.php:79
44
+ msgid "How many items would you like to display?"
45
  msgstr ""
46
 
47
+ #: wp-twitter-widget.php:90
48
+ msgid "Hide @replies"
49
  msgstr ""
50
 
51
+ #: wp-twitter-widget.php:93
52
+ msgid "What to display when Twitter is down (optional):"
53
  msgstr ""
54
 
55
+ #: wp-twitter-widget.php:94
56
+ msgid "errmsg"
 
57
  msgstr ""
58
 
59
+ #: wp-twitter-widget.php:97
60
+ msgid "Number of seconds to wait for a response from Twitter (default 2):"
 
61
  msgstr ""
62
 
63
+ #: wp-twitter-widget.php:98
64
+ msgid "fetchTimeOut"
 
 
 
65
  msgstr ""
66
 
67
+ #: wp-twitter-widget.php:101
68
+ msgid "Show date/time of Tweet (rather than 2 ____ ago):"
69
  msgstr ""
70
 
71
+ #: wp-twitter-widget.php:103
72
+ msgid "Always"
73
  msgstr ""
74
 
75
+ #: wp-twitter-widget.php:104
76
+ msgid "If over an hour old"
77
  msgstr ""
78
 
79
+ #: wp-twitter-widget.php:105
80
+ msgid "If over a day old"
81
  msgstr ""
82
 
83
+ #: wp-twitter-widget.php:106
84
+ msgid "If over a week old"
85
  msgstr ""
86
 
87
+ #: wp-twitter-widget.php:107
88
+ msgid "If over a month old"
89
  msgstr ""
90
 
91
+ #: wp-twitter-widget.php:108
92
+ msgid "If over a year old"
93
  msgstr ""
94
 
95
+ #: wp-twitter-widget.php:109
96
+ msgid "Never"
97
  msgstr ""
98
 
99
+ #: wp-twitter-widget.php:114
100
+ msgid "Hide RSS Icon and Link"
101
  msgstr ""
102
 
103
+ #: wp-twitter-widget.php:118
104
+ msgid "Show Profile Image"
105
  msgstr ""
106
 
107
+ #: wp-twitter-widget.php:122
108
+ msgid "Show Link to Twitter Widget Pro"
109
  msgstr ""
110
 
111
+ #: wp-twitter-widget.php:176
112
+ msgid "Syndicate this content"
113
  msgstr ""
114
 
115
+ #: wp-twitter-widget.php:194
116
+ msgid "No Tweets Available"
117
  msgstr ""
118
 
119
+ #: wp-twitter-widget.php:211
120
+ #, php-format
121
+ msgid "from %s"
122
  msgstr ""
123
 
124
+ #: wp-twitter-widget.php:215
125
+ #, php-format
126
+ msgid "in reply to %s"
127
  msgstr ""
128
 
129
+ #: wp-twitter-widget.php:239
130
+ #, php-format
131
+ msgid ""
132
+ "Powered by <a href=\"%s\" title=\"Get Twitter Widget for your WordPress site"
133
+ "\">WordPress Twitter Widget Pro</a>"
134
  msgstr ""
135
 
136
+ #: wp-twitter-widget.php:295
137
+ msgid "Invalid Twitter Response."
138
  msgstr ""
139
 
140
+ #: wp-twitter-widget.php:309
141
+ msgid "Could not connect to Twitter"
142
  msgstr ""
143
 
144
+ #: wp-twitter-widget.php:372
145
  #, php-format
146
  msgid "about %s year ago"
147
  msgid_plural "about %s years ago"
148
  msgstr[0] ""
149
  msgstr[1] ""
150
 
151
+ #: wp-twitter-widget.php:373
152
  #, php-format
153
  msgid "about %s month ago"
154
  msgid_plural "about %s months ago"
155
  msgstr[0] ""
156
  msgstr[1] ""
157
 
158
+ #: wp-twitter-widget.php:374
159
  #, php-format
160
  msgid "about %s week ago"
161
  msgid_plural "about %s weeks ago"
162
  msgstr[0] ""
163
  msgstr[1] ""
164
 
165
+ #: wp-twitter-widget.php:375
166
  #, php-format
167
  msgid "about %s day ago"
168
  msgid_plural "about %s days ago"
169
  msgstr[0] ""
170
  msgstr[1] ""
171
 
172
+ #: wp-twitter-widget.php:376
173
  #, php-format
174
  msgid "about %s hour ago"
175
  msgid_plural "about %s hours ago"
176
  msgstr[0] ""
177
  msgstr[1] ""
178
 
179
+ #: wp-twitter-widget.php:377
180
  #, php-format
181
  msgid "about %s minute ago"
182
  msgid_plural "about %s minutes ago"
183
  msgstr[0] ""
184
  msgstr[1] ""
185
 
186
+ #: wp-twitter-widget.php:378
187
  #, php-format
188
  msgid "about %s second ago"
189
  msgid_plural "about %s seconds ago"
190
  msgstr[0] ""
191
  msgstr[1] ""
192
 
193
+ #: wp-twitter-widget.php:465
194
+ msgid "Twitter Widget Pro Options"
195
+ msgstr ""
196
+
197
+ #: wp-twitter-widget.php:471
198
+ msgid "Click for Help!"
199
+ msgstr ""
200
+
201
+ #: wp-twitter-widget.php:472
202
+ msgid "System Information:"
203
+ msgstr ""
204
+
205
+ #: wp-twitter-widget.php:477
206
+ msgid "I agree to send anonymous system information"
207
+ msgstr ""
208
+
209
+ #: wp-twitter-widget.php:479
210
+ msgid ""
211
+ "You can help by sending anonymous system information that will help Xavisys "
212
+ "make better decisions about new features."
213
+ msgstr ""
214
+
215
+ #: wp-twitter-widget.php:480
216
+ msgid ""
217
+ "The information will be sent anonymously, but a unique identifier will be "
218
+ "sent to prevent duplicate entries from the same installation."
219
+ msgstr ""
220
+
221
+ #: wp-twitter-widget.php:486
222
+ msgid "Update Options &raquo;"
223
+ msgstr ""
224
+
225
+ #: wp-twitter-widget.php:579
226
  msgid "Settings"
227
  msgstr ""
readme.txt CHANGED
@@ -2,9 +2,9 @@
2
  Contributors: aaroncampbell
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=paypal%40xavisys%2ecom&item_name=Twitter%20Widget%20Pro&no_shipping=0&no_note=1&tax=0&currency_code=USD&lc=US&bn=PP%2dDonationsBF&charset=UTF%2d8
4
  Tags: twitter, widget, feed
5
- Requires at least: 2.7
6
- Tested up to: 2.8.2
7
- Stable tag: 1.5.1
8
 
9
  A widget that properly handles twitter feeds, including parsing @username, #hashtags, and URLs into links. Requires PHP5.
10
 
@@ -46,6 +46,11 @@ Aparently the database queries required to display the friends feed was causing
46
 
47
  == Changelog ==
48
 
 
 
 
 
 
49
  = 1.5.1 =
50
  * Re-enables the caching that got inadvertantly disabled in 1.5.0.
51
 
2
  Contributors: aaroncampbell
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=paypal%40xavisys%2ecom&item_name=Twitter%20Widget%20Pro&no_shipping=0&no_note=1&tax=0&currency_code=USD&lc=US&bn=PP%2dDonationsBF&charset=UTF%2d8
4
  Tags: twitter, widget, feed
5
+ Requires at least: 2.8
6
+ Tested up to: 2.8.4
7
+ Stable tag: 2.0.0
8
 
9
  A widget that properly handles twitter feeds, including parsing @username, #hashtags, and URLs into links. Requires PHP5.
10
 
46
 
47
  == Changelog ==
48
 
49
+ = 2.0.0 =
50
+ * Completely rewitten to use the new widget class introduced in WordPress 2.8
51
+ * Now uses the json class that will be included in WordPress 2.9 for anyone on PHP < 5.2
52
+ * The "Show Link to Twitter Widget Pro" is now off by default to comply with the latest decisions regarding the plugin repository
53
+
54
  = 1.5.1 =
55
  * Re-enables the caching that got inadvertantly disabled in 1.5.0.
56
 
upgrade.html CHANGED
@@ -1,4 +1,7 @@
1
  <p><a href="http://wordpress.org/extend/plugins/twitter-widget-pro/changelog/">Change Log</a></p>
2
  <ul style="list-style: disc inside; padding: 5px 0 0 15px; font-weight: normal;">
3
- <li>Re-enables the caching that got inadvertantly disabled in 1.5.0.</li>
 
 
 
4
  </ul>
1
  <p><a href="http://wordpress.org/extend/plugins/twitter-widget-pro/changelog/">Change Log</a></p>
2
  <ul style="list-style: disc inside; padding: 5px 0 0 15px; font-weight: normal;">
3
+ <li style="color:red; font-weight:bold;">Now requires WordPress 2.8 and still required PHP 5+</li>
4
+ <li>Completely rewitten to use the new widget class introduced in WordPress 2.8</li>
5
+ <li>Now uses the json class that will be included in WordPress 2.9 for anyone on PHP < 5.2</li>
6
+ <li>The "Show Link to Twitter Widget Pro" is now off by default to comply with the latest decisions regarding the plugin repository</li>
7
  </ul>
wp-twitter-widget.php CHANGED
@@ -3,334 +3,186 @@
3
  * Plugin Name: Twitter Widget Pro
4
  * Plugin URI: http://xavisys.com/wordpress-twitter-widget/
5
  * Description: A widget that properly handles twitter feeds, including @username, #hashtag, and link parsing. It can even display profile images for the users. Requires PHP5.
6
- * Version: 1.5.1
7
  * Author: Aaron D. Campbell
8
  * Author URI: http://xavisys.com/
9
  * Text Domain: twitter-widget-pro
10
  */
11
 
12
- /* Copyright 2006 Aaron D. Campbell (email : wp_plugins@xavisys.com)
 
13
 
14
- This program is free software; you can redistribute it and/or modify
15
- it under the terms of the GNU General Public License as published by
16
- the Free Software Foundation; either version 2 of the License, or
17
- (at your option) any later version.
18
 
19
- This program is distributed in the hope that it will be useful,
20
- but WITHOUT ANY WARRANTY; without even the implied warranty of
21
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
- GNU General Public License for more details.
23
 
24
- You should have received a copy of the GNU General Public License
25
- along with this program; if not, write to the Free Software
26
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
  */
28
- /**
29
- * wpTwitterWidget is the class that handles ALL of the plugin functionality.
30
- * It helps us avoid name collisions
31
- * http://codex.wordpress.org/Writing_a_Plugin#Avoiding_Function_Name_Collisions
32
- */
33
 
34
  class wpTwitterWidgetException extends Exception {}
35
 
36
- class wpTwitterWidget
37
- {
38
- /**
39
- * @var array Plugin settings
40
- */
41
- private $_settings;
42
-
43
- /**
44
- * Plugin file to test against on plugins page
45
- *
46
- * @var string
47
- */
48
- private $_pluginBasename;
49
-
50
- /**
51
- * Repository base url
52
- *
53
- * @since 1.4.4
54
- * @var string
55
- */
56
- private $_reposUrl = 'http://plugins.svn.wordpress.org/';
57
-
58
- public function __construct() {
59
- /**
60
- * Add update messages that can be attached to the CURRENT release (not
61
- * this one), but only for 2.8+
62
- */
63
- global $wp_version;
64
- if ( version_compare('2.8', $wp_version, '<=') ) {
65
- add_action ( 'in_plugin_update_message-'.plugin_basename ( __FILE__ ) , array ( $this , '_changelog' ), null, 2 );
66
- }
67
- }
68
 
69
- public function registerSettings() {
70
- register_setting( 'twitter_widget_pro_options', 'twitter_widget_pro' );
71
  }
72
 
73
- public function admin_menu() {
74
- add_options_page(__('Twitter Widget Pro', 'twitter-widget-pro'), __('Twitter Widget Pro', 'twitter-widget-pro'), 'manage_options', 'TwitterWidgetPro', array($this, 'options'));
75
- }
 
 
 
 
 
 
 
 
 
76
 
77
- public function init_locale(){
78
- $lang_dir = basename(dirname(__FILE__)) . '/languages';
79
- load_plugin_textdomain('twitter-widget-pro', 'wp-content/plugins/' . $lang_dir, $lang_dir);
80
  }
81
 
82
- /**
83
- * This is used to display the options page for this plugin
84
- */
85
- public function options() {
86
- //Get our options
87
- $this->_getSettings();
88
  ?>
89
- <div class="wrap">
90
- <h2><?php _e('Twitter Widget Pro Options', 'twitter-widget-pro') ?></h2>
91
- <form action="options.php" method="post" id="wp_twitter_widget_pro">
92
- <?php settings_fields( 'twitter_widget_pro_options' ); ?>
93
- <table class="form-table">
94
- <tr valign="top">
95
- <th scope="row">
96
- <a title="<?php _e('Click for Help!', 'twitter-widget-pro'); ?>" href="#" onclick="jQuery('#twp_user_agreed_to_send_system_information_help').toggle(); return false;">
97
- <?php _e('System Information:', 'twitter-widget-pro') ?>
98
- </a>
99
- </th>
100
- <td>
101
- <input type="hidden" name="twitter_widget_pro[user_agreed_to_send_system_information]" value="false" />
102
- <label for="twp_user_agreed_to_send_system_information"><input type="checkbox" name="twitter_widget_pro[user_agreed_to_send_system_information]" value="true" id="twp_user_agreed_to_send_system_information"<?php checked('true', $this->_settings['user_agreed_to_send_system_information']); ?> /> <?php _e('I agree to send anonymous system information', 'twitter-widget-pro'); ?></label><br />
103
- <small id="twp_user_agreed_to_send_system_information_help" style="display:none;">
104
- <?php _e('You can help by sending anonymous system information that will help Xavisys make better decisions about new features.', 'twitter-widget-pro'); ?><br />
105
- <?php _e('The information will be sent anonymously, but a unique identifier will be sent to prevent duplicate entries from the same installation.', 'twitter-widget-pro'); ?>
106
- </small>
107
- </td>
108
- </tr>
109
- </table>
110
- <p class="submit">
111
- <input type="submit" name="Submit" value="<?php _e('Update Options &raquo;', 'twitter-widget-pro'); ?>" />
112
- </p>
113
- </form>
114
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  <?php
 
116
  }
117
 
118
- public function _changelog ($pluginData, $newPluginData) {
119
- $url = "{$this->_reposUrl}/{$newPluginData->slug}/tags/{$newPluginData->new_version}/upgrade.html";
120
- $response = wp_remote_get ( $url );
121
- $code = (int) wp_remote_retrieve_response_code ( $response );
122
- if ( $code == 200 ) {
123
- echo wp_remote_retrieve_body ( $response );
124
- }
125
- }
126
-
127
- /**
128
- * Pulls the JSON feed from Twitter and returns an array of objects
129
- *
130
- * @param array $widgetOptions - settings needed to get feed url, etc
131
- * @return array
132
- */
133
- private function _parseFeed($widgetOptions) {
134
- $feedUrl = $this->_getFeedUrl($widgetOptions);
135
- $resp = wp_remote_request($feedUrl, array('timeout' => $widgetOptions['fetchTimeOut']));
136
 
137
- if ( !is_wp_error($resp) && $resp['response']['code'] >= 200 && $resp['response']['code'] < 300 ) {
138
- if (function_exists('json_decode')) {
139
- $decodedResponse = json_decode($resp['body']);
140
- } else {
141
- require_once('json_decode.php');
142
- $decodedResponse = Zend_Json_Decoder::decode($resp['body']);
143
- }
144
- if ( empty($decodedResponse) ) {
145
- if (empty($widgetOptions['errmsg'])) {
146
- $widgetOptions['errmsg'] = __('Invalid Twitter Response.', 'twitter-widget-pro');
147
- }
148
- throw new wpTwitterWidgetException($widgetOptions['errmsg']);
149
- } elseif( !empty($decodedResponse->error) ) {
150
- if (empty($widgetOptions['errmsg'])) {
151
- $widgetOptions['errmsg'] = $decodedResponse->error;
152
- }
153
- throw new wpTwitterWidgetException($widgetOptions['errmsg']);
154
- } else {
155
- return $decodedResponse;
156
- }
157
- } else {
158
- // Failed to fetch url;
159
- if (empty($widgetOptions['errmsg'])) {
160
- $widgetOptions['errmsg'] = __('Could not connect to Twitter', 'twitter-widget-pro');
161
- }
162
- throw new wpTwitterWidgetException($widgetOptions['errmsg']);
163
- }
164
- }
165
 
166
- /**
167
- * Gets the URL for the desired feed.
168
- *
169
- * @param array $widgetOptions - settings needed such as username, feet type, etc
170
- * @param string[optional] $type - 'rss' or 'json'
171
- * @param bool[optional] $count - If true, it adds the count parameter to the URL
172
- * @return string - Twitter feed URL
173
- */
174
- private function _getFeedUrl($widgetOptions, $type = 'json', $count = true) {
175
- if (!in_array($type, array('rss', 'json'))) {
176
- $type = 'json';
177
- }
178
- if ( $count ) {
179
- $num = ($widgetOptions['hidereplies'])? 100:$widgetOptions['items'];
180
- $count = sprintf('?count=%u', $num);
181
- } else {
182
- $count = '';
183
  }
184
- return sprintf('http://twitter.com/statuses/user_timeline/%1$s.%2$s%3$s', $widgetOptions['username'], $type, $count);
185
- }
186
 
187
- /**
188
- * Replace @username with a link to that twitter user
189
- *
190
- * @param string $text - Tweet text
191
- * @return string - Tweet text with @replies linked
192
- */
193
- public function linkTwitterUsers($text) {
194
- $text = preg_replace('/(^|\s)@(\w*)/i', '$1<a href="http://twitter.com/$2" class="twitter-user">@$2</a>', $text);
195
- return $text;
196
  }
197
 
198
- /**
199
- * Replace #hashtag with a link to search.twitter.com for that hashtag
200
- *
201
- * @param string $text - Tweet text
202
- * @return string - Tweet text with #hashtags linked
203
- */
204
- public function linkHashtags($text) {
205
- $text = preg_replace_callback('/(^|\s)(#\w*)/i', array($this, '_hashtagLink'), $text);
206
- return $text;
207
  }
208
 
209
- /**
210
- * Replace #hashtag with a link to search.twitter.com for that hashtag
211
- *
212
- * @param array $matches - Tweet text
213
- * @return string - Tweet text with #hashtags linked
214
- */
215
- private function _hashtagLink($matches) {
216
- return "{$matches[1]}<a href='http://search.twitter.com/search?q="
217
- . urlencode($matches[2])
218
- . "' class='twitter-hashtag'>{$matches[2]}</a>";
219
- }
220
-
221
- /**
222
- * Turn URLs into links
223
- *
224
- * @param string $text - Tweet text
225
- * @return string - Tweet text with URLs repalced with links
226
- */
227
- public function linkUrls($text) {
228
- /**
229
- * match protocol://address/path/file.extension?some=variable&another=asf%
230
- * $1 is a possible space, this keeps us from linking href="[link]" etc
231
- * $2 is the whole URL
232
- * $3 is protocol://
233
- * $4 is the URL without the protocol://
234
- * $5 is the URL parameters
235
- */
236
- $text = preg_replace("/(^|\s)(([a-zA-Z]+:\/\/)([a-z][a-z0-9_\..-]*[a-z]{2,6})([a-zA-Z0-9~\/*-?&%]*))/i", "$1<a href=\"$2\">$2</a>", $text);
237
-
238
- /**
239
- * match www.something.domain/path/file.extension?some=variable&another=asf%
240
- * $1 is a possible space, this keeps us from linking href="[link]" etc
241
- * $2 is the whole URL that was matched. The protocol is missing, so we assume http://
242
- * $3 is www.
243
- * $4 is the URL matched without the www.
244
- * $5 is the URL parameters
245
- */
246
- $text = preg_replace("/(^|\s)(www\.([a-z][a-z0-9_\..-]*[a-z]{2,6})([a-zA-Z0-9~\/*-?&%]*))/i", "$1<a href=\"http://$2\">$2</a>", $text);
247
-
248
- return $text;
249
- }
250
-
251
- /**
252
- * Gets tweets, from cache if possible
253
- *
254
- * @param array $widgetOptions - options needed to get feeds
255
- * @return array - Array of objects
256
- */
257
- private function _getTweets($widgetOptions) {
258
- $feedHash = sha1($this->_getFeedUrl($widgetOptions));
259
- $tweets = get_option("wptw-{$feedHash}");
260
- $cacheAge = get_option("wptw-{$feedHash}-time");
261
- //If we don't have cache or it's more than 5 minutes old
262
- if ( empty($tweets) || (time() - $cacheAge) > 300 ) {
263
- try {
264
- $tweets = $this->_parseFeed($widgetOptions);
265
- update_option("wptw-{$feedHash}", $tweets);
266
- update_option("wptw-{$feedHash}-time", time());
267
- } catch (wpTwitterWidgetException $e) {
268
- throw $e;
269
- }
270
- }
271
- return $tweets;
272
- }
273
-
274
- /**
275
- * Displays the Twitter widget, with all tweets in an unordered list.
276
- * Things are classed but not styled to allow easy styling.
277
- *
278
- * @param array $args - Widget Settings
279
- * @param array|int $widget_args - Widget Number
280
- */
281
- public function display($args, $widget_args = 1) {
282
- extract( $args, EXTR_SKIP );
283
- if ( is_numeric($widget_args) )
284
- $widget_args = array( 'number' => $widget_args );
285
- $widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) );
286
- extract( $widget_args, EXTR_SKIP );
287
-
288
- $options = get_option('widget_twitter');
289
- if ( !isset($options[$number]) ) {
290
- return;
291
- }
292
 
293
  // Validate our options
294
- $options[$number]['items'] = (int) $options[$number]['items'];
295
- if ( $options[$number]['items'] < 1 || 20 < $options[$number]['items'] ) {
296
- $options[$number]['items'] = 10;
297
  }
298
- if (!isset($options[$number]['showts'])) {
299
- $options[$number]['showts'] = 86400;
300
  }
301
 
302
- $options[$number]['hiderss'] = (isset($options[$number]['hiderss']) && $options[$number]['hiderss']);
303
- $options[$number]['hidereplies'] = (isset($options[$number]['hidereplies']) && $options[$number]['hidereplies']);
304
- $options[$number]['avatar'] = (isset($options[$number]['avatar']) && $options[$number]['avatar']);
305
- $options[$number]['showXavisysLink'] = (!isset($options[$number]['showXavisysLink']) || $options[$number]['showXavisysLink'] != 'false');
306
-
307
-
308
  try {
309
- $tweets = $this->_getTweets($options[$number]);
310
  } catch (wpTwitterWidgetException $e) {
311
  $tweets = $e;
312
  }
313
 
314
- echo $before_widget . '<div>';
315
 
316
  // If "hide rss" hasn't been checked, show the linked icon
317
- if (!$options[$number]['hiderss']) {
318
  if ( file_exists(dirname(__FILE__) . '/rss.png') ) {
319
  $icon = str_replace(ABSPATH, get_option('siteurl').'/', dirname(__FILE__)) . '/rss.png';
320
  } else {
321
  $icon = get_option('siteurl').'/wp-includes/images/rss.png';
322
  }
323
- $feedUrl = $this->_getFeedUrl($options[$number], 'rss', false);
324
- $before_title .= "<a class='twitterwidget' href='{$feedUrl}' title='" . attribute_escape(__('Syndicate this content', 'twitter-widget-pro')) ."'><img style='background:orange;color:white;border:none;' width='14' height='14' src='{$icon}' alt='RSS' /></a> ";
325
  }
326
- $twitterLink = 'http://twitter.com/' . $options[$number]['username'];
327
- $before_title .= "<a class='twitterwidget' href='{$twitterLink}' title='" . attribute_escape("Twitter: {$options[$number]['username']}") . "'>";
328
- $after_title = '</a>' . $after_title;
329
- if (empty($options[$number]['title'])) {
330
- $options[$number]['title'] = "Twitter: {$options[$number]['username']}";
331
  }
332
- echo $before_title . $options[$number]['title'] . $after_title;
333
- if (!is_a($tweets, 'wpTwitterWidgetException') && !empty($tweets[0]) && $options[$number]['avatar']) {
334
  echo '<div class="twitter-avatar">';
335
  echo $this->_getProfileImage($tweets[0]->user);
336
  echo '</div>';
@@ -343,9 +195,9 @@ class wpTwitterWidget
343
  } else {
344
  $count = 0;
345
  foreach ($tweets as $tweet) {
346
- if (!$options[$number]['hidereplies'] || empty($tweet->in_reply_to_user_id)) {
347
  // Set our "ago" string which converts the date to "# ___(s) ago"
348
- $tweet->ago = $this->_timeSince(strtotime($tweet->created_at), $options[$number]['showts']);
349
  ?>
350
  <li>
351
  <span class="entry-content"><?php echo apply_filters( 'widget_twitter_content', $tweet->text ); ?></span>
@@ -373,14 +225,14 @@ replyTo;
373
  </span>
374
  </li>
375
  <?php
376
- if (++$count >= $options[$number]['items']) {
377
  break;
378
  }
379
  }
380
  }
381
  }
382
 
383
- if ($options[$number]['showXavisysLink']) {
384
  ?>
385
  <li class="xavisys-link">
386
  <span class="xavisys-link-text">
@@ -389,212 +241,96 @@ replyTo;
389
  </li>
390
  <?php
391
  }
392
- echo '</ul></div>' . $after_widget;
393
  }
394
 
395
  /**
396
- * Returns the Twitter user's profile image, linked to that user's profile
397
  *
398
- * @param object $user - Twitter User
399
- * @return string - Linked image (XHTML)
400
  */
401
- private function _getProfileImage($user) {
402
- return <<<profileImage
403
- <a title="{$user->name}" href="http://twitter.com/{$user->screen_name}">
404
- <img alt="{$user->name}" src="{$user->profile_image_url}" />
405
- </a>
406
- profileImage;
 
 
 
 
 
 
 
 
 
407
  }
408
 
409
  /**
410
- * Returns the user's screen name as a link inside strong tags.
411
  *
412
- * @param object $user - Twitter user
413
- * @return string - Username as link (XHTML)
414
  */
415
- private function _getUserName($user) {
416
- return <<<profileImage
417
- <strong>
418
- <a title="{$user->name}" href="http://twitter.com/{$user->screen_name}">{$user->screen_name}</a>
419
- </strong>
420
- profileImage;
421
- }
422
 
423
- /**
424
- * Sets up admin forms to manage widgets
425
- *
426
- * @param array|int $widget_args - Widget Number
427
- */
428
- public function control($widget_args) {
429
- global $wp_registered_widgets;
430
- static $updated = false;
431
-
432
- if ( is_numeric($widget_args) )
433
- $widget_args = array( 'number' => $widget_args );
434
- $widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) );
435
- extract( $widget_args, EXTR_SKIP );
436
-
437
- $options = get_option('widget_twitter');
438
-
439
- if ( !is_array($options) )
440
- $options = array();
441
-
442
- if ( !$updated && !empty($_POST['sidebar']) ) {
443
- $sidebar = (string) $_POST['sidebar'];
444
-
445
- $sidebars_widgets = wp_get_sidebars_widgets();
446
- if ( isset($sidebars_widgets[$sidebar]) )
447
- $this_sidebar =& $sidebars_widgets[$sidebar];
448
- else
449
- $this_sidebar = array();
450
-
451
- foreach ( $this_sidebar as $_widget_id ) {
452
- if ( array($this,'display') == $wp_registered_widgets[$_widget_id]['callback'] && isset($wp_registered_widgets[$_widget_id]['params'][0]['number']) ) {
453
- $widget_number = $wp_registered_widgets[$_widget_id]['params'][0]['number'];
454
- if ( !in_array( "twitter-$widget_number", $_POST['widget-id'] ) ) // the widget has been removed.
455
- unset($options[$widget_number]);
456
- }
457
- }
458
 
459
- foreach ( (array) $_POST['widget-twitter'] as $widget_number => $widget_twitter ) {
460
- if ( !isset($widget_twitter['username']) && isset($options[$widget_number]) ) // user clicked cancel
461
- continue;
 
462
 
463
- $widget_twitter['title'] = stripslashes($widget_twitter['title']);
464
- $widget_twitter['errmsg'] = stripslashes($widget_twitter['errmsg']);
465
- if ( !current_user_can('unfiltered_html') ) {
466
- $widget_twitter['title'] = strip_tags($widget_twitter['title']);
467
- $widget_twitter['errmsg'] = strip_tags($widget_twitter['errmsg']);
468
  }
469
- $options[$widget_number] = $widget_twitter;
 
 
 
 
 
 
 
 
 
 
 
 
470
  }
471
-
472
- update_option('widget_twitter', $options);
473
- $updated = true;
474
- }
475
-
476
- if ( -1 != $number ) {
477
- $options[$number]['number'] = $number;
478
- $options[$number]['title'] = attribute_escape($options[$number]['title']);
479
- $options[$number]['errmsg'] = attribute_escape($options[$number]['errmsg']);
480
- $options[$number]['fetchTimeOut'] = attribute_escape($options[$number]['fetchTimeOut']);
481
- $options[$number]['username'] = attribute_escape($options[$number]['username']);
482
- $options[$number]['hiderss'] = (bool) $options[$number]['hiderss'];
483
- $options[$number]['hidereplies'] = (bool) $options[$number]['hidereplies'];
484
- $options[$number]['avatar'] = (bool) $options[$number]['avatar'];
485
- $options[$number]['showXavisysLink'] = (!isset($options[$number]['showXavisysLink']) || $options[$number]['showXavisysLink'] != 'false');
486
- }
487
- // Fix Undefined offset Notice
488
- if ( empty($options[$number]) ) {
489
- $options[$number] = array();
490
  }
491
- $this->_showForm($options[$number]);
492
  }
493
 
494
  /**
495
- * Registers widget in such a way as to allow multiple instances of it
496
  *
497
- * @see wp-includes/widgets.php
 
 
 
498
  */
499
- public function register() {
500
- if ( !$options = get_option('widget_twitter') )
501
- $options = array();
502
- $widget_ops = array('classname' => 'widget_twitter', 'description' => __('Follow a Twitter Feed', 'twitter-widget-pro'));
503
- $control_ops = array('width' => 400, 'height' => 350, 'id_base' => 'twitter');
504
- $name = __('Twitter Widget Pro', 'twitter-widget-pro');
505
-
506
- $id = false;
507
- foreach ( array_keys($options) as $o ) {
508
- // Old widgets can have null values for some reason
509
- if ( !isset($options[$o]['title']) || !isset($options[$o]['username']) )
510
- continue;
511
- $id = "twitter-$o"; // Never never never translate an id
512
- wp_register_sidebar_widget($id, $name, array($this,'display'), $widget_ops, array( 'number' => $o ));
513
- wp_register_widget_control($id, $name, array($this,'control'), $control_ops, array( 'number' => $o ));
514
  }
515
-
516
- // If there are none, we register the widget's existance with a generic template
517
- if ( !$id ) {
518
- wp_register_sidebar_widget( 'twitter-1', $name, array($this,'display'), $widget_ops, array( 'number' => -1 ) );
519
- wp_register_widget_control( 'twitter-1', $name, array($this,'control'), $control_ops, array( 'number' => -1 ) );
520
  }
521
- }
522
-
523
- /**
524
- * Displays the actualy for that populates the widget options box in the
525
- * admin section
526
- *
527
- * @param array $args - Current widget settings and widget number, gets combind with defaults
528
- */
529
- private function _showForm($args) {
530
-
531
- $defaultArgs = array( 'title' => '',
532
- 'errmsg' => '',
533
- 'fetchTimeOut' => '2',
534
- 'username' => '',
535
- 'hiderss' => false,
536
- 'hidereplies' => false,
537
- 'avatar' => false,
538
- 'showXavisysLink' => true,
539
- 'items' => 10,
540
- 'showts' => 60 * 60 * 24,
541
- 'number' => '%i%' );
542
-
543
- $args = wp_parse_args( $args, $defaultArgs );
544
- extract( $args );
545
- ?>
546
- <p>
547
- <label for="twitter-username-<?php echo $number; ?>"><?php _e('Twitter username:', 'twitter-widget-pro'); ?></label>
548
- <input class="widefat" id="twitter-username-<?php echo $number; ?>" name="widget-twitter[<?php echo $number; ?>][username]" type="text" value="<?php echo $username; ?>" />
549
- </p>
550
- <p>
551
- <label for="twitter-title-<?php echo $number; ?>"><?php _e('Give the feed a title (optional):', 'twitter-widget-pro'); ?></label>
552
- <input class="widefat" id="twitter-title-<?php echo $number; ?>" name="widget-twitter[<?php echo $number; ?>][title]" type="text" value="<?php echo $title; ?>" />
553
- </p>
554
- <p>
555
- <label for="twitter-items-<?php echo $number; ?>"><?php _e('How many items would you like to display?', 'twitter-widget-pro'); ?></label>
556
- <select id="twitter-items-<?php echo $number; ?>" name="widget-twitter[<?php echo $number; ?>][items]">
557
- <?php
558
- for ( $i = 1; $i <= 20; ++$i ) {
559
- echo "<option value='$i' ", selected($items, $i), ">$i</option>";
560
- }
561
- ?>
562
- </select>
563
- </p>
564
- <p>
565
- <label for="twitter-hidereplies-<?php echo $number; ?>"><input class="checkbox" type="checkbox" id="twitter-hidereplies-<?php echo $number; ?>" name="widget-twitter[<?php echo $number; ?>][hidereplies]"<?php checked($hidereplies, true); ?> /> <?php _e('Hide @replies', 'twitter-widget-pro'); ?></label>
566
- </p>
567
- <p>
568
- <label for="twitter-errmsg-<?php echo $number; ?>"><?php _e('What to display when Twitter is down (optional):', 'twitter-widget-pro'); ?></label>
569
- <input class="widefat" id="twitter-errmsg-<?php echo $number; ?>" name="widget-twitter[<?php echo $number; ?>][errmsg]" type="text" value="<?php echo $errmsg; ?>" />
570
- </p>
571
- <p>
572
- <label for="twitter-fetchTimeOut-<?php echo $number; ?>"><?php _e('Number of seconds to wait for a response from Twitter (default 2):', 'twitter-widget-pro'); ?></label>
573
- <input class="widefat" id="twitter-fetchTimeOut-<?php echo $number; ?>" name="widget-twitter[<?php echo $number; ?>][fetchTimeOut]" type="text" value="<?php echo $fetchTimeOut; ?>" />
574
- </p>
575
- <p>
576
- <label for="twitter-showts-<?php echo $number; ?>"><?php _e('Show date/time of Tweet (rather than 2 ____ ago):', 'twitter-widget-pro'); ?></label>
577
- <select id="twitter-showts-<?php echo $number; ?>" name="widget-twitter[<?php echo $number; ?>][showts]">
578
- <option value="0" <?php echo selected($showts, '0'); ?>><?php _e('Always', 'twitter-widget-pro');?></a>
579
- <option value="3600" <?php echo selected($showts, '3600'); ?>><?php _e('If over an hour old', 'twitter-widget-pro');?></a>
580
- <option value="86400" <?php echo selected($showts, '86400'); ?>><?php _e('If over a day old', 'twitter-widget-pro');?></a>
581
- <option value="604800" <?php echo selected($showts, '604800'); ?>><?php _e('If over a week old', 'twitter-widget-pro');?></a>
582
- <option value="2592000" <?php echo selected($showts, '2592000'); ?>><?php _e('If over a month old', 'twitter-widget-pro');?></a>
583
- <option value="31536000" <?php echo selected($showts, '31536000'); ?>><?php _e('If over a year old', 'twitter-widget-pro');?></a>
584
- <option value="-1" <?php echo selected($showts, '-1'); ?>><?php _e('Never', 'twitter-widget-pro');?></a>
585
- </select>
586
- </p>
587
- <p>
588
- <label for="twitter-hiderss-<?php echo $number; ?>"><input class="checkbox" type="checkbox" id="twitter-hiderss-<?php echo $number; ?>" name="widget-twitter[<?php echo $number; ?>][hiderss]"<?php checked($hiderss, true); ?> /> <?php _e('Hide RSS Icon and Link', 'twitter-widget-pro'); ?></label>
589
- </p>
590
- <p>
591
- <label for="twitter-avatar-<?php echo $number; ?>"><input class="checkbox" type="checkbox" id="twitter-avatar-<?php echo $number; ?>" name="widget-twitter[<?php echo $number; ?>][avatar]"<?php checked($avatar, true); ?> /> <?php _e('Show Profile Image', 'twitter-widget-pro'); ?></label>
592
- </p>
593
- <p>
594
- <input type="hidden" name="widget-twitter[<?php echo $number; ?>][showXavisysLink]" value="false" />
595
- <label for="twitter-showXavisysLink-<?php echo $number; ?>"><input class="checkbox" type="checkbox" value="true" id="twitter-showXavisysLink-<?php echo $number; ?>" name="widget-twitter[<?php echo $number; ?>][showXavisysLink]"<?php checked($showXavisysLink, true); ?> /> <?php _e('Show Link to Twitter Widget Pro', 'twitter-widget-pro'); ?></label>
596
- </p>
597
- <?php
598
  }
599
 
600
  /**
@@ -608,8 +344,8 @@ profileImage;
608
  * @return string
609
  */
610
  private function _timeSince($startTimestamp, $max) {
611
- // array of time period chunks
612
- $chunks = array(
613
  'year' => 60 * 60 * 24 * 365, // 31,536,000 seconds
614
  'month' => 60 * 60 * 24 * 30, // 2,592,000 seconds
615
  'week' => 60 * 60 * 24 * 7, // 604,800 seconds
@@ -617,22 +353,22 @@ profileImage;
617
  'hour' => 60 * 60, // 3600 seconds
618
  'minute' => 60, // 60 seconds
619
  'second' => 1 // 1 second
620
- );
621
 
622
- $since = time() - $startTimestamp;
623
 
624
- if ($max != '-1' && $since >= $max) {
625
  return date_i18n('h:i:s A F d, Y', $startTimestamp);
626
- }
627
 
628
  foreach ( $chunks as $key => $seconds ) {
629
- // finding the biggest chunk (if the chunk fits, break)
630
- if (($count = floor($since / $seconds)) != 0) {
631
- break;
632
- }
633
  }
634
 
635
- $messages = array(
636
  'year' => _n('about %s year ago', 'about %s years ago', $count, 'twitter-widget-pro'),
637
  'month' => _n('about %s month ago', 'about %s months ago', $count, 'twitter-widget-pro'),
638
  'week' => _n('about %s week ago', 'about %s weeks ago', $count, 'twitter-widget-pro'),
@@ -640,9 +376,141 @@ profileImage;
640
  'hour' => _n('about %s hour ago', 'about %s hours ago', $count, 'twitter-widget-pro'),
641
  'minute' => _n('about %s minute ago', 'about %s minutes ago', $count, 'twitter-widget-pro'),
642
  'second' => _n('about %s second ago', 'about %s seconds ago', $count, 'twitter-widget-pro'),
643
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
644
 
645
- return sprintf($messages[$key], $count);
 
 
 
 
 
 
 
 
 
 
 
646
  }
647
 
648
  /**
@@ -653,7 +521,7 @@ profileImage;
653
  $this->_getSettings();
654
  if ($this->_settings['user_agreed_to_send_system_information'] == 'true') {
655
  $lastSent = get_option('twp-sysinfo');
656
- $sysinfo = $this->_get_sysinfo();
657
  if (serialize($lastSent) != serialize($sysinfo)) {
658
  $params = array(
659
  'method' => 'POST',
@@ -713,20 +581,74 @@ profileImage;
713
  }
714
  return $links;
715
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
716
  }
717
  // Instantiate our class
718
  $wpTwitterWidget = new wpTwitterWidget();
719
-
720
- /**
721
- * Add filters and actions
722
- */
723
- add_action( 'admin_menu', array($wpTwitterWidget,'admin_menu') );
724
- add_filter( 'init', array( $wpTwitterWidget, 'init_locale') );
725
- add_filter( 'admin_init', array( $wpTwitterWidget, 'registerSettings') );
726
- add_filter( 'admin_init', array( $wpTwitterWidget, 'sendSysInfo') );
727
- add_action( 'widgets_init', array($wpTwitterWidget, 'register') );
728
- add_filter( 'widget_twitter_content', array($wpTwitterWidget, 'linkTwitterUsers') );
729
- add_filter( 'widget_twitter_content', array($wpTwitterWidget, 'linkUrls') );
730
- add_filter( 'widget_twitter_content', array($wpTwitterWidget, 'linkHashtags') );
731
- add_filter( 'widget_twitter_content', 'convert_chars' );
732
- add_filter( 'plugin_action_links', array($wpTwitterWidget, 'addSettingLink'), 10, 2 );
3
  * Plugin Name: Twitter Widget Pro
4
  * Plugin URI: http://xavisys.com/wordpress-twitter-widget/
5
  * Description: A widget that properly handles twitter feeds, including @username, #hashtag, and link parsing. It can even display profile images for the users. Requires PHP5.
6
+ * Version: 2.0.0
7
  * Author: Aaron D. Campbell
8
  * Author URI: http://xavisys.com/
9
  * Text Domain: twitter-widget-pro
10
  */
11
 
12
+ /*
13
+ Copyright 2006-current Aaron D. Campbell (email : wp_plugins@xavisys.com)
14
 
15
+ This program is free software; you can redistribute it and/or modify
16
+ it under the terms of the GNU General Public License as published by
17
+ the Free Software Foundation; either version 2 of the License, or
18
+ (at your option) any later version.
19
 
20
+ This program is distributed in the hope that it will be useful,
21
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
22
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
+ GNU General Public License for more details.
24
 
25
+ You should have received a copy of the GNU General Public License
26
+ along with this program; if not, write to the Free Software
27
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28
  */
 
 
 
 
 
29
 
30
  class wpTwitterWidgetException extends Exception {}
31
 
32
+ /**
33
+ * WP_Widget_Twitter_Pro is the class that handles the main widget.
34
+ */
35
+ class WP_Widget_Twitter_Pro extends WP_Widget {
36
+ public function WP_Widget_Twitter_Pro () {
37
+ $widget_ops = array(
38
+ 'classname' => 'widget_twitter',
39
+ 'description' => __( 'Follow a Twitter Feed', 'twitter-widget-pro' )
40
+ );
41
+ $control_ops = array(
42
+ 'width' => 400,
43
+ 'height' => 350,
44
+ 'id_base' => 'twitter'
45
+ );
46
+ $name = __( 'Twitter Widget Pro', 'twitter-widget-pro' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
+ $this->WP_Widget('twitter', $name, $widget_ops, $control_ops);
 
49
  }
50
 
51
+ private function _getInstanceSettings ( $instance ) {
52
+ $defaultArgs = array( 'title' => '',
53
+ 'errmsg' => '',
54
+ 'fetchTimeOut' => '2',
55
+ 'username' => '',
56
+ 'hiderss' => false,
57
+ 'hidereplies' => false,
58
+ 'avatar' => false,
59
+ 'showXavisysLink' => false,
60
+ 'items' => 10,
61
+ 'showts' => 60 * 60 * 24,
62
+ );
63
 
64
+ return wp_parse_args( $instance, $defaultArgs );
 
 
65
  }
66
 
67
+ public function form( $instance ) {
68
+ $instance = $this->_getInstanceSettings( $instance );
 
 
 
 
69
  ?>
70
+ <p>
71
+ <label for="<?php echo $this->get_field_id('username'); ?>"><?php _e('Twitter username:', 'twitter-widget-pro'); ?></label>
72
+ <input class="widefat" id="<?php echo $this->get_field_id('username'); ?>" name="<?php echo $this->get_field_name('username'); ?>" type="text" value="<?php esc_attr_e($instance['username']); ?>" />
73
+ </p>
74
+ <p>
75
+ <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Give the feed a title (optional):', 'twitter-widget-pro'); ?></label>
76
+ <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php esc_attr_e($instance['title']); ?>" />
77
+ </p>
78
+ <p>
79
+ <label for="<?php echo $this->get_field_id('items'); ?>"><?php _e('How many items would you like to display?', 'twitter-widget-pro'); ?></label>
80
+ <select id="<?php echo $this->get_field_id('items'); ?>" name="<?php echo $this->get_field_name('items'); ?>">
81
+ <?php
82
+ for ( $i = 1; $i <= 20; ++$i ) {
83
+ echo "<option value='$i' ". selected($instance['items'], $i, false). ">$i</option>";
84
+ }
85
+ ?>
86
+ </select>
87
+ </p>
88
+ <p>
89
+ <input class="checkbox" type="checkbox" value="true" id="<?php echo $this->get_field_id('hidereplies'); ?>" name="<?php echo $this->get_field_name('hidereplies'); ?>"<?php checked($instance['hidereplies'], 'true'); ?> />
90
+ <label for="<?php echo $this->get_field_id('hidereplies'); ?>"><?php _e('Hide @replies', 'twitter-widget-pro'); ?></label>
91
+ </p>
92
+ <p>
93
+ <label for="<?php echo $this->get_field_id('errmsg'); ?>"><?php _e('What to display when Twitter is down (optional):', 'twitter-widget-pro'); ?></label>
94
+ <input class="widefat" id="<?php echo $this->get_field_id('errmsg'); ?>" name="<?php echo $this->get_field_name('errmsg'); ?>" type="text" value="<?php esc_attr_e($instance['errmsg']); ?>" />
95
+ </p>
96
+ <p>
97
+ <label for="<?php echo $this->get_field_id('fetchTimeOut'); ?>"><?php _e('Number of seconds to wait for a response from Twitter (default 2):', 'twitter-widget-pro'); ?></label>
98
+ <input class="widefat" id="<?php echo $this->get_field_id('fetchTimeOut'); ?>" name="<?php echo $this->get_field_name('fetchTimeOut'); ?>" type="text" value="<?php esc_attr_e($instance['fetchTimeOut']); ?>" />
99
+ </p>
100
+ <p>
101
+ <label for="<?php echo $this->get_field_id('showts'); ?>"><?php _e('Show date/time of Tweet (rather than 2 ____ ago):', 'twitter-widget-pro'); ?></label>
102
+ <select id="<?php echo $this->get_field_id('showts'); ?>" name="<?php echo $this->get_field_name('showts'); ?>">
103
+ <option value="0" <?php selected($instance['showts'], '0'); ?>><?php _e('Always', 'twitter-widget-pro');?></option>
104
+ <option value="3600" <?php selected($instance['showts'], '3600'); ?>><?php _e('If over an hour old', 'twitter-widget-pro');?></option>
105
+ <option value="86400" <?php selected($instance['showts'], '86400'); ?>><?php _e('If over a day old', 'twitter-widget-pro');?></option>
106
+ <option value="604800" <?php selected($instance['showts'], '604800'); ?>><?php _e('If over a week old', 'twitter-widget-pro');?></option>
107
+ <option value="2592000" <?php selected($instance['showts'], '2592000'); ?>><?php _e('If over a month old', 'twitter-widget-pro');?></option>
108
+ <option value="31536000" <?php selected($instance['showts'], '31536000'); ?>><?php _e('If over a year old', 'twitter-widget-pro');?></option>
109
+ <option value="-1" <?php selected($instance['showts'], '-1'); ?>><?php _e('Never', 'twitter-widget-pro');?></option>
110
+ </select>
111
+ </p>
112
+ <p>
113
+ <input class="checkbox" type="checkbox" value="true" id="<?php echo $this->get_field_id('hiderss'); ?>" name="<?php echo $this->get_field_name('hiderss'); ?>"<?php checked($instance['hiderss'], 'true'); ?> />
114
+ <label for="<?php echo $this->get_field_id('hiderss'); ?>"><?php _e('Hide RSS Icon and Link', 'twitter-widget-pro'); ?></label>
115
+ </p>
116
+ <p>
117
+ <input class="checkbox" type="checkbox" value="true" id="<?php echo $this->get_field_id('avatar'); ?>" name="<?php echo $this->get_field_name('avatar'); ?>"<?php checked($instance['avatar'], 'true'); ?> />
118
+ <label for="<?php echo $this->get_field_id('avatar'); ?>"><?php _e('Show Profile Image', 'twitter-widget-pro'); ?></label>
119
+ </p>
120
+ <p>
121
+ <input class="checkbox" type="checkbox" value="true" id="<?php echo $this->get_field_id('showXavisysLink'); ?>" name="<?php echo $this->get_field_name('showXavisysLink'); ?>"<?php checked($instance['showXavisysLink'], 'true'); ?> />
122
+ <label for="<?php echo $this->get_field_id('showXavisysLink'); ?>"><?php _e('Show Link to Twitter Widget Pro', 'twitter-widget-pro'); ?></label>
123
+ </p>
124
  <?php
125
+ return;
126
  }
127
 
128
+ public function update( $new_instance, $old_instance ) {
129
+ $instance = $this->_getInstanceSettings( $new_instance );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
+ // Clean up the free-form areas
132
+ $instance['title'] = stripslashes($new_instance['title']);
133
+ $instance['errmsg'] = stripslashes($new_instance['errmsg']);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
+ // If the current user isn't allowed to use unfiltered HTML, filter it
136
+ if ( !current_user_can('unfiltered_html') ) {
137
+ $instance['title'] = strip_tags($new_instance['title']);
138
+ $instance['errmsg'] = strip_tags($new_instance['errmsg']);
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  }
 
 
140
 
141
+ return $instance;
 
 
 
 
 
 
 
 
142
  }
143
 
144
+ public function flush_widget_cache() {
145
+ wp_cache_delete('widget_twitter_widget_pro', 'widget');
 
 
 
 
 
 
 
146
  }
147
 
148
+ public function widget( $args, $instance ) {
149
+ $instance = $this->_getInstanceSettings( $instance );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
  // Validate our options
152
+ $instance['items'] = (int) $instance['items'];
153
+ if ( $instance['items'] < 1 || 20 < $instance['items'] ) {
154
+ $instance['items'] = 10;
155
  }
156
+ if (!isset($instance['showts'])) {
157
+ $instance['showts'] = 86400;
158
  }
159
 
 
 
 
 
 
 
160
  try {
161
+ $tweets = $this->_getTweets($instance);
162
  } catch (wpTwitterWidgetException $e) {
163
  $tweets = $e;
164
  }
165
 
166
+ echo $args['before_widget'] . '<div>';
167
 
168
  // If "hide rss" hasn't been checked, show the linked icon
169
+ if ( $instance['hiderss'] != 'true' ) {
170
  if ( file_exists(dirname(__FILE__) . '/rss.png') ) {
171
  $icon = str_replace(ABSPATH, get_option('siteurl').'/', dirname(__FILE__)) . '/rss.png';
172
  } else {
173
  $icon = get_option('siteurl').'/wp-includes/images/rss.png';
174
  }
175
+ $feedUrl = $this->_getFeedUrl($instance, 'rss', false);
176
+ $args['before_title'] .= "<a class='twitterwidget' href='{$feedUrl}' title='" . attribute_escape(__('Syndicate this content', 'twitter-widget-pro')) ."'><img style='background:orange;color:white;border:none;' width='14' height='14' src='{$icon}' alt='RSS' /></a> ";
177
  }
178
+ $twitterLink = 'http://twitter.com/' . $instance['username'];
179
+ $args['before_title'] .= "<a class='twitterwidget' href='{$twitterLink}' title='" . attribute_escape("Twitter: {$instance['username']}") . "'>";
180
+ $args['after_title'] = '</a>' . $args['after_title'];
181
+ if (empty($instance['title'])) {
182
+ $instance['title'] = "Twitter: {$instance['username']}";
183
  }
184
+ echo $args['before_title'] . $instance['title'] . $args['after_title'];
185
+ if (!is_a($tweets, 'wpTwitterWidgetException') && !empty($tweets[0]) && $instance['avatar'] == 'true') {
186
  echo '<div class="twitter-avatar">';
187
  echo $this->_getProfileImage($tweets[0]->user);
188
  echo '</div>';
195
  } else {
196
  $count = 0;
197
  foreach ($tweets as $tweet) {
198
+ if ( $instance['hidereplies'] != 'true' || empty($tweet->in_reply_to_user_id)) {
199
  // Set our "ago" string which converts the date to "# ___(s) ago"
200
+ $tweet->ago = $this->_timeSince(strtotime($tweet->created_at), $instance['showts']);
201
  ?>
202
  <li>
203
  <span class="entry-content"><?php echo apply_filters( 'widget_twitter_content', $tweet->text ); ?></span>
225
  </span>
226
  </li>
227
  <?php
228
+ if (++$count >= $instance['items']) {
229
  break;
230
  }
231
  }
232
  }
233
  }
234
 
235
+ if ( $instance['showXavisysLink'] == 'true' ) {
236
  ?>
237
  <li class="xavisys-link">
238
  <span class="xavisys-link-text">
241
  </li>
242
  <?php
243
  }
244
+ echo '</ul></div>' . $args['after_widget'];
245
  }
246
 
247
  /**
248
+ * Gets tweets, from cache if possible
249
  *
250
+ * @param array $widgetOptions - options needed to get feeds
251
+ * @return array - Array of objects
252
  */
253
+ private function _getTweets($widgetOptions) {
254
+ $feedHash = sha1($this->_getFeedUrl($widgetOptions));
255
+ $tweets = get_option("wptw-{$feedHash}");
256
+ $cacheAge = get_option("wptw-{$feedHash}-time");
257
+ //If we don't have cache or it's more than 5 minutes old
258
+ if ( empty($tweets) || (time() - $cacheAge) > 300 ) {
259
+ try {
260
+ $tweets = $this->_parseFeed($widgetOptions);
261
+ update_option("wptw-{$feedHash}", $tweets);
262
+ update_option("wptw-{$feedHash}-time", time());
263
+ } catch (wpTwitterWidgetException $e) {
264
+ throw $e;
265
+ }
266
+ }
267
+ return $tweets;
268
  }
269
 
270
  /**
271
+ * Pulls the JSON feed from Twitter and returns an array of objects
272
  *
273
+ * @param array $widgetOptions - settings needed to get feed url, etc
274
+ * @return array
275
  */
276
+ private function _parseFeed($widgetOptions) {
277
+ $feedUrl = $this->_getFeedUrl($widgetOptions);
278
+ $resp = wp_remote_request($feedUrl, array('timeout' => $widgetOptions['fetchTimeOut']));
 
 
 
 
279
 
280
+ if ( !is_wp_error($resp) && $resp['response']['code'] >= 200 && $resp['response']['code'] < 300 ) {
281
+ if (function_exists('json_decode')) {
282
+ $decodedResponse = json_decode( $resp['body'] );
283
+ } else {
284
+ global $wp_json;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
 
286
+ if ( !is_a($wp_json, 'Services_JSON') ) {
287
+ require_once( 'class-json.php' );
288
+ $wp_json = new Services_JSON();
289
+ }
290
 
291
+ $decodedResponse = $wp_json->decode( $resp['body'] );
292
+ }
293
+ if ( empty($decodedResponse) ) {
294
+ if (empty($widgetOptions['errmsg'])) {
295
+ $widgetOptions['errmsg'] = __('Invalid Twitter Response.', 'twitter-widget-pro');
296
  }
297
+ throw new wpTwitterWidgetException($widgetOptions['errmsg']);
298
+ } elseif( !empty($decodedResponse->error) ) {
299
+ if (empty($widgetOptions['errmsg'])) {
300
+ $widgetOptions['errmsg'] = $decodedResponse->error;
301
+ }
302
+ throw new wpTwitterWidgetException($widgetOptions['errmsg']);
303
+ } else {
304
+ return $decodedResponse;
305
+ }
306
+ } else {
307
+ // Failed to fetch url;
308
+ if (empty($widgetOptions['errmsg'])) {
309
+ $widgetOptions['errmsg'] = __('Could not connect to Twitter', 'twitter-widget-pro');
310
  }
311
+ throw new wpTwitterWidgetException($widgetOptions['errmsg']);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  }
 
313
  }
314
 
315
  /**
316
+ * Gets the URL for the desired feed.
317
  *
318
+ * @param array $widgetOptions - settings needed such as username, feet type, etc
319
+ * @param string[optional] $type - 'rss' or 'json'
320
+ * @param bool[optional] $count - If true, it adds the count parameter to the URL
321
+ * @return string - Twitter feed URL
322
  */
323
+ private function _getFeedUrl($widgetOptions, $type = 'json', $count = true) {
324
+ if (!in_array($type, array('rss', 'json'))) {
325
+ $type = 'json';
 
 
 
 
 
 
 
 
 
 
 
 
326
  }
327
+ if ( $count ) {
328
+ $num = ($widgetOptions['hidereplies'])? 100:$widgetOptions['items'];
329
+ $count = sprintf('?count=%u', $num);
330
+ } else {
331
+ $count = '';
332
  }
333
+ return sprintf('http://twitter.com/statuses/user_timeline/%1$s.%2$s%3$s', $widgetOptions['username'], $type, $count);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  }
335
 
336
  /**
344
  * @return string
345
  */
346
  private function _timeSince($startTimestamp, $max) {
347
+ // array of time period chunks
348
+ $chunks = array(
349
  'year' => 60 * 60 * 24 * 365, // 31,536,000 seconds
350
  'month' => 60 * 60 * 24 * 30, // 2,592,000 seconds
351
  'week' => 60 * 60 * 24 * 7, // 604,800 seconds
353
  'hour' => 60 * 60, // 3600 seconds
354
  'minute' => 60, // 60 seconds
355
  'second' => 1 // 1 second
356
+ );
357
 
358
+ $since = time() - $startTimestamp;
359
 
360
+ if ($max != '-1' && $since >= $max) {
361
  return date_i18n('h:i:s A F d, Y', $startTimestamp);
362
+ }
363
 
364
  foreach ( $chunks as $key => $seconds ) {
365
+ // finding the biggest chunk (if the chunk fits, break)
366
+ if (($count = floor($since / $seconds)) != 0) {
367
+ break;
368
+ }
369
  }
370
 
371
+ $messages = array(
372
  'year' => _n('about %s year ago', 'about %s years ago', $count, 'twitter-widget-pro'),
373
  'month' => _n('about %s month ago', 'about %s months ago', $count, 'twitter-widget-pro'),
374
  'week' => _n('about %s week ago', 'about %s weeks ago', $count, 'twitter-widget-pro'),
376
  'hour' => _n('about %s hour ago', 'about %s hours ago', $count, 'twitter-widget-pro'),
377
  'minute' => _n('about %s minute ago', 'about %s minutes ago', $count, 'twitter-widget-pro'),
378
  'second' => _n('about %s second ago', 'about %s seconds ago', $count, 'twitter-widget-pro'),
379
+ );
380
+
381
+ return sprintf($messages[$key], $count);
382
+ }
383
+ }
384
+
385
+
386
+ /**
387
+ * wpTwitterWidget is the class that handles everything outside the widget. This
388
+ * includes filters that modify tweet content for things like linked usernames.
389
+ * It also helps us avoid name collisions.
390
+ */
391
+ class wpTwitterWidget
392
+ {
393
+ /**
394
+ * @var array Plugin settings
395
+ */
396
+ private $_settings;
397
+
398
+ /**
399
+ * Plugin file to test against on plugins page
400
+ *
401
+ * @var string
402
+ */
403
+ private $_pluginBasename;
404
+
405
+ /**
406
+ * Repository base url
407
+ *
408
+ * @since 1.4.4
409
+ * @var string
410
+ */
411
+ private $_reposUrl = 'http://plugins.svn.wordpress.org/';
412
+
413
+ public function __construct() {
414
+ /**
415
+ * Add filters and actions
416
+ */
417
+ add_action( 'admin_menu', array($wpTwitterWidget,'admin_menu') );
418
+ add_filter( 'init', array( $wpTwitterWidget, 'init_locale') );
419
+ add_filter( 'admin_init', array( $wpTwitterWidget, 'registerSettings') );
420
+ add_filter( 'admin_init', array( $wpTwitterWidget, 'sendSysInfo') );
421
+ add_action( 'widgets_init', array($wpTwitterWidget, 'register') );
422
+ add_filter( 'widget_twitter_content', array($wpTwitterWidget, 'linkTwitterUsers') );
423
+ add_filter( 'widget_twitter_content', array($wpTwitterWidget, 'linkUrls') );
424
+ add_filter( 'widget_twitter_content', array($wpTwitterWidget, 'linkHashtags') );
425
+ add_filter( 'widget_twitter_content', 'convert_chars' );
426
+ add_filter( 'plugin_action_links', array($wpTwitterWidget, 'addSettingLink'), 10, 2 );
427
+ add_action ( 'in_plugin_update_message-'.plugin_basename ( __FILE__ ) , array ( $this , '_changelog' ), null, 2 );
428
+ }
429
+
430
+ public function registerSettings() {
431
+ register_setting( 'twitter_widget_pro_options', 'twitter_widget_pro' );
432
+ }
433
+
434
+ public function admin_menu() {
435
+ add_options_page(__('Twitter Widget Pro', 'twitter-widget-pro'), __('Twitter Widget Pro', 'twitter-widget-pro'), 'manage_options', 'TwitterWidgetPro', array($this, 'options'));
436
+ }
437
+
438
+ public function init_locale() {
439
+ $lang_dir = basename(dirname(__FILE__)) . '/languages';
440
+ load_plugin_textdomain('twitter-widget-pro', 'wp-content/plugins/' . $lang_dir, $lang_dir);
441
+ }
442
+
443
+ /**
444
+ * This is used to display the options page for this plugin
445
+ */
446
+ public function options() {
447
+ //Get our options
448
+ $this->_getSettings();
449
+ ?>
450
+ <div class="wrap">
451
+ <h2><?php _e('Twitter Widget Pro Options', 'twitter-widget-pro') ?></h2>
452
+ <form action="options.php" method="post" id="wp_twitter_widget_pro">
453
+ <?php settings_fields( 'twitter_widget_pro_options' ); ?>
454
+ <table class="form-table">
455
+ <tr valign="top">
456
+ <th scope="row">
457
+ <a title="<?php _e('Click for Help!', 'twitter-widget-pro'); ?>" href="#" onclick="jQuery('#twp_user_agreed_to_send_system_information_help').toggle(); return false;">
458
+ <?php _e('System Information:', 'twitter-widget-pro') ?>
459
+ </a>
460
+ </th>
461
+ <td>
462
+ <input type="hidden" name="twitter_widget_pro[user_agreed_to_send_system_information]" value="false" />
463
+ <label for="twp_user_agreed_to_send_system_information"><input type="checkbox" name="twitter_widget_pro[user_agreed_to_send_system_information]" value="true" id="twp_user_agreed_to_send_system_information"<?php checked('true', $this->_settings['user_agreed_to_send_system_information']); ?> /> <?php _e('I agree to send anonymous system information', 'twitter-widget-pro'); ?></label><br />
464
+ <small id="twp_user_agreed_to_send_system_information_help" style="display:none;">
465
+ <?php _e('You can help by sending anonymous system information that will help Xavisys make better decisions about new features.', 'twitter-widget-pro'); ?><br />
466
+ <?php _e('The information will be sent anonymously, but a unique identifier will be sent to prevent duplicate entries from the same installation.', 'twitter-widget-pro'); ?>
467
+ </small>
468
+ </td>
469
+ </tr>
470
+ </table>
471
+ <p class="submit">
472
+ <input type="submit" name="Submit" value="<?php _e('Update Options &raquo;', 'twitter-widget-pro'); ?>" />
473
+ </p>
474
+ </form>
475
+ </div>
476
+ <?php
477
+ }
478
+
479
+ public function _changelog ($pluginData, $newPluginData) {
480
+ $url = "{$this->_reposUrl}/{$newPluginData->slug}/tags/{$newPluginData->new_version}/upgrade.html";
481
+ $response = wp_remote_get ( $url );
482
+ $code = (int) wp_remote_retrieve_response_code ( $response );
483
+ if ( $code == 200 ) {
484
+ echo wp_remote_retrieve_body ( $response );
485
+ }
486
+ }
487
+
488
+ /**
489
+ * Returns the Twitter user's profile image, linked to that user's profile
490
+ *
491
+ * @param object $user - Twitter User
492
+ * @return string - Linked image (XHTML)
493
+ */
494
+ private function _getProfileImage($user) {
495
+ return <<<profileImage
496
+ <a title="{$user->name}" href="http://twitter.com/{$user->screen_name}">
497
+ <img alt="{$user->name}" src="{$user->profile_image_url}" />
498
+ </a>
499
+ profileImage;
500
+ }
501
 
502
+ /**
503
+ * Returns the user's screen name as a link inside strong tags.
504
+ *
505
+ * @param object $user - Twitter user
506
+ * @return string - Username as link (XHTML)
507
+ */
508
+ private function _getUserName($user) {
509
+ return <<<profileImage
510
+ <strong>
511
+ <a title="{$user->name}" href="http://twitter.com/{$user->screen_name}">{$user->screen_name}</a>
512
+ </strong>
513
+ profileImage;
514
  }
515
 
516
  /**
521
  $this->_getSettings();
522
  if ($this->_settings['user_agreed_to_send_system_information'] == 'true') {
523
  $lastSent = get_option('twp-sysinfo');
524
+ $sysinfo = $this->_get_sysinfo();
525
  if (serialize($lastSent) != serialize($sysinfo)) {
526
  $params = array(
527
  'method' => 'POST',
581
  }
582
  return $links;
583
  }
584
+
585
+ /**
586
+ * Replace @username with a link to that twitter user
587
+ *
588
+ * @param string $text - Tweet text
589
+ * @return string - Tweet text with @replies linked
590
+ */
591
+ public function linkTwitterUsers($text) {
592
+ $text = preg_replace('/(^|\s)@(\w*)/i', '$1<a href="http://twitter.com/$2" class="twitter-user">@$2</a>', $text);
593
+ return $text;
594
+ }
595
+
596
+ /**
597
+ * Replace #hashtag with a link to search.twitter.com for that hashtag
598
+ *
599
+ * @param string $text - Tweet text
600
+ * @return string - Tweet text with #hashtags linked
601
+ */
602
+ public function linkHashtags($text) {
603
+ $text = preg_replace_callback('/(^|\s)(#\w*)/i', array($this, '_hashtagLink'), $text);
604
+ return $text;
605
+ }
606
+
607
+ /**
608
+ * Replace #hashtag with a link to search.twitter.com for that hashtag
609
+ *
610
+ * @param array $matches - Tweet text
611
+ * @return string - Tweet text with #hashtags linked
612
+ */
613
+ private function _hashtagLink($matches) {
614
+ return "{$matches[1]}<a href='http://search.twitter.com/search?q="
615
+ . urlencode($matches[2])
616
+ . "' class='twitter-hashtag'>{$matches[2]}</a>";
617
+ }
618
+
619
+ /**
620
+ * Turn URLs into links
621
+ *
622
+ * @param string $text - Tweet text
623
+ * @return string - Tweet text with URLs repalced with links
624
+ */
625
+ public function linkUrls($text) {
626
+ /**
627
+ * match protocol://address/path/file.extension?some=variable&another=asf%
628
+ * $1 is a possible space, this keeps us from linking href="[link]" etc
629
+ * $2 is the whole URL
630
+ * $3 is protocol://
631
+ * $4 is the URL without the protocol://
632
+ * $5 is the URL parameters
633
+ */
634
+ $text = preg_replace("/(^|\s)(([a-zA-Z]+:\/\/)([a-z][a-z0-9_\..-]*[a-z]{2,6})([a-zA-Z0-9~\/*-?&%]*))/i", "$1<a href=\"$2\">$2</a>", $text);
635
+
636
+ /**
637
+ * match www.something.domain/path/file.extension?some=variable&another=asf%
638
+ * $1 is a possible space, this keeps us from linking href="[link]" etc
639
+ * $2 is the whole URL that was matched. The protocol is missing, so we assume http://
640
+ * $3 is www.
641
+ * $4 is the URL matched without the www.
642
+ * $5 is the URL parameters
643
+ */
644
+ $text = preg_replace("/(^|\s)(www\.([a-z][a-z0-9_\..-]*[a-z]{2,6})([a-zA-Z0-9~\/*-?&%]*))/i", "$1<a href=\"http://$2\">$2</a>", $text);
645
+
646
+ return $text;
647
+ }
648
+
649
+ function register() {
650
+ register_widget('WP_Widget_Twitter_Pro');
651
+ }
652
  }
653
  // Instantiate our class
654
  $wpTwitterWidget = new wpTwitterWidget();