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