remove source
|
@ -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 |
Before Width: | Height: | Size: 211 B |
Before Width: | Height: | Size: 201 B |
Before Width: | Height: | Size: 670 B |
Before Width: | Height: | Size: 594 B |
Before Width: | Height: | Size: 537 B |