Compare commits
586 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
cb1f49e7f6 | ||
|
8729263ea0 | ||
|
91bed9297e | ||
|
e1dff88920 | ||
|
12682fe229 | ||
|
8bea495cef | ||
|
b5bea64195 | ||
|
edc9c7ae40 | ||
|
746425114d | ||
|
f647e4fb38 | ||
|
047b751f84 | ||
|
32e70e22d1 | ||
|
b685c84209 | ||
|
410ae399de | ||
|
ec6d950d76 | ||
|
705f31ea4e | ||
|
9d87109a80 | ||
|
3ac30a24b9 | ||
|
6c7cfe66d6 | ||
|
e8040656e8 | ||
|
0b12bbf2a3 | ||
|
8a8fef4b38 | ||
|
0d50d68da0 | ||
|
980df44dd8 | ||
|
55f223ead1 | ||
|
21fdbdddba | ||
|
1a3fa47217 | ||
|
101cd38e1a | ||
|
f4dc0a5764 | ||
|
96fc3f7e94 | ||
|
84ba3c10ab | ||
|
06c61978d7 | ||
|
ef2e94e70a | ||
|
992316d10c | ||
|
b32f2de040 | ||
|
0ff432abf6 | ||
|
068ff1ac62 | ||
|
e577ffa785 | ||
|
9b5c65f11f | ||
|
063cdd4746 | ||
|
81ea250fdf | ||
|
ebf25505eb | ||
|
926ecf3974 | ||
|
f6863c7fb8 | ||
|
b294486b0e | ||
|
6720f639c8 | ||
|
ef073290c0 | ||
|
501a8485ce | ||
|
9e05a34ac9 | ||
|
4e48e02856 | ||
|
4eb19631b7 | ||
|
d31760a334 | ||
|
85178ff127 | ||
|
65c26e5825 | ||
|
e00e5ff0d1 | ||
|
8a0eda15eb | ||
|
b1f78d0f33 | ||
|
d2469191f9 | ||
|
62fd2952ff | ||
|
c276063389 | ||
|
dcd4b21a01 | ||
|
7943cd7682 | ||
|
d57d9ee2de | ||
|
c82350da05 | ||
|
68ce20ae8b | ||
|
918c645829 | ||
|
c555dca40f | ||
|
806352c0cb | ||
|
6fdc478b0f | ||
|
a8da2c14d6 | ||
|
c0ba0c3f3e | ||
|
aea5058d18 | ||
|
306d99d56c | ||
|
9918c1119c | ||
|
449adbaff8 | ||
|
fdd8e7cb7b | ||
|
aad087cea8 | ||
|
4457de3cda | ||
|
d521b3739c | ||
|
09d08fa0e3 | ||
|
7e0c39fe78 | ||
|
d2875b98f1 | ||
|
b9474d9aa4 | ||
|
b46d806fb6 | ||
|
393a88ca14 | ||
|
f57aa7ef5c | ||
|
40ca6316fd | ||
|
1ca3b24973 | ||
|
d8eb1a6b20 | ||
|
67a3776cef | ||
|
f33f1b4d77 | ||
|
ea1e8249b7 | ||
|
b0150b0b93 | ||
|
7b14e24475 | ||
|
81c661fe8b | ||
|
64ca3d6018 | ||
|
d5d59ce608 | ||
|
9ce1ccbe6d | ||
|
727941bc6b | ||
|
5dda40513c | ||
|
794eff56b4 | ||
|
b7ca52165b | ||
|
9e7b1b1e79 | ||
|
fd3ff15485 | ||
|
40b10a06e1 | ||
|
230d253142 | ||
|
1f9b0a8f71 | ||
|
a1ca6b8b1b | ||
|
91d423b22b | ||
|
2045d12bed | ||
|
0e2c73ec2a | ||
|
b45875619e | ||
|
06bd68e128 | ||
|
d92ee0f8b8 | ||
|
e7bf4f9a8a | ||
|
0b90d02318 | ||
|
d47b903449 | ||
|
14e5e96480 | ||
|
a4c93959f7 | ||
|
1d3d923449 | ||
|
67698b9c35 | ||
|
4d5a8c9303 | ||
|
c3cc2f6097 | ||
|
08fd957933 | ||
|
30779e0afc | ||
|
bbdfc1fa89 | ||
|
ce488eb7e9 | ||
|
cf51d26d1a | ||
|
59b25a0fe1 | ||
|
f6d0c4df1b | ||
|
6fa8442288 | ||
|
bf2c629864 | ||
|
c38a66ae8f | ||
|
f9411207d1 | ||
|
dcf2623dfe | ||
|
82145b853f | ||
|
00b0cd7a39 | ||
|
d1e1554827 | ||
|
6e1eca8aeb | ||
|
21d0eecebf | ||
|
b635a6441e | ||
|
0ba2a49730 | ||
|
9b2299cc23 | ||
|
bcb98db9fb | ||
|
c69f6c454c | ||
|
3682f84f04 | ||
|
f93b2ad65b | ||
|
db5a0c682e | ||
|
134e4f8824 | ||
|
a4b6ebbf89 | ||
|
0f85240af7 | ||
|
2f31c5995a | ||
|
51a695f185 | ||
|
2d66fafcf0 | ||
|
6f3b992e9d | ||
|
a5efff0ce9 | ||
|
0f66c9d0cb | ||
|
958efc2101 | ||
|
23b44d2860 | ||
|
d52aff3396 | ||
|
f6687fcca0 | ||
|
cd6fd1e87a | ||
|
b4584f7003 | ||
|
abebbb36b8 | ||
|
252dc45298 | ||
|
5e09a0dee4 | ||
|
c4a58d4d48 | ||
|
d4c25d0684 | ||
|
7d07ad28ef | ||
|
3f4f455ad6 | ||
|
b865e696bc | ||
|
164a0490b1 | ||
|
74000231dd | ||
|
d3234ad96f | ||
|
43871d5cff | ||
|
d20a6c17bb | ||
|
9c88160f50 | ||
|
1ac292697b | ||
|
b2edef6a83 | ||
|
37619e9776 | ||
|
9d88f44d07 | ||
|
1efb08b0fc | ||
|
e4a91ec215 | ||
|
039f721725 | ||
|
66ce235436 | ||
|
ac3d7960d6 | ||
|
ba466412e4 | ||
|
011e23b2fc | ||
|
60e6f9b3da | ||
|
8e54831873 | ||
|
91c06bd20e | ||
|
b4624e912a | ||
|
17218b0b08 | ||
|
983f8451cf | ||
|
24fcc208aa | ||
|
48a4dbd6e1 | ||
|
a02e440048 | ||
|
8de01a6184 | ||
|
cea83d65ee | ||
|
f21817b0d1 | ||
|
fe43af788f | ||
|
454ade1769 | ||
|
2be4d32869 | ||
|
70498bb07f | ||
|
9fbea9b440 | ||
|
54f44c7893 | ||
|
8ba3d43128 | ||
|
50683e929e | ||
|
411b8b3bf7 | ||
|
83e5fb847a | ||
|
3df4c712fa | ||
|
181bbcc9b7 | ||
|
c59ef1f355 | ||
|
5690ab8078 | ||
|
a74e4bb0c8 | ||
|
41e17a16ba | ||
|
dc7de96ed5 | ||
|
fbfa45fadf | ||
|
102c8b413d | ||
|
a681aae873 | ||
|
2a6978abd9 | ||
|
306a1bf093 | ||
|
0f38ac7a3c | ||
|
78b85cb769 | ||
|
41811385fa | ||
|
891fb0478e | ||
|
8a26b4e9d1 | ||
|
cd931fb036 | ||
|
f26f5b2d89 | ||
|
147075f539 | ||
|
f8765de7a9 | ||
|
0ca0a546e5 | ||
|
695a5a3a4b | ||
|
35dccaf181 | ||
|
e9b49a794f | ||
|
2f32024f71 | ||
|
dc28be215b | ||
|
93d3f471c4 | ||
|
cd150f1d6d | ||
|
43b957d98a | ||
|
e0dd4b3a3a | ||
|
d46421c538 | ||
|
8ec9ba0493 | ||
|
fbc06a1973 | ||
|
ba5f436cf3 | ||
|
cc8c8161d8 | ||
|
270cdf2da5 | ||
|
647e97ba86 | ||
|
fada3d8750 | ||
|
3c40ca8c47 | ||
|
02c9c7eced | ||
|
618f739a59 | ||
|
cab222c348 | ||
|
da4431e58d | ||
|
10f6874164 | ||
|
d638dc4e53 | ||
|
02b7333eb8 | ||
|
f720a4e56b | ||
|
a84055a5e1 | ||
|
fcf39e933b | ||
|
f496bc3a54 | ||
|
652cc475b1 | ||
|
dbbcb51e2c | ||
|
f0c18d3547 | ||
|
fa3706243c | ||
|
08320c5b3f | ||
|
8275222ae6 | ||
|
0633ad1f81 | ||
|
64eb907810 | ||
|
7c2d0473d2 | ||
|
3890fbb8ee | ||
|
3026e9b2cc | ||
|
73abe594d1 | ||
|
ec0d732a26 | ||
|
3b758e4075 | ||
|
3d8361ab71 | ||
|
97af29d7b6 | ||
|
0df9672063 | ||
|
5c2515151a | ||
|
36812d085a | ||
|
cc5a6a119a | ||
|
035f8bda6e | ||
|
eeee22863d | ||
|
ddd91196d9 | ||
|
d6280617b6 | ||
|
e7f3d015c6 | ||
|
b42aa936d6 | ||
|
c36a49edc4 | ||
|
fafe81b258 | ||
|
5813e23da9 | ||
|
11d686586d | ||
|
e0584f6e3e | ||
|
7c946db89f | ||
|
4b9f311ffd | ||
|
535bcc2a00 | ||
|
3bc10bf64f | ||
|
33dc03fb80 | ||
|
6c1a218353 | ||
|
831dc3449c | ||
|
180afca67a | ||
|
d70e00754f | ||
|
2b691408ed | ||
|
e0ab091d09 | ||
|
d91acbb03f | ||
|
0f34d28c33 | ||
|
92c8738747 | ||
|
5d88c4bff0 | ||
|
68df7b45e0 | ||
|
9fb32d9f66 | ||
|
fa404edb11 | ||
|
06f9ee1834 | ||
|
33f6695e28 | ||
|
a5df66f6bc | ||
|
e8861e4768 | ||
|
58febcd400 | ||
|
2ff8dbe54c | ||
|
c6f940664f | ||
|
d46dca7fab | ||
|
4a2297bd22 | ||
|
08bb4a7671 | ||
|
4a75b8a7a3 | ||
|
2f7565cbcb | ||
|
1087409364 | ||
|
1ccf760709 | ||
|
f4d167809b | ||
|
a424b3632d | ||
|
eeaebd6e3a | ||
|
f718193449 | ||
|
e0fd446d84 | ||
|
3b2a05ac2d | ||
|
e18c8d27db | ||
|
0d34c9b422 | ||
|
7b3cef4a9b | ||
|
49502e82c1 | ||
|
6af8fa8d72 | ||
|
74b471c30a | ||
|
3e9bf58335 | ||
|
d49e16ed9d | ||
|
efec93833a | ||
|
8a2914ca74 | ||
|
8966cc6345 | ||
|
c83f872723 | ||
|
a32c7997b4 | ||
|
c3f5ca032b | ||
|
8f17ef521c | ||
|
5f7cba6940 | ||
|
2e3967bcb1 | ||
|
223f0cc4eb | ||
|
b44f2919db | ||
|
486a3ae08e | ||
|
da46e000fb | ||
|
bd71a71020 | ||
|
e2cb8f8cb0 | ||
|
2ba033de32 | ||
|
a47bc67c50 | ||
|
016f62dda4 | ||
|
b9fabb5d8a | ||
|
fc6ed22988 | ||
|
fd619d15d7 | ||
|
4a4bc1a37a | ||
|
362891b4ed | ||
|
80e450ff0f | ||
|
9f9986970e | ||
|
283b2e974f | ||
|
ad772677a8 | ||
|
e4f987c7ab | ||
|
695352ad16 | ||
|
046fb2c33a | ||
|
ca97036735 | ||
|
fc28b7a663 | ||
|
cc07077621 | ||
|
81afbb30be | ||
|
0c9bd105cc | ||
|
45f6f89aed | ||
|
35dd7fdf11 | ||
|
0ed1db7838 | ||
|
7fcf6c8de1 | ||
|
3677bcd793 | ||
|
64d8b52b7b | ||
|
1c81ee280a | ||
|
9aa87ba8ea | ||
|
36f1fbc8bc | ||
|
9ca3e6e03f | ||
|
49030ecffd | ||
|
e694d69eb6 | ||
|
8ad176ade5 | ||
|
eacc3af131 | ||
|
40f04677d0 | ||
|
2e63812024 | ||
|
2661e26d81 | ||
|
00b66e383c | ||
|
b13ccccd69 | ||
|
ea03506471 | ||
|
cc3af2b76f | ||
|
dd5ffa26ef | ||
|
ae0498e4f0 | ||
|
612cb3768b | ||
|
e9ab06e1e9 | ||
|
88eecb5a6b | ||
|
dca588450b | ||
|
8b29aa80fe | ||
|
994fa2a9b6 | ||
|
fa30a08246 | ||
|
f83b8b473e | ||
|
bdd0299cf0 | ||
|
bc706fde05 | ||
|
374a85e545 | ||
|
0528d0a765 | ||
|
be3e582de6 | ||
|
8e00495f7e | ||
|
c2c7e245d8 | ||
|
047166a92c | ||
|
b1407028b1 | ||
|
2eeb0b1caf | ||
|
b2c2368cfc | ||
|
6322589265 | ||
|
83d92c6413 | ||
|
80cb9d0083 | ||
|
632bf99261 | ||
|
33fbc52862 | ||
|
f12c963ee0 | ||
|
5a8bcad72c | ||
|
f44634b67d | ||
|
8f7041f1d7 | ||
|
1b63cffd2d | ||
|
50acd8c6ad | ||
|
c6636265cb | ||
|
c525be6b31 | ||
|
02ea593091 | ||
|
98fa324786 | ||
|
613be41240 | ||
|
008709af26 | ||
|
c2a56728d6 | ||
|
aaa379930b | ||
|
0d2c597010 | ||
|
efe4852dc4 | ||
|
9575a06c68 | ||
|
85e65075e6 | ||
|
57d8a8bfd6 | ||
|
0e4eac909c | ||
|
9913937a25 | ||
|
476d2a33c7 | ||
|
2758b13601 | ||
|
b1dbb3a792 | ||
|
4cc77668e6 | ||
|
0563d3fb1f | ||
|
450102233a | ||
|
353970a902 | ||
|
e29db93834 | ||
|
7336ff2b22 | ||
|
f8feecf5e2 | ||
|
8fd6d44958 | ||
|
a69bf05e36 | ||
|
fdd1ffaf0f | ||
|
66ef82e460 | ||
|
56072075f7 | ||
|
adffa0f18f | ||
|
8680f27bf1 | ||
|
1052b5d356 | ||
|
8a64e171b7 | ||
|
cc9cefe41e | ||
|
2c5a688335 | ||
|
fc7fd8d1f7 | ||
|
fd85f390e5 | ||
|
2b4733bf06 | ||
|
ce76dfbb2b | ||
|
58f2ba539a | ||
|
88c7233ced | ||
|
cff145d72b | ||
|
6f2711f422 | ||
|
67f3ce0543 | ||
|
f712147553 | ||
|
c31342a1c7 | ||
|
f27fa1f314 | ||
|
05449ef8d3 | ||
|
2cd3b0812b | ||
|
8b0d1dc3db | ||
|
a348e3df29 | ||
|
9a1de36a23 | ||
|
281638c295 | ||
|
487a59b0db | ||
|
bec162293e | ||
|
3ebf861441 | ||
|
d9ec5056d4 | ||
|
16fe1424dd | ||
|
96ce9c70a7 | ||
|
cdeacb9765 | ||
|
96352152b2 | ||
|
eda3b0aa61 | ||
|
2da1cd8c9d | ||
|
9c6d0dc394 | ||
|
8cd6dbc2a0 | ||
|
af8c59fcd1 | ||
|
4148106ff0 | ||
|
9724b13f81 | ||
|
2082334c24 | ||
|
8230a36565 | ||
|
e81c3e288d | ||
|
c9f743bf8f | ||
|
c917bf0434 | ||
|
cb56587970 | ||
|
7a3e032096 | ||
|
5922fb6f25 | ||
|
bf2fdffc75 | ||
|
75d4c6cf73 | ||
|
d08b7faf63 | ||
|
d7ce9add6a | ||
|
9d968b630d | ||
|
4f14197577 | ||
|
179e18abb2 | ||
|
9483cc59f3 | ||
|
ead9e27dea | ||
|
d2cad188b4 | ||
|
f59e39d42b | ||
|
819fb722c3 | ||
|
412b71c2d3 | ||
|
4ac62e5544 | ||
|
6b88e5fd5a | ||
|
5c1093bcf0 | ||
|
fe09b18832 | ||
|
ac6812d184 | ||
|
b9d80f8d38 | ||
|
8db522b916 | ||
|
ae85d76f15 | ||
|
2ad2689d28 | ||
|
20ed4f1503 | ||
|
99534afe08 | ||
|
4fbd714930 | ||
|
c953d5bd6c | ||
|
9a830edbb7 | ||
|
784296d22c | ||
|
2644eed5bc | ||
|
ac0dad4093 | ||
|
8af3c4f24e | ||
|
f8ec3baeea | ||
|
ab1d755a01 | ||
|
351c3469dc | ||
|
7d827d3392 | ||
|
ef5d7474c5 | ||
|
ca675fcbb0 | ||
|
d240a29aa9 | ||
|
b6a4911b67 | ||
|
91829cc2ea | ||
|
43c98e51c2 | ||
|
5c88b84378 | ||
|
3da11198af | ||
|
18f9a72851 | ||
|
de7f1f9cfe | ||
|
06cfda990d | ||
|
dd60840e4b | ||
|
5ab092946a | ||
|
e29d475dea | ||
|
0ac787c3e3 | ||
|
aeba6665f0 | ||
|
74a0bc642e | ||
|
2bdfec3e76 | ||
|
7fcaf851f1 | ||
|
bc23362bda | ||
|
74655bac27 | ||
|
5384e11735 | ||
|
509ea8614c | ||
|
a1cc9d1953 | ||
|
70173468a5 | ||
|
7229b2669a | ||
|
482e22c90e | ||
|
40b2a4cb05 | ||
|
52cc6c95db | ||
|
d525fc6894 | ||
|
4bdd544ff1 | ||
|
47bc2570b6 | ||
|
09b1bb3554 | ||
|
8512f45c9d | ||
|
8b207b84fe | ||
|
25e77bb7c1 | ||
|
d15e8d231f | ||
|
dfc7411975 | ||
|
a543f75c99 | ||
|
bb38904cdf | ||
|
fbd7039ad3 | ||
|
963535b748 | ||
|
b49bc84c51 | ||
|
6d8ac0b4e2 | ||
|
275e0b3b64 | ||
|
cca47a1df9 | ||
|
26e1383b0a | ||
|
199a0d709c |
17
.gitignore
vendored
17
.gitignore
vendored
@ -2,9 +2,13 @@
|
||||
config.php
|
||||
images
|
||||
thumbs
|
||||
data
|
||||
sql.log
|
||||
shimmie.log
|
||||
ext/admin
|
||||
ext/autocomplete
|
||||
ext/ban_words
|
||||
ext/blotter
|
||||
ext/browser_search
|
||||
ext/bulk_add
|
||||
ext/danbooru_api
|
||||
@ -12,7 +16,9 @@ ext/downtime
|
||||
ext/emoticons
|
||||
ext/et
|
||||
ext/event_log
|
||||
ext/favorites
|
||||
ext/featured
|
||||
ext/forum
|
||||
ext/handle_archive
|
||||
ext/handle_flash
|
||||
ext/handle_ico
|
||||
@ -22,11 +28,15 @@ 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
|
||||
@ -34,12 +44,17 @@ 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/tag_history
|
||||
ext/tagger
|
||||
ext/tag_history
|
||||
ext/text_score
|
||||
ext/tips
|
||||
ext/amazon_s3
|
||||
ext/upload_cmd
|
||||
ext/wiki
|
||||
ext/word_filter
|
||||
ext/zoom
|
||||
|
34
.htaccess
34
.htaccess
@ -1,23 +1,23 @@
|
||||
<IfModule mod_dir.c>
|
||||
DirectoryIndex index.php5 index.php
|
||||
DirectoryIndex index.php5 index.php
|
||||
</IfModule>
|
||||
|
||||
<FilesMatch "\.(sqlite|sdb|s3db|db)$">
|
||||
Deny from all
|
||||
Deny from all
|
||||
</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/([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]
|
||||
# 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/([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]
|
||||
# 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>
|
||||
@ -27,3 +27,15 @@ RewriteRule ^(.*)$ index.php?q=$1&%{QUERY_STRING} [L]
|
||||
</IfModule>
|
||||
|
||||
DefaultType image/jpeg
|
||||
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresDefault "access plus 1 month"
|
||||
ExpiresByType text/html "now"
|
||||
ExpiresByType text/plain "now"
|
||||
</IfModule>
|
||||
|
||||
<ifmodule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
|
||||
AddOutputFilterByType DEFLATE application/x-javascript application/javascript
|
||||
</ifmodule>
|
||||
|
79
README.txt
79
README.txt
@ -6,14 +6,52 @@
|
||||
/_______ /|___| /__|__|_| /__|_| /__|\___ >_______ \
|
||||
\/ \/ \/ \/ \/ \/
|
||||
|
||||
Shimmie Alpha
|
||||
~~~~~~~~~~~~~
|
||||
This code is for people who want to write extensions compatible with the
|
||||
next version of shimmie. You can run a production site with it if you're
|
||||
feeling brave, but it's not recommended.
|
||||
Shimmie 2.3
|
||||
~~~~~~~~~~~
|
||||
1000 days since the first import was converted to git \o/
|
||||
|
||||
If there is a feature here, and not in the stable branch, that's probably
|
||||
because the feature doesn't work yet :P
|
||||
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
|
||||
@ -41,17 +79,32 @@ Installation
|
||||
|
||||
Upgrade from 2.2.X
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Should be automatic, just unzip and copy across config.php, images and thumbs
|
||||
folders from the old version. This includes automatically messing with the
|
||||
database -- back it up first!
|
||||
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
|
||||
~~~~~~~
|
||||
http://forum.shishnet.org/viewforum.php?f=6 -- discussion forum
|
||||
http://trac.shishnet.org/shimmie2/ -- bug tracker
|
||||
webmaster at shishnet.org -- email
|
||||
#shimmie on Freenode -- IRC
|
||||
webmaster at shishnet.org -- email
|
||||
https://github.com/shish/shimmie2/issues -- bug tracker
|
||||
|
||||
|
||||
Licence
|
||||
|
@ -1,6 +1,31 @@
|
||||
<?php
|
||||
/* AdminBuildingEvent {{{
|
||||
*
|
||||
/**
|
||||
* 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 {
|
||||
@ -9,7 +34,6 @@ class AdminBuildingEvent extends Event {
|
||||
$this->page = $page;
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
|
||||
class AdminPage implements Extension {
|
||||
var $theme;
|
||||
@ -23,30 +47,21 @@ class AdminPage implements Extension {
|
||||
$this->theme->display_permission_denied($page);
|
||||
}
|
||||
else {
|
||||
if($event->get_arg(0) == "delete_image") {
|
||||
// FIXME: missing lots of else {complain}
|
||||
if(isset($_POST['image_id'])) {
|
||||
$image = Image::by_id($_POST['image_id']);
|
||||
if($image) {
|
||||
send_event(new ImageDeletionEvent($image));
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("post/list"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
send_event(new AdminBuildingEvent($page));
|
||||
}
|
||||
send_event(new AdminBuildingEvent($page));
|
||||
}
|
||||
}
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("admin_utils")) {
|
||||
if($user->is_admin()) {
|
||||
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;
|
||||
@ -59,6 +74,10 @@ class AdminPage implements Extension {
|
||||
$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;
|
||||
@ -71,12 +90,6 @@ class AdminPage implements Extension {
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof ImageAdminBlockBuildingEvent) {
|
||||
if($user->is_admin()) {
|
||||
$event->add_part($this->theme->get_deleter_html($event->image->id));
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof AdminBuildingEvent) {
|
||||
$this->theme->display_page($page);
|
||||
$this->theme->display_form($page);
|
||||
@ -89,6 +102,14 @@ class AdminPage implements Extension {
|
||||
}
|
||||
}
|
||||
|
||||
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)");
|
||||
@ -96,7 +117,12 @@ class AdminPage implements Extension {
|
||||
|
||||
private function recount_tag_use() {
|
||||
global $database;
|
||||
$database->Execute("UPDATE tags SET count=(SELECT COUNT(image_id) FROM image_tags WHERE tag_id=tags.id GROUP BY tag_id)");
|
||||
$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() {
|
||||
@ -139,6 +165,17 @@ class AdminPage implements Extension {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
75
contrib/admin/test.php
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
@ -10,22 +10,6 @@ class AdminPageTheme extends Themelet {
|
||||
$page->add_block(new NavBlock());
|
||||
}
|
||||
|
||||
/*
|
||||
* Display a link to delete an image
|
||||
*
|
||||
* $image_id = the image to delete
|
||||
*/
|
||||
public function get_deleter_html($image_id) {
|
||||
$i_image_id = int_escape($image_id);
|
||||
$html = "
|
||||
<form action='".make_link("admin/delete_image")."' method='POST'>
|
||||
<input type='hidden' name='image_id' value='$i_image_id'>
|
||||
<input type='submit' value='Delete'>
|
||||
</form>
|
||||
";
|
||||
return $html;
|
||||
}
|
||||
|
||||
/*
|
||||
* Show a form which links to admin_utils with POST[action] set to one of:
|
||||
* 'lowercase all tags'
|
||||
@ -33,18 +17,30 @@ class AdminPageTheme extends Themelet {
|
||||
* 'purge unused tags'
|
||||
*/
|
||||
public function display_form(Page $page) {
|
||||
global $user;
|
||||
|
||||
$html = "
|
||||
<p><form action='".make_link("admin_utils")."' method='POST'>
|
||||
".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));
|
||||
}
|
||||
}
|
||||
?>
|
75
contrib/amazon_s3/main.php
Normal file
75
contrib/amazon_s3/main.php
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Autocomplete
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Auto-complete for search and upload tags
|
||||
* Documentation:
|
||||
* Just enable and things should start autocompleting as if
|
||||
* by magic. That is, if this extension actually worked...
|
||||
*/
|
||||
|
||||
class AutoComplete implements Extension {
|
||||
public function receive_event(Event $event) {
|
||||
if(($event instanceof PageRequestEvent) && ($event->page_matches("index") || $event->page_matches("view"))) {
|
||||
$event->page->add_header("<script>autocomplete_url='".html_escape(make_link("autocomplete"))."';</script>");
|
||||
}
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("autocomplete")) {
|
||||
$event->page->set_mode("data");
|
||||
$event->page->set_type("text/plain");
|
||||
$event->page->set_data($this->get_completions($event->get_arg(0)));
|
||||
}
|
||||
}
|
||||
|
||||
private function get_completions($start) {
|
||||
global $database;
|
||||
$tags = $database->db->GetCol("SELECT tag,count FROM tags WHERE tag LIKE ? ORDER BY count DESC", array($start.'%'));
|
||||
return implode("\n", $tags);
|
||||
}
|
||||
}
|
||||
add_event_listener(new AutoComplete());
|
||||
?>
|
@ -1,98 +0,0 @@
|
||||
|
||||
// addEvent(window, "load", function() {
|
||||
// initAjax("searchBox", "search_completions");
|
||||
// initAjax("tagBox", "upload_completions");
|
||||
// });
|
||||
|
||||
function endWord(sentance) {
|
||||
words = sentance.split(" ");
|
||||
return words[words.length-1];
|
||||
}
|
||||
|
||||
var resultCache = new Array();
|
||||
resultCache[""] = new Array();
|
||||
|
||||
function complete(boxname, text) {
|
||||
box = byId(boxname);
|
||||
words = box.value.split(" ");
|
||||
box.value = "";
|
||||
for(n=0; n<words.length-1; n++) {
|
||||
box.value += words[n]+" ";
|
||||
}
|
||||
box.value += text+" ";
|
||||
box.focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
function fillCompletionZone(boxname, areaname, results) {
|
||||
byId(areaname).innerHTML = "";
|
||||
for(i=0; i<results.length; i++) {
|
||||
byId(areaname).innerHTML += "<br><a href=\"#\" onclick=\"complete('"+boxname+"', '"+results[i]+"');\">"+results[i]+"</a>";
|
||||
}
|
||||
}
|
||||
|
||||
function initAjax(boxname, areaname) {
|
||||
var box = byId(boxname);
|
||||
if(!box) return;
|
||||
|
||||
addEvent(
|
||||
box,
|
||||
"keyup",
|
||||
function f() {
|
||||
starter = endWord(box.value);
|
||||
|
||||
if(resultCache[starter]) {
|
||||
fillCompletionZone(boxname, areaname, resultCache[starter]);
|
||||
}
|
||||
else {
|
||||
ajaxRequest(
|
||||
"ajax.php?start="+starter,
|
||||
function g(text) {
|
||||
resultCache[starter] = text.split("\n");
|
||||
fillCompletionZone(boxname, areaname, resultCache[starter]);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
//completion_cache = new array();
|
||||
|
||||
input = byId("search_input");
|
||||
output = byId("search_completions");
|
||||
|
||||
function get_cached_completions(start) {
|
||||
// if(completion_cache[start]) {
|
||||
// return completion_cache[start];
|
||||
// }
|
||||
// else {
|
||||
return null;
|
||||
// }
|
||||
}
|
||||
function get_completions(start) {
|
||||
cached = get_cached_completions(start);
|
||||
if(cached) {
|
||||
output.innerHTML = cached;
|
||||
}
|
||||
else {
|
||||
ajaxRequest(autocomplete_url+"/"+start, function(data) {set_completions(start, data);});
|
||||
}
|
||||
}
|
||||
function set_completions(start, data) {
|
||||
// completion_cache[start] = data;
|
||||
output.innerHTML = data;
|
||||
}
|
||||
|
||||
if(input) {
|
||||
input.onkeyup = function() {
|
||||
if(input.value.length < 3) {
|
||||
output.innerHTML = "";
|
||||
}
|
||||
else {
|
||||
get_completions(input.value);
|
||||
}
|
||||
};
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: Comment Word Ban
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
@ -19,49 +19,72 @@
|
||||
* from Essex"
|
||||
*/
|
||||
|
||||
class BanWords implements Extension {
|
||||
public function receive_event(Event $event) {
|
||||
if($event instanceof InitExtEvent) {
|
||||
global $config;
|
||||
$config->set_default_string('banned_words', "
|
||||
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
|
||||
porn
|
||||
");
|
||||
}
|
||||
xanax
|
||||
");
|
||||
}
|
||||
|
||||
if($event instanceof CommentPostingEvent) {
|
||||
global $config;
|
||||
$banned = $config->get_string("banned_words");
|
||||
$comment = strtolower($event->comment);
|
||||
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;
|
||||
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 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");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// other words are literal
|
||||
if(strpos($comment, $word) !== false) {
|
||||
throw new CommentPostingException("Comment contains banned terms");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof SetupBuildingEvent) {
|
||||
$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 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;}
|
||||
}
|
||||
add_event_listener(new BanWords(), 30); // before the comment is added
|
||||
?>
|
||||
|
45
contrib/ban_words/test.php
Normal file
45
contrib/ban_words/test.php
Normal 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
130
contrib/blotter/main.php
Normal 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=?", array($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 ?", array($limit));
|
||||
$this->theme->display_blotter($entries);
|
||||
}
|
||||
}
|
||||
?>
|
37
contrib/blotter/test.php
Normal file
37
contrib/blotter/test.php
Normal 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
189
contrib/blotter/theme.php
Normal 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;
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,68 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Bookmarks
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Allow users to bookmark searches
|
||||
*/
|
||||
|
||||
class Bookmarks 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("bookmark")) {
|
||||
if($event->get_arg(0) == "add") {
|
||||
if(isset($_POST['url'])) {
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("user"));
|
||||
}
|
||||
}
|
||||
else if($event->get_arg(0) == "remove") {
|
||||
if(isset($_POST['id'])) {
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("user"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function install() {
|
||||
global $database;
|
||||
global $config;
|
||||
|
||||
// shortcut to latest
|
||||
if($config->get_int("ext_bookmarks_version") < 1) {
|
||||
$database->create_table("bookmark", "
|
||||
id SCORE_AIPK,
|
||||
owner_id INTEGER NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
title TET NOT NULL,
|
||||
INDEX (owner_id),
|
||||
FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
");
|
||||
$config->set_int("ext_bookmarks_version", 1);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_bookmarks() {
|
||||
global $database;
|
||||
$bms = $database->get_all("
|
||||
SELECT *
|
||||
FROM bookmark
|
||||
WHERE bookmark.owner_id = ?
|
||||
");
|
||||
if($bms) {return $bms;}
|
||||
else {return array();}
|
||||
}
|
||||
|
||||
private function add_bookmark($url, $title) {
|
||||
global $database;
|
||||
$sql = "INSERT INTO bookmark(owner_id, url, title) VALUES (?, ?, ?)";
|
||||
$database->Execute($sql, array($user->id, $url, $title));
|
||||
}
|
||||
}
|
||||
add_event_listener(new Bookmarks());
|
||||
?>
|
@ -1,5 +0,0 @@
|
||||
<?php
|
||||
|
||||
class BookmarksTheme extends Themelet {
|
||||
}
|
||||
?>
|
@ -1,14 +1,16 @@
|
||||
<?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
|
||||
*
|
||||
* 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 {
|
||||
@ -37,6 +39,7 @@ class BrowserSearch implements Extension {
|
||||
//$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
|
||||
@ -44,8 +47,7 @@ class BrowserSearch implements Extension {
|
||||
<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,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFhYAfX19AH9/fwCAgIAAgYGBAIODgwCEhIQAhoaGAIeHhwCJiYkAioqKAIyMjACPj48AkJCQAJKSkgCTk5MAlZWVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEREREREREREREREREREREREAAAAAAAAAAAAAABERERERAAEBAQEBAQEBAQAAEREREQABAQEBAQEBAQEAAQAREREAAQEBAQEBAQEBAAEBABERAAEBAQEBAQEBAQABAQAREQAGBgUEAwIBAQEAAgEAEREADAwMCwsKCQkHAAkIABERABAQEBAQEA4ODAANDQAREQAQEBAQEBAQEBAAEBAAEREAEBAQEBAQEBAQABAQABERAAAAAAAAAAAAAAAQEAAREREAEBAPEBAQEBAQABAAEREREQAQEBAQEBAPEBAAABERERERAAAAAAAAAAAAAAAREREREREREREREREREREREf//AACADwAAgAcAAIADAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAMABAADgAQAA8AEAAP//AAA=</os:Image>
|
||||
<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}'/>
|
||||
@ -104,7 +106,7 @@ class BrowserSearch implements Extension {
|
||||
$sort_by['Tag Count'] = 't';
|
||||
|
||||
$sb = new SetupBlock("Browser Search");
|
||||
$sb->add_bool_option("disable_search_suggestions", "Disable search suggestions when using browser-based 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);
|
||||
|
8
contrib/browser_search/test.php
Normal file
8
contrib/browser_search/test.php
Normal 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");
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: Bulk Add
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
@ -11,44 +11,42 @@
|
||||
* 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 implements Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event(Event $event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object($this);
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("bulk_add")) {
|
||||
if($event->user->is_admin() && isset($_POST['dir'])) {
|
||||
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($event->page);
|
||||
$this->theme->display_upload_results($page);
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof AdminBuildingEvent) {
|
||||
global $page;
|
||||
$this->theme->display_admin_block($page);
|
||||
}
|
||||
}
|
||||
|
||||
public function onAdminBuilding($event) {
|
||||
$this->theme->display_admin_block();
|
||||
}
|
||||
|
||||
|
||||
private function add_image($tmpname, $filename, $tags) {
|
||||
if(file_exists($tmpname)) {
|
||||
global $user;
|
||||
$pathinfo = pathinfo($filename);
|
||||
$metadata['filename'] = $pathinfo['basename'];
|
||||
$metadata['extension'] = $pathinfo['extension'];
|
||||
$metadata['tags'] = $tags;
|
||||
$metadata['source'] = null;
|
||||
try {
|
||||
$event = new DataUploadEvent($user, $tmpname, $metadata);
|
||||
send_event($event);
|
||||
}
|
||||
catch(Exception $ex) {
|
||||
return $ex->getMessage();
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,40 +60,36 @@ class BulkAdd implements Extension {
|
||||
|
||||
$list = "";
|
||||
|
||||
$dir = opendir("$base/$subdir");
|
||||
while($filename = readdir($dir)) {
|
||||
$fullpath = "$base/$subdir/$filename";
|
||||
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)) {
|
||||
if($filename[0] != ".") {
|
||||
$this->add_dir($base, "$subdir/$filename");
|
||||
}
|
||||
$this->add_dir($base, str_replace($base, "", $fullpath));
|
||||
}
|
||||
else {
|
||||
$tmpfile = $fullpath;
|
||||
$pathinfo = pathinfo($fullpath);
|
||||
$tags = $subdir;
|
||||
$tags = str_replace("/", " ", $tags);
|
||||
$tags = str_replace("__", " ", $tags);
|
||||
$tags = trim($tags);
|
||||
$list .= "<br>".html_escape("$subdir/$filename (".str_replace(" ", ", ", $tags).")... ");
|
||||
$error = $this->add_image($tmpfile, $filename, $tags);
|
||||
if(is_null($error)) {
|
||||
$list .= "<br>".html_escape("$shortpath (".str_replace(" ", ", ", $tags).")... ");
|
||||
try{
|
||||
$this->add_image($fullpath, $pathinfo["basename"], $tags);
|
||||
$list .= "ok\n";
|
||||
}
|
||||
else {
|
||||
$list .= "failed:<br>$error\n";
|
||||
catch(Exception $ex) {
|
||||
$list .= "failed:<br>". $ex->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
|
||||
if(strlen($list) > 0) {
|
||||
$this->theme->add_status("Adding $subdir", $list);
|
||||
}
|
||||
}
|
||||
}
|
||||
add_event_listener(new BulkAdd());
|
||||
?>
|
||||
|
30
contrib/bulk_add/test.php
Normal file
30
contrib/bulk_add/test.php
Normal 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();
|
||||
}
|
||||
}
|
||||
?>
|
@ -20,14 +20,15 @@ class BulkAddTheme extends Themelet {
|
||||
* links to bulk_add with POST[dir] set to the name of a server-side
|
||||
* directory full of images
|
||||
*/
|
||||
public function display_admin_block(Page $page) {
|
||||
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><form action='".make_link("bulk_add")."' method='POST'>
|
||||
<p>".make_form(make_link("bulk_add"))."
|
||||
Directory to add: <input type='text' name='dir' size='40'>
|
||||
<input type='submit' value='Add'>
|
||||
</form>
|
||||
|
@ -1,17 +1,20 @@
|
||||
<?php
|
||||
/*
|
||||
Name: Danbooru Client API
|
||||
Description: Allow Danbooru apps like Danbooru Uploader for Firefox to communicate with Shimmie
|
||||
|
||||
Author: JJS <jsutinen@gmail.com>
|
||||
Notes:
|
||||
danbooru API based on documentation from danbooru 1.0 - http://attachr.com/7569
|
||||
I've only been able to test add_post and find_tags because I use the old danbooru firefox extension for firefox 1.5
|
||||
|
||||
Functions currently implemented:
|
||||
add_post - title and rating are currently ignored because shimmie does not support them
|
||||
find_posts - sort of works, filename is returned as the original filename and probably won't help when it comes to actually downloading it
|
||||
find_tags - id, name, and after_id all work but the tags parameter is ignored just like danbooru 1.0 ignores it
|
||||
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
|
||||
@ -61,7 +64,8 @@ class DanbooruApi implements Extension
|
||||
if(preg_match("/^md5:([0-9a-fA-F]*)$/i", $event->term, $matches))
|
||||
{
|
||||
$hash = strtolower($matches[1]);
|
||||
$event->set_querylet(new Querylet("images.hash = '$hash'"));
|
||||
$event->add_querylet(new Querylet("images.hash = '$hash'")); // :-O
|
||||
// $event->set_querylet(new Querylet("images.hash = '$hash'"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -80,6 +84,8 @@ class DanbooruApi implements Extension
|
||||
|
||||
$results = array();
|
||||
|
||||
$danboorup_kludge=1; // danboorup for firefox makes broken links out of location: /path
|
||||
|
||||
/*
|
||||
add_post()
|
||||
Adds a post to the database.
|
||||
@ -113,6 +119,7 @@ class DanbooruApi implements Extension
|
||||
$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;
|
||||
@ -213,7 +220,9 @@ class DanbooruApi implements Extension
|
||||
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
|
||||
@ -222,13 +231,18 @@ class DanbooruApi implements Extension
|
||||
$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);
|
||||
$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')
|
||||
{
|
||||
@ -240,7 +254,7 @@ class DanbooruApi implements Extension
|
||||
catch(UploadException $ex) {
|
||||
// Did something screw up?
|
||||
header("HTTP/1.0 409 Conflict");
|
||||
header("X-Danbooru-Errors: ". $ex->getMessage());
|
||||
header("X-Danbooru-Errors: exception - " . $ex->getMessage());
|
||||
return;
|
||||
}
|
||||
} else
|
||||
@ -264,6 +278,7 @@ class DanbooruApi implements Extension
|
||||
*/
|
||||
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']);
|
||||
|
@ -1,9 +1,14 @@
|
||||
<?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 {
|
||||
@ -22,22 +27,16 @@ class Downtime implements Extension {
|
||||
|
||||
if($event instanceof PageRequestEvent) {
|
||||
if($config->get_bool("downtime")) {
|
||||
$this->check_downtime($event);
|
||||
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 check_downtime(PageRequestEvent $event) {
|
||||
global $user, $config;
|
||||
|
||||
if($config->get_bool("downtime") && !$user->is_admin() &&
|
||||
($event instanceof PageRequestEvent) && !$this->is_safe_page($event)) {
|
||||
$msg = $config->get_string("downtime_message");
|
||||
$this->theme->display_message($msg);
|
||||
}
|
||||
}
|
||||
|
||||
private function is_safe_page(PageRequestEvent $event) {
|
||||
if($event->page_matches("user_admin/login")) return true;
|
||||
else return false;
|
||||
|
23
contrib/downtime/test.php
Normal file
23
contrib/downtime/test.php
Normal 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();
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
class DowntimeTheme Extends Themelet {
|
||||
/*
|
||||
class DowntimeTheme extends Themelet {
|
||||
/**
|
||||
* Show the admin that downtime mode is enabled
|
||||
*/
|
||||
public function display_notification(Page $page) {
|
||||
@ -9,22 +9,50 @@ class DowntimeTheme Extends Themelet {
|
||||
"<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>
|
||||
$message
|
||||
<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;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: Emoticon Filter
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
@ -24,4 +24,12 @@ class Emoticons extends FormatterExtension {
|
||||
}
|
||||
}
|
||||
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/*"));
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
22
contrib/emoticons/test.php
Normal file
22
contrib/emoticons/test.php
Normal 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();
|
||||
}
|
||||
}
|
||||
?>
|
21
contrib/emoticons/theme.php
Normal file
21
contrib/emoticons/theme.php
Normal 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);
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: System Info
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
@ -39,13 +39,14 @@ class ET implements Extension {
|
||||
$info = array();
|
||||
$info['site_title'] = $config->get_string("title");
|
||||
$info['site_theme'] = $config->get_string("theme");
|
||||
$info['site_genre'] = "[please write something here]";
|
||||
$info['site_url'] = isset($_SERVER['SCRIPT_URI']) ? dirname($_SERVER['SCRIPT_URI']) : "???";
|
||||
$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);
|
||||
|
10
contrib/et/test.php
Normal file
10
contrib/et/test.php
Normal 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();
|
||||
}
|
||||
}
|
||||
?>
|
@ -16,10 +16,9 @@ class ETTheme extends Themelet {
|
||||
protected function build_data_form($info) {
|
||||
$data = <<<EOD
|
||||
Optional:
|
||||
Add this site to the public shimmie users list: No
|
||||
Site title: {$info['site_title']}
|
||||
Theme: {$info['site_theme']}
|
||||
Genre: {$info['site_genre']}
|
||||
Genre: [describe your site here]
|
||||
URL: {$info['site_url']}
|
||||
|
||||
System stats:
|
||||
@ -29,7 +28,7 @@ PHP: {$info['sys_php']}
|
||||
OS: {$info['sys_os']}
|
||||
Server: {$info['sys_server']}
|
||||
Database: {$info['sys_db']}
|
||||
Extensions: {$info['sys_extensions']}
|
||||
Disk use: {$info['sys_disk']}
|
||||
|
||||
Shimmie stats:
|
||||
Images: {$info['stat_images']}
|
||||
@ -37,6 +36,7 @@ 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'>
|
||||
|
186
contrib/favorites/main.php
Normal file
186
contrib/favorites/main.php
Normal 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();
|
||||
}
|
||||
}
|
||||
?>
|
33
contrib/favorites/test.php
Normal file
33
contrib/favorites/test.php
Normal 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();
|
||||
}
|
||||
}
|
||||
?>
|
36
contrib/favorites/theme.php
Normal file
36
contrib/favorites/theme.php
Normal 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));
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: Featured Image
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
@ -9,57 +9,76 @@
|
||||
* 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 implements Extension {
|
||||
var $theme;
|
||||
class Featured extends SimpleExtension {
|
||||
public function onInitExt($event) {
|
||||
global $config;
|
||||
$config->set_default_int('featured_id', 0);
|
||||
}
|
||||
|
||||
public function receive_event(Event $event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object($this);
|
||||
|
||||
if($event instanceof InitExtEvent) {
|
||||
global $config;
|
||||
$config->set_default_int('featured_id', 0);
|
||||
}
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("set_feature")) {
|
||||
global $user;
|
||||
if($user->is_admin() && isset($_POST['image_id'])) {
|
||||
global $config;
|
||||
$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"));
|
||||
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 instanceof PostListBuildingEvent) {
|
||||
global $config, $page;
|
||||
$fid = $config->get_int("featured_id");
|
||||
if($fid > 0) {
|
||||
$image = Image::by_id($fid);
|
||||
if($event->get_arg(0) == "download") {
|
||||
$image = Image::by_id($config->get_int("featured_id"));
|
||||
if(!is_null($image)) {
|
||||
$this->theme->display_featured($page, $image);
|
||||
$page->set_mode("data");
|
||||
$page->set_type("image/jpeg");
|
||||
$page->set_data(file_get_contents($image->get_image_filename()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if(($event instanceof SetupBuildingEvent)) {
|
||||
$sb = new SetupBlock("Featured Image");
|
||||
$sb->add_int_option("featured_id", "Image ID: ");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
*/
|
||||
|
||||
if($event instanceof ImageAdminBlockBuildingEvent) {
|
||||
if($event->user->is_admin()) {
|
||||
$event->add_part($this->theme->get_buttons_html($event->image->id));
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
add_event_listener(new Featured());
|
||||
?>
|
||||
|
26
contrib/featured/test.php
Normal file
26
contrib/featured/test.php
Normal 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
|
||||
}
|
||||
}
|
||||
?>
|
@ -9,8 +9,10 @@ class FeaturedTheme extends Themelet {
|
||||
}
|
||||
|
||||
public function get_buttons_html($image_id) {
|
||||
global $user;
|
||||
return "
|
||||
<form action='".make_link("set_feature")."' method='POST'>
|
||||
".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>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Archive File Handler
|
||||
/*
|
||||
* Name: Handle Archives
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Description: Allow users to upload archives (zip, etc)
|
||||
* Documentation:
|
||||
@ -10,55 +10,59 @@
|
||||
* <br>7-zip: <code>7zr x -o"%d" "%f"</code>
|
||||
*/
|
||||
|
||||
class ArchiveFileHandler implements Extension {
|
||||
public function receive_event(Event $event) {
|
||||
if($event instanceof InitExtEvent) {
|
||||
global $config;
|
||||
$config->set_default_string('archive_extract_command', 'unzip -d "%d" "%f"');
|
||||
}
|
||||
class ArchiveFileHandler extends SimpleExtension {
|
||||
public function onInitExt($event) {
|
||||
global $config;
|
||||
$config->set_default_string('archive_extract_command', 'unzip -d "%d" "%f"');
|
||||
}
|
||||
|
||||
if($event instanceof SetupBuildingEvent) {
|
||||
$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 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);
|
||||
}
|
||||
|
||||
if(($event instanceof DataUploadEvent) && $this->supported_ext($event->type)) {
|
||||
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->tmpfile);
|
||||
$cmd = str_replace('%d', $tmpdir);
|
||||
system($cmd);
|
||||
$cmd = str_replace('%f', $event->tmpname, $cmd);
|
||||
$cmd = str_replace('%d', $tmpdir, $cmd);
|
||||
exec($cmd);
|
||||
$this->add_dir($tmpdir);
|
||||
unlink($tmpdir);
|
||||
deltree($tmpdir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function supported_ext($ext) {
|
||||
$exts = array("zip");
|
||||
return array_contains($exts, strtolower($ext));
|
||||
return in_array(strtolower($ext), $exts);
|
||||
}
|
||||
|
||||
// copied from bulk add extension
|
||||
private function add_image($tmpname, $filename, $tags) {
|
||||
if(file_exists($tmpname)) {
|
||||
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;
|
||||
try {
|
||||
$event = new DataUploadEvent($user, $tmpname, $metadata);
|
||||
send_event($event);
|
||||
}
|
||||
catch(UploadException $ex) {
|
||||
return $ex->getMessage();
|
||||
}
|
||||
$event = new DataUploadEvent($user, $tmpname, $metadata);
|
||||
send_event($event);
|
||||
}
|
||||
catch(UploadException $ex) {
|
||||
return $ex->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,5 +104,4 @@ class ArchiveFileHandler implements Extension {
|
||||
// $this->theme->add_status("Adding $subdir", $list);
|
||||
}
|
||||
}
|
||||
add_event_listener(new ArchiveFileHandler());
|
||||
?>
|
||||
|
@ -1,48 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Flash File Handler
|
||||
/*
|
||||
* Name: Handle Flash
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Description: Handle Flash files
|
||||
*/
|
||||
|
||||
class FlashFileHandler 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("images/$ha/$hash", $event->metadata);
|
||||
if(is_null($image)) {
|
||||
throw new UploadException(
|
||||
"Flash Handler failed to create image object from data. ".
|
||||
"Note: compressed flash files are currently unsupported");
|
||||
}
|
||||
send_event(new ImageAdditionEvent($event->user, $image));
|
||||
}
|
||||
|
||||
if(($event instanceof ThumbnailGenerationEvent) && $this->supported_ext($event->type)) {
|
||||
$hash = $event->hash;
|
||||
$ha = substr($hash, 0, 2);
|
||||
// FIXME: scale image, as not all boards use 192x192
|
||||
copy("ext/handle_flash/thumb.jpg", "thumbs/$ha/$hash");
|
||||
}
|
||||
|
||||
if(($event instanceof DisplayingImageEvent) && $this->supported_ext($event->image->ext)) {
|
||||
$this->theme->display_image($event->page, $event->image);
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
private function supported_ext($ext) {
|
||||
protected function supported_ext($ext) {
|
||||
$exts = array("swf");
|
||||
return array_contains($exts, strtolower($ext));
|
||||
return in_array(strtolower($ext), $exts);
|
||||
}
|
||||
|
||||
private function create_image_from_data($filename, $metadata) {
|
||||
protected function create_image_from_data($filename, $metadata) {
|
||||
global $config;
|
||||
|
||||
$image = new Image();
|
||||
@ -70,6 +44,17 @@ class FlashFileHandler implements Extension {
|
||||
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++) {
|
||||
@ -115,17 +100,6 @@ class FlashFileHandler implements Extension {
|
||||
|
||||
return $bounds;
|
||||
}
|
||||
|
||||
private function check_contents($file) {
|
||||
if(!file_exists($file)) return false;
|
||||
|
||||
$fp = fopen($file, "r");
|
||||
$head = fread($fp, 3);
|
||||
fclose($fp);
|
||||
if(!array_contains(array("CWS", "FWS"), $head)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
add_event_listener(new FlashFileHandler());
|
||||
?>
|
||||
|
@ -18,7 +18,7 @@ class FlashFileHandlerTheme extends Themelet {
|
||||
width='{$image->width}'
|
||||
type='application/x-shockwave-flash'></embed>
|
||||
</object>";
|
||||
$page->add_block(new Block("Image", $html, "main", 0));
|
||||
$page->add_block(new Block("Flash Animation", $html, "main", 0));
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -1,17 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: ICO File Handler
|
||||
/*
|
||||
* Name: Handle ICO
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Description: Handle windows icons
|
||||
*/
|
||||
|
||||
class IcoFileHandler 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)) {
|
||||
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;
|
||||
@ -20,34 +16,43 @@ class IcoFileHandler implements Extension {
|
||||
if(is_null($image)) {
|
||||
throw new UploadException("Icon handler failed to create image object from data");
|
||||
}
|
||||
send_event(new ImageAdditionEvent($event->user, $image));
|
||||
$iae = new ImageAdditionEvent($event->user, $image);
|
||||
send_event($iae);
|
||||
$event->image_id = $iae->image->id;
|
||||
}
|
||||
}
|
||||
|
||||
if(($event instanceof ThumbnailGenerationEvent) && $this->supported_ext($event->type)) {
|
||||
public function onThumbnailGeneration($event) {
|
||||
if($this->supported_ext($event->type)) {
|
||||
$this->create_thumb($event->hash);
|
||||
}
|
||||
}
|
||||
|
||||
if(($event instanceof DisplayingImageEvent) && $this->supported_ext($event->image->ext)) {
|
||||
$this->theme->display_image($event->page, $event->image);
|
||||
public function onDisplayingImage($event) {
|
||||
global $page;
|
||||
if($this->supported_ext($event->image->ext)) {
|
||||
$this->theme->display_image($page, $event->image);
|
||||
}
|
||||
}
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("get_ico")) {
|
||||
global $config;
|
||||
global $database;
|
||||
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);
|
||||
|
||||
$event->page->set_type("image/x-icon");
|
||||
$event->page->set_mode("data");
|
||||
$event->page->set_data(file_get_contents("images/$ha/$hash"));
|
||||
$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 array_contains($exts, strtolower($ext));
|
||||
return in_array(strtolower($ext), $exts);
|
||||
}
|
||||
|
||||
private function create_image_from_data($filename, $metadata) {
|
||||
@ -86,9 +91,8 @@ class IcoFileHandler implements Extension {
|
||||
private function create_thumb($hash) {
|
||||
global $config;
|
||||
|
||||
$ha = substr($hash, 0, 2);
|
||||
$inname = "images/$ha/$hash";
|
||||
$outname = "thumbs/$ha/$hash";
|
||||
$inname = warehouse_path("images", $hash);
|
||||
$outname = warehouse_path("thumbs", $hash);
|
||||
|
||||
$w = $config->get_int("thumb_width");
|
||||
$h = $config->get_int("thumb_height");
|
||||
@ -106,5 +110,4 @@ class IcoFileHandler implements Extension {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
add_event_listener(new IcoFileHandler());
|
||||
?>
|
||||
|
19
contrib/handle_ico/test.php
Normal file
19
contrib/handle_ico/test.php
Normal 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
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,46 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: MP3 File Handler
|
||||
/*
|
||||
* Name: Handle MP3
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Description: Handle MP3 files
|
||||
*/
|
||||
|
||||
class MP3FileHandler 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("images/$ha/$hash", $event->metadata);
|
||||
if(is_null($image)) {
|
||||
throw new UploadException("MP3 handler failed to create image object from data");
|
||||
}
|
||||
send_event(new ImageAdditionEvent($event->user, $image));
|
||||
}
|
||||
|
||||
if(($event instanceof ThumbnailGenerationEvent) && $this->supported_ext($event->type)) {
|
||||
$hash = $event->hash;
|
||||
$ha = substr($hash, 0, 2);
|
||||
// FIXME: scale image, as not all boards use 192x192
|
||||
copy("ext/handle_mp3/thumb.jpg", "thumbs/$ha/$hash");
|
||||
}
|
||||
|
||||
if(($event instanceof DisplayingImageEvent) && $this->supported_ext($event->image->ext)) {
|
||||
$this->theme->display_image($event->page, $event->image);
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
private function supported_ext($ext) {
|
||||
protected function supported_ext($ext) {
|
||||
$exts = array("mp3");
|
||||
return array_contains($exts, strtolower($ext));
|
||||
return in_array(strtolower($ext), $exts);
|
||||
}
|
||||
|
||||
private function create_image_from_data($filename, $metadata) {
|
||||
protected function create_image_from_data($filename, $metadata) {
|
||||
global $config;
|
||||
|
||||
$image = new Image();
|
||||
@ -59,7 +35,7 @@ class MP3FileHandler implements Extension {
|
||||
return $image;
|
||||
}
|
||||
|
||||
private function check_contents($file) {
|
||||
protected function check_contents($file) {
|
||||
// FIXME: mp3 magic header?
|
||||
return (file_exists($file));
|
||||
}
|
||||
|
@ -7,12 +7,12 @@ class MP3FileHandlerTheme extends Themelet {
|
||||
$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='170'>
|
||||
<param name='movie' value='$data_href/ext/handle_mp3/xspf_player.swf?song_url=$ilink'/>
|
||||
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.swf?song_url=$ilink' quality='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='170'
|
||||
width='400' height='15'
|
||||
type='application/x-shockwave-flash'></embed>
|
||||
</object>
|
||||
<p><a href='$ilink'>Download</a>";
|
||||
|
@ -1,448 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
stop();
|
||||
//autoplay=true
|
||||
//repeat_playlist = true;
|
||||
//playlist_size = 3;
|
||||
//player_title = "customizeable title test"
|
||||
//song_url = "http://downloads.betterpropaganda.com/music/Imperial_Teen-Ivanka_128.mp3";
|
||||
//playlist_url = "http://cchits.ning.com/recent/xspf/?xn_auth=no";
|
||||
//playlist_url = "http://hideout.com.br/shows/radio-test.xspf";
|
||||
//radio_mode = true;
|
||||
//song_title = "Imperial Teen - Ivanka";
|
||||
autoload=true;
|
||||
//info_button_text = "Add to Cart"
|
||||
//playlist_url = "http%3A%2F%2Fwebjay%2Eorg%2Fby%2Flucas%5Fgonze%2Flaconic%2Exspf"
|
||||
//playlist_url= "http://hideout.com.br/tests/hideout2325.xspf"
|
||||
//constants
|
||||
DEFAULT_PLAYLIST_URL = "http://hideout.com.br/shows/allshows.xspf";
|
||||
DEFAULT_WELCOME_MSG = "Hideout XSPF Music Player - by Fabricio Zuardi";
|
||||
LOADING_PLAYLIST_MSG = "Loading Playlist...";
|
||||
DEFAULT_LOADED_PLAYLIST_MSG = "- click to start"
|
||||
DEFAULT_INFOBUTTON_TXT = "Info"
|
||||
//playlists has priority over songs, if a playlist_url parameter is found the song_url is ignored
|
||||
//default playlist if none is passed through query string
|
||||
if(!playlist_url){
|
||||
if(!song_url){
|
||||
playlist_url = DEFAULT_PLAYLIST_URL;
|
||||
}else{
|
||||
single_music_playlist = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><playlist version=\"1\" xmlns = \"http://xspf.org/ns/0/\"><trackList>";
|
||||
single_music_playlist += "<track><location>"+song_url+"</location><annotation>"+song_title+"</annotation></track>"
|
||||
single_music_playlist += "</trackList></playlist>"
|
||||
}
|
||||
}
|
||||
info_mc._visible=false;
|
||||
if(!info_button_text){
|
||||
info_button_text = DEFAULT_INFOBUTTON_TXT;
|
||||
}
|
||||
info_mc.display_txt.text = info_button_text;
|
||||
|
||||
//variables initialization
|
||||
playlist_array = [];
|
||||
track_index = 0;
|
||||
playlist_mc.track_count = 0;
|
||||
pause_position = 0;
|
||||
volume_level = 100;
|
||||
playlist_xml = new XML();
|
||||
playlist_xml.ignoreWhite = true;
|
||||
playlist_xml.onLoad = playlistLoaded;
|
||||
mysound = new Sound(this);
|
||||
playlist_listener = new Object();
|
||||
playlist_list.addEventListener("change", playlist_listener)
|
||||
//play_btn.onPress = playTrack;
|
||||
//functions
|
||||
//xml parser
|
||||
function playlistLoaded (success){
|
||||
if(success){
|
||||
var root_node = this.firstChild;
|
||||
for(var node = root_node.firstChild; node != null; node = node.nextSibling){
|
||||
if(node.nodeName == "title"){
|
||||
playlist_title = node.firstChild.nodeValue;
|
||||
}
|
||||
if(node.nodeName == "trackList"){
|
||||
//tracks
|
||||
var tracks_array = [];
|
||||
for(var track_node = node.firstChild; track_node != null; track_node = track_node.nextSibling){
|
||||
var track_obj = new Object()
|
||||
//track attributes
|
||||
for(var track_child = track_node.firstChild; track_child != null; track_child = track_child.nextSibling){
|
||||
if(track_child.nodeName=="location"){
|
||||
track_obj.location = track_child.firstChild.nodeValue
|
||||
}
|
||||
if(track_child.nodeName=="image"){
|
||||
track_obj.image = track_child.firstChild.nodeValue
|
||||
}
|
||||
if(track_child.nodeName=="title"){
|
||||
track_obj.title = track_child.firstChild.nodeValue
|
||||
}
|
||||
if(track_child.nodeName=="creator"){
|
||||
track_obj.creator = track_child.firstChild.nodeValue
|
||||
}
|
||||
if(track_child.nodeName=="annotation"){
|
||||
track_obj.annotation = track_child.firstChild.nodeValue
|
||||
}
|
||||
if(track_child.nodeName=="info"){
|
||||
track_obj.info = track_child.firstChild.nodeValue
|
||||
}
|
||||
}
|
||||
track_obj.label = (tracks_array.length+1) +". ";
|
||||
if(track_obj.title) {
|
||||
if(track_obj.creator) {
|
||||
track_obj.label += track_obj.creator+' - ';
|
||||
}
|
||||
track_obj.label += track_obj.title;
|
||||
} else {
|
||||
track_obj.label += track_obj.annotation;
|
||||
}
|
||||
tracks_array.push(track_obj)
|
||||
addTrack(track_obj.label);
|
||||
}
|
||||
}
|
||||
}
|
||||
playlist_array = tracks_array;
|
||||
if(!playlist_size) playlist_size = playlist_array.length;
|
||||
playlist_mc.tracks_mc["track_"+track_index+"_mc"].bg_mc.gotoAndStop(2)
|
||||
if(autoplay){
|
||||
loadTrack()
|
||||
}else{
|
||||
start_btn_mc.start_btn.onPress = loadTrack;
|
||||
track_display_mc.display_txt.text = playlist_title+" "+DEFAULT_LOADED_PLAYLIST_MSG;
|
||||
if(track_display_mc.display_txt._width>track_display_mc.mask_mc._width){
|
||||
track_display_mc.onEnterFrame = scrollTitle;
|
||||
}else{
|
||||
track_display_mc.onEnterFrame = null;
|
||||
track_display_mc.display_txt._x = 0;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
annotation_txt.text = "Error opening "+playlist_url;
|
||||
}
|
||||
}
|
||||
|
||||
playlist_listener.change = function(eventObject){
|
||||
annotation_txt.text = playlist_list.selectedItem.annotation;
|
||||
location_txt.text = playlist_list.selectedItem.location;
|
||||
}
|
||||
|
||||
function loadTrack(){
|
||||
|
||||
//Radio Mode feature by nosferathoo, more info in: https://sourceforge.net/tracker/index.php?func=detail&aid=1341940&group_id=128363&atid=711474
|
||||
if (radio_mode && track_index==playlist_size-1) {
|
||||
playlist_url=playlist_array[track_index].location;
|
||||
for (i=0;i<playlist_mc.track_count;++i) {
|
||||
removeMovieClip(playlist_mc.tracks_mc["track_"+i+"_mc"]);
|
||||
}
|
||||
playlist_mc.track_count=0;
|
||||
playlist_size=0;
|
||||
track_index=0;
|
||||
autoload=true;
|
||||
autoplay=true;
|
||||
loadPlaylist();
|
||||
return(0);
|
||||
}
|
||||
|
||||
start_btn_mc.start_btn._visible = false;
|
||||
track_display_mc.display_txt.text = playlist_array[track_index].label;
|
||||
if(track_display_mc.display_txt._width>track_display_mc.mask_mc._width){
|
||||
track_display_mc.onEnterFrame = scrollTitle;
|
||||
}else{
|
||||
track_display_mc.onEnterFrame = null;
|
||||
track_display_mc.display_txt._x = 0;
|
||||
}
|
||||
cover_mc.content_mc["photo"+last_track_index].removeMovieClip();
|
||||
mysound.loadSound(playlist_array[track_index].location,true);
|
||||
play_mc.gotoAndStop(2)
|
||||
|
||||
//image from playlist
|
||||
if(playlist_array[track_index].image!=undefined){
|
||||
cover_mc.content_mc.createEmptyMovieClip("photo"+track_index,track_index)
|
||||
cover_mc.content_mc["photo"+track_index].loadMovie(playlist_array[track_index].image)
|
||||
}else{
|
||||
}
|
||||
//info button
|
||||
if(playlist_array[track_index].info!=undefined){
|
||||
info_mc._visible = true;
|
||||
info_mc.info_btn.onPress = function(){
|
||||
getURL(playlist_array[track_index].info,"_blank")
|
||||
}
|
||||
}else{
|
||||
info_mc._visible = false;
|
||||
}
|
||||
_root.onEnterFrame=function(){
|
||||
//HACK doesnt need to set the volume at every enterframe
|
||||
mysound.setVolume(this.volume_level)
|
||||
var sound_load_percent = (mysound.getBytesLoaded()/mysound.getBytesTotal())*100
|
||||
track_display_mc.loader_mc.load_bar_mc._xscale = sound_load_percent;
|
||||
var image_load_percent = (cover_mc.content_mc["photo"+track_index].getBytesLoaded()/cover_mc.content_mc["photo"+track_index].getBytesTotal())*100
|
||||
cover_mc.load_bar_mc._xscale = image_load_percent;
|
||||
if((cover_mc.content_mc["photo"+track_index].getBytesLoaded()>4)&&(image_load_percent==100)){
|
||||
//image loaded
|
||||
//make image fit
|
||||
cover_mc.content_mc["photo"+track_index]._width = cover_mc.load_bar_mc._width
|
||||
cover_mc.content_mc["photo"+track_index]._height = cover_mc.load_bar_mc._height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stop_btn.onRelease = stopTrack;
|
||||
play_mc.play_btn.onRelease = playTrack
|
||||
next_btn.onRelease = nextTrack
|
||||
prev_btn.onRelease = prevTrack
|
||||
mysound.onSoundComplete = nextTrack;
|
||||
volume_mc.volume_btn.onPress = volumeChange;
|
||||
volume_mc.volume_btn.onRelease = volume_mc.volume_btn.onReleaseOutside = function(){
|
||||
this._parent.onMouseMove = this._parent.onMouseDown = null;
|
||||
}
|
||||
|
||||
function volumeChange(){
|
||||
this._parent.onMouseMove = this._parent.onMouseDown = function(){
|
||||
var percent = (this._xmouse/this._width)*100
|
||||
if(percent>100)percent=100;
|
||||
if(percent<0)percent=0;
|
||||
this.volume_bar_mc._xscale = percent
|
||||
this._parent.volume_level = percent;
|
||||
mysound.setVolume(percent)
|
||||
}
|
||||
}
|
||||
|
||||
function stopTrack() {
|
||||
mysound.stop();
|
||||
play_mc.gotoAndStop(1)
|
||||
mysound.stop();
|
||||
mysound.start();
|
||||
mysound.stop();
|
||||
_root.pause_position = 0;
|
||||
|
||||
};
|
||||
function playTrack() {
|
||||
if(play_mc._currentframe==1){ //play
|
||||
seekTrack(_root.pause_position)
|
||||
play_mc.gotoAndStop(2)
|
||||
}else if(play_mc._currentframe==2){
|
||||
_root.pause_position = mysound.position;
|
||||
mysound.stop();
|
||||
play_mc.gotoAndStop(1)
|
||||
}
|
||||
|
||||
};
|
||||
function seekTrack(p_offset:Number){
|
||||
mysound.stop()
|
||||
mysound.start(int((p_offset)/1000),1)
|
||||
}
|
||||
function nextTrack(){
|
||||
if(track_index<playlist_size-1){
|
||||
last_track_index = track_index;
|
||||
track_index ++;
|
||||
loadTrack();
|
||||
}else{
|
||||
if(repeat_playlist){
|
||||
last_track_index = track_index;
|
||||
track_index = 0;
|
||||
loadTrack()
|
||||
}
|
||||
}
|
||||
playlist_mc.tracks_mc["track_"+last_track_index+"_mc"].bg_mc.gotoAndStop(1)
|
||||
playlist_mc.tracks_mc["track_"+track_index+"_mc"].bg_mc.gotoAndStop(2)
|
||||
}
|
||||
|
||||
function prevTrack(){
|
||||
if(track_index>0){
|
||||
last_track_index = track_index;
|
||||
track_index --;
|
||||
loadTrack();
|
||||
}
|
||||
playlist_mc.tracks_mc["track_"+last_track_index+"_mc"].bg_mc.gotoAndStop(1)
|
||||
playlist_mc.tracks_mc["track_"+track_index+"_mc"].bg_mc.gotoAndStop(2)
|
||||
}
|
||||
|
||||
function scrollTitle(){
|
||||
track_display_mc.display_txt._x -= 5;
|
||||
if (track_display_mc.display_txt._x+track_display_mc.display_txt._width<0){
|
||||
track_display_mc.display_txt._x = track_display_mc.mask_mc._width;
|
||||
}
|
||||
}
|
||||
|
||||
function resizeUI(){
|
||||
bg_mc._width = Stage.width;
|
||||
track_display_mc.loader_mc._width = Stage.width - track_display_mc._x - 2;
|
||||
track_display_mc.mask_mc._width = track_display_mc.loader_mc._width-3;
|
||||
if(track_display_mc.display_txt._width>track_display_mc.mask_mc._width){
|
||||
track_display_mc.onEnterFrame = scrollTitle;
|
||||
}else{
|
||||
track_display_mc.onEnterFrame = null;
|
||||
track_display_mc.display_txt._x = 0;
|
||||
}
|
||||
volume_mc._x = Stage.width - 22;
|
||||
start_btn_mc._xscale = Stage.width;
|
||||
//playlist area tinnier that the album cover
|
||||
if(Stage.width<2.5*cover_mc._width){
|
||||
//
|
||||
if (Stage.height>2.5*cover_mc._height){
|
||||
//send album cover to bottom
|
||||
cover_mc._y = Stage.height - cover_mc._height -2- info_mc._height -2;
|
||||
info_mc._y = Stage.height - info_mc._height -2;
|
||||
var covervisible =1;
|
||||
}else{
|
||||
var covervisible =0;
|
||||
//hide album cover
|
||||
cover_mc._y = Stage.height;
|
||||
}
|
||||
//send playlist to left
|
||||
playlist_mc._x = cover_mc._x;
|
||||
scrollbar_mc.bg_mc._height = Stage.height - (19+(cover_mc._height+info_mc._height+4)*covervisible);
|
||||
playlist_mc.bg_mc._height = Stage.height - (19+(cover_mc._height+info_mc._height+4)*covervisible);
|
||||
playlist_mc.mask_mc._height = Stage.height - (23+(cover_mc._height+info_mc._height+4)*covervisible);
|
||||
}else{
|
||||
cover_mc._y = 17;
|
||||
info_mc._y = 153;
|
||||
playlist_mc._x = 138;
|
||||
scrollbar_mc.bg_mc._height = Stage.height -19;
|
||||
playlist_mc.bg_mc._height = Stage.height - 19;
|
||||
playlist_mc.mask_mc._height = Stage.height - 23;
|
||||
}
|
||||
scrollbar_mc._x = Stage.width - 12;
|
||||
playlist_mc.mask_mc._width = Stage.width - (playlist_mc._x + 19);
|
||||
playlist_mc.bg_mc._width = Stage.width - (playlist_mc._x + 14);
|
||||
}
|
||||
function addTrack(track_label){
|
||||
if(playlist_mc.track_count>0) {
|
||||
playlist_mc.tracks_mc.track_0_mc.duplicateMovieClip("track_"+playlist_mc.track_count+"_mc",playlist_mc.track_count);
|
||||
}
|
||||
playlist_mc.tracks_mc["track_"+playlist_mc.track_count+"_mc"]._y += playlist_mc.track_count*14;
|
||||
playlist_mc.tracks_mc["track_"+playlist_mc.track_count+"_mc"].display_txt.autoSize = "left";
|
||||
playlist_mc.tracks_mc["track_"+playlist_mc.track_count+"_mc"].display_txt.text = track_label;
|
||||
playlist_mc.tracks_mc["track_"+playlist_mc.track_count+"_mc"].bg_mc.index = playlist_mc.track_count;
|
||||
playlist_mc.tracks_mc["track_"+playlist_mc.track_count+"_mc"].bg_mc.select_btn.onPress = function(){
|
||||
last_track_index = track_index;
|
||||
playlist_mc.tracks_mc["track_"+track_index+"_mc"].bg_mc.gotoAndStop(1)
|
||||
track_index = this._parent.index;
|
||||
playlist_mc.tracks_mc["track_"+track_index+"_mc"].bg_mc.gotoAndStop(2)
|
||||
loadTrack();
|
||||
}
|
||||
playlist_mc.track_count ++;
|
||||
}
|
||||
//scroll
|
||||
|
||||
scrollbar_mc.up_btn.onPress = function(){
|
||||
this._parent.v = -1;
|
||||
this._parent.onEnterFrame = scrollTracks;
|
||||
}
|
||||
scrollbar_mc.down_btn.onPress = function(){
|
||||
this._parent.v = 1;
|
||||
this._parent.onEnterFrame = scrollTracks;
|
||||
}
|
||||
scrollbar_mc.up_btn.onRelease = scrollbar_mc.down_btn.onRelease = function(){
|
||||
this._parent.onEnterFrame = null;
|
||||
}
|
||||
scrollbar_mc.handler_mc.drag_btn.onPress = function(){
|
||||
var scroll_top_limit = 19;
|
||||
var scroll_bottom_limit = scrollbar_mc.bg_mc._height - scrollbar_mc.handler_mc._height - 2;
|
||||
this._parent.startDrag(false,this._parent._x,scroll_top_limit,this._parent._x,scroll_bottom_limit)
|
||||
this._parent.isdragging = true;
|
||||
this._parent.onEnterFrame = scrollTracks;
|
||||
}
|
||||
scrollbar_mc.handler_mc.drag_btn.onRelease = scrollbar_mc.handler_mc.drag_btn.onReleaseOutside = function(){
|
||||
stopDrag()
|
||||
this._parent.isdragging = false;
|
||||
this._parent.onEnterFrame = null;
|
||||
}
|
||||
function scrollTracks(){
|
||||
var scroll_top_limit = 19;
|
||||
var scroll_bottom_limit = scrollbar_mc.bg_mc._height - scrollbar_mc.handler_mc._height - 2;
|
||||
var list_bottom_limit = 1;
|
||||
var list_top_limit = (1-Math.round(playlist_mc.tracks_mc._height))+Math.floor(playlist_mc.mask_mc._height/14)*14
|
||||
if(playlist_mc.tracks_mc._height>playlist_mc.mask_mc._height){
|
||||
if(scrollbar_mc.handler_mc.isdragging){
|
||||
var percent = (scrollbar_mc.handler_mc._y-scroll_top_limit)/(scroll_bottom_limit-scroll_top_limit)
|
||||
playlist_mc.tracks_mc._y = (list_top_limit-list_bottom_limit)*percent+list_bottom_limit;
|
||||
}else{
|
||||
if(scrollbar_mc.v==-1){
|
||||
if(playlist_mc.tracks_mc._y+14<list_bottom_limit){
|
||||
playlist_mc.tracks_mc._y += 14;
|
||||
}else{
|
||||
playlist_mc.tracks_mc._y = list_bottom_limit;
|
||||
}
|
||||
var percent = (playlist_mc.tracks_mc._y-1)/(list_top_limit-1)
|
||||
scrollbar_mc.handler_mc._y = percent*(scroll_bottom_limit - scroll_top_limit)+scroll_top_limit;
|
||||
}else if(scrollbar_mc.v==1){
|
||||
if(playlist_mc.tracks_mc._y-14>list_top_limit){
|
||||
playlist_mc.tracks_mc._y -= 14;
|
||||
}else{
|
||||
playlist_mc.tracks_mc._y = list_top_limit;
|
||||
}
|
||||
var percent = (playlist_mc.tracks_mc._y-1)/(list_top_limit-1)
|
||||
scrollbar_mc.handler_mc._y = percent*(scroll_bottom_limit - scroll_top_limit)+scroll_top_limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function loadPlaylist(){
|
||||
track_display_mc.display_txt.text = LOADING_PLAYLIST_MSG;
|
||||
if(track_display_mc.display_txt._width>track_display_mc.mask_mc._width){
|
||||
track_display_mc.onEnterFrame = scrollTitle;
|
||||
}else{
|
||||
track_display_mc.onEnterFrame = null;
|
||||
track_display_mc.display_txt._x = 0;
|
||||
}
|
||||
|
||||
//playlist
|
||||
if(playlist_url){
|
||||
playlist_xml.load(unescape(playlist_url))
|
||||
}else{
|
||||
//single track
|
||||
playlist_xml.parseXML(single_music_playlist)
|
||||
playlist_xml.onLoad(true);
|
||||
}
|
||||
}
|
||||
|
||||
//first click - load playlist
|
||||
start_btn_mc.start_btn.onPress = function(){
|
||||
autoplay = true;
|
||||
loadPlaylist();
|
||||
}
|
||||
|
||||
|
||||
//main
|
||||
Stage.scaleMode = "noScale"
|
||||
Stage.align = "LT";
|
||||
this.onResize = resizeUI;
|
||||
Stage.addListener(this);
|
||||
if(!player_title) player_title = DEFAULT_WELCOME_MSG;
|
||||
track_display_mc.display_txt.autoSize = "left";
|
||||
track_display_mc.display_txt.text = player_title;
|
||||
if(track_display_mc.display_txt._width>track_display_mc.mask_mc._width){
|
||||
track_display_mc.onEnterFrame = scrollTitle;
|
||||
}else{
|
||||
track_display_mc.onEnterFrame = null;
|
||||
track_display_mc.display_txt._x = 0;
|
||||
}
|
||||
//start to play automatically if parameter autoplay is present
|
||||
if(autoplay){
|
||||
start_btn_mc.start_btn.onPress();
|
||||
} else if (autoload){
|
||||
loadPlaylist()
|
||||
}
|
||||
//customized menu
|
||||
var my_cm:ContextMenu = new ContextMenu();
|
||||
my_cm.customItems.push(new ContextMenuItem("Stop", stopTrack));
|
||||
my_cm.customItems.push(new ContextMenuItem("Play!", playTrack));
|
||||
my_cm.customItems.push(new ContextMenuItem("Next", nextTrack));
|
||||
my_cm.customItems.push(new ContextMenuItem("Previous", prevTrack));
|
||||
my_cm.customItems.push(new ContextMenuItem("Download this song", function(){getURL(playlist_array[track_index].location,"_blank")},true));
|
||||
my_cm.customItems.push(new ContextMenuItem("Add song to Webjay playlist", function(){getURL("http://webjay.org/poster?media="+escape(playlist_array[track_index].location),"_blank")}));
|
||||
my_cm.customItems.push(new ContextMenuItem("About Hideout", function(){getURL("http://www.hideout.com.br","_blank")},true));
|
||||
//my_cm.customItems.push(new ContextMenuItem("Crossfade", function(){}));
|
||||
//my_cm.customItems.push(new ContextMenuItem("Mando Diao - Paralyzed", function(){}));
|
||||
my_cm.hideBuiltInItems();
|
||||
this.menu = my_cm;
|
||||
resizeUI();
|
Binary file not shown.
Binary file not shown.
BIN
contrib/handle_mp3/xspf_player_slim.swf
Normal file
BIN
contrib/handle_mp3/xspf_player_slim.swf
Normal file
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: SVG File Handler
|
||||
/*
|
||||
* Name: Handle SVG
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Description: Handle SVG files
|
||||
*/
|
||||
@ -16,11 +16,13 @@ class SVGFileHandler implements Extension {
|
||||
$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);
|
||||
$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");
|
||||
}
|
||||
send_event(new ImageAdditionEvent($event->user, $image));
|
||||
$iae = new ImageAdditionEvent($event->user, $image);
|
||||
send_event($iae);
|
||||
$event->image_id = $iae->image->id;
|
||||
}
|
||||
|
||||
if(($event instanceof ThumbnailGenerationEvent) && $this->supported_ext($event->type)) {
|
||||
@ -39,31 +41,30 @@ class SVGFileHandler implements Extension {
|
||||
// }
|
||||
// else {
|
||||
// FIXME: scale image, as not all boards use 192x192
|
||||
copy("ext/handle_svg/thumb.jpg", "thumbs/$ha/$hash");
|
||||
copy("ext/handle_svg/thumb.jpg", warehouse_path("thumbs", $hash));
|
||||
// }
|
||||
}
|
||||
|
||||
if(($event instanceof DisplayingImageEvent) && $this->supported_ext($event->image->ext)) {
|
||||
$this->theme->display_image($event->page, $event->image);
|
||||
global $page;
|
||||
$this->theme->display_image($page, $event->image);
|
||||
}
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("get_svg")) {
|
||||
global $config;
|
||||
global $database;
|
||||
global $config, $database, $page;
|
||||
$id = int_escape($event->get_arg(0));
|
||||
$image = Image::by_id($id);
|
||||
$hash = $image->hash;
|
||||
$ha = substr($hash, 0, 2);
|
||||
|
||||
$event->page->set_type("image/svg+xml");
|
||||
$event->page->set_mode("data");
|
||||
$event->page->set_data(file_get_contents("images/$ha/$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 array_contains($exts, strtolower($ext));
|
||||
return in_array(strtolower($ext), $exts);
|
||||
}
|
||||
|
||||
private function create_image_from_data($filename, $metadata) {
|
||||
|
37
contrib/handle_svg/test.php
Normal file
37
contrib/handle_svg/test.php
Normal 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
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,8 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: Home Page
|
||||
* Author: Bzchan <bzchan@animemahou.com>
|
||||
* License: GPLv2
|
||||
* Visibility: admin
|
||||
* Description: Displays a front page with logo, search box and image count
|
||||
* Documentation:
|
||||
* Once enabled, the page will show up at the URL "home", so if you want
|
||||
@ -14,37 +15,46 @@
|
||||
* alongside the default choices.
|
||||
*/
|
||||
|
||||
class Home implements Extension {
|
||||
var $theme;
|
||||
class Home extends SimpleExtension {
|
||||
public function onInitExt(InitExtEvent $event) {
|
||||
global $config;
|
||||
$config->set_default_string("home_links", '[$base/post/list|Posts]
|
||||
[$base/comment/list|Comments]
|
||||
[$base/tags|Tags]
|
||||
[$base/wiki|Wiki]
|
||||
[$base/wiki/more|»]');
|
||||
}
|
||||
|
||||
public function receive_event(Event $event) {
|
||||
global $config, $database, $page, $user;
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object($this);
|
||||
public function onPageRequest(PageRequestEvent $event) {
|
||||
global $config, $page;
|
||||
if($event->page_matches("home")) {
|
||||
$base_href = $config->get_string('base_href');
|
||||
$data_href = get_base_href();
|
||||
$sitename = $config->get_string('title');
|
||||
$theme_name = $config->get_string('theme');
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("home"))
|
||||
{
|
||||
// this is a request to display this page so output the page.
|
||||
$this->output_pages($page);
|
||||
}
|
||||
if($event instanceof SetupBuildingEvent)
|
||||
{
|
||||
$counters = array();
|
||||
foreach(glob("ext/home/counters/*") as $counter_dirname) {
|
||||
$name = str_replace("ext/home/counters/", "", $counter_dirname);
|
||||
$counters[ucfirst($name)] = $name;
|
||||
}
|
||||
$body = $this->get_body();
|
||||
|
||||
$sb = new SetupBlock("Home Page");
|
||||
$sb->add_label("Page Links - Example: [$"."base/index|Posts]");
|
||||
$sb->add_longtext_option("home_links", "<br>");
|
||||
$sb->add_choice_option("home_counter", $counters, "<br>Counter: ");
|
||||
$sb->add_label("<br>Note: page accessed via /home");
|
||||
$event->panel->add_block($sb);
|
||||
$this->theme->display_page($page, $sitename, $data_href, $theme_name, $body);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_body()
|
||||
{
|
||||
public function onSetupBuilding(SetupBuildingEvent $event) {
|
||||
$counters = array();
|
||||
foreach(glob("ext/home/counters/*") as $counter_dirname) {
|
||||
$name = str_replace("ext/home/counters/", "", $counter_dirname);
|
||||
$counters[ucfirst($name)] = $name;
|
||||
}
|
||||
|
||||
$sb = new SetupBlock("Home Page");
|
||||
$sb->add_longtext_option("home_links", 'Page Links - Example: [/post/list|Posts]<br>');
|
||||
$sb->add_longtext_option("home_text", "<br>Page Text:<br>");
|
||||
$sb->add_choice_option("home_counter", $counters, "<br>Counter: ");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
|
||||
|
||||
private function get_body() {
|
||||
// returns just the contents of the body
|
||||
global $database;
|
||||
global $config;
|
||||
@ -54,14 +64,12 @@ class Home implements Extension {
|
||||
$contact_link = $config->get_string('contact_link');
|
||||
$counter_dir = $config->get_string('home_counter', 'default');
|
||||
|
||||
$total = ceil($database->db->GetOne("SELECT COUNT(*) FROM images"));
|
||||
$total = Image::count_images();
|
||||
$strtotal = "$total";
|
||||
|
||||
$num_comma = number_format($total);
|
||||
|
||||
$counter_text = "";
|
||||
for($n=0; $n<strlen($strtotal); $n++)
|
||||
{
|
||||
for($n=0; $n<strlen($strtotal); $n++) {
|
||||
$cur = $strtotal[$n];
|
||||
$counter_text .= " <img alt='$cur' src='$data_href/ext/home/counters/$counter_dir/$cur.gif' /> ";
|
||||
}
|
||||
@ -69,27 +77,12 @@ class Home implements Extension {
|
||||
// get the homelinks and process them
|
||||
$main_links = $config->get_string('home_links');
|
||||
$main_links = str_replace('$base', $base_href, $main_links);
|
||||
$main_links = str_replace('[', "<a href='", $main_links);
|
||||
$main_links = str_replace('|', "'>", $main_links);
|
||||
$main_links = str_replace(']', "</a>", $main_links);
|
||||
$main_links = preg_replace('#\[(.*?)\|(.*?)\]#', "<a href='\\1'>\\2</a>", $main_links);
|
||||
$main_links = str_replace('//', "/", $main_links);
|
||||
|
||||
return $this->theme->build_body($sitename, $main_links, $contact_link, $num_comma, $counter_text);
|
||||
$main_text = $config->get_string('home_text');
|
||||
|
||||
return $this->theme->build_body($sitename, $main_links, $main_text, $contact_link, $num_comma, $counter_text);
|
||||
}
|
||||
|
||||
private function output_pages($page)
|
||||
{
|
||||
// output a sectionalised list of all the main pages on the site.
|
||||
global $config;
|
||||
$base_href = $config->get_string('base_href');
|
||||
$data_href = get_base_href();
|
||||
$sitename = $config->get_string('title');
|
||||
$theme_name = $config->get_string('theme');
|
||||
|
||||
$body = $this->get_body();
|
||||
|
||||
$this->theme->display_page($page, $sitename, $data_href, $theme_name, $body);
|
||||
}
|
||||
|
||||
}
|
||||
add_event_listener(new Home());
|
||||
?>
|
||||
|
@ -1,9 +1,11 @@
|
||||
<?php
|
||||
class HomeTest extends WebTestCase {
|
||||
class HomeTest extends ShimmieWebTestCase {
|
||||
function testHomePage() {
|
||||
$this->get(TEST_BASE.'/home');
|
||||
$this->assertTitle('Shimmie Testbed');
|
||||
$this->assertText('Shimmie Testbed');
|
||||
$this->get_page('home');
|
||||
$this->assert_title('Shimmie');
|
||||
$this->assert_text('Shimmie');
|
||||
|
||||
# FIXME: test search box
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -10,7 +10,7 @@ class HomeTheme extends Themelet {
|
||||
<link rel='stylesheet' href='$data_href/themes/$theme_name/style.css' type='text/css'>
|
||||
</head>
|
||||
<style>
|
||||
div#front-page h1 {font-size: 4em; margin-top: 2em; text-align: center; border: none; background: none;}
|
||||
div#front-page h1 {font-size: 4em; margin-top: 2em; margin-bottom: 0px; text-align: center; border: none; background: none; box-shadow: none; -webkit-box-shadow: none; -moz-box-shadow: none;}
|
||||
div#front-page {text-align:center;}
|
||||
.space {margin-bottom: 1em;}
|
||||
div#front-page div#links a {margin: 0 0.5em;}
|
||||
@ -24,31 +24,31 @@ EOD
|
||||
);
|
||||
}
|
||||
|
||||
public function build_body($sitename, $main_links, $contact_link, $num_comma, $counter_text) {
|
||||
return "
|
||||
<div id='front-page'>
|
||||
<h1>
|
||||
<a style='text-decoration: none;' href='".make_link()."'><span>$sitename</span></a>
|
||||
</h1>
|
||||
<div class='space' id='links'>
|
||||
$main_links
|
||||
</div>
|
||||
<div class='space'>
|
||||
public function build_body($sitename, $main_links, $main_text, $contact_link, $num_comma, $counter_text) {
|
||||
$main_links_html = empty($main_links) ? "" : "<div class='space' id='links'>$main_links</div>";
|
||||
$message_html = empty($main_text) ? "" : "<div class='space' id='message'>$main_text</div>";
|
||||
$counter_html = empty($counter_text) ? "" : "<div class='space' id='counter'>$counter_text</div>";
|
||||
$search_html = "
|
||||
<div class='space' id='search'>
|
||||
<form action='".make_link("post/list")."' method='GET'>
|
||||
<input id='search_input' name='search' size='55' type='text' value='' autocomplete='off' /><br/>
|
||||
<input id='search_input' name='search' size='30' type='text' value='' autocomplete='off' />
|
||||
<input type='hidden' name='q' value='/post/list'>
|
||||
<input type='submit' value='Search'/>
|
||||
</form>
|
||||
</div>
|
||||
<div style='font-size: 80%; margin-bottom: 2em;'>
|
||||
<a href='$contact_link'>contact</a> – Serving $num_comma posts
|
||||
</div>
|
||||
|
||||
<div class='space'>
|
||||
Powered by <a href='http://trac.shishnet.org/shimmie2/'>Shimmie</a>
|
||||
</div>
|
||||
<div class='space'>
|
||||
$counter_text
|
||||
";
|
||||
return "
|
||||
<div id='front-page'>
|
||||
<h1><a style='text-decoration: none;' href='".make_link()."'><span>$sitename</span></a></h1>
|
||||
$main_links_html
|
||||
$search_html
|
||||
$message_html
|
||||
$counter_html
|
||||
<div class='space' id='foot'>
|
||||
<small><small>
|
||||
<a href='$contact_link'>Contact</a> – Serving $num_comma posts –
|
||||
Running <a href='http://code.shishnet.org/shimmie2/'>Shimmie</a>
|
||||
</small></small>
|
||||
</div>
|
||||
</div>";
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: Image Hash Ban
|
||||
* Author: ATravelingGeek <atg@atravelinggeek.com>
|
||||
* Link: http://atravelinggeek.com/
|
||||
* License: GPLv2
|
||||
* Description: Ban images based on their hash
|
||||
* Based on the ResolutionLimit and IPban extensions by Shish
|
||||
* Version 0.1
|
||||
* October 21, 2007
|
||||
* Version 0.1, October 21, 2007
|
||||
*/
|
||||
|
||||
// RemoveImageHashBanEvent {{{
|
||||
@ -30,35 +29,49 @@ class AddImageHashBanEvent extends Event {
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
class ImageBan implements Extension {
|
||||
var $theme;
|
||||
class ImageBan extends SimpleExtension {
|
||||
public function onInitExt(InitExtEvent $event) {
|
||||
global $config, $database;
|
||||
if($config->get_int("ext_imageban_version") < 1) {
|
||||
$database->create_table("image_bans", "
|
||||
id SCORE_AIPK,
|
||||
hash CHAR(32) NOT NULL,
|
||||
date DATETIME DEFAULT SCORE_NOW,
|
||||
reason TEXT NOT NULL
|
||||
");
|
||||
$config->set_int("ext_imageban_version", 1);
|
||||
}
|
||||
}
|
||||
|
||||
public function receive_event(Event $event) {
|
||||
public function onDataUpload(DataUploadEvent $event) {
|
||||
global $database;
|
||||
$row = $database->db->GetRow("SELECT * FROM image_bans WHERE hash = ?", $event->hash);
|
||||
if($row) {
|
||||
log_info("image_hash_ban", "Blocked image ({$event->hash})");
|
||||
throw new UploadException("Image ".html_escape($row["hash"])." has been banned, reason: ".format_text($row["reason"]));
|
||||
}
|
||||
}
|
||||
|
||||
public function onPageRequest(PageRequestEvent $event) {
|
||||
global $config, $database, $page, $user;
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object($this);
|
||||
|
||||
if($event instanceof InitExtEvent) {
|
||||
if($config->get_int("ext_imageban_version") < 1) {
|
||||
$this->install();
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof DataUploadEvent) {
|
||||
$row = $database->db->GetRow("SELECT * FROM image_bans WHERE hash = ?", $event->hash);
|
||||
if($row) {
|
||||
log_info("image_hash_ban", "Blocked image ({$event->hash})");
|
||||
throw new UploadException("Image ".html_escape($row["hash"])." has been banned, reason: ".format_text($row["reason"]));
|
||||
}
|
||||
}
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("image_hash_ban")) {
|
||||
if($event->page_matches("image_hash_ban")) {
|
||||
if($user->is_admin()) {
|
||||
if($event->get_arg(0) == "add") {
|
||||
if($event->get_arg(0) == "dnp") {
|
||||
$image = Image::by_id(int_escape($event->get_arg(1)));
|
||||
if($image) {
|
||||
send_event(new AddImageHashBanEvent($image->hash, "DNP"));
|
||||
send_event(new ImageDeletionEvent($image));
|
||||
}
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect($_SERVER["HTTP_REFERER"]);
|
||||
}
|
||||
else if($event->get_arg(0) == "add") {
|
||||
if(isset($_POST['hash']) && isset($_POST['reason'])) {
|
||||
send_event(new AddImageHashBanEvent($_POST['hash'], $_POST['reason']));
|
||||
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("admin"));
|
||||
$page->set_redirect(make_link("image_hash_ban/list/1"));
|
||||
}
|
||||
if(isset($_POST['image_id'])) {
|
||||
$image = Image::by_id(int_escape($_POST['image_id']));
|
||||
@ -74,7 +87,7 @@ class ImageBan implements Extension {
|
||||
send_event(new RemoveImageHashBanEvent($_POST['hash']));
|
||||
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("admin"));
|
||||
$page->set_redirect(make_link("image_hash_ban/list/1"));
|
||||
}
|
||||
}
|
||||
else if($event->get_arg(0) == "list") {
|
||||
@ -82,47 +95,39 @@ class ImageBan implements Extension {
|
||||
if($event->count_args() == 2) {
|
||||
$page_num = int_escape($event->get_arg(1));
|
||||
}
|
||||
$this->theme->display_Image_hash_Bans($page, $page_num, $this->get_image_hash_bans($page_num));
|
||||
$page_size = 100;
|
||||
$page_count = ceil($database->db->getone("SELECT COUNT(id) FROM image_bans")/$page_size);
|
||||
$this->theme->display_Image_hash_Bans($page, $page_num, $page_count, $this->get_image_hash_bans($page_num, $page_size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof UserBlockBuildingEvent) {
|
||||
if($user->is_admin()) {
|
||||
$event->add_link("Image Bans", make_link("image_hash_ban/list/1"));
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof AddImageHashBanEvent) {
|
||||
$this->add_image_hash_ban($event->hash, $event->reason);
|
||||
}
|
||||
|
||||
if($event instanceof RemoveImageHashBanEvent) {
|
||||
$this->remove_image_hash_ban($event->hash);
|
||||
}
|
||||
|
||||
if($event instanceof ImageAdminBlockBuildingEvent) {
|
||||
if($user->is_admin()) {
|
||||
$event->add_part($this->theme->get_buttons_html($event->image));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function install() {
|
||||
global $database;
|
||||
global $config;
|
||||
$database->create_table("image_bans", "
|
||||
id SCORE_AIPK,
|
||||
hash CHAR(32) NOT NULL,
|
||||
date DATETIME DEFAULT SCORE_NOW,
|
||||
reason TEXT NOT NULL
|
||||
");
|
||||
$config->set_int("ext_imageban_version", 1);
|
||||
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
|
||||
global $user;
|
||||
if($user->is_admin()) {
|
||||
$event->add_link("Image Bans", make_link("image_hash_ban/list/1"));
|
||||
}
|
||||
}
|
||||
|
||||
public function onAddImageHashBan(AddImageHashBanEvent $event) {
|
||||
$this->add_image_hash_ban($event->hash, $event->reason);
|
||||
}
|
||||
|
||||
public function onRemoveImageHashBan(RemoveImageHashBanEvent $event) {
|
||||
$this->remove_image_hash_ban($event->hash);
|
||||
}
|
||||
|
||||
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) {
|
||||
global $user;
|
||||
if($user->is_admin()) {
|
||||
$event->add_part($this->theme->get_buttons_html($event->image));
|
||||
}
|
||||
}
|
||||
|
||||
// DB funness
|
||||
|
||||
public function get_image_hash_bans($page, $size=1000) {
|
||||
public function get_image_hash_bans($page, $size=100) {
|
||||
// FIXME: many
|
||||
$size_i = int_escape($size);
|
||||
$offset_i = int_escape($page-1)*$size_i;
|
||||
@ -144,6 +149,7 @@ class ImageBan implements Extension {
|
||||
$database->Execute("DELETE FROM image_bans WHERE hash = ?", array($hash));
|
||||
}
|
||||
|
||||
// in before resolution limit plugin
|
||||
public function get_priority() {return 30;}
|
||||
}
|
||||
add_event_listener(new ImageBan(), 30); // in before resolution limit plugin
|
||||
?>
|
||||
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
class ImageHashBanTest extends WebTestCase {}
|
||||
|
||||
if(!defined(VERSION)) return;
|
||||
|
||||
class ImageHashBanUnitTest extends UnitTestCase {
|
||||
public function _broken_testUploadFailsWhenBanned() {
|
||||
$ihb = new ImageHashBan();
|
||||
$due = new DataUploadEvent();
|
||||
|
||||
try {
|
||||
$ihb->receive_event($due);
|
||||
$this->assertTrue(false); // shouldn't work
|
||||
}
|
||||
catch(DataUploadException $ex) {
|
||||
$this->assertTrue(true); // should fail
|
||||
}
|
||||
catch(Exception $ex) {
|
||||
$this->assertTrue(false); // but not with any other error
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
@ -20,7 +20,7 @@ class ImageBanTheme extends Themelet {
|
||||
* 'date' => when the ban started
|
||||
* )
|
||||
*/
|
||||
public function display_image_hash_bans(Page $page, $page_number, $bans) {
|
||||
public function display_image_hash_bans(Page $page, $page_number, $page_count, $bans) {
|
||||
$h_bans = "";
|
||||
$n = 0;
|
||||
foreach($bans as $ban) {
|
||||
@ -30,7 +30,7 @@ class ImageBanTheme extends Themelet {
|
||||
<td width='30%'>{$ban['hash']}</td>
|
||||
<td>{$ban['reason']}</td>
|
||||
<td width='10%'>
|
||||
<form action='".make_link("image_hash_ban/remove")."' method='POST'>
|
||||
".make_form(make_link("image_hash_ban/remove"))."
|
||||
<input type='hidden' name='hash' value='{$ban['hash']}'>
|
||||
<input type='submit' value='Remove'>
|
||||
</form>
|
||||
@ -39,11 +39,16 @@ class ImageBanTheme extends Themelet {
|
||||
";
|
||||
}
|
||||
$html = "
|
||||
<table class='zebra'>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$(\"#image_bans\").tablesorter();
|
||||
});
|
||||
</script>
|
||||
<table id='image_bans' class='zebra'>
|
||||
<thead><th>Hash</th><th>Reason</th><th>Action</th></thead>
|
||||
$h_bans
|
||||
<tfoot><tr>
|
||||
<form action='".make_link("image_hash_ban/add")."' method='POST'>
|
||||
".make_form(make_link("image_hash_ban/add"))."
|
||||
<td><input type='text' name='hash'></td>
|
||||
<td><input type='text' name='reason'></td>
|
||||
<td><input type='submit' value='Ban'></td>
|
||||
@ -57,7 +62,7 @@ class ImageBanTheme extends Themelet {
|
||||
|
||||
$h_prev = ($page_number <= 1) ? "Prev" : "<a href='".make_link("image_hash_ban/list/$prev")."'>Prev</a>";
|
||||
$h_index = "<a href='".make_link()."'>Index</a>";
|
||||
$h_next = "<a href='".make_link("image_hash_ban/list/$next")."'>Next</a>";
|
||||
$h_next = ($page_number >= $page_count) ? "Next" : "<a href='".make_link("image_hash_ban/list/$next")."'>Next</a>";
|
||||
|
||||
$nav = "$h_prev | $h_index | $h_next";
|
||||
|
||||
@ -65,6 +70,7 @@ class ImageBanTheme extends Themelet {
|
||||
$page->set_heading("Image Bans");
|
||||
$page->add_block(new Block("Edit Image Bans", $html));
|
||||
$page->add_block(new Block("Navigation", $nav, "left", 0));
|
||||
$this->display_paginator($page, "image_hash_ban/list", null, $page_number, $page_count);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -74,7 +80,7 @@ class ImageBanTheme extends Themelet {
|
||||
*/
|
||||
public function get_buttons_html(Image $image) {
|
||||
$html = "
|
||||
<form action='".make_link("image_hash_ban/add")."' method='POST'>
|
||||
".make_form(make_link("image_hash_ban/add"))."
|
||||
<input type='hidden' name='hash' value='{$image->hash}'>
|
||||
<input type='hidden' name='image_id' value='{$image->id}'>
|
||||
<input type='text' name='reason'>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: IP Ban
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
@ -51,7 +51,7 @@ class IPBan implements Extension {
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("ip_ban")) {
|
||||
if($user->is_admin()) {
|
||||
if($event->get_arg(0) == "add") {
|
||||
if($event->get_arg(0) == "add" && $user->check_auth_token()) {
|
||||
if(isset($_POST['ip']) && isset($_POST['reason']) && isset($_POST['end'])) {
|
||||
if(empty($_POST['end'])) $end = null;
|
||||
else $end = $_POST['end'];
|
||||
@ -61,7 +61,7 @@ class IPBan implements Extension {
|
||||
$page->set_redirect(make_link("ip_ban/list"));
|
||||
}
|
||||
}
|
||||
else if($event->get_arg(0) == "remove") {
|
||||
else if($event->get_arg(0) == "remove" && $user->check_auth_token()) {
|
||||
if(isset($_POST['id'])) {
|
||||
send_event(new RemoveIPBanEvent($_POST['id']));
|
||||
$page->set_mode("redirect");
|
||||
@ -90,6 +90,7 @@ class IPBan implements Extension {
|
||||
|
||||
if($event instanceof RemoveIPBanEvent) {
|
||||
$database->Execute("DELETE FROM bans WHERE id = ?", array($event->id));
|
||||
$database->cache->delete("ip_bans");
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
@ -163,16 +164,20 @@ class IPBan implements Extension {
|
||||
global $config;
|
||||
global $database;
|
||||
|
||||
$prefix = ($database->engine->name == "sqlite" ? "bans." : "");
|
||||
|
||||
$remote = $_SERVER['REMOTE_ADDR'];
|
||||
$bans = $this->get_active_bans();
|
||||
foreach($bans as $row) {
|
||||
$ip = $row[$prefix."ip"];
|
||||
if(
|
||||
(strstr($row['ip'], '/') && ip_in_range($remote, $row['ip'])) ||
|
||||
($row['ip'] == $remote)
|
||||
(strstr($ip, '/') && ip_in_range($remote, $ip)) ||
|
||||
($ip == $remote)
|
||||
) {
|
||||
$admin = User::by_id($row['banner_id']);
|
||||
$date = date("Y-m-d", $row['end_timestamp']);
|
||||
print "IP <b>{$row['ip']}</b> has been banned until <b>$date</b> by <b>{$admin->name}</b> because of <b>{$row['reason']}</b>";
|
||||
$reason = $row[$prefix.'reason'];
|
||||
$admin = User::by_id($row[$prefix.'banner_id']);
|
||||
$date = date("Y-m-d", $row[$prefix.'end_timestamp']);
|
||||
print "IP <b>$ip</b> has been banned until <b>$date</b> by <b>{$admin->name}</b> because of <b>$reason</b>";
|
||||
|
||||
$contact_link = $config->get_string("contact_link");
|
||||
if(!empty($contact_link)) {
|
||||
@ -190,7 +195,7 @@ class IPBan implements Extension {
|
||||
SELECT bans.*, users.name as banner_name
|
||||
FROM bans
|
||||
JOIN users ON banner_id = users.id
|
||||
ORDER BY end_timestamp, id
|
||||
ORDER BY end_timestamp, bans.id
|
||||
");
|
||||
if($bans) {return $bans;}
|
||||
else {return array();}
|
||||
@ -199,18 +204,18 @@ class IPBan implements Extension {
|
||||
private function get_active_bans() {
|
||||
global $database;
|
||||
|
||||
$cached = $database->cache->get("bans");
|
||||
$cached = $database->cache->get("ip_bans");
|
||||
if($cached) return $cached;
|
||||
|
||||
$bans = $database->get_all("
|
||||
SELECT bans.*, users.name as banner_name
|
||||
FROM bans
|
||||
JOIN users ON banner_id = users.id
|
||||
WHERE (end_timestamp > UNIX_TIMESTAMP(now())) OR (end_timestamp IS NULL)
|
||||
ORDER BY end_timestamp, id
|
||||
");
|
||||
WHERE (end_timestamp > ?) OR (end_timestamp IS NULL)
|
||||
ORDER BY end_timestamp, bans.id
|
||||
", array(time()));
|
||||
|
||||
$database->cache->set("bans", $bans);
|
||||
$database->cache->set("ip_bans", $bans, 600);
|
||||
|
||||
if($bans) {return $bans;}
|
||||
else {return array();}
|
||||
@ -220,7 +225,7 @@ class IPBan implements Extension {
|
||||
global $database;
|
||||
$sql = "INSERT INTO bans (ip, reason, end_timestamp, banner_id) VALUES (?, ?, ?, ?)";
|
||||
$database->Execute($sql, array($ip, $reason, strtotime($end), $user->id));
|
||||
$database->cache->delete("bans");
|
||||
$database->cache->delete("ip_bans");
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
|
@ -1,28 +1,28 @@
|
||||
<?php
|
||||
class IPBanTest extends WebTestCase {
|
||||
class IPBanTest extends SCoreWebTestCase {
|
||||
function testIPBan() {
|
||||
$this->get(TEST_BASE.'/ip_ban/list');
|
||||
$this->assertResponse(403);
|
||||
$this->assertTitle("Permission Denied");
|
||||
$this->get_page('ip_ban/list');
|
||||
$this->assert_response(403);
|
||||
$this->assert_title("Permission Denied");
|
||||
|
||||
$this->get(TEST_BASE.'/user');
|
||||
$this->assertText("Login");
|
||||
$this->setField('user', ADMIN_NAME);
|
||||
$this->setField('pass', ADMIN_PASS);
|
||||
$this->click("Log In");
|
||||
$this->log_in_as_admin();
|
||||
|
||||
$this->get(TEST_BASE.'/ip_ban/list');
|
||||
$this->assertNoText("42.42.42.42");
|
||||
$this->setField('ip', '42.42.42.42');
|
||||
$this->setField('reason', 'unit testing');
|
||||
$this->setField('end', '1 week');
|
||||
$this->get_page('ip_ban/list');
|
||||
$this->assert_no_text("42.42.42.42");
|
||||
$this->set_field('ip', '42.42.42.42');
|
||||
$this->set_field('reason', 'unit testing');
|
||||
$this->set_field('end', '1 week');
|
||||
$this->click("Ban");
|
||||
|
||||
$this->assertText("42.42.42.42");
|
||||
$this->assert_text("42.42.42.42");
|
||||
$this->click("Remove"); // FIXME: remove which ban? :S
|
||||
$this->assertNoText("42.42.42.42");
|
||||
$this->assert_no_text("42.42.42.42");
|
||||
|
||||
$this->click('Log Out');
|
||||
$this->get_page('ip_ban/list?all=on'); // just test it doesn't crash for now
|
||||
|
||||
$this->log_out();
|
||||
|
||||
# FIXME: test that the IP is actually banned
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -12,21 +12,23 @@ class IPBanTheme extends Themelet {
|
||||
* )
|
||||
*/
|
||||
public function display_bans(Page $page, $bans) {
|
||||
global $user;
|
||||
global $database, $user;
|
||||
$h_bans = "";
|
||||
$n = 0;
|
||||
$prefix = ($database->engine->name == "sqlite" ? "bans." : "");
|
||||
$prefix2 = ($database->engine->name == "sqlite" ? "users." : "");
|
||||
foreach($bans as $ban) {
|
||||
$end_human = date('Y-m-d', $ban['end_timestamp']);
|
||||
$end_human = date('Y-m-d', $ban[$prefix.'end_timestamp']);
|
||||
$oe = ($n++ % 2 == 0) ? "even" : "odd";
|
||||
$h_bans .= "
|
||||
<tr class='$oe'>
|
||||
<td width='10%'>{$ban['ip']}</td>
|
||||
<td>{$ban['reason']}</td>
|
||||
<td width='10%'>{$ban[$prefix.'ip']}</td>
|
||||
<td>{$ban[$prefix.'reason']}</td>
|
||||
<td width='10%'>{$ban['banner_name']}</td>
|
||||
<td width='15%'>{$end_human}</td>
|
||||
<td width='10%'>
|
||||
<form action='".make_link("ip_ban/remove")."' method='POST'>
|
||||
<input type='hidden' name='id' value='{$ban['id']}'>
|
||||
".make_form(make_link("ip_ban/remove"))."
|
||||
<input type='hidden' name='id' value='{$ban[$prefix.'id']}'>
|
||||
<input type='submit' value='Remove'>
|
||||
</form>
|
||||
</td>
|
||||
@ -34,12 +36,17 @@ class IPBanTheme extends Themelet {
|
||||
";
|
||||
}
|
||||
$html = "
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$(\"#bans\").tablesorter();
|
||||
});
|
||||
</script>
|
||||
<a href='".make_link("ip_ban/list", "all=on")."'>Show All</a>
|
||||
<p><table class='zebra'>
|
||||
<thead><th>IP</th><th>Reason</th><th>By</th><th>Until</th><th>Action</th></thead>
|
||||
<p><table id='bans' class='zebra'>
|
||||
<thead><tr><th>IP</th><th>Reason</th><th>By</th><th>Until</th><th>Action</th></tr></thead>
|
||||
$h_bans
|
||||
<tfoot><tr>
|
||||
<form action='".make_link("ip_ban/add")."' method='POST'>
|
||||
".make_form(make_link("ip_ban/add"))."
|
||||
<td><input type='text' name='ip'></td>
|
||||
<td><input type='text' name='reason'></td>
|
||||
<td>{$user->name}</td>
|
||||
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* This file may not be distributed without its readme.txt
|
||||
**/
|
||||
|
||||
/* * * Link to Image * * */
|
||||
#Link_to_Image {
|
||||
/* allows borders to encompass the content; */
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
#Link_to_Image fieldset {
|
||||
width: 32%;
|
||||
float:left;
|
||||
min-width:25em;
|
||||
}
|
||||
|
||||
#Link_to_Image input, #Link_to_Image label {
|
||||
display:block;
|
||||
width:66%;
|
||||
float:left;
|
||||
margin-bottom:2.5px;
|
||||
}
|
||||
|
||||
#Link_to_Image label {
|
||||
width:30%;
|
||||
text-align:left;
|
||||
padding-right:2%;
|
||||
cursor:pointer;
|
||||
}
|
||||
#Link_to_Image input {
|
||||
font-size:0.7em;
|
||||
font-family:courier, fixed, monospace;
|
||||
}
|
||||
|
||||
#Link_to_Image br {
|
||||
clear:both;
|
||||
}
|
||||
|
||||
#Link_to_Image label:hover {
|
||||
border-bottom:1px dashed;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: Link to Image
|
||||
* Author: Artanis <artanis.00@gmail.com>
|
||||
* Description: Show various forms of link to each image, for copy & paste
|
||||
@ -12,9 +12,6 @@ class LinkImage implements Extension {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object($this);
|
||||
|
||||
if(($event instanceof DisplayingImageEvent)) {
|
||||
$data_href = get_base_href();
|
||||
$page->add_header("<link rel='stylesheet' href='$data_href/ext/link_image/_style.css' type='text/css'>",0);
|
||||
|
||||
$this->theme->links_block($page, $this->data($event->image));
|
||||
}
|
||||
if($event instanceof SetupBuildingEvent) {
|
||||
@ -28,17 +25,27 @@ class LinkImage implements Extension {
|
||||
'$title - $id ($ext $size $filesize)');
|
||||
}
|
||||
}
|
||||
|
||||
private function hostify($str) {
|
||||
$str = str_replace(" ", "%20", $str);
|
||||
if(strpos($str, "ttp://") > 0) {
|
||||
return $str;
|
||||
}
|
||||
else {
|
||||
return "http://" . $_SERVER["HTTP_HOST"] . $str;
|
||||
}
|
||||
}
|
||||
private function data($image) {
|
||||
global $config;
|
||||
|
||||
$text_link = $image->parse_link_template($config->get_string("ext_link-img_text-link_format"));
|
||||
$text_link = $text_link==" "? null : $text_link; // null blank setting so the url gets filled in on the text links.
|
||||
$text_link = trim($text_link) == "" ? null : $text_link; // null blank setting so the url gets filled in on the text links.
|
||||
|
||||
return array(
|
||||
'thumb_src' => $image->get_thumb_link(),
|
||||
'image_src' => $image->get_image_link(),
|
||||
'post_link' => $image->get_short_link(),
|
||||
'text_link' => $text_link);
|
||||
'thumb_src' => $this->hostify($image->get_thumb_link()),
|
||||
'image_src' => $this->hostify($image->get_image_link()),
|
||||
'post_link' => $this->hostify($_SERVER["REQUEST_URI"]),
|
||||
'text_link' => $text_link);
|
||||
}
|
||||
}
|
||||
add_event_listener(new LinkImage());
|
||||
|
23
contrib/link_image/test.php
Normal file
23
contrib/link_image/test.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
class LinkImageTest extends ShimmieWebTestCase {
|
||||
function testLinkImage() {
|
||||
$this->log_in_as_user();
|
||||
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pie");
|
||||
|
||||
# look in the "plain text link to post" box, follow the link
|
||||
# in there, see if it takes us to the right page
|
||||
$raw = $this->get_page("post/view/$image_id");
|
||||
$matches = array();
|
||||
preg_match("#value='(http://.*(/|%2F)post(/|%2F)view(/|%2F)[0-9]+)'#", $raw, $matches);
|
||||
$this->assertTrue(count($matches) > 0);
|
||||
$this->get($matches[1]);
|
||||
$this->assert_title("Image $image_id: pie");
|
||||
|
||||
$this->log_out();
|
||||
|
||||
$this->log_in_as_admin();
|
||||
$this->delete_image($image_id);
|
||||
$this->log_out();
|
||||
}
|
||||
}
|
||||
?>
|
@ -4,36 +4,55 @@ class LinkImageTheme extends Themelet {
|
||||
$thumb_src = $data['thumb_src'];
|
||||
$image_src = $data['image_src'];
|
||||
$post_link = $data['post_link'];
|
||||
$text_link = $data['text_link'];
|
||||
$text_link = $data['text_link'];
|
||||
|
||||
|
||||
$page->add_block( new Block(
|
||||
"Link to Image",
|
||||
"<fieldset>".
|
||||
"<legend><a href='http://en.wikipedia.org/wiki/Bbcode' target='_blank'>BBCode</a></legend>".
|
||||
$this->link_code("Text Link",$this->url($post_link, $text_link,"ubb"),"ubb_text-link").
|
||||
$this->link_code("Thumbnail Link",$this->url($post_link, $this->img($thumb_src,"ubb"),"ubb"),"ubb_thumb-link").
|
||||
$this->link_code("Inline Image", $this->img($image_src,"ubb"), "ubb_full-img").
|
||||
"</fieldset>".
|
||||
"
|
||||
<table><tr>
|
||||
|
||||
"<fieldset>".
|
||||
"<legend><a href='http://en.wikipedia.org/wiki/Html' target='_blank'>HTML</a></legend>".
|
||||
$this->link_code("Text Link", $this->url($post_link, $text_link,"html"), "html_text-link").
|
||||
$this->link_code("Thumbnail Link", $this->url($post_link,$this->img($thumb_src,"html"),"html"), "html_thumb-link").
|
||||
$this->link_code("Inline Image", $this->img($image_src,"html"), "html_full-image").
|
||||
"</fieldset>".
|
||||
<td><fieldset>
|
||||
<legend><a href='http://en.wikipedia.org/wiki/Bbcode' target='_blank'>BBCode</a></legend>
|
||||
<table>
|
||||
".
|
||||
$this->link_code("Link",$this->url($post_link, $text_link,"ubb"),"ubb_text-link").
|
||||
$this->link_code("Thumb",$this->url($post_link, $this->img($thumb_src,"ubb"),"ubb"),"ubb_thumb-link").
|
||||
$this->link_code("Image", $this->img($image_src,"ubb"), "ubb_full-img").
|
||||
"
|
||||
</table>
|
||||
</fieldset></td>
|
||||
|
||||
"<fieldset>".
|
||||
"<legend>Plain Text</legend>".
|
||||
$this->link_code("Post URL",$post_link,"text_post-link").
|
||||
$this->link_code("Thumbnail URL",$thumb_src,"text_thumb-url").
|
||||
$this->link_code("Image URL",$image_src,"text_image-src").
|
||||
"</fieldset>",
|
||||
<td><fieldset>
|
||||
<legend><a href='http://en.wikipedia.org/wiki/Html' target='_blank'>HTML</a></legend>
|
||||
<table>
|
||||
".
|
||||
$this->link_code("Link", $this->url($post_link, $text_link,"html"), "html_text-link").
|
||||
$this->link_code("Thumb", $this->url($post_link,$this->img($thumb_src,"html"),"html"), "html_thumb-link").
|
||||
$this->link_code("Image", $this->img($image_src,"html"), "html_full-image").
|
||||
"
|
||||
</table>
|
||||
</fieldset></td>
|
||||
|
||||
<td><fieldset>
|
||||
<legend>Plain Text</legend>
|
||||
<table>
|
||||
".
|
||||
$this->link_code("Link",$post_link,"text_post-link").
|
||||
$this->link_code("Thumb",$thumb_src,"text_thumb-url").
|
||||
$this->link_code("Image",$image_src,"text_image-src").
|
||||
"
|
||||
</table>
|
||||
</fieldset></td>
|
||||
|
||||
</tr></table>
|
||||
",
|
||||
"main",
|
||||
50));
|
||||
}
|
||||
|
||||
protected function url ($url,$content,$type) {
|
||||
if ($content == NULL) {$content=$url;}
|
||||
if ($content == NULL) {$content=$url;}
|
||||
|
||||
switch ($type) {
|
||||
case "html":
|
||||
@ -62,9 +81,14 @@ class LinkImageTheme extends Themelet {
|
||||
return $text;
|
||||
}
|
||||
|
||||
protected function link_code($label,$content,$id=NULL) {
|
||||
return "<label for='".$id."' title='Click to select the textbox'>$label</label>\n".
|
||||
"<input type='text' readonly='readonly' id='".$id."' name='".$id."' value='".html_escape($content)."' onfocus='this.select();'></input>\n<br/>\n";
|
||||
protected function link_code($label,$content,$id=NULL) {
|
||||
return "
|
||||
<tr>
|
||||
<td><label for='".$id."' title='Click to select the textbox'>$label</label></td>
|
||||
<td><input type='text' readonly='readonly' id='".$id."' name='".$id."'
|
||||
value='".html_escape($content)."' onfocus='this.select();'></input></td>
|
||||
</tr>
|
||||
";
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
132
contrib/log_db/main.php
Normal file
132
contrib/log_db/main.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/*
|
||||
* Name: Logging (Database)
|
||||
* Author: Shish
|
||||
* Description: Keep a record of SCore events
|
||||
* Visibility: admin
|
||||
*/
|
||||
|
||||
class LogDatabase extends SimpleExtension {
|
||||
public function onInitExt($event) {
|
||||
global $database;
|
||||
global $config;
|
||||
|
||||
if($config->get_int("ext_log_database_version") < 1) {
|
||||
$database->create_table("score_log", "
|
||||
id SCORE_AIPK,
|
||||
date_sent SCORE_DATETIME NOT NULL,
|
||||
section VARCHAR(32) NOT NULL,
|
||||
username VARCHAR(32) NOT NULL,
|
||||
address SCORE_INET NOT NULL,
|
||||
priority INT NOT NULL,
|
||||
message TEXT NOT NULL
|
||||
");
|
||||
//INDEX(section)
|
||||
$config->set_int("ext_log_database_version", 1);
|
||||
}
|
||||
|
||||
$config->set_default_int("log_db_priority", SCORE_LOG_INFO);
|
||||
}
|
||||
|
||||
public function onSetupBuilding($event) {
|
||||
$sb = new SetupBlock("Logging (Database)");
|
||||
$sb->add_choice_option("log_db_priority", array(
|
||||
"Debug" => SCORE_LOG_DEBUG,
|
||||
"Info" => SCORE_LOG_INFO,
|
||||
"Warning" => SCORE_LOG_WARNING,
|
||||
"Error" => SCORE_LOG_ERROR,
|
||||
"Critical" => SCORE_LOG_CRITICAL,
|
||||
), "Debug Level: ");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
|
||||
public function onPageRequest($event) {
|
||||
global $database, $user;
|
||||
if($event->page_matches("log/view")) {
|
||||
if($user->is_admin()) {
|
||||
$wheres = array();
|
||||
$args = array();
|
||||
$page_num = int_escape($event->get_arg(0));
|
||||
if($page_num <= 0) $page_num = 1;
|
||||
if(!empty($_GET["time"])) {
|
||||
$wheres[] = "date_sent LIKE ?";
|
||||
$args[] = $_GET["time"]."%";
|
||||
}
|
||||
if(!empty($_GET["module"])) {
|
||||
$wheres[] = "section = ?";
|
||||
$args[] = $_GET["module"];
|
||||
}
|
||||
if(!empty($_GET["user"])) {
|
||||
if($database->engine->name == "pgsql") {
|
||||
if(preg_match("#\d+\.\d+\.\d+\.\d+(/\d+)?#", $_GET["user"])) {
|
||||
$wheres[] = "(username = ? OR address << ?)";
|
||||
$args[] = $_GET["user"];
|
||||
$args[] = $_GET["user"];
|
||||
}
|
||||
else {
|
||||
$wheres[] = "lower(username) = lower(?)";
|
||||
$args[] = $_GET["user"];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$wheres[] = "(username = ? OR address = ?)";
|
||||
$args[] = $_GET["user"];
|
||||
$args[] = $_GET["user"];
|
||||
}
|
||||
}
|
||||
if(!empty($_GET["priority"])) {
|
||||
$wheres[] = "priority >= ?";
|
||||
$args[] = int_escape($_GET["priority"]);
|
||||
}
|
||||
else {
|
||||
$wheres[] = "priority >= ?";
|
||||
$args[] = 20;
|
||||
}
|
||||
$where = "";
|
||||
if(count($wheres) > 0) {
|
||||
$where = "WHERE ";
|
||||
$where .= join(" AND ", $wheres);
|
||||
}
|
||||
|
||||
$limit = 50;
|
||||
$offset = ($page_num-1) * $limit;
|
||||
$page_total = $database->cache->get("event_log_length");
|
||||
if(!$page_total) {
|
||||
$page_total = $database->db->GetOne("SELECT count(*) FROM score_log $where", $args);
|
||||
// don't cache a length of zero when the extension is first installed
|
||||
if($page_total > 10) {
|
||||
$database->cache->set("event_log_length", 600);
|
||||
}
|
||||
}
|
||||
|
||||
$args[] = $limit;
|
||||
$args[] = $offset;
|
||||
$events = $database->get_all("SELECT * FROM score_log $where ORDER BY id DESC LIMIT ? OFFSET ?", $args);
|
||||
|
||||
$this->theme->display_events($events, $page_num, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onUserBlockBuilding($event) {
|
||||
global $user;
|
||||
if($user->is_admin()) {
|
||||
$event->add_link("Event Log", make_link("log/view"));
|
||||
}
|
||||
}
|
||||
|
||||
public function onLog($event) {
|
||||
global $config, $database, $user;
|
||||
|
||||
// not installed yet...
|
||||
if($config->get_int("ext_log_database_version") < 1) return;
|
||||
|
||||
if($event->priority >= $config->get_int("log_db_priority")) {
|
||||
$database->execute("
|
||||
INSERT INTO score_log(date_sent, section, priority, username, address, message)
|
||||
VALUES(now(), ?, ?, ?, ?, ?)
|
||||
", array($event->section, $event->priority, $user->name, $_SERVER['REMOTE_ADDR'], $event->message));
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
8
contrib/log_db/test.php
Normal file
8
contrib/log_db/test.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
class LogDatabaseTest extends SCoreWebTestCase {
|
||||
function testLog() {
|
||||
$this->log_in_as_admin();
|
||||
$this->log_out();
|
||||
}
|
||||
}
|
||||
?>
|
106
contrib/log_db/theme.php
Normal file
106
contrib/log_db/theme.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
class LogDatabaseTheme extends Themelet {
|
||||
protected function heie($var) {
|
||||
if(isset($_GET[$var])) return html_escape($_GET[$var]);
|
||||
else return "";
|
||||
}
|
||||
|
||||
protected function ueie($var) {
|
||||
if(isset($_GET[$var])) return $var."=".url_escape($_GET[$var]);
|
||||
else return "";
|
||||
}
|
||||
|
||||
public function display_events($events, $page_num, $page_total) {
|
||||
$table = "
|
||||
<style>
|
||||
.sizedinputs TD INPUT {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<table class='zebra'>
|
||||
<thead>
|
||||
<tr><th>Time</th><th>Module</th><th>User</th><th>Message</th></tr>
|
||||
<form action='".make_link("log/view")."' method='GET'>
|
||||
<tr class='sizedinputs'>
|
||||
<td><input type='text' name='time' value='".$this->heie("time")."'></td>
|
||||
<td><input type='text' name='module' value='".$this->heie("module")."'></td>
|
||||
<td><input type='text' name='user' value='".$this->heie("user")."'></td>
|
||||
<td>
|
||||
<select name='priority'>
|
||||
<option value='".SCORE_LOG_DEBUG."'>Debug</option>
|
||||
<option value='".SCORE_LOG_INFO."' selected>Info</option>
|
||||
<option value='".SCORE_LOG_WARNING."'>Warning</option>
|
||||
<option value='".SCORE_LOG_ERROR."'>Error</option>
|
||||
<option value='".SCORE_LOG_CRITICAL."'>Critical</option>
|
||||
</select>
|
||||
<input type='submit' value='Search' style='width: 20%'>
|
||||
</td>
|
||||
</tr>
|
||||
</form>
|
||||
</thead>
|
||||
<tbody>\n";
|
||||
$n = 0;
|
||||
foreach($events as $event) {
|
||||
$oe = ($n++ % 2 == 0) ? "even" : "odd";
|
||||
$c = $this->pri_to_col($event['priority']);
|
||||
$table .= "<tr style='color: $c' class='$oe'>";
|
||||
$table .= "<td>".str_replace(" ", " ", $event['date_sent'])."</td>";
|
||||
$table .= "<td>".$event['section']."</td>";
|
||||
if($event['username'] == "Anonymous") {
|
||||
$table .= "<td>".$event['address']."</td>";
|
||||
}
|
||||
else {
|
||||
$table .= "<td><span title='".$event['address']."'>".
|
||||
"<a href='".make_link("user/".url_escape($event['username']))."'>".html_escape($event['username'])."</a>".
|
||||
"</span></td>";
|
||||
}
|
||||
$table .= "<td>".$this->scan_entities(html_escape($event['message']))."</td>";
|
||||
$table .= "</tr>\n";
|
||||
}
|
||||
$table .= "</tbody></table>";
|
||||
|
||||
global $page;
|
||||
$page->set_title("Event Log");
|
||||
$page->set_heading("Event Log");
|
||||
$page->add_block(new NavBlock());
|
||||
$page->add_block(new Block("Events", $table));
|
||||
|
||||
$args = "";
|
||||
// Check if each arg is actually empty and skip it if so
|
||||
if(strlen($this->ueie("time")))
|
||||
$args .= $this->ueie("time")."&";
|
||||
if(strlen($this->ueie("module")))
|
||||
$args .= $this->ueie("module")."&";
|
||||
if(strlen($this->ueie("user")))
|
||||
$args .= $this->ueie("user")."&";
|
||||
if(strlen($this->ueie("priority")))
|
||||
$args .= $this->ueie("priority");
|
||||
// If there are no args at all, set $args to null to prevent an unnecessary ? at the end of the paginator url
|
||||
if(strlen($args) == 0)
|
||||
$args = null;
|
||||
$this->display_paginator($page, "log/view", $args, $page_num, $page_total);
|
||||
}
|
||||
|
||||
protected function pri_to_col($pri) {
|
||||
switch($pri) {
|
||||
case SCORE_LOG_DEBUG: return "#999";
|
||||
case SCORE_LOG_INFO: return "#000";
|
||||
case SCORE_LOG_WARNING: return "#800";
|
||||
case SCORE_LOG_ERROR: return "#C00";
|
||||
case SCORE_LOG_CRITICAL: return "#F00";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
protected function scan_entities($line) {
|
||||
$line = preg_replace_callback("/Image #(\d+)/s", array($this, "link_image"), $line);
|
||||
return $line;
|
||||
}
|
||||
|
||||
protected function link_image($id) {
|
||||
$iid = int_escape($id[1]);
|
||||
return "<a href='".make_link("post/view/$iid")."'>Image #$iid</a>";
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: News
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
@ -8,25 +8,18 @@
|
||||
* Any HTML is allowed
|
||||
*/
|
||||
|
||||
class News 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 PostListBuildingEvent) {
|
||||
if(strlen($config->get_string("news_text")) > 0) {
|
||||
$this->theme->display_news($page, $config->get_string("news_text"));
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof SetupBuildingEvent) {
|
||||
$sb = new SetupBlock("News");
|
||||
$sb->add_longtext_option("news_text");
|
||||
$event->panel->add_block($sb);
|
||||
class News extends SimpleExtension {
|
||||
public function onPostListBuilding($event) {
|
||||
global $config, $page;
|
||||
if(strlen($config->get_string("news_text")) > 0) {
|
||||
$this->theme->display_news($page, $config->get_string("news_text"));
|
||||
}
|
||||
}
|
||||
|
||||
public function onSetupBuilding($event) {
|
||||
$sb = new SetupBlock("News");
|
||||
$sb->add_longtext_option("news_text");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
}
|
||||
add_event_listener(new News());
|
||||
?>
|
||||
|
25
contrib/news/test.php
Normal file
25
contrib/news/test.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
class NewsTest extends SCoreWebTestCase {
|
||||
function testNews() {
|
||||
$this->log_in_as_admin();
|
||||
|
||||
$this->get_page("setup");
|
||||
$this->set_field("_config_news_text", "kittens");
|
||||
$this->click("Save Settings");
|
||||
|
||||
$this->get_page("post/list");
|
||||
$this->assert_text("Note");
|
||||
$this->assert_text("kittens");
|
||||
|
||||
$this->get_page("setup");
|
||||
$this->set_field("_config_news_text", "");
|
||||
$this->click("Save Settings");
|
||||
|
||||
$this->get_page("post/list");
|
||||
$this->assert_no_text("Note");
|
||||
$this->assert_no_text("kittens");
|
||||
|
||||
$this->log_out();
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,52 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Image Notes
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Adds notes overlaid on the images
|
||||
* Documentation:
|
||||
* This is quite broken :(
|
||||
*/
|
||||
|
||||
class Notes 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 InitExtEvent) {
|
||||
if($config->get_int("ext_notes_version") < 1) {
|
||||
$this->install();
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof DisplayingImageEvent) {
|
||||
$notes = $database->get_all("SELECT * FROM image_notes WHERE image_id = ?", array($event->image->id));
|
||||
$this->theme->display_notes($page, $notes);
|
||||
}
|
||||
}
|
||||
|
||||
protected function install() {
|
||||
global $database;
|
||||
global $config;
|
||||
$database->create_table("image_notes", "
|
||||
id SCORE_AIPK,
|
||||
image_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
owner_ip SCORE_INET NOT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
updated_at DATETIME NOT NULL,
|
||||
version INTEGER DEFAULT 1 NOT NULL,
|
||||
is_active SCORE_BOOL DEFAULT SCORE_BOOL_Y NOT NULL,
|
||||
x INTEGER NOT NULL,
|
||||
y INTEGER NOT NULL,
|
||||
w INTEGER NOT NULL,
|
||||
h INTEGER NOT NULL,
|
||||
body TEXT NOT NULL
|
||||
");
|
||||
$config->set_int("ext_notes_version", 1);
|
||||
}
|
||||
}
|
||||
add_event_listener(new Notes());
|
||||
?>
|
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
class NotesTheme extends Themelet {
|
||||
public function display_notes(Page $page, $notes) {
|
||||
$html = <<<EOD
|
||||
<script type="text/javascript">
|
||||
img = byId("main_image");
|
||||
</script>
|
||||
EOD;
|
||||
$page->add_block(new Block(null, $html));
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: Image Scores (Numeric)
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
@ -35,24 +35,77 @@ class NumericScore implements Extension {
|
||||
if($event instanceof DisplayingImageEvent) {
|
||||
if(!$user->is_anonymous()) {
|
||||
$html = $this->theme->get_voter_html($event->image);
|
||||
if($user->is_admin()) {
|
||||
$html .= "<p><a href='/numeric_score_votes/{$event->image->id}'>See All Votes</a>";
|
||||
}
|
||||
$page->add_block(new Block("Image Score", $html, "left", 20));
|
||||
}
|
||||
}
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("numeric_score_vote")) {
|
||||
if(!$user->is_anonymous()) {
|
||||
$image_id = int_escape($_POST['image_id']);
|
||||
$char = $_POST['vote'];
|
||||
$score = 0;
|
||||
if($char == "up") $score = 1;
|
||||
else if($char == "down") $score = -1;
|
||||
if($score != 0) send_event(new NumericScoreSetEvent($image_id, $user, $score));
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("post/view/$image_id"));
|
||||
if($event instanceof PageRequestEvent) {
|
||||
if($event->page_matches("numeric_score_votes")) {
|
||||
$image_id = int_escape($event->get_arg(0));
|
||||
$x = $database->get_all(
|
||||
"SELECT users.name as username, user_id, score
|
||||
FROM numeric_score_votes
|
||||
JOIN users ON numeric_score_votes.user_id=users.id
|
||||
WHERE image_id=?",
|
||||
array($image_id));
|
||||
$html = "<table>";
|
||||
foreach($x as $vote) {
|
||||
$html .= "<tr><td>";
|
||||
$html .= "<a href='/user/{$vote['username']}'>{$vote['username']}</a>";
|
||||
$html .= "</td><td>";
|
||||
$html .= $vote['score'];
|
||||
$html .= "</td></tr>";
|
||||
}
|
||||
die($html);
|
||||
}
|
||||
if($event->page_matches("numeric_score_vote") && $user->check_auth_token()) {
|
||||
if(!$user->is_anonymous()) {
|
||||
$image_id = int_escape($_POST['image_id']);
|
||||
$char = $_POST['vote'];
|
||||
$score = null;
|
||||
if($char == "up") $score = 1;
|
||||
else if($char == "null") $score = 0;
|
||||
else if($char == "down") $score = -1;
|
||||
if(!is_null($score) && $image_id>0) send_event(new NumericScoreSetEvent($image_id, $user, $score));
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("post/view/$image_id"));
|
||||
}
|
||||
}
|
||||
if($event->page_matches("numeric_score/remove_votes_on") && $user->check_auth_token()) {
|
||||
if($user->is_admin()) {
|
||||
$image_id = int_escape($_POST['image_id']);
|
||||
$database->execute(
|
||||
"DELETE FROM numeric_score_votes WHERE image_id=?",
|
||||
array($image_id));
|
||||
$database->execute(
|
||||
"UPDATE images SET numeric_score=0 WHERE id=?",
|
||||
array($image_id));
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("post/view/$image_id"));
|
||||
}
|
||||
}
|
||||
if($event->page_matches("numeric_score/remove_votes_by") && $user->check_auth_token()) {
|
||||
if($user->is_admin()) {
|
||||
$user_id = int_escape($_POST['user_id']);
|
||||
$image_ids = $database->get_col("SELECT image_id FROM numeric_score_votes WHERE user_id=?", array($user_id));
|
||||
|
||||
$database->execute(
|
||||
"DELETE FROM numeric_score_votes WHERE user_id=? AND image_id IN ?",
|
||||
array($user_id, $image_ids));
|
||||
$database->execute(
|
||||
"UPDATE images SET numeric_score=(SELECT SUM(score) FROM numeric_score_votes WHERE image_id=images.id) WHERE images.id IN ?",
|
||||
array($image_ids));
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("post/view/$image_id"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof NumericScoreSetEvent) {
|
||||
log_info("numeric_score", "Rated Image #{$event->image_id} as {$event->score}");
|
||||
$this->add_vote($event->image_id, $user->id, $event->score);
|
||||
}
|
||||
|
||||
@ -69,10 +122,39 @@ class NumericScore implements Extension {
|
||||
if(preg_match("/^score(<|<=|=|>=|>)(\d+)$/", $event->term, $matches)) {
|
||||
$cmp = $matches[1];
|
||||
$score = $matches[2];
|
||||
$event->set_querylet(new Querylet("numeric_score $cmp $score"));
|
||||
$event->add_querylet(new Querylet("numeric_score $cmp $score"));
|
||||
}
|
||||
if(preg_match("/^favou?rite$/", $event->term, $matches)) {
|
||||
$event->set_querylet(new Querylet("images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=? AND score=1)", array($user->id)));
|
||||
if(preg_match("/^upvoted_by=(.*)$/", $event->term, $matches)) {
|
||||
$duser = User::by_name($matches[1]);
|
||||
if(is_null($duser)) {
|
||||
throw new SearchTermParseException(
|
||||
"Can't find the user named ".html_escape($matches[1]));
|
||||
}
|
||||
$event->add_querylet(new Querylet(
|
||||
"images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=? AND score=1)",
|
||||
array($duser->id)));
|
||||
}
|
||||
if(preg_match("/^downvoted_by=(.*)$/", $event->term, $matches)) {
|
||||
$duser = User::by_name($matches[1]);
|
||||
if(is_null($duser)) {
|
||||
throw new SearchTermParseException(
|
||||
"Can't find the user named ".html_escape($matches[1]));
|
||||
}
|
||||
$event->add_querylet(new Querylet(
|
||||
"images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=? AND score=-1)",
|
||||
array($duser->id)));
|
||||
}
|
||||
if(preg_match("/^upvoted_by_id=(\d+)$/", $event->term, $matches)) {
|
||||
$iid = int_escape($matches[1]);
|
||||
$event->add_querylet(new Querylet(
|
||||
"images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=? AND score=1)",
|
||||
array($iid)));
|
||||
}
|
||||
if(preg_match("/^downvoted_by_id=(\d+)$/", $event->term, $matches)) {
|
||||
$iid = int_escape($matches[1]);
|
||||
$event->add_querylet(new Querylet(
|
||||
"images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=? AND score=-1)",
|
||||
array($iid)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -96,7 +178,7 @@ class NumericScore implements Extension {
|
||||
$config->set_int("ext_numeric_score_version", 1);
|
||||
}
|
||||
if($config->get_int("ext_numeric_score_version") < 2) {
|
||||
$database->Execute("CREATE INDEX numeric_score_votes__user_votes ON numeric_score_votes(user_id, scores)");
|
||||
$database->Execute("CREATE INDEX numeric_score_votes__user_votes ON numeric_score_votes(user_id, score)");
|
||||
$config->set_int("ext_numeric_score_version", 2);
|
||||
}
|
||||
}
|
||||
@ -106,9 +188,11 @@ class NumericScore implements Extension {
|
||||
$database->Execute(
|
||||
"DELETE FROM numeric_score_votes WHERE image_id=? AND user_id=?",
|
||||
array($image_id, $user_id));
|
||||
$database->Execute(
|
||||
"INSERT INTO numeric_score_votes(image_id, user_id, score) VALUES(?, ?, ?)",
|
||||
array($image_id, $user_id, $score));
|
||||
if($score != 0) {
|
||||
$database->Execute(
|
||||
"INSERT INTO numeric_score_votes(image_id, user_id, score) VALUES(?, ?, ?)",
|
||||
array($image_id, $user_id, $score));
|
||||
}
|
||||
$database->Execute(
|
||||
"UPDATE images SET numeric_score=(SELECT SUM(score) FROM numeric_score_votes WHERE image_id=?) WHERE id=?",
|
||||
array($image_id, $image_id));
|
||||
|
57
contrib/numeric_score/test.php
Normal file
57
contrib/numeric_score/test.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
class NumericScoreTest extends ShimmieWebTestCase {
|
||||
function testNumericScore() {
|
||||
$this->log_in_as_user();
|
||||
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pbx");
|
||||
$this->get_page("post/view/$image_id");
|
||||
$this->assert_text("Current Score: 0");
|
||||
$this->click("Vote Down");
|
||||
$this->assert_text("Current Score: -1");
|
||||
$this->click("Vote Up");
|
||||
$this->assert_text("Current Score: 1");
|
||||
# FIXME: "remove vote" button?
|
||||
# FIXME: test that up and down are hidden if already voted up or down
|
||||
|
||||
# test search by score
|
||||
$this->get_page("post/list/score=1/1");
|
||||
$this->assert_title("Image $image_id: pbx");
|
||||
|
||||
$this->get_page("post/list/score>0/1");
|
||||
$this->assert_title("Image $image_id: pbx");
|
||||
|
||||
$this->get_page("post/list/score>-5/1");
|
||||
$this->assert_title("Image $image_id: pbx");
|
||||
|
||||
$this->get_page("post/list/-score>5/1");
|
||||
$this->assert_title("Image $image_id: pbx");
|
||||
|
||||
$this->get_page("post/list/-score<-5/1");
|
||||
$this->assert_title("Image $image_id: pbx");
|
||||
|
||||
# test search by vote
|
||||
$this->get_page("post/list/upvoted_by=test/1");
|
||||
$this->assert_title("Image $image_id: pbx");
|
||||
$this->assert_no_text("No Images Found");
|
||||
|
||||
# and downvote
|
||||
$this->get_page("post/list/downvoted_by=test/1");
|
||||
$this->assert_text("No Images Found");
|
||||
|
||||
# test errors
|
||||
$this->get_page("post/list/upvoted_by=asdfasdf/1");
|
||||
$this->assert_text("No Images Found");
|
||||
$this->get_page("post/list/downvoted_by=asdfasdf/1");
|
||||
$this->assert_text("No Images Found");
|
||||
$this->get_page("post/list/upvoted_by_id=0/1");
|
||||
$this->assert_text("No Images Found");
|
||||
$this->get_page("post/list/downvoted_by_id=0/1");
|
||||
$this->assert_text("No Images Found");
|
||||
|
||||
$this->log_out();
|
||||
|
||||
$this->log_in_as_admin();
|
||||
$this->delete_image($image_id);
|
||||
$this->log_out();
|
||||
}
|
||||
}
|
||||
?>
|
@ -2,6 +2,7 @@
|
||||
|
||||
class NumericScoreTheme extends Themelet {
|
||||
public function get_voter_html(Image $image) {
|
||||
global $user;
|
||||
$i_image_id = int_escape($image->id);
|
||||
$i_score = int_escape($image->numeric_score);
|
||||
|
||||
@ -9,17 +10,35 @@ class NumericScoreTheme extends Themelet {
|
||||
Current Score: $i_score
|
||||
|
||||
<p><form action='".make_link("numeric_score_vote")."' method='POST'>
|
||||
".$user->get_auth_html()."
|
||||
<input type='hidden' name='image_id' value='$i_image_id'>
|
||||
<input type='hidden' name='vote' value='up'>
|
||||
<input type='submit' value='Vote Up'>
|
||||
</form>
|
||||
|
||||
<p><form action='".make_link("numeric_score_vote")."' method='POST'>
|
||||
<form action='".make_link("numeric_score_vote")."' method='POST'>
|
||||
".$user->get_auth_html()."
|
||||
<input type='hidden' name='image_id' value='$i_image_id'>
|
||||
<input type='hidden' name='vote' value='null'>
|
||||
<input type='submit' value='Remove Vote'>
|
||||
</form>
|
||||
|
||||
<form action='".make_link("numeric_score_vote")."' method='POST'>
|
||||
".$user->get_auth_html()."
|
||||
<input type='hidden' name='image_id' value='$i_image_id'>
|
||||
<input type='hidden' name='vote' value='down'>
|
||||
<input type='submit' value='Vote Down'>
|
||||
</form>
|
||||
";
|
||||
if($user->is_admin()) {
|
||||
$html .= "
|
||||
<form action='".make_link("numeric_score/remove_votes_on")."' method='POST'>
|
||||
".$user->get_auth_html()."
|
||||
<input type='hidden' name='image_id' value='$i_image_id'>
|
||||
<input type='submit' value='Remove All Votes'>
|
||||
</form>
|
||||
";
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: PicLens Button
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Adds a link to piclensify the gallery
|
||||
* Documentation:
|
||||
* This extension only provides a button to the javascript
|
||||
* version of the gallery; the "RSS for Images" extension
|
||||
* is piclens-compatible to start with. (And that extension
|
||||
* must be active for this one to do anything useful)
|
||||
*/
|
||||
class PicLens implements Extension {
|
||||
public function receive_event(Event $event) {
|
||||
global $page;
|
||||
if($event instanceof PageRequestEvent) {
|
||||
$page->add_header("<script type=\"text/javascript\" src=\"http://lite.piclens.com/current/piclens.js\"></script>");
|
||||
}
|
||||
if($event instanceof PostListBuildingEvent) {
|
||||
$foo='
|
||||
<a href="javascript:PicLensLite.start();">Start Slideshow
|
||||
<img src="http://lite.piclens.com/images/PicLensButton.png"
|
||||
alt="PicLens" width="16" height="12" border="0"
|
||||
align="absmiddle"></a>';
|
||||
$page->add_block(new Block("PicLens", $foo, "left", 20));
|
||||
}
|
||||
}
|
||||
}
|
||||
add_event_listener(new PicLens());
|
||||
?>
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: Private Messaging
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
@ -11,110 +11,39 @@
|
||||
*/
|
||||
|
||||
class SendPMEvent extends Event {
|
||||
public function __construct($from_id, $from_ip, $to_id, $subject, $message) {
|
||||
$this->from_id = $from_id;
|
||||
$this->from_ip = $from_ip;
|
||||
$this->to_id = $to_id;
|
||||
$this->subject = $subject;
|
||||
$this->message = $message;
|
||||
public function __construct($pm) {
|
||||
$this->pm = $pm;
|
||||
}
|
||||
}
|
||||
|
||||
class PM 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 InitExtEvent) {
|
||||
if($config->get_int("pm_version") < 1) {
|
||||
$this->install();
|
||||
}
|
||||
class PM {
|
||||
public function __construct($from_id=0, $from_ip="0.0.0.0", $to_id=0, $subject="A Message", $message="Some Text", $read=False) {
|
||||
# PHP: the P stands for "really", the H stands for "awful" and the other P stands for "language"
|
||||
if(is_array($from_id)) {
|
||||
$a = $from_id;
|
||||
$this->id = $a["id"];
|
||||
$this->from_id = $a["from_id"];
|
||||
$this->from_ip = $a["from_ip"];
|
||||
$this->to_id = $a["to_id"];
|
||||
$this->sent_date = $a["sent_date"];
|
||||
$this->subject = $a["subject"];
|
||||
$this->message = $a["message"];
|
||||
$this->is_read = undb_bool($a["is_read"]);
|
||||
}
|
||||
|
||||
/*
|
||||
if($event instanceof UserBlockBuildingEvent) {
|
||||
if(!$user->is_anonymous()) {
|
||||
$event->add_link("Private Messages", make_link("pm"));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if($event instanceof UserPageBuildingEvent) {
|
||||
$duser = $event->display_user;
|
||||
if(!$user->is_anonymous() && !$duser->is_anonymous()) {
|
||||
if(($user->id == $duser->id) || $user->is_admin()) {
|
||||
$this->theme->display_pms($page, $this->get_pms($duser));
|
||||
}
|
||||
if($user->id != $duser->id) {
|
||||
$this->theme->display_composer($page, $user, $duser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("pm")) {
|
||||
if(!$user->is_anonymous()) {
|
||||
switch($event->get_arg(0)) {
|
||||
case "read":
|
||||
$pm_id = int_escape($event->get_arg(1));
|
||||
$pm = $database->get_row("SELECT * FROM private_message WHERE id = ?", array($pm_id));
|
||||
if(is_null($pm)) {
|
||||
$this->theme->display_error($page, "No such PM", "There is no PM #$pm_id");
|
||||
}
|
||||
else if(($pm["to_id"] == $user->id) || $user->is_admin()) {
|
||||
$from_user = User::by_id(int_escape($pm["from_id"]));
|
||||
$database->get_row("UPDATE private_message SET is_read='Y' WHERE id = ?", array($pm_id));
|
||||
$this->theme->display_message($page, $from_user, $user, $pm);
|
||||
}
|
||||
else {
|
||||
// permission denied
|
||||
}
|
||||
break;
|
||||
case "delete":
|
||||
$pm_id = int_escape($event->get_arg(1));
|
||||
$pm = $database->get_row("SELECT * FROM private_message WHERE id = ?", array($pm_id));
|
||||
if(is_null($pm)) {
|
||||
$this->theme->display_error($page, "No such PM", "There is no PM #$pm_id");
|
||||
}
|
||||
else if(($pm["to_id"] == $user->id) || $user->is_admin()) {
|
||||
$database->execute("DELETE FROM private_message WHERE id = ?", array($pm_id));
|
||||
log_info("pm", "Deleted PM #$pm_id");
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("user"));
|
||||
}
|
||||
else {
|
||||
// permission denied
|
||||
}
|
||||
break;
|
||||
case "send":
|
||||
$to_id = int_escape($_POST["to_id"]);
|
||||
$from_id = $user->id;
|
||||
$subject = $_POST["subject"];
|
||||
$message = $_POST["message"];
|
||||
send_event(new SendPMEvent($from_id, $_SERVER["REMOTE_ADDR"], $to_id, $subject, $message));
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link($_SERVER["REFERER"]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof SendPMEvent) {
|
||||
$database->execute("
|
||||
INSERT INTO private_message(
|
||||
from_id, from_ip, to_id,
|
||||
sent_date, subject, message)
|
||||
VALUES(?, ?, ?, now(), ?, ?)",
|
||||
array($event->from_id, $event->from_ip,
|
||||
$event->to_id, $event->subject, $event->message)
|
||||
);
|
||||
log_info("pm", "Sent PM to User #$to_id");
|
||||
else {
|
||||
$this->id = -1;
|
||||
$this->from_id = $from_id;
|
||||
$this->from_ip = $from_ip;
|
||||
$this->to_id = $to_id;
|
||||
$this->subject = $subject;
|
||||
$this->message = $message;
|
||||
$this->is_read = $read;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function install() {
|
||||
class PrivMsg extends SimpleExtension {
|
||||
public function onInitExt($event) {
|
||||
global $config, $database;
|
||||
|
||||
// shortcut to latest
|
||||
@ -131,21 +60,114 @@ class PM implements Extension {
|
||||
INDEX (to_id)
|
||||
");
|
||||
$config->set_int("pm_version", 1);
|
||||
log_info("pm", "extension installed");
|
||||
}
|
||||
|
||||
log_info("pm", "extension installed");
|
||||
}
|
||||
|
||||
/*
|
||||
public function onUserBlockBuilding($event) {
|
||||
global $user;
|
||||
if(!$user->is_anonymous()) {
|
||||
$event->add_link("Private Messages", make_link("pm"));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public function onUserPageBuilding($event) {
|
||||
global $page, $user;
|
||||
$duser = $event->display_user;
|
||||
if(!$user->is_anonymous() && !$duser->is_anonymous()) {
|
||||
if(($user->id == $duser->id) || $user->is_admin()) {
|
||||
$this->theme->display_pms($page, $this->get_pms($duser));
|
||||
}
|
||||
if($user->id != $duser->id) {
|
||||
$this->theme->display_composer($page, $user, $duser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onPageRequest($event) {
|
||||
global $database, $page, $user;
|
||||
if($event->page_matches("pm")) {
|
||||
if(!$user->is_anonymous()) {
|
||||
switch($event->get_arg(0)) {
|
||||
case "read":
|
||||
$pm_id = int_escape($event->get_arg(1));
|
||||
$pm = $database->get_row("SELECT * FROM private_message WHERE id = ?", array($pm_id));
|
||||
if(is_null($pm)) {
|
||||
$this->theme->display_error($page, "No such PM", "There is no PM #$pm_id");
|
||||
}
|
||||
else if(($pm["to_id"] == $user->id) || $user->is_admin()) {
|
||||
$from_user = User::by_id(int_escape($pm["from_id"]));
|
||||
$database->get_row("UPDATE private_message SET is_read='Y' WHERE id = ?", array($pm_id));
|
||||
$this->theme->display_message($page, $from_user, $user, new PM($pm));
|
||||
}
|
||||
else {
|
||||
// permission denied
|
||||
}
|
||||
break;
|
||||
case "delete":
|
||||
if($user->check_auth_token()) {
|
||||
$pm_id = int_escape($_POST["pm_id"]);
|
||||
$pm = $database->get_row("SELECT * FROM private_message WHERE id = ?", array($pm_id));
|
||||
if(is_null($pm)) {
|
||||
$this->theme->display_error($page, "No such PM", "There is no PM #$pm_id");
|
||||
}
|
||||
else if(($pm["to_id"] == $user->id) || $user->is_admin()) {
|
||||
$database->execute("DELETE FROM private_message WHERE id = ?", array($pm_id));
|
||||
log_info("pm", "Deleted PM #$pm_id");
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect($_SERVER["HTTP_REFERER"]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "send":
|
||||
if($user->check_auth_token()) {
|
||||
$to_id = int_escape($_POST["to_id"]);
|
||||
$from_id = $user->id;
|
||||
$subject = $_POST["subject"];
|
||||
$message = $_POST["message"];
|
||||
send_event(new SendPMEvent(new PM($from_id, $_SERVER["REMOTE_ADDR"], $to_id, $subject, $message)));
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect($_SERVER["HTTP_REFERER"]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$this->theme->display_error($page, "Invalid action", "That's not something you can do with a PM");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onSendPM($event) {
|
||||
global $database;
|
||||
$database->execute("
|
||||
INSERT INTO private_message(
|
||||
from_id, from_ip, to_id,
|
||||
sent_date, subject, message)
|
||||
VALUES(?, ?, ?, now(), ?, ?)",
|
||||
array($event->pm->from_id, $event->pm->from_ip,
|
||||
$event->pm->to_id, $event->pm->subject, $event->pm->message)
|
||||
);
|
||||
log_info("pm", "Sent PM to User #{$event->pm->to_id}");
|
||||
}
|
||||
|
||||
|
||||
private function get_pms(User $user) {
|
||||
global $database;
|
||||
|
||||
return $database->get_all("
|
||||
$arr = $database->get_all("
|
||||
SELECT private_message.*,user_from.name AS from_name
|
||||
FROM private_message
|
||||
JOIN users AS user_from ON user_from.id=from_id
|
||||
WHERE to_id = ?
|
||||
", array($user->id));
|
||||
$pms = array();
|
||||
foreach($arr as $pm) {
|
||||
$pms[] = new PM($pm);
|
||||
}
|
||||
return $pms;
|
||||
}
|
||||
}
|
||||
add_event_listener(new PM());
|
||||
?>
|
||||
|
52
contrib/pm/test.php
Normal file
52
contrib/pm/test.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
class PrivMsgTest extends SCoreWebTestCase {
|
||||
function testPM() {
|
||||
$this->log_in_as_admin();
|
||||
$this->get_page("user/test");
|
||||
$this->set_field('subject', "message demo to test");
|
||||
$this->set_field('message', "message contents");
|
||||
$this->click("Send");
|
||||
$this->log_out();
|
||||
|
||||
$this->log_in_as_user();
|
||||
$this->get_page("user");
|
||||
$this->assert_text("message demo to test");
|
||||
$this->click("message demo to test");
|
||||
$this->assert_text("message contents");
|
||||
$this->back();
|
||||
$this->click("Delete");
|
||||
$this->assert_no_text("message demo to test");
|
||||
|
||||
$this->get_page("pm/read/0");
|
||||
$this->assert_text("No such PM");
|
||||
$this->get_page("pm/delete/0");
|
||||
$this->assert_text("No such PM");
|
||||
$this->get_page("pm/waffle/0");
|
||||
$this->assert_text("Invalid action");
|
||||
|
||||
$this->log_out();
|
||||
}
|
||||
|
||||
function testAdminAccess() {
|
||||
$this->log_in_as_admin();
|
||||
$this->get_page("user/test");
|
||||
$this->set_field('subject', "message demo to test");
|
||||
$this->set_field('message', "message contents");
|
||||
$this->click("Send");
|
||||
|
||||
$this->get_page("user/test");
|
||||
$this->assert_text("message demo to test");
|
||||
$this->click("message demo to test");
|
||||
$this->assert_text("message contents");
|
||||
$this->back();
|
||||
$this->click("Delete");
|
||||
|
||||
# simpletest bug? - redirect(referrer) works in opera, not in
|
||||
# webtestcase, so we end up at the wrong page...
|
||||
$this->get_page("user/test");
|
||||
$this->assert_title("test's Page");
|
||||
$this->assert_no_text("message demo to test");
|
||||
$this->log_out();
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,32 +1,54 @@
|
||||
<?php
|
||||
|
||||
class PMTheme extends Themelet {
|
||||
class PrivMsgTheme extends Themelet {
|
||||
public function display_pms(Page $page, $pms) {
|
||||
$html = "<table>";
|
||||
$html .= "<tr><th>Subject</th><th>From</th><th>Date</th><th>Action</th></tr>";
|
||||
global $user;
|
||||
|
||||
$html = "
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$(\"#pms\").tablesorter();
|
||||
});
|
||||
</script>
|
||||
<table id='pms' class='zebra'>
|
||||
<thead><tr><th>Subject</th><th>From</th><th>Date</th><th>Action</th></tr></thead>
|
||||
<tbody>";
|
||||
$n = 0;
|
||||
foreach($pms as $pm) {
|
||||
$h_subject = html_escape($pm["subject"]);
|
||||
$oe = ($n++ % 2 == 0) ? "even" : "odd";
|
||||
$h_subject = html_escape($pm->subject);
|
||||
if(strlen(trim($h_subject)) == 0) $h_subject = "(No subject)";
|
||||
$h_from = html_escape($pm["from_name"]);
|
||||
$from_url = make_link("user/".url_escape($pm["from_name"]));
|
||||
$pm_url = make_link("pm/read/".$pm["id"]);
|
||||
$del_url = make_link("pm/delete/".$pm["id"]);
|
||||
$h_date = html_escape($pm["sent_date"]);
|
||||
if($pm["is_read"] == "N") $h_subject = "<b>$h_subject</b>";
|
||||
$html .= "<tr><td><a href='$pm_url'>$h_subject</a></td>
|
||||
$from_name = User::by_id($pm->from_id)->name;
|
||||
$h_from = html_escape($from_name);
|
||||
$from_url = make_link("user/".url_escape($from_name));
|
||||
$pm_url = make_link("pm/read/".$pm->id);
|
||||
$del_url = make_link("pm/delete");
|
||||
$h_date = html_escape($pm->sent_date);
|
||||
if($pm->is_read) $h_subject = "<b>$h_subject</b>";
|
||||
$html .= "<tr class='$oe'><td><a href='$pm_url'>$h_subject</a></td>
|
||||
<td><a href='$from_url'>$h_from</a></td><td>$h_date</td>
|
||||
<td><form action='$del_url'><input type='submit' value='Delete'></form></td></tr>";
|
||||
<td><form action='$del_url' method='POST'>
|
||||
<input type='hidden' name='pm_id' value='{$pm->id}'>
|
||||
".$user->get_auth_html()."
|
||||
<input type='submit' value='Delete'>
|
||||
</form></td></tr>";
|
||||
}
|
||||
$html .= "</table>";
|
||||
$html .= "
|
||||
</tbody>
|
||||
</table>
|
||||
";
|
||||
$page->add_block(new Block("Private Messages", $html, "main", 10));
|
||||
}
|
||||
|
||||
public function display_composer(Page $page, User $from, User $to, $subject="") {
|
||||
global $user;
|
||||
$post_url = make_link("pm/send");
|
||||
$h_subject = html_escape($subject);
|
||||
$to_id = $to->id;
|
||||
$auth = $user->get_auth_html();
|
||||
$html = <<<EOD
|
||||
<form action="$post_url" method="POST">
|
||||
$auth
|
||||
<input type="hidden" name="to_id" value="$to_id">
|
||||
<table style="width: 400px;">
|
||||
<tr><td>Subject:</td><td><input type="text" name="subject" value="$h_subject"></td></tr>
|
||||
@ -38,12 +60,12 @@ EOD;
|
||||
$page->add_block(new Block("Write a PM", $html, "main", 20));
|
||||
}
|
||||
|
||||
public function display_message(Page $page, User $from, User $to, $pm) {
|
||||
$this->display_composer($page, $to, $from, "Re: ".$pm["subject"]);
|
||||
public function display_message(Page $page, User $from, User $to, PM $pm) {
|
||||
$this->display_composer($page, $to, $from, "Re: ".$pm->subject);
|
||||
$page->set_title("Private Message");
|
||||
$page->set_heading(html_escape($pm["subject"]));
|
||||
$page->set_heading(html_escape($pm->subject));
|
||||
$page->add_block(new NavBlock());
|
||||
$page->add_block(new Block("Message", format_text($pm["message"]), "main", 10));
|
||||
$page->add_block(new Block("Message", format_text($pm->message), "main", 10));
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
686
contrib/pools/main.php
Normal file
686
contrib/pools/main.php
Normal file
@ -0,0 +1,686 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Pools System
|
||||
* Author: Sein Kraft <mail@seinkraft.info>
|
||||
* License: GPLv2
|
||||
* Description: Allow users to create groups of images
|
||||
* Documentation:
|
||||
*/
|
||||
|
||||
class PoolCreationException extends SCoreException {
|
||||
}
|
||||
|
||||
class Pools extends SimpleExtension {
|
||||
public function onInitExt($event) {
|
||||
global $config, $database;
|
||||
|
||||
if ($config->get_int("ext_pools_version") < 1){
|
||||
$database->create_table("pools", "
|
||||
id SCORE_AIPK,
|
||||
user_id INTEGER NOT NULL,
|
||||
public SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
date DATETIME NOT NULL,
|
||||
posts INTEGER NOT NULL DEFAULT 0,
|
||||
INDEX (id)
|
||||
");
|
||||
$database->create_table("pool_images", "
|
||||
pool_id INTEGER NOT NULL,
|
||||
image_id INTEGER NOT NULL,
|
||||
image_order INTEGER NOT NULL DEFAULT 0
|
||||
");
|
||||
$database->create_table("pool_history", "
|
||||
id SCORE_AIPK,
|
||||
pool_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
action INTEGER NOT NULL,
|
||||
images TEXT,
|
||||
count INTEGER NOT NULL DEFAULT 0,
|
||||
date DATETIME NOT NULL,
|
||||
INDEX (id)
|
||||
");
|
||||
|
||||
$config->set_int("ext_pools_version", 1);
|
||||
|
||||
$config->set_int("poolsMaxImportResults", 1000);
|
||||
$config->set_int("poolsImagesPerPage", 20);
|
||||
$config->set_int("poolsListsPerPage", 20);
|
||||
$config->set_int("poolsUpdatedPerPage", 20);
|
||||
$config->set_bool("poolsInfoOnViewImage", "N");
|
||||
$config->set_bool("poolsAdderOnViewImage", "N");
|
||||
|
||||
log_info("pools", "extension installed");
|
||||
}
|
||||
}
|
||||
|
||||
public function onSetupBuilding(SetupBuildingEvent $event) {
|
||||
$sb = new SetupBlock("Pools");
|
||||
$sb->add_int_option("poolsMaxImportResults", "Max results on import: ");
|
||||
$sb->add_int_option("poolsImagesPerPage", "<br>Images per page: ");
|
||||
$sb->add_int_option("poolsListsPerPage", "<br>Index list items per page: ");
|
||||
$sb->add_int_option("poolsUpdatedPerPage", "<br>Updated list items per page: ");
|
||||
$sb->add_bool_option("poolsInfoOnViewImage", "<br>Show pool info on image: ");
|
||||
//$sb->add_bool_option("poolsAdderOnViewImage", "<br>Show pool adder on image: ");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
|
||||
public function onPageRequest($event) {
|
||||
global $config, $page, $user;
|
||||
|
||||
if($event->page_matches("pool")) {
|
||||
switch($event->get_arg(0)) {
|
||||
case "list": //index
|
||||
$this->list_pools($page, int_escape($event->get_arg(1)));
|
||||
break;
|
||||
|
||||
case "new": // Show form
|
||||
if(!$user->is_anonymous()){
|
||||
$this->theme->new_pool_composer($page);
|
||||
} else {
|
||||
$errMessage = "You must be registered and logged in to create a new pool.";
|
||||
$this->theme->display_error($errMessage);
|
||||
}
|
||||
break;
|
||||
|
||||
case "create": // ADD _POST
|
||||
try {
|
||||
$newPoolID = $this->add_pool();
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("pool/view/".$newPoolID));
|
||||
}
|
||||
catch(PoolCreationException $pce) {
|
||||
$this->theme->display_error($pce->getMessage());
|
||||
}
|
||||
break;
|
||||
|
||||
case "view":
|
||||
$poolID = int_escape($event->get_arg(1));
|
||||
$this->get_posts($event, $poolID);
|
||||
break;
|
||||
|
||||
case "updated":
|
||||
$this->get_history(int_escape($event->get_arg(1)));
|
||||
break;
|
||||
|
||||
case "revert":
|
||||
if(!$user->is_anonymous()) {
|
||||
$historyID = int_escape($event->get_arg(1));
|
||||
$this->revert_history($historyID);
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("pool/updated"));
|
||||
}
|
||||
break;
|
||||
|
||||
case "edit":
|
||||
$poolID = int_escape($event->get_arg(1));
|
||||
$pools = $this->get_pool($poolID);
|
||||
|
||||
foreach($pools as $pool) {
|
||||
// if the pool is public and user is logged OR if the user is admin OR the user is the owner
|
||||
if(($pool['public'] == "Y" && !$user->is_anonymous()) || $user->is_admin() || $user->id == $pool['user_id']) {
|
||||
$this->theme->edit_pool($page, $this->get_pool($poolID), $this->edit_posts($poolID));
|
||||
} else {
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("pool/view/".$poolID));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "order":
|
||||
if($_SERVER["REQUEST_METHOD"] == "GET") {
|
||||
$poolID = int_escape($event->get_arg(1));
|
||||
$pools = $this->get_pool($poolID);
|
||||
|
||||
foreach($pools as $pool) {
|
||||
//if the pool is public and user is logged OR if the user is admin
|
||||
if(($pool['public'] == "Y" && !$user->is_anonymous()) || $user->is_admin() || $user->id == $pool['user_id']) {
|
||||
$this->theme->edit_order($page, $this->get_pool($poolID), $this->edit_order($poolID));
|
||||
} else {
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("pool/view/".$poolID));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$pool_id = int_escape($_POST["pool_id"]);
|
||||
$pool = $this->get_single_pool($pool_id);
|
||||
|
||||
if(($pool['public'] == "Y" && !$user->is_anonymous()) || $user->is_admin() || $user->id == $pool['user_id']) {
|
||||
$this->order_posts();
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("pool/view/".$pool_id));
|
||||
} else {
|
||||
$this->theme->display_error("Permssion denied.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "import":
|
||||
$pool_id = int_escape($_POST["pool_id"]);
|
||||
$pool = $this->get_single_pool($pool_id);
|
||||
|
||||
if(($pool['public'] == "Y" && !$user->is_anonymous()) || $user->is_admin() || $user->id == $pool['user_id']) {
|
||||
$this->import_posts();
|
||||
} else {
|
||||
$this->theme->display_error("Permssion denied.");
|
||||
}
|
||||
break;
|
||||
|
||||
case "add_posts":
|
||||
$pool_id = int_escape($_POST["pool_id"]);
|
||||
$pool = $this->get_single_pool($pool_id);
|
||||
|
||||
if(($pool['public'] == "Y" && !$user->is_anonymous()) || $user->is_admin() || $user->id == $pool['user_id']) {
|
||||
$this->add_posts();
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("pool/view/".$pool_id));
|
||||
} else {
|
||||
$this->theme->display_error("Permssion denied.");
|
||||
}
|
||||
break;
|
||||
|
||||
case "remove_posts":
|
||||
$pool_id = int_escape($_POST["pool_id"]);
|
||||
$pool = $this->get_single_pool($pool_id);
|
||||
|
||||
if(($pool['public'] == "Y" && !$user->is_anonymous()) || $user->is_admin() || $user->id == $pool['user_id']) {
|
||||
$this->remove_posts();
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("pool/view/".$pool_id));
|
||||
} else {
|
||||
$this->theme->display_error("Permssion denied.");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "nuke":
|
||||
$pool_id = int_escape($_POST['pool_id']);
|
||||
$pool = $this->get_single_pool($pool_id);
|
||||
|
||||
// only admins and owners may do this
|
||||
if($user->is_admin() || $user->id == $pool['user_id']) {
|
||||
$this->nuke_pool($pool_id);
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("pool/list"));
|
||||
} else {
|
||||
$this->theme->display_error("Permssion denied.");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("pool/list"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onUserBlockBuilding($event) {
|
||||
$event->add_link("Pools", make_link("pool/list"));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE GET THE POOLS WHERE THE IMAGE APPEARS WHEN THE IMAGE IS DISPLAYED
|
||||
*/
|
||||
public function onDisplayingImage($event) {
|
||||
global $config, $database, $page;
|
||||
|
||||
if($config->get_bool("poolsInfoOnViewImage")) {
|
||||
$imageID = $event->image->id;
|
||||
$poolsIDs = $this->get_pool_id($imageID);
|
||||
|
||||
$linksPools = array();
|
||||
foreach($poolsIDs as $poolID) {
|
||||
$pools = $this->get_pool($poolID['pool_id']);
|
||||
foreach ($pools as $pool){
|
||||
$linksPools[] = "<a href='".make_link("pool/view/".$pool['id'])."'>".html_escape($pool['title'])."</a>";
|
||||
}
|
||||
}
|
||||
$this->theme->pool_info($linksPools);
|
||||
}
|
||||
}
|
||||
|
||||
public function onImageAdminBlockBuilding($event) {
|
||||
global $config, $database, $user;
|
||||
if($config->get_bool("poolsAdderOnViewImage") && !$user->is_anonymous()) {
|
||||
if($user->is_admin()) {
|
||||
$pools = $database->get_all("SELECT * FROM pools");
|
||||
}
|
||||
else {
|
||||
$pools = $database->get_all("SELECT * FROM pools WHERE user_id=?", array($user->id));
|
||||
}
|
||||
if(count($pools) > 0) {
|
||||
$event->add_part($this->theme->get_adder_html($event->image, $pools));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE GET THE LIST OF POOLS
|
||||
*/
|
||||
private function list_pools(Page $page, $pageNumber) {
|
||||
global $config, $database;
|
||||
|
||||
if(is_null($pageNumber) || !is_numeric($pageNumber))
|
||||
$pageNumber = 0;
|
||||
else if ($pageNumber <= 0)
|
||||
$pageNumber = 0;
|
||||
else
|
||||
$pageNumber--;
|
||||
|
||||
$poolsPerPage = $config->get_int("poolsListsPerPage");
|
||||
|
||||
$pools = $database->get_all("
|
||||
SELECT p.id, p.user_id, p.public, p.title, p.description,
|
||||
p.posts, u.name as user_name
|
||||
FROM pools AS p
|
||||
INNER JOIN users AS u
|
||||
ON p.user_id = u.id
|
||||
ORDER BY p.date DESC
|
||||
LIMIT ? OFFSET ?
|
||||
", array($poolsPerPage, $pageNumber * $poolsPerPage)
|
||||
);
|
||||
|
||||
$totalPages = ceil($database->db->GetOne("SELECT COUNT(*) FROM pools") / $poolsPerPage);
|
||||
|
||||
$this->theme->list_pools($page, $pools, $pageNumber + 1, $totalPages);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE CREATE A NEW POOL
|
||||
*/
|
||||
private function add_pool() {
|
||||
global $user, $database;
|
||||
|
||||
if($user->is_anonymous()) {
|
||||
throw new PoolCreationException("You must be registered and logged in to add a image.");
|
||||
}
|
||||
if(empty($_POST["title"])) {
|
||||
throw new PoolCreationException("Pool needs a title");
|
||||
}
|
||||
|
||||
$public = $_POST["public"] == "Y" ? "Y" : "N";
|
||||
$database->execute("
|
||||
INSERT INTO pools (user_id, public, title, description, date)
|
||||
VALUES (?, ?, ?, ?, now())",
|
||||
array($user->id, $public, $_POST["title"], $_POST["description"]));
|
||||
|
||||
$result = $database->get_row("SELECT LAST_INSERT_ID() AS poolID"); # FIXME database specific?
|
||||
|
||||
log_info("pools", "Pool {$result["poolID"]} created by {$user->name}");
|
||||
|
||||
return $result["poolID"];
|
||||
}
|
||||
|
||||
private function get_pool($poolID) {
|
||||
global $database;
|
||||
return $database->get_all("SELECT * FROM pools WHERE id=?", array($poolID));
|
||||
}
|
||||
|
||||
private function get_single_pool($poolID) {
|
||||
global $database;
|
||||
return $database->get_row("SELECT * FROM pools WHERE id=?", array($poolID));
|
||||
}
|
||||
|
||||
/*
|
||||
* HERE WE GET THE ID OF THE POOL FROM AN IMAGE
|
||||
*/
|
||||
private function get_pool_id($imageID) {
|
||||
global $database;
|
||||
return $database->get_all("SELECT pool_id FROM pool_images WHERE image_id=?", array($imageID));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE GET THE IMAGES FROM THE TAG ON IMPORT
|
||||
*/
|
||||
private function import_posts() {
|
||||
global $page, $config, $database;
|
||||
|
||||
$pool_id = int_escape($_POST["pool_id"]);
|
||||
|
||||
$poolsMaxResults = $config->get_int("poolsMaxImportResults", 1000);
|
||||
|
||||
$images = $images = Image::find_images(0, $poolsMaxResults, Tag::explode($_POST["pool_tag"]));
|
||||
$this->theme->pool_result($page, $images, $pool_id);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE ADD CHECKED IMAGES FROM POOL AND UPDATE THE HISTORY
|
||||
*/
|
||||
private function add_posts() {
|
||||
global $database;
|
||||
|
||||
$poolID = int_escape($_POST['pool_id']);
|
||||
$images = "";
|
||||
|
||||
foreach ($_POST['check'] as $imageID){
|
||||
if(!$this->check_post($poolID, $imageID)){
|
||||
$database->execute("
|
||||
INSERT INTO pool_images (pool_id, image_id)
|
||||
VALUES (?, ?)",
|
||||
array($poolID, $imageID));
|
||||
|
||||
$images .= " ".$imageID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!strlen($images) == 0) {
|
||||
$count = $database->db->GetOne("SELECT COUNT(*) FROM pool_images WHERE pool_id=?", array($poolID));
|
||||
$this->add_history($poolID, 1, $images, $count);
|
||||
}
|
||||
|
||||
$database->Execute("
|
||||
UPDATE pools
|
||||
SET posts=(SELECT COUNT(*) FROM pool_images WHERE pool_id=?)
|
||||
WHERE id=?",
|
||||
array($poolID, $poolID)
|
||||
);
|
||||
return $poolID;
|
||||
}
|
||||
|
||||
private function order_posts() {
|
||||
global $database;
|
||||
|
||||
$poolID = int_escape($_POST['pool_id']);
|
||||
|
||||
foreach($_POST['imgs'] as $data) {
|
||||
list($imageORDER, $imageID) = $data;
|
||||
$database->Execute("
|
||||
UPDATE pool_images
|
||||
SET image_order = ?
|
||||
WHERE pool_id = ? AND image_id = ?",
|
||||
array($imageORDER, $poolID, $imageID)
|
||||
);
|
||||
}
|
||||
|
||||
return $poolID;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE REMOVE CHECKED IMAGES FROM POOL AND UPDATE THE HISTORY
|
||||
*/
|
||||
private function remove_posts() {
|
||||
global $database;
|
||||
|
||||
$poolID = int_escape($_POST['pool_id']);
|
||||
$images = "";
|
||||
|
||||
foreach($_POST['check'] as $imageID) {
|
||||
$database->execute("DELETE FROM pool_images WHERE pool_id = ? AND image_id = ?", array($poolID, $imageID));
|
||||
$images .= " ".$imageID;
|
||||
}
|
||||
|
||||
$count = $database->db->GetOne("SELECT COUNT(*) FROM pool_images WHERE pool_id=?", array($poolID));
|
||||
$this->add_history($poolID, 0, $images, $count);
|
||||
return $poolID;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE CHECK IF THE POST IS ALREADY ON POOL
|
||||
* USED IN add_posts()
|
||||
*/
|
||||
private function check_post($poolID, $imageID) {
|
||||
global $database;
|
||||
$result = $database->db->GetOne("SELECT COUNT(*) FROM pool_images WHERE pool_id=? AND image_id=?", array($poolID, $imageID));
|
||||
return ($result != 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE GET ALL IMAGES FOR THE POOL
|
||||
*/
|
||||
private function get_posts($event, $poolID) {
|
||||
global $config, $user, $database;
|
||||
|
||||
$pageNumber = int_escape($event->get_arg(2));
|
||||
if(is_null($pageNumber) || !is_numeric($pageNumber))
|
||||
$pageNumber = 0;
|
||||
else if ($pageNumber <= 0)
|
||||
$pageNumber = 0;
|
||||
else
|
||||
$pageNumber--;
|
||||
|
||||
$poolID = int_escape($poolID);
|
||||
|
||||
$imagesPerPage = $config->get_int("poolsImagesPerPage");
|
||||
|
||||
// WE CHECK IF THE EXTENSION RATING IS INSTALLED, WHICH VERSION AND IF IT
|
||||
// WORKS TO SHOW/HIDE SAFE, QUESTIONABLE, EXPLICIT AND UNRATED IMAGES FROM USER
|
||||
if(class_exists("Ratings")) {
|
||||
$rating = Ratings::privs_to_sql(Ratings::get_user_privs($user));
|
||||
|
||||
$result = $database->get_all("
|
||||
SELECT p.image_id
|
||||
FROM pool_images AS p
|
||||
INNER JOIN images AS i ON i.id = p.image_id
|
||||
WHERE p.pool_id = ? AND i.rating IN ($rating)
|
||||
ORDER BY p.image_order ASC
|
||||
LIMIT ? OFFSET ?",
|
||||
array($poolID, $imagesPerPage, $pageNumber * $imagesPerPage));
|
||||
|
||||
$totalPages = ceil($database->db->GetOne("
|
||||
SELECT COUNT(*)
|
||||
FROM pool_images AS p
|
||||
INNER JOIN images AS i ON i.id = p.image_id
|
||||
WHERE pool_id=? AND i.rating IN ($rating)",
|
||||
array($poolID)) / $imagesPerPage);
|
||||
}
|
||||
else {
|
||||
$result = $database->get_all("
|
||||
SELECT image_id
|
||||
FROM pool_images
|
||||
WHERE pool_id=?
|
||||
ORDER BY image_order ASC
|
||||
LIMIT ? OFFSET ?",
|
||||
array($poolID, $imagesPerPage, $pageNumber * $imagesPerPage));
|
||||
$totalPages = ceil($database->db->GetOne("SELECT COUNT(*) FROM pool_images WHERE pool_id=?", array($poolID)) / $imagesPerPage);
|
||||
}
|
||||
|
||||
$images = array();
|
||||
foreach($result as $singleResult) {
|
||||
$images[] = Image::by_id($singleResult["image_id"]);
|
||||
}
|
||||
|
||||
$pool = $this->get_pool($poolID);
|
||||
$this->theme->view_pool($pool, $images, $pageNumber + 1, $totalPages);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* WE GET THE ORDER OF THE IMAGES
|
||||
*/
|
||||
private function edit_posts($poolID) {
|
||||
global $database;
|
||||
|
||||
$result = $database->Execute("SELECT image_id FROM pool_images WHERE pool_id=? ORDER BY image_order ASC", array($poolID));
|
||||
|
||||
$images = array();
|
||||
while(!$result->EOF) {
|
||||
$image = Image::by_id($result->fields["image_id"]);
|
||||
$images[] = array($image);
|
||||
$result->MoveNext();
|
||||
}
|
||||
|
||||
return $images;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* WE GET THE ORDER OF THE IMAGES BUT HERE WE SEND KEYS ADDED IN ARRAY TO GET THE ORDER IN THE INPUT VALUE
|
||||
*/
|
||||
private function edit_order($poolID) {
|
||||
global $database;
|
||||
|
||||
$result = $database->Execute("SELECT image_id FROM pool_images WHERE pool_id=? ORDER BY image_order ASC", array($poolID));
|
||||
$images = array();
|
||||
while(!$result->EOF) {
|
||||
$image = $database->get_row("
|
||||
SELECT * FROM images AS i
|
||||
INNER JOIN pool_images AS p ON i.id = p.image_id
|
||||
WHERE pool_id=? AND i.id=?",
|
||||
array($poolID, $result->fields["image_id"]));
|
||||
$image = ($image ? new Image($image) : null);
|
||||
$images[] = array($image);
|
||||
$result->MoveNext();
|
||||
}
|
||||
// Original code
|
||||
//
|
||||
// $images = array();
|
||||
// while(!$result->EOF) {
|
||||
// $image = Image::by_id($result->fields["image_id"]);
|
||||
// $images[] = array($image);
|
||||
// $result->MoveNext();
|
||||
// }
|
||||
return $images;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE NUKE ENTIRE POOL. WE REMOVE POOLS AND POSTS FROM REMOVED POOL AND HISTORIES ENTRIES FROM REMOVED POOL
|
||||
*/
|
||||
private function nuke_pool($poolID) {
|
||||
global $user, $database;
|
||||
|
||||
if($user->is_admin()) {
|
||||
$database->execute("DELETE FROM pool_history WHERE pool_id = ?", array($poolID));
|
||||
$database->execute("DELETE FROM pool_images WHERE pool_id = ?", array($poolID));
|
||||
$database->execute("DELETE FROM pools WHERE id = ?", array($poolID));
|
||||
} elseif(!$user->is_anonymous()) {
|
||||
// FIXME: WE CHECK IF THE USER IS THE OWNER OF THE POOL IF NOT HE CAN'T DO ANYTHING
|
||||
$database->execute("DELETE FROM pool_history WHERE pool_id = ?", array($poolID));
|
||||
$database->execute("DELETE FROM pool_images WHERE pool_id = ?", array($poolID));
|
||||
$database->execute("DELETE FROM pools WHERE id = ? AND user_id = ?", array($poolID, $user->id));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE ADD A HISTORY ENTRY
|
||||
* FOR $action 1 (one) MEANS ADDED, 0 (zero) MEANS REMOVED
|
||||
*/
|
||||
private function add_history($poolID, $action, $images, $count) {
|
||||
global $user, $database;
|
||||
$database->execute("
|
||||
INSERT INTO pool_history (pool_id, user_id, action, images, count, date)
|
||||
VALUES (?, ?, ?, ?, ?, now())",
|
||||
array($poolID, $user->id, $action, $images, $count));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE GET THE HISTORY LIST
|
||||
*/
|
||||
private function get_history($pageNumber) {
|
||||
global $config, $database;
|
||||
|
||||
if(is_null($pageNumber) || !is_numeric($pageNumber))
|
||||
$pageNumber = 0;
|
||||
else if ($pageNumber <= 0)
|
||||
$pageNumber = 0;
|
||||
else
|
||||
$pageNumber--;
|
||||
|
||||
|
||||
$historiesPerPage = $config->get_int("poolsUpdatedPerPage");
|
||||
|
||||
$history = $database->get_all("
|
||||
SELECT h.id, h.pool_id, h.user_id, h.action, h.images,
|
||||
h.count, h.date, u.name as user_name, p.title as title
|
||||
FROM pool_history AS h
|
||||
INNER JOIN pools AS p
|
||||
ON p.id = h.pool_id
|
||||
INNER JOIN users AS u
|
||||
ON h.user_id = u.id
|
||||
ORDER BY h.date DESC
|
||||
LIMIT ? OFFSET ?
|
||||
", array($historiesPerPage, $pageNumber * $historiesPerPage));
|
||||
|
||||
$totalPages = ceil($database->db->GetOne("SELECT COUNT(*) FROM pool_history") / $historiesPerPage);
|
||||
|
||||
$this->theme->show_history($history, $pageNumber + 1, $totalPages);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* HERE GO BACK IN HISTORY AND ADD OR REMOVE POSTS TO POOL
|
||||
*/
|
||||
private function revert_history($historyID) {
|
||||
global $database;
|
||||
$status = $database->get_all("SELECT * FROM pool_history WHERE id=?", array($historyID));
|
||||
|
||||
foreach($status as $entry) {
|
||||
$images = trim($entry['images']);
|
||||
$images = explode(" ", $images);
|
||||
$poolID = $entry['pool_id'];
|
||||
$imageArray = "";
|
||||
|
||||
if($entry['action'] == 0) {
|
||||
// READ ENTRIES
|
||||
foreach($images as $image) {
|
||||
$imageID = $image;
|
||||
$this->add_post($poolID, $imageID);
|
||||
|
||||
$imageArray .= " ".$imageID;
|
||||
$newAction = 1;
|
||||
}
|
||||
}
|
||||
else if($entry['action'] == 1) {
|
||||
// DELETE ENTRIES
|
||||
foreach($images as $image) {
|
||||
$imageID = $image;
|
||||
$this->delete_post($poolID, $imageID);
|
||||
|
||||
$imageArray .= " ".$imageID;
|
||||
$newAction = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$count = $database->db->GetOne("SELECT COUNT(*) FROM pool_images WHERE pool_id=?", array($poolID));
|
||||
$this->add_history($poolID, $newAction, $imageArray, $count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE ADD A SIMPLE POST FROM POOL
|
||||
* USED WITH FOREACH IN revert_history()
|
||||
*/
|
||||
private function add_post($poolID, $imageID) {
|
||||
global $database;
|
||||
|
||||
if(!$this->check_post($poolID, $imageID)) {
|
||||
$database->execute("
|
||||
INSERT INTO pool_images (pool_id, image_id)
|
||||
VALUES (?, ?)",
|
||||
array($poolID, $imageID));
|
||||
}
|
||||
|
||||
$database->execute("UPDATE pools SET posts=(SELECT COUNT(*) FROM pool_images WHERE pool_id=?) WHERE id=?", array($poolID, $poolID));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE REMOVE A SIMPLE POST FROM POOL
|
||||
* USED WITH FOREACH IN revert_history()
|
||||
*/
|
||||
private function delete_post($poolID, $imageID) {
|
||||
global $database;
|
||||
|
||||
$database->execute("DELETE FROM pool_images WHERE pool_id = ? AND image_id = ?", array($poolID, $imageID));
|
||||
$database->execute("UPDATE pools SET posts=(SELECT COUNT(*) FROM pool_images WHERE pool_id=?) WHERE id=?", array($poolID, $poolID));
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
41
contrib/pools/test.php
Normal file
41
contrib/pools/test.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
class PoolsTest extends ShimmieWebTestCase {
|
||||
function testPools() {
|
||||
$this->get_page('pool/list');
|
||||
$this->assert_title("Pools");
|
||||
|
||||
$this->get_page('pool/new');
|
||||
$this->assert_title("Error");
|
||||
|
||||
|
||||
$this->log_in_as_user();
|
||||
|
||||
$this->get_page('pool/list');
|
||||
$this->click("Create Pool");
|
||||
$this->assert_title("Create Pool");
|
||||
$this->click("Create");
|
||||
$this->assert_title("Error");
|
||||
|
||||
$this->get_page('pool/new');
|
||||
$this->assert_title("Create Pool");
|
||||
$this->set_field("title", "Test Pool Title");
|
||||
$this->set_field("description", "Test pool description");
|
||||
$this->click("Create");
|
||||
$this->assert_title("Pool: Test Pool Title");
|
||||
|
||||
$this->log_out();
|
||||
|
||||
|
||||
$this->log_in_as_admin();
|
||||
|
||||
$this->get_page('pool/list');
|
||||
$this->click("Test Pool Title");
|
||||
$this->assert_title("Pool: Test Pool Title");
|
||||
$this->click("Delete Pool");
|
||||
$this->assert_title("Pools");
|
||||
$this->assert_no_text("Test Pool Title");
|
||||
|
||||
$this->log_out();
|
||||
}
|
||||
}
|
||||
?>
|
406
contrib/pools/theme.php
Normal file
406
contrib/pools/theme.php
Normal file
@ -0,0 +1,406 @@
|
||||
<?php
|
||||
class PoolsTheme extends Themelet {
|
||||
/*
|
||||
* HERE WE ADD THE POOL INFO ON IMAGE
|
||||
*/
|
||||
public function pool_info($linksPools) {
|
||||
global $page;
|
||||
if(count($linksPools) > 0) {
|
||||
$page->add_block(new Block("Pools", implode("<br>", $linksPools), "left"));
|
||||
}
|
||||
}
|
||||
|
||||
public function get_adder_html(Image $image, $pools) {
|
||||
$editor = "";
|
||||
$h = "";
|
||||
foreach($pools as $pool) {
|
||||
$h .= "<option value='".$pool['id']."'>".html_escape($pool['title'])."</option>";
|
||||
}
|
||||
$editor = "
|
||||
".make_form(make_link("pool/add_post"))."
|
||||
<select name='pool_id'>
|
||||
$h
|
||||
</select>
|
||||
<input type='hidden' name='image_id' value='{$image->id}'>
|
||||
<input type='submit' value='Add Image to Pool'>
|
||||
</form>
|
||||
";
|
||||
return $editor;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE SHOWS THE LIST OF POOLS
|
||||
*/
|
||||
public function list_pools(Page $page, $pools, $pageNumber, $totalPages) {
|
||||
global $user;
|
||||
$html = '<table id="poolsList" class="zebra">'.
|
||||
"<thead><tr>".
|
||||
"<th>Name</th>".
|
||||
"<th>Creator</th>".
|
||||
"<th>Posts</th>".
|
||||
"<th>Public</th>".
|
||||
"</tr></thead>";
|
||||
|
||||
$n = 0;
|
||||
foreach($pools as $pool) {
|
||||
$oe = ($n++ % 2 == 0) ? "even" : "odd";
|
||||
|
||||
$pool_link = '<a href="'.make_link("pool/view/".$pool['id']).'">'.html_escape($pool['title'])."</a>";
|
||||
$user_link = '<a href="'.make_link("user/".url_escape($pool['user_name'])).'">'.html_escape($pool['user_name'])."</a>";
|
||||
$public = ($pool['public'] == "Y" ? "Yes" : "No");
|
||||
|
||||
$html .= "<tr class='$oe'>".
|
||||
"<td class='left'>".$pool_link."</td>".
|
||||
"<td>".$user_link."</td>".
|
||||
"<td>".$pool['posts']."</td>".
|
||||
"<td>".$public."</td>".
|
||||
"</tr>";
|
||||
}
|
||||
|
||||
$html .= "</tbody></table>";
|
||||
|
||||
|
||||
$nav_html = "
|
||||
<a href=".make_link().">Index</a>
|
||||
<br><a href=".make_link("pool/new").">Create Pool</a>
|
||||
<br><a href=".make_link("pool/updated").">Pool Changes</a>
|
||||
";
|
||||
|
||||
$blockTitle = "Pools";
|
||||
$page->set_title(html_escape($blockTitle));
|
||||
$page->set_heading(html_escape($blockTitle));
|
||||
$page->add_block(new Block($blockTitle, $html, "main", 10));
|
||||
$page->add_block(new Block("Navigation", $nav_html, "left", 10));
|
||||
|
||||
$this->display_paginator($page, "pool/list", null, $pageNumber, $totalPages);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE DISPLAY THE NEW POOL COMPOSER
|
||||
*/
|
||||
public function new_pool_composer(Page $page) {
|
||||
$create_html = "
|
||||
".make_form(make_link("pool/create"))."
|
||||
<table>
|
||||
<tr><td>Title:</td><td><input type='text' name='title'></td></tr>
|
||||
<tr><td>Public?</td><td><input name='public' type='checkbox' value='Y' checked='checked'/></td></tr>
|
||||
<tr><td>Description:</td><td><textarea name='description'></textarea></td></tr>
|
||||
<tr><td colspan='2'><input type='submit' value='Create' /></td></tr>
|
||||
</table>
|
||||
</form>
|
||||
";
|
||||
|
||||
$blockTitle = "Create Pool";
|
||||
$page->set_title(html_escape($blockTitle));
|
||||
$page->set_heading(html_escape($blockTitle));
|
||||
$page->add_block(new Block("Create Pool", $create_html, "main", 20));
|
||||
}
|
||||
|
||||
|
||||
private function display_top($pools, $heading, $check_all=false) {
|
||||
global $page, $user;
|
||||
|
||||
$page->set_title($heading);
|
||||
$page->set_heading($heading);
|
||||
if(count($pools) == 1) {
|
||||
$pool = $pools[0];
|
||||
if($pool['public'] == "Y" || $user->is_admin()) {// IF THE POOL IS PUBLIC OR IS ADMIN SHOW EDIT PANEL
|
||||
if(!$user->is_anonymous()) {// IF THE USER IS REGISTERED AND LOGGED IN SHOW EDIT PANEL
|
||||
$this->sidebar_options($page, $pool, $check_all);
|
||||
}
|
||||
}
|
||||
$page->add_block(new Block(html_escape($pool['title']), html_escape($pool['description']), "main", 10));
|
||||
}
|
||||
else {
|
||||
$pool_info = "<table id='poolsList' class='zebra'>".
|
||||
"<thead><tr>".
|
||||
"<th class='left'>Title</th>".
|
||||
"<th class='left'>Description</th>".
|
||||
"</tr></thead>";
|
||||
|
||||
$n = 0;
|
||||
foreach($pools as $pool) {
|
||||
$oe = ($n++ % 2 == 0) ? "even" : "odd";
|
||||
|
||||
$pool_info .= "<tr class='$oe'>".
|
||||
"<td class='left'>".html_escape($pool['title'])."</td>".
|
||||
"<td class='left'>".html_escape($pool['description'])."</td>".
|
||||
"</tr>";
|
||||
|
||||
// this will make disasters if more than one pool comes in the parameter
|
||||
if($pool['public'] == "Y" || $user->is_admin()) {// IF THE POOL IS PUBLIC OR IS ADMIN SHOW EDIT PANEL
|
||||
if(!$user->is_anonymous()) {// IF THE USER IS REGISTERED AND LOGGED IN SHOW EDIT PANEL
|
||||
$this->sidebar_options($page, $pool, $check_all);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$pool_info .= "</tbody></table>";
|
||||
$page->add_block(new Block($heading, $pool_info, "main", 10));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE DISPLAY THE POOL WITH TITLE DESCRIPTION AND IMAGES WITH PAGINATION
|
||||
*/
|
||||
public function view_pool($pools, $images, $pageNumber, $totalPages) {
|
||||
global $user, $page;
|
||||
|
||||
$this->display_top($pools, "Pool: ".html_escape($pools[0]['title']));
|
||||
|
||||
$pool_images = '';
|
||||
foreach($images as $image) {
|
||||
$thumb_html = $this->build_thumb_html($image);
|
||||
$pool_images .= '<span class="thumb">'.
|
||||
'<a href="$image_link">'.$thumb_html.'</a>'.
|
||||
'</span>';
|
||||
}
|
||||
|
||||
$page->add_block(new Block("Viewing Posts", $pool_images, "main", 30));
|
||||
$this->display_paginator($page, "pool/view/".$pools[0]['id'], null, $pageNumber, $totalPages);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE DISPLAY THE POOL OPTIONS ON SIDEBAR BUT WE HIDE REMOVE OPTION IF THE USER IS NOT THE OWNER OR ADMIN
|
||||
*/
|
||||
public function sidebar_options(Page $page, $pool, $check_all) {
|
||||
global $user;
|
||||
|
||||
$editor = "
|
||||
".make_form(make_link("pool/import"))."
|
||||
<input type='text' name='pool_tag' id='edit' value='Please enter a tag' onclick='this.value=\"\";'/>
|
||||
<input type='submit' name='edit' id='edit' value='Import'/>
|
||||
<input type='hidden' name='pool_id' value='".$pool['id']."'>
|
||||
</form>
|
||||
|
||||
<form method='GET' action='".make_link("pool/edit/".$pool['id'])."'>
|
||||
<input type='submit' name='edit' id='edit' value='Edit Pool'/>
|
||||
</form>
|
||||
|
||||
<form method='GET' action='".make_link("pool/order/".$pool['id'])."'>
|
||||
<input type='submit' name='edit' id='edit' value='Order Pool'/>
|
||||
</form>
|
||||
";
|
||||
|
||||
if($user->id == $pool['user_id'] || $user->is_admin()){
|
||||
$editor .= "
|
||||
<script type='text/javascript'>
|
||||
function confirm_action() {
|
||||
return confirm('Are you sure that you want to delete this pool?');
|
||||
}
|
||||
</script>
|
||||
|
||||
".make_form(make_link("pool/nuke"))."
|
||||
<input type='submit' name='delete' id='delete' value='Delete Pool' onclick='return confirm_action()' />
|
||||
<input type='hidden' name='pool_id' value='".$pool['id']."'>
|
||||
</form>
|
||||
";
|
||||
}
|
||||
|
||||
if($check_all) {
|
||||
$editor .= "
|
||||
<script language='JavaScript' type='text/javascript'>
|
||||
function setAll(value) {
|
||||
var a=new Array();
|
||||
a=document.getElementsByName('check[]');
|
||||
var p=0;
|
||||
for(i=0;i<a.length;i++){
|
||||
a[i].checked = value;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<br><input type='button' name='CheckAll' value='Check All' onClick='setAll(true)'>
|
||||
<input type='button' name='UnCheckAll' value='Uncheck All' onClick='setAll(false)'>
|
||||
";
|
||||
}
|
||||
$page->add_block(new Block("Manage Pool", $editor, "left", 10));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE DISPLAY THE RESULT OF THE SEARCH ON IMPORT
|
||||
*/
|
||||
public function pool_result(Page $page, $images, $pool_id) {
|
||||
$pool_images = "
|
||||
<script language='JavaScript' type='text/javascript'>
|
||||
function setAll(value) {
|
||||
var a=new Array();
|
||||
a=document.getElementsByName('check[]');
|
||||
var p=0;
|
||||
for(i=0;i<a.length;i++) {
|
||||
a[i].checked = value;
|
||||
}
|
||||
}
|
||||
|
||||
function confirm_action() {
|
||||
return confirm('Are you sure you want to add selected posts to this pool?');
|
||||
}
|
||||
</script>
|
||||
";
|
||||
|
||||
$pool_images .= "<form action='".make_link("pool/add_posts")."' method='POST' name='checks'>";
|
||||
|
||||
foreach($images as $image) {
|
||||
$thumb_html = $this->build_thumb_html($image);
|
||||
|
||||
$pool_images .= '<span class="thumb">'.
|
||||
'<a href="$image_link">'.$thumb_html.'</a>'.
|
||||
'<br>'.
|
||||
'<input name="check[]" type="checkbox" value="'.$image->id.'" />'.
|
||||
'</span>';
|
||||
}
|
||||
$pool_images .= "<br>".
|
||||
"<input type='submit' name='edit' id='edit' value='Add Selected' onclick='return confirm_action()'/>".
|
||||
"<input type='hidden' name='pool_id' value='".$pool_id."'>".
|
||||
"</form>";
|
||||
|
||||
$page->add_block(new Block("Import", $pool_images, "main", 10));
|
||||
|
||||
$editor = "
|
||||
<input type='button' name='CheckAll' value='Check All' onClick='setAll(true)'>
|
||||
<input type='button' name='UnCheckAll' value='Uncheck All' onClick='setAll(false)'>
|
||||
";
|
||||
|
||||
$page->add_block(new Block("Manage Pool", $editor, "left", 10));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE DISPLAY THE POOL ORDERER
|
||||
* WE LIST ALL IMAGES ON POOL WITHOUT PAGINATION AND WITH A TEXT INPUT TO SET A NUMBER AND CHANGE THE ORDER
|
||||
*/
|
||||
public function edit_order(Page $page, $pools, $images) {
|
||||
global $user;
|
||||
|
||||
$this->display_top($pools, "Sorting Pool");
|
||||
|
||||
$pool_images = "<form action='".make_link("pool/order")."' method='POST' name='checks'>";
|
||||
$n = 0;
|
||||
foreach($images as $pair) {
|
||||
$image = $pair[0];
|
||||
$thumb_html = $this->build_thumb_html($image);
|
||||
$pool_images .= '<span class="thumb">'.
|
||||
'<a href="$image_link">'.$thumb_html.'</a>'.
|
||||
'<br><input name="imgs['.$n.'][]" type="text" style="max-width:50px;" value="'.$image->image_order.'" />'.
|
||||
'<input name="imgs['.$n.'][]" type="hidden" value="'.$image->id.'" />'.
|
||||
'</span>';
|
||||
$n++;
|
||||
}
|
||||
|
||||
$pool_images .= "<br>".
|
||||
"<input type='submit' name='edit' id='edit' value='Order'/>".
|
||||
"<input type='hidden' name='pool_id' value='".$pools[0]['id']."'>".
|
||||
"</form>";
|
||||
|
||||
$page->add_block(new Block("Sorting Posts", $pool_images, "main", 30));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE DISPLAY THE POOL EDITOR
|
||||
* WE LIST ALL IMAGES ON POOL WITHOUT PAGINATION AND WITH
|
||||
* A CHECKBOX TO SELECT WHICH IMAGE WE WANT TO REMOVE
|
||||
*/
|
||||
public function edit_pool(Page $page, $pools, $images) {
|
||||
global $user;
|
||||
|
||||
$this->display_top($pools, "Editing Pool", true);
|
||||
|
||||
$pool_images = "
|
||||
";
|
||||
|
||||
$pool_images = "<form action='".make_link("pool/remove_posts")."' method='POST' name='checks'>";
|
||||
|
||||
foreach($images as $pair) {
|
||||
$image = $pair[0];
|
||||
|
||||
$thumb_html = $this->build_thumb_html($image);
|
||||
|
||||
$pool_images .= '<span class="thumb">'.
|
||||
'<a href="$image_link">'.$thumb_html.'</a>'.
|
||||
'<br><input name="check[]" type="checkbox" value="'.$image->id.'" />'.
|
||||
'</span>';
|
||||
}
|
||||
|
||||
$pool_images .= "<br>".
|
||||
"<input type='submit' name='edit' id='edit' value='Remove Selected'/>".
|
||||
"<input type='hidden' name='pool_id' value='".$pools[0]['id']."'>".
|
||||
"</form>";
|
||||
|
||||
$page->add_block(new Block("Editing Posts", $pool_images, "main", 30));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE DISPLAY THE HISTORY LIST
|
||||
*/
|
||||
public function show_history($histories, $pageNumber, $totalPages) {
|
||||
global $page;
|
||||
$html = "<table id='poolsList' class='zebra'>".
|
||||
"<thead><tr>".
|
||||
"<th>Pool</th>".
|
||||
"<th>Post Count</th>".
|
||||
"<th>Changes</th>".
|
||||
"<th>Updater</th>".
|
||||
"<th>Date</th>".
|
||||
"<th>Action</th>".
|
||||
"</tr></thead>";
|
||||
|
||||
$n = 0;
|
||||
foreach($histories as $history) {
|
||||
$oe = ($n++ % 2 == 0) ? "even" : "odd";
|
||||
|
||||
$pool_link = "<a href='".make_link("pool/view/".$history['pool_id'])."'>".html_escape($history['title'])."</a>";
|
||||
$user_link = "<a href='".make_link("user/".url_escape($history['user_name']))."'>".html_escape($history['user_name'])."</a>";
|
||||
$revert_link = "<a href='".make_link("pool/revert/".$history['id'])."'>Revert</a>";
|
||||
|
||||
if ($history['action'] == 1) {
|
||||
$prefix = "+";
|
||||
} elseif ($history['action'] == 0) {
|
||||
$prefix = "-";
|
||||
}
|
||||
|
||||
$images = trim($history['images']);
|
||||
$images = explode(" ", $images);
|
||||
|
||||
$image_link = "";
|
||||
foreach ($images as $image) {
|
||||
$image_link .= "<a href='".make_link("post/view/".$image)."'>".$prefix.$image." </a>";
|
||||
}
|
||||
|
||||
$html .= "<tr class='$oe'>".
|
||||
"<td class='left'>".$pool_link."</td>".
|
||||
"<td>".$history['count']."</td>".
|
||||
"<td>".$image_link."</td>".
|
||||
"<td>".$user_link."</td>".
|
||||
"<td>".$history['date']."</td>".
|
||||
"<td>".$revert_link."</td>".
|
||||
"</tr>";
|
||||
}
|
||||
|
||||
$html .= "</tbody></table>";
|
||||
|
||||
$page->set_title("Recent Changes");
|
||||
$page->set_heading("Recent Changes");
|
||||
$page->add_block(new Block("Recent Changes", $html, "main", 10));
|
||||
|
||||
$this->display_paginator($page, "pool/updated", null, $pageNumber, $totalPages);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HERE WE DISPLAY THE ERROR
|
||||
*/
|
||||
public function display_error($errMessage) {
|
||||
global $page;
|
||||
|
||||
$page->set_title("Error");
|
||||
$page->set_heading("Error");
|
||||
$page->add_block(new Block("Error", $errMessage, "main", 10));
|
||||
}
|
||||
}
|
||||
?>
|
15
contrib/qr_code/main.php
Normal file
15
contrib/qr_code/main.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/*
|
||||
* Name: QR Codes
|
||||
* Author: Zach Hall <zach@sosguy.net> [http://seemslegit.com]
|
||||
* Description: Shows a QR Code for downloading an image to cell phones.
|
||||
* Based on Artanis's Link to Image Extension <artanis.00@gmail.com>
|
||||
* Further modified by Shish to remove the 7MB local QR generator
|
||||
* and replace it with a link to google chart APIs
|
||||
*/
|
||||
class QRImage extends SimpleExtension {
|
||||
public function onDisplayingImage($event) {
|
||||
$this->theme->links_block(make_http(make_link('image/'.$event->image->id.'.jpg')));
|
||||
}
|
||||
}
|
||||
?>
|
9
contrib/qr_code/theme.php
Normal file
9
contrib/qr_code/theme.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
class QRImageTheme extends Themelet {
|
||||
public function links_block($link) {
|
||||
global $page;
|
||||
$page->add_block( new Block(
|
||||
"QR Code","<img src='http://chart.apis.google.com/chart?chs=150x150&cht=qr&chl=$link' />","left",50));
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: Random Image
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
@ -16,17 +16,13 @@
|
||||
* <br>Adding a slash and some search terms will get a random image
|
||||
* from those results. This can be useful if you want a specific size
|
||||
* of random image, or from a category. You could link to
|
||||
* <code>/random_image/download/size:1024x768+cute</code>
|
||||
* <code>/random_image/download/size=1024x768+cute</code>
|
||||
*/
|
||||
|
||||
class RandomImage implements Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event(Event $event) {
|
||||
class RandomImage extends SimpleExtension {
|
||||
public function onPageRequest($event) {
|
||||
global $config, $database, $page, $user;
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object($this);
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("random_image")) {
|
||||
if($event->page_matches("random_image")) {
|
||||
if($event->count_args() == 1) {
|
||||
$action = $event->get_arg(0);
|
||||
$search_terms = array();
|
||||
@ -50,22 +46,22 @@ class RandomImage implements Extension {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(($event instanceof SetupBuildingEvent)) {
|
||||
$sb = new SetupBlock("Random Image");
|
||||
$sb->add_bool_option("show_random_block", "Show Random Block: ");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
public function onSetupBuilding($event) {
|
||||
$sb = new SetupBlock("Random Image");
|
||||
$sb->add_bool_option("show_random_block", "Show Random Block: ");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
|
||||
if($event instanceof PostListBuildingEvent) {
|
||||
if($config->get_bool("show_random_block")) {
|
||||
$image = Image::by_random($event->search_terms);
|
||||
if(!is_null($image)) {
|
||||
$this->theme->display_random($page, $image);
|
||||
}
|
||||
public function onPostListBuilding($event) {
|
||||
global $config, $page;
|
||||
if($config->get_bool("show_random_block")) {
|
||||
$image = Image::by_random($event->search_terms);
|
||||
if(!is_null($image)) {
|
||||
$this->theme->display_random($page, $image);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add_event_listener(new RandomImage());
|
||||
?>
|
||||
|
19
contrib/random_image/test.php
Normal file
19
contrib/random_image/test.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
class RandomTest extends ShimmieWebTestCase {
|
||||
function testRandom() {
|
||||
$this->log_in_as_user();
|
||||
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "test");
|
||||
$this->log_out();
|
||||
|
||||
$this->get_page("random_image/view");
|
||||
$this->assert_title("Image $image_id: test");
|
||||
|
||||
$this->log_in_as_admin();
|
||||
$this->delete_image($image_id);
|
||||
$this->log_out();
|
||||
|
||||
# FIXME: test random_image/download
|
||||
# FIXME: test random_image/ratio=4:3/download
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: Image Ratings
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
@ -7,10 +7,11 @@
|
||||
*/
|
||||
|
||||
class RatingSetEvent extends Event {
|
||||
var $image_id, $user, $rating;
|
||||
var $image, $user, $rating;
|
||||
|
||||
public function RatingSetEvent($image_id, $user, $rating) {
|
||||
$this->image_id = $image_id;
|
||||
public function RatingSetEvent(Image $image, User $user, $rating) {
|
||||
assert(in_array($rating, array("s", "q", "e", "u")));
|
||||
$this->image = $image;
|
||||
$this->user = $user;
|
||||
$this->rating = $rating;
|
||||
}
|
||||
@ -23,40 +24,73 @@ class Ratings implements Extension {
|
||||
global $config, $database, $page, $user;
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object($this);
|
||||
|
||||
if($event instanceof AdminBuildingEvent) {
|
||||
$this->theme->display_bulk_rater();
|
||||
}
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("admin/bulk_rate")) {
|
||||
global $database, $user, $page;
|
||||
if(!$user->is_admin()) {
|
||||
throw PermissionDeniedException();
|
||||
}
|
||||
else {
|
||||
$n = 0;
|
||||
while(true) {
|
||||
$images = Image::find_images($n, 100, Tag::explode($_POST["query"]));
|
||||
if(count($images) == 0) break;
|
||||
foreach($images as $image) {
|
||||
send_event(new RatingSetEvent($image, $user, $_POST['rating']));
|
||||
}
|
||||
$n += 100;
|
||||
}
|
||||
#$database->execute("
|
||||
# update images set rating=? where images.id in (
|
||||
# select image_id from image_tags join tags
|
||||
# on image_tags.tag_id = tags.id where tags.tag = ?);
|
||||
# ", array($_POST["rating"], $_POST["tag"]));
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("admin"));
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof InitExtEvent) {
|
||||
if($config->get_int("ext_ratings2_version") < 2) {
|
||||
$this->install();
|
||||
}
|
||||
|
||||
$config->set_default_string("ext_rating_anon_privs", 'sq');
|
||||
$config->set_default_string("ext_rating_user_privs", 'sq');
|
||||
$config->set_default_string("ext_rating_anon_privs", 'squ');
|
||||
$config->set_default_string("ext_rating_user_privs", 'sqeu');
|
||||
$config->set_default_string("ext_rating_admin_privs", 'sqeu');
|
||||
}
|
||||
|
||||
if($event instanceof RatingSetEvent) {
|
||||
$this->set_rating($event->image_id, $event->rating);
|
||||
$this->set_rating($event->image->id, $event->rating);
|
||||
}
|
||||
|
||||
if($event instanceof ImageInfoBoxBuildingEvent) {
|
||||
if($user->is_admin()) {
|
||||
if($this->can_rate()) {
|
||||
$event->add_part($this->theme->get_rater_html($event->image->id, $event->image->rating), 80);
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof ImageInfoSetEvent) {
|
||||
if($user->is_admin()) {
|
||||
send_event(new RatingSetEvent($event->image->id, $user, $_POST['rating']));
|
||||
if($this->can_rate() && isset($_POST["rating"])) {
|
||||
send_event(new RatingSetEvent($event->image, $user, $_POST['rating']));
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof SetupBuildingEvent) {
|
||||
$privs = array();
|
||||
$privs['Safe Only'] = 's';
|
||||
$privs['Safe and Unknown'] = 'su';
|
||||
$privs['Safe and Questionable'] = 'sq';
|
||||
$privs['Safe, Questionable, Unknown'] = 'squ';
|
||||
$privs['All'] = 'sqeu';
|
||||
|
||||
$sb = new SetupBlock("Image Ratings");
|
||||
$sb->add_choice_option("ext_rating_anon_privs", $privs, "Anonymous: ");
|
||||
$sb->add_choice_option("ext_rating_user_privs", $privs, "<br>Logged in: ");
|
||||
$sb->add_choice_option("ext_rating_user_privs", $privs, "<br>Users: ");
|
||||
$sb->add_choice_option("ext_rating_admin_privs", $privs, "<br>Admins: ");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
|
||||
@ -67,17 +101,7 @@ class Ratings implements Extension {
|
||||
if($event instanceof SearchTermParseEvent) {
|
||||
$matches = array();
|
||||
if(is_null($event->term) && $this->no_rating_query($event->context)) {
|
||||
if($user->is_anonymous()) {
|
||||
$sqes = $config->get_string("ext_rating_anon_privs");
|
||||
}
|
||||
else {
|
||||
$sqes = $config->get_string("ext_rating_user_privs");
|
||||
}
|
||||
$arr = array();
|
||||
for($i=0; $i<strlen($sqes); $i++) {
|
||||
$arr[] = "'" . $sqes[$i] . "'";
|
||||
}
|
||||
$set = join(', ', $arr);
|
||||
$set = Ratings::privs_to_sql(Ratings::get_user_privs($user));
|
||||
$event->add_querylet(new Querylet("rating IN ($set)"));
|
||||
}
|
||||
if(preg_match("/^rating=([sqeu]+)$/", $event->term, $matches)) {
|
||||
@ -89,12 +113,71 @@ class Ratings implements Extension {
|
||||
$set = join(', ', $arr);
|
||||
$event->add_querylet(new Querylet("rating IN ($set)"));
|
||||
}
|
||||
if(preg_match("/^rating=(safe|questionable|explicit|unknown)$/", strtolower($event->term), $matches)) {
|
||||
$text = $matches[1];
|
||||
$char = $text[0];
|
||||
$event->add_querylet(new Querylet("rating = ?", array($char)));
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof DisplayingImageEvent) {
|
||||
/**
|
||||
* Deny images upon insufficient permissions.
|
||||
**/
|
||||
global $user, $database, $page;
|
||||
$user_view_level = Ratings::get_user_privs($user);
|
||||
$user_view_level = preg_split('//', $user_view_level, -1);
|
||||
if(!in_array($event->image->rating, $user_view_level)) {
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("post/list"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function get_user_privs($user) {
|
||||
global $config;
|
||||
if($user->is_anonymous()) {
|
||||
$sqes = $config->get_string("ext_rating_anon_privs");
|
||||
}
|
||||
else if($user->is_admin()) {
|
||||
$sqes = $config->get_string("ext_rating_admin_privs");
|
||||
}
|
||||
else {
|
||||
$sqes = $config->get_string("ext_rating_user_privs");
|
||||
}
|
||||
return $sqes;
|
||||
}
|
||||
|
||||
public static function privs_to_sql($sqes) {
|
||||
$arr = array();
|
||||
for($i=0; $i<strlen($sqes); $i++) {
|
||||
$arr[] = "'" . $sqes[$i] . "'";
|
||||
}
|
||||
$set = join(', ', $arr);
|
||||
return $set;
|
||||
}
|
||||
|
||||
public static function rating_to_human($rating) {
|
||||
switch($rating) {
|
||||
case "s": return "Safe";
|
||||
case "q": return "Questionable";
|
||||
case "e": return "Explicit";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this is a bit ugly and guessey, should have proper options
|
||||
private function can_rate() {
|
||||
global $config, $user;
|
||||
if($user->is_anonymous() && $config->get_string("ext_rating_anon_privs") == "sqeu") return false;
|
||||
if($user->is_admin()) return true;
|
||||
if(!$user->is_anonymous() && $config->get_string("ext_rating_user_privs") == "sqeu") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private function no_rating_query($context) {
|
||||
foreach($context as $term) {
|
||||
if(preg_match("/^rating=([sqeu]+)$/", $term)) {
|
||||
if(preg_match("/^rating=/", $term)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
55
contrib/rating/test.php
Normal file
55
contrib/rating/test.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
class RatingTest extends ShimmieWebTestCase {
|
||||
function testRating() {
|
||||
$this->log_in_as_user();
|
||||
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pbx");
|
||||
|
||||
# test for bug #735: user forced to set rating, can't
|
||||
# set tags and leave unrated
|
||||
$this->get_page("post/view/$image_id");
|
||||
$this->assert_title("Image $image_id: pbx");
|
||||
$this->set_field("tag_edit__tags", "new");
|
||||
$this->click("Set");
|
||||
$this->assert_title("Image $image_id: new");
|
||||
|
||||
# set safe
|
||||
$this->set_field("rating", "s");
|
||||
$this->click("Set");
|
||||
$this->assert_title("Image $image_id: new");
|
||||
|
||||
# search for it in various ways
|
||||
$this->get_page("post/list/rating=Safe/1");
|
||||
$this->assert_title("Image $image_id: new");
|
||||
|
||||
$this->get_page("post/list/rating=s/1");
|
||||
$this->assert_title("Image $image_id: new");
|
||||
|
||||
$this->get_page("post/list/rating=sqe/1");
|
||||
$this->assert_title("Image $image_id: new");
|
||||
|
||||
# test that search by tag still works
|
||||
$this->get_page("post/list/new/1");
|
||||
$this->assert_title("Image $image_id: new");
|
||||
|
||||
# searching for a different rating should return nothing
|
||||
$this->get_page("post/list/rating=q/1");
|
||||
$this->assert_text("No Images Found");
|
||||
|
||||
# now set explicit, for the next test
|
||||
$this->get_page("post/view/$image_id");
|
||||
$this->set_field("rating", "e");
|
||||
$this->click("Set");
|
||||
$this->assert_title("Image $image_id: new");
|
||||
|
||||
$this->log_out();
|
||||
|
||||
# the explicit image shouldn't show up in anon's searches
|
||||
$this->get_page("post/list/new/1");
|
||||
$this->assert_text("No Images Found");
|
||||
|
||||
$this->log_in_as_admin();
|
||||
$this->delete_image($image_id);
|
||||
$this->log_out();
|
||||
}
|
||||
}
|
||||
?>
|
@ -19,6 +19,37 @@ class RatingsTheme extends Themelet {
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function display_bulk_rater() {
|
||||
global $page;
|
||||
$html = "
|
||||
".make_form(make_link("admin/bulk_rate"))."
|
||||
<table style='width: 300px'>
|
||||
<tr>
|
||||
<td>Search</td>
|
||||
<td>
|
||||
<input type='text' name='query'>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Rating</td>
|
||||
<td>
|
||||
<select name='rating'>
|
||||
<option value='s'>Safe</option>
|
||||
<option value='q'>Questionable</option>
|
||||
<option value='e'>Explicit</option>
|
||||
<option value='u'>Unrated</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan='2'><input type='submit' value='Go'></td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
";
|
||||
$page->add_block(new Block("Bulk Rating", $html));
|
||||
}
|
||||
|
||||
public function rating_to_name($rating) {
|
||||
switch($rating) {
|
||||
case 's': return "Safe";
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: Regen Thumb
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
@ -12,29 +12,22 @@
|
||||
* since been increased.
|
||||
*/
|
||||
|
||||
class RegenThumb implements Extension {
|
||||
var $theme;
|
||||
class RegenThumb extends SimpleExtension {
|
||||
public function onPageRequest($event) {
|
||||
global $config, $database, $page, $user;
|
||||
|
||||
public function receive_event(Event $event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object($this);
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("regen_thumb")) {
|
||||
global $user;
|
||||
if($user->is_admin() && isset($_POST['image_id'])) {
|
||||
global $config;
|
||||
global $database;
|
||||
$image = Image::by_id(int_escape($_POST['image_id']));
|
||||
send_event(new ThumbnailGenerationEvent($image->hash, $image->ext));
|
||||
$this->theme->display_results($event->page, $image);
|
||||
}
|
||||
if($event->page_matches("regen_thumb") && $user->is_admin() && isset($_POST['image_id'])) {
|
||||
$image = Image::by_id(int_escape($_POST['image_id']));
|
||||
send_event(new ThumbnailGenerationEvent($image->hash, $image->ext));
|
||||
$this->theme->display_results($page, $image);
|
||||
}
|
||||
}
|
||||
|
||||
if($event instanceof ImageAdminBlockBuildingEvent) {
|
||||
if($event->user->is_admin()) {
|
||||
$event->add_part($this->theme->get_buttons_html($event->image->id));
|
||||
}
|
||||
public function onImageAdminBlockBuilding($event) {
|
||||
global $user;
|
||||
if($user->is_admin()) {
|
||||
$event->add_part($this->theme->get_buttons_html($event->image->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
add_event_listener(new RegenThumb());
|
||||
?>
|
||||
|
15
contrib/regen_thumb/test.php
Normal file
15
contrib/regen_thumb/test.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
class RegenThumbTest extends ShimmieWebTestCase {
|
||||
function testRegenThumb() {
|
||||
$this->log_in_as_admin();
|
||||
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pbx computer screenshot");
|
||||
$this->get_page("post/view/$image_id");
|
||||
$this->click("Regenerate");
|
||||
$this->assert_title("Thumbnail Regenerated");
|
||||
$this->delete_image($image_id);
|
||||
$this->log_out();
|
||||
|
||||
# FIXME: test that the thumb's modified time has been updated
|
||||
}
|
||||
}
|
||||
?>
|
@ -6,7 +6,7 @@ class RegenThumbTheme extends Themelet {
|
||||
*/
|
||||
public function get_buttons_html($image_id) {
|
||||
return "
|
||||
<form action='".make_link("regen_thumb")."' method='POST'>
|
||||
".make_form(make_link("regen_thumb"))."
|
||||
<input type='hidden' name='image_id' value='$image_id'>
|
||||
<input type='submit' value='Regenerate'>
|
||||
</form>
|
||||
|
40
contrib/report_image/test.php
Normal file
40
contrib/report_image/test.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
class ReportImageTest extends ShimmieWebTestCase {
|
||||
function testReportImage() {
|
||||
$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('reason', "report details");
|
||||
$this->click("Report");
|
||||
$this->log_out();
|
||||
|
||||
$this->log_in_as_admin();
|
||||
|
||||
$this->get_page("setup");
|
||||
$this->set_field("_config_report_image_show_thumbs", true);
|
||||
$this->click("Save Settings");
|
||||
$this->get_page("image_report/list");
|
||||
$this->assert_title("Reported Images");
|
||||
$this->assert_text("report details");
|
||||
|
||||
$this->get_page("setup");
|
||||
$this->set_field("_config_report_image_show_thumbs", false);
|
||||
$this->click("Save Settings");
|
||||
$this->get_page("image_report/list");
|
||||
$this->assert_title("Reported Images");
|
||||
$this->assert_text("report details");
|
||||
$this->assert_text("$image_id");
|
||||
|
||||
$this->get_page("image_report/list");
|
||||
$this->click("Remove Report");
|
||||
$this->assert_title("Reported Images");
|
||||
$this->assert_no_text("report details");
|
||||
|
||||
$this->delete_image($image_id);
|
||||
$this->log_out();
|
||||
|
||||
# FIXME: test delete image from report screen
|
||||
# FIXME: test that >>123 works
|
||||
}
|
||||
}
|
||||
?>
|
@ -15,9 +15,10 @@ class ReportImageTheme extends Themelet {
|
||||
global $config;
|
||||
|
||||
$h_reportedimages = "";
|
||||
$n = 0;
|
||||
foreach($reports as $report) {
|
||||
$image = $report['image'];
|
||||
$h_reason = html_escape($report['reason']);
|
||||
$h_reason = format_text($report['reason']);
|
||||
|
||||
if($config->get_bool('report_image_show_thumbs')) {
|
||||
$image_link = $this->build_thumb_html($image);
|
||||
@ -35,12 +36,13 @@ class ReportImageTheme extends Themelet {
|
||||
ksort($iabbe->parts);
|
||||
$actions = join("<br>", $iabbe->parts);
|
||||
|
||||
$oe = ($n++ % 2 == 0) ? "even" : "odd";
|
||||
$h_reportedimages .= "
|
||||
<tr>
|
||||
<tr class='$oe'>
|
||||
<td>{$image_link}</td>
|
||||
<td>Report by $userlink: $h_reason</td>
|
||||
<td class='formstretch'>
|
||||
<form action='".make_link("image_report/remove")."' method='POST'>
|
||||
".make_form(make_link("image_report/remove"))."
|
||||
<input type='hidden' name='id' value='{$report['id']}'>
|
||||
<input type='submit' value='Remove Report'>
|
||||
</form>
|
||||
@ -53,12 +55,7 @@ class ReportImageTheme extends Themelet {
|
||||
|
||||
$thumb_width = $config->get_int("thumb_width");
|
||||
$html = "
|
||||
<style>
|
||||
.formstretch FORM INPUT {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<table border='1'>
|
||||
<table id='reportedimage' class='zebra'>
|
||||
<thead><td width='$thumb_width'>Image</td><td>Reason</td><td width='128'>Action</td></thead>
|
||||
$h_reportedimages
|
||||
</table>
|
||||
@ -76,9 +73,9 @@ class ReportImageTheme extends Themelet {
|
||||
|
||||
$i_image = int_escape($image->id);
|
||||
$html = "
|
||||
<form action='".make_link("image_report/add")."' method='POST'>
|
||||
".make_form(make_link("image_report/add"))."
|
||||
<input type='hidden' name='image_id' value='$i_image'>
|
||||
<input type='field' name='reason' value='Please enter a reason' onclick='this.value=\"\";'>
|
||||
<input type='text' name='reason' value='Please enter a reason' onclick='this.value=\"\";'>
|
||||
<input type='submit' value='Report'>
|
||||
</form>
|
||||
";
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: Resolution Limiter
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
@ -14,20 +14,21 @@ class ResolutionLimit implements Extension {
|
||||
$max_w = $config->get_int("upload_max_width", -1);
|
||||
$max_h = $config->get_int("upload_max_height", -1);
|
||||
$ratios = explode(" ", $config->get_string("upload_ratios", ""));
|
||||
$ratios = array_filter($ratios, "strlen");
|
||||
|
||||
$image = $event->image;
|
||||
|
||||
if($min_w > 0 && $image->width < $min_w) throw new UploadException("Image too small");
|
||||
if($min_h > 0 && $image->height < $min_h) throw new UploadException("Image too small");
|
||||
if($max_w > 0 && $image->width > $min_w) throw new UploadExceptiono("Image too large");
|
||||
if($max_h > 0 && $image->height > $min_h) throw new UploadException("Image too large");
|
||||
if($max_w > 0 && $image->width > $max_w) throw new UploadException("Image too large");
|
||||
if($max_h > 0 && $image->height > $max_h) throw new UploadException("Image too large");
|
||||
|
||||
if(count($ratios) > 0) {
|
||||
$ok = false;
|
||||
$valids = 0;
|
||||
foreach($ratios as $ratio) {
|
||||
$parts = explode(":", $ratio);
|
||||
if(count($parts) < 2) continue;
|
||||
$valids++;
|
||||
$width = $parts[0];
|
||||
$height = $parts[1];
|
||||
if($image->width / $width == $image->height / $height) {
|
||||
@ -35,7 +36,7 @@ class ResolutionLimit implements Extension {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!$ok) {
|
||||
if($valids > 0 && !$ok) {
|
||||
throw new UploadException(
|
||||
"Image needs to be in one of these ratios: ".
|
||||
html_escape($config->get_string("upload_ratios", "")));
|
||||
|
113
contrib/res_limit/test.php
Normal file
113
contrib/res_limit/test.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
class ResLimitTest extends ShimmieWebTestCase {
|
||||
function testResLimitOK() {
|
||||
$this->log_in_as_admin();
|
||||
$this->get_page("setup");
|
||||
$this->set_field("_config_upload_min_height", "0");
|
||||
$this->set_field("_config_upload_min_width", "0");
|
||||
$this->set_field("_config_upload_max_height", "2000");
|
||||
$this->set_field("_config_upload_max_width", "2000");
|
||||
$this->set_field("_config_upload_ratios", "4:3 16:9");
|
||||
$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->assert_response(302);
|
||||
$this->assert_no_text("Image too large");
|
||||
$this->assert_no_text("Image too small");
|
||||
$this->assert_no_text("ratio");
|
||||
$this->log_out();
|
||||
|
||||
$this->log_in_as_admin();
|
||||
$this->delete_image($image_id);
|
||||
$this->log_out();
|
||||
}
|
||||
|
||||
function testResLimitSmall() {
|
||||
$this->log_in_as_admin();
|
||||
$this->get_page("setup");
|
||||
$this->set_field("_config_upload_min_height", "900");
|
||||
$this->set_field("_config_upload_min_width", "900");
|
||||
$this->set_field("_config_upload_max_height", "-1");
|
||||
$this->set_field("_config_upload_max_width", "-1");
|
||||
$this->set_field("_config_upload_ratios", "4:3 16:9");
|
||||
$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->assert_response(200);
|
||||
$this->assert_title("Upload Status");
|
||||
$this->assert_text("Image too small");
|
||||
$this->log_out();
|
||||
|
||||
# hopefully a noop, but just in case
|
||||
$this->log_in_as_admin();
|
||||
$this->delete_image($image_id);
|
||||
$this->log_out();
|
||||
}
|
||||
|
||||
function testResLimitLarge() {
|
||||
$this->log_in_as_admin();
|
||||
$this->get_page("setup");
|
||||
$this->set_field("_config_upload_min_height", "0");
|
||||
$this->set_field("_config_upload_min_width", "0");
|
||||
$this->set_field("_config_upload_max_height", "100");
|
||||
$this->set_field("_config_upload_max_width", "100");
|
||||
$this->set_field("_config_upload_ratios", "4:3 16:9");
|
||||
$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->assert_response(200);
|
||||
$this->assert_title("Upload Status");
|
||||
$this->assert_text("Image too large");
|
||||
$this->log_out();
|
||||
|
||||
# hopefully a noop, but just in case
|
||||
$this->log_in_as_admin();
|
||||
$this->delete_image($image_id);
|
||||
$this->log_out();
|
||||
}
|
||||
|
||||
function testResLimitRatio() {
|
||||
$this->log_in_as_admin();
|
||||
$this->get_page("setup");
|
||||
$this->set_field("_config_upload_min_height", "-1");
|
||||
$this->set_field("_config_upload_min_width", "-1");
|
||||
$this->set_field("_config_upload_max_height", "-1");
|
||||
$this->set_field("_config_upload_max_width", "-1");
|
||||
$this->set_field("_config_upload_ratios", "16:9");
|
||||
$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->assert_response(200);
|
||||
$this->assert_title("Upload Status");
|
||||
$this->assert_text("Image needs to be in one of these ratios");
|
||||
$this->log_out();
|
||||
|
||||
# hopefully a noop, but just in case
|
||||
$this->log_in_as_admin();
|
||||
$this->delete_image($image_id);
|
||||
$this->log_out();
|
||||
}
|
||||
|
||||
# reset to defaults, otherwise this can interfere with
|
||||
# other extensions' test suites
|
||||
public function tearDown() {
|
||||
$this->log_in_as_admin();
|
||||
$this->get_page("setup");
|
||||
$this->set_field("_config_upload_min_height", "-1");
|
||||
$this->set_field("_config_upload_min_width", "-1");
|
||||
$this->set_field("_config_upload_max_height", "-1");
|
||||
$this->set_field("_config_upload_max_width", "-1");
|
||||
$this->set_field("_config_upload_ratios", "");
|
||||
$this->click("Save Settings");
|
||||
$this->log_out();
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,79 +1,68 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: RSS for Comments
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Self explanitory
|
||||
* Description: Self explanatory
|
||||
*/
|
||||
|
||||
class RSS_Comments implements Extension {
|
||||
// event handling {{{
|
||||
public function receive_event(Event $event) {
|
||||
if($event instanceof PostListBuildingEvent) {
|
||||
global $page;
|
||||
global $config;
|
||||
$title = $config->get_string('title');
|
||||
|
||||
$page->add_header("<link rel=\"alternate\" type=\"application/rss+xml\" ".
|
||||
"title=\"$title - Comments\" href=\"".make_link("rss/comments")."\" />");
|
||||
}
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("rss")) {
|
||||
if($event->get_arg(0) == 'comments') {
|
||||
global $database;
|
||||
$this->do_rss($database);
|
||||
}
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// output {{{
|
||||
private function do_rss($database) {
|
||||
global $page;
|
||||
global $config;
|
||||
$page->set_mode("data");
|
||||
$page->set_type("application/rss+xml");
|
||||
|
||||
$comments = $database->get_all("
|
||||
SELECT
|
||||
users.id as user_id, users.name as user_name,
|
||||
comments.comment as comment, comments.id as comment_id,
|
||||
comments.image_id as image_id, comments.owner_ip as poster_ip,
|
||||
UNIX_TIMESTAMP(posted) AS posted_timestamp
|
||||
FROM comments
|
||||
LEFT JOIN users ON comments.owner_id=users.id
|
||||
ORDER BY comments.id DESC
|
||||
LIMIT 10
|
||||
");
|
||||
|
||||
$data = "";
|
||||
foreach($comments as $comment) {
|
||||
$image_id = $comment['image_id'];
|
||||
$comment_id = $comment['comment_id'];
|
||||
$link = make_link("post/view/$image_id");
|
||||
$owner = html_escape($comment['user_name']);
|
||||
$posted = date(DATE_RSS, $comment['posted_timestamp']);
|
||||
$comment = html_escape($comment['comment']);
|
||||
$content = html_escape("$owner: $comment");
|
||||
|
||||
$data .= "
|
||||
<item>
|
||||
<title>$owner comments on $image_id</title>
|
||||
<link>$link</link>
|
||||
<guid isPermaLink=\"false\">$comment_id</guid>
|
||||
<pubDate>$posted</pubDate>
|
||||
<description>$content</description>
|
||||
</item>
|
||||
";
|
||||
}
|
||||
|
||||
class RSS_Comments extends SimpleExtension {
|
||||
public function onPostListBuilding($event) {
|
||||
global $config, $page;
|
||||
$title = $config->get_string('title');
|
||||
$base_href = $config->get_string('base_href');
|
||||
$version = $config->get_string('version');
|
||||
$xml = <<<EOD
|
||||
|
||||
$page->add_header("<link rel=\"alternate\" type=\"application/rss+xml\" ".
|
||||
"title=\"$title - Comments\" href=\"".make_link("rss/comments")."\" />");
|
||||
}
|
||||
|
||||
public function onPageRequest($event) {
|
||||
global $config, $database, $page;
|
||||
if($event->page_matches("rss/comments")) {
|
||||
$page->set_mode("data");
|
||||
$page->set_type("application/rss+xml");
|
||||
|
||||
$comments = $database->get_all("
|
||||
SELECT
|
||||
users.id as user_id, users.name as user_name,
|
||||
comments.comment as comment, comments.id as comment_id,
|
||||
comments.image_id as image_id, comments.owner_ip as poster_ip,
|
||||
UNIX_TIMESTAMP(posted) AS posted_timestamp
|
||||
FROM comments
|
||||
LEFT JOIN users ON comments.owner_id=users.id
|
||||
ORDER BY comments.id DESC
|
||||
LIMIT 10
|
||||
");
|
||||
|
||||
$data = "";
|
||||
foreach($comments as $comment) {
|
||||
$image_id = $comment['image_id'];
|
||||
$comment_id = $comment['comment_id'];
|
||||
$link = make_http(make_link("post/view/$image_id"));
|
||||
$owner = html_escape($comment['user_name']);
|
||||
$posted = date(DATE_RSS, $comment['posted_timestamp']);
|
||||
$comment = html_escape($comment['comment']);
|
||||
$content = html_escape("$owner: $comment");
|
||||
|
||||
$data .= "
|
||||
<item>
|
||||
<title>$owner comments on $image_id</title>
|
||||
<link>$link</link>
|
||||
<guid isPermaLink=\"false\">$comment_id</guid>
|
||||
<pubDate>$posted</pubDate>
|
||||
<description>$content</description>
|
||||
</item>
|
||||
";
|
||||
}
|
||||
|
||||
$title = $config->get_string('title');
|
||||
$base_href = make_http($config->get_string('base_href'));
|
||||
$version = $config->get_string('version');
|
||||
$xml = <<<EOD
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>$title</title>
|
||||
<description>The latest comments on the image board</description>
|
||||
<channel>
|
||||
<title>$title</title>
|
||||
<description>The latest comments on the image board</description>
|
||||
<link>$base_href</link>
|
||||
<generator>$version</generator>
|
||||
<copyright>(c) 2007 Shish</copyright>
|
||||
@ -81,9 +70,8 @@ class RSS_Comments implements Extension {
|
||||
</channel>
|
||||
</rss>
|
||||
EOD;
|
||||
$page->set_data($xml);
|
||||
$page->set_data($xml);
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
add_event_listener(new RSS_Comments());
|
||||
?>
|
||||
|
@ -1,9 +1,22 @@
|
||||
<?php
|
||||
class RSSCommentsTest extends WebTestCase {
|
||||
class RSSCommentsTest extends ShimmieWebTestCase {
|
||||
function testImageFeed() {
|
||||
$this->get(TEST_BASE.'/rss/comments');
|
||||
$this->assertMime("application/rss+xml");
|
||||
$this->assertNoText("Exception");
|
||||
$this->log_in_as_user();
|
||||
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pbx");
|
||||
$this->get_page("post/view/$image_id");
|
||||
$this->set_field('comment', "Test Comment ASDFASDF");
|
||||
$this->click("Post Comment");
|
||||
$this->assert_text("ASDFASDF");
|
||||
$this->log_out();
|
||||
|
||||
$this->get_page('rss/comments');
|
||||
$this->assert_mime("application/rss+xml");
|
||||
$this->assert_no_text("Exception");
|
||||
$this->assert_text("ASDFASDF");
|
||||
|
||||
$this->log_in_as_admin();
|
||||
$this->delete_image($image_id);
|
||||
$this->log_out();
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -1,53 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* Name: RSS for Images
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Self explanitory
|
||||
* Description: Self explanatory
|
||||
*/
|
||||
|
||||
class RSS_Images implements Extension {
|
||||
// event handling {{{
|
||||
public function receive_event(Event $event) {
|
||||
global $config, $database, $page, $user;
|
||||
class RSS_Images extends SimpleExtension {
|
||||
public function onPostListBuilding($event) {
|
||||
global $config, $page;
|
||||
$title = $config->get_string('title');
|
||||
|
||||
if($event instanceof PostListBuildingEvent) {
|
||||
$title = $config->get_string('title');
|
||||
|
||||
if(count($event->search_terms) > 0) {
|
||||
$search = implode(' ', $event->search_terms);
|
||||
$page->add_header("<link id=\"images\" rel=\"alternate\" type=\"application/rss+xml\" ".
|
||||
"title=\"$title - Images with tags: $search\" href=\"".make_link("rss/images/$search/1")."\" />");
|
||||
}
|
||||
else {
|
||||
$page->add_header("<link id=\"images\" rel=\"alternate\" type=\"application/rss+xml\" ".
|
||||
"title=\"$title - Images\" href=\"".make_link("rss/images/1")."\" />");
|
||||
}
|
||||
if(count($event->search_terms) > 0) {
|
||||
$search = html_escape(implode(' ', $event->search_terms));
|
||||
$page->add_header("<link id=\"images\" rel=\"alternate\" type=\"application/rss+xml\" ".
|
||||
"title=\"$title - Images with tags: $search\" href=\"".make_link("rss/images/$search/1")."\" />");
|
||||
}
|
||||
else {
|
||||
$page->add_header("<link id=\"images\" rel=\"alternate\" type=\"application/rss+xml\" ".
|
||||
"title=\"$title - Images\" href=\"".make_link("rss/images/1")."\" />");
|
||||
}
|
||||
}
|
||||
|
||||
if(($event instanceof PageRequestEvent) && $event->page_matches("rss/images")) {
|
||||
$page_number = 0;
|
||||
$search_terms = array();
|
||||
|
||||
if($event->count_args() == 1) {
|
||||
$page_number = int_escape($event->get_arg(0));
|
||||
// compat hack, deprecate this later
|
||||
if($page_number == 0) {
|
||||
$search_terms = explode(' ', $event->get_arg(0));
|
||||
$page_number = 1;
|
||||
}
|
||||
}
|
||||
else if($event->count_args() == 2) {
|
||||
$search_terms = explode(' ', $event->get_arg(0));
|
||||
$page_number = int_escape($event->get_arg(1));
|
||||
}
|
||||
|
||||
$images = Image::find_images(($page_number-1)*10, 10, $search_terms);
|
||||
public function onPageRequest($event) {
|
||||
if($event->page_matches("rss/images")) {
|
||||
$search_terms = $event->get_search_terms();
|
||||
$page_number = $event->get_page_number();
|
||||
$page_size = $event->get_page_size();
|
||||
$images = Image::find_images(($page_number-1)*$page_size, $page_size, $search_terms);
|
||||
$this->do_rss($images, $search_terms, $page_number);
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// output {{{
|
||||
|
||||
|
||||
private function do_rss($images, $search_terms, $page_number) {
|
||||
global $page;
|
||||
global $config;
|
||||
@ -56,15 +41,15 @@ class RSS_Images implements Extension {
|
||||
|
||||
$data = "";
|
||||
foreach($images as $image) {
|
||||
$link = make_link("post/view/{$image->id}");
|
||||
$tags = $image->get_tag_list();
|
||||
$link = make_http(make_link("post/view/{$image->id}"));
|
||||
$tags = html_escape($image->get_tag_list());
|
||||
$owner = $image->get_owner();
|
||||
$thumb_url = $image->get_thumb_link();
|
||||
$image_url = $image->get_image_link();
|
||||
$posted = date(DATE_RSS, $image->posted_timestamp);
|
||||
$content = html_escape(
|
||||
"<p>" . Themelet::build_thumb_html($image) . "</p>" .
|
||||
"<p>Uploaded by " . $owner->name . "</p>"
|
||||
"<p>Uploaded by " . html_escape($owner->name) . "</p>"
|
||||
);
|
||||
|
||||
$data .= "
|
||||
@ -81,10 +66,10 @@ class RSS_Images implements Extension {
|
||||
}
|
||||
|
||||
$title = $config->get_string('title');
|
||||
$base_href = $config->get_string('base_href');
|
||||
$base_href = make_http($config->get_string('base_href'));
|
||||
$search = "";
|
||||
if(count($search_terms) > 0) {
|
||||
$search = html_escape(implode(" ", $search_terms)) . "/";
|
||||
$search = url_escape(implode(" ", $search_terms)) . "/";
|
||||
}
|
||||
|
||||
if($page_number > 1) {
|
||||
@ -113,7 +98,5 @@ class RSS_Images implements Extension {
|
||||
</rss>";
|
||||
$page->set_data($xml);
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
add_event_listener(new RSS_Images());
|
||||
?>
|
||||
|
@ -1,21 +1,36 @@
|
||||
<?php
|
||||
class RSSImagesTest extends WebTestCase {
|
||||
class RSSImagesTest extends ShimmieWebTestCase {
|
||||
function testImageFeed() {
|
||||
$this->get(TEST_BASE.'/rss/images');
|
||||
$this->assertMime("application/rss+xml");
|
||||
$this->assertNoText("Exception");
|
||||
$this->log_in_as_user();
|
||||
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pbx computer screenshot");
|
||||
$this->log_out();
|
||||
|
||||
$this->get(TEST_BASE.'/rss/images/1');
|
||||
$this->assertMime("application/rss+xml");
|
||||
$this->assertNoText("Exception");
|
||||
$this->get_page('rss/images');
|
||||
$this->assert_mime("application/rss+xml");
|
||||
$this->assert_no_text("Exception");
|
||||
|
||||
$this->get(TEST_BASE.'/rss/images/tagme/1');
|
||||
$this->assertMime("application/rss+xml");
|
||||
$this->assertNoText("Exception");
|
||||
$this->get_page('rss/images/1');
|
||||
$this->assert_mime("application/rss+xml");
|
||||
$this->assert_no_text("Exception");
|
||||
|
||||
$this->get(TEST_BASE.'/rss/images/tagme/2');
|
||||
$this->assertMime("application/rss+xml");
|
||||
$this->assertNoText("Exception");
|
||||
# FIXME: test that the image is actually found
|
||||
$this->get_page('rss/images/computer/1');
|
||||
$this->assert_mime("application/rss+xml");
|
||||
$this->assert_no_text("Exception");
|
||||
|
||||
# valid tag, invalid page
|
||||
$this->get_page('rss/images/computer/2');
|
||||
$this->assert_mime("application/rss+xml");
|
||||
$this->assert_no_text("Exception");
|
||||
|
||||
# not found
|
||||
$this->get_page('rss/images/waffle/2');
|
||||
$this->assert_mime("application/rss+xml");
|
||||
$this->assert_no_text("Exception");
|
||||
|
||||
$this->log_in_as_admin();
|
||||
$this->delete_image($image_id);
|
||||
$this->log_out();
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
73
contrib/shimmie_api/main.php
Normal file
73
contrib/shimmie_api/main.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/*
|
||||
* Name: [Beta] Shimmie JSON API
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Description: A JSON interface to shimmie data [WARNING]
|
||||
* Documentation:
|
||||
* <b>Admin Warning:</b> this exposes private data, eg IP addresses
|
||||
* <p><b>Developer Warning:</b> the API is unstable; notably, private data may get hidden
|
||||
*/
|
||||
|
||||
|
||||
class _SafeImage {
|
||||
#{"id":"2","height":"768","width":"1024","hash":"71cdfaabbcdad3f777e0b60418532e94","filesize":"439561","filename":"HeilAmu.png","ext":"png","owner_ip":"0.0.0.0","posted":"0000-00-00 00:00:00","source":null,"locked":"N","owner_id":"0","rating":"u","numeric_score":"0","text_score":"0","notes":"0","favorites":"0","posted_timestamp":-62169955200,"tag_array":["cat","kunimitsu"]}
|
||||
|
||||
function __construct(Image $img) {
|
||||
$this->id = $img->id;
|
||||
$this->height = $img->height;
|
||||
$this->width = $img->width;
|
||||
$this->hash = $img->hash;
|
||||
$this->filesize = $img->filesize;
|
||||
$this->ext = $img->ext;
|
||||
$this->posted = $img->posted_timestamp;
|
||||
$this->source = $img->source;
|
||||
$this->owner_id = $img->owner_id;
|
||||
$this->tags = $img->tag_array;
|
||||
}
|
||||
}
|
||||
|
||||
class ShimmieApi extends SimpleExtension {
|
||||
public function onPageRequest(PageRequestEvent $event) {
|
||||
global $database, $page;
|
||||
|
||||
if($event->page_matches("api/shimmie")) {
|
||||
$page->set_mode("data");
|
||||
$page->set_type("text/plain");
|
||||
|
||||
if($event->page_matches("api/shimmie/get_tags")) {
|
||||
if($event->count_args() == 2) {
|
||||
$all = $database->get_all(
|
||||
"SELECT tag FROM tags WHERE tag LIKE ?",
|
||||
array($event->get_arg(0)."%"));
|
||||
}
|
||||
else {
|
||||
$all = $database->get_all("SELECT tag FROM tags");
|
||||
}
|
||||
$res = array();
|
||||
foreach($all as $row) {$res[] = $row["tag"];}
|
||||
$page->set_data(json_encode($res));
|
||||
}
|
||||
|
||||
if($event->page_matches("api/shimmie/get_image")) {
|
||||
$image = Image::by_id(int_escape($event->get_arg(0)));
|
||||
$image->get_tag_array(); // tag data isn't loaded into the object until necessary
|
||||
$safe_image = new _SafeImage($image);
|
||||
$page->set_data(json_encode($safe_image));
|
||||
}
|
||||
|
||||
if($event->page_matches("api/shimmie/find_images")) {
|
||||
$search_terms = $event->get_search_terms();
|
||||
$page_number = $event->get_page_number();
|
||||
$page_size = $event->get_page_size();
|
||||
$images = Image::find_images(($page_number-1)*$page_size, $page_size, $search_terms);
|
||||
$safe_images = array();
|
||||
foreach($images as $image) {
|
||||
$image->get_tag_array();
|
||||
$safe_images[] = new _SafeImage($image);
|
||||
}
|
||||
$page->set_data(json_encode($safe_images));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
BIN
contrib/simpletest/data/bedroom_workshop.jpg
Normal file
BIN
contrib/simpletest/data/bedroom_workshop.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user