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