]> git.wh0rd.org - tt-rss.git/blame - include/functions.php
report all libxml errors in updater debug output
[tt-rss.git] / include / functions.php
CommitLineData
1d3a17c7 1<?php
6e658547 2 define('EXPECTED_CONFIG_VERSION', 26);
e596b5a9 3 define('SCHEMA_VERSION', 123);
545ca067 4
f822a8e5 5 define('LABEL_BASE_INDEX', -1024);
a413f53e 6 define('PLUGIN_FEED_BASE_INDEX', -128);
f822a8e5 7
e57a1507
AD
8 define('COOKIE_LIFETIME_LONG', 86400*365);
9
23d2471c 10 $fetch_last_error = false;
7a01dc77 11 $fetch_last_error_code = false;
ef39be2b 12 $fetch_last_content_type = false;
3f6f0857 13 $fetch_curl_used = false;
4f71d743 14 $suppress_debugging = false;
23d2471c 15
d68629dc 16 mb_internal_encoding("UTF-8");
324944f3 17 date_default_timezone_set('UTC');
8a7f5767
CW
18 if (defined('E_DEPRECATED')) {
19 error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
20 } else {
21 error_reporting(E_ALL & ~E_NOTICE);
22 }
cce28758 23
40d13c28 24 require_once 'config.php';
cc17c205 25
046ec657
BK
26 /**
27 * Define a constant if not already defined
28 *
29 * @param string $name The constant name.
30 * @param mixed $value The constant value.
31 * @access public
32 * @return boolean True if defined successfully or not.
33 */
34 function define_default($name, $value) {
046ec657
BK
35 defined($name) or define($name, $value);
36 }
37
9ef8798b
BK
38 ///// Some defaults that you can override in config.php //////
39
40 define_default('FEED_FETCH_TIMEOUT', 45);
41 // How may seconds to wait for response when requesting feed from a site
42 define_default('FEED_FETCH_NO_CACHE_TIMEOUT', 15);
43 // How may seconds to wait for response when requesting feed from a
44 // site when that feed wasn't cached before
45 define_default('FILE_FETCH_TIMEOUT', 45);
46 // Default timeout when fetching files from remote sites
47 define_default('FILE_FETCH_CONNECT_TIMEOUT', 15);
48 // How many seconds to wait for initial response from website when
49 // fetching files from remote sites
046ec657 50
fc2b26a6
AD
51 if (DB_TYPE == "pgsql") {
52 define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
53 } else {
54 define('SUBSTRING_FOR_DATE', 'SUBSTRING');
55 }
56
9632f884
AD
57 /**
58 * Return available translations names.
8d505d78 59 *
9632f884
AD
60 * @access public
61 * @return array A array of available translations.
62 */
f8c612d4 63 function get_translations() {
6a214f92 64 $tr = array(
8d505d78 65 "auto" => "Detect automatically",
a3162add 66 "ca_CA" => "Català",
a06b79c4 67 "cs_CZ" => "Česky",
6a214f92 68 "en_US" => "English",
36d0510c 69 "es_ES" => "Español",
a927fe7b 70 "de_DE" => "Deutsch",
6a214f92 71 "fr_FR" => "Français",
e78fd196 72 "hu_HU" => "Magyar (Hungarian)",
bb5d3960 73 "it_IT" => "Italiano",
1d004f12 74 "ja_JP" => "日本語 (Japanese)",
7b6c1ca7 75 "lv_LV" => "Latviešu",
592535d7 76 "nb_NO" => "Norwegian bokmål",
9e7f1f12 77 "nl_NL" => "Dutch",
ea45791a 78 "pl_PL" => "Polski",
d3b923c9 79 "ru_RU" => "Русский",
9a063469 80 "pt_BR" => "Portuguese/Brazil",
5d608138 81 "zh_CN" => "Simplified Chinese",
2324f153 82 "sv_SE" => "Svenska",
5d608138 83 "fi_FI" => "Suomi");
f8c612d4
AD
84
85 return $tr;
86 }
87
7b26a148
AD
88 require_once "lib/accept-to-gettext.php";
89 require_once "lib/gettext/gettext.inc";
aba609e0 90
6b461797 91 require_once "lib/languagedetect/LanguageDetect.php";
87d7e850 92
7b26a148 93 function startup_gettext() {
8d505d78 94
7b26a148
AD
95 # Get locale from Accept-Language header
96 $lang = al2gt(array_keys(get_translations()), "text/html");
89cb787e 97
7b26a148
AD
98 if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
99 $lang = _TRANSLATION_OVERRIDE_DEFAULT;
100 }
89cb787e 101
b18d109f
AD
102 if ($_SESSION["uid"] && get_schema_version() >= 120) {
103 $pref_lang = get_pref("USER_LANGUAGE", $_SESSION["uid"]);
1ee4900a 104
c4db796f 105 if ($pref_lang && $pref_lang != 'auto') {
7b149552
AD
106 $lang = $pref_lang;
107 }
7b26a148 108 }
7c33dbd4 109
7b26a148
AD
110 if ($lang) {
111 if (defined('LC_MESSAGES')) {
112 _setlocale(LC_MESSAGES, $lang);
113 } else if (defined('LC_ALL')) {
114 _setlocale(LC_ALL, $lang);
8d039718 115 }
aba609e0 116
d98e76d9 117 _bindtextdomain("messages", "locale");
865220a4 118
7b26a148
AD
119 _textdomain("messages");
120 _bind_textdomain_codeset("messages", "UTF-8");
865220a4 121 }
7b26a148
AD
122 }
123
b619ff15 124 require_once 'db-prefs.php';
8911ac8b 125 require_once 'version.php';
87d7e850
AD
126 require_once 'ccache.php';
127 require_once 'labels.php';
40d13c28 128
fb850eec 129 define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION . ' (http://tt-rss.org/)');
500943a4
AD
130 ini_set('user_agent', SELF_USER_AGENT);
131
b0f379df 132 require_once 'lib/pubsubhubbub/publisher.php';
010efc9b 133
7d96bfcd
AD
134 $schema_version = false;
135
4f71d743
AD
136 function _debug_suppress($suppress) {
137 global $suppress_debugging;
138
139 $suppress_debugging = $suppress;
140 }
141
45004d43
AD
142 /**
143 * Print a timestamped debug message.
8d505d78 144 *
45004d43
AD
145 * @param string $msg The debug message.
146 * @return void
147 */
68cccafc 148 function _debug($msg, $show = true) {
4f71d743
AD
149 global $suppress_debugging;
150
151 //echo "[$suppress_debugging] $msg $show\n";
152
153 if ($suppress_debugging) return false;
9ec10352 154
6f9e33e4 155 $ts = strftime("%H:%M:%S", time());
2a6a9395
AD
156 if (function_exists('posix_getpid')) {
157 $ts = "$ts/" . posix_getpid();
158 }
2191eb7a 159
68cccafc 160 if ($show && !(defined('QUIET') && QUIET)) {
2191eb7a
AD
161 print "[$ts] $msg\n";
162 }
163
164 if (defined('LOGFILE')) {
165 $fp = fopen(LOGFILE, 'a+');
166
167 if ($fp) {
a33558a6
AD
168 $locked = false;
169
170 if (function_exists("flock")) {
171 $tries = 0;
172
173 // try to lock logfile for writing
174 while ($tries < 5 && !$locked = flock($fp, LOCK_EX | LOCK_NB)) {
175 sleep(1);
176 ++$tries;
177 }
178
179 if (!$locked) {
180 fclose($fp);
181 return;
182 }
183 }
184
2191eb7a 185 fputs($fp, "[$ts] $msg\n");
a33558a6
AD
186
187 if (function_exists("flock")) {
188 flock($fp, LOCK_UN);
189 }
190
2191eb7a
AD
191 fclose($fp);
192 }
193 }
194
45004d43 195 } // function _debug
6f9e33e4 196
9632f884
AD
197 /**
198 * Purge a feed old posts.
8d505d78 199 *
9632f884
AD
200 * @param mixed $link A database connection.
201 * @param mixed $feed_id The id of the purged feed.
202 * @param mixed $purge_interval Olderness of purged posts.
203 * @param boolean $debug Set to True to enable the debug. False by default.
204 * @access public
205 * @return void
206 */
a42c55f0 207 function purge_feed($feed_id, $purge_interval, $debug = false) {
ad507f85 208
a42c55f0 209 if (!$purge_interval) $purge_interval = feed_purge_interval($feed_id);
8d505d78 210
ad507f85 211 $rows = -1;
4c193675 212
6322ac79 213 $result = db_query(
07d0efe9
AD
214 "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
215
216 $owner_uid = false;
217
218 if (db_num_rows($result) == 1) {
219 $owner_uid = db_fetch_result($result, 0, "owner_uid");
220 }
221
ab954dff
AD
222 if ($purge_interval == -1 || !$purge_interval) {
223 if ($owner_uid) {
a42c55f0 224 ccache_update($feed_id, $owner_uid);
ab954dff
AD
225 }
226 return;
227 }
228
07d0efe9
AD
229 if (!$owner_uid) return;
230
3907ef71 231 if (FORCE_ARTICLE_PURGE == 0) {
a42c55f0 232 $purge_unread = get_pref("PURGE_UNREAD_ARTICLES",
3907ef71
AD
233 $owner_uid, false);
234 } else {
235 $purge_unread = true;
236 $purge_interval = FORCE_ARTICLE_PURGE;
237 }
07d0efe9
AD
238
239 if (!$purge_unread) $query_limit = " unread = false AND ";
240
fefa6ca3 241 if (DB_TYPE == "pgsql") {
6322ac79 242 $pg_version = get_pgsql_version();
6e7f8d26
AD
243
244 if (preg_match("/^7\./", $pg_version) || preg_match("/^8\.0/", $pg_version)) {
1e59ae35 245
a42c55f0 246 $result = db_query("DELETE FROM ttrss_user_entries WHERE
8d505d78
AD
247 ttrss_entries.id = ref_id AND
248 marked = false AND
249 feed_id = '$feed_id' AND
07d0efe9 250 $query_limit
25ea2805 251 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
1e59ae35
AD
252
253 } else {
254
a42c55f0 255 $result = db_query("DELETE FROM ttrss_user_entries
8d505d78
AD
256 USING ttrss_entries
257 WHERE ttrss_entries.id = ref_id AND
258 marked = false AND
259 feed_id = '$feed_id' AND
07d0efe9 260 $query_limit
25ea2805 261 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
1e59ae35 262 }
ad507f85 263
fefa6ca3 264 } else {
8d505d78 265
a42c55f0 266/* $result = db_query("DELETE FROM ttrss_user_entries WHERE
fefa6ca3 267 marked = false AND feed_id = '$feed_id' AND
8d505d78 268 (SELECT date_updated FROM ttrss_entries WHERE
30f1746f
AD
269 id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
270
a42c55f0 271 $result = db_query("DELETE FROM ttrss_user_entries
8d505d78
AD
272 USING ttrss_user_entries, ttrss_entries
273 WHERE ttrss_entries.id = ref_id AND
274 marked = false AND
275 feed_id = '$feed_id' AND
07d0efe9 276 $query_limit
25ea2805 277 ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
ad507f85
AD
278 }
279
a08f94bd 280 $rows = db_affected_rows($result);
3f6f0857 281
a42c55f0 282 ccache_update($feed_id, $owner_uid);
ced46404 283
ad507f85 284 if ($debug) {
6f9e33e4 285 _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
fefa6ca3 286 }
2ea09bde
AD
287
288 return $rows;
9632f884 289 } // function purge_feed
fefa6ca3 290
a42c55f0 291 function feed_purge_interval($feed_id) {
07d0efe9 292
a42c55f0 293 $result = db_query("SELECT purge_interval, owner_uid FROM ttrss_feeds
07d0efe9
AD
294 WHERE id = '$feed_id'");
295
296 if (db_num_rows($result) == 1) {
297 $purge_interval = db_fetch_result($result, 0, "purge_interval");
298 $owner_uid = db_fetch_result($result, 0, "owner_uid");
299
6322ac79 300 if ($purge_interval == 0) $purge_interval = get_pref(
863be6ca 301 'PURGE_OLD_DAYS', $owner_uid);
07d0efe9
AD
302
303 return $purge_interval;
304
305 } else {
306 return -1;
307 }
308 }
309
a42c55f0 310 function purge_orphans($do_output = false) {
a2d79981 311
71604ca4 312 // purge orphaned posts in main content table
a42c55f0 313 $result = db_query("DELETE FROM ttrss_entries WHERE
71604ca4 314 (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
a2d79981
AD
315
316 if ($do_output) {
a42c55f0 317 $rows = db_affected_rows($result);
a2d79981
AD
318 _debug("Purged $rows orphaned posts.");
319 }
c3a8d71a
AD
320 }
321
a42c55f0
AD
322 function get_feed_update_interval($feed_id) {
323 $result = db_query("SELECT owner_uid, update_interval FROM
c7d57b66
AD
324 ttrss_feeds WHERE id = '$feed_id'");
325
326 if (db_num_rows($result) == 1) {
327 $update_interval = db_fetch_result($result, 0, "update_interval");
328 $owner_uid = db_fetch_result($result, 0, "owner_uid");
329
330 if ($update_interval != 0) {
331 return $update_interval;
332 } else {
a42c55f0 333 return get_pref('DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
c7d57b66
AD
334 }
335
336 } else {
337 return -1;
338 }
339 }
340
7a01dc77 341 function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false, $timestamp = 0) {
8d505d78 342
23d2471c 343 global $fetch_last_error;
7a01dc77 344 global $fetch_last_error_code;
ef39be2b 345 global $fetch_last_content_type;
3f6f0857 346 global $fetch_curl_used;
33373eca 347
2375b6b6 348 $url = str_replace(' ', '%20', $url);
23d2471c 349
3f6f0857
AD
350 if (!defined('NO_CURL') && function_exists('curl_init')) {
351
352 $fetch_curl_used = true;
b799dc8b 353
438a3ecb 354 if (ini_get("safe_mode") || ini_get("open_basedir")) {
e2b4c0b7
RH
355 $new_url = geturl($url);
356 if (!$new_url) {
357 // geturl has already populated $fetch_last_error
358 return false;
359 }
360 $ch = curl_init($new_url);
b799dc8b
AD
361 } else {
362 $ch = curl_init($url);
363 }
a1af1574 364
81c20663 365 if ($timestamp && !$post_query) {
7a01dc77
AD
366 curl_setopt($ch, CURLOPT_HTTPHEADER,
367 array("If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T', $timestamp)));
368 }
369
8401101d
BK
370 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout ? $timeout : FILE_FETCH_CONNECT_TIMEOUT);
371 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout ? $timeout : FILE_FETCH_TIMEOUT);
438a3ecb 372 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, !ini_get("safe_mode") && !ini_get("open_basedir"));
a1af1574
AD
373 curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
374 curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
375 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
8d505d78 376 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
5f6804bc 377 curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
19929bbe 378 curl_setopt($ch, CURLOPT_USERAGENT, SELF_USER_AGENT);
3f6f0857 379 curl_setopt($ch, CURLOPT_ENCODING, "");
48b657fc 380 curl_setopt($ch, CURLOPT_REFERER, $url);
0f6b9263
AD
381
382 if (!ini_get("safe_mode") && !ini_get("open_basedir")) {
383 curl_setopt($ch, CURLOPT_COOKIEJAR, "/dev/null");
384 }
8d505d78 385
05f14a7d
AD
386 if (defined('_CURL_HTTP_PROXY')) {
387 curl_setopt($ch, CURLOPT_PROXY, _CURL_HTTP_PROXY);
388 }
389
ae5f7bb1
AD
390 if ($post_query) {
391 curl_setopt($ch, CURLOPT_POST, true);
392 curl_setopt($ch, CURLOPT_POSTFIELDS, $post_query);
393 }
394
d86945c7 395 if ((OPENSSL_VERSION_NUMBER >= 0x0090808f) && (OPENSSL_VERSION_NUMBER < 0x10000000)) {
893960b0 396 curl_setopt($ch, CURLOPT_SSLVERSION, 3);
d86945c7
AD
397 }
398
8d505d78
AD
399 if ($login && $pass)
400 curl_setopt($ch, CURLOPT_USERPWD, "$login:$pass");
a1af1574 401
fb074239 402 $contents = @curl_exec($ch);
268a06dc 403
48b657fc
AD
404 if (curl_errno($ch) === 23 || curl_errno($ch) === 61) {
405 curl_setopt($ch, CURLOPT_ENCODING, 'none');
406 $contents = @curl_exec($ch);
fb850eec
AD
407 }
408
a1af1574 409 if ($contents === false) {
fb850eec 410 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
a1af1574
AD
411 curl_close($ch);
412 return false;
4065b60b
AD
413 }
414
8d505d78 415 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
ef39be2b 416 $fetch_last_content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
4065b60b 417
7a01dc77
AD
418 $fetch_last_error_code = $http_code;
419
ef39be2b 420 if ($http_code != 200 || $type && strpos($fetch_last_content_type, "$type") === false) {
fb850eec
AD
421 if (curl_errno($ch) != 0) {
422 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
423 } else {
424 $fetch_last_error = "HTTP Code: $http_code";
425 }
426 curl_close($ch);
a1af1574
AD
427 return false;
428 }
4065b60b 429
fb850eec
AD
430 curl_close($ch);
431
a1af1574 432 return $contents;
4065b60b 433 } else {
3f6f0857
AD
434
435 $fetch_curl_used = false;
436
d3911f80 437 if ($login && $pass){
8d505d78
AD
438 $url_parts = array();
439
440 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
441
d3911f80
AD
442 $pass = urlencode($pass);
443
8d505d78
AD
444 if ($url_parts[1] && $url_parts[2]) {
445 $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
446 }
447 }
448
01311d86
AD
449 if (!$post_query && $timestamp) {
450 $context = stream_context_create(array(
451 'http' => array(
452 'method' => 'GET',
453 'header' => "If-Modified-Since: ".gmdate("D, d M Y H:i:s \\G\\M\\T\r\n", $timestamp)
454 )));
455 } else {
456 $context = NULL;
457 }
458
6122c449
AD
459 $old_error = error_get_last();
460
01311d86 461 $data = @file_get_contents($url, false, $context);
23d2471c 462
ef39be2b 463 $fetch_last_content_type = false; // reset if no type was sent from server
37ddf5b7 464 if (isset($http_response_header) && is_array($http_response_header)) {
df25e4d2
AD
465 foreach ($http_response_header as $h) {
466 if (substr(strtolower($h), 0, 13) == 'content-type:') {
467 $fetch_last_content_type = substr($h, 14);
468 // don't abort here b/c there might be more than one
469 // e.g. if we were being redirected -- last one is the right one
470 }
01311d86
AD
471
472 if (substr(strtolower($h), 0, 7) == 'http/1.') {
473 $fetch_last_error_code = (int) substr($h, 9, 3);
474 }
ef39be2b
MB
475 }
476 }
477
6122c449 478 if (!$data) {
23d2471c 479 $error = error_get_last();
6122c449
AD
480
481 if ($error['message'] != $old_error['message']) {
482 $fetch_last_error = $error["message"];
483 } else {
484 $fetch_last_error = "HTTP Code: $fetch_last_error_code";
485 }
23d2471c
AD
486 }
487 return $data;
4065b60b
AD
488 }
489
490 }
78800912 491
9632f884
AD
492 /**
493 * Try to determine the favicon URL for a feed.
494 * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
495 * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
8d505d78 496 *
9632f884
AD
497 * @param string $url A feed or page URL
498 * @access public
499 * @return mixed The favicon URL, or false if none was found.
500 */
1bd11fdf 501 function get_favicon_url($url) {
99331724 502
1bd11fdf 503 $favicon_url = false;
ed214298 504
4065b60b 505 if ($html = @fetch_file_contents($url)) {
78800912 506
ed214298 507 libxml_use_internal_errors(true);
c798704b 508
ed214298
AD
509 $doc = new DOMDocument();
510 $doc->loadHTML($html);
511 $xpath = new DOMXPath($doc);
717f5e64 512
a712429e
AD
513 $base = $xpath->query('/html/head/base');
514 foreach ($base as $b) {
515 $url = $b->getAttribute("href");
516 break;
517 }
518
1bd11fdf 519 $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
ed214298
AD
520 if (count($entries) > 0) {
521 foreach ($entries as $entry) {
1bd11fdf
AD
522 $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
523 break;
ed214298 524 }
8d505d78 525 }
4065b60b 526 }
c798704b 527
1bd11fdf
AD
528 if (!$favicon_url)
529 $favicon_url = rewrite_relative_url($url, "/favicon.ico");
530
531 return $favicon_url;
532 } // function get_favicon_url
533
6322ac79 534 function check_feed_favicon($site_url, $feed) {
882311d9 535# print "FAVICON [$site_url]: $favicon_url\n";
4065b60b 536
1bd11fdf
AD
537 $icon_file = ICONS_DIR . "/$feed.ico";
538
539 if (!file_exists($icon_file)) {
540 $favicon_url = get_favicon_url($site_url);
541
542 if ($favicon_url) {
543 // Limiting to "image" type misses those served with text/plain
544 $contents = fetch_file_contents($favicon_url); // , "image");
545
546 if ($contents) {
547 // Crude image type matching.
548 // Patterns gleaned from the file(1) source code.
549 if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
550 // 0 string \000\000\001\000 MS Windows icon resource
551 //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
552 }
553 elseif (preg_match('/^GIF8/', $contents)) {
554 // 0 string GIF8 GIF image data
555 //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
556 }
557 elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
558 // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
559 //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
560 }
561 elseif (preg_match('/^\xff\xd8/', $contents)) {
562 // 0 beshort 0xffd8 JPEG image data
563 //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
564 }
565 else {
566 //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
567 $contents = "";
568 }
569 }
570
571 if ($contents) {
572 $fp = @fopen($icon_file, "w");
573
574 if ($fp) {
575 fwrite($fp, $contents);
576 fclose($fp);
577 chmod($icon_file, 0644);
578 }
579 }
580 }
2cfbb448 581 return $icon_file;
78800912
AD
582 }
583 }
14118af3 584
f175937c 585 function print_select($id, $default, $values, $attributes = "") {
79f3553b 586 print "<select name=\"$id\" id=\"$id\" $attributes>";
a0d53889
AD
587 foreach ($values as $v) {
588 if ($v == $default)
60807300 589 $sel = "selected=\"1\"";
a0d53889
AD
590 else
591 $sel = "";
8d505d78 592
e88c1943
AD
593 $v = trim($v);
594
60807300 595 print "<option value=\"$v\" $sel>$v</option>";
a0d53889
AD
596 }
597 print "</select>";
598 }
40d13c28 599
79f3553b
AD
600 function print_select_hash($id, $default, $values, $attributes = "") {
601 print "<select name=\"$id\" id='$id' $attributes>";
673d54ca
AD
602 foreach (array_keys($values) as $v) {
603 if ($v == $default)
74d5c8fa 604 $sel = 'selected="selected"';
673d54ca
AD
605 else
606 $sel = "";
8d505d78 607
e88c1943
AD
608 $v = trim($v);
609
673d54ca
AD
610 print "<option $sel value=\"$v\">".$values[$v]."</option>";
611 }
612
613 print "</select>";
614 }
615
f541eb78 616 function print_radio($id, $default, $true_is, $values, $attributes = "") {
77e96719 617 foreach ($values as $v) {
8d505d78 618
77e96719 619 if ($v == $default)
5da169d9 620 $sel = "checked";
77e96719 621 else
5da169d9
AD
622 $sel = "";
623
f541eb78 624 if ($v == $true_is) {
5da169d9
AD
625 $sel .= " value=\"1\"";
626 } else {
627 $sel .= " value=\"0\"";
628 }
8d505d78
AD
629
630 print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
69654950 631 type=\"radio\" $sel $attributes name=\"$id\">&nbsp;$v&nbsp;";
77e96719
AD
632
633 }
634 }
635
a42c55f0 636 function initialize_user_prefs($uid, $profile = false) {
ff485f1d 637
a42c55f0 638 $uid = db_escape_string($uid);
ff485f1d 639
d9084cf2
AD
640 if (!$profile) {
641 $profile = "NULL";
f9aa6a89 642 $profile_qpart = "AND profile IS NULL";
d9084cf2 643 } else {
f9aa6a89 644 $profile_qpart = "AND profile = '$profile'";
d9084cf2
AD
645 }
646
6322ac79 647 if (get_schema_version() < 63) $profile_qpart = "";
f9aa6a89 648
a42c55f0 649 db_query("BEGIN");
ff485f1d 650
a42c55f0 651 $result = db_query("SELECT pref_name,def_value FROM ttrss_prefs");
8d505d78 652
a42c55f0 653 $u_result = db_query("SELECT pref_name
f9aa6a89 654 FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
ff485f1d
AD
655
656 $active_prefs = array();
657
658 while ($line = db_fetch_assoc($u_result)) {
8d505d78 659 array_push($active_prefs, $line["pref_name"]);
ff485f1d
AD
660 }
661
662 while ($line = db_fetch_assoc($result)) {
663 if (array_search($line["pref_name"], $active_prefs) === FALSE) {
664// print "adding " . $line["pref_name"] . "<br>";
665
a42c55f0
AD
666 $line["def_value"] = db_escape_string($line["def_value"]);
667 $line["pref_name"] = db_escape_string($line["pref_name"]);
d296ba50 668
6322ac79 669 if (get_schema_version() < 63) {
a42c55f0 670 db_query("INSERT INTO ttrss_user_prefs
8d505d78 671 (owner_uid,pref_name,value) VALUES
f9aa6a89
AD
672 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
673
674 } else {
a42c55f0 675 db_query("INSERT INTO ttrss_user_prefs
8d505d78 676 (owner_uid,pref_name,value, profile) VALUES
f9aa6a89
AD
677 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
678 }
ff485f1d
AD
679
680 }
681 }
682
a42c55f0 683 db_query("COMMIT");
ff485f1d
AD
684
685 }
956c7629 686
8de8bfb8
AD
687 function get_ssl_certificate_id() {
688 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
689 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
690 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
691 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
692 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
693 }
694 return "";
695 }
696
a42c55f0 697 function authenticate_user($login, $password, $check_only = false) {
c8437f35 698
131b01b3 699 if (!SINGLE_USER_MODE) {
0d421af8 700 $user_id = false;
0f28f81f 701
1ffe3391 702 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_AUTH_USER) as $plugin) {
0f28f81f
AD
703
704 $user_id = (int) $plugin->authenticate($login, $password);
705
706 if ($user_id) {
707 $_SESSION["auth_module"] = strtolower(get_class($plugin));
708 break;
709 }
461766f3
AD
710 }
711
0d421af8 712 if ($user_id && !$check_only) {
70400171
AD
713 @session_start();
714
0d421af8 715 $_SESSION["uid"] = $user_id;
3472c4c5 716 $_SESSION["version"] = VERSION_STATIC;
0d421af8 717
a42c55f0 718 $result = db_query("SELECT login,access_level,pwd_hash FROM ttrss_users
0d421af8 719 WHERE id = '$user_id'");
8d505d78 720
131b01b3
AD
721 $_SESSION["name"] = db_fetch_result($result, 0, "login");
722 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
8484ce22 723 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
8d505d78 724
a42c55f0 725 db_query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
131b01b3 726 $_SESSION["uid"]);
8d505d78 727
131b01b3 728 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
837ec70e 729 $_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
1a9f4d3c 730 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
91c5f229
AD
731
732 $_SESSION["last_version_check"] = time();
8d505d78 733
a42c55f0 734 initialize_user_prefs($_SESSION["uid"]);
8d505d78 735
131b01b3
AD
736 return true;
737 }
8d505d78 738
131b01b3 739 return false;
503eb349 740
131b01b3 741 } else {
503eb349 742
131b01b3
AD
743 $_SESSION["uid"] = 1;
744 $_SESSION["name"] = "admin";
787e5ebc 745 $_SESSION["access_level"] = 10;
21e42e5f 746
0d421af8
AD
747 $_SESSION["hide_hello"] = true;
748 $_SESSION["hide_logout"] = true;
749
d5fd183d
AD
750 $_SESSION["auth_module"] = false;
751
21e42e5f
AD
752 if (!$_SESSION["csrf_token"]) {
753 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
754 }
f557cd78 755
0bbba72d 756 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
8d505d78 757
a42c55f0 758 initialize_user_prefs($_SESSION["uid"]);
8d505d78 759
c8437f35
AD
760 return true;
761 }
c8437f35
AD
762 }
763
e6cb77a0
AD
764 function make_password($length = 8) {
765
85db6213
AD
766 $password = "";
767 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
768
769 $i = 0;
770
771 while ($i < $length) {
772 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
773
774 if (!strstr($password, $char)) {
775 $password .= $char;
776 $i++;
777 }
778 }
779 return $password;
e6cb77a0
AD
780 }
781
782 // this is called after user is created to initialize default feeds, labels
783 // or whatever else
8d505d78 784
e6cb77a0
AD
785 // user preferences are checked on every login, not here
786
a42c55f0 787 function initialize_user($uid) {
e6cb77a0 788
a42c55f0 789 db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
74bff337 790 values ('$uid', 'Tiny Tiny RSS: New Releases',
b6d486a3 791 'http://tt-rss.org/releases.rss')");
3b0feb9b 792
a42c55f0 793 db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
cd2cd415 794 values ('$uid', 'Tiny Tiny RSS: Forum',
f0855b88 795 'http://tt-rss.org/forum/rss.php')");
3b0feb9b 796 }
e6cb77a0 797
b8aa49bc 798 function logout_user() {
5ccc1cf5
AD
799 session_destroy();
800 if (isset($_COOKIE[session_name()])) {
801 setcookie(session_name(), '', time()-42000, '/');
802 }
b8aa49bc
AD
803 }
804
8484ce22
AD
805 function validate_csrf($csrf_token) {
806 return $csrf_token == $_SESSION['csrf_token'];
807 }
808
a42c55f0 809 function load_user_plugins($owner_uid) {
6d45a152 810 if ($owner_uid && SCHEMA_VERSION >= 100) {
a42c55f0 811 $plugins = get_pref("_ENABLED_PLUGINS", $owner_uid);
de612e7a 812
1ffe3391 813 PluginHost::getInstance()->load($plugins, PluginHost::KIND_USER, $owner_uid);
e9c04fd4 814
6322ac79 815 if (get_schema_version() > 100) {
1ffe3391 816 PluginHost::getInstance()->load_data();
e9c04fd4 817 }
de612e7a
AD
818 }
819 }
820
6322ac79 821 function login_sequence() {
97acbaf1 822 if (SINGLE_USER_MODE) {
25db6c51 823 @session_start();
a42c55f0 824 authenticate_user("admin", null);
68349f55 825 startup_gettext();
a42c55f0 826 load_user_plugins($_SESSION["uid"]);
97acbaf1 827 } else {
6322ac79 828 if (!validate_session()) $_SESSION["uid"] = false;
d0eef2a3
AD
829
830 if (!$_SESSION["uid"]) {
97acbaf1 831
a42c55f0
AD
832 if (AUTH_AUTO_LOGIN && authenticate_user(null, null)) {
833 $_SESSION["ref_schema_version"] = get_schema_version(true);
97acbaf1 834 } else {
a42c55f0 835 authenticate_user(null, null, true);
97acbaf1
AD
836 }
837
d0eef2a3 838 if (!$_SESSION["uid"]) {
d0eef2a3
AD
839 @session_destroy();
840 setcookie(session_name(), '', time()-42000, '/');
9ce7a554 841
6322ac79 842 render_login_form();
d0eef2a3
AD
843 exit;
844 }
4ad99f23 845
97acbaf1
AD
846 } else {
847 /* bump login timestamp */
a42c55f0 848 db_query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
97acbaf1 849 $_SESSION["uid"]);
06b0777f 850 $_SESSION["last_login_update"] = time();
01a87dff
AD
851 }
852
de612e7a 853 if ($_SESSION["uid"]) {
7b149552 854 startup_gettext();
a42c55f0 855 load_user_plugins($_SESSION["uid"]);
b1b1d25f
AD
856
857 /* cleanup ccache */
858
a42c55f0 859 db_query("DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
b1b1d25f
AD
860 $_SESSION["uid"] . " AND
861 (SELECT COUNT(id) FROM ttrss_feeds WHERE
862 ttrss_feeds.id = feed_id) = 0");
863
a42c55f0 864 db_query("DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
b1b1d25f
AD
865 $_SESSION["uid"] . " AND
866 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
867 ttrss_feed_categories.id = feed_id) = 0");
868
de612e7a 869 }
b1b1d25f 870
b8aa49bc 871 }
afc3cf55 872 }
3547842a 873
411fe209 874 function truncate_string($str, $max_len, $suffix = '&hellip;') {
12db369c 875 if (mb_strlen($str, "utf-8") > $max_len - 3) {
411fe209 876 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
3547842a
AD
877 } else {
878 return $str;
879 }
880 }
54a60e1a 881
ab4b768f
AD
882 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
883
884 try {
885 $source_tz = new DateTimeZone($source_tz);
886 } catch (Exception $e) {
887 $source_tz = new DateTimeZone('UTC');
888 }
889
890 try {
891 $dest_tz = new DateTimeZone($dest_tz);
892 } catch (Exception $e) {
893 $dest_tz = new DateTimeZone('UTC');
894 }
895
896 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
897 return $dt->format('U') + $dest_tz->getOffset($dt);
898 }
899
a42c55f0 900 function make_local_datetime($timestamp, $long, $owner_uid = false,
324944f3
AD
901 $no_smart_dt = false) {
902
903 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
904 if (!$timestamp) $timestamp = '1970-01-01 0:00';
905
7d96bfcd 906 global $utc_tz;
5ddef5ba
AD
907 global $user_tz;
908
909 if (!$utc_tz) $utc_tz = new DateTimeZone('UTC');
324944f3 910
90e5f4f1
AD
911 $timestamp = substr($timestamp, 0, 19);
912
7d96bfcd
AD
913 # We store date in UTC internally
914 $dt = new DateTime($timestamp, $utc_tz);
915
5ddef5ba 916 $user_tz_string = get_pref('USER_TIMEZONE', $owner_uid);
7d96bfcd 917
6bfc97da 918 if ($user_tz_string != 'Automatic') {
324944f3 919
6bfc97da
AD
920 try {
921 if (!$user_tz) $user_tz = new DateTimeZone($user_tz_string);
922 } catch (Exception $e) {
923 $user_tz = $utc_tz;
924 }
925
926 $tz_offset = $user_tz->getOffset($dt);
927 } else {
96f0cbe3 928 $tz_offset = (int) -$_SESSION["clientTzOffset"];
6bfc97da 929 }
5ddef5ba 930
7d96bfcd 931 $user_timestamp = $dt->format('U') + $tz_offset;
324944f3 932
1dc52ae7 933 if (!$no_smart_dt) {
a42c55f0 934 return smart_date_time($user_timestamp,
7d96bfcd 935 $tz_offset, $owner_uid);
324944f3
AD
936 } else {
937 if ($long)
a42c55f0 938 $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
324944f3 939 else
a42c55f0 940 $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
324944f3
AD
941
942 return date($format, $user_timestamp);
943 }
944 }
945
a42c55f0 946 function smart_date_time($timestamp, $tz_offset = 0, $owner_uid = false) {
2a5c136e
AD
947 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
948
949 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() + $tz_offset)) {
be773442 950 return date("G:i", $timestamp);
2a5c136e 951 } else if (date("Y", $timestamp) == date("Y", time() + $tz_offset)) {
a42c55f0 952 $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
2a5c136e 953 return date($format, $timestamp);
be773442 954 } else {
a42c55f0 955 $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
2a5c136e 956 return date($format, $timestamp);
be773442
AD
957 }
958 }
959
e3c99f3b 960 function sql_bool_to_bool($s) {
9955a134 961 if ($s == "t" || $s == "1" || strtolower($s) == "true") {
e3c99f3b
AD
962 return true;
963 } else {
964 return false;
965 }
966 }
8d505d78 967
badac687
AD
968 function bool_to_sql_bool($s) {
969 if ($s) {
970 return "true";
971 } else {
972 return "false";
973 }
974 }
e3c99f3b 975
fcfa9ef1
AD
976 // Session caching removed due to causing wrong redirects to upgrade
977 // script when get_schema_version() is called on an obsolete session
978 // created on a previous schema version.
a42c55f0 979 function get_schema_version($nocache = false) {
7d96bfcd
AD
980 global $schema_version;
981
e2cf81e2 982 if (!$schema_version && !$nocache) {
a42c55f0 983 $result = db_query("SELECT schema_version FROM ttrss_version");
199db684 984 $version = db_fetch_result($result, 0, "schema_version");
7d96bfcd 985 $schema_version = $version;
199db684 986 return $version;
7d96bfcd
AD
987 } else {
988 return $schema_version;
989 }
e4c51a6c
AD
990 }
991
6322ac79 992 function sanity_check() {
31303c6b 993 require_once 'errors.php';
ebb948c2 994
6043fb7e 995 $error_code = 0;
a42c55f0 996 $schema_version = get_schema_version(true);
6043fb7e
AD
997
998 if ($schema_version != SCHEMA_VERSION) {
999 $error_code = 5;
1000 }
1001
aec3ce39 1002 if (DB_TYPE == "mysql") {
a42c55f0 1003 $result = db_query("SELECT true", false);
aec3ce39
AD
1004 if (db_num_rows($result) != 1) {
1005 $error_code = 10;
1006 }
1007 }
1008
a42c55f0 1009 if (db_escape_string("testTEST") != "testTEST") {
f29ba148
AD
1010 $error_code = 12;
1011 }
1012
ebb948c2 1013 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
6043fb7e
AD
1014 }
1015
27981ca3 1016 function file_is_locked($filename) {
8ff2a86c
AD
1017 if (file_exists(LOCK_DIRECTORY . "/$filename")) {
1018 if (function_exists('flock')) {
1019 $fp = @fopen(LOCK_DIRECTORY . "/$filename", "r");
1020 if ($fp) {
1021 if (flock($fp, LOCK_EX | LOCK_NB)) {
1022 flock($fp, LOCK_UN);
1023 fclose($fp);
1024 return false;
1025 }
31a6d42d 1026 fclose($fp);
8ff2a86c
AD
1027 return true;
1028 } else {
31a6d42d
AD
1029 return false;
1030 }
27981ca3 1031 }
8ff2a86c
AD
1032 return true; // consider the file always locked and skip the test
1033 } else {
1034 return false;
27981ca3 1035 }
27981ca3
AD
1036 }
1037
8ff2a86c 1038
fcb4c0c9 1039 function make_lockfile($filename) {
cfa43e02 1040 $fp = fopen(LOCK_DIRECTORY . "/$filename", "w");
fcb4c0c9 1041
a44bfcfd 1042 if ($fp && flock($fp, LOCK_EX | LOCK_NB)) {
58fc7095
AD
1043 $stat_h = fstat($fp);
1044 $stat_f = stat(LOCK_DIRECTORY . "/$filename");
1045
1fcebfb3
AD
1046 if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
1047 if ($stat_h["ino"] != $stat_f["ino"] ||
1048 $stat_h["dev"] != $stat_f["dev"]) {
1049
1050 return false;
1051 }
58fc7095
AD
1052 }
1053
4c59adb1
AD
1054 if (function_exists('posix_getpid')) {
1055 fwrite($fp, posix_getpid() . "\n");
1056 }
fcb4c0c9
AD
1057 return $fp;
1058 } else {
1059 return false;
1060 }
1061 }
1062
bf7fcde8 1063 function make_stampfile($filename) {
cfa43e02 1064 $fp = fopen(LOCK_DIRECTORY . "/$filename", "w");
bf7fcde8 1065
8e00ae9b 1066 if (flock($fp, LOCK_EX | LOCK_NB)) {
bf7fcde8 1067 fwrite($fp, time() . "\n");
8e00ae9b 1068 flock($fp, LOCK_UN);
bf7fcde8
AD
1069 fclose($fp);
1070 return true;
1071 } else {
1072 return false;
1073 }
1074 }
1075
894ebcf5 1076 function sql_random_function() {
8c0496f7 1077 if (DB_TYPE == "mysql") {
894ebcf5
AD
1078 return "RAND()";
1079 } else {
1080 return "RANDOM()";
1081 }
1082 }
1083
a42c55f0 1084 function catchup_feed($feed, $cat_view, $owner_uid = false, $max_id = false, $mode = 'all') {
c7e51de1
AD
1085
1086 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
88040f57 1087
37c03d3a 1088 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
22fdebff 1089
c8b693cf
AD
1090 // Todo: all this interval stuff needs some generic generator function
1091
1092 $date_qpart = "false";
1093
1094 switch ($mode) {
1095 case "1day":
1096 if (DB_TYPE == "pgsql") {
94828a8b 1097 $date_qpart = "date_entered < NOW() - INTERVAL '1 day' ";
c8b693cf 1098 } else {
d5381016 1099 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) ";
c8b693cf
AD
1100 }
1101 break;
1102 case "1week":
1103 if (DB_TYPE == "pgsql") {
94828a8b 1104 $date_qpart = "date_entered < NOW() - INTERVAL '1 week' ";
c8b693cf 1105 } else {
94828a8b 1106 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
c8b693cf
AD
1107 }
1108 break;
8d476359 1109 case "2week":
c8b693cf 1110 if (DB_TYPE == "pgsql") {
94828a8b 1111 $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
c8b693cf 1112 } else {
94828a8b 1113 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) ";
c8b693cf
AD
1114 }
1115 break;
1116 default:
1117 $date_qpart = "true";
1118 }
1119
37c03d3a 1120 if (is_numeric($feed)) {
23aa0d16
AD
1121 if ($cat_view) {
1122
72a2f4f5 1123 if ($feed >= 0) {
f9fca8cb
AD
1124
1125 if ($feed > 0) {
a42c55f0 1126 $children = getChildCategories($feed, $owner_uid);
bda6afa2
AD
1127 array_push($children, $feed);
1128
1129 $children = join(",", $children);
1130
1131 $cat_qpart = "cat_id IN ($children)";
f9fca8cb
AD
1132 } else {
1133 $cat_qpart = "cat_id IS NULL";
1134 }
8d505d78 1135
a42c55f0 1136 db_query("UPDATE ttrss_user_entries
c8b693cf
AD
1137 SET unread = false, last_read = NOW() WHERE ref_id IN
1138 (SELECT id FROM
1139 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1140 AND owner_uid = $owner_uid AND unread = true AND feed_id IN
1141 (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart) as tmp)");
23aa0d16 1142
f9fca8cb 1143 } else if ($feed == -2) {
23aa0d16 1144
a42c55f0 1145 db_query("UPDATE ttrss_user_entries
8d505d78
AD
1146 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1147 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
c8b693cf 1148 AND unread = true AND $date_qpart AND owner_uid = $owner_uid");
23aa0d16
AD
1149 }
1150
1151 } else if ($feed > 0) {
1152
a42c55f0 1153 db_query("UPDATE ttrss_user_entries
c8b693cf
AD
1154 SET unread = false, last_read = NOW() WHERE ref_id IN
1155 (SELECT id FROM
1156 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1157 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart) as tmp)");
8d505d78 1158
f822a8e5 1159 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX) { // special, like starred
23aa0d16
AD
1160
1161 if ($feed == -1) {
a42c55f0 1162 db_query("UPDATE ttrss_user_entries
c8b693cf
AD
1163 SET unread = false, last_read = NOW() WHERE ref_id IN
1164 (SELECT id FROM
1165 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1166 AND owner_uid = $owner_uid AND unread = true AND marked = true AND $date_qpart) as tmp)");
23aa0d16 1167 }
e4f4b46f
AD
1168
1169 if ($feed == -2) {
a42c55f0 1170 db_query("UPDATE ttrss_user_entries
c8b693cf
AD
1171 SET unread = false, last_read = NOW() WHERE ref_id IN
1172 (SELECT id FROM
1173 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1174 AND owner_uid = $owner_uid AND unread = true AND published = true AND $date_qpart) as tmp)");
e4f4b46f
AD
1175 }
1176
2d24f032
AD
1177 if ($feed == -3) {
1178
a42c55f0 1179 $intl = get_pref("FRESH_ARTICLE_MAX_AGE");
c1d7e6c3 1180
2d24f032 1181 if (DB_TYPE == "pgsql") {
cfd34086 1182 $match_part = "date_entered > NOW() - INTERVAL '$intl hour' ";
2d24f032 1183 } else {
cfd34086 1184 $match_part = "date_entered > DATE_SUB(NOW(),
c1d7e6c3 1185 INTERVAL $intl HOUR) ";
2d24f032
AD
1186 }
1187
a42c55f0 1188 db_query("UPDATE ttrss_user_entries
c8b693cf
AD
1189 SET unread = false, last_read = NOW() WHERE ref_id IN
1190 (SELECT id FROM
1191 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
cfd34086 1192 AND owner_uid = $owner_uid AND unread = true AND $date_qpart AND $match_part) as tmp)");
2d24f032
AD
1193 }
1194
3584cb11 1195 if ($feed == -4) {
a42c55f0 1196 db_query("UPDATE ttrss_user_entries
c8b693cf
AD
1197 SET unread = false, last_read = NOW() WHERE ref_id IN
1198 (SELECT id FROM
1199 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1200 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
3584cb11
AD
1201 }
1202
f822a8e5 1203 } else if ($feed < LABEL_BASE_INDEX) { // label
23aa0d16 1204
f822a8e5 1205 $label_id = feed_to_label_id($feed);
23aa0d16 1206
a42c55f0 1207 db_query("UPDATE ttrss_user_entries
c8b693cf
AD
1208 SET unread = false, last_read = NOW() WHERE ref_id IN
1209 (SELECT id FROM
1210 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id
1211 AND label_id = '$label_id' AND ref_id = article_id
1212 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
23aa0d16 1213
23aa0d16 1214 }
ad0056a8 1215
a42c55f0 1216 ccache_update($feed, $owner_uid, $cat_view);
ad0056a8 1217
23aa0d16 1218 } else { // tag
a42c55f0 1219 db_query("UPDATE ttrss_user_entries
c8b693cf
AD
1220 SET unread = false, last_read = NOW() WHERE ref_id IN
1221 (SELECT id FROM
1222 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id
1223 AND post_int_id = int_id AND tag_name = '$feed'
1224 AND ttrss_user_entries.owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
23aa0d16 1225
23aa0d16
AD
1226 }
1227 }
1228
6322ac79
AD
1229 function getAllCounters() {
1230 $data = getGlobalCounters();
8d505d78 1231
6322ac79
AD
1232 $data = array_merge($data, getVirtCounters());
1233 $data = array_merge($data, getLabelCounters());
6f7798b6 1234 $data = array_merge($data, getFeedCounters());
6322ac79 1235 $data = array_merge($data, getCategoryCounters());
6a7817c1
AD
1236
1237 return $data;
8d505d78 1238 }
a9cb1f83 1239
a42c55f0 1240 function getCategoryTitle($cat_id) {
79178062
AD
1241
1242 if ($cat_id == -1) {
1243 return __("Special");
1244 } else if ($cat_id == -2) {
1245 return __("Labels");
1246 } else {
1247
a42c55f0 1248 $result = db_query("SELECT title FROM ttrss_feed_categories WHERE
79178062
AD
1249 id = '$cat_id'");
1250
1251 if (db_num_rows($result) == 1) {
1252 return db_fetch_result($result, 0, "title");
1253 } else {
f99759da 1254 return __("Uncategorized");
79178062
AD
1255 }
1256 }
1257 }
1258
1259
6322ac79 1260 function getCategoryCounters() {
6a7817c1 1261 $ret_arr = array();
bba7c4bf 1262
6a7817c1 1263 /* Labels category */
bba7c4bf 1264
8acc449c 1265 $cv = array("id" => -2, "kind" => "cat",
a42c55f0 1266 "counter" => getCategoryUnread(-2));
bba7c4bf 1267
6a7817c1 1268 array_push($ret_arr, $cv);
bba7c4bf 1269
a42c55f0 1270 $result = db_query("SELECT id AS cat_id, value AS unread,
2c5f231e
AD
1271 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1272 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
8d505d78
AD
1273 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1274 WHERE ttrss_cat_counters_cache.feed_id = id AND
fc9de939 1275 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
31375163 1276 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
a9cb1f83
AD
1277
1278 while ($line = db_fetch_assoc($result)) {
22fdebff 1279 $line["cat_id"] = (int) $line["cat_id"];
8a4c759e 1280
2c5f231e 1281 if ($line["num_children"] > 0) {
a42c55f0 1282 $child_counter = getCategoryChildrenUnread($line["cat_id"], $_SESSION["uid"]);
2c5f231e
AD
1283 } else {
1284 $child_counter = 0;
1285 }
1286
8acc449c 1287 $cv = array("id" => $line["cat_id"], "kind" => "cat",
0ef32f48 1288 "counter" => $line["unread"] + $child_counter);
6a7817c1
AD
1289
1290 array_push($ret_arr, $cv);
a9cb1f83 1291 }
d232a40f
AD
1292
1293 /* Special case: NULL category doesn't actually exist in the DB */
1294
9798b2b4 1295 $cv = array("id" => 0, "kind" => "cat",
a42c55f0 1296 "counter" => (int) ccache_find(0, $_SESSION["uid"], true));
d232a40f 1297
6a7817c1
AD
1298 array_push($ret_arr, $cv);
1299
1300 return $ret_arr;
a9cb1f83
AD
1301 }
1302
2c5f231e 1303 // only accepts real cats (>= 0)
a42c55f0 1304 function getCategoryChildrenUnread($cat, $owner_uid = false) {
2c5f231e
AD
1305 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1306
a42c55f0 1307 $result = db_query("SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
2c5f231e
AD
1308 AND owner_uid = $owner_uid");
1309
1310 $unread = 0;
1311
1312 while ($line = db_fetch_assoc($result)) {
a42c55f0
AD
1313 $unread += getCategoryUnread($line["id"], $owner_uid);
1314 $unread += getCategoryChildrenUnread($line["id"], $owner_uid);
2c5f231e
AD
1315 }
1316
1317 return $unread;
1318 }
1319
a42c55f0 1320 function getCategoryUnread($cat, $owner_uid = false) {
b6d486a3
AD
1321
1322 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
f295c368 1323
bba7c4bf 1324 if ($cat >= 0) {
18664970 1325
bba7c4bf
AD
1326 if ($cat != 0) {
1327 $cat_query = "cat_id = '$cat'";
1328 } else {
1329 $cat_query = "cat_id IS NULL";
1330 }
14073c0a 1331
a42c55f0 1332 $result = db_query("SELECT id FROM ttrss_feeds WHERE $cat_query
b6d486a3 1333 AND owner_uid = " . $owner_uid);
8d505d78 1334
bba7c4bf
AD
1335 $cat_feeds = array();
1336 while ($line = db_fetch_assoc($result)) {
1337 array_push($cat_feeds, "feed_id = " . $line["id"]);
1338 }
8d505d78 1339
bba7c4bf 1340 if (count($cat_feeds) == 0) return 0;
8d505d78 1341
bba7c4bf 1342 $match_part = implode(" OR ", $cat_feeds);
8d505d78 1343
a42c55f0 1344 $result = db_query("SELECT COUNT(int_id) AS unread
687bb90d
AD
1345 FROM ttrss_user_entries
1346 WHERE unread = true AND ($match_part)
1347 AND owner_uid = " . $owner_uid);
8d505d78 1348
bba7c4bf 1349 $unread = 0;
8d505d78 1350
bba7c4bf
AD
1351 # this needs to be rewritten
1352 while ($line = db_fetch_assoc($result)) {
1353 $unread += $line["unread"];
1354 }
8d505d78 1355
bba7c4bf
AD
1356 return $unread;
1357 } else if ($cat == -1) {
6f7798b6 1358 return getFeedUnread(-1) + getFeedUnread(-2) + getFeedUnread(-3) + getFeedUnread(0);
bba7c4bf 1359 } else if ($cat == -2) {
f295c368 1360
a42c55f0 1361 $result = db_query("
8d505d78 1362 SELECT COUNT(unread) AS unread FROM
687bb90d
AD
1363 ttrss_user_entries, ttrss_user_labels2
1364 WHERE article_id = ref_id AND unread = true
b2531a28 1365 AND ttrss_user_entries.owner_uid = '$owner_uid'");
ceb30ba4 1366
b2531a28 1367 $unread = db_fetch_result($result, 0, "unread");
f295c368 1368
b2531a28 1369 return $unread;
f295c368 1370
8d505d78 1371 }
f295c368
AD
1372 }
1373
a42c55f0
AD
1374 function getFeedUnread($feed, $is_cat = false) {
1375 return getFeedArticles($feed, $is_cat, true, $_SESSION["uid"]);
bdb7369b
AD
1376 }
1377
a42c55f0 1378 function getLabelUnread($label_id, $owner_uid = false) {
ceb30ba4
AD
1379 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1380
a42c55f0 1381 $result = db_query("SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
f360b028 1382 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
ceb30ba4
AD
1383
1384 if (db_num_rows($result) != 0) {
1385 return db_fetch_result($result, 0, "unread");
1386 } else {
1387 return 0;
1388 }
1389 }
1390
a42c55f0 1391 function getFeedArticles($feed, $is_cat = false, $unread_only = false,
2627f2d0
AD
1392 $owner_uid = false) {
1393
22fdebff 1394 $n_feed = (int) $feed;
687bb90d 1395 $need_entries = false;
f295c368 1396
2627f2d0
AD
1397 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1398
bdb7369b
AD
1399 if ($unread_only) {
1400 $unread_qpart = "unread = true";
1401 } else {
1402 $unread_qpart = "true";
1403 }
1404
f295c368 1405 if ($is_cat) {
a42c55f0 1406 return getCategoryUnread($n_feed, $owner_uid);
5417fbd7
AD
1407 } else if ($n_feed == -6) {
1408 return 0;
1409 } else if ($feed != "0" && $n_feed == 0) {
326469fc 1410
a42c55f0 1411 $feed = db_escape_string($feed);
c5701e70 1412
a42c55f0 1413 $result = db_query("SELECT SUM((SELECT COUNT(int_id)
8d505d78 1414 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
687bb90d 1415 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
326469fc
AD
1416 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1417 return db_fetch_result($result, 0, "count");
1418
f295c368 1419 } else if ($n_feed == -1) {
a9cb1f83 1420 $match_part = "marked = true";
e4f4b46f
AD
1421 } else if ($n_feed == -2) {
1422 $match_part = "published = true";
2d24f032 1423 } else if ($n_feed == -3) {
cd2cc43d 1424 $match_part = "unread = true AND score >= 0";
2d24f032 1425
a42c55f0 1426 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
c1d7e6c3 1427
2d24f032 1428 if (DB_TYPE == "pgsql") {
924a89eb 1429 $match_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2d24f032 1430 } else {
924a89eb 1431 $match_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2d24f032 1432 }
687bb90d
AD
1433
1434 $need_entries = true;
1435
b2531a28
AD
1436 } else if ($n_feed == -4) {
1437 $match_part = "true";
e04c18a2 1438 } else if ($n_feed >= 0) {
831ff047 1439
6e63a7c3
AD
1440 if ($n_feed != 0) {
1441 $match_part = "feed_id = '$n_feed'";
831ff047 1442 } else {
6e63a7c3 1443 $match_part = "feed_id IS NULL";
831ff047 1444 }
6e63a7c3 1445
f822a8e5 1446 } else if ($feed < LABEL_BASE_INDEX) {
318260cc 1447
f822a8e5 1448 $label_id = feed_to_label_id($feed);
a9cb1f83 1449
a42c55f0 1450 return getLabelUnread($label_id, $owner_uid);
a9cb1f83 1451
a9cb1f83
AD
1452 }
1453
1454 if ($match_part) {
e04c18a2 1455
687bb90d 1456 if ($need_entries) {
e04c18a2 1457 $from_qpart = "ttrss_user_entries,ttrss_entries";
687bb90d
AD
1458 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1459 } else {
1460 $from_qpart = "ttrss_user_entries";
e04c18a2
AD
1461 }
1462
8d505d78 1463 $query = "SELECT count(int_id) AS unread
e04c18a2 1464 FROM $from_qpart WHERE
687bb90d
AD
1465 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1466
1467 //echo "[$feed/$query]\n";
dbfc4365 1468
a42c55f0 1469 $result = db_query($query);
8d505d78 1470
a9cb1f83 1471 } else {
8d505d78 1472
a42c55f0 1473 $result = db_query("SELECT COUNT(post_int_id) AS unread
8d505d78
AD
1474 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1475 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
687bb90d 1476 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
a9cb1f83 1477 }
8d505d78 1478
a9cb1f83 1479 $unread = db_fetch_result($result, 0, "unread");
cfb02131 1480
a9cb1f83
AD
1481 return $unread;
1482 }
1483
a42c55f0 1484 function getGlobalUnread($user_id = false) {
f3acc32e
AD
1485
1486 if (!$user_id) {
1487 $user_id = $_SESSION["uid"];
1488 }
1489
a42c55f0 1490 $result = db_query("SELECT SUM(value) AS c_id FROM ttrss_counters_cache
8a4c759e
AD
1491 WHERE owner_uid = '$user_id' AND feed_id > 0");
1492
8d505d78 1493 $c_id = db_fetch_result($result, 0, "c_id");
8a4c759e 1494
a9cb1f83
AD
1495 return $c_id;
1496 }
1497
a42c55f0 1498 function getGlobalCounters($global_unread = -1) {
6a7817c1
AD
1499 $ret_arr = array();
1500
8d505d78 1501 if ($global_unread == -1) {
6322ac79 1502 $global_unread = getGlobalUnread();
a9cb1f83 1503 }
6a7817c1 1504
8d505d78 1505 $cv = array("id" => "global-unread",
12e6de72 1506 "counter" => (int) $global_unread);
6a7817c1
AD
1507
1508 array_push($ret_arr, $cv);
7bf7e4d3 1509
a42c55f0 1510 $result = db_query("SELECT COUNT(id) AS fn FROM
7bf7e4d3
AD
1511 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1512
1513 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1514
8d505d78 1515 $cv = array("id" => "subscribed-feeds",
12e6de72 1516 "counter" => (int) $subscribed_feeds);
7bf7e4d3 1517
6a7817c1
AD
1518 array_push($ret_arr, $cv);
1519
1520 return $ret_arr;
a9cb1f83
AD
1521 }
1522
6322ac79 1523 function getVirtCounters() {
a9cb1f83 1524
ef393de7 1525 $ret_arr = array();
bdb7369b 1526
e04c18a2 1527 for ($i = 0; $i >= -4; $i--) {
bdb7369b 1528
a42c55f0 1529 $count = getFeedUnread($i);
6a7817c1 1530
c594eca0
AD
1531 if ($i == 0 || $i == -1 || $i == -2)
1532 $auxctr = getFeedArticles($i, false);
1533 else
1534 $auxctr = 0;
1535
6a7817c1 1536 $cv = array("id" => $i,
c594eca0
AD
1537 "counter" => (int) $count,
1538 "auxcounter" => $auxctr);
8d505d78 1539
a42c55f0
AD
1540// if (get_pref('EXTENDED_FEEDLIST'))
1541// $cv["xmsg"] = getFeedArticles($i)." ".__("total");
bdb7369b 1542
6a7817c1 1543 array_push($ret_arr, $cv);
8d505d78 1544 }
0a6e5382 1545
1ffe3391 1546 $feeds = PluginHost::getInstance()->get_feeds(-1);
a413f53e 1547
1ffe3391
AD
1548 if (is_array($feeds)) {
1549 foreach ($feeds as $feed) {
1550 $cv = array("id" => PluginHost::pfeed_to_feed_id($feed['id']),
1551 "counter" => $feed['sender']->get_unread($feed['id']));
a6a61a8c
AD
1552
1553 if (method_exists($feed['sender'], 'get_total'))
1554 $cv["auxcounter"] = $feed['sender']->get_total($feed['id']);
1555
1556 array_push($ret_arr, $cv);
a413f53e 1557 }
a413f53e
AD
1558 }
1559
0a6e5382
AD
1560 return $ret_arr;
1561 }
1562
a42c55f0 1563 function getLabelCounters($descriptions = false) {
6a7817c1
AD
1564
1565 $ret_arr = array();
0a6e5382 1566
3809b278 1567 $owner_uid = $_SESSION["uid"];
bdb7369b 1568
4d8f4c59 1569 $result = db_query("SELECT id,caption,SUM(CASE WHEN u1.unread = true THEN 1 ELSE 0 END) AS unread, COUNT(u1.unread) AS total
45942238
AD
1570 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1571 (ttrss_labels2.id = label_id)
4d8f4c59 1572 LEFT JOIN ttrss_user_entries AS u1 ON u1.ref_id = article_id
123a7643
AD
1573 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1574 ttrss_labels2.caption");
8d505d78 1575
3809b278 1576 while ($line = db_fetch_assoc($result)) {
2d24f032 1577
f822a8e5 1578 $id = label_to_feed_id($line["id"]);
e4f4b46f 1579
6a7817c1 1580 $cv = array("id" => $id,
d7b5a9e7
AD
1581 "counter" => (int) $line["unread"],
1582 "auxcounter" => (int) $line["total"]);
11232703
AD
1583
1584 if ($descriptions)
d7b5a9e7 1585 $cv["description"] = $line["caption"];
ef393de7 1586
6a7817c1 1587 array_push($ret_arr, $cv);
3809b278 1588 }
8d505d78 1589
ef393de7 1590 return $ret_arr;
a9cb1f83
AD
1591 }
1592
a42c55f0 1593 function getFeedCounters($active_feed = false) {
a9cb1f83 1594
6a7817c1
AD
1595 $ret_arr = array();
1596
41759103 1597 $query = "SELECT ttrss_feeds.id,
8a4c759e 1598 ttrss_feeds.title,
8d505d78 1599 ".SUBSTRING_FOR_DATE."(ttrss_feeds.last_updated,1,19) AS last_updated,
de0a2122
AD
1600 last_error, value AS count
1601 FROM ttrss_feeds, ttrss_counters_cache
8d505d78 1602 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
fc9de939 1603 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
55e01d7e 1604 AND ttrss_counters_cache.feed_id = id";
a9cb1f83 1605
a42c55f0 1606 $result = db_query($query);
a9cb1f83
AD
1607 $fctrs_modified = false;
1608
1609 while ($line = db_fetch_assoc($result)) {
8d505d78 1610
a9cb1f83 1611 $id = $line["id"];
de0a2122 1612 $count = $line["count"];
a9cb1f83 1613 $last_error = htmlspecialchars($line["last_error"]);
fb1fb4ab 1614
a42c55f0 1615 $last_updated = make_local_datetime($line['last_updated'], false);
fb1fb4ab 1616
7defa089 1617 $has_img = feed_has_icon($id);
a9cb1f83 1618
428b704d
AD
1619 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1620 $last_updated = '';
1621
6a7817c1 1622 $cv = array("id" => $id,
21884958 1623 "updated" => $last_updated,
12e6de72 1624 "counter" => (int) $count,
6a7817c1 1625 "has_img" => (int) $has_img);
a9cb1f83 1626
6a7817c1
AD
1627 if ($last_error)
1628 $cv["error"] = $last_error;
4ffa126e 1629
a42c55f0
AD
1630// if (get_pref('EXTENDED_FEEDLIST'))
1631// $cv["xmsg"] = getFeedArticles($id)." ".__("total");
bdb7369b 1632
6a7817c1 1633 if ($active_feed && $id == $active_feed)
fbc95c5b 1634 $cv["title"] = truncate_string($line["title"], 30);
6a7817c1
AD
1635
1636 array_push($ret_arr, $cv);
a9cb1f83 1637
a9cb1f83 1638 }
6a7817c1
AD
1639
1640 return $ret_arr;
a9cb1f83
AD
1641 }
1642
6322ac79 1643 function get_pgsql_version() {
a42c55f0 1644 $result = db_query("SELECT version() AS version");
9949bd15 1645 $version = explode(" ", db_fetch_result($result, 0, "version"));
6e7f8d26
AD
1646 return $version[1];
1647 }
1648
2b8290cd 1649 /**
23d2471c
AD
1650 * @return array (code => Status code, message => error message if available)
1651 *
2b8290cd
CW
1652 * 0 - OK, Feed already exists
1653 * 1 - OK, Feed added
1654 * 2 - Invalid URL
9a8ce956
CW
1655 * 3 - URL content is HTML, no feeds available
1656 * 4 - URL content is HTML which contains multiple feeds.
1657 * Here you should call extractfeedurls in rpc-backend
1658 * to get all possible feeds.
5414ad4c 1659 * 5 - Couldn't download the URL content.
ebec81a6 1660 * 6 - Content is an invalid XML.
2b8290cd 1661 */
a42c55f0 1662 function subscribe_to_feed($url, $cat_id = 0,
efc6553d 1663 $auth_login = '', $auth_pass = '') {
bb0f29a4 1664
23d2471c
AD
1665 global $fetch_last_error;
1666
2c08214a
AD
1667 require_once "include/rssfuncs.php";
1668
f0266f51 1669 $url = fix_url($url);
ec39a02c 1670
23d2471c 1671 if (!$url || !validate_feed_url($url)) return array("code" => 2);
a5819bb3 1672
759e5132
AD
1673 $contents = @fetch_file_contents($url, false, $auth_login, $auth_pass);
1674
1675 if (!$contents) {
304aadb9 1676 return array("code" => 5, "message" => $fetch_last_error);
759e5132
AD
1677 }
1678
1679 if (is_html($contents)) {
1680 $feedUrls = get_feeds_from_html($url, $contents);
304aadb9 1681
304aadb9
AD
1682 if (count($feedUrls) == 0) {
1683 return array("code" => 3);
1684 } else if (count($feedUrls) > 1) {
759e5132 1685 return array("code" => 4, "feeds" => $feedUrls);
f6d8345b 1686 }
304aadb9
AD
1687 //use feed url as new URL
1688 $url = key($feedUrls);
1689 }
f6d8345b 1690
9ce600c8 1691 /* libxml_use_internal_errors(true);
ebec81a6 1692 $doc = new DOMDocument();
9ce600c8 1693 $doc->loadXML($contents);
ebec81a6
AD
1694 $error = libxml_get_last_error();
1695 libxml_clear_errors();
1696
1697 if ($error) {
1698 $error_message = format_libxml_error($error);
1699
1700 return array("code" => 6, "message" => $error_message);
9ce600c8 1701 } */
ebec81a6 1702
956c7629
AD
1703 if ($cat_id == "0" || !$cat_id) {
1704 $cat_qpart = "NULL";
1705 } else {
1706 $cat_qpart = "'$cat_id'";
1707 }
8d505d78 1708
6322ac79 1709 $result = db_query(
8d505d78 1710 "SELECT id FROM ttrss_feeds
a5819bb3 1711 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
8d505d78 1712
044cff2d
AD
1713 if (strlen(FEED_CRYPT_KEY) > 0) {
1714 require_once "crypt.php";
1715 $auth_pass = substr(encrypt_string($auth_pass), 0, 250);
1716 $auth_pass_encrypted = 'true';
1717 } else {
1718 $auth_pass_encrypted = 'false';
1719 }
1720
a42c55f0 1721 $auth_pass = db_escape_string($auth_pass);
41694a95 1722
956c7629 1723 if (db_num_rows($result) == 0) {
6322ac79 1724 $result = db_query(
8d505d78 1725 "INSERT INTO ttrss_feeds
044cff2d 1726 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method,auth_pass_encrypted)
8d505d78 1727 VALUES ('".$_SESSION["uid"]."', '$url',
044cff2d 1728 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0, $auth_pass_encrypted)");
8d505d78 1729
6322ac79 1730 $result = db_query(
8d505d78 1731 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
f27de515 1732 AND owner_uid = " . $_SESSION["uid"]);
8d505d78 1733
956c7629 1734 $feed_id = db_fetch_result($result, 0, "id");
8d505d78 1735
956c7629 1736 if ($feed_id) {
fd687300 1737 update_rss_feed($feed_id, true);
956c7629
AD
1738 }
1739
23d2471c 1740 return array("code" => 1);
956c7629 1741 } else {
23d2471c 1742 return array("code" => 0);
956c7629
AD
1743 }
1744 }
1745
a42c55f0 1746 function print_feed_select($id, $default_id = "",
4c9d0490
AD
1747 $attributes = "", $include_all_feeds = true,
1748 $root_id = false, $nest_level = 0) {
1749
1750 if (!$root_id) {
1751 print "<select id=\"$id\" name=\"$id\" $attributes>";
1752 if ($include_all_feeds) {
1753 $is_selected = ("0" == $default_id) ? "selected=\"1\"" : "";
1754 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1755 }
673d54ca 1756 }
8d505d78 1757
a42c55f0 1758 if (get_pref('ENABLE_FEED_CATS')) {
673d54ca 1759
4c9d0490
AD
1760 if ($root_id)
1761 $parent_qpart = "parent_cat = '$root_id'";
1762 else
1763 $parent_qpart = "parent_cat IS NULL";
673d54ca 1764
a42c55f0 1765 $result = db_query("SELECT id,title,
4c9d0490
AD
1766 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1767 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1768 FROM ttrss_feed_categories
1769 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1770
1771 while ($line = db_fetch_assoc($result)) {
1772
1773 for ($i = 0; $i < $nest_level; $i++)
1774 $line["title"] = " - " . $line["title"];
1775
1776 $is_selected = ("CAT:".$line["id"] == $default_id) ? "selected=\"1\"" : "";
1777
1778 printf("<option $is_selected value='CAT:%d'>%s</option>",
1779 $line["id"], htmlspecialchars($line["title"]));
1780
1781 if ($line["num_children"] > 0)
a42c55f0 1782 print_feed_select($id, $default_id, $attributes,
4c9d0490
AD
1783 $include_all_feeds, $line["id"], $nest_level+1);
1784
a42c55f0 1785 $feed_result = db_query("SELECT id,title FROM ttrss_feeds
4c9d0490
AD
1786 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1787
1788 while ($fline = db_fetch_assoc($feed_result)) {
1789 $is_selected = ($fline["id"] == $default_id) ? "selected=\"1\"" : "";
1790
1791 $fline["title"] = " + " . $fline["title"];
1792
1793 for ($i = 0; $i < $nest_level; $i++)
1794 $fline["title"] = " - " . $fline["title"];
1795
1796 printf("<option $is_selected value='%d'>%s</option>",
1797 $fline["id"], htmlspecialchars($fline["title"]));
1798 }
673d54ca 1799 }
b1710666 1800
4c9d0490 1801 if (!$root_id) {
6f7798b6
RL
1802 $default_is_cat = ($default_id == "CAT:0");
1803 $is_selected = $default_is_cat ? "selected=\"1\"" : "";
4c9d0490
AD
1804
1805 printf("<option $is_selected value='CAT:0'>%s</option>",
1806 __("Uncategorized"));
1807
a42c55f0 1808 $feed_result = db_query("SELECT id,title FROM ttrss_feeds
4c9d0490
AD
1809 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1810
1811 while ($fline = db_fetch_assoc($feed_result)) {
1812 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ? "selected=\"1\"" : "";
1813
1814 $fline["title"] = " + " . $fline["title"];
1815
1816 for ($i = 0; $i < $nest_level; $i++)
1817 $fline["title"] = " - " . $fline["title"];
1818
1819 printf("<option $is_selected value='%d'>%s</option>",
1820 $fline["id"], htmlspecialchars($fline["title"]));
1821 }
1822 }
b1710666 1823
4c9d0490 1824 } else {
a42c55f0 1825 $result = db_query("SELECT id,title FROM ttrss_feeds
4c9d0490
AD
1826 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1827
1828 while ($line = db_fetch_assoc($result)) {
1829
1830 $is_selected = ($line["id"] == $default_id) ? "selected=\"1\"" : "";
1831
1832 printf("<option $is_selected value='%d'>%s</option>",
1833 $line["id"], htmlspecialchars($line["title"]));
1834 }
673d54ca 1835 }
8d505d78 1836
4c9d0490
AD
1837 if (!$root_id) {
1838 print "</select>";
1839 }
673d54ca
AD
1840 }
1841
a42c55f0 1842 function print_feed_cat_select($id, $default_id,
fbf85cf6 1843 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
8d505d78 1844
fbf85cf6
AD
1845 if (!$root_id) {
1846 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1847 }
673d54ca 1848
fbf85cf6
AD
1849 if ($root_id)
1850 $parent_qpart = "parent_cat = '$root_id'";
1851 else
1852 $parent_qpart = "parent_cat IS NULL";
673d54ca 1853
a42c55f0 1854 $result = db_query("SELECT id,title,
fbf85cf6
AD
1855 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1856 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1857 FROM ttrss_feed_categories
1858 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
673d54ca 1859
fbf85cf6
AD
1860 while ($line = db_fetch_assoc($result)) {
1861 if ($line["id"] == $default_id) {
1862 $is_selected = "selected=\"1\"";
1863 } else {
1864 $is_selected = "";
1865 }
673d54ca 1866
fbf85cf6
AD
1867 for ($i = 0; $i < $nest_level; $i++)
1868 $line["title"] = " - " . $line["title"];
c00907f2 1869
fbf85cf6
AD
1870 if ($line["title"])
1871 printf("<option $is_selected value='%d'>%s</option>",
1872 $line["id"], htmlspecialchars($line["title"]));
673d54ca 1873
fbf85cf6 1874 if ($line["num_children"] > 0)
a42c55f0 1875 print_feed_cat_select($id, $default_id, $attributes,
fbf85cf6
AD
1876 $include_all_cats, $line["id"], $nest_level+1);
1877 }
5c7c7da9 1878
fbf85cf6
AD
1879 if (!$root_id) {
1880 if ($include_all_cats) {
1881 if (db_num_rows($result) > 0) {
1882 print "<option disabled=\"1\">--------</option>";
1883 }
7e18f8e7
AD
1884
1885 if ($default_id == 0) {
1886 $is_selected = "selected=\"1\"";
1887 } else {
1888 $is_selected = "";
1889 }
1890
1891 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
fbf85cf6
AD
1892 }
1893 print "</select>";
1894 }
1895 }
8d505d78 1896
14f69488
AD
1897 function checkbox_to_sql_bool($val) {
1898 return ($val == "on") ? "true" : "false";
1899 }
86b682ce 1900
a42c55f0 1901 function getFeedCatTitle($id) {
86b682ce 1902 if ($id == -1) {
d1db26aa 1903 return __("Special");
f822a8e5 1904 } else if ($id < LABEL_BASE_INDEX) {
d1db26aa 1905 return __("Labels");
86b682ce 1906 } else if ($id > 0) {
a42c55f0 1907 $result = db_query("SELECT ttrss_feed_categories.title
86b682ce
AD
1908 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1909 cat_id = ttrss_feed_categories.id");
1910 if (db_num_rows($result) == 1) {
1911 return db_fetch_result($result, 0, "title");
1912 } else {
d1db26aa 1913 return __("Uncategorized");
86b682ce
AD
1914 }
1915 } else {
1916 return "getFeedCatTitle($id) failed";
1917 }
1918
1919 }
1920
9299102f 1921 function getFeedIcon($id) {
af88c48a 1922 switch ($id) {
4bee8b5f
AD
1923 case 0:
1924 return "images/archive.png";
1925 break;
af88c48a 1926 case -1:
2f20dd58 1927 return "images/star.png";
af88c48a
AD
1928 break;
1929 case -2:
2f20dd58 1930 return "images/feed.png";
af88c48a
AD
1931 break;
1932 case -3:
1933 return "images/fresh.png";
1934 break;
1935 case -4:
2f20dd58 1936 return "images/folder.png";
af88c48a 1937 break;
5417fbd7 1938 case -6:
2f20dd58 1939 return "images/time.png";
5417fbd7 1940 break;
af88c48a 1941 default:
f822a8e5 1942 if ($id < LABEL_BASE_INDEX) {
4bee8b5f
AD
1943 return "images/label.png";
1944 } else {
8d505d78 1945 if (file_exists(ICONS_DIR . "/$id.ico"))
e2eda979 1946 return ICONS_URL . "/$id.ico";
4bee8b5f 1947 }
af88c48a
AD
1948 break;
1949 }
89473cb5
AD
1950
1951 return false;
af88c48a
AD
1952 }
1953
a42c55f0 1954 function getFeedTitle($id, $cat = false) {
fd994f1a 1955 if ($cat) {
a42c55f0 1956 return getCategoryTitle($id);
fd994f1a 1957 } else if ($id == -1) {
d1db26aa 1958 return __("Starred articles");
945c243e
AD
1959 } else if ($id == -2) {
1960 return __("Published articles");
2d24f032
AD
1961 } else if ($id == -3) {
1962 return __("Fresh articles");
b2531a28
AD
1963 } else if ($id == -4) {
1964 return __("All articles");
80db1113 1965 } else if ($id === 0 || $id === "0") {
e04c18a2 1966 return __("Archived articles");
5417fbd7
AD
1967 } else if ($id == -6) {
1968 return __("Recently read");
f822a8e5
AD
1969 } else if ($id < LABEL_BASE_INDEX) {
1970 $label_id = feed_to_label_id($id);
a42c55f0 1971 $result = db_query("SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
86b682ce 1972 if (db_num_rows($result) == 1) {
ceb30ba4 1973 return db_fetch_result($result, 0, "caption");
86b682ce
AD
1974 } else {
1975 return "Unknown label ($label_id)";
1976 }
1977
147f5632 1978 } else if (is_numeric($id) && $id > 0) {
a42c55f0 1979 $result = db_query("SELECT title FROM ttrss_feeds WHERE id = '$id'");
86b682ce
AD
1980 if (db_num_rows($result) == 1) {
1981 return db_fetch_result($result, 0, "title");
1982 } else {
1983 return "Unknown feed ($id)";
1984 }
1985 } else {
22fdebff 1986 return $id;
86b682ce 1987 }
86b682ce 1988 }
3dd46f19 1989
6322ac79 1990 function make_init_params() {
f1f3a642 1991 $params = array();
c9268ed5 1992
f1f3a642
AD
1993 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1994 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
f17cac6b 1995 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE",
30b6ee8c 1996 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
40496720 1997
a42c55f0 1998 $params[strtolower($param)] = (int) get_pref($param);
f1f3a642 1999 }
40496720 2000
c4f7ba80
AD
2001 $params["icons_url"] = ICONS_URL;
2002 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME;
a42c55f0
AD
2003 $params["default_view_mode"] = get_pref("_DEFAULT_VIEW_MODE");
2004 $params["default_view_limit"] = (int) get_pref("_DEFAULT_VIEW_LIMIT");
2005 $params["default_view_order_by"] = get_pref("_DEFAULT_VIEW_ORDER_BY");
c4f7ba80 2006 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
88a41b64 2007 $params["label_base_index"] = (int) LABEL_BASE_INDEX;
59b223d7 2008
a42c55f0 2009 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
9b7ecc0a
AD
2010 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2011
8cd576a1
AD
2012 $max_feed_id = db_fetch_result($result, 0, "mid");
2013 $num_feeds = db_fetch_result($result, 0, "nf");
9b7ecc0a 2014
8cd576a1 2015 $params["max_feed_id"] = (int) $max_feed_id;
c4f7ba80 2016 $params["num_feeds"] = (int) $num_feeds;
8cd576a1 2017
6322ac79 2018 $params["hotkeys"] = get_hotkeys_map();
9b7ecc0a 2019
8484ce22 2020 $params["csrf_token"] = $_SESSION["csrf_token"];
f03701fe 2021 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
8484ce22 2022
6b1a4ecd 2023 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE;
8b83bf5f 2024
d8221301 2025 return $params;
3ac2b520 2026 }
f54f515f 2027
6322ac79 2028 function get_hotkeys_info() {
b8cb4d08
AD
2029 $hotkeys = array(
2030 __("Navigation") => array(
2031 "next_feed" => __("Open next feed"),
2032 "prev_feed" => __("Open previous feed"),
2033 "next_article" => __("Open next article"),
2034 "prev_article" => __("Open previous article"),
c22580b5
AD
2035 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
2036 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
8b35d171
SC
2037 "next_article_noexpand" => __("Move to next article (don't expand or mark read)"),
2038 "prev_article_noexpand" => __("Move to previous article (don't expand or mark read)"),
b8cb4d08
AD
2039 "search_dialog" => __("Show search dialog")),
2040 __("Article") => array(
2041 "toggle_mark" => __("Toggle starred"),
2042 "toggle_publ" => __("Toggle published"),
2043 "toggle_unread" => __("Toggle unread"),
2044 "edit_tags" => __("Edit tags"),
2045 "dismiss_selected" => __("Dismiss selected"),
2046 "dismiss_read" => __("Dismiss read"),
2047 "open_in_new_window" => __("Open in new window"),
2048 "catchup_below" => __("Mark below as read"),
2049 "catchup_above" => __("Mark above as read"),
2050 "article_scroll_down" => __("Scroll down"),
2051 "article_scroll_up" => __("Scroll up"),
2052 "select_article_cursor" => __("Select article under cursor"),
1bcf8f45 2053 "email_article" => __("Email article"),
414191d4 2054 "close_article" => __("Close/collapse article"),
8b35d171 2055 "toggle_expand" => __("Toggle article expansion (combined mode)"),
2ccc7b8e
AD
2056 "toggle_widescreen" => __("Toggle widescreen mode"),
2057 "toggle_embed_original" => __("Toggle embed original")),
b8cb4d08
AD
2058 __("Article selection") => array(
2059 "select_all" => __("Select all articles"),
2060 "select_unread" => __("Select unread"),
2061 "select_marked" => __("Select starred"),
2062 "select_published" => __("Select published"),
2063 "select_invert" => __("Invert selection"),
2064 "select_none" => __("Deselect everything")),
2065 __("Feed") => array(
2066 "feed_refresh" => __("Refresh current feed"),
2067 "feed_unhide_read" => __("Un/hide read feeds"),
2068 "feed_subscribe" => __("Subscribe to feed"),
2069 "feed_edit" => __("Edit feed"),
2070 "feed_catchup" => __("Mark as read"),
2071 "feed_reverse" => __("Reverse headlines"),
43f775de 2072 "feed_debug_update" => __("Debug feed update"),
b8cb4d08 2073 "catchup_all" => __("Mark all feeds as read"),
4b27f0c0 2074 "cat_toggle_collapse" => __("Un/collapse current category"),
60b88b25
AD
2075 "toggle_combined_mode" => __("Toggle combined mode"),
2076 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
b8cb4d08
AD
2077 __("Go to") => array(
2078 "goto_all" => __("All articles"),
2079 "goto_fresh" => __("Fresh"),
2080 "goto_marked" => __("Starred"),
2081 "goto_published" => __("Published"),
2082 "goto_tagcloud" => __("Tag cloud"),
2083 "goto_prefs" => __("Preferences")),
2084 __("Other") => array(
2085 "create_label" => __("Create label"),
2086 "create_filter" => __("Create filter"),
2087 "collapse_sidebar" => __("Un/collapse sidebar"),
2088 "help_dialog" => __("Show help dialog"))
2089 );
2090
1ffe3391 2091 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_HOTKEY_INFO) as $plugin) {
47854200
DA
2092 $hotkeys = $plugin->hook_hotkey_info($hotkeys);
2093 }
2094
b8cb4d08
AD
2095 return $hotkeys;
2096 }
2097
6322ac79 2098 function get_hotkeys_map() {
a83b58f1 2099 $hotkeys = array(
e218c5f5
AD
2100// "navigation" => array(
2101 "k" => "next_feed",
2102 "j" => "prev_feed",
2103 "n" => "next_article",
2104 "p" => "prev_article",
e5e2cf3b
AD
2105 "(38)|up" => "prev_article",
2106 "(40)|down" => "next_article",
da15c140
AD
2107// "^(38)|Ctrl-up" => "prev_article_noscroll",
2108// "^(40)|Ctrl-down" => "next_article_noscroll",
e5e2cf3b 2109 "(191)|/" => "search_dialog",
e218c5f5
AD
2110// "article" => array(
2111 "s" => "toggle_mark",
5b18c936 2112 "*s" => "toggle_publ",
e218c5f5 2113 "u" => "toggle_unread",
5b18c936
AD
2114 "*t" => "edit_tags",
2115 "*d" => "dismiss_selected",
2116 "*x" => "dismiss_read",
e218c5f5
AD
2117 "o" => "open_in_new_window",
2118 "c p" => "catchup_below",
2119 "c n" => "catchup_above",
5b18c936
AD
2120 "*n" => "article_scroll_down",
2121 "*p" => "article_scroll_up",
d2db81a5
AD
2122 "*(38)|Shift+up" => "article_scroll_up",
2123 "*(40)|Shift+down" => "article_scroll_down",
5b18c936 2124 "a *w" => "toggle_widescreen",
2ccc7b8e 2125 "a e" => "toggle_embed_original",
e218c5f5 2126 "e" => "email_article",
2cda4314 2127 "a q" => "close_article",
e218c5f5
AD
2128// "article_selection" => array(
2129 "a a" => "select_all",
2130 "a u" => "select_unread",
5b18c936 2131 "a *u" => "select_marked",
e218c5f5
AD
2132 "a p" => "select_published",
2133 "a i" => "select_invert",
2134 "a n" => "select_none",
2135// "feed" => array(
2136 "f r" => "feed_refresh",
2137 "f a" => "feed_unhide_read",
2138 "f s" => "feed_subscribe",
2139 "f e" => "feed_edit",
2140 "f q" => "feed_catchup",
2141 "f x" => "feed_reverse",
5b18c936
AD
2142 "f *d" => "feed_debug_update",
2143 "f *c" => "toggle_combined_mode",
60b88b25 2144 "f c" => "toggle_cdm_expanded",
5b18c936 2145 "*q" => "catchup_all",
e218c5f5
AD
2146 "x" => "cat_toggle_collapse",
2147// "goto" => array(
2148 "g a" => "goto_all",
2149 "g f" => "goto_fresh",
2150 "g s" => "goto_marked",
2151 "g p" => "goto_published",
2152 "g t" => "goto_tagcloud",
5b18c936 2153 "g *p" => "goto_prefs",
e218c5f5 2154// "other" => array(
3fb40112 2155 "(9)|Tab" => "select_article_cursor", // tab
e218c5f5
AD
2156 "c l" => "create_label",
2157 "c f" => "create_filter",
2158 "c s" => "collapse_sidebar",
3fb40112 2159 "^(191)|Ctrl+/" => "help_dialog",
a83b58f1
AD
2160 );
2161
a42c55f0 2162 if (get_pref('COMBINED_DISPLAY_MODE')) {
da15c140
AD
2163 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2164 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2165 }
2166
1ffe3391 2167 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_HOTKEY_MAP) as $plugin) {
e218c5f5
AD
2168 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2169 }
2170
2171 $prefixes = array();
2172
2173 foreach (array_keys($hotkeys) as $hotkey) {
2174 $pair = explode(" ", $hotkey, 2);
2175
2176 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2177 array_push($prefixes, $pair[0]);
2178 }
2179 }
2180
2181 return array($prefixes, $hotkeys);
a83b58f1
AD
2182 }
2183
6322ac79 2184 function make_runtime_info() {
8cd576a1
AD
2185 $data = array();
2186
a42c55f0 2187 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
9b7ecc0a
AD
2188 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2189
8cd576a1
AD
2190 $max_feed_id = db_fetch_result($result, 0, "mid");
2191 $num_feeds = db_fetch_result($result, 0, "nf");
9b7ecc0a 2192
8cd576a1
AD
2193 $data["max_feed_id"] = (int) $max_feed_id;
2194 $data["num_feeds"] = (int) $num_feeds;
c4f7ba80 2195
6322ac79 2196 $data['last_article_id'] = getLastArticleId();
a42c55f0 2197 $data['cdm_expanded'] = get_pref('CDM_EXPANDED');
f8fb4498 2198
16314dda 2199 $data['dep_ts'] = calculate_dep_timestamp();
4cdb8173 2200 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
16314dda 2201
dbaa4e4a 2202 if (file_exists(LOCK_DIRECTORY . "/update_daemon.lock")) {
c4f7ba80
AD
2203
2204 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
8e00ae9b 2205
9041f58b 2206 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
8e00ae9b 2207
fb074239 2208 $stamp = (int) @file_get_contents(LOCK_DIRECTORY . "/update_daemon.stamp");
fbae93d8 2209
8e00ae9b 2210 if ($stamp) {
9041f58b
AD
2211 $stamp_delta = time() - $stamp;
2212
2213 if ($stamp_delta > 1800) {
f6854e44 2214 $stamp_check = 0;
8e00ae9b 2215 } else {
f6854e44
AD
2216 $stamp_check = 1;
2217 $_SESSION["daemon_stamp_check"] = time();
8e00ae9b
AD
2218 }
2219
c4f7ba80 2220 $data['daemon_stamp_ok'] = $stamp_check;
f6854e44 2221
8e00ae9b
AD
2222 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2223
c4f7ba80 2224 $data['daemon_stamp'] = $stamp_fmt;
8e00ae9b 2225 }
8e00ae9b 2226 }
71ad883b 2227 }
8e00ae9b 2228
63855db1 2229 if ($_SESSION["last_version_check"] + 86400 + rand(-1000, 1000) < time()) {
6322ac79 2230 $new_version_details = @check_for_update();
d9fa39f1 2231
63855db1 2232 $data['new_version_available'] = (int) ($new_version_details != false);
d9fa39f1
AD
2233
2234 $_SESSION["last_version_check"] = time();
27211afe 2235 $_SESSION["version_data"] = $new_version_details;
d9fa39f1
AD
2236 }
2237
c4f7ba80 2238 return $data;
f54f515f 2239 }
ef393de7 2240
a42c55f0 2241 function search_to_sql($search) {
ef393de7 2242
88040f57 2243 $search_query_part = "";
e20c9d88 2244
9949bd15 2245 $keywords = explode(" ", $search);
88040f57 2246 $query_keywords = array();
dd90eb2c 2247 $search_words = array();
e20c9d88 2248
ab4b768f
AD
2249 foreach ($keywords as $k) {
2250 if (strpos($k, "-") === 0) {
2251 $k = substr($k, 1);
2252 $not = "NOT";
2253 } else {
2254 $not = "";
88040f57 2255 }
e20c9d88 2256
9949bd15 2257 $commandpair = explode(":", mb_strtolower($k), 2);
53003548 2258
0fc3930b
AD
2259 switch ($commandpair[0]) {
2260 case "title":
2261 if ($commandpair[1]) {
2262 array_push($query_keywords, "($not (LOWER(ttrss_entries.title) LIKE '%".
a42c55f0 2263 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
efd840d8
AD
2264 } else {
2265 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2266 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
dd90eb2c 2267 array_push($search_words, $k);
0fc3930b
AD
2268 }
2269 break;
2270 case "author":
2271 if ($commandpair[1]) {
2272 array_push($query_keywords, "($not (LOWER(author) LIKE '%".
a42c55f0 2273 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
efd840d8
AD
2274 } else {
2275 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2276 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
dd90eb2c 2277 array_push($search_words, $k);
0fc3930b
AD
2278 }
2279 break;
2280 case "note":
2281 if ($commandpair[1]) {
2282 if ($commandpair[1] == "true")
2283 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2284 else if ($commandpair[1] == "false")
2285 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2286 else
2287 array_push($query_keywords, "($not (LOWER(note) LIKE '%".
a42c55f0 2288 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
efd840d8
AD
2289 } else {
2290 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2291 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
dd90eb2c 2292 if (!$not) array_push($search_words, $k);
0fc3930b
AD
2293 }
2294 break;
2295 case "star":
53003548 2296
0fc3930b
AD
2297 if ($commandpair[1]) {
2298 if ($commandpair[1] == "true")
2299 array_push($query_keywords, "($not (marked = true))");
2300 else
2301 array_push($query_keywords, "($not (marked = false))");
efd840d8
AD
2302 } else {
2303 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2304 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
dd90eb2c 2305 if (!$not) array_push($search_words, $k);
0fc3930b
AD
2306 }
2307 break;
2308 case "pub":
2309 if ($commandpair[1]) {
2310 if ($commandpair[1] == "true")
2311 array_push($query_keywords, "($not (published = true))");
2312 else
2313 array_push($query_keywords, "($not (published = false))");
53003548 2314
efd840d8
AD
2315 } else {
2316 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2317 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
dd90eb2c 2318 if (!$not) array_push($search_words, $k);
0fc3930b
AD
2319 }
2320 break;
2321 default:
2322 if (strpos($k, "@") === 0) {
e20c9d88 2323
a42c55f0 2324 $user_tz_string = get_pref('USER_TIMEZONE', $_SESSION['uid']);
0fc3930b
AD
2325 $orig_ts = strtotime(substr($k, 1));
2326 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
8d505d78 2327
0fc3930b 2328 //$k = date("Y-m-d", strtotime(substr($k, 1)));
53003548 2329
0fc3930b
AD
2330 array_push($query_keywords, "(".SUBSTRING_FOR_DATE."(updated,1,LENGTH('$k')) $not = '$k')");
2331 } else {
2332 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2333 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
dd90eb2c
AD
2334
2335 if (!$not) array_push($search_words, $k);
0fc3930b 2336 }
88040f57
AD
2337 }
2338 }
2339
2340 $search_query_part = implode("AND", $query_keywords);
2341
dd90eb2c 2342 return array($search_query_part, $search_words);
88040f57
AD
2343 }
2344
a42c55f0 2345 function getParentCategories($cat, $owner_uid) {
67bd0b1f
AD
2346 $rv = array();
2347
a42c55f0 2348 $result = db_query("SELECT parent_cat FROM ttrss_feed_categories
67bd0b1f
AD
2349 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2350
2351 while ($line = db_fetch_assoc($result)) {
2352 array_push($rv, $line["parent_cat"]);
a42c55f0 2353 $rv = array_merge($rv, getParentCategories($line["parent_cat"], $owner_uid));
67bd0b1f
AD
2354 }
2355
2356 return $rv;
2357 }
2358
a42c55f0 2359 function getChildCategories($cat, $owner_uid) {
6d8d00e8
AD
2360 $rv = array();
2361
a42c55f0 2362 $result = db_query("SELECT id FROM ttrss_feed_categories
6d8d00e8
AD
2363 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2364
2365 while ($line = db_fetch_assoc($result)) {
2366 array_push($rv, $line["id"]);
a42c55f0 2367 $rv = array_merge($rv, getChildCategories($line["id"], $owner_uid));
6d8d00e8
AD
2368 }
2369
2370 return $rv;
2371 }
147f5632 2372
a6a61a8c 2373 function queryFeedHeadlines($feed, $limit, $view_mode, $cat_view, $search, $search_mode, $override_order = false, $offset = 0, $owner_uid = 0, $filter = false, $since_id = 0, $include_children = false, $ignore_vfeed_group = false, $override_strategy = false, $override_vfeed = false) {
c36bf4d5
AD
2374
2375 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
c1a0b534 2376
c3fddd05 2377 $ext_tables_part = "";
dd90eb2c 2378 $search_words = array();
c3fddd05 2379
88040f57 2380 if ($search) {
e4f7f8df
AD
2381
2382 if (SPHINX_ENABLED) {
2383 $ids = join(",", @sphinx_search($search, 0, 500));
2384
8d505d78 2385 if ($ids)
e4f7f8df
AD
2386 $search_query_part = "ref_id IN ($ids) AND ";
2387 else
2388 $search_query_part = "ref_id = -1 AND ";
2389
2390 } else {
dd90eb2c 2391 list($search_query_part, $search_words) = search_to_sql($search);
e4f7f8df 2392 $search_query_part .= " AND ";
8d505d78 2393 }
e20c9d88 2394
ef393de7
AD
2395 } else {
2396 $search_query_part = "";
2397 }
2398
36184020 2399 if ($filter) {
4e02f582
AD
2400
2401 if (DB_TYPE == "pgsql") {
2402 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2403 } else {
2404 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2405 }
2406
2407 $override_order = "updated DESC";
2408
a42c55f0 2409 $filter_query_part = filter_to_sql($filter, $owner_uid);
dd8c36af
AD
2410
2411 // Try to check if SQL regexp implementation chokes on a valid regexp
7711086b
AD
2412
2413
a42c55f0 2414 $result = db_query("SELECT true AS true_val FROM ttrss_entries,
7711086b 2415 ttrss_user_entries, ttrss_feeds
dd8c36af
AD
2416 WHERE $filter_query_part LIMIT 1", false);
2417
7726063c
AD
2418 if ($result) {
2419 $test = db_fetch_result($result, 0, "true_val");
dd8c36af 2420
7726063c
AD
2421 if (!$test) {
2422 $filter_query_part = "false AND";
2423 } else {
2424 $filter_query_part .= " AND";
2425 }
dd8c36af 2426 } else {
7726063c 2427 $filter_query_part = "false AND";
dd8c36af
AD
2428 }
2429
36184020
AD
2430 } else {
2431 $filter_query_part = "";
2432 }
2433
97e5dbb2
AD
2434 if ($since_id) {
2435 $since_id_part = "ttrss_entries.id > $since_id AND ";
2436 } else {
2437 $since_id_part = "";
2438 }
2439
ef393de7 2440 $view_query_part = "";
8d505d78 2441
9b523c01 2442 if ($view_mode == "adaptive") {
ef393de7
AD
2443 if ($search) {
2444 $view_query_part = " ";
2445 } else if ($feed != -1) {
6d8d00e8 2446
a42c55f0 2447 $unread = getFeedUnread($feed, $cat_view);
f4a2f12a 2448
a6adb136 2449 if ($cat_view && $feed > 0 && $include_children)
a42c55f0 2450 $unread += getCategoryChildrenUnread($feed);
f4a2f12a 2451
a6adb136
AD
2452 if ($unread > 0)
2453 $view_query_part = " unread = true AND ";
f4a2f12a 2454
ef393de7
AD
2455 }
2456 }
8d505d78 2457
ef393de7
AD
2458 if ($view_mode == "marked") {
2459 $view_query_part = " marked = true AND ";
2460 }
23d72f39 2461
127aaaa0
AD
2462 if ($view_mode == "has_note") {
2463 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2464 }
2465
23d72f39
AD
2466 if ($view_mode == "published") {
2467 $view_query_part = " published = true AND ";
2468 }
2469
0bf65987 2470 if ($view_mode == "unread" && $feed != -6) {
ef393de7
AD
2471 $view_query_part = " unread = true AND ";
2472 }
8b09eac8 2473
ef393de7
AD
2474 if ($limit > 0) {
2475 $limit_query_part = "LIMIT " . $limit;
8d505d78 2476 }
ef393de7 2477
8361e724
AD
2478 $allow_archived = false;
2479
ef393de7 2480 $vfeed_query_part = "";
8d505d78 2481
ef393de7
AD
2482 // override query strategy and enable feed display when searching globally
2483 if ($search && $search_mode == "all_feeds") {
7032f2a5 2484 $query_strategy_part = "true";
8d505d78 2485 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
22fdebff 2486 /* tags */
75c648cf 2487 } else if (!is_numeric($feed)) {
7032f2a5 2488 $query_strategy_part = "true";
ef393de7
AD
2489 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2490 id = feed_id) as feed_title,";
7032f2a5 2491 } else if ($search && $search_mode == "this_cat") {
8d505d78 2492 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
0a6c4846 2493
7032f2a5
AD
2494 if ($feed > 0) {
2495 if ($include_children) {
a42c55f0 2496 $subcats = getChildCategories($feed, $owner_uid);
7032f2a5
AD
2497 array_push($subcats, $feed);
2498 $cats_qpart = join(",", $subcats);
2499 } else {
2500 $cats_qpart = $feed;
ef393de7 2501 }
8d505d78 2502
7032f2a5 2503 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
8d505d78 2504
ef393de7 2505 } else {
7032f2a5 2506 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
ef393de7 2507 }
8d505d78 2508
e04c18a2 2509 } else if ($feed > 0) {
8d505d78 2510
ef393de7 2511 if ($cat_view) {
5c365f60 2512
ef393de7 2513 if ($feed > 0) {
09101297
AD
2514 if ($include_children) {
2515 # sub-cats
a42c55f0 2516 $subcats = getChildCategories($feed, $owner_uid);
09101297 2517
7032f2a5
AD
2518 array_push($subcats, $feed);
2519 $query_strategy_part = "cat_id IN (".
09101297 2520 implode(",", $subcats).")";
7032f2a5 2521
6d8d00e8 2522 } else {
09101297 2523 $query_strategy_part = "cat_id = '$feed'";
6d8d00e8
AD
2524 }
2525
ef393de7
AD
2526 } else {
2527 $query_strategy_part = "cat_id IS NULL";
2528 }
8d505d78 2529
ef393de7 2530 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
5c365f60 2531
8d505d78 2532 } else {
6e63a7c3 2533 $query_strategy_part = "feed_id = '$feed'";
ef393de7 2534 }
bfe5ddfc 2535 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
e04c18a2 2536 $query_strategy_part = "feed_id IS NULL";
8361e724 2537 $allow_archived = true;
bfe5ddfc 2538 } else if ($feed == 0 && $cat_view) { // uncategorized
65dd90f2 2539 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
bfe5ddfc 2540 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
ef393de7
AD
2541 } else if ($feed == -1) { // starred virtual feed
2542 $query_strategy_part = "marked = true";
2543 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
835fb294
AD
2544 $allow_archived = true;
2545
1bd7413f 2546 if (!$override_order) {
b9a06a0e 2547 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
1bd7413f 2548 }
7873d588 2549
e6a38cde
AD
2550 } else if ($feed == -2) { // published virtual feed OR labels category
2551
2552 if (!$cat_view) {
2553 $query_strategy_part = "published = true";
2554 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
8361e724 2555 $allow_archived = true;
46b78149 2556
1bd7413f 2557 if (!$override_order) {
b9a06a0e 2558 $override_order = "last_published DESC, date_entered DESC, updated DESC";
1bd7413f
AD
2559 }
2560
e6a38cde
AD
2561 } else {
2562 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2563
2564 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
8d505d78 2565
e6a38cde
AD
2566 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2567 ttrss_user_labels2.article_id = ref_id";
2568
2569 }
5417fbd7 2570 } else if ($feed == -6) { // recently read
5089b30b 2571 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
5417fbd7 2572 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
835fb294 2573 $allow_archived = true;
46b78149
AD
2574
2575 if (!$override_order) $override_order = "last_read DESC";
a6a61a8c
AD
2576
2577/* } else if ($feed == -7) { // shared
2578 $query_strategy_part = "uuid != ''";
2579 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2580 $allow_archived = true; */
2d24f032 2581 } else if ($feed == -3) { // fresh virtual feed
cd2cc43d 2582 $query_strategy_part = "unread = true AND score >= 0";
2d24f032 2583
a42c55f0 2584 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
c1d7e6c3 2585
2d24f032 2586 if (DB_TYPE == "pgsql") {
be574731 2587 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2d24f032 2588 } else {
be574731 2589 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2d24f032
AD
2590 }
2591
b2531a28
AD
2592 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2593 } else if ($feed == -4) { // all articles virtual feed
cf57eb3c 2594 $allow_archived = true;
b2531a28 2595 $query_strategy_part = "true";
e4f4b46f 2596 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
f822a8e5
AD
2597 } else if ($feed <= LABEL_BASE_INDEX) { // labels
2598 $label_id = feed_to_label_id($feed);
3de0261a 2599
ceb30ba4
AD
2600 $query_strategy_part = "label_id = '$label_id' AND
2601 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2602 ttrss_user_labels2.article_id = ref_id";
3de0261a 2603
ef393de7 2604 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
ceb30ba4 2605 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
835fb294 2606 $allow_archived = true;
8d505d78 2607
ef393de7 2608 } else {
835fb294 2609 $query_strategy_part = "true";
ef393de7 2610 }
d6e5706d 2611
686852d5 2612 $order_by = "score DESC, date_entered DESC, updated DESC";
e939722a 2613
2e4faaac 2614 if ($view_mode == "unread_first") {
434bf856 2615 $order_by = "unread DESC, $order_by";
2e4faaac
AD
2616 }
2617
e939722a
AD
2618 if ($override_order) {
2619 $order_by = $override_order;
2620 }
8d505d78 2621
a6a61a8c
AD
2622 if ($override_strategy) {
2623 $query_strategy_part = $override_strategy;
2624 }
2625
2626 if ($override_vfeed) {
2627 $vfeed_query_part = $override_vfeed;
2628 }
2629
ef393de7
AD
2630 $feed_title = "";
2631
22fdebff 2632 if ($search) {
7032f2a5 2633 $feed_title = T_sprintf("Search results: %s", $search);
22fdebff 2634 } else {
ef393de7 2635 if ($cat_view) {
a42c55f0 2636 $feed_title = getCategoryTitle($feed);
ef393de7 2637 } else {
147f5632 2638 if (is_numeric($feed) && $feed > 0) {
6e3e8db9 2639 $result = db_query("SELECT title,site_url,last_error,last_updated
22fdebff 2640 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
8d505d78 2641
22fdebff
AD
2642 $feed_title = db_fetch_result($result, 0, "title");
2643 $feed_site_url = db_fetch_result($result, 0, "site_url");
2644 $last_error = db_fetch_result($result, 0, "last_error");
6e3e8db9 2645 $last_updated = db_fetch_result($result, 0, "last_updated");
22fdebff 2646 } else {
a42c55f0 2647 $feed_title = getFeedTitle($feed);
8d505d78 2648 }
88040f57 2649 }
ef393de7
AD
2650 }
2651
3b96b0ed 2652
c052e25a 2653 $content_query_part = "content, content AS content_preview, ";
62129e67 2654
62129e67 2655
75c648cf 2656 if (is_numeric($feed)) {
8d505d78 2657
ef393de7
AD
2658 if ($feed >= 0) {
2659 $feed_kind = "Feeds";
2660 } else {
2661 $feed_kind = "Labels";
2662 }
8d505d78 2663
95a82c08
AD
2664 if ($limit_query_part) {
2665 $offset_query_part = "OFFSET $offset";
2666 }
2667
7fdf8eca 2668 // proper override_order applied above
a42c55f0 2669 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) {
6cfea5c7 2670 if (!$override_order) {
8d505d78 2671 $order_by = "ttrss_feeds.title, $order_by";
7fdf8eca
AD
2672 } else {
2673 $order_by = "ttrss_feeds.title, $override_order";
43fc671f 2674 }
6cfea5c7
AD
2675 }
2676
8361e724 2677 if (!$allow_archived) {
e04c18a2 2678 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
117335bf 2679 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
e04c18a2
AD
2680
2681 } else {
835fb294 2682 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
e04c18a2
AD
2683 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2684 }
2685
63c323f7
AD
2686 if ($vfeed_query_part)
2687 $vfeed_query_part .= "favicon_avg_color,";
2688
8d505d78 2689 $query = "SELECT DISTINCT
f9b2d27c 2690 date_entered,
1f64b1be 2691 guid,
ef393de7 2692 ttrss_entries.id,ttrss_entries.title,
46921916 2693 updated,
9c506873
AD
2694 label_cache,
2695 tag_cache,
c0644ee4 2696 always_display_enclosures,
d1fc2f92 2697 site_url,
c7e51de1 2698 note,
13992673
AD
2699 num_comments,
2700 comments,
db16ae50 2701 int_id,
abb04b76 2702 uuid,
6b461797 2703 lang,
bfd61d3f 2704 hide_images,
494a64ea 2705 unread,feed_id,marked,published,link,last_read,orig_feed_id,
7873d588 2706 last_marked, last_published,
ef393de7
AD
2707 $vfeed_query_part
2708 $content_query_part
ff6e357a 2709 author,score
ef393de7 2710 FROM
e04c18a2 2711 $from_qpart
ef393de7 2712 WHERE
e04c18a2 2713 $feed_check_qpart
ef393de7 2714 ttrss_user_entries.ref_id = ttrss_entries.id AND
c36bf4d5 2715 ttrss_user_entries.owner_uid = '$owner_uid' AND
ef393de7 2716 $search_query_part
36184020 2717 $filter_query_part
ef393de7 2718 $view_query_part
97e5dbb2 2719 $since_id_part
ef393de7 2720 $query_strategy_part ORDER BY $order_by
95a82c08 2721 $limit_query_part $offset_query_part";
4bc311fc 2722
b4e75b2a 2723 if ($_REQUEST["debug"]) print $query;
4bc311fc 2724
a42c55f0 2725 $result = db_query($query);
8d505d78 2726
ef393de7
AD
2727 } else {
2728 // browsing by tag
8d505d78 2729
147f5632
CM
2730 $select_qpart = "SELECT DISTINCT " .
2731 "date_entered," .
2732 "guid," .
2733 "note," .
2734 "ttrss_entries.id as id," .
2735 "title," .
2736 "updated," .
2737 "unread," .
2738 "feed_id," .
2739 "orig_feed_id," .
2740 "marked," .
d1fc2f92
AD
2741 "num_comments, " .
2742 "comments, " .
c0644ee4
AD
2743 "tag_cache," .
2744 "label_cache," .
147f5632 2745 "link," .
6b461797 2746 "lang," .
abb04b76 2747 "uuid," .
147f5632 2748 "last_read," .
94a567df 2749 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
7873d588 2750 "last_marked, last_published, " .
97e5dbb2 2751 $since_id_part .
147f5632
CM
2752 $vfeed_query_part .
2753 $content_query_part .
147f5632
CM
2754 "score ";
2755
ef393de7 2756 $feed_kind = "Tags";
147f5632
CM
2757 $all_tags = explode(",", $feed);
2758 if ($search_mode == 'any') {
2759 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2760 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2761 $where_qpart = " WHERE " .
2762 "ref_id = ttrss_entries.id AND " .
2763 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2764 "post_int_id = int_id AND $tag_sql AND " .
2765 $view_query_part .
2766 $search_query_part .
2767 $query_strategy_part . " ORDER BY $order_by " .
2768 $limit_query_part;
8d505d78 2769
147f5632
CM
2770 } else {
2771 $i = 1;
2772 $sub_selects = array();
2773 $sub_ands = array();
2774 foreach ($all_tags as $term) {
2775 array_push($sub_selects, "(SELECT post_int_id from ttrss_tags WHERE tag_name = " . db_quote($term) . " AND owner_uid = $owner_uid) as A$i");
2776 $i++;
2777 }
2778 if ($i > 2) {
2779 $x = 1;
2780 $y = 2;
2781 do {
2782 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2783 $x++;
2784 $y++;
2785 } while ($y < $i);
2786 }
2787 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2788 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2789 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2790 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2791 }
2792 // error_log("TAG SQL: " . $tag_sql);
2793 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2794
2795 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
a42c55f0 2796 $result = db_query($select_qpart . $from_qpart . $where_qpart);
ef393de7
AD
2797 }
2798
dd90eb2c 2799 return array($result, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words);
8d505d78 2800
ef393de7
AD
2801 }
2802
910592b4 2803 function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false, $article_id = false) {
ceb0cab5
AD
2804 if (!$owner) $owner = $_SESSION["uid"];
2805
96811a55
AD
2806 $res = trim($str); if (!$res) return '';
2807
8cc3c778
AD
2808 $charset_hack = '<head>
2809 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2810 </head>';
2811
96811a55
AD
2812 $res = trim($res); if (!$res) return '';
2813
8cc3c778
AD
2814 libxml_use_internal_errors(true);
2815
2816 $doc = new DOMDocument();
2817 $doc->loadHTML($charset_hack . $res);
2818 $xpath = new DOMXPath($doc);
8d505d78 2819
8cc3c778
AD
2820 $entries = $xpath->query('(//a[@href]|//img[@src])');
2821
2822 foreach ($entries as $entry) {
2823
2824 if ($site_url) {
2825
891e36f5 2826 if ($entry->hasAttribute('href'))
8cc3c778
AD
2827 $entry->setAttribute('href',
2828 rewrite_relative_url($site_url, $entry->getAttribute('href')));
8d505d78 2829
f0bd8e65
AD
2830 if ($entry->hasAttribute('src')) {
2831 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2832
2833 $cached_filename = CACHE_DIR . '/images/' . sha1($src) . '.png';
2834
2835 if (file_exists($cached_filename)) {
2836 $src = SELF_URL_PATH . '/image.php?hash=' . sha1($src);
2837 }
2838
2839 $entry->setAttribute('src', $src);
2840 }
bfd61d3f
AD
2841
2842 if ($entry->nodeName == 'img') {
a42c55f0 2843 if (($owner && get_pref("STRIP_IMAGES", $owner)) ||
ba79634c 2844 $force_remove_images || $_SESSION["bw_limit"]) {
bfd61d3f
AD
2845
2846 $p = $doc->createElement('p');
2847
2848 $a = $doc->createElement('a');
2849 $a->setAttribute('href', $entry->getAttribute('src'));
2850
2851 $a->appendChild(new DOMText($entry->getAttribute('src')));
2852 $a->setAttribute('target', '_blank');
2853
2854 $p->appendChild($a);
2855
2856 $entry->parentNode->replaceChild($p, $entry);
2857 }
2858 }
8cc3c778
AD
2859 }
2860
fa403733 2861 if (strtolower($entry->nodeName) == "a") {
c401d5c9 2862 $entry->setAttribute("target", "_blank");
fa403733 2863 }
8dccabed 2864 }
8d505d78 2865
254a3f56
AD
2866 $entries = $xpath->query('//iframe');
2867 foreach ($entries as $entry) {
4e404802
AD
2868 $entry->setAttribute('sandbox', 'allow-scripts');
2869
254a3f56 2870 }
8dccabed 2871
1701b965 2872 $allowed_elements = array('a', 'address', 'audio', 'article', 'aside',
2873 'b', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br',
2874 'caption', 'cite', 'center', 'code', 'col', 'colgroup',
2875 'data', 'dd', 'del', 'details', 'div', 'dl', 'font',
2876 'dt', 'em', 'footer', 'figure', 'figcaption',
2877 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'html', 'i',
2878 'img', 'ins', 'kbd', 'li', 'main', 'mark', 'nav', 'noscript',
e0d12697 2879 'ol', 'p', 'pre', 'q', 'ruby', 'rp', 'rt', 's', 'samp', 'section',
2880 'small', 'source', 'span', 'strike', 'strong', 'sub', 'summary',
1701b965 2881 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'time',
b70ccfe6
FE
2882 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2883
2884 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2885
2886 $disallowed_attributes = array('id', 'style', 'class');
2887
1ffe3391 2888 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SANITIZE) as $plugin) {
910592b4 2889 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes, $article_id);
1ffe3391
AD
2890 if (is_array($retval)) {
2891 $doc = $retval[0];
2892 $allowed_elements = $retval[1];
2893 $disallowed_attributes = $retval[2];
2894 } else {
2895 $doc = $retval;
e9b86f0a
AD
2896 }
2897 }
2898
e198cca7 2899 $doc->removeChild($doc->firstChild); //remove doctype
b70ccfe6 2900 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
dd90eb2c
AD
2901
2902 if ($highlight_words) {
2903 foreach ($highlight_words as $word) {
2904
d41ad037
AD
2905 // http://stackoverflow.com/questions/4081372/highlight-keywords-in-a-paragraph
2906
79834eda
AD
2907 $elements = $xpath->query("//*/text()");
2908
2909 foreach ($elements as $child) {
79834eda
AD
2910
2911 $fragment = $doc->createDocumentFragment();
2912 $text = $child->textContent;
2913 $stubs = array();
2914
2915 while (($pos = mb_stripos($text, $word)) !== false) {
2916 $fragment->appendChild(new DomText(mb_substr($text, 0, $pos)));
2917 $word = mb_substr($text, $pos, mb_strlen($word));
2918 $highlight = $doc->createElement('span');
2919 $highlight->appendChild(new DomText($word));
2920 $highlight->setAttribute('class', 'highlight');
2921 $fragment->appendChild($highlight);
2922 $text = mb_substr($text, $pos + mb_strlen($word));
2923 }
dd90eb2c 2924
79834eda 2925 if (!empty($text)) $fragment->appendChild(new DomText($text));
dd90eb2c 2926
79834eda 2927 $child->parentNode->replaceChild($fragment, $child);
dd90eb2c
AD
2928 }
2929 }
2930 }
2931
e198cca7 2932 $res = $doc->saveHTML();
dd90eb2c 2933
254a3f56
AD
2934 return $res;
2935 }
16ad9085 2936
b70ccfe6 2937 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
dcd7ecaa
AD
2938 $xpath = new DOMXPath($doc);
2939 $entries = $xpath->query('//*');
16ad9085 2940
254a3f56
AD
2941 foreach ($entries as $entry) {
2942 if (!in_array($entry->nodeName, $allowed_elements)) {
2943 $entry->parentNode->removeChild($entry);
2944 }
2945
2946 if ($entry->hasAttributes()) {
5f0081b0
AD
2947 $attrs_to_remove = array();
2948
2949 foreach ($entry->attributes as $attr) {
254a3f56
AD
2950
2951 if (strpos($attr->nodeName, 'on') === 0) {
5f0081b0 2952 array_push($attrs_to_remove, $attr);
254a3f56
AD
2953 }
2954
2955 if (in_array($attr->nodeName, $disallowed_attributes)) {
5f0081b0 2956 array_push($attrs_to_remove, $attr);
254a3f56
AD
2957 }
2958 }
5f0081b0
AD
2959
2960 foreach ($attrs_to_remove as $attr) {
2961 $entry->removeAttributeNode($attr);
2962 }
254a3f56
AD
2963 }
2964 }
2965
2966 return $doc;
183ad07b 2967 }
b72c3ef8 2968
6322ac79 2969 function check_for_update() {
63855db1 2970 if (CHECK_FOR_NEW_VERSION && $_SESSION['access_level'] >= 10) {
f6064662
AD
2971 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION .
2972 "&iid=" . sha1(SELF_URL_PATH);
b72c3ef8 2973
63855db1 2974 $version_data = @fetch_file_contents($version_url);
b72c3ef8 2975
63855db1
AD
2976 if ($version_data) {
2977 $version_data = json_decode($version_data, true);
8d505d78 2978 if ($version_data && $version_data['version']) {
74a8b2f6 2979 if (version_compare(VERSION_STATIC, $version_data['version']) == -1) {
e91ad1e9 2980 return $version_data;
63855db1
AD
2981 }
2982 }
f67d9754 2983 }
b72c3ef8 2984 }
63855db1 2985 return false;
b72c3ef8 2986 }
472782e8 2987
a42c55f0 2988 function catchupArticlesById($ids, $cmode, $owner_uid = false) {
9968d46f
AD
2989
2990 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
ed41f171 2991 if (count($ids) == 0) return;
472782e8
AD
2992
2993 $tmp_ids = array();
2994
2995 foreach ($ids as $id) {
2996 array_push($tmp_ids, "ref_id = '$id'");
2997 }
2998
2999 $ids_qpart = join(" OR ", $tmp_ids);
3000
3001 if ($cmode == 0) {
a42c55f0 3002 db_query("UPDATE ttrss_user_entries SET
472782e8 3003 unread = false,last_read = NOW()
9968d46f 3004 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
472782e8 3005 } else if ($cmode == 1) {
a42c55f0 3006 db_query("UPDATE ttrss_user_entries SET
472782e8 3007 unread = true
9968d46f 3008 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
472782e8 3009 } else {
a42c55f0 3010 db_query("UPDATE ttrss_user_entries SET
472782e8 3011 unread = NOT unread,last_read = NOW()
9968d46f 3012 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
472782e8 3013 }
0737b95a
AD
3014
3015 /* update ccache */
3016
a42c55f0 3017 $result = db_query("SELECT DISTINCT feed_id FROM ttrss_user_entries
0737b95a
AD
3018 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
3019
3020 while ($line = db_fetch_assoc($result)) {
a42c55f0 3021 ccache_update($line["feed_id"], $owner_uid);
0737b95a 3022 }
472782e8
AD
3023 }
3024
a42c55f0 3025 function get_article_tags($id, $owner_uid = 0, $tag_cache = false) {
0b126ac2 3026
a42c55f0 3027 $a_id = db_escape_string($id);
0b126ac2 3028
bc976a8c
AD
3029 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3030
8d505d78 3031 $query = "SELECT DISTINCT tag_name,
0c3d1c68 3032 owner_uid as owner FROM
0b126ac2 3033 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
bd3f2ade 3034 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
0b126ac2 3035
8d505d78 3036 $tags = array();
bd3f2ade 3037
0e4a7d7a 3038 /* check cache first */
490c366d 3039
0e4a7d7a 3040 if ($tag_cache === false) {
a42c55f0 3041 $result = db_query("SELECT tag_cache FROM ttrss_user_entries
0e4a7d7a 3042 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
490c366d 3043
0e4a7d7a
AD
3044 $tag_cache = db_fetch_result($result, 0, "tag_cache");
3045 }
bd3f2ade 3046
0e4a7d7a
AD
3047 if ($tag_cache) {
3048 $tags = explode(",", $tag_cache);
3049 } else {
490c366d 3050
0e4a7d7a 3051 /* do it the hard way */
490c366d 3052
a42c55f0 3053 $tmp_result = db_query($query);
490c366d 3054
0e4a7d7a
AD
3055 while ($tmp_line = db_fetch_assoc($tmp_result)) {
3056 array_push($tags, $tmp_line["tag_name"]);
3057 }
490c366d 3058
0e4a7d7a 3059 /* update the cache */
490c366d 3060
a42c55f0 3061 $tags_str = db_escape_string(join(",", $tags));
bd3f2ade 3062
a42c55f0 3063 db_query("UPDATE ttrss_user_entries
0e4a7d7a
AD
3064 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
3065 AND owner_uid = $owner_uid");
0b126ac2
AD
3066 }
3067
3068 return $tags;
3069 }
3070
d62a3b63
AD
3071 function trim_array($array) {
3072 $tmp = $array;
3415b075 3073 array_walk($tmp, 'trim');
d62a3b63
AD
3074 return $tmp;
3075 }
3076
be832a1a 3077 function tag_is_valid($tag) {
ef063748
AD
3078 if ($tag == '') return false;
3079 if (preg_match("/^[0-9]*$/", $tag)) return false;
41f7498a 3080 if (mb_strlen($tag) > 250) return false;
ef063748 3081
ef063748
AD
3082 if (!$tag) return false;
3083
3084 return true;
be832a1a
AD
3085 }
3086
6322ac79 3087 function render_login_form() {
bb399c62
AD
3088 header('Cache-Control: public');
3089
d98e76d9 3090 require_once "login_form.php";
97acbaf1 3091 exit;
01a87dff
AD
3092 }
3093
42395d28 3094 function format_warning($msg, $id = "") {
883fee8d 3095 global $link;
8d505d78 3096 return "<div class=\"warning\" id=\"$id\">
2f20dd58 3097 <span><img src=\"images/alert.png\"></span><span>$msg</span></div>";
0d32b41e
AD
3098 }
3099
08ac193a 3100 function format_notice($msg, $id = "") {
883fee8d 3101 global $link;
8d505d78 3102 return "<div class=\"notice\" id=\"$id\">
2f20dd58 3103 <span><img src=\"images/information.png\"></span><span>$msg</span></div>";
0d32b41e
AD
3104 }
3105
08ac193a 3106 function format_error($msg, $id = "") {
883fee8d 3107 global $link;
8d505d78 3108 return "<div class=\"error\" id=\"$id\">
2f20dd58 3109 <span><img src=\"images/alert.png\"></span><span>$msg</span></div>";
68d2f95e
AD
3110 }
3111
4dccf1ed
AD
3112 function print_notice($msg) {
3113 return print format_notice($msg);
3114 }
3115
3116 function print_warning($msg) {
3117 return print format_warning($msg);
3118 }
3119
68d2f95e
AD
3120 function print_error($msg) {
3121 return print format_error($msg);
3122 }
3123
3124
4dccf1ed
AD
3125 function T_sprintf() {
3126 $args = func_get_args();
3127 return vsprintf(__(array_shift($args)), $args);
3128 }
3129
a42c55f0 3130 function format_inline_player($url, $ctype) {
51682b23
AD
3131
3132 $entry = "";
3133
44cd77b6
AD
3134 $url = htmlspecialchars($url);
3135
8d505d78 3136 if (strpos($ctype, "audio/") === 0) {
c3edc667
AD
3137
3138 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
40fe2d73 3139 $_SESSION["hasMp3"])) {
c3edc667 3140
afd0849e 3141 $entry .= "<audio preload=\"none\" controls>
ca3bca99 3142 <source type=\"$ctype\" src=\"$url\"></source>
8d505d78 3143 </audio>";
c3edc667 3144
c3edc667 3145 } else {
8d505d78
AD
3146
3147 $entry .= "<object type=\"application/x-shockwave-flash\"
ad95edc2 3148 data=\"lib/button/musicplayer.swf?song_url=$url\"
8d505d78
AD
3149 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
3150 <param name=\"movie\"
ad95edc2 3151 value=\"lib/button/musicplayer.swf?song_url=$url\" />
8d505d78 3152 </object>";
c3edc667 3153 }
ca3bca99 3154
44cd77b6
AD
3155 if ($entry) $entry .= "&nbsp; <a target=\"_blank\"
3156 href=\"$url\">" . basename($url) . "</a>";
ca3bca99
AD
3157
3158 return $entry;
3159
51682b23
AD
3160 }
3161
ca3bca99
AD
3162 return "";
3163
3164/* $filename = substr($url, strrpos($url, "/")+1);
c3edc667
AD
3165
3166 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
ca3bca99 3167 $filename . " (" . $ctype . ")" . "</a>"; */
c3edc667 3168
51682b23
AD
3169 }
3170
a42c55f0 3171 function format_article($id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
64436e10 3172 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3de0261a 3173
009646d2
AD
3174 $rv = array();
3175
3176 $rv['id'] = $id;
3177
10eb9da8 3178 /* we can figure out feed_id from article id anyway, why do we
a42c55f0 3179 * pass feed_id here? let's ignore the argument :(*/
10eb9da8 3180
a42c55f0 3181 $result = db_query("SELECT feed_id FROM ttrss_user_entries
10eb9da8
AD
3182 WHERE ref_id = '$id'");
3183
e04c18a2 3184 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
10eb9da8 3185
009646d2
AD
3186 $rv['feed_id'] = $feed_id;
3187
3188 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
3de0261a 3189
3de0261a 3190 if ($mark_as_read) {
a42c55f0 3191 $result = db_query("UPDATE ttrss_user_entries
8d505d78 3192 SET unread = false,last_read = NOW()
64436e10 3193 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
8a4c759e 3194
a42c55f0 3195 ccache_update($feed_id, $owner_uid);
3de0261a
AD
3196 }
3197
6b461797 3198 $result = db_query("SELECT id,title,link,content,feed_id,comments,int_id,lang,
fc2b26a6 3199 ".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
8cc3c778 3200 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
ff04fe06 3201 (SELECT title FROM ttrss_feeds WHERE id = feed_id) as feed_title,
33de3d37 3202 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
60376e9d 3203 (SELECT always_display_enclosures FROM ttrss_feeds WHERE id = feed_id) as always_display_enclosures,
3de0261a 3204 num_comments,
9c506873 3205 tag_cache,
c7e51de1 3206 author,
ef83538d 3207 orig_feed_id,
c052e25a 3208 note
3de0261a 3209 FROM ttrss_entries,ttrss_user_entries
64436e10 3210 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
3de0261a
AD
3211
3212 if ($result) {
3213
3de0261a
AD
3214 $line = db_fetch_assoc($result);
3215
84d952f1
AD
3216 $tag_cache = $line["tag_cache"];
3217
a42c55f0 3218 $line["tags"] = get_article_tags($id, $owner_uid, $line["tag_cache"]);
84d952f1
AD
3219 unset($line["tag_cache"]);
3220
53a49ff8
AD
3221 $line["content"] = sanitize($line["content"],
3222 sql_bool_to_bool($line['hide_images']),
910592b4 3223 $owner_uid, $line["site_url"], false, $line["id"]);
84d952f1 3224
1ffe3391 3225 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE) as $p) {
84d952f1
AD
3226 $line = $p->hook_render_article($line);
3227 }
8cc3c778 3228
3de0261a
AD
3229 $num_comments = $line["num_comments"];
3230 $entry_comments = "";
3231
3232 if ($num_comments > 0) {
3233 if ($line["comments"]) {
6e577ba1 3234 $comments_url = htmlspecialchars($line["comments"]);
3de0261a 3235 } else {
6e577ba1 3236 $comments_url = htmlspecialchars($line["link"]);
3de0261a 3237 }
ff04fe06
AD
3238 $entry_comments = "<a class=\"postComments\"
3239 target='_blank' href=\"$comments_url\">$num_comments ".
3240 _ngettext("comment", "comments", $num_comments)."</a>";
3241
3de0261a
AD
3242 } else {
3243 if ($line["comments"] && $line["link"] != $line["comments"]) {
ff04fe06 3244 $entry_comments = "<a class=\"postComments\" target='_blank' href=\"".htmlspecialchars($line["comments"])."\">".__("comments")."</a>";
8d505d78 3245 }
3de0261a
AD
3246 }
3247
eedfb635
AD
3248 if ($zoom_mode) {
3249 header("Content-Type: text/html");
009646d2 3250 $rv['content'] .= "<html><head>
5bb0cc8e 3251 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
eedfb635 3252 <title>Tiny Tiny RSS - ".$line["title"]."</title>
5bbc4bb4 3253 <link rel=\"stylesheet\" type=\"text/css\" href=\"css/tt-rss.css\">
d0ee0f52
AD
3254 <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\">
3255 <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\">
3256
761af552
AD
3257 <script type=\"text/javascript\">
3258 function openSelectedAttachment(elem) {
3259 try {
3260 var url = elem[elem.selectedIndex].value;
3261
3262 if (url) {
3263 window.open(url);
3264 elem.selectedIndex = 0;
3265 }
3266
3267 } catch (e) {
3268 exception_error(\"openSelectedAttachment\", e);
3269 }
3270 }
3271 </script>
b87a625c 3272 </head><body id=\"ttrssZoom\">";
eedfb635
AD
3273 }
3274
009646d2 3275 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
bc372fe3 3276
126e639a 3277 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3de0261a
AD
3278
3279 $entry_author = $line["author"];
3280
3281 if ($entry_author) {
60164936 3282 $entry_author = __(" - ") . $entry_author;
3de0261a
AD
3283 }
3284
a42c55f0 3285 $parsed_updated = make_local_datetime($line["updated"], true,
64436e10 3286 $owner_uid, true);
324944f3 3287
f035e6dc
AD
3288 if (!$zoom_mode)
3289 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3de0261a
AD
3290
3291 if ($line["link"]) {
c6c010d9 3292 $rv['content'] .= "<div class='postTitle'><a target='_blank'
bf1dc420 3293 title=\"".htmlspecialchars($line['title'])."\"
8d505d78 3294 href=\"" .
5c568973 3295 htmlspecialchars($line["link"]) . "\">" .
11bd95b4
AD
3296 $line["title"] . "</a>" .
3297 "<span class='author'>$entry_author</span></div>";
3de0261a 3298 } else {
c6c010d9 3299 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3de0261a
AD
3300 }
3301
ff04fe06
AD
3302 if ($zoom_mode) {
3303 $feed_title = "<a href=\"".htmlspecialchars($line["site_url"]).
3304 "\" target=\"_blank\">".
3305 htmlspecialchars($line["feed_title"])."</a>";
3306
3307 $rv['content'] .= "<div class=\"postFeedTitle\">$feed_title</div>";
3308
f035e6dc 3309 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
ff04fe06 3310 }
f035e6dc 3311
84d952f1
AD
3312 $tags_str = format_tags_string($line["tags"], $id);
3313 $tags_str_full = join(", ", $line["tags"]);
0780f4f4
AD
3314
3315 if (!$tags_str_full) $tags_str_full = __("no tags");
e7544143 3316
3de0261a
AD
3317 if (!$entry_comments) $entry_comments = "&nbsp;"; # placeholder
3318
f0755b7c 3319 $rv['content'] .= "<div class='postTags' style='float : right'>
2a3b6de0 3320 <img src='images/tag.png'
e9823609 3321 class='tagsPic' alt='Tags' title='Tags'>&nbsp;";
eedfb635
AD
3322
3323 if (!$zoom_mode) {
009646d2 3324 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
8d505d78 3325 <a title=\"".__('Edit tags for this article')."\"
31a53903 3326 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
4710e3dc 3327
0780f4f4
AD
3328 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3329 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3330 position=\"below\">$tags_str_full</div>";
3331
1ffe3391 3332 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_BUTTON) as $p) {
19c73507 3333 $rv['content'] .= $p->hook_article_button($line);
411fe209
AD
3334 }
3335
24ecbcae
AD
3336 } else {
3337 $tags_str = strip_tags($tags_str);
009646d2 3338 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
eedfb635 3339 }
009646d2 3340 $rv['content'] .= "</div>";
ccb2b8dd
AD
3341 $rv['content'] .= "<div clear='both'>";
3342
1ffe3391 3343 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_LEFT_BUTTON) as $p) {
ccb2b8dd
AD
3344 $rv['content'] .= $p->hook_article_left_button($line);
3345 }
3346
3347 $rv['content'] .= "$entry_comments</div>";
3de0261a 3348
ef83538d
AD
3349 if ($line["orig_feed_id"]) {
3350
a42c55f0 3351 $tmp_result = db_query("SELECT * FROM ttrss_archived_feeds
ef83538d
AD
3352 WHERE id = ".$line["orig_feed_id"]);
3353
3354 if (db_num_rows($tmp_result) != 0) {
3355
009646d2
AD
3356 $rv['content'] .= "<div clear='both'>";
3357 $rv['content'] .= __("Originally from:");
ef83538d 3358
009646d2 3359 $rv['content'] .= "&nbsp;";
ef83538d
AD
3360
3361 $tmp_line = db_fetch_assoc($tmp_result);
3362
009646d2 3363 $rv['content'] .= "<a target='_blank'
ef83538d
AD
3364 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3365 $tmp_line['title'] . "</a>";
3366
009646d2 3367 $rv['content'] .= "&nbsp;";
ef83538d 3368
009646d2 3369 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
c2167866 3370 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
ef83538d 3371
009646d2 3372 $rv['content'] .= "</div>";
ef83538d
AD
3373 }
3374 }
3375
009646d2 3376 $rv['content'] .= "</div>";
3de0261a 3377
009646d2 3378 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
c7e51de1 3379 if ($line['note']) {
16cbc19a 3380 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
c7e51de1 3381 }
009646d2 3382 $rv['content'] .= "</div>";
c7e51de1 3383
07eb3658
AD
3384 if (!$line['lang']) $line['lang'] = 'en';
3385
6b461797 3386 $rv['content'] .= "<div class=\"postContent\" lang=\"".$line['lang']."\">";
741b6090 3387
84d952f1 3388 $rv['content'] .= $line["content"];
a42c55f0 3389 $rv['content'] .= format_article_enclosures($id,
60376e9d
TE
3390 sql_bool_to_bool($line["always_display_enclosures"]),
3391 $line["content"],
3392 sql_bool_to_bool($line["hide_images"]));
ce53e200 3393
009646d2 3394 $rv['content'] .= "</div>";
dad14b51 3395
009646d2 3396 $rv['content'] .= "</div>";
3de0261a
AD
3397
3398 }
3399
009646d2
AD
3400 if ($zoom_mode) {
3401 $rv['content'] .= "
b87a625c 3402 <div class='footer'>
2ae69126
AD
3403 <button onclick=\"return window.close()\">".
3404 __("Close this window")."</button></div>";
009646d2 3405 $rv['content'] .= "</body></html>";
eedfb635 3406 }
3de0261a 3407
009646d2
AD
3408 return $rv;
3409
3de0261a
AD
3410 }
3411
79178062 3412 function print_checkpoint($n, $s) {
fa9e88c3 3413 $ts = microtime(true);
f8a25082 3414 echo sprintf("<!-- CP[$n] %.4f seconds -->\n", $ts - $s);
79178062
AD
3415 return $ts;
3416 }
3de0261a 3417
79178062
AD
3418 function sanitize_tag($tag) {
3419 $tag = trim($tag);
52d7e7da 3420
79178062 3421 $tag = mb_strtolower($tag, 'utf-8');
bd202c3f 3422
79178062 3423 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
46921916 3424
79178062
AD
3425// $tag = str_replace('"', "", $tag);
3426// $tag = str_replace("+", " ", $tag);
3427 $tag = str_replace("technorati tag: ", "", $tag);
961f4c73 3428
79178062
AD
3429 return $tag;
3430 }
3de0261a 3431
79178062 3432 function get_self_url_prefix() {
51cc3873
AD
3433 if (strrpos(SELF_URL_PATH, "/") === strlen(SELF_URL_PATH)-1) {
3434 return substr(SELF_URL_PATH, 0, strlen(SELF_URL_PATH)-1);
3435 } else {
3436 return SELF_URL_PATH;
3437 }
79178062 3438 }
a9bcfb8f 3439
45004d43
AD
3440 /**
3441 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3442 *
3443 * @return string The Mozilla Firefox feed adding URL.
3444 */
3445 function add_feed_url() {
ed102aa0
AD
3446 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3447
3448 $url_path = get_self_url_prefix() .
97acbaf1 3449 "/public.php?op=subscribe&feed_url=%s";
755a43ee 3450 return $url_path;
45004d43
AD
3451 } // function add_feed_url
3452
e90053fe
AD
3453 function encrypt_password($pass, $salt = '', $mode2 = false) {
3454 if ($salt && $mode2) {
3455 return "MODE2:" . hash('sha256', $salt . $pass);
3456 } else if ($salt) {
3457 return "SHA1X:" . sha1("$salt:$pass");
1a9f4d3c
AD
3458 } else {
3459 return "SHA1:" . sha1($pass);
3460 }
45004d43
AD
3461 } // function encrypt_password
3462
a42c55f0 3463 function load_filters($feed_id, $owner_uid, $action_id = false) {
fee840fb
AD
3464 $filters = array();
3465
a42c55f0 3466 $cat_id = (int)getFeedCategory($feed_id);
fee840fb 3467
34e55fe8
AD
3468 if ($cat_id == 0)
3469 $null_cat_qpart = "cat_id IS NULL OR";
3470 else
3471 $null_cat_qpart = "";
3472
a42c55f0 3473 $result = db_query("SELECT * FROM ttrss_filters2 WHERE
8e8c8934 3474 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
8d505d78 3475
67bd0b1f 3476 $check_cats = join(",", array_merge(
a42c55f0 3477 getParentCategories($cat_id, $owner_uid),
67bd0b1f
AD
3478 array($cat_id)));
3479
0e4a7d7a 3480 while ($line = db_fetch_assoc($result)) {
6aff7845
AD
3481 $filter_id = $line["id"];
3482
a42c55f0 3483 $result2 = db_query("SELECT
a3a896a1 3484 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
6aff7845
AD
3485 FROM ttrss_filters2_rules AS r,
3486 ttrss_filter_types AS t
3487 WHERE
34e55fe8 3488 ($null_cat_qpart (cat_id IS NULL AND cat_filter = false) OR cat_id IN ($check_cats)) AND
6aff7845
AD
3489 (feed_id IS NULL OR feed_id = '$feed_id') AND
3490 filter_type = t.id AND filter_id = '$filter_id'");
3491
3492 $rules = array();
3493 $actions = array();
ba975b2e 3494
6aff7845
AD
3495 while ($rule_line = db_fetch_assoc($result2)) {
3496# print_r($rule_line);
8d505d78 3497
6aff7845
AD
3498 $rule = array();
3499 $rule["reg_exp"] = $rule_line["reg_exp"];
3500 $rule["type"] = $rule_line["type_name"];
a3a896a1 3501 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
6aff7845
AD
3502
3503 array_push($rules, $rule);
3504 }
3505
a42c55f0 3506 $result2 = db_query("SELECT a.action_param,t.name AS type_name
6aff7845
AD
3507 FROM ttrss_filters2_actions AS a,
3508 ttrss_filter_actions AS t
3509 WHERE
3510 action_id = t.id AND filter_id = '$filter_id'");
3511
3512 while ($action_line = db_fetch_assoc($result2)) {
3513# print_r($action_line);
3514
3515 $action = array();
3516 $action["type"] = $action_line["type_name"];
3517 $action["param"] = $action_line["action_param"];
3518
3519 array_push($actions, $action);
0e4a7d7a 3520 }
b8ffa322 3521
b8ffa322 3522
6aff7845
AD
3523 $filter = array();
3524 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
a3a896a1 3525 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
6aff7845
AD
3526 $filter["rules"] = $rules;
3527 $filter["actions"] = $actions;
3528
3529 if (count($rules) > 0 && count($actions) > 0) {
3530 array_push($filters, $filter);
3531 }
3532 }
3533
0e4a7d7a 3534 return $filters;
fee840fb 3535 }
1e36af0c
AD
3536
3537 function get_score_pic($score) {
8d505d78
AD
3538 if ($score > 100) {
3539 return "score_high.png";
3540 } else if ($score > 0) {
883fee8d 3541 return "score_half_high.png";
1cce3aca 3542 } else if ($score < -100) {
883fee8d 3543 return "score_low.png";
1cce3aca 3544 } else if ($score < 0) {
883fee8d 3545 return "score_half_low.png";
8d505d78 3546 } else {
883fee8d 3547 return "score_neutral.png";
1e36af0c
AD
3548 }
3549 }
ec92c9d1 3550
7defa089
AD
3551 function feed_has_icon($id) {
3552 return is_file(ICONS_DIR . "/$id.ico") && filesize(ICONS_DIR . "/$id.ico") > 0;
3553 }
f29ba148 3554
6322ac79 3555 function init_plugins() {
1ffe3391 3556 PluginHost::getInstance()->load(PLUGINS, PluginHost::KIND_ALL);
19c73507 3557
ba68b681 3558 return true;
f29ba148 3559 }
5e96ca9d 3560
307d187c 3561 function format_tags_string($tags, $id) {
54b7590c
AD
3562 if (!is_array($tags) || count($tags) == 0) {
3563 return __("no tags");
3564 } else {
3565 $maxtags = min(5, count($tags));
307d187c 3566
54b7590c 3567 for ($i = 0; $i < $maxtags; $i++) {
67f4fe17 3568 $tags_str .= "<a class=\"tag\" href=\"#\" onclick=\"viewfeed('".$tags[$i]."')\">" . $tags[$i] . "</a>, ";
307d187c 3569 }
307d187c 3570
54b7590c 3571 $tags_str = mb_substr($tags_str, 0, mb_strlen($tags_str)-2);
307d187c 3572
54b7590c
AD
3573 if (count($tags) > $maxtags)
3574 $tags_str .= ", &hellip;";
307d187c 3575
54b7590c 3576 return $tags_str;
307d187c 3577 }
307d187c 3578 }
2eb9c95c
AD
3579
3580 function format_article_labels($labels, $id) {
3581
c35d0d8f 3582 if (!is_array($labels)) return '';
76e087b0 3583
2eb9c95c
AD
3584 $labels_str = "";
3585
3586 foreach ($labels as $l) {
8d505d78 3587 $labels_str .= sprintf("<span class='hlLabelRef'
2eb9c95c
AD
3588 style='color : %s; background-color : %s'>%s</span>",
3589 $l[2], $l[3], $l[1]);
3590 }
3591
3592 return $labels_str;
3593
3594 }
c7e51de1 3595
16cbc19a 3596 function format_article_note($id, $note, $allow_edit = true) {
c7e51de1 3597
fcfa9ef1
AD
3598 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3599 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
16cbc19a 3600 ($allow_edit ? __('(edit note)') : "")."</div>$note</div>";
c7e51de1
AD
3601
3602 return $str;
3603 }
7f969260 3604
7e329f13 3605
a42c55f0 3606 function get_feed_category($feed_cat, $parent_cat_id = false) {
d2a317e3
AD
3607 if ($parent_cat_id) {
3608 $parent_qpart = "parent_cat = '$parent_cat_id'";
3609 $parent_insert = "'$parent_cat_id'";
3610 } else {
3611 $parent_qpart = "parent_cat IS NULL";
3612 $parent_insert = "NULL";
3613 }
3614
6322ac79 3615 $result = db_query(
d2a317e3
AD
3616 "SELECT id FROM ttrss_feed_categories
3617 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3618
3619 if (db_num_rows($result) == 0) {
3620 return false;
3621 } else {
3622 return db_fetch_result($result, 0, "id");
3623 }
3624 }
3625
a42c55f0 3626 function add_feed_category($feed_cat, $parent_cat_id = false) {
c00907f2
AD
3627
3628 if (!$feed_cat) return false;
3629
a42c55f0 3630 db_query("BEGIN");
5c7c7da9 3631
d2a317e3
AD
3632 if ($parent_cat_id) {
3633 $parent_qpart = "parent_cat = '$parent_cat_id'";
3634 $parent_insert = "'$parent_cat_id'";
3635 } else {
3636 $parent_qpart = "parent_cat IS NULL";
3637 $parent_insert = "NULL";
3638 }
3639
129562e0
AD
3640 $feed_cat = mb_substr($feed_cat, 0, 250);
3641
6322ac79 3642 $result = db_query(
5c7c7da9 3643 "SELECT id FROM ttrss_feed_categories
d2a317e3 3644 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
5c7c7da9
AD
3645
3646 if (db_num_rows($result) == 0) {
8d505d78 3647
6322ac79 3648 $result = db_query(
d2a317e3
AD
3649 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3650 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
5c7c7da9 3651
a42c55f0 3652 db_query("COMMIT");
5c7c7da9
AD
3653
3654 return true;
3655 }
3656
3657 return false;
8d505d78 3658 }
5c7c7da9 3659
a42c55f0
AD
3660 function getArticleFeed($id) {
3661 $result = db_query("SELECT feed_id FROM ttrss_user_entries
a545dc31 3662 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
ab197ae1
AD
3663
3664 if (db_num_rows($result) != 0) {
3665 return db_fetch_result($result, 0, "feed_id");
3666 } else {
3667 return 0;
3668 }
3669 }
a5819bb3 3670
f2c6c008
CW
3671 /**
3672 * Fixes incomplete URLs by prepending "http://".
f0266f51
CW
3673 * Also replaces feed:// with http://, and
3674 * prepends a trailing slash if the url is a domain name only.
f2c6c008
CW
3675 *
3676 * @param string $url Possibly incomplete URL
3677 *
3678 * @return string Fixed URL.
3679 */
3680 function fix_url($url) {
3681 if (strpos($url, '://') === false) {
3682 $url = 'http://' . $url;
f0266f51
CW
3683 } else if (substr($url, 0, 5) == 'feed:') {
3684 $url = 'http:' . substr($url, 5);
3685 }
3686
3687 //prepend slash if the URL has no slash in it
3688 // "http://www.example" -> "http://www.example/"
44453773 3689 if (strpos($url, '/', strpos($url, ':') + 3) === false) {
f0266f51 3690 $url .= '/';
f2c6c008 3691 }
ec39a02c
AD
3692
3693 if ($url != "http:///")
3694 return $url;
3695 else
3696 return '';
f2c6c008
CW
3697 }
3698
a5819bb3
AD
3699 function validate_feed_url($url) {
3700 $parts = parse_url($url);
3701
3702 return ($parts['scheme'] == 'http' || $parts['scheme'] == 'feed' || $parts['scheme'] == 'https');
3703
3704 }
d9084cf2 3705
a42c55f0 3706 function get_article_enclosures($id) {
be35798b 3707
8d505d78 3708 $query = "SELECT * FROM ttrss_enclosures
be35798b
AD
3709 WHERE post_id = '$id' AND content_url != ''";
3710
be35798b
AD
3711 $rv = array();
3712
a42c55f0 3713 $result = db_query($query);
be35798b 3714
0e4a7d7a
AD
3715 if (db_num_rows($result) > 0) {
3716 while ($line = db_fetch_assoc($result)) {
3717 array_push($rv, $line);
be35798b
AD
3718 }
3719 }
3720
3721 return $rv;
3722 }
3723
a42c55f0 3724 function save_email_address($email) {
31a53903
AD
3725 // FIXME: implement persistent storage of emails
3726
8d505d78 3727 if (!$_SESSION['stored_emails'])
31a53903
AD
3728 $_SESSION['stored_emails'] = array();
3729
3730 if (!in_array($email, $_SESSION['stored_emails']))
3731 array_push($_SESSION['stored_emails'], $email);
3732 }
8801fb01 3733
8801fb01 3734
a42c55f0 3735 function get_feed_access_key($feed_id, $is_cat, $owner_uid = false) {
8801fb01
AD
3736
3737 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3738
3739 $sql_is_cat = bool_to_sql_bool($is_cat);
3740
a42c55f0 3741 $result = db_query("SELECT access_key FROM ttrss_access_keys
8d505d78 3742 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
8801fb01
AD
3743 AND owner_uid = " . $owner_uid);
3744
3745 if (db_num_rows($result) == 1) {
3746 return db_fetch_result($result, 0, "access_key");
3747 } else {
a42c55f0 3748 $key = db_escape_string(sha1(uniqid(rand(), true)));
8801fb01 3749
a42c55f0 3750 $result = db_query("INSERT INTO ttrss_access_keys
8801fb01
AD
3751 (access_key, feed_id, is_cat, owner_uid)
3752 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3753
3754 return $key;
3755 }
3756 return false;
3757 }
f0266f51 3758
759e5132 3759 function get_feeds_from_html($url, $content)
f0266f51
CW
3760 {
3761 $url = fix_url($url);
3762 $baseUrl = substr($url, 0, strrpos($url, '/') + 1);
3763
fb074239
AD
3764 libxml_use_internal_errors(true);
3765
f0266f51 3766 $doc = new DOMDocument();
8d505d78 3767 $doc->loadHTML($content);
f0266f51
CW
3768 $xpath = new DOMXPath($doc);
3769 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3770 $feedUrls = array();
3771 foreach ($entries as $entry) {
3772 if ($entry->hasAttribute('href')) {
3773 $title = $entry->getAttribute('title');
3774 if ($title == '') {
3775 $title = $entry->getAttribute('type');
3776 }
923818fc
CW
3777 $feedUrl = rewrite_relative_url(
3778 $baseUrl, $entry->getAttribute('href')
3779 );
f0266f51
CW
3780 $feedUrls[$feedUrl] = $title;
3781 }
3782 }
3783 return $feedUrls;
3784 }
3785
759e5132 3786 function is_html($content) {
32b86711 3787 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
759e5132 3788 }
f33479da 3789
759e5132
AD
3790 function url_is_html($url, $login = false, $pass = false) {
3791 return is_html(fetch_file_contents($url, false, $login, $pass));
f33479da 3792 }
24e2bb3a 3793
a42c55f0 3794 function print_label_select($name, $value, $attributes = "") {
24e2bb3a 3795
a42c55f0 3796 $result = db_query("SELECT caption FROM ttrss_labels2
24e2bb3a
AD
3797 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3798
8d505d78 3799 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
d90868d7 3800 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
24e2bb3a
AD
3801
3802 while ($line = db_fetch_assoc($result)) {
3803
3804 $issel = ($line["caption"] == $value) ? "selected=\"1\"" : "";
3805
d90868d7
AD
3806 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3807 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
24e2bb3a
AD
3808
3809 }
3810
d90868d7 3811# print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
24e2bb3a
AD
3812
3813 print "</select>";
3814
3815
3816 }
3817
a42c55f0 3818 function format_article_enclosures($id, $always_display_enclosures,
33de3d37 3819 $article_content, $hide_images = false) {
dad14b51 3820
a42c55f0 3821 $result = get_article_enclosures($id);
009646d2 3822 $rv = '';
8d505d78 3823
dad14b51 3824 if (count($result) > 0) {
8d505d78 3825
dad14b51
AD
3826 $entries_html = array();
3827 $entries = array();
ca3bca99 3828 $entries_inline = array();
8d505d78 3829
dad14b51 3830 foreach ($result as $line) {
8d505d78 3831
dad14b51
AD
3832 $url = $line["content_url"];
3833 $ctype = $line["content_type"];
5c54e683 3834 $title = $line["title"];
8d505d78 3835
dad14b51 3836 if (!$ctype) $ctype = __("unknown type");
8d505d78 3837
749b56bd 3838 $filename = substr($url, strrpos($url, "/")+1);
8d505d78 3839
a42c55f0 3840 $player = format_inline_player($url, $ctype);
ca3bca99
AD
3841
3842 if ($player) array_push($entries_inline, $player);
8d505d78 3843
c3edc667
AD
3844# $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3845# $filename . " (" . $ctype . ")" . "</a>";
8d505d78 3846
749b56bd
AD
3847 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3848 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3849
dad14b51 3850 array_push($entries_html, $entry);
8d505d78 3851
dad14b51 3852 $entry = array();
8d505d78 3853
dad14b51
AD
3854 $entry["type"] = $ctype;
3855 $entry["filename"] = $filename;
3856 $entry["url"] = $url;
5c54e683 3857 $entry["title"] = $title;
8d505d78 3858
dad14b51
AD
3859 array_push($entries, $entry);
3860 }
8d505d78 3861
a42c55f0 3862 if ($_SESSION['uid'] && !get_pref("STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
dad14b51
AD
3863 if ($always_display_enclosures ||
3864 !preg_match("/<img/i", $article_content)) {
8d505d78 3865
dad14b51 3866 foreach ($entries as $entry) {
8d505d78 3867
dad14b51
AD
3868 if (preg_match("/image/", $entry["type"]) ||
3869 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
8d505d78 3870
33de3d37
AD
3871 if (!$hide_images) {
3872 $rv .= "<p><img
3873 alt=\"".htmlspecialchars($entry["filename"])."\"
3874 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3875 } else {
3876 $rv .= "<p><a target=\"_blank\"
3877 href=\"".htmlspecialchars($entry["url"])."\"
3878 >" .htmlspecialchars($entry["url"]) . "</a></p>";
5c54e683 3879 }
749b56bd 3880
5c54e683
AD
3881 if ($entry['title']) {
3882 $rv.= "<div class=\"enclosure_title\">${entry['title']}</div>";
33de3d37 3883 }
dad14b51
AD
3884 }
3885 }
3886 }
3887 }
8d505d78 3888
ca3bca99
AD
3889 if (count($entries_inline) > 0) {
3890 $rv .= "<hr clear='both'/>";
3891 foreach ($entries_inline as $entry) { $rv .= $entry; };
3892 $rv .= "<hr clear='both'/>";
3893 }
3894
bf6df236 3895 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
f9432f26 3896 "<option value=''>" . __('Attachments')."</option>";
8d505d78 3897
f9432f26 3898 foreach ($entries as $entry) {
5c54e683
AD
3899 if ($entry["title"])
3900 $title = "&mdash; " . truncate_string($entry["title"], 30);
3901 else
3902 $title = "";
3903
3904 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "$title</option>";
8d505d78 3905
f9432f26
AD
3906 };
3907
3908 $rv .= "</select>";
dad14b51 3909 }
009646d2
AD
3910
3911 return $rv;
dad14b51
AD
3912 }
3913
6322ac79 3914 function getLastArticleId() {
a42c55f0 3915 $result = db_query("SELECT MAX(ref_id) AS id FROM ttrss_user_entries
f8fb4498
AD
3916 WHERE owner_uid = " . $_SESSION["uid"]);
3917
3918 if (db_num_rows($result) == 1) {
3919 return db_fetch_result($result, 0, "id");
3920 } else {
3921 return -1;
3922 }
3923 }
8cc3c778
AD
3924
3925 function build_url($parts) {
3926 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3927 }
3928
f679105c
CW
3929 /**
3930 * Converts a (possibly) relative URL to a absolute one.
3931 *
3932 * @param string $url Base URL (i.e. from where the document is)
3933 * @param string $rel_url Possibly relative URL in the document
3934 *
3935 * @return string Absolute URL
3936 */
8cc3c778 3937 function rewrite_relative_url($url, $rel_url) {
c722c0be 3938 if (strpos($rel_url, ":") !== false) {
b4520bb8
AD
3939 return $rel_url;
3940 } else if (strpos($rel_url, "://") !== false) {
8cc3c778 3941 return $rel_url;
f9052d35 3942 } else if (strpos($rel_url, "//") === 0) {
3943 # protocol-relative URL (rare but they exist)
3944 return $rel_url;
8d505d78 3945 } else if (strpos($rel_url, "/") === 0)
8cc3c778
AD
3946 {
3947 $parts = parse_url($url);
3948 $parts['path'] = $rel_url;
3949
3950 return build_url($parts);
3951
3952 } else {
3953 $parts = parse_url($url);
f679105c
CW
3954 if (!isset($parts['path'])) {
3955 $parts['path'] = '/';
3956 }
3957 $dir = $parts['path'];
3958 if (substr($dir, -1) !== '/') {
3959 $dir = dirname($parts['path']);
3960 $dir !== '/' && $dir .= '/';
3961 }
3962 $parts['path'] = $dir . $rel_url;
8cc3c778
AD
3963
3964 return build_url($parts);
3965 }
3966 }
3967
e4f7f8df 3968 function sphinx_search($query, $offset = 0, $limit = 30) {
030b2a31 3969 require_once 'lib/sphinxapi.php';
31303c6b 3970
e4f7f8df
AD
3971 $sphinxClient = new SphinxClient();
3972
52803a0b
AD
3973 $sphinxpair = explode(":", SPHINX_SERVER, 2);
3974
7c01db43 3975 $sphinxClient->SetServer($sphinxpair[0], (int)$sphinxpair[1]);
e4f7f8df
AD
3976 $sphinxClient->SetConnectTimeout(1);
3977
8d505d78 3978 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
e4f7f8df
AD
3979 'feed_title' => 20));
3980
3981 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2);
3982 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25);
3983 $sphinxClient->SetLimits($offset, $limit, 1000);
3984 $sphinxClient->SetArrayResult(false);
3985 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
8d505d78 3986
e4f7f8df
AD
3987 $result = $sphinxClient->Query($query, SPHINX_INDEX);
3988
3989 $ids = array();
3990
3991 if (is_array($result['matches'])) {
3992 foreach (array_keys($result['matches']) as $int_id) {
3993 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3994 array_push($ids, $ref_id);
3995 }
3996 }
3997
3998 return $ids;
3999 }
4000
a42c55f0 4001 function cleanup_tags($days = 14, $limit = 1000) {
868650e4
AD
4002
4003 if (DB_TYPE == "pgsql") {
4004 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
4005 } else if (DB_TYPE == "mysql") {
4006 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
4007 }
4008
b5ec13fa 4009 $tags_deleted = 0;
868650e4 4010
b5ec13fa
AD
4011 while ($limit > 0) {
4012 $limit_part = 500;
4013
8d505d78
AD
4014 $query = "SELECT ttrss_tags.id AS id
4015 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
b5ec13fa
AD
4016 WHERE post_int_id = int_id AND $interval_query AND
4017 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
8d505d78 4018
a42c55f0 4019 $result = db_query($query);
b5ec13fa
AD
4020
4021 $ids = array();
4022
4023 while ($line = db_fetch_assoc($result)) {
4024 array_push($ids, $line['id']);
4025 }
4026
4027 if (count($ids) > 0) {
4028 $ids = join(",", $ids);
b5ec13fa 4029
a42c55f0
AD
4030 $tmp_result = db_query("DELETE FROM ttrss_tags WHERE id IN ($ids)");
4031 $tags_deleted += db_affected_rows($tmp_result);
b5ec13fa
AD
4032 } else {
4033 break;
4034 }
4035
4036 $limit -= $limit_part;
4037 }
4038
b5ec13fa 4039 return $tags_deleted;
868650e4
AD
4040 }
4041
6322ac79 4042 function print_user_stylesheet() {
a42c55f0 4043 $value = get_pref('USER_STYLESHEET');
88e4e597
AD
4044
4045 if ($value) {
4046 print "<style type=\"text/css\">";
5823f9fb 4047 print str_replace("<br/>", "\n", $value);
88e4e597
AD
4048 print "</style>";
4049 }
4050
4051 }
4052
a42c55f0 4053 function filter_to_sql($filter, $owner_uid) {
4e02f582 4054 $query = array();
36184020 4055
4e02f582
AD
4056 if (DB_TYPE == "pgsql")
4057 $reg_qpart = "~";
4058 else
4059 $reg_qpart = "REGEXP";
36184020 4060
4e02f582 4061 foreach ($filter["rules"] AS $rule) {
4cb2c550 4062 $rule['reg_exp'] = str_replace('/', '\/', $rule["reg_exp"]);
4e02f582
AD
4063 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
4064 $rule['reg_exp']) !== FALSE;
36184020 4065
4e02f582 4066 if ($regexp_valid) {
36184020 4067
a42c55f0 4068 $rule['reg_exp'] = db_escape_string($rule['reg_exp']);
36184020 4069
a3a896a1 4070 switch ($rule["type"]) {
4e02f582
AD
4071 case "title":
4072 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
4073 $rule['reg_exp'] . "')";
4074 break;
4075 case "content":
4076 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
4077 $rule['reg_exp'] . "')";
4078 break;
4079 case "both":
4080 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
4081 $rule['reg_exp'] . "') OR LOWER(" .
4082 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
4083 break;
4084 case "tag":
4085 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
4086 $rule['reg_exp'] . "')";
4087 break;
4088 case "link":
4089 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
4090 $rule['reg_exp'] . "')";
4091 break;
4092 case "author":
4093 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
4094 $rule['reg_exp'] . "')";
4095 break;
4096 }
36184020 4097
ec1f8a3d
AD
4098 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
4099
6b218731 4100 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
a42c55f0 4101 $qpart .= " AND feed_id = " . db_escape_string($rule["feed_id"]);
4e02f582 4102 }
6b8b3af8 4103
4e02f582 4104 if (isset($rule["cat_id"])) {
2680295b
AD
4105
4106 if ($rule["cat_id"] > 0) {
a42c55f0 4107 $children = getChildCategories($rule["cat_id"], $owner_uid);
2680295b
AD
4108 array_push($children, $rule["cat_id"]);
4109
4110 $children = join(",", $children);
4111
4112 $cat_qpart = "cat_id IN ($children)";
4113 } else {
4114 $cat_qpart = "cat_id IS NULL";
4115 }
4116
4117 $qpart .= " AND $cat_qpart";
56fbb82c 4118 }
4e02f582 4119
5d4d5918
AD
4120 $qpart .= " AND feed_id IS NOT NULL";
4121
4e02f582
AD
4122 array_push($query, "($qpart)");
4123
56fbb82c 4124 }
4e02f582 4125 }
56fbb82c 4126
4e02f582 4127 if (count($query) > 0) {
a3a896a1 4128 $fullquery = "(" . join($filter["match_any_rule"] ? "OR" : "AND", $query) . ")";
56fbb82c 4129 } else {
a3a896a1 4130 $fullquery = "(false)";
56fbb82c 4131 }
a3a896a1
AD
4132
4133 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
4134
4135 return $fullquery;
36184020 4136 }
ae5f7bb1 4137
3382bce1
AD
4138 if (!function_exists('gzdecode')) {
4139 function gzdecode($string) { // no support for 2nd argument
4140 return file_get_contents('compress.zlib://data:who/cares;base64,'.
4141 base64_encode($string));
4142 }
4143 }
4144
8db5d8ea
AD
4145 function get_random_bytes($length) {
4146 if (function_exists('openssl_random_pseudo_bytes')) {
4147 return openssl_random_pseudo_bytes($length);
4148 } else {
4149 $output = "";
4150
4151 for ($i = 0; $i < $length; $i++)
4152 $output .= chr(mt_rand(0, 255));
4153
4154 return $output;
4155 }
4156 }
871f0a7a
AD
4157
4158 function read_stdin() {
4159 $fp = fopen("php://stdin", "r");
4160
4161 if ($fp) {
4162 $line = trim(fgets($fp));
4163 fclose($fp);
4164 return $line;
4165 }
4166
4167 return null;
4168 }
e3449aa1
AD
4169
4170 function tmpdirname($path, $prefix) {
4171 // Use PHP's tmpfile function to create a temporary
4172 // directory name. Delete the file and keep the name.
4173 $tempname = tempnam($path,$prefix);
4174 if (!$tempname)
4175 return false;
4176
4177 if (!unlink($tempname))
4178 return false;
4179
4180 return $tempname;
4181 }
4182
a42c55f0
AD
4183 function getFeedCategory($feed) {
4184 $result = db_query("SELECT cat_id FROM ttrss_feeds
6aff7845
AD
4185 WHERE id = '$feed'");
4186
4187 if (db_num_rows($result) > 0) {
4188 return db_fetch_result($result, 0, "cat_id");
4189 } else {
4190 return false;
4191 }
4192
4193 }
4194
8dcb2b47
AD
4195 function implements_interface($class, $interface) {
4196 return in_array($interface, class_implements($class));
4197 }
e88c1943 4198
fe61dc18
AD
4199 function geturl($url, $depth = 0){
4200
4201 if ($depth == 20) return $url;
e2b0054b 4202
3f6f0857
AD
4203 if (!function_exists('curl_init'))
4204 return user_error('CURL Must be installed for geturl function to work. Ask your host to enable it or uncomment extension=php_curl.dll in php.ini', E_USER_ERROR);
e2b0054b
AD
4205
4206 $curl = curl_init();
4207 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4208 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4209 $header[] = "Cache-Control: max-age=0";
4210 $header[] = "Connection: keep-alive";
4211 $header[] = "Keep-Alive: 300";
4212 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4213 $header[] = "Accept-Language: en-us,en;q=0.5";
4214 $header[] = "Pragma: ";
4215
4216 curl_setopt($curl, CURLOPT_URL, $url);
4217 curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4218 curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
4219 curl_setopt($curl, CURLOPT_HEADER, true);
4220 curl_setopt($curl, CURLOPT_REFERER, $url);
4221 curl_setopt($curl, CURLOPT_ENCODING, 'gzip,deflate');
4222 curl_setopt($curl, CURLOPT_AUTOREFERER, true);
4223 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
4224 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4225 curl_setopt($curl, CURLOPT_TIMEOUT, 60);
40a35a62 4226 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
e2b0054b 4227
05f14a7d
AD
4228 if (defined('_CURL_HTTP_PROXY')) {
4229 curl_setopt($curl, CURLOPT_PROXY, _CURL_HTTP_PROXY);
4230 }
4231
aefed7f7
AD
4232 if ((OPENSSL_VERSION_NUMBER >= 0x0090808f) && (OPENSSL_VERSION_NUMBER < 0x10000000)) {
4233 curl_setopt($curl, CURLOPT_SSLVERSION, 3);
4234 }
4235
e2b0054b
AD
4236 $html = curl_exec($curl);
4237
4238 $status = curl_getinfo($curl);
e2b0054b
AD
4239
4240 if($status['http_code']!=200){
4241 if($status['http_code'] == 301 || $status['http_code'] == 302) {
e2b4c0b7 4242 curl_close($curl);
e2b0054b
AD
4243 list($header) = explode("\r\n\r\n", $html, 2);
4244 $matches = array();
4245 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4246 $url = trim(str_replace($matches[1],"",$matches[0]));
4247 $url_parsed = parse_url($url);
fe61dc18 4248 return (isset($url_parsed))? geturl($url, $depth + 1):'';
e2b0054b 4249 }
e2b4c0b7
RH
4250
4251 global $fetch_last_error;
4252
4253 $fetch_last_error = curl_errno($curl) . " " . curl_error($curl);
4254 curl_close($curl);
4255
e2b0054b
AD
4256 $oline='';
4257 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4258 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
911845b5
AD
4259# $handle = @fopen('./curl.error.log', 'a');
4260# fwrite($handle, $line);
e2b0054b
AD
4261 return FALSE;
4262 }
e2b4c0b7 4263 curl_close($curl);
e2b0054b
AD
4264 return $url;
4265 }
8dcb2b47 4266
c670a80d
AD
4267 function get_minified_js($files) {
4268 require_once 'lib/jshrink/Minifier.php';
4269
4270 $rv = '';
4271
4272 foreach ($files as $js) {
4273 if (!isset($_GET['debug'])) {
32011b9b 4274 $cached_file = CACHE_DIR . "/js/".basename($js).".js";
c670a80d
AD
4275
4276 if (file_exists($cached_file) &&
4277 is_readable($cached_file) &&
4278 filemtime($cached_file) >= filemtime("js/$js.js")) {
4279
4280 $rv .= file_get_contents($cached_file);
4281
4282 } else {
4283 $minified = JShrink\Minifier::minify(file_get_contents("js/$js.js"));
4284 file_put_contents($cached_file, $minified);
4285 $rv .= $minified;
4286 }
4287 } else {
4288 $rv .= file_get_contents("js/$js.js");
4289 }
4290 }
4291
4292 return $rv;
4293 }
4294
b5d4716a
AD
4295 function stylesheet_tag($filename) {
4296 $timestamp = filemtime($filename);
4297
4298 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4299 }
4300
4301 function javascript_tag($filename) {
4302 $query = "";
4303
4304 if (!(strpos($filename, "?") === FALSE)) {
4305 $query = substr($filename, strpos($filename, "?")+1);
4306 $filename = substr($filename, 0, strpos($filename, "?"));
4307 }
4308
4309 $timestamp = filemtime($filename);
4310
4311 if ($query) $timestamp .= "&$query";
4312
4313 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4314 }
4315
16314dda 4316 function calculate_dep_timestamp() {
9717d5de 4317 $files = array_merge(glob("js/*.js"), glob("css/*.css"));
16314dda
AD
4318
4319 $max_ts = -1;
4320
4321 foreach ($files as $file) {
4322 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4323 }
4324
4325 return $max_ts;
4326 }
4327
bcbb2ec7
AD
4328 function T_js_decl($s1, $s2) {
4329 if ($s1 && $s2) {
4330 $s1 = preg_replace("/\n/", "", $s1);
4331 $s2 = preg_replace("/\n/", "", $s2);
4332
4333 $s1 = preg_replace("/\"/", "\\\"", $s1);
4334 $s2 = preg_replace("/\"/", "\\\"", $s2);
4335
4336 return "T_messages[\"$s1\"] = \"$s2\";\n";
4337 }
4338 }
4339
4340 function init_js_translations() {
4341
4342 print 'var T_messages = new Object();
4343
4344 function __(msg) {
4345 if (T_messages[msg]) {
4346 return T_messages[msg];
4347 } else {
4348 return msg;
4349 }
4350 }
4351
4352 function ngettext(msg1, msg2, n) {
da07af65 4353 return __((parseInt(n) > 1) ? msg2 : msg1);
bcbb2ec7
AD
4354 }';
4355
4356 $l10n = _get_reader();
4357
4358 for ($i = 0; $i < $l10n->total; $i++) {
4359 $orig = $l10n->get_original_string($i);
da07af65
Y
4360 if(strpos($orig, "\000") !== FALSE) { // Plural forms
4361 $key = explode(chr(0), $orig);
be5e469d 4362 print T_js_decl($key[0], _ngettext($key[0], $key[1], 1)); // Singular
4363 print T_js_decl($key[1], _ngettext($key[0], $key[1], 2)); // Plural
da07af65
Y
4364 } else {
4365 $translation = __($orig);
4366 print T_js_decl($orig, $translation);
4367 }
bcbb2ec7
AD
4368 }
4369 }
4370
f822a8e5
AD
4371 function label_to_feed_id($label) {
4372 return LABEL_BASE_INDEX - 1 - abs($label);
4373 }
4374
4375 function feed_to_label_id($feed) {
4376 return LABEL_BASE_INDEX - 1 + abs($feed);
4377 }
4378
ebec81a6
AD
4379 function format_libxml_error($error) {
4380 return T_sprintf("LibXML error %s at line %d (column %d): %s",
4381 $error->code, $error->line, $error->column,
4382 $error->message);
4383 }
4384
8c0496f7 4385?>