Compare commits
79 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
3cf59cc545 | ||
|
327d0dd51f | ||
|
90fcccdab0 | ||
|
9d868750e2 | ||
|
770e313da1 | ||
|
2002d555c1 | ||
|
36064595f5 | ||
|
2c4582f147 | ||
|
9993370fc6 | ||
|
5d42ee21de | ||
|
6e0b3bca6d | ||
|
32d12602cf | ||
|
058f2ff1ed | ||
|
79767033b7 | ||
|
8f05de4b66 | ||
|
34d2165dfa | ||
|
593dc2f694 | ||
|
a1199c3bd4 | ||
|
4b666a8f1a | ||
|
9044abdfc2 | ||
|
e17c85d379 | ||
|
554ff9051c | ||
|
04f335b797 | ||
|
661b5eac78 | ||
|
763538c096 | ||
|
5f473aff6d | ||
|
c4fdf771ee | ||
|
94725f1266 | ||
|
f1ce267015 | ||
|
25bfb82653 | ||
|
0c334f05ed | ||
|
bea09d836d | ||
|
384e8bbdda | ||
|
96509995a8 | ||
|
642a3d02e1 | ||
|
f180624ebf | ||
|
482312b11e | ||
|
aaa8ccf874 | ||
|
c2ab5e5fcd | ||
|
3f0d6562ef | ||
|
d1ce3db7e2 | ||
|
24a35c37ea | ||
|
457d5d8ad9 | ||
|
56ccd7df40 | ||
|
f8b414a15f | ||
|
51f6c486a2 | ||
|
367546fe06 | ||
|
ffde804f24 | ||
|
b57ba4d484 | ||
|
ce8d6f71fd | ||
|
1fd2fe084b | ||
|
7e92bdbd8e | ||
|
4c5f415da7 | ||
|
b7fc28e17f | ||
|
b2f8165a59 | ||
|
57b50d8881 | ||
|
9f595e6273 | ||
|
0c9fff3d11 | ||
|
d3ceb8ef97 | ||
|
64db094721 | ||
|
d47fd5f25c | ||
|
f7d1df2727 | ||
|
67455583e5 | ||
|
2e40bc73c8 | ||
|
c012717bce | ||
|
7b4dde4918 | ||
|
5ad77079a6 | ||
|
43028ebd67 | ||
|
f821e752fc | ||
|
73d5fe129a | ||
|
a226a17787 | ||
|
03c9d6b298 | ||
|
37a0aeb8ae | ||
|
47361369ff | ||
|
6c53088929 | ||
|
a6d68c6f1b | ||
|
57a6c39f2a | ||
|
a5f7b400f0 | ||
|
2a990735a0 |
@ -1,8 +0,0 @@
|
|||||||
vendor
|
|
||||||
.git
|
|
||||||
*.phar
|
|
||||||
data
|
|
||||||
images
|
|
||||||
thumbs
|
|
||||||
*.sqlite
|
|
||||||
Dockerfile
|
|
@ -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
@ -1 +0,0 @@
|
|||||||
*.php text eol=lf
|
|
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -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.
|
|
||||||
|
|
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -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.
|
|
62
.github/workflows/release.yml
vendored
@ -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
|
|
95
.github/workflows/test_and_publish.yml
vendored
@ -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
@ -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
|
|
72
.htaccess
@ -1,67 +1,25 @@
|
|||||||
<IfModule mod_dir.c>
|
<IfModule mod_dir.c>
|
||||||
DirectoryIndex index.php5 index.php
|
DirectoryIndex index.php5 index.php
|
||||||
</IfModule>
|
</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>
|
<IfModule mod_rewrite.c>
|
||||||
RewriteEngine On
|
RewriteEngine on
|
||||||
|
|
||||||
# rather than link to images/ha/hash and have an ugly filename,
|
# rather than link to images/ha/hash and have an ugly filename,
|
||||||
# we link to images/hash/tags.ext; mod_rewrite splits things so
|
# we link to images/hash/tags.ext; mod_rewrite splits things so
|
||||||
# that shimmie sees hash and the user sees tags.ext
|
# that shimmie sees hash and the user sees tags.ext
|
||||||
RewriteRule ^_(images|thumbs)/([0-9a-f]{2})([0-9a-f]{30}).*$ data/$1/$2/$2$3 [L]
|
RewriteRule ^_images/([0-9a-f]{2})([0-9a-f]{30}).*$ images/$1/$1$2 [L]
|
||||||
|
RewriteRule ^_thumbs/([0-9a-f]{2})([0-9a-f]{30}).*$ thumbs/$1/$1$2 [L]
|
||||||
|
|
||||||
# any requests for files which don't physically exist should be handled by index.php
|
# any requests for files which don't physically exist should be handled by index.php
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
RewriteRule ^(.*)$ index.php?q=$1&%{QUERY_STRING} [L]
|
RewriteRule ^(.*)$ index.php?q=$1&%{QUERY_STRING} [L]
|
||||||
</IfModule>
|
</IfModule>
|
||||||
|
|
||||||
<IfModule mod_expires.c>
|
<IfModule mod_php5.c>
|
||||||
ExpiresActive On
|
php_flag register_globals 0
|
||||||
<FilesMatch "([0-9a-f]{32}|\.(gif|jpe?g|png|webp|css|js))$">
|
php_flag magic_quotes_gpc 0
|
||||||
<IfModule mod_headers.c>
|
php_flag magic_quotes_runtime 0
|
||||||
Header set Cache-Control "public, max-age=2629743"
|
|
||||||
</IfModule>
|
|
||||||
ExpiresDefault "access plus 1 month"
|
|
||||||
</FilesMatch>
|
|
||||||
#ExpiresByType text/html "now"
|
|
||||||
#ExpiresByType text/plain "now"
|
|
||||||
</IfModule>
|
</IfModule>
|
||||||
|
|
||||||
<IfModule mod_deflate.c>
|
DefaultType image/jpeg
|
||||||
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
|
|
||||||
|
19
.php_cs.dist
@ -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)
|
|
||||||
;
|
|
||||||
|
|
||||||
?>
|
|
@ -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
|
|
49
Dockerfile
@ -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"]
|
|
339
LICENSE.txt
@ -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.
|
|
43
README.md
@ -1,43 +0,0 @@
|
|||||||
```
|
|
||||||
_________.__ .__ .__ ________
|
|
||||||
/ _____/| |__ |__| _____ _____ |__| ____ \_____ \
|
|
||||||
\_____ \ | | \ | | / \ / \ | |_/ __ \ / ____/
|
|
||||||
/ \| Y \| || Y Y \| Y Y \| |\ ___/ / \
|
|
||||||
/_______ /|___| /|__||__|_| /|__|_| /|__| \___ >\_______ \
|
|
||||||
\/ \/ \/ \/ \/ \/
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
# Shimmie
|
|
||||||
|
|
||||||
[](https://github.com/shish/shimmie2/actions)
|
|
||||||
[](https://scrutinizer-ci.com/g/shish/shimmie2/?branch=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
@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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
@ -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"
|
|
||||||
}
|
|
56
contrib/ban_words/main.php
Normal 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
@ -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
@ -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());
|
||||||
|
?>
|
42
contrib/bulk_add/theme.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
433
contrib/danbooru_api/main.php
Normal 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 doesn‘t authenticate, he post will be added anonymously.
|
||||||
|
* If the md5 parameter is supplied and does not match the hash of what‘s 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
@ -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);
|
||||||
|
?>
|
30
contrib/downtime/theme.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
Before Width: | Height: | Size: 170 B After Width: | Height: | Size: 170 B |
Before Width: | Height: | Size: 172 B After Width: | Height: | Size: 172 B |
Before Width: | Height: | Size: 171 B After Width: | Height: | Size: 171 B |
Before Width: | Height: | Size: 172 B After Width: | Height: | Size: 172 B |
Before Width: | Height: | Size: 498 B After Width: | Height: | Size: 498 B |
Before Width: | Height: | Size: 170 B After Width: | Height: | Size: 170 B |
Before Width: | Height: | Size: 236 B After Width: | Height: | Size: 236 B |
Before Width: | Height: | Size: 236 B After Width: | Height: | Size: 236 B |
Before Width: | Height: | Size: 171 B After Width: | Height: | Size: 171 B |
Before Width: | Height: | Size: 176 B After Width: | Height: | Size: 176 B |
Before Width: | Height: | Size: 336 B After Width: | Height: | Size: 336 B |
Before Width: | Height: | Size: 174 B After Width: | Height: | Size: 174 B |
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 349 B |
Before Width: | Height: | Size: 171 B After Width: | Height: | Size: 171 B |
Before Width: | Height: | Size: 248 B After Width: | Height: | Size: 248 B |
Before Width: | Height: | Size: 176 B After Width: | Height: | Size: 176 B |
Before Width: | Height: | Size: 650 B After Width: | Height: | Size: 650 B |
Before Width: | Height: | Size: 485 B After Width: | Height: | Size: 485 B |
Before Width: | Height: | Size: 171 B After Width: | Height: | Size: 171 B |
Before Width: | Height: | Size: 174 B After Width: | Height: | Size: 174 B |
Before Width: | Height: | Size: 174 B After Width: | Height: | Size: 174 B |
Before Width: | Height: | Size: 238 B After Width: | Height: | Size: 238 B |
Before Width: | Height: | Size: 170 B After Width: | Height: | Size: 170 B |
30
contrib/emoticons/main.php
Normal 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
@ -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
@ -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
@ -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
|
||||||
|
?>
|
83
contrib/event_log/theme.php
Normal 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
@ -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());
|
||||||
|
?>
|
20
contrib/featured/theme.php
Normal 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>
|
||||||
|
";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
98
contrib/handle_archive/main.php
Normal 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());
|
||||||
|
?>
|
131
contrib/handle_flash/main.php
Normal 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());
|
||||||
|
?>
|
24
contrib/handle_flash/theme.php
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
116
contrib/handle_ico/main.php
Normal 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());
|
||||||
|
?>
|
12
contrib/handle_ico/theme.php
Normal 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
@ -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());
|
||||||
|
?>
|
15
contrib/handle_svg/theme.php
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
88
contrib/home/main.php
Normal 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
@ -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> – 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>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
143
contrib/image_hash_ban/main.php
Normal 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
|
||||||
|
?>
|
79
contrib/image_hash_ban/theme.php
Normal 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
@ -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
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
41
contrib/link_image/_style.css
Normal 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;
|
||||||
|
}
|
45
contrib/link_image/main.php
Normal 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());
|
||||||
|
?>
|
82
contrib/link_image/readme.txt
Normal 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 "<" from the first escape becoming "&gt;" and "&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
|
71
contrib/link_image/theme.php
Normal 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
@ -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
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
119
contrib/numeric_score/main.php
Normal 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());
|
||||||
|
?>
|
27
contrib/numeric_score/theme.php
Normal 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
@ -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());
|
||||||
|
?>
|
41
contrib/random_image/main.php
Normal 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());
|
||||||
|
?>
|