Compare commits

..

79 Commits

Author SHA1 Message Date
shish
3cf59cc545 version bump
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@1012 7f39781d-f577-437e-ae19-be835c7a54ca
2008-09-01 12:57:23 +00:00
shish
327d0dd51f realpath() fix from trunk
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@1011 7f39781d-f577-437e-ae19-be835c7a54ca
2008-09-01 12:56:48 +00:00
shish
90fcccdab0 various fix backports
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@1006 7f39781d-f577-437e-ae19-be835c7a54ca
2008-08-26 09:06:29 +00:00
shish
9d868750e2 merge some fixes
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@984 7f39781d-f577-437e-ae19-be835c7a54ca
2008-08-12 02:39:08 +00:00
shish
770e313da1 and in 2.2...
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@941 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-27 15:56:45 +00:00
shish
2002d555c1 and stable
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@939 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-26 07:06:42 +00:00
shish
36064595f5 moo
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@937 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-25 17:56:34 +00:00
shish
2c4582f147 memcache api, for anyone who wants it :3
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@935 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-25 12:44:16 +00:00
shish
9993370fc6 sql compliance
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@934 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-25 12:06:13 +00:00
shish
5d42ee21de debreak
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@933 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-25 11:56:46 +00:00
shish
6e0b3bca6d back again
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@931 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-25 11:44:05 +00:00
shish
32d12602cf back
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@929 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-24 16:22:15 +00:00
shish
058f2ff1ed more compat
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@927 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-24 15:35:57 +00:00
shish
79767033b7 backport
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@924 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-24 14:06:47 +00:00
shish
8f05de4b66 backport by request~
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@922 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-24 07:51:35 +00:00
shish
34d2165dfa to 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@920 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-23 10:58:50 +00:00
shish
593dc2f694 version bump
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@918 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-21 19:07:52 +00:00
shish
a1199c3bd4 and 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@917 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-21 15:18:37 +00:00
shish
4b666a8f1a backport
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@915 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-21 11:14:17 +00:00
shish
9044abdfc2 backport
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@913 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-21 11:05:20 +00:00
shish
e17c85d379 backport to 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@911 7f39781d-f577-437e-ae19-be835c7a54ca
2008-07-17 08:14:52 +00:00
shish
554ff9051c broken in 2.2? :S Removing for a minimal release, 2.2.1 will be for extra features and things again \o/
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@906 7f39781d-f577-437e-ae19-be835c7a54ca
2008-06-20 14:33:52 +00:00
shish
04f335b797 backport
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@905 7f39781d-f577-437e-ae19-be835c7a54ca
2008-06-19 14:56:13 +00:00
shish
661b5eac78 add the last couple of fixes
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@903 7f39781d-f577-437e-ae19-be835c7a54ca
2008-06-16 11:30:46 +00:00
shish
763538c096 2.2 too
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@900 7f39781d-f577-437e-ae19-be835c7a54ca
2008-06-14 18:45:42 +00:00
shish
5f473aff6d tentative release
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@898 7f39781d-f577-437e-ae19-be835c7a54ca
2008-06-14 17:23:17 +00:00
shish
c4fdf771ee backports
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@895 7f39781d-f577-437e-ae19-be835c7a54ca
2008-06-09 11:23:02 +00:00
shish
94725f1266 subtitles
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@888 7f39781d-f577-437e-ae19-be835c7a54ca
2008-06-08 15:08:24 +00:00
shish
f1ce267015 stable
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@886 7f39781d-f577-437e-ae19-be835c7a54ca
2008-05-28 22:39:41 +00:00
shish
25bfb82653 now that flash file dimentions are detected, it's good enough for stable \o/
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@884 7f39781d-f577-437e-ae19-be835c7a54ca
2008-05-26 22:08:11 +00:00
shish
0c334f05ed backports~
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@881 7f39781d-f577-437e-ae19-be835c7a54ca
2008-05-25 23:06:10 +00:00
shish
bea09d836d to 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@878 7f39781d-f577-437e-ae19-be835c7a54ca
2008-05-22 20:42:20 +00:00
shish
384e8bbdda backport
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@876 7f39781d-f577-437e-ae19-be835c7a54ca
2008-05-20 19:35:23 +00:00
shish
96509995a8 2.2 merge
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@871 7f39781d-f577-437e-ae19-be835c7a54ca
2008-05-20 03:07:26 +00:00
shish
642a3d02e1 fixes / reworking to 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@868 7f39781d-f577-437e-ae19-be835c7a54ca
2008-05-19 17:11:08 +00:00
shish
f180624ebf get_random_image in 2.2 also
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@866 7f39781d-f577-437e-ae19-be835c7a54ca
2008-05-19 13:07:16 +00:00
shish
482312b11e featured image for 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@862 7f39781d-f577-437e-ae19-be835c7a54ca
2008-05-19 02:21:47 +00:00
shish
aaa8ccf874 2.2 also
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@857 7f39781d-f577-437e-ae19-be835c7a54ca
2008-05-18 03:33:11 +00:00
shish
c2ab5e5fcd xmldb removal changes to 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@855 7f39781d-f577-437e-ae19-be835c7a54ca
2008-05-18 02:38:36 +00:00
shish
3f0d6562ef data dir is unused, and could be a security hole in the way I'd intended it to be used...
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@854 7f39781d-f577-437e-ae19-be835c7a54ca
2008-05-18 02:32:04 +00:00
shish
d1ce3db7e2 2.2 also
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@841 7f39781d-f577-437e-ae19-be835c7a54ca
2008-05-04 21:00:29 +00:00
shish
24a35c37ea same weird non-bug
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@838 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-30 12:33:36 +00:00
shish
457d5d8ad9 a couple more bbcode tags
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@837 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-30 12:33:19 +00:00
shish
56ccd7df40 2.2 merge
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@828 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-19 18:59:00 +00:00
shish
f8b414a15f 2.2 too
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@825 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-16 10:02:11 +00:00
shish
51f6c486a2 backport :3
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@820 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-14 11:21:38 +00:00
shish
367546fe06 back to 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@818 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-14 10:51:25 +00:00
shish
ffde804f24 SVG should be fit for stable \o/
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@815 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-11 06:59:23 +00:00
shish
b57ba4d484 backport
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@813 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-11 06:27:28 +00:00
shish
ce8d6f71fd and 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@811 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-11 06:14:59 +00:00
shish
1fd2fe084b apply cleanups to 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@807 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-11 05:47:16 +00:00
shish
7e92bdbd8e backport
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@802 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-11 05:26:02 +00:00
shish
4c5f415da7 2.2 too
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@798 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-11 01:49:55 +00:00
shish
b7fc28e17f changed include file
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@795 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-09 01:10:29 +00:00
shish
b2f8165a59 remove user enable / disable in 2.2 too
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@794 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-09 00:34:08 +00:00
shish
57b50d8881 all those into 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@793 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-08 21:59:20 +00:00
shish
9f595e6273 backport
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@788 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-08 17:16:56 +00:00
shish
0c9fff3d11 backport
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@786 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-08 16:58:31 +00:00
shish
d3ceb8ef97 changes to 2.2, and remove broken text score
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@784 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-08 16:56:12 +00:00
shish
64db094721 2.2 too, as this is probably useful to a lot of people
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@780 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-08 16:03:30 +00:00
shish
d47fd5f25c regex thing in index in 2.2 too
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@778 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-08 15:42:05 +00:00
shish
f7d1df2727 both of those into 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@775 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-07 23:18:32 +00:00
shish
67455583e5 This feature freeze is more of a feature mild chill...
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@769 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-06 21:07:29 +00:00
shish
2e40bc73c8 I wonder if 2.2 should have branched this early <_<;;
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@767 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-06 18:42:52 +00:00
shish
c012717bce readme update
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@765 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-06 18:00:35 +00:00
shish
7b4dde4918 and, again, 2.2~
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@764 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-06 17:57:04 +00:00
shish
5ad77079a6 2.2.too
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@762 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-06 17:44:02 +00:00
shish
43028ebd67 both of them to 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@760 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-06 16:47:44 +00:00
shish
f821e752fc back to 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@757 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-06 16:38:32 +00:00
shish
73d5fe129a UploadingImageEvent -> DataUploadEvent
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@754 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-04 22:16:48 +00:00
shish
a226a17787 and in 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@751 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-04 10:38:41 +00:00
shish
03c9d6b298 better cache dir in 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@748 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-01 11:02:30 +00:00
shish
37a0aeb8ae ban by range in 2.2
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@747 7f39781d-f577-437e-ae19-be835c7a54ca
2008-04-01 11:00:19 +00:00
shish
47361369ff pull a couple of mostly cosmetic changes into stable
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@741 7f39781d-f577-437e-ae19-be835c7a54ca
2008-03-31 03:01:35 +00:00
shish
6c53088929 version number
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@736 7f39781d-f577-437e-ae19-be835c7a54ca
2008-03-30 06:06:00 +00:00
shish
a6d68c6f1b and commit it to 2.2...
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@735 7f39781d-f577-437e-ae19-be835c7a54ca
2008-03-29 04:13:54 +00:00
shish
57a6c39f2a stable too
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@733 7f39781d-f577-437e-ae19-be835c7a54ca
2008-03-29 04:00:09 +00:00
shish
a5f7b400f0 remove broken / non-gpl extensions
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@731 7f39781d-f577-437e-ae19-be835c7a54ca
2008-03-29 03:42:48 +00:00
shish
2a990735a0 branch for 2.2 stabilisation work
git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.2@730 7f39781d-f577-437e-ae19-be835c7a54ca
2008-03-29 03:38:54 +00:00
724 changed files with 27260 additions and 53475 deletions

View File

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

View File

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

1
.gitattributes vendored
View File

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

View File

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

View File

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

View File

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

View File

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

89
.gitignore vendored
View File

@ -1,89 +0,0 @@
backup
data
images
thumbs
*.phar
*.sqlite
*.cache
.devcontainer
trace.json
#Composer
composer.phar
composer.lock
/vendor/
# Created by http://www.gitignore.io
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
### OSX ###
.DS_Store
.AppleDouble
.LSOverride
# Icon must ends with two \r.
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
### Linux ###
*~
# KDE directory preferences
.directory
### vim ###
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
### PhpStorm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
## Directory-based project format
.idea/
# if you remove the above rule, at least ignore user-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# and these sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
## File-based project format
*.ipr
*.iws
*.iml
## Additional for IntelliJ
out/
# generated by mpeltonen/sbt-idea plugin
.idea_modules/
# generated by JIRA plugin
atlassian-ide-plugin.xml
# generated by Crashlytics plugin (for Android Studio and Intellij)
com_crashlytics_export_strings.xml

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

62
README.txt Normal file
View File

@ -0,0 +1,62 @@
_________.__ .__ .__ ________
/ _____/| |__ |__| _____ _____ |__| ____ \_____ \
\_____ \ | | \| |/ \ / \| |/ __ \ / ____/
/ \| Y \ | Y Y \ Y Y \ \ ___// \
/_______ /|___| /__|__|_| /__|_| /__|\___ >_______ \
\/ \/ \/ \/ \/ \/
Shimmie 2.2
~~~~~~~~~~~
Integrated extension management, tag history, RSS for search results, unified
image info editor, extensible file format support (ZIP, SWF, SVG, MP3, ...),
wget transloader, event log filtering and sorting, as well as the usual misc
improvements and bug fixes~
Requirements
~~~~~~~~~~~~
MySQL 4.1+
PHP 5.0+
GD or ImageMagick
PHP 4 support has currently been dropped, because it's a pain in the ass to
support. If you are unfortunate enough to be stuck on a PHP4-only host, I'd
be glad to host your image board for you :3
Installation
~~~~~~~~~~~~
1) Create a blank database
2) Unzip shimmie into a folder on the web host
3) Visit the folder with a web browser
4) Enter the location of the database, and choose login details for the first
admin of the board
5) Click "install". Hopefully you'll end up at the configuration screen; if
not, you should be given instructions on how to fix any errors~
Upgrade from 2.1.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!
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
Licence
~~~~~~~
All code is GPL2; ie, if you give shimmie to someone else, you have to give
them the source (which should be easy, as PHP is an interpreted language...).
If you want to add customisations to your own site, then those customisations
belong to you, and you can do what you want with them.

View File

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

461
composer.lock generated
View File

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

View File

@ -0,0 +1,56 @@
<?php
/**
* Name: Comment Word Ban
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: For stopping spam and other comment abuse
*/
class BanWords extends Extension {
public function receive_event($event) {
if(is_a($event, 'InitExtEvent')) {
global $config;
$config->set_default_string('banned_words', "
viagra
porn
");
}
if(is_a($event, 'CommentPostingEvent')) {
global $config;
$banned = $config->get_string("banned_words");
$comment = strtolower($event->comment);
foreach(explode("\n", $banned) as $word) {
$word = trim(strtolower($word));
if(strlen($word) == 0) {
// line is blank
continue;
}
else if($word[0] == '/') {
// lines that start with slash are regex
if(preg_match($word, $comment)) {
$event->veto("Comment contains banned terms");
break;
}
}
else {
// other words are literal
if(strpos($comment, $word) !== false) {
$event->veto("Comment contains banned terms");
break;
}
}
}
}
if(is_a($event, '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);
}
}
}
add_event_listener(new BanWords(), 30); // before the comment is added
?>

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

@ -0,0 +1,114 @@
<?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
*
*/
class BrowserSearch extends Extension {
public function receive_event($event) {
global $page;
global $config;
if(is_a($event, 'InitExtEvent')) {
$config->set_default_string("search_suggestions_results_order", 'a');
}
// Add in header code to let the browser know that the search plugin exists
if(is_a($event, 'PageRequestEvent')) {
// We need to build the data for the header
global $config;
$search_title = $config->get_string('title');
$search_file_url = make_link('browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml');
$page->add_header("<link rel='search' type='application/opensearchdescription+xml' title='$search_title' href='$search_file_url'>");
}
// The search.xml file that is generated on the fly
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "browser_search") && $event->get_arg(0) == "please_dont_use_this_tag_as_it_would_break_stuff__search.xml") {
// First, we need to build all the variables we'll need
$search_title = $config->get_string('title');
//$search_form_url = $config->get_string('base_href'); //make_link('post/list');
$search_form_url = make_link('post/list/{searchTerms}');
$suggenton_url = make_link('browser_search/')."{searchTerms}";
// Now for the XML
$xml = "
<SearchPlugin xmlns='http://www.mozilla.org/2006/browser/search/' xmlns:os='http://a9.com/-/spec/opensearch/1.1/'>
<os:ShortName>$search_title</os:ShortName>
<os:InputEncoding>UTF-8</os:InputEncoding>
<os:Image width='16'
height='16'>data:image/x-icon;base64,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFhYAfX19AH9/fwCAgIAAgYGBAIODgwCEhIQAhoaGAIeHhwCJiYkAioqKAIyMjACPj48AkJCQAJKSkgCTk5MAlZWVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEREREREREREREREREREREREAAAAAAAAAAAAAABERERERAAEBAQEBAQEBAQAAEREREQABAQEBAQEBAQEAAQAREREAAQEBAQEBAQEBAAEBABERAAEBAQEBAQEBAQABAQAREQAGBgUEAwIBAQEAAgEAEREADAwMCwsKCQkHAAkIABERABAQEBAQEA4ODAANDQAREQAQEBAQEBAQEBAAEBAAEREAEBAQEBAQEBAQABAQABERAAAAAAAAAAAAAAAQEAAREREAEBAPEBAQEBAQABAAEREREQAQEBAQEBAPEBAAABERERERAAAAAAAAAAAAAAAREREREREREREREREREREREf//AACADwAAgAcAAIADAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAMABAADgAQAA8AEAAP//AAA=</os:Image>
<SearchForm>$search_form_url</SearchForm>
<os:Url type='text/html' method='GET' template='$search_form_url'>
<os:Param name='search' value='{searchTerms}'/>
</os:Url>
<Url type='application/x-suggestions+json' template='$suggenton_url'/>
</SearchPlugin>
";
// And now to send it to the browser
$page->set_mode("data");
$page->set_type("text/xml");
$page->set_data($xml);
} else if(is_a($event, 'PageRequestEvent') && ($event->page_name == "browser_search") && !$config->get_bool("disable_search_suggestions")) { // We need to return results!
global $database;
// We have to build some json stuff
$tag_search = $event->get_arg(0);
// Now to get DB results
if($config->get_string("search_suggestions_results_order") == "a") {
$tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY tag ASC LIMIT 30",array($tag_search."%"));
} else {
$tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY count DESC LIMIT 30",array($tag_search."%"));
}
// And to do stuff with it. We want our output to look like:
// ["shimmie",["shimmies","shimmy","shimmie","21 shimmies","hip shimmies","skea shimmies"],[],[]]
$json_tag_list = "";
$tags_array = array();
foreach($tags as $tag) {
array_push($tags_array,$tag['tag']);
}
$json_tag_list .= implode("\",\"", $tags_array);
// $json_tag_list = implode($tags_array,", ");
// $json_tag_list = "\"".implode($tags_array,"\", \"")."\"";
// And now for the final output
$json_string = "[\"$tag_search\",[\"$json_tag_list\"],[],[]]";
$page->set_mode("data");
$page->set_data($json_string);
}
if(is_a($event, 'SetupBuildingEvent')) {
$sort_by = array();
$sort_by['Alphabetical'] = 'a';
$sort_by['Tag Count'] = 't';
$sb = new SetupBlock("Browser Search");
$sb->add_bool_option("disable_search_suggestions", "Disable search suggestions when using browser-based search: ");
$sb->add_label("<br>");
$sb->add_choice_option("search_suggestions_results_order", $sort_by, "Sort the suggestions by:");
$event->panel->add_block($sb);
}
}
}
add_event_listener(new BrowserSearch());
?>

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

@ -0,0 +1,89 @@
<?php
/**
* Name: Bulk Add
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Bulk add server-side images
*/
class BulkAdd extends Extension {
var $theme;
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("bulk_add", "BulkAddTheme");
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "bulk_add")) {
if($event->user->is_admin() && isset($_POST['dir'])) {
set_time_limit(0);
$this->add_dir($_POST['dir']);
$this->theme->display_upload_results($event->page);
}
}
if(is_a($event, 'AdminBuildingEvent')) {
global $page;
$this->theme->display_admin_block($page);
}
}
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;
$event = new DataUploadEvent($user, $tmpname, $metadata);
send_event($event);
if($event->vetoed) {
return $event->veto_reason;
}
}
}
private function add_dir($base, $subdir="") {
global $page;
if(!is_dir($base)) {
$this->theme->add_status("Error", "$base is not a directory");
return;
}
$list = "";
$dir = opendir("$base/$subdir");
while($filename = readdir($dir)) {
$fullpath = "$base/$subdir/$filename";
if(is_link($fullpath)) {
// ignore
}
else if(is_dir($fullpath)) {
if($filename[0] != ".") {
$this->add_dir($base, "$subdir/$filename");
}
}
else {
$tmpfile = $fullpath;
$tags = $subdir;
$tags = str_replace("/", " ", $tags);
$tags = str_replace("__", " ", $tags);
$list .= "<br>".html_escape("$subdir/$filename (".str_replace(" ", ",", $tags).")...");
$error = $this->add_image($tmpfile, $filename, $tags);
if(is_null($error)) {
$list .= "ok\n";
}
else {
$list .= "failed: $error\n";
}
}
}
closedir($dir);
$this->theme->add_status("Adding $subdir", $list);
}
}
add_event_listener(new BulkAdd());
?>

View File

@ -0,0 +1,42 @@
<?php
class BulkAddTheme extends Themelet {
var $messages = array();
/*
* Show a standard page for results to be put into
*/
public function display_upload_results($page) {
$page->set_title("Adding folder");
$page->set_heading("Adding folder");
$page->add_block(new NavBlock());
foreach($this->messages as $block) {
$page->add_block($block);
}
}
/*
* Add a section to the admin page. This should contain a form which
* links to bulk_add with POST[dir] set to the name of a server-side
* directory full of images
*/
public function display_admin_block($page) {
$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'>
Directory to add: <input type='text' name='dir' size='40'>
<input type='submit' value='Add'>
</form>
";
$page->add_block(new Block("Bulk Add", $html));
}
public function add_status($title, $body) {
$this->messages[] = new Block($title, $body);
}
}
?>

View File

@ -0,0 +1,433 @@
<?php
/*
Name: Danbooru Client API for Shimmie2
Description: Provides simple interfaces for third party software to interact with Shimmie via
simple HTTP GET/POST requests.
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_comment - NOT DONE YET, waiting on some backend shimmie code :)
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
CHANGELOG
01-MAR-08 7:00PM CST - JJS
Rewrote to make it compatible with Shimmie trunk again (r723 at least)
It may or may not support the new file handling stuff correctly, I'm only testing with images and the danbooru uploader for firefox
21-OCT-07 9:07PM CST - JJS
Turns out I actually did need to implement the new parameter names
for danbooru api v1.8.1. Now danbooruup should work when used with /api/danbooru/post/create.xml
Also correctly redirects the url provided by danbooruup in the event
of a duplicate image.
19-OCT-07 4:46PM CST - JJS
Add compatibility with danbooru api v1.8.1 style urls
for find_posts and add_post. NOTE: This does not implement
the changes to the parameter names, it is simply a
workaround for the latest danbooruup firefox extension.
Completely compatibility will probably involve a rewrite with a different URL
*/
class DanbooruApi extends Extension
{
// Receive the event
public function receive_event($event)
{
// Check if someone is accessing /api/danbooru (us)
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "api") && ($event->get_arg(0) == 'danbooru'))
{
// execute the danbooru processing code
$this->api_danbooru($event);
}
if(is_a($event, 'SearchTermParseEvent'))
{
$matches = array();
if(preg_match("/md5:([0-9a-fA-F]*)/i", $event->term, $matches))
{
$hash = strtolower($matches[1]);
$event->set_querylet(new Querylet("images.hash = '$hash'"));
}
}
}
// Danbooru API
private function api_danbooru($event)
{
global $page;
global $config;
global $database;
global $user;
$page->set_mode("data");
$page->set_type("application/xml");
//debug
//$page->set_type("text/plain");
$results = array();
/*
add_comment()
Adds a comment to a post.
Parameters
* body: the body of the comment
* post_id: the post id
* login: your login
* password: your password Response
* 200: success
* 500: error. response body will the the error message.
*/
if($event->get_arg(1) == 'add_comment')
{
// On error the response body is the error message so plain text is fine
$page->set_type("text/plain");
// We do wish to auth the user if possible, if it fails treat as anonymous
$this->authenticate_user();
// Check if anonymous commenting is allowed before proceeding
if($config->get_bool("comment_anon") || !$user->is_anonymous())
{
// Did the user supply a post_id and a comment body?
if(isset($_REQUEST['post_id']) && isset($_REQUEST['body']) && trim($_REQUEST['body']) != "")
{
// waiting for someone to write an event handler for the comments extension :)
} else
{
// User didn't supply necessary parameters, tell them that
header("HTTP/1.0 500 Internal Server Error");
$page->set_data("You forgot to supply either a post id or the body of your comment");
}
} else
{
header("HTTP/1.0 500 Internal Server Error");
$page->set_data("You supplied an invalid login or password or anonymous commenting is currently disabled");
}
}
/*
add_post()
Adds a post to the database.
Parameters
* login: login
* password: password
* file: file as a multipart form
* source: source url
* title: title **IGNORED**
* tags: list of tags as a string, delimited by whitespace
* md5: MD5 hash of upload in hexadecimal format
* rating: rating of the post. can be explicit, questionable, or safe. **IGNORED**
Notes
* The only necessary parameter is tags and either file or source.
* If you want to sign your post, you need a way to authenticate your account, either by supplying login and password, or by supplying a cookie.
* If an account is not supplied or if it doesnt authenticate, he post will be added anonymously.
* If the md5 parameter is supplied and does not match the hash of whats on the server, the post is rejected.
Response
The response depends on the method used:
Post
* X-Danbooru-Location set to the URL for newly uploaded post.
Get
* Redirected to the newly uploaded post.
*/
if(($event->get_arg(1) == 'add_post') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'create.xml')))
{
// No XML data is returned from this function
$page->set_type("text/plain");
// Check first if a login was supplied, if it wasn't check if the user is logged in via cookie
// If all that fails, it's an anonymous upload
$this->authenticate_user();
// Now we check if a file was uploaded or a url was provided to transload
// Much of this code is borrowed from /ext/upload
if($config->get_bool("upload_anon") || !$user->is_anonymous())
{
$file = null;
$filename = "";
$source = "";
if(isset($_FILES['file']))
{ // A file was POST'd in
$file = $_FILES['file']['tmp_name'];
$filename = $_FILES['file']['name'];
// If both a file is posted and a source provided, I'm assuming source is the source of the file
if(isset($_REQUEST['source']) && !empty($_REQUEST['source']))
{
$source = $_REQUEST['source'];
} else
{
$source = null;
}
} elseif(isset($_FILES['post']))
{
$file = $_FILES['post']['tmp_name']['file'];
$filename = $_FILES['post']['name']['file'];
if(isset($_REQUEST['post']['source']) && !empty($_REQUEST['post']['source']))
{
$source = $_REQUEST['post']['source'];
} else
{
$source = null;
}
} elseif(isset($_REQUEST['source']) || isset($_REQUEST['post']['source']))
{ // A url was provided
$url = isset($_REQUEST['source']) ? $_REQUEST['source'] : $_REQUEST['post']['source'];
$source = $url;
$tmp_filename = tempnam("/tmp", "shimmie_transload");
// Are we using fopen wrappers or curl?
if($config->get_string("transload_engine") == "fopen")
{
$fp = fopen($url, "r");
if(!$fp) {
header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: fopen read error");
}
$data = "";
$length = 0;
while(!feof($fp) && $length <= $config->get_int('upload_size'))
{
$data .= fread($fp, 8192);
$length = strlen($data);
}
fclose($fp);
$fp = fopen($tmp_filename, "w");
fwrite($fp, $data);
fclose($fp);
}
if($config->get_string("transload_engine") == "curl")
{
$ch = curl_init($url);
$fp = fopen($tmp_filename, "w");
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
fclose($fp);
}
$file = $tmp_filename;
$filename = basename($url);
} else
{ // Nothing was specified at all
header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: no input files");
return;
}
// Get tags out of url
$posttags = tag_explode(isset($_REQUEST['tags']) ? $_REQUEST['tags'] : $_REQUEST['post']['tags']);
$hash = md5_file($file);
// Was an md5 supplied? Does it match the file hash?
if(isset($_REQUEST['md5']))
{
if(strtolower($_REQUEST['md5']) != $hash)
{
header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: md5 mismatch");
return;
}
}
// Upload size checking is now performed in the upload extension
// It is also currently broken due to some confusion over file variable ($tmp_filename?)
// Does it exist already?
$existing = $database->get_image_by_hash($hash);
if(!is_null($existing)) {
header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: duplicate");
$existinglink = make_link("post/view/" . $existing->id);
header("X-Danbooru-Location: $existinglink");
}
// Fire off an event which should process the new file and add it to the db
$fileinfo = pathinfo($filename);
$metadata['filename'] = $fileinfo['basename'];
$metadata['extension'] = $fileinfo['extension'];
$metadata['tags'] = $posttags;
$metadata['source'] = $source;
$nevent = new DataUploadEvent($user, $file, $metadata);
send_event($nevent);
// Did something screw up?
if($event->vetoed) {
header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: $event->veto_reason");
return;
} else
{ // If it went ok, grab the id for the newly uploaded image and pass it in the header
$newimg = $database->get_image_by_hash($hash);
$newid = make_link("post/view/" . $newimg->id);
// Did we POST or GET this call?
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
header("X-Danbooru-Location: $newid");
}
else
header("Location: $newid");
}
} else
{
header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: authentication error");
return;
}
}
/*
find_posts()
Find all posts that match the search criteria. Posts will be ordered by id descending.
Parameters
* md5: md5 hash to search for (comma delimited)
* id: id to search for (comma delimited)
* tags: what tags to search for
* limit: limit
* offset: offset
* after_id: limit results to posts added after this id
*/
if(($event->get_arg(1) == 'find_posts') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'index.xml')))
{
if(isset($_GET['md5']))
{
$md5list = explode(",",$_GET['md5']);
foreach($md5list as $md5)
{
$results[] = $database->get_image_by_hash($md5);
}
} elseif(isset($_GET['id']))
{
$idlist = explode(",",$_GET['id']);
foreach($idlist as $id)
{
$results[] = $database->get_image($id);
}
} else
{
$limit = isset($_GET['limit']) ? int_escape($_GET['limit']) : 100;
$start = isset($_GET['offset']) ? int_escape($_GET['offset']) : 0;
$tags = isset($_GET['tags']) ? tag_explode($_GET['tags']) : array();
$results = $database->get_images($start,$limit,$tags);
}
// Now we have the array $results filled with Image objects
// Let's display them
$xml = "<posts>\n";
foreach($results as $img)
{
// Sanity check to see if $img is really an image object
// If it isn't (e.g. someone requested an invalid md5 or id), break out of the this
if(!is_object($img))
continue;
$taglist = $img->get_tag_list();
$owner = $img->get_owner();
$xml .= "<post md5=\"$img->hash\" rating=\"Questionable\" date=\"$img->posted\" is_warehoused=\"false\" file_name=\"$img->filename\" tags=\"$taglist\" source=\"$img->source\" score=\"0\" id=\"$img->id\" author=\"$owner->name\"/>\n";
}
$xml .= "</posts>";
$page->set_data($xml);
}
/*
find_tags() Find all tags that match the search criteria.
Parameters
* id: A comma delimited list of tag id numbers.
* name: A comma delimited list of tag names.
* tags: any typical tag query. See Tag#parse_query for details.
* after_id: limit results to tags with an id number after after_id. Useful if you only want to refresh
*/
if($event->get_arg(1) == 'find_tags')
{
if(isset($_GET['id']))
{
$idlist = explode(",",$_GET['id']);
foreach($idlist as $id)
{
$sqlresult = $database->execute("SELECT id,tag,count FROM tags WHERE id = ?", array($id));
if(!$sqlresult->EOF)
{
$results[] = array($sqlresult->fields['count'], $sqlresult->fields['tag'], $sqlresult->fields['id']);
}
}
} elseif(isset($_GET['name']))
{
$namelist = explode(",",$_GET['name']);
foreach($namelist as $name)
{
$sqlresult = $database->execute("SELECT id,tag,count FROM tags WHERE tag = ?", array($name));
if(!$sqlresult->EOF)
{
$results[] = array($sqlresult->fields['count'], $sqlresult->fields['tag'], $sqlresult->fields['id']);
}
}
}
/* Currently disabled to maintain identical functionality to danbooru 1.0's own "broken" find_tags
elseif(isset($_GET['tags']))
{
$start = isset($_GET['after_id']) ? int_escape($_GET['offset']) : 0;
$tags = tag_explode($_GET['tags']);
}
*/
else
{
$start = isset($_GET['after_id']) ? int_escape($_GET['offset']) : 0;
$sqlresult = $database->execute("SELECT id,tag,count FROM tags WHERE count > 0 AND id >= ? ORDER BY id DESC",array($start));
while(!$sqlresult->EOF)
{
$results[] = array($sqlresult->fields['count'], $sqlresult->fields['tag'], $sqlresult->fields['id']);
$sqlresult->MoveNext();
}
}
// Tag results collected, build XML output
$xml = "<tags>\n";
foreach($results as $tag)
{
$xml .= "<tag type=\"0\" count=\"$tag[0]\" name=\"$tag[1]\" id=\"$tag[2]\"/>\n";
}
$xml .= "</tags>";
$page->set_data($xml);
}
// Hackery for danbooruup 0.3.2 providing the wrong view url. This simply redirects to the proper
// Shimmie view page
// Example: danbooruup says the url is http://shimmie/api/danbooru/post/show/123
// This redirects that to http://shimmie/post/view/123
if(($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show'))
{
$fixedlocation = make_link("post/view/" . $event->get_arg(3));
header("Location: $fixedlocation");
}
}
// Turns out I use this a couple times so let's make it a utility function
// Authenticates a user based on the contents of the login and password parameters
// or makes them anonymous. Does not set any cookies or anything permanent.
private function authenticate_user()
{
global $database;
global $user;
if(isset($_REQUEST['login']) && isset($_REQUEST['password']))
{
// Get this user from the db, if it fails the user becomes anonymous
// Code borrowed from /ext/user
$name = $_REQUEST['login'];
$pass = $_REQUEST['password'];
$hash = md5( strtolower($name) . $pass );
$duser = $database->get_user_by_name_and_hash($name, $hash);
if(!is_null($duser)) {
$user = $duser;
} else
{
$user = $database->get_user_by_id($config->get_int("anon_id", 0));
}
}
}
}
add_event_listener(new DanbooruApi());
?>

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

@ -0,0 +1,48 @@
<?php
/**
* Name: Downtime
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Show a "down for maintenance" page
*/
class Downtime extends Extension {
var $theme;
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("downtime", "DowntimeTheme");
if(is_a($event, 'SetupBuildingEvent')) {
$sb = new SetupBlock("Downtime");
$sb->add_bool_option("downtime", "Disable non-admin access: ");
$sb->add_longtext_option("downtime_message", "<br>");
$event->panel->add_block($sb);
}
if(is_a($event, 'PageRequestEvent')) {
global $config;
if($config->get_bool("downtime")) {
$this->check_downtime($event);
$this->theme->display_notification($event->page);
}
}
}
private function check_downtime($event) {
global $user;
global $config;
if($config->get_bool("downtime") && !$user->is_admin() &&
is_a($event, 'PageRequestEvent') && !$this->is_safe_page($event)) {
$msg = $config->get_string("downtime_message");
$this->theme->display_message($msg);
}
}
private function is_safe_page($event) {
if($event->page_name == "user_admin" && $event->get_arg(0) == "login") return true;
else return false;
}
}
add_event_listener(new Downtime(), 10);
?>

View File

@ -0,0 +1,30 @@
<?php
class DowntimeTheme Extends Themelet {
/*
* Show the admin that downtime mode is enabled
*/
public function display_notification($page) {
$page->add_block(new Block("Downtime",
"<span style='font-size: 1.5em'><b>DOWNTIME MODE IS ON!</b></span>", "left", 0));
}
/*
* Display $message and exit
*/
public function display_message($message) {
header("HTTP/1.0 503 Service Temporarily Unavailable");
print <<<EOD
<html>
<head>
<title>Downtime</title>
</head>
<body>
$message
</body>
</html>
EOD;
exit;
}
}
?>

View File

Before

Width:  |  Height:  |  Size: 170 B

After

Width:  |  Height:  |  Size: 170 B

View File

Before

Width:  |  Height:  |  Size: 172 B

After

Width:  |  Height:  |  Size: 172 B

View File

Before

Width:  |  Height:  |  Size: 171 B

After

Width:  |  Height:  |  Size: 171 B

View File

Before

Width:  |  Height:  |  Size: 172 B

After

Width:  |  Height:  |  Size: 172 B

View File

Before

Width:  |  Height:  |  Size: 498 B

After

Width:  |  Height:  |  Size: 498 B

View File

Before

Width:  |  Height:  |  Size: 170 B

After

Width:  |  Height:  |  Size: 170 B

View File

Before

Width:  |  Height:  |  Size: 236 B

After

Width:  |  Height:  |  Size: 236 B

View File

Before

Width:  |  Height:  |  Size: 236 B

After

Width:  |  Height:  |  Size: 236 B

View File

Before

Width:  |  Height:  |  Size: 171 B

After

Width:  |  Height:  |  Size: 171 B

View File

Before

Width:  |  Height:  |  Size: 176 B

After

Width:  |  Height:  |  Size: 176 B

View File

Before

Width:  |  Height:  |  Size: 336 B

After

Width:  |  Height:  |  Size: 336 B

View File

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 174 B

View File

Before

Width:  |  Height:  |  Size: 349 B

After

Width:  |  Height:  |  Size: 349 B

View File

Before

Width:  |  Height:  |  Size: 171 B

After

Width:  |  Height:  |  Size: 171 B

View File

Before

Width:  |  Height:  |  Size: 248 B

After

Width:  |  Height:  |  Size: 248 B

View File

Before

Width:  |  Height:  |  Size: 176 B

After

Width:  |  Height:  |  Size: 176 B

View File

Before

Width:  |  Height:  |  Size: 650 B

After

Width:  |  Height:  |  Size: 650 B

View File

Before

Width:  |  Height:  |  Size: 485 B

After

Width:  |  Height:  |  Size: 485 B

View File

Before

Width:  |  Height:  |  Size: 171 B

After

Width:  |  Height:  |  Size: 171 B

View File

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 174 B

View File

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 174 B

View File

Before

Width:  |  Height:  |  Size: 238 B

After

Width:  |  Height:  |  Size: 238 B

View File

Before

Width:  |  Height:  |  Size: 170 B

After

Width:  |  Height:  |  Size: 170 B

View File

@ -0,0 +1,30 @@
<?php
/**
* Name: Emoticon Filter
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Turn :smile: into a link to smile.gif
*/
class Emoticons extends Extension {
public function receive_event($event) {
if(is_a($event, 'TextFormattingEvent')) {
$event->formatted = $this->bbcode_to_html($event->formatted);
$event->stripped = $this->bbcode_to_text($event->stripped);
}
}
private function bbcode_to_html($text) {
global $config;
$data_href = get_base_href();
$text = preg_replace("/:([a-z]*?):/s", "<img src='$data_href/ext/emoticons/default/\\1.gif'>", $text);
return $text;
}
private function bbcode_to_text($text) {
// $text = preg_replace("/:([a-z]*?):/s", "\\1", $text);
return $text;
}
}
add_event_listener(new Emoticons());
?>

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

@ -0,0 +1,73 @@
<?php
/**
* Name: System Info
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Show various bits of system information, for debugging
*/
class ET extends Extension {
var $theme;
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("et", "ETTheme");
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "system_info")) {
if($event->user->is_admin()) {
$this->theme->display_info_page($event->page, $this->get_info());
}
}
if(is_a($event, 'UserBlockBuildingEvent')) {
if($event->user->is_admin()) {
$event->add_link("System Info", make_link("system_info"));
}
}
}
// do it {{{
private function get_info() {
global $database;
global $config;
global $_event_listeners; // yay for using secret globals \o/
$info = array();
$info['site_title'] = $config->get_string("title");
$info['site_theme'] = $config->get_string("theme");
$info['site_genre'] = "[please write something here]";
$info['site_url'] = isset($_SERVER['SCRIPT_URI']) ? dirname($_SERVER['SCRIPT_URI']) : "???";
$info['sys_shimmie'] = VERSION;
$info['sys_schema'] = $config->get_string("db_version");
$info['sys_php'] = phpversion();
$info['sys_os'] = php_uname();
$info['sys_server'] = $_SERVER["SERVER_SOFTWARE"];
include "config.php"; // more magical hax
$proto = preg_replace("#(.*)://.*#", "$1", $database_dsn);
$db = $database->db->ServerInfo();
$info['sys_db'] = "$proto / {$db['version']}";
$info['stat_images'] = $database->db->GetOne("SELECT COUNT(*) FROM images");
$info['stat_comments'] = $database->db->GetOne("SELECT COUNT(*) FROM comments");
$info['stat_users'] = $database->db->GetOne("SELECT COUNT(*) FROM users");
$info['stat_tags'] = $database->db->GetOne("SELECT COUNT(*) FROM tags");
$info['stat_image_tags'] = $database->db->GetOne("SELECT COUNT(*) FROM image_tags");
$els = array();
foreach($_event_listeners as $el) {
$els[] = get_class($el);
}
$info['sys_extensions'] = join(', ', $els);
//$cfs = array();
//foreach($database->get_all("SELECT name, value FROM config") as $pair) {
// $cfs[] = $pair['name']."=".$pair['value'];
//}
//$info[''] = "Config: ".join(", ", $cfs);
return $info;
}
// }}}
}
add_event_listener(new ET());
?>

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

@ -0,0 +1,54 @@
<?php
class ETTheme extends Themelet {
/*
* Create a page showing info
*
* $info = an array of ($name => $value)
*/
public function display_info_page($page, $info) {
$page->set_title("System Info");
$page->set_heading("System Info");
$page->add_block(new NavBlock());
$page->add_block(new Block("Data which is to be sent:", $this->build_data_form($info)));
}
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']}
URL: {$info['site_url']}
System stats:
Shimmie: {$info['sys_shimmie']}
Schema: {$info['sys_schema']}
PHP: {$info['sys_php']}
OS: {$info['sys_os']}
Server: {$info['sys_server']}
Database: {$info['sys_db']}
Extensions: {$info['sys_extensions']}
Shimmie stats:
Images: {$info['stat_images']}
Comments: {$info['stat_comments']}
Users: {$info['stat_users']}
Tags: {$info['stat_tags']}
Applications: {$info['stat_image_tags']}
EOD;
$html = <<<EOD
<form action='http://shimmie.shishnet.org/register.php' method='POST'>
<input type='hidden' name='registration_api' value='1'>
<textarea name='data' rows='20' cols='80'>$data</textarea>
<br><input type='submit' value='Click to send to Shish'>
<br>Your stats are useful so that I know which combinations
of web servers / databases / etc I need to support,
<br>and so that I can get some idea of how people use shimmie generally
</form>
EOD;
return $html;
}
}
?>

120
contrib/event_log/main.php Normal file
View File

@ -0,0 +1,120 @@
<?php
/**
* Name: EventLog
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: A log of things that happen, for abuse tracking
*/
class EventLog extends Extension {
var $theme;
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("event_log", "EventLogTheme");
if(is_a($event, 'InitExtEvent')) {
$this->setup();
}
if(is_a($event, 'PageRequestEvent') && $event->page_name == "event_log") {
global $database;
if($event->user->is_admin()) {
if(isset($_POST['action'])) {
switch($_POST['action']) {
case 'clear':
$database->execute("DELETE FROM event_log");
break;
}
}
$columns = array("name", "date", "owner_ip", "event");
$orders = array("ASC", "DESC");
$sort = "date";
if(isset($_GET['sort']) && in_array($_GET['sort'], $columns)) {
$sort = $_GET['sort'];
}
$order = "DESC";
if(isset($_GET['order']) && in_array($_GET['order'], $orders)) {
$order = $_GET['order'];
}
$filter_sql = "";
if(isset($_GET['filter']) && isset($_GET['where']) && in_array($_GET['filter'], $columns)) {
$filter = $_GET['filter'];
$where = $database->db->Quote($_GET['where']);
$filter_sql = "WHERE $filter = $where";
}
$events = $database->get_all("
SELECT event_log.*,users.name FROM event_log
JOIN users ON event_log.owner_id = users.id
$filter_sql
ORDER BY $sort $order
");
$this->theme->display_page($event->page, $events);
}
else {
$this->theme->display_error($event->page, "Denied", "Only admins can see the event log");
}
}
if(is_a($event, 'UserBlockBuildingEvent')) {
if($event->user->is_admin()) {
$event->add_link("Event Log", make_link("event_log"));
}
}
global $user; // bad
if(is_a($event, 'UploadingImageEvent')) {
$this->add_to_log($event->user, 'Uploading Image', "Uploaded a new image");
}
if(is_a($event, 'CommentPostingEvent')) {
$this->add_to_log($event->user, 'Comment Posting', "Posted a comment on image #{$event->image_id}");
}
if(is_a($event, 'WikiUpdateEvent')) {
$this->add_to_log($event->user, 'Wiki Update', "Edited '{$event->wikipage->title}'");
}
if(is_a($event, 'ConfigSaveEvent')) {
$this->add_to_log($user, 'Config Save', "Updated the board config");
}
if(is_a($event, 'ImageDeletionEvent')) {
$this->add_to_log($user, 'Image Deletion', "Deleted image {$event->image->id} (tags: {$event->image->get_tag_list()})");
}
if(is_a($event, 'SourceSetEvent')) {
$this->add_to_log($user, 'Source Set', "Source for image #{$event->image_id} set to '{$event->source}'");
}
if(is_a($event, 'TagSetEvent')) {
$tags = implode($event->tags, ", ");
$this->add_to_log($user, 'Tags Set', "Tags for image #{$event->image_id} set to '$tags'");
}
}
private function add_to_log($user, $event, $entry) {
global $database;
$database->execute("
INSERT INTO event_log (owner_id, owner_ip, date, event, entry)
VALUES (?, ?, now(), ?, ?)",
array($user->id, $_SERVER['REMOTE_ADDR'], $event, $entry));
}
private function setup() {
global $database;
global $config;
if($config->get_int("ext_event_log_version", 0) < 1) {
$database->Execute("CREATE TABLE event_log (
id int(11) NOT NULL auto_increment primary key,
owner_id int(11) NOT NULL,
owner_ip char(15) NOT NULL,
date datetime NOT NULL,
event varchar(32) NOT NULL,
entry varchar(255) NOT NULL
)");
$config->set_int("ext_event_log_version", 1);
}
}
}
add_event_listener(new EventLog(), 99); // ignore vetoed events
?>

View File

@ -0,0 +1,83 @@
<?php
class EventLogTheme extends Themelet {
public function display_page($page, $events) {
$page->set_title("Event Log");
$page->set_heading("Event Log");
$page->add_block(new NavBlock());
$this->display_table($page, $events);
$this->display_controls($page);
}
protected function display_table($page, $events) {
$table = "
<style>
.event_log_table TD {
font-size: 0.75em;
}
.event_log_table TD.entry {
text-align: left;
vertical-align: middle;
}
</style>
<table border='1' class='event_log_table'>
<tr>
<th>User
<a href='".make_link("event_log", "sort=name&order=ASC")."'>+</a>
<a href='".make_link("event_log", "sort=name&order=DESC")."'>-</a>
</th>
<th style='width: 10em;'>IP
<a href='".make_link("event_log", "sort=owner_ip&order=ASC")."'>+</a>
<a href='".make_link("event_log", "sort=owner_ip&order=DESC")."'>-</a>
</th>
<th rowspan='2' class='entry'>Entry</th>
</tr>
<tr>
<th style='width: 10em;'>Date
<a href='".make_link("event_log", "sort=date&order=ASC")."'>+</a>
<a href='".make_link("event_log", "sort=date&order=DESC")."'>-</a>
</th>
<th>Event
<a href='".make_link("event_log", "sort=event&order=ASC")."'>+</a>
<a href='".make_link("event_log", "sort=event&order=DESC")."'>-</a>
</th>
</tr>
";
foreach($events as $event) {
$entry = html_escape($event['entry']);
$table .= "
<tr>
<td>
<a href='".make_link("event_log", "filter=name&where={$event['name']}")."'>{$event['name']}</a>
</td>
<td>
<a href='".make_link("event_log", "filter=owner_ip&where={$event['owner_ip']}")."'>{$event['owner_ip']}</a>
</td>
<td rowspan='2' class='entry'>{$entry}</td>
</tr>
<tr>
<td>
{$event['date']}
</td>
<td>
<a href='".make_link("event_log", "filter=event&where={$event['event']}")."'>{$event['event']}</a>
</td>
</tr>
";
}
$table .= "</table>";
$page->add_block(new Block("Log Contents", $table));
}
protected function display_controls($page) {
$html = "
<form action='".make_link("event_log")."' method='POST'>
<input type='hidden' name='action' value='clear'>
<input type='submit' value='Clear Log'>
</form>
";
$page->add_block(new Block(null, $html, "main", 60));
}
}
?>

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

@ -0,0 +1,60 @@
<?php
/**
* Name: Featured Image
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Bring a specific image to the users' attentions
*/
class Featured extends Extension {
var $theme;
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("featured", "FeaturedTheme");
if(is_a($event, 'InitExtEvent')) {
global $config;
$config->set_default_int('featured_id', 0);
}
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "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);
$event->page->set_mode("redirect");
$event->page->set_redirect(make_link("post/view/$id"));
}
}
}
if(is_a($event, 'PostListBuildingEvent')) {
global $config, $database;
$fid = $config->get_int("featured_id");
if($fid > 0) {
$image = $database->get_image($fid);
if(!is_null($image)) {
$this->theme->display_featured($event->page, $image);
}
}
}
/*
if(is_a($event, 'SetupBuildingEvent')) {
$sb = new SetupBlock("Featured Image");
$sb->add_int_option("featured_id", "Image ID: ");
$event->panel->add_block($sb);
}
*/
if(is_a($event, 'ImageAdminBlockBuildingEvent')) {
if($event->user->is_admin()) {
$event->add_part($this->theme->get_buttons_html($event->image->id));
}
}
}
}
add_event_listener(new Featured());
?>

View File

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

View File

@ -0,0 +1,98 @@
<?php
/**
* Name: Archive File Handler
* Author: Shish <webmaster@shishnet.org>
* Description: Allow users to upload archives (zip, etc)
*/
class ArchiveFileHandler extends Extension {
public function receive_event($event) {
if(is_a($event, 'InitExtEvent')) {
global $config;
$config->set_default_string('archive_tmp_dir', "/tmp");
$config->set_default_string('archive_extract_command', 'unzip -d "%d" "%f"');
}
if(is_a($event, '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);
}
if(is_a($event, 'DataUploadEvent') && $this->supported_ext($event->type)) {
global $config;
$tmp = $config->get_string('archive_tmp_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);
$this->add_dir($tmpdir);
unlink($tmpdir);
}
}
private function supported_ext($ext) {
$exts = array("zip");
return array_contains($exts, strtolower($ext));
}
// copied from bulk add extension
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;
$event = new DataUploadEvent($user, $tmpname, $metadata);
send_event($event);
if($event->vetoed) {
return $event->veto_reason;
}
}
}
// copied from bulk add extension
private function add_dir($base, $subdir="") {
global $page;
$list = "";
$dir = opendir("$base/$subdir");
while($filename = readdir($dir)) {
$fullpath = "$base/$subdir/$filename";
if(is_link($fullpath)) {
// ignore
}
else if(is_dir($fullpath)) {
if($filename[0] != ".") {
$this->add_dir($base, "$subdir/$filename");
}
}
else {
$tmpfile = $fullpath;
$tags = $subdir;
$tags = str_replace("/", " ", $tags);
$tags = str_replace("__", " ", $tags);
$list .= "<br>".html_escape("$subdir/$filename (".str_replace(" ", ",", $tags).")...");
$error = $this->add_image($tmpfile, $filename, $tags);
if(is_null($error)) {
$list .= "ok\n";
}
else {
$list .= "failed: $error\n";
}
}
}
closedir($dir);
// $this->theme->add_status("Adding $subdir", $list);
}
}
add_event_listener(new ArchiveFileHandler());
?>

View File

@ -0,0 +1,131 @@
<?php
/**
* Name: Flash File Handler
* Author: Shish <webmaster@shishnet.org>
* Description: Handle Flash files
*/
class FlashFileHandler extends Extension {
var $theme;
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("handle_flash", "FlashFileHandlerTheme");
if(is_a($event, '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)) {
$event->veto("Flash Handler failed to create image object from data. ".
"Note: compressed flash files are currently unsupported");
return;
}
send_event(new ImageAdditionEvent($event->user, $image));
}
if(is_a($event, '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(is_a($event, 'DisplayingImageEvent') && $this->supported_ext($event->image->ext)) {
$this->theme->display_image($event->page, $event->image);
}
}
private function supported_ext($ext) {
$exts = array("swf");
return array_contains($exts, strtolower($ext));
}
private function create_image_from_data($filename, $metadata) {
global $config;
$image = new Image();
$image->filesize = $metadata['size'];
$image->hash = $metadata['hash'];
$image->filename = $metadata['filename'];
$image->ext = $metadata['extension'];
$image->tag_array = tag_explode($metadata['tags']);
$image->source = $metadata['source'];
// redundant, since getimagesize() works on SWF o_O
// $rect = $this->swf_get_bounds($filename);
// if(is_null($rect)) {
// return $null;
// }
// $image->width = $rect[1];
// $image->height = $rect[3];
if(!($info = getimagesize($filename))) return null;
$image->width = $info[0];
$image->height = $info[1];
return $image;
}
private function str_to_binarray($string) {
$binary = array();
for($j=0; $j<strlen($string); $j++) {
$c = ord($string[$j]);
for($i=7; $i>=0; $i--) {
$binary[] = ($c >> $i) & 0x01;
}
}
return $binary;
}
private function binarray_to_int($binarray, $start=0, $length=32) {
$int = 0;
for($i=$start; $i<$start + $length; $i++) {
$int = $int << 1;
$int = $int + ($binarray[$i] == "1" ? 1 : 0);
}
return $int;
}
private function swf_get_bounds($filename) {
$fp = fopen($filename, "r");
$head = fread($fp, 3);
$version = fread($fp, 1);
$length = fread($fp, 4);
if($head == "FWS") {
$data = fread($fp, 16);
}
else if($head == "CWS") {
$data = fread($fp, 128*1024);
$data = gzuncompress($data);
$data = substr($data, 0, 16);
}
$bounds = array();
$rect_bin = $this->str_to_binarray($data);
$nbits = $this->binarray_to_int($rect_bin, 0, 5);
$bounds[] = $this->binarray_to_int($rect_bin, 5 + 0 * $nbits, $nbits) / 20;
$bounds[] = $this->binarray_to_int($rect_bin, 5 + 1 * $nbits, $nbits) / 20;
$bounds[] = $this->binarray_to_int($rect_bin, 5 + 2 * $nbits, $nbits) / 20;
$bounds[] = $this->binarray_to_int($rect_bin, 5 + 3 * $nbits, $nbits) / 20;
return $bounds;
}
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());
?>

View File

@ -0,0 +1,24 @@
<?php
class FlashFileHandlerTheme extends Themelet {
public function display_image($page, $image) {
$ilink = $image->get_image_link();
// FIXME: object and embed have "height" and "width"
$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'
height='{$image->height}'
width='{$image->width}'
>
<param name='movie' value='$ilink'/>
<param name='quality' value='high' />
<embed src='$ilink' quality='high'
pluginspage='http://www.macromedia.com/go/getflashplayer'
height='{$image->height}'
width='{$image->width}'
type='application/x-shockwave-flash'></embed>
</object>";
$page->add_block(new Block("Image", $html, "main", 0));
}
}
?>

View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

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

@ -0,0 +1,116 @@
<?php
/**
* Name: ICO File Handler
* Author: Shish <webmaster@shishnet.org>
* Description: Handle windows icons
*/
class IcoFileHandler extends Extension {
var $theme;
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("handle_ico", "IcoFileHandlerTheme");
if(is_a($event, '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)) {
$event->veto("Handler failed to create image object from data");
return;
}
$iae = new ImageAdditionEvent($event->user, $image);
send_event($iae);
if($iae->vetoed) {
$event->veto($iae->veto_reason);
return;
}
}
if(is_a($event, 'ThumbnailGenerationEvent') && $this->supported_ext($event->type)) {
$this->create_thumb($event->hash);
}
if(is_a($event, 'DisplayingImageEvent') && $this->supported_ext($event->image->ext)) {
$this->theme->display_image($event->page, $event->image);
}
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "get_ico")) {
global $database;
$id = int_escape($event->get_arg(0));
$image = $database->get_image($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"));
}
}
private function supported_ext($ext) {
$exts = array("ico", "ani", "cur");
return array_contains($exts, strtolower($ext));
}
private function create_image_from_data($filename, $metadata) {
global $config;
$image = new Image();
$info = "";
$fp = fopen($filename, "r");
$header = unpack("snull/stype/scount", fread($fp, 6));
$subheader = unpack("cwidth/cheight/ccolours/cnull/splanes/sbpp/lsize/loffset", fread($fp, 16));
fclose($fp);
$image->width = $subheader['width'];
$image->height = $subheader['height'];
$image->filesize = $metadata['size'];
$image->hash = $metadata['hash'];
$image->filename = $metadata['filename'];
$image->ext = $metadata['extension'];
$image->tag_array = tag_explode($metadata['tags']);
$image->source = $metadata['source'];
return $image;
}
private function check_contents($file) {
if(!file_exists($file)) return false;
$fp = fopen($file, "r");
$header = unpack("snull/stype/scount", fread($fp, 6));
fclose($fp);
return ($header['null'] == 0 && ($header['type'] == 0 || $header['type'] == 1));
}
private function create_thumb($hash) {
global $config;
$ha = substr($hash, 0, 2);
$inname = "images/$ha/$hash";
$outname = "thumbs/$ha/$hash";
$w = $config->get_int("thumb_width");
$h = $config->get_int("thumb_height");
$q = $config->get_int("thumb_quality");
$mem = $config->get_int("thumb_max_memory") / 1024 / 1024; // IM takes memory in MB
if($config->get_bool("ico_convert")) {
// "-limit memory $mem" broken?
exec("convert {$inname}[0] -geometry {$w}x{$h} -quality {$q} jpg:$outname");
}
else {
copy($inname, $outname);
}
return true;
}
}
add_event_listener(new IcoFileHandler());
?>

View File

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

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

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

View File

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

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

88
contrib/home/main.php Normal file
View File

@ -0,0 +1,88 @@
<?php
/**
* Name: Home Extension
* Author: Bzchan <bzchan@animemahou.com>
* License: GPLv2
* Description: Extension adds a page "home" containing user specified
* links and a counter showing total number of posts. The
* page is accessed via /home.
*/
class Home extends Extension {
var $theme;
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("home", "HomeTheme");
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "home"))
{
// this is a request to display this page so output the page.
$this->output_pages($event->page);
}
if(is_a($event, 'SetupBuildingEvent'))
{
$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_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);
}
}
private function get_body()
{
// returns just the contents of the body
global $database;
global $config;
$base_href = $config->get_string('base_href');
$data_href = get_base_href();
$sitename = $config->get_string('title');
$contact_link = $config->get_string('contact_link');
$counter_dir = $config->get_string('home_counter', 'default');
$total = ceil($database->db->GetOne("SELECT COUNT(*) FROM images"));
$strtotal = "$total";
$num_comma = number_format($total);
$counter_text = "";
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' /> ";
}
// 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);
return $this->theme->build_body($sitename, $main_links, $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());
?>

56
contrib/home/theme.php Normal file
View File

@ -0,0 +1,56 @@
<?php
class HomeTheme extends Themelet {
public function display_page($page, $sitename, $data_href, $theme_name, $body) {
$page->set_mode("data");
$page->set_data(<<<EOD
<html>
<head>
<title>$sitename</title>
<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 {text-align:center;}
.space {margin-bottom: 1em;}
div#front-page div#links a {margin: 0 0.5em;}
div#front-page li {list-style-type: none; margin: 0;}
</style>
<body>
$body
</body>
</html>
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'>
<form action='".make_link("post/list")."' method='GET'>
<input id='search_input' name='search' size='55' type='text' value='' autocomplete='off' /><br/>
<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> &ndash; 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
</div>
</div>";
}
}
?>

View File

@ -0,0 +1,143 @@
<?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
*/
// RemoveImageHashBanEvent {{{
class RemoveImageHashBanEvent extends Event {
var $hash;
public function RemoveImageHashBanEvent($hash) {
$this->hash = $hash;
}
}
// }}}
// AddImageHashBanEvent {{{
class AddImageHashBanEvent extends Event {
var $hash;
var $reason;
public function AddImageHashBanEvent($hash, $reason) {
$this->hash = $hash;
$this->reason = $reason;
}
}
// }}}
class Image_Hash_Ban extends Extension {
var $theme;
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("Image_Hash_Ban", "ImageBanTheme");
if(is_a($event, 'InitExtEvent')) {
global $config;
if($config->get_int("ext_imageban_version") < 1) {
$this->install();
}
}
if(is_a($event, 'DataUploadEvent')) {
global $database;
if ($database->db->GetOne("SELECT COUNT(*) FROM image_bans WHERE hash = ?", $event->hash) == 1) {
$event->veto("This image has been banned!");
}
}
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "image_hash_ban")) {
if($event->user->is_admin()) {
if($event->get_arg(0) == "add") {
if(isset($_POST['hash']) && isset($_POST['reason'])) {
send_event(new AddImageHashBanEvent($_POST['hash'], $_POST['reason']));
global $page;
$page->set_mode("redirect");
$page->set_redirect(make_link("admin"));
}
if(isset($_POST['image_id'])) {
global $database;
$image = $database->get_image($_POST['image_id']);
if($image) {
send_event(new ImageDeletionEvent($image));
$event->page->set_mode("redirect");
$event->page->set_redirect(make_link("post/list"));
}
}
}
else if($event->get_arg(0) == "remove") {
if(isset($_POST['hash'])) {
send_event(new RemoveImageHashBanEvent($_POST['hash']));
global $page;
$page->set_mode("redirect");
$page->set_redirect(make_link("admin"));
}
}
}
}
if(is_a($event, 'AdminBuildingEvent')) {
global $page;
$this->theme->display_Image_hash_Bans($page, $this->get_image_hash_bans());
}
if(is_a($event, 'AddImageHashBanEvent')) {
$this->add_image_hash_ban($event->hash, $event->reason);
}
if(is_a($event, 'RemoveImageHashBanEvent')) {
$this->remove_image_hash_ban($event->hash);
}
if(is_a($event, 'ImageAdminBlockBuildingEvent')) {
if($event->user->is_admin()) {
$event->add_part($this->theme->get_buttons_html($event->image));
}
}
}
protected function install() {
global $database;
global $config;
$database->Execute("CREATE TABLE image_bans (
id int(11) NOT NULL auto_increment,
hash char(32) default NULL,
date datetime default NULL,
reason varchar(255) default NULL,
PRIMARY KEY (id)
)");
$config->set_int("ext_imageban_version", 1);
}
// DB funness
public function get_image_hash_bans() {
// FIXME: many
global $database;
$bans = $database->get_all("SELECT * FROM image_bans");
if($bans) {return $bans;}
else {return array();}
}
public function add_image_hash_ban($hash, $reason) {
global $database;
$database->Execute(
"INSERT INTO image_bans (hash, reason, date) VALUES (?, ?, now())",
array($hash, $reason));
}
public function remove_image_hash_ban($hash) {
global $database;
$database->Execute("DELETE FROM image_bans WHERE hash = ?", array($hash));
}
}
add_event_listener(new Image_Hash_Ban(), 30); // in before resolution limit plugin
?>

View File

@ -0,0 +1,79 @@
<?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
*/
class ImageBanTheme extends Themelet {
/*
* Show all the bans
*
* $bans = an array of (
* 'hash' => the banned hash
* 'reason' => why the hash was banned
* 'date' => when the ban started
* )
*/
public function display_image_hash_bans($page, $bans) {
$h_bans = "";
foreach($bans as $ban) {
$h_bans .= "
<tr>
<td>{$ban['hash']}</td>
<td>{$ban['reason']}</td>
<td>
<form action='".make_link("image_hash_ban/remove")."' method='POST'>
<input type='hidden' name='hash' value='{$ban['hash']}'>
<input type='submit' value='Remove'>
</form>
</td>
</tr>
";
}
$html = "
<table border='1'>
<thead><td>Hash</td><td>Reason</td><td>Action</td></thead>
$h_bans
<tr>
<form action='".make_link("image_hash_ban/add")."' method='POST'>
<td><input type='text' name='hash'></td>
<td><input type='text' name='reason'></td>
<td><input type='submit' value='Ban'></td>
</form>
</tr>
</table>
";
$page->add_block(new Block("Edit Image Bans", $html));
}
public function display_page($page) {
$page->set_title("Image Ban");
$page->set_heading("Image Ban");
$page->add_block(new NavBlock());
}
/*
* Display a link to delete an image
*
* $image_id = the image to delete
*/
public function get_buttons_html($image) {
$html = "
<form action='".make_link("image_hash_ban/add")."' method='POST'>
<input type='hidden' name='hash' value='{$image->hash}'>
<input type='hidden' name='image_id' value='{$image->id}'>
<input type='text' name='reason'>
<input type='submit' value='Ban and Delete'>
</form>
";
return $html;
}
}
?>

208
contrib/ipban/main.php Normal file
View File

@ -0,0 +1,208 @@
<?php
/**
* Name: IP Ban
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Ban IP addresses
* Link: http://trac.shishnet.org/shimmie2/wiki/Contrib/Extensions/IPBan
*/
// RemoveIPBanEvent {{{
class RemoveIPBanEvent extends Event {
var $id;
public function RemoveIPBanEvent($id) {
$this->id = $id;
}
}
// }}}
// AddIPBanEvent {{{
class AddIPBanEvent extends Event {
var $ip;
var $reason;
var $end;
public function AddIPBanEvent($ip, $reason, $end) {
$this->ip = $ip;
$this->reason = $reason;
$this->end = $end;
}
}
// }}}
class IPBan extends Extension {
var $theme;
// event handler {{{
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("ipban", "IPBanTheme");
if(is_a($event, 'InitExtEvent')) {
global $config;
if($config->get_int("ext_ipban_version") < 5) {
$this->install();
}
$this->check_ip_ban();
}
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "ip_ban")) {
global $user;
if($user->is_admin()) {
if($event->get_arg(0) == "add") {
if(isset($_POST['ip']) && isset($_POST['reason']) && isset($_POST['end'])) {
if(empty($_POST['end'])) $end = null;
else $end = $_POST['end'];
send_event(new AddIPBanEvent($_POST['ip'], $_POST['reason'], $end));
$event->page->set_mode("redirect");
$event->page->set_redirect(make_link("ip_ban/list"));
}
}
else if($event->get_arg(0) == "remove") {
if(isset($_POST['id'])) {
send_event(new RemoveIPBanEvent($_POST['id']));
$event->page->set_mode("redirect");
$event->page->set_redirect(make_link("ip_ban/list"));
}
}
else if($event->get_arg(0) == "list") {
$this->theme->display_bans($event->page, $this->get_bans());
}
}
}
if(is_a($event, 'UserBlockBuildingEvent')) {
if($event->user->is_admin()) {
$event->add_link("IP Bans", make_link("ip_ban/list"));
}
}
if(is_a($event, 'AddIPBanEvent')) {
global $user;
$this->add_ip_ban($event->ip, $event->reason, $event->end, $user);
}
if(is_a($event, 'RemoveIPBanEvent')) {
global $database;
$database->Execute("DELETE FROM bans WHERE id = ?", array($event->id));
}
}
// }}}
// installer {{{
protected function install() {
global $database;
global $config;
// shortcut to latest
if($config->get_int("ext_ipban_version") < 1) {
$database->execute("
CREATE TABLE bans (
id {$database->engine->auto_increment},
banner_id INTEGER NOT NULL,
ip CHAR(15) NOT NULL,
end_timestamp INTEGER,
reason TEXT NOT NULL,
INDEX (end_timestamp)
) {$database->engine->create_table_extras};
");
$config->set_int("ext_ipban_version", 5);
}
// ===
if($config->get_int("ext_ipban_version") < 1) {
$database->Execute("CREATE TABLE bans (
id int(11) NOT NULL auto_increment,
ip char(15) default NULL,
date datetime default NULL,
end datetime default NULL,
reason varchar(255) default NULL,
PRIMARY KEY (id)
)");
$config->set_int("ext_ipban_version", 1);
}
if($config->get_int("ext_ipban_version") == 1) {
$database->execute("ALTER TABLE bans ADD COLUMN banner_id INTEGER NOT NULL AFTER id");
$config->set_int("ext_ipban_version", 2);
}
if($config->get_int("ext_ipban_version") == 2) {
$database->execute("ALTER TABLE bans DROP COLUMN date");
$database->execute("ALTER TABLE bans CHANGE ip ip CHAR(20) NOT NULL");
$database->execute("ALTER TABLE bans CHANGE reason reason TEXT NOT NULL");
$database->execute("CREATE INDEX bans__end ON bans(end)");
$config->set_int("ext_ipban_version", 3);
}
if($config->get_int("ext_ipban_version") == 3) {
$database->execute("ALTER TABLE bans CHANGE end old_end DATE NOT NULL");
$database->execute("ALTER TABLE bans ADD COLUMN end INTEGER");
$database->execute("UPDATE bans SET end = UNIX_TIMESTAMP(old_end)");
$database->execute("ALTER TABLE bans DROP COLUMN old_end");
$database->execute("CREATE INDEX bans__end ON bans(end)");
$config->set_int("ext_ipban_version", 4);
}
if($config->get_int("ext_ipban_version") == 4) {
$database->execute("ALTER TABLE bans CHANGE end end_timestamp INTEGER");
$config->set_int("ext_ipban_version", 5);
}
}
// }}}
// deal with banned person {{{
private function check_ip_ban() {
global $config;
global $database;
$remote = $_SERVER['REMOTE_ADDR'];
$bans = $this->get_active_bans();
foreach($bans as $row) {
if(
(strstr($row['ip'], '/') && ip_in_range($remote, $row['ip'])) ||
($row['ip'] == $remote)
) {
$admin = $database->get_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>";
$contact_link = $config->get_string("contact_link");
if(!empty($contact_link)) {
print "<p><a href='$contact_link'>Contact The Admin</a>";
}
exit;
}
}
}
// }}}
// database {{{
private function get_bans() {
global $database;
$bans = $database->get_all("
SELECT bans.*, users.name as banner_name
FROM bans
JOIN users ON banner_id = users.id
ORDER BY end_timestamp, id");
if($bans) {return $bans;}
else {return array();}
}
private function get_active_bans() {
global $database;
$bans = $database->get_all("
SELECT * FROM bans
WHERE (end_timestamp > now()) OR (end_timestamp IS NULL)
");
if($bans) {return $bans;}
else {return array();}
}
private function add_ip_ban($ip, $reason, $end, $user) {
global $database;
$sql = "INSERT INTO bans (ip, reason, end_timestamp, banner_id) VALUES (?, ?, ?, ?)";
$database->Execute($sql, array($ip, $reason, strtotime($end), $user->id));
}
// }}}
}
add_event_listener(new IPBan(), 10);
?>

55
contrib/ipban/theme.php Normal file
View File

@ -0,0 +1,55 @@
<?php
class IPBanTheme extends Themelet {
/*
* Show all the bans
*
* $bans = an array of (
* 'ip' => the banned IP
* 'reason' => why the IP was banned
* 'date' => when the ban started
* 'end' => when the ban will end
* )
*/
public function display_bans($page, $bans) {
global $user;
$h_bans = "";
foreach($bans as $ban) {
$end_human = date('Y-m-d', $ban['end_timestamp']);
$h_bans .= "
<tr>
<td>{$ban['ip']}</td>
<td>{$ban['reason']}</td>
<td>{$ban['banner_name']}</td>
<td>{$end_human}</td>
<td>
<form action='".make_link("ip_ban/remove")."' method='POST'>
<input type='hidden' name='id' value='{$ban['id']}'>
<input type='submit' value='Remove'>
</form>
</td>
</tr>
";
}
$html = "
<table border='1'>
<thead><td>IP</td><td>Reason</td><td>By</td><td>Until</td><td>Action</td></thead>
$h_bans
<tr>
<form action='".make_link("ip_ban/add")."' method='POST'>
<td><input type='text' name='ip'></td>
<td><input type='text' name='reason'></td>
<td>{$user->name}</td>
<td><input type='text' name='end'></td>
<td><input type='submit' value='Ban'></td>
</form>
</tr>
</table>
";
$page->set_title("IP Bans");
$page->set_heading("IP Bans");
$page->add_block(new NavBlock());
$page->add_block(new Block("Edit IP Bans", $html));
}
}
?>

View File

@ -0,0 +1,41 @@
/*
* 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;
}

View File

@ -0,0 +1,45 @@
<?php
/**
* Name: Link to Image
* Author: Artanis <artanis.00@gmail.com>
* Description: Show various forms of link to each image, for copy & paste
*/
class LinkImage extends Extension {
var $theme;
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("link_image", "LinkImageTheme");
if(is_a($event, 'DisplayingImageEvent')) {
global $config;
$data_href = get_base_href();
$event->page->add_header("<link rel='stylesheet' href='$data_href/ext/link_image/_style.css' type='text/css'>",0);
$this->theme->links_block($event->page,$this->data($event->image));
}
if(is_a($event, 'SetupBuildingEvent')) {
$sb = new SetupBlock("Link to Image");
$sb->add_text_option("ext_link-img_text-link_format", "Text Link Format: ");
$event->panel->add_block($sb);
}
if(is_a($event, 'InitExtEvent')) {
global $config;
//just set default if empty.
$config->set_default_string("ext_link-img_text-link_format",
'$title - $id ($ext $size $filesize)');
}
}
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.
return array(
'thumb_src' => $image->get_thumb_link(),
'image_src' => $image->get_image_link(),
'post_link' => $image->get_short_link(),
'text_link' => $text_link);
}
}
add_event_listener(new LinkImage());
?>

View File

@ -0,0 +1,82 @@
Link to Image adds BBCode and HTML link codes to the image view. Offers code for a customizable text link, thumbnail links, and full image inline.
Author: Erik Youngren <artanis.00@gmail.com>
License: GPLv2
Submit a Bug Report or Suggestion for Link to Image:
* http://trac.shishnet.org/shimmie2/newticket?owner=artanis.00@gmail.com&component=third%20party%20extensions&keywords=link_to_image
= Use =
There is one option in Board Config: Text Link Format.
It takes the following arguments as well as plain text.
|| arguments || replacement ||
|| $id || The image ID. ||
|| $hash || The MD5 hash of the image. ||
|| $tags || The image's tag list. ||
|| $base || The base HREF as set in Config. ||
|| $ext || The image's extension. ||
|| $size || The image's display size. ||
|| $filesize || The image's size in KB. ||
|| $filename || The image's original filename. ||
|| $title || The site title as set in Config. ||
Link to Image will default this option to '$title - $id ($ext $size $filesize)'.
To reset to the default, simply clear the current setting. Link to Image will then fill in the default value after the save.
To leave the setting blank for any reason, leave a space (' ') in it.
= Install =
1. Copy the folder {{{contrib/link_image/}}} to {{{ext/}}}.
2. In the Config panel, make sure Base URL is set (you may as well set Data URL while you're there, if you haven't already.)
3. Make sure Image Link, Thumb Link, and Short Link all contain the full path ("http://" and onward,) either by using $base or plain text. Link to Image will not be able to retrieve the correct paths without these variables.
= Change Log =
== Version 0.3.0 ==
* Moved Link to Image over to the official theme engine. This functions basically the same as what the prototype was, but it's more thought out and nicer.
* Cleaned up the insides a bit.
== Version 0.2.0 ==
* Changed the HTML generation to use a prototype theme engine. All HTML generation is now contained within {{{link_image.html.php}}}, which may be copied to the current theme folder and edited from there.
== Version 0.1.4 - 20070510 ==
* Style changes.
* Added output containing only the locations of the thumb, image and post.
* Added a link to wikipedia's HTML page, just as BBCode has a wikipedia link.
== Version 0.1.3b - 20070509 ==
* Renamed style.css to _style.css to avoid the auto loader.
== Version 0.1.3 - 20070508 ==
* Created Readme.txt
* Merged 0.1.2 into 0.1.2b
* Removed uneeded documentation from main.php
* Rewrote the css to be unique. Previously used CSS I wrote for elsewhere. Styled to reduce space consumption.
* Added code to insert the CSS import.
* Updated Nice URLs to allow access to the /ext/ folder. (Why is my stylesheet returning HTML instead of CSS?)
* First SVN update.
== Version 0.1.2b - 20070507 ==
(fairly simultaneous with 0.1.2)
* shish:
* Updated to new extension format
* Created folder link_image in trunk/contrib
* Renamed link_image.ext.php to main.php and moved to /link_image/
* Created style.css {{{ /* 404'd :|*/ }}}.
* Documentation (different from mine.)
* Changed add_text_option() and added add_label() in SetupBuildingEvent because I was using an edited version of the function that shish didn't know about. It was a wonder that didn't throw massive errors.
* Published on SVN.
== Version 0.1.2 - 20070506 ==
* Textboxes now select-all when they gain focus.
* Commenting and documentation.
== Version 0.1.1 - 20070506 ==
* Fixed HTML thumbnail link code. (image tag was being html_escaped twice, resulting in "$gt;" and "&lt;" from the first escape becoming "&amp;gt;" and "&amp;lt;") It turns out that html_escape was completely unnecessary, all I had to do was replace the single-quotes around the attributes with escaped double-quotes ('\"'.)
== Version 0.1.0 - 20070506 ==
* Release.
= Links =
* http://trac.shishnet.org/shimmie2/wiki/Contrib/Extensions/LinkToImage - Home
* http://forum.shishnet.org/viewtopic.php?p=153 - Discussion
* http://trac.shishnet.org/shimmie2/browser/trunk/contrib/link_image - Shimmie2 Trac SVN

View File

@ -0,0 +1,71 @@
<?php
class LinkImageTheme extends Themelet {
public function links_block($page,$data) {
$thumb_src = $data['thumb_src'];
$image_src = $data['image_src'];
$post_link = $data['post_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>".
"<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>".
"<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>",
"main",
50));
}
protected function url ($url,$content,$type) {
if ($content == NULL) {$content=$url;}
switch ($type) {
case "html":
$text = "<a href=\"".$url."\">".$content."</a>";
break;
case "ubb":
$text = "[url=".$url."]".$content."[/url]";
break;
default:
$text = $link." - ".$content;
}
return $text;
}
protected function img ($src,$type) {
switch ($type) {
case "html":
$text = "<img src=\"$src\" />";
break;
case "ubb":
$text = "[img]".$src."[/img]";
break;
default:
$text = $src;
}
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";
}
}
?>

30
contrib/news/main.php Normal file
View File

@ -0,0 +1,30 @@
<?php
/**
* Name: News
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Show a short amonut of text in a block on the post list
*/
class News extends Extension {
var $theme;
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("news", "NewsTheme");
if(is_a($event, 'PostListBuildingEvent')) {
global $config;
if(strlen($config->get_string("news_text")) > 0) {
$this->theme->display_news($event->page, $config->get_string("news_text"));
}
}
if(is_a($event, 'SetupBuildingEvent')) {
$sb = new SetupBlock("News");
$sb->add_longtext_option("news_text");
$event->panel->add_block($sb);
}
}
}
add_event_listener(new News());
?>

11
contrib/news/theme.php Normal file
View File

@ -0,0 +1,11 @@
<?php
class NewsTheme extends Themelet {
/*
* Show $text on the $page
*/
public function display_news($page, $text) {
$page->add_block(new Block("Note", $text, "left", 5));
}
}
?>

View File

@ -0,0 +1,119 @@
<?php
/**
* Name: Image Scores (Numeric)
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Allow users to score images
*/
class NumericScoreSetEvent extends Event {
var $image_id, $user, $score;
public function NumericScoreSetEvent($image_id, $user, $score) {
$this->image_id = $image_id;
$this->user = $user;
$this->score = $score;
}
}
class NumericScore extends Extension {
var $theme;
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("numeric_score", "NumericScoreTheme");
if(is_a($event, 'InitExtEvent')) {
global $config;
if($config->get_int("ext_numeric_score_version", 0) < 1) {
$this->install();
}
}
if(is_a($event, 'DisplayingImageEvent')) {
global $user;
if(!$user->is_anonymous()) {
$html = $this->theme->get_voter_html($event->image);
$event->page->add_block(new Block("Image Score", $html, "left", 20));
}
}
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "numeric_score_vote")) {
if(!$event->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, $event->user, $score));
$event->page->set_mode("redirect");
$event->page->set_redirect(make_link("post/view/$image_id"));
}
}
if(is_a($event, 'ImageInfoSetEvent')) {
global $user;
$char = $_POST['numeric_score'];
$score = 0;
if($char == "u") $score = 1;
else if($char == "d") $score = -1;
if($score != 0) send_event(new NumericScoreSetEvent($event->image_id, $user, $score));
}
if(is_a($event, 'NumericScoreSetEvent')) {
$this->add_vote($event->image_id, $event->user->id, $event->score);
}
if(is_a($event, 'ImageDeletionEvent')) {
global $database;
$database->execute("DELETE FROM numeric_score_votes WHERE image_id=?", array($event->image->id));
}
if(is_a($event, 'ParseLinkTemplateEvent')) {
$event->replace('$score', $event->image->numeric_score);
}
if(is_a($event, 'SearchTermParseEvent')) {
$matches = array();
if(preg_match("/score(<|<=|=|>=|>)(\d+)/", $event->term, $matches)) {
$cmp = $matches[1];
$score = $matches[2];
$event->set_querylet(new Querylet("numeric_score $cmp $score"));
}
}
}
private function install() {
global $database;
global $config;
if($config->get_int("ext_numeric_score_version") < 1) {
$database->Execute("ALTER TABLE images ADD COLUMN numeric_score INTEGER NOT NULL DEFAULT 0");
$database->Execute("CREATE INDEX images__numeric_score ON images(numeric_score)");
$database->Execute("
CREATE TABLE numeric_score_votes (
image_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
score INTEGER NOT NULL,
UNIQUE(image_id, user_id),
INDEX(image_id)
)
");
$config->set_int("ext_numeric_score_version", 1);
}
}
private function add_vote($image_id, $user_id, $score) {
global $database;
$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));
$database->Execute(
"UPDATE images SET numeric_score=(SELECT SUM(score) FROM numeric_score_votes WHERE image_id=?) WHERE id=?",
array($image_id, $image_id));
}
}
add_event_listener(new NumericScore());
?>

View File

@ -0,0 +1,27 @@
<?php
class NumericScoreTheme extends Themelet {
public function get_voter_html($image) {
$i_image_id = int_escape($image->id);
$i_score = int_escape($image->numeric_score);
$html = "
Current Score: $i_score
<p><form action='".make_link("numeric_score_vote")."' method='POST'>
<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'>
<input type='hidden' name='image_id' value='$i_image_id'>
<input type='hidden' name='vote' value='down'>
<input type='submit' value='Vote Down'>
</form>
";
return $html;
}
}
?>

24
contrib/piclens/main.php Normal file
View File

@ -0,0 +1,24 @@
<?php
/**
* Name: PicLens Button
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Adds a link to piclensify the gallery
*/
class PicLens extends Extension {
public function receive_event($event) {
if(is_a($event, 'PageRequestEvent')) {
$event->page->add_header("<script type=\"text/javascript\" src=\"http://lite.piclens.com/current/piclens.js\"></script>");
}
if(is_a($event, '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>';
$event->page->add_block(new Block("PicLens", $foo, "left", 20));
}
}
}
add_event_listener(new PicLens());
?>

View File

@ -0,0 +1,41 @@
<?php
/**
* Name: Random Image
* Author: Shish <webmaster@shishnet.org>
* License: GPLv2
* Description: Do things with a random image
* Link: http://trac.shishnet.org/shimmie2/wiki/Contrib/Extensions/RandomImage
*/
class RandomImage extends Extension {
public function receive_event($event) {
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "random_image")) {
global $database;
if($event->count_args() == 1) {
$action = $event->get_arg(0);
$search_terms = array();
}
else if($event->count_args() == 2) {
$action = $event->get_arg(0);
$search_terms = explode(' ', $event->get_arg(1));
}
$image = $database->get_random_image($search_terms);
if($event->get_arg(0) == "download") {
if(!is_null($image)) {
$event->page->set_mode("data");
$event->page->set_type("image/jpeg");
$event->page->set_data(file_get_contents($image->get_image_filename()));
}
}
if($event->get_arg(0) == "view") {
if(!is_null($image)) {
send_event(new DisplayingImageEvent($image, $event->page));
}
}
}
}
}
add_event_listener(new RandomImage());
?>

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