Compare commits

..

591 Commits

Author SHA1 Message Date
Shish
4cda95f8e5 bumps, and turn off debugging 2011-10-05 20:26:12 +01:00
Shish
30576bd897 danbooru theme was broken v.v 2011-09-21 13:37:47 +01:00
Shish
3aeb288b29 remove quotes from convert.exe in some cases 2011-08-08 13:02:12 +01:00
Shish
3692b7c75e pdo syntax for blotter 2011-07-28 12:17:55 +01:00
Shish
01400a10ec type->engine for mysql 5.5 2011-07-14 10:13:24 +01:00
Shish
cb1f49e7f6 new DB code slipped into the old version >_< 2011-03-24 17:52:15 +00:00
Shish
8729263ea0 merg activity cookie 2011-03-23 11:41:33 +00:00
Shish
91bed9297e merge comment count cache 2011-03-23 11:39:09 +00:00
Shish
e1dff88920 remove votes on image 2011-03-23 11:31:25 +00:00
Shish
12682fe229 only check event type once 2011-03-23 11:30:51 +00:00
Shish
8bea495cef merge anon cache 2011-03-23 11:27:45 +00:00
Shish
b5bea64195 cd variable for sites who want /ab/cb/abcd 2011-03-23 11:19:47 +00:00
Shish
edc9c7ae40 DNP action, for one-click bans 2011-03-23 11:17:35 +00:00
Shish
746425114d caching for featured image 2011-03-23 11:15:54 +00:00
Shish
f647e4fb38 give auth token for image featuring 2011-03-23 11:13:29 +00:00
Shish
047b751f84 no auth needed 2011-03-12 18:13:19 +00:00
Shish
32e70e22d1 make CommentList::get_hash static, for calling from other places 2011-03-06 12:45:08 +00:00
Diftraku
b685c84209 Because Windows... shitty OS is shitty. Added a Windows-only fix to the extension symlink and deltree.
Windows will nag about file info not found on relative paths with symlink (php bug?).
Also, when deleting a symlink on Windows, it needs to be rmdir'd, no unlink'd (kept throwing permission denied for unlink).
2011-03-03 12:47:57 +00:00
Shish
410ae399de log image votes 2011-02-28 11:29:15 +00:00
Shish
ec6d950d76 downtime theme needs access to user global 2011-02-23 15:20:47 +00:00
zshall
705f31ea4e Added admin setting ability back where it belongs in lite and danbooru themes. 2011-02-22 17:44:34 +00:00
Shish
9d87109a80 don't have super-thin / super-short thumbs 2011-02-13 11:19:04 +00:00
Shish
3ac30a24b9 remove extra debug 2011-01-26 17:21:06 +00:00
Shish
6c7cfe66d6 make sure thumbnails have a max ratio of 5:1 2011-01-26 17:10:26 +00:00
Shish
e8040656e8 missed a global 2011-01-23 11:34:50 +00:00
Shish
0b12bbf2a3 cache popular tags 2011-01-22 17:37:12 +00:00
Shish
8a8fef4b38 warn on login failure 2011-01-22 16:10:10 +00:00
Shish
0d50d68da0 left/right/center tags, from r34 2011-01-22 16:03:41 +00:00
Shish
980df44dd8 strnorm bits 2011-01-22 15:53:01 +00:00
Shish
55f223ead1 mime_type or ext, not type 2011-01-01 16:23:58 +00:00
Shish
21fdbdddba 2.3.0, merry christmas! 2010-12-25 00:04:25 +00:00
Shish
1a3fa47217 Revert "checks for files without extensions, from AtomicDryad"
This seems to break something, such that uploading a jpeg results
in image size 0x0 (other bits seem to work though o_O)

Will take a look at what's up and re-commit later...

This reverts commit ead87d3934ad7d29394fd39e309bd75f5eb78fbb.
2010-12-24 20:46:52 +00:00
Shish
101cd38e1a readme updates 2010-12-23 14:10:58 +00:00
Shish
f4dc0a5764 delete by query 2010-12-23 14:01:25 +00:00
Shish
96fc3f7e94 merge of AD's updates (now with real git-merge, rather than patching by hand; apologies if the hand-patches broke anything) 2010-12-23 01:15:48 +00:00
atomicdryad
84ba3c10ab Extension: tag_editcloud: Add or remove tags to the editor via clicking 2010-12-23 00:49:13 +00:00
Shish
06c61978d7 several themes used the same script.js, and several extensions depend upon these scripts -- move all the functions to a single .js file in lib 2010-12-22 22:40:59 +00:00
Shish
ef2e94e70a checks for files without extensions, from AtomicDryad 2010-12-22 21:43:19 +00:00
Shish
992316d10c Danbooru API fixes from AtomicDryad 2010-12-22 21:38:51 +00:00
Shish
b32f2de040 use google charts for QR code generation, save 7MB 2010-12-14 20:34:35 +00:00
Shish
0ff432abf6 what the fuck, php? this should not be necessary :/ 2010-10-11 12:21:30 +01:00
Shish
068ff1ac62 use config.php as CSRF salt 2010-10-07 21:31:26 +01:00
Shish
e577ffa785 <form> -> make_form(), or add auth token manually 2010-10-07 21:31:07 +01:00
Shish
9b5c65f11f csrf-proofing for extensions 2010-10-07 21:29:59 +01:00
Shish
063cdd4746 csrf functions 2010-10-07 21:29:47 +01:00
Shish
81ea250fdf split is deprecated in php5.3, use explode instead 2010-07-30 15:37:03 +01:00
Shish
ebf25505eb external CSS comes before external JS 2010-07-30 13:39:32 +01:00
Shish
926ecf3974 specify image size in HTML rather than CSS; chrome claims it loads faster 2010-07-30 13:26:05 +01:00
Shish
f6863c7fb8 bump 2010-07-27 08:27:56 +01:00
Shish
b294486b0e another 2010-07-26 23:29:22 +01:00
Shish
6720f639c8 more url_escape 2010-07-26 23:10:57 +01:00
Shish
ef073290c0 not all imageinfoset events have favorite_action 2010-07-26 23:06:26 +01:00
Shish
501a8485ce only vote for valid images 2010-07-26 23:00:24 +01:00
Shish
9e05a34ac9 only favourite valid images 2010-07-26 22:59:36 +01:00
Shish
4e48e02856 curl might not be installed 2010-07-26 22:48:44 +01:00
Shish
4eb19631b7 escape more data, fix an XSS hole 2010-07-26 17:21:58 +01:00
Shish
d31760a334 repeat of GET forms breaking on sites without niceurls 2010-07-26 11:18:38 +01:00
Shish
85178ff127 hide metadata on windows 2010-07-19 13:57:40 +01:00
Shish
65c26e5825 more themes, more updates 2010-07-19 13:53:29 +01:00
Shish
e00e5ff0d1 bump 2010-07-19 13:38:04 +01:00
Shish
8a0eda15eb hack 2010-07-19 13:35:24 +01:00
Shish
b1f78d0f33 adodb updates 2010-07-19 13:10:02 +01:00
Shish
d2469191f9 ignore it too 2010-07-19 13:06:43 +01:00
Shish
62fd2952ff and the S3 library 2010-07-19 13:06:30 +01:00
Shish
c276063389 S3 mirroring 2010-07-12 22:27:59 +01:00
Shish
dcd4b21a01 api safety 2010-07-09 13:51:42 +01:00
Shish
7943cd7682 start of the api 2010-07-09 13:51:29 +01:00
Shish
d57d9ee2de note the docs in the readme 2010-07-07 13:03:11 +01:00
Shish
c82350da05 hide disk_free_space errors 2010-06-27 17:32:34 +01:00
Shish
68ce20ae8b blargh 2010-06-17 19:44:26 +01:00
Shish
918c645829 file extension to confuse android less 2010-06-17 19:42:41 +01:00
Shish
c555dca40f the right class... 2010-05-28 13:36:56 +01:00
Shish
806352c0cb sometimes we want test uploads to fail 2010-05-28 13:25:48 +01:00
Shish
6fdc478b0f Convert some Extensions to SimpleExtensions with priorities 2010-05-28 13:08:04 +01:00
Shish
a8da2c14d6 SimpleExtension priority framework 2010-05-28 13:07:51 +01:00
Shish
c0ba0c3f3e nullify score button 2010-05-28 11:59:56 +01:00
Shish
aea5058d18 note that bulk_add requires admin 2010-05-28 11:39:46 +01:00
Shish
306d99d56c timeline function for profiling 2010-05-27 11:48:58 +01:00
Josh Sutinen
9918c1119c Paginator link code was ugly, if no filter criteria were set it would dump "&&&" at the end of the query string. This makes it look prettier. (v2 of fix) 2010-05-23 22:03:59 +01:00
Shish
449adbaff8 selectFirst is still a whore 2010-05-16 15:28:03 +01:00
Shish
fdd8e7cb7b hack to allow hardcoded database_dsn in hiphop 2010-05-15 16:25:12 +01:00
Shish
aad087cea8 version bump 2010-05-15 16:24:52 +01:00
Shish
4457de3cda for compiling under hiphop 2010-05-15 14:53:53 +01:00
Shish
d521b3739c final one? 2010-05-15 14:24:17 +01:00
Shish
09d08fa0e3 these are redundant too 2010-05-15 14:23:00 +01:00
Shish
7e0c39fe78 get rid of extra bits 2010-05-15 14:21:02 +01:00
Shish
d2875b98f1 class type hints for hiphop's benefit 2010-05-15 12:17:54 +01:00
Shish
b9474d9aa4 more numeric_score tests 2010-05-13 10:45:14 +01:00
Shish
b46d806fb6 A bunch of testing / fixes 2010-05-05 14:16:47 +01:00
Shish
393a88ca14 selectfirst is annoying 2010-04-28 11:21:26 +01:00
Shish
f57aa7ef5c mysql can go choke on a bucket of cocks 2010-04-27 11:36:36 +01:00
Shish
40ca6316fd turns out that this was necessary o_O 2010-04-26 06:07:49 +01:00
Shish
1ca3b24973 subtitle is ugly in this theme 2010-04-26 06:05:41 +01:00
Shish
d8eb1a6b20 a bunch of autocompletion 2010-04-26 06:00:57 +01:00
Shish
67a3776cef tag completion internal api 2010-04-26 05:50:20 +01:00
Shish
f33f1b4d77 shimmie already uses ?q= 2010-04-26 05:50:07 +01:00
Shish
ea1e8249b7 jquery versions; use a custom autocomplete plugin rather than jquery-ui's 2010-04-26 05:49:54 +01:00
Shish
b0150b0b93 Revert "search field autocomplete"
This reverts commit 29d9c7ae1600abd8f0136776daef16c9a23d2b8b.

zshall has already done this, better >_<
2010-04-26 05:04:29 +01:00
Shish
7b14e24475 search field autocomplete 2010-04-26 04:57:00 +01:00
Shish
81c661fe8b Merge branch 'pagevent' 2010-04-26 04:54:12 +01:00
Shish
64ca3d6018 new jquery 2010-04-26 04:52:54 +01:00
Shish
d5d59ce608 [postgres] remove warnings about overly-committed transactions 2010-04-23 16:18:55 +01:00
Shish
9ce1ccbe6d 'offset ? limit ?' is supported by more than 'limit ?, ?' 2010-04-23 16:11:03 +01:00
Shish
727941bc6b [postgres] offset/limit 2010-04-23 16:04:45 +01:00
Shish
5dda40513c move warehouse directory creation into a single place 2010-04-23 05:46:34 +01:00
Shish
794eff56b4 crudely hide explicit images from the comment list of anons 2010-04-23 04:08:40 +01:00
Shish
b7ca52165b double-escape backslashes as well as forward 2010-04-23 03:31:38 +01:00
Shish
9e7b1b1e79 link to ext_doc rather than ext_manager, for non-admins 2010-04-21 18:04:12 +01:00
Shish
fd3ff15485 avatar options 2010-04-21 17:57:47 +01:00
Shish
40b10a06e1 in theory, selectable avatar hosts 2010-04-21 17:32:59 +01:00
Shish
230d253142 Merge branch 'alltags' 2010-04-21 17:11:27 +01:00
Shish
1f9b0a8f71 remove shadow 2010-04-20 16:43:09 +01:00
Shish
a1ca6b8b1b opera = standards :O 2010-04-20 02:52:22 +01:00
Shish
91d423b22b new default 2010-04-20 02:32:49 +01:00
Shish
2045d12bed html5 theme 2010-04-20 02:32:37 +01:00
Shish
0e2c73ec2a border cancelling; somewhat of an ugly hack... 2010-04-19 20:42:26 +01:00
Shish
b45875619e remove error marker >_< 2010-04-11 13:14:13 +01:00
Zach Hall
06bd68e128 Removed some junk text from the lite theme. 2010-04-11 13:12:30 +01:00
Erik Youngren
d92ee0f8b8 Propogate 375c73d to the rest of the default themes. 2010-04-08 14:02:01 +01:00
Erik Youngren
e7bf4f9a8a Key sort page headers. 1000 should not be first. 2010-04-08 14:00:10 +01:00
Zach Hall
0b90d02318 Err... forgot one line, but now it's finished. 2010-04-08 13:23:51 +01:00
Zach Hall
d47b903449 Titlebar updates - current page links 2010-04-08 13:23:38 +01:00
Shish
14e5e96480 Merge branch 'lite_theme' 2010-04-07 13:46:14 +01:00
Shish
a4c93959f7 Comment::get_owner() function, as an official way to access user data (eg User::get_avatar()) 2010-04-07 13:39:12 +01:00
Shish
1d3d923449 timestamped comments 2010-04-07 13:26:10 +01:00
Josh Sutinen
67698b9c35 Fix bookmarklet using relative url into absolute url with make_http magic 2010-03-29 03:58:50 +01:00
Shish
4d5a8c9303 remove carridge returns when parsing alias CSVs 2010-03-27 04:01:50 +00:00
Shish
c3cc2f6097 make memcachecache pay attention to the URI 2010-03-25 10:53:17 +00:00
Shish
08fd957933 show user ID number 2010-03-25 10:51:47 +00:00
Shish
30779e0afc if necessary 2010-03-25 10:38:14 +00:00
Shish
bbdfc1fa89 version number >_< 2010-03-24 18:36:44 +00:00
Shish
ce488eb7e9 avoid double escapes 2010-03-24 18:25:08 +00:00
Shish
cf51d26d1a _end_coverage as a shutdown function, so that it still gets called on 'exit;' 2010-03-24 12:46:00 +00:00
Shish
59b25a0fe1 browser search test 2010-03-24 12:36:55 +00:00
Shish
f6d0c4df1b admin tools tests 2010-03-24 12:32:24 +00:00
Shish
6fa8442288 save a query 2010-03-24 04:44:49 +00:00
Zach Hall
bf2c629864 If a user doesn't have permission to view an image, not only will we hide it from the search results, we'll also disallow them access, redirecting them to post/list. 2010-03-24 04:41:28 +00:00
Shish
c38a66ae8f more banned words by default 2010-03-24 02:28:19 +00:00
Shish
f9411207d1 tidy up 2010-03-23 04:30:50 +00:00
Shish
dcf2623dfe remove redundantness 2010-03-23 01:08:27 +00:00
Shish
82145b853f make this work? 2010-03-22 15:36:59 +00:00
Shish
00b0cd7a39 more coverage 2010-03-22 14:47:49 +00:00
Shish
d1e1554827 more code coverage 2010-03-22 14:42:43 +00:00
Shish
6e1eca8aeb case sensitivity? 2010-03-22 04:30:23 +00:00
Shish
21d0eecebf two comments should exist now 2010-03-22 04:00:40 +00:00
Shish
b635a6441e only show extension docs if the extension exists 2010-03-22 03:58:37 +00:00
Shish
0ba2a49730 AHA! This bug took ages to track down, as the trigger was in a completely different system to the error message... Many, many thanks to zshall for managing to reproduce the error, then narrowing it down to a single line \o/ 2010-03-21 02:20:39 +00:00
Shish
9b2299cc23 build warehouse directories on demand 2010-03-15 05:35:22 +00:00
Shish
bcb98db9fb recovery console 2010-03-15 05:30:56 +00:00
Shish
c69f6c454c two references to trac, both are old 2010-03-15 03:32:15 +00:00
Shish
3682f84f04 test unicode comments 2010-03-14 20:51:35 +00:00
Shish
f93b2ad65b ... yeah 2010-03-14 02:19:29 +00:00
Shish
db5a0c682e make captchas optional and off by default, some people are having problems 2010-03-14 02:16:14 +00:00
Shish
134e4f8824 this uses post_image 2010-03-12 20:31:06 +00:00
Shish
a4b6ebbf89 more tests for search methods 2010-03-12 18:55:22 +00:00
Shish
0f85240af7 image ext tests 2010-03-12 18:48:43 +00:00
Shish
2f31c5995a more ext manager tests 2010-03-12 18:44:34 +00:00
Shish
51a695f185 ul and ol tags 2010-03-12 18:39:22 +00:00
Shish
2d66fafcf0 tips tests 2010-03-12 18:34:54 +00:00
Shish
6f3b992e9d test generation of the actual map 2010-03-12 17:58:40 +00:00
Shish
a5efff0ce9 this link is broken v.v 2010-03-10 19:27:15 +00:00
Shish
0f66c9d0cb avoid breakage when multiple extensions are installed alongside log_db 2010-03-08 21:56:50 +00:00
Shish
958efc2101 test source editing 2010-03-08 21:23:41 +00:00
Shish
23b44d2860 actually, we can use this 2010-03-08 18:30:25 +00:00
Shish
d52aff3396 next/prev with query test 2010-03-08 18:25:59 +00:00
Shish
f6687fcca0 note for updaters from 2.0 and 2.1 2010-03-02 02:48:37 +00:00
Shish
cd6fd1e87a search votes by IDs, in case of username weirdness 2010-03-01 00:16:13 +00:00
Shish
b4584f7003 indentation 2010-02-27 02:42:05 +00:00
Shish
abebbb36b8 found a bug 2010-02-27 02:41:25 +00:00
Shish
252dc45298 doc fix 2010-02-27 02:09:35 +00:00
Shish
5e09a0dee4 no need for datadict 2010-02-23 09:14:59 +00:00
Shish
c4a58d4d48 need page for error 2010-02-18 16:26:22 +00:00
Shish
d4c25d0684 403, not 503 2010-02-18 16:24:46 +00:00
Shish
7d07ad28ef test blotter denial 2010-02-18 16:22:41 +00:00
Shish
3f4f455ad6 spell 2010-02-18 15:59:21 +00:00
Shish
b865e696bc more blotter testing 2010-02-18 15:58:45 +00:00
Shish
164a0490b1 more ignorance 2010-02-18 14:35:43 +00:00
Shish
74000231dd blotter test 2010-02-18 14:35:10 +00:00
Shish
d3234ad96f html typo 2010-02-18 14:24:25 +00:00
Shish
43871d5cff SQL niceness 2010-02-18 14:20:51 +00:00
Shish
d20a6c17bb a bit more 2010-02-18 14:14:45 +00:00
Shish
9c88160f50 whitespace consistency 2010-02-18 14:13:15 +00:00
Zach Hall
1ac292697b Blotter Extension 2010-02-18 14:06:34 +00:00
Shish
b2edef6a83 postgres is true too 2010-02-17 15:14:08 +00:00
Shish
37619e9776 image locking 2010-02-17 14:17:03 +00:00
Shish
9d88f44d07 paginated logs 2010-02-12 17:22:43 +00:00
Shish
1efb08b0fc ipban: delete the cache after changing the data 2010-02-11 11:12:41 +00:00
Shish
e4a91ec215 regex for homepage links, hopefully less brokenness than the hand written parser 2010-02-11 11:08:12 +00:00
Shish
039f721725 Use Image::count_images() rather than SQL, to take advantage of advanced features and caching 2010-02-11 11:06:52 +00:00
Shish
66ce235436 fortmatted report text 2010-02-11 10:51:13 +00:00
Shish
ac3d7960d6 the final bit? 2010-02-09 10:08:24 +00:00
Shish
ba466412e4 post/view?search -> post/view#search for better caching 2010-02-09 10:06:34 +00:00
Shish
011e23b2fc post/view?search -> post/view#search for better caching 2010-02-09 10:06:13 +00:00
Shish
60e6f9b3da take next/prev from hash rather than query 2010-02-09 10:05:41 +00:00
Shish
8e54831873 URI based hash, for nginx compatability 2010-02-09 07:42:39 +00:00
Shish
91c06bd20e a technical explanation of build_accurate_search_querylet 2010-02-09 02:07:36 +00:00
Shish
b4624e912a link to tag changes 2010-02-03 23:55:49 +00:00
Shish
17218b0b08 correct some seemingly innocent behaviour, and thus fix a corner case in another extension 2010-02-03 23:55:37 +00:00
Shish
983f8451cf only optimise this case for regular tags 2010-02-03 22:04:22 +00:00
Shish
24fcc208aa comment IDs to link to 2010-02-03 16:01:24 +00:00
Shish
48a4dbd6e1 reset anon IDs on the comment/list too 2010-02-03 15:23:21 +00:00
Shish
a02e440048 postgres barfs if you try to compare a string to an inet_addr 2010-02-03 14:30:21 +00:00
Shish
8de01a6184 searchable event log 2010-02-03 13:58:51 +00:00
Shish
cea83d65ee if the search is for one tag, then 'count(tag)' (expensive function) = 'tag.count' (index lookup) 2010-02-02 18:20:13 +00:00
Shish
f21817b0d1 ID's anons 2010-02-02 18:15:53 +00:00
Shish
fe43af788f make apc not warn 2010-02-02 18:14:50 +00:00
Shish
454ade1769 activate caches if cache_dsn is set 2010-02-02 17:36:16 +00:00
Shish
2be4d32869 APC cache mechanism, faster than memcache? 2010-02-02 17:36:03 +00:00
Shish
70498bb07f limit/offset incompatability blah 2010-02-02 17:35:50 +00:00
Shish
9fbea9b440 make IP bans work better 2010-02-02 11:53:37 +00:00
Shish
54f44c7893 make ip_bans work without mysql specific code 2010-02-02 11:53:25 +00:00
Shish
8ba3d43128 case-insensitive search 2010-02-02 11:53:13 +00:00
Shish
50683e929e SCORE_STRNORM = lowercase-if-necessary-for-comparison 2010-02-02 02:14:29 +00:00
Shish
411b8b3bf7 no ha 2010-02-02 01:57:10 +00:00
Shish
83e5fb847a this uses the warehouse too 2010-02-02 01:55:55 +00:00
Shish
3df4c712fa more warehouse_path use 2010-02-02 01:07:48 +00:00
Shish
181bbcc9b7 link to image in the event log 2010-02-02 00:57:14 +00:00
Shish
c59ef1f355 typo 2010-02-02 00:56:53 +00:00
Shish
5690ab8078 lowercase match for pgsql 2010-02-02 00:45:35 +00:00
Shish
a74e4bb0c8 some only have concat, some only have pipes... 2010-02-02 00:43:26 +00:00
Shish
41e17a16ba wtf git 2010-02-02 00:35:59 +00:00
Shish
dc7de96ed5 more ignores 2010-02-01 23:51:11 +00:00
Shish
fbfa45fadf more sanity checks 2010-02-01 23:31:45 +00:00
Shish
102c8b413d pgsql fails at db->Insert_ID() 2010-02-01 23:25:35 +00:00
Shish
a681aae873 link to user's profile from the event log 2010-02-01 23:11:16 +00:00
Shish
2a6978abd9 log the right variable 2010-02-01 18:35:03 +00:00
Shish
306a1bf093 postgres requires separate 'offset' aside from 'limit' 2010-02-01 16:19:59 +00:00
Shish
0f38ac7a3c database-neutral table creation 2010-02-01 16:18:48 +00:00
Shish
78b85cb769 MemCache is already used... 2010-02-01 16:17:29 +00:00
Shish
41811385fa no concat in postgres, use pipes 2010-02-01 16:16:26 +00:00
Shish
891fb0478e 'PRAGMA foreign_keys' is SQLite-specific 2010-02-01 16:13:42 +00:00
Shish
8a26b4e9d1 make memcaching work for pages, and log if DEBUG is set 2010-02-01 16:11:54 +00:00
Zach Hall
cd931fb036 "Change e-mail" was missing in the danbooru theme. 2010-02-01 00:17:11 +00:00
Zach Hall
f26f5b2d89 CAPTCHA in Danbooru user signup form. 2010-01-28 13:35:43 +00:00
Shish
147075f539 fix pixel thumb gen 2010-01-27 12:25:47 +00:00
Shish
f8765de7a9 Turn the QR Code extension into a SimpleExtension, trim the boilerplate 2010-01-26 13:38:25 +00:00
Zach Hall
0ca0a546e5 QR Code extension 2010-01-26 13:35:55 +00:00
Shish
695a5a3a4b tag_history logging 2010-01-23 18:07:59 +00:00
Shish
35dccaf181 non-admins are allowed to see the extension list now (read-only) 2010-01-23 13:20:45 +00:00
Shish
e9b49a794f disable captchas when debugging from localhost 2010-01-23 12:48:54 +00:00
Shish
2f32024f71 put the captcha in the table 2010-01-23 12:40:15 +00:00
Shish
dc28be215b redundant declaration 2010-01-23 12:39:49 +00:00
Shish
93d3f471c4 make convert.exe findable on windows 2010-01-22 17:42:26 +00:00
Shish
cd150f1d6d strip metadata in a single imagemagick command 2010-01-21 16:11:56 +00:00
Shish
43b957d98a make make_http not break with '' as an argument 2010-01-18 08:29:48 +00:00
Shish
e0dd4b3a3a wider danbooru margin 2010-01-18 08:21:49 +00:00
Shish
d46421c538 dupe function name 2010-01-17 09:54:16 +00:00
Shish
8ec9ba0493 try to recognise the DB engine *before* trying to connect... 2010-01-17 09:43:59 +00:00
Shish
fbc06a1973 SQLite now supports foreign keys (3.6.19 onwards) 2010-01-17 09:42:52 +00:00
Shish
ba5f436cf3 documentation updates 2010-01-12 15:01:53 +00:00
Shish
cc8c8161d8 haaaack 2010-01-11 15:19:52 +00:00
Shish
270cdf2da5 Copyright notice updates 2010-01-11 15:11:05 +00:00
Shish
647e97ba86 nearly release yet? 2010-01-11 08:55:55 +00:00
Shish
fada3d8750 htmlable t&c 2010-01-05 19:06:45 +00:00
Shish
3c40ca8c47 set user creation to use the generic captcha api 2010-01-05 17:57:33 +00:00
Shish
02c9c7eced paginated image bans 2010-01-05 17:32:12 +00:00
Shish
618f739a59 more documentation, and set some extensions to be hidden from the user docs list 2010-01-05 13:18:01 +00:00
Shish
cab222c348 admin extension is no longer core 2010-01-05 11:08:46 +00:00
Shish
da4431e58d move image deletion function to ImageIO 2010-01-05 11:08:29 +00:00
Shish
10f6874164 better logging 2010-01-05 10:52:44 +00:00
Shish
d638dc4e53 Show all extensions in the extension list 2010-01-05 10:32:58 +00:00
Shish
02b7333eb8 lots of extension docs 2010-01-05 10:12:07 +00:00
Shish
f720a4e56b documentation 2010-01-05 10:11:31 +00:00
Shish
a84055a5e1 search by number of tags 2010-01-05 09:05:48 +00:00
Shish
fcf39e933b update extensions links 2010-01-04 12:44:34 +00:00
Shish
f496bc3a54 documentation updates 2010-01-04 12:42:01 +00:00
Shish
652cc475b1 give comments their own subnav 2010-01-04 11:52:28 +00:00
Shish
dbbcb51e2c for the danbooru subnav bar, assume that the front page = post/list 2010-01-04 11:25:22 +00:00
Shish
f0c18d3547 type checking for FavoriteSetEvent 2010-01-03 10:05:40 +00:00
Shish
fa3706243c style tips on danbooru 2010-01-03 09:57:53 +00:00
Shish
08320c5b3f add subheading support to all themes 2010-01-03 09:56:20 +00:00
Shish
8275222ae6 don't halt the page load while waiting for this 2010-01-03 09:51:20 +00:00
Shish
0633ad1f81 make the version string ungooglable 2010-01-03 09:43:48 +00:00
Shish
64eb907810 more danbooru updates 2010-01-03 09:42:45 +00:00
Shish
7c2d0473d2 and add the securimage library 2010-01-03 08:57:24 +00:00
Shish
3890fbb8ee updates for the danbooru theme 2010-01-03 08:56:03 +00:00
Shish
3026e9b2cc securimage support as an alternative captcha 2010-01-03 08:22:47 +00:00
Shish
73abe594d1 view and download featured 2010-01-02 10:17:17 +00:00
Shish
ec0d732a26 don't redirect-to-image if this is a single image on the last page of several pages 2009-12-30 09:29:49 +00:00
Shish
3b758e4075 email bbcode tag 2009-12-30 09:14:12 +00:00
Shish
3d8361ab71 preliminary image locking 2009-12-30 08:54:18 +00:00
Shish
97af29d7b6 make sure all tables are innodb, for foreign keys to work 2009-12-30 08:48:59 +00:00
Shish
0df9672063 argh whitespace 2009-12-30 08:25:03 +00:00
Shish
5c2515151a publicise Ratings functions because Pools wants to do the same thing 2009-12-30 08:24:19 +00:00
Shish
36812d085a allow authenticated find_posts in danbooru api 2009-12-30 08:22:03 +00:00
Shish
cc5a6a119a don't show explicit features to those who aren't allowed 2009-12-30 08:18:02 +00:00
Shish
035f8bda6e gah 2009-12-30 08:18:00 +00:00
Shish
eeee22863d pools extension 2009-12-30 08:11:38 +00:00
Shish
ddd91196d9 proper logging 2009-12-30 08:00:00 +00:00
Shish
d6280617b6 make danbooru aware of specific extensions 2009-12-30 07:01:28 +00:00
Shish
e7f3d015c6 Content-Length header for data pages 2009-12-26 01:01:03 +00:00
Shish
b42aa936d6 get the THUMB link... *headdesk* 2009-12-24 07:34:45 +00:00
Shish
c36a49edc4 into a clean folder 2009-12-24 07:08:22 +00:00
Shish
fafe81b258 double-bump 2009-12-24 06:58:05 +00:00
Shish
5813e23da9 ignore orphanned comments 2009-12-22 17:05:51 +00:00
Shish
11d686586d this breaks on windows? 2009-11-29 09:38:45 +00:00
Shish
e0584f6e3e dos2unix 2009-11-24 14:11:44 +00:00
Shish
7c946db89f how did nobody spot this? x_x 2009-11-20 11:52:26 +00:00
Shish
4b9f311ffd forgot to replace this 2009-11-15 12:09:40 +00:00
Shish
535bcc2a00 handle the case of the SQL log failing to open 2009-11-15 10:27:08 +00:00
Shish
3bc10bf64f fixed? 2009-11-12 09:30:52 +00:00
Shish
33dc03fb80 fail 2009-11-12 09:27:25 +00:00
Shish
6c1a218353 hash_ab and hash_cd templates for image URLs 2009-11-12 09:24:19 +00:00
Shish
831dc3449c sorted advanced options, easier to browse 2009-11-12 09:14:59 +00:00
Shish
180afca67a recaptcha signup link including domain 2009-11-10 04:36:23 +00:00
Shish
d70e00754f html tooltip for autodates 2009-11-10 03:50:53 +00:00
Shish
2b691408ed months and years for autodate 2009-11-10 03:36:51 +00:00
Shish
e0ab091d09 signup captcha 2009-11-10 03:21:19 +00:00
Shish
d91acbb03f this line is jarringly long... 2009-11-10 03:11:40 +00:00
Shish
0f34d28c33 better layout 2009-11-10 03:09:53 +00:00
Shish
92c8738747 remote api keys in their own block 2009-11-10 03:07:27 +00:00
Shish
5d88c4bff0 make recaptcha work 2009-11-10 03:01:42 +00:00
Shish
68df7b45e0 recaptcha support 2009-11-10 03:55:34 +00:00
Shish
9fb32d9f66 togglable setup blocks 2009-11-10 03:43:56 +00:00
Shish
fa404edb11 noscript link to a larger upload form 2009-10-26 12:09:08 +00:00
Shish
06f9ee1834 and remove the setting 2009-10-26 11:41:56 +00:00
Shish
33f6695e28 this setting is used before the database is connected to... 2009-10-26 11:40:27 +00:00
Shish
a5df66f6bc test the right thing... 2009-10-26 11:22:48 +00:00
Shish
e8861e4768 set comment limits higher 2009-10-26 11:22:45 +00:00
Shish
58febcd400 fix for bug #779 2009-10-23 14:52:09 +01:00
Shish
2ff8dbe54c test for bug #779 2009-10-23 14:52:08 +01:00
Shish
c6f940664f optional word wrapping 2009-10-18 00:33:28 +01:00
Shish
d46dca7fab avatar css 2009-10-10 01:53:50 +01:00
Shish
4a2297bd22 spelling 2009-10-09 13:30:33 +01:00
Shish
08bb4a7671 subheading section in the default theme 2009-10-09 12:30:18 +01:00
Shish
4a75b8a7a3 prefixed cookies 2009-10-09 12:29:28 +01:00
Shish
2f7565cbcb how did this work in the first place? x_x 2009-10-09 12:28:51 +01:00
Shish
1087409364 test to confirm bug #774 2009-10-09 12:27:55 +01:00
Shish
1ccf760709 not /all/ input elements should be 100% wide 2009-10-09 12:27:49 +01:00
Shish
f4d167809b a handy thing from the danbooru theme 2009-10-09 12:27:48 +01:00
Shish
a424b3632d truthomatic 2009-10-08 17:44:41 +01:00
Shish
eeaebd6e3a find truth 2009-10-08 17:44:08 +01:00
Shish
f718193449 add test case to confirm #761 2009-10-08 12:47:55 +01:00
Shish
e0fd446d84 configurability for avatars 2009-10-08 12:41:03 +01:00
Shish
3b2a05ac2d fix avatar display on user page 2009-10-08 12:32:28 +01:00
Shish
e18c8d27db User::get_avatar_html() 2009-10-08 02:59:16 +01:00
Shish
0d34c9b422 scaled category thumbnails 2009-10-08 02:52:03 +01:00
Shish
7b3cef4a9b easier to style quotes 2009-10-08 02:45:59 +01:00
Shish
49502e82c1 more testing 2009-09-27 15:48:57 +01:00
Shish
6af8fa8d72 make debugging and coverage separate settings 2009-09-27 14:04:38 +01:00
Shish
74b471c30a tips extension 2009-09-27 14:00:50 +01:00
Shish
3e9bf58335 wide textareas in tables too 2009-09-27 12:35:16 +01:00
Shish
d49e16ed9d more testes 2009-09-20 04:10:46 +01:00
Shish
efec93833a test that comments work in the comment RSS 2009-09-20 03:59:03 +01:00
Shish
8a2914ca74 test that this doesn't crash 2009-09-20 03:53:36 +01:00
Shish
8966cc6345 another test 2009-09-20 03:50:41 +01:00
Shish
c83f872723 nobody should be linking to pages like this any more... 2009-09-20 03:49:02 +01:00
Shish
a32c7997b4 not finished, but add to test coverage 2009-09-20 03:48:34 +01:00
Shish
c3f5ca032b test blank lines 2009-09-20 03:47:56 +01:00
Shish
8f17ef521c this isn't slow any more 2009-09-19 23:08:27 +01:00
Shish
5f7cba6940 more testing 2009-09-19 23:08:26 +01:00
Shish
2e3967bcb1 code coverage stuff 2009-09-19 20:25:12 +01:00
Shish
223f0cc4eb test spoiler code 2009-09-19 20:24:45 +01:00
Shish
b44f2919db test the next and prev buttons 2009-09-19 20:24:42 +01:00
Shish
486a3ae08e test the emoticon list 2009-09-19 20:24:39 +01:00
Shish
da46e000fb ignore more stuff 2009-09-19 15:22:27 +01:00
Shish
bd71a71020 link to target of alias 2009-09-15 18:43:15 +01:00
Shish
e2cb8f8cb0 don't code while asleep -_- 2009-09-15 18:35:09 +01:00
Shish
2ba033de32 configurable autodate format 2009-09-15 18:30:10 +01:00
Shish
a47bc67c50 split() is deprecated in php5.3 2009-09-14 21:19:35 +01:00
Shish
016f62dda4 extra unit testing note 2009-08-25 18:40:00 +01:00
Shish
b9fabb5d8a paginated aliases 2009-08-25 02:36:18 +01:00
Shish
fc6ed22988 meta keywords with commas 2009-08-24 06:50:43 +01:00
Shish
fd619d15d7 remove the whitespace 2009-08-24 05:34:14 +01:00
Shish
4a4bc1a37a allow themes to override this function 2009-08-24 03:01:03 +01:00
Shish
362891b4ed zebra styling 2009-08-24 03:57:34 +01:00
Shish
80e450ff0f odd/even comments 2009-08-24 03:43:20 +01:00
Shish
9f9986970e ignore duplicate tag history entries 2009-08-24 03:34:14 +01:00
Shish
283b2e974f bleh 2009-08-24 01:56:45 +01:00
Shish
ad772677a8 home page updates 2009-08-24 02:16:15 +01:00
Shish
e4f987c7ab extension documentation updates 2009-08-20 23:37:17 +01:00
Shish
695352ad16 correct docs 2009-08-20 20:51:40 +01:00
Shish
046fb2c33a did I mention 'aaaaaaargh php'? 2009-08-19 05:53:38 +01:00
Shish
ca97036735 sometimes these return values are used 2009-08-19 05:04:32 +01:00
Shish
fc28b7a663 this does need to be at the top, but not double star started... 2009-08-19 04:57:42 +01:00
Shish
cc07077621 more testing docs 2009-08-19 02:13:58 +01:00
Shish
81afbb30be consistent naming for test functions 2009-08-19 01:34:55 +01:00
Shish
0c9bd105cc only show the recent few comments on each image (for the long comment list) 2009-08-19 01:30:01 +01:00
Shish
45f6f89aed a start on testing docs, and some underscore-camelcase maps 2009-08-19 01:26:37 +01:00
Shish
35dd7fdf11 tags as page keywords 2009-08-19 00:51:55 +01:00
Shish
0ed1db7838 use_autodate option 2009-08-18 23:08:11 +01:00
Shish
7fcf6c8de1 zebraify reported images table 2009-08-18 22:42:37 +01:00
Shish
3677bcd793 update other themes 2009-08-18 22:33:24 +01:00
Shish
64d8b52b7b extendable user stats rather than hardcoded with assumptions of extensions 2009-08-18 22:31:28 +01:00
Shish
1c81ee280a fix the tests.. 2009-08-16 20:19:45 +01:00
Shish
9aa87ba8ea minmal rating tests 2009-08-13 21:43:06 +01:00
Shish
36f1fbc8bc don't force the user to set a rating 2009-08-13 20:11:05 +01:00
Shish
9ca3e6e03f danbooru theme updates by zshall <http://seemslegit.com> 2009-08-13 19:41:35 +01:00
Shish
49030ecffd only cache gets 2009-08-13 19:29:38 +01:00
Shish
e694d69eb6 test it 2009-08-13 19:27:20 +01:00
Shish
8ad176ade5 replace numeric_score 'favorite' with 'upvoted_by=name' (and add the same for downvote) 2009-08-13 19:26:23 +01:00
Shish
eacc3af131 fix this test 2009-08-13 19:01:23 +01:00
Shish
40f04677d0 test searching by score 2009-08-13 17:48:50 +01:00
Shish
2e63812024 test deleting one comment at a time 2009-08-12 15:45:38 +01:00
Shish
2661e26d81 make comment deletion do something... 2009-08-12 15:40:27 +01:00
Shish
00b66e383c missed a spot 2009-08-12 15:35:52 +01:00
Shish
b13ccccd69 settable email field 2009-08-11 17:09:56 +01:00
Shish
ea03506471 gravatar on user page 2009-08-11 15:43:51 +01:00
Shish
cc3af2b76f default to showing user links in danbooru submenu 2009-08-11 15:38:11 +01:00
Shish
dd5ffa26ef slightly less strict filename sanitising, for the unicode users 2009-08-11 15:13:20 +01:00
Shish
ae0498e4f0 missed a spot 2009-08-10 21:51:34 +01:00
Shish
612cb3768b if hit on disk, reload into memcache 2009-08-10 13:34:19 +01:00
Shish
e9ab06e1e9 make image metadata optional 2009-08-09 22:01:28 +01:00
Shish
88eecb5a6b better caching code 2009-08-09 13:14:27 +01:00
Shish
dca588450b load config.php sooner 2009-08-09 13:14:25 +01:00
Shish
8b29aa80fe emoticon list 2009-08-08 17:43:40 +01:00
Shish
994fa2a9b6 fix comment page count 2009-08-04 17:58:41 +01:00
Shish
fa30a08246 make user email accessable to comment themers 2009-08-04 17:52:00 +01:00
Shish
f83b8b473e this went in the wrong theme 2009-08-04 17:49:00 +01:00
Shish
bdd0299cf0 comment theme API changes 2009-08-04 17:45:42 +01:00
Shish
bc706fde05 ignore more 2009-08-04 17:19:21 +01:00
Shish
374a85e545 remove minimal theme, it is full of ugly 2009-08-04 17:18:21 +01:00
Shish
0528d0a765 better looking downtime message, and login box 2009-08-03 19:58:12 +01:00
Shish
be3e582de6 sitemap test file 2009-08-03 18:35:49 +01:00
Shish
8e00495f7e sitemap extension from Sein Kraft 2009-08-03 18:25:51 +01:00
Shish
c2c7e245d8 case 2009-08-03 12:08:06 +01:00
Shish
047166a92c bbcode img tag support, and unwrap wordwrapped urls 2009-08-03 12:37:35 +01:00
Shish
b1407028b1 wiki diff on conflict 2009-08-03 11:23:49 +01:00
Shish
2eeb0b1caf wiki updates 2009-08-03 11:04:43 +01:00
Shish
b2c2368cfc themable user links 2009-08-03 10:47:09 +01:00
Shish
6322589265 only show uploader to admins 2009-08-03 10:24:48 +01:00
Shish
83d92c6413 bulk upload / zip upload betterness 2009-08-03 10:19:33 +01:00
Shish
80cb9d0083 search images by comment metadata 2009-08-03 10:18:40 +01:00
Shish
632bf99261 xspf player is bsd, and why did we have the full size version instead of slim? 2009-08-02 14:44:51 +01:00
Shish
33fbc52862 glob rather than readdir, sorts too 2009-08-02 09:36:27 +01:00
Shish
f12c963ee0 version bump 2009-08-02 09:28:28 +01:00
Shish
5a8bcad72c any search for a rating will override the default 2009-08-02 09:24:55 +01:00
Shish
f44634b67d update this too 2009-08-02 09:10:43 +01:00
Shish
8f7041f1d7 everything else only has add boxes at the bottom 2009-08-02 09:08:28 +01:00
Shish
1b63cffd2d test that too 2009-08-02 08:59:54 +01:00
Shish
50acd8c6ad Merge branch 'branch_2.3' of ssh://shish@marigold.shishnet.org/home/shish/git/shimmie2 into branch_2.3 2009-08-02 08:57:42 +01:00
Shish
c6636265cb Merge branch 'branch_2.3' of ssh://marigold.shishnet.org/git/shimmie2 into branch_2.3 2009-08-02 09:47:34 +01:00
Shish
c525be6b31 too much search & replace 2009-08-02 09:29:06 +01:00
Shish
02ea593091 also allow search by 'md5=' 2009-08-02 08:57:30 +01:00
Shish
98fa324786 search for ratings with full words 2009-08-02 08:43:13 +01:00
Shish
613be41240 this needs autoblanking too 2009-08-02 08:33:33 +01:00
Shish
008709af26 bulk image rater 2009-08-02 08:20:07 +01:00
Shish
c2a56728d6 crushed pngs 2009-08-01 09:16:26 +01:00
Shish
aaa379930b Revert "load scripts at the bottom of the page" - inline scripts require jquery >_<
This reverts commit efe4852dc4be1e010d204bdc1fa221c6440c50d7.
2009-08-01 09:04:40 +01:00
Shish
0d2c597010 not in this theme 2009-08-01 09:00:06 +01:00
Shish
efe4852dc4 load scripts at the bottom of the page 2009-08-01 08:58:20 +01:00
Shish
9575a06c68 compressed cached pages 2009-08-01 08:57:50 +01:00
Shish
85e65075e6 off by default 2009-08-01 02:32:28 +01:00
Shish
57d8a8bfd6 static file caching 2009-08-01 02:32:07 +01:00
Shish
0e4eac909c give a better label to flash things 2009-07-30 05:12:36 +01:00
Shish
9913937a25 bring back X blah per day 2009-07-30 03:48:16 +01:00
Shish
476d2a33c7 make search by favourite count work 2009-07-30 03:27:40 +01:00
Shish
2758b13601 make autodate work, and use it a bit 2009-07-28 23:56:56 +01:00
Shish
b1dbb3a792 long line splitter 2009-07-28 23:42:55 +01:00
Shish
4cc77668e6 link to help 2009-07-28 23:21:59 +01:00
Shish
0563d3fb1f danbooru theme updates 2009-07-28 23:07:21 +01:00
Shish
450102233a link to site needs httpisation too 2009-07-28 21:13:17 +01:00
Shish
353970a902 favorites testing and tweak 2009-07-28 11:45:21 +01:00
Shish
e29db93834 make favorites work with 2.3 2009-07-28 11:22:13 +01:00
Shish
7336ff2b22 initial import of favorites extension 2009-07-28 11:22:08 +01:00
Shish
f8feecf5e2 not only for admins 2009-07-28 11:18:24 +01:00
Shish
8fd6d44958 a ton of wiki improvements 2009-07-28 09:40:37 +01:00
Shish
a69bf05e36 a bunch of wiki fixes 2009-07-28 04:49:24 +01:00
Shish
fdd1ffaf0f make testing work again... 2009-07-28 04:45:50 +01:00
Shish
66ef82e460 wiki updates (broken atm) 2009-07-28 02:07:07 +01:00
Shish
56072075f7 escape it 2009-07-28 01:32:18 +01:00
Shish
adffa0f18f show wiki page title 2009-07-28 01:30:41 +01:00
Shish
8680f27bf1 add label for tag_history 2009-07-28 01:25:49 +01:00
Shish
1052b5d356 mysql fails at count() = 0 2009-07-28 01:20:41 +01:00
Shish
8a64e171b7 updateses 2009-07-28 01:09:49 +01:00
Shish
cc9cefe41e somewhat updated danbooru comment theme 2009-07-28 01:09:44 +01:00
Shish
2c5a688335 exif ifd0 info in the sidebar 2009-07-28 00:22:02 +01:00
Shish
fc7fd8d1f7 here too 2009-07-24 08:10:23 +01:00
Shish
fd85f390e5 make_http function 2009-07-24 08:10:11 +01:00
Shish
2b4733bf06 site description keywords 2009-07-24 04:36:05 +01:00
Shish
ce76dfbb2b show tag count, rather than calculated count 2009-07-24 03:07:25 +01:00
Shish
58f2ba539a new theme 2009-07-23 02:33:21 +01:00
Shish
88c7233ced Merge branch 'branch_2.3' of ssh://shish@marigold.shishnet.org/home/shish/git/shimmie2 into branch_2.3 2009-07-22 22:09:28 +01:00
Shish
cff145d72b one transparent circle, rather than a colour per rect 2009-07-22 22:09:05 +01:00
Shish
6f2711f422 custom page is custom 2009-07-21 07:40:00 +01:00
Shish
67f3ce0543 Merge branch 'branch_2.3' of ssh://marigold.shishnet.org/git/shimmie2 into branch_2.3 2009-07-21 07:36:27 +01:00
Shish
f712147553 lots of docs, and some internal changes 2009-07-21 07:36:12 +01:00
Shish
c31342a1c7 bump 2009-07-21 05:08:23 +01:00
Shish
f27fa1f314 docs 2009-07-21 04:18:40 +01:00
Shish
05449ef8d3 more tests... 2009-07-20 07:31:41 +01:00
Shish
2cd3b0812b MOAR TESTING. 2009-07-20 06:51:36 +01:00
Shish
8b0d1dc3db hide user stats from anonymous 2009-07-20 06:42:09 +01:00
Shish
a348e3df29 disk stats for et 2009-07-20 04:27:21 +01:00
Shish
9a1de36a23 more tests, and make link to image work better 2009-07-19 19:35:46 +01:00
Shish
281638c295 use the current favicon rather than a hardcoded one 2009-07-19 18:04:52 +01:00
Shish
487a59b0db more tests, more fixes 2009-07-19 17:59:57 +01:00
Shish
bec162293e more tests and fixes 2009-07-19 17:39:07 +01:00
Shish
3ebf861441 someone else is defining these... 2009-07-19 17:21:49 +01:00
Shish
d9ec5056d4 res_limit testing and fixing 2009-07-19 17:18:06 +01:00
Shish
16fe1424dd bug fixing & tidying 2009-07-19 08:56:24 +01:00
Shish
96ce9c70a7 lots of docs 2009-07-19 08:38:13 +01:00
Shish
cdeacb9765 make images in the nav bar break less 2009-07-19 07:32:56 +01:00
Shish
96352152b2 make the featured image extension work again 2009-07-19 07:28:54 +01:00
Shish
eda3b0aa61 a load more tests 2009-07-19 04:48:25 +01:00
Shish
2da1cd8c9d Merge branch 'branch_2.3' of ssh://marigold.shishnet.org/git/shimmie2 into branch_2.3 2009-07-19 01:54:19 +01:00
Shish
9c6d0dc394 more testing 2009-07-19 01:29:59 +01:00
Shish
8cd6dbc2a0 allow people to view images rated 'unknown'... 2009-07-19 01:29:48 +01:00
Shish
af8c59fcd1 version bump 2009-07-17 03:00:04 +01:00
Shish
4148106ff0 sqlite compat for ipbans 2009-07-17 02:43:57 +01:00
Shish
9724b13f81 merge both log functions into one; simply saying that they take different numbers of arguments isn't enough... 2009-07-17 01:55:07 +01:00
Shish
2082334c24 Merge branch 'branch_2.3' of ssh://marigold.shishnet.org/git/shimmie2 into branch_2.3 2009-07-17 00:36:50 +01:00
Shish
8230a36565 save some bandwidth by removing a load of decimal places 2009-07-17 00:35:52 +01:00
Shish
e81c3e288d sqlite fails at count distinct 2009-07-17 00:28:07 +01:00
Shish
c9f743bf8f sqlite returns column names as table.name, not just name 2009-07-17 00:27:40 +01:00
Shish
c917bf0434 only hidablise relevant things, and make the whole block clickable 2009-07-16 21:05:00 +01:00
Shish
cb56587970 make sure 0 is printed 2009-07-16 20:56:39 +01:00
Shish
7a3e032096 rename an ambiguous column 2009-07-16 20:51:35 +01:00
Shish
5922fb6f25 Merge branch 'branch_2.3' of ssh://marigold.shishnet.org/git/shimmie2 into branch_2.3 2009-07-16 20:44:20 +01:00
Shish
bf2fdffc75 re-initialise the database after re-opening the connection (allows sqlite to use now()) 2009-07-16 20:44:04 +01:00
Shish
75d4c6cf73 yet more tests 2009-07-16 20:21:49 +01:00
Shish
d08b7faf63 more tests 2009-07-16 20:20:53 +01:00
Shish
d7ce9add6a aaaaaaaargh dates and times in sql 2009-07-15 23:29:14 +01:00
Shish
9d968b630d more reliable self finding 2009-07-15 23:29:01 +01:00
Shish
4f14197577 remove the auto install file after using it 2009-07-15 22:20:10 +01:00
Shish
179e18abb2 warn about missing mysql 2009-07-15 22:17:53 +01:00
Shish
9483cc59f3 actually that doesn't work v_v 2009-07-15 19:07:09 +01:00
Shish
ead9e27dea more tests 2009-07-15 17:09:50 +01:00
Shish
d2cad188b4 A ton of tests, figured out how to test uploads \o/ 2009-07-15 02:42:18 +01:00
Shish Moom
f59e39d42b minor fix 2009-07-14 13:10:16 -07:00
Shish Moom
819fb722c3 store arrays in config, edit in setup with add_multichoice_option 2009-07-11 04:44:08 -07:00
Shish Moom
412b71c2d3 margiiiiin~ 2009-07-09 12:51:00 -07:00
Shish Moom
4ac62e5544 tweaks for fresh install 2009-07-07 08:52:09 -07:00
Shish Moom
6b88e5fd5a .htaccess updates, expires and compression headers 2009-07-07 08:33:42 -07:00
Shish Moom
5c1093bcf0 no more trac 2009-07-07 08:08:19 -07:00
Shish Moom
fe09b18832 contact details 2009-07-07 07:56:06 -07:00
Shish Moom
ac6812d184 bump 2009-07-07 07:51:05 -07:00
Shish Moom
b9d80f8d38 get rid of shimmie's JS lib, use jquery 2009-07-07 07:50:09 -07:00
Shish Moom
8db522b916 remove table border 2009-07-07 07:50:05 -07:00
Shish Moom
ae85d76f15 auto-include library scripts 2009-07-07 07:49:58 -07:00
Shish Moom
2ad2689d28 jquery start 2009-07-07 07:49:54 -07:00
Shish Moom
20ed4f1503 more theme niceness 2009-07-06 14:24:02 -07:00
Shish Moom
99534afe08 feature! 2009-07-06 05:09:33 -07:00
Shish Moom
4fbd714930 smaller font for setup boxes 2009-07-06 05:08:11 -07:00
Shish Moom
c953d5bd6c more niceness 2009-07-06 04:58:43 -07:00
Shish Moom
9a830edbb7 hide the whole block 2009-07-06 04:58:35 -07:00
Shish Moom
784296d22c textarea tweak 2009-07-06 04:58:31 -07:00
Shish Moom
2644eed5bc new default theme 2009-07-06 04:58:27 -07:00
Shish Moom
ac0dad4093 move old default 2009-07-06 04:58:23 -07:00
Shish Moom
8af3c4f24e theme changes 2009-07-06 04:58:14 -07:00
Shish Moom
f8ec3baeea User::by_list from trunk 2009-07-01 16:55:49 -07:00
Shish Moom
ab1d755a01 another simpleextnsion 2009-07-01 16:52:48 -07:00
Shish Moom
351c3469dc regen_thumb still had event->page, and version bump 2009-07-01 16:45:06 -07:00
Shish Moom
7d827d3392 no need for hostname 2009-07-01 05:17:31 -07:00
Shish
ef5d7474c5 don't break when base_href='' 2009-06-29 19:03:14 -07:00
Shish Moom
ca675fcbb0 fix login... 2009-06-06 12:08:19 -07:00
Shish Moom
d240a29aa9 basic user list 2009-06-06 12:07:44 -07:00
Shish Moom
b6a4911b67 allow null as navigation 2009-06-06 12:01:33 -07:00
Erik Youngren
91829cc2ea You know that feeling you've done something the hard way? Yeah. Replacing theme_redirect with a three line function in core/utils.inc.php 2009-06-06 06:09:47 -07:00
Shish Moom
43c98e51c2 array_contains -> in_array 2009-06-05 12:54:23 -07:00
Shish Moom
5c88b84378 update links 2009-06-05 09:57:22 -07:00
Shish Moom
3da11198af wiki fixes 2009-05-30 06:50:24 -07:00
Shish
18f9a72851 explanation and credit 2009-05-15 01:53:15 -07:00
Shish
de7f1f9cfe forum is broken >_< 2009-05-12 06:00:33 -07:00
JJS
06cfda990d Fix bug in Source: link in danbooru theme 2009-05-12 03:12:48 -07:00
JJS
dd60840e4b Bugfix: $c->get_string to $config->get_string 2009-05-12 03:11:29 -07:00
Shish
5ab092946a make things use SimpleExtension 2009-05-11 14:10:15 -07:00
Shish
e29d475dea SimpleExtension, like Extension but with more Magic 2009-05-11 14:10:09 -07:00
Shish
0ac787c3e3 allow this to be turned on via config 2009-05-11 14:10:01 -07:00
Shish
aeba6665f0 a couple of fixes 2009-05-11 07:48:43 -07:00
Shish
74a0bc642e typo 2009-05-11 07:13:49 -07:00
Shish
2bdfec3e76 merge changes from master 2009-05-11 07:10:18 -07:00
Shish
7fcaf851f1 a couple of changes from master 2009-05-11 03:46:18 -07:00
Shish
bc23362bda more 2009-05-11 03:42:54 -07:00
Shish
74655bac27 comment out example thingy 2009-05-11 03:42:44 -07:00
Shish
5384e11735 punctuations 2009-05-11 03:42:36 -07:00
Shish
509ea8614c no need for formatting 2009-05-11 03:42:31 -07:00
Shish
a1cc9d1953 newline 2009-05-11 03:42:24 -07:00
Shish
70173468a5 logging things 2009-05-11 03:42:18 -07:00
Shish
7229b2669a php has a constant for this 2009-05-11 03:37:30 -07:00
Shish
482e22c90e static 2009-05-11 03:32:53 -07:00
Shish
40b2a4cb05 fixes for mysql 2009-05-09 05:40:26 -07:00
Shish
52cc6c95db search term may expand into several querylets 2009-05-09 05:37:50 -07:00
Shish
d525fc6894 missed a spot 2009-05-09 04:57:38 -07:00
Shish
4bdd544ff1 space 2009-05-09 01:47:02 -07:00
Shish
47bc2570b6 fix in 2.3 too 2009-05-09 01:31:19 -07:00
Shish
09b1bb3554 separate query path for mysql, because it chokes on subqueries 2009-05-08 07:44:23 -07:00
Shish
8512f45c9d note favourite search 2009-05-08 04:49:47 -07:00
Shish
8b207b84fe only search for prev/next when asked for 2009-05-08 04:48:21 -07:00
Shish
25e77bb7c1 search for upvoted images 2009-05-08 04:48:13 -07:00
Shish
d15e8d231f for searches with a single result, view the result 2009-05-08 04:47:57 -07:00
Shish
dfc7411975 Merge commit 'origin/master' into branch_2.3 2009-01-25 09:58:56 -08:00
Shish
a543f75c99 basic word filter test 2009-01-25 12:42:37 +00:00
Shish
bb38904cdf call assert() from the right place 2009-01-25 12:42:21 +00:00
Shish
fbd7039ad3 preliminary hash ban testing 2009-01-25 12:37:45 +00:00
Shish
963535b748 more bbcode tests 2009-01-25 12:26:07 +00:00
Shish
b49bc84c51 move shimmie-specific sqlite things out of adodb and into shimmie's database engine 2009-01-25 12:10:10 +00:00
Shish
6d8ac0b4e2 more functions that sqlite needs 2009-01-25 11:49:58 +00:00
Shish
275e0b3b64 remove in-development extensions 2009-01-25 11:49:42 +00:00
Shish
cca47a1df9 new feature list 2009-01-25 11:47:07 +00:00
Shish
26e1383b0a tag_explode -> Tag::explode 2009-01-24 11:05:07 -08:00
Shish
199a0d709c fixes 2009-01-24 11:01:23 -08:00
859 changed files with 58118 additions and 51167 deletions

View File

@ -1,8 +0,0 @@
vendor
.git
*.phar
data
images
thumbs
*.sqlite
Dockerfile

View File

@ -1,21 +0,0 @@
# In retrospect I'm less of a fan of tabs for indentation, because
# while they're better when they work, they're worse when they don't
# work, and so many people use terrible editors when they don't work
# that everything is inconsistent... but tabs are what Shimmie went
# with back in the 90's, so that's what we use now, and we deal with
# the pain of making sure everybody configures their editor properly
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.{js,css,php}]
charset = utf-8
indent_style = space
indent_size = 4

1
.gitattributes vendored
View File

@ -1 +0,0 @@
*.php text eol=lf

View File

@ -1,28 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
**Server Software**
(You can get all these stats from `http://<your site>/system_info`)
- Shimmie version:
- Database: [mysql, postgres, ...]
- Web server: [apache, nginx, ...]
**Client Software (please complete the following information)**
- Device: [e.g. iphone, windows desktop]
- Browser: [e.g. chrome, safari]
**What steps trigger this bug**
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
**What did you expect to happen?**
A clear and concise description of what you expected to happen.
**What actually happened?**
If applicable, add screenshots to help explain your problem.

View File

@ -1,17 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -1,62 +0,0 @@
name: Create Release
on:
push:
tags:
- 'v*'
jobs:
build:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@master
- name: Get version from tag
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
- name: Test version in sys_config
run: grep ${{ steps.get_version.outputs.VERSION }} core/sys_config.php
- name: Build
run: |
composer install --no-dev
cd ..
tar cvzf shimmie2-${{ steps.get_version.outputs.VERSION }}.tgz shimmie2
zip -r shimmie2-${{ steps.get_version.outputs.VERSION }}.zip shimmie2
- name: Create Release
id: create_release
uses: actions/create-release@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Shimmie ${{ steps.get_version.outputs.VERSION }}
body: Automated release from tags
draft: false
prerelease: false
- name: Upload Zip
id: upload-release-asset-zip
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ../shimmie2-${{ steps.get_version.outputs.VERSION }}.zip
asset_name: shimmie2-${{ steps.get_version.outputs.VERSION }}.zip
asset_content_type: application/zip
- name: Upload Tar
id: upload-release-asset-tar
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ../shimmie2-${{ steps.get_version.outputs.VERSION }}.tgz
asset_name: shimmie2-${{ steps.get_version.outputs.VERSION }}.tgz
asset_content_type: application/gzip

View File

@ -1,95 +0,0 @@
name: Test & Publish
on:
push:
pull_request:
schedule:
- cron: '0 2 * * 0' # Weekly on Sundays at 02:00
jobs:
test:
name: PHP ${{ matrix.php }} / DB ${{ matrix.database }}
strategy:
max-parallel: 3
fail-fast: false
matrix:
php: ['7.3']
database: ['pgsql', 'mysql', 'sqlite']
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up PHP
uses: shivammathur/setup-php@master
with:
php-version: ${{ matrix.php }}
coverage: pcov
extensions: mbstring
- name: Set up database
run: |
mkdir -p data/config
if [[ "${{ matrix.database }}" == "pgsql" ]]; then
sudo systemctl start postgresql ;
psql --version ;
sudo -u postgres psql -c "SELECT set_config('log_statement', 'all', false);" -U postgres ;
sudo -u postgres psql -c "CREATE USER shimmie WITH PASSWORD 'shimmie';" -U postgres ;
sudo -u postgres psql -c "CREATE DATABASE shimmie WITH OWNER shimmie;" -U postgres ;
fi
if [[ "${{ matrix.database }}" == "mysql" ]]; then
sudo systemctl start mysql ;
mysql --version ;
mysql -e "SET GLOBAL general_log = 'ON';" -uroot -proot ;
mysql -e "CREATE DATABASE shimmie;" -uroot -proot ;
fi
if [[ "${{ matrix.database }}" == "sqlite" ]]; then
sudo apt update && sudo apt-get install -y sqlite3 ;
sqlite3 --version ;
fi
- name: Check versions
run: php -v && composer -V
- name: Validate composer.json and composer.lock
run: composer validate
- name: Install PHP dependencies
run: composer install --prefer-dist --no-progress --no-suggest
- name: Install shimmie
run: php index.php
- name: Run test suite
run: |
if [[ "${{ matrix.database }}" == "pgsql" ]]; then
export TEST_DSN="pgsql:user=shimmie;password=shimmie;host=127.0.0.1;dbname=shimmie"
fi
if [[ "${{ matrix.database }}" == "mysql" ]]; then
export TEST_DSN="mysql:user=root;password=root;host=127.0.0.1;dbname=shimmie"
fi
if [[ "${{ matrix.database }}" == "sqlite" ]]; then
export TEST_DSN="sqlite:data/shimmie.sqlite"
fi
vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-clover=data/coverage.clover
- name: Upload coverage
run: |
wget https://scrutinizer-ci.com/ocular.phar
php ocular.phar code-coverage:upload --format=php-clover data/coverage.clover
publish:
name: Publish
runs-on: ubuntu-latest
needs: test
if: github.event_name == 'push'
steps:
- uses: actions/checkout@master
- name: Publish to Registry
uses: elgohr/Publish-Docker-Github-Action@master
with:
name: shish2k/shimmie2
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
cache: ${{ github.event_name != 'schedule' }}
buildoptions: "--build-arg RUN_TESTS=false"

145
.gitignore vendored
View File

@ -1,89 +1,60 @@
backup
data
.svn
config.php
images
thumbs
*.phar
*.sqlite
*.cache
.devcontainer
trace.json
#Composer
composer.phar
composer.lock
/vendor/
# Created by http://www.gitignore.io
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
### OSX ###
.DS_Store
.AppleDouble
.LSOverride
# Icon must ends with two \r.
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
### Linux ###
*~
# KDE directory preferences
.directory
### vim ###
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
### PhpStorm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
## Directory-based project format
.idea/
# if you remove the above rule, at least ignore user-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# and these sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
## File-based project format
*.ipr
*.iws
*.iml
## Additional for IntelliJ
out/
# generated by mpeltonen/sbt-idea plugin
.idea_modules/
# generated by JIRA plugin
atlassian-ide-plugin.xml
# generated by Crashlytics plugin (for Android Studio and Intellij)
com_crashlytics_export_strings.xml
data
sql.log
shimmie.log
ext/admin
ext/autocomplete
ext/ban_words
ext/blotter
ext/browser_search
ext/bulk_add
ext/danbooru_api
ext/downtime
ext/emoticons
ext/et
ext/event_log
ext/favorites
ext/featured
ext/forum
ext/handle_archive
ext/handle_flash
ext/handle_ico
ext/handle_mp3
ext/handle_svg
ext/home
ext/image_hash_ban
ext/ipban
ext/link_image
ext/log_db
ext/news
ext/notes
ext/notes
ext/numeric_score
ext/piclens
ext/pm
ext/pools
ext/qr_code
ext/random_image
ext/rating
ext/regen_thumb
ext/report_image
ext/res_limit
ext/rss_comments
ext/rss_images
ext/shimmie_api
ext/simpletest
ext/site_description
ext/sitemap
ext/svn_update
ext/tagger
ext/tag_history
ext/text_score
ext/tips
ext/amazon_s3
ext/upload_cmd
ext/wiki
ext/word_filter
ext/zoom

View File

@ -3,65 +3,39 @@
</IfModule>
<FilesMatch "\.(sqlite|sdb|s3db|db)$">
<IfModule mod_authz_host.c>
Require all denied
</IfModule>
<IfModule !mod_authz_host.c>
Deny from all
</IfModule>
</FilesMatch>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteEngine on
# rather than link to images/ha/hash and have an ugly filename,
# we link to images/hash/tags.ext; mod_rewrite splits things so
# that shimmie sees hash and the user sees tags.ext
RewriteRule ^_(images|thumbs)/([0-9a-f]{2})([0-9a-f]{30}).*$ data/$1/$2/$2$3 [L]
RewriteRule ^_images/([0-9a-f]{2})([0-9a-f]{30}).*$ images/$1/$1$2 [L]
RewriteRule ^_thumbs/([0-9a-f]{2})([0-9a-f]{30}).*$ thumbs/$1/$1$2 [L]
# any requests for files which don't physically exist should be handled by index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?q=$1&%{QUERY_STRING} [L]
</IfModule>
<IfModule mod_php5.c>
php_flag register_globals 0
php_flag magic_quotes_gpc 0
php_flag magic_quotes_runtime 0
</IfModule>
DefaultType image/jpeg
<IfModule mod_expires.c>
ExpiresActive On
<FilesMatch "([0-9a-f]{32}|\.(gif|jpe?g|png|webp|css|js))$">
<IfModule mod_headers.c>
Header set Cache-Control "public, max-age=2629743"
</IfModule>
ExpiresDefault "access plus 1 month"
</FilesMatch>
#ExpiresByType text/html "now"
#ExpiresByType text/plain "now"
ExpiresByType text/html "now"
ExpiresByType text/plain "now"
</IfModule>
<IfModule mod_deflate.c>
<ifmodule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
AddOutputFilterByType DEFLATE application/x-javascript application/javascript
</IfModule>
#EXT: handle_pixel
AddType image/jpeg jpg jpeg
AddType image/gif gif
AddType image/png png
AddType image/webp webp
#EXT: handle_ico
AddType image/x-icon ico ani cur
#EXT: handle_flash
AddType application/x-shockwave-flash swf
#EXT: handle_mp3
AddType audio/mpeg mp3
#EXT: handle_svg
AddType image/svg+xml svg svgz
#EXT: handle_video
AddType video/x-flv flv
AddType video/mp4 f4v f4p m4v mp4
AddType audio/mp4 f4a f4b m4a
AddType video/ogg ogv
AddType video/webm webm
</ifmodule>

View File

@ -1,19 +0,0 @@
<?php
$finder = PhpCsFixer\Finder::create()
->exclude('ext/amazon_s3/lib')
->exclude('vendor')
->exclude('data')
->in(__DIR__)
;
return PhpCsFixer\Config::create()
->setRules([
'@PSR2' => true,
//'strict_param' => true,
'array_syntax' => ['syntax' => 'short'],
])
->setFinder($finder)
;
?>

View File

@ -1,19 +0,0 @@
imports:
- javascript
- php
filter:
excluded_paths: [ext/*/lib/*,ext/tagger/script.js,tests/*]
build:
nodes:
analysis:
tests:
before:
- mkdir -p data/config
- cp tests/defines.php data/config/shimmie.conf.php
override:
- php-scrutinizer-run
tools:
external_code_coverage: true

View File

@ -1,49 +0,0 @@
# "Build" shimmie (composer install - done in its own stage so that we don't
# need to include all the composer fluff in the final image)
FROM debian:stable-slim AS app
RUN apt update && apt install -y composer php7.3-gd php7.3-dom php7.3-sqlite3 php-xdebug imagemagick
COPY composer.json composer.lock /app/
WORKDIR /app
RUN composer install --no-dev
COPY . /app/
# Tests in their own image. Really we should inherit from app and then
# `composer install` phpunit on top of that; but for some reason
# `composer install --no-dev && composer install` doesn't install dev
FROM debian:stable-slim AS tests
RUN apt update && apt install -y composer php7.3-gd php7.3-dom php7.3-sqlite3 php-xdebug imagemagick
COPY composer.json composer.lock /app/
WORKDIR /app
RUN composer install
COPY . /app/
ARG RUN_TESTS=true
RUN [ $RUN_TESTS = false ] || (\
echo '=== Installing ===' && mkdir -p data/config && INSTALL_DSN="sqlite:data/shimmie.sqlite" php index.php && \
echo '=== Smoke Test ===' && php index.php get-page /post/list && \
echo '=== Unit Tests ===' && ./vendor/bin/phpunit --configuration tests/phpunit.xml && \
echo '=== Coverage ===' && ./vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text && \
echo '=== Cleaning ===' && rm -rf data)
# Build su-exec so that our final image can be nicer
FROM debian:stable-slim AS suexec
RUN apt-get update && apt-get install -y --no-install-recommends gcc libc-dev curl
RUN curl -k -o /usr/local/bin/su-exec.c https://raw.githubusercontent.com/ncopa/su-exec/master/su-exec.c; \
gcc -Wall /usr/local/bin/su-exec.c -o/usr/local/bin/su-exec; \
chown root:root /usr/local/bin/su-exec; \
chmod 0755 /usr/local/bin/su-exec;
# Actually run shimmie
FROM debian:stable-slim
EXPOSE 8000
HEALTHCHECK --interval=5m --timeout=3s CMD curl --fail http://127.0.0.1:8000/ || exit 1
ENV UID=1000 \
GID=1000
RUN apt update && apt install -y curl \
php7.3-cli php7.3-gd php7.3-pgsql php7.3-mysql php7.3-sqlite3 php7.3-zip php7.3-dom php7.3-mbstring \
imagemagick zip unzip && \
rm -rf /var/lib/apt/lists/*
COPY --from=app /app /app
COPY --from=suexec /usr/local/bin/su-exec /usr/local/bin/su-exec
WORKDIR /app
CMD ["/bin/sh", "/app/tests/docker-init.sh"]

View File

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

View File

@ -1,43 +0,0 @@
```
_________.__ .__ .__ ________
/ _____/| |__ |__| _____ _____ |__| ____ \_____ \
\_____ \ | | \ | | / \ / \ | |_/ __ \ / ____/
/ \| Y \| || Y Y \| Y Y \| |\ ___/ / \
/_______ /|___| /|__||__|_| /|__|_| /|__| \___ >\_______ \
\/ \/ \/ \/ \/ \/
```
# Shimmie
[![Test & Publish](https://github.com/shish/shimmie2/workflows/Test%20&%20Publish/badge.svg)](https://github.com/shish/shimmie2/actions)
[![Code Quality](https://scrutinizer-ci.com/g/shish/shimmie2/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/shish/shimmie2/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/shish/shimmie2/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/shish/shimmie2/?branch=master)
# Documentation
* [Install straight on disk](https://github.com/shish/shimmie2/wiki/Install)
* [Install in docker container](https://github.com/shish/shimmie2/wiki/Docker)
* [Upgrade process](https://github.com/shish/shimmie2/wiki/Upgrade)
* [Basic settings](https://github.com/shish/shimmie2/wiki/Settings)
* [Advanced config](https://github.com/shish/shimmie2/wiki/Advanced-Config)
* [Developer notes](https://github.com/shish/shimmie2/wiki/Development-Info)
* [High-performance notes](https://github.com/shish/shimmie2/wiki/Performance)
# Contact
Email: webmaster at shishnet.org
Issue/Bug tracker: https://github.com/shish/shimmie2/issues
# Licence
All code is released under the [GNU GPL Version 2](https://www.gnu.org/licenses/gpl-2.0.html) unless mentioned otherwise.
If you give shimmie to someone else, you have to give them the source (which
should be easy, as PHP is an interpreted language...). If you want to add
customisations to your own site, then those customisations belong to you,
and you can do what you want with them.

119
README.txt Normal file
View File

@ -0,0 +1,119 @@
_________.__ .__ .__ ________
/ _____/| |__ |__| _____ _____ |__| ____ \_____ \
\_____ \ | | \| |/ \ / \| |/ __ \ / ____/
/ \| Y \ | Y Y \ Y Y \ \ ___// \
/_______ /|___| /__|__|_| /__|_| /__|\___ >_______ \
\/ \/ \/ \/ \/ \/
Shimmie 2.3
~~~~~~~~~~~
1000 days since the first import was converted to git \o/
And over a year since the last non-beta release x_x
New since 2.2
~~~~~~~~~~~~~
For admins:
o) Simplified installer, only one question
o) Improved mass tag editor, allows searching and replacing multiple tags
o) Automated installation
o) Automatic detection of niceurl support
o) Support for caching data, to lower database load
o) Improved extension management, built-in documentation function
o) Built-in documentation for extensions
o) Better spam filtering
o) CAPTCHA tests for signup and anonymous comments
o) Detailed event logging
o) Delete by query
For users:
o) New default theme
o) Better BBCode support
o) Image ratings, with eg. hiding of explicit images to anonymous users
o) Theme improvements, new themes
o) Better searching
o) Cooliris support
o) Search for images you've voted for (simple "favourites" system)
o) Gravatar support
o) Nicer date formatting
o) Random image extension
o) The current featured image is linkable
o) Private message system
o) Only the most recent posts for each image shown on the comment list
o) Image pools extension
o) Tagger cloud
For developers:
o) More sensible APIs
o) Automated testing
o) Preliminary SQLite & Postgres support, for people with very small and
very large sites (Not all extensions are compatible yet though)
And much more that I can't remember \o/
Requirements
~~~~~~~~~~~~
MySQL 4.1+
PHP 5.0+
GD or ImageMagick
There is no PHP4 support, because it lacks many useful features which make
shimmie development easier, faster, and less bug prone. PHP4 is officially
dead, if your host hasn't upgraded to at least 5, I would suggest switching
hosts. I'll even host galleries for free if you can't get hosting elsewhere
for whatever reason~
Installation
~~~~~~~~~~~~
1) Create a blank database
2) Unzip shimmie into a folder on the web host
3) Visit the folder with a web browser
4) Enter the location of the database
5) Click "install". Hopefully you'll end up at the welcome screen; if
not, you should be given instructions on how to fix any errors~
Upgrade from 2.2.X
~~~~~~~~~~~~~~~~~~
Should be automatic, just unzip into a clean folder and copy across
config.php, images and thumbs folders from the old version. This
includes automatically messing with the database -- back it up first!
Upgrade from earlier versions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I very much recommend going via each major release in turn (eg, 2.0.6
-> 2.1.3 -> 2.2.4 -> 2.3.0 rather than 2.0.6 -> 2.3.0). While the basic
database and file formats haven't changed *completely*, it's different
enough to be a pain.
Development Info
~~~~~~~~~~~~~~~~
http://shimmie.shishnet.org/doc/
Please tell me if those docs are lacking in any way, so that they can be
improved for the next person who uses them
Contact
~~~~~~~
#shimmie on Freenode -- IRC
webmaster at shishnet.org -- email
https://github.com/shish/shimmie2/issues -- bug tracker
Licence
~~~~~~~
All code is GPLv2 unless mentioned otherwise; ie, if you give shimmie to
someone else, you have to give them the source (which should be easy, as PHP
is an interpreted language...). If you want to add customisations to your own
site, then those customisations belong to you, and you can do what you want
with them.

View File

@ -1,67 +0,0 @@
{
"name": "shish/shimmie2",
"description": "A tag-based image gallery",
"type" : "project",
"license" : "GPL-2.0-or-later",
"minimum-stability" : "dev",
"repositories" : [
{
"type" : "package",
"package" : {
"name" : "ifixit/php-akismet",
"version" : "1.1",
"source" : {
"url" : "https://github.com/iFixit/php-akismet.git",
"type" : "git",
"reference" : "fd4ff50eb577457c1b7b887401663e91e77625ae"
}
}
}
],
"require" : {
"php" : "^7.3",
"ext-pdo": "*",
"ext-json": "*",
"ext-fileinfo": "*",
"flexihash/flexihash" : "^2.0.0",
"ifixit/php-akismet" : "1.*",
"google/recaptcha" : "~1.1",
"dapphp/securimage" : "3.6.*",
"shish/eventtracer-php" : "^2.0.0",
"shish/ffsphp" : "^1.0.0",
"shish/microcrud" : "^2.0.0",
"shish/microhtml" : "^2.0.0",
"enshrined/svg-sanitize" : "0.13.*",
"bower-asset/jquery" : "1.12.*",
"bower-asset/jquery-timeago" : "1.5.*",
"bower-asset/mediaelement" : "2.21.*",
"bower-asset/js-cookie" : "2.1.*"
},
"require-dev" : {
},
"suggest": {
"ext-memcache": "memcache caching",
"ext-memcached": "memcached caching",
"ext-apc": "apc caching",
"ext-redis": "redis caching",
"ext-dom": "some extensions",
"ext-curl": "some extensions",
"ext-ctype": "some extensions",
"ext-json": "some extensions",
"ext-zip": "self-updater extension, bulk import/export",
"ext-zlib": "anti-spam",
"ext-xml": "some extensions",
"ext-gd": "GD-based thumbnailing"
},"replace": {
"bower-asset/jquery": ">=1.11.0",
"bower-asset/inputmask": ">=3.2.0",
"bower-asset/punycode": ">=1.3.0",
"bower-asset/yii2-pjax": ">=2.0.0"
}
}

461
composer.lock generated
View File

@ -1,461 +0,0 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ca096500833679c5fa819eb5cfbd1d80",
"packages": [
{
"name": "bower-asset/jquery-timeago",
"version": "v1.5.4",
"source": {
"type": "git",
"url": "git@github.com:rmm5t/jquery-timeago.git",
"reference": "180864a9c544a49e43719b457250af216d5e4c3a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rmm5t/jquery-timeago/zipball/180864a9c544a49e43719b457250af216d5e4c3a",
"reference": "180864a9c544a49e43719b457250af216d5e4c3a"
},
"require": {
"bower-asset/jquery": ">=1.4"
},
"type": "bower-asset",
"license": [
"MIT"
]
},
{
"name": "bower-asset/js-cookie",
"version": "v2.1.4",
"source": {
"type": "git",
"url": "git@github.com:js-cookie/js-cookie.git",
"reference": "8b70250875f7e07445b6a457f9c2474ead4cba44"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/js-cookie/js-cookie/zipball/8b70250875f7e07445b6a457f9c2474ead4cba44",
"reference": "8b70250875f7e07445b6a457f9c2474ead4cba44"
},
"type": "bower-asset",
"license": [
"MIT"
]
},
{
"name": "bower-asset/mediaelement",
"version": "2.21.2",
"source": {
"type": "git",
"url": "git@github.com:johndyer/mediaelement.git",
"reference": "394db3b4a2e3f5f7988cacdefe62ed973bf4a3ce"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/johndyer/mediaelement/zipball/394db3b4a2e3f5f7988cacdefe62ed973bf4a3ce",
"reference": "394db3b4a2e3f5f7988cacdefe62ed973bf4a3ce"
},
"type": "bower-asset",
"license": [
"MIT"
]
},
{
"name": "dapphp/securimage",
"version": "3.6.8",
"source": {
"type": "git",
"url": "https://github.com/dapphp/securimage.git",
"reference": "5fc5953c4ffba1eb214cc83100672f238c184ca4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dapphp/securimage/zipball/5fc5953c4ffba1eb214cc83100672f238c184ca4",
"reference": "5fc5953c4ffba1eb214cc83100672f238c184ca4",
"shasum": ""
},
"require": {
"ext-gd": "*",
"php": ">=5.4"
},
"suggest": {
"ext-pdo": "For database storage support",
"ext-pdo_mysql": "For MySQL database support",
"ext-pdo_sqlite": "For SQLite3 database support"
},
"type": "library",
"autoload": {
"classmap": [
"securimage.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Drew Phillips",
"email": "drew@drew-phillips.com"
}
],
"description": "PHP CAPTCHA Library",
"homepage": "https://www.phpcaptcha.org",
"keywords": [
"Forms",
"anti-spam",
"captcha",
"security"
],
"time": "2020-05-30T09:43:22+00:00"
},
{
"name": "enshrined/svg-sanitize",
"version": "0.13.3",
"source": {
"type": "git",
"url": "https://github.com/darylldoyle/svg-sanitizer.git",
"reference": "bc66593f255b7d2613d8f22041180036979b6403"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/bc66593f255b7d2613d8f22041180036979b6403",
"reference": "bc66593f255b7d2613d8f22041180036979b6403",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*"
},
"require-dev": {
"codeclimate/php-test-reporter": "^0.1.2",
"phpunit/phpunit": "^6"
},
"type": "library",
"autoload": {
"psr-4": {
"enshrined\\svgSanitize\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0-or-later"
],
"authors": [
{
"name": "Daryll Doyle",
"email": "daryll@enshrined.co.uk"
}
],
"description": "An SVG sanitizer for PHP",
"time": "2020-01-20T01:34:17+00:00"
},
{
"name": "flexihash/flexihash",
"version": "v2.0.2",
"source": {
"type": "git",
"url": "https://github.com/pda/flexihash.git",
"reference": "497ba5782606d998f8ab0b4e5942e3a799bec018"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pda/flexihash/zipball/497ba5782606d998f8ab0b4e5942e3a799bec018",
"reference": "497ba5782606d998f8ab0b4e5942e3a799bec018",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8",
"satooshi/php-coveralls": "~1.0",
"squizlabs/php_codesniffer": "^2.3",
"symfony/config": "^2.0.0",
"symfony/console": "^2.0.0",
"symfony/filesystem": "^2.0.0",
"symfony/stopwatch": "^2.0.0",
"symfony/yaml": "^2.0.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Flexihash\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paul Annesley",
"email": "paul@annesley.cc",
"homepage": "http://paul.annesley.cc"
},
{
"name": "Dom Morgan",
"email": "dom@d3r.com",
"homepage": "https://d3r.com"
}
],
"description": "Flexihash is a small PHP library which implements consistent hashing",
"homepage": "https://github.com/pda/flexihash",
"time": "2016-04-22T21:03:23+00:00"
},
{
"name": "google/recaptcha",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/google/recaptcha.git",
"reference": "79ccf652575e138d51742d04e78a5adbb9892f9d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/google/recaptcha/zipball/79ccf652575e138d51742d04e78a5adbb9892f9d",
"reference": "79ccf652575e138d51742d04e78a5adbb9892f9d",
"shasum": ""
},
"require": {
"php": ">=5.5"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.2.20|^2.15",
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^4.8.36|^5.7.27|^6.59|^7.5.11"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"psr-4": {
"ReCaptcha\\": "src/ReCaptcha"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "Client library for reCAPTCHA, a free service that protects websites from spam and abuse.",
"homepage": "https://www.google.com/recaptcha/",
"keywords": [
"Abuse",
"captcha",
"recaptcha",
"spam"
],
"time": "2020-05-21T08:56:25+00:00"
},
{
"name": "ifixit/php-akismet",
"version": "1.1",
"source": {
"type": "git",
"url": "https://github.com/iFixit/php-akismet.git",
"reference": "fd4ff50eb577457c1b7b887401663e91e77625ae"
},
"type": "library"
},
{
"name": "shish/eventtracer-php",
"version": "v2.0.0",
"source": {
"type": "git",
"url": "https://github.com/shish/eventtracer-php.git",
"reference": "0328454c58d240667f004f3efd863b8ef521ba2a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/shish/eventtracer-php/zipball/0328454c58d240667f004f3efd863b8ef521ba2a",
"reference": "0328454c58d240667f004f3efd863b8ef521ba2a",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Shish",
"email": "webmaster@shishnet.org",
"homepage": "http://shishnet.org",
"role": "Developer"
}
],
"description": "An API to write JSON traces as used by the Chrome Trace Viewer",
"homepage": "https://github.com/shish/eventtracer-php",
"time": "2020-09-20T11:46:44+00:00"
},
{
"name": "shish/ffsphp",
"version": "v1.0.2",
"source": {
"type": "git",
"url": "https://github.com/shish/ffsphp.git",
"reference": "82d45b2691da11c82a28f85b07752334a06b8a8e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/shish/ffsphp/zipball/82d45b2691da11c82a28f85b07752334a06b8a8e",
"reference": "82d45b2691da11c82a28f85b07752334a06b8a8e",
"shasum": ""
},
"require": {
"php": "^7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
},
"type": "library",
"autoload": {
"psr-4": {
"FFSPHP\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Shish",
"email": "webmaster@shishnet.org",
"homepage": "http://shishnet.org",
"role": "Developer"
}
],
"description": "A collection of workarounds for stupid PHP things",
"homepage": "https://github.com/shish/ffsphp",
"time": "2020-09-20T13:15:53+00:00"
},
{
"name": "shish/microcrud",
"version": "v2.0.0",
"source": {
"type": "git",
"url": "https://github.com/shish/microcrud.git",
"reference": "1d55c02c405fc75ad6a0e952e9333552bef492f0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/shish/microcrud/zipball/1d55c02c405fc75ad6a0e952e9333552bef492f0",
"reference": "1d55c02c405fc75ad6a0e952e9333552bef492f0",
"shasum": ""
},
"require": {
"ext-pdo": "*",
"php": "^7.3",
"shish/ffsphp": "^1.0",
"shish/microhtml": "^2.0.2"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
},
"type": "library",
"autoload": {
"psr-4": {
"MicroCRUD\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Shish",
"email": "webmaster@shishnet.org",
"homepage": "http://shishnet.org",
"role": "Developer"
}
],
"description": "A minimal CRUD generating library",
"homepage": "https://github.com/shish/microcrud",
"keywords": [
"crud",
"generator"
],
"time": "2020-09-20T13:10:42+00:00"
},
{
"name": "shish/microhtml",
"version": "v2.0.2",
"source": {
"type": "git",
"url": "https://github.com/shish/microhtml.git",
"reference": "d5c393d5a47bb3b3febdfde9ebf6e91cb9b41947"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/shish/microhtml/zipball/d5c393d5a47bb3b3febdfde9ebf6e91cb9b41947",
"reference": "d5c393d5a47bb3b3febdfde9ebf6e91cb9b41947",
"shasum": ""
},
"require": {
"php": "^7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
},
"type": "library",
"autoload": {
"files": [
"src/microhtml.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Shish",
"email": "webmaster@shishnet.org",
"homepage": "http://shishnet.org",
"role": "Developer"
}
],
"description": "A minimal HTML generating library",
"homepage": "https://github.com/shish/microhtml",
"keywords": [
"generator",
"html"
],
"time": "2020-09-20T13:05:01+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "^7.3",
"ext-pdo": "*",
"ext-json": "*",
"ext-fileinfo": "*"
},
"platform-dev": [],
"plugin-api-version": "1.1.0"
}

181
contrib/admin/main.php Normal file
View File

@ -0,0 +1,181 @@
<?php
/**
* Name: Admin Controls
* Author: Shish <webmaster@shishnet.org>
* Link: http://code.shishnet.org/shimmie2/
* License: GPLv2
* Description: Various things to make admins' lives easier
* Documentation:
* <p>Lowercase all tags:
* <br>Set all tags to lowercase for consistency
* <p>Recount tag use:
* <br>If the counts of images per tag get messed up somehow, this will reset them
* <p>Purge unused tags:
* <br>Get rid of all the tags that don't have any images associated with
* them (normally they were created as typos or spam); this is mostly for
* neatness, the performance gain is tiny...
* <p>Convert to InnoDB:
* <br>Convert your database tables to InnoDB, thus allowing shimmie to
* take advantage of useful InnoDB-only features (this should be done
* automatically, this button only exists as a backup). This only applies
* to MySQL -- all other databases come with useful features enabled
* as standard.
* <p>Database dump:
* <br>Download the contents of the database in plain text format, useful
* for backups.
*/
/**
* Sent when the admin page is ready to be added to
*/
class AdminBuildingEvent extends Event {
var $page;
public function AdminBuildingEvent($page) {
$this->page = $page;
}
}
class AdminPage implements Extension {
var $theme;
public function receive_event(Event $event) {
global $config, $database, $page, $user;
if(is_null($this->theme)) $this->theme = get_theme_object($this);
if(($event instanceof PageRequestEvent) && $event->page_matches("admin")) {
if(!$user->is_admin()) {
$this->theme->display_permission_denied($page);
}
else {
send_event(new AdminBuildingEvent($page));
}
}
if(($event instanceof PageRequestEvent) && $event->page_matches("admin_utils")) {
if($user->is_admin() && $user->check_auth_token()) {
log_info("admin", "Util: {$_POST['action']}");
set_time_limit(0);
$redirect = false;
switch($_POST['action']) {
case 'delete by query':
$this->delete_by_query($_POST['query']);
$redirect = true;
break;
case 'lowercase all tags':
$this->lowercase_all_tags();
$redirect = true;
break;
case 'recount tag use':
$this->recount_tag_use();
$redirect = true;
break;
case 'purge unused tags':
$this->purge_unused_tags();
$redirect = true;
break;
case 'convert to innodb':
$this->convert_to_innodb();
$redirect = true;
break;
case 'database dump':
$this->dbdump($page);
break;
}
if($redirect) {
$page->set_mode("redirect");
$page->set_redirect(make_link("admin"));
}
}
}
if($event instanceof AdminBuildingEvent) {
$this->theme->display_page($page);
$this->theme->display_form($page);
}
if($event instanceof UserBlockBuildingEvent) {
if($user->is_admin()) {
$event->add_link("Board Admin", make_link("admin"));
}
}
}
private function delete_by_query($query) {
global $page, $user;
assert(strlen($query) > 1);
foreach(Image::find_images(0, 1000000, Tag::explode($query)) as $image) {
send_event(new ImageDeletionEvent($image));
}
}
private function lowercase_all_tags() {
global $database;
$database->execute("UPDATE tags SET tag=lower(tag)");
}
private function recount_tag_use() {
global $database;
$database->Execute("
UPDATE tags
SET count = COALESCE(
(SELECT COUNT(image_id) FROM image_tags WHERE tag_id=tags.id GROUP BY tag_id),
0
)");
}
private function purge_unused_tags() {
global $database;
$this->recount_tag_use();
$database->Execute("DELETE FROM tags WHERE count=0");
}
private function dbdump($page) {
include "config.php";
$matches = array();
preg_match("#(\w+)://(\w+):(\w+)@([\w\.\-]+)/([\w_]+)(\?.*)?#", $database_dsn, $matches);
$software = $matches[1];
$username = $matches[2];
$password = $matches[3];
$hostname = $matches[4];
$database = $matches[5];
switch($software) {
case 'mysql':
$cmd = "mysqldump -h$hostname -u$username -p$password $database";
break;
}
$page->set_mode("data");
$page->set_type("application/x-unknown");
$page->set_filename('shimmie-'.date('Ymd').'.sql');
$page->set_data(shell_exec($cmd));
}
private function check_for_orphanned_images() {
$orphans = array();
foreach(glob("images/*") as $dir) {
foreach(glob("$dir/*") as $file) {
$hash = str_replace("$dir/", "", $file);
if(!$this->db_has_hash($hash)) {
$orphans[] = $hash;
}
}
}
}
private function convert_to_innodb() {
global $database;
if($database->engine->name == "mysql") {
$tables = $database->db->MetaTables();
foreach($tables as $table) {
log_info("upgrade", "converting $table to innodb");
$database->execute("ALTER TABLE $table TYPE=INNODB");
}
}
}
}
add_event_listener(new AdminPage());
?>

75
contrib/admin/test.php Normal file
View File

@ -0,0 +1,75 @@
<?php
class AdminPageTest extends ShimmieWebTestCase {
function testAuth() {
$this->get_page('admin');
$this->assert_response(403);
$this->assert_title("Permission Denied");
$this->log_in_as_user();
$this->get_page('admin');
$this->assert_response(403);
$this->assert_title("Permission Denied");
$this->log_out();
}
function testLowercase() {
$ts = time(); // we need a tag that hasn't been used before
$this->log_in_as_admin();
$image_id_1 = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "TeStCase$ts");
$this->get_page("post/view/$image_id_1");
$this->assert_title("Image $image_id_1: TeStCase$ts");
$this->get_page('admin');
$this->assert_title("Admin Tools");
$this->set_field("action", "lowercase all tags");
$this->click("Go");
$this->log_out();
$this->get_page("post/view/$image_id_1");
$this->assert_title("Image $image_id_1: testcase$ts");
$this->delete_image($image_id_1);
$this->log_out();
}
# FIXME: make sure the admin tools actually work
function testRecount() {
$this->log_in_as_admin();
$this->get_page('admin');
$this->assert_title("Admin Tools");
$this->set_field("action", "recount tag use");
$this->click("Go");
$this->log_out();
}
function testPurge() {
$this->log_in_as_admin();
$this->get_page('admin');
$this->assert_title("Admin Tools");
$this->set_field("action", "purge unused tags");
$this->click("Go");
$this->log_out();
}
function testConvert() {
$this->log_in_as_admin();
$this->get_page('admin');
$this->assert_title("Admin Tools");
$this->set_field("action", "convert to inodb");
$this->click("Go");
$this->log_out();
}
function testDump() {
$this->log_in_as_admin();
$this->get_page('admin');
$this->assert_title("Admin Tools");
$this->set_field("action", "database dump");
$this->click("Go");
$this->log_out();
}
}
?>

46
contrib/admin/theme.php Normal file
View File

@ -0,0 +1,46 @@
<?php
class AdminPageTheme extends Themelet {
/*
* Show the basics of a page, for other extensions to add to
*/
public function display_page(Page $page) {
$page->set_title("Admin Tools");
$page->set_heading("Admin Tools");
$page->add_block(new NavBlock());
}
/*
* Show a form which links to admin_utils with POST[action] set to one of:
* 'lowercase all tags'
* 'recount tag use'
* 'purge unused tags'
*/
public function display_form(Page $page) {
global $user;
$html = "
".make_form(make_link("admin_utils"))."
<select name='action'>
<option value='lowercase all tags'>All tags to lowercase</option>
<option value='recount tag use'>Recount tag use</option>
<option value='purge unused tags'>Purge unused tags</option>
<option value='database dump'>Download database contents</option>
<option value='convert to innodb'>Convert database to InnoDB (MySQL only)</option>
</select>
<input type='submit' value='Go'>
</form>
";
$page->add_block(new Block("Misc Admin Tools", $html));
$html = "
".make_form(make_link("admin_utils"))."
<input type='hidden' name='action' value='delete by query'>
<input type='text' name='query'>
<input type='submit' value='Go'>
</form>
";
$page->add_block(new Block("Delete by Query", $html));
}
}
?>

View File

@ -0,0 +1,75 @@
<?php
/*
* Name: Amazon S3 Mirror
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Copy uploaded files to S3
* Documentation:
*/
require_once "lib/S3.php";
class UploadS3 extends SimpleExtension {
public function onInitExt(InitExtEvent $event) {
global $config;
$config->set_default_string("amazon_s3_access", "");
$config->set_default_string("amazon_s3_secret", "");
$config->set_default_string("amazon_s3_bucket", "");
}
public function onSetupBuilding(SetupBuildingEvent $event) {
$sb = new SetupBlock("Amazon S3");
$sb->add_text_option("amazon_s3_access", "Access key: ");
$sb->add_text_option("amazon_s3_secret", "<br>Secret key: ");
$sb->add_text_option("amazon_s3_bucket", "<br>Bucket: ");
$event->panel->add_block($sb);
}
public function onImageAddition(ImageAdditionEvent $event) {
global $config;
$access = $config->get_string("amazon_s3_access");
$secret = $config->get_string("amazon_s3_secret");
$bucket = $config->get_string("amazon_s3_bucket");
if(!empty($bucket)) {
log_debug("amazon_s3", "Mirroring Image #".$event->image->id." to S3 #$bucket");
$s3 = new S3($access, $secret);
$s3->putBucket($bucket, S3::ACL_PUBLIC_READ);
$s3->putObjectFile(
warehouse_path("thumbs", $event->image->hash),
$bucket,
'thumbs/'.$event->image->hash,
S3::ACL_PUBLIC_READ,
array(),
array(
"Content-Type" => "image/jpeg",
"Content-Disposition" => "inline; filename=image-" . $event->image->id . ".jpg",
)
);
$s3->putObjectFile(
warehouse_path("images", $event->image->hash),
$bucket,
'images/'.$event->image->hash,
S3::ACL_PUBLIC_READ,
array(),
array(
"Content-Type" => $event->image->get_mime_type(),
"Content-Disposition" => "inline; filename=image-" . $event->image->id . "." . $event->image->ext,
)
);
}
}
public function onImageDeletion(ImageDeletionEvent $event) {
global $config;
$access = $config->get_string("amazon_s3_access");
$secret = $config->get_string("amazon_s3_secret");
$bucket = $config->get_string("amazon_s3_bucket");
if(!empty($bucket)) {
log_debug("amazon_s3", "Deleting Image #".$event->image->id." from S3");
$s3 = new S3($access, $secret);
$s3->deleteObject($bucket, "images/"+$event->image->hash);
$s3->deleteObject($bucket, "thumbs/"+$event->image->hash);
}
}
}
?>

View File

@ -0,0 +1,90 @@
<?php
/*
* Name: Comment Word Ban
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: For stopping spam and other comment abuse
* Documentation:
* Allows an administrator to ban certain words
* from comments. This can be a very simple but effective way
* of stopping spam; just add "viagra", "porn", etc to the
* banned words list.
* <p>Regex bans are also supported, allowing more complicated
* bans like <code>/http:.*\.cn\//</code> to block links to
* chinese websites, or <code>/.*?http.*?http.*?http.*?http.*?/</code>
* to block comments with four (or more) links in.
* <p>Note that for non-regex matches, only whole words are
* matched, eg banning "sex" would block the comment "get free
* sex call this number", but allow "This is a photo of Bob
* from Essex"
*/
class BanWords extends SimpleExtension {
public function onInitExt(InitExtEvent $event) {
global $config;
$config->set_default_string('banned_words', "
a href=
anal
blowjob
/buy-.*-online/
casino
cialis
doors.txt
fuck
hot video
kaboodle.com
lesbian
nexium
penis
/pokerst.*/
pornhub
porno
purchase
sex
sex tape
spinnenwerk.de
thx for all
TRAMADOL
ultram
very nice site
viagra
xanax
");
}
public function onCommentPosting(CommentPostingEvent $event) {
global $config;
$banned = $config->get_string("banned_words");
$comment = strtolower($event->comment);
foreach(explode("\n", $banned) as $word) {
$word = trim(strtolower($word));
if(strlen($word) == 0) {
// line is blank
continue;
}
else if($word[0] == '/') {
// lines that start with slash are regex
if(preg_match($word, $comment)) {
throw new CommentPostingException("Comment contains banned terms");
}
}
else {
// other words are literal
if(strpos($comment, $word) !== false) {
throw new CommentPostingException("Comment contains banned terms");
}
}
}
}
public function onSetupBuilding(SetupBuildingEvent $event) {
$sb = new SetupBlock("Banned Phrases");
$sb->add_label("One per line, lines that start with slashes are treated as regex<br/>");
$sb->add_longtext_option("banned_words");
$event->panel->add_block($sb);
}
public function get_priority() {return 30;}
}
?>

View File

@ -0,0 +1,45 @@
<?php
class BanWordsTest extends ShimmieWebTestCase {
function testWordBan() {
$this->log_in_as_admin();
$this->get_page("setup");
$this->set_field("_config_banned_words", "viagra\nporn\n\n/http:.*\.cn\//");
$this->click("Save Settings");
$this->log_out();
$this->log_in_as_user();
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pbx computer screenshot");
$this->get_page("post/view/$image_id");
$this->set_field('comment', "kittens and viagra");
$this->click("Post Comment");
$this->assert_title("Comment Blocked");
$this->get_page("post/view/$image_id");
$this->set_field('comment', "kittens and ViagrA");
$this->click("Post Comment");
$this->assert_title("Comment Blocked");
$this->get_page("post/view/$image_id");
$this->set_field('comment', "kittens and viagra!");
$this->click("Post Comment");
$this->assert_title("Comment Blocked");
$this->get_page("post/view/$image_id");
$this->set_field('comment', "some link to http://something.cn/");
$this->click("Post Comment");
$this->assert_title("Comment Blocked");
$this->get_page('comment/list');
$this->assert_title('Comments');
$this->assert_no_text('viagra');
$this->assert_no_text('ViagrA');
$this->assert_no_text('http://something.cn/');
$this->log_out();
$this->log_in_as_admin();
$this->delete_image($image_id);
$this->log_out();
}
}
?>

130
contrib/blotter/main.php Normal file
View File

@ -0,0 +1,130 @@
<?php
/*
* Name: Blotter
* Author: Zach Hall <zach@sosguy.net> [http://seemslegit.com/]
* License: GPLv2
* Description: Displays brief updates about whatever you want on every page.
* Colors and positioning can be configured to match your site's design.
*
* Development TODO at http://github.com/zshall/shimmie2/issues
*/
class Blotter extends SimpleExtension {
public function onInitExt(Event $event) {
/**
* I love re-using this installer don't I...
*/
global $config;
$version = $config->get_int("blotter_version", 0);
/**
* If this version is less than "1", it's time to install.
*
* REMINDER: If I change the database tables, I must change up version by 1.
*/
if($version < 1) {
/**
* Installer
*/
global $database, $config;
$database->create_table("blotter", "
id SCORE_AIPK,
entry_date SCORE_DATETIME DEFAULT SCORE_NOW,
entry_text TEXT NOT NULL,
important SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N
");
// Insert sample data:
$database->execute("INSERT INTO blotter (id, entry_date, entry_text, important) VALUES (?, now(), ?, ?)",
array(NULL, "Installed the blotter extension!", "Y"));
log_info("blotter", "Installed tables for blotter extension.");
$config->set_int("blotter_version", 1);
}
// Set default config:
$config->set_default_int("blotter_recent", 5);
$config->set_default_string("blotter_color", "FF0000");
$config->set_default_string("blotter_position", "subheading");
}
public function onSetupBuilding(Event $event) {
global $config;
$sb = new SetupBlock("Blotter");
$sb->add_int_option("blotter_recent", "<br />Number of recent entries to display: ");
$sb->add_text_option("blotter_color", "<br />Color of important updates: (ABCDEF format) ");
$sb->add_choice_option("blotter_position", array("Top of page" => "subheading", "In navigation bar" => "left"), "<br>Position: ");
$event->panel->add_block($sb);
}
public function onUserBlockBuilding(Event $event) {
global $user;
if($user->is_admin()) {
$event->add_link("Blotter Editor", make_link("blotter/editor"));
}
}
public function onPageRequest(Event $event) {
global $page, $database, $user;
if($event->page_matches("blotter")) {
switch($event->get_arg(0)) {
case "editor":
/**
* Displays the blotter editor.
*/
if(!$user->is_admin()) {
$this->theme->display_permission_denied($page);
} else {
$entries = $database->get_all("SELECT * FROM blotter ORDER BY id DESC");
$this->theme->display_editor($entries);
}
break;
case "add":
/**
* Adds an entry
*/
if(!$user->is_admin() || !$user->check_auth_token()) {
$this->theme->display_permission_denied($page);
} else {
$entry_text = $_POST['entry_text'];
if($entry_text == "") { die("No entry message!"); }
if(isset($_POST['important'])) { $important = 'Y'; } else { $important = 'N'; }
// Now insert into db:
$database->execute("INSERT INTO blotter (entry_date, entry_text, important) VALUES (now(), ?, ?)",
array($entry_text, $important));
log_info("blotter", "Added Message: $entry_text");
$page->set_mode("redirect");
$page->set_redirect(make_link("blotter/editor"));
}
break;
case "remove":
/**
* Removes an entry
*/
if(!$user->is_admin() || !$user->check_auth_token()) {
$this->theme->display_permission_denied($page);
} else {
$id = int_escape($_POST['id']);
if(!isset($id)) { die("No ID!"); }
$database->Execute("DELETE FROM blotter WHERE id=:id", array("id"=>$id));
log_info("blotter", "Removed Entry #$id");
$page->set_mode("redirect");
$page->set_redirect(make_link("blotter/editor"));
}
break;
case "":
/**
* Displays all blotter entries
*/
$entries = $database->get_all("SELECT * FROM blotter ORDER BY id DESC");
$this->theme->display_blotter_page($entries);
break;
}
}
/**
* Finally, display the blotter on whatever page we're viewing.
*/
$this->display_blotter();
}
private function display_blotter() {
global $database, $config;
$limit = $config->get_int("blotter_recent", 5);
$entries = $database->get_all("SELECT * FROM blotter ORDER BY id DESC LIMIT :lim", array("lim"=>$limit));
$this->theme->display_blotter($entries);
}
}
?>

37
contrib/blotter/test.php Normal file
View File

@ -0,0 +1,37 @@
<?php
class BlotterTest extends SCoreWebTestCase {
function testLogin() {
$this->log_in_as_admin();
$this->assert_text("Blotter Editor");
$this->click("Blotter Editor");
$this->log_out();
}
function testDenial() {
$this->get_page("blotter/editor");
$this->assert_response(403);
$this->get_page("blotter/add");
$this->assert_response(403);
$this->get_page("blotter/remove");
$this->assert_response(403);
}
function testAddViewRemove() {
$this->log_in_as_admin();
$this->get_page("blotter/editor");
$this->set_field("entry_text", "blotter testing");
$this->click("Add");
$this->assert_text("blotter testing");
$this->get_page("blotter");
$this->assert_text("blotter testing");
$this->get_page("blotter/editor");
$this->click("Remove");
$this->assert_no_text("blotter testing");
$this->log_out();
}
}
?>

189
contrib/blotter/theme.php Normal file
View File

@ -0,0 +1,189 @@
<?php
class BlotterTheme extends Themelet {
public function display_editor($entries) {
global $page;
$html = $this->get_html_for_blotter_editor($entries);
$page->set_title("Blotter Editor");
$page->set_heading("Blotter Editor");
$page->add_block(new Block("Welcome to the Blotter Editor!", $html, "main", 10));
$page->add_block(new Block("Navigation", "<a href='".make_link()."'>Index</a>", "left", 0));
}
public function display_blotter_page($entries) {
global $page;
$html = $this->get_html_for_blotter_page($entries);
$page->set_mode("data");
$page->set_data($html);
}
public function display_blotter($entries) {
global $page, $config;
$html = $this->get_html_for_blotter($entries);
$position = $config->get_string("blotter_position", "subheading");
$page->add_block(new Block(null, $html, $position, 20));
}
private function is_odd($number) {
return $number & 1; // 0 = even, 1 = odd
}
private function get_html_for_blotter_editor($entries) {
global $user;
/**
* Long function name, but at least I won't confuse it with something else ^_^
*/
$html = "";
// Add_new stuff goes here.
$table_header = "
<tr>
<th>Date</th>
<th>Message</th>
<th>Important?</th>
<th>Action</th>
</tr>";
$add_new = "
<tr class='even'>
".make_form(make_link("blotter/add"))."
<td colspan='2'><textarea style='text-align:left;' name='entry_text' rows='2' /></textarea></td>
<td><input type='checkbox' name='important' /></td>
<td><input type='submit' value='Add'></td>
</form>
</tr>";
// Now, time for entries list.
$table_rows = "";
for ($i = 0 ; $i < count($entries) ; $i++)
{
/**
* Add table rows
*/
$id = $entries[$i]['id'];
$entry_date = $entries[$i]['entry_date'];
$entry_text = $entries[$i]['entry_text'];
if($entries[$i]['important'] == 'Y') { $important = 'Y'; } else { $important = 'N'; }
if(!$this->is_odd($i)) {$tr_class = "odd";}
if($this->is_odd($i)) {$tr_class = "even";}
// Add the new table row(s)
$table_rows .=
"<tr class='{$tr_class}'>
<td>$entry_date</td>
<td>$entry_text</td>
<td>$important</td>
<td><form name='remove$id' method='post' action='".make_link("blotter/remove")."'>
".$user->get_auth_html()."
<input type='hidden' name='id' value='$id' />
<input type='submit' style='width: 100%;' value='Remove' />
</form>
</td>
</tr>";
}
$html = "
<table id='blotter_entries' class='zebra'>
<thead>$table_header</thead>
<tbody>$add_new</tbody>
<tfoot>$table_rows</tfoot>
</table>
<br />
<b>Help:</b><br />
<blockquote>Add entries to the blotter, and they will be displayed.</blockquote>";
return $html;
}
private function get_html_for_blotter_page($entries) {
/**
* This one displays a list of all blotter entries.
*/
global $config;
$i_color = $config->get_string("blotter_color","#FF0000");
$html = "";
$html .= "<html><head><title>Blotter</title></head>
<body><pre>";
for ($i = 0 ; $i < count($entries) ; $i++)
{
/**
* Blotter entries
*/
// Reset variables:
$i_open = "";
$i_close = "";
$id = $entries[$i]['id'];
$messy_date = $entries[$i]['entry_date'];
$clean_date = date("m/d/y",strtotime($messy_date));
$entry_text = $entries[$i]['entry_text'];
if($entries[$i]['important'] == 'Y') { $i_open = "<font color='#{$i_color}'>"; $i_close="</font>"; }
$html .= "{$i_open}{$clean_date} - {$entry_text}{$i_close}<br /><br />";
}
$html .= "</pre></body></html>";
return $html;
}
private function get_html_for_blotter($entries) {
/**
* Show the blotter widget
* Although I am starting to learn PHP, I got no idea how to do javascript... to the tutorials!
*/
global $config;
$i_color = $config->get_string("blotter_color","#FF0000");
$position = $config->get_string("blotter_position", "subheading");
$html = "<style type='text/css'>
#blotter1 {font-size: 80%; position: relative;}
#blotter2 {font-size: 80%;}
</style>";
$html .= "<script><!--
$(document).ready(function() {
$(\"#blotter2-toggle\").click(function() {
$(\"#blotter2\").slideToggle(\"slow\", function() {
if($(\"#blotter2\").is(\":hidden\")) {
$.cookie(\"blotter2-hidden\", 'true', {path: '/'});
}
else {
$.cookie(\"blotter2-hidden\", 'false', {path: '/'});
}
});
});
if($.cookie(\"blotter2-hidden\") == 'true') {
$(\"#blotter2\").hide();
}
});
//--></script>";
$entries_list = "";
for ($i = 0 ; $i < count($entries) ; $i++)
{
/**
* Blotter entries
*/
// Reset variables:
$i_open = "";
$i_close = "";
$id = $entries[$i]['id'];
$messy_date = $entries[$i]['entry_date'];
$clean_date = date("m/d/y",strtotime($messy_date));
$entry_text = $entries[$i]['entry_text'];
if($entries[$i]['important'] == 'Y') { $i_open = "<font color='#{$i_color}'>"; $i_close="</font>"; }
$entries_list .= "<li>{$i_open}{$clean_date} - {$entry_text}{$i_close}</li>";
}
$out_text = "";
$in_text = "";
$pos_break = "";
$pos_align = "text-align: right; position: absolute; right: 0px;";
if($position == "left") { $pos_break = "<br />"; $pos_align = ""; }
if(count($entries) == 0) { $out_text = "No blotter entries yet."; $in_text = "Empty.";}
else { $clean_date = date("m/d/y",strtotime($entries[0]['entry_date']));
$out_text = "Blotter updated: {$clean_date}";
$in_text = "<ul>$entries_list</ul>";
}
$html .= "<div id='blotter1'><span>$out_text</span>{$pos_break}<span style='{$pos_align}'><a href='#' id='blotter2-toggle'>Show/Hide</a> <a href='".make_link("blotter")."'>Show All</a></span></div>";
$html .= "<div id='blotter2'>$in_text</div>";
return $html;
}
}
?>

117
contrib/browser_search/main.php Executable file
View File

@ -0,0 +1,117 @@
<?php
/*
* Name: Browser Search
* Author: ATravelingGeek <atg@atravelinggeek.com>
* Some code (and lots of help) by Artanis (Erik Youngren <artanis.00@gmail.com>) from the 'tagger' extention - Used with permission
* Link: http://atravelinggeek.com/
* License: GPLv2
* Description: Allows the user to add a browser 'plugin' to search the site with real-time suggestions
* Version: 0.1c, October 26, 2007
* Documentation:
* Once installed, users with an opensearch compatible browser should see
* their search box light up with whatever "click here to add a search
* engine" notification they have
*/
class BrowserSearch implements Extension {
public function receive_event(Event $event) {
global $page;
global $config;
if($event instanceof InitExtEvent) {
$config->set_default_string("search_suggestions_results_order", 'a');
}
// Add in header code to let the browser know that the search plugin exists
if($event instanceof PageRequestEvent) {
// We need to build the data for the header
global $config;
$search_title = $config->get_string('title');
$search_file_url = make_link('browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml');
$page->add_header("<link rel='search' type='application/opensearchdescription+xml' title='$search_title' href='$search_file_url'>");
}
// The search.xml file that is generated on the fly
if(($event instanceof PageRequestEvent) && $event->page_matches("browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml")) {
// First, we need to build all the variables we'll need
$search_title = $config->get_string('title');
//$search_form_url = $config->get_string('base_href'); //make_link('post/list');
$search_form_url = make_link('post/list/{searchTerms}');
$suggenton_url = make_link('browser_search/')."{searchTerms}";
$icon_b64 = base64_encode(file_get_contents("favicon.ico"));
// Now for the XML
$xml = "
<SearchPlugin xmlns='http://www.mozilla.org/2006/browser/search/' xmlns:os='http://a9.com/-/spec/opensearch/1.1/'>
<os:ShortName>$search_title</os:ShortName>
<os:InputEncoding>UTF-8</os:InputEncoding>
<os:Image width='16' height='16'>data:image/x-icon;base64,$icon_b64</os:Image>
<SearchForm>$search_form_url</SearchForm>
<os:Url type='text/html' method='GET' template='$search_form_url'>
<os:Param name='search' value='{searchTerms}'/>
</os:Url>
<Url type='application/x-suggestions+json' template='$suggenton_url'/>
</SearchPlugin>
";
// And now to send it to the browser
$page->set_mode("data");
$page->set_type("text/xml");
$page->set_data($xml);
}
else if(($event instanceof PageRequestEvent) && (
$event->page_matches("browser_search") &&
!$config->get_bool("disable_search_suggestions")
)) {
global $database;
// We have to build some json stuff
$tag_search = $event->get_arg(0);
// Now to get DB results
if($config->get_string("search_suggestions_results_order") == "a") {
$tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY tag ASC LIMIT 30",array($tag_search."%"));
} else {
$tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY count DESC LIMIT 30",array($tag_search."%"));
}
// And to do stuff with it. We want our output to look like:
// ["shimmie",["shimmies","shimmy","shimmie","21 shimmies","hip shimmies","skea shimmies"],[],[]]
$json_tag_list = "";
$tags_array = array();
foreach($tags as $tag) {
array_push($tags_array,$tag['tag']);
}
$json_tag_list .= implode("\",\"", $tags_array);
// $json_tag_list = implode($tags_array,", ");
// $json_tag_list = "\"".implode($tags_array,"\", \"")."\"";
// And now for the final output
$json_string = "[\"$tag_search\",[\"$json_tag_list\"],[],[]]";
$page->set_mode("data");
$page->set_data($json_string);
}
if($event instanceof SetupBuildingEvent) {
$sort_by = array();
$sort_by['Alphabetical'] = 'a';
$sort_by['Tag Count'] = 't';
$sb = new SetupBlock("Browser Search");
$sb->add_bool_option("disable_search_suggestions", "Disable search suggestions: ");
$sb->add_label("<br>");
$sb->add_choice_option("search_suggestions_results_order", $sort_by, "Sort the suggestions by:");
$event->panel->add_block($sb);
}
}
}
add_event_listener(new BrowserSearch());
?>

View File

@ -0,0 +1,8 @@
<?php
class BrowserSearchTest extends SCoreWebTestCase {
function testBasic() {
$this->get_page("browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml");
$this->get_page("browser_search/test");
}
}
?>

95
contrib/bulk_add/main.php Normal file
View File

@ -0,0 +1,95 @@
<?php
/*
* Name: Bulk Add
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Bulk add server-side images
* Documentation:
* Upload the images into a new directory via ftp or similar, go to
* shimmie's admin page and put that directory in the bulk add box.
* If there are subdirectories, they get used as tags (eg if you
* upload into <code>/home/bob/uploads/holiday/2008/</code> and point
* shimmie at <code>/home/bob/uploads</code>, then images will be
* tagged "holiday 2008")
* <p><b>Note:</b> requires the "admin" extension to be enabled
*/
class BulkAdd extends SimpleExtension {
public function onPageRequest($event) {
global $page, $user;
if($event->page_matches("bulk_add")) {
if($user->is_admin() && $user->check_auth_token() && isset($_POST['dir'])) {
set_time_limit(0);
$this->add_dir($_POST['dir']);
$this->theme->display_upload_results($page);
}
}
}
public function onAdminBuilding($event) {
$this->theme->display_admin_block();
}
private function add_image($tmpname, $filename, $tags) {
assert(file_exists($tmpname));
global $user;
$pathinfo = pathinfo($filename);
if(!array_key_exists('extension', $pathinfo)) {
throw new UploadException("File has no extension");
}
$metadata['filename'] = $pathinfo['basename'];
$metadata['extension'] = $pathinfo['extension'];
$metadata['tags'] = $tags;
$metadata['source'] = null;
$event = new DataUploadEvent($user, $tmpname, $metadata);
send_event($event);
if($event->image_id == -1) {
throw new UploadException("File type not recognised");
}
}
private function add_dir($base, $subdir="") {
global $page;
if(!is_dir($base)) {
$this->theme->add_status("Error", "$base is not a directory");
return;
}
$list = "";
foreach(glob("$base/$subdir/*") as $fullpath) {
$fullpath = str_replace("//", "/", $fullpath);
$shortpath = str_replace($base, "", $fullpath);
if(is_link($fullpath)) {
// ignore
}
else if(is_dir($fullpath)) {
$this->add_dir($base, str_replace($base, "", $fullpath));
}
else {
$pathinfo = pathinfo($fullpath);
$tags = $subdir;
$tags = str_replace("/", " ", $tags);
$tags = str_replace("__", " ", $tags);
$tags = trim($tags);
$list .= "<br>".html_escape("$shortpath (".str_replace(" ", ", ", $tags).")... ");
try{
$this->add_image($fullpath, $pathinfo["basename"], $tags);
$list .= "ok\n";
}
catch(Exception $ex) {
$list .= "failed:<br>". $ex->getMessage();
}
}
}
if(strlen($list) > 0) {
$this->theme->add_status("Adding $subdir", $list);
}
}
}
?>

30
contrib/bulk_add/test.php Normal file
View File

@ -0,0 +1,30 @@
<?php
class BulkAddTest extends ShimmieWebTestCase {
function testBulkAdd() {
$this->log_in_as_admin();
$this->get_page('admin');
$this->assert_title("Admin Tools");
$this->set_field('dir', "asdf");
$this->click("Add");
$this->assert_text("is not a directory");
$this->get_page('admin');
$this->assert_title("Admin Tools");
$this->set_field('dir', "contrib/simpletest");
$this->click("Add");
# FIXME: test that the output here makes sense, no "adding foo.php ... ok"
$this->get_page("post/list/hash=17fc89f372ed3636e28bd25cc7f3bac1/1");
$this->assert_title(new PatternExpectation("/^Image \d+: data/"));
$this->click("Delete");
$this->get_page("post/list/hash=feb01bab5698a11dd87416724c7a89e3/1");
$this->assert_title(new PatternExpectation("/^Image \d+: data/"));
$this->click("Delete");
$this->log_out();
}
}
?>

View File

@ -0,0 +1,43 @@
<?php
class BulkAddTheme extends Themelet {
var $messages = array();
/*
* Show a standard page for results to be put into
*/
public function display_upload_results(Page $page) {
$page->set_title("Adding folder");
$page->set_heading("Adding folder");
$page->add_block(new NavBlock());
foreach($this->messages as $block) {
$page->add_block($block);
}
}
/*
* Add a section to the admin page. This should contain a form which
* links to bulk_add with POST[dir] set to the name of a server-side
* directory full of images
*/
public function display_admin_block() {
global $page, $user;
$html = "
Add a folder full of images; any subfolders will have their names
used as tags for the images within.
<br>Note: this is the folder as seen by the server -- you need to
upload via FTP or something first.
<p>".make_form(make_link("bulk_add"))."
Directory to add: <input type='text' name='dir' size='40'>
<input type='submit' value='Add'>
</form>
";
$page->add_block(new Block("Bulk Add", $html));
}
public function add_status($title, $body) {
$this->messages[] = new Block($title, $body);
}
}
?>

View File

@ -0,0 +1,429 @@
<?php
/*
Name: Danbooru Client API
Author: JJS <jsutinen@gmail.com>
Description: Allow Danbooru apps like Danbooru Uploader for Firefox to communicate with Shimmie
Documentation:
<p>Notes:
<br>danbooru API based on documentation from danbooru 1.0 -
http://attachr.com/7569
<br>I've only been able to test add_post and find_tags because I use the
old danbooru firefox extension for firefox 1.5
<p>Functions currently implemented:
<ul>
<li>add_post - title and rating are currently ignored because shimmie does not support them
<li>find_posts - sort of works, filename is returned as the original filename and probably won't help when it comes to actually downloading it
<li>find_tags - id, name, and after_id all work but the tags parameter is ignored just like danbooru 1.0 ignores it
</ul>
CHANGELOG
13-OCT-08 8:00PM CST - JJS
Bugfix - Properly escape source attribute
17-SEP-08 10:00PM CST - JJS
Bugfix for changed page name checker in PageRequestEvent
13-APR-08 10:00PM CST - JJS
Properly escape the tags returned in find_tags and find_posts - Caught by ATravelingGeek
Updated extension info to be a bit more clear about its purpose
Deleted add_comment code as it didn't do anything anyway
01-MAR-08 7:00PM CST - JJS
Rewrote to make it compatible with Shimmie trunk again (r723 at least)
It may or may not support the new file handling stuff correctly, I'm only testing with images and the danbooru uploader for firefox
21-OCT-07 9:07PM CST - JJS
Turns out I actually did need to implement the new parameter names
for danbooru api v1.8.1. Now danbooruup should work when used with /api/danbooru/post/create.xml
Also correctly redirects the url provided by danbooruup in the event
of a duplicate image.
19-OCT-07 4:46PM CST - JJS
Add compatibility with danbooru api v1.8.1 style urls
for find_posts and add_post. NOTE: This does not implement
the changes to the parameter names, it is simply a
workaround for the latest danbooruup firefox extension.
Completely compatibility will probably involve a rewrite with a different URL
*/
class DanbooruApi implements Extension
{
// Receive the event
public function receive_event(Event $event)
{
// Check if someone is accessing /api/danbooru (us)
if(($event instanceof PageRequestEvent) && ($event->page_matches("api")) && ($event->get_arg(0) == 'danbooru'))
{
// execute the danbooru processing code
$this->api_danbooru($event);
}
if($event instanceof SearchTermParseEvent)
{
$matches = array();
if(preg_match("/^md5:([0-9a-fA-F]*)$/i", $event->term, $matches))
{
$hash = strtolower($matches[1]);
$event->add_querylet(new Querylet("images.hash = '$hash'")); // :-O
// $event->set_querylet(new Querylet("images.hash = '$hash'"));
}
}
}
// Danbooru API
private function api_danbooru($event)
{
global $page;
global $config;
global $database;
global $user;
$page->set_mode("data");
$page->set_type("application/xml");
//debug
//$page->set_type("text/plain");
$results = array();
$danboorup_kludge=1; // danboorup for firefox makes broken links out of location: /path
/*
add_post()
Adds a post to the database.
Parameters
* login: login
* password: password
* file: file as a multipart form
* source: source url
* title: title **IGNORED**
* tags: list of tags as a string, delimited by whitespace
* md5: MD5 hash of upload in hexadecimal format
* rating: rating of the post. can be explicit, questionable, or safe. **IGNORED**
Notes
* The only necessary parameter is tags and either file or source.
* If you want to sign your post, you need a way to authenticate your account, either by supplying login and password, or by supplying a cookie.
* If an account is not supplied or if it doesnt authenticate, he post will be added anonymously.
* If the md5 parameter is supplied and does not match the hash of whats on the server, the post is rejected.
Response
The response depends on the method used:
Post
* X-Danbooru-Location set to the URL for newly uploaded post.
Get
* Redirected to the newly uploaded post.
*/
if(($event->get_arg(1) == 'add_post') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'create.xml')))
{
// No XML data is returned from this function
$page->set_type("text/plain");
// Check first if a login was supplied, if it wasn't check if the user is logged in via cookie
// If all that fails, it's an anonymous upload
$this->authenticate_user();
// Now we check if a file was uploaded or a url was provided to transload
// Much of this code is borrowed from /ext/upload
if($config->get_bool("upload_anon") || !$user->is_anonymous())
{
$file = null;
$filename = "";
$source = "";
if(isset($_FILES['file']))
{ // A file was POST'd in
$file = $_FILES['file']['tmp_name'];
$filename = $_FILES['file']['name'];
// If both a file is posted and a source provided, I'm assuming source is the source of the file
if(isset($_REQUEST['source']) && !empty($_REQUEST['source']))
{
$source = $_REQUEST['source'];
} else
{
$source = null;
}
} elseif(isset($_FILES['post']))
{
$file = $_FILES['post']['tmp_name']['file'];
$filename = $_FILES['post']['name']['file'];
if(isset($_REQUEST['post']['source']) && !empty($_REQUEST['post']['source']))
{
$source = $_REQUEST['post']['source'];
} else
{
$source = null;
}
} elseif(isset($_REQUEST['source']) || isset($_REQUEST['post']['source']))
{ // A url was provided
$url = isset($_REQUEST['source']) ? $_REQUEST['source'] : $_REQUEST['post']['source'];
$source = $url;
$tmp_filename = tempnam("/tmp", "shimmie_transload");
// Are we using fopen wrappers or curl?
if($config->get_string("transload_engine") == "fopen")
{
$fp = fopen($url, "r");
if(!$fp) {
header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: fopen read error");
}
$data = "";
$length = 0;
while(!feof($fp) && $length <= $config->get_int('upload_size'))
{
$data .= fread($fp, 8192);
$length = strlen($data);
}
fclose($fp);
$fp = fopen($tmp_filename, "w");
fwrite($fp, $data);
fclose($fp);
}
if($config->get_string("transload_engine") == "curl")
{
$ch = curl_init($url);
$fp = fopen($tmp_filename, "w");
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
fclose($fp);
}
$file = $tmp_filename;
$filename = basename($url);
} else
{ // Nothing was specified at all
header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: no input files");
return;
}
// Get tags out of url
$posttags = Tag::explode(isset($_REQUEST['tags']) ? $_REQUEST['tags'] : $_REQUEST['post']['tags']);
$hash = md5_file($file);
// Was an md5 supplied? Does it match the file hash?
if(isset($_REQUEST['md5']))
{
if(strtolower($_REQUEST['md5']) != $hash)
{
header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: md5 mismatch");
return;
}
}
// Upload size checking is now performed in the upload extension
// It is also currently broken due to some confusion over file variable ($tmp_filename?)
// Does it exist already?
$existing = Image::by_hash($hash);
if(!is_null($existing)) {
header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: duplicate");
$existinglink = make_link("post/view/" . $existing->id);
if($danboorup_kludge) $existinglink=make_http($existinglink);
header("X-Danbooru-Location: $existinglink");
return; // wut!
}
// Fire off an event which should process the new file and add it to the db
$fileinfo = pathinfo($filename);
$metadata['filename'] = $fileinfo['basename'];
$metadata['extension'] = $fileinfo['extension'];
$metadata['tags'] = $posttags;
$metadata['source'] = $source;
//log_debug("danbooru_api","========== NEW($filename) =========");
//log_debug("danbooru_api", "upload($filename): fileinfo(".var_export($fileinfo,TRUE)."), metadata(".var_export($metadata,TRUE).")...");
try {
$nevent = new DataUploadEvent($user, $file, $metadata);
//log_debug("danbooru_api", "send_event(".var_export($nevent,TRUE).")");
send_event($nevent);
// If it went ok, grab the id for the newly uploaded image and pass it in the header
$newimg = Image::by_hash($hash); // FIXME: Unsupported file doesn't throw an error?
$newid = make_link("post/view/" . $newimg->id);
if($danboorup_kludge) $newid=make_http($newid);
// Did we POST or GET this call?
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
header("X-Danbooru-Location: $newid");
}
else
header("Location: $newid");
}
catch(UploadException $ex) {
// Did something screw up?
header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: exception - " . $ex->getMessage());
return;
}
} else
{
header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: authentication error");
return;
}
}
/*
find_posts()
Find all posts that match the search criteria. Posts will be ordered by id descending.
Parameters
* md5: md5 hash to search for (comma delimited)
* id: id to search for (comma delimited)
* tags: what tags to search for
* limit: limit
* offset: offset
* after_id: limit results to posts added after this id
*/
if(($event->get_arg(1) == 'find_posts') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'index.xml')))
{
$this->authenticate_user();
if(isset($_GET['md5']))
{
$md5list = explode(",",$_GET['md5']);
foreach($md5list as $md5)
{
$results[] = Image::by_hash($md5);
}
} elseif(isset($_GET['id']))
{
$idlist = explode(",",$_GET['id']);
foreach($idlist as $id)
{
$results[] = Image::by_id($id);
}
} else
{
$limit = isset($_GET['limit']) ? int_escape($_GET['limit']) : 100;
$start = isset($_GET['offset']) ? int_escape($_GET['offset']) : 0;
$tags = isset($_GET['tags']) ? Tag::explode($_GET['tags']) : array();
$results = Image::find_images($start, $limit, $tags);
}
// Now we have the array $results filled with Image objects
// Let's display them
$xml = "<posts>\n";
foreach($results as $img)
{
// Sanity check to see if $img is really an image object
// If it isn't (e.g. someone requested an invalid md5 or id), break out of the this
if(!is_object($img))
continue;
$taglist = $img->get_tag_list();
$owner = $img->get_owner();
$xml .= "<post md5=\"$img->hash\" rating=\"Questionable\" date=\"$img->posted\" is_warehoused=\"false\" file_name=\"$img->filename\" tags=\"" . $this->xmlspecialchars($taglist) . "\" source=\"" . $this->xmlspecialchars($img->source) . "\" score=\"0\" id=\"$img->id\" author=\"$owner->name\"/>\n";
}
$xml .= "</posts>";
$page->set_data($xml);
}
/*
find_tags() Find all tags that match the search criteria.
Parameters
* id: A comma delimited list of tag id numbers.
* name: A comma delimited list of tag names.
* tags: any typical tag query. See Tag#parse_query for details.
* after_id: limit results to tags with an id number after after_id. Useful if you only want to refresh
*/
if($event->get_arg(1) == 'find_tags')
{
if(isset($_GET['id']))
{
$idlist = explode(",",$_GET['id']);
foreach($idlist as $id)
{
$sqlresult = $database->execute("SELECT id,tag,count FROM tags WHERE id = ?", array($id));
if(!$sqlresult->EOF)
{
$results[] = array($sqlresult->fields['count'], $sqlresult->fields['tag'], $sqlresult->fields['id']);
}
}
} elseif(isset($_GET['name']))
{
$namelist = explode(",",$_GET['name']);
foreach($namelist as $name)
{
$sqlresult = $database->execute("SELECT id,tag,count FROM tags WHERE tag = ?", array($name));
if(!$sqlresult->EOF)
{
$results[] = array($sqlresult->fields['count'], $sqlresult->fields['tag'], $sqlresult->fields['id']);
}
}
}
/* Currently disabled to maintain identical functionality to danbooru 1.0's own "broken" find_tags
elseif(isset($_GET['tags']))
{
$start = isset($_GET['after_id']) ? int_escape($_GET['offset']) : 0;
$tags = Tag::explode($_GET['tags']);
}
*/
else
{
$start = isset($_GET['after_id']) ? int_escape($_GET['offset']) : 0;
$sqlresult = $database->execute("SELECT id,tag,count FROM tags WHERE count > 0 AND id >= ? ORDER BY id DESC",array($start));
while(!$sqlresult->EOF)
{
$results[] = array($sqlresult->fields['count'], $sqlresult->fields['tag'], $sqlresult->fields['id']);
$sqlresult->MoveNext();
}
}
// Tag results collected, build XML output
$xml = "<tags>\n";
foreach($results as $tag)
{
$xml .= "<tag type=\"0\" count=\"$tag[0]\" name=\"" . $this->xmlspecialchars($tag[1]) . "\" id=\"$tag[2]\"/>\n";
}
$xml .= "</tags>";
$page->set_data($xml);
}
// Hackery for danbooruup 0.3.2 providing the wrong view url. This simply redirects to the proper
// Shimmie view page
// Example: danbooruup says the url is http://shimmie/api/danbooru/post/show/123
// This redirects that to http://shimmie/post/view/123
if(($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show'))
{
$fixedlocation = make_link("post/view/" . $event->get_arg(3));
header("Location: $fixedlocation");
}
}
// Turns out I use this a couple times so let's make it a utility function
// Authenticates a user based on the contents of the login and password parameters
// or makes them anonymous. Does not set any cookies or anything permanent.
private function authenticate_user()
{
global $config;
global $database;
global $user;
if(isset($_REQUEST['login']) && isset($_REQUEST['password']))
{
// Get this user from the db, if it fails the user becomes anonymous
// Code borrowed from /ext/user
$name = $_REQUEST['login'];
$pass = $_REQUEST['password'];
$hash = md5( strtolower($name) . $pass );
$duser = User::by_name_and_hash($name, $hash);
if(!is_null($duser)) {
$user = $duser;
} else
{
$user = User::by_id($config->get_int("anon_id", 0));
}
}
}
// From htmlspecialchars man page on php.net comments
// If tags contain quotes they need to be htmlified
private function xmlspecialchars($text)
{
return str_replace('&#039;', '&apos;', htmlspecialchars($text, ENT_QUOTES));
}
}
add_event_listener(new DanbooruApi());
?>

46
contrib/downtime/main.php Normal file
View File

@ -0,0 +1,46 @@
<?php
/*
* Name: Downtime
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Show a "down for maintenance" page
* Documentation:
* Once installed there will be some more options on the config page --
* Ticking "disable non-admin access" will mean that regular and anonymous
* users will be blocked from accessing the site, only able to view the
* message specified in the box.
*/
class Downtime implements Extension {
var $theme;
public function receive_event(Event $event) {
global $config, $database, $page, $user;
if(is_null($this->theme)) $this->theme = get_theme_object($this);
if($event instanceof SetupBuildingEvent) {
$sb = new SetupBlock("Downtime");
$sb->add_bool_option("downtime", "Disable non-admin access: ");
$sb->add_longtext_option("downtime_message", "<br>");
$event->panel->add_block($sb);
}
if($event instanceof PageRequestEvent) {
if($config->get_bool("downtime")) {
if(!$user->is_admin() && !$this->is_safe_page($event)) {
$msg = $config->get_string("downtime_message");
$this->theme->display_message($msg);
exit;
}
$this->theme->display_notification($page);
}
}
}
private function is_safe_page(PageRequestEvent $event) {
if($event->page_matches("user_admin/login")) return true;
else return false;
}
}
add_event_listener(new Downtime(), 10);
?>

23
contrib/downtime/test.php Normal file
View File

@ -0,0 +1,23 @@
<?php
class DowntimeTest extends SCoreWebTestCase {
function testDowntime() {
$this->log_in_as_admin();
$this->get_page("setup");
$this->set_field("_config_downtime", true);
$this->set_field("_config_downtime_message", "brb, unit testing");
$this->click("Save Settings");
$this->assert_text("DOWNTIME MODE IS ON!");
$this->log_out();
$this->get_page("post/list");
$this->assert_text("brb, unit testing");
$this->log_in_as_admin();
$this->get_page("setup");
$this->set_field("_config_downtime", false);
$this->click("Save Settings");
$this->assert_no_text("DOWNTIME MODE IS ON!");
$this->log_out();
}
}
?>

View File

@ -0,0 +1,58 @@
<?php
class DowntimeTheme extends Themelet {
/**
* Show the admin that downtime mode is enabled
*/
public function display_notification(Page $page) {
$page->add_block(new Block("Downtime",
"<span style='font-size: 1.5em'><b>DOWNTIME MODE IS ON!</b></span>", "left", 0));
}
/**
* Display $message and exit
*/
public function display_message($message) {
global $config, $user;
$theme_name = $config->get_string('theme');
$data_href = get_base_href();
$login_link = make_link("user_admin/login");
header("HTTP/1.0 503 Service Temporarily Unavailable");
$auth = $user->get_auth_html();
print <<<EOD
<html>
<head>
<title>Downtime</title>
<link rel="stylesheet" href="$data_href/themes/$theme_name/style.css" type="text/css">
</head>
<body>
<div id="downtime">
<h1>Down for Maintenance</h1>
<div id="message">
$message
</div>
<h3>Admin Login</h3>
<div id="login">
<form action="$login_link" method="POST">
$auth
<table id="login_table" summary="Login Form">
<tr>
<td width="70"><label for="user">Name</label></td>
<td width="70"><input id="user" type="text" name="user"></td>
</tr>
<tr>
<td><label for="pass">Password</label></td>
<td><input id="pass" type="password" name="pass"></td>
</tr>
<tr><td colspan="2"><input type="submit" value="Log In"></td></tr>
</table>
</form>
</div>
</div>
</body>
</html>
EOD;
}
}
?>

View File

Before

Width:  |  Height:  |  Size: 170 B

After

Width:  |  Height:  |  Size: 170 B

View File

Before

Width:  |  Height:  |  Size: 172 B

After

Width:  |  Height:  |  Size: 172 B

View File

Before

Width:  |  Height:  |  Size: 171 B

After

Width:  |  Height:  |  Size: 171 B

View File

Before

Width:  |  Height:  |  Size: 172 B

After

Width:  |  Height:  |  Size: 172 B

View File

Before

Width:  |  Height:  |  Size: 498 B

After

Width:  |  Height:  |  Size: 498 B

View File

Before

Width:  |  Height:  |  Size: 170 B

After

Width:  |  Height:  |  Size: 170 B

View File

Before

Width:  |  Height:  |  Size: 236 B

After

Width:  |  Height:  |  Size: 236 B

View File

Before

Width:  |  Height:  |  Size: 236 B

After

Width:  |  Height:  |  Size: 236 B

View File

Before

Width:  |  Height:  |  Size: 171 B

After

Width:  |  Height:  |  Size: 171 B

View File

Before

Width:  |  Height:  |  Size: 176 B

After

Width:  |  Height:  |  Size: 176 B

View File

Before

Width:  |  Height:  |  Size: 336 B

After

Width:  |  Height:  |  Size: 336 B

View File

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 174 B

View File

Before

Width:  |  Height:  |  Size: 349 B

After

Width:  |  Height:  |  Size: 349 B

View File

Before

Width:  |  Height:  |  Size: 171 B

After

Width:  |  Height:  |  Size: 171 B

View File

Before

Width:  |  Height:  |  Size: 248 B

After

Width:  |  Height:  |  Size: 248 B

View File

Before

Width:  |  Height:  |  Size: 176 B

After

Width:  |  Height:  |  Size: 176 B

View File

Before

Width:  |  Height:  |  Size: 650 B

After

Width:  |  Height:  |  Size: 650 B

View File

Before

Width:  |  Height:  |  Size: 485 B

After

Width:  |  Height:  |  Size: 485 B

View File

Before

Width:  |  Height:  |  Size: 171 B

After

Width:  |  Height:  |  Size: 171 B

View File

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 174 B

View File

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 174 B

View File

Before

Width:  |  Height:  |  Size: 238 B

After

Width:  |  Height:  |  Size: 238 B

View File

Before

Width:  |  Height:  |  Size: 170 B

After

Width:  |  Height:  |  Size: 170 B

View File

@ -0,0 +1,35 @@
<?php
/*
* Name: Emoticon Filter
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Lets users use graphical smilies
* Documentation:
* This extension will turn colon-something-colon into a link
* to an image with that something as the name, eg :smile:
* becomes a link to smile.gif
* <p>Images are stored in /ext/emoticons/default/, and you can
* add more emoticons by uploading images into that folder.
*/
class Emoticons extends FormatterExtension {
public function format($text) {
$data_href = get_base_href();
$text = preg_replace("/:([a-z]*?):/s", "<img src='$data_href/ext/emoticons/default/\\1.gif'>", $text);
return $text;
}
public function strip($text) {
return $text;
}
}
add_event_listener(new Emoticons());
class EmoticonList extends SimpleExtension {
public function onPageRequest($event) {
if($event->page_matches("emote/list")) {
$this->theme->display_emotes(glob("ext/emoticons/default/*"));
}
}
}
?>

View File

@ -0,0 +1,22 @@
<?php
class EmoticonTest extends ShimmieWebTestCase {
function testEmoticons() {
$this->log_in_as_user();
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pbx computer screenshot");
$this->get_page("post/view/$image_id");
$this->set_field('comment', ":cool: :beans:");
$this->click("Post Comment");
$this->assert_no_text(":cool:"); # FIXME: test for working image link
#$this->assert_text(":beans:"); # FIXME: this should be left as-is
$this->get_page("emote/list");
$this->assert_text(":arrow:");
$this->log_out();
$this->log_in_as_admin();
$this->delete_image($image_id);
$this->log_out();
}
}
?>

View File

@ -0,0 +1,21 @@
<?php
class EmoticonListTheme extends Themelet {
public function display_emotes($list) {
global $page;
$data_href = get_base_href();
$html = "<html><head><title>Emoticon list</title></head><body>";
$html .= "<table><tr>";
$n = 1;
foreach($list as $item) {
$pathinfo = pathinfo($item);
$name = $pathinfo["filename"];
$html .= "<td><img src='$data_href/$item'> :$name:</td>";
if($n++ % 3 == 0) $html .= "</tr><tr>";
}
$html .= "</tr></table>";
$html .= "</body></html>";
$page->set_mode("data");
$page->set_data($html);
}
}
?>

79
contrib/et/main.php Normal file
View File

@ -0,0 +1,79 @@
<?php
/*
* Name: System Info
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Show various bits of system information
* Documentation:
* Knowing the information that this extension shows can be
* very useful for debugging. There's also an option to send
* your stats to my database, so I can get some idea of how
* shimmie is used, which servers I need to support, which
* versions of PHP I should test with, etc.
*/
class ET implements Extension {
var $theme;
public function receive_event(Event $event) {
global $config, $database, $page, $user;
if(is_null($this->theme)) $this->theme = get_theme_object($this);
if(($event instanceof PageRequestEvent) && $event->page_matches("system_info")) {
if($user->is_admin()) {
$this->theme->display_info_page($page, $this->get_info());
}
}
if($event instanceof UserBlockBuildingEvent) {
if($user->is_admin()) {
$event->add_link("System Info", make_link("system_info"));
}
}
}
private function get_info() {
global $config, $database;
global $_event_listeners; // yay for using secret globals \o/
$info = array();
$info['site_title'] = $config->get_string("title");
$info['site_theme'] = $config->get_string("theme");
$info['site_url'] = "http://" . $_SERVER["HTTP_HOST"] . get_base_href();
$info['sys_shimmie'] = VERSION;
$info['sys_schema'] = $config->get_string("db_version");
$info['sys_php'] = phpversion();
$info['sys_os'] = php_uname();
$info['sys_disk'] = to_shorthand_int(disk_total_space("./") - disk_free_space("./")) . " / " .
to_shorthand_int(disk_total_space("./"));
$info['sys_server'] = $_SERVER["SERVER_SOFTWARE"];
include "config.php"; // more magical hax
$proto = preg_replace("#(.*)://.*#", "$1", $database_dsn);
$db = $database->db->ServerInfo();
$info['sys_db'] = "$proto / {$db['version']}";
$info['stat_images'] = $database->db->GetOne("SELECT COUNT(*) FROM images");
$info['stat_comments'] = $database->db->GetOne("SELECT COUNT(*) FROM comments");
$info['stat_users'] = $database->db->GetOne("SELECT COUNT(*) FROM users");
$info['stat_tags'] = $database->db->GetOne("SELECT COUNT(*) FROM tags");
$info['stat_image_tags'] = $database->db->GetOne("SELECT COUNT(*) FROM image_tags");
$els = array();
foreach($_event_listeners as $el) {
$els[] = get_class($el);
}
$info['sys_extensions'] = join(', ', $els);
//$cfs = array();
//foreach($database->get_all("SELECT name, value FROM config") as $pair) {
// $cfs[] = $pair['name']."=".$pair['value'];
//}
//$info[''] = "Config: ".join(", ", $cfs);
return $info;
}
// }}}
}
add_event_listener(new ET());
?>

10
contrib/et/test.php Normal file
View File

@ -0,0 +1,10 @@
<?php
class ETTest extends ShimmieWebTestCase {
function testET() {
$this->log_in_as_admin();
$this->get_page("system_info");
$this->assert_title("System Info");
$this->log_out();
}
}
?>

53
contrib/et/theme.php Normal file
View File

@ -0,0 +1,53 @@
<?php
class ETTheme extends Themelet {
/*
* Create a page showing info
*
* $info = an array of ($name => $value)
*/
public function display_info_page(Page $page, $info) {
$page->set_title("System Info");
$page->set_heading("System Info");
$page->add_block(new NavBlock());
$page->add_block(new Block("Information:", $this->build_data_form($info)));
}
protected function build_data_form($info) {
$data = <<<EOD
Optional:
Site title: {$info['site_title']}
Theme: {$info['site_theme']}
Genre: [describe your site here]
URL: {$info['site_url']}
System stats:
Shimmie: {$info['sys_shimmie']}
Schema: {$info['sys_schema']}
PHP: {$info['sys_php']}
OS: {$info['sys_os']}
Server: {$info['sys_server']}
Database: {$info['sys_db']}
Disk use: {$info['sys_disk']}
Shimmie stats:
Images: {$info['stat_images']}
Comments: {$info['stat_comments']}
Users: {$info['stat_users']}
Tags: {$info['stat_tags']}
Applications: {$info['stat_image_tags']}
Extensions: {$info['sys_extensions']}
EOD;
$html = <<<EOD
<form action='http://shimmie.shishnet.org/register.php' method='POST'>
<input type='hidden' name='registration_api' value='1'>
<textarea name='data' rows='20' cols='80'>$data</textarea>
<br><input type='submit' value='Click to send to Shish'>
<br>Your stats are useful so that I know which combinations
of web servers / databases / etc I need to support.
</form>
EOD;
return $html;
}
}
?>

186
contrib/favorites/main.php Normal file
View File

@ -0,0 +1,186 @@
<?php
/*
* Name: Favorites
* Author: Daniel Marschall <info@daniel-marschall.de>
* License: GPLv2
* Description: Allow users to favorite images
* Documentation:
* Gives users a "favorite this image" button that they can press
* <p>Favorites for a user can then be retrieved by searching for
* "favorited_by=UserName"
* <p>Popular images can be searched for by eg. "favorites>5"
* <p>Favorite info can be added to an image's filename or tooltip
* using the $favorites placeholder
*/
class FavoriteSetEvent extends Event {
var $image_id, $user, $do_set;
public function FavoriteSetEvent($image_id, User $user, $do_set) {
assert(is_numeric($image_id));
assert(is_bool($do_set));
$this->image_id = $image_id;
$this->user = $user;
$this->do_set = $do_set;
}
}
class Favorites extends SimpleExtension {
public function onInitExt($event) {
global $config;
if($config->get_int("ext_favorites_version", 0) < 1) {
$this->install();
}
}
public function onImageAdminBlockBuilding($event) {
global $database, $page, $user;
if(!$user->is_anonymous()) {
$user_id = $user->id;
$image_id = $event->image->id;
$is_favorited = $database->db->GetOne(
"SELECT COUNT(*) AS ct FROM user_favorites WHERE user_id = ? AND image_id = ?",
array($user_id, $image_id)) > 0;
$event->add_part($this->theme->get_voter_html($event->image, $is_favorited));
}
}
public function onDisplayingImage($event) {
$people = $this->list_persons_who_have_favorited($event->image);
if(count($people) > 0) {
$html = $this->theme->display_people($people);
}
}
public function onPageRequest($event) {
global $page, $user;
if($event->page_matches("change_favorite") && !$user->is_anonymous() && $user->check_auth_token()) {
$image_id = int_escape($_POST['image_id']);
if((($_POST['favorite_action'] == "set") || ($_POST['favorite_action'] == "unset")) && ($image_id > 0)) {
send_event(new FavoriteSetEvent($image_id, $user, ($_POST['favorite_action'] == "set")));
}
$page->set_mode("redirect");
$page->set_redirect(make_link("post/view/$image_id"));
}
}
public function onUserPageBuilding($event) {
$i_favorites_count = Image::count_images(array("favorited_by={$event->display_user->name}"));
$i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1;
$h_favorites_rate = sprintf("%.1f", ($i_favorites_count / $i_days_old));
$favorites_link = make_link("post/list/favorited_by={$event->display_user->name}/1");
$event->add_stats("<a href='$favorites_link'>Images favorited</a>: $i_favorites_count, $h_favorites_rate per day");
}
public function onImageInfoSet($event) {
global $user;
if(
in_array('favorite_action', $_POST) &&
(($_POST['favorite_action'] == "set") || ($_POST['favorite_action'] == "unset"))
) {
send_event(new FavoriteSetEvent($event->image_id, $user, ($_POST['favorite_action'] == "set")));
}
}
public function onFavoriteSet($event) {
global $user;
$this->add_vote($event->image_id, $user->id, $event->do_set);
}
public function onImageDeletion($event) {
global $database;
$database->execute("DELETE FROM user_favorites WHERE image_id=?", array($event->image->id));
}
public function onParseLinkTemplate($event) {
$event->replace('$favorites', $event->image->favorites);
}
public function onUserBlockBuilding($event) {
global $user;
if(strpos($user->name, ' ') === false) {
$username = url_escape($user->name);
$link = make_link("post/list/favorited_by=$username/1");
} else {
$userid = $user->id;
$link = make_link("post/list/favorited_by_userno=$userid/1");
}
$event->add_link("My Favorites", $link);
}
public function onSearchTermParse($event) {
$matches = array();
if(preg_match("/favorites(<|>|<=|>=|=)(\d+)/", $event->term, $matches)) {
$cmp = $matches[1];
$favorites = $matches[2];
$event->add_querylet(new Querylet("images.id IN (SELECT id FROM images WHERE favorites $cmp $favorites)"));
}
else if(preg_match("/favorited_by=(.*)/i", $event->term, $matches)) {
global $database;
$user = User::by_name($matches[1]);
if(!is_null($user)) {
$user_id = $user->id;
}
else {
$user_id = -1;
}
$event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM user_favorites WHERE user_id = $user_id)"));
}
else if(preg_match("/favorited_by_userno=([0-9]+)/i", $event->term, $matches)) {
$user_id = int_escape($matches[1]);
$event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM user_favorites WHERE user_id = $user_id)"));
}
}
private function install() {
global $database;
global $config;
if($config->get_int("ext_favorites_version") < 1) {
$database->Execute("ALTER TABLE images ADD COLUMN favorites INTEGER NOT NULL DEFAULT 0");
$database->Execute("CREATE INDEX images__favorites ON images(favorites)");
$database->Execute("
CREATE TABLE user_favorites (
image_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
created_at DATETIME NOT NULL,
UNIQUE(image_id, user_id),
INDEX(image_id)
)
");
$config->set_int("ext_favorites_version", 1);
}
}
private function add_vote($image_id, $user_id, $do_set) {
global $database;
if ($do_set) {
$database->Execute(
"INSERT INTO user_favorites(image_id, user_id, created_at) VALUES(?, ?, NOW())",
array($image_id, $user_id));
} else {
$database->Execute(
"DELETE FROM user_favorites WHERE image_id = ? AND user_id = ?",
array($image_id, $user_id));
}
$database->Execute(
"UPDATE images SET favorites=(SELECT COUNT(*) FROM user_favorites WHERE image_id=?) WHERE id=?",
array($image_id, $image_id));
}
private function list_persons_who_have_favorited($image) {
global $database;
$result = $database->execute(
"SELECT name FROM users WHERE id IN (SELECT user_id FROM user_favorites WHERE image_id = ?) ORDER BY name",
array($image->id));
return $result->GetArray();
}
}
?>

View File

@ -0,0 +1,33 @@
<?php
class FavoritesTest extends ShimmieWebTestCase {
function testFavorites() {
$this->log_in_as_user();
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "test");
$this->get_page("post/view/$image_id");
$this->assert_title("Image $image_id: test");
$this->assert_no_text("Favorited By");
$this->click("Favorite");
$this->assert_text("Favorited By");
$this->get_page("post/list/favorited_by=test/1");
$this->assert_title("Image $image_id: test");
$this->assert_text("Favorited By");
$this->get_page("user/test");
$this->assert_text("Images favorited: 1");
$this->click("Images favorited");
$this->assert_title("Image $image_id: test");
$this->click("Un-Favorite");
$this->assert_no_text("Favorited By");
$this->log_out();
$this->log_in_as_admin();
$this->delete_image($image_id);
$this->log_out();
}
}
?>

View File

@ -0,0 +1,36 @@
<?php
class FavoritesTheme extends Themelet {
public function get_voter_html(Image $image, $is_favorited) {
global $page, $user;
$i_image_id = int_escape($image->id);
$name = $is_favorited ? "unset" : "set";
$label = $is_favorited ? "Un-Favorite" : "Favorite";
$html = "
".make_form(make_link("change_favorite"))."
<input type='hidden' name='image_id' value='$i_image_id'>
<input type='hidden' name='favorite_action' value='$name'>
<input type='submit' value='$label'>
</form>
";
return $html;
}
public function display_people($username_array) {
global $page;
$i_favorites = count($username_array);
$html = "$i_favorites people:";
foreach($username_array as $row) {
$username = html_escape($row['name']);
$html .= "<br><a href='".make_link("user/$username")."'>$username</a>";
}
$page->add_block(new Block("Favorited By", $html, "left", 25));
}
}
?>

84
contrib/featured/main.php Normal file
View File

@ -0,0 +1,84 @@
<?php
/*
* Name: Featured Image
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Bring a specific image to the users' attentions
* Documentation:
* Once enabled, a new "feature this" button will appear next
* to the other image control buttons (delete, rotate, etc).
* Clicking it will set the image as the site's current feature,
* which will be shown in the side bar of the post list.
* <p><b>Viewing a featured image</b>
* <br>Visit <code>/featured_image/view</code>
* <p><b>Downloading a featured image</b>
* <br>Link to <code>/featured_image/download</code>. This will give
* the raw data for an image (no HTML). This is useful so that you
* can set your desktop wallpaper to be the download URL, refreshed
* every couple of hours.
*/
class Featured extends SimpleExtension {
public function onInitExt($event) {
global $config;
$config->set_default_int('featured_id', 0);
}
public function onPageRequest($event) {
global $config, $page, $user;
if($event->page_matches("featured_image")) {
if($event->get_arg(0) == "set" && $user->check_auth_token()) {
if($user->is_admin() && isset($_POST['image_id'])) {
$id = int_escape($_POST['image_id']);
if($id > 0) {
$config->set_int("featured_id", $id);
$page->set_mode("redirect");
$page->set_redirect(make_link("post/view/$id"));
}
}
}
if($event->get_arg(0) == "download") {
$image = Image::by_id($config->get_int("featured_id"));
if(!is_null($image)) {
$page->set_mode("data");
$page->set_type("image/jpeg");
$page->set_data(file_get_contents($image->get_image_filename()));
}
}
if($event->get_arg(0) == "view") {
$image = Image::by_id($config->get_int("featured_id"));
if(!is_null($image)) {
send_event(new DisplayingImageEvent($image, $page));
}
}
}
}
public function onPostListBuilding($event) {
global $config, $database, $page, $user;
$fid = $config->get_int("featured_id");
if($fid > 0) {
$image = $database->cache->get("featured_image_object");
if(empty($image)) {
$image = Image::by_id($fid);
$database->cache->set("featured_image_object", $image, 60);
}
if(!is_null($image)) {
if(class_exists("Ratings")) {
if(strpos(Ratings::get_user_privs($user), $image->rating) === FALSE) {
return;
}
}
$this->theme->display_featured($page, $image);
}
}
}
public function onImageAdminBlockBuilding($event) {
global $user;
if($user->is_admin()) {
$event->add_part($this->theme->get_buttons_html($event->image->id));
}
}
}
?>

26
contrib/featured/test.php Normal file
View File

@ -0,0 +1,26 @@
<?php
class FeaturedTest extends ShimmieWebTestCase {
function testFeatured() {
$this->log_in_as_user();
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pbx");
$this->log_out();
# FIXME: test that regular users can't feature things
$this->log_in_as_admin();
$this->get_page("post/view/$image_id");
$this->assert_title("Image $image_id: pbx");
$this->click("Feature This");
$this->get_page("post/list");
$this->assert_text("Featured Image");
$this->delete_image($image_id);
$this->log_out();
# after deletion, there should be no feature
$this->get_page("post/list");
$this->assert_no_text("Featured Image");
# FIXME: test changing from one feature to another
}
}
?>

View File

@ -0,0 +1,22 @@
<?php
class FeaturedTheme extends Themelet {
/*
* Show $text on the $page
*/
public function display_featured(Page $page, Image $image) {
$page->add_block(new Block("Featured Image", $this->build_thumb_html($image), "left", 3));
}
public function get_buttons_html($image_id) {
global $user;
return "
".make_form(make_link("featured_image/set"))."
".$user->get_auth_html()."
<input type='hidden' name='image_id' value='$image_id'>
<input type='submit' value='Feature This'>
</form>
";
}
}
?>

View File

@ -0,0 +1,107 @@
<?php
/*
* Name: Handle Archives
* Author: Shish <webmaster@shishnet.org>
* Description: Allow users to upload archives (zip, etc)
* Documentation:
* Note: requires exec() access and an external unzip command
* <p>Any command line unzipper should work, some examples:
* <p>unzip: <code>unzip -d "%d" "%f"</code>
* <br>7-zip: <code>7zr x -o"%d" "%f"</code>
*/
class ArchiveFileHandler extends SimpleExtension {
public function onInitExt($event) {
global $config;
$config->set_default_string('archive_extract_command', 'unzip -d "%d" "%f"');
}
public function onSetupBuilding($event) {
$sb = new SetupBlock("Archive Handler Options");
$sb->add_text_option("archive_tmp_dir", "Temporary folder: ");
$sb->add_text_option("archive_extract_command", "<br>Extraction command: ");
$sb->add_label("<br>%f for archive, %d for temporary directory");
$event->panel->add_block($sb);
}
public function onDataUpload($event) {
if($this->supported_ext($event->type)) {
global $config;
$tmp = sys_get_temp_dir();
$tmpdir = "$tmp/shimmie-archive-{$event->hash}";
$cmd = $config->get_string('archive_extract_command');
$cmd = str_replace('%f', $event->tmpname, $cmd);
$cmd = str_replace('%d', $tmpdir, $cmd);
exec($cmd);
$this->add_dir($tmpdir);
deltree($tmpdir);
}
}
private function supported_ext($ext) {
$exts = array("zip");
return in_array(strtolower($ext), $exts);
}
// copied from bulk add extension
private function add_image($tmpname, $filename, $tags) {
assert(file_exists($tmpname));
try {
global $user;
$pathinfo = pathinfo($filename);
if(!array_key_exists('extension', $pathinfo)) {
throw new UploadException("File has no extension");
}
$metadata['filename'] = $pathinfo['basename'];
$metadata['extension'] = $pathinfo['extension'];
$metadata['tags'] = $tags;
$metadata['source'] = null;
$event = new DataUploadEvent($user, $tmpname, $metadata);
send_event($event);
}
catch(UploadException $ex) {
return $ex->getMessage();
}
}
// copied from bulk add extension
private function add_dir($base, $subdir="") {
global $page;
$list = "";
$dir = opendir("$base/$subdir");
while($filename = readdir($dir)) {
$fullpath = "$base/$subdir/$filename";
if(is_link($fullpath)) {
// ignore
}
else if(is_dir($fullpath)) {
if($filename[0] != ".") {
$this->add_dir($base, "$subdir/$filename");
}
}
else {
$tmpfile = $fullpath;
$tags = $subdir;
$tags = str_replace("/", " ", $tags);
$tags = str_replace("__", " ", $tags);
$list .= "<br>".html_escape("$subdir/$filename (".str_replace(" ", ",", $tags).")...");
$error = $this->add_image($tmpfile, $filename, $tags);
if(is_null($error)) {
$list .= "ok\n";
}
else {
$list .= "failed: $error\n";
}
}
}
closedir($dir);
// $this->theme->add_status("Adding $subdir", $list);
}
}
?>

View File

@ -0,0 +1,105 @@
<?php
/*
* Name: Handle Flash
* Author: Shish <webmaster@shishnet.org>
* Description: Handle Flash files
*/
class FlashFileHandler extends DataHandlerExtension {
protected function create_thumb($hash) {
// FIXME: scale image, as not all boards use 192x192
copy("ext/handle_flash/thumb.jpg", warehouse_path("thumbs", $hash));
}
protected function supported_ext($ext) {
$exts = array("swf");
return in_array(strtolower($ext), $exts);
}
protected function create_image_from_data($filename, $metadata) {
global $config;
$image = new Image();
$image->filesize = $metadata['size'];
$image->hash = $metadata['hash'];
$image->filename = $metadata['filename'];
$image->ext = $metadata['extension'];
$image->tag_array = Tag::explode($metadata['tags']);
$image->source = $metadata['source'];
// redundant, since getimagesize() works on SWF o_O
// $rect = $this->swf_get_bounds($filename);
// if(is_null($rect)) {
// return $null;
// }
// $image->width = $rect[1];
// $image->height = $rect[3];
if(!($info = getimagesize($filename))) return null;
$image->width = $info[0];
$image->height = $info[1];
return $image;
}
protected function check_contents($file) {
if(!file_exists($file)) return false;
$fp = fopen($file, "r");
$head = fread($fp, 3);
fclose($fp);
if(!in_array($head, array("CWS", "FWS"))) return false;
return true;
}
private function str_to_binarray($string) {
$binary = array();
for($j=0; $j<strlen($string); $j++) {
$c = ord($string[$j]);
for($i=7; $i>=0; $i--) {
$binary[] = ($c >> $i) & 0x01;
}
}
return $binary;
}
private function binarray_to_int($binarray, $start=0, $length=32) {
$int = 0;
for($i=$start; $i<$start + $length; $i++) {
$int = $int << 1;
$int = $int + ($binarray[$i] == "1" ? 1 : 0);
}
return $int;
}
private function swf_get_bounds($filename) {
$fp = fopen($filename, "r");
$head = fread($fp, 3);
$version = fread($fp, 1);
$length = fread($fp, 4);
if($head == "FWS") {
$data = fread($fp, 16);
}
else if($head == "CWS") {
$data = fread($fp, 128*1024);
$data = gzuncompress($data);
$data = substr($data, 0, 16);
}
$bounds = array();
$rect_bin = $this->str_to_binarray($data);
$nbits = $this->binarray_to_int($rect_bin, 0, 5);
$bounds[] = $this->binarray_to_int($rect_bin, 5 + 0 * $nbits, $nbits) / 20;
$bounds[] = $this->binarray_to_int($rect_bin, 5 + 1 * $nbits, $nbits) / 20;
$bounds[] = $this->binarray_to_int($rect_bin, 5 + 2 * $nbits, $nbits) / 20;
$bounds[] = $this->binarray_to_int($rect_bin, 5 + 3 * $nbits, $nbits) / 20;
return $bounds;
}
}
add_event_listener(new FlashFileHandler());
?>

View File

@ -1,9 +1,7 @@
<?php declare(strict_types=1);
<?php
class FlashFileHandlerTheme extends Themelet
{
public function display_image(Page $page, Image $image)
{
class FlashFileHandlerTheme extends Themelet {
public function display_image(Page $page, Image $image) {
$ilink = $image->get_image_link();
// FIXME: object and embed have "height" and "width"
$html = "
@ -11,7 +9,6 @@ class FlashFileHandlerTheme extends Themelet
codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0'
height='{$image->height}'
width='{$image->width}'
wmode='opaque'
>
<param name='movie' value='$ilink'/>
<param name='quality' value='high' />
@ -19,9 +16,9 @@ class FlashFileHandlerTheme extends Themelet
pluginspage='http://www.macromedia.com/go/getflashplayer'
height='{$image->height}'
width='{$image->width}'
wmode='opaque'
type='application/x-shockwave-flash' />
type='application/x-shockwave-flash'></embed>
</object>";
$page->add_block(new Block("Flash Animation", $html, "main", 10));
$page->add_block(new Block("Flash Animation", $html, "main", 0));
}
}
?>

View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

113
contrib/handle_ico/main.php Normal file
View File

@ -0,0 +1,113 @@
<?php
/*
* Name: Handle ICO
* Author: Shish <webmaster@shishnet.org>
* Description: Handle windows icons
*/
class IcoFileHandler extends SimpleExtension {
public function onDataUpload($event) {
if($this->supported_ext($event->type) && $this->check_contents($event->tmpname)) {
$hash = $event->hash;
$ha = substr($hash, 0, 2);
if(!move_upload_to_archive($event)) return;
send_event(new ThumbnailGenerationEvent($event->hash, $event->type));
$image = $this->create_image_from_data("images/$ha/$hash", $event->metadata);
if(is_null($image)) {
throw new UploadException("Icon handler failed to create image object from data");
}
$iae = new ImageAdditionEvent($event->user, $image);
send_event($iae);
$event->image_id = $iae->image->id;
}
}
public function onThumbnailGeneration($event) {
if($this->supported_ext($event->type)) {
$this->create_thumb($event->hash);
}
}
public function onDisplayingImage($event) {
global $page;
if($this->supported_ext($event->image->ext)) {
$this->theme->display_image($page, $event->image);
}
}
public function onPageRequest($event) {
global $config, $database, $page;
if($event->page_matches("get_ico")) {
$id = int_escape($event->get_arg(0));
$image = Image::by_id($id);
$hash = $image->hash;
$ha = substr($hash, 0, 2);
$page->set_type("image/x-icon");
$page->set_mode("data");
$page->set_data(file_get_contents("images/$ha/$hash"));
}
}
private function supported_ext($ext) {
$exts = array("ico", "ani", "cur");
return in_array(strtolower($ext), $exts);
}
private function create_image_from_data($filename, $metadata) {
global $config;
$image = new Image();
$info = "";
$fp = fopen($filename, "r");
$header = unpack("snull/stype/scount", fread($fp, 6));
$subheader = unpack("cwidth/cheight/ccolours/cnull/splanes/sbpp/lsize/loffset", fread($fp, 16));
fclose($fp);
$image->width = $subheader['width'];
$image->height = $subheader['height'];
$image->filesize = $metadata['size'];
$image->hash = $metadata['hash'];
$image->filename = $metadata['filename'];
$image->ext = $metadata['extension'];
$image->tag_array = Tag::explode($metadata['tags']);
$image->source = $metadata['source'];
return $image;
}
private function check_contents($file) {
if(!file_exists($file)) return false;
$fp = fopen($file, "r");
$header = unpack("snull/stype/scount", fread($fp, 6));
fclose($fp);
return ($header['null'] == 0 && ($header['type'] == 0 || $header['type'] == 1));
}
private function create_thumb($hash) {
global $config;
$inname = warehouse_path("images", $hash);
$outname = warehouse_path("thumbs", $hash);
$w = $config->get_int("thumb_width");
$h = $config->get_int("thumb_height");
$q = $config->get_int("thumb_quality");
$mem = $config->get_int("thumb_max_memory") / 1024 / 1024; // IM takes memory in MB
if($config->get_bool("ico_convert")) {
// "-limit memory $mem" broken?
exec("convert {$inname}[0] -geometry {$w}x{$h} -quality {$q} jpg:$outname");
}
else {
copy($inname, $outname);
}
return true;
}
}
?>

View File

@ -0,0 +1,19 @@
<?php
class IcoHandlerTest extends ShimmieWebTestCase {
function testPixelHander() {
$this->log_in_as_user();
$image_id = $this->post_image("favicon.ico", "shimmie favicon");
$this->assert_response(302);
$this->log_out();
$raw = $this->get_page("get_ico/$image_id"); // test for no crash
$this->log_in_as_admin();
$this->delete_image($image_id);
$this->log_out();
# FIXME: test that the thumb works
# FIXME: test that it gets displayed properly
}
}
?>

View File

@ -0,0 +1,12 @@
<?php
class IcoFileHandlerTheme extends Themelet {
public function display_image(Page $page, Image $image) {
$ilink = make_link("get_ico/{$image->id}/{$image->id}.ico");
$html = "
<img id='main_image' src='$ilink'>
";
$page->add_block(new Block("Image", $html, "main", 0));
}
}
?>

View File

@ -0,0 +1,44 @@
<?php
/*
* Name: Handle MP3
* Author: Shish <webmaster@shishnet.org>
* Description: Handle MP3 files
*/
class MP3FileHandler extends DataHandlerExtension {
protected function create_thumb($hash) {
// FIXME: scale image, as not all boards use 192x192
copy("ext/handle_mp3/thumb.jpg", warehouse_path("thumbs", $hash));
}
protected function supported_ext($ext) {
$exts = array("mp3");
return in_array(strtolower($ext), $exts);
}
protected function create_image_from_data($filename, $metadata) {
global $config;
$image = new Image();
// FIXME: need more flash format specs :|
$image->width = 0;
$image->height = 0;
$image->filesize = $metadata['size'];
$image->hash = $metadata['hash'];
$image->filename = $metadata['filename'];
$image->ext = $metadata['extension'];
$image->tag_array = Tag::explode($metadata['tags']);
$image->source = $metadata['source'];
return $image;
}
protected function check_contents($file) {
// FIXME: mp3 magic header?
return (file_exists($file));
}
}
add_event_listener(new MP3FileHandler());
?>

View File

@ -0,0 +1,22 @@
<?php
class MP3FileHandlerTheme extends Themelet {
public function display_image(Page $page, Image $image) {
$data_href = get_base_href();
$ilink = $image->get_image_link();
$html = "
<object classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'
codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0'
width='400' height='15'>
<param name='movie' value='$data_href/ext/handle_mp3/xspf_player_slim.swf?song_url=$ilink'/>
<param name='quality' value='high' />
<embed src='$data_href/ext/handle_mp3/xspf_player_slim.swf?song_url=$ilink' quality='high'
pluginspage='http://www.macromedia.com/go/getflashplayer'
width='400' height='15'
type='application/x-shockwave-flash'></embed>
</object>
<p><a href='$ilink'>Download</a>";
$page->add_block(new Block("Music", $html, "main", 0));
}
}
?>

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,10 @@
Copyright (c) 2005, Fabricio Zuardi
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Binary file not shown.

119
contrib/handle_svg/main.php Normal file
View File

@ -0,0 +1,119 @@
<?php
/*
* Name: Handle SVG
* Author: Shish <webmaster@shishnet.org>
* Description: Handle SVG files
*/
class SVGFileHandler implements Extension {
var $theme;
public function receive_event(Event $event) {
if(is_null($this->theme)) $this->theme = get_theme_object($this);
if(($event instanceof DataUploadEvent) && $this->supported_ext($event->type) && $this->check_contents($event->tmpname)) {
$hash = $event->hash;
$ha = substr($hash, 0, 2);
if(!move_upload_to_archive($event)) return;
send_event(new ThumbnailGenerationEvent($event->hash, $event->type));
$image = $this->create_image_from_data(warehouse_path("images", $hash), $event->metadata);
if(is_null($image)) {
throw new UploadException("SVG handler failed to create image object from data");
}
$iae = new ImageAdditionEvent($event->user, $image);
send_event($iae);
$event->image_id = $iae->image->id;
}
if(($event instanceof ThumbnailGenerationEvent) && $this->supported_ext($event->type)) {
$hash = $event->hash;
$ha = substr($hash, 0, 2);
global $config;
// if($config->get_string("thumb_engine") == "convert") {
// $w = $config->get_int("thumb_width");
// $h = $config->get_int("thumb_height");
// $q = $config->get_int("thumb_quality");
// $mem = $config->get_int("thumb_max_memory") / 1024 / 1024; // IM takes memory in MB
//
// exec("convert images/{$ha}/{$hash}[0] -geometry {$w}x{$h} -quality {$q} jpg:thumbs/{$ha}/{$hash}");
// }
// else {
// FIXME: scale image, as not all boards use 192x192
copy("ext/handle_svg/thumb.jpg", warehouse_path("thumbs", $hash));
// }
}
if(($event instanceof DisplayingImageEvent) && $this->supported_ext($event->image->ext)) {
global $page;
$this->theme->display_image($page, $event->image);
}
if(($event instanceof PageRequestEvent) && $event->page_matches("get_svg")) {
global $config, $database, $page;
$id = int_escape($event->get_arg(0));
$image = Image::by_id($id);
$hash = $image->hash;
$page->set_type("image/svg+xml");
$page->set_mode("data");
$page->set_data(file_get_contents(warehouse_path("images", $hash)));
}
}
private function supported_ext($ext) {
$exts = array("svg");
return in_array(strtolower($ext), $exts);
}
private function create_image_from_data($filename, $metadata) {
global $config;
$image = new Image();
$msp = new MiniSVGParser($filename);
$image->width = $msp->width;
$image->height = $msp->height;
$image->filesize = $metadata['size'];
$image->hash = $metadata['hash'];
$image->filename = $metadata['filename'];
$image->ext = $metadata['extension'];
$image->tag_array = Tag::explode($metadata['tags']);
$image->source = $metadata['source'];
return $image;
}
private function check_contents($file) {
if(!file_exists($file)) return false;
$msp = new MiniSVGParser($file);
return $msp->valid;
}
}
class MiniSVGParser {
var $valid=false, $width=0, $height=0;
function MiniSVGParser($file) {
$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, array($this, "startElement"), array($this, "endElement"));
$this->valid = xml_parse($xml_parser, file_get_contents($file), true);
xml_parser_free($xml_parser);
}
function startElement($parser, $name, $attrs) {
if($name == "SVG") {
$this->width = int_escape($attrs["WIDTH"]);
$this->height = int_escape($attrs["HEIGHT"]);
}
}
function endElement($parser, $name) {
}
}
add_event_listener(new SVGFileHandler());
?>

View File

@ -0,0 +1,37 @@
<?php
class SVGHandlerTest extends ShimmieWebTestCase {
function testSVGHander() {
file_put_contents("test.svg", '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns="http://www.w3.org/2000/svg"
width="128"
height="128"
id="svg2"
version="1.0">
<g id="layer1">
<path
style="fill:#0000ff;stroke:#213847;stroke-opacity:1"
id="path2383"
d="M 120.07832,64.983688 A 55.573441,53.092484 0 1 1 8.9314423,64.983688 A 55.573441,53.092484 0 1 1 120.07832,64.983688 z" />
</g>
</svg>');
$this->log_in_as_user();
$image_id = $this->post_image("test.svg", "something");
$this->assert_response(302);
$this->log_out();
$raw = $this->get_page("get_svg/$image_id");
$this->assertTrue(strpos($raw, "www.w3.org") > 0);
$this->log_in_as_admin();
$this->delete_image($image_id);
$this->log_out();
unlink("test.svg");
# FIXME: test that the thumb works
# FIXME: test that it gets displayed properly
}
}
?>

View File

@ -0,0 +1,15 @@
<?php
class SVGFileHandlerTheme extends Themelet {
public function display_image(Page $page, Image $image) {
$ilink = make_link("get_svg/{$image->id}/{$image->id}.svg");
// $ilink = $image->get_image_link();
$html = "
<object data='$ilink' type='image/svg+xml' width='{$image->width}' height='{$image->height}'>
<embed src='$ilink' type='image/svg+xml' width='{$image->width}' height='{$image->height}' />
</object>
";
$page->add_block(new Block("Image", $html, "main", 0));
}
}
?>

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

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