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