Merge pull request #31 from rndmh3ro/update_ttrss_version
Update ttrss version
|
@ -1,4 +1,4 @@
|
|||
<?php
|
||||
<?php
|
||||
// *******************************************
|
||||
// *** Database configuration (important!) ***
|
||||
// *******************************************
|
||||
|
@ -20,11 +20,16 @@ define('MYSQL_CHARSET', 'UTF8');
|
|||
|
||||
define('SELF_URL_PATH', 'yunopath');
|
||||
// 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
|
||||
// including PUSH, bookmarklets and browser integration will not work properly.
|
||||
|
||||
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
|
||||
// in the database. A string of 24 random characters. If left blank, encryption
|
||||
// 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
|
||||
// won't try to subscribe to PUSH feed updates.
|
||||
|
||||
// *********************
|
||||
// *** Sphinx search ***
|
||||
// *********************
|
||||
|
||||
define('SPHINX_ENABLED', false);
|
||||
// Enable fulltext search using Sphinx (http://www.sphinxsearch.com)
|
||||
// Please see http://tt-rss.org/wiki/SphinxSearch for more information.
|
||||
// ****************************
|
||||
// *** Sphinx search plugin ***
|
||||
// ****************************
|
||||
|
||||
define('SPHINX_SERVER', 'localhost:9312');
|
||||
// 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,
|
||||
// 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 ***
|
||||
// *********************************
|
||||
|
@ -181,14 +175,8 @@ define('SMTP_SECURE', '');
|
|||
// *** 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('CHECK_FOR_UPDATES', true);
|
||||
// Check for updates automatically if running Git version
|
||||
|
||||
define('ENABLE_GZIP_OUTPUT', false);
|
||||
// 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
|
||||
// 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.
|
||||
// System plugins have to be specified here. Please enable at least one
|
||||
// authentication plugin here (auth_*).
|
||||
|
@ -217,4 +205,3 @@ define('CONFIG_VERSION', 26);
|
|||
// if necessary (after migrating all new options from this file).
|
||||
|
||||
// vim:ft=php
|
||||
?>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
"fr": "Un lecteur de flux en PHP et Ajax"
|
||||
},
|
||||
"url": "http://tt-rss.org",
|
||||
"version": "17.1",
|
||||
"maintainer": {
|
||||
"name": "titoko",
|
||||
"email": "titoko@titoko.fr"
|
||||
|
|
32
scripts/_common.sh
Normal 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"
|
||||
}
|
|
@ -3,6 +3,10 @@
|
|||
# causes the shell to exit if any subcommand or pipeline returns a non-zero status
|
||||
set -e
|
||||
|
||||
# Load common variables
|
||||
source ./_common.sh
|
||||
|
||||
|
||||
# Retrieve arguments
|
||||
domain=$1
|
||||
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')
|
||||
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
|
||||
final_path=/var/www/$app
|
||||
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/*.patch /tmp/
|
||||
sudo patch -d $final_path -p0 < /tmp/update.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
|
||||
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
|
||||
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"
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
# causes the shell to exit if any subcommand or pipeline returns a non-zero status
|
||||
set -e
|
||||
|
||||
# Load common variables
|
||||
source ./_common.sh
|
||||
|
||||
app=${!#}
|
||||
|
||||
domain=$(sudo yunohost app setting $app domain)
|
||||
|
@ -14,7 +17,8 @@ db_pwd=$(sudo yunohost app setting $app mysqlpwd)
|
|||
final_path=/var/www/$app
|
||||
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/*.patch /tmp/
|
||||
sudo patch -d $final_path -p0 < /tmp/update.patch
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
AddType image/svg+xml svg
|
||||
AddType image/svg+xml svgz
|
||||
|
339
source/LICENSE
|
@ -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.
|
|
@ -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
|
|
@ -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();
|
||||
?>
|
|
@ -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;</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>
|
||||
|
|
@ -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)));
|
||||
|
||||
?>
|
2
source/cache/.htaccess
vendored
|
@ -1,2 +0,0 @@
|
|||
Order deny,allow
|
||||
Deny from all
|
0
source/cache/export/.empty
vendored
0
source/cache/images/.empty
vendored
0
source/cache/js/.empty
vendored
0
source/cache/simplepie/.empty
vendored
0
source/cache/upload/.empty
vendored
|
@ -1,2 +0,0 @@
|
|||
Order deny,allow
|
||||
Deny from all
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -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>"; */
|
||||
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
} ?>
|
|
@ -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:"). " " .
|
||||
"<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 " ";
|
||||
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 " ";
|
||||
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>";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
?>
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
class FeedEnclosure {
|
||||
public $link;
|
||||
public $type;
|
||||
public $length;
|
||||
public $title;
|
||||
public $height;
|
||||
public $width;
|
||||
}
|
||||
?>
|
|
@ -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();
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
} ?>
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
class Handler_Protected extends Handler {
|
||||
|
||||
function before($method) {
|
||||
return parent::before($method) && $_SESSION['uid'];
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
interface IAuthModule {
|
||||
function authenticate($login, $password);
|
||||
}
|
||||
?>
|
|
@ -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();
|
||||
}
|
||||
?>
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
interface IHandler {
|
||||
function csrf_ignore($method);
|
||||
function before($method);
|
||||
function after();
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
|
@ -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");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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'>α</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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -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 " <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
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
|
@ -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 <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\"> ...</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%\"> </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"] = " ";
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
|
@ -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"));
|
||||
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
html, body#ttrssMain, body#ttrssPrefs, #main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
|
@ -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%;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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. <a href='db-updater.php'>Please update</a>.");
|
||||
|
||||
$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";
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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.";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
Before Width: | Height: | Size: 701 B |
Before Width: | Height: | Size: 555 B |
Before Width: | Height: | Size: 80 B |
Before Width: | Height: | Size: 317 B |
Before Width: | Height: | Size: 655 B |
Before Width: | Height: | Size: 666 B |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 772 B |
Before Width: | Height: | Size: 691 B |
Before Width: | Height: | Size: 586 B |
Before Width: | Height: | Size: 537 B |
Before Width: | Height: | Size: 633 B |
Before Width: | Height: | Size: 723 B |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 778 B |
Before Width: | Height: | Size: 586 B |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 670 B |
Before Width: | Height: | Size: 623 B |
Before Width: | Height: | Size: 372 B |
Before Width: | Height: | Size: 612 B |
Before Width: | Height: | Size: 591 B |
Before Width: | Height: | Size: 347 B |
Before Width: | Height: | Size: 691 B |
Before Width: | Height: | Size: 717 B |
Before Width: | Height: | Size: 332 B |
Before Width: | Height: | Size: 295 B |
Before Width: | Height: | Size: 295 B |