Compare commits

...

501 Commits
0.3 ... 1.5.2

Author SHA1 Message Date
99679d0688 Merge pull request #481 from wallabag/dev
1.5.2
2014-02-21 15:57:10 +01:00
d3b47e9470 [release] 1.5.2 2014-02-21 15:44:57 +01:00
1570a65381 [fix] content is now cleaned by HTML purifier from prevent XSS attack 2014-02-21 15:44:13 +01:00
d4949327ef [add] HTML Purifier added to clean code 2014-02-21 15:43:14 +01:00
c9bd17a100 [add] languages well displayed on config page #480 2014-02-21 15:24:33 +01:00
0a022f9a39 [add] slovene language 2014-02-21 15:11:45 +01:00
565bb72d99 [fix] link in footer was unclickable #453 2014-02-21 15:00:21 +01:00
e5382002b4 [change] login button into sign in 2014-02-21 14:58:07 +01:00
3628b24d12 [change] remove some poche terms in old default theme #471 2014-02-21 14:53:43 +01:00
cd425599ce typo in fonts call and in label on config screen 2014-02-21 14:08:20 +01:00
e7345a2c4f Merge branch 'dev' of git://github.com/mariroz/wallabag into dev 2014-02-21 13:44:30 +01:00
032e0ca13a a lot of refactoring: tag action is now handled by home view and uses sorting and pagination. Some small view enhacenments. Fix of #476, #461 for baggy and other themes 2014-02-20 19:28:39 +02:00
3ade95a3d7 [fix] themes sorted A->Z #470 2014-02-20 13:29:53 +01:00
fddf4fbacc Merge branch 'dev' of git://github.com/arnaudmm/wallabag into arnaudmm-dev 2014-02-20 13:15:27 +01:00
926acd7bba Feature #457 : import from pocket now import tags too 2014-02-20 13:09:27 +01:00
8975653d4c Merge branch 'mariroz-dev' into dev 2014-02-20 13:03:59 +01:00
689de3dbcc rename font folder into fonts in baggy theme 2014-02-20 13:02:13 +01:00
ab5bb94b12 Merge branch 'dev' of git://github.com/mariroz/wallabag into mariroz-dev 2014-02-20 12:49:49 +01:00
6203ef8e51 Merge remote-tracking branch 'upstream/dev' into dev 2014-02-20 11:42:18 +02:00
e83cf5a787 multiple tag entry displayed fix, issue #474 2014-02-20 10:41:16 +02:00
d09a5674e9 [add] message in install screen to prevent user when wallabag is already installed 2014-02-20 08:58:59 +01:00
d0a599bbae Merge pull request #473 from nsteinmetz/dev
Baggy Theme - PtSans font is now local
2014-02-20 08:27:54 +01:00
30c12d3927 add font file 2014-02-19 23:30:55 +01:00
860473f33c Update font.css
Add reference for ptsans font in local instead of google fonts
2014-02-19 23:09:12 +01:00
e1cfef7bf1 Update _head.twig
Remove googlefonts now called in font.css - need still to put the woff file in font directory
2014-02-19 23:08:01 +01:00
b4fd2154fe Full-Text RSS included as a script instead of file_get_contents call. Tnx to @Faless. Fix issues #366 and #463 2014-02-19 19:08:19 +02:00
f37891fdb6 [fix] css display with baggy theme 2014-02-19 15:20:13 +01:00
aad8fbab09 Merge branch 'dev' of https://github.com/wallabag/wallabag into dev 2014-02-19 15:19:02 +01:00
7785f0c75f Merge pull request #467 from mariroz/dev
"save a link" added to top menu (default theme) to toggle "save link" form,  issue #461
2014-02-19 15:17:13 +01:00
a71dc5d7d0 Merge branch 'dev' of git://github.com/mariroz/wallabag into dev 2014-02-19 15:12:49 +01:00
655214ab30 Merge pull request #469 from wallabag/dev
version number 1.5.1
2014-02-19 13:56:06 +01:00
fb5a9666ed version number 1.5.1 2014-02-19 13:55:40 +01:00
60ca369cd3 Merge pull request #468 from wallabag/dev
1.5.1
2014-02-19 13:47:11 +01:00
b89d5a2bf4 [fix] security problems with tags 2014-02-19 13:25:28 +01:00
53ae58e1a1 Merge branch 'dev' of https://github.com/wallabag/wallabag into dev 2014-02-18 21:22:41 +01:00
792097fb6a [add] script to make the install more user friendly #466 2014-02-18 21:22:34 +01:00
970cfb1166 Merge pull request #460 from mariroz/dev
duplicate check added. fix of issue #400
2014-02-18 15:29:40 +01:00
01cd443441 "save a link" added to top menu (default theme) to toggle "save link" form message, issue #461. "Mark all the entries as read" link added in default theme. 2014-02-16 21:00:30 +02:00
488fc63b67 duplicate check added. fix of issue #400 2014-02-14 17:27:22 +02:00
6285e57c49 [add] link to empty cache in config screen, #454 2014-02-14 15:11:57 +01:00
243e13ab59 [fix] #452 remove crappy code 2014-02-13 23:00:26 +01:00
5e98c2183a Merge pull request #450 from wallabag/dev
1.5.0
2014-02-13 19:49:25 +01:00
9f3148fec7 [change] change default content #445 2014-02-13 19:31:46 +01:00
c9357429fd [fix] remove htacces in inc/, bug to fetch content 2014-02-13 19:22:54 +01:00
41265e07d4 add help about vendor.zip 2014-02-13 18:57:57 +01:00
2e4440c3f8 [change] wallabag in feeds title 2014-02-13 08:57:44 +01:00
3141347214 new default theme, baggy \o/ 2014-02-12 21:59:02 +01:00
943ac3c77e new theme, baggy one cf #448 2014-02-12 21:58:40 +01:00
83b47311ba go go go, 1.5 powa 2014-02-12 21:58:14 +01:00
16fd1cce61 [change] time for session 2014-02-12 21:52:51 +01:00
f14807de06 [add] mark all as read #385 2014-02-12 21:52:01 +01:00
ed2853564e [fix] somes fixes on old default theme 2014-02-12 21:51:11 +01:00
26170f4613 change static files 2014-02-12 21:46:49 +01:00
68268c0199 Merge branch 'dev' of https://github.com/wallabag/wallabag into dev 2014-02-12 20:05:51 +01:00
5966d2c2d3 change README 2014-02-12 20:05:47 +01:00
26929c08d3 bug fix #430 - welcome to your wallabag 2014-02-12 20:04:47 +01:00
044bf638a8 bug fix #364 - RSS Feed URL problem with + sign 2014-02-12 19:58:49 +01:00
58f6269f36 Merge pull request #439 from flolauck/master
Adding .htaccess files to prohibit access to critical directories, e.g., db/
2014-02-10 12:10:08 +01:00
3a68883ae9 Adding .htaccess files to prohibit access to critical directories, e.g., db/ 2014-02-10 08:58:21 +01:00
211ed48361 Merge pull request #435 from mariroz/dev
Polish and Ukrainian translations added. Russian - updated. Plust 2 smal...
2014-02-07 18:41:30 +01:00
c515ffec9c Polish and Ukrainian translations added. Russian - updated. Plust 2 small translation related fixes in code. 2014-02-07 17:49:27 +02:00
4e09039d2c Merge pull request #434 from thomaslebeau/theme-courgette
Theme courgette
2014-02-07 09:41:42 +01:00
1e1e4e4eca [remove] fontello theme courgette 2014-02-07 00:08:58 +01:00
c8265d95b0 [add] maj theme courgette : ajout tag 2014-02-07 00:07:54 +01:00
2e384abab6 Merge pull request #428 from mariroz/dev
all locale files re-compiled: fix of #416 Some language problems in the ...
2014-02-04 02:13:32 -08:00
736a4fb77e all locale files re-compiled: fix of #416 Some language problems in the french Config Page 2014-02-04 11:52:03 +02:00
38dafee05d Merge pull request #427 from wallabag/dev
changelog 1.4
2014-02-03 10:03:41 -08:00
fa0bfb775a [fix] #389 Empty article title (blank title tag) 2014-02-03 17:11:14 +01:00
445a1a1c8d [fix] Add support for X-Forwarded-Proto header field #413 2014-02-03 12:46:09 +01:00
1e1fd6f24d [add] link to test file when we install wallabag #392 2014-02-03 12:43:34 +01:00
a678f9df38 [fix] #421 Tables content in solarized themes unreadable 2014-02-03 12:34:28 +01:00
f85bfdf186 Merge branch 'dev' of git://github.com/mariroz/wallabag into mariroz-dev 2014-02-03 11:56:01 +01:00
cae70cdbdb [fix] courgette theme: Old constant call, replaced by the new one. 2014-02-02 19:13:05 +01:00
ebae8c8315 [del] courgette theme: Useless templates because they are identical to those of default theme and so inherited from it. 2014-02-02 19:10:35 +01:00
6af66b1106 fix of bug #368 Endless redirects or user doesn't exist with basic authentication 2014-01-30 16:35:31 +02:00
f355d2c87f poche -> wallabag 2014-01-28 13:47:15 +01:00
f4fbfaa7cb some fix to courgette theme 2014-01-28 11:19:06 +01:00
21f50d5a08 changed some poche with wallabag 2014-01-28 11:08:21 +01:00
1b539ba1ec Merge branch 'extraction-with-basic-auth' of git://github.com/aaa2000/poche into aaa2000-extraction-with-basic-auth 2014-01-28 10:56:57 +01:00
3e0e7e1208 [fix] inthepoche.com => wallabag.org 2014-01-28 10:49:57 +01:00
b8fdd2d85f [fix] change twitter account for sharing entry 2014-01-28 10:37:37 +01:00
c95b78a8ce poche is dead, welcome wallabag 2014-01-28 10:36:04 +01:00
3ae345b3d7 Merge pull request #410 from Lonnytunes/dev
Fix: stops multiplication, in database, of a same user config item
2014-01-21 04:46:14 -08:00
d1d3498b62 [fix] Stops multiplication, in database, of a same user config item (error of variable name). 2014-01-20 00:44:51 +01:00
f878daeb8b add basic auth in file_get_contents for content extraction when user use basic auth 2014-01-12 17:08:52 +01:00
9ba98a0abe [add] display token and user id 2014-01-12 17:06:52 +01:00
1cecaa7926 [add] display token and user id 2014-01-10 16:33:10 +01:00
2744d07d71 Merge branch 'dev' of https://github.com/inthepoche/poche into dev 2014-01-08 11:28:53 +01:00
5ed8050791 [add] courgette theme 2014-01-08 11:28:46 +01:00
c3b261e321 Merge pull request #391 from Newinx/master
Correction de bugs dans le schema mysql
2014-01-07 23:22:32 -08:00
9ffce01e0c Correction de bugs dans le schema mysql
- users/name type de int a varchar
	- valeurs par defaut ajoutees pour entries/is_read et is_fav
2014-01-07 22:55:48 +01:00
8905191413 [add] import from poche, thank you @tsadiq #388 2014-01-07 20:21:43 +01:00
b4b22940df Merge pull request #387 from inthepoche/dev
poche 1.3.1
2014-01-07 04:20:23 -08:00
e1cf0fda27 [add] user_agent in file_get_contents 2014-01-07 13:15:43 +01:00
f41d00ed8a Merge branch 'dev' of https://github.com/inthepoche/poche into dev 2014-01-07 13:13:30 +01:00
d866e8be91 [change] poche logo is now at SVG format, see #373 2014-01-07 13:13:24 +01:00
be4c8197eb Merge pull request #381 from tcitworld/dev
Flattr Class : Bug 359
2014-01-06 10:07:29 -08:00
fcb5fd27e2 [change] update poche version in compatibility test file 2014-01-06 08:32:28 +01:00
9c9b226589 Merge pull request #382 from aaa2000/table_tags_entries_already_exists
Create sqlite table tags_entries only if not already exists
2014-01-05 10:39:44 -08:00
a562e3905a Create sqlite table tags_entries only if not already exists 2014-01-05 15:03:05 +01:00
607e12b4f2 Fixes bug 359 2014-01-04 21:50:08 +01:00
7f66783976 Merge pull request #1 from inthepoche/dev
Dev
2014-01-04 12:30:31 -08:00
2abcccb371 Merge branch 'dev' of https://github.com/inthepoche/poche into dev 2014-01-03 15:18:32 +01:00
9bc32632af [fix] #375 Readability.com changed its export format 2014-01-03 15:18:13 +01:00
52e3f58c72 Merge pull request #380 from F1reF0x/dev
Change Permissions in pochePictures.php
2014-01-03 02:20:40 -08:00
b5c1ed1227 Change Permissions in pochePictures.php
Stored Pictures are not accessible (on my server), when permission is set to 0705, but instead, when using 0755 (or for example to 0715) all is working as expected. So maybe it would be good, considering in changing the permission of created directories in the assets directory
2014-01-03 11:17:15 +01:00
4d058d4824 [fix] code display when printing a page was buggy 2014-01-03 10:33:01 +01:00
cb4fba5a33 [del] remove inthepoche.com config file, website has changed 2014-01-03 10:22:12 +01:00
4a84d94e91 [add] config file for interviewmagazine.com 2014-01-03 10:21:18 +01:00
0b57c6825a [fix] bugs #374 and #376 - encoding in rss 2014-01-03 10:15:05 +01:00
529db4861d Merge branch 'dev' of https://github.com/inthepoche/poche into dev 2014-01-03 10:06:33 +01:00
9de34d4e84 [fix] error in query to get entries and tags 2014-01-03 10:06:26 +01:00
d7ad5d6560 Merge pull request #379 from williamtheaker/dev
Minor changes to tag edit and config pages
2014-01-02 21:54:50 -08:00
aeea7c6af0 Updated config page 2014-01-02 21:15:41 -05:00
b1bfd4cb0c Updated tag edit page 2014-01-02 21:04:56 -05:00
2eb111a300 Merge pull request #370 from DmitrySandalov/dev
docs link, typos
2013-12-28 05:04:45 -08:00
76e487cd7e docs link, typos 2013-12-28 11:47:10 +04:00
60fc4f4b1a Merge pull request #363 from inthepoche/dev
poche 1.3.0
2013-12-23 02:28:56 -08:00
da5fc42f61 [fix] bug with queries when postgresql is used 2013-12-23 11:23:12 +01:00
1151e9cc8f [add] availability to regenerate feed token 2013-12-23 11:01:41 +01:00
dfde415198 [change] install doc 2013-12-23 10:45:16 +01:00
e2b83a8298 [change] 1.3, let's rock baby 2013-12-23 10:44:22 +01:00
5cfafc6110 [add] check tags tables 2013-12-23 10:35:09 +01:00
1810c13b55 PHP_AUTH_USER isn't available when using php as cgi 2013-12-23 09:09:10 +01:00
a0aa150418 fix for long lasting session 2013-12-21 23:39:45 +04:00
5c8d438c08 Merge pull request #360 from DmitrySandalov/dev
tags: mysql create tables if not exists
2013-12-19 05:28:22 -08:00
17bd2cc94c tags: mysql create tables if not exists 2013-12-19 16:24:49 +03:00
cbfd5a1019 Update INSTALL.md 2013-12-19 09:56:29 +01:00
04fcbad8c5 Merge pull request #358 from williamtheaker/dev
Edited English text
2013-12-16 00:24:27 -08:00
5df72bb4a4 Typo fixed 2013-12-15 00:44:13 -05:00
41acd466ce typo 2013-12-15 00:41:49 -05:00
43c1115eeb Edited text 2013-12-15 00:18:26 -05:00
db75a4425c Edited text 2013-12-15 00:17:30 -05:00
0f9d3ef173 [fix] typo in config screen 2013-12-12 11:11:53 +01:00
05d6dd487c Merge pull request #356 from inthepoche/tags
Tags feature
2013-12-12 01:48:24 -08:00
d460914f65 [add] download database if sqlite is on 2013-12-12 09:42:19 +01:00
6bf4702608 [add] tags and tags_entries for mysql & postgresql 2013-12-06 15:16:02 +01:00
f014856424 [add] tags and tags_entries tables in poche.sqlite 2013-12-06 15:11:08 +01:00
c432fa1674 [add] assign and remove a tag to an entry 2013-12-06 15:07:51 +01:00
f778e47283 [add] rss for tag 2013-12-06 14:37:42 +01:00
4886ed6d36 [add] page which lists entries for a tag 2013-12-06 14:22:29 +01:00
74ec445a66 [change] simplify Tools::getTplFile 2013-12-06 14:07:00 +01:00
6cab59c340 [add] edit tags page 2013-12-06 14:03:14 +01:00
2e2ebe5ec7 [add] create tags page 2013-12-06 13:15:06 +01:00
68e2061666 [add] tags displaying 2013-12-06 13:02:58 +01:00
7b171c7340 [add] send tags to article view 2013-12-06 13:02:38 +01:00
5bea1a4d31 [add] function to get tags by entry 2013-12-06 13:02:15 +01:00
9e7c840b18 [fix] RSS feeds were buggy when I update full-text RSS 2013-12-06 10:14:59 +01:00
ac4d114214 [add] new specific configuration files 2013-12-06 10:13:03 +01:00
d5501950e2 Merge pull request #353 from inthepoche/ftr
[change] we now use Full-Text RSS 3.1, thank you so much @fivefilters
2013-12-06 00:49:43 -08:00
42c80841c8 [change] we now use Full-Text RSS 3.1, thank you so much @fivefilters 2013-12-06 09:45:27 +01:00
0b5c6ff319 Merge pull request #352 from versvs/dev
updating the "es_ES" locale
2013-12-05 10:56:23 -08:00
05b6401817 updating the "es_ES" locale
Fixed some strings to fit the common use that people understand and find and other es_ES web apps. Completed some non-translated strings.

Also, opted for an editorial line in which "Poche" is treated as a noun (therefore, I capitalized the first letter).
2013-12-05 19:36:20 +01:00
59cc585271 [add] add RSS feed for archive 2013-12-05 15:13:32 +01:00
f0133fe5f4 [fix] undefined notice for feed 2013-12-03 12:07:02 +01:00
b8bb2b4ab6 Merge pull request #350 from inthepoche/rss
add atom feeds for unread / favs
2013-12-03 01:42:21 -08:00
72c20a5297 [add] atom feeds for unread / fav items 2013-12-03 10:40:27 +01:00
5846b0f1b3 [change] getConfigUser is now a public function 2013-12-03 10:39:45 +01:00
39cc09dfc3 [change] update FeedWriter class 2013-12-03 10:39:00 +01:00
678f2cb6ee Merge pull request #347 from evgeni/fix-links
Fix links of third applications
2013-12-02 23:15:39 -08:00
13991e5288 Merge pull request #349 from JasonGhent/master
Stackoverflow parsing and subdomain failover fix.
2013-12-02 23:14:49 -08:00
5c0ad7376a do not link to the french mozilla site, let the server decide the language 2013-12-02 19:52:56 +01:00
af1daea191 proper spacing around 'or' 2013-12-02 19:52:50 +01:00
9772578ee2 fix link to Google Play 2013-12-02 19:52:45 +01:00
16ac4e3dbe Subdomain to domain failover left incorrect leading '.'. This has been remedied. 2013-11-30 13:02:18 -05:00
2ab37d6205 Addition of stackexchange parser. 2013-11-30 13:00:24 -05:00
e070b9b511 Added stackoverflow parsing. 2013-11-30 12:08:17 -05:00
e232d5532a add compiled file for persian language 2013-11-25 15:53:40 +01:00
74dc587452 Merge pull request #329 from mabkenar/dev
Create fa_IR.utf8.po
2013-11-25 06:52:36 -08:00
87a44f4704 Create fa_IR.utf8.po
A Persian (fa_IR) translation.
2013-11-25 15:46:36 +01:00
c2b7a11c77 Merge pull request #326 from inthepoche/dev
1.2.0
2013-11-25 02:00:07 -08:00
7a4482b8a4 1.2.0: here we go 2013-11-25 09:57:10 +01:00
defa7754a4 Merge branch 'dev' of https://github.com/inthepoche/poche into dev 2013-11-25 09:49:08 +01:00
1bf152a551 add italian language 2013-11-25 09:48:51 +01:00
99c8761b75 fix russian directory 2013-11-25 09:43:45 +01:00
0c2f453750 Fix Undefined offset Notice (thx @vjousse) 2013-11-20 10:22:21 +01:00
d7aec74403 [change] reuse existing class for article_toolbar 2013-11-14 11:14:15 +01:00
760d44d194 Merge branch 'DmitrySandalov-master' into dev 2013-11-13 15:23:52 +01:00
f2d3ee98a6 [fix] bug fix #287: test if open_basedir & safe_mode are active to use CURLOPT_FOLLOWLOCATION 2013-11-13 15:17:34 +01:00
705f04937f Merge branch 'master' of git://github.com/DmitrySandalov/poche into DmitrySandalov-master 2013-11-13 14:52:28 +01:00
5862c872c6 Merge branch 'dev' of https://github.com/inthepoche/poche into dev 2013-11-13 14:51:14 +01:00
b7066c0333 [fix] bug fix #311: remove toolbar when printing article 2013-11-13 14:50:56 +01:00
e4178da65c Merge pull request #310 from jn0/dev
dark theme images replaced to visible set, no executable bits for pictures
2013-11-12 08:38:24 -08:00
jno
c7abf20bbe dark theme images replaced to visible set, no executable bits for pictures 2013-11-12 20:34:55 +04:00
b3a2586ecf [add] russian language 2013-11-12 13:05:27 +01:00
45e9e0f565 fix #270 access from remote machine
Replacing SERVER_NAME with HTTP_HOST allows me to use Poche on remote machines
2013-11-05 12:13:55 +03:00
352523640d compatibility with 5.3.3 2013-11-04 08:44:56 +01:00
05824223a9 Merge pull request #291 from banux/dev
Autoclose
2013-10-28 00:38:05 -07:00
f616ab60ef use 2 seprate variable for autoclosing windows to avoid to quick closing when sharing, the popup can be close before the link is save 2013-10-27 07:47:14 +01:00
363bc4eb86 Add a autoclose parameters. When we use sharing method in plugins like the tiny tiny rss one or the firefox plugins we can passe the autoclose=true parameters that close the popup. 2013-10-27 07:37:05 +01:00
2b191d8c37 fix download links 2013-10-26 13:23:31 +02:00
7d7ed6a0ee change link to themes 2013-10-26 12:59:55 +02:00
83020b993c parse dilbert.com 2013-10-25 15:50:16 +02:00
985ce3ec53 bug fix #259: Deleting article doesn't redirect 2013-10-25 14:25:37 +02:00
33fe6a46a4 bug fix #278: mysql collation not UTF8 2013-10-25 14:20:18 +02:00
cd8a344156 Merge pull request #289 from inthepoche/dev
1.1.0
2013-10-24 23:51:59 -07:00
6ae804853f on the road to 1.1.0 2013-10-25 08:45:28 +02:00
b6d859fa92 Merge branch 'dev' of https://github.com/inthepoche/poche into dev 2013-10-22 08:51:30 +02:00
1a0d776394 fix #286: german translation updated 2013-10-22 08:51:22 +02:00
1ba1628ed6 Merge pull request #285 from dsacchet/dev
Adding support for http_auth
2013-10-21 08:31:24 -07:00
027b4e1568 Adding support for http_auth 2013-10-20 23:28:45 +02:00
df6afaf090 Added support for http_auth 2013-10-20 16:53:54 +02:00
c1e24b0461 Merge branch 'dev' of https://github.com/inthepoche/poche into dev 2013-10-17 17:04:12 +02:00
36b9219842 bug fix #251: login failure 2013-10-17 17:03:55 +02:00
8e76eaad19 Update CONTRIBUTING.md 2013-10-17 11:39:19 +02:00
fad925644f Merge pull request #278 from tcitworld/dev
Updated Screenshots URLs
2013-10-13 23:13:31 -07:00
a84f77d6ba Updated Screenshots URLs
From the poche-themes repository to the poche repository, folder themes.
2013-10-13 20:39:12 +02:00
0c994bd934 Merge branch 'dev' of https://github.com/inthepoche/poche into dev 2013-10-10 14:07:13 +02:00
18889e230a remove empty file 2013-10-10 14:06:53 +02:00
e557b7bb44 Merge pull request #274 from NumEricR/select-theme
Update select theme filter and sort names (dev branch PR)
2013-10-08 23:33:48 -07:00
3ae9190e78 Merge branch 'dev' of https://github.com/inthepoche/poche into dev 2013-10-08 13:38:03 +02:00
e231f3f023 add czech locale, thanks to @dstancl 2013-10-08 13:37:46 +02:00
2502e1359c Merge pull request #273 from jcsaaddupuy/emded-dependencies
Emdeded Roboto webfont
2013-10-08 04:10:37 -07:00
20f00edd15 Merge branch 'dev' into emded-dependencies 2013-10-08 13:04:05 +02:00
2f3425dff6 Added font-face linked to local font 2013-10-08 13:00:43 +02:00
272600ff80 Removed google font link 2013-10-08 12:57:33 +02:00
1745ebc832 Added Roboto font from https://themes.googleusercontent.com/static/fonts/roboto/v9/2UX7WLTfW3W8TclTUvlFyQ.woff 2013-10-08 12:57:02 +02:00
5188585864 Merge pull request #272 from jcsaaddupuy/custom_ssl_port
Custom ssl port
2013-10-08 03:48:49 -07:00
1e98ee1de0 bug fix: changing password was buggy 2013-10-07 14:56:21 +02:00
5011388f39 bug fix #215: change language from config screen 2013-10-07 14:26:25 +02:00
5e07dc8b51 Merge remote branch 'upstream/dev' into dev
Conflicts:
	themes/default/_head.twig
2013-10-07 14:25:24 +02:00
894c36ea32 what this f* F? 2013-10-07 14:00:45 +02:00
031df528b6 bug fix #268: move POCHE_VERSION in index.php and change the name to avoid conflicts when updating 2013-10-07 13:19:34 +02:00
9d3b88b379 bug fix #266: make installation steps easier 2013-10-07 13:12:28 +02:00
5eebe4e50d delete href 2013-10-07 13:06:10 +02:00
2916d24b20 Added support for custom SSL port 2013-10-07 12:47:13 +02:00
3352332f3f Merge branch 'dev' into custom_port 2013-10-07 12:39:29 +02:00
2621569200 missing } 2013-10-07 12:39:11 +02:00
93eed12505 Fixed typo 2013-10-07 12:31:46 +02:00
125f9ee838 Added support for custom ssl port 2013-10-07 12:30:40 +02:00
8df8eb6c95 Merge pull request #264 from jcsaaddupuy/dev
Embeding jquery
2013-10-07 00:31:42 -07:00
bf7516112f Embeded jquery 2.0.3 dependancie 2013-10-06 20:31:11 +02:00
5a26af476e Merge pull request #261 from NumEricR/toolbar-height
Fix #255 : increase article toolbar height if necessary
2013-10-06 11:07:24 -07:00
2287bf06f5 Sort themes alphabetically in config list 2013-10-05 11:08:38 +02:00
89812ec81c Remove useless filter on .git folder 2013-10-05 10:52:08 +02:00
06fef43180 Fix #255 : increase article toolbar height if necessary 2013-10-05 10:51:25 +02:00
01e671f4d1 Merge pull request #256 from inthepoche/dev
merge 1.0.0
2013-10-03 10:14:54 -07:00
d47d2533ac change doc link 2013-10-03 19:03:52 +02:00
27a74816da change export link 2013-10-03 18:33:20 +02:00
47baa1077e reopen #219 when archive last poched links from a page, redirect to an other page 2013-10-03 15:43:24 +02:00
f0f7b94362 remove print_r 2013-10-03 14:53:08 +02:00
747c6698b6 Merge branch 'dev' of git://github.com/tcitworld/poche into tcitworld-dev 2013-10-03 14:51:20 +02:00
8f91e10faa preparing to 1.0.0 2013-10-03 14:51:04 +02:00
6cd8af85da bug fix #227: Deleting element in archive redirect to home 2013-10-03 14:48:53 +02:00
34d67c835e bug fix #219: when archive last poched links from a page, redirect to an other page 2013-10-03 14:46:46 +02:00
7f17a38d35 change instructions messages 2013-10-03 14:23:03 +02:00
07ae20eeed change reading time displaying 2013-10-03 14:17:45 +02:00
4cc3c2ac17 embed themes 2013-10-03 14:10:44 +02:00
66e074b43d remove themes 2013-10-03 14:09:58 +02:00
eb44ca4213 embed themes with poche 2013-10-03 14:06:20 +02:00
dfbbc14b33 remove themes 2013-10-03 14:05:50 +02:00
34bf601a56 change submodule & gitignore 2013-10-03 13:59:52 +02:00
969a91a1e3 site_config is now embedded with poche 2013-10-03 13:48:58 +02:00
fbe8e27568 Merge pull request #250 from NumEricR/dl-export
Force download on poche export
2013-10-03 01:52:07 -07:00
4ee705a79c Add .htaccess file to force download of poche export 2013-09-28 11:15:35 +02:00
8623a53200 Merge pull request #245 from NumEricR/themes-list
Avoid ".git" option in themes list of config page
2013-09-27 02:19:26 -07:00
4e5b04113d Changed Flattr Caching System
From md5(url) to the ID of an article. Easier and faster.
2013-09-27 11:11:45 +02:00
3cc22aab82 Avoid ".git" option in themes list of config page 2013-09-26 23:37:07 +02:00
be2b9055b9 Changed url's encryption from base64 to md5
Fixes Issue #243.
md5 hashes are only coded on 32 hexadecimal characters, so it won't make
too long file names.
2013-09-26 22:00:44 +02:00
92cd6e9af8 change import files constants 2013-09-21 21:44:49 +02:00
58ace4941e bug fix #229: theme not stored on updated poches 2013-09-21 14:37:53 +02:00
37527034ab unactivate debug 2013-09-21 14:18:28 +02:00
88e1108f11 upgrading themes 2013-09-20 14:30:07 +02:00
3408ed48ba fix bug #225: blank page on article page 2013-09-20 14:09:26 +02:00
b8c67f8068 remove define.inc.php 2013-09-20 13:48:29 +02:00
79026b73a8 Merge pull request #226 from inthepoche/dev
beta5
2013-09-20 04:36:35 -07:00
6a6c1c1172 update themes submodule 2013-09-20 11:58:08 +02:00
18bd1cc3b1 update themes 2013-09-20 11:54:39 +02:00
74c0733c1e update themes 2013-09-20 11:50:19 +02:00
f55f734fda remove themes 2013-09-20 11:35:46 +02:00
d477db2cfb add themes 2013-09-20 11:32:19 +02:00
6fe07ab815 remove themes 2013-09-20 11:31:28 +02:00
1a1142893f remove themes 2013-09-20 11:30:53 +02:00
7bda34c66b help when sqlite file not found 2013-09-20 11:25:44 +02:00
2af5015668 remove config.inc.php 2013-09-20 11:05:12 +02:00
5801355cbc chmod & mail address 2013-09-20 11:04:36 +02:00
d6b28d43c8 update themes 2013-09-20 11:00:33 +02:00
6d7e6f5e36 themes submodule 2013-09-20 10:58:53 +02:00
469590fde5 remove themes folder 2013-09-20 10:55:46 +02:00
00dbaf90bc merge #224 2013-09-20 10:21:39 +02:00
705250b93d fixes with new session class 2013-09-20 09:32:49 +02:00
0d64be15de remove csrf check 2013-09-18 09:25:28 +02:00
f6597c7cb9 fix bug #127: update session class 2013-09-17 14:48:16 +02:00
a8778dc23e fix bug #105: Scroll position save / sync 2013-09-17 13:27:16 +02:00
d62dfd88d2 Merge pull request #221 from NumEricR/language
Fix #183: language declaration
2013-09-15 10:12:56 -07:00
d081f272b0 Use dynamic lang value in IE conditional comments 2013-09-14 10:06:59 +02:00
1b2abab6dd Add lang attribute in html tag 2013-09-14 10:06:59 +02:00
10ab20d8e2 Fix missing lang in html tag of update pages 2013-09-14 10:06:59 +02:00
48207b6814 remove poche.js 2013-09-13 10:29:03 +02:00
1a08e7b6f6 titles with colon bad parsed 2013-09-13 09:01:33 +02:00
b9523a0ba0 fix bug #209: titles with colon bad parsed 2013-09-12 19:28:59 +02:00
084ec2a63d change strings for gettext 2013-09-12 14:26:30 +02:00
b9e0514783 update locale files 2013-09-12 13:24:09 +02:00
3e05742568 Merge branch 'dev' of https://github.com/inthepoche/poche into dev 2013-09-12 12:36:07 +02:00
660b998eb7 add english language 2013-09-12 12:28:22 +02:00
b084cde854 Merge pull request #216 from NumEricR/css-icons
Move icon's no-repeat to common code and clean CSS
2013-09-10 21:43:46 -07:00
8ca368e7e3 fix flattr span 2013-09-11 06:40:29 +02:00
a0fd7c5b44 Format CSS code 2013-09-10 23:54:40 +02:00
7eb64927cc Move icons no-repeat into common style 2013-09-10 23:54:18 +02:00
9074534c93 fix flattr span 2013-09-10 22:19:34 +02:00
12d9cfbcaa Merge branch 'Flattr' of git://github.com/tcitworld/poche into tcitworld-Flattr
Conflicts:
	inc/3rdparty/site_config
2013-09-10 19:22:47 +02:00
f3a6080fce add site_config submodule 2013-09-10 18:50:42 +02:00
f16b0747a4 remove site_config to manage them by a submodule 2013-09-10 18:28:17 +02:00
964481d023 Fixed bugs, added a flattr button and an option
There's a button and an option in define.inc.php to show the button or
not.
2013-09-10 18:23:56 +02:00
d143cae25a remove comments in poche_compatibility_test 2013-09-10 15:47:09 +02:00
d11e2bcf48 change doc url 2013-09-10 15:46:05 +02:00
3c33e40b61 change email address in header 2013-09-10 15:43:01 +02:00
eaf2c769be Merge branch 'dev' of https://github.com/inthepoche/poche into dev 2013-09-10 14:44:59 +02:00
ce4a1dcc19 changes to add url directly from poche 2013-09-10 14:41:58 +02:00
2230a38cd6 Merge pull request #205 from NumEricR/less-is-more
Less is more :-)
2013-09-09 05:01:58 -07:00
ef6051b95e Merge pull request #199 from NumEricR/nb-results
Add the number of results next to pager
2013-09-09 04:55:56 -07:00
af1d279226 Fixed errors and changed source of flattrs
Fixed errors, and the number displayed is no longer the number of peope
who has flattred an article, but the number of flattrs he has got.
2013-09-08 21:52:01 +02:00
d5ce28df67 Fixed bugs due to migration 2013-09-08 21:26:30 +02:00
693b3f8677 Implemented Add Button
Added a button to add an URL when directly into Poche. If JS isn't
enabled, nothing happens.
2013-09-08 21:07:59 +02:00
a322312740 Implemented Flattr changes
Added a button to say if the article is flattrable or not and how many
people have flattred this object.
2013-09-08 20:54:11 +02:00
2edde7fe33 Display sort links only if there is at least 2 articles in current list 2013-09-05 22:16:36 +02:00
08a12b6dbb Hide disabled pagination links 2013-09-05 22:01:15 +02:00
876bb3af42 Remove on login and home views useless link "back to home" 2013-09-03 23:56:37 +02:00
7f9f5281e5 Add number of results next to pager 2013-08-28 21:23:26 +02:00
3eb049036e Add warning message when there is no entry in current view 2013-08-27 16:03:50 +02:00
093f1efb21 Update comment 2013-08-27 16:03:50 +02:00
7d1778bbd2 config file for tldp.org 2013-08-27 12:52:35 +02:00
5ebf6eeca1 add configuration to parse bfmtv.com 2013-08-27 11:05:57 +02:00
6fb4600334 fix bug #186: content was empty when enabling downloading pictures 2013-08-26 17:38:01 +02:00
b6b36e1b5a fix bug #182: Wrong title on favorites and archives pages 2013-08-26 17:18:08 +02:00
c51be6b697 Merge pull request #181 from inthepoche/dev
beta4
2013-08-25 12:12:53 -07:00
063fc1a7ba changelog in update 2013-08-25 21:10:01 +02:00
07a5909d73 Merge branch 'dev' of github.com:inthepoche/poche into dev 2013-08-25 20:10:34 +02:00
ec3972361d poche now uses Full Text RSS to fetch content 2013-08-25 20:10:23 +02:00
fccd59e2e3 fix display message for import 2013-08-25 20:08:07 +02:00
eb16ab407b Merge pull request #180 from NumEricR/externalize-css
Externalize some CSS code
2013-08-25 08:04:51 -07:00
e6ba035911 Add bookmarklet id in config page 2013-08-25 16:57:37 +02:00
8e1ed353bb Externalize top link style 2013-08-25 16:42:04 +02:00
a4990dd294 Externalize bookmarklet style 2013-08-25 16:42:04 +02:00
b4f5c46ab0 doc updated 2013-08-25 13:04:03 +02:00
6fa7d08860 poche compatibility test file 2013-08-25 12:40:16 +02:00
3044b21eb6 title page in article view was wrong 2013-08-25 08:49:21 +02:00
d7e8dd590c links to original article were wrong 2013-08-25 08:43:22 +02:00
b66802446d contributing.md updated 2013-08-25 08:32:58 +02:00
d7c2f0cc47 fix bug #175 IP addresses do not appear in view original 2013-08-25 08:01:18 +02:00
27574abf46 README updated with languages 2013-08-25 08:00:51 +02:00
e6a8dd60af Merge pull request #169 from NumEricR/entries-height
Entries height with short description
2013-08-24 22:44:09 -07:00
1cbd70639c Merge pull request #170 from NumEricR/login-button
Login button
2013-08-24 22:43:16 -07:00
217bacc663 Merge pull request #173 from EliasZ/dev
Graceful error-handling with imports and defining where import files are stored
2013-08-24 22:42:37 -07:00
83954e711d Merge pull request #174 from nicofrand/dev
Add support for URLS with digits
2013-08-24 22:36:26 -07:00
149df445d6 Add IPv4 url support (and others beginning by a digit) 2013-08-24 16:50:28 +02:00
38b95e7be9 Add IPv4 url support (and others beginning by a digit) 2013-08-24 16:31:22 +02:00
db4767e22b config template its import section reflects defines 2013-08-24 16:03:49 +02:00
66b6a3b5e2 graceful error-handling with imports, define where import files are stored 2013-08-24 15:59:51 +02:00
7374ff30ef Add IPv4 url support (and others beginning by a digit) 2013-08-24 11:10:07 +02:00
06cf8fb963 Fix login button wording 2013-08-24 10:27:33 +02:00
6a7d779024 Fix issue on entries' height 2013-08-24 09:56:59 +02:00
5ae45084dc CSS clean up again 2013-08-24 09:53:02 +02:00
38b418b7d6 resolve conflicts 2013-08-23 23:18:36 +02:00
fa5f9764ae Add SHAARLI support to view template 2013-08-23 23:11:34 +02:00
fd99a8c02d Revert "Add SHAARLI support to view template"
This reverts commit 0269cd8213.
2013-08-23 23:09:43 +02:00
0269cd8213 Add SHAARLI support to view template 2013-08-23 23:05:45 +02:00
d29b3e5f9e changes in md files 2013-08-23 23:04:56 +02:00
cb844c4238 Restore config modifications related to #162 2013-08-23 22:52:37 +02:00
679b6b85ad preparing to beta4 2013-08-23 22:43:16 +02:00
258d7032bb Clean up the templates markup 2013-08-23 22:38:14 +02:00
6d7defc87c new messages when update / install 2013-08-23 22:24:41 +02:00
b548b2192c Clean markup 2013-08-23 22:24:11 +02:00
f3e4f109a3 Revert "Clean markup in templates"
This reverts commit 8413a63f7d.
2013-08-23 22:23:55 +02:00
8413a63f7d Clean markup in templates 2013-08-23 22:20:04 +02:00
76beb27135 new default content in poche.sqlite 2013-08-23 22:12:37 +02:00
12c9c72984 text in shaarli link 2013-08-23 22:05:14 +02:00
cf9e886076 encode url to share with twitter / email / shaarli 2013-08-23 22:04:09 +02:00
3008e36a3e fix bug #162 links to firefox / chrome / android apps 2013-08-23 22:01:17 +02:00
b64a882696 notice with poche_version undefined' 2013-08-23 21:30:55 +02:00
8be8c44596 gitignore 2013-08-23 21:17:41 +02:00
b48fd706b7 assets directory 2013-08-23 21:16:37 +02:00
d9afe1abd7 Merge branch 'dev' of github.com:inthepoche/poche into dev 2013-08-23 21:15:39 +02:00
70c39d1618 tabs2spaces 2013-08-23 21:07:32 +02:00
ffc966d72f fix bug #151: HTML entities in titles are encoded twice 2013-08-23 21:06:45 +02:00
1e0f9166cc set shaarli to false 2013-08-23 19:49:17 +02:00
c2618ca791 Merge pull request #163 from NumEricR/tools-list
Improve tools list code
2013-08-23 10:42:31 -07:00
4b4afc7322 Add textual content on tools links 2013-08-23 18:45:34 +02:00
8340fedd64 fix bug #150 Add support of text files by setting a default title 2013-08-23 18:29:36 +02:00
b194cf21dc fix bug #147 add db directory in versionning 2013-08-23 18:19:47 +02:00
29773c9729 Fix HTML code of tools lists 2013-08-23 18:10:42 +02:00
fd21828080 updated german translation, thanks to HLFH 2013-08-23 17:43:49 +02:00
a458cff25e Merge pull request #159 from NumEricR/css-cleanup
CSS clean up on style.css file
2013-08-23 08:34:58 -07:00
e93c7c9c46 Merge pull request #160 from nicofrand/dev
Add a print stylesheet
2013-08-23 08:32:03 -07:00
00e4700abb Fix #157 2013-08-23 13:45:46 +02:00
096fb74bf1 CSS clean up on style.css file
Syntax homogenization (spaces, lowercase for hexa, ...)
Code refactoring
Simplification of some selectors
Simplification of "0px" and hexa values
More detailed font stack
Add cursor pointer on ".bouton" elements
2013-08-23 12:12:05 +02:00
64fa45e262 Merge pull request #154 from nicofrand/dev
poche won't import the content of some articles
2013-08-22 23:09:46 -07:00
57c91e427d Merge branch 'dev' of https://github.com/nicofrand/poche into dev 2013-08-22 00:57:11 +02:00
2bf93dc034 Fix #149 2013-08-22 00:49:23 +02:00
746f93c290 fix bug #148 Use of undefined constant POCHE_VERSION 2013-08-20 11:23:32 +02:00
38330bfd65 fix bug #112 link to shaarli 2013-08-20 11:23:00 +02:00
2fdcbd1ac3 typo in gitignore 2013-08-17 21:27:14 +02:00
8d52e6b4e3 Merge branch 'dev' of github.com:inthepoche/poche into dev 2013-08-17 21:26:11 +02:00
e8d1f1e5f0 gitignore 2013-08-17 21:25:53 +02:00
7ba37bd91a Merge pull request #141 from inthepoche/dev
beta3
2013-08-17 11:27:13 -07:00
9067b484ce Merge pull request #140 from inthepoche/master
beta3
2013-08-17 11:23:21 -07:00
c3df0ebdc2 typo 2013-08-16 21:21:25 +02:00
abed0f2122 some precisons in updating poche 2013-08-16 20:40:31 +02:00
12c4098773 rename german folder & files 2013-08-16 20:21:12 +02:00
bb5a7d9ede updating script 2013-08-16 20:19:31 +02:00
1c9cd3463b spanish language, thanks to Nitche 2013-08-16 20:18:11 +02:00
6bc2da5191 ignore myconfig.inc.php in git 2013-08-16 20:17:16 +02:00
042b590606 german language 2013-08-15 20:00:02 +02:00
0b05568c9f fix #138: change pattern to parse url with # 2013-08-15 11:46:40 +02:00
2021fc614e close #137 : Mixed content alert in HTTPS 2013-08-15 11:31:28 +02:00
c0d321c1bf #136 error during readability import 2013-08-15 11:29:28 +02:00
20ced8a806 contributing file 2013-08-15 11:03:36 +02:00
b448a9214c fix #130 : disallow robots 2013-08-15 10:58:25 +02:00
4a2912880f more verif while installing 2013-08-15 10:54:14 +02:00
ca1b0a1a6f install 2013-08-12 19:02:36 +02:00
86848edc84 upgrading README 2013-08-12 18:17:13 +02:00
f93aaa65c4 how installing poche 2013-08-12 18:11:56 +02:00
cd03b575df error msg when install folder exists 2013-08-12 14:27:51 +02:00
667009727a Merge pull request #132 from inthepoche/dev
merge with beta2
2013-08-12 05:14:04 -07:00
6a9e4bfc90 version number 2013-08-11 21:04:04 +02:00
15493df62d fixed #114 : minimum & maximum scale removed 2013-08-11 19:42:52 +02:00
82051a6a6a fixed #128: back to home after mark a link as read is fixed 2013-08-11 19:10:48 +02:00
5bf30b0060 layout 2013-08-10 22:17:00 +02:00
baea9b6de5 new logo 2013-08-10 21:39:11 +02:00
fa0483b361 closed #120: top link 2013-08-10 12:21:36 +02:00
b58e261db9 bug in downloading pictures : article content wasn't updated anymore 2013-08-09 23:30:20 +02:00
d8d1542e52 typo 2013-08-09 22:15:40 +02:00
56ab22d02c only display latest dev version if DEBUG is on 2013-08-09 13:05:35 +02:00
6c15854448 Closed #111 : test of install folder moved before 2013-08-09 11:47:37 +02:00
d91787589b fix #113 - reading time 2013-08-09 08:25:16 +02:00
3d8bded89e debug false 2013-08-08 21:28:37 +02:00
5f9bff0f71 some urls weren't well parsed 2013-08-08 21:13:37 +02:00
301e4c5acb installation 2013-08-08 18:41:40 +02:00
82757801b9 installation 2013-08-08 18:40:55 +02:00
9a8b4ff4ed Merge pull request #109 from inthepoche/dev
merge dev into master
2013-08-08 09:36:10 -07:00
572e758bf2 empty phpunit file 2013-08-08 14:07:43 +02:00
64f8970215 travis img 2013-08-08 13:50:53 +02:00
a3436d4cba travis 2013-08-08 13:49:57 +02:00
07ee09f49a comments 2013-08-08 12:33:02 +02:00
25b5caeed1 ignore doc output 2013-08-08 09:57:18 +02:00
313f4ca785 ignore doc output 2013-08-08 09:55:04 +02:00
ed06f04077 test if /install exists 2013-08-08 09:11:12 +02:00
7aa8ccc47d preparing travis 2013-08-08 07:16:07 +02:00
04b55e97de populate user_config 2013-08-07 21:16:12 +02:00
76e9ca2acf good version of poche.sqlite 2013-08-07 21:00:06 +02:00
77dea8a234 1.0 beta1 2013-08-07 20:05:14 +02:00
01c0e050ad Merge pull request #104 from inthepoche/twig
Twig version on dev branch
2013-08-07 10:41:26 -07:00
339d510fda installation instructions 2013-08-07 19:33:56 +02:00
6e5f8db8bb installation instructions 2013-08-07 19:33:13 +02:00
145f50402e installation instructions 2013-08-07 19:31:11 +02:00
1d517de67b installation instructions 2013-08-07 19:23:00 +02:00
c7ec97ce98 cache to FALSE 2013-08-07 19:14:48 +02:00
b916bcfccc fixes for 1.0-beta 2013-08-07 19:14:28 +02:00
580d60b941 file to update from 0.x to 1.x \o/ 2013-08-07 15:46:17 +02:00
68857cea8c setup of storage 2013-08-07 14:38:58 +02:00
bc1ee8524e postgres 2013-08-07 14:24:07 +02:00
8d3275bee4 multi user 2013-08-06 15:51:48 +02:00
7ce7ec4c94 prepare to multi users 2013-08-06 14:18:03 +02:00
17a9cb9608 minify bookmarklet js 2013-08-06 11:00:42 +02:00
f6df40db46 default sorting 2013-08-05 23:11:10 +02:00
6fb3a2a185 move xsrf test 2013-08-05 22:50:00 +02:00
d28a7ca30f remove js 2013-08-05 21:59:21 +02:00
6a361945ea new design, pagination & more 2013-08-05 21:56:32 +02:00
55821e04c1 share email +twitter / class messages 2013-08-05 15:54:37 +02:00
b161295d0b remove xsrf check 2013-08-05 12:53:56 +02:00
4d0e254491 link to download poche when update available 2013-08-05 12:39:24 +02:00
3252078501 close #69: in the config page, you are notified of the release of a new version 2013-08-05 12:34:16 +02:00
21e0af98eb information about db/poche.sqlite 2013-08-05 10:34:57 +02:00
7f959169b7 copy of poche.sqlite 2013-08-05 10:32:15 +02:00
e4ed594d82 rm poche.sqlite 2013-08-05 10:29:49 +02:00
a12832488d update poche.sqlite 2013-08-05 10:03:59 +02:00
a62788c61e #100: welcome to you, instapaper users 2013-08-05 09:43:33 +02:00
3208d538a7 mysql support 2013-08-05 08:54:42 +02:00
2a1791a4b1 view of an article 2013-08-04 22:51:12 +02:00
63c35580c7 twig implementation 2013-08-04 22:35:08 +02:00
c765c3679f import in poche and not in an external file 2013-08-04 21:42:46 +02:00
eb1af59219 refactoring 2013-08-04 20:58:31 +02:00
3ba5f81b7b twig implementation 2013-08-04 18:07:41 +02:00
07b9821e24 composer 2013-08-04 18:03:30 +02:00
15771f4452 gitignore 2013-08-04 18:02:29 +02:00
3dcab1a782 rm composer.phar 2013-08-04 17:51:33 +02:00
46b77928f7 rm vendor 2013-08-04 17:50:34 +02:00
68abd9c71b gitignore vendor 2013-08-04 17:44:17 +02:00
d51ecb6ea8 ignore vendor 2013-08-04 17:35:02 +02:00
7d2eb7a7b9 composer 2013-08-03 20:47:17 +02:00
4f5b44bd3b twig implementation 2013-08-03 19:26:54 +02:00
2b840e0cfb twig implementation 2013-08-03 08:57:35 +02:00
8cbb2a8802 twig implementation 2013-08-03 08:25:11 +02:00
c67e13e04b new tpl files 2013-08-03 08:24:42 +02:00
afe60d614b remove file 2013-08-03 08:05:02 +02:00
161395d709 mv pochetool pochetools 2013-08-02 23:04:24 +02:00
5ffe5cf541 rename pocheTool -> pocheTools 2013-08-02 23:00:57 +02:00
8069e235fd move Twig in 3rdparty 2013-08-02 22:43:56 +02:00
45161a6402 delete some files 2013-08-02 22:41:21 +02:00
a4565e88ed add Twig & refactor poche 2013-08-02 22:40:51 +02:00
f6c9baab3e rename myTool -> pocheTool and delete some stuff 2013-08-02 21:40:29 +02:00
da2c5d6fc3 display URL at home poche 2013-08-02 13:51:22 +02:00
408f3df28e URL is now encoded 2013-08-02 11:48:57 +02:00
2ee436eaa1 poche / pocket / bolsillo / Tasche & more 2013-08-02 10:39:03 +02:00
02ea9f0769 first test for readability import 2013-08-01 18:34:50 +02:00
99dd7d3877 testing icon in bookmarklet, thanks to @GeekShadow in #94 2013-08-01 11:00:27 +02:00
4d8bcc0c6b set demo mode to false 2013-07-31 19:58:14 +02:00
538cdfa883 fix #70: if demo mode, fields are filled 2013-07-31 19:37:14 +02:00
70b5d24f72 fix #80: add a link in the footer to report a wrong display 2013-07-31 19:21:49 +02:00
3db95a85de update external libs 2013-07-31 19:09:06 +02:00
95a7596cb4 fix #92: add a link to the top 2013-07-31 19:03:02 +02:00
1537 changed files with 88694 additions and 4859 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
assets/*
cache/*
vendor
composer.phar
db/poche.sqlite
inc/poche/config.inc.php

11
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,11 @@
# How contributing
## You found a bug
Please [open a new issue](https://github.com/wallabag/wallabag/issues/new).
To fix the bug quickly, we need some infos:
* your wallabag version (in ./index.php)
* the link you want to save and which causes problem
## You want to fix a bug or to add a feature
Please fork wallabag and work with **the dev branch** only. **Do not work on master branch**.

View File

16
CREDITS
View File

@ -1,16 +0,0 @@
poche is based on :
* ReadItYourself http://www.memiks.fr/readityourself/
* PHP Readability http://www.keyvan.net/2010/08/php-readability/
* Encoding https://github.com/neitanod/forceutf8
* logo by Brightmix http://www.iconfinder.com/icondetails/43256/128/jeans_monotone_pocket_icon
* icons http://icomoon.io
* PHP Simple HTML DOM Parser (for Pocket import) http://simplehtmldom.sourceforge.net/
* Session https://github.com/tontof/kriss_feed/blob/master/src/class/Session.php
poche is developed by Nicolas Lœuillet under the Do What the Fuck You Want to Public License
Contributors :
Nicolas Lœuillet aka nico_somb
Tom.C. aka tmos
PeaceCopathe
Gregoire_M

15
CREDITS.md Normal file
View File

@ -0,0 +1,15 @@
wallabag is based on :
* PHP Readability https://bitbucket.org/fivefilters/php-readability
* Full Text RSS http://code.fivefilters.org/full-text-rss/src
* Encoding https://github.com/neitanod/forceutf8
* logo by Maylis Agniel https://github.com/wallabag/logo
* icons http://icomoon.io
* PHP Simple HTML DOM Parser (for Pocket import) http://simplehtmldom.sourceforge.net/
* Session https://github.com/tontof/kriss_feed/blob/master/src/class/Session.php
* Twig http://twig.sensiolabs.org
* Flash messages https://github.com/plasticbrain/PHP-Flash-Messages
* Pagination https://github.com/daveismyname/pagination
wallabag is developed by Nicolas Lœuillet under the Do What the Fuck You Want to Public License
Contributors : https://github.com/wallabag/wallabag/graphs/contributors

View File

@ -1,54 +1,7 @@
# poche
Abandon Pocket, Instapaper and other Readability service : adopt poche. It is the same, but it is open source.
# what is wallabag ?
wallabag is a self hostable application allowing you to not miss any content anymore. Click, save, read it when you can. It extracts content so that you can read it when you have time.
![poche](http://inthepoche.com/img/logo.png)
The website of poche is [inthepoche.com](http://inthepoche.com).
To test poche, a demo website is online : [demo.inthepoche.com](http://demo.inthepoche.com) (login poche, password poche).
To get news from poche, [follow us on twitter](http://twitter.com/getpoche) or [read the poche blog](http://inthepoche.com/blog). A Google Group is also available : [poche-users](https://groups.google.com/forum/#!forum/poche-users).
[![flattr](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/1265480/poche-a-read-it-later-open-source-system)
## Usage
You can easily add a "poched" page with the bookmarklet.
poche save the entire content of a poched links : text and pictures are stored on your server.
You can :
* read a page in a comfortable reading view
* archive a link
* put a link in favorite
* delete a link
## Requirements & installation
You have to install [sqlite for php](http://www.php.net/manual/en/book.sqlite.php) on your server.
Get the [latest version](https://github.com/inthepoche/poche) of poche on github. Unzip it and upload it on your server. poche must have write access on assets, cache and db directories.
That's all, **poche works** !
## Security
You **have** to protect your db/poche.sqlite file. Modify the virtual host of your website to add this condition :
```apache
<Files ~ "\.sqlite$">
Order allow,deny
Deny from all
</Files>
```
Nginx version:
```nginx
location ~ /(db) {
deny all;
return 404;
}
```
## Import from Pocket
If you want to import your Pocket datas, [export them here](https://getpocket.com/export). Put the HTML file in your poche directory, execute import.php file locally by following instructions. Be careful, the script can take a very long time.
More informations on our website: [wallabag.org](http://wallabag.org)
## License
Copyright © 2010-2013 Nicolas Lœuillet <nicolas@loeuillet.org>

0
assets/.gitignore vendored Executable file
View File

2
cache/.gitignore vendored
View File

@ -1 +1 @@
*
!.htaccess

2
cache/.htaccess vendored Normal file
View File

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

40
check_setup.php Normal file
View File

@ -0,0 +1,40 @@
<?php
// PHP 5.3 minimum
if (version_compare(PHP_VERSION, '5.3.3', '<')) {
die('This software require PHP 5.3.3 minimum');
}
// Short tags must be enabled for PHP < 5.4
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
if (! ini_get('short_open_tag')) {
die('This software require to have short tags enabled, check your php.ini => "short_open_tag = On"');
}
}
// Check PDO Sqlite
if (! extension_loaded('pdo_sqlite')) {
die('PHP extension required: pdo_sqlite');
}
// Check ZIP
if (! extension_loaded('zip')) {
die('PHP extension required: zip');
}
// Check if /cache is writeable
if (! is_writable('cache')) {
die('The directory "cache" must be writeable by your web server user');
}
// Check if /db is writeable
if (! is_writable('db')) {
die('The directory "db" must be writeable by your web server user');
}
// install folder still present, need to install wallabag
if (is_dir('install')) {
require('install/index.php');
exit;
}

7
composer.json Normal file
View File

@ -0,0 +1,7 @@
{
"require": {
"twig/twig": "1.*",
"twig/extensions": "1.0.*",
"umpirsky/twig-gettext-extractor": "1.1.*"
}
}

744
composer.lock generated Normal file
View File

@ -0,0 +1,744 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
],
"hash": "1c8badb14d91f4f3ef1cfae23252a2c4",
"packages": [
{
"name": "symfony/event-dispatcher",
"version": "v2.3.2",
"target-dir": "Symfony/Component/EventDispatcher",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher.git",
"reference": "v2.3.2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.3.2",
"reference": "v2.3.2",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/dependency-injection": "~2.0"
},
"suggest": {
"symfony/dependency-injection": "",
"symfony/http-kernel": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\EventDispatcher\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony EventDispatcher Component",
"homepage": "http://symfony.com",
"time": "2013-05-13 14:36:40"
},
{
"name": "symfony/filesystem",
"version": "v2.3.2",
"target-dir": "Symfony/Component/Filesystem",
"source": {
"type": "git",
"url": "https://github.com/symfony/Filesystem.git",
"reference": "v2.3.2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/v2.3.2",
"reference": "v2.3.2",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Filesystem\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Filesystem Component",
"homepage": "http://symfony.com",
"time": "2013-06-04 15:02:05"
},
{
"name": "symfony/form",
"version": "v2.3.2",
"target-dir": "Symfony/Component/Form",
"source": {
"type": "git",
"url": "https://github.com/symfony/Form.git",
"reference": "v2.3.2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Form/zipball/v2.3.2",
"reference": "v2.3.2",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/event-dispatcher": "~2.1",
"symfony/intl": "~2.3",
"symfony/options-resolver": "~2.1",
"symfony/property-access": "~2.2"
},
"require-dev": {
"symfony/http-foundation": "~2.2",
"symfony/validator": "~2.2"
},
"suggest": {
"symfony/http-foundation": "",
"symfony/validator": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Form\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Form Component",
"homepage": "http://symfony.com",
"time": "2013-07-01 12:24:43"
},
{
"name": "symfony/icu",
"version": "v1.0.0",
"target-dir": "Symfony/Component/Icu",
"source": {
"type": "git",
"url": "https://github.com/symfony/Icu.git",
"reference": "v1.0.0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Icu/zipball/v1.0.0",
"reference": "v1.0.0",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/intl": ">=2.3,<3.0"
},
"type": "library",
"autoload": {
"psr-0": {
"Symfony\\Component\\Icu\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
}
],
"description": "Contains an excerpt of the ICU data and classes to load it.",
"homepage": "http://symfony.com",
"keywords": [
"icu",
"intl"
],
"time": "2013-06-03 18:32:07"
},
{
"name": "symfony/intl",
"version": "v2.3.2",
"target-dir": "Symfony/Component/Intl",
"source": {
"type": "git",
"url": "https://github.com/symfony/Intl.git",
"reference": "v2.3.2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Intl/zipball/v2.3.2",
"reference": "v2.3.2",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/icu": "~1.0-RC"
},
"require-dev": {
"symfony/filesystem": ">=2.1"
},
"suggest": {
"ext-intl": "to use the component with locales other than \"en\""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Intl\\": ""
},
"classmap": [
"Symfony/Component/Intl/Resources/stubs"
],
"files": [
"Symfony/Component/Intl/Resources/stubs/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch",
"homepage": "http://wiedler.ch/igor/"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
},
{
"name": "Eriksen Costa",
"email": "eriksen.costa@infranology.com.br"
}
],
"description": "A PHP replacement layer for the C intl extension that includes additional data from the ICU library.",
"homepage": "http://symfony.com",
"keywords": [
"i18n",
"icu",
"internationalization",
"intl",
"l10n",
"localization"
],
"time": "2013-07-08 13:00:35"
},
{
"name": "symfony/options-resolver",
"version": "v2.3.2",
"target-dir": "Symfony/Component/OptionsResolver",
"source": {
"type": "git",
"url": "https://github.com/symfony/OptionsResolver.git",
"reference": "v2.3.2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/OptionsResolver/zipball/v2.3.2",
"reference": "v2.3.2",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\OptionsResolver\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony OptionsResolver Component",
"homepage": "http://symfony.com",
"keywords": [
"config",
"configuration",
"options"
],
"time": "2013-04-11 06:50:46"
},
{
"name": "symfony/property-access",
"version": "v2.3.2",
"target-dir": "Symfony/Component/PropertyAccess",
"source": {
"type": "git",
"url": "https://github.com/symfony/PropertyAccess.git",
"reference": "v2.3.2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/PropertyAccess/zipball/v2.3.2",
"reference": "v2.3.2",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\PropertyAccess\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony PropertyAccess Component",
"homepage": "http://symfony.com",
"keywords": [
"access",
"array",
"extraction",
"index",
"injection",
"object",
"property",
"property path",
"reflection"
],
"time": "2013-07-01 12:24:43"
},
{
"name": "symfony/routing",
"version": "v2.3.2",
"target-dir": "Symfony/Component/Routing",
"source": {
"type": "git",
"url": "https://github.com/symfony/Routing.git",
"reference": "v2.3.2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Routing/zipball/v2.3.2",
"reference": "v2.3.2",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"doctrine/common": "~2.2",
"psr/log": "~1.0",
"symfony/config": "~2.2",
"symfony/yaml": "~2.0"
},
"suggest": {
"doctrine/common": "",
"symfony/config": "",
"symfony/yaml": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Routing\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Routing Component",
"homepage": "http://symfony.com",
"time": "2013-06-23 08:16:02"
},
{
"name": "symfony/translation",
"version": "v2.3.2",
"target-dir": "Symfony/Component/Translation",
"source": {
"type": "git",
"url": "https://github.com/symfony/Translation.git",
"reference": "v2.3.2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Translation/zipball/v2.3.2",
"reference": "v2.3.2",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/config": "~2.0",
"symfony/yaml": "~2.2"
},
"suggest": {
"symfony/config": "",
"symfony/yaml": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Translation\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Translation Component",
"homepage": "http://symfony.com",
"time": "2013-05-13 14:36:40"
},
{
"name": "symfony/twig-bridge",
"version": "v2.3.2",
"target-dir": "Symfony/Bridge/Twig",
"source": {
"type": "git",
"url": "https://github.com/symfony/TwigBridge.git",
"reference": "v2.3.2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/TwigBridge/zipball/v2.3.2",
"reference": "v2.3.2",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"twig/twig": "~1.11"
},
"require-dev": {
"symfony/form": "2.2.*",
"symfony/http-kernel": "~2.2",
"symfony/routing": "~2.2",
"symfony/security": "~2.0",
"symfony/templating": "~2.1",
"symfony/translation": "~2.2",
"symfony/yaml": "~2.0"
},
"suggest": {
"symfony/form": "",
"symfony/http-kernel": "",
"symfony/routing": "",
"symfony/security": "",
"symfony/templating": "",
"symfony/translation": "",
"symfony/yaml": ""
},
"type": "symfony-bridge",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Bridge\\Twig\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Twig Bridge",
"homepage": "http://symfony.com",
"time": "2013-05-16 10:19:58"
},
{
"name": "twig/extensions",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Twig-extensions.git",
"reference": "v1.0.0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Twig-extensions/zipball/v1.0.0",
"reference": "v1.0.0",
"shasum": ""
},
"require": {
"twig/twig": "1.*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-0": {
"Twig_Extensions_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Common additional features for Twig that do not directly belong in core",
"homepage": "https://github.com/fabpot/Twig-extensions",
"keywords": [
"debug",
"i18n",
"text"
],
"time": "2013-02-28 14:21:30"
},
{
"name": "twig/twig",
"version": "v1.13.2",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Twig.git",
"reference": "v1.13.2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Twig/zipball/v1.13.2",
"reference": "v1.13.2",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13-dev"
}
},
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
],
"time": "2013-08-03 15:35:31"
},
{
"name": "umpirsky/twig-gettext-extractor",
"version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/umpirsky/Twig-Gettext-Extractor.git",
"reference": "1.1.3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/umpirsky/Twig-Gettext-Extractor/zipball/1.1.3",
"reference": "1.1.3",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/filesystem": ">=2.0,<3.0",
"symfony/form": ">=2.0,<3.0",
"symfony/routing": ">=2.0,<3.0",
"symfony/translation": ">=2.0,<3.0",
"symfony/twig-bridge": ">=2.0,<3.0",
"twig/extensions": "1.0.*",
"twig/twig": ">=1.2.0,<2.0-dev"
},
"require-dev": {
"symfony/config": "2.1.*"
},
"bin": [
"twig-gettext-extractor"
],
"type": "application",
"autoload": {
"psr-0": {
"Twig\\Gettext": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Саша Стаменковић",
"email": "umpirsky@gmail.com",
"homepage": "http://umpirsky.com"
}
],
"description": "The Twig Gettext Extractor is Poedit friendly tool which extracts translations from twig templates.",
"time": "2013-02-14 16:41:48"
}
],
"packages-dev": [
],
"aliases": [
],
"minimum-stability": "stable",
"stability-flags": [
],
"platform": [
],
"platform-dev": [
]
}

View File

@ -1,90 +0,0 @@
/*** GENERAL ***/
body {
color: #fff;
background-color: #0d0d0d;
}
a, a:hover, a:visited {
color: #fff;
}
#main ul#links li a.current {
background-color: #000;
color: #fff;
}
#links a:hover, .backhome a:hover{
background-color: #fff;
color: #000;
}
input[type=submit].delete {
background : url('../img/dark/remove.png') no-repeat center center;
color : transparent;
}
#main .entrie {
color: #fff;
background-color: #000;
border: 1px solid #fff;
}
#main .entrie h2 a:hover {
color: #29B1E3;
}
a.fav span {
background: url('../img/dark/star-on.png') no-repeat;
}
a.fav span:hover {
background: url('../img/dark/star-off.png') no-repeat;
}
a.fav-off span {
background: url('../img/dark/star-off.png') no-repeat;
}
a.fav-off span:hover {
background: url('../img/dark/star-on.png') no-repeat;
}
a.archive span {
background: url('../img/dark/checkmark-on.png') no-repeat;
}
a.archive span:hover {
background: url('../img/dark/checkmark-off.png') no-repeat;
}
a.archive-off span {
background: url('../img/dark/checkmark-off.png') no-repeat;
}
a.archive-off span:hover {
background: url('../img/dark/checkmark-on.png') no-repeat;
}
/*** ***/
/*** ARTICLE PAGE ***/
body.article {
color: #fff;
background-color: #0d0d0d;
}
#article header {
border-bottom: 1px solid #222222;
}
#article article {
border-bottom: 1px solid #222222;
}
.vieworiginal a {
color: #888888;
}
.entrie {
background-color: #fff;
}

View File

@ -1,100 +0,0 @@
/*** GENERAL ***/
body {
color: #222222;
background-color: #F1F1F1;
}
a, a:hover, a:visited {
color: #000;
}
.bouton {
background-color: #000;
color: #fff;
border: none;
}
.bouton:hover {
background-color: #222222;
color: #F1F1F1;
}
#main ul#links li a.current {
background-color: #000;
color: #fff;
}
#links a:hover, .backhome a:hover{
background-color: #040707;
color: #F1F1F1;
}
input[type=submit].delete {
background : url('../img/light/remove.png') no-repeat center center;
color : transparent;
}
#main .entrie {
color: #2e2e2e;
background-color: #ffffff;
border: 1px solid #000;
}
#main .entrie h2 a:hover {
color: #F5BE00;
}
a.fav span {
background: url('../img/light/star-on.png') no-repeat;
}
a.fav span:hover {
background: url('../img/light/star-off.png') no-repeat;
}
a.fav-off span {
background: url('../img/light/star-off.png') no-repeat;
}
a.fav-off span:hover {
background: url('../img/light/star-on.png') no-repeat;
}
a.archive span {
background: url('../img/light/checkmark-on.png') no-repeat;
}
a.archive span:hover {
background: url('../img/light/checkmark-off.png') no-repeat;
}
a.archive-off span {
background: url('../img/light/checkmark-off.png') no-repeat;
}
a.archive-off span:hover {
background: url('../img/light/checkmark-on.png') no-repeat;
}
/*** ***/
/*** ARTICLE PAGE ***/
body.article {
color: #222222;
background-color: #F1F1F1;
}
#article header {
border-bottom: 1px solid #222222;
}
#article article {
border-bottom: 1px solid #222222;
}
.vieworiginal a {
color: #888888;
}
.entrie {
background-color: #fff;
}

View File

@ -1,215 +0,0 @@
/*** GENERAL ***/
body {
font: 20px/1.3em Palatino,Georgia,serif;
margin: 10px;
}
header {
text-align: center;
}
.bouton {
border-radius: 2px;
}
#main ul#links {
padding: 0;
list-style-type: none;
text-align: center;
}
#main ul#links li {
display: inline;
}
#main ul#links li a.current {
-webkit-border-radius: 2px;
border-radius: 2px;
}
#main ul#sort {
padding: 0;
list-style-type: none;
text-align: center;
}
#main ul#sort li {
display: inline;
font-size: 0.9em;
}
#main ul#sort img:hover {
cursor: pointer;
}
#main, #article {
margin: 0 auto;
}
#links a, .backhome a{
text-decoration: none;
padding: 5px 10px;
}
#links a:hover, .backhome a:hover{
-webkit-border-radius: 2px;
border-radius: 2px;
}
footer {
text-align: right;
}
/*** ***/
/*** LINKS DISPLAY ***/
#main a.tool {
text-decoration: none;
cursor: pointer;
}
input[type=submit].delete {
width : 16px;
height :16px;
border : none;
cursor: pointer;
font-size : 0;
}
#main #content {
margin-top: 20px;
}
#main .entrie {
padding: 15px;
min-height: 8em;
border: 1px solid;
}
#main .entrie h2 a {
text-decoration: none;
}
.tools {
text-align: right;
}
.tools ul {
padding: 0; margin: 0;
list-style-type: none;
}
.tools ul li {
line-height: 20px;
}
.tools a.tool {
cursor: pointer;
}
#article .tools {
position: relative;
display: inline;
top: 0px;
right: 0px;
width: 100%;
text-align: left;
}
#article .tools ul li{
display: inline;
}
#main .entrie .tools a.tool span, #article .tools a.tool span {
display: inline-block;
width: 16px;
height: 16px;
}
/*** ***/
/*** ARTICLE PAGE ***/
body.article {
font: 20px/1.3em Palatino,Georgia,serif;
}
#article header {
text-align: left;
}
#article header a {
text-decoration: none;
}
.vieworiginal a {
text-decoration: none;
}
.backhome {
display: inline;
}
/*** ***/
#main
{
max-width: 60em; /* 960 px */
margin: 0 auto;
}
#content
{
width: 103.125%; /* 990px */
overflow: hidden;
margin-left: -1.562%; /* 15px */
margin-bottom: -1.875em; /* 30px */
}
.entrie
{
width: 30.303%; /* 300px */
background-color: #fff;
float: left;
margin: 0 1.515% 1.875em; /* 15px 30px */
}
@media only screen and ( max-width: 40em ) /* 640px */
{
.entrie
{
width: 46.876%; /* 305px */
margin-bottom: 0.938em; /* 15px */
}
}
@media only screen and ( max-width: 20em ) /* 320px */
{
#content
{
width: 100%;
margin-left: 0;
}
.entrie
{
width: 100%;
margin-left: 0;
margin-right: 0;
}
}
/*** ***/
/*** MESSAGES ***/
.messages { width: 100%; -moz-border-radius: 4px; border-radius: 4px; display: block; padding: 10px 0; margin: 10px auto 10px; clear: both; }
.messages a.closeMessage { margin: -14px -8px 0 0; display:none; width: 16px; height: 16px; float: right; background: url(../img/messages/close.png) no-repeat; }
/*.messages:hover a.closeMessage { visibility:visible; }*/
.messages p { margin: 3px 0 3px 10px !important; padding: 0 10px 0 23px !important; font-size: 14px; line-height: 16px; }
.messages.error { border: 1px solid #C42608; color: #c00 !important; background: #FFF0EF; }
.messages.error p { background: url(../img/messages/cross.png ) no-repeat 0px 50%; color:#c00 !important; }
.messages.success {background: #E0FBCC; border: 1px solid #6DC70C; }
.messages.success p { background: url(../img/messages/tick.png) no-repeat 0px 50%; color: #2B6301 !important; }
.messages.warning { background: #FFFCD3; border: 1px solid #EBCD41; color: #000; }
.messages.warning p { background: url(../img/messages/warning.png ) no-repeat 0px 50%; color: #5F4E01; }
.messages.information, .messages.info { background: #DFEBFB; border: 1px solid #82AEE7; }
.messages.information p, .messages.info p { background: url(../img/messages/help.png ) no-repeat 0px 50%; color: #064393; }
.messages.information a { text-decoration: underline; }

0
db/.gitignore vendored Normal file
View File

2
db/.htaccess Normal file
View File

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

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 B

View File

@ -1,50 +0,0 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file
*/
set_time_limit(0);
include dirname(__FILE__).'/inc/config.php';
include dirname(__FILE__).'/inc/simple_html_dom.php';
if (!isset($_GET['start'])) {
echo 'Please execute the import script locally, it can take a very long time. <br /><a href="import.php?start">Bye bye Pocket, let\'s go !</a>';
}
else {
$html = new simple_html_dom();
$html->load_file('ril_export.html');
$read = 0;
$errors = array();
foreach($html->find('ul') as $ul)
{
foreach($ul->find('li') as $li)
{
$a = $li->find('a');
$url = $a[0]->href;
action_to_do('add', $url);
if ($read == '1') {
$last_id = $db->getHandle()->lastInsertId();
$sql_update = "UPDATE entries SET is_read=~is_read WHERE id=?";
$params_update = array($last_id);
$query_update = $db->getHandle()->prepare($sql_update);
$query_update->execute($params_update);
}
}
# Pocket génère un fichier HTML avec deux <ul>
# Le premier concerne les éléments non lus
# Le second concerne les éléments archivés
$read = 1;
}
echo 'Import from Pocket completed. <a href="index.php">Welcome to #poche !</a>';
logm('import from pocket completed');
}

49
inc/3rdparty/FlattrItem.class.php vendored Normal file
View File

@ -0,0 +1,49 @@
<?php
/*
* Class for Flattr querying
*/
class FlattrItem {
public $status;
public $urltoflattr;
public $flattrItemURL;
public $numflattrs;
public function checkItem($urltoflattr,$id) {
$this->cacheflattrfile($urltoflattr, $id);
$flattrResponse = file_get_contents(CACHE . "/flattr/".$id.".cache");
if($flattrResponse != FALSE) {
$result = json_decode($flattrResponse);
if (isset($result->message)){
if ($result->message == "flattrable") {
$this->status = FLATTRABLE;
}
}
elseif (is_object($result) && $result->link) {
$this->status = FLATTRED;
$this->flattrItemURL = $result->link;
$this->numflattrs = $result->flattrs;
}
else {
$this->status = NOT_FLATTRABLE;
}
}
else {
$this->status = "FLATTR_ERR_CONNECTION";
}
}
private function cacheflattrfile($urltoflattr, $id) {
if (!is_dir(CACHE . '/flattr')) {
mkdir(CACHE . '/flattr', 0777);
}
// if a cache flattr file for this url already exists and it's been less than one day than it have been updated, see in /cache
if ((!file_exists(CACHE . "/flattr/".$id.".cache")) || (time() - filemtime(CACHE . "/flattr/".$id.".cache") > 86400)) {
$askForFlattr = Tools::getFile(FLATTR_API . $urltoflattr);
$flattrCacheFile = fopen(CACHE . "/flattr/".$id.".cache", 'w+');
fwrite($flattrCacheFile, $askForFlattr);
fclose($flattrCacheFile);
}
}
}

286
inc/3rdparty/Session.class.php vendored Normal file
View File

@ -0,0 +1,286 @@
<?php
/**
* Session management class
*
* http://www.developpez.net/forums/d51943/php/langage/sessions/
* http://sebsauvage.net/wiki/doku.php?id=php:session
* http://sebsauvage.net/wiki/doku.php?id=php:shaarli
*
* Features:
* - Everything is stored on server-side (we do not trust client-side data,
* such as cookie expiration)
* - IP addresses are checked on each access to prevent session cookie hijacking
* (such as Firesheep)
* - Session expires on user inactivity (Session expiration date is
* automatically updated everytime the user accesses a page.)
* - A unique secret key is generated on server-side for this session
* (and never sent over the wire) which can be used to sign forms (HMAC)
* (See $_SESSION['uid'])
* - Token management to prevent XSRF attacks
* - Brute force protection with ban management
*
* TODOs
* - Replace globals with variables in Session class
*
* How to use:
* - http://tontof.net/kriss/php5/session
*/
class Session
{
// Personnalize PHP session name
public static $sessionName = '';
// If the user does not access any page within this time,
// his/her session is considered expired (3600 sec. = 1 hour)
public static $inactivityTimeout = 86400;
// Extra timeout for long sessions (if enabled) (82800 sec. = 23 hours)
public static $longSessionTimeout = 31536000;
// If you get disconnected often or if your IP address changes often.
// Let you disable session cookie hijacking protection
public static $disableSessionProtection = false;
// Ban IP after this many failures.
public static $banAfter = 4;
// Ban duration for IP address after login failures (in seconds).
// (1800 sec. = 30 minutes)
public static $banDuration = 1800;
// File storage for failures and bans. If empty, no ban management.
public static $banFile = '';
/**
* Initialize session
*/
public static function init()
{
// Force cookie path (but do not change lifetime)
$cookie = session_get_cookie_params();
// Default cookie expiration and path.
$cookiedir = '';
if (dirname($_SERVER['SCRIPT_NAME'])!='/') {
$cookiedir = dirname($_SERVER["SCRIPT_NAME"]).'/';
}
$ssl = false;
if (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on") {
$ssl = true;
}
session_set_cookie_params($cookie['lifetime'], $cookiedir, $_SERVER['HTTP_HOST'], $ssl);
// Use cookies to store session.
ini_set('session.use_cookies', 1);
// Force cookies for session (phpsessionID forbidden in URL)
ini_set('session.use_only_cookies', 1);
if (!session_id()) {
// Prevent php to use sessionID in URL if cookies are disabled.
ini_set('session.use_trans_sid', false);
if (!empty(self::$sessionName)) {
session_name(self::$sessionName);
}
session_start();
}
}
/**
* Returns the IP address
* (Used to prevent session cookie hijacking.)
*
* @return string IP addresses
*/
private static function _allIPs()
{
$ip = $_SERVER["REMOTE_ADDR"];
$ip.= isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? '_'.$_SERVER['HTTP_X_FORWARDED_FOR'] : '';
$ip.= isset($_SERVER['HTTP_CLIENT_IP']) ? '_'.$_SERVER['HTTP_CLIENT_IP'] : '';
return $ip;
}
/**
* Check that user/password is correct and then init some SESSION variables.
*
* @param string $login Login reference
* @param string $password Password reference
* @param string $loginTest Login to compare with login reference
* @param string $passwordTest Password to compare with password reference
* @param array $pValues Array of variables to store in SESSION
*
* @return true|false True if login and password are correct, false
* otherwise
*/
public static function login (
$login,
$password,
$loginTest,
$passwordTest,
$longlastingsession,
$pValues = array())
{
self::banInit();
if (self::banCanLogin()) {
if ($login === $loginTest && $password === $passwordTest) {
self::banLoginOk();
// Generate unique random number to sign forms (HMAC)
$_SESSION['uid'] = sha1(uniqid('', true).'_'.mt_rand());
$_SESSION['ip'] = self::_allIPs();
$_SESSION['username'] = $login;
// Set session expiration.
$_SESSION['expires_on'] = time() + self::$inactivityTimeout;
if ($longlastingsession) {
$_SESSION['longlastingsession'] = self::$longSessionTimeout;
$_SESSION['expires_on'] += $_SESSION['longlastingsession'];
}
foreach ($pValues as $key => $value) {
$_SESSION[$key] = $value;
}
return true;
}
self::banLoginFailed();
}
return false;
}
/**
* Unset SESSION variable to force logout
*/
public static function logout()
{
unset($_SESSION['uid'],$_SESSION['ip'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass'], $_SESSION['longlastingsession'], $_SESSION['poche_user']);
}
/**
* Make sure user is logged in.
*
* @return true|false True if user is logged in, false otherwise
*/
public static function isLogged()
{
if (!isset ($_SESSION['uid'])
|| (self::$disableSessionProtection === false
&& $_SESSION['ip'] !== self::_allIPs())
|| time() >= $_SESSION['expires_on']) {
self::logout();
return false;
}
// User accessed a page : Update his/her session expiration date.
$_SESSION['expires_on'] = time() + self::$inactivityTimeout;
if (!empty($_SESSION['longlastingsession'])) {
$_SESSION['expires_on'] += $_SESSION['longlastingsession'];
}
return true;
}
/**
* Create a token, store it in SESSION and return it
*
* @param string $salt to prevent birthday attack
*
* @return string Token created
*/
public static function getToken($salt = '')
{
if (!isset($_SESSION['tokens'])) {
$_SESSION['tokens']=array();
}
// We generate a random string and store it on the server side.
$rnd = sha1(uniqid('', true).'_'.mt_rand().$salt);
$_SESSION['tokens'][$rnd]=1;
return $rnd;
}
/**
* Tells if a token is ok. Using this function will destroy the token.
*
* @param string $token Token to test
*
* @return true|false True if token is correct, false otherwise
*/
public static function isToken($token)
{
if (isset($_SESSION['tokens'][$token])) {
unset($_SESSION['tokens'][$token]); // Token is used: destroy it.
return true; // Token is ok.
}
return false; // Wrong token, or already used.
}
/**
* Signal a failed login. Will ban the IP if too many failures:
*/
public static function banLoginFailed()
{
if (self::$banFile !== '') {
$ip = $_SERVER["REMOTE_ADDR"];
$gb = $GLOBALS['IPBANS'];
if (!isset($gb['FAILURES'][$ip])) {
$gb['FAILURES'][$ip] = 0;
}
$gb['FAILURES'][$ip]++;
if ($gb['FAILURES'][$ip] > (self::$banAfter - 1)) {
$gb['BANS'][$ip]= time() + self::$banDuration;
}
$GLOBALS['IPBANS'] = $gb;
file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>");
}
}
/**
* Signals a successful login. Resets failed login counter.
*/
public static function banLoginOk()
{
if (self::$banFile !== '') {
$ip = $_SERVER["REMOTE_ADDR"];
$gb = $GLOBALS['IPBANS'];
unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]);
$GLOBALS['IPBANS'] = $gb;
file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>");
}
}
/**
* Ban init
*/
public static function banInit()
{
if (self::$banFile !== '') {
if (!is_file(self::$banFile)) {
file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export(array('FAILURES'=>array(), 'BANS'=>array()), true).";\n?>");
}
include self::$banFile;
}
}
/**
* Checks if the user CAN login. If 'true', the user can try to login.
*
* @return boolean true if user is banned, false otherwise
*/
public static function banCanLogin()
{
if (self::$banFile !== '') {
$ip = $_SERVER["REMOTE_ADDR"];
$gb = $GLOBALS['IPBANS'];
if (isset($gb['BANS'][$ip])) {
// User is banned. Check if the ban has expired:
if ($gb['BANS'][$ip] <= time()) {
// Ban expired, user can try to login again.
unset($gb['FAILURES'][$ip]);
unset($gb['BANS'][$ip]);
file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>");
return true; // Ban has expired, user can login.
}
return false; // User is banned.
}
}
return true; // User is not banned.
}
}

View File

@ -1,231 +1,231 @@
<?php
//--------------------------------------------------------------------------------------------------
// Session-Based Flash Messages v1.0
// Copyright 2012 Mike Everhart (http://mikeeverhart.net)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//------------------------------------------------------------------------------
// Description:
//------------------------------------------------------------------------------
//
// Stores messages in Session data to be easily retrieved later on.
// This class includes four different types of messages:
// - Success
// - Error
// - Warning
// - Information
//
// See README for basic usage instructions, or see samples/index.php for more advanced samples
//
//--------------------------------------------------------------------------------------------------
// Changelog
//--------------------------------------------------------------------------------------------------
//
// 2011-05-15 - v1.0 - Initial Version
//
//--------------------------------------------------------------------------------------------------
class Messages {
//-----------------------------------------------------------------------------------------------
// Class Variables
//-----------------------------------------------------------------------------------------------
var $msgId;
var $msgTypes = array( 'help', 'info', 'warning', 'success', 'error' );
var $msgClass = 'messages';
var $msgWrapper = "<div class='%s %s'><a href='#' class='closeMessage'></a>\n%s</div>\n";
var $msgBefore = '<p>';
var $msgAfter = "</p>\n";
/**
* Constructor
* @author Mike Everhart
*/
public function __construct() {
// Generate a unique ID for this user and session
$this->msgId = md5(uniqid());
// Create the session array if it doesnt already exist
if( !array_key_exists('flash_messages', $_SESSION) ) $_SESSION['flash_messages'] = array();
}
/**
* Add a message to the queue
*
* @author Mike Everhart
*
* @param string $type The type of message to add
* @param string $message The message
* @param string $redirect_to (optional) If set, the user will be redirected to this URL
* @return bool
*
*/
public function add($type, $message, $redirect_to=null) {
if( !isset($_SESSION['flash_messages']) ) return false;
if( !isset($type) || !isset($message[0]) ) return false;
// Replace any shorthand codes with their full version
if( strlen(trim($type)) == 1 ) {
$type = str_replace( array('h', 'i', 'w', 'e', 's'), array('help', 'info', 'warning', 'error', 'success'), $type );
// Backwards compatibility...
} elseif( $type == 'information' ) {
$type = 'info';
}
// Make sure it's a valid message type
if( !in_array($type, $this->msgTypes) ) die('"' . strip_tags($type) . '" is not a valid message type!' );
// If the session array doesn't exist, create it
if( !array_key_exists( $type, $_SESSION['flash_messages'] ) ) $_SESSION['flash_messages'][$type] = array();
$_SESSION['flash_messages'][$type][] = $message;
if( !is_null($redirect_to) ) {
header("Location: $redirect_to");
exit();
}
return true;
}
//-----------------------------------------------------------------------------------------------
// display()
// print queued messages to the screen
//-----------------------------------------------------------------------------------------------
/**
* Display the queued messages
*
* @author Mike Everhart
*
* @param string $type Which messages to display
* @param bool $print True = print the messages on the screen
* @return mixed
*
*/
public function display($type='all', $print=true) {
$messages = '';
$data = '';
if( !isset($_SESSION['flash_messages']) ) return false;
if( $type == 'g' || $type == 'growl' ) {
$this->displayGrowlMessages();
return true;
}
// Print a certain type of message?
if( in_array($type, $this->msgTypes) ) {
foreach( $_SESSION['flash_messages'][$type] as $msg ) {
$messages .= $this->msgBefore . $msg . $this->msgAfter;
}
$data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);
// Clear the viewed messages
$this->clear($type);
// Print ALL queued messages
} elseif( $type == 'all' ) {
foreach( $_SESSION['flash_messages'] as $type => $msgArray ) {
$messages = '';
foreach( $msgArray as $msg ) {
$messages .= $this->msgBefore . $msg . $this->msgAfter;
}
$data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);
}
// Clear ALL of the messages
$this->clear();
// Invalid Message Type?
} else {
return false;
}
// Print everything to the screen or return the data
if( $print ) {
echo $data;
} else {
return $data;
}
}
/**
* Check to see if there are any queued error messages
*
* @author Mike Everhart
*
* @return bool true = There ARE error messages
* false = There are NOT any error messages
*
*/
public function hasErrors() {
return empty($_SESSION['flash_messages']['error']) ? false : true;
}
/**
* Check to see if there are any ($type) messages queued
*
* @author Mike Everhart
*
* @param string $type The type of messages to check for
* @return bool
*
*/
public function hasMessages($type=null) {
if( !is_null($type) ) {
if( !empty($_SESSION['flash_messages'][$type]) ) return $_SESSION['flash_messages'][$type];
} else {
foreach( $this->msgTypes as $type ) {
if( !empty($_SESSION['flash_messages']) ) return true;
}
}
return false;
}
/**
* Clear messages from the session data
*
* @author Mike Everhart
*
* @param string $type The type of messages to clear
* @return bool
*
*/
public function clear($type='all') {
if( $type == 'all' ) {
unset($_SESSION['flash_messages']);
} else {
unset($_SESSION['flash_messages'][$type]);
}
return true;
}
public function __toString() { return $this->hasMessages(); }
public function __destruct() {
//$this->clear();
}
} // end class
<?php
//--------------------------------------------------------------------------------------------------
// Session-Based Flash Messages v1.0
// Copyright 2012 Mike Everhart (http://mikeeverhart.net)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//------------------------------------------------------------------------------
// Description:
//------------------------------------------------------------------------------
//
// Stores messages in Session data to be easily retrieved later on.
// This class includes four different types of messages:
// - Success
// - Error
// - Warning
// - Information
//
// See README for basic usage instructions, or see samples/index.php for more advanced samples
//
//--------------------------------------------------------------------------------------------------
// Changelog
//--------------------------------------------------------------------------------------------------
//
// 2011-05-15 - v1.0 - Initial Version
//
//--------------------------------------------------------------------------------------------------
class Messages {
//-----------------------------------------------------------------------------------------------
// Class Variables
//-----------------------------------------------------------------------------------------------
var $msgId;
var $msgTypes = array( 'help', 'info', 'warning', 'success', 'error' );
var $msgClass = 'messages';
var $msgWrapper = "<div class='%s %s'><a href='#' class='closeMessage'>X</a>\n%s</div>\n";
var $msgBefore = '<p>';
var $msgAfter = "</p>\n";
/**
* Constructor
* @author Mike Everhart
*/
public function __construct() {
// Generate a unique ID for this user and session
$this->msgId = md5(uniqid());
// Create the session array if it doesnt already exist
if( !array_key_exists('flash_messages', $_SESSION) ) $_SESSION['flash_messages'] = array();
}
/**
* Add a message to the queue
*
* @author Mike Everhart
*
* @param string $type The type of message to add
* @param string $message The message
* @param string $redirect_to (optional) If set, the user will be redirected to this URL
* @return bool
*
*/
public function add($type, $message, $redirect_to=null) {
if( !isset($_SESSION['flash_messages']) ) return false;
if( !isset($type) || !isset($message[0]) ) return false;
// Replace any shorthand codes with their full version
if( strlen(trim($type)) == 1 ) {
$type = str_replace( array('h', 'i', 'w', 'e', 's'), array('help', 'info', 'warning', 'error', 'success'), $type );
// Backwards compatibility...
} elseif( $type == 'information' ) {
$type = 'info';
}
// Make sure it's a valid message type
if( !in_array($type, $this->msgTypes) ) die('"' . strip_tags($type) . '" is not a valid message type!' );
// If the session array doesn't exist, create it
if( !array_key_exists( $type, $_SESSION['flash_messages'] ) ) $_SESSION['flash_messages'][$type] = array();
$_SESSION['flash_messages'][$type][] = $message;
if( !is_null($redirect_to) ) {
header("Location: $redirect_to");
exit();
}
return true;
}
//-----------------------------------------------------------------------------------------------
// display()
// print queued messages to the screen
//-----------------------------------------------------------------------------------------------
/**
* Display the queued messages
*
* @author Mike Everhart
*
* @param string $type Which messages to display
* @param bool $print True = print the messages on the screen
* @return mixed
*
*/
public function display($type='all', $print=true) {
$messages = '';
$data = '';
if( !isset($_SESSION['flash_messages']) ) return false;
if( $type == 'g' || $type == 'growl' ) {
$this->displayGrowlMessages();
return true;
}
// Print a certain type of message?
if( in_array($type, $this->msgTypes) ) {
foreach( $_SESSION['flash_messages'][$type] as $msg ) {
$messages .= $this->msgBefore . $msg . $this->msgAfter;
}
$data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);
// Clear the viewed messages
$this->clear($type);
// Print ALL queued messages
} elseif( $type == 'all' ) {
foreach( $_SESSION['flash_messages'] as $type => $msgArray ) {
$messages = '';
foreach( $msgArray as $msg ) {
$messages .= $this->msgBefore . $msg . $this->msgAfter;
}
$data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);
}
// Clear ALL of the messages
$this->clear();
// Invalid Message Type?
} else {
return false;
}
// Print everything to the screen or return the data
if( $print ) {
echo $data;
} else {
return $data;
}
}
/**
* Check to see if there are any queued error messages
*
* @author Mike Everhart
*
* @return bool true = There ARE error messages
* false = There are NOT any error messages
*
*/
public function hasErrors() {
return empty($_SESSION['flash_messages']['error']) ? false : true;
}
/**
* Check to see if there are any ($type) messages queued
*
* @author Mike Everhart
*
* @param string $type The type of messages to check for
* @return bool
*
*/
public function hasMessages($type=null) {
if( !is_null($type) ) {
if( !empty($_SESSION['flash_messages'][$type]) ) return $_SESSION['flash_messages'][$type];
} else {
foreach( $this->msgTypes as $type ) {
if( !empty($_SESSION['flash_messages']) ) return true;
}
}
return false;
}
/**
* Clear messages from the session data
*
* @author Mike Everhart
*
* @param string $type The type of messages to clear
* @return bool
*
*/
public function clear($type='all') {
if( $type == 'all' ) {
unset($_SESSION['flash_messages']);
} else {
unset($_SESSION['flash_messages'][$type]);
}
return true;
}
public function __toString() { return $this->hasMessages(); }
public function __destruct() {
//$this->clear();
}
} // end class
?>

407
inc/3rdparty/config.php vendored Executable file
View File

@ -0,0 +1,407 @@
<?php
/* Full-Text RSS config */
// ......IMPORTANT......................................
// .....................................................
// Please do not change this file (config.php) directly.
// Save a copy as custom_config.php and make your
// changes to that instead. It will automatically
// override anything in config.php. Because config.php
// always gets loaded anyway, you can simply specify
// options you'd like to override in custom_config.php.
// .....................................................
global $options;
// Create config object
if (!isset($options)) $options = new stdClass();
// Enable service
// ----------------------
// Set this to false if you want to disable the service.
// If set to false, no feed is produced and users will
// be told that the service is disabled.
$options->enabled = true;
// Debug mode
// ----------------------
// Enable or disable debugging. When enabled debugging works by passing
// &debug to the makefulltextfeed.php querystring.
// Valid values:
// true or 'user' (default) - let user decide
// 'admin' - debug works only for logged in admin users
// false - disabled
$options->debug = true;
// Default entries (without access key)
// ----------------------
// The number of feed items to process when no API key is supplied
// and no &max=x value is supplied in the querystring.
$options->default_entries = 5;
// Max entries (without access key)
// ----------------------
// The maximum number of feed items to process when no access key is supplied.
// This limits the user-supplied &max=x value. For example, if the user
// asks for 20 items to be processed (&max=20), if max_entries is set to
// 10, only 10 will be processed.
$options->max_entries = 10;
// Rewrite relative URLs
// ----------------------
// With this enabled relative URLs found in the extracted content
// block are automatically rewritten as absolute URLs.
$options->rewrite_relative_urls = true;
// Exclude items if extraction fails
// ---------------------------------
// Excludes items from the resulting feed
// if we cannot extract any content from the
// item URL.
// Possible values...
// Enable: true
// Disable: false (default)
// User decides: 'user' (this option will appear on the form)
$options->exclude_items_on_fail = 'user';
// Enable multi-page support
// -------------------------
// If enabled, we will try to follow next page links on multi-page articles.
// Currently this only happens for sites where next_page_link has been defined
// in a site config file.
$options->multipage = true;
// Enable caching
// ----------------------
// Enable this if you'd like to cache results
// for 10 minutes. Cache files are written to disk (in cache/ subfolders
// - which must be writable).
// Initially it's best to keep this disabled to make sure everything works
// as expected. If you have APC enabled, please also see smart_cache in the
// advanced section.
$options->caching = false;
// Cache directory
// ----------------------
// Only used if caching is true
$options->cache_dir = dirname(__FILE__).'/cache';
// Message to prepend (without access key)
// ----------------------
// HTML to insert at the beginning of each feed item when no access key is supplied.
// Substitution tags:
// {url} - Feed item URL
// {effective-url} - Feed item URL after we've followed all redirects
$options->message_to_prepend = '';
// Message to append (without access key)
// ----------------------
// HTML to insert at the end of each feed item when no access key is supplied.
// Substitution tags:
// {url} - Feed item URL
// {effective-url} - Feed item URL after we've followed all redirects
$options->message_to_append = '';
// Error message when content extraction fails (without access key)
// ----------------------
$options->error_message = '[unable to retrieve full-text content]';
// Keep enclosure in feed items
// If enabled, we will try to preserve enclosures if present.
// ----------------------
$options->keep_enclosures = true;
// Detect language
// ---------------
// Should we try and find/guess the language of the article being processed?
// Values will be placed inside the <dc:language> element inside each <item> element
// Possible values:
// * Ignore language: 0
// * Use article/feed metadata (e.g. HTML lang attribute): 1 (default)
// * As above, but guess if not present: 2
// * Always guess: 3
// * User decides: 'user' (value of 0-3 can be passed in querystring: e.g. &l=2)
$options->detect_language = 1;
// Registration key
// ---------------
// The registration key is optional. It is not required to use Full-Text RSS,
// and does not affect the normal operation of Full-Text RSS. It is currently
// only used on admin pages which help you update site patterns with the
// latest version offered by FiveFilters.org. For these admin-related
// tasks to complete, we will require a valid registration key.
// If you would like one, you can purchase the latest version of Full-Text RSS
// at http://fivefilters.org/content-only/
// Your registration key will automatically be sent in the confirmation email.
// Once you have it, simply copy and paste it here.
$options->registration_key = '';
/////////////////////////////////////////////////
/// RESTRICT ACCESS /////////////////////////////
/////////////////////////////////////////////////
// Admin credentials
// ----------------------
// Certain pages/actions, e.g. updating site patterns with our online tool, will require admin credentials.
// To use these pages, enter a password here and you'll be prompted for it when you try to access those pages.
// If no password or username is set, pages requiring admin privelages will be inaccessible.
// The default username is 'admin'.
// If overriding with an environment variable, separate username and password with a colon, e.g.:
// ftr_admin_credentials: admin:my-secret-password
// Example: $options->admin_credentials = array('username'=>'admin', 'password'=>'my-secret-password');
$options->admin_credentials = array('username'=>'admin', 'password'=>'admin');
// URLs to allow
// ----------------------
// List of URLs (or parts of a URL) which the service will accept.
// If the list is empty, all URLs (except those specified in the blocked list below)
// will be permitted.
// Empty: array();
// Non-empty example: array('example.com', 'anothersite.org');
$options->allowed_urls = array();
// URLs to block
// ----------------------
// List of URLs (or parts of a URL) which the service will not accept.
// Note: this list is ignored if allowed_urls is not empty
$options->blocked_urls = array();
// Key holder(s) only?
// ----------------------
// Set this to true if you want to restrict access only to
// those with a key (see below to specify key(s)).
// If set to true, no feed is produced unless a valid
// key is provided.
$options->key_required = false;
// Favour item titles in feed
// ----------------------
// By default, when processing feeds, we assume item titles in the feed
// have not been truncated. So after processing web pages, the extracted titles
// are not used in the generated feed. If you prefer to have extracted titles in
// the feed you can either set this to false, in which case we will always favour
// extracted titles. Alternatively, if set to 'user' (default) we'll use the
// extracted title if you pass '&use_extracted_title' in the querystring.
// Possible values:
// * Favour feed titles: true
// * Favour extracted titles: false
// * Favour feed titles with user override: 'user' (default)
// Note: this has no effect when the input URL is to a web page - in these cases
// we always use the extracted title in the generated feed.
$options->favour_feed_titles = 'user';
// Access keys (password protected access)
// ------------------------------------
// NOTE: You do not need an API key from fivefilters.org to run your own
// copy of the code. This is here if you'd like to restrict access to
// _your_ copy.
// Keys let you group users - those with a key and those without - and
// restrict access to the service to those without a key.
// If you want everyone to access the service in the same way, you can
// leave the array below empty and ignore the access key options further down.
// The options further down let you control how the service should behave
// in each mode.
// Note: Explicitly including the index number (1 and 2 in the examples below)
// is highly recommended (when generating feeds, we encode the key and
// refer to it by index number and hash).
$options->api_keys = array();
// Example:
// $options->api_keys[1] = 'secret-key-1';
// $options->api_keys[2] = 'secret-key-2';
// Default entries (with access key)
// ----------------------
// The number of feed items to process when a valid access key is supplied.
$options->default_entries_with_key = 5;
// Max entries (with access key)
// ----------------------
// The maximum number of feed items to process when a valid access key is supplied.
$options->max_entries_with_key = 10;
/////////////////////////////////////////////////
/// ADVANCED OPTIONS ////////////////////////////
/////////////////////////////////////////////////
// Enable XSS filter?
// ----------------------
// We have not enabled this by default because we assume the majority of
// our users do not display the HTML retrieved by Full-Text RSS
// in a web page without further processing. If you subscribe to our generated
// feeds in your news reader application, it should, if it's good software, already
// filter the resulting HTML for XSS attacks, making it redundant for
// Full-Text RSS do the same. Similarly with frameworks/CMS which display
// feed content - the content should be treated like any other user-submitted content.
//
// If you are writing an application yourself which is processing feeds generated by
// Full-Text RSS, you can either filter the HTML yourself to remove potential XSS attacks
// or enable this option. This might be useful if you are processing our generated
// feeds with JavaScript on the client side - although there's client side xss
// filtering available too, e.g. https://code.google.com/p/google-caja/wiki/JsHtmlSanitizer
//
// If enabled, we'll pass retrieved HTML content through htmLawed with
// safe flag on and style attributes denied, see
// http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/htmLawed_README.htm#s3.6
// Note: if enabled this will also remove certain elements you may want to preserve, such as iframes.
//
// Valid values:
// true - enabled, all content will be filtered
// 'user' (default) - user must pass &xss in makefulltextfeed.php querystring to enable
// false - disabled
$options->xss_filter = 'user';
// Allowed parsers
// ----------------------
// Full-Text RSS attempts to use PHP's libxml extension to process HTML.
// While fast, on some sites it may not always produce good results.
// For these sites, you can specify an alternative HTML parser:
// parser: html5lib
// The html5lib parser is bundled with Full-Text RSS.
// see http://code.google.com/p/html5lib/
//
// To disable HTML parsing with html5lib, you can remove it from this list.
// By default we allow both: libxml and html5lib.
$options->allowed_parsers = array('libxml', 'html5lib');
//$options->allowed_parsers = array('libxml'); //disable html5lib - forcing libxml in all cases
// Enable Cross-Origin Resource Sharing (CORS)
// ----------------------
// If enabled we'll send the following HTTP header
// Access-Control-Allow-Origin: *
// see http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
$options->cors = false;
// Use APC user cache?
// ----------------------
// If enabled we will store site config files (when requested
// for the first time) in APC's user cache. Keys prefixed with 'sc.'
// This improves performance by reducing disk access.
// Note: this has no effect if APC is unavailable on your server.
$options->apc = true;
// Smart cache (experimental)
// ----------------------
// With this option enabled we will not cache to disk immediately.
// We will store the cache key in APC and if it's requested again
// we will cache results to disk. Keys prefixed with 'cache.'
// This improves performance by reducing disk access.
// Note: this has no effect if APC is disabled or unavailable on your server,
// or if you have caching disabled.
$options->smart_cache = true;
// Fingerprints
// ----------------------
// key is fingerprint (fragment to find in HTML)
// value is host name to use for site config lookup if fingerprint matches
$options->fingerprints = array(
// Posterous
'<meta name="generator" content="Posterous"' => array('hostname'=>'fingerprint.posterous.com', 'head'=>true),
// Blogger
'<meta content=\'blogger\' name=\'generator\'' => array('hostname'=>'fingerprint.blogspot.com', 'head'=>true),
'<meta name="generator" content="Blogger"' => array('hostname'=>'fingerprint.blogspot.com', 'head'=>true),
// WordPress (hosted)
// '<meta name="generator" content="WordPress.com"' => array('hostname'=>'fingerprint.wordpress.com', 'head'=>true),
// WordPress (self-hosted and hosted)
'<meta name="generator" content="WordPress' => array('hostname'=>'fingerprint.wordpress.com', 'head'=>true)
);
// User Agent strings - mapping domain names
// ----------------------
// e.g. $options->user_agents = array('example.org' => 'PHP/5.2');
$options->user_agents = array( 'lifehacker.com' => 'PHP/5.2',
'gawker.com' => 'PHP/5.2',
'deadspin.com' => 'PHP/5.2',
'kotaku.com' => 'PHP/5.2',
'jezebel.com' => 'PHP/5.2',
'io9.com' => 'PHP/5.2',
'jalopnik.com' => 'PHP/5.2',
'gizmodo.com' => 'PHP/5.2',
'.wikipedia.org' => 'Mozilla/5.2',
'.fok.nl' => 'Googlebot/2.1',
'getpocket.com' => 'PHP/5.2'
);
// URL Rewriting
// ----------------------
// Currently allows simple string replace of URLs.
// Useful for rewriting certain URLs to point to a single page
// or HTML view. Although using the single_page_link site config
// instruction is the preferred way to do this, sometimes, as
// with Google Docs URLs, it's not possible.
// Note: this might move to the site config file at some point.
$options->rewrite_url = array(
// Rewrite public Google Docs URLs to point to HTML view:
// if a URL contains docs.google.com, replace /Doc? with /View?
'docs.google.com' => array('/Doc?' => '/View?'),
'tnr.com' => array('tnr.com/article/' => 'tnr.com/print/article/'),
'.m.wikipedia.org' => array('.m.wikipedia.org' => '.wikipedia.org'),
'm.vanityfair.com' => array('m.vanityfair.com' => 'www.vanityfair.com')
);
// Content-Type exceptions
// -----------------------
// Here you can define different actions based
// on the Content-Type header returned by server.
// MIME type as key, action as value.
// Valid actions:
// * 'exclude' - exclude this item from the result
// * 'link' - create HTML link to the item
$options->content_type_exc = array(
'application/pdf' => array('action'=>'link', 'name'=>'PDF'),
'image' => array('action'=>'link', 'name'=>'Image'),
'audio' => array('action'=>'link', 'name'=>'Audio'),
'video' => array('action'=>'link', 'name'=>'Video')
);
// Cache directory level
// ----------------------
// Spread cache files over different directories (only used if caching is enabled).
// Used to prevent large number of files in one directory.
// This corresponds to Zend_Cache's hashed_directory_level
// see http://framework.zend.com/manual/en/zend.cache.backends.html
// It's best not to change this if you're unsure.
$options->cache_directory_level = 0;
// Cache cleanup
// -------------
// 0 = script will not clean cache (rename cachecleanup.php and use it for scheduled (e.g. cron) cache cleanup)
// 1 = clean cache everytime the script runs (not recommended)
// 100 = clean cache roughly once every 100 script runs
// x = clean cache roughly once every x script runs
// ...you get the idea :)
$options->cache_cleanup = 100;
/////////////////////////////////////////////////
/// DO NOT CHANGE ANYTHING BELOW THIS ///////////
/////////////////////////////////////////////////
if (!defined('_FF_FTR_VERSION')) define('_FF_FTR_VERSION', '3.1');
if (basename(__FILE__) == 'config.php') {
if (file_exists(dirname(__FILE__).'/custom_config.php')) {
require_once dirname(__FILE__).'/custom_config.php';
}
// check for environment variables - often used on cloud platforms
// environment variables should be prefixed with 'ftr_', e.g.
// ftr_max_entries: 1
// will set the max_entries value to 1.
foreach ($options as $_key=>&$_val) {
$_key = "ftr_$_key";
if (($_env = getenv($_key)) !== false) {
if (is_array($_val)) {
if ($_key === 'ftr_admin_credentials') {
$_val = array_combine(array('username', 'password'), array_map('trim', explode(':', $_env, 2)));
if ($_val === false) $_val = array('username'=>'admin', 'password'=>'');
}
} elseif ($_env === 'true' || $_env === 'false') {
$_val = ($_env === 'true');
} elseif (is_numeric($_env)) {
$_val = (int)$_env;
} else { // string
$_val = $_env;
}
}
}
unset($_key, $_val, $_env);
}

View File

@ -0,0 +1,11 @@
<?php
/**
* This is a stub include that automatically configures the include path.
*/
set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
require_once 'HTMLPurifier/Bootstrap.php';
require_once 'HTMLPurifier.autoload.php';
// vim: et sw=4 sts=4

View File

@ -0,0 +1,27 @@
<?php
/**
* @file
* Convenience file that registers autoload handler for HTML Purifier.
* It also does some sanity checks.
*/
if (function_exists('spl_autoload_register') && function_exists('spl_autoload_unregister')) {
// We need unregister for our pre-registering functionality
HTMLPurifier_Bootstrap::registerAutoload();
if (function_exists('__autoload')) {
// Be polite and ensure that userland autoload gets retained
spl_autoload_register('__autoload');
}
} elseif (!function_exists('__autoload')) {
function __autoload($class)
{
return HTMLPurifier_Bootstrap::autoload($class);
}
}
if (ini_get('zend.ze1_compatibility_mode')) {
trigger_error("HTML Purifier is not compatible with zend.ze1_compatibility_mode; please turn it off", E_USER_ERROR);
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,4 @@
<?php
if (!defined('HTMLPURIFIER_PREFIX')) {
define('HTMLPURIFIER_PREFIX', __DIR__);
}

View File

@ -0,0 +1,25 @@
<?php
/**
* @file
* Defines a function wrapper for HTML Purifier for quick use.
* @note ''HTMLPurifier()'' is NOT the same as ''new HTMLPurifier()''
*/
/**
* Purify HTML.
* @param string $html String HTML to purify
* @param mixed $config Configuration to use, can be any value accepted by
* HTMLPurifier_Config::create()
* @return string
*/
function HTMLPurifier($html, $config = null)
{
static $purifier = false;
if (!$purifier) {
$purifier = new HTMLPurifier();
}
return $purifier->purify($html, $config);
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,229 @@
<?php
/**
* @file
* This file was auto-generated by generate-includes.php and includes all of
* the core files required by HTML Purifier. Use this if performance is a
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
* FILE, changes will be overwritten the next time the script is run.
*
* @version 4.6.0
*
* @warning
* You must *not* include any other HTML Purifier files before this file,
* because 'require' not 'require_once' is used.
*
* @warning
* This file requires that the include path contains the HTML Purifier
* library directory; this is not auto-set.
*/
require 'HTMLPurifier.php';
require 'HTMLPurifier/Arborize.php';
require 'HTMLPurifier/AttrCollections.php';
require 'HTMLPurifier/AttrDef.php';
require 'HTMLPurifier/AttrTransform.php';
require 'HTMLPurifier/AttrTypes.php';
require 'HTMLPurifier/AttrValidator.php';
require 'HTMLPurifier/Bootstrap.php';
require 'HTMLPurifier/Definition.php';
require 'HTMLPurifier/CSSDefinition.php';
require 'HTMLPurifier/ChildDef.php';
require 'HTMLPurifier/Config.php';
require 'HTMLPurifier/ConfigSchema.php';
require 'HTMLPurifier/ContentSets.php';
require 'HTMLPurifier/Context.php';
require 'HTMLPurifier/DefinitionCache.php';
require 'HTMLPurifier/DefinitionCacheFactory.php';
require 'HTMLPurifier/Doctype.php';
require 'HTMLPurifier/DoctypeRegistry.php';
require 'HTMLPurifier/ElementDef.php';
require 'HTMLPurifier/Encoder.php';
require 'HTMLPurifier/EntityLookup.php';
require 'HTMLPurifier/EntityParser.php';
require 'HTMLPurifier/ErrorCollector.php';
require 'HTMLPurifier/ErrorStruct.php';
require 'HTMLPurifier/Exception.php';
require 'HTMLPurifier/Filter.php';
require 'HTMLPurifier/Generator.php';
require 'HTMLPurifier/HTMLDefinition.php';
require 'HTMLPurifier/HTMLModule.php';
require 'HTMLPurifier/HTMLModuleManager.php';
require 'HTMLPurifier/IDAccumulator.php';
require 'HTMLPurifier/Injector.php';
require 'HTMLPurifier/Language.php';
require 'HTMLPurifier/LanguageFactory.php';
require 'HTMLPurifier/Length.php';
require 'HTMLPurifier/Lexer.php';
require 'HTMLPurifier/Node.php';
require 'HTMLPurifier/PercentEncoder.php';
require 'HTMLPurifier/PropertyList.php';
require 'HTMLPurifier/PropertyListIterator.php';
require 'HTMLPurifier/Queue.php';
require 'HTMLPurifier/Strategy.php';
require 'HTMLPurifier/StringHash.php';
require 'HTMLPurifier/StringHashParser.php';
require 'HTMLPurifier/TagTransform.php';
require 'HTMLPurifier/Token.php';
require 'HTMLPurifier/TokenFactory.php';
require 'HTMLPurifier/URI.php';
require 'HTMLPurifier/URIDefinition.php';
require 'HTMLPurifier/URIFilter.php';
require 'HTMLPurifier/URIParser.php';
require 'HTMLPurifier/URIScheme.php';
require 'HTMLPurifier/URISchemeRegistry.php';
require 'HTMLPurifier/UnitConverter.php';
require 'HTMLPurifier/VarParser.php';
require 'HTMLPurifier/VarParserException.php';
require 'HTMLPurifier/Zipper.php';
require 'HTMLPurifier/AttrDef/CSS.php';
require 'HTMLPurifier/AttrDef/Clone.php';
require 'HTMLPurifier/AttrDef/Enum.php';
require 'HTMLPurifier/AttrDef/Integer.php';
require 'HTMLPurifier/AttrDef/Lang.php';
require 'HTMLPurifier/AttrDef/Switch.php';
require 'HTMLPurifier/AttrDef/Text.php';
require 'HTMLPurifier/AttrDef/URI.php';
require 'HTMLPurifier/AttrDef/CSS/Number.php';
require 'HTMLPurifier/AttrDef/CSS/AlphaValue.php';
require 'HTMLPurifier/AttrDef/CSS/Background.php';
require 'HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
require 'HTMLPurifier/AttrDef/CSS/Border.php';
require 'HTMLPurifier/AttrDef/CSS/Color.php';
require 'HTMLPurifier/AttrDef/CSS/Composite.php';
require 'HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
require 'HTMLPurifier/AttrDef/CSS/Filter.php';
require 'HTMLPurifier/AttrDef/CSS/Font.php';
require 'HTMLPurifier/AttrDef/CSS/FontFamily.php';
require 'HTMLPurifier/AttrDef/CSS/Ident.php';
require 'HTMLPurifier/AttrDef/CSS/ImportantDecorator.php';
require 'HTMLPurifier/AttrDef/CSS/Length.php';
require 'HTMLPurifier/AttrDef/CSS/ListStyle.php';
require 'HTMLPurifier/AttrDef/CSS/Multiple.php';
require 'HTMLPurifier/AttrDef/CSS/Percentage.php';
require 'HTMLPurifier/AttrDef/CSS/TextDecoration.php';
require 'HTMLPurifier/AttrDef/CSS/URI.php';
require 'HTMLPurifier/AttrDef/HTML/Bool.php';
require 'HTMLPurifier/AttrDef/HTML/Nmtokens.php';
require 'HTMLPurifier/AttrDef/HTML/Class.php';
require 'HTMLPurifier/AttrDef/HTML/Color.php';
require 'HTMLPurifier/AttrDef/HTML/FrameTarget.php';
require 'HTMLPurifier/AttrDef/HTML/ID.php';
require 'HTMLPurifier/AttrDef/HTML/Pixels.php';
require 'HTMLPurifier/AttrDef/HTML/Length.php';
require 'HTMLPurifier/AttrDef/HTML/LinkTypes.php';
require 'HTMLPurifier/AttrDef/HTML/MultiLength.php';
require 'HTMLPurifier/AttrDef/URI/Email.php';
require 'HTMLPurifier/AttrDef/URI/Host.php';
require 'HTMLPurifier/AttrDef/URI/IPv4.php';
require 'HTMLPurifier/AttrDef/URI/IPv6.php';
require 'HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php';
require 'HTMLPurifier/AttrTransform/Background.php';
require 'HTMLPurifier/AttrTransform/BdoDir.php';
require 'HTMLPurifier/AttrTransform/BgColor.php';
require 'HTMLPurifier/AttrTransform/BoolToCSS.php';
require 'HTMLPurifier/AttrTransform/Border.php';
require 'HTMLPurifier/AttrTransform/EnumToCSS.php';
require 'HTMLPurifier/AttrTransform/ImgRequired.php';
require 'HTMLPurifier/AttrTransform/ImgSpace.php';
require 'HTMLPurifier/AttrTransform/Input.php';
require 'HTMLPurifier/AttrTransform/Lang.php';
require 'HTMLPurifier/AttrTransform/Length.php';
require 'HTMLPurifier/AttrTransform/Name.php';
require 'HTMLPurifier/AttrTransform/NameSync.php';
require 'HTMLPurifier/AttrTransform/Nofollow.php';
require 'HTMLPurifier/AttrTransform/SafeEmbed.php';
require 'HTMLPurifier/AttrTransform/SafeObject.php';
require 'HTMLPurifier/AttrTransform/SafeParam.php';
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
require 'HTMLPurifier/AttrTransform/TargetBlank.php';
require 'HTMLPurifier/AttrTransform/Textarea.php';
require 'HTMLPurifier/ChildDef/Chameleon.php';
require 'HTMLPurifier/ChildDef/Custom.php';
require 'HTMLPurifier/ChildDef/Empty.php';
require 'HTMLPurifier/ChildDef/List.php';
require 'HTMLPurifier/ChildDef/Required.php';
require 'HTMLPurifier/ChildDef/Optional.php';
require 'HTMLPurifier/ChildDef/StrictBlockquote.php';
require 'HTMLPurifier/ChildDef/Table.php';
require 'HTMLPurifier/DefinitionCache/Decorator.php';
require 'HTMLPurifier/DefinitionCache/Null.php';
require 'HTMLPurifier/DefinitionCache/Serializer.php';
require 'HTMLPurifier/DefinitionCache/Decorator/Cleanup.php';
require 'HTMLPurifier/DefinitionCache/Decorator/Memory.php';
require 'HTMLPurifier/HTMLModule/Bdo.php';
require 'HTMLPurifier/HTMLModule/CommonAttributes.php';
require 'HTMLPurifier/HTMLModule/Edit.php';
require 'HTMLPurifier/HTMLModule/Forms.php';
require 'HTMLPurifier/HTMLModule/Hypertext.php';
require 'HTMLPurifier/HTMLModule/Iframe.php';
require 'HTMLPurifier/HTMLModule/Image.php';
require 'HTMLPurifier/HTMLModule/Legacy.php';
require 'HTMLPurifier/HTMLModule/List.php';
require 'HTMLPurifier/HTMLModule/Name.php';
require 'HTMLPurifier/HTMLModule/Nofollow.php';
require 'HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php';
require 'HTMLPurifier/HTMLModule/Object.php';
require 'HTMLPurifier/HTMLModule/Presentation.php';
require 'HTMLPurifier/HTMLModule/Proprietary.php';
require 'HTMLPurifier/HTMLModule/Ruby.php';
require 'HTMLPurifier/HTMLModule/SafeEmbed.php';
require 'HTMLPurifier/HTMLModule/SafeObject.php';
require 'HTMLPurifier/HTMLModule/SafeScripting.php';
require 'HTMLPurifier/HTMLModule/Scripting.php';
require 'HTMLPurifier/HTMLModule/StyleAttribute.php';
require 'HTMLPurifier/HTMLModule/Tables.php';
require 'HTMLPurifier/HTMLModule/Target.php';
require 'HTMLPurifier/HTMLModule/TargetBlank.php';
require 'HTMLPurifier/HTMLModule/Text.php';
require 'HTMLPurifier/HTMLModule/Tidy.php';
require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
require 'HTMLPurifier/HTMLModule/Tidy/Name.php';
require 'HTMLPurifier/HTMLModule/Tidy/Proprietary.php';
require 'HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php';
require 'HTMLPurifier/HTMLModule/Tidy/Strict.php';
require 'HTMLPurifier/HTMLModule/Tidy/Transitional.php';
require 'HTMLPurifier/HTMLModule/Tidy/XHTML.php';
require 'HTMLPurifier/Injector/AutoParagraph.php';
require 'HTMLPurifier/Injector/DisplayLinkURI.php';
require 'HTMLPurifier/Injector/Linkify.php';
require 'HTMLPurifier/Injector/PurifierLinkify.php';
require 'HTMLPurifier/Injector/RemoveEmpty.php';
require 'HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php';
require 'HTMLPurifier/Injector/SafeObject.php';
require 'HTMLPurifier/Lexer/DOMLex.php';
require 'HTMLPurifier/Lexer/DirectLex.php';
require 'HTMLPurifier/Node/Comment.php';
require 'HTMLPurifier/Node/Element.php';
require 'HTMLPurifier/Node/Text.php';
require 'HTMLPurifier/Strategy/Composite.php';
require 'HTMLPurifier/Strategy/Core.php';
require 'HTMLPurifier/Strategy/FixNesting.php';
require 'HTMLPurifier/Strategy/MakeWellFormed.php';
require 'HTMLPurifier/Strategy/RemoveForeignElements.php';
require 'HTMLPurifier/Strategy/ValidateAttributes.php';
require 'HTMLPurifier/TagTransform/Font.php';
require 'HTMLPurifier/TagTransform/Simple.php';
require 'HTMLPurifier/Token/Comment.php';
require 'HTMLPurifier/Token/Tag.php';
require 'HTMLPurifier/Token/Empty.php';
require 'HTMLPurifier/Token/End.php';
require 'HTMLPurifier/Token/Start.php';
require 'HTMLPurifier/Token/Text.php';
require 'HTMLPurifier/URIFilter/DisableExternal.php';
require 'HTMLPurifier/URIFilter/DisableExternalResources.php';
require 'HTMLPurifier/URIFilter/DisableResources.php';
require 'HTMLPurifier/URIFilter/HostBlacklist.php';
require 'HTMLPurifier/URIFilter/MakeAbsolute.php';
require 'HTMLPurifier/URIFilter/Munge.php';
require 'HTMLPurifier/URIFilter/SafeIframe.php';
require 'HTMLPurifier/URIScheme/data.php';
require 'HTMLPurifier/URIScheme/file.php';
require 'HTMLPurifier/URIScheme/ftp.php';
require 'HTMLPurifier/URIScheme/http.php';
require 'HTMLPurifier/URIScheme/https.php';
require 'HTMLPurifier/URIScheme/mailto.php';
require 'HTMLPurifier/URIScheme/news.php';
require 'HTMLPurifier/URIScheme/nntp.php';
require 'HTMLPurifier/VarParser/Flexible.php';
require 'HTMLPurifier/VarParser/Native.php';

View File

@ -0,0 +1,30 @@
<?php
/**
* @file
* Emulation layer for code that used kses(), substituting in HTML Purifier.
*/
require_once dirname(__FILE__) . '/HTMLPurifier.auto.php';
function kses($string, $allowed_html, $allowed_protocols = null)
{
$config = HTMLPurifier_Config::createDefault();
$allowed_elements = array();
$allowed_attributes = array();
foreach ($allowed_html as $element => $attributes) {
$allowed_elements[$element] = true;
foreach ($attributes as $attribute => $x) {
$allowed_attributes["$element.$attribute"] = true;
}
}
$config->set('HTML.AllowedElements', $allowed_elements);
$config->set('HTML.AllowedAttributes', $allowed_attributes);
if ($allowed_protocols !== null) {
$config->set('URI.AllowedSchemes', $allowed_protocols);
}
$purifier = new HTMLPurifier($config);
return $purifier->purify($string);
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,11 @@
<?php
/**
* @file
* Convenience stub file that adds HTML Purifier's library file to the path
* without any other side-effects.
*/
set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
// vim: et sw=4 sts=4

View File

@ -0,0 +1,292 @@
<?php
/*! @mainpage
*
* HTML Purifier is an HTML filter that will take an arbitrary snippet of
* HTML and rigorously test, validate and filter it into a version that
* is safe for output onto webpages. It achieves this by:
*
* -# Lexing (parsing into tokens) the document,
* -# Executing various strategies on the tokens:
* -# Removing all elements not in the whitelist,
* -# Making the tokens well-formed,
* -# Fixing the nesting of the nodes, and
* -# Validating attributes of the nodes; and
* -# Generating HTML from the purified tokens.
*
* However, most users will only need to interface with the HTMLPurifier
* and HTMLPurifier_Config.
*/
/*
HTML Purifier 4.6.0 - Standards Compliant HTML Filtering
Copyright (C) 2006-2008 Edward Z. Yang
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Facade that coordinates HTML Purifier's subsystems in order to purify HTML.
*
* @note There are several points in which configuration can be specified
* for HTML Purifier. The precedence of these (from lowest to
* highest) is as follows:
* -# Instance: new HTMLPurifier($config)
* -# Invocation: purify($html, $config)
* These configurations are entirely independent of each other and
* are *not* merged (this behavior may change in the future).
*
* @todo We need an easier way to inject strategies using the configuration
* object.
*/
class HTMLPurifier
{
/**
* Version of HTML Purifier.
* @type string
*/
public $version = '4.6.0';
/**
* Constant with version of HTML Purifier.
*/
const VERSION = '4.6.0';
/**
* Global configuration object.
* @type HTMLPurifier_Config
*/
public $config;
/**
* Array of extra filter objects to run on HTML,
* for backwards compatibility.
* @type HTMLPurifier_Filter[]
*/
private $filters = array();
/**
* Single instance of HTML Purifier.
* @type HTMLPurifier
*/
private static $instance;
/**
* @type HTMLPurifier_Strategy_Core
*/
protected $strategy;
/**
* @type HTMLPurifier_Generator
*/
protected $generator;
/**
* Resultant context of last run purification.
* Is an array of contexts if the last called method was purifyArray().
* @type HTMLPurifier_Context
*/
public $context;
/**
* Initializes the purifier.
*
* @param HTMLPurifier_Config $config Optional HTMLPurifier_Config object
* for all instances of the purifier, if omitted, a default
* configuration is supplied (which can be overridden on a
* per-use basis).
* The parameter can also be any type that
* HTMLPurifier_Config::create() supports.
*/
public function __construct($config = null)
{
$this->config = HTMLPurifier_Config::create($config);
$this->strategy = new HTMLPurifier_Strategy_Core();
}
/**
* Adds a filter to process the output. First come first serve
*
* @param HTMLPurifier_Filter $filter HTMLPurifier_Filter object
*/
public function addFilter($filter)
{
trigger_error(
'HTMLPurifier->addFilter() is deprecated, use configuration directives' .
' in the Filter namespace or Filter.Custom',
E_USER_WARNING
);
$this->filters[] = $filter;
}
/**
* Filters an HTML snippet/document to be XSS-free and standards-compliant.
*
* @param string $html String of HTML to purify
* @param HTMLPurifier_Config $config Config object for this operation,
* if omitted, defaults to the config object specified during this
* object's construction. The parameter can also be any type
* that HTMLPurifier_Config::create() supports.
*
* @return string Purified HTML
*/
public function purify($html, $config = null)
{
// :TODO: make the config merge in, instead of replace
$config = $config ? HTMLPurifier_Config::create($config) : $this->config;
// implementation is partially environment dependant, partially
// configuration dependant
$lexer = HTMLPurifier_Lexer::create($config);
$context = new HTMLPurifier_Context();
// setup HTML generator
$this->generator = new HTMLPurifier_Generator($config, $context);
$context->register('Generator', $this->generator);
// set up global context variables
if ($config->get('Core.CollectErrors')) {
// may get moved out if other facilities use it
$language_factory = HTMLPurifier_LanguageFactory::instance();
$language = $language_factory->create($config, $context);
$context->register('Locale', $language);
$error_collector = new HTMLPurifier_ErrorCollector($context);
$context->register('ErrorCollector', $error_collector);
}
// setup id_accumulator context, necessary due to the fact that
// AttrValidator can be called from many places
$id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
$context->register('IDAccumulator', $id_accumulator);
$html = HTMLPurifier_Encoder::convertToUTF8($html, $config, $context);
// setup filters
$filter_flags = $config->getBatch('Filter');
$custom_filters = $filter_flags['Custom'];
unset($filter_flags['Custom']);
$filters = array();
foreach ($filter_flags as $filter => $flag) {
if (!$flag) {
continue;
}
if (strpos($filter, '.') !== false) {
continue;
}
$class = "HTMLPurifier_Filter_$filter";
$filters[] = new $class;
}
foreach ($custom_filters as $filter) {
// maybe "HTMLPurifier_Filter_$filter", but be consistent with AutoFormat
$filters[] = $filter;
}
$filters = array_merge($filters, $this->filters);
// maybe prepare(), but later
for ($i = 0, $filter_size = count($filters); $i < $filter_size; $i++) {
$html = $filters[$i]->preFilter($html, $config, $context);
}
// purified HTML
$html =
$this->generator->generateFromTokens(
// list of tokens
$this->strategy->execute(
// list of un-purified tokens
$lexer->tokenizeHTML(
// un-purified HTML
$html,
$config,
$context
),
$config,
$context
)
);
for ($i = $filter_size - 1; $i >= 0; $i--) {
$html = $filters[$i]->postFilter($html, $config, $context);
}
$html = HTMLPurifier_Encoder::convertFromUTF8($html, $config, $context);
$this->context =& $context;
return $html;
}
/**
* Filters an array of HTML snippets
*
* @param string[] $array_of_html Array of html snippets
* @param HTMLPurifier_Config $config Optional config object for this operation.
* See HTMLPurifier::purify() for more details.
*
* @return string[] Array of purified HTML
*/
public function purifyArray($array_of_html, $config = null)
{
$context_array = array();
foreach ($array_of_html as $key => $html) {
$array_of_html[$key] = $this->purify($html, $config);
$context_array[$key] = $this->context;
}
$this->context = $context_array;
return $array_of_html;
}
/**
* Singleton for enforcing just one HTML Purifier in your system
*
* @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype
* HTMLPurifier instance to overload singleton with,
* or HTMLPurifier_Config instance to configure the
* generated version with.
*
* @return HTMLPurifier
*/
public static function instance($prototype = null)
{
if (!self::$instance || $prototype) {
if ($prototype instanceof HTMLPurifier) {
self::$instance = $prototype;
} elseif ($prototype) {
self::$instance = new HTMLPurifier($prototype);
} else {
self::$instance = new HTMLPurifier();
}
}
return self::$instance;
}
/**
* Singleton for enforcing just one HTML Purifier in your system
*
* @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype
* HTMLPurifier instance to overload singleton with,
* or HTMLPurifier_Config instance to configure the
* generated version with.
*
* @return HTMLPurifier
* @note Backwards compatibility, see instance()
*/
public static function getInstance($prototype = null)
{
return HTMLPurifier::instance($prototype);
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,223 @@
<?php
/**
* @file
* This file was auto-generated by generate-includes.php and includes all of
* the core files required by HTML Purifier. This is a convenience stub that
* includes all files using dirname(__FILE__) and require_once. PLEASE DO NOT
* EDIT THIS FILE, changes will be overwritten the next time the script is run.
*
* Changes to include_path are not necessary.
*/
$__dir = dirname(__FILE__);
require_once $__dir . '/HTMLPurifier.php';
require_once $__dir . '/HTMLPurifier/Arborize.php';
require_once $__dir . '/HTMLPurifier/AttrCollections.php';
require_once $__dir . '/HTMLPurifier/AttrDef.php';
require_once $__dir . '/HTMLPurifier/AttrTransform.php';
require_once $__dir . '/HTMLPurifier/AttrTypes.php';
require_once $__dir . '/HTMLPurifier/AttrValidator.php';
require_once $__dir . '/HTMLPurifier/Bootstrap.php';
require_once $__dir . '/HTMLPurifier/Definition.php';
require_once $__dir . '/HTMLPurifier/CSSDefinition.php';
require_once $__dir . '/HTMLPurifier/ChildDef.php';
require_once $__dir . '/HTMLPurifier/Config.php';
require_once $__dir . '/HTMLPurifier/ConfigSchema.php';
require_once $__dir . '/HTMLPurifier/ContentSets.php';
require_once $__dir . '/HTMLPurifier/Context.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache.php';
require_once $__dir . '/HTMLPurifier/DefinitionCacheFactory.php';
require_once $__dir . '/HTMLPurifier/Doctype.php';
require_once $__dir . '/HTMLPurifier/DoctypeRegistry.php';
require_once $__dir . '/HTMLPurifier/ElementDef.php';
require_once $__dir . '/HTMLPurifier/Encoder.php';
require_once $__dir . '/HTMLPurifier/EntityLookup.php';
require_once $__dir . '/HTMLPurifier/EntityParser.php';
require_once $__dir . '/HTMLPurifier/ErrorCollector.php';
require_once $__dir . '/HTMLPurifier/ErrorStruct.php';
require_once $__dir . '/HTMLPurifier/Exception.php';
require_once $__dir . '/HTMLPurifier/Filter.php';
require_once $__dir . '/HTMLPurifier/Generator.php';
require_once $__dir . '/HTMLPurifier/HTMLDefinition.php';
require_once $__dir . '/HTMLPurifier/HTMLModule.php';
require_once $__dir . '/HTMLPurifier/HTMLModuleManager.php';
require_once $__dir . '/HTMLPurifier/IDAccumulator.php';
require_once $__dir . '/HTMLPurifier/Injector.php';
require_once $__dir . '/HTMLPurifier/Language.php';
require_once $__dir . '/HTMLPurifier/LanguageFactory.php';
require_once $__dir . '/HTMLPurifier/Length.php';
require_once $__dir . '/HTMLPurifier/Lexer.php';
require_once $__dir . '/HTMLPurifier/Node.php';
require_once $__dir . '/HTMLPurifier/PercentEncoder.php';
require_once $__dir . '/HTMLPurifier/PropertyList.php';
require_once $__dir . '/HTMLPurifier/PropertyListIterator.php';
require_once $__dir . '/HTMLPurifier/Queue.php';
require_once $__dir . '/HTMLPurifier/Strategy.php';
require_once $__dir . '/HTMLPurifier/StringHash.php';
require_once $__dir . '/HTMLPurifier/StringHashParser.php';
require_once $__dir . '/HTMLPurifier/TagTransform.php';
require_once $__dir . '/HTMLPurifier/Token.php';
require_once $__dir . '/HTMLPurifier/TokenFactory.php';
require_once $__dir . '/HTMLPurifier/URI.php';
require_once $__dir . '/HTMLPurifier/URIDefinition.php';
require_once $__dir . '/HTMLPurifier/URIFilter.php';
require_once $__dir . '/HTMLPurifier/URIParser.php';
require_once $__dir . '/HTMLPurifier/URIScheme.php';
require_once $__dir . '/HTMLPurifier/URISchemeRegistry.php';
require_once $__dir . '/HTMLPurifier/UnitConverter.php';
require_once $__dir . '/HTMLPurifier/VarParser.php';
require_once $__dir . '/HTMLPurifier/VarParserException.php';
require_once $__dir . '/HTMLPurifier/Zipper.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Clone.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Enum.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Integer.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Lang.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Switch.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Text.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Number.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/AlphaValue.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Background.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Border.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Color.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Composite.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Filter.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Font.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/FontFamily.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Ident.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Length.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ListStyle.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Multiple.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Percentage.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/TextDecoration.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/URI.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Bool.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Nmtokens.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Class.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Color.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/FrameTarget.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/ID.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Pixels.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Length.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/LinkTypes.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/MultiLength.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Host.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv4.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv6.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Background.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/BdoDir.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/BgColor.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/BoolToCSS.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Border.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/EnumToCSS.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgRequired.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Input.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/NameSync.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Nofollow.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetBlank.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Empty.php';
require_once $__dir . '/HTMLPurifier/ChildDef/List.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Required.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Optional.php';
require_once $__dir . '/HTMLPurifier/ChildDef/StrictBlockquote.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Table.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Null.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Serializer.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Memory.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Bdo.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/CommonAttributes.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Edit.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Forms.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Hypertext.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Iframe.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/List.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Name.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Nofollow.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Object.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Presentation.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Proprietary.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Ruby.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/SafeEmbed.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/SafeObject.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/SafeScripting.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Scripting.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/StyleAttribute.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetBlank.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Name.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Proprietary.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Strict.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Transitional.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTML.php';
require_once $__dir . '/HTMLPurifier/Injector/AutoParagraph.php';
require_once $__dir . '/HTMLPurifier/Injector/DisplayLinkURI.php';
require_once $__dir . '/HTMLPurifier/Injector/Linkify.php';
require_once $__dir . '/HTMLPurifier/Injector/PurifierLinkify.php';
require_once $__dir . '/HTMLPurifier/Injector/RemoveEmpty.php';
require_once $__dir . '/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php';
require_once $__dir . '/HTMLPurifier/Injector/SafeObject.php';
require_once $__dir . '/HTMLPurifier/Lexer/DOMLex.php';
require_once $__dir . '/HTMLPurifier/Lexer/DirectLex.php';
require_once $__dir . '/HTMLPurifier/Node/Comment.php';
require_once $__dir . '/HTMLPurifier/Node/Element.php';
require_once $__dir . '/HTMLPurifier/Node/Text.php';
require_once $__dir . '/HTMLPurifier/Strategy/Composite.php';
require_once $__dir . '/HTMLPurifier/Strategy/Core.php';
require_once $__dir . '/HTMLPurifier/Strategy/FixNesting.php';
require_once $__dir . '/HTMLPurifier/Strategy/MakeWellFormed.php';
require_once $__dir . '/HTMLPurifier/Strategy/RemoveForeignElements.php';
require_once $__dir . '/HTMLPurifier/Strategy/ValidateAttributes.php';
require_once $__dir . '/HTMLPurifier/TagTransform/Font.php';
require_once $__dir . '/HTMLPurifier/TagTransform/Simple.php';
require_once $__dir . '/HTMLPurifier/Token/Comment.php';
require_once $__dir . '/HTMLPurifier/Token/Tag.php';
require_once $__dir . '/HTMLPurifier/Token/Empty.php';
require_once $__dir . '/HTMLPurifier/Token/End.php';
require_once $__dir . '/HTMLPurifier/Token/Start.php';
require_once $__dir . '/HTMLPurifier/Token/Text.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableResources.php';
require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php';
require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php';
require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php';
require_once $__dir . '/HTMLPurifier/URIFilter/SafeIframe.php';
require_once $__dir . '/HTMLPurifier/URIScheme/data.php';
require_once $__dir . '/HTMLPurifier/URIScheme/file.php';
require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php';
require_once $__dir . '/HTMLPurifier/URIScheme/http.php';
require_once $__dir . '/HTMLPurifier/URIScheme/https.php';
require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php';
require_once $__dir . '/HTMLPurifier/URIScheme/news.php';
require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php';
require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php';
require_once $__dir . '/HTMLPurifier/VarParser/Native.php';

View File

@ -0,0 +1,71 @@
<?php
/**
* Converts a stream of HTMLPurifier_Token into an HTMLPurifier_Node,
* and back again.
*
* @note This transformation is not an equivalence. We mutate the input
* token stream to make it so; see all [MUT] markers in code.
*/
class HTMLPurifier_Arborize
{
public static function arborize($tokens, $config, $context) {
$definition = $config->getHTMLDefinition();
$parent = new HTMLPurifier_Token_Start($definition->info_parent);
$stack = array($parent->toNode());
foreach ($tokens as $token) {
$token->skip = null; // [MUT]
$token->carryover = null; // [MUT]
if ($token instanceof HTMLPurifier_Token_End) {
$token->start = null; // [MUT]
$r = array_pop($stack);
assert($r->name === $token->name);
assert(empty($token->attr));
$r->endCol = $token->col;
$r->endLine = $token->line;
$r->endArmor = $token->armor;
continue;
}
$node = $token->toNode();
$stack[count($stack)-1]->children[] = $node;
if ($token instanceof HTMLPurifier_Token_Start) {
$stack[] = $node;
}
}
assert(count($stack) == 1);
return $stack[0];
}
public static function flatten($node, $config, $context) {
$level = 0;
$nodes = array($level => new HTMLPurifier_Queue(array($node)));
$closingTokens = array();
$tokens = array();
do {
while (!$nodes[$level]->isEmpty()) {
$node = $nodes[$level]->shift(); // FIFO
list($start, $end) = $node->toTokenPair();
if ($level > 0) {
$tokens[] = $start;
}
if ($end !== NULL) {
$closingTokens[$level][] = $end;
}
if ($node instanceof HTMLPurifier_Node_Element) {
$level++;
$nodes[$level] = new HTMLPurifier_Queue();
foreach ($node->children as $childNode) {
$nodes[$level]->push($childNode);
}
}
}
$level--;
if ($level && isset($closingTokens[$level])) {
while ($token = array_pop($closingTokens[$level])) {
$tokens[] = $token;
}
}
} while ($level > 0);
return $tokens;
}
}

View File

@ -0,0 +1,143 @@
<?php
/**
* Defines common attribute collections that modules reference
*/
class HTMLPurifier_AttrCollections
{
/**
* Associative array of attribute collections, indexed by name.
* @type array
*/
public $info = array();
/**
* Performs all expansions on internal data for use by other inclusions
* It also collects all attribute collection extensions from
* modules
* @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance
* @param HTMLPurifier_HTMLModule[] $modules Hash array of HTMLPurifier_HTMLModule members
*/
public function __construct($attr_types, $modules)
{
// load extensions from the modules
foreach ($modules as $module) {
foreach ($module->attr_collections as $coll_i => $coll) {
if (!isset($this->info[$coll_i])) {
$this->info[$coll_i] = array();
}
foreach ($coll as $attr_i => $attr) {
if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) {
// merge in includes
$this->info[$coll_i][$attr_i] = array_merge(
$this->info[$coll_i][$attr_i],
$attr
);
continue;
}
$this->info[$coll_i][$attr_i] = $attr;
}
}
}
// perform internal expansions and inclusions
foreach ($this->info as $name => $attr) {
// merge attribute collections that include others
$this->performInclusions($this->info[$name]);
// replace string identifiers with actual attribute objects
$this->expandIdentifiers($this->info[$name], $attr_types);
}
}
/**
* Takes a reference to an attribute associative array and performs
* all inclusions specified by the zero index.
* @param array &$attr Reference to attribute array
*/
public function performInclusions(&$attr)
{
if (!isset($attr[0])) {
return;
}
$merge = $attr[0];
$seen = array(); // recursion guard
// loop through all the inclusions
for ($i = 0; isset($merge[$i]); $i++) {
if (isset($seen[$merge[$i]])) {
continue;
}
$seen[$merge[$i]] = true;
// foreach attribute of the inclusion, copy it over
if (!isset($this->info[$merge[$i]])) {
continue;
}
foreach ($this->info[$merge[$i]] as $key => $value) {
if (isset($attr[$key])) {
continue;
} // also catches more inclusions
$attr[$key] = $value;
}
if (isset($this->info[$merge[$i]][0])) {
// recursion
$merge = array_merge($merge, $this->info[$merge[$i]][0]);
}
}
unset($attr[0]);
}
/**
* Expands all string identifiers in an attribute array by replacing
* them with the appropriate values inside HTMLPurifier_AttrTypes
* @param array &$attr Reference to attribute array
* @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance
*/
public function expandIdentifiers(&$attr, $attr_types)
{
// because foreach will process new elements we add, make sure we
// skip duplicates
$processed = array();
foreach ($attr as $def_i => $def) {
// skip inclusions
if ($def_i === 0) {
continue;
}
if (isset($processed[$def_i])) {
continue;
}
// determine whether or not attribute is required
if ($required = (strpos($def_i, '*') !== false)) {
// rename the definition
unset($attr[$def_i]);
$def_i = trim($def_i, '*');
$attr[$def_i] = $def;
}
$processed[$def_i] = true;
// if we've already got a literal object, move on
if (is_object($def)) {
// preserve previous required
$attr[$def_i]->required = ($required || $attr[$def_i]->required);
continue;
}
if ($def === false) {
unset($attr[$def_i]);
continue;
}
if ($t = $attr_types->get($def)) {
$attr[$def_i] = $t;
$attr[$def_i]->required = $required;
} else {
unset($attr[$def_i]);
}
}
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,138 @@
<?php
/**
* Base class for all validating attribute definitions.
*
* This family of classes forms the core for not only HTML attribute validation,
* but also any sort of string that needs to be validated or cleaned (which
* means CSS properties and composite definitions are defined here too).
* Besides defining (through code) what precisely makes the string valid,
* subclasses are also responsible for cleaning the code if possible.
*/
abstract class HTMLPurifier_AttrDef
{
/**
* Tells us whether or not an HTML attribute is minimized.
* Has no meaning in other contexts.
* @type bool
*/
public $minimized = false;
/**
* Tells us whether or not an HTML attribute is required.
* Has no meaning in other contexts
* @type bool
*/
public $required = false;
/**
* Validates and cleans passed string according to a definition.
*
* @param string $string String to be validated and cleaned.
* @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object.
* @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object.
*/
abstract public function validate($string, $config, $context);
/**
* Convenience method that parses a string as if it were CDATA.
*
* This method process a string in the manner specified at
* <http://www.w3.org/TR/html4/types.html#h-6.2> by removing
* leading and trailing whitespace, ignoring line feeds, and replacing
* carriage returns and tabs with spaces. While most useful for HTML
* attributes specified as CDATA, it can also be applied to most CSS
* values.
*
* @note This method is not entirely standards compliant, as trim() removes
* more types of whitespace than specified in the spec. In practice,
* this is rarely a problem, as those extra characters usually have
* already been removed by HTMLPurifier_Encoder.
*
* @warning This processing is inconsistent with XML's whitespace handling
* as specified by section 3.3.3 and referenced XHTML 1.0 section
* 4.7. However, note that we are NOT necessarily
* parsing XML, thus, this behavior may still be correct. We
* assume that newlines have been normalized.
*/
public function parseCDATA($string)
{
$string = trim($string);
$string = str_replace(array("\n", "\t", "\r"), ' ', $string);
return $string;
}
/**
* Factory method for creating this class from a string.
* @param string $string String construction info
* @return HTMLPurifier_AttrDef Created AttrDef object corresponding to $string
*/
public function make($string)
{
// default implementation, return a flyweight of this object.
// If $string has an effect on the returned object (i.e. you
// need to overload this method), it is best
// to clone or instantiate new copies. (Instantiation is safer.)
return $this;
}
/**
* Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work
* properly. THIS IS A HACK!
* @param string $string a CSS colour definition
* @return string
*/
protected function mungeRgb($string)
{
return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string);
}
/**
* Parses a possibly escaped CSS string and returns the "pure"
* version of it.
*/
protected function expandCSSEscape($string)
{
// flexibly parse it
$ret = '';
for ($i = 0, $c = strlen($string); $i < $c; $i++) {
if ($string[$i] === '\\') {
$i++;
if ($i >= $c) {
$ret .= '\\';
break;
}
if (ctype_xdigit($string[$i])) {
$code = $string[$i];
for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) {
if (!ctype_xdigit($string[$i])) {
break;
}
$code .= $string[$i];
}
// We have to be extremely careful when adding
// new characters, to make sure we're not breaking
// the encoding.
$char = HTMLPurifier_Encoder::unichr(hexdec($code));
if (HTMLPurifier_Encoder::cleanUTF8($char) === '') {
continue;
}
$ret .= $char;
if ($i < $c && trim($string[$i]) !== '') {
$i--;
}
continue;
}
if ($string[$i] === "\n") {
continue;
}
}
$ret .= $string[$i];
}
return $ret;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,106 @@
<?php
/**
* Validates the HTML attribute style, otherwise known as CSS.
* @note We don't implement the whole CSS specification, so it might be
* difficult to reuse this component in the context of validating
* actual stylesheet declarations.
* @note If we were really serious about validating the CSS, we would
* tokenize the styles and then parse the tokens. Obviously, we
* are not doing that. Doing that could seriously harm performance,
* but would make these components a lot more viable for a CSS
* filtering solution.
*/
class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
{
/**
* @param string $css
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($css, $config, $context)
{
$css = $this->parseCDATA($css);
$definition = $config->getCSSDefinition();
// we're going to break the spec and explode by semicolons.
// This is because semicolon rarely appears in escaped form
// Doing this is generally flaky but fast
// IT MIGHT APPEAR IN URIs, see HTMLPurifier_AttrDef_CSSURI
// for details
$declarations = explode(';', $css);
$propvalues = array();
/**
* Name of the current CSS property being validated.
*/
$property = false;
$context->register('CurrentCSSProperty', $property);
foreach ($declarations as $declaration) {
if (!$declaration) {
continue;
}
if (!strpos($declaration, ':')) {
continue;
}
list($property, $value) = explode(':', $declaration, 2);
$property = trim($property);
$value = trim($value);
$ok = false;
do {
if (isset($definition->info[$property])) {
$ok = true;
break;
}
if (ctype_lower($property)) {
break;
}
$property = strtolower($property);
if (isset($definition->info[$property])) {
$ok = true;
break;
}
} while (0);
if (!$ok) {
continue;
}
// inefficient call, since the validator will do this again
if (strtolower(trim($value)) !== 'inherit') {
// inherit works for everything (but only on the base property)
$result = $definition->info[$property]->validate(
$value,
$config,
$context
);
} else {
$result = 'inherit';
}
if ($result === false) {
continue;
}
$propvalues[$property] = $result;
}
$context->destroy('CurrentCSSProperty');
// procedure does not write the new CSS simultaneously, so it's
// slightly inefficient, but it's the only way of getting rid of
// duplicates. Perhaps config to optimize it, but not now.
$new_declarations = '';
foreach ($propvalues as $prop => $value) {
$new_declarations .= "$prop:$value;";
}
return $new_declarations ? $new_declarations : false;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,34 @@
<?php
class HTMLPurifier_AttrDef_CSS_AlphaValue extends HTMLPurifier_AttrDef_CSS_Number
{
public function __construct()
{
parent::__construct(false); // opacity is non-negative, but we will clamp it
}
/**
* @param string $number
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return string
*/
public function validate($number, $config, $context)
{
$result = parent::validate($number, $config, $context);
if ($result === false) {
return $result;
}
$float = (float)$result;
if ($float < 0.0) {
$result = '0';
}
if ($float > 1.0) {
$result = '1';
}
return $result;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,111 @@
<?php
/**
* Validates shorthand CSS property background.
* @warning Does not support url tokens that have internal spaces.
*/
class HTMLPurifier_AttrDef_CSS_Background extends HTMLPurifier_AttrDef
{
/**
* Local copy of component validators.
* @type HTMLPurifier_AttrDef[]
* @note See HTMLPurifier_AttrDef_Font::$info for a similar impl.
*/
protected $info;
/**
* @param HTMLPurifier_Config $config
*/
public function __construct($config)
{
$def = $config->getCSSDefinition();
$this->info['background-color'] = $def->info['background-color'];
$this->info['background-image'] = $def->info['background-image'];
$this->info['background-repeat'] = $def->info['background-repeat'];
$this->info['background-attachment'] = $def->info['background-attachment'];
$this->info['background-position'] = $def->info['background-position'];
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
// regular pre-processing
$string = $this->parseCDATA($string);
if ($string === '') {
return false;
}
// munge rgb() decl if necessary
$string = $this->mungeRgb($string);
// assumes URI doesn't have spaces in it
$bits = explode(' ', $string); // bits to process
$caught = array();
$caught['color'] = false;
$caught['image'] = false;
$caught['repeat'] = false;
$caught['attachment'] = false;
$caught['position'] = false;
$i = 0; // number of catches
foreach ($bits as $bit) {
if ($bit === '') {
continue;
}
foreach ($caught as $key => $status) {
if ($key != 'position') {
if ($status !== false) {
continue;
}
$r = $this->info['background-' . $key]->validate($bit, $config, $context);
} else {
$r = $bit;
}
if ($r === false) {
continue;
}
if ($key == 'position') {
if ($caught[$key] === false) {
$caught[$key] = '';
}
$caught[$key] .= $r . ' ';
} else {
$caught[$key] = $r;
}
$i++;
break;
}
}
if (!$i) {
return false;
}
if ($caught['position'] !== false) {
$caught['position'] = $this->info['background-position']->
validate($caught['position'], $config, $context);
}
$ret = array();
foreach ($caught as $value) {
if ($value === false) {
continue;
}
$ret[] = $value;
}
if (empty($ret)) {
return false;
}
return implode(' ', $ret);
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,157 @@
<?php
/* W3C says:
[ // adjective and number must be in correct order, even if
// you could switch them without introducing ambiguity.
// some browsers support that syntax
[
<percentage> | <length> | left | center | right
]
[
<percentage> | <length> | top | center | bottom
]?
] |
[ // this signifies that the vertical and horizontal adjectives
// can be arbitrarily ordered, however, there can only be two,
// one of each, or none at all
[
left | center | right
] ||
[
top | center | bottom
]
]
top, left = 0%
center, (none) = 50%
bottom, right = 100%
*/
/* QuirksMode says:
keyword + length/percentage must be ordered correctly, as per W3C
Internet Explorer and Opera, however, support arbitrary ordering. We
should fix it up.
Minor issue though, not strictly necessary.
*/
// control freaks may appreciate the ability to convert these to
// percentages or something, but it's not necessary
/**
* Validates the value of background-position.
*/
class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef
{
/**
* @type HTMLPurifier_AttrDef_CSS_Length
*/
protected $length;
/**
* @type HTMLPurifier_AttrDef_CSS_Percentage
*/
protected $percentage;
public function __construct()
{
$this->length = new HTMLPurifier_AttrDef_CSS_Length();
$this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage();
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$string = $this->parseCDATA($string);
$bits = explode(' ', $string);
$keywords = array();
$keywords['h'] = false; // left, right
$keywords['v'] = false; // top, bottom
$keywords['ch'] = false; // center (first word)
$keywords['cv'] = false; // center (second word)
$measures = array();
$i = 0;
$lookup = array(
'top' => 'v',
'bottom' => 'v',
'left' => 'h',
'right' => 'h',
'center' => 'c'
);
foreach ($bits as $bit) {
if ($bit === '') {
continue;
}
// test for keyword
$lbit = ctype_lower($bit) ? $bit : strtolower($bit);
if (isset($lookup[$lbit])) {
$status = $lookup[$lbit];
if ($status == 'c') {
if ($i == 0) {
$status = 'ch';
} else {
$status = 'cv';
}
}
$keywords[$status] = $lbit;
$i++;
}
// test for length
$r = $this->length->validate($bit, $config, $context);
if ($r !== false) {
$measures[] = $r;
$i++;
}
// test for percentage
$r = $this->percentage->validate($bit, $config, $context);
if ($r !== false) {
$measures[] = $r;
$i++;
}
}
if (!$i) {
return false;
} // no valid values were caught
$ret = array();
// first keyword
if ($keywords['h']) {
$ret[] = $keywords['h'];
} elseif ($keywords['ch']) {
$ret[] = $keywords['ch'];
$keywords['cv'] = false; // prevent re-use: center = center center
} elseif (count($measures)) {
$ret[] = array_shift($measures);
}
if ($keywords['v']) {
$ret[] = $keywords['v'];
} elseif ($keywords['cv']) {
$ret[] = $keywords['cv'];
} elseif (count($measures)) {
$ret[] = array_shift($measures);
}
if (empty($ret)) {
return false;
}
return implode(' ', $ret);
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,56 @@
<?php
/**
* Validates the border property as defined by CSS.
*/
class HTMLPurifier_AttrDef_CSS_Border extends HTMLPurifier_AttrDef
{
/**
* Local copy of properties this property is shorthand for.
* @type HTMLPurifier_AttrDef[]
*/
protected $info = array();
/**
* @param HTMLPurifier_Config $config
*/
public function __construct($config)
{
$def = $config->getCSSDefinition();
$this->info['border-width'] = $def->info['border-width'];
$this->info['border-style'] = $def->info['border-style'];
$this->info['border-top-color'] = $def->info['border-top-color'];
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$string = $this->parseCDATA($string);
$string = $this->mungeRgb($string);
$bits = explode(' ', $string);
$done = array(); // segments we've finished
$ret = ''; // return value
foreach ($bits as $bit) {
foreach ($this->info as $propname => $validator) {
if (isset($done[$propname])) {
continue;
}
$r = $validator->validate($bit, $config, $context);
if ($r !== false) {
$ret .= $r . ' ';
$done[$propname] = true;
break;
}
}
}
return rtrim($ret);
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,105 @@
<?php
/**
* Validates Color as defined by CSS.
*/
class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
{
/**
* @param string $color
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($color, $config, $context)
{
static $colors = null;
if ($colors === null) {
$colors = $config->get('Core.ColorKeywords');
}
$color = trim($color);
if ($color === '') {
return false;
}
$lower = strtolower($color);
if (isset($colors[$lower])) {
return $colors[$lower];
}
if (strpos($color, 'rgb(') !== false) {
// rgb literal handling
$length = strlen($color);
if (strpos($color, ')') !== $length - 1) {
return false;
}
$triad = substr($color, 4, $length - 4 - 1);
$parts = explode(',', $triad);
if (count($parts) !== 3) {
return false;
}
$type = false; // to ensure that they're all the same type
$new_parts = array();
foreach ($parts as $part) {
$part = trim($part);
if ($part === '') {
return false;
}
$length = strlen($part);
if ($part[$length - 1] === '%') {
// handle percents
if (!$type) {
$type = 'percentage';
} elseif ($type !== 'percentage') {
return false;
}
$num = (float)substr($part, 0, $length - 1);
if ($num < 0) {
$num = 0;
}
if ($num > 100) {
$num = 100;
}
$new_parts[] = "$num%";
} else {
// handle integers
if (!$type) {
$type = 'integer';
} elseif ($type !== 'integer') {
return false;
}
$num = (int)$part;
if ($num < 0) {
$num = 0;
}
if ($num > 255) {
$num = 255;
}
$new_parts[] = (string)$num;
}
}
$new_triad = implode(',', $new_parts);
$color = "rgb($new_triad)";
} else {
// hexadecimal handling
if ($color[0] === '#') {
$hex = substr($color, 1);
} else {
$hex = $color;
$color = '#' . $color;
}
$length = strlen($hex);
if ($length !== 3 && $length !== 6) {
return false;
}
if (!ctype_xdigit($hex)) {
return false;
}
}
return $color;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,48 @@
<?php
/**
* Allows multiple validators to attempt to validate attribute.
*
* Composite is just what it sounds like: a composite of many validators.
* This means that multiple HTMLPurifier_AttrDef objects will have a whack
* at the string. If one of them passes, that's what is returned. This is
* especially useful for CSS values, which often are a choice between
* an enumerated set of predefined values or a flexible data type.
*/
class HTMLPurifier_AttrDef_CSS_Composite extends HTMLPurifier_AttrDef
{
/**
* List of objects that may process strings.
* @type HTMLPurifier_AttrDef[]
* @todo Make protected
*/
public $defs;
/**
* @param HTMLPurifier_AttrDef[] $defs List of HTMLPurifier_AttrDef objects
*/
public function __construct($defs)
{
$this->defs = $defs;
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
foreach ($this->defs as $i => $def) {
$result = $this->defs[$i]->validate($string, $config, $context);
if ($result !== false) {
return $result;
}
}
return false;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,44 @@
<?php
/**
* Decorator which enables CSS properties to be disabled for specific elements.
*/
class HTMLPurifier_AttrDef_CSS_DenyElementDecorator extends HTMLPurifier_AttrDef
{
/**
* @type HTMLPurifier_AttrDef
*/
public $def;
/**
* @type string
*/
public $element;
/**
* @param HTMLPurifier_AttrDef $def Definition to wrap
* @param string $element Element to deny
*/
public function __construct($def, $element)
{
$this->def = $def;
$this->element = $element;
}
/**
* Checks if CurrentToken is set and equal to $this->element
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$token = $context->get('CurrentToken', true);
if ($token && $token->name == $this->element) {
return false;
}
return $this->def->validate($string, $config, $context);
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,77 @@
<?php
/**
* Microsoft's proprietary filter: CSS property
* @note Currently supports the alpha filter. In the future, this will
* probably need an extensible framework
*/
class HTMLPurifier_AttrDef_CSS_Filter extends HTMLPurifier_AttrDef
{
/**
* @type HTMLPurifier_AttrDef_Integer
*/
protected $intValidator;
public function __construct()
{
$this->intValidator = new HTMLPurifier_AttrDef_Integer();
}
/**
* @param string $value
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($value, $config, $context)
{
$value = $this->parseCDATA($value);
if ($value === 'none') {
return $value;
}
// if we looped this we could support multiple filters
$function_length = strcspn($value, '(');
$function = trim(substr($value, 0, $function_length));
if ($function !== 'alpha' &&
$function !== 'Alpha' &&
$function !== 'progid:DXImageTransform.Microsoft.Alpha'
) {
return false;
}
$cursor = $function_length + 1;
$parameters_length = strcspn($value, ')', $cursor);
$parameters = substr($value, $cursor, $parameters_length);
$params = explode(',', $parameters);
$ret_params = array();
$lookup = array();
foreach ($params as $param) {
list($key, $value) = explode('=', $param);
$key = trim($key);
$value = trim($value);
if (isset($lookup[$key])) {
continue;
}
if ($key !== 'opacity') {
continue;
}
$value = $this->intValidator->validate($value, $config, $context);
if ($value === false) {
continue;
}
$int = (int)$value;
if ($int > 100) {
$value = '100';
}
if ($int < 0) {
$value = '0';
}
$ret_params[] = "$key=$value";
$lookup[$key] = true;
}
$ret_parameters = implode(',', $ret_params);
$ret_function = "$function($ret_parameters)";
return $ret_function;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,176 @@
<?php
/**
* Validates shorthand CSS property font.
*/
class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef
{
/**
* Local copy of validators
* @type HTMLPurifier_AttrDef[]
* @note If we moved specific CSS property definitions to their own
* classes instead of having them be assembled at run time by
* CSSDefinition, this wouldn't be necessary. We'd instantiate
* our own copies.
*/
protected $info = array();
/**
* @param HTMLPurifier_Config $config
*/
public function __construct($config)
{
$def = $config->getCSSDefinition();
$this->info['font-style'] = $def->info['font-style'];
$this->info['font-variant'] = $def->info['font-variant'];
$this->info['font-weight'] = $def->info['font-weight'];
$this->info['font-size'] = $def->info['font-size'];
$this->info['line-height'] = $def->info['line-height'];
$this->info['font-family'] = $def->info['font-family'];
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
static $system_fonts = array(
'caption' => true,
'icon' => true,
'menu' => true,
'message-box' => true,
'small-caption' => true,
'status-bar' => true
);
// regular pre-processing
$string = $this->parseCDATA($string);
if ($string === '') {
return false;
}
// check if it's one of the keywords
$lowercase_string = strtolower($string);
if (isset($system_fonts[$lowercase_string])) {
return $lowercase_string;
}
$bits = explode(' ', $string); // bits to process
$stage = 0; // this indicates what we're looking for
$caught = array(); // which stage 0 properties have we caught?
$stage_1 = array('font-style', 'font-variant', 'font-weight');
$final = ''; // output
for ($i = 0, $size = count($bits); $i < $size; $i++) {
if ($bits[$i] === '') {
continue;
}
switch ($stage) {
case 0: // attempting to catch font-style, font-variant or font-weight
foreach ($stage_1 as $validator_name) {
if (isset($caught[$validator_name])) {
continue;
}
$r = $this->info[$validator_name]->validate(
$bits[$i],
$config,
$context
);
if ($r !== false) {
$final .= $r . ' ';
$caught[$validator_name] = true;
break;
}
}
// all three caught, continue on
if (count($caught) >= 3) {
$stage = 1;
}
if ($r !== false) {
break;
}
case 1: // attempting to catch font-size and perhaps line-height
$found_slash = false;
if (strpos($bits[$i], '/') !== false) {
list($font_size, $line_height) =
explode('/', $bits[$i]);
if ($line_height === '') {
// ooh, there's a space after the slash!
$line_height = false;
$found_slash = true;
}
} else {
$font_size = $bits[$i];
$line_height = false;
}
$r = $this->info['font-size']->validate(
$font_size,
$config,
$context
);
if ($r !== false) {
$final .= $r;
// attempt to catch line-height
if ($line_height === false) {
// we need to scroll forward
for ($j = $i + 1; $j < $size; $j++) {
if ($bits[$j] === '') {
continue;
}
if ($bits[$j] === '/') {
if ($found_slash) {
return false;
} else {
$found_slash = true;
continue;
}
}
$line_height = $bits[$j];
break;
}
} else {
// slash already found
$found_slash = true;
$j = $i;
}
if ($found_slash) {
$i = $j;
$r = $this->info['line-height']->validate(
$line_height,
$config,
$context
);
if ($r !== false) {
$final .= '/' . $r;
}
}
$final .= ' ';
$stage = 2;
break;
}
return false;
case 2: // attempting to catch font-family
$font_family =
implode(' ', array_slice($bits, $i, $size - $i));
$r = $this->info['font-family']->validate(
$font_family,
$config,
$context
);
if ($r !== false) {
$final .= $r . ' ';
// processing completed successfully
return rtrim($final);
}
return false;
}
}
return false;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,219 @@
<?php
/**
* Validates a font family list according to CSS spec
*/
class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
{
protected $mask = null;
public function __construct()
{
$this->mask = '_- ';
for ($c = 'a'; $c <= 'z'; $c++) {
$this->mask .= $c;
}
for ($c = 'A'; $c <= 'Z'; $c++) {
$this->mask .= $c;
}
for ($c = '0'; $c <= '9'; $c++) {
$this->mask .= $c;
} // cast-y, but should be fine
// special bytes used by UTF-8
for ($i = 0x80; $i <= 0xFF; $i++) {
// We don't bother excluding invalid bytes in this range,
// because the our restriction of well-formed UTF-8 will
// prevent these from ever occurring.
$this->mask .= chr($i);
}
/*
PHP's internal strcspn implementation is
O(length of string * length of mask), making it inefficient
for large masks. However, it's still faster than
preg_match 8)
for (p = s1;;) {
spanp = s2;
do {
if (*spanp == c || p == s1_end) {
return p - s1;
}
} while (spanp++ < (s2_end - 1));
c = *++p;
}
*/
// possible optimization: invert the mask.
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
static $generic_names = array(
'serif' => true,
'sans-serif' => true,
'monospace' => true,
'fantasy' => true,
'cursive' => true
);
$allowed_fonts = $config->get('CSS.AllowedFonts');
// assume that no font names contain commas in them
$fonts = explode(',', $string);
$final = '';
foreach ($fonts as $font) {
$font = trim($font);
if ($font === '') {
continue;
}
// match a generic name
if (isset($generic_names[$font])) {
if ($allowed_fonts === null || isset($allowed_fonts[$font])) {
$final .= $font . ', ';
}
continue;
}
// match a quoted name
if ($font[0] === '"' || $font[0] === "'") {
$length = strlen($font);
if ($length <= 2) {
continue;
}
$quote = $font[0];
if ($font[$length - 1] !== $quote) {
continue;
}
$font = substr($font, 1, $length - 2);
}
$font = $this->expandCSSEscape($font);
// $font is a pure representation of the font name
if ($allowed_fonts !== null && !isset($allowed_fonts[$font])) {
continue;
}
if (ctype_alnum($font) && $font !== '') {
// very simple font, allow it in unharmed
$final .= $font . ', ';
continue;
}
// bugger out on whitespace. form feed (0C) really
// shouldn't show up regardless
$font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font);
// Here, there are various classes of characters which need
// to be treated differently:
// - Alphanumeric characters are essentially safe. We
// handled these above.
// - Spaces require quoting, though most parsers will do
// the right thing if there aren't any characters that
// can be misinterpreted
// - Dashes rarely occur, but they fairly unproblematic
// for parsing/rendering purposes.
// The above characters cover the majority of Western font
// names.
// - Arbitrary Unicode characters not in ASCII. Because
// most parsers give little thought to Unicode, treatment
// of these codepoints is basically uniform, even for
// punctuation-like codepoints. These characters can
// show up in non-Western pages and are supported by most
// major browsers, for example: " 明朝" is a
// legitimate font-name
// <http://ja.wikipedia.org/wiki/MS_明朝>. See
// the CSS3 spec for more examples:
// <http://www.w3.org/TR/2011/WD-css3-fonts-20110324/localizedfamilynames.png>
// You can see live samples of these on the Internet:
// <http://www.google.co.jp/search?q=font-family++明朝|ゴシック>
// However, most of these fonts have ASCII equivalents:
// for example, 'MS Mincho', and it's considered
// professional to use ASCII font names instead of
// Unicode font names. Thanks Takeshi Terada for
// providing this information.
// The following characters, to my knowledge, have not been
// used to name font names.
// - Single quote. While theoretically you might find a
// font name that has a single quote in its name (serving
// as an apostrophe, e.g. Dave's Scribble), I haven't
// been able to find any actual examples of this.
// Internet Explorer's cssText translation (which I
// believe is invoked by innerHTML) normalizes any
// quoting to single quotes, and fails to escape single
// quotes. (Note that this is not IE's behavior for all
// CSS properties, just some sort of special casing for
// font-family). So a single quote *cannot* be used
// safely in the font-family context if there will be an
// innerHTML/cssText translation. Note that Firefox 3.x
// does this too.
// - Double quote. In IE, these get normalized to
// single-quotes, no matter what the encoding. (Fun
// fact, in IE8, the 'content' CSS property gained
// support, where they special cased to preserve encoded
// double quotes, but still translate unadorned double
// quotes into single quotes.) So, because their
// fixpoint behavior is identical to single quotes, they
// cannot be allowed either. Firefox 3.x displays
// single-quote style behavior.
// - Backslashes are reduced by one (so \\ -> \) every
// iteration, so they cannot be used safely. This shows
// up in IE7, IE8 and FF3
// - Semicolons, commas and backticks are handled properly.
// - The rest of the ASCII punctuation is handled properly.
// We haven't checked what browsers do to unadorned
// versions, but this is not important as long as the
// browser doesn't /remove/ surrounding quotes (as IE does
// for HTML).
//
// With these results in hand, we conclude that there are
// various levels of safety:
// - Paranoid: alphanumeric, spaces and dashes(?)
// - International: Paranoid + non-ASCII Unicode
// - Edgy: Everything except quotes, backslashes
// - NoJS: Standards compliance, e.g. sod IE. Note that
// with some judicious character escaping (since certain
// types of escaping doesn't work) this is theoretically
// OK as long as innerHTML/cssText is not called.
// We believe that international is a reasonable default
// (that we will implement now), and once we do more
// extensive research, we may feel comfortable with dropping
// it down to edgy.
// Edgy: alphanumeric, spaces, dashes, underscores and Unicode. Use of
// str(c)spn assumes that the string was already well formed
// Unicode (which of course it is).
if (strspn($font, $this->mask) !== strlen($font)) {
continue;
}
// Historical:
// In the absence of innerHTML/cssText, these ugly
// transforms don't pose a security risk (as \\ and \"
// might--these escapes are not supported by most browsers).
// We could try to be clever and use single-quote wrapping
// when there is a double quote present, but I have choosen
// not to implement that. (NOTE: you can reduce the amount
// of escapes by one depending on what quoting style you use)
// $font = str_replace('\\', '\\5C ', $font);
// $font = str_replace('"', '\\22 ', $font);
// $font = str_replace("'", '\\27 ', $font);
// font possibly with spaces, requires quoting
$final .= "'$font', ";
}
$final = rtrim($final, ', ');
if ($final === '') {
return false;
}
return $final;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,32 @@
<?php
/**
* Validates based on {ident} CSS grammar production
*/
class HTMLPurifier_AttrDef_CSS_Ident extends HTMLPurifier_AttrDef
{
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$string = trim($string);
// early abort: '' and '0' (strings that convert to false) are invalid
if (!$string) {
return false;
}
$pattern = '/^(-?[A-Za-z_][A-Za-z_\-0-9]*)$/';
if (!preg_match($pattern, $string)) {
return false;
}
return $string;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,56 @@
<?php
/**
* Decorator which enables !important to be used in CSS values.
*/
class HTMLPurifier_AttrDef_CSS_ImportantDecorator extends HTMLPurifier_AttrDef
{
/**
* @type HTMLPurifier_AttrDef
*/
public $def;
/**
* @type bool
*/
public $allow;
/**
* @param HTMLPurifier_AttrDef $def Definition to wrap
* @param bool $allow Whether or not to allow !important
*/
public function __construct($def, $allow = false)
{
$this->def = $def;
$this->allow = $allow;
}
/**
* Intercepts and removes !important if necessary
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
// test for ! and important tokens
$string = trim($string);
$is_important = false;
// :TODO: optimization: test directly for !important and ! important
if (strlen($string) >= 9 && substr($string, -9) === 'important') {
$temp = rtrim(substr($string, 0, -9));
// use a temp, because we might want to restore important
if (strlen($temp) >= 1 && substr($temp, -1) === '!') {
$string = rtrim(substr($temp, 0, -1));
$is_important = true;
}
}
$string = $this->def->validate($string, $config, $context);
if ($this->allow && $is_important) {
$string .= ' !important';
}
return $string;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,77 @@
<?php
/**
* Represents a Length as defined by CSS.
*/
class HTMLPurifier_AttrDef_CSS_Length extends HTMLPurifier_AttrDef
{
/**
* @type HTMLPurifier_Length|string
*/
protected $min;
/**
* @type HTMLPurifier_Length|string
*/
protected $max;
/**
* @param HTMLPurifier_Length|string $min Minimum length, or null for no bound. String is also acceptable.
* @param HTMLPurifier_Length|string $max Maximum length, or null for no bound. String is also acceptable.
*/
public function __construct($min = null, $max = null)
{
$this->min = $min !== null ? HTMLPurifier_Length::make($min) : null;
$this->max = $max !== null ? HTMLPurifier_Length::make($max) : null;
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$string = $this->parseCDATA($string);
// Optimizations
if ($string === '') {
return false;
}
if ($string === '0') {
return '0';
}
if (strlen($string) === 1) {
return false;
}
$length = HTMLPurifier_Length::make($string);
if (!$length->isValid()) {
return false;
}
if ($this->min) {
$c = $length->compareTo($this->min);
if ($c === false) {
return false;
}
if ($c < 0) {
return false;
}
}
if ($this->max) {
$c = $length->compareTo($this->max);
if ($c === false) {
return false;
}
if ($c > 0) {
return false;
}
}
return $length->toString();
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,112 @@
<?php
/**
* Validates shorthand CSS property list-style.
* @warning Does not support url tokens that have internal spaces.
*/
class HTMLPurifier_AttrDef_CSS_ListStyle extends HTMLPurifier_AttrDef
{
/**
* Local copy of validators.
* @type HTMLPurifier_AttrDef[]
* @note See HTMLPurifier_AttrDef_CSS_Font::$info for a similar impl.
*/
protected $info;
/**
* @param HTMLPurifier_Config $config
*/
public function __construct($config)
{
$def = $config->getCSSDefinition();
$this->info['list-style-type'] = $def->info['list-style-type'];
$this->info['list-style-position'] = $def->info['list-style-position'];
$this->info['list-style-image'] = $def->info['list-style-image'];
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
// regular pre-processing
$string = $this->parseCDATA($string);
if ($string === '') {
return false;
}
// assumes URI doesn't have spaces in it
$bits = explode(' ', strtolower($string)); // bits to process
$caught = array();
$caught['type'] = false;
$caught['position'] = false;
$caught['image'] = false;
$i = 0; // number of catches
$none = false;
foreach ($bits as $bit) {
if ($i >= 3) {
return;
} // optimization bit
if ($bit === '') {
continue;
}
foreach ($caught as $key => $status) {
if ($status !== false) {
continue;
}
$r = $this->info['list-style-' . $key]->validate($bit, $config, $context);
if ($r === false) {
continue;
}
if ($r === 'none') {
if ($none) {
continue;
} else {
$none = true;
}
if ($key == 'image') {
continue;
}
}
$caught[$key] = $r;
$i++;
break;
}
}
if (!$i) {
return false;
}
$ret = array();
// construct type
if ($caught['type']) {
$ret[] = $caught['type'];
}
// construct image
if ($caught['image']) {
$ret[] = $caught['image'];
}
// construct position
if ($caught['position']) {
$ret[] = $caught['position'];
}
if (empty($ret)) {
return false;
}
return implode(' ', $ret);
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,71 @@
<?php
/**
* Framework class for strings that involve multiple values.
*
* Certain CSS properties such as border-width and margin allow multiple
* lengths to be specified. This class can take a vanilla border-width
* definition and multiply it, usually into a max of four.
*
* @note Even though the CSS specification isn't clear about it, inherit
* can only be used alone: it will never manifest as part of a multi
* shorthand declaration. Thus, this class does not allow inherit.
*/
class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef
{
/**
* Instance of component definition to defer validation to.
* @type HTMLPurifier_AttrDef
* @todo Make protected
*/
public $single;
/**
* Max number of values allowed.
* @todo Make protected
*/
public $max;
/**
* @param HTMLPurifier_AttrDef $single HTMLPurifier_AttrDef to multiply
* @param int $max Max number of values allowed (usually four)
*/
public function __construct($single, $max = 4)
{
$this->single = $single;
$this->max = $max;
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$string = $this->parseCDATA($string);
if ($string === '') {
return false;
}
$parts = explode(' ', $string); // parseCDATA replaced \r, \t and \n
$length = count($parts);
$final = '';
for ($i = 0, $num = 0; $i < $length && $num < $this->max; $i++) {
if (ctype_space($parts[$i])) {
continue;
}
$result = $this->single->validate($parts[$i], $config, $context);
if ($result !== false) {
$final .= $result . ' ';
$num++;
}
}
if ($final === '') {
return false;
}
return rtrim($final);
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,84 @@
<?php
/**
* Validates a number as defined by the CSS spec.
*/
class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef
{
/**
* Indicates whether or not only positive values are allowed.
* @type bool
*/
protected $non_negative = false;
/**
* @param bool $non_negative indicates whether negatives are forbidden
*/
public function __construct($non_negative = false)
{
$this->non_negative = $non_negative;
}
/**
* @param string $number
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return string|bool
* @warning Some contexts do not pass $config, $context. These
* variables should not be used without checking HTMLPurifier_Length
*/
public function validate($number, $config, $context)
{
$number = $this->parseCDATA($number);
if ($number === '') {
return false;
}
if ($number === '0') {
return '0';
}
$sign = '';
switch ($number[0]) {
case '-':
if ($this->non_negative) {
return false;
}
$sign = '-';
case '+':
$number = substr($number, 1);
}
if (ctype_digit($number)) {
$number = ltrim($number, '0');
return $number ? $sign . $number : '0';
}
// Period is the only non-numeric character allowed
if (strpos($number, '.') === false) {
return false;
}
list($left, $right) = explode('.', $number, 2);
if ($left === '' && $right === '') {
return false;
}
if ($left !== '' && !ctype_digit($left)) {
return false;
}
$left = ltrim($left, '0');
$right = rtrim($right, '0');
if ($right === '') {
return $left ? $sign . $left : '0';
} elseif (!ctype_digit($right)) {
return false;
}
return $sign . $left . '.' . $right;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,54 @@
<?php
/**
* Validates a Percentage as defined by the CSS spec.
*/
class HTMLPurifier_AttrDef_CSS_Percentage extends HTMLPurifier_AttrDef
{
/**
* Instance to defer number validation to.
* @type HTMLPurifier_AttrDef_CSS_Number
*/
protected $number_def;
/**
* @param bool $non_negative Whether to forbid negative values
*/
public function __construct($non_negative = false)
{
$this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative);
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$string = $this->parseCDATA($string);
if ($string === '') {
return false;
}
$length = strlen($string);
if ($length === 1) {
return false;
}
if ($string[$length - 1] !== '%') {
return false;
}
$number = substr($string, 0, $length - 1);
$number = $this->number_def->validate($number, $config, $context);
if ($number === false) {
return false;
}
return "$number%";
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,46 @@
<?php
/**
* Validates the value for the CSS property text-decoration
* @note This class could be generalized into a version that acts sort of
* like Enum except you can compound the allowed values.
*/
class HTMLPurifier_AttrDef_CSS_TextDecoration extends HTMLPurifier_AttrDef
{
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
static $allowed_values = array(
'line-through' => true,
'overline' => true,
'underline' => true,
);
$string = strtolower($this->parseCDATA($string));
if ($string === 'none') {
return $string;
}
$parts = explode(' ', $string);
$final = '';
foreach ($parts as $part) {
if (isset($allowed_values[$part])) {
$final .= $part . ' ';
}
}
$final = rtrim($final);
if ($final === '') {
return false;
}
return $final;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,74 @@
<?php
/**
* Validates a URI in CSS syntax, which uses url('http://example.com')
* @note While theoretically speaking a URI in a CSS document could
* be non-embedded, as of CSS2 there is no such usage so we're
* generalizing it. This may need to be changed in the future.
* @warning Since HTMLPurifier_AttrDef_CSS blindly uses semicolons as
* the separator, you cannot put a literal semicolon in
* in the URI. Try percent encoding it, in that case.
*/
class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI
{
public function __construct()
{
parent::__construct(true); // always embedded
}
/**
* @param string $uri_string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($uri_string, $config, $context)
{
// parse the URI out of the string and then pass it onto
// the parent object
$uri_string = $this->parseCDATA($uri_string);
if (strpos($uri_string, 'url(') !== 0) {
return false;
}
$uri_string = substr($uri_string, 4);
$new_length = strlen($uri_string) - 1;
if ($uri_string[$new_length] != ')') {
return false;
}
$uri = trim(substr($uri_string, 0, $new_length));
if (!empty($uri) && ($uri[0] == "'" || $uri[0] == '"')) {
$quote = $uri[0];
$new_length = strlen($uri) - 1;
if ($uri[$new_length] !== $quote) {
return false;
}
$uri = substr($uri, 1, $new_length - 1);
}
$uri = $this->expandCSSEscape($uri);
$result = parent::validate($uri, $config, $context);
if ($result === false) {
return false;
}
// extra sanity check; should have been done by URI
$result = str_replace(array('"', "\\", "\n", "\x0c", "\r"), "", $result);
// suspicious characters are ()'; we're going to percent encode
// them for safety.
$result = str_replace(array('(', ')', "'"), array('%28', '%29', '%27'), $result);
// there's an extra bug where ampersands lose their escaping on
// an innerHTML cycle, so a very unlucky query parameter could
// then change the meaning of the URL. Unfortunately, there's
// not much we can do about that...
return "url(\"$result\")";
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,44 @@
<?php
/**
* Dummy AttrDef that mimics another AttrDef, BUT it generates clones
* with make.
*/
class HTMLPurifier_AttrDef_Clone extends HTMLPurifier_AttrDef
{
/**
* What we're cloning.
* @type HTMLPurifier_AttrDef
*/
protected $clone;
/**
* @param HTMLPurifier_AttrDef $clone
*/
public function __construct($clone)
{
$this->clone = $clone;
}
/**
* @param string $v
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($v, $config, $context)
{
return $this->clone->validate($v, $config, $context);
}
/**
* @param string $string
* @return HTMLPurifier_AttrDef
*/
public function make($string)
{
return clone $this->clone;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,73 @@
<?php
// Enum = Enumerated
/**
* Validates a keyword against a list of valid values.
* @warning The case-insensitive compare of this function uses PHP's
* built-in strtolower and ctype_lower functions, which may
* cause problems with international comparisons
*/
class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef
{
/**
* Lookup table of valid values.
* @type array
* @todo Make protected
*/
public $valid_values = array();
/**
* Bool indicating whether or not enumeration is case sensitive.
* @note In general this is always case insensitive.
*/
protected $case_sensitive = false; // values according to W3C spec
/**
* @param array $valid_values List of valid values
* @param bool $case_sensitive Whether or not case sensitive
*/
public function __construct($valid_values = array(), $case_sensitive = false)
{
$this->valid_values = array_flip($valid_values);
$this->case_sensitive = $case_sensitive;
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$string = trim($string);
if (!$this->case_sensitive) {
// we may want to do full case-insensitive libraries
$string = ctype_lower($string) ? $string : strtolower($string);
}
$result = isset($this->valid_values[$string]);
return $result ? $string : false;
}
/**
* @param string $string In form of comma-delimited list of case-insensitive
* valid values. Example: "foo,bar,baz". Prepend "s:" to make
* case sensitive
* @return HTMLPurifier_AttrDef_Enum
*/
public function make($string)
{
if (strlen($string) > 2 && $string[0] == 's' && $string[1] == ':') {
$string = substr($string, 2);
$sensitive = true;
} else {
$sensitive = false;
}
$values = explode(',', $string);
return new HTMLPurifier_AttrDef_Enum($values, $sensitive);
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,51 @@
<?php
/**
* Validates a boolean attribute
*/
class HTMLPurifier_AttrDef_HTML_Bool extends HTMLPurifier_AttrDef
{
/**
* @type bool
*/
protected $name;
/**
* @type bool
*/
public $minimized = true;
/**
* @param bool $name
*/
public function __construct($name = false)
{
$this->name = $name;
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
if (empty($string)) {
return false;
}
return $this->name;
}
/**
* @param string $string Name of attribute
* @return HTMLPurifier_AttrDef_HTML_Bool
*/
public function make($string)
{
return new HTMLPurifier_AttrDef_HTML_Bool($string);
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,48 @@
<?php
/**
* Implements special behavior for class attribute (normally NMTOKENS)
*/
class HTMLPurifier_AttrDef_HTML_Class extends HTMLPurifier_AttrDef_HTML_Nmtokens
{
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
protected function split($string, $config, $context)
{
// really, this twiddle should be lazy loaded
$name = $config->getDefinition('HTML')->doctype->name;
if ($name == "XHTML 1.1" || $name == "XHTML 2.0") {
return parent::split($string, $config, $context);
} else {
return preg_split('/\s+/', $string);
}
}
/**
* @param array $tokens
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
protected function filter($tokens, $config, $context)
{
$allowed = $config->get('Attr.AllowedClasses');
$forbidden = $config->get('Attr.ForbiddenClasses');
$ret = array();
foreach ($tokens as $token) {
if (($allowed === null || isset($allowed[$token])) &&
!isset($forbidden[$token]) &&
// We need this O(n) check because of PHP's array
// implementation that casts -0 to 0.
!in_array($token, $ret, true)
) {
$ret[] = $token;
}
}
return $ret;
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* Validates a color according to the HTML spec.
*/
class HTMLPurifier_AttrDef_HTML_Color extends HTMLPurifier_AttrDef
{
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
static $colors = null;
if ($colors === null) {
$colors = $config->get('Core.ColorKeywords');
}
$string = trim($string);
if (empty($string)) {
return false;
}
$lower = strtolower($string);
if (isset($colors[$lower])) {
return $colors[$lower];
}
if ($string[0] === '#') {
$hex = substr($string, 1);
} else {
$hex = $string;
}
$length = strlen($hex);
if ($length !== 3 && $length !== 6) {
return false;
}
if (!ctype_xdigit($hex)) {
return false;
}
if ($length === 3) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
return "#$hex";
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,38 @@
<?php
/**
* Special-case enum attribute definition that lazy loads allowed frame targets
*/
class HTMLPurifier_AttrDef_HTML_FrameTarget extends HTMLPurifier_AttrDef_Enum
{
/**
* @type array
*/
public $valid_values = false; // uninitialized value
/**
* @type bool
*/
protected $case_sensitive = false;
public function __construct()
{
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
if ($this->valid_values === false) {
$this->valid_values = $config->get('Attr.AllowedFrameTargets');
}
return parent::validate($string, $config, $context);
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,105 @@
<?php
/**
* Validates the HTML attribute ID.
* @warning Even though this is the id processor, it
* will ignore the directive Attr:IDBlacklist, since it will only
* go according to the ID accumulator. Since the accumulator is
* automatically generated, it will have already absorbed the
* blacklist. If you're hacking around, make sure you use load()!
*/
class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
{
// selector is NOT a valid thing to use for IDREFs, because IDREFs
// *must* target IDs that exist, whereas selector #ids do not.
/**
* Determines whether or not we're validating an ID in a CSS
* selector context.
* @type bool
*/
protected $selector;
/**
* @param bool $selector
*/
public function __construct($selector = false)
{
$this->selector = $selector;
}
/**
* @param string $id
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($id, $config, $context)
{
if (!$this->selector && !$config->get('Attr.EnableID')) {
return false;
}
$id = trim($id); // trim it first
if ($id === '') {
return false;
}
$prefix = $config->get('Attr.IDPrefix');
if ($prefix !== '') {
$prefix .= $config->get('Attr.IDPrefixLocal');
// prevent re-appending the prefix
if (strpos($id, $prefix) !== 0) {
$id = $prefix . $id;
}
} elseif ($config->get('Attr.IDPrefixLocal') !== '') {
trigger_error(
'%Attr.IDPrefixLocal cannot be used unless ' .
'%Attr.IDPrefix is set',
E_USER_WARNING
);
}
if (!$this->selector) {
$id_accumulator =& $context->get('IDAccumulator');
if (isset($id_accumulator->ids[$id])) {
return false;
}
}
// we purposely avoid using regex, hopefully this is faster
if (ctype_alpha($id)) {
$result = true;
} else {
if (!ctype_alpha(@$id[0])) {
return false;
}
// primitive style of regexps, I suppose
$trim = trim(
$id,
'A..Za..z0..9:-._'
);
$result = ($trim === '');
}
$regexp = $config->get('Attr.IDBlacklistRegexp');
if ($regexp && preg_match($regexp, $id)) {
return false;
}
if (!$this->selector && $result) {
$id_accumulator->add($id);
}
// if no change was made to the ID, return the result
// else, return the new id if stripping whitespace made it
// valid, or return false.
return $result ? $id : false;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,56 @@
<?php
/**
* Validates the HTML type length (not to be confused with CSS's length).
*
* This accepts integer pixels or percentages as lengths for certain
* HTML attributes.
*/
class HTMLPurifier_AttrDef_HTML_Length extends HTMLPurifier_AttrDef_HTML_Pixels
{
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$string = trim($string);
if ($string === '') {
return false;
}
$parent_result = parent::validate($string, $config, $context);
if ($parent_result !== false) {
return $parent_result;
}
$length = strlen($string);
$last_char = $string[$length - 1];
if ($last_char !== '%') {
return false;
}
$points = substr($string, 0, $length - 1);
if (!is_numeric($points)) {
return false;
}
$points = (int)$points;
if ($points < 0) {
return '0%';
}
if ($points > 100) {
return '100%';
}
return ((string)$points) . '%';
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,72 @@
<?php
/**
* Validates a rel/rev link attribute against a directive of allowed values
* @note We cannot use Enum because link types allow multiple
* values.
* @note Assumes link types are ASCII text
*/
class HTMLPurifier_AttrDef_HTML_LinkTypes extends HTMLPurifier_AttrDef
{
/**
* Name config attribute to pull.
* @type string
*/
protected $name;
/**
* @param string $name
*/
public function __construct($name)
{
$configLookup = array(
'rel' => 'AllowedRel',
'rev' => 'AllowedRev'
);
if (!isset($configLookup[$name])) {
trigger_error(
'Unrecognized attribute name for link ' .
'relationship.',
E_USER_ERROR
);
return;
}
$this->name = $configLookup[$name];
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$allowed = $config->get('Attr.' . $this->name);
if (empty($allowed)) {
return false;
}
$string = $this->parseCDATA($string);
$parts = explode(' ', $string);
// lookup to prevent duplicates
$ret_lookup = array();
foreach ($parts as $part) {
$part = strtolower(trim($part));
if (!isset($allowed[$part])) {
continue;
}
$ret_lookup[$part] = true;
}
if (empty($ret_lookup)) {
return false;
}
$string = implode(' ', array_keys($ret_lookup));
return $string;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,60 @@
<?php
/**
* Validates a MultiLength as defined by the HTML spec.
*
* A multilength is either a integer (pixel count), a percentage, or
* a relative number.
*/
class HTMLPurifier_AttrDef_HTML_MultiLength extends HTMLPurifier_AttrDef_HTML_Length
{
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$string = trim($string);
if ($string === '') {
return false;
}
$parent_result = parent::validate($string, $config, $context);
if ($parent_result !== false) {
return $parent_result;
}
$length = strlen($string);
$last_char = $string[$length - 1];
if ($last_char !== '*') {
return false;
}
$int = substr($string, 0, $length - 1);
if ($int == '') {
return '*';
}
if (!is_numeric($int)) {
return false;
}
$int = (int)$int;
if ($int < 0) {
return false;
}
if ($int == 0) {
return '0';
}
if ($int == 1) {
return '*';
}
return ((string)$int) . '*';
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,70 @@
<?php
/**
* Validates contents based on NMTOKENS attribute type.
*/
class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef
{
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$string = trim($string);
// early abort: '' and '0' (strings that convert to false) are invalid
if (!$string) {
return false;
}
$tokens = $this->split($string, $config, $context);
$tokens = $this->filter($tokens, $config, $context);
if (empty($tokens)) {
return false;
}
return implode(' ', $tokens);
}
/**
* Splits a space separated list of tokens into its constituent parts.
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
protected function split($string, $config, $context)
{
// OPTIMIZABLE!
// do the preg_match, capture all subpatterns for reformulation
// we don't support U+00A1 and up codepoints or
// escaping because I don't know how to do that with regexps
// and plus it would complicate optimization efforts (you never
// see that anyway).
$pattern = '/(?:(?<=\s)|\A)' . // look behind for space or string start
'((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)' .
'(?:(?=\s)|\z)/'; // look ahead for space or string end
preg_match_all($pattern, $string, $matches);
return $matches[1];
}
/**
* Template method for removing certain tokens based on arbitrary criteria.
* @note If we wanted to be really functional, we'd do an array_filter
* with a callback. But... we're not.
* @param array $tokens
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
protected function filter($tokens, $config, $context)
{
return $tokens;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,76 @@
<?php
/**
* Validates an integer representation of pixels according to the HTML spec.
*/
class HTMLPurifier_AttrDef_HTML_Pixels extends HTMLPurifier_AttrDef
{
/**
* @type int
*/
protected $max;
/**
* @param int $max
*/
public function __construct($max = null)
{
$this->max = $max;
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$string = trim($string);
if ($string === '0') {
return $string;
}
if ($string === '') {
return false;
}
$length = strlen($string);
if (substr($string, $length - 2) == 'px') {
$string = substr($string, 0, $length - 2);
}
if (!is_numeric($string)) {
return false;
}
$int = (int)$string;
if ($int < 0) {
return '0';
}
// upper-bound value, extremely high values can
// crash operating systems, see <http://ha.ckers.org/imagecrash.html>
// WARNING, above link WILL crash you if you're using Windows
if ($this->max !== null && $int > $this->max) {
return (string)$this->max;
}
return (string)$int;
}
/**
* @param string $string
* @return HTMLPurifier_AttrDef
*/
public function make($string)
{
if ($string === '') {
$max = null;
} else {
$max = (int)$string;
}
$class = get_class($this);
return new $class($max);
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,91 @@
<?php
/**
* Validates an integer.
* @note While this class was modeled off the CSS definition, no currently
* allowed CSS uses this type. The properties that do are: widows,
* orphans, z-index, counter-increment, counter-reset. Some of the
* HTML attributes, however, find use for a non-negative version of this.
*/
class HTMLPurifier_AttrDef_Integer extends HTMLPurifier_AttrDef
{
/**
* Whether or not negative values are allowed.
* @type bool
*/
protected $negative = true;
/**
* Whether or not zero is allowed.
* @type bool
*/
protected $zero = true;
/**
* Whether or not positive values are allowed.
* @type bool
*/
protected $positive = true;
/**
* @param $negative Bool indicating whether or not negative values are allowed
* @param $zero Bool indicating whether or not zero is allowed
* @param $positive Bool indicating whether or not positive values are allowed
*/
public function __construct($negative = true, $zero = true, $positive = true)
{
$this->negative = $negative;
$this->zero = $zero;
$this->positive = $positive;
}
/**
* @param string $integer
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($integer, $config, $context)
{
$integer = $this->parseCDATA($integer);
if ($integer === '') {
return false;
}
// we could possibly simply typecast it to integer, but there are
// certain fringe cases that must not return an integer.
// clip leading sign
if ($this->negative && $integer[0] === '-') {
$digits = substr($integer, 1);
if ($digits === '0') {
$integer = '0';
} // rm minus sign for zero
} elseif ($this->positive && $integer[0] === '+') {
$digits = $integer = substr($integer, 1); // rm unnecessary plus
} else {
$digits = $integer;
}
// test if it's numeric
if (!ctype_digit($digits)) {
return false;
}
// perform scope tests
if (!$this->zero && $integer == 0) {
return false;
}
if (!$this->positive && $integer > 0) {
return false;
}
if (!$this->negative && $integer < 0) {
return false;
}
return $integer;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,86 @@
<?php
/**
* Validates the HTML attribute lang, effectively a language code.
* @note Built according to RFC 3066, which obsoleted RFC 1766
*/
class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
{
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$string = trim($string);
if (!$string) {
return false;
}
$subtags = explode('-', $string);
$num_subtags = count($subtags);
if ($num_subtags == 0) { // sanity check
return false;
}
// process primary subtag : $subtags[0]
$length = strlen($subtags[0]);
switch ($length) {
case 0:
return false;
case 1:
if (!($subtags[0] == 'x' || $subtags[0] == 'i')) {
return false;
}
break;
case 2:
case 3:
if (!ctype_alpha($subtags[0])) {
return false;
} elseif (!ctype_lower($subtags[0])) {
$subtags[0] = strtolower($subtags[0]);
}
break;
default:
return false;
}
$new_string = $subtags[0];
if ($num_subtags == 1) {
return $new_string;
}
// process second subtag : $subtags[1]
$length = strlen($subtags[1]);
if ($length == 0 || ($length == 1 && $subtags[1] != 'x') || $length > 8 || !ctype_alnum($subtags[1])) {
return $new_string;
}
if (!ctype_lower($subtags[1])) {
$subtags[1] = strtolower($subtags[1]);
}
$new_string .= '-' . $subtags[1];
if ($num_subtags == 2) {
return $new_string;
}
// process all other subtags, index 2 and up
for ($i = 2; $i < $num_subtags; $i++) {
$length = strlen($subtags[$i]);
if ($length == 0 || $length > 8 || !ctype_alnum($subtags[$i])) {
return $new_string;
}
if (!ctype_lower($subtags[$i])) {
$subtags[$i] = strtolower($subtags[$i]);
}
$new_string .= '-' . $subtags[$i];
}
return $new_string;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,53 @@
<?php
/**
* Decorator that, depending on a token, switches between two definitions.
*/
class HTMLPurifier_AttrDef_Switch
{
/**
* @type string
*/
protected $tag;
/**
* @type HTMLPurifier_AttrDef
*/
protected $withTag;
/**
* @type HTMLPurifier_AttrDef
*/
protected $withoutTag;
/**
* @param string $tag Tag name to switch upon
* @param HTMLPurifier_AttrDef $with_tag Call if token matches tag
* @param HTMLPurifier_AttrDef $without_tag Call if token doesn't match, or there is no token
*/
public function __construct($tag, $with_tag, $without_tag)
{
$this->tag = $tag;
$this->withTag = $with_tag;
$this->withoutTag = $without_tag;
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$token = $context->get('CurrentToken', true);
if (!$token || $token->name !== $this->tag) {
return $this->withoutTag->validate($string, $config, $context);
} else {
return $this->withTag->validate($string, $config, $context);
}
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,21 @@
<?php
/**
* Validates arbitrary text according to the HTML spec.
*/
class HTMLPurifier_AttrDef_Text extends HTMLPurifier_AttrDef
{
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
return $this->parseCDATA($string);
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,111 @@
<?php
/**
* Validates a URI as defined by RFC 3986.
* @note Scheme-specific mechanics deferred to HTMLPurifier_URIScheme
*/
class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
{
/**
* @type HTMLPurifier_URIParser
*/
protected $parser;
/**
* @type bool
*/
protected $embedsResource;
/**
* @param bool $embeds_resource Does the URI here result in an extra HTTP request?
*/
public function __construct($embeds_resource = false)
{
$this->parser = new HTMLPurifier_URIParser();
$this->embedsResource = (bool)$embeds_resource;
}
/**
* @param string $string
* @return HTMLPurifier_AttrDef_URI
*/
public function make($string)
{
$embeds = ($string === 'embedded');
return new HTMLPurifier_AttrDef_URI($embeds);
}
/**
* @param string $uri
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($uri, $config, $context)
{
if ($config->get('URI.Disable')) {
return false;
}
$uri = $this->parseCDATA($uri);
// parse the URI
$uri = $this->parser->parse($uri);
if ($uri === false) {
return false;
}
// add embedded flag to context for validators
$context->register('EmbeddedURI', $this->embedsResource);
$ok = false;
do {
// generic validation
$result = $uri->validate($config, $context);
if (!$result) {
break;
}
// chained filtering
$uri_def = $config->getDefinition('URI');
$result = $uri_def->filter($uri, $config, $context);
if (!$result) {
break;
}
// scheme-specific validation
$scheme_obj = $uri->getSchemeObj($config, $context);
if (!$scheme_obj) {
break;
}
if ($this->embedsResource && !$scheme_obj->browsable) {
break;
}
$result = $scheme_obj->validate($uri, $config, $context);
if (!$result) {
break;
}
// Post chained filtering
$result = $uri_def->postFilter($uri, $config, $context);
if (!$result) {
break;
}
// survived gauntlet
$ok = true;
} while (false);
$context->destroy('EmbeddedURI');
if (!$ok) {
return false;
}
// back to string
return $uri->toString();
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,20 @@
<?php
abstract class HTMLPurifier_AttrDef_URI_Email extends HTMLPurifier_AttrDef
{
/**
* Unpacks a mailbox into its display-name and address
* @param string $string
* @return mixed
*/
public function unpack($string)
{
// needs to be implemented
}
}
// sub-implementations
// vim: et sw=4 sts=4

View File

@ -0,0 +1,29 @@
<?php
/**
* Primitive email validation class based on the regexp found at
* http://www.regular-expressions.info/email.html
*/
class HTMLPurifier_AttrDef_URI_Email_SimpleCheck extends HTMLPurifier_AttrDef_URI_Email
{
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
// no support for named mailboxes i.e. "Bob <bob@example.com>"
// that needs more percent encoding to be done
if ($string == '') {
return false;
}
$string = trim($string);
$result = preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $string);
return $result ? $string : false;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,128 @@
<?php
/**
* Validates a host according to the IPv4, IPv6 and DNS (future) specifications.
*/
class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
{
/**
* IPv4 sub-validator.
* @type HTMLPurifier_AttrDef_URI_IPv4
*/
protected $ipv4;
/**
* IPv6 sub-validator.
* @type HTMLPurifier_AttrDef_URI_IPv6
*/
protected $ipv6;
public function __construct()
{
$this->ipv4 = new HTMLPurifier_AttrDef_URI_IPv4();
$this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6();
}
/**
* @param string $string
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($string, $config, $context)
{
$length = strlen($string);
// empty hostname is OK; it's usually semantically equivalent:
// the default host as defined by a URI scheme is used:
//
// If the URI scheme defines a default for host, then that
// default applies when the host subcomponent is undefined
// or when the registered name is empty (zero length).
if ($string === '') {
return '';
}
if ($length > 1 && $string[0] === '[' && $string[$length - 1] === ']') {
//IPv6
$ip = substr($string, 1, $length - 2);
$valid = $this->ipv6->validate($ip, $config, $context);
if ($valid === false) {
return false;
}
return '[' . $valid . ']';
}
// need to do checks on unusual encodings too
$ipv4 = $this->ipv4->validate($string, $config, $context);
if ($ipv4 !== false) {
return $ipv4;
}
// A regular domain name.
// This doesn't match I18N domain names, but we don't have proper IRI support,
// so force users to insert Punycode.
// There is not a good sense in which underscores should be
// allowed, since it's technically not! (And if you go as
// far to allow everything as specified by the DNS spec...
// well, that's literally everything, modulo some space limits
// for the components and the overall name (which, by the way,
// we are NOT checking!). So we (arbitrarily) decide this:
// let's allow underscores wherever we would have allowed
// hyphens, if they are enabled. This is a pretty good match
// for browser behavior, for example, a large number of browsers
// cannot handle foo_.example.com, but foo_bar.example.com is
// fairly well supported.
$underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : '';
// The productions describing this are:
$a = '[a-z]'; // alpha
$an = '[a-z0-9]'; // alphanum
$and = "[a-z0-9-$underscore]"; // alphanum | "-"
// domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
$domainlabel = "$an($and*$an)?";
// toplabel = alpha | alpha *( alphanum | "-" ) alphanum
$toplabel = "$a($and*$an)?";
// hostname = *( domainlabel "." ) toplabel [ "." ]
if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
return $string;
}
// If we have Net_IDNA2 support, we can support IRIs by
// punycoding them. (This is the most portable thing to do,
// since otherwise we have to assume browsers support
if ($config->get('Core.EnableIDNA')) {
$idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => false, 'strict' => true));
// we need to encode each period separately
$parts = explode('.', $string);
try {
$new_parts = array();
foreach ($parts as $part) {
$encodable = false;
for ($i = 0, $c = strlen($part); $i < $c; $i++) {
if (ord($part[$i]) > 0x7a) {
$encodable = true;
break;
}
}
if (!$encodable) {
$new_parts[] = $part;
} else {
$new_parts[] = $idna->encode($part);
}
}
$string = implode('.', $new_parts);
if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
return $string;
}
} catch (Exception $e) {
// XXX error reporting
}
}
return false;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,45 @@
<?php
/**
* Validates an IPv4 address
* @author Feyd @ forums.devnetwork.net (public domain)
*/
class HTMLPurifier_AttrDef_URI_IPv4 extends HTMLPurifier_AttrDef
{
/**
* IPv4 regex, protected so that IPv6 can reuse it.
* @type string
*/
protected $ip4;
/**
* @param string $aIP
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($aIP, $config, $context)
{
if (!$this->ip4) {
$this->_loadRegex();
}
if (preg_match('#^' . $this->ip4 . '$#s', $aIP)) {
return $aIP;
}
return false;
}
/**
* Lazy load function to prevent regex from being stuffed in
* cache.
*/
protected function _loadRegex()
{
$oct = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'; // 0-255
$this->ip4 = "(?:{$oct}\\.{$oct}\\.{$oct}\\.{$oct})";
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,89 @@
<?php
/**
* Validates an IPv6 address.
* @author Feyd @ forums.devnetwork.net (public domain)
* @note This function requires brackets to have been removed from address
* in URI.
*/
class HTMLPurifier_AttrDef_URI_IPv6 extends HTMLPurifier_AttrDef_URI_IPv4
{
/**
* @param string $aIP
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool|string
*/
public function validate($aIP, $config, $context)
{
if (!$this->ip4) {
$this->_loadRegex();
}
$original = $aIP;
$hex = '[0-9a-fA-F]';
$blk = '(?:' . $hex . '{1,4})';
$pre = '(?:/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))'; // /0 - /128
// prefix check
if (strpos($aIP, '/') !== false) {
if (preg_match('#' . $pre . '$#s', $aIP, $find)) {
$aIP = substr($aIP, 0, 0 - strlen($find[0]));
unset($find);
} else {
return false;
}
}
// IPv4-compatiblity check
if (preg_match('#(?<=:' . ')' . $this->ip4 . '$#s', $aIP, $find)) {
$aIP = substr($aIP, 0, 0 - strlen($find[0]));
$ip = explode('.', $find[0]);
$ip = array_map('dechex', $ip);
$aIP .= $ip[0] . $ip[1] . ':' . $ip[2] . $ip[3];
unset($find, $ip);
}
// compression check
$aIP = explode('::', $aIP);
$c = count($aIP);
if ($c > 2) {
return false;
} elseif ($c == 2) {
list($first, $second) = $aIP;
$first = explode(':', $first);
$second = explode(':', $second);
if (count($first) + count($second) > 8) {
return false;
}
while (count($first) < 8) {
array_push($first, '0');
}
array_splice($first, 8 - count($second), 8, $second);
$aIP = $first;
unset($first, $second);
} else {
$aIP = explode(':', $aIP[0]);
}
$c = count($aIP);
if ($c != 8) {
return false;
}
// All the pieces should be 16-bit hex strings. Are they?
foreach ($aIP as $piece) {
if (!preg_match('#^[0-9a-fA-F]{4}$#s', sprintf('%04s', $piece))) {
return false;
}
}
return $original;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,60 @@
<?php
/**
* Processes an entire attribute array for corrections needing multiple values.
*
* Occasionally, a certain attribute will need to be removed and popped onto
* another value. Instead of creating a complex return syntax for
* HTMLPurifier_AttrDef, we just pass the whole attribute array to a
* specialized object and have that do the special work. That is the
* family of HTMLPurifier_AttrTransform.
*
* An attribute transformation can be assigned to run before or after
* HTMLPurifier_AttrDef validation. See HTMLPurifier_HTMLDefinition for
* more details.
*/
abstract class HTMLPurifier_AttrTransform
{
/**
* Abstract: makes changes to the attributes dependent on multiple values.
*
* @param array $attr Assoc array of attributes, usually from
* HTMLPurifier_Token_Tag::$attr
* @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object.
* @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object
* @return array Processed attribute array.
*/
abstract public function transform($attr, $config, $context);
/**
* Prepends CSS properties to the style attribute, creating the
* attribute if it doesn't exist.
* @param array &$attr Attribute array to process (passed by reference)
* @param string $css CSS to prepend
*/
public function prependCSS(&$attr, $css)
{
$attr['style'] = isset($attr['style']) ? $attr['style'] : '';
$attr['style'] = $css . $attr['style'];
}
/**
* Retrieves and removes an attribute
* @param array &$attr Attribute array to process (passed by reference)
* @param mixed $key Key of attribute to confiscate
* @return mixed
*/
public function confiscateAttr(&$attr, $key)
{
if (!isset($attr[$key])) {
return null;
}
$value = $attr[$key];
unset($attr[$key]);
return $value;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,28 @@
<?php
/**
* Pre-transform that changes proprietary background attribute to CSS.
*/
class HTMLPurifier_AttrTransform_Background extends HTMLPurifier_AttrTransform
{
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
if (!isset($attr['background'])) {
return $attr;
}
$background = $this->confiscateAttr($attr, 'background');
// some validation should happen here
$this->prependCSS($attr, "background-image:url($background);");
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,27 @@
<?php
// this MUST be placed in post, as it assumes that any value in dir is valid
/**
* Post-trasnform that ensures that bdo tags have the dir attribute set.
*/
class HTMLPurifier_AttrTransform_BdoDir extends HTMLPurifier_AttrTransform
{
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
if (isset($attr['dir'])) {
return $attr;
}
$attr['dir'] = $config->get('Attr.DefaultTextDir');
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,28 @@
<?php
/**
* Pre-transform that changes deprecated bgcolor attribute to CSS.
*/
class HTMLPurifier_AttrTransform_BgColor extends HTMLPurifier_AttrTransform
{
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
if (!isset($attr['bgcolor'])) {
return $attr;
}
$bgcolor = $this->confiscateAttr($attr, 'bgcolor');
// some validation should happen here
$this->prependCSS($attr, "background-color:$bgcolor;");
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,47 @@
<?php
/**
* Pre-transform that changes converts a boolean attribute to fixed CSS
*/
class HTMLPurifier_AttrTransform_BoolToCSS extends HTMLPurifier_AttrTransform
{
/**
* Name of boolean attribute that is trigger.
* @type string
*/
protected $attr;
/**
* CSS declarations to add to style, needs trailing semicolon.
* @type string
*/
protected $css;
/**
* @param string $attr attribute name to convert from
* @param string $css CSS declarations to add to style (needs semicolon)
*/
public function __construct($attr, $css)
{
$this->attr = $attr;
$this->css = $css;
}
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
if (!isset($attr[$this->attr])) {
return $attr;
}
unset($attr[$this->attr]);
$this->prependCSS($attr, $this->css);
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,26 @@
<?php
/**
* Pre-transform that changes deprecated border attribute to CSS.
*/
class HTMLPurifier_AttrTransform_Border extends HTMLPurifier_AttrTransform
{
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
if (!isset($attr['border'])) {
return $attr;
}
$border_width = $this->confiscateAttr($attr, 'border');
// some validation should happen here
$this->prependCSS($attr, "border:{$border_width}px solid;");
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,68 @@
<?php
/**
* Generic pre-transform that converts an attribute with a fixed number of
* values (enumerated) to CSS.
*/
class HTMLPurifier_AttrTransform_EnumToCSS extends HTMLPurifier_AttrTransform
{
/**
* Name of attribute to transform from.
* @type string
*/
protected $attr;
/**
* Lookup array of attribute values to CSS.
* @type array
*/
protected $enumToCSS = array();
/**
* Case sensitivity of the matching.
* @type bool
* @warning Currently can only be guaranteed to work with ASCII
* values.
*/
protected $caseSensitive = false;
/**
* @param string $attr Attribute name to transform from
* @param array $enum_to_css Lookup array of attribute values to CSS
* @param bool $case_sensitive Case sensitivity indicator, default false
*/
public function __construct($attr, $enum_to_css, $case_sensitive = false)
{
$this->attr = $attr;
$this->enumToCSS = $enum_to_css;
$this->caseSensitive = (bool)$case_sensitive;
}
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
if (!isset($attr[$this->attr])) {
return $attr;
}
$value = trim($attr[$this->attr]);
unset($attr[$this->attr]);
if (!$this->caseSensitive) {
$value = strtolower($value);
}
if (!isset($this->enumToCSS[$value])) {
return $attr;
}
$this->prependCSS($attr, $this->enumToCSS[$value]);
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,48 @@
<?php
// must be called POST validation
/**
* Transform that supplies default values for the src and alt attributes
* in img tags, as well as prevents the img tag from being removed
* because of a missing alt tag. This needs to be registered as both
* a pre and post attribute transform.
*/
class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
{
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
$src = true;
if (!isset($attr['src'])) {
if ($config->get('Core.RemoveInvalidImg')) {
return $attr;
}
$attr['src'] = $config->get('Attr.DefaultInvalidImage');
$src = false;
}
if (!isset($attr['alt'])) {
if ($src) {
$alt = $config->get('Attr.DefaultImageAlt');
if ($alt === null) {
// truncate if the alt is too long
$attr['alt'] = substr(basename($attr['src']), 0, 40);
} else {
$attr['alt'] = $alt;
}
} else {
$attr['alt'] = $config->get('Attr.DefaultInvalidImageAlt');
}
}
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,61 @@
<?php
/**
* Pre-transform that changes deprecated hspace and vspace attributes to CSS
*/
class HTMLPurifier_AttrTransform_ImgSpace extends HTMLPurifier_AttrTransform
{
/**
* @type string
*/
protected $attr;
/**
* @type array
*/
protected $css = array(
'hspace' => array('left', 'right'),
'vspace' => array('top', 'bottom')
);
/**
* @param string $attr
*/
public function __construct($attr)
{
$this->attr = $attr;
if (!isset($this->css[$attr])) {
trigger_error(htmlspecialchars($attr) . ' is not valid space attribute');
}
}
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
if (!isset($attr[$this->attr])) {
return $attr;
}
$width = $this->confiscateAttr($attr, $this->attr);
// some validation could happen here
if (!isset($this->css[$this->attr])) {
return $attr;
}
$style = '';
foreach ($this->css[$this->attr] as $suffix) {
$property = "margin-$suffix";
$style .= "$property:{$width}px;";
}
$this->prependCSS($attr, $style);
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,56 @@
<?php
/**
* Performs miscellaneous cross attribute validation and filtering for
* input elements. This is meant to be a post-transform.
*/
class HTMLPurifier_AttrTransform_Input extends HTMLPurifier_AttrTransform
{
/**
* @type HTMLPurifier_AttrDef_HTML_Pixels
*/
protected $pixels;
public function __construct()
{
$this->pixels = new HTMLPurifier_AttrDef_HTML_Pixels();
}
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
if (!isset($attr['type'])) {
$t = 'text';
} else {
$t = strtolower($attr['type']);
}
if (isset($attr['checked']) && $t !== 'radio' && $t !== 'checkbox') {
unset($attr['checked']);
}
if (isset($attr['maxlength']) && $t !== 'text' && $t !== 'password') {
unset($attr['maxlength']);
}
if (isset($attr['size']) && $t !== 'text' && $t !== 'password') {
$result = $this->pixels->validate($attr['size'], $config, $context);
if ($result === false) {
unset($attr['size']);
} else {
$attr['size'] = $result;
}
}
if (isset($attr['src']) && $t !== 'image') {
unset($attr['src']);
}
if (!isset($attr['value']) && ($t === 'radio' || $t === 'checkbox')) {
$attr['value'] = '';
}
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,31 @@
<?php
/**
* Post-transform that copies lang's value to xml:lang (and vice-versa)
* @note Theoretically speaking, this could be a pre-transform, but putting
* post is more efficient.
*/
class HTMLPurifier_AttrTransform_Lang extends HTMLPurifier_AttrTransform
{
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
$lang = isset($attr['lang']) ? $attr['lang'] : false;
$xml_lang = isset($attr['xml:lang']) ? $attr['xml:lang'] : false;
if ($lang !== false && $xml_lang === false) {
$attr['xml:lang'] = $lang;
} elseif ($xml_lang !== false) {
$attr['lang'] = $xml_lang;
}
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,45 @@
<?php
/**
* Class for handling width/height length attribute transformations to CSS
*/
class HTMLPurifier_AttrTransform_Length extends HTMLPurifier_AttrTransform
{
/**
* @type string
*/
protected $name;
/**
* @type string
*/
protected $cssName;
public function __construct($name, $css_name = null)
{
$this->name = $name;
$this->cssName = $css_name ? $css_name : $name;
}
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
if (!isset($attr[$this->name])) {
return $attr;
}
$length = $this->confiscateAttr($attr, $this->name);
if (ctype_digit($length)) {
$length .= 'px';
}
$this->prependCSS($attr, $this->cssName . ":$length;");
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,33 @@
<?php
/**
* Pre-transform that changes deprecated name attribute to ID if necessary
*/
class HTMLPurifier_AttrTransform_Name extends HTMLPurifier_AttrTransform
{
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
// Abort early if we're using relaxed definition of name
if ($config->get('HTML.Attr.Name.UseCDATA')) {
return $attr;
}
if (!isset($attr['name'])) {
return $attr;
}
$id = $this->confiscateAttr($attr, 'name');
if (isset($attr['id'])) {
return $attr;
}
$attr['id'] = $id;
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,41 @@
<?php
/**
* Post-transform that performs validation to the name attribute; if
* it is present with an equivalent id attribute, it is passed through;
* otherwise validation is performed.
*/
class HTMLPurifier_AttrTransform_NameSync extends HTMLPurifier_AttrTransform
{
public function __construct()
{
$this->idDef = new HTMLPurifier_AttrDef_HTML_ID();
}
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
if (!isset($attr['name'])) {
return $attr;
}
$name = $attr['name'];
if (isset($attr['id']) && $attr['id'] === $name) {
return $attr;
}
$result = $this->idDef->validate($name, $config, $context);
if ($result === false) {
unset($attr['name']);
} else {
$attr['name'] = $result;
}
return $attr;
}
}
// vim: et sw=4 sts=4

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