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