1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/ttrss_ynh.git synced 2024-10-01 13:34:46 +02:00

Merge pull request #31 from rndmh3ro/update_ttrss_version

Update ttrss version
This commit is contained in:
JimboJoe 2017-03-10 21:22:26 +01:00 committed by GitHub
commit 6a8fb9f277
2418 changed files with 66 additions and 193803 deletions

View file

@ -1,4 +1,4 @@
<?php <?php
// ******************************************* // *******************************************
// *** Database configuration (important!) *** // *** Database configuration (important!) ***
// ******************************************* // *******************************************
@ -20,11 +20,16 @@ define('MYSQL_CHARSET', 'UTF8');
define('SELF_URL_PATH', 'yunopath'); define('SELF_URL_PATH', 'yunopath');
// Full URL of your tt-rss installation. This should be set to the // Full URL of your tt-rss installation. This should be set to the
// location of tt-rss directory, e.g. http://yourserver/tt-rss/ // location of tt-rss directory, e.g. http://example.org/tt-rss/
// You need to set this option correctly otherwise several features // You need to set this option correctly otherwise several features
// including PUSH, bookmarklets and browser integration will not work properly. // including PUSH, bookmarklets and browser integration will not work properly.
define('FEED_CRYPT_KEY', ''); define('FEED_CRYPT_KEY', '');
// WARNING: mcrypt is deprecated in php 7.1. This directive exists for backwards
// compatibility with existing installs, new passwords are NOT going to be encrypted.
// Use update.php --decrypt-feeds to decrypt existing passwords in the database while
// mcrypt is still available.
// Key used for encryption of passwords for password-protected feeds // Key used for encryption of passwords for password-protected feeds
// in the database. A string of 24 random characters. If left blank, encryption // in the database. A string of 24 random characters. If left blank, encryption
// is not used. Requires mcrypt functions. // is not used. Requires mcrypt functions.
@ -104,13 +109,9 @@ define('PUBSUBHUBBUB_ENABLED', false);
// Enable client PubSubHubbub support in tt-rss. When disabled, tt-rss // Enable client PubSubHubbub support in tt-rss. When disabled, tt-rss
// won't try to subscribe to PUSH feed updates. // won't try to subscribe to PUSH feed updates.
// ********************* // ****************************
// *** Sphinx search *** // *** Sphinx search plugin ***
// ********************* // ****************************
define('SPHINX_ENABLED', false);
// Enable fulltext search using Sphinx (http://www.sphinxsearch.com)
// Please see http://tt-rss.org/wiki/SphinxSearch for more information.
define('SPHINX_SERVER', 'localhost:9312'); define('SPHINX_SERVER', 'localhost:9312');
// Hostname:port combination for the Sphinx server. // Hostname:port combination for the Sphinx server.
@ -145,13 +146,6 @@ define('SESSION_COOKIE_LIFETIME', 86400);
// Default lifetime of a session (e.g. login) cookie. In seconds, // Default lifetime of a session (e.g. login) cookie. In seconds,
// 0 means cookie will be deleted when browser closes. // 0 means cookie will be deleted when browser closes.
define('SESSION_CHECK_ADDRESS', 1);
// Check client IP address when validating session:
// 0 - disable checking
// 1 - check first 3 octets of an address (recommended)
// 2 - check first 2 octets of an address
// 3 - check entire address
// ********************************* // *********************************
// *** Email and digest settings *** // *** Email and digest settings ***
// ********************************* // *********************************
@ -181,14 +175,8 @@ define('SMTP_SECURE', '');
// *** Other settings (less important) *** // *** Other settings (less important) ***
// *************************************** // ***************************************
define('CHECK_FOR_NEW_VERSION', true); define('CHECK_FOR_UPDATES', true);
// Check for new versions of tt-rss automatically. // Check for updates automatically if running Git version
define('DETECT_ARTICLE_LANGUAGE', false);
// Detect article language when updating feeds, presently this is only
// used for hyphenation. This may increase amount of CPU time used by
// update processes, disable if necessary (i.e. you are being billed
// for CPU time)
define('ENABLE_GZIP_OUTPUT', false); define('ENABLE_GZIP_OUTPUT', false);
// Selectively gzip output to improve wire performance. This requires // Selectively gzip output to improve wire performance. This requires
@ -197,7 +185,7 @@ define('ENABLE_GZIP_OUTPUT', false);
// if you experience weird errors and tt-rss failing to start, blank pages // if you experience weird errors and tt-rss failing to start, blank pages
// after login, or content encoding errors, disable it. // after login, or content encoding errors, disable it.
define('PLUGINS', 'auth_remote, auth_internal, note, updater'); define('PLUGINS', 'auth_remote, auth_internal, note');
// Comma-separated list of plugins to load automatically for all users. // Comma-separated list of plugins to load automatically for all users.
// System plugins have to be specified here. Please enable at least one // System plugins have to be specified here. Please enable at least one
// authentication plugin here (auth_*). // authentication plugin here (auth_*).
@ -217,4 +205,3 @@ define('CONFIG_VERSION', 26);
// if necessary (after migrating all new options from this file). // if necessary (after migrating all new options from this file).
// vim:ft=php // vim:ft=php
?>

View file

@ -10,6 +10,7 @@
"fr": "Un lecteur de flux en PHP et Ajax" "fr": "Un lecteur de flux en PHP et Ajax"
}, },
"url": "http://tt-rss.org", "url": "http://tt-rss.org",
"version": "17.1",
"maintainer": { "maintainer": {
"name": "titoko", "name": "titoko",
"email": "titoko@titoko.fr" "email": "titoko@titoko.fr"

32
scripts/_common.sh Normal file
View file

@ -0,0 +1,32 @@
#
# Common variables
#
APPNAME="ttrss"
# ttrss version
VERSION="17.1"
# Remote URL to fetch ttrss tarball
TTRSS_BINARY_URL="https://tt-rss.org/gitlab/fox/tt-rss/repository/archive.zip?ref=${VERSION}"
#
# Common helpers
#
# Download and extract ttrss binary to the given directory
# usage: extract_ttrss DESTDIR
extract_ttrss() {
local DESTDIR=$1
local TMPDIR=$(mktemp -d)
# retrieve and extract ttrss tarball
ttrss_tarball="/tmp/ttrss.zip"
rm -f "$ttrss_tarball"
wget -q -O "$ttrss_tarball" "$TTRSS_BINARY_URL" \
|| ynh_die "Unable to download ttrss tarball"
unzip -q "$ttrss_tarball" -d "$TMPDIR" \
|| ynh_die "Unable to extract ttrss tarball"
sudo rsync -a "$TMPDIR"/tt-rss.git/* "$DESTDIR"
rm -rf "$ttrss_tarball" "$TMPDIR"
}

View file

@ -3,6 +3,10 @@
# causes the shell to exit if any subcommand or pipeline returns a non-zero status # causes the shell to exit if any subcommand or pipeline returns a non-zero status
set -e set -e
# Load common variables
source ./_common.sh
# Retrieve arguments # Retrieve arguments
domain=$1 domain=$1
path=$2 path=$2
@ -18,22 +22,21 @@ fi
deskey=$(dd if=/dev/urandom bs=1 count=200 2> /dev/null | tr -c -d 'A-Za-z0-9' | sed -n 's/\(.\{24\}\).*/\1/p') deskey=$(dd if=/dev/urandom bs=1 count=200 2> /dev/null | tr -c -d 'A-Za-z0-9' | sed -n 's/\(.\{24\}\).*/\1/p')
db_pwd=$(dd if=/dev/urandom bs=1 count=200 2> /dev/null | tr -c -d 'A-Za-z0-9' | sed -n 's/\(.\{24\}\).*/\1/p') db_pwd=$(dd if=/dev/urandom bs=1 count=200 2> /dev/null | tr -c -d 'A-Za-z0-9' | sed -n 's/\(.\{24\}\).*/\1/p')
# Use 'ttrss' as database name and user
db_user=$app
# Initialize database and store mysql password for upgrade
sudo yunohost app initdb $db_user -p $db_pwd -s $(readlink -e ../source/schema/TTRSS_schema_mysql.sql)
sudo yunohost app setting $app mysqlpwd -v $db_pwd
# Copy files to the right place # Copy files to the right place
final_path=/var/www/$app final_path=/var/www/$app
sudo mkdir -p $final_path sudo mkdir -p $final_path
sudo cp -r ../source/* $final_path extract_ttrss $final_path
sudo cp ../conf/config.php $final_path/ sudo cp ../conf/config.php $final_path/
sudo cp ../conf/*.patch /tmp/ sudo cp ../conf/*.patch /tmp/
sudo patch -d $final_path -p0 < /tmp/update.patch sudo patch -d $final_path -p0 < /tmp/update.patch
sudo patch -d $final_path/plugins/auth_remote/ -p0 < /tmp/init.patch sudo patch -d $final_path/plugins/auth_remote/ -p0 < /tmp/init.patch
# Use 'ttrss' as database name and user
db_user=$app
# Initialize database and store mysql password for upgrade
sudo yunohost app initdb $db_user -p $db_pwd -s $(readlink -e $final_path/schema/ttrss_schema_mysql.sql)
sudo yunohost app setting $app mysqlpwd -v $db_pwd
# Change variables in ttrss configuration # Change variables in ttrss configuration
sudo sed -i "s/yunouser/$db_user/g" $final_path/config.php sudo sed -i "s/yunouser/$db_user/g" $final_path/config.php
@ -74,4 +77,8 @@ sudo cp ../conf/nginx.conf /etc/nginx/conf.d/$domain.d/$app.conf
# Reload Nginx and regenerate SSOwat conf # Reload Nginx and regenerate SSOwat conf
sudo service nginx reload sudo service nginx reload
# Update database schema
sudo sudo -u www-data php ${final_path}/update.php --update-schema
sudo yunohost app setting $app skipped_uris -v "/public.php,/api" sudo yunohost app setting $app skipped_uris -v "/public.php,/api"

View file

@ -3,6 +3,9 @@
# causes the shell to exit if any subcommand or pipeline returns a non-zero status # causes the shell to exit if any subcommand or pipeline returns a non-zero status
set -e set -e
# Load common variables
source ./_common.sh
app=${!#} app=${!#}
domain=$(sudo yunohost app setting $app domain) domain=$(sudo yunohost app setting $app domain)
@ -14,7 +17,8 @@ db_pwd=$(sudo yunohost app setting $app mysqlpwd)
final_path=/var/www/$app final_path=/var/www/$app
sudo mkdir -p $final_path sudo mkdir -p $final_path
sudo cp -a ../source/* $final_path extract_ttrss $final_path
sudo cp ../conf/config.php $final_path/ sudo cp ../conf/config.php $final_path/
sudo cp ../conf/*.patch /tmp/ sudo cp ../conf/*.patch /tmp/
sudo patch -d $final_path -p0 < /tmp/update.patch sudo patch -d $final_path -p0 < /tmp/update.patch

View file

@ -1,3 +0,0 @@
AddType image/svg+xml svg
AddType image/svg+xml svgz

View file

@ -1,339 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View file

@ -1,41 +0,0 @@
Tiny Tiny RSS
=============
Web-based news feed aggregator, designed to allow you to read news from
any location, while feeling as close to a real desktop application as possible.
http://tt-rss.org (http://mirror.tt-rss.org)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Copyright (c) 2005 Andrew Dolgov (unless explicitly stated otherwise).
Uses Silk icons by Mark James: http://www.famfamfam.com/lab/icons/silk/
## Requirements
* Compatible web browser (http://tt-rss.org/wiki/CompatibleBrowsers)
* Web server, for example Apache
* PHP (with support for mbstring functions)
* PostgreSQL (tested on 8.3) or MySQL (InnoDB and version 4.1+ required)
## Installation Notes
http://tt-rss.org/wiki/InstallationNotes
## See also
* FAQ: http://tt-rss.org/wiki/FrequentlyAskedQuestions
* Forum: http://tt-rss.org/forum
* Wiki: http://tt-rss.org/wiki/WikiStart

View file

@ -1,74 +0,0 @@
<?php
error_reporting(E_ERROR | E_PARSE);
require_once "../config.php";
set_include_path(dirname(__FILE__) . PATH_SEPARATOR .
dirname(dirname(__FILE__)) . PATH_SEPARATOR .
dirname(dirname(__FILE__)) . "/include" . PATH_SEPARATOR .
get_include_path());
chdir("..");
define('TTRSS_SESSION_NAME', 'ttrss_api_sid');
define('NO_SESSION_AUTOSTART', true);
require_once "autoload.php";
require_once "db.php";
require_once "db-prefs.php";
require_once "functions.php";
require_once "sessions.php";
ini_set("session.gc_maxlifetime", 86400);
define('AUTH_DISABLE_OTP', true);
if (defined('ENABLE_GZIP_OUTPUT') && ENABLE_GZIP_OUTPUT &&
function_exists("ob_gzhandler")) {
ob_start("ob_gzhandler");
} else {
ob_start();
}
$input = file_get_contents("php://input");
if (defined('_API_DEBUG_HTTP_ENABLED') && _API_DEBUG_HTTP_ENABLED) {
// Override $_REQUEST with JSON-encoded data if available
// fallback on HTTP parameters
if ($input) {
$input = json_decode($input, true);
if ($input) $_REQUEST = $input;
}
} else {
// Accept JSON only
$input = json_decode($input, true);
$_REQUEST = $input;
}
if ($_REQUEST["sid"]) {
session_id($_REQUEST["sid"]);
@session_start();
} else if (defined('_API_DEBUG_HTTP_ENABLED')) {
@session_start();
}
if (!init_plugins()) return;
$method = strtolower($_REQUEST["op"]);
$handler = new API($_REQUEST);
if ($handler->before($method)) {
if ($method && method_exists($handler, $method)) {
$handler->$method();
} else if (method_exists($handler, 'index')) {
$handler->index($method);
}
$handler->after();
}
header("Api-Content-Length: " . ob_get_length());
ob_end_flush();
?>

View file

@ -1,51 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/atom:feed">
<html>
<head>
<title><xsl:value-of select="atom:title"/></title>
<link rel="stylesheet" type="text/css" href="css/utility.css"/>
<script language="javascript" src="lib/xsl_mop-up.js"></script>
</head>
<body onload="go_decoding()">
<div id="cometestme" style="display:none;">
<xsl:text disable-output-escaping="yes">&amp;amp;</xsl:text>
</div>
<div class="rss">
<h1><xsl:value-of select="atom:title"/></h1>
<p class="description">This feed has been exported from
<a target="_new" class="extlink" href="http://tt-rss.org">Tiny Tiny RSS</a>.
It contains the following items:</p>
<xsl:for-each select="atom:entry">
<h2><a target="_new" href="{atom:link/@href}"><xsl:value-of select="atom:title"/></a></h2>
<div name="decodeme" class="content">
<xsl:value-of select="atom:content" disable-output-escaping="yes"/>
</div>
<xsl:if test="enclosure">
<p><a href="{enclosure/@url}">Extra...</a></p>
</xsl:if>
</xsl:for-each>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

View file

@ -1,154 +0,0 @@
<?php
set_include_path(dirname(__FILE__) ."/include" . PATH_SEPARATOR .
get_include_path());
/* remove ill effects of magic quotes */
if (get_magic_quotes_gpc()) {
function stripslashes_deep($value) {
$value = is_array($value) ?
array_map('stripslashes_deep', $value) : stripslashes($value);
return $value;
}
$_POST = array_map('stripslashes_deep', $_POST);
$_GET = array_map('stripslashes_deep', $_GET);
$_COOKIE = array_map('stripslashes_deep', $_COOKIE);
$_REQUEST = array_map('stripslashes_deep', $_REQUEST);
}
$op = $_REQUEST["op"];
@$method = $_REQUEST['subop'] ? $_REQUEST['subop'] : $_REQUEST["method"];
if (!$method)
$method = 'index';
else
$method = strtolower($method);
/* Public calls compatibility shim */
$public_calls = array("globalUpdateFeeds", "rss", "getUnread", "getProfiles", "share",
"fbexport", "logout", "pubsub");
if (array_search($op, $public_calls) !== false) {
header("Location: public.php?" . $_SERVER['QUERY_STRING']);
return;
}
@$csrf_token = $_REQUEST['csrf_token'];
require_once "autoload.php";
require_once "sessions.php";
require_once "functions.php";
require_once "config.php";
require_once "db.php";
require_once "db-prefs.php";
startup_gettext();
$script_started = microtime(true);
if (!init_plugins()) return;
header("Content-Type: text/json; charset=utf-8");
if (ENABLE_GZIP_OUTPUT && function_exists("ob_gzhandler")) {
ob_start("ob_gzhandler");
}
if (SINGLE_USER_MODE) {
authenticate_user( "admin", null);
}
if ($_SESSION["uid"]) {
if (!validate_session()) {
header("Content-Type: text/json");
print json_encode(array("error" => array("code" => 6)));
return;
}
load_user_plugins( $_SESSION["uid"]);
}
$purge_intervals = array(
0 => __("Use default"),
-1 => __("Never purge"),
5 => __("1 week old"),
14 => __("2 weeks old"),
31 => __("1 month old"),
60 => __("2 months old"),
90 => __("3 months old"));
$update_intervals = array(
0 => __("Default interval"),
-1 => __("Disable updates"),
15 => __("Each 15 minutes"),
30 => __("Each 30 minutes"),
60 => __("Hourly"),
240 => __("Each 4 hours"),
720 => __("Each 12 hours"),
1440 => __("Daily"),
10080 => __("Weekly"));
$update_intervals_nodefault = array(
-1 => __("Disable updates"),
15 => __("Each 15 minutes"),
30 => __("Each 30 minutes"),
60 => __("Hourly"),
240 => __("Each 4 hours"),
720 => __("Each 12 hours"),
1440 => __("Daily"),
10080 => __("Weekly"));
$access_level_names = array(
0 => __("User"),
5 => __("Power User"),
10 => __("Administrator"));
#$error = sanity_check();
#if ($error['code'] != 0 && $op != "logout") {
# print json_encode(array("error" => $error));
# return;
#}
$op = str_replace("-", "_", $op);
$override = PluginHost::getInstance()->lookup_handler($op, $method);
if (class_exists($op) || $override) {
if ($override) {
$handler = $override;
} else {
$handler = new $op($_REQUEST);
}
if ($handler && implements_interface($handler, 'IHandler')) {
if (validate_csrf($csrf_token) || $handler->csrf_ignore($method)) {
if ($handler->before($method)) {
if ($method && method_exists($handler, $method)) {
$handler->$method();
} else {
if (method_exists($handler, "catchall")) {
$handler->catchall($method);
}
}
$handler->after();
return;
} else {
header("Content-Type: text/json");
print json_encode(array("error" => array("code" => 6)));
return;
}
} else {
header("Content-Type: text/json");
print json_encode(array("error" => array("code" => 6)));
return;
}
}
}
header("Content-Type: text/json");
print json_encode(array("error" => array("code" => 7)));
?>

View file

@ -1,2 +0,0 @@
Order deny,allow
Deny from all

View file

View file

View file

View file

View file

View file

@ -1,2 +0,0 @@
Order deny,allow
Deny from all

View file

@ -1,833 +0,0 @@
<?php
class API extends Handler {
const API_LEVEL = 11;
const STATUS_OK = 0;
const STATUS_ERR = 1;
private $seq;
function before($method) {
if (parent::before($method)) {
header("Content-Type: text/json");
if (!$_SESSION["uid"] && $method != "login" && $method != "isloggedin") {
$this->wrap(self::STATUS_ERR, array("error" => 'NOT_LOGGED_IN'));
return false;
}
if ($_SESSION["uid"] && $method != "logout" && !get_pref('ENABLE_API_ACCESS')) {
$this->wrap(self::STATUS_ERR, array("error" => 'API_DISABLED'));
return false;
}
$this->seq = (int) $_REQUEST['seq'];
return true;
}
return false;
}
function wrap($status, $reply) {
print json_encode(array("seq" => $this->seq,
"status" => $status,
"content" => $reply));
}
function getVersion() {
$rv = array("version" => VERSION);
$this->wrap(self::STATUS_OK, $rv);
}
function getApiLevel() {
$rv = array("level" => self::API_LEVEL);
$this->wrap(self::STATUS_OK, $rv);
}
function login() {
@session_destroy();
@session_start();
$login = $this->dbh->escape_string($_REQUEST["user"]);
$password = $_REQUEST["password"];
$password_base64 = base64_decode($_REQUEST["password"]);
if (SINGLE_USER_MODE) $login = "admin";
$result = $this->dbh->query("SELECT id FROM ttrss_users WHERE login = '$login'");
if ($this->dbh->num_rows($result) != 0) {
$uid = $this->dbh->fetch_result($result, 0, "id");
} else {
$uid = 0;
}
if (!$uid) {
$this->wrap(self::STATUS_ERR, array("error" => "LOGIN_ERROR"));
return;
}
if (get_pref("ENABLE_API_ACCESS", $uid)) {
if (authenticate_user($login, $password)) { // try login with normal password
$this->wrap(self::STATUS_OK, array("session_id" => session_id(),
"api_level" => self::API_LEVEL));
} else if (authenticate_user($login, $password_base64)) { // else try with base64_decoded password
$this->wrap(self::STATUS_OK, array("session_id" => session_id(),
"api_level" => self::API_LEVEL));
} else { // else we are not logged in
user_error("Failed login attempt for $login from {$_SERVER['REMOTE_ADDR']}", E_USER_WARNING);
$this->wrap(self::STATUS_ERR, array("error" => "LOGIN_ERROR"));
}
} else {
$this->wrap(self::STATUS_ERR, array("error" => "API_DISABLED"));
}
}
function logout() {
logout_user();
$this->wrap(self::STATUS_OK, array("status" => "OK"));
}
function isLoggedIn() {
$this->wrap(self::STATUS_OK, array("status" => $_SESSION["uid"] != ''));
}
function getUnread() {
$feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]);
$is_cat = $this->dbh->escape_string($_REQUEST["is_cat"]);
if ($feed_id) {
$this->wrap(self::STATUS_OK, array("unread" => getFeedUnread($feed_id, $is_cat)));
} else {
$this->wrap(self::STATUS_OK, array("unread" => getGlobalUnread()));
}
}
/* Method added for ttrss-reader for Android */
function getCounters() {
$this->wrap(self::STATUS_OK, getAllCounters());
}
function getFeeds() {
$cat_id = $this->dbh->escape_string($_REQUEST["cat_id"]);
$unread_only = sql_bool_to_bool($_REQUEST["unread_only"]);
$limit = (int) $this->dbh->escape_string($_REQUEST["limit"]);
$offset = (int) $this->dbh->escape_string($_REQUEST["offset"]);
$include_nested = sql_bool_to_bool($_REQUEST["include_nested"]);
$feeds = $this->api_get_feeds($cat_id, $unread_only, $limit, $offset, $include_nested);
$this->wrap(self::STATUS_OK, $feeds);
}
function getCategories() {
$unread_only = sql_bool_to_bool($_REQUEST["unread_only"]);
$enable_nested = sql_bool_to_bool($_REQUEST["enable_nested"]);
$include_empty = sql_bool_to_bool($_REQUEST['include_empty']);
// TODO do not return empty categories, return Uncategorized and standard virtual cats
if ($enable_nested)
$nested_qpart = "parent_cat IS NULL";
else
$nested_qpart = "true";
$result = $this->dbh->query("SELECT
id, title, order_id, (SELECT COUNT(id) FROM
ttrss_feeds WHERE
ttrss_feed_categories.id IS NOT NULL AND cat_id = ttrss_feed_categories.id) AS num_feeds,
(SELECT COUNT(id) FROM
ttrss_feed_categories AS c2 WHERE
c2.parent_cat = ttrss_feed_categories.id) AS num_cats
FROM ttrss_feed_categories
WHERE $nested_qpart AND owner_uid = " .
$_SESSION["uid"]);
$cats = array();
while ($line = $this->dbh->fetch_assoc($result)) {
if ($include_empty || $line["num_feeds"] > 0 || $line["num_cats"] > 0) {
$unread = getFeedUnread($line["id"], true);
if ($enable_nested)
$unread += getCategoryChildrenUnread($line["id"]);
if ($unread || !$unread_only) {
array_push($cats, array("id" => $line["id"],
"title" => $line["title"],
"unread" => $unread,
"order_id" => (int) $line["order_id"],
));
}
}
}
foreach (array(-2,-1,0) as $cat_id) {
if ($include_empty || !$this->isCategoryEmpty($cat_id)) {
$unread = getFeedUnread($cat_id, true);
if ($unread || !$unread_only) {
array_push($cats, array("id" => $cat_id,
"title" => getCategoryTitle($cat_id),
"unread" => $unread));
}
}
}
$this->wrap(self::STATUS_OK, $cats);
}
function getHeadlines() {
$feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]);
if ($feed_id != "") {
$limit = (int)$this->dbh->escape_string($_REQUEST["limit"]);
if (!$limit || $limit >= 200) $limit = 200;
$offset = (int)$this->dbh->escape_string($_REQUEST["skip"]);
$filter = $this->dbh->escape_string($_REQUEST["filter"]);
$is_cat = sql_bool_to_bool($_REQUEST["is_cat"]);
$show_excerpt = sql_bool_to_bool($_REQUEST["show_excerpt"]);
$show_content = sql_bool_to_bool($_REQUEST["show_content"]);
/* all_articles, unread, adaptive, marked, updated */
$view_mode = $this->dbh->escape_string($_REQUEST["view_mode"]);
$include_attachments = sql_bool_to_bool($_REQUEST["include_attachments"]);
$since_id = (int)$this->dbh->escape_string($_REQUEST["since_id"]);
$include_nested = sql_bool_to_bool($_REQUEST["include_nested"]);
$sanitize_content = !isset($_REQUEST["sanitize"]) ||
sql_bool_to_bool($_REQUEST["sanitize"]);
$force_update = sql_bool_to_bool($_REQUEST["force_update"]);
$has_sandbox = sql_bool_to_bool($_REQUEST["has_sandbox"]);
$excerpt_length = (int)$this->dbh->escape_string($_REQUEST["excerpt_length"]);
$_SESSION['hasSandbox'] = $has_sandbox;
$override_order = false;
switch ($_REQUEST["order_by"]) {
case "title":
$override_order = "ttrss_entries.title";
break;
case "date_reverse":
$override_order = "score DESC, date_entered, updated";
break;
case "feed_dates":
$override_order = "updated DESC";
break;
}
/* do not rely on params below */
$search = $this->dbh->escape_string($_REQUEST["search"]);
$search_mode = $this->dbh->escape_string($_REQUEST["search_mode"]);
$headlines = $this->api_get_headlines($feed_id, $limit, $offset,
$filter, $is_cat, $show_excerpt, $show_content, $view_mode, $override_order,
$include_attachments, $since_id, $search, $search_mode,
$include_nested, $sanitize_content, $force_update, $excerpt_length);
$this->wrap(self::STATUS_OK, $headlines);
} else {
$this->wrap(self::STATUS_ERR, array("error" => 'INCORRECT_USAGE'));
}
}
function updateArticle() {
$article_ids = array_filter(explode(",", $this->dbh->escape_string($_REQUEST["article_ids"])), is_numeric);
$mode = (int) $this->dbh->escape_string($_REQUEST["mode"]);
$data = $this->dbh->escape_string($_REQUEST["data"]);
$field_raw = (int)$this->dbh->escape_string($_REQUEST["field"]);
$field = "";
$set_to = "";
switch ($field_raw) {
case 0:
$field = "marked";
$additional_fields = ",last_marked = NOW()";
break;
case 1:
$field = "published";
$additional_fields = ",last_published = NOW()";
break;
case 2:
$field = "unread";
$additional_fields = ",last_read = NOW()";
break;
case 3:
$field = "note";
};
switch ($mode) {
case 1:
$set_to = "true";
break;
case 0:
$set_to = "false";
break;
case 2:
$set_to = "NOT $field";
break;
}
if ($field == "note") $set_to = "'$data'";
if ($field && $set_to && count($article_ids) > 0) {
$article_ids = join(", ", $article_ids);
$result = $this->dbh->query("UPDATE ttrss_user_entries SET $field = $set_to $additional_fields WHERE ref_id IN ($article_ids) AND owner_uid = " . $_SESSION["uid"]);
$num_updated = $this->dbh->affected_rows($result);
if ($num_updated > 0 && $field == "unread") {
$result = $this->dbh->query("SELECT DISTINCT feed_id FROM ttrss_user_entries
WHERE ref_id IN ($article_ids)");
while ($line = $this->dbh->fetch_assoc($result)) {
ccache_update($line["feed_id"], $_SESSION["uid"]);
}
}
if ($num_updated > 0 && $field == "published") {
if (PUBSUBHUBBUB_HUB) {
$rss_link = get_self_url_prefix() .
"/public.php?op=rss&id=-2&key=" .
get_feed_access_key(-2, false);
$p = new Publisher(PUBSUBHUBBUB_HUB);
$pubsub_result = $p->publish_update($rss_link);
}
}
$this->wrap(self::STATUS_OK, array("status" => "OK",
"updated" => $num_updated));
} else {
$this->wrap(self::STATUS_ERR, array("error" => 'INCORRECT_USAGE'));
}
}
function getArticle() {
$article_id = join(",", array_filter(explode(",", $this->dbh->escape_string($_REQUEST["article_id"])), is_numeric));
if ($article_id) {
$query = "SELECT id,title,link,content,feed_id,comments,int_id,
marked,unread,published,score,note,lang,
".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
author,(SELECT title FROM ttrss_feeds WHERE id = feed_id) AS feed_title
FROM ttrss_entries,ttrss_user_entries
WHERE id IN ($article_id) AND ref_id = id AND owner_uid = " .
$_SESSION["uid"] ;
$result = $this->dbh->query($query);
$articles = array();
if ($this->dbh->num_rows($result) != 0) {
while ($line = $this->dbh->fetch_assoc($result)) {
$attachments = get_article_enclosures($line['id']);
$article = array(
"id" => $line["id"],
"title" => $line["title"],
"link" => $line["link"],
"labels" => get_article_labels($line['id']),
"unread" => sql_bool_to_bool($line["unread"]),
"marked" => sql_bool_to_bool($line["marked"]),
"published" => sql_bool_to_bool($line["published"]),
"comments" => $line["comments"],
"author" => $line["author"],
"updated" => (int) strtotime($line["updated"]),
"content" => $line["content"],
"feed_id" => $line["feed_id"],
"attachments" => $attachments,
"score" => (int)$line["score"],
"feed_title" => $line["feed_title"],
"note" => $line["note"],
"lang" => $line["lang"]
);
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_API) as $p) {
$article = $p->hook_render_article_api(array("article" => $article));
}
array_push($articles, $article);
}
}
$this->wrap(self::STATUS_OK, $articles);
} else {
$this->wrap(self::STATUS_ERR, array("error" => 'INCORRECT_USAGE'));
}
}
function getConfig() {
$config = array(
"icons_dir" => ICONS_DIR,
"icons_url" => ICONS_URL);
$config["daemon_is_running"] = file_is_locked("update_daemon.lock");
$result = $this->dbh->query("SELECT COUNT(*) AS cf FROM
ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
$num_feeds = $this->dbh->fetch_result($result, 0, "cf");
$config["num_feeds"] = (int)$num_feeds;
$this->wrap(self::STATUS_OK, $config);
}
function updateFeed() {
require_once "include/rssfuncs.php";
$feed_id = (int) $this->dbh->escape_string($_REQUEST["feed_id"]);
update_rss_feed($feed_id, true);
$this->wrap(self::STATUS_OK, array("status" => "OK"));
}
function catchupFeed() {
$feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]);
$is_cat = $this->dbh->escape_string($_REQUEST["is_cat"]);
catchup_feed($feed_id, $is_cat);
$this->wrap(self::STATUS_OK, array("status" => "OK"));
}
function getPref() {
$pref_name = $this->dbh->escape_string($_REQUEST["pref_name"]);
$this->wrap(self::STATUS_OK, array("value" => get_pref($pref_name)));
}
function getLabels() {
//$article_ids = array_filter(explode(",", $this->dbh->escape_string($_REQUEST["article_ids"])), is_numeric);
$article_id = (int)$_REQUEST['article_id'];
$rv = array();
$result = $this->dbh->query("SELECT id, caption, fg_color, bg_color
FROM ttrss_labels2
WHERE owner_uid = '".$_SESSION['uid']."' ORDER BY caption");
if ($article_id)
$article_labels = get_article_labels($article_id);
else
$article_labels = array();
while ($line = $this->dbh->fetch_assoc($result)) {
$checked = false;
foreach ($article_labels as $al) {
if (feed_to_label_id($al[0]) == $line['id']) {
$checked = true;
break;
}
}
array_push($rv, array(
"id" => (int)label_to_feed_id($line['id']),
"caption" => $line['caption'],
"fg_color" => $line['fg_color'],
"bg_color" => $line['bg_color'],
"checked" => $checked));
}
$this->wrap(self::STATUS_OK, $rv);
}
function setArticleLabel() {
$article_ids = array_filter(explode(",", $this->dbh->escape_string($_REQUEST["article_ids"])), is_numeric);
$label_id = (int) $this->dbh->escape_string($_REQUEST['label_id']);
$assign = (bool) $this->dbh->escape_string($_REQUEST['assign']) == "true";
$label = $this->dbh->escape_string(label_find_caption(
feed_to_label_id($label_id), $_SESSION["uid"]));
$num_updated = 0;
if ($label) {
foreach ($article_ids as $id) {
if ($assign)
label_add_article($id, $label, $_SESSION["uid"]);
else
label_remove_article($id, $label, $_SESSION["uid"]);
++$num_updated;
}
}
$this->wrap(self::STATUS_OK, array("status" => "OK",
"updated" => $num_updated));
}
function index($method) {
$plugin = PluginHost::getInstance()->get_api_method(strtolower($method));
if ($plugin && method_exists($plugin, $method)) {
$reply = $plugin->$method();
$this->wrap($reply[0], $reply[1]);
} else {
$this->wrap(self::STATUS_ERR, array("error" => 'UNKNOWN_METHOD', "method" => $method));
}
}
function shareToPublished() {
$title = $this->dbh->escape_string(strip_tags($_REQUEST["title"]));
$url = $this->dbh->escape_string(strip_tags($_REQUEST["url"]));
$content = $this->dbh->escape_string(strip_tags($_REQUEST["content"]));
if (Article::create_published_article($title, $url, $content, "", $_SESSION["uid"])) {
$this->wrap(self::STATUS_OK, array("status" => 'OK'));
} else {
$this->wrap(self::STATUS_ERR, array("error" => 'Publishing failed'));
}
}
static function api_get_feeds($cat_id, $unread_only, $limit, $offset, $include_nested = false) {
$feeds = array();
/* Labels */
if ($cat_id == -4 || $cat_id == -2) {
$counters = getLabelCounters(true);
foreach (array_values($counters) as $cv) {
$unread = $cv["counter"];
if ($unread || !$unread_only) {
$row = array(
"id" => (int) $cv["id"],
"title" => $cv["description"],
"unread" => $cv["counter"],
"cat_id" => -2,
);
array_push($feeds, $row);
}
}
}
/* Virtual feeds */
if ($cat_id == -4 || $cat_id == -1) {
foreach (array(-1, -2, -3, -4, -6, 0) as $i) {
$unread = getFeedUnread($i);
if ($unread || !$unread_only) {
$title = getFeedTitle($i);
$row = array(
"id" => $i,
"title" => $title,
"unread" => $unread,
"cat_id" => -1,
);
array_push($feeds, $row);
}
}
}
/* Child cats */
if ($include_nested && $cat_id) {
$result = db_query("SELECT
id, title FROM ttrss_feed_categories
WHERE parent_cat = '$cat_id' AND owner_uid = " . $_SESSION["uid"] .
" ORDER BY id, title");
while ($line = db_fetch_assoc($result)) {
$unread = getFeedUnread($line["id"], true) +
getCategoryChildrenUnread($line["id"]);
if ($unread || !$unread_only) {
$row = array(
"id" => (int) $line["id"],
"title" => $line["title"],
"unread" => $unread,
"is_cat" => true,
);
array_push($feeds, $row);
}
}
}
/* Real feeds */
if ($limit) {
$limit_qpart = "LIMIT $limit OFFSET $offset";
} else {
$limit_qpart = "";
}
if ($cat_id == -4 || $cat_id == -3) {
$result = db_query("SELECT
id, feed_url, cat_id, title, order_id, ".
SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
FROM ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"] .
" ORDER BY cat_id, title " . $limit_qpart);
} else {
if ($cat_id)
$cat_qpart = "cat_id = '$cat_id'";
else
$cat_qpart = "cat_id IS NULL";
$result = db_query("SELECT
id, feed_url, cat_id, title, order_id, ".
SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
FROM ttrss_feeds WHERE
$cat_qpart AND owner_uid = " . $_SESSION["uid"] .
" ORDER BY cat_id, title " . $limit_qpart);
}
while ($line = db_fetch_assoc($result)) {
$unread = getFeedUnread($line["id"]);
$has_icon = feed_has_icon($line['id']);
if ($unread || !$unread_only) {
$row = array(
"feed_url" => $line["feed_url"],
"title" => $line["title"],
"id" => (int)$line["id"],
"unread" => (int)$unread,
"has_icon" => $has_icon,
"cat_id" => (int)$line["cat_id"],
"last_updated" => (int) strtotime($line["last_updated"]),
"order_id" => (int) $line["order_id"],
);
array_push($feeds, $row);
}
}
return $feeds;
}
static function api_get_headlines($feed_id, $limit, $offset,
$filter, $is_cat, $show_excerpt, $show_content, $view_mode, $order,
$include_attachments, $since_id,
$search = "", $search_mode = "",
$include_nested = false, $sanitize_content = true, $force_update = false, $excerpt_length = 100) {
if ($force_update && $feed_id > 0 && is_numeric($feed_id)) {
// Update the feed if required with some basic flood control
$result = db_query(
"SELECT cache_images,".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
FROM ttrss_feeds WHERE id = '$feed_id'");
if (db_num_rows($result) != 0) {
$last_updated = strtotime(db_fetch_result($result, 0, "last_updated"));
$cache_images = sql_bool_to_bool(db_fetch_result($result, 0, "cache_images"));
if (!$cache_images && time() - $last_updated > 120) {
include "rssfuncs.php";
update_rss_feed($feed_id, true, true);
} else {
db_query("UPDATE ttrss_feeds SET last_updated = '1970-01-01', last_update_started = '1970-01-01'
WHERE id = '$feed_id'");
}
}
}
$qfh_ret = queryFeedHeadlines($feed_id, $limit,
$view_mode, $is_cat, $search, $search_mode,
$order, $offset, 0, false, $since_id, $include_nested);
$result = $qfh_ret[0];
$feed_title = $qfh_ret[1];
$headlines = array();
while ($line = db_fetch_assoc($result)) {
$line["content_preview"] = truncate_string(strip_tags($line["content"]), $excerpt_length);
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_QUERY_HEADLINES) as $p) {
$line = $p->hook_query_headlines($line, $excerpt_length, true);
}
$is_updated = ($line["last_read"] == "" &&
($line["unread"] != "t" && $line["unread"] != "1"));
$tags = explode(",", $line["tag_cache"]);
$label_cache = $line["label_cache"];
$labels = false;
if ($label_cache) {
$label_cache = json_decode($label_cache, true);
if ($label_cache) {
if ($label_cache["no-labels"] == 1)
$labels = array();
else
$labels = $label_cache;
}
}
if (!is_array($labels)) $labels = get_article_labels($line["id"]);
//if (!$tags) $tags = get_article_tags($line["id"]);
//if (!$labels) $labels = get_article_labels($line["id"]);
$headline_row = array(
"id" => (int)$line["id"],
"unread" => sql_bool_to_bool($line["unread"]),
"marked" => sql_bool_to_bool($line["marked"]),
"published" => sql_bool_to_bool($line["published"]),
"updated" => (int) strtotime($line["updated"]),
"is_updated" => $is_updated,
"title" => $line["title"],
"link" => $line["link"],
"feed_id" => $line["feed_id"],
"tags" => $tags,
);
if ($include_attachments)
$headline_row['attachments'] = get_article_enclosures(
$line['id']);
if ($show_excerpt)
$headline_row["excerpt"] = $line["content_preview"];
if ($show_content) {
if ($sanitize_content) {
$headline_row["content"] = sanitize(
$line["content"],
sql_bool_to_bool($line['hide_images']),
false, $line["site_url"], false, $line["id"]);
} else {
$headline_row["content"] = $line["content"];
}
}
// unify label output to ease parsing
if ($labels["no-labels"] == 1) $labels = array();
$headline_row["labels"] = $labels;
$headline_row["feed_title"] = $line["feed_title"] ? $line["feed_title"] :
$feed_title;
$headline_row["comments_count"] = (int)$line["num_comments"];
$headline_row["comments_link"] = $line["comments"];
$headline_row["always_display_attachments"] = sql_bool_to_bool($line["always_display_enclosures"]);
$headline_row["author"] = $line["author"];
$headline_row["score"] = (int)$line["score"];
$headline_row["note"] = $line["note"];
$headline_row["lang"] = $line["lang"];
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_API) as $p) {
$headline_row = $p->hook_render_article_api(array("headline" => $headline_row));
}
array_push($headlines, $headline_row);
}
return $headlines;
}
function unsubscribeFeed() {
$feed_id = (int) $this->dbh->escape_string($_REQUEST["feed_id"]);
$result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE
id = '$feed_id' AND owner_uid = ".$_SESSION["uid"]);
if ($this->dbh->num_rows($result) != 0) {
Pref_Feeds::remove_feed($feed_id, $_SESSION["uid"]);
$this->wrap(self::STATUS_OK, array("status" => "OK"));
} else {
$this->wrap(self::STATUS_ERR, array("error" => "FEED_NOT_FOUND"));
}
}
function subscribeToFeed() {
$feed_url = $this->dbh->escape_string($_REQUEST["feed_url"]);
$category_id = (int) $this->dbh->escape_string($_REQUEST["category_id"]);
$login = $this->dbh->escape_string($_REQUEST["login"]);
$password = $this->dbh->escape_string($_REQUEST["password"]);
if ($feed_url) {
$rc = subscribe_to_feed($feed_url, $category_id, $login, $password);
$this->wrap(self::STATUS_OK, array("status" => $rc));
} else {
$this->wrap(self::STATUS_ERR, array("error" => 'INCORRECT_USAGE'));
}
}
function getFeedTree() {
$include_empty = sql_bool_to_bool($_REQUEST['include_empty']);
$pf = new Pref_Feeds($_REQUEST);
$_REQUEST['mode'] = 2;
$_REQUEST['force_show_empty'] = $include_empty;
if ($pf){
$data = $pf->makefeedtree();
$this->wrap(self::STATUS_OK, array("categories" => $data));
} else {
$this->wrap(self::STATUS_ERR, array("error" =>
'UNABLE_TO_INSTANTIATE_OBJECT'));
}
}
// only works for labels or uncategorized for the time being
private function isCategoryEmpty($id) {
if ($id == -2) {
$result = $this->dbh->query("SELECT COUNT(*) AS count FROM ttrss_labels2
WHERE owner_uid = " . $_SESSION["uid"]);
return $this->dbh->fetch_result($result, 0, "count") == 0;
} else if ($id == 0) {
$result = $this->dbh->query("SELECT COUNT(*) AS count FROM ttrss_feeds
WHERE cat_id IS NULL AND owner_uid = " . $_SESSION["uid"]);
return $this->dbh->fetch_result($result, 0, "count") == 0;
}
return false;
}
}
?>

View file

@ -1,345 +0,0 @@
<?php
class Article extends Handler_Protected {
function csrf_ignore($method) {
$csrf_ignored = array("redirect", "editarticletags");
return array_search($method, $csrf_ignored) !== false;
}
function redirect() {
$id = $this->dbh->escape_string($_REQUEST['id']);
$result = $this->dbh->query("SELECT link FROM ttrss_entries, ttrss_user_entries
WHERE id = '$id' AND id = ref_id AND owner_uid = '".$_SESSION['uid']."'
LIMIT 1");
if ($this->dbh->num_rows($result) == 1) {
$article_url = $this->dbh->fetch_result($result, 0, 'link');
$article_url = str_replace("\n", "", $article_url);
header("Location: $article_url");
return;
} else {
print_error(__("Article not found."));
}
}
function view() {
$id = $this->dbh->escape_string($_REQUEST["id"]);
$cids = explode(",", $this->dbh->escape_string($_REQUEST["cids"]));
$mode = $this->dbh->escape_string($_REQUEST["mode"]);
// in prefetch mode we only output requested cids, main article
// just gets marked as read (it already exists in client cache)
$articles = array();
if ($mode == "") {
array_push($articles, format_article($id, false));
} else if ($mode == "zoom") {
array_push($articles, format_article($id, true, true));
} else if ($mode == "raw") {
if ($_REQUEST['html']) {
header("Content-Type: text/html");
print '<link rel="stylesheet" type="text/css" href="css/tt-rss.css"/>';
}
$article = format_article($id, false);
print $article['content'];
return;
}
$this->catchupArticleById($id, 0);
if (!$_SESSION["bw_limit"]) {
foreach ($cids as $cid) {
if ($cid) {
array_push($articles, format_article($cid, false, false));
}
}
}
print json_encode($articles);
}
private function catchupArticleById($id, $cmode) {
if ($cmode == 0) {
$this->dbh->query("UPDATE ttrss_user_entries SET
unread = false,last_read = NOW()
WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
} else if ($cmode == 1) {
$this->dbh->query("UPDATE ttrss_user_entries SET
unread = true
WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
} else {
$this->dbh->query("UPDATE ttrss_user_entries SET
unread = NOT unread,last_read = NOW()
WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
}
$feed_id = getArticleFeed($id);
ccache_update($feed_id, $_SESSION["uid"]);
}
static function create_published_article($title, $url, $content, $labels_str,
$owner_uid) {
$guid = 'SHA1:' . sha1("ttshared:" . $url . $owner_uid); // include owner_uid to prevent global GUID clash
$content_hash = sha1($content);
if ($labels_str != "") {
$labels = explode(",", $labels_str);
} else {
$labels = array();
}
$rc = false;
if (!$title) $title = $url;
if (!$title && !$url) return false;
if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) return false;
db_query("BEGIN");
// only check for our user data here, others might have shared this with different content etc
$result = db_query("SELECT id FROM ttrss_entries, ttrss_user_entries WHERE
guid = '$guid' AND ref_id = id AND owner_uid = '$owner_uid' LIMIT 1");
if (db_num_rows($result) != 0) {
$ref_id = db_fetch_result($result, 0, "id");
$result = db_query("SELECT int_id FROM ttrss_user_entries WHERE
ref_id = '$ref_id' AND owner_uid = '$owner_uid' LIMIT 1");
if (db_num_rows($result) != 0) {
$int_id = db_fetch_result($result, 0, "int_id");
db_query("UPDATE ttrss_entries SET
content = '$content', content_hash = '$content_hash' WHERE id = '$ref_id'");
db_query("UPDATE ttrss_user_entries SET published = true,
last_published = NOW() WHERE
int_id = '$int_id' AND owner_uid = '$owner_uid'");
} else {
db_query("INSERT INTO ttrss_user_entries
(ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache,
last_read, note, unread, last_published)
VALUES
('$ref_id', '', NULL, NULL, $owner_uid, true, '', '', NOW(), '', false, NOW())");
}
if (count($labels) != 0) {
foreach ($labels as $label) {
label_add_article($ref_id, trim($label), $owner_uid);
}
}
$rc = true;
} else {
$result = db_query("INSERT INTO ttrss_entries
(title, guid, link, updated, content, content_hash, date_entered, date_updated)
VALUES
('$title', '$guid', '$url', NOW(), '$content', '$content_hash', NOW(), NOW())");
$result = db_query("SELECT id FROM ttrss_entries WHERE guid = '$guid'");
if (db_num_rows($result) != 0) {
$ref_id = db_fetch_result($result, 0, "id");
db_query("INSERT INTO ttrss_user_entries
(ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache,
last_read, note, unread, last_published)
VALUES
('$ref_id', '', NULL, NULL, $owner_uid, true, '', '', NOW(), '', false, NOW())");
if (count($labels) != 0) {
foreach ($labels as $label) {
label_add_article($ref_id, trim($label), $owner_uid);
}
}
$rc = true;
}
}
db_query("COMMIT");
return $rc;
}
function editArticleTags() {
print __("Tags for this article (separated by commas):")."<br>";
$param = $this->dbh->escape_string($_REQUEST['param']);
$tags = get_article_tags($this->dbh->escape_string($param));
$tags_str = join(", ", $tags);
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$param\">";
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"article\">";
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"setArticleTags\">";
print "<table width='100%'><tr><td>";
print "<textarea dojoType=\"dijit.form.SimpleTextarea\" rows='4'
style='font-size : 12px; width : 100%' id=\"tags_str\"
name='tags_str'>$tags_str</textarea>
<div class=\"autocomplete\" id=\"tags_choices\"
style=\"display:none\"></div>";
print "</td></tr></table>";
print "<div class='dlgButtons'>";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"dijit.byId('editTagsDlg').execute()\">".__('Save')."</button> ";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"dijit.byId('editTagsDlg').hide()\">".__('Cancel')."</button>";
print "</div>";
}
function setScore() {
$ids = $this->dbh->escape_string($_REQUEST['id']);
$score = (int)$this->dbh->escape_string($_REQUEST['score']);
$this->dbh->query("UPDATE ttrss_user_entries SET
score = '$score' WHERE ref_id IN ($ids) AND owner_uid = " . $_SESSION["uid"]);
print json_encode(array("id" => $ids,
"score_pic" => get_score_pic($score)));
}
function setArticleTags() {
$id = $this->dbh->escape_string($_REQUEST["id"]);
$tags_str = $this->dbh->escape_string($_REQUEST["tags_str"]);
$tags = array_unique(trim_array(explode(",", $tags_str)));
$this->dbh->query("BEGIN");
$result = $this->dbh->query("SELECT int_id FROM ttrss_user_entries WHERE
ref_id = '$id' AND owner_uid = '".$_SESSION["uid"]."' LIMIT 1");
if ($this->dbh->num_rows($result) == 1) {
$tags_to_cache = array();
$int_id = $this->dbh->fetch_result($result, 0, "int_id");
$this->dbh->query("DELETE FROM ttrss_tags WHERE
post_int_id = $int_id AND owner_uid = '".$_SESSION["uid"]."'");
foreach ($tags as $tag) {
$tag = sanitize_tag($tag);
if (!tag_is_valid($tag)) {
continue;
}
if (preg_match("/^[0-9]*$/", $tag)) {
continue;
}
// print "<!-- $id : $int_id : $tag -->";
if ($tag != '') {
$this->dbh->query("INSERT INTO ttrss_tags
(post_int_id, owner_uid, tag_name) VALUES ('$int_id', '".$_SESSION["uid"]."', '$tag')");
}
array_push($tags_to_cache, $tag);
}
/* update tag cache */
sort($tags_to_cache);
$tags_str = join(",", $tags_to_cache);
$this->dbh->query("UPDATE ttrss_user_entries
SET tag_cache = '$tags_str' WHERE ref_id = '$id'
AND owner_uid = " . $_SESSION["uid"]);
}
$this->dbh->query("COMMIT");
$tags = get_article_tags($id);
$tags_str = format_tags_string($tags, $id);
$tags_str_full = join(", ", $tags);
if (!$tags_str_full) $tags_str_full = __("no tags");
print json_encode(array("id" => (int)$id,
"content" => $tags_str, "content_full" => $tags_str_full));
}
function completeTags() {
$search = $this->dbh->escape_string($_REQUEST["search"]);
$result = $this->dbh->query("SELECT DISTINCT tag_name FROM ttrss_tags
WHERE owner_uid = '".$_SESSION["uid"]."' AND
tag_name LIKE '$search%' ORDER BY tag_name
LIMIT 10");
print "<ul>";
while ($line = $this->dbh->fetch_assoc($result)) {
print "<li>" . $line["tag_name"] . "</li>";
}
print "</ul>";
}
function assigntolabel() {
return $this->labelops(true);
}
function removefromlabel() {
return $this->labelops(false);
}
private function labelops($assign) {
$reply = array();
$ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
$label_id = $this->dbh->escape_string($_REQUEST["lid"]);
$label = $this->dbh->escape_string(label_find_caption($label_id,
$_SESSION["uid"]));
$reply["info-for-headlines"] = array();
if ($label) {
foreach ($ids as $id) {
if ($assign)
label_add_article($id, $label, $_SESSION["uid"]);
else
label_remove_article($id, $label, $_SESSION["uid"]);
$labels = get_article_labels($id, $_SESSION["uid"]);
array_push($reply["info-for-headlines"],
array("id" => $id, "labels" => format_article_labels($labels, $id)));
}
}
$reply["message"] = "UPDATE_COUNTERS";
print json_encode($reply);
}
}

View file

@ -1,61 +0,0 @@
<?php
class Auth_Base {
private $dbh;
function __construct() {
$this->dbh = Db::get();
}
function check_password($owner_uid, $password) {
return false;
}
function authenticate($login, $password) {
return false;
}
// Auto-creates specified user if allowed by system configuration
// Can be used instead of find_user_by_login() by external auth modules
function auto_create_user($login, $password = false) {
if ($login && defined('AUTH_AUTO_CREATE') && AUTH_AUTO_CREATE) {
$user_id = $this->find_user_by_login($login);
if (!$password) $password = make_password();
if (!$user_id) {
$login = $this->dbh->escape_string($login);
$salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
$pwd_hash = encrypt_password($password, $salt, true);
$query = "INSERT INTO ttrss_users
(login,access_level,last_login,created,pwd_hash,salt)
VALUES ('$login', 0, null, NOW(), '$pwd_hash','$salt')";
$this->dbh->query($query);
return $this->find_user_by_login($login);
} else {
return $user_id;
}
}
return $this->find_user_by_login($login);
}
function find_user_by_login($login) {
$login = $this->dbh->escape_string($login);
$result = $this->dbh->query("SELECT id FROM ttrss_users WHERE
login = '$login'");
if ($this->dbh->num_rows($result) > 0) {
return $this->dbh->fetch_result($result, 0, "id");
} else {
return false;
}
}
}
?>

View file

@ -1,119 +0,0 @@
<?php
class Backend extends Handler {
function loading() {
header("Content-type: text/html");
print __("Loading, please wait...") . " " .
"<img src='images/indicator_tiny.gif'>";
}
function digestTest() {
header("Content-type: text/html");
require_once "digest.php";
$rv = prepare_headlines_digest($_SESSION['uid'], 1, 1000);
$rv[3] = "<pre>" . $rv[3] . "</pre>";
print_r($rv);
}
private function display_main_help() {
$info = get_hotkeys_info();
$imap = get_hotkeys_map();
$omap = array();
foreach ($imap[1] as $sequence => $action) {
if (!isset($omap[$action])) $omap[$action] = array();
array_push($omap[$action], $sequence);
}
print_notice("<a target=\"_blank\" href=\"http://tt-rss.org/wiki/InterfaceTips\">".
__("Other interface tips are available in the Tiny Tiny RSS wiki.") .
"</a>");
print "<ul class='helpKbList' id='helpKbList'>";
print "<h2>" . __("Keyboard Shortcuts") . "</h2>";
foreach ($info as $section => $hotkeys) {
print "<li><h3>" . $section . "</h3></li>";
foreach ($hotkeys as $action => $description) {
if (is_array($omap[$action])) {
foreach ($omap[$action] as $sequence) {
if (strpos($sequence, "|") !== FALSE) {
$sequence = substr($sequence,
strpos($sequence, "|")+1,
strlen($sequence));
} else {
$keys = explode(" ", $sequence);
for ($i = 0; $i < count($keys); $i++) {
if (strlen($keys[$i]) > 1) {
$tmp = '';
foreach (str_split($keys[$i]) as $c) {
switch ($c) {
case '*':
$tmp .= __('Shift') . '+';
break;
case '^':
$tmp .= __('Ctrl') . '+';
break;
default:
$tmp .= $c;
}
}
$keys[$i] = $tmp;
}
}
$sequence = join(" ", $keys);
}
print "<li>";
print "<span class='hksequence'>$sequence</span>";
print $description;
print "</li>";
}
}
}
}
print "</ul>";
}
function help() {
$topic = basename($_REQUEST["topic"]);
switch ($topic) {
case "main":
$this->display_main_help();
break;
case "prefs":
//$this->display_prefs_help();
break;
default:
print "<p>".__("Help topic not found.")."</p>";
}
print "<div align='center'>";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"return dijit.byId('helpDlg').hide()\">".
__('Close this window')."</button>";
print "</div>";
/* if (file_exists("help/$topic.php")) {
include("help/$topic.php");
} else {
print "<p>".__("Help topic not found.")."</p>";
} */
/* print "<div align='center'>
<button onclick=\"javascript:window.close()\">".
__('Close this window')."</button></div>"; */
}
}
?>

View file

@ -1,98 +0,0 @@
<?php
class Db implements IDb {
private static $instance;
private $adapter;
private $link;
private function __construct() {
$er = error_reporting(E_ALL);
if (defined('_ENABLE_PDO') && _ENABLE_PDO && class_exists("PDO")) {
$this->adapter = new Db_PDO();
} else {
switch (DB_TYPE) {
case "mysql":
if (function_exists("mysqli_connect")) {
$this->adapter = new Db_Mysqli();
} else {
$this->adapter = new Db_Mysql();
}
break;
case "pgsql":
$this->adapter = new Db_Pgsql();
break;
default:
die("Unknown DB_TYPE: " . DB_TYPE);
}
}
if (!$this->adapter) die("Error initializing database adapter for " . DB_TYPE);
$this->link = $this->adapter->connect(DB_HOST, DB_USER, DB_PASS, DB_NAME, defined('DB_PORT') ? DB_PORT : "");
if (!$this->link) {
die("Error connecting through adapter: " . $this->adapter->last_error());
}
error_reporting($er);
}
private function __clone() {
//
}
public static function get() {
if (self::$instance == null)
self::$instance = new self();
return self::$instance;
}
static function quote($str){
return("'$str'");
}
function reconnect() {
$this->link = $this->adapter->connect(DB_HOST, DB_USER, DB_PASS, DB_NAME, defined('DB_PORT') ? DB_PORT : "");
}
function connect($host, $user, $pass, $db, $port) {
//return $this->adapter->connect($host, $user, $pass, $db, $port);
return ;
}
function escape_string($s, $strip_tags = true) {
return $this->adapter->escape_string($s, $strip_tags);
}
function query($query, $die_on_error = true) {
return $this->adapter->query($query, $die_on_error);
}
function fetch_assoc($result) {
return $this->adapter->fetch_assoc($result);
}
function num_rows($result) {
return $this->adapter->num_rows($result);
}
function fetch_result($result, $row, $param) {
return $this->adapter->fetch_result($result, $row, $param);
}
function close() {
return $this->adapter->close();
}
function affected_rows($result) {
return $this->adapter->affected_rows($result);
}
function last_error() {
return $this->adapter->last_error();
}
}
?>

View file

@ -1,76 +0,0 @@
<?php
class Db_Mysql implements IDb {
private $link;
function connect($host, $user, $pass, $db, $port) {
$this->link = mysql_connect($host, $user, $pass);
if ($this->link) {
$result = mysql_select_db($db, $this->link);
if (!$result) {
die("Can't select DB: " . mysql_error($this->link));
}
$this->init();
return $this->link;
} else {
die("Unable to connect to database (as $user to $host, database $db): " . mysql_error());
}
}
function escape_string($s, $strip_tags = true) {
if ($strip_tags) $s = strip_tags($s);
return mysql_real_escape_string($s, $this->link);
}
function query($query, $die_on_error = true) {
$result = @mysql_query($query, $this->link);
if (!$result) {
$error = @mysql_error($this->link);
@mysql_query("ROLLBACK", $this->link);
user_error("Query $query failed: " . ($this->link ? $error : "No connection"),
$die_on_error ? E_USER_ERROR : E_USER_WARNING);
}
return $result;
}
function fetch_assoc($result) {
return mysql_fetch_assoc($result);
}
function num_rows($result) {
return mysql_num_rows($result);
}
function fetch_result($result, $row, $param) {
return mysql_result($result, $row, $param);
}
function close() {
return mysql_close($this->link);
}
function affected_rows($result) {
return mysql_affected_rows($this->link);
}
function last_error() {
return mysql_error();
}
function init() {
$this->query("SET time_zone = '+0:0'");
if (defined('MYSQL_CHARSET') && MYSQL_CHARSET) {
$this->query("SET NAMES " . MYSQL_CHARSET);
}
return true;
}
}
?>

View file

@ -1,80 +0,0 @@
<?php
class Db_Mysqli implements IDb {
private $link;
function connect($host, $user, $pass, $db, $port) {
if ($port)
$this->link = mysqli_connect($host, $user, $pass, $db, $port);
else
$this->link = mysqli_connect($host, $user, $pass, $db);
if ($this->link) {
$this->init();
return $this->link;
} else {
die("Unable to connect to database (as $user to $host, database $db): " . mysqli_connect_error());
}
}
function escape_string($s, $strip_tags = true) {
if ($strip_tags) $s = strip_tags($s);
return mysqli_real_escape_string($this->link, $s);
}
function query($query, $die_on_error = true) {
$result = @mysqli_query($this->link, $query);
if (!$result) {
$error = @mysqli_error($this->link);
@mysqli_query($this->link, "ROLLBACK");
user_error("Query $query failed: " . ($this->link ? $error : "No connection"),
$die_on_error ? E_USER_ERROR : E_USER_WARNING);
}
return $result;
}
function fetch_assoc($result) {
return mysqli_fetch_assoc($result);
}
function num_rows($result) {
return mysqli_num_rows($result);
}
function fetch_result($result, $row, $param) {
if (mysqli_data_seek($result, $row)) {
$line = mysqli_fetch_assoc($result);
return $line[$param];
} else {
return false;
}
}
function close() {
return mysqli_close($this->link);
}
function affected_rows($result) {
return mysqli_affected_rows($this->link);
}
function last_error() {
return mysqli_error();
}
function init() {
$this->query("SET time_zone = '+0:0'");
if (defined('MYSQL_CHARSET') && MYSQL_CHARSET) {
$this->query("SET NAMES " . MYSQL_CHARSET);
}
return true;
}
}
?>

View file

@ -1,100 +0,0 @@
<?php
class Db_PDO implements IDb {
private $pdo;
function connect($host, $user, $pass, $db, $port) {
$connstr = DB_TYPE . ":host=$host;dbname=$db";
if (DB_TYPE == "mysql") $connstr .= ";charset=utf8";
try {
$this->pdo = new PDO($connstr, $user, $pass);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->init();
} catch (PDOException $e) {
die($e->getMessage());
}
return $this->pdo;
}
function escape_string($s, $strip_tags = true) {
if ($strip_tags) $s = strip_tags($s);
$qs = $this->pdo->quote($s);
return mb_substr($qs, 1, mb_strlen($qs)-2);
}
function query($query, $die_on_error = true) {
try {
return new Db_Stmt($this->pdo->query($query));
} catch (PDOException $e) {
user_error($e->getMessage(), $die_on_error ? E_USER_ERROR : E_USER_WARNING);
}
}
function fetch_assoc($result) {
try {
if ($result) {
return $result->fetch();
} else {
return null;
}
} catch (PDOException $e) {
user_error($e->getMessage(), E_USER_WARNING);
}
}
function num_rows($result) {
try {
if ($result) {
return $result->rowCount();
} else {
return false;
}
} catch (PDOException $e) {
user_error($e->getMessage(), E_USER_WARNING);
}
}
function fetch_result($result, $row, $param) {
return $result->fetch_result($row, $param);
}
function close() {
$this->pdo = null;
}
function affected_rows($result) {
try {
if ($result) {
return $result->rowCount();
} else {
return null;
}
} catch (PDOException $e) {
user_error($e->getMessage(), E_USER_WARNING);
}
}
function last_error() {
return join(" ", $this->pdo->errorInfo());
}
function init() {
switch (DB_TYPE) {
case "pgsql":
$this->query("set client_encoding = 'UTF-8'");
$this->query("set datestyle = 'ISO, european'");
$this->query("set TIME ZONE 0");
case "mysql":
$this->query("SET time_zone = '+0:0'");
return;
}
return true;
}
}
?>

View file

@ -1,85 +0,0 @@
<?php
class Db_Pgsql implements IDb {
private $link;
function connect($host, $user, $pass, $db, $port) {
$string = "dbname=$db user=$user";
if ($pass) {
$string .= " password=$pass";
}
if ($host) {
$string .= " host=$host";
}
if (is_numeric($port) && $port > 0) {
$string = "$string port=" . $port;
}
$this->link = pg_connect($string);
if (!$this->link) {
die("Unable to connect to database (as $user to $host, database $db):" . pg_last_error());
}
$this->init();
return $this->link;
}
function escape_string($s, $strip_tags = true) {
if ($strip_tags) $s = strip_tags($s);
return pg_escape_string($s);
}
function query($query, $die_on_error = true) {
$result = @pg_query($this->link, $query);
if (!$result) {
$error = @pg_last_error($this->link);
@pg_query($this->link, "ROLLBACK");
$query = htmlspecialchars($query); // just in case
user_error("Query $query failed: " . ($this->link ? $error : "No connection"),
$die_on_error ? E_USER_ERROR : E_USER_WARNING);
}
return $result;
}
function fetch_assoc($result) {
return pg_fetch_assoc($result);
}
function num_rows($result) {
return pg_num_rows($result);
}
function fetch_result($result, $row, $param) {
return pg_fetch_result($result, $row, $param);
}
function close() {
return pg_close($this->link);
}
function affected_rows($result) {
return pg_affected_rows($result);
}
function last_error() {
return pg_last_error($this->link);
}
function init() {
$this->query("set client_encoding = 'UTF-8'");
pg_set_client_encoding("UNICODE");
$this->query("set datestyle = 'ISO, european'");
$this->query("set TIME ZONE 0");
return true;
}
}
?>

View file

@ -1,190 +0,0 @@
<?php
class Db_Prefs {
private $dbh;
private static $instance;
private $cache;
function __construct() {
$this->dbh = Db::get();
$this->cache = array();
if ($_SESSION["uid"]) $this->cache();
}
private function __clone() {
//
}
public static function get() {
if (self::$instance == null)
self::$instance = new self();
return self::$instance;
}
function cache() {
$profile = false;
$user_id = $_SESSION["uid"];
@$profile = $_SESSION["profile"];
if ($profile) {
$profile_qpart = "profile = '$profile' AND";
} else {
$profile_qpart = "profile IS NULL AND";
}
if (get_schema_version() < 63) $profile_qpart = "";
$result = db_query("SELECT
value,ttrss_prefs_types.type_name as type_name,ttrss_prefs.pref_name AS pref_name
FROM
ttrss_user_prefs,ttrss_prefs,ttrss_prefs_types
WHERE
$profile_qpart
ttrss_prefs.pref_name NOT LIKE '_MOBILE%' AND
ttrss_prefs_types.id = type_id AND
owner_uid = '$user_id' AND
ttrss_user_prefs.pref_name = ttrss_prefs.pref_name");
while ($line = db_fetch_assoc($result)) {
if ($user_id == $_SESSION["uid"]) {
$pref_name = $line["pref_name"];
$this->cache[$pref_name]["type"] = $line["type_name"];
$this->cache[$pref_name]["value"] = $line["value"];
}
}
}
function read($pref_name, $user_id = false, $die_on_error = false) {
$pref_name = db_escape_string($pref_name);
$profile = false;
if (!$user_id) {
$user_id = $_SESSION["uid"];
@$profile = $_SESSION["profile"];
} else {
$user_id = sprintf("%d", $user_id);
}
if (isset($this->cache[$pref_name])) {
$tuple = $this->cache[$pref_name];
return $this->convert($tuple["value"], $tuple["type"]);
}
if ($profile) {
$profile_qpart = "profile = '$profile' AND";
} else {
$profile_qpart = "profile IS NULL AND";
}
if (get_schema_version() < 63) $profile_qpart = "";
$result = db_query("SELECT
value,ttrss_prefs_types.type_name as type_name
FROM
ttrss_user_prefs,ttrss_prefs,ttrss_prefs_types
WHERE
$profile_qpart
ttrss_user_prefs.pref_name = '$pref_name' AND
ttrss_prefs_types.id = type_id AND
owner_uid = '$user_id' AND
ttrss_user_prefs.pref_name = ttrss_prefs.pref_name");
if (db_num_rows($result) > 0) {
$value = db_fetch_result($result, 0, "value");
$type_name = db_fetch_result($result, 0, "type_name");
if ($user_id == $_SESSION["uid"]) {
$this->cache[$pref_name]["type"] = $type_name;
$this->cache[$pref_name]["value"] = $value;
}
return $this->convert($value, $type_name);
} else {
user_error("Fatal error, unknown preferences key: $pref_name (owner: $user_id)", $die_on_error ? E_USER_ERROR : E_USER_WARNING);
return null;
}
}
function convert($value, $type_name) {
if ($type_name == "bool") {
return $value == "true";
} else if ($type_name == "integer") {
return (int)$value;
} else {
return $value;
}
}
function write($pref_name, $value, $user_id = false, $strip_tags = true) {
$pref_name = db_escape_string($pref_name);
$value = db_escape_string($value, $strip_tags);
if (!$user_id) {
$user_id = $_SESSION["uid"];
@$profile = $_SESSION["profile"];
} else {
$user_id = sprintf("%d", $user_id);
$prefs_cache = false;
}
if ($profile) {
$profile_qpart = "AND profile = '$profile'";
} else {
$profile_qpart = "AND profile IS NULL";
}
if (get_schema_version() < 63) $profile_qpart = "";
$type_name = "";
$current_value = "";
if (isset($this->cache[$pref_name])) {
$type_name = $this->cache[$pref_name]["type"];
$current_value = $this->cache[$pref_name]["value"];
}
if (!$type_name) {
$result = db_query("SELECT type_name
FROM ttrss_prefs,ttrss_prefs_types
WHERE pref_name = '$pref_name' AND type_id = ttrss_prefs_types.id");
if (db_num_rows($result) > 0)
$type_name = db_fetch_result($result, 0, "type_name");
} else if ($current_value == $value) {
return;
}
if ($type_name) {
if ($type_name == "bool") {
if ($value == "1" || $value == "true") {
$value = "true";
} else {
$value = "false";
}
} else if ($type_name == "integer") {
$value = sprintf("%d", $value);
}
if ($pref_name == 'USER_TIMEZONE' && $value == '') {
$value = 'UTC';
}
db_query("UPDATE ttrss_user_prefs SET
value = '$value' WHERE pref_name = '$pref_name'
$profile_qpart
AND owner_uid = " . $_SESSION["uid"]);
if ($user_id == $_SESSION["uid"]) {
$this->cache[$pref_name]["type"] = $type_name;
$this->cache[$pref_name]["value"] = $value;
}
}
}
}
?>

View file

@ -1,32 +0,0 @@
<?php
class Db_Stmt {
private $stmt;
private $cache;
function __construct($stmt) {
$this->stmt = $stmt;
$this->cache = false;
}
function fetch_result($row, $param) {
if (!$this->cache) {
$this->cache = $this->stmt->fetchAll();
}
if (isset($this->cache[$row])) {
return $this->cache[$row][$param];
} else {
user_error("Unable to jump to row $row", E_USER_WARNING);
return false;
}
}
function rowCount() {
return $this->stmt->rowCount();
}
function fetch() {
return $this->stmt->fetch();
}
}
?>

View file

@ -1,65 +0,0 @@
<?php
class DbUpdater {
private $dbh;
private $db_type;
private $need_version;
function __construct($dbh, $db_type, $need_version) {
$this->dbh = $dbh;
$this->db_type = $db_type;
$this->need_version = (int) $need_version;
}
function getSchemaVersion() {
$result = db_query("SELECT schema_version FROM ttrss_version");
return (int) db_fetch_result($result, 0, "schema_version");
}
function isUpdateRequired() {
return $this->getSchemaVersion() < $this->need_version;
}
function getSchemaLines($version) {
$filename = "schema/versions/".$this->db_type."/$version.sql";
if (file_exists($filename)) {
return explode(";", preg_replace("/[\r\n]/", "", file_get_contents($filename)));
} else {
return false;
}
}
function performUpdateTo($version) {
if ($this->getSchemaVersion() == $version - 1) {
$lines = $this->getSchemaLines($version);
if (is_array($lines)) {
db_query("BEGIN");
foreach ($lines as $line) {
if (strpos($line, "--") !== 0 && $line) {
db_query($line);
}
}
$db_version = $this->getSchemaVersion();
if ($db_version == $version) {
db_query("COMMIT");
return true;
} else {
db_query("ROLLBACK");
return false;
}
} else {
return true;
}
} else {
return false;
}
}
} ?>

View file

@ -1,271 +0,0 @@
<?php
class Dlg extends Handler_Protected {
private $param;
function before($method) {
if (parent::before($method)) {
header("Content-Type: text/html"); # required for iframe
$this->param = $this->dbh->escape_string($_REQUEST["param"]);
return true;
}
return false;
}
function importOpml() {
print __("If you have imported labels and/or filters, you might need to reload preferences to see your new data.") . "</p>";
print "<div class=\"prefFeedOPMLHolder\">";
$this->dbh->query("BEGIN");
print "<ul class='nomarks'>";
$opml = new Opml($_REQUEST);
$opml->opml_import($_SESSION["uid"]);
$this->dbh->query("COMMIT");
print "</ul>";
print "</div>";
print "<div align='center'>";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"dijit.byId('opmlImportDlg').execute()\">".
__('Close this window')."</button>";
print "</div>";
print "</div>";
//return;
}
function pubOPMLUrl() {
$url_path = Opml::opml_publish_url();
print __("Your Public OPML URL is:");
print "<div class=\"tagCloudContainer\">";
print "<a id='pub_opml_url' href='$url_path' target='_blank'>$url_path</a>";
print "</div>";
print "<div align='center'>";
print "<button dojoType=\"dijit.form.Button\" onclick=\"return opmlRegenKey()\">".
__('Generate new URL')."</button> ";
print "<button dojoType=\"dijit.form.Button\" onclick=\"return closeInfoBox()\">".
__('Close this window')."</button>";
print "</div>";
//return;
}
function explainError() {
print "<div class=\"errorExplained\">";
if ($this->param == 1) {
print __("Update daemon is enabled in configuration, but daemon process is not running, which prevents all feeds from updating. Please start the daemon process or contact instance owner.");
$stamp = (int) file_get_contents(LOCK_DIRECTORY . "/update_daemon.stamp");
print "<p>" . __("Last update:") . " " . date("Y.m.d, G:i", $stamp);
}
if ($this->param == 3) {
print __("Update daemon is taking too long to perform a feed update. This could indicate a problem like crash or a hang. Please check the daemon process or contact instance owner.");
$stamp = (int) file_get_contents(LOCK_DIRECTORY . "/update_daemon.stamp");
print "<p>" . __("Last update:") . " " . date("Y.m.d, G:i", $stamp);
}
print "</div>";
print "<div align='center'>";
print "<button onclick=\"return closeInfoBox()\">".
__('Close this window')."</button>";
print "</div>";
//return;
}
function printTagCloud() {
print "<div class=\"tagCloudContainer\">";
// from here: http://www.roscripts.com/Create_tag_cloud-71.html
$query = "SELECT tag_name, COUNT(post_int_id) AS count
FROM ttrss_tags WHERE owner_uid = ".$_SESSION["uid"]."
GROUP BY tag_name ORDER BY count DESC LIMIT 50";
$result = $this->dbh->query($query);
$tags = array();
while ($line = $this->dbh->fetch_assoc($result)) {
$tags[$line["tag_name"]] = $line["count"];
}
if(count($tags) == 0 ){ return; }
ksort($tags);
$max_size = 32; // max font size in pixels
$min_size = 11; // min font size in pixels
// largest and smallest array values
$max_qty = max(array_values($tags));
$min_qty = min(array_values($tags));
// find the range of values
$spread = $max_qty - $min_qty;
if ($spread == 0) { // we don't want to divide by zero
$spread = 1;
}
// set the font-size increment
$step = ($max_size - $min_size) / ($spread);
// loop through the tag array
foreach ($tags as $key => $value) {
// calculate font-size
// find the $value in excess of $min_qty
// multiply by the font-size increment ($size)
// and add the $min_size set above
$size = round($min_size + (($value - $min_qty) * $step));
$key_escaped = str_replace("'", "\\'", $key);
echo "<a href=\"javascript:viewfeed('$key_escaped') \" style=\"font-size: " .
$size . "px\" title=\"$value articles tagged with " .
$key . '">' . $key . '</a> ';
}
print "</div>";
print "<div align='center'>";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"return closeInfoBox()\">".
__('Close this window')."</button>";
print "</div>";
}
function printTagSelect() {
print __("Match:"). "&nbsp;" .
"<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\" type=\"radio\" checked value=\"any\" name=\"tag_mode\" id=\"tag_mode_any\">";
print "<label for=\"tag_mode_any\">".__("Any")."</label>";
print "&nbsp;";
print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\" type=\"radio\" value=\"all\" name=\"tag_mode\" id=\"tag_mode_all\">";
print "<label for=\"tag_mode_all\">".__("All tags.")."</input>";
print "<select id=\"all_tags\" name=\"all_tags\" title=\"" . __('Which Tags?') . "\" multiple=\"multiple\" size=\"10\" style=\"width : 100%\">";
$result = $this->dbh->query("SELECT DISTINCT tag_name FROM ttrss_tags WHERE owner_uid = ".$_SESSION['uid']."
AND LENGTH(tag_name) <= 30 ORDER BY tag_name ASC");
while ($row = $this->dbh->fetch_assoc($result)) {
$tmp = htmlspecialchars($row["tag_name"]);
print "<option value=\"$tmp\">$tmp</option>";
}
print "</select>";
print "<div align='right'>";
print "<button dojoType=\"dijit.form.Button\" onclick=\"viewfeed(get_all_tags($('all_tags')),
get_radio_checked($('tag_mode')));\">" . __('Display entries') . "</button>";
print "&nbsp;";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"return closeInfoBox()\">" .
__('Close this window') . "</button>";
print "</div>";
}
function generatedFeed() {
$this->params = explode(":", $this->param, 3);
$feed_id = $this->dbh->escape_string($this->params[0]);
$is_cat = (bool) $this->params[1];
$key = get_feed_access_key($feed_id, $is_cat);
$url_path = htmlspecialchars($this->params[2]) . "&key=" . $key;
print "<h2>".__("You can view this feed as RSS using the following URL:")."</h2>";
print "<div class=\"tagCloudContainer\">";
print "<a id='gen_feed_url' href='$url_path' target='_blank'>$url_path</a>";
print "</div>";
print "<div align='center'>";
print "<button dojoType=\"dijit.form.Button\" onclick=\"return genUrlChangeKey('$feed_id', '$is_cat')\">".
__('Generate new URL')."</button> ";
print "<button dojoType=\"dijit.form.Button\" onclick=\"return closeInfoBox()\">".
__('Close this window')."</button>";
print "</div>";
//return;
}
function newVersion() {
$version_data = check_for_update();
$version = $version_data['version'];
$id = $version_data['version_id'];
if ($version && $id) {
print "<div class='tagCloudContainer'>";
print T_sprintf("New version of Tiny Tiny RSS is available (%s).",
"<b>$version</b>");
print "</div>";
$details = "http://tt-rss.org/redmine/versions/$id";
$download = "http://tt-rss.org/#Download";
print "<p align='center'>".__("You can update using built-in updater in the Preferences or by using update.php")."</p>";
print "<div style='text-align : center'>";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"return window.open('$details')\">".__("See the release notes")."</button>";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"return window.open('$download')\">".__("Download")."</button>";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"return dijit.byId('newVersionDlg').hide()\">".
__('Close this window')."</button>";
} else {
print "<div class='tagCloudContainer'>";
print "<p align='center'>".__("Error receiving version information or no new version available.")."</p>";
print "</div>";
print "<div style='text-align : center'>";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"return dijit.byId('newVersionDlg').hide()\">".
__('Close this window')."</button>";
print "</div>";
}
print "</div>";
}
}
?>

View file

@ -1,10 +0,0 @@
<?php
class FeedEnclosure {
public $link;
public $type;
public $length;
public $title;
public $height;
public $width;
}
?>

View file

@ -1,15 +0,0 @@
<?php
abstract class FeedItem {
abstract function get_id();
abstract function get_date();
abstract function get_link();
abstract function get_title();
abstract function get_description();
abstract function get_content();
abstract function get_comments_url();
abstract function get_comments_count();
abstract function get_categories();
abstract function get_enclosures();
abstract function get_author();
}
?>

View file

@ -1,201 +0,0 @@
<?php
class FeedItem_Atom extends FeedItem_Common {
function get_id() {
$id = $this->elem->getElementsByTagName("id")->item(0);
if ($id) {
return $id->nodeValue;
} else {
return $this->get_link();
}
}
function get_date() {
$updated = $this->elem->getElementsByTagName("updated")->item(0);
if ($updated) {
return strtotime($updated->nodeValue);
}
$published = $this->elem->getElementsByTagName("published")->item(0);
if ($published) {
return strtotime($published->nodeValue);
}
$date = $this->xpath->query("dc:date", $this->elem)->item(0);
if ($date) {
return strtotime($date->nodeValue);
}
}
function get_link() {
$links = $this->elem->getElementsByTagName("link");
foreach ($links as $link) {
if ($link && $link->hasAttribute("href") &&
(!$link->hasAttribute("rel")
|| $link->getAttribute("rel") == "alternate"
|| $link->getAttribute("rel") == "standout")) {
$base = $this->xpath->evaluate("string(ancestor-or-self::*[@xml:base][1]/@xml:base)", $link);
if ($base)
return rewrite_relative_url($base, trim($link->getAttribute("href")));
else
return trim($link->getAttribute("href"));
}
}
}
function get_title() {
$title = $this->elem->getElementsByTagName("title")->item(0);
if ($title) {
return trim($title->nodeValue);
}
}
function get_content() {
$content = $this->elem->getElementsByTagName("content")->item(0);
if ($content) {
if ($content->hasAttribute('type')) {
if ($content->getAttribute('type') == 'xhtml') {
for ($i = 0; $i < $content->childNodes->length; $i++) {
$child = $content->childNodes->item($i);
if ($child->hasChildNodes()) {
return $this->doc->saveXML($child);
}
}
}
}
return $content->nodeValue;
}
}
function get_description() {
$content = $this->elem->getElementsByTagName("summary")->item(0);
if ($content) {
if ($content->hasAttribute('type')) {
if ($content->getAttribute('type') == 'xhtml') {
for ($i = 0; $i < $content->childNodes->length; $i++) {
$child = $content->childNodes->item($i);
if ($child->hasChildNodes()) {
return $this->doc->saveXML($child);
}
}
}
}
return $content->nodeValue;
}
}
function get_categories() {
$categories = $this->elem->getElementsByTagName("category");
$cats = array();
foreach ($categories as $cat) {
if ($cat->hasAttribute("term"))
array_push($cats, trim($cat->getAttribute("term")));
}
$categories = $this->xpath->query("dc:subject", $this->elem);
foreach ($categories as $cat) {
array_push($cats, trim($cat->nodeValue));
}
return $cats;
}
function get_enclosures() {
$links = $this->elem->getElementsByTagName("link");
$encs = array();
foreach ($links as $link) {
if ($link && $link->hasAttribute("href") && $link->hasAttribute("rel")) {
if ($link->getAttribute("rel") == "enclosure") {
$enc = new FeedEnclosure();
$enc->type = $link->getAttribute("type");
$enc->link = $link->getAttribute("href");
$enc->length = $link->getAttribute("length");
array_push($encs, $enc);
}
}
}
$enclosures = $this->xpath->query("media:content", $this->elem);
foreach ($enclosures as $enclosure) {
$enc = new FeedEnclosure();
$enc->type = $enclosure->getAttribute("type");
$enc->link = $enclosure->getAttribute("url");
$enc->length = $enclosure->getAttribute("length");
$enc->height = $enclosure->getAttribute("height");
$enc->width = $enclosure->getAttribute("width");
$desc = $this->xpath->query("media:description", $enclosure)->item(0);
if ($desc) $enc->title = strip_tags($desc->nodeValue);
array_push($encs, $enc);
}
$enclosures = $this->xpath->query("media:group", $this->elem);
foreach ($enclosures as $enclosure) {
$enc = new FeedEnclosure();
$content = $this->xpath->query("media:content", $enclosure)->item(0);
if ($content) {
$enc->type = $content->getAttribute("type");
$enc->link = $content->getAttribute("url");
$enc->length = $content->getAttribute("length");
$enc->height = $content->getAttribute("height");
$enc->width = $content->getAttribute("width");
$desc = $this->xpath->query("media:description", $content)->item(0);
if ($desc) {
$enc->title = strip_tags($desc->nodeValue);
} else {
$desc = $this->xpath->query("media:description", $enclosure)->item(0);
if ($desc) $enc->title = strip_tags($desc->nodeValue);
}
array_push($encs, $enc);
}
}
$enclosures = $this->xpath->query("media:thumbnail", $this->elem);
foreach ($enclosures as $enclosure) {
$enc = new FeedEnclosure();
$enc->type = "image/generic";
$enc->link = $enclosure->getAttribute("url");
$enc->height = $enclosure->getAttribute("height");
$enc->width = $enclosure->getAttribute("width");
array_push($encs, $enc);
}
return $encs;
}
}
?>

View file

@ -1,75 +0,0 @@
<?php
abstract class FeedItem_Common extends FeedItem {
protected $elem;
protected $xpath;
protected $doc;
function __construct($elem, $doc, $xpath) {
$this->elem = $elem;
$this->xpath = $xpath;
$this->doc = $doc;
try {
$source = $elem->getElementsByTagName("source")->item(0);
// we don't need <source> element
if ($source)
$elem->removeChild($source);
} catch (DOMException $e) {
//
}
}
function get_author() {
$author = $this->elem->getElementsByTagName("author")->item(0);
if ($author) {
$name = $author->getElementsByTagName("name")->item(0);
if ($name) return $name->nodeValue;
$email = $author->getElementsByTagName("email")->item(0);
if ($email) return $email->nodeValue;
if ($author->nodeValue)
return $author->nodeValue;
}
$author = $this->xpath->query("dc:creator", $this->elem)->item(0);
if ($author) {
return $author->nodeValue;
}
}
function get_comments_url() {
//RSS only. Use a query here to avoid namespace clashes (e.g. with slash).
//might give a wrong result if a default namespace was declared (possible with XPath 2.0)
$com_url = $this->xpath->query("comments", $this->elem)->item(0);
if($com_url)
return $com_url->nodeValue;
//Atom Threading Extension (RFC 4685) stuff. Could be used in RSS feeds, so it's in common.
//'text/html' for type is too restrictive?
$com_url = $this->xpath->query("atom:link[@rel='replies' and contains(@type,'text/html')]/@href", $this->elem)->item(0);
if($com_url)
return $com_url->nodeValue;
}
function get_comments_count() {
//also query for ATE stuff here
$query = "slash:comments|thread:total|atom:link[@rel='replies']/@thread:count";
$comments = $this->xpath->query($query, $this->elem)->item(0);
if ($comments) {
return $comments->nodeValue;
}
}
}
?>

View file

@ -1,183 +0,0 @@
<?php
class FeedItem_RSS extends FeedItem_Common {
function get_id() {
$id = $this->elem->getElementsByTagName("guid")->item(0);
if ($id) {
return $id->nodeValue;
} else {
return $this->get_link();
}
}
function get_date() {
$pubDate = $this->elem->getElementsByTagName("pubDate")->item(0);
if ($pubDate) {
return strtotime($pubDate->nodeValue);
}
$date = $this->xpath->query("dc:date", $this->elem)->item(0);
if ($date) {
return strtotime($date->nodeValue);
}
}
function get_link() {
$links = $this->xpath->query("atom:link", $this->elem);
foreach ($links as $link) {
if ($link && $link->hasAttribute("href") &&
(!$link->hasAttribute("rel")
|| $link->getAttribute("rel") == "alternate"
|| $link->getAttribute("rel") == "standout")) {
return trim($link->getAttribute("href"));
}
}
$link = $this->elem->getElementsByTagName("guid")->item(0);
if ($link && $link->hasAttributes() && $link->getAttribute("isPermaLink") == "true") {
return trim($link->nodeValue);
}
$link = $this->elem->getElementsByTagName("link")->item(0);
if ($link) {
return trim($link->nodeValue);
}
}
function get_title() {
$title = $this->elem->getElementsByTagName("title")->item(0);
if ($title) {
return trim($title->nodeValue);
}
}
function get_content() {
$contentA = $this->xpath->query("content:encoded", $this->elem)->item(0);
$contentB = $this->elem->getElementsByTagName("description")->item(0);
if ($contentA && !$contentB) {
return $contentA->nodeValue;
}
if ($contentB && !$contentA) {
return $contentB->nodeValue;
}
if ($contentA && $contentB) {
return mb_strlen($contentA->nodeValue) > mb_strlen($contentB->nodeValue) ?
$contentA->nodeValue : $contentB->nodeValue;
}
}
function get_description() {
$summary = $this->elem->getElementsByTagName("description")->item(0);
if ($summary) {
return $summary->nodeValue;
}
}
function get_categories() {
$categories = $this->elem->getElementsByTagName("category");
$cats = array();
foreach ($categories as $cat) {
array_push($cats, trim($cat->nodeValue));
}
$categories = $this->xpath->query("dc:subject", $this->elem);
foreach ($categories as $cat) {
array_push($cats, trim($cat->nodeValue));
}
return $cats;
}
function get_enclosures() {
$enclosures = $this->elem->getElementsByTagName("enclosure");
$encs = array();
foreach ($enclosures as $enclosure) {
$enc = new FeedEnclosure();
$enc->type = $enclosure->getAttribute("type");
$enc->link = $enclosure->getAttribute("url");
$enc->length = $enclosure->getAttribute("length");
$enc->height = $enclosure->getAttribute("height");
$enc->width = $enclosure->getAttribute("width");
array_push($encs, $enc);
}
$enclosures = $this->xpath->query("media:content", $this->elem);
foreach ($enclosures as $enclosure) {
$enc = new FeedEnclosure();
$enc->type = $enclosure->getAttribute("type");
$enc->link = $enclosure->getAttribute("url");
$enc->length = $enclosure->getAttribute("length");
$enc->height = $enclosure->getAttribute("height");
$enc->width = $enclosure->getAttribute("width");
$desc = $this->xpath->query("media:description", $enclosure)->item(0);
if ($desc) $enc->title = strip_tags($desc->nodeValue);
array_push($encs, $enc);
}
$enclosures = $this->xpath->query("media:group", $this->elem);
foreach ($enclosures as $enclosure) {
$enc = new FeedEnclosure();
$content = $this->xpath->query("media:content", $enclosure)->item(0);
if ($content) {
$enc->type = $content->getAttribute("type");
$enc->link = $content->getAttribute("url");
$enc->length = $content->getAttribute("length");
$enc->height = $content->getAttribute("height");
$enc->width = $content->getAttribute("width");
$desc = $this->xpath->query("media:description", $content)->item(0);
if ($desc) {
$enc->title = strip_tags($desc->nodeValue);
} else {
$desc = $this->xpath->query("media:description", $enclosure)->item(0);
if ($desc) $enc->title = strip_tags($desc->nodeValue);
}
array_push($encs, $enc);
}
}
$enclosures = $this->xpath->query("media:thumbnail", $this->elem);
foreach ($enclosures as $enclosure) {
$enc = new FeedEnclosure();
$enc->type = "image/generic";
$enc->link = $enclosure->getAttribute("url");
$enc->height = $enclosure->getAttribute("height");
$enc->width = $enclosure->getAttribute("width");
array_push($encs, $enc);
}
return $encs;
}
}
?>

View file

@ -1,276 +0,0 @@
<?php
class FeedParser {
private $doc;
private $error;
private $libxml_errors = array();
private $items;
private $link;
private $title;
private $type;
private $xpath;
const FEED_RDF = 0;
const FEED_RSS = 1;
const FEED_ATOM = 2;
function normalize_encoding($data) {
if (preg_match('/^(<\?xml[\t\n\r ].*?encoding[\t\n\r ]*=[\t\n\r ]*["\'])(.+?)(["\'].*?\?>)/s', $data, $matches) === 1) {
$data = mb_convert_encoding($data, 'UTF-8', $matches[2]);
$data = preg_replace('/^<\?xml[\t\n\r ].*?\?>/s', $matches[1] . "UTF-8" . $matches[3] , $data);
}
return $data;
}
function __construct($data) {
libxml_use_internal_errors(true);
libxml_clear_errors();
$this->doc = new DOMDocument();
$this->doc->loadXML($data);
mb_substitute_character("none");
$error = libxml_get_last_error();
// libxml compiled without iconv?
if ($error && $error->code == 32) {
$data = $this->normalize_encoding($data);
if ($data) {
libxml_clear_errors();
$this->doc = new DOMDocument();
$this->doc->loadXML($data);
$error = libxml_get_last_error();
}
}
// some terrible invalid unicode entity?
if ($error) {
foreach (libxml_get_errors() as $err) {
if ($err->code == 9) {
// if the source feed is not in utf8, next conversion will fail
$data = $this->normalize_encoding($data);
// remove dangling bytes
$data = mb_convert_encoding($data, 'UTF-8', 'UTF-8');
// apparently not all UTF-8 characters are valid for XML
$data = preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $data);
if ($data) {
libxml_clear_errors();
$this->doc = new DOMDocument();
$this->doc->loadXML($data);
$error = libxml_get_last_error();
}
break;
}
}
}
if ($error) {
foreach (libxml_get_errors() as $error) {
if ($error->level == LIBXML_ERR_FATAL) {
if(!isset($this->error)) //currently only the first error is reported
$this->error = $this->format_error($error);
$this->libxml_errors [] = $this->format_error($error);
}
}
}
libxml_clear_errors();
$this->items = array();
}
function init() {
$root = $this->doc->firstChild;
$xpath = new DOMXPath($this->doc);
$xpath->registerNamespace('atom', 'http://www.w3.org/2005/Atom');
$xpath->registerNamespace('atom03', 'http://purl.org/atom/ns#');
$xpath->registerNamespace('media', 'http://search.yahoo.com/mrss/');
$xpath->registerNamespace('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
$xpath->registerNamespace('slash', 'http://purl.org/rss/1.0/modules/slash/');
$xpath->registerNamespace('dc', 'http://purl.org/dc/elements/1.1/');
$xpath->registerNamespace('content', 'http://purl.org/rss/1.0/modules/content/');
$xpath->registerNamespace('thread', 'http://purl.org/syndication/thread/1.0');
$this->xpath = $xpath;
$root = $xpath->query("(//atom03:feed|//atom:feed|//channel|//rdf:rdf|//rdf:RDF)");
if ($root && $root->length > 0) {
$root = $root->item(0);
if ($root) {
switch (mb_strtolower($root->tagName)) {
case "rdf:rdf":
$this->type = $this::FEED_RDF;
break;
case "channel":
$this->type = $this::FEED_RSS;
break;
case "feed":
$this->type = $this::FEED_ATOM;
break;
default:
if( !isset($this->error) ){
$this->error = "Unknown/unsupported feed type";
}
return;
}
}
switch ($this->type) {
case $this::FEED_ATOM:
$title = $xpath->query("//atom:feed/atom:title")->item(0);
if (!$title)
$title = $xpath->query("//atom03:feed/atom03:title")->item(0);
if ($title) {
$this->title = $title->nodeValue;
}
$link = $xpath->query("//atom:feed/atom:link[not(@rel)]")->item(0);
if (!$link)
$link = $xpath->query("//atom03:feed/atom03:link[not(@rel)]")->item(0);
if ($link && $link->hasAttributes()) {
$this->link = $link->getAttribute("href");
}
$articles = $xpath->query("//atom:entry");
if (!$articles || $articles->length == 0)
$articles = $xpath->query("//atom03:entry");
foreach ($articles as $article) {
array_push($this->items, new FeedItem_Atom($article, $this->doc, $this->xpath));
}
break;
case $this::FEED_RSS:
$title = $xpath->query("//channel/title")->item(0);
if ($title) {
$this->title = $title->nodeValue;
}
$link = $xpath->query("//channel/link")->item(0);
if ($link) {
if ($link->getAttribute("href"))
$this->link = $link->getAttribute("href");
else if ($link->nodeValue)
$this->link = $link->nodeValue;
}
$articles = $xpath->query("//channel/item");
foreach ($articles as $article) {
array_push($this->items, new FeedItem_RSS($article, $this->doc, $this->xpath));
}
break;
case $this::FEED_RDF:
$xpath->registerNamespace('rssfake', 'http://purl.org/rss/1.0/');
$title = $xpath->query("//rssfake:channel/rssfake:title")->item(0);
if ($title) {
$this->title = $title->nodeValue;
}
$link = $xpath->query("//rssfake:channel/rssfake:link")->item(0);
if ($link) {
$this->link = $link->nodeValue;
}
$articles = $xpath->query("//rssfake:item");
foreach ($articles as $article) {
array_push($this->items, new FeedItem_RSS($article, $this->doc, $this->xpath));
}
break;
}
if ($this->title) $this->title = trim($this->title);
if ($this->link) $this->link = trim($this->link);
} else {
if( !isset($this->error) ){
$this->error = "Unknown/unsupported feed type";
}
return;
}
}
function format_error($error) {
if ($error) {
return sprintf("LibXML error %s at line %d (column %d): %s",
$error->code, $error->line, $error->column,
$error->message);
} else {
return "";
}
}
function error() {
return $this->error;
}
function errors() {
return $this->libxml_errors;
}
function get_link() {
return $this->link;
}
function get_title() {
return $this->title;
}
function get_items() {
return $this->items;
}
function get_links($rel) {
$rv = array();
switch ($this->type) {
case $this::FEED_ATOM:
$links = $this->xpath->query("//atom:feed/atom:link");
foreach ($links as $link) {
if (!$rel || $link->hasAttribute('rel') && $link->getAttribute('rel') == $rel) {
array_push($rv, trim($link->getAttribute('href')));
}
}
break;
case $this::FEED_RSS:
$links = $this->xpath->query("//atom:link");
foreach ($links as $link) {
if (!$rel || $link->hasAttribute('rel') && $link->getAttribute('rel') == $rel) {
array_push($rv, trim($link->getAttribute('href')));
}
}
break;
}
return $rv;
}
} ?>

File diff suppressed because it is too large Load diff

View file

@ -1,24 +0,0 @@
<?php
class Handler implements IHandler {
protected $dbh;
protected $args;
function __construct($args) {
$this->dbh = Db::get();
$this->args = $args;
}
function csrf_ignore($method) {
return true;
}
function before($method) {
return true;
}
function after() {
return true;
}
}
?>

View file

@ -1,8 +0,0 @@
<?php
class Handler_Protected extends Handler {
function before($method) {
return parent::before($method) && $_SESSION['uid'];
}
}
?>

File diff suppressed because it is too large Load diff

View file

@ -1,5 +0,0 @@
<?php
interface IAuthModule {
function authenticate($login, $password);
}
?>

View file

@ -1,13 +0,0 @@
<?php
interface IDb {
function connect($host, $user, $pass, $db, $port);
function escape_string($s, $strip_tags = true);
function query($query, $die_on_error = true);
function fetch_assoc($result);
function num_rows($result);
function fetch_result($result, $row, $param);
function close();
function affected_rows($result);
function last_error();
}
?>

View file

@ -1,7 +0,0 @@
<?php
interface IHandler {
function csrf_ignore($method);
function before($method);
function after();
}
?>

View file

@ -1,65 +0,0 @@
<?php
class Logger {
private static $instance;
private $adapter;
public static $errornames = array(
1 => 'E_ERROR',
2 => 'E_WARNING',
4 => 'E_PARSE',
8 => 'E_NOTICE',
16 => 'E_CORE_ERROR',
32 => 'E_CORE_WARNING',
64 => 'E_COMPILE_ERROR',
128 => 'E_COMPILE_WARNING',
256 => 'E_USER_ERROR',
512 => 'E_USER_WARNING',
1024 => 'E_USER_NOTICE',
2048 => 'E_STRICT',
4096 => 'E_RECOVERABLE_ERROR',
8192 => 'E_DEPRECATED',
16384 => 'E_USER_DEPRECATED',
32767 => 'E_ALL');
function log_error($errno, $errstr, $file, $line, $context) {
if ($errno == E_NOTICE) return false;
if ($this->adapter)
return $this->adapter->log_error($errno, $errstr, $file, $line, $context);
else
return false;
}
function log($string) {
if ($this->adapter)
return $this->adapter->log($string);
else
return false;
}
private function __clone() {
//
}
function __construct() {
switch (LOG_DESTINATION) {
case "sql":
$this->adapter = new Logger_SQL();
break;
case "syslog":
$this->adapter = new Logger_Syslog();
break;
default:
$this->adapter = false;
}
}
public static function get() {
if (self::$instance == null)
self::$instance = new self();
return self::$instance;
}
}
?>

View file

@ -1,28 +0,0 @@
<?php
class Logger_SQL {
function log_error($errno, $errstr, $file, $line, $context) {
if (Db::get() && get_schema_version() > 117) {
$errno = Db::get()->escape_string($errno);
$errstr = Db::get()->escape_string($errstr);
$file = Db::get()->escape_string($file);
$line = Db::get()->escape_string($line);
$context = ''; // backtrace is a lot of data which is not really critical to store
//$context = $this->dbh->escape_string(serialize($context));
$owner_uid = $_SESSION["uid"] ? $_SESSION["uid"] : "NULL";
$result = Db::get()->query(
"INSERT INTO ttrss_error_log
(errno, errstr, filename, lineno, context, owner_uid, created_at) VALUES
($errno, '$errstr', '$file', '$line', '$context', $owner_uid, NOW())");
return Db::get()->affected_rows($result) != 0;
}
return false;
}
}
?>

View file

@ -1,31 +0,0 @@
<?php
class Logger_Syslog {
function log_error($errno, $errstr, $file, $line, $context) {
switch ($errno) {
case E_ERROR:
case E_PARSE:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
$priority = LOG_ERR;
break;
case E_WARNING:
case E_CORE_WARNING:
case E_COMPILE_WARNING:
case E_USER_WARNING:
$priority = LOG_WARNING;
break;
default:
$priority = LOG_INFO;
}
$errname = Logger::$errornames[$errno] . " ($errno)";
syslog($priority, "[tt-rss] $errname ($file:$line) $errstr");
}
}
?>

View file

@ -1,525 +0,0 @@
<?php
class Opml extends Handler_Protected {
function csrf_ignore($method) {
$csrf_ignored = array("export", "import");
return array_search($method, $csrf_ignored) !== false;
}
function export() {
$output_name = $_REQUEST["filename"];
if (!$output_name) $output_name = "TinyTinyRSS.opml";
$show_settings = $_REQUEST["settings"];
$owner_uid = $_SESSION["uid"];
return $this->opml_export($output_name, $owner_uid, false, ($show_settings == 1));
}
function import() {
$owner_uid = $_SESSION["uid"];
header('Content-Type: text/html; charset=utf-8');
print "<html>
<head>
<link rel=\"stylesheet\" href=\"css/utility.css\" type=\"text/css\">
<title>".__("OPML Utility")."</title>
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
</head>
<body>
<div class=\"floatingLogo\"><img src=\"images/logo_small.png\"></div>
<h1>".__('OPML Utility')."</h1><div class='content'>";
add_feed_category("Imported feeds");
$this->opml_notice(__("Importing OPML..."));
$this->opml_import($owner_uid);
print "<br><form method=\"GET\" action=\"prefs.php\">
<input type=\"submit\" value=\"".__("Return to preferences")."\">
</form>";
print "</div></body></html>";
}
// Export
private function opml_export_category($owner_uid, $cat_id, $hide_private_feeds=false) {
if ($cat_id) {
$cat_qpart = "parent_cat = '$cat_id'";
$feed_cat_qpart = "cat_id = '$cat_id'";
} else {
$cat_qpart = "parent_cat IS NULL";
$feed_cat_qpart = "cat_id IS NULL";
}
if ($hide_private_feeds)
$hide_qpart = "(private IS false AND auth_login = '' AND auth_pass = '')";
else
$hide_qpart = "true";
$out = "";
if ($cat_id) {
$result = $this->dbh->query("SELECT title FROM ttrss_feed_categories WHERE id = '$cat_id'
AND owner_uid = '$owner_uid'");
$cat_title = htmlspecialchars($this->dbh->fetch_result($result, 0, "title"));
}
if ($cat_title) $out .= "<outline text=\"$cat_title\">\n";
$result = $this->dbh->query("SELECT id,title
FROM ttrss_feed_categories WHERE
$cat_qpart AND owner_uid = '$owner_uid' ORDER BY order_id, title");
while ($line = $this->dbh->fetch_assoc($result)) {
$title = htmlspecialchars($line["title"]);
$out .= $this->opml_export_category($owner_uid, $line["id"], $hide_private_feeds);
}
$feeds_result = $this->dbh->query("select title, feed_url, site_url
from ttrss_feeds where $feed_cat_qpart AND owner_uid = '$owner_uid' AND $hide_qpart
order by order_id, title");
while ($fline = $this->dbh->fetch_assoc($feeds_result)) {
$title = htmlspecialchars($fline["title"]);
$url = htmlspecialchars($fline["feed_url"]);
$site_url = htmlspecialchars($fline["site_url"]);
if ($site_url) {
$html_url_qpart = "htmlUrl=\"$site_url\"";
} else {
$html_url_qpart = "";
}
$out .= "<outline type=\"rss\" text=\"$title\" xmlUrl=\"$url\" $html_url_qpart/>\n";
}
if ($cat_title) $out .= "</outline>\n";
return $out;
}
function opml_export($name, $owner_uid, $hide_private_feeds=false, $include_settings=true) {
if (!$owner_uid) return;
if (!isset($_REQUEST["debug"])) {
header("Content-type: application/xml+opml");
header("Content-Disposition: attachment; filename=" . $name );
} else {
header("Content-type: text/xml");
}
$out = "<?xml version=\"1.0\" encoding=\"utf-8\"?".">";
$out .= "<opml version=\"1.0\">";
$out .= "<head>
<dateCreated>" . date("r", time()) . "</dateCreated>
<title>Tiny Tiny RSS Feed Export</title>
</head>";
$out .= "<body>";
$out .= $this->opml_export_category($owner_uid, false, $hide_private_feeds);
# export tt-rss settings
if ($include_settings) {
$out .= "<outline text=\"tt-rss-prefs\" schema-version=\"".SCHEMA_VERSION."\">";
$result = $this->dbh->query("SELECT pref_name, value FROM ttrss_user_prefs WHERE
profile IS NULL AND owner_uid = " . $_SESSION["uid"] . " ORDER BY pref_name");
while ($line = $this->dbh->fetch_assoc($result)) {
$name = $line["pref_name"];
$value = htmlspecialchars($line["value"]);
$out .= "<outline pref-name=\"$name\" value=\"$value\"/>";
}
$out .= "</outline>";
$out .= "<outline text=\"tt-rss-labels\" schema-version=\"".SCHEMA_VERSION."\">";
$result = $this->dbh->query("SELECT * FROM ttrss_labels2 WHERE
owner_uid = " . $_SESSION['uid']);
while ($line = $this->dbh->fetch_assoc($result)) {
$name = htmlspecialchars($line['caption']);
$fg_color = htmlspecialchars($line['fg_color']);
$bg_color = htmlspecialchars($line['bg_color']);
$out .= "<outline label-name=\"$name\" label-fg-color=\"$fg_color\" label-bg-color=\"$bg_color\"/>";
}
$out .= "</outline>";
$out .= "<outline text=\"tt-rss-filters\" schema-version=\"".SCHEMA_VERSION."\">";
$result = $this->dbh->query("SELECT * FROM ttrss_filters2
WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY id");
while ($line = $this->dbh->fetch_assoc($result)) {
foreach (array('enabled', 'match_any_rule', 'inverse') as $b) {
$line[$b] = sql_bool_to_bool($line[$b]);
}
$line["rules"] = array();
$line["actions"] = array();
$tmp_result = $this->dbh->query("SELECT * FROM ttrss_filters2_rules
WHERE filter_id = ".$line["id"]);
while ($tmp_line = $this->dbh->fetch_assoc($tmp_result)) {
unset($tmp_line["id"]);
unset($tmp_line["filter_id"]);
$cat_filter = sql_bool_to_bool($tmp_line["cat_filter"]);
if ($cat_filter && $tmp_line["cat_id"] || $tmp_line["feed_id"]) {
$tmp_line["feed"] = getFeedTitle(
$cat_filter ? $tmp_line["cat_id"] : $tmp_line["feed_id"],
$cat_filter);
} else {
$tmp_line["feed"] = "";
}
$tmp_line["cat_filter"] = sql_bool_to_bool($tmp_line["cat_filter"]);
$tmp_line["inverse"] = sql_bool_to_bool($tmp_line["inverse"]);
unset($tmp_line["feed_id"]);
unset($tmp_line["cat_id"]);
array_push($line["rules"], $tmp_line);
}
$tmp_result = $this->dbh->query("SELECT * FROM ttrss_filters2_actions
WHERE filter_id = ".$line["id"]);
while ($tmp_line = $this->dbh->fetch_assoc($tmp_result)) {
unset($tmp_line["id"]);
unset($tmp_line["filter_id"]);
array_push($line["actions"], $tmp_line);
}
unset($line["id"]);
unset($line["owner_uid"]);
$filter = json_encode($line);
$out .= "<outline filter-type=\"2\"><![CDATA[$filter]]></outline>";
}
$out .= "</outline>";
}
$out .= "</body></opml>";
// Format output.
$doc = new DOMDocument();
$doc->formatOutput = true;
$doc->preserveWhiteSpace = false;
$doc->loadXML($out);
$xpath = new DOMXpath($doc);
$outlines = $xpath->query("//outline[@title]");
// cleanup empty categories
foreach ($outlines as $node) {
if ($node->getElementsByTagName('outline')->length == 0)
$node->parentNode->removeChild($node);
}
$res = $doc->saveXML();
/* // saveXML uses a two-space indent. Change to tabs.
$res = preg_replace_callback('/^(?: )+/mu',
create_function(
'$matches',
'return str_repeat("\t", intval(strlen($matches[0])/2));'),
$res); */
print $res;
}
// Import
private function opml_import_feed($doc, $node, $cat_id, $owner_uid) {
$attrs = $node->attributes;
$feed_title = $this->dbh->escape_string(mb_substr($attrs->getNamedItem('text')->nodeValue, 0, 250));
if (!$feed_title) $feed_title = $this->dbh->escape_string(mb_substr($attrs->getNamedItem('title')->nodeValue, 0, 250));
$feed_url = $this->dbh->escape_string($attrs->getNamedItem('xmlUrl')->nodeValue);
if (!$feed_url) $feed_url = $this->dbh->escape_string($attrs->getNamedItem('xmlURL')->nodeValue);
$site_url = $this->dbh->escape_string(mb_substr($attrs->getNamedItem('htmlUrl')->nodeValue, 0, 250));
if ($feed_url && $feed_title) {
$result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE
feed_url = '$feed_url' AND owner_uid = '$owner_uid'");
if ($this->dbh->num_rows($result) == 0) {
#$this->opml_notice("[FEED] [$feed_title/$feed_url] dst_CAT=$cat_id");
$this->opml_notice(T_sprintf("Adding feed: %s", $feed_title));
if (!$cat_id) $cat_id = 'NULL';
$query = "INSERT INTO ttrss_feeds
(title, feed_url, owner_uid, cat_id, site_url, order_id) VALUES
('$feed_title', '$feed_url', '$owner_uid',
$cat_id, '$site_url', 0)";
$this->dbh->query($query);
} else {
$this->opml_notice(T_sprintf("Duplicate feed: %s", $feed_title));
}
}
}
private function opml_import_label($doc, $node, $owner_uid) {
$attrs = $node->attributes;
$label_name = $this->dbh->escape_string($attrs->getNamedItem('label-name')->nodeValue);
if ($label_name) {
$fg_color = $this->dbh->escape_string($attrs->getNamedItem('label-fg-color')->nodeValue);
$bg_color = $this->dbh->escape_string($attrs->getNamedItem('label-bg-color')->nodeValue);
if (!label_find_id($label_name, $_SESSION['uid'])) {
$this->opml_notice(T_sprintf("Adding label %s", htmlspecialchars($label_name)));
label_create($label_name, $fg_color, $bg_color, $owner_uid);
} else {
$this->opml_notice(T_sprintf("Duplicate label: %s", htmlspecialchars($label_name)));
}
}
}
private function opml_import_preference($doc, $node, $owner_uid) {
$attrs = $node->attributes;
$pref_name = $this->dbh->escape_string($attrs->getNamedItem('pref-name')->nodeValue);
if ($pref_name) {
$pref_value = $this->dbh->escape_string($attrs->getNamedItem('value')->nodeValue);
$this->opml_notice(T_sprintf("Setting preference key %s to %s",
$pref_name, $pref_value));
set_pref($pref_name, $pref_value);
}
}
private function opml_import_filter($doc, $node, $owner_uid) {
$attrs = $node->attributes;
$filter_type = $this->dbh->escape_string($attrs->getNamedItem('filter-type')->nodeValue);
if ($filter_type == '2') {
$filter = json_decode($node->nodeValue, true);
if ($filter) {
$match_any_rule = bool_to_sql_bool($filter["match_any_rule"]);
$enabled = bool_to_sql_bool($filter["enabled"]);
$inverse = bool_to_sql_bool($filter["inverse"]);
$title = db_escape_string($filter["title"]);
$this->dbh->query("BEGIN");
$this->dbh->query("INSERT INTO ttrss_filters2 (match_any_rule,enabled,inverse,title,owner_uid)
VALUES ($match_any_rule, $enabled, $inverse, '$title',
".$_SESSION["uid"].")");
$result = $this->dbh->query("SELECT MAX(id) AS id FROM ttrss_filters2 WHERE
owner_uid = ".$_SESSION["uid"]);
$filter_id = $this->dbh->fetch_result($result, 0, "id");
if ($filter_id) {
$this->opml_notice(T_sprintf("Adding filter..."));
foreach ($filter["rules"] as $rule) {
$feed_id = "NULL";
$cat_id = "NULL";
if (!$rule["cat_filter"]) {
$tmp_result = $this->dbh->query("SELECT id FROM ttrss_feeds
WHERE title = '".$this->dbh->escape_string($rule["feed"])."' AND owner_uid = ".$_SESSION["uid"]);
if ($this->dbh->num_rows($tmp_result) > 0) {
$feed_id = $this->dbh->fetch_result($tmp_result, 0, "id");
}
} else {
$tmp_result = $this->dbh->query("SELECT id FROM ttrss_feed_categories
WHERE title = '".$this->dbh->escape_string($rule["feed"])."' AND owner_uid = ".$_SESSION["uid"]);
if ($this->dbh->num_rows($tmp_result) > 0) {
$cat_id = $this->dbh->fetch_result($tmp_result, 0, "id");
}
}
$cat_filter = bool_to_sql_bool($rule["cat_filter"]);
$reg_exp = $this->dbh->escape_string($rule["reg_exp"]);
$filter_type = (int)$rule["filter_type"];
$inverse = bool_to_sql_bool($rule["inverse"]);
$this->dbh->query("INSERT INTO ttrss_filters2_rules (feed_id,cat_id,filter_id,filter_type,reg_exp,cat_filter,inverse)
VALUES ($feed_id, $cat_id, $filter_id, $filter_type, '$reg_exp', $cat_filter,$inverse)");
}
foreach ($filter["actions"] as $action) {
$action_id = (int)$action["action_id"];
$action_param = $this->dbh->escape_string($action["action_param"]);
$this->dbh->query("INSERT INTO ttrss_filters2_actions (filter_id,action_id,action_param)
VALUES ($filter_id, $action_id, '$action_param')");
}
}
$this->dbh->query("COMMIT");
}
}
}
private function opml_import_category($doc, $root_node, $owner_uid, $parent_id) {
$body = $doc->getElementsByTagName('body');
$default_cat_id = (int) get_feed_category('Imported feeds', false);
if ($root_node) {
$cat_title = $this->dbh->escape_string(mb_substr($root_node->attributes->getNamedItem('text')->nodeValue, 0, 250));
if (!$cat_title)
$cat_title = $this->dbh->escape_string(mb_substr($root_node->attributes->getNamedItem('title')->nodeValue, 0, 250));
if (!in_array($cat_title, array("tt-rss-filters", "tt-rss-labels", "tt-rss-prefs"))) {
$cat_id = get_feed_category($cat_title, $parent_id);
$this->dbh->query("BEGIN");
if ($cat_id === false) {
add_feed_category($cat_title, $parent_id);
$cat_id = get_feed_category($cat_title, $parent_id);
}
$this->dbh->query("COMMIT");
} else {
$cat_id = 0;
}
$outlines = $root_node->childNodes;
} else {
$xpath = new DOMXpath($doc);
$outlines = $xpath->query("//opml/body/outline");
$cat_id = 0;
}
#$this->opml_notice("[CAT] $cat_title id: $cat_id P_id: $parent_id");
$this->opml_notice(T_sprintf("Processing category: %s", $cat_title ? $cat_title : __("Uncategorized")));
foreach ($outlines as $node) {
if ($node->hasAttributes() && strtolower($node->tagName) == "outline") {
$attrs = $node->attributes;
$node_cat_title = $this->dbh->escape_string($attrs->getNamedItem('text')->nodeValue);
if (!$node_cat_title)
$node_cat_title = $this->dbh->escape_string($attrs->getNamedItem('title')->nodeValue);
$node_feed_url = $this->dbh->escape_string($attrs->getNamedItem('xmlUrl')->nodeValue);
if ($node_cat_title && !$node_feed_url) {
$this->opml_import_category($doc, $node, $owner_uid, $cat_id);
} else {
if (!$cat_id) {
$dst_cat_id = $default_cat_id;
} else {
$dst_cat_id = $cat_id;
}
switch ($cat_title) {
case "tt-rss-prefs":
$this->opml_import_preference($doc, $node, $owner_uid);
break;
case "tt-rss-labels":
$this->opml_import_label($doc, $node, $owner_uid);
break;
case "tt-rss-filters":
$this->opml_import_filter($doc, $node, $owner_uid);
break;
default:
$this->opml_import_feed($doc, $node, $dst_cat_id, $owner_uid);
}
}
}
}
}
function opml_import($owner_uid) {
if (!$owner_uid) return;
$debug = isset($_REQUEST["debug"]);
$doc = false;
# if ($debug) $doc = DOMDocument::load("/tmp/test.opml");
if ($_FILES['opml_file']['error'] != 0) {
print_error(T_sprintf("Upload failed with error code %d",
$_FILES['opml_file']['error']));
return;
}
$tmp_file = false;
if (is_uploaded_file($_FILES['opml_file']['tmp_name'])) {
$tmp_file = tempnam(CACHE_DIR . '/upload', 'opml');
$result = move_uploaded_file($_FILES['opml_file']['tmp_name'],
$tmp_file);
if (!$result) {
print_error(__("Unable to move uploaded file."));
return;
}
} else {
print_error(__('Error: please upload OPML file.'));
return;
}
if (is_file($tmp_file)) {
$doc = new DOMDocument();
libxml_disable_entity_loader(false);
$doc->load($tmp_file);
libxml_disable_entity_loader(true);
unlink($tmp_file);
} else if (!$doc) {
print_error(__('Error: unable to find moved OPML file.'));
return;
}
if ($doc) {
$this->opml_import_category($doc, false, $owner_uid, false);
} else {
print_error(__('Error while parsing document.'));
}
}
private function opml_notice($msg) {
print "$msg<br/>";
}
static function opml_publish_url(){
$url_path = get_self_url_prefix();
$url_path .= "/opml.php?op=publish&key=" .
get_feed_access_key('OPML:Publish', false, $_SESSION["uid"]);
return $url_path;
}
}
?>

View file

@ -1,30 +0,0 @@
<?php
class Plugin {
private $dbh;
private $host;
const API_VERSION_COMPAT = 1;
function init($host) {
$this->dbh = $host->get_dbh();
$this->host = $host;
}
function about() {
// version, name, description, author, is_system
return array(1.0, "plugin", "No description", "No author", false);
}
function get_js() {
return "";
}
function get_prefs_js() {
return "";
}
function api_version() {
return Plugin::API_VERSION_COMPAT;
}
}
?>

View file

@ -1,22 +0,0 @@
<?php
class PluginHandler extends Handler_Protected {
function csrf_ignore($method) {
return true;
}
function catchall($method) {
$plugin = PluginHost::getInstance()->get_plugin($_REQUEST["plugin"]);
if ($plugin) {
if (method_exists($plugin, $method)) {
$plugin->$method();
} else {
print json_encode(array("error" => "METHOD_NOT_FOUND"));
}
} else {
print json_encode(array("error" => "PLUGIN_NOT_FOUND"));
}
}
}
?>

View file

@ -1,403 +0,0 @@
<?php
class PluginHost {
private $dbh;
private $hooks = array();
private $plugins = array();
private $handlers = array();
private $commands = array();
private $storage = array();
private $feeds = array();
private $api_methods = array();
private $owner_uid;
private $debug;
private $last_registered;
private static $instance;
const API_VERSION = 2;
const HOOK_ARTICLE_BUTTON = 1;
const HOOK_ARTICLE_FILTER = 2;
const HOOK_PREFS_TAB = 3;
const HOOK_PREFS_TAB_SECTION = 4;
const HOOK_PREFS_TABS = 5;
const HOOK_FEED_PARSED = 6;
const HOOK_UPDATE_TASK = 7;
const HOOK_AUTH_USER = 8;
const HOOK_HOTKEY_MAP = 9;
const HOOK_RENDER_ARTICLE = 10;
const HOOK_RENDER_ARTICLE_CDM = 11;
const HOOK_FEED_FETCHED = 12;
const HOOK_SANITIZE = 13;
const HOOK_RENDER_ARTICLE_API = 14;
const HOOK_TOOLBAR_BUTTON = 15;
const HOOK_ACTION_ITEM = 16;
const HOOK_HEADLINE_TOOLBAR_BUTTON = 17;
const HOOK_HOTKEY_INFO = 18;
const HOOK_ARTICLE_LEFT_BUTTON = 19;
const HOOK_PREFS_EDIT_FEED = 20;
const HOOK_PREFS_SAVE_FEED = 21;
const HOOK_FETCH_FEED = 22;
const HOOK_QUERY_HEADLINES = 23;
const HOOK_HOUSE_KEEPING = 24;
const HOOK_SEARCH = 25;
const HOOK_FORMAT_ENCLOSURES = 26;
const HOOK_SUBSCRIBE_FEED = 27;
const HOOK_HEADLINES_BEFORE = 28;
const KIND_ALL = 1;
const KIND_SYSTEM = 2;
const KIND_USER = 3;
function __construct() {
$this->dbh = Db::get();
$this->storage = array();
}
private function __clone() {
//
}
public static function getInstance() {
if (self::$instance == null)
self::$instance = new self();
return self::$instance;
}
private function register_plugin($name, $plugin) {
//array_push($this->plugins, $plugin);
$this->plugins[$name] = $plugin;
}
// needed for compatibility with API 1
function get_link() {
return false;
}
function get_dbh() {
return $this->dbh;
}
function get_plugin_names() {
$names = array();
foreach ($this->plugins as $p) {
array_push($names, get_class($p));
}
return $names;
}
function get_plugins() {
return $this->plugins;
}
function get_plugin($name) {
return $this->plugins[$name];
}
function run_hooks($type, $method, $args) {
foreach ($this->get_hooks($type) as $hook) {
$hook->$method($args);
}
}
function add_hook($type, $sender) {
if (!is_array($this->hooks[$type])) {
$this->hooks[$type] = array();
}
array_push($this->hooks[$type], $sender);
}
function del_hook($type, $sender) {
if (is_array($this->hooks[$type])) {
$key = array_Search($sender, $this->hooks[$type]);
if ($key !== FALSE) {
unset($this->hooks[$type][$key]);
}
}
}
function get_hooks($type) {
if (isset($this->hooks[$type])) {
return $this->hooks[$type];
} else {
return array();
}
}
function load_all($kind, $owner_uid = false) {
$plugins = array_map("basename", glob("plugins/*"));
$this->load(join(",", $plugins), $kind, $owner_uid);
}
function load($classlist, $kind, $owner_uid = false) {
$plugins = explode(",", $classlist);
$this->owner_uid = (int) $owner_uid;
foreach ($plugins as $class) {
$class = trim($class);
$class_file = strtolower(basename($class));
if (!is_dir(dirname(__FILE__)."/../plugins/$class_file")) continue;
$file = dirname(__FILE__)."/../plugins/$class_file/init.php";
if (!isset($this->plugins[$class])) {
if (file_exists($file)) require_once $file;
if (class_exists($class) && is_subclass_of($class, "Plugin")) {
$plugin = new $class($this);
$plugin_api = $plugin->api_version();
if ($plugin_api < PluginHost::API_VERSION) {
user_error("Plugin $class is not compatible with current API version (need: " . PluginHost::API_VERSION . ", got: $plugin_api)", E_USER_WARNING);
continue;
}
$this->last_registered = $class;
switch ($kind) {
case $this::KIND_SYSTEM:
if ($this->is_system($plugin)) {
$plugin->init($this);
$this->register_plugin($class, $plugin);
}
break;
case $this::KIND_USER:
if (!$this->is_system($plugin)) {
$plugin->init($this);
$this->register_plugin($class, $plugin);
}
break;
case $this::KIND_ALL:
$plugin->init($this);
$this->register_plugin($class, $plugin);
break;
}
}
}
}
}
function is_system($plugin) {
$about = $plugin->about();
return @$about[3];
}
// only system plugins are allowed to modify routing
function add_handler($handler, $method, $sender) {
$handler = str_replace("-", "_", strtolower($handler));
$method = strtolower($method);
if ($this->is_system($sender)) {
if (!is_array($this->handlers[$handler])) {
$this->handlers[$handler] = array();
}
$this->handlers[$handler][$method] = $sender;
}
}
function del_handler($handler, $method, $sender) {
$handler = str_replace("-", "_", strtolower($handler));
$method = strtolower($method);
if ($this->is_system($sender)) {
unset($this->handlers[$handler][$method]);
}
}
function lookup_handler($handler, $method) {
$handler = str_replace("-", "_", strtolower($handler));
$method = strtolower($method);
if (is_array($this->handlers[$handler])) {
if (isset($this->handlers[$handler]["*"])) {
return $this->handlers[$handler]["*"];
} else {
return $this->handlers[$handler][$method];
}
}
return false;
}
function add_command($command, $description, $sender, $suffix = "", $arghelp = "") {
$command = str_replace("-", "_", strtolower($command));
$this->commands[$command] = array("description" => $description,
"suffix" => $suffix,
"arghelp" => $arghelp,
"class" => $sender);
}
function del_command($command) {
$command = "-" . strtolower($command);
unset($this->commands[$command]);
}
function lookup_command($command) {
$command = "-" . strtolower($command);
if (is_array($this->commands[$command])) {
return $this->commands[$command]["class"];
} else {
return false;
}
return false;
}
function get_commands() {
return $this->commands;
}
function run_commands($args) {
foreach ($this->get_commands() as $command => $data) {
if (isset($args[$command])) {
$command = str_replace("-", "", $command);
$data["class"]->$command($args);
}
}
}
function load_data($force = false) {
if ($this->owner_uid) {
$result = $this->dbh->query("SELECT name, content FROM ttrss_plugin_storage
WHERE owner_uid = '".$this->owner_uid."'");
while ($line = $this->dbh->fetch_assoc($result)) {
$this->storage[$line["name"]] = unserialize($line["content"]);
}
}
}
private function save_data($plugin) {
if ($this->owner_uid) {
$plugin = $this->dbh->escape_string($plugin);
$this->dbh->query("BEGIN");
$result = $this->dbh->query("SELECT id FROM ttrss_plugin_storage WHERE
owner_uid= '".$this->owner_uid."' AND name = '$plugin'");
if (!isset($this->storage[$plugin]))
$this->storage[$plugin] = array();
$content = $this->dbh->escape_string(serialize($this->storage[$plugin]),
false);
if ($this->dbh->num_rows($result) != 0) {
$this->dbh->query("UPDATE ttrss_plugin_storage SET content = '$content'
WHERE owner_uid= '".$this->owner_uid."' AND name = '$plugin'");
} else {
$this->dbh->query("INSERT INTO ttrss_plugin_storage
(name,owner_uid,content) VALUES
('$plugin','".$this->owner_uid."','$content')");
}
$this->dbh->query("COMMIT");
}
}
function set($sender, $name, $value, $sync = true) {
$idx = get_class($sender);
if (!isset($this->storage[$idx]))
$this->storage[$idx] = array();
$this->storage[$idx][$name] = $value;
if ($sync) $this->save_data(get_class($sender));
}
function get($sender, $name, $default_value = false) {
$idx = get_class($sender);
if (isset($this->storage[$idx][$name])) {
return $this->storage[$idx][$name];
} else {
return $default_value;
}
}
function get_all($sender) {
$idx = get_class($sender);
return $this->storage[$idx];
}
function clear_data($sender) {
if ($this->owner_uid) {
$idx = get_class($sender);
unset($this->storage[$idx]);
$this->dbh->query("DELETE FROM ttrss_plugin_storage WHERE name = '$idx'
AND owner_uid = " . $this->owner_uid);
}
}
function set_debug($debug) {
$this->debug = $debug;
}
function get_debug() {
return $this->debug;
}
// Plugin feed functions are *EXPERIMENTAL*!
// cat_id: only -1 is supported (Special)
function add_feed($cat_id, $title, $icon, $sender) {
if (!$this->feeds[$cat_id]) $this->feeds[$cat_id] = array();
$id = count($this->feeds[$cat_id]);
array_push($this->feeds[$cat_id],
array('id' => $id, 'title' => $title, 'sender' => $sender, 'icon' => $icon));
return $id;
}
function get_feeds($cat_id) {
return $this->feeds[$cat_id];
}
// convert feed_id (e.g. -129) to pfeed_id first
function get_feed_handler($pfeed_id) {
foreach ($this->feeds as $cat) {
foreach ($cat as $feed) {
if ($feed['id'] == $pfeed_id) {
return $feed['sender'];
}
}
}
}
static function pfeed_to_feed_id($label) {
return PLUGIN_FEED_BASE_INDEX - 1 - abs($label);
}
static function feed_to_pfeed_id($feed) {
return PLUGIN_FEED_BASE_INDEX - 1 + abs($feed);
}
function add_api_method($name, $sender) {
if ($this->is_system($sender)) {
$this->api_methods[strtolower($name)] = $sender;
}
}
function get_api_method($name) {
return $this->api_methods[$name];
}
}
?>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,330 +0,0 @@
<?php
class Pref_Labels extends Handler_Protected {
function csrf_ignore($method) {
$csrf_ignored = array("index", "getlabeltree", "edit");
return array_search($method, $csrf_ignored) !== false;
}
function edit() {
$label_id = $this->dbh->escape_string($_REQUEST['id']);
$result = $this->dbh->query("SELECT * FROM ttrss_labels2 WHERE
id = '$label_id' AND owner_uid = " . $_SESSION["uid"]);
$line = $this->dbh->fetch_assoc($result);
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$label_id\">";
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-labels\">";
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"save\">";
print "<div class=\"dlgSec\">".__("Caption")."</div>";
print "<div class=\"dlgSecCont\">";
$fg_color = $line['fg_color'];
$bg_color = $line['bg_color'];
print "<span class=\"labelColorIndicator\" id=\"label-editor-indicator\" style='color : $fg_color; background-color : $bg_color; margin-bottom : 4px; margin-right : 4px'>&alpha;</span>";
print "<input style=\"font-size : 16px\" name=\"caption\"
dojoType=\"dijit.form.ValidationTextBox\"
required=\"true\"
value=\"".htmlspecialchars($line['caption'])."\">";
print "</div>";
print "<div class=\"dlgSec\">" . __("Colors") . "</div>";
print "<div class=\"dlgSecCont\">";
print "<table cellspacing=\"0\">";
print "<tr><td>".__("Foreground:")."</td><td>".__("Background:").
"</td></tr>";
print "<tr><td style='padding-right : 10px'>";
print "<input dojoType=\"dijit.form.TextBox\"
style=\"display : none\" id=\"labelEdit_fgColor\"
name=\"fg_color\" value=\"$fg_color\">";
print "<input dojoType=\"dijit.form.TextBox\"
style=\"display : none\" id=\"labelEdit_bgColor\"
name=\"bg_color\" value=\"$bg_color\">";
print "<div dojoType=\"dijit.ColorPalette\">
<script type=\"dojo/method\" event=\"onChange\" args=\"fg_color\">
dijit.byId(\"labelEdit_fgColor\").attr('value', fg_color);
$('label-editor-indicator').setStyle({color: fg_color});
</script>
</div>";
print "</div>";
print "</td><td>";
print "<div dojoType=\"dijit.ColorPalette\">
<script type=\"dojo/method\" event=\"onChange\" args=\"bg_color\">
dijit.byId(\"labelEdit_bgColor\").attr('value', bg_color);
$('label-editor-indicator').setStyle({backgroundColor: bg_color});
</script>
</div>";
print "</div>";
print "</td></tr></table>";
print "</div>";
# print "</form>";
print "<div class=\"dlgButtons\">";
print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('labelEditDlg').execute()\">".
__('Save')."</button>";
print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('labelEditDlg').hide()\">".
__('Cancel')."</button>";
print "</div>";
return;
}
function getlabeltree() {
$root = array();
$root['id'] = 'root';
$root['name'] = __('Labels');
$root['items'] = array();
$result = $this->dbh->query("SELECT *
FROM ttrss_labels2
WHERE owner_uid = ".$_SESSION["uid"]."
ORDER BY caption");
while ($line = $this->dbh->fetch_assoc($result)) {
$label = array();
$label['id'] = 'LABEL:' . $line['id'];
$label['bare_id'] = $line['id'];
$label['name'] = $line['caption'];
$label['fg_color'] = $line['fg_color'];
$label['bg_color'] = $line['bg_color'];
$label['type'] = 'label';
$label['checkbox'] = false;
array_push($root['items'], $label);
}
$fl = array();
$fl['identifier'] = 'id';
$fl['label'] = 'name';
$fl['items'] = array($root);
print json_encode($fl);
return;
}
function colorset() {
$kind = $this->dbh->escape_string($_REQUEST["kind"]);
$ids = explode(',', $this->dbh->escape_string($_REQUEST["ids"]));
$color = $this->dbh->escape_string($_REQUEST["color"]);
$fg = $this->dbh->escape_string($_REQUEST["fg"]);
$bg = $this->dbh->escape_string($_REQUEST["bg"]);
foreach ($ids as $id) {
if ($kind == "fg" || $kind == "bg") {
$this->dbh->query("UPDATE ttrss_labels2 SET
${kind}_color = '$color' WHERE id = '$id'
AND owner_uid = " . $_SESSION["uid"]);
} else {
$this->dbh->query("UPDATE ttrss_labels2 SET
fg_color = '$fg', bg_color = '$bg' WHERE id = '$id'
AND owner_uid = " . $_SESSION["uid"]);
}
$caption = $this->dbh->escape_string(label_find_caption($id, $_SESSION["uid"]));
/* Remove cached data */
$this->dbh->query("UPDATE ttrss_user_entries SET label_cache = ''
WHERE label_cache LIKE '%$caption%' AND owner_uid = " . $_SESSION["uid"]);
}
return;
}
function colorreset() {
$ids = explode(',', $this->dbh->escape_string($_REQUEST["ids"]));
foreach ($ids as $id) {
$this->dbh->query("UPDATE ttrss_labels2 SET
fg_color = '', bg_color = '' WHERE id = '$id'
AND owner_uid = " . $_SESSION["uid"]);
$caption = $this->dbh->escape_string(label_find_caption($id, $_SESSION["uid"]));
/* Remove cached data */
$this->dbh->query("UPDATE ttrss_user_entries SET label_cache = ''
WHERE label_cache LIKE '%$caption%' AND owner_uid = " . $_SESSION["uid"]);
}
}
function save() {
$id = $this->dbh->escape_string($_REQUEST["id"]);
$caption = $this->dbh->escape_string(trim($_REQUEST["caption"]));
$this->dbh->query("BEGIN");
$result = $this->dbh->query("SELECT caption FROM ttrss_labels2
WHERE id = '$id' AND owner_uid = ". $_SESSION["uid"]);
if ($this->dbh->num_rows($result) != 0) {
$old_caption = $this->dbh->fetch_result($result, 0, "caption");
$result = $this->dbh->query("SELECT id FROM ttrss_labels2
WHERE caption = '$caption' AND owner_uid = ". $_SESSION["uid"]);
if ($this->dbh->num_rows($result) == 0) {
if ($caption) {
$result = $this->dbh->query("UPDATE ttrss_labels2 SET
caption = '$caption' WHERE id = '$id' AND
owner_uid = " . $_SESSION["uid"]);
/* Update filters that reference label being renamed */
$old_caption = $this->dbh->escape_string($old_caption);
$this->dbh->query("UPDATE ttrss_filters2_actions SET
action_param = '$caption' WHERE action_param = '$old_caption'
AND action_id = 7
AND filter_id IN (SELECT id FROM ttrss_filters2 WHERE owner_uid = ".$_SESSION["uid"].")");
print $_REQUEST["value"];
} else {
print $old_caption;
}
} else {
print $old_caption;
}
}
$this->dbh->query("COMMIT");
return;
}
function remove() {
$ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
foreach ($ids as $id) {
label_remove($id, $_SESSION["uid"]);
}
}
function add() {
$caption = $this->dbh->escape_string($_REQUEST["caption"]);
$output = $this->dbh->escape_string($_REQUEST["output"]);
if ($caption) {
if (label_create($caption)) {
if (!$output) {
print T_sprintf("Created label <b>%s</b>", htmlspecialchars($caption));
}
}
if ($output == "select") {
header("Content-Type: text/xml");
print "<rpc-reply><payload>";
print_label_select("select_label",
$caption, "");
print "</payload></rpc-reply>";
}
}
return;
}
function index() {
$sort = $this->dbh->escape_string($_REQUEST["sort"]);
if (!$sort || $sort == "undefined") {
$sort = "caption";
}
$label_search = $this->dbh->escape_string($_REQUEST["search"]);
if (array_key_exists("search", $_REQUEST)) {
$_SESSION["prefs_label_search"] = $label_search;
} else {
$label_search = $_SESSION["prefs_label_search"];
}
print "<div id=\"pref-label-wrap\" dojoType=\"dijit.layout.BorderContainer\" gutters=\"false\">";
print "<div id=\"pref-label-header\" dojoType=\"dijit.layout.ContentPane\" region=\"top\">";
print "<div id=\"pref-label-toolbar\" dojoType=\"dijit.Toolbar\">";
print "<div dojoType=\"dijit.form.DropDownButton\">".
"<span>" . __('Select')."</span>";
print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
print "<div onclick=\"dijit.byId('labelTree').model.setAllChecked(true)\"
dojoType=\"dijit.MenuItem\">".__('All')."</div>";
print "<div onclick=\"dijit.byId('labelTree').model.setAllChecked(false)\"
dojoType=\"dijit.MenuItem\">".__('None')."</div>";
print "</div></div>";
print"<button dojoType=\"dijit.form.Button\" onclick=\"return addLabel()\">".
__('Create label')."</button dojoType=\"dijit.form.Button\"> ";
print "<button dojoType=\"dijit.form.Button\" onclick=\"removeSelectedLabels()\">".
__('Remove')."</button dojoType=\"dijit.form.Button\"> ";
print "<button dojoType=\"dijit.form.Button\" onclick=\"labelColorReset()\">".
__('Clear colors')."</button dojoType=\"dijit.form.Button\">";
print "</div>"; #toolbar
print "</div>"; #pane
print "<div id=\"pref-label-content\" dojoType=\"dijit.layout.ContentPane\" region=\"center\">";
print "<div id=\"labellistLoading\">
<img src='images/indicator_tiny.gif'>".
__("Loading, please wait...")."</div>";
print "<div dojoType=\"dojo.data.ItemFileWriteStore\" jsId=\"labelStore\"
url=\"backend.php?op=pref-labels&method=getlabeltree\">
</div>
<div dojoType=\"lib.CheckBoxStoreModel\" jsId=\"labelModel\" store=\"labelStore\"
query=\"{id:'root'}\" rootId=\"root\"
childrenAttrs=\"items\" checkboxStrict=\"false\" checkboxAll=\"false\">
</div>
<div dojoType=\"fox.PrefLabelTree\" id=\"labelTree\"
model=\"labelModel\" openOnClick=\"true\">
<script type=\"dojo/method\" event=\"onLoad\" args=\"item\">
Element.hide(\"labellistLoading\");
</script>
<script type=\"dojo/method\" event=\"onClick\" args=\"item\">
var id = String(item.id);
var bare_id = id.substr(id.indexOf(':')+1);
if (id.match('LABEL:')) {
editLabel(bare_id);
}
</script>
</div>";
print "</div>"; #pane
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB,
"hook_prefs_tab", "prefLabels");
print "</div>"; #container
}
}
?>

File diff suppressed because it is too large Load diff

View file

@ -1,90 +0,0 @@
<?php
class Pref_System extends Handler_Protected {
function before($method) {
if (parent::before($method)) {
if ($_SESSION["access_level"] < 10) {
print __("Your access level is insufficient to open this tab.");
return false;
}
return true;
}
return false;
}
function csrf_ignore($method) {
$csrf_ignored = array("index");
return array_search($method, $csrf_ignored) !== false;
}
function clearLog() {
$this->dbh->query("DELETE FROM ttrss_error_log");
}
function index() {
print "<div dojoType=\"dijit.layout.AccordionContainer\" region=\"center\">";
print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Error Log')."\">";
if (LOG_DESTINATION == "sql") {
$result = $this->dbh->query("SELECT errno, errstr, filename, lineno,
created_at, login FROM ttrss_error_log
LEFT JOIN ttrss_users ON (owner_uid = ttrss_users.id)
ORDER BY ttrss_error_log.id DESC
LIMIT 100");
print "<button dojoType=\"dijit.form.Button\"
onclick=\"updateSystemList()\">".__('Refresh')."</button> ";
print "&nbsp;<button dojoType=\"dijit.form.Button\"
onclick=\"clearSqlLog()\">".__('Clear log')."</button> ";
print "<p><table width=\"100%\" cellspacing=\"10\" class=\"prefErrorLog\">";
print "<tr class=\"title\">
<td width='5%'>".__("Error")."</td>
<td>".__("Filename")."</td>
<td>".__("Message")."</td>
<td width='5%'>".__("User")."</td>
<td width='5%'>".__("Date")."</td>
</tr>";
while ($line = $this->dbh->fetch_assoc($result)) {
print "<tr class=\"errrow\">";
foreach ($line as $k => $v) {
$line[$k] = htmlspecialchars($v);
}
print "<td class='errno'>" . Logger::$errornames[$line["errno"]] . " (" . $line["errno"] . ")</td>";
print "<td class='filename'>" . $line["filename"] . ":" . $line["lineno"] . "</td>";
print "<td class='errstr'>" . $line["errstr"] . "</td>";
print "<td class='login'>" . $line["login"] . "</td>";
print "<td class='timestamp'>" .
make_local_datetime(
$line["created_at"], false) . "</td>";
print "</tr>";
}
print "</table>";
} else {
print_notice("Please set LOG_DESTINATION to 'sql' in config.php to enable database logging.");
}
print "</div>";
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB,
"hook_prefs_tab", "prefSystem");
print "</div>"; #container
}
}
?>

View file

@ -1,457 +0,0 @@
<?php
class Pref_Users extends Handler_Protected {
function before($method) {
if (parent::before($method)) {
if ($_SESSION["access_level"] < 10) {
print __("Your access level is insufficient to open this tab.");
return false;
}
return true;
}
return false;
}
function csrf_ignore($method) {
$csrf_ignored = array("index", "edit", "userdetails");
return array_search($method, $csrf_ignored) !== false;
}
function userdetails() {
$uid = sprintf("%d", $_REQUEST["id"]);
$result = $this->dbh->query("SELECT login,
".SUBSTRING_FOR_DATE."(last_login,1,16) AS last_login,
access_level,
(SELECT COUNT(int_id) FROM ttrss_user_entries
WHERE owner_uid = id) AS stored_articles,
".SUBSTRING_FOR_DATE."(created,1,16) AS created
FROM ttrss_users
WHERE id = '$uid'");
if ($this->dbh->num_rows($result) == 0) {
print "<h1>".__('User not found')."</h1>";
return;
}
// print "<h1>User Details</h1>";
$login = $this->dbh->fetch_result($result, 0, "login");
print "<table width='100%'>";
$last_login = make_local_datetime(
$this->dbh->fetch_result($result, 0, "last_login"), true);
$created = make_local_datetime(
$this->dbh->fetch_result($result, 0, "created"), true);
$access_level = $this->dbh->fetch_result($result, 0, "access_level");
$stored_articles = $this->dbh->fetch_result($result, 0, "stored_articles");
print "<tr><td>".__('Registered')."</td><td>$created</td></tr>";
print "<tr><td>".__('Last logged in')."</td><td>$last_login</td></tr>";
$result = $this->dbh->query("SELECT COUNT(id) as num_feeds FROM ttrss_feeds
WHERE owner_uid = '$uid'");
$num_feeds = $this->dbh->fetch_result($result, 0, "num_feeds");
print "<tr><td>".__('Subscribed feeds count')."</td><td>$num_feeds</td></tr>";
print "</table>";
print "<h1>".__('Subscribed feeds')."</h1>";
$result = $this->dbh->query("SELECT id,title,site_url FROM ttrss_feeds
WHERE owner_uid = '$uid' ORDER BY title");
print "<ul class=\"userFeedList\">";
while ($line = $this->dbh->fetch_assoc($result)) {
$icon_file = ICONS_URL."/".$line["id"].".ico";
if (file_exists($icon_file) && filesize($icon_file) > 0) {
$feed_icon = "<img class=\"tinyFeedIcon\" src=\"$icon_file\">";
} else {
$feed_icon = "<img class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\">";
}
print "<li>$feed_icon&nbsp;<a href=\"".$line["site_url"]."\">".$line["title"]."</a></li>";
}
if ($this->dbh->num_rows($result) < $num_feeds) {
// FIXME - add link to show ALL subscribed feeds here somewhere
print "<li><img
class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\">&nbsp;...</li>";
}
print "</ul>";
print "<div align='center'>
<button dojoType=\"dijit.form.Button\" type=\"submit\">".__("Close this window").
"</button></div>";
return;
}
function edit() {
global $access_level_names;
$id = $this->dbh->escape_string($_REQUEST["id"]);
print "<form id=\"user_edit_form\" onsubmit='return false' dojoType=\"dijit.form.Form\">";
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$id\">";
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-users\">";
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"editSave\">";
$result = $this->dbh->query("SELECT * FROM ttrss_users WHERE id = '$id'");
$login = $this->dbh->fetch_result($result, 0, "login");
$access_level = $this->dbh->fetch_result($result, 0, "access_level");
$email = $this->dbh->fetch_result($result, 0, "email");
$sel_disabled = ($id == $_SESSION["uid"]) ? "disabled" : "";
print "<div class=\"dlgSec\">".__("User")."</div>";
print "<div class=\"dlgSecCont\">";
if ($sel_disabled) {
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"login\" value=\"$login\">";
}
print "<input size=\"30\" style=\"font-size : 16px\"
dojoType=\"dijit.form.ValidationTextBox\" required=\"1\"
onkeypress=\"return filterCR(event, userEditSave)\" $sel_disabled
name=\"login\" value=\"$login\">";
print "</div>";
print "<div class=\"dlgSec\">".__("Authentication")."</div>";
print "<div class=\"dlgSecCont\">";
print __('Access level: ') . " ";
if (!$sel_disabled) {
print_select_hash("access_level", $access_level, $access_level_names,
"dojoType=\"dijit.form.Select\" $sel_disabled");
} else {
print_select_hash("", $access_level, $access_level_names,
"dojoType=\"dijit.form.Select\" $sel_disabled");
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"access_level\" value=\"$access_level\">";
}
print "<hr/>";
print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" size=\"20\" onkeypress=\"return filterCR(event, userEditSave)\" placeholder=\"Change password\"
name=\"password\">";
print "</div>";
print "<div class=\"dlgSec\">".__("Options")."</div>";
print "<div class=\"dlgSecCont\">";
print "<input dojoType=\"dijit.form.TextBox\" size=\"30\" name=\"email\" onkeypress=\"return filterCR(event, userEditSave)\" placeholder=\"E-mail\"
value=\"$email\">";
print "</div>";
print "</table>";
print "</form>";
print "<div class=\"dlgButtons\">
<button dojoType=\"dijit.form.Button\" type=\"submit\">".
__('Save')."</button>
<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('userEditDlg').hide()\">".
__('Cancel')."</button></div>";
return;
}
function editSave() {
$login = $this->dbh->escape_string(trim($_REQUEST["login"]));
$uid = $this->dbh->escape_string($_REQUEST["id"]);
$access_level = (int) $_REQUEST["access_level"];
$email = $this->dbh->escape_string(trim($_REQUEST["email"]));
$password = $_REQUEST["password"];
if ($password) {
$salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
$pwd_hash = encrypt_password($password, $salt, true);
$pass_query_part = "pwd_hash = '$pwd_hash', salt = '$salt',";
} else {
$pass_query_part = "";
}
$this->dbh->query("UPDATE ttrss_users SET $pass_query_part login = '$login',
access_level = '$access_level', email = '$email', otp_enabled = false
WHERE id = '$uid'");
}
function remove() {
$ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
foreach ($ids as $id) {
if ($id != $_SESSION["uid"] && $id != 1) {
$this->dbh->query("DELETE FROM ttrss_tags WHERE owner_uid = '$id'");
$this->dbh->query("DELETE FROM ttrss_feeds WHERE owner_uid = '$id'");
$this->dbh->query("DELETE FROM ttrss_users WHERE id = '$id'");
}
}
}
function add() {
$login = $this->dbh->escape_string(trim($_REQUEST["login"]));
$tmp_user_pwd = make_password(8);
$salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
$pwd_hash = encrypt_password($tmp_user_pwd, $salt, true);
$result = $this->dbh->query("SELECT id FROM ttrss_users WHERE
login = '$login'");
if ($this->dbh->num_rows($result) == 0) {
$this->dbh->query("INSERT INTO ttrss_users
(login,pwd_hash,access_level,last_login,created, salt)
VALUES ('$login', '$pwd_hash', 0, null, NOW(), '$salt')");
$result = $this->dbh->query("SELECT id FROM ttrss_users WHERE
login = '$login' AND pwd_hash = '$pwd_hash'");
if ($this->dbh->num_rows($result) == 1) {
$new_uid = $this->dbh->fetch_result($result, 0, "id");
print format_notice(T_sprintf("Added user <b>%s</b> with password <b>%s</b>",
$login, $tmp_user_pwd));
initialize_user($new_uid);
} else {
print format_warning(T_sprintf("Could not create user <b>%s</b>", $login));
}
} else {
print format_warning(T_sprintf("User <b>%s</b> already exists.", $login));
}
}
static function resetUserPassword($uid, $show_password) {
$result = db_query("SELECT login,email
FROM ttrss_users WHERE id = '$uid'");
$login = db_fetch_result($result, 0, "login");
$email = db_fetch_result($result, 0, "email");
$salt = db_fetch_result($result, 0, "salt");
$new_salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
$tmp_user_pwd = make_password(8);
$pwd_hash = encrypt_password($tmp_user_pwd, $new_salt, true);
db_query("UPDATE ttrss_users SET pwd_hash = '$pwd_hash', salt = '$new_salt', otp_enabled = false
WHERE id = '$uid'");
if ($show_password) {
print T_sprintf("Changed password of user <b>%s</b> to <b>%s</b>", $login, $tmp_user_pwd);
} else {
print_notice(T_sprintf("Sending new password of user <b>%s</b> to <b>%s</b>", $login, $email));
}
require_once 'classes/ttrssmailer.php';
if ($email) {
require_once "lib/MiniTemplator.class.php";
$tpl = new MiniTemplator;
$tpl->readTemplateFromFile("templates/resetpass_template.txt");
$tpl->setVariable('LOGIN', $login);
$tpl->setVariable('NEWPASS', $tmp_user_pwd);
$tpl->addBlock('message');
$message = "";
$tpl->generateOutputToString($message);
$mail = new ttrssMailer();
$rc = $mail->quickMail($email, $login,
__("[tt-rss] Password change notification"),
$message, false);
if (!$rc) print_error($mail->ErrorInfo);
}
}
function resetPass() {
$uid = $this->dbh->escape_string($_REQUEST["id"]);
Pref_Users::resetUserPassword($uid, true);
}
function index() {
global $access_level_names;
print "<div id=\"pref-user-wrap\" dojoType=\"dijit.layout.BorderContainer\" gutters=\"false\">";
print "<div id=\"pref-user-header\" dojoType=\"dijit.layout.ContentPane\" region=\"top\">";
print "<div id=\"pref-user-toolbar\" dojoType=\"dijit.Toolbar\">";
$user_search = $this->dbh->escape_string($_REQUEST["search"]);
if (array_key_exists("search", $_REQUEST)) {
$_SESSION["prefs_user_search"] = $user_search;
} else {
$user_search = $_SESSION["prefs_user_search"];
}
print "<div style='float : right; padding-right : 4px;'>
<input dojoType=\"dijit.form.TextBox\" id=\"user_search\" size=\"20\" type=\"search\"
value=\"$user_search\">
<button dojoType=\"dijit.form.Button\" onclick=\"updateUsersList()\">".
__('Search')."</button>
</div>";
$sort = $this->dbh->escape_string($_REQUEST["sort"]);
if (!$sort || $sort == "undefined") {
$sort = "login";
}
print "<div dojoType=\"dijit.form.DropDownButton\">".
"<span>" . __('Select')."</span>";
print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
print "<div onclick=\"selectTableRows('prefUserList', 'all')\"
dojoType=\"dijit.MenuItem\">".__('All')."</div>";
print "<div onclick=\"selectTableRows('prefUserList', 'none')\"
dojoType=\"dijit.MenuItem\">".__('None')."</div>";
print "</div></div>";
print "<button dojoType=\"dijit.form.Button\" onclick=\"addUser()\">".__('Create user')."</button>";
print "
<button dojoType=\"dijit.form.Button\" onclick=\"selectedUserDetails()\">".
__('Details')."</button dojoType=\"dijit.form.Button\">
<button dojoType=\"dijit.form.Button\" onclick=\"editSelectedUser()\">".
__('Edit')."</button dojoType=\"dijit.form.Button\">
<button dojoType=\"dijit.form.Button\" onclick=\"removeSelectedUsers()\">".
__('Remove')."</button dojoType=\"dijit.form.Button\">
<button dojoType=\"dijit.form.Button\" onclick=\"resetSelectedUserPass()\">".
__('Reset password')."</button dojoType=\"dijit.form.Button\">";
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION,
"hook_prefs_tab_section", "prefUsersToolbar");
print "</div>"; #toolbar
print "</div>"; #pane
print "<div id=\"pref-user-content\" dojoType=\"dijit.layout.ContentPane\" region=\"center\">";
print "<div id=\"sticky-status-msg\"></div>";
if ($user_search) {
$user_search = explode(" ", $user_search);
$tokens = array();
foreach ($user_search as $token) {
$token = trim($token);
array_push($tokens, "(UPPER(login) LIKE UPPER('%$token%'))");
}
$user_search_query = "(" . join($tokens, " AND ") . ") AND ";
} else {
$user_search_query = "";
}
$result = $this->dbh->query("SELECT
id,login,access_level,email,
".SUBSTRING_FOR_DATE."(last_login,1,16) as last_login,
".SUBSTRING_FOR_DATE."(created,1,16) as created
FROM
ttrss_users
WHERE
$user_search_query
id > 0
ORDER BY $sort");
if ($this->dbh->num_rows($result) > 0) {
print "<p><table width=\"100%\" cellspacing=\"0\"
class=\"prefUserList\" id=\"prefUserList\">";
print "<tr class=\"title\">
<td align='center' width=\"5%\">&nbsp;</td>
<td width='30%'><a href=\"#\" onclick=\"updateUsersList('login')\">".__('Login')."</a></td>
<td width='30%'><a href=\"#\" onclick=\"updateUsersList('access_level')\">".__('Access Level')."</a></td>
<td width='20%'><a href=\"#\" onclick=\"updateUsersList('created')\">".__('Registered')."</a></td>
<td width='20%'><a href=\"#\" onclick=\"updateUsersList('last_login')\">".__('Last login')."</a></td></tr>";
$lnum = 0;
while ($line = $this->dbh->fetch_assoc($result)) {
$uid = $line["id"];
print "<tr id=\"UMRR-$uid\">";
$line["login"] = htmlspecialchars($line["login"]);
$line["created"] = make_local_datetime($line["created"], false);
$line["last_login"] = make_local_datetime($line["last_login"], false);
print "<td align='center'><input onclick='toggleSelectRow2(this);'
dojoType=\"dijit.form.CheckBox\" type=\"checkbox\"
id=\"UMCHK-$uid\"></td>";
$onclick = "onclick='editUser($uid, event)' title='".__('Click to edit')."'";
print "<td $onclick><img src='images/user.png' class='markedPic' alt=''> " . $line["login"] . "</td>";
if (!$line["email"]) $line["email"] = "&nbsp;";
print "<td $onclick>" . $access_level_names[$line["access_level"]] . "</td>";
print "<td $onclick>" . $line["created"] . "</td>";
print "<td $onclick>" . $line["last_login"] . "</td>";
print "</tr>";
++$lnum;
}
print "</table>";
} else {
print "<p>";
if (!$user_search) {
print_warning(__('No users defined.'));
} else {
print_warning(__('No matching users found.'));
}
print "</p>";
}
print "</div>"; #pane
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB,
"hook_prefs_tab", "prefUsers");
print "</div>"; #container
}
}
?>

View file

@ -1,654 +0,0 @@
<?php
class RPC extends Handler_Protected {
function csrf_ignore($method) {
$csrf_ignored = array("sanitycheck", "completelabels");
return array_search($method, $csrf_ignored) !== false;
}
function setprofile() {
$id = $this->dbh->escape_string($_REQUEST["id"]);
$_SESSION["profile"] = $id;
}
function remprofiles() {
$ids = explode(",", $this->dbh->escape_string(trim($_REQUEST["ids"])));
foreach ($ids as $id) {
if ($_SESSION["profile"] != $id) {
$this->dbh->query("DELETE FROM ttrss_settings_profiles WHERE id = '$id' AND
owner_uid = " . $_SESSION["uid"]);
}
}
}
// Silent
function addprofile() {
$title = $this->dbh->escape_string(trim($_REQUEST["title"]));
if ($title) {
$this->dbh->query("BEGIN");
$result = $this->dbh->query("SELECT id FROM ttrss_settings_profiles
WHERE title = '$title' AND owner_uid = " . $_SESSION["uid"]);
if ($this->dbh->num_rows($result) == 0) {
$this->dbh->query("INSERT INTO ttrss_settings_profiles (title, owner_uid)
VALUES ('$title', ".$_SESSION["uid"] .")");
$result = $this->dbh->query("SELECT id FROM ttrss_settings_profiles WHERE
title = '$title'");
if ($this->dbh->num_rows($result) != 0) {
$profile_id = $this->dbh->fetch_result($result, 0, "id");
if ($profile_id) {
initialize_user_prefs($_SESSION["uid"], $profile_id);
}
}
}
$this->dbh->query("COMMIT");
}
}
// Silent
function saveprofile() {
$id = $this->dbh->escape_string($_REQUEST["id"]);
$title = $this->dbh->escape_string(trim($_REQUEST["value"]));
if ($id == 0) {
print __("Default profile");
return;
}
if ($title) {
$this->dbh->query("BEGIN");
$result = $this->dbh->query("SELECT id FROM ttrss_settings_profiles
WHERE title = '$title' AND owner_uid =" . $_SESSION["uid"]);
if ($this->dbh->num_rows($result) == 0) {
$this->dbh->query("UPDATE ttrss_settings_profiles
SET title = '$title' WHERE id = '$id' AND
owner_uid = " . $_SESSION["uid"]);
print $title;
} else {
$result = $this->dbh->query("SELECT title FROM ttrss_settings_profiles
WHERE id = '$id' AND owner_uid =" . $_SESSION["uid"]);
print $this->dbh->fetch_result($result, 0, "title");
}
$this->dbh->query("COMMIT");
}
}
// Silent
function remarchive() {
$ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
foreach ($ids as $id) {
$result = $this->dbh->query("DELETE FROM ttrss_archived_feeds WHERE
(SELECT COUNT(*) FROM ttrss_user_entries
WHERE orig_feed_id = '$id') = 0 AND
id = '$id' AND owner_uid = ".$_SESSION["uid"]);
$this->dbh->affected_rows($result);
}
}
function addfeed() {
$feed = $this->dbh->escape_string($_REQUEST['feed']);
$cat = $this->dbh->escape_string($_REQUEST['cat']);
$login = $this->dbh->escape_string($_REQUEST['login']);
$pass = trim($_REQUEST['pass']); // escaped later
$rc = subscribe_to_feed($feed, $cat, $login, $pass);
print json_encode(array("result" => $rc));
}
function togglepref() {
$key = $this->dbh->escape_string($_REQUEST["key"]);
set_pref($key, !get_pref($key));
$value = get_pref($key);
print json_encode(array("param" =>$key, "value" => $value));
}
function setpref() {
// set_pref escapes input, so no need to double escape it here
$key = $_REQUEST['key'];
$value = str_replace("\n", "<br/>", $_REQUEST['value']);
set_pref($key, $value, $_SESSION['uid'], $key != 'USER_STYLESHEET');
print json_encode(array("param" =>$key, "value" => $value));
}
function mark() {
$mark = $_REQUEST["mark"];
$id = $this->dbh->escape_string($_REQUEST["id"]);
if ($mark == "1") {
$mark = "true";
} else {
$mark = "false";
}
$this->dbh->query("UPDATE ttrss_user_entries SET marked = $mark,
last_marked = NOW()
WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
print json_encode(array("message" => "UPDATE_COUNTERS"));
}
function delete() {
$ids = $this->dbh->escape_string($_REQUEST["ids"]);
$this->dbh->query("DELETE FROM ttrss_user_entries
WHERE ref_id IN ($ids) AND owner_uid = " . $_SESSION["uid"]);
purge_orphans();
print json_encode(array("message" => "UPDATE_COUNTERS"));
}
function unarchive() {
$ids = explode(",", $_REQUEST["ids"]);
foreach ($ids as $id) {
$id = $this->dbh->escape_string(trim($id));
$this->dbh->query("BEGIN");
$result = $this->dbh->query("SELECT feed_url,site_url,title FROM ttrss_archived_feeds
WHERE id = (SELECT orig_feed_id FROM ttrss_user_entries WHERE ref_id = $id
AND owner_uid = ".$_SESSION["uid"].")");
if ($this->dbh->num_rows($result) != 0) {
$feed_url = $this->dbh->escape_string(db_fetch_result($result, 0, "feed_url"));
$site_url = $this->dbh->escape_string(db_fetch_result($result, 0, "site_url"));
$title = $this->dbh->escape_string(db_fetch_result($result, 0, "title"));
$result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE feed_url = '$feed_url'
AND owner_uid = " .$_SESSION["uid"]);
if ($this->dbh->num_rows($result) == 0) {
if (!$title) $title = '[Unknown]';
$result = $this->dbh->query(
"INSERT INTO ttrss_feeds
(owner_uid,feed_url,site_url,title,cat_id,auth_login,auth_pass,update_method)
VALUES (".$_SESSION["uid"].",
'$feed_url',
'$site_url',
'$title',
NULL, '', '', 0)");
$result = $this->dbh->query(
"SELECT id FROM ttrss_feeds WHERE feed_url = '$feed_url'
AND owner_uid = ".$_SESSION["uid"]);
if ($this->dbh->num_rows($result) != 0) {
$feed_id = $this->dbh->fetch_result($result, 0, "id");
}
} else {
$feed_id = $this->dbh->fetch_result($result, 0, "id");
}
if ($feed_id) {
$result = $this->dbh->query("UPDATE ttrss_user_entries
SET feed_id = '$feed_id', orig_feed_id = NULL
WHERE ref_id = $id AND owner_uid = " . $_SESSION["uid"]);
}
}
$this->dbh->query("COMMIT");
}
print json_encode(array("message" => "UPDATE_COUNTERS"));
}
function archive() {
$ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
foreach ($ids as $id) {
$this->archive_article($id, $_SESSION["uid"]);
}
print json_encode(array("message" => "UPDATE_COUNTERS"));
}
private function archive_article($id, $owner_uid) {
$this->dbh->query("BEGIN");
$result = $this->dbh->query("SELECT feed_id FROM ttrss_user_entries
WHERE ref_id = '$id' AND owner_uid = $owner_uid");
if ($this->dbh->num_rows($result) != 0) {
/* prepare the archived table */
$feed_id = (int) $this->dbh->fetch_result($result, 0, "feed_id");
if ($feed_id) {
$result = $this->dbh->query("SELECT id FROM ttrss_archived_feeds
WHERE id = '$feed_id'");
if ($this->dbh->num_rows($result) == 0) {
$this->dbh->query("INSERT INTO ttrss_archived_feeds
(id, owner_uid, title, feed_url, site_url)
SELECT id, owner_uid, title, feed_url, site_url from ttrss_feeds
WHERE id = '$feed_id'");
}
$this->dbh->query("UPDATE ttrss_user_entries
SET orig_feed_id = feed_id, feed_id = NULL
WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
}
}
$this->dbh->query("COMMIT");
}
function publ() {
$pub = $_REQUEST["pub"];
$id = $this->dbh->escape_string($_REQUEST["id"]);
if ($pub == "1") {
$pub = "true";
} else {
$pub = "false";
}
$this->dbh->query("UPDATE ttrss_user_entries SET
published = $pub, last_published = NOW()
WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
$pubsub_result = false;
if (PUBSUBHUBBUB_HUB) {
$rss_link = get_self_url_prefix() .
"/public.php?op=rss&id=-2&key=" .
get_feed_access_key(-2, false);
$p = new Publisher(PUBSUBHUBBUB_HUB);
$pubsub_result = $p->publish_update($rss_link);
}
print json_encode(array("message" => "UPDATE_COUNTERS",
"pubsub_result" => $pubsub_result));
}
function getAllCounters() {
$last_article_id = (int) $_REQUEST["last_article_id"];
$reply = array();
if (!empty($_REQUEST['seq'])) $reply['seq'] = (int) $_REQUEST['seq'];
if ($last_article_id != getLastArticleId()) {
$reply['counters'] = getAllCounters();
}
$reply['runtime-info'] = make_runtime_info();
print json_encode($reply);
}
/* GET["cmode"] = 0 - mark as read, 1 - as unread, 2 - toggle */
function catchupSelected() {
$ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
$cmode = sprintf("%d", $_REQUEST["cmode"]);
catchupArticlesById($ids, $cmode);
print json_encode(array("message" => "UPDATE_COUNTERS", "ids" => $ids));
}
function markSelected() {
$ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
$cmode = sprintf("%d", $_REQUEST["cmode"]);
$this->markArticlesById($ids, $cmode);
print json_encode(array("message" => "UPDATE_COUNTERS"));
}
function publishSelected() {
$ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
$cmode = sprintf("%d", $_REQUEST["cmode"]);
$this->publishArticlesById($ids, $cmode);
print json_encode(array("message" => "UPDATE_COUNTERS"));
}
function sanityCheck() {
$_SESSION["hasAudio"] = $_REQUEST["hasAudio"] === "true";
$_SESSION["hasSandbox"] = $_REQUEST["hasSandbox"] === "true";
$_SESSION["hasMp3"] = $_REQUEST["hasMp3"] === "true";
$_SESSION["clientTzOffset"] = $_REQUEST["clientTzOffset"];
$reply = array();
$reply['error'] = sanity_check();
if ($reply['error']['code'] == 0) {
$reply['init-params'] = make_init_params();
$reply['runtime-info'] = make_runtime_info();
}
print json_encode($reply);
}
function completeLabels() {
$search = $this->dbh->escape_string($_REQUEST["search"]);
$result = $this->dbh->query("SELECT DISTINCT caption FROM
ttrss_labels2
WHERE owner_uid = '".$_SESSION["uid"]."' AND
LOWER(caption) LIKE LOWER('$search%') ORDER BY caption
LIMIT 5");
print "<ul>";
while ($line = $this->dbh->fetch_assoc($result)) {
print "<li>" . $line["caption"] . "</li>";
}
print "</ul>";
}
function purge() {
$ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
$days = sprintf("%d", $_REQUEST["days"]);
foreach ($ids as $id) {
$result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE
id = '$id' AND owner_uid = ".$_SESSION["uid"]);
if ($this->dbh->num_rows($result) == 1) {
purge_feed($id, $days);
}
}
}
function updateFeedBrowser() {
$search = $this->dbh->escape_string($_REQUEST["search"]);
$limit = $this->dbh->escape_string($_REQUEST["limit"]);
$mode = (int) $this->dbh->escape_string($_REQUEST["mode"]);
require_once "feedbrowser.php";
print json_encode(array("content" =>
make_feed_browser($search, $limit, $mode),
"mode" => $mode));
}
// Silent
function massSubscribe() {
$payload = json_decode($_REQUEST["payload"], false);
$mode = $_REQUEST["mode"];
if (!$payload || !is_array($payload)) return;
if ($mode == 1) {
foreach ($payload as $feed) {
$title = $this->dbh->escape_string($feed[0]);
$feed_url = $this->dbh->escape_string($feed[1]);
$result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE
feed_url = '$feed_url' AND owner_uid = " . $_SESSION["uid"]);
if ($this->dbh->num_rows($result) == 0) {
$result = $this->dbh->query("INSERT INTO ttrss_feeds
(owner_uid,feed_url,title,cat_id,site_url)
VALUES ('".$_SESSION["uid"]."',
'$feed_url', '$title', NULL, '')");
}
}
} else if ($mode == 2) {
// feed archive
foreach ($payload as $id) {
$result = $this->dbh->query("SELECT * FROM ttrss_archived_feeds
WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]);
if ($this->dbh->num_rows($result) != 0) {
$site_url = $this->dbh->escape_string(db_fetch_result($result, 0, "site_url"));
$feed_url = $this->dbh->escape_string(db_fetch_result($result, 0, "feed_url"));
$title = $this->dbh->escape_string(db_fetch_result($result, 0, "title"));
$result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE
feed_url = '$feed_url' AND owner_uid = " . $_SESSION["uid"]);
if ($this->dbh->num_rows($result) == 0) {
$result = $this->dbh->query("INSERT INTO ttrss_feeds
(owner_uid,feed_url,title,cat_id,site_url)
VALUES ('$id','".$_SESSION["uid"]."',
'$feed_url', '$title', NULL, '$site_url')");
}
}
}
}
}
function catchupFeed() {
$feed_id = $this->dbh->escape_string($_REQUEST['feed_id']);
$is_cat = $this->dbh->escape_string($_REQUEST['is_cat']) == "true";
$mode = $this->dbh->escape_string($_REQUEST['mode']);
catchup_feed($feed_id, $is_cat, false, false, $mode);
print json_encode(array("message" => "UPDATE_COUNTERS"));
}
function quickAddCat() {
$cat = $this->dbh->escape_string($_REQUEST["cat"]);
add_feed_category($cat);
$result = $this->dbh->query("SELECT id FROM ttrss_feed_categories WHERE
title = '$cat' AND owner_uid = " . $_SESSION["uid"]);
if ($this->dbh->num_rows($result) == 1) {
$id = $this->dbh->fetch_result($result, 0, "id");
} else {
$id = 0;
}
print_feed_cat_select("cat_id", $id, '');
}
function setpanelmode() {
$wide = (int) $_REQUEST["wide"];
setcookie("ttrss_widescreen", $wide,
time() + COOKIE_LIFETIME_LONG);
print json_encode(array("wide" => $wide));
}
static function updaterandomfeed_real($dbh) {
// Test if the feed need a update (update interval exceded).
if (DB_TYPE == "pgsql") {
$update_limit_qpart = "AND ((
ttrss_feeds.update_interval = 0
AND ttrss_feeds.last_updated < NOW() - CAST((ttrss_user_prefs.value || ' minutes') AS INTERVAL)
) OR (
ttrss_feeds.update_interval > 0
AND ttrss_feeds.last_updated < NOW() - CAST((ttrss_feeds.update_interval || ' minutes') AS INTERVAL)
) OR ttrss_feeds.last_updated IS NULL
OR last_updated = '1970-01-01 00:00:00')";
} else {
$update_limit_qpart = "AND ((
ttrss_feeds.update_interval = 0
AND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL CONVERT(ttrss_user_prefs.value, SIGNED INTEGER) MINUTE)
) OR (
ttrss_feeds.update_interval > 0
AND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL ttrss_feeds.update_interval MINUTE)
) OR ttrss_feeds.last_updated IS NULL
OR last_updated = '1970-01-01 00:00:00')";
}
// Test if feed is currently being updated by another process.
if (DB_TYPE == "pgsql") {
$updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < NOW() - INTERVAL '5 minutes')";
} else {
$updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < DATE_SUB(NOW(), INTERVAL 5 MINUTE))";
}
$random_qpart = sql_random_function();
// we could be invoked from public.php with no active session
if ($_SESSION["uid"]) {
$owner_check_qpart = "AND ttrss_feeds.owner_uid = '".$_SESSION["uid"]."'";
} else {
$owner_check_qpart = "";
}
// We search for feed needing update.
$result = $dbh->query("SELECT ttrss_feeds.feed_url,ttrss_feeds.id
FROM
ttrss_feeds, ttrss_users, ttrss_user_prefs
WHERE
ttrss_feeds.owner_uid = ttrss_users.id
AND ttrss_users.id = ttrss_user_prefs.owner_uid
AND ttrss_user_prefs.pref_name = 'DEFAULT_UPDATE_INTERVAL'
$owner_check_qpart
$update_limit_qpart
$updstart_thresh_qpart
ORDER BY $random_qpart LIMIT 30");
$feed_id = -1;
require_once "rssfuncs.php";
$num_updated = 0;
$tstart = time();
while ($line = $dbh->fetch_assoc($result)) {
$feed_id = $line["id"];
if (time() - $tstart < ini_get("max_execution_time") * 0.7) {
update_rss_feed($feed_id, true);
++$num_updated;
} else {
break;
}
}
// Purge orphans and cleanup tags
purge_orphans();
cleanup_tags(14, 50000);
if ($num_updated > 0) {
print json_encode(array("message" => "UPDATE_COUNTERS",
"num_updated" => $num_updated));
} else {
print json_encode(array("message" => "NOTHING_TO_UPDATE"));
}
}
function updaterandomfeed() {
RPC::updaterandomfeed_real($this->dbh);
}
private function markArticlesById($ids, $cmode) {
$tmp_ids = array();
foreach ($ids as $id) {
array_push($tmp_ids, "ref_id = '$id'");
}
$ids_qpart = join(" OR ", $tmp_ids);
if ($cmode == 0) {
$this->dbh->query("UPDATE ttrss_user_entries SET
marked = false, last_marked = NOW()
WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
} else if ($cmode == 1) {
$this->dbh->query("UPDATE ttrss_user_entries SET
marked = true, last_marked = NOW()
WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
} else {
$this->dbh->query("UPDATE ttrss_user_entries SET
marked = NOT marked,last_marked = NOW()
WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
}
}
private function publishArticlesById($ids, $cmode) {
$tmp_ids = array();
foreach ($ids as $id) {
array_push($tmp_ids, "ref_id = '$id'");
}
$ids_qpart = join(" OR ", $tmp_ids);
if ($cmode == 0) {
$this->dbh->query("UPDATE ttrss_user_entries SET
published = false,last_published = NOW()
WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
} else if ($cmode == 1) {
$this->dbh->query("UPDATE ttrss_user_entries SET
published = true,last_published = NOW()
WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
} else {
$this->dbh->query("UPDATE ttrss_user_entries SET
published = NOT published,last_published = NOW()
WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
}
if (PUBSUBHUBBUB_HUB) {
$rss_link = get_self_url_prefix() .
"/public.php?op=rss&id=-2&key=" .
get_feed_access_key(-2, false);
$p = new Publisher(PUBSUBHUBBUB_HUB);
/* $pubsub_result = */ $p->publish_update($rss_link);
}
}
function getlinktitlebyid() {
$id = $this->dbh->escape_string($_REQUEST['id']);
$result = $this->dbh->query("SELECT link, title FROM ttrss_entries, ttrss_user_entries
WHERE ref_id = '$id' AND ref_id = id AND owner_uid = ". $_SESSION["uid"]);
if ($this->dbh->num_rows($result) != 0) {
$link = $this->dbh->fetch_result($result, 0, "link");
$title = $this->dbh->fetch_result($result, 0, "title");
echo json_encode(array("link" => $link, "title" => $title));
} else {
echo json_encode(array("error" => "ARTICLE_NOT_FOUND"));
}
}
function log() {
$logmsg = $this->dbh->escape_string($_REQUEST['logmsg']);
if ($logmsg) {
Logger::get()->log_error(E_USER_WARNING,
$logmsg, '[client-js]', 0, false);
}
echo json_encode(array("message" => "HOST_ERROR_LOGGED"));
}
}
?>

View file

@ -1,64 +0,0 @@
<?php
/* @class ttrssMailer
* @brief A TTRSS extension to the PHPMailer class
* Configures default values through the __construct() function
* @author Derek Murawsky
* @version .1 (alpha)
*
*/
require_once 'lib/phpmailer/class.phpmailer.php';
require_once "config.php";
class ttrssMailer extends PHPMailer {
//define all items that we want to override with defaults in PHPMailer
public $From = SMTP_FROM_ADDRESS;
public $FromName = SMTP_FROM_NAME;
public $CharSet = "UTF-8";
public $PluginDir = "lib/phpmailer/";
public $ContentType = "text/html"; //default email type is HTML
function __construct() {
$this->SetLanguage("en", "lib/phpmailer/language/");
if (SMTP_SERVER) {
$pair = explode(":", SMTP_SERVER, 2);
$this->Mailer = "smtp";
$this->Host = $pair[0];
$this->Port = $pair[1];
if (!$this->Port) $this->Port = 25;
} else {
$this->Host = '';
$this->Port = '';
}
//if SMTP_LOGIN is specified, set credentials and enable auth
if(SMTP_LOGIN){
$this->SMTPAuth = true;
$this->Username = SMTP_LOGIN;
$this->Password = SMTP_PASSWORD;
}
if(SMTP_SECURE)
$this->SMTPSecure = SMTP_SECURE;
}
/* @brief a simple mail function to send email using the defaults
* This will send an HTML email using the configured defaults
* @param $toAddress A string with the recipients email address
* @param $toName A string with the recipients name
* @param $subject A string with the emails subject
* @param $body A string containing the body of the email
*/
public function quickMail ($toAddress, $toName, $subject, $body, $altbody=""){
$this->addAddress($toAddress, $toName);
$this->Subject = $subject;
$this->Body = $body;
$this->IsHTML($altbody != '');
$rc=$this->send();
return $rc;
}
}
?>

View file

@ -1,215 +0,0 @@
<?php
// *******************************************
// *** Database configuration (important!) ***
// *******************************************
define('DB_TYPE', "pgsql"); // or mysql
define('DB_HOST', "localhost");
define('DB_USER', "fox");
define('DB_NAME', "fox");
define('DB_PASS', "XXXXXX");
define('DB_PORT', ''); // usually 5432 for PostgreSQL, 3306 for MySQL
define('MYSQL_CHARSET', 'UTF8');
// Connection charset for MySQL. If you have a legacy database and/or experience
// garbage unicode characters with this option, try setting it to a blank string.
// ***********************************
// *** Basic settings (important!) ***
// ***********************************
define('SELF_URL_PATH', 'http://example.org/tt-rss/');
// Full URL of your tt-rss installation. This should be set to the
// location of tt-rss directory, e.g. http://example.org/tt-rss/
// You need to set this option correctly otherwise several features
// including PUSH, bookmarklets and browser integration will not work properly.
define('FEED_CRYPT_KEY', '');
// Key used for encryption of passwords for password-protected feeds
// in the database. A string of 24 random characters. If left blank, encryption
// is not used. Requires mcrypt functions.
// Warning: changing this key will make your stored feed passwords impossible
// to decrypt.
define('SINGLE_USER_MODE', false);
// Operate in single user mode, disables all functionality related to
// multiple users and authentication. Enabling this assumes you have
// your tt-rss directory protected by other means (e.g. http auth).
define('SIMPLE_UPDATE_MODE', false);
// Enables fallback update mode where tt-rss tries to update feeds in
// background while tt-rss is open in your browser.
// If you don't have a lot of feeds and don't want to or can't run
// background processes while not running tt-rss, this method is generally
// viable to keep your feeds up to date.
// Still, there are more robust (and recommended) updating methods
// available, you can read about them here: http://tt-rss.org/wiki/UpdatingFeeds
// *****************************
// *** Files and directories ***
// *****************************
define('PHP_EXECUTABLE', '/usr/bin/php');
// Path to PHP *COMMAND LINE* executable, used for various command-line tt-rss
// programs and update daemon. Do not try to use CGI binary here, it won't work.
// If you see HTTP headers being displayed while running tt-rss scripts,
// then most probably you are using the CGI binary. If you are unsure what to
// put in here, ask your hosting provider.
define('LOCK_DIRECTORY', 'lock');
// Directory for lockfiles, must be writable to the user you run
// daemon process or cronjobs under.
define('CACHE_DIR', 'cache');
// Local cache directory for RSS feed content.
define('ICONS_DIR', "feed-icons");
define('ICONS_URL', "feed-icons");
// Local and URL path to the directory, where feed favicons are stored.
// Unless you really know what you're doing, please keep those relative
// to tt-rss main directory.
// **********************
// *** Authentication ***
// **********************
// Please see PLUGINS below to configure various authentication modules.
define('AUTH_AUTO_CREATE', true);
// Allow authentication modules to auto-create users in tt-rss internal
// database when authenticated successfully.
define('AUTH_AUTO_LOGIN', true);
// Automatically login user on remote or other kind of externally supplied
// authentication, otherwise redirect to login form as normal.
// If set to true, users won't be able to set application language
// and settings profile.
// *********************
// *** Feed settings ***
// *********************
define('FORCE_ARTICLE_PURGE', 0);
// When this option is not 0, users ability to control feed purging
// intervals is disabled and all articles (which are not starred)
// older than this amount of days are purged.
// *** PubSubHubbub settings ***
define('PUBSUBHUBBUB_HUB', '');
// URL to a PubSubHubbub-compatible hub server. If defined, "Published
// articles" generated feed would automatically become PUSH-enabled.
define('PUBSUBHUBBUB_ENABLED', false);
// Enable client PubSubHubbub support in tt-rss. When disabled, tt-rss
// won't try to subscribe to PUSH feed updates.
// ****************************
// *** Sphinx search plugin ***
// ****************************
define('SPHINX_SERVER', 'localhost:9312');
// Hostname:port combination for the Sphinx server.
define('SPHINX_INDEX', 'ttrss, delta');
// Index name in Sphinx configuration. You can specify multiple indexes
// as a comma-separated string.
// Example configuration files are available on tt-rss wiki.
// ***********************************
// *** Self-registrations by users ***
// ***********************************
define('ENABLE_REGISTRATION', false);
// Allow users to register themselves. Please be aware that allowing
// random people to access your tt-rss installation is a security risk
// and potentially might lead to data loss or server exploit. Disabled
// by default.
define('REG_NOTIFY_ADDRESS', 'user@your.domain.dom');
// Email address to send new user notifications to.
define('REG_MAX_USERS', 10);
// Maximum amount of users which will be allowed to register on this
// system. 0 - no limit.
// **********************************
// *** Cookies and login sessions ***
// **********************************
define('SESSION_COOKIE_LIFETIME', 86400);
// Default lifetime of a session (e.g. login) cookie. In seconds,
// 0 means cookie will be deleted when browser closes.
define('SESSION_CHECK_ADDRESS', 1);
// Check client IP address when validating session:
// 0 - disable checking
// 1 - check first 3 octets of an address (recommended)
// 2 - check first 2 octets of an address
// 3 - check entire address
// *********************************
// *** Email and digest settings ***
// *********************************
define('SMTP_FROM_NAME', 'Tiny Tiny RSS');
define('SMTP_FROM_ADDRESS', 'noreply@your.domain.dom');
// Name, address and subject for sending outgoing mail. This applies
// to password reset notifications, digest emails and any other mail.
define('DIGEST_SUBJECT', '[tt-rss] New headlines for last 24 hours');
// Subject line for email digests
define('SMTP_SERVER', '');
// Hostname:port combination to send outgoing mail (i.e. localhost:25).
// Blank - use system MTA.
define('SMTP_LOGIN', '');
define('SMTP_PASSWORD', '');
// These two options enable SMTP authentication when sending
// outgoing mail. Only used with SMTP_SERVER.
define('SMTP_SECURE', '');
// Used to select a secure SMTP connection. Allowed values: ssl, tls,
// or empty.
// ***************************************
// *** Other settings (less important) ***
// ***************************************
define('CHECK_FOR_NEW_VERSION', true);
// Check for new versions of tt-rss automatically.
define('DETECT_ARTICLE_LANGUAGE', false);
// Detect article language when updating feeds, presently this is only
// used for hyphenation. This may increase amount of CPU time used by
// update processes, disable if necessary (i.e. you are being billed
// for CPU time).
define('ENABLE_GZIP_OUTPUT', false);
// Selectively gzip output to improve wire performance. This requires
// PHP Zlib extension on the server.
// Enabling this can break tt-rss in several httpd/php configurations,
// if you experience weird errors and tt-rss failing to start, blank pages
// after login, or content encoding errors, disable it.
define('PLUGINS', 'auth_internal, note, updater');
// Comma-separated list of plugins to load automatically for all users.
// System plugins have to be specified here. Please enable at least one
// authentication plugin here (auth_*).
// Users may enable other user plugins from Preferences/Plugins but may not
// disable plugins specified in this list.
// Disabling auth_internal in this list would automatically disable
// reset password link on the login form.
define('LOG_DESTINATION', 'sql');
// Log destination to use. Possible values: sql (uses internal logging
// you can read in Preferences -> System), syslog - logs to system log.
// Setting this to blank uses PHP logging (usually to http server
// error.log).
define('CONFIG_VERSION', 26);
// Expected config version. Please update this option in config.php
// if necessary (after migrating all new options from this file).
// vim:ft=php

View file

@ -1,400 +0,0 @@
div.cdmHeader img, div.cdmHeader input, div.cdmFooter img {
vertical-align : middle;
}
div.cdmHeader {
display : table;
}
div.cdmHeader > * {
display : table-cell;
padding : 5px;
}
div.cdmHeader > div {
white-space : nowrap;
}
div.cdmHeader > span {
width : 100%;
}
div.cdmHeader span.updated {
color : #555;
font-weight : normal;
font-size : 11px;
white-space : nowrap;
vertical-align : middle;
}
div.cdmHeader input {
margin-right : 5px;
}
div.cdmHeader div.updPic {
width : 25px;
display : inline-block;
text-align : center;
}
div.cdmHeader div.updPic img {
vertical-align : middle;
}
div.cdmHeader img, div.cdmFooter img {
margin : 0px 4px;
}
div.cdmHeader input {
margin-left : 4px;
margin-right : 4px;
}
div.cdmContentInner {
margin : 10px;
line-height : 1.5;
font-size : 15px;
}
div.cdmContentInner img {
border-width : 0px;
max-width : 98%;
height : auto;
}
div.cdmContentInner h1 {
font-size : 16px;
}
div.cdmContentInner h2,
div.cdmContentInner h3,
div.cdmContentInner h4 {
font-size : 15px;
}
div.cdmFooter {
padding : 5px;
font-weight : normal;
color : #555;
clear : both;
}
div.cdm {
margin-right : 4px;
}
div.cdm.expanded {
margin-top : 4px;
margin-bottom : 4px;
}
div.cdm.expanded div.cdmFooter {
border-style : solid;
border-width : 0px 0px 1px 0px;
border-color : #ddd;
}
div.cdm.expandable {
background-color : #f0f0f0;
border-width : 0px 0px 1px 0px;
border-color : #ddd;
border-style : solid;
}
div.cdm.expandable > hr {
display : none;
}
div.cdm.expanded > hr {
margin-top : 0px;
margin-bottom : 0px;
}
div.cdm.expandable.Unread {
background : white;
}
div.cdm.expandable.Selected {
background : #f9fbff;
}
div.cdm.expandable.active {
background : white ! important;
}
div.cdm.expandable div.cdmHeader span.titleWrap {
white-space : nowrap;
text-overflow : ellipsis;
overflow : hidden;
max-width : 500px;
}
div.cdm.expandable.active div.cdmHeader span.titleWrap {
white-space : normal;
}
div.cdm.expandable div.cdmHeader a.title {
font-weight : 600;
color : #555;
font-size : 14px;
-webkit-transition : color 0.2s;
transition : color 0.2s;
text-rendering: optimizelegibility;
font-family : "Segoe WP Semibold", "Segoe UI Semibold",
"Segoe UI Web Semibold", "Segoe UI", Ubuntu, "DejaVu Sans", "Helvetica Neue",
Helvetica, Arial, sans-serif;
}
div.cdm.expandable.Unread div.cdmHeader a.title {
color : black;
}
div.cdm.expandable.active div.cdmHeader a.title {
color : #4684ff;
font-size : 16px;
font-weight : 600;
text-rendering: optimizelegibility;
font-family : "Segoe WP Semibold", "Segoe UI Semibold",
"Segoe UI Web Semibold", "Segoe UI", Ubuntu, "DejaVu Sans", "Helvetica Neue",
Helvetica, Arial, sans-serif;
}
div.cdm.expanded div.cdmHeader {
background : transparent ! important;
}
div.cdm.expanded div.cdmHeader a.title {
font-size : 16px;
color : #999;
font-weight : 600;
-webkit-transition : color 0.2s;
transition : color 0.2s;
text-rendering: optimizelegibility;
font-family : "Segoe WP Semibold", "Segoe UI Semibold",
"Segoe UI Web Semibold", "Segoe UI", Ubuntu, "DejaVu Sans", "Helvetica Neue",
Helvetica, Arial, sans-serif;
}
div.cdm.expanded.active {
background : white;
}
div.cdm.expanded.active div.cdmHeader a.title {
color : #4684ff;
}
div.cdm.expanded.Unread div.cdmHeader a.title {
color : black;
}
div.cdm.expanded div.cdmContent {
color : #555;
}
div.cdm.expanded.Unread div.cdmContent {
color : black;
}
div.cdm.active div.cdmContent {
color : black;
}
span.cdmExcerpt {
font-size : 11px;
color : #999;
font-weight : normal;
cursor : pointer;
}
div.cdmContent div.postEnclosures {
margin-top : 1em;
color : #555;
}
div.cdmFeedTitle {
border-color : #a0a0a0;
border-width : 0px 0px 1px 0px;
border-style : solid;
padding : 5px 3px 5px 5px;
background : url("../images/toolbar.png") bottom left;
background-repeat : repeat-x;
}
div.cdmFeedTitle a.title {
color : #555;
font-style : italic;
font-weight : bold;
}
div.cdmFeedTitle a {
color : #555;
}
div.cdmFeedTitle a:hover {
color : #4684ff;
}
div.cdmHeader span.hlFeed {
float : right;
font-weight : normal;
font-style : italic;
}
div.cdmHeader div.hlFeed, div.cdmHeader div.hlFeed a {
vertical-align : middle;
color : #555;
font-weight : normal;
font-style : italic;
font-size : 11px;
}
div.cdm .hlFeed a {
border-radius : 4px;
display : inline-block;
padding : 1px 4px 1px 4px;
}
div.cdmContentInner p {
max-width : 650px;
-webkit-hyphens: auto;
-moz-hyphens: auto;
hyphens: auto;
}
div.cdmContentInner iframe {
min-width : 50%;
}
div.cdmHeader span.author {
color : #555;
font-size : 11px;
font-weight : normal;
}
div#floatingTitle {
position : absolute;
z-index : 5;
top : 0px;
right : 0px;
left : 0px;
border-color : #ddd;
border-width : 0px 0px 1px 0px;
border-style : solid;
background : white;
color : #555;
box-shadow : 0px 1px 1px -1px rgba(0,0,0,0.1);
}
div#floatingTitle > * {
display : table-cell;
white-space : nowrap;
vertical-align : middle;
padding : 9px 5px;
}
div#floatingTitle img {
margin-right : 4px;
margin-left : 4px;
}
div#floatingTitle span.author {
color : #555;
font-size : 11px;
font-weight : normal;
}
div#floatingTitle a.title {
font-size : 16px;
color : #999;
-webkit-transition : color 0.2s;
transition : color 0.2s;
font-weight : 600;
text-rendering: optimizelegibility;
font-family : "Segoe WP Semibold", "Segoe UI Semibold",
"Segoe UI Web Semibold", "Segoe UI", Ubuntu, "DejaVu Sans", "Helvetica Neue",
Helvetica, Arial, sans-serif;
}
div#floatingTitle.Unread a.title {
color : black;
}
div#floatingTitle img.anchor {
margin-left : 0px;
}
div#floatingTitle div.hlFeed {
padding-right : 10px;
color : #555;
font-weight : normal;
font-style : italic;
font-size : 11px;
white-space : nowrap;
}
div#floatingTitle div.hlFeed a {
border-radius : 4px;
display : inline-block;
padding : 1px 4px 1px 4px;
}
div#floatingTitle span.updated {
padding-right : 10px;
white-space : nowrap;
color : #555;
font-size : 11px;
}
div#floatingTitle div.hlFeed a {
color : #555;
}
div#floatingTitle span.titleWrap {
width : 100%;
white-space : normal;
}
div#floatingTitle .dijit,
div#floatingTitle img.hlScorePic {
display : none;
}
.cdm.high .cdmHeader a.title.high, .cdm.high .cdmHeader .cdmExcerpt,
.cdm.high .cdmHeader span.author {
color : #00aa00;
}
.cdm.Unread.high .cdmHeader a.title.high, .cdm.Unread.high .cdmHeader .cdmExcerpt,
.cdm.Unread.high .cdmHeader span.author {
color : #00dd00;
}
.cdm .cdmHeader a.title.low, .cdm.low .cdmHeader .cdmExcerpt,
.cdm.Unread .cdmHeader a.title.low, .cdm.Unread.low .cdmHeader .cdmExcerpt,
.cdm.low .cdmHeader span.author {
color : #909090;
text-decoration : line-through;
}
div.cdmFeedTitle > * {
display : table-cell;
vertical-align : middle;
}
div.cdmFeedTitle a.title {
width : 100%;
}
div.cdmFeedTitle a.catchup {
text-align : right;
color : #555;
padding-right : 10px;
font-size : 11px;
white-space : nowrap;
}
div.cdmFeedTitle a.catchup:hover {
color : #4684ff;
}

View file

@ -1,413 +0,0 @@
/* Tree */
.claro .dijitTreeRow .dijitCheckBox {
position : relative;
top : -2px;
}
.claro .dijitTreeLabel {
outline : 0;
}
.claro .dijitTree .feedParam {
color : #555;
float : right;
margin-right : 1em;
}
.claro .dijitTree .filterRules {
display : block;
color : #ccc;
font-size : 10px;
margin-left : 100px;
}
.claro .dijitTree .filterRules span {
display : block;
color : green;
}
#filterDlg_Matches span.filterRule {
color : green;
}
.claro .dijitTree .filterRules span.inverse,
#filterDlg_Matches span.filterRule.inverse {
color : red;
}
.claro .dijitTree .labelParam {
float : right;
margin-right : 1em;
}
.claro .dijitTree .dijitTreeLabel.Disabled,
.claro .dijitTree .labelParam.Disabled {
color : #555;
}
.claro .dijitTreeRow.Error {
color : red;
}
.claro .dijitTreeRow.Hidden {
display : none;
}
.claro .dijitTreeNode .loadingNode {
margin-left : 3px;
height : 9px;
}
.claro .dijitFolderClosed,
.claro .dijitFolderOpened {
display : none;
}
.claro .dijitTreeNode .dijitCheckBox {
margin-left : 4px;
}
.claro .dijitTreeIsRoot > .dijitTreeRow > .dijitTreeExpando {
margin-left : 5px;
}
.claro .dijitTree .dijitTreeExpando {
margin-top : 0px;
opacity : 0.6;
}
.claro .dijitTree .dijitTreeNode {
padding : 0px;
border-width : 0px;
}
.claro .dijitTree .dijitTreeRow {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
.claro .dijitTree .dijitTreeRowSelected {
background : white;
}
.claro #feedTree.dijitTree .dijitTreeRowSelected {
box-shadow : -1px 0px 2px -1px rgba(0,0,0,0.1);
}
.claro .dijitTree .dijitTreeRowHover {
background : #f0f0f0;
border-color : #ddd;
}
.claro .dijitTree .dijitTreeRowSelected {
background : white;
border-color : #ddd;
}
.claro .dijitTreeRowSelected .dijitTreeLabel {
text-shadow : 1px 1px 2px #fff;
}
.claro .dijitTreeRow .dijitTreeExpando {
background-image: url("../images/treeExpandImages.png");
position : relative;
top : -1px;
}
.claro .dijitTreeRow .dijitTreeExpandoLeaf {
background : none;
}
/* Toolbar */
.claro .dijitToolbar {
background : #f5f5f5;
border-color : #ddd;
/* text-rendering: optimizelegibility;
font-family : "Segoe WP Semibold", "Segoe UI Semibold",
"Segoe UI Web Semibold", "Segoe UI", "Helvetica Neue",
Helvetica, Arial, sans-serif; */
}
/* .claro .dijitToolbar {
text-shadow : 1px 1px 2px #fff;
} */
.claro .dijitDialog .dijitToolbar {
border : 1px solid #ddd;
}
/* Dialog */
.claro .dijitDialog h2 {
margin-top : 0px;
margin-bottom : 4px;
border-width : 0px;
}
.claro .dijitMenuItemLabel {
font-size : 13px;
}
/* Checkbox */
.claro .dijitCheckBox {
background-image : url("../images/untick.png");
background-color : transparent;
width : 15px;
height : 15px;
margin : 1px;
opacity : 0.7;
background-position : center center;
transition : opacity 0.25s;
-webkit-transition : opacity 0.25s;
/* border : 1px solid #b5bcc7; */
padding : 1px;
}
.claro .dijitCheckBox:hover {
opacity : 1;
}
.claro .dijitCheckBox.dijitCheckBoxDisabled:hover {
opacity : 0.7;
}
.claro .dijitCheckBox.dijitCheckBoxChecked {
border-color : #69C671;
background-image : url("../images/tick.png");
opacity : 1;
}
/* Various buttons */
.claro .dijitButton .dijitButtonNode,
.claro .dijitComboButton .dijitButtonNode,
.claro .dijitToolbar .dijitDropDownButton .dijitButtonNode,
.claro .dijitToolbar .dijitComboButton,
.claro .dijitToolbar .dijitComboButton .dijitButtonNode {
background : none;
border-color : transparent;
box-shadow : none;
}
button,
input[type="submit"] {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size : 14px;
}
button,
input[type="submit"],
.claro .dijitButton,
.claro .dijitComboButton {
display: inline-block;
padding: 4px 12px;
margin-bottom: 0;
font-size: 14px;
line-height: 20px;
color: #333333;
text-align: center;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
vertical-align: middle;
cursor: pointer;
background-color: #f5f5f5;
background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
background-repeat: repeat-x;
border: 1px solid #cccccc;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
border-bottom-color: #b3b3b3;
-webkit-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
}
button:hover,
button:focus,
button:active,
input[type="submit"]:hover,
input[type="submit"]:focus,
input[type="submit"]:active,
.claro .dijitButton:hover,
.claro .dijitButton:focus,
.claro .dijitButton:active,
.claro .dijitComboButton:hover,
.claro .dijitComboButton:focus,
.claro .dijitComboButton:active,
.claro .dijitButton.dijitButtonDisabled {
color: #333333;
background-color: #e6e6e6;
}
button:active,
input[type="submit"]:active,
.claro .dijitButton:active,
.claro .dijitComboButton:active {
background-color: #cccccc \9;
}
.claro .dijitToolbar .dijitButton,
.claro .dijitToolbar .dijitButton.dijitHover,
.claro .dijitToolbar .dijitComboButton,
.claro .dijitToolbar .dijitComboButton.dijitHover {
background : none;
border-color : transparent;
box-shadow : none;
padding : 0px;
margin : 0px;
line-height : auto;
text-shadow : none;
}
.claro .dijitToolbar .dijitDropDownButton .dijitButtonText,
.claro .dijitToolbar .dijitComboButton .dijitButtonText {
padding : 0px;
}
.claro .dijitToolbar .dijitDropDownButton .dijitButtonNode {
border-radius : 4px;
}
.claro .dijitToolbar .dijitButton.dijitHover,
.claro .dijitToolbar .dijitDropDownButton.dijitHover .dijitButtonNode,
.claro .dijitToolbar .dijitComboButton.dijitHover {
border-color : #ccc;
}
.claro .dijitToolbar .dijitButton.dijitHover .dijitButtonNode,
.claro .dijitToolbar .dijitButton.dijitButtonActive .dijitButtonNode {
background : none;
}
.claro .dijitToolbar .dijitButton .dijitButtonContents,
.claro .dijitToolbar .dijitDropDownButton .dijitButtonContents,
.claro .dijitToolbar .dijitComboButton .dijitButtonContents {
font-size : 13px;
}
button:hover,
button:focus,
input[type="submit"]:hover,
input[type="submit"]:focus,
.claro .dijitButton:hover,
.claro .dijitToolbar .dijitButton:hover .dijitButtonNode,
.claro .dijitToolbar .dijitButton.dijitHover .dijitButtonNode,
.claro .dijitButton:focus,
.claro .dijitComboButton:hover,
.claro .dijitComboButton:focus {
color: #333333;
text-decoration: none;
background-position: 0 -15px;
-webkit-transition: background-position 0.1s linear;
transition: background-position 0.1s linear;
}
button:focus,
input[type="submit"]:focus,
.claro .dijitButton:focus,
.claro .dijitComboButton:focus {
outline: thin dotted #333;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
button:active,
input[type="submit"]:active,
.claro .dijitButton:active,
.claro .dijitComboButton:active,
.claro .dijitToolbar .dijitDropDownButton.dijitOpened,
.claro .dijitToolbar .dijitComboButton.dijitOpened,
.claro .dijitToolbar .dijitButton.dijitButtonActive .dijitButtonNode {
background-image: none;
outline: 0;
-webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
}
input[type="submit"][disabled],
button[disabled],
.claro .dijitButton[disabled],
.claro .dijitButton.dijitButtonDisabled,
.claro .dijitComboButton.dijitButtonDisabled {
cursor: default;
background-image: none;
opacity: 0.65;
filter: alpha(opacity=65);
-webkit-box-shadow: none;
box-shadow: none;
}
.claro .dijitButton .dijitButtonContents,
.claro .dijitComboButton .dijitButtonContents {
font-size : 14px;
font-weight : normal;
line-height : 20px;
}
.claro .dijitButton.small .dijitButtonText {
font-size : 11px;
}
.claro .dijitMenu {
border-color : #ccc;
}
.claro .dijitMenu .dijitMenuItem.dijitHover,
.claro .dijitMenu .dijitMenuItem.dijitFocused,
.claro .dijitMenuTable .dijitMenuItem.dijitHover .dijitMenuItemLabel,
.claro .dijitMenuTable .dijitMenuItem.dijitFocused .dijitMenuItemLabel {
background : #eee;
border-color : transparent;
}
.claro .dijitButton .dijitButtonNode,
.claro .dijitComboButton .dijitButtonNode {
padding : 0px;
}
/* Other stuff */
/* .claro .dijitAccordionTitleFocus {
text-shadow : 1px 1px 2px #fff;
}
.claro .dijitAccordionTitle {
text-rendering: optimizelegibility;
font-family : "Segoe WP Semibold", "Segoe UI Semibold",
"Segoe UI Web Semibold", "Segoe UI", "Helvetica Neue",
Helvetica, Arial, sans-serif;
} */
.claro .dijitAccordionInnerContainer.dijitAccordionInnerContainerSelected {
border-color : #ccc;
}
.claro .dijitAccordionContainer .dijitAccordionChildWrapper {
border-color : #ddd;
}
/* Tabs */
.claro .dijitTabContent {
background : #eee;
}
.claro .dijitTabContent.dijitTabChecked,
.claro .dijitTabContent.dijitTabHover,
.claro .dijitTabContent.dijitFocused {
background : white;
}
.claro .dijitTabPaneWrapper,
.claro .dijitTabContainerTop-tabs,
.claro .dijitTab,
.claro .dijitAccordionInnerContainer {
border-color : #ddd;
}

View file

@ -1,7 +0,0 @@
html, body#ttrssMain, body#ttrssPrefs, #main {
width: 100%;
height: 100%;
border: 0;
padding: 0;
margin: 0;
}

View file

@ -1,137 +0,0 @@
body#ttrssPrefs {
background-color : #f5f5f5;
}
body#ttrssPrefs #footer, body#ttrssPrefs #header {
background-color : #f5f5f5;
padding-left : 8px;
padding-right : 8px;
}
#header a:hover {
color : black;
}
#header img {
vertical-align : middle;
cursor : pointer;
}
div#pref-tabs .dijitContentPane {
font-size : 13px;
}
div#pref-tabs {
box-shadow : 0px 1px 1px -1px rgba(0,0,0,0.1);
margin : 0px 5px 0px 5px;
}
div#pref-tabs .dijitContentPane h3 {
font-size : 14px;
}
#pref-filter-wrap, #pref-filter-header, #pref-filter-content,
#pref-label-wrap, #pref-label-header, #pref-label-content,
#pref-user-wrap, #pref-user-header, #pref-user-content,
#pref-instance-wrap, #pref-instance-header, #pref-instance-content {
margin : 0px;
padding : 0px;
border-width : 0px;
}
#userConfigTab, #labelConfigTab, #filterConfigTab, #pref-feeds-feeds, #instanceConfigTab {
padding : 0px;
}
/* preferences */
table.prefPrefsList h3 {
margin-top : 0.5em;
margin-bottom : 0px;
}
tr.title td {
border-width : 0px 0px 1px 0px;
border-color : #ecf4ff;
border-style : solid;
color : #4684ff;
}
div.prefProfileHolder, div.prefFeedOPMLHolder, div.inactiveFeedHolder {
height : 300px;
overflow : auto;
border-width : 0px 1px 1px 1px;
border-color : #ddd;
border-style : solid;
margin : 0px 0px 5px 0px;
background-color : white;
}
div.filterTestHolder, div.prefFeedOPMLHolder {
border-width : 1px;
}
ul.selfUpdateList, ul.userFeedList {
height : 200px;
overflow : auto;
list-style-type : none;
border : 1px solid #ddd;
background-color : #f5f5f5;
margin : 0px 0px 5px 0px;
padding : 5px;
}
div#feedlistLoading, div#filterlistLoading, div#labellistLoading {
text-align : center;
padding : 5px;
color : #555;
}
div#feedlistLoading img, div#filterlistLoading img, div#labellistLoading {
margin-right : 5px;
}
#errorButton {
color : red;
}
a.bookmarklet {
color : #4684ff;
border : 1px solid #ddd;
background : #f5f5f5;
padding : 2px;
}
table.prefPluginsList td label, table.prefUserList td {
cursor : pointer;
}
table.prefPluginsList label {
white-space : nowrap;
}
table.prefPluginsList label img {
vertical-align : middle;
}
table.prefErrorLog tr.errrow td {
font-size : 10px;
}
table.prefErrorLog tr.errrow td.errno {
font-style : italic;
font-weight : bold;
white-space : nowrap;
}
table.prefErrorLog td.filename, table.prefErrorLog td.login, table.prefErrorLog td.timestamp {
color : #555;
}
body#ttrssPrefs hr {
border-color : #ecf4ff;
max-width : 100%;
}

File diff suppressed because it is too large Load diff

View file

@ -1,261 +0,0 @@
body {
background : #f5f5f5;
color : black;
padding : 0px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
margin-left : auto;
margin-right : auto;
max-width : 800px;
}
form {
margin : 10px 0px 0px 0px;
padding : 0px;
}
div.content {
overflow : hidden;
background : white;
border : 1px solid #ddd;
padding : 10px;
box-shadow : 0px 1px 1px -1px rgba(0,0,0,0.1);
}
p.warning {
color : red;
}
p.query, code {
color : green;
}
p.insensitive {
color : gray;
}
div.insensitive-small {
color : gray;
font-size : 10px;
}
.floatingLogo {
display : none;
}
a {
color : #4684ff;
text-decoration : none;
}
a:hover {
color : black;
}
div.notice, div.warning, div.error {
padding : 4px 10px 4px 4px;
display : inline-block;
margin : 2px 0px 4px 0px;
font-size : 12px;
border-style : solid;
border-color : #ccc;
border-width : 1px;
box-shadow : inset 0px 0px 2px rgba(0,0,0,0.1);
}
div.notice div.inner, div.warning div.inner, div.error div.inner {
vertical-align : middle;
}
div.notice {
background : #ecf4ff;
border-color : #88b0f0;
}
div.warning {
border-color : #EFDC88;
background : #fff7d5;
}
div.error {
background : #ffcccc;
border-color : #ff0000;
}
div.warning img, div.notice img, div.error img {
margin : 4px;
vertical-align : middle;
}
div.warning span, div.notice span, div.error span {
display : table-cell;
vertical-align : middle;
}
h1 {
color : #88b0f0;
font-size : 32px;
margin : 20px 0px 5px 0px;
text-shadow : 0 0 6px #fff;
}
h2 {
color : #88b0f0;
font-size : 14pt;
border-width : 0px 0px 1px 0px;
border-color : #f0f0f0;
border-style : solid;
}
div.content > h2 {
margin-top : 0px;
}
div.rss h1 {
border-width : 0px 0px 1px 0px;
border-color : gray;
border-style : dotted;
color : gray;
}
div.rss h2 {
font-size : 12pt;
}
div.rss a.extlink {
color : gray;
border-width : 0px 0px 1px 0px;
border-color : #778899;
border-style : dotted;
font-size : 9pt;
}
div.rss img {
max-width : 775px;
}
div.rss p.description {
color : gray;
font-size : 9pt;
}
div.rss div.content {
margin-top : 0.5em;
}
div.rss img.feedicon {
float : right;
}
div.rss hr {
border-width : 0px 0px 1px 0px;
border-style : dashed;
border-color : #e0e0e0;
}
body#sharepopup {
background-color : white;
background-image : url("../images/toolbar.png");
background-repeat : repeat-x;
background-position : bottom;
margin : 10px;
padding : 0px;
}
body#sharepopup h1 {
font-size : 14px;
margin : 0px;
color : #88b0f0;
}
body#sharepopup table {
background : white;
border : 1px solid #88b0f0;
padding : 5px;
}
body#sharepopup form {
height : 100%;
}
body#sharepopup input {
width : 100%;
}
div.autocomplete {
position : absolute;
width : 250px;
background-color : white;
border :1px solid #778899;
margin : 0px;
padding : 0px;
z-index : 4;
}
div.autocomplete ul {
list-style-type : none;
margin : 0px;
padding : 0px;
font-size : 10px;
}
div.autocomplete ul li.selected {
background-color : #fff7d5;
}
div.autocomplete ul li {
list-style-type : none;
display : block;
margin : 0;
padding : 2px;
height : 32px;
cursor : pointer;
}
fieldset {
border-width : 0px;
padding : 0px 0px 5px 0px;
margin : 0px;
}
fieldset input {
font-family : sans-serif;
font-size : medium;
border-spacing : 2px;
border : 1px solid #b5bcc7;
padding : 2px;
}
fieldset label {
width : 120px;
margin-right : 20px;
display : inline-block;
text-align : right;
color : gray;
}
body.otp {
margin : 1em;
padding : 0px;
}
form.otpform {
margin : 0px;
padding : 0px;
}
form.otpform label {
margin : 0px;
padding : 0px;
}
body.otp div.content {
display : inline-block;
width : auto;
}
span.hint {
font-size : 10px;
color : gray;
}

View file

@ -1,105 +0,0 @@
body#ttrssZoom {
margin-left : auto;
margin-right : auto;
padding : 20px;
max-width : 670px;
background : #f5f5f5;
}
body#ttrssZoom div.postHeader div.postFeedTitle {
float : left;
text-align : right;
padding-left : 0px;
font-size : 11px;
}
body#ttrssZoom div.postHeader a.postComments {
text-align : right;
padding-left : 0px;
font-size : 11px;
}
body#ttrssZoom div.postHeader div.postDate {
float : none;
text-align : right;
padding-left : 0px;
color : #777;
font-size : 11px;
}
body#ttrssZoom div.postHeader div.postTags {
color : #777;
font-size : 11px;
}
body#ttrssZoom div.postHeader div.postTitle {
white-space : normal;
font-size : 16px;
}
body#ttrssZoom div.postContent {
font-size : 15px;
line-height : 1.5;
}
body#ttrssZoom div.postContent p {
max-width : 650px;
-webkit-hyphens: auto;
-moz-hyphens: auto;
hyphens: auto;
}
body#ttrssZoom div.postHeader {
margin : 10px;
border-width : 0px 0px 1px 0px;
border-style : solid;
border-color : #eee;
background : white;
}
body#ttrssZoom div.postReply {
border : 1px solid #ddd;
background : white;
box-shadow : 0px 1px 1px -1px rgba(0,0,0,0.1);
}
body#ttrssZoom div.footer {
margin-top : 1em;
text-align : center;
}
body#ttrssZoom div.postContent img {
max-width : 630px;
height : auto;
}
body#ttrssZoom div.postContent blockquote {
margin : 5px 0px 5px 0px;
color : #555;
padding-left : 10px;
border-width : 0px 0px 0px 4px;
border-color : #ccc;
border-style : solid;
}
body#ttrssZoom div.postContent code {
color : #009900;
font-family : monospace;
font-size : 12px;
}
body#ttrssZoom div.postContent pre {
margin : 5px 0px 5px 0px;
padding : 10px;
color : #555;
font-family : monospace;
font-size : 12px;
border-width : 0px;
border-color : #ccc;
border-style : solid;
background : #f5f5f5;
display : block;
max-width : 98%;
overflow : auto;
}

View file

@ -1,50 +0,0 @@
<?php
set_include_path(dirname(__FILE__) ."/include" . PATH_SEPARATOR .
get_include_path());
require_once "functions.php";
$ERRORS[0] = "";
$ERRORS[1] = __("This program requires XmlHttpRequest " .
"to function properly. Your browser doesn't seem to support it.");
$ERRORS[2] = __("This program requires cookies " .
"to function properly. Your browser doesn't seem to support them.");
$ERRORS[3] = __("Backend sanity check failed.");
$ERRORS[4] = __("Frontend sanity check failed.");
$ERRORS[5] = __("Incorrect database schema version. &lt;a href='db-updater.php'&gt;Please update&lt;/a&gt;.");
$ERRORS[6] = __("Request not authorized.");
$ERRORS[7] = __("No operation to perform.");
$ERRORS[8] = __("Could not display feed: query failed. Please check label match syntax or local configuration.");
$ERRORS[8] = __("Denied. Your access level is insufficient to access this page.");
$ERRORS[9] = __("Configuration check failed");
$ERRORS[10] = __("Your version of MySQL is not currently supported. Please see official site for more information.");
$ERRORS[11] = "[This error is not returned by server]";
$ERRORS[12] = __("SQL escaping test failed, check your database and PHP configuration");
if ($_REQUEST['mode'] == 'js') {
header("Content-Type: text/javascript; charset=UTF-8");
print "var ERRORS = [];\n";
foreach ($ERRORS as $id => $error) {
$error = preg_replace("/\n/", "", $error);
$error = preg_replace("/\"/", "\\\"", $error);
print "ERRORS[$id] = \"$error\";\n";
}
}
?>

View file

@ -1,52 +0,0 @@
<?php
set_include_path(dirname(__FILE__) ."/include" . PATH_SEPARATOR .
get_include_path());
require_once "config.php";
// backwards compatible wrapper for old-style image caching
/* if (isset($_GET['url'])) {
$url = base64_decode($_GET['url']);
$filename = CACHE_DIR . '/images/' . sha1($url) . '.png';
if (file_exists($filename)) {
header("Content-type: image/png");
echo file_get_contents($filename);
} else {
header("Location: $url");
}
return;
} */
@$hash = basename($_GET['hash']);
if ($hash) {
$filename = CACHE_DIR . '/images/' . $hash . '.png';
if (file_exists($filename)) {
/* See if we can use X-Sendfile */
$xsendfile = false;
if (function_exists('apache_get_modules') &&
array_search('mod_xsendfile', apache_get_modules()))
$xsendfile = true;
if ($xsendfile) {
header("X-Sendfile: $filename");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
} else {
header("Content-type: image/png");
$stamp = gmdate("D, d M Y H:i:s", filemtime($filename)). " GMT";
header("Last-Modified: $stamp", true);
readfile($filename);
}
} else {
header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
echo "File not found.";
}
}
?>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 778 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 623 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

Some files were not shown because too many files have changed in this diff Show more