DJANGO REWRITE // DJANGO REWRITE // DJANGO REWRITE

feature parity, except pdf labels
This commit is contained in:
Tomáš Mládek 2020-03-18 21:58:15 +01:00
parent 603005f683
commit 8d4010702a
59 changed files with 917 additions and 1873 deletions

5
.gitignore vendored
View file

@ -1,9 +1,6 @@
pile.db
db.sqlite3
docs
dev
_vendor
*.ttf
*.woff
error_log

View file

@ -1,26 +0,0 @@
CREATE TABLE Documents (
ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
Title TEXT NOT NULL,
Description TEXT,
Author TEXT,
Published TEXT,
URL TEXT
);
CREATE TABLE DocumentsToTags (
Document INTEGER NOT NULL,
Tag INTEGER NOT NULL
);
CREATE TABLE Tags (
ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
Name TEXT NOT NULL,
Description TEXT,
Parent INTEGER
);
CREATE TABLE Users (
ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
Username TEXT NOT NULL,
Password TEXT NOT NULL
);

View file

@ -1 +0,0 @@
ALTER TABLE Documents ADD UploadedTime INTEGER;

View file

@ -1,21 +0,0 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
if [ -z "$1" ]; then
echo "Please specify database file."
exit -1
fi
if ! which sqlite3; then
echo "Couldn't find \`sqlite3\` in \$PATH!"
exit -1
fi
set -e
FILES=$(ls ${DIR}/*.sql|sort)
for sql_file in $FILES;do
echo "Processing \"${sql_file}\"..."
sqlite3 -echo "$1" < "$sql_file"
done
echo "Done."

View file

@ -1,21 +0,0 @@
FROM php:7.2-apache
COPY www/ /var/www/html/
RUN rm -f /var/www/html/pile.db
COPY opt/ /opt/pile
RUN mkdir -p /var/tmp/pile
COPY db_versions/ /var/tmp/pile/db_versions
RUN apt-get update && apt-get -y install sqlite3 wget \
libfreetype6-dev \
libjpeg62-turbo-dev \
libpng-dev
RUN docker-php-ext-install -j$(nproc) iconv \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd
RUN cd /opt/pile/ && /opt/pile/install_composer.sh
RUN cd /var/www/html && /opt/pile/composer.phar install
RUN /var/tmp/pile/db_versions/apply_all.sh /var/www/html/pile.db

View file

@ -1,11 +0,0 @@
version: '3'
services:
pile:
build:
context: ../
dockerfile: ./docker/Dockerfile
ports:
- "8080:80"
volumes:
- ../www:/var/www/html

21
manage.py Executable file
View file

@ -0,0 +1,21 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sdbs_pile.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

View file

@ -1,17 +0,0 @@
#!/bin/sh
EXPECTED_SIGNATURE="$(wget -q -O - https://composer.github.io/installer.sig)"
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_SIGNATURE="$(php -r "echo hash_file('sha384', 'composer-setup.php');")"
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
then
>&2 echo 'ERROR: Invalid installer signature'
rm composer-setup.php
exit 1
fi
php composer-setup.php --quiet
RESULT=$?
rm composer-setup.php
exit $RESULT

324
poetry.lock generated Normal file
View file

@ -0,0 +1,324 @@
[[package]]
category = "dev"
description = "Disable App Nap on OS X 10.9"
marker = "sys_platform == \"darwin\""
name = "appnope"
optional = false
python-versions = "*"
version = "0.1.0"
[[package]]
category = "main"
description = "ASGI specs, helper code, and adapters"
name = "asgiref"
optional = false
python-versions = ">=3.5"
version = "3.2.5"
[package.extras]
tests = ["pytest (>=4.3.0,<4.4.0)", "pytest-asyncio (>=0.10.0,<0.11.0)"]
[[package]]
category = "dev"
description = "Specifications for callback functions passed in to an API"
name = "backcall"
optional = false
python-versions = "*"
version = "0.1.0"
[[package]]
category = "dev"
description = "Cross-platform colored terminal text."
marker = "sys_platform == \"win32\""
name = "colorama"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "0.4.3"
[[package]]
category = "dev"
description = "Decorators for Humans"
name = "decorator"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*"
version = "4.4.2"
[[package]]
category = "main"
description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design."
name = "django"
optional = false
python-versions = ">=3.6"
version = "3.0.4"
[package.dependencies]
asgiref = ">=3.2,<4.0"
pytz = "*"
sqlparse = ">=0.2.2"
[package.extras]
argon2 = ["argon2-cffi (>=16.1.0)"]
bcrypt = ["bcrypt"]
[[package]]
category = "main"
description = "Django model mixins and utilities"
name = "django-model-utils"
optional = false
python-versions = "*"
version = "4.0.0"
[package.dependencies]
Django = ">=2.0.1"
[[package]]
category = "dev"
description = "IPython: Productive Interactive Computing"
name = "ipython"
optional = false
python-versions = ">=3.6"
version = "7.13.0"
[package.dependencies]
appnope = "*"
backcall = "*"
colorama = "*"
decorator = "*"
jedi = ">=0.10"
pexpect = "*"
pickleshare = "*"
prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
pygments = "*"
setuptools = ">=18.5"
traitlets = ">=4.2"
[package.extras]
all = ["numpy (>=1.14)", "testpath", "notebook", "nose (>=0.10.1)", "nbconvert", "requests", "ipywidgets", "qtconsole", "ipyparallel", "Sphinx (>=1.3)", "pygments", "nbformat", "ipykernel"]
doc = ["Sphinx (>=1.3)"]
kernel = ["ipykernel"]
nbconvert = ["nbconvert"]
nbformat = ["nbformat"]
notebook = ["notebook", "ipywidgets"]
parallel = ["ipyparallel"]
qtconsole = ["qtconsole"]
test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"]
[[package]]
category = "dev"
description = "Vestigial utilities from IPython"
name = "ipython-genutils"
optional = false
python-versions = "*"
version = "0.2.0"
[[package]]
category = "dev"
description = "An autocompletion tool for Python that can be used for text editors."
name = "jedi"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.16.0"
[package.dependencies]
parso = ">=0.5.2"
[package.extras]
qa = ["flake8 (3.7.9)"]
testing = ["colorama (0.4.1)", "docopt", "pytest (>=3.9.0,<5.0.0)"]
[[package]]
category = "dev"
description = "A Python Parser"
name = "parso"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.6.2"
[package.extras]
testing = ["docopt", "pytest (>=3.0.7)"]
[[package]]
category = "dev"
description = "Pexpect allows easy control of interactive console applications."
marker = "sys_platform != \"win32\""
name = "pexpect"
optional = false
python-versions = "*"
version = "4.8.0"
[package.dependencies]
ptyprocess = ">=0.5"
[[package]]
category = "dev"
description = "Tiny 'shelve'-like database with concurrency support"
name = "pickleshare"
optional = false
python-versions = "*"
version = "0.7.5"
[[package]]
category = "dev"
description = "Library for building powerful interactive command lines in Python"
name = "prompt-toolkit"
optional = false
python-versions = ">=3.6.1"
version = "3.0.4"
[package.dependencies]
wcwidth = "*"
[[package]]
category = "dev"
description = "Run a subprocess in a pseudo terminal"
marker = "sys_platform != \"win32\""
name = "ptyprocess"
optional = false
python-versions = "*"
version = "0.6.0"
[[package]]
category = "dev"
description = "Pygments is a syntax highlighting package written in Python."
name = "pygments"
optional = false
python-versions = ">=3.5"
version = "2.6.1"
[[package]]
category = "main"
description = "World timezone definitions, modern and historical"
name = "pytz"
optional = false
python-versions = "*"
version = "2019.3"
[[package]]
category = "dev"
description = "Python 2 and 3 compatibility utilities"
name = "six"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
version = "1.14.0"
[[package]]
category = "main"
description = "Non-validating SQL parser"
name = "sqlparse"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.3.1"
[[package]]
category = "dev"
description = "Traitlets Python config system"
name = "traitlets"
optional = false
python-versions = "*"
version = "4.3.3"
[package.dependencies]
decorator = "*"
ipython-genutils = "*"
six = "*"
[package.extras]
test = ["pytest", "mock"]
[[package]]
category = "dev"
description = "Measures number of Terminal column cells of wide-character codes"
name = "wcwidth"
optional = false
python-versions = "*"
version = "0.1.8"
[metadata]
content-hash = "29f69026d536c0781f6a7eb9b4f1221a083dcc7cd39e8dce9975de116abfafe2"
python-versions = "^3.8"
[metadata.files]
appnope = [
{file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"},
{file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"},
]
asgiref = [
{file = "asgiref-3.2.5-py2.py3-none-any.whl", hash = "sha256:3e4192eaec0758b99722f0b0666d5fbfaa713054d92e8de5b58ba84ec5ce696f"},
{file = "asgiref-3.2.5.tar.gz", hash = "sha256:c8f49dd3b42edcc51d09dd2eea8a92b3cfc987ff7e6486be734b4d0cbfd5d315"},
]
backcall = [
{file = "backcall-0.1.0.tar.gz", hash = "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4"},
{file = "backcall-0.1.0.zip", hash = "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"},
]
colorama = [
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"},
]
decorator = [
{file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"},
{file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"},
]
django = [
{file = "Django-3.0.4-py3-none-any.whl", hash = "sha256:89e451bfbb815280b137e33e454ddd56481fdaa6334054e6e031041ee1eda360"},
{file = "Django-3.0.4.tar.gz", hash = "sha256:50b781f6cbeb98f673aa76ed8e572a019a45e52bdd4ad09001072dfd91ab07c8"},
]
django-model-utils = [
{file = "django-model-utils-4.0.0.tar.gz", hash = "sha256:adf09e5be15122a7f4e372cb5a6dd512bbf8d78a23a90770ad0983ee9d909061"},
{file = "django_model_utils-4.0.0-py2.py3-none-any.whl", hash = "sha256:9cf882e5b604421b62dbe57ad2b18464dc9c8f963fc3f9831badccae66c1139c"},
]
ipython = [
{file = "ipython-7.13.0-py3-none-any.whl", hash = "sha256:eb8d075de37f678424527b5ef6ea23f7b80240ca031c2dd6de5879d687a65333"},
{file = "ipython-7.13.0.tar.gz", hash = "sha256:ca478e52ae1f88da0102360e57e528b92f3ae4316aabac80a2cd7f7ab2efb48a"},
]
ipython-genutils = [
{file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"},
{file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"},
]
jedi = [
{file = "jedi-0.16.0-py2.py3-none-any.whl", hash = "sha256:b4f4052551025c6b0b0b193b29a6ff7bdb74c52450631206c262aef9f7159ad2"},
{file = "jedi-0.16.0.tar.gz", hash = "sha256:d5c871cb9360b414f981e7072c52c33258d598305280fef91c6cae34739d65d5"},
]
parso = [
{file = "parso-0.6.2-py2.py3-none-any.whl", hash = "sha256:8515fc12cfca6ee3aa59138741fc5624d62340c97e401c74875769948d4f2995"},
{file = "parso-0.6.2.tar.gz", hash = "sha256:0c5659e0c6eba20636f99a04f469798dca8da279645ce5c387315b2c23912157"},
]
pexpect = [
{file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
{file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
]
pickleshare = [
{file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
{file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
]
prompt-toolkit = [
{file = "prompt_toolkit-3.0.4-py3-none-any.whl", hash = "sha256:859e1b205b6cf6a51fa57fa34202e45365cf58f8338f0ee9f4e84a4165b37d5b"},
{file = "prompt_toolkit-3.0.4.tar.gz", hash = "sha256:ebe6b1b08c888b84c50d7f93dee21a09af39860144ff6130aadbd61ae8d29783"},
]
ptyprocess = [
{file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"},
{file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"},
]
pygments = [
{file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"},
{file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"},
]
pytz = [
{file = "pytz-2019.3-py2.py3-none-any.whl", hash = "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d"},
{file = "pytz-2019.3.tar.gz", hash = "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"},
]
six = [
{file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"},
{file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"},
]
sqlparse = [
{file = "sqlparse-0.3.1-py2.py3-none-any.whl", hash = "sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e"},
{file = "sqlparse-0.3.1.tar.gz", hash = "sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548"},
]
traitlets = [
{file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"},
{file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"},
]
wcwidth = [
{file = "wcwidth-0.1.8-py2.py3-none-any.whl", hash = "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603"},
{file = "wcwidth-0.1.8.tar.gz", hash = "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"},
]

17
pyproject.toml Normal file
View file

@ -0,0 +1,17 @@
[tool.poetry]
name = "pile"
version = "0.1.0"
description = ""
authors = ["Tomáš Mládek <t@mldk.cz>"]
[tool.poetry.dependencies]
python = "^3.8"
django = "^3.0.4"
django-model-utils = "^4.0.0"
[tool.poetry.dev-dependencies]
ipython = "^7.13.0"
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"

16
sdbs_pile/asgi.py Normal file
View file

@ -0,0 +1,16 @@
"""
ASGI config for sdbs_pile project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sdbs_pile.settings')
application = get_asgi_application()

View file

27
sdbs_pile/pile/admin.py Normal file
View file

@ -0,0 +1,27 @@
from django.contrib import admin
from sdbs_pile.pile.models import Tag, Document
class TagAdmin(admin.ModelAdmin):
exclude = ('is_removed',)
@staticmethod
def document_count(tag: Tag):
return tag.documents.count()
list_display = ('name', 'document_count')
class DocumentAdmin(admin.ModelAdmin):
exclude = ('is_removed',)
@staticmethod
def filed_under(document: Document):
return ", ".join(tag.name for tag in document.tags.all())
list_display = ('title', 'filed_under')
admin.site.register(Tag, TagAdmin)
admin.site.register(Document, DocumentAdmin)

5
sdbs_pile/pile/apps.py Normal file
View file

@ -0,0 +1,5 @@
from django.apps import AppConfig
class PileConfig(AppConfig):
name = 'pile'

View file

@ -0,0 +1,44 @@
# Generated by Django 3.0.4 on 2020-03-17 22:29
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Tag',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_removed', models.BooleanField(default=False)),
('name', models.CharField(max_length=128)),
('description', models.TextField(blank=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Document',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_removed', models.BooleanField(default=False)),
('title', models.CharField(max_length=512)),
('description', models.TextField(blank=True)),
('author', models.CharField(blank=True, max_length=512)),
('published', models.CharField(blank=True, max_length=128)),
('external_url', models.URLField(blank=True, null=True)),
('file', models.FileField(blank=True, null=True, upload_to='')),
('uploaded', models.DateTimeField(auto_now_add=True)),
('tags', models.ManyToManyField(related_name='documents', to='pile.Tag')),
],
options={
'abstract': False,
},
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 3.0.4 on 2020-03-17 22:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pile', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='document',
name='uploaded',
field=models.DateTimeField(auto_now_add=True, null=True),
),
]

View file

39
sdbs_pile/pile/models.py Normal file
View file

@ -0,0 +1,39 @@
from django.core.exceptions import ValidationError
from django.core.files.storage import FileSystemStorage
from django.db import models
from model_utils.models import SoftDeletableModel
class Tag(SoftDeletableModel):
name = models.CharField(max_length=128, null=False, blank=False)
description = models.TextField(null=False, blank=True)
def __str__(self):
return self.name
class Document(SoftDeletableModel):
title = models.CharField(max_length=512, null=False, blank=False)
description = models.TextField(null=False, blank=True)
author = models.CharField(max_length=512, null=False, blank=True)
published = models.CharField(max_length=128, null=False, blank=True)
external_url = models.URLField(null=True, blank=True)
file = models.FileField(null=True, blank=True, storage=FileSystemStorage(location='docs'))
tags = models.ManyToManyField(Tag, related_name="documents")
uploaded = models.DateTimeField(auto_now_add=True, null=True)
class Meta:
ordering = ['-id']
@property
def url(self):
if self.file:
return self.file.url
return self.external_url
def clean(self):
if not (self.file or self.external_url):
raise ValidationError("An uploaded document or an external URL is required.")
def __str__(self):
return f"{self.title}{f' ({self.author})' if self.author else ''}"

View file

@ -44,7 +44,7 @@ ul > li:before {
background-color: #202020;
color: white;
background-image: url(/assets/pile_white.svg);
background-image: url(/static/pile_white.svg);
background-repeat: no-repeat;
background-position: center 1.5em;
background-size: 50%;
@ -167,6 +167,14 @@ ul > li:before {
font-weight: bold;
}
.doc-description {
padding: 1em 0;
}
.doc-description p {
margin: 0;
}
.doc-link-intro:before {
content: "➜ ";
}
@ -234,16 +242,8 @@ ul > li:before {
right: 2rem;
}
#login input {
height: 14pt;
border: 1px solid lightgray;
}
#login button {
height: 14pt;
font-size: 8pt;
border: 1px solid lightgray;
background: white;
#login a {
color: black;
}
.czech {

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View file

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View file

@ -0,0 +1,56 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>The /-\ pile{% block title %}{% endblock %}</title>
<link rel="stylesheet" type="text/css" href="{% static "main.css" %}">
<link rel="icon" type="image/png" href="{% static "favicon.png" %}">
{% block meta-og %}{% endblock %}
<meta property="og:image" content="{% static "favicon.png" %}"/>
</head>
<body>
<div id="sidebar">
<div id="sidebar-head">
<h1><a href="..">The /-\ pile</a></h1>
</div>
<div id="sidebar-taglist">
<ul>
<li id="sidebar-taglist-top"><a href="{% url "pile:tag" "*" %}">ALL ({{ document_count }})</a></li>
{% if untagged.count %}
<li id="sidebar-taglist-top"><a href="?tag=_">UNTAGGED ({{ untagged_count }})</a></li>
{% endif %}
{% for tag in tags %}
{% if tag.documents.count > 0 %}
<li><a href="{% url 'pile:tag' tag.id %}">{{ tag.name }} ({{ tag.documents.count }})</a></li>
{% endif %}
{% endfor %}
</ul>
</div>
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
<div id="login">
<a href="/admin">[maintain]</a>
</div>
<script>
(function () {
window.counter = 'https://sdbs_pile.goatcounter.com/count'
var script = document.createElement('script');
script.async = 1;
script.src = '//gc.zgo.at/count.js';
var ins = document.getElementsByTagName('script')[0];
ins.parentNode.insertBefore(script, ins)
})();
</script>
</body>
</html>

View file

@ -0,0 +1,56 @@
{% extends "front_base.html" %}
{% block title %}: {{ document.title }}{% endblock %}
{% block content %}
<div class="text document">
<h1>{{ document.title }}</h1>
{% if document.author %}
<h2>{{ document.author }}</h2>
{% endif %}
{% if document.published %}
<h3>Published: {{ document.published }}</h3>
{% endif %}
{% if document.tags.count > 0 %}
<h3 class="doc-taglist">
<span>Tags:</span>
{% for tag in document.tags.all %}
<li>
<a href="{% url "pile:tag" tag.id %}" title="{{ tag.description }}">
{{ tag.name }}
</a>
</li>
{% endfor %}
</h3>
{% endif %}
{% if document.description %}
<div class="doc-description">
<span class="doc-description-intro">
{% if document.url %}Description{% else %}Content{% endif %}:
</span>
<p> {{ document.description }} </p>
</div>
{% endif %}
{# <div class="doc-link"><span class="doc-link-intro">Get (document with) print label: </span>#}
{# <a href="/label.php?id=<?= $doc[" ID"] ?>">https://pile.sdbs.cz/label.php?id=<?= $doc["ID"] ?></a></div>#}
{% if document.url %}
<div class="doc-link">
<span class="doc-link-intro">Access file at:</span>
<a href="{{ document.url }}">{{ document.url }}</a></div>
{% endif %}
</div>
{% endblock %}
{% block meta-og %}
<title>The /-\ pile: {{ document.title }}</title>
<meta property="og:title" content="The /-\ Pile: {{ document.title }}"/>
<meta property="og:url" content="{% url "pile:document" document.id %}"/>
<meta property="og:description" content="{{ document.description }}"/>
<meta property="og:type" content="article"/>
{% endblock %}

View file

@ -0,0 +1,22 @@
{% extends "front_base.html" %}
{% block content %}
{% if tag %}
<div class="text tag-text">
<h1>{{ tag.name }}</h1>
<p class="tag-desc">{{ tag.description }}</p>
</div>
{% endif %}
{% for document in documents %}
<div class="text doc-item">
<a class="doc-item-link" href="{{ document.url }}">🔗</a>
<a href="{% url "pile:document" document.id %}">
<div class="doc-item-text">
<h2>{{ document.title }}</h2>
<h3>{{ document.author }} {{ document.published }}</h3>
</div>
</a>
</div>
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,33 @@
{% extends "front_base.html" %}
{% block content %}
<div class="text">
<p class="intro"> This site is the sdbs pile, where we upload the stuff we consider important to the larger
conceptual and thematic landscape of what we do: "confronting apathy", inter-subjectivity, the human right
to
self-determination, counter-culture and such...</p>
<p class="intro czech">Tohle je hromádka zajímavýho materiálu co něco znamená v kontextu sdbs - budeme sem
postupně dávat ty nejdůležitější nebo nejzajímavější věci, zatim se o tom ale nikde moc nešiřte.</p>
<p class="intro sign">/-\</p>
</div>
<div class="text recent-additions">
<h2>Recent additions</h2>
<ul>
{% for document in recent_documents %}
<li>
<a href="{% url 'pile:document' document.id %}">
{% if document.uploaded %}
<em>({{ document.uploaded|date:"Y/m/d G:i:s" }})</em>
{% endif %}
{{ document.title }}
<div class="recent-additions-desc">
{{ document.description }}
</div>
</a>
</li>
{% endfor %}
</ul>
</div>
{% endblock %}

3
sdbs_pile/pile/tests.py Normal file
View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

10
sdbs_pile/pile/urls.py Normal file
View file

@ -0,0 +1,10 @@
from django.urls import path
from . import views
app_name = 'pile'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('tag/<name_or_id>', views.TagView.as_view(), name='tag'),
path('item/<int:document_id>', views.DocumentView.as_view(), name='document'),
]

62
sdbs_pile/pile/views.py Normal file
View file

@ -0,0 +1,62 @@
# Create your views here.
from django.db.models import Count
from django.views.generic import TemplateView
from sdbs_pile.pile.models import Tag, Document
class BasePileView(TemplateView):
def get_context_data(self, **kwargs):
return {
'tags': sorted(sorted(Tag.objects.all(), key=lambda tag: tag.name),
key=lambda tag: tag.documents.count(), reverse=True),
'document_count': Document.objects.count(),
'untagged_count': Document.objects.annotate(tag_count=Count('tags')).filter(tag_count__gt=0).count()
}
class IndexView(BasePileView):
template_name = "front_intro.html"
def get_context_data(self, **kwargs):
base_context_data = super(IndexView, self).get_context_data(**kwargs)
return {
'recent_documents': Document.objects.order_by('-uploaded')[:5],
**base_context_data
}
class TagView(BasePileView):
template_name = "front_doc_listing.html"
def get_context_data(self, name_or_id: str):
base_context_data = super(TagView, self).get_context_data()
if name_or_id == "*":
tag = None
documents = Document.objects.all()
else:
try:
tag = Tag.objects.get(id=int(name_or_id))
except ValueError:
tag = Tag.objects.get(name=name_or_id)
documents = tag.documents.all()
return {
'tag': tag,
'documents': documents,
**base_context_data
}
class DocumentView(BasePileView):
template_name = "front_doc_detail.html"
def get_context_data(self, document_id: int):
base_context_data = super(DocumentView, self).get_context_data()
return {
'document': Document.objects.get(pk=document_id),
**base_context_data
}

114
sdbs_pile/settings.py Normal file
View file

@ -0,0 +1,114 @@
"""
Django settings for sdbs_pile project.
Generated by 'django-admin startproject' using Django 3.0.4.
For more information on this file, see
https://docs.djangoproject.com/en/3.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.0/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'x5j0*70$a&l*n_@kn=j%4_@_+ismdl-o8!z0@vyjfl0tt+nlxx'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'sdbs_pile.pile'
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'sdbs_pile.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'sdbs_pile.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Europe/Prague'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = '/static/'

22
sdbs_pile/urls.py Normal file
View file

@ -0,0 +1,22 @@
"""sdbs_pile URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('', include('sdbs_pile.pile.urls')),
path('admin/', admin.site.urls),
]

16
sdbs_pile/wsgi.py Normal file
View file

@ -0,0 +1,16 @@
"""
WSGI config for sdbs_pile project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sdbs_pile.settings')
application = get_wsgi_application()

View file

@ -1 +0,0 @@
Deny from all

View file

@ -1,35 +0,0 @@
<?php
// All credit goes to Chad Minick:
// http://chadminick.com/articles/simple-php-template-engine.html
class Template
{
private $vars = array();
public function __get($name)
{
return $this->vars[$name];
}
public function __set($name, $value)
{
if ($name == 'view_template_file') {
throw new Exception("Cannot bind variable named 'view_template_file'");
}
$this->vars[$name] = $value;
}
public function render($view_template_file)
{
if (array_key_exists('view_template_file', $this->vars)) {
throw new Exception("Cannot bind variable called 'view_template_file'");
}
extract($this->vars);
ob_start();
include($view_template_file);
return ob_get_clean();
}
}
?>

View file

@ -1,37 +0,0 @@
<div class="text document edit-form">
<form method="post" id="form" action="admin.php?action=edit_item<?= empty($doc) ? "" : "&item=" . $doc["ID"] ?>"
enctype="multipart/form-data">
<strong>Title:</strong> <input id="title-input" type="text" name="Title"
value="<?= empty($doc) ? "" : $doc["Title"] ?>"><br>
<strong>Author:</strong> <input type="text" name="Author" value="<?= empty($doc) ? "" : $doc["Author"] ?>"><br>
<strong>Date published:</strong> <input type="text" name="Published"
value="<?= empty($doc) ? "" : $doc["Published"] ?>"><br>
<strong>Description:</strong><br>
<textarea name="Description" cols="120" rows="20">
<?= empty($doc) ? "" : $doc["Description"] ?>
</textarea><br>
<strong>File:</strong> <input id="file-input" type="file" name="upfile" onchange="updateTitle()"><br>
<strong>URL:</strong> <input type="text" name="URL" value="<?= empty($doc) ? "" : $doc["URL"] ?>"><br>
<strong>Tags:</strong> <input type="text" name="Tags" value="<?php
if (!empty($doc)) {
$tags = [];
foreach ($doc["tags"] as $tag) {
array_push($tags, $tag["Name"]);
}
echo implode(", ", $tags);
} else if (!empty($_GET["tag"])) {
echo $tag["Name"];
}
?>"><br>
<input type="submit">
</form>
</div>
<script>
function updateTitle() {
const titleInput = document.getElementById("title-input");
if (titleInput.value.length === 0) {
const filename = document.getElementById("file-input").value;
titleInput.value = filename.replace(/.*[\/\\]/, '').replace(/\.[\w]{2,}$/, '');
}
}
</script>

View file

@ -1,39 +0,0 @@
<?php if (isset($tag)): ?>
<div class="text tag-text">
<h1><?= $tag["Name"] ?></h1>
<p class="tag-desc"><?= $tag["Description"] ?></p>
<a class="tag-edit-link" href="?action=edit_tag&tag=<?= $tag["ID"] ?>">[edit tag]</a>
<a class="tag-edit-link" href="#" onclick="confirmDelete()">[delete tag]</a>
</div>
<?php endif; ?>
<?php if ($_GET["tag"] != "*" &&
$_GET["tag"] != "_"): ?>
<div class="text doc-item doc-new-item">
<a href="?action=new_item&tag=<?= $tag["ID"] ?>">
<div class="doc-item-text">
<h2>Upload a new document</h2>
</div>
</a>
</div>
<?php endif; ?>
<?php foreach ($docs as $doc): ?>
<div class="text doc-item">
<a class="doc-item-link" href="?action=remove&item=<?= $doc["ID"] ?>">[X]</a>
<a href="?action=edit_item&item=<?= $doc["ID"] ?>">
<div class="doc-item-text">
<h2><?= $doc["Title"] ?></h2>
<h3><?= $doc["Author"] . " " . $doc['date'] ?></h3>
</div>
</a>
</div>
<?php endforeach; ?>
<script>
function confirmDelete() {
if (window.confirm("Do you really wish to delete this tag?")) {
window.open("?action=delete_tag&tag=<?= $tag["ID"] ?>")
}
}
</script>

View file

@ -1,5 +0,0 @@
<div class="text">
<p>Confirm deletion of <strong>"<?= $doc["Title"] ?>"</strong>:</p>
<a href="admin.php?action=remove&confirm=yes&item=<?= $doc["ID"] ?>&ret=<?= $_SERVER['HTTP_REFERER']; ?>"
class="button">Remove from database</a>
</div>

View file

@ -1,3 +0,0 @@
<div class="text">
<p class="intro">Handle with care.</p>
</div>

View file

@ -1,10 +0,0 @@
<div class="text document edit-form">
<form method="post" id="form" action="admin.php?action=edit_tag<?= empty($tag) ? "" : "&tag=" . $tag["ID"] ?>">
<strong>Name:</strong> <input type="text" name="Name" value="<?= empty($tag) ? "" : $tag["Name"] ?>"><br>
<strong>Description:</strong><br>
<textarea name="Description" cols="120" rows="20">
<?= empty($tag) ? "" : $tag["Description"] ?>
</textarea><br>
<input type="submit">
</form>
</div>

View file

@ -1,46 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>pile ADMIN INTERFACE</title>
<link rel="stylesheet" type="text/css" href="assets/admin.css">
<link rel="icon" type="image/png" href="/favicon.png">
</head>
<body>
<div id="sidebar">
<div id="sidebar-head">
<h1><a href="admin.php">pile admin</a></h1>
</div>
<div id="sidebar-taglist">
<ul id="sidebar-taglist-overview">
<li id="sidebar-taglist-top"><a href="?tag=*">ALL (<?= $all_count ?>)</a></li>
<li id="sidebar-taglist-top"><a href="?tag=_">UNTAGGED (<?= $none_count ?>)</a></li>
<li id="sidebar-taglist-top"><a href="?action=new_tag">ADD TAG</a></li>
</ul>
<ul>
<?php
foreach ($tags as $tag) {
echo '<li><a href="?tag=' . $tag['id'] . "\">" . $tag['name'] . " (" . $tag['count'] . ")</a></li>";
}
?>
</ul>
</div>
</div>
<div id="content">
<?php echo $content ?>
</div>
<div id="login">
<form method="get">
<input type="hidden" name="action" value="logout">
<button type="submit" id="login-button">log out</button>
</form>
</div>
</body>
</html>

View file

@ -1,21 +0,0 @@
<?php /** @noinspection PhpUndefinedVariableInspection */
if (isset($tag)): ?>
<div class="text tag-text">
<h1><?= $tag["Name"] ?></h1>
<p class="tag-desc"><?= $tag["HTMLDescription"] ?></p>
</div>
<?php endif; ?>
<?php foreach ($docs as $doc): ?>
<div class="text doc-item">
<?php if (!empty($doc["URL"])): ?>
<a class="doc-item-link" href="<?= $doc["URL"] ?>">🔗</a>
<?php endif; ?>
<a href="?item=<?= $doc["ID"] ?>">
<div class="doc-item-text">
<h2><?= $doc["Title"] ?></h2>
<h3><?= $doc["Author"] . " " . $doc['date'] ?></h3>
</div>
</a>
</div>
<?php endforeach; ?>

View file

@ -1,35 +0,0 @@
<div class="text document">
<h1><?= $doc["Title"] ?></h1>
<?php if (!empty($doc["Author"])): ?>
<h2><?= $doc["Author"] ?></h2>
<?php endif; ?>
<?php if (!empty($doc["Published"])): ?>
<h3>Published: <?= $doc["Published"] ?></h3>
<?php endif; ?>
<?php if (!empty($doc["tags"])): ?>
<h3 class="doc-taglist">Tags:
<?php
foreach ($doc["tags"] as $tag) {
echo '<li><a href="?tag=' . $tag["ID"] . "\">" . $tag["Name"] . "</a></li>";
}
?>
</h3>
<?php endif; ?>
<?php if ($doc["HTMLDescription"]): ?>
<p class="doc-description"><span
class="doc-description-intro"><?= empty($doc["URL"]) ? "Content" : "Description" ?>
: </span><?= $doc["HTMLDescription"] ?></p>
<?php endif; ?>
<div class="doc-link"><span class="doc-link-intro">Get (document with) print label: </span>
<a href="/label.php?id=<?= $doc["ID"] ?>">https://pile.sdbs.cz/label.php?id=<?= $doc["ID"] ?></a></div>
<?php if (!empty($doc["URL"])): ?>
<div class="doc-link"><span class="doc-link-intro">Access file at: </span><a
href="<?= $doc["URL"] ?>"><?= urldecode($doc["URL"]) ?></a></div>
<?php endif; ?>
</div>

View file

@ -1,28 +0,0 @@
<div class="text">
<p class="intro"> This site is the sdbs pile, where we upload the stuff we consider important to the larger
conceptual and thematic landscape of what we do: "confronting apathy", inter-subjectivity, the human right to
self-determination, counter-culture and such...</p>
<p class="intro czech">Tohle je hromádka zajímavýho materiálu co něco znamená v kontextu sdbs - budeme sem postupně
dávat ty nejdůležitější nebo nejzajímavější věci, zatim se o tom ale nikde moc nešiřte.</p>
<p class="intro sign">/-\</p>
</div>
<div class="text recent-additions">
<h2>Recent additions</h2>
<ul>
<?php foreach (array_slice($recent_docs, 0, 5) as $doc): ?>
<li>
<a href="/?item=<?= $doc['ID'] ?>">
<?php if (!empty($doc['UploadedTime'])): ?>
<em>(<?= date("Y/m/d H:i:s", $doc['UploadedTime']); ?>)</em>
<?php endif; ?>
<?= $doc['Title'] ?>
<div class="recent-additions-desc">
<?= $doc['Description'] ?>
</div>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>

View file

@ -1,88 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="assets/main.css">
<link rel="icon" type="image/png" href="/favicon.png">
<?php if ($selected_doc): ?>
<title>The /-\ pile: <?= $selected_doc['Title'] ?></title>
<meta property="og:title" content="The /-\ Pile: <?= $selected_doc['Title'] ?>"/>
<meta property="og:url" content="https://pile.sdbs.cz/?item=<?= $selected_doc['ID'] ?>"/>
<meta property="og:description" content="<?= $selected_doc['Description'] ?>"/>
<meta property="og:type" content="article"/>
<?php elseif ($selected_tag): ?>
<title>The /-\ pile: Filed under "<?= $selected_tag['Name'] ?>"</title>
<meta property="og:title" content="The /-\ Pile: Documents under '<?= $selected_tag['Name'] ?>'"/>
<meta property="og:url" content="https://pile.sdbs.cz/?tag=<?= $selected_tag['ID'] ?>"/>
<meta property="og:description" content="<?= $selected_tag['Description'] ?>"/>
<meta property="og:type" content="website"/>
<?php else: ?>
<title>The /-\ pile</title>
<meta property="og:title" content="The /-\ Pile"/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://pile.sdbs.cz/"/>
<meta property="og:description"
content="This is where we upload the stuff we consider important to the larger conceptual and thematic landscape of what we do: confronting apathy, inter-subjectivity, the human right to self-determination, counter-culture and such..."/>
<?php endif; ?>
<meta property="og:image" content="https://pile.sdbs.cz/favicon.png"/>
</head>
<body>
<div id="sidebar">
<div id="sidebar-head">
<h1><a href="..">The /-\ pile</a></h1>
</div>
<div id="sidebar-taglist">
<ul>
<li id="sidebar-taglist-top"><a href="?tag=*">ALL (<?= $doc_count ?>)</a></li>
<?php if ($none_count > 0): ?>
<li id="sidebar-taglist-top"><a href="?tag=_">UNTAGGED (<?= $none_count ?>)</a></li>
<?php endif; ?>
<?php
foreach ($tags as $tag) {
if ($tag['count'] > 0) {
echo '<li><a href="?tag=' . $tag['id'] . "\">" . $tag['name'] . " (" . $tag['count'] . ")</a></li>";
}
}
?>
</ul>
</div>
</div>
<div id="content">
<?php echo $content ?>
</div>
<div id="login">
<?php if ($logged): ?>
<form method="get" action="admin.php">
<button type="submit" id="login-button">enter admin interface</button>
</form>
<?php else: ?>
<form method="post" action="admin.php">
<input type="text" name="username" id="login-user"></input>
<input type="password" name="password" id="login-pass"></input>
<button type="submit" id="login-button">></button>
</form>
<?php endif; ?>
</div>
<script>
(function () {
window.counter = 'https://sdbs_pile.goatcounter.com/count'
var script = document.createElement('script');
script.async = 1;
script.src = '//gc.zgo.at/count.js';
var ins = document.getElementsByTagName('script')[0];
ins.parentNode.insertBefore(script, ins)
})();
</script>
</body>
</html>

View file

@ -1,41 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<?php if (isset($redirect)): ?>
<meta http-equiv="refresh" content="1;URL=<?= $redirect ?>"/>
<?php endif; ?>
<title>The /-\ pile</title>
<link rel="stylesheet" type="text/css" href="assets/main.css">
<link rel="icon" type="image/png" href="/favicon.png">
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
display: table
}
body p {
font-size: 3rem;
text-align: center;
}
div {
display: table-cell;
text-align: center;
vertical-align: middle;
}
</style>
</head>
<body>
<div>
<p>
<?= $text ?>
</p>
</div>
</body>
</html>

View file

@ -1,8 +0,0 @@
<div class="text">
<p class="intro"> This site is the sdbs pile, where we upload the stuff we consider important to the larger
conceptual and thematic landscape of what we do: "confronting apathy", inter-subjectivity, the human right to
self-determination, counter-culture and such...</p>
<p class="intro czech">Tohle je hromádka zajímavýho materiálu co něco znamená v kontextu sdbs - budeme sem postupně
dávat ty nejdůležitější nebo nejzajímavější věci, zatim se o tom ale nikde moc nešiřte.</p>
<p class="intro sign">/-\</p>
</div>

View file

@ -1,136 +0,0 @@
<?php /** @noinspection PhpUndefinedVariableInspection */ ?>
<head>
<title>LABEL FOR <?= $doc["Title"] ?></title>
<!--suppress CssNoGenericFontName -->
<style>
body {
font-family: prociono;
font-size: 14px;
}
h1, h2, h3 {
margin: 0;
padding: 0;
}
h1, h2 {
font-size: 24px;
}
a {
color: black;
text-decoration: none;
}
.exlibris {
text-align: center;
margin-bottom: 10px;
}
.label-outer {
width: 100%;
border: 5px solid black;
padding: 10px;
}
.label-stamp-outer {
float: left;
width: 150px;
}
#pile-logo {
width: 100%;
margin: 10px 0;
}
.label-stamp {
border: 2px solid black;
margin-right: 10px;
}
.label-stamp-subtitle {
text-align: center;
width: 100%;
margin-bottom: 5px;
font-size: 20px;
margin-top: -9px; /* prociono specific */
}
.label-text {
float: right;
}
.label-title {
margin: 0;
padding: 0;
}
.label-otherinfo {
font-size: 14px;
}
.label-description {
text-align: justify;
margin-top: 0;
padding-top: 0;
}
.label-footer {
width: 100%;
text-align: right;
}
.absolute-footer {
width: 100%;
position: absolute;
text-align: center;
bottom: 20px;
left: 0;
}
</style>
</head>
<body>
<h1 class="exlibris">ex libris /-\ pile</h1>
<div class="label-outer">
<div class="label-upper">
<div class="label-stamp-outer">
<div class="label-column label-stamp">
<img id="pile-logo" src="assets/pile_300dpi.png" alt="/-\ Pile"/>
<div class="label-stamp-subtitle">#<?= str_pad($doc["ID"], 4, "0", STR_PAD_LEFT); ?></div>
</div>
</div>
<div class="label-column label-text">
<h2 class="label-title"><?= $doc["Title"] ?></h2>
<h3 class="label-otherinfo">
<?php if ($doc["Author"]): ?>
By <?= $doc["Author"] ?>
<?php endif; ?>
<?php if ($doc["Published"]): ?>
<div class="label-otherinfo-date">(Published: <?= $doc["Published"] ?>)</div>
<?php endif; ?>
</h3>
<p class="label-description"><?= $doc["HTMLDescription"] ?></p>
</div>
</div>
<div class="label-footer">
<?php if (count($doc["tags"]) > 0): ?>
<div class="label-footer-tags">Filed under:
<?php
for ($i = 0; $i < count($doc["tags"]); $i++) {
echo $doc["tags"][$i]["Name"];
}
?>
</div>
<?php endif; ?>
<div class="label-footer-url">
Available at: <a href="https://pile.sdbs.cz/?item=<?= $doc["ID"] ?>">https://pile.sdbs.cz/?item=<?= $doc["ID"] ?></a>
</div>
</div>
</div>
<div class="absolute-footer">
read or share
</div>
</body>

View file

@ -1 +0,0 @@
Deny from all

View file

@ -1,274 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../_vendor/autoload.php';
class PileDB
{
protected $db;
private $pd;
function __construct($dbpath = "pile.db")
{
$this->db = new SQLite3($dbpath);
$this->pd = new Parsedown();
}
function prepare($statement)
{
return $this->db->prepare($statement);
}
function query($statement)
{
return $this->db->query($statement);
}
public function getDocCount()
{
$ret_count = $this->db->query("SELECT count(ID) FROM Documents")->fetchArray(SQLITE3_NUM);
return $ret_count[0];
}
public function getUntaggedDocCount()
{
$ret_count = $this->db->query("SELECT
count(ID)
FROM
Documents d
LEFT OUTER JOIN
DocumentstoTags dt ON dt.Document = d.ID
WHERE dt.Document IS NULL")->fetchArray(SQLITE3_NUM);
return $ret_count[0];
}
public function getTags()
{
$tag_query = "SELECT
ID, Name, count(Document)
FROM
Tags t
LEFT OUTER JOIN
DocumentstoTags d ON t.ID = d.Tag
GROUP BY Name
ORDER BY count(Document) DESC, Name";
$tags_ret = $this->db->query($tag_query);
$tags = [];
while ($row = $tags_ret->fetchArray(SQLITE3_NUM)) {
array_push($tags, array(
'id' => $row[0],
'name' => $row[1],
'count' => $row[2]
));
}
return $tags;
}
/**
* Fetch pile document as an associative array.
*
* @throws NotFoundException when document isn't found.
*/
public function fetchDoc($id)
{
$stmt_doc = $this->db->prepare("SELECT * FROM Documents WHERE ID = :id");
$stmt_doc->bindValue(":id", $id, SQLITE3_INTEGER);
$doc = $stmt_doc->execute()->fetchArray(SQLITE3_ASSOC);
if ($doc == false) {
throw new NotFoundException();
}
$doc["HTMLDescription"] = $this->pd->text($doc["Description"]);
$stmt_tags = $this->db->prepare("SELECT t.ID, t.Name FROM Tags t
JOIN DocumentsToTags dt ON t.ID = dt.Tag
JOIN Documents d on d.ID = dt.Document
WHERE d.ID = :id");
$stmt_tags->bindValue(":id", $id, SQLITE3_INTEGER);
$ret = $stmt_tags->execute();
$doc["tags"] = [];
while ($tag = $ret->fetchArray(SQLITE3_ASSOC)) {
array_push($doc["tags"], $tag);
}
return $doc;
}
public function listDocs()
{
if (func_num_args() > 0) {
$tag = func_get_arg(0);
if ($tag > 0) {
$stmt = $this->db->prepare("SELECT
ID, Title, Author, Published, URL
FROM
Documents d
LEFT OUTER JOIN
DocumentsToTags dt ON d.ID = dt.Document
WHERE Tag == :tag");
$stmt->bindValue(":tag", $tag, SQLITE3_INTEGER);
} else {
$stmt = $this->db->prepare("SELECT
ID, Title, Author, Published, URL
FROM
Documents d
LEFT OUTER JOIN
DocumentsToTags dt ON d.ID = dt.Document
WHERE dt.Document IS NULL");
}
$doc_ret = $stmt->execute();
} else {
$query = "SELECT ID, Title, Author, Published, URL FROM Documents";
$doc_ret = $this->db->query($query);
}
$docs = [];
while ($doc = $doc_ret->fetchArray(SQLITE3_ASSOC)) {
$doc['date'] = empty($doc["Published"]) ? "" : "(" . $doc["Published"] . ")";
array_push($docs, $doc);
}
return $docs;
}
public function getRecentDocs($count = 15)
{
$query = $this->db->prepare("SELECT * FROM Documents ORDER BY ID DESC LIMIT :count");
$query->bindValue("count", $count);
$query_ret = $query->execute();
$result = [];
while ($row = $query_ret->fetchArray(SQLITE3_ASSOC)) {
array_push($result, $row);
}
return $result;
}
public function updateDoc($id, $title, $author, $description, $published, $url, $tag_ids)
{
if (empty($id)) {
$stmt = $this->db->prepare("INSERT INTO Documents
(ID, Title, Author, Description, Published, URL, UploadedTime)
VALUES
(NULL, :title, :author, :description, :published, :url, :uploadedtime)");
$stmt->bindValue(":uploadedtime", time(), SQLITE3_INTEGER);
} else {
$stmt = $this->db->prepare("UPDATE Documents SET
Title=:title,
Author=:author,
Description=:description,
Published=:published,
URL=:url
WHERE ID = :id");
$stmt->bindValue(":id", $id, SQLITE3_INTEGER);
}
$stmt->bindValue(":title", $title, SQLITE3_TEXT);
$stmt->bindValue(":author", $author, SQLITE3_TEXT);
$stmt->bindValue(":description", $description, SQLITE3_TEXT);
$stmt->bindValue(":published", $published, SQLITE3_TEXT);
$stmt->bindValue(":url", $url, SQLITE3_TEXT);
$stmt->execute();
if (empty($id)) {
$id = $this->db->lastInsertRowid();
}
if (!empty($id)) {
$delete_stmt = $this->db->prepare("DELETE FROM DocumentsToTags
WHERE Document = :id");
$delete_stmt->bindValue(":id", $id, SQLITE3_INTEGER);
$delete_stmt->execute();
}
foreach ($tag_ids as $tag) {
$tag_stmt = $this->db->prepare("INSERT INTO DocumentsToTags ('Document', 'Tag')
VALUES (:doc, :tag)");
$tag_stmt->bindValue("doc", $id, SQLITE3_INTEGER);
$tag_stmt->bindValue("tag", $tag, SQLITE3_INTEGER);
$tag_stmt->execute();
}
}
public function removeDoc($id)
{
$doc_stmt = $this->db->prepare("DELETE FROM Documents
WHERE ID = :id");
$doc_stmt->bindValue("id", $id, SQLITE3_INTEGER);
$doc_stmt->execute();
$tag_stmt = $this->db->prepare("DELETE FROM DocumentsToTags
WHERE Document = :id");
$tag_stmt->bindValue("id", $id, SQLITE3_INTEGER);
$tag_stmt->execute();
}
public function findTag($name)
{
$stmt = $this->db->prepare("SELECT * FROM Tags WHERE Name == :name COLLATE NOCASE");
$stmt->bindValue(":name", $name, SQLITE3_TEXT);
return $stmt->execute()->fetchArray(SQLITE3_ASSOC);
}
/***
* Fetches tag as associative array.
* @throws NotFoundException
*/
public function fetchTag($tag)
{
$stmt = $this->db->prepare("SELECT * FROM Tags WHERE ID == :tag");
$stmt->bindValue(":tag", $tag, SQLITE3_INTEGER);
$tag = $stmt->execute()->fetchArray(SQLITE3_ASSOC);
if ($tag == false) {
throw new NotFoundException();
}
$tag["HTMLDescription"] = $this->pd->text($tag["Description"]);
return $tag;
}
public function updateTag($id, $name, $description)
{
if (empty($id)) {
$stmt = $this->db->prepare("INSERT INTO Tags
(ID, Name, Description)
VALUES
(NULL, :name, :description)");
} else {
$stmt = $this->db->prepare("UPDATE Tags SET
Name=:name,
Description=:description,
Parent=:Parent
WHERE ID = :id");
$stmt->bindValue(":id", $id, SQLITE3_INTEGER);
}
$stmt->bindValue(":name", $name, SQLITE3_TEXT);
$stmt->bindValue(":description", $description, SQLITE3_TEXT);
return $stmt->execute();
}
public function deleteTag($tag)
{
$stmt = $this->db->prepare("DELETE FROM Tags WHERE ID == :tag");
$stmt->bindValue(":tag", $tag, SQLITE3_INTEGER);
return $stmt->execute();
}
public function authenticate($username, $password)
{
$stmt = $this->db->prepare("SELECT
*
FROM
Users
WHERE
Username = :username");
$stmt->bindValue(":username", $username, SQLITE3_TEXT);
$auth_ret = $stmt->execute();
$auth = $auth_ret->fetchArray(SQLITE3_ASSOC);
if (password_verify($password, $auth["Password"])) {
return $auth["ID"];
} else {
return -1;
}
}
}
class NotFoundException extends Exception
{
// noop
}
?>

View file

@ -1,54 +0,0 @@
<?php
class Uploader
{
public function handle($files, $dir)
{
if (is_array($files['upfile']['error'])) {
throw new RuntimeException('Invalid parameters.');
}
switch ($files['upfile']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
throw new RuntimeException('No file sent.');
case UPLOAD_ERR_INI_SIZE:
throw new RuntimeException('Exceeded INI filesize limit.');
case UPLOAD_ERR_FORM_SIZE:
throw new RuntimeException('Exceeded form filesize limit.');
default:
throw new RuntimeException('Unknown errors.');
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
if (false === $ext = array_search(
$finfo->file($files['upfile']['tmp_name']),
array(
'pdf' => 'application/pdf',
'zip' => 'application/zip',
'rar' => 'application/rar',
'txt' => 'text/plain'
),
true
)) {
throw new RuntimeException('Invalid file format.');
}
$name = basename($files['upfile']['name']);
$name = preg_replace('/[^\x20-\x7E]/', '', $name);
if ($name != ".htaccess") {
if (!move_uploaded_file(
$files['upfile']['tmp_name'],
$dir . $name)) {
throw new RuntimeException('Failed to move uploaded file.');
}
} else {
throw new RuntimeException('Invalid filename.');
}
return $name;
}
}
?>

View file

@ -1 +0,0 @@
Deny from all

View file

@ -1,162 +0,0 @@
<?php
require '_templates/Template.php';
require '_util/PileDB.php';
require '_util/Uploader.php';
$db = new PileDB();
$uploader = new Uploader();
session_start();
if (isset($_SESSION['ID'])) {
$page = new Template();
if (isset($_GET["action"])) {
switch ($_GET["action"]) {
case "new_tag":
$content = $page->render("admin_tag_edit.php");
break;
case "edit_tag":
if (isset($_POST["Name"])) {
$db->updateTag(
$_GET["tag"],
$_POST["Name"],
$_POST["Description"]
);
}
if (!empty($_GET["tag"])) {
$page->tag = $db->fetchTag($_GET["tag"]);
}
$content = $page->render("admin_tag_edit.php");
break;
case "delete_tag":
if (!empty($_GET["tag"])) {
$db->deleteTag($_GET["tag"]);
$page->text = "Tag deleted successfully.";
$page->redirect = "/admin.php";
echo $page->render('full_text.php');
return;
}
break;
case "new_item":
if (!empty($_GET["tag"])) {
$page->tag = $db->fetchTag($_GET["tag"]);
}
$content = $page->render("admin_doc_edit.php");
break;
case "edit_item":
if (isset($_POST["Title"]) || !empty($_FILES['upfile']['name'])) {
$title = $_POST["Title"];
if (!empty($_FILES['upfile']['name'])) {
try {
if (empty($title)) {
$title = pathinfo($_FILES['upfile']['name'], PATHINFO_FILENAME);
$title = str_replace("_", " ", $title);
$title = trim($title);
}
$url = "http://pile.sdbs.cz/docs/" . rawurlencode($uploader->handle($_FILES, "docs/"));
} catch (RuntimeException $ex) {
$page->text = $ex->getMessage();
echo $page->render('full_text.php');
return;
}
} else {
$url = $_POST["URL"];
}
$doc_tags = [];
foreach (explode(",", $_POST["Tags"]) as $tagName) {
$tagName = trim($tagName);
$tag = $db->findTag($tagName);
if (!in_array($tag["ID"], $doc_tags)) {
array_push($doc_tags, $tag["ID"]);
}
}
$db->updateDoc(
$_GET["item"],
$title,
$_POST["Author"],
$_POST["Description"],
$_POST["Published"],
$url,
$doc_tags
);
}
if (!empty($_GET["item"])) {
$page->doc = $db->fetchDoc($_GET["item"]);
}
$content = $page->render("admin_doc_edit.php");
break;
case "remove":
if (!empty($_GET["confirm"]) && $_GET["confirm"] == "yes") {
$db->removeDoc($_GET["item"]);
$page->text = "Document deleted.";
$page->redirect = $_GET["ret"];
echo $page->render("full_text.php");
return;
} else {
$page->doc = $db->fetchDoc($_GET["item"]);
$content = $page->render("admin_doc_remove.php");
}
break;
case "logout":
unset($_SESSION["ID"]);
$page->text = "See you.";
$page->redirect = "/";
echo $page->render("full_text.php");
return;
}
} elseif (isset($_GET["tag"])) {
$doc_list_template = new Template();
if ($_GET["tag"] == "*") {
$docs = $db->listDocs();
} elseif ($_GET["tag"] == "_") {
$docs = $db->listDocs(-1);
} else {
$tag = $db->fetchTag($_GET["tag"]);
$docs = $db->listDocs($tag["ID"]);
$doc_list_template->tag = $tag;
}
$doc_list_template->docs = $docs;
$content = $doc_list_template->render('admin_doc_listing.php');
} else {
$intro_template = new Template();
$content = $intro_template->render('admin_intro.php');
}
$all_count = $db->getDocCount();
$none_count = $db->getUntaggedDocCount();
$tags = $db->getTags();
$page->all_count = $all_count;
$page->none_count = $none_count;
$page->tags = $tags;
$page->content = $content;
echo $page->render('admin_wrap.php');
} else {
$page = new Template();
if (isset($_POST['username']) && isset($_POST['password'])) {
$ret_id = $db->authenticate($_POST["username"], $_POST["password"]);
if ($ret_id > 0) {
$_SESSION['ID'] = $ret_id;
$page->text = "You have logged in successfully.";
$page->redirect = "admin.php";
} else {
$page->text = "Username and/or password incorrect.";
$page->redirect = "/";
}
} else {
$page->text = "Please log in before accessing this page.";
$page->redirect = "/";
}
echo $page->render('full_text.php');
}
?>

View file

@ -1,244 +0,0 @@
@font-face {
font-family: Prociono;
src: url(Prociono.woff);
}
*, *:before, *:after {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
font-family: Prociono;
margin: 0;
padding: 0;
position: relative;
height: 100%;
background: #202020;
}
a {
color: #202020;
text-decoration: none;
}
#sidebar {
font-family: Prociono, serif;
}
#sidebar-head {
background-color: #f2f2f2;
color: #202020;
text-align: center;
}
#sidebar-head > h1 {
padding: 0.5em;
margin: 0 0 1rem 0;;
}
#sidebar-taglist {
background: #f2f2f2;
color: #202020;
padding: 1rem 0 1rem 0;
}
#sidebar-taglist > ul > li {
list-style: none;
font-size: 14pt;
margin-bottom: .5em;
}
#sidebar-taglist > ul > li:before {
content: "/ ";
}
#sidebar-taglist-top:before {
content: "\\ " !important;
}
#sidebar-taglist a {
color: #202020;
text-decoration: none;
}
#sidebar-taglist ul:not(:last-child) {
padding-bottom: .5rem;
box-shadow: 0px 24px 0px -22px #202020;
}
#content {
font-size: 14pt;
}
.text {
background: #f2f2f2;
color: #202020;
padding: 1rem;
margin: 0 0 1rem 0;
}
.doc-item-text {
display: inline-block;
margin-right: 3em;
}
.doc-item h2 {
font-size: 14pt;
font-weight: normal;
margin: 0;
}
.doc-item h3 {
margin: 0;
font-size: 11pt;
font-weight: normal;
font-style: italic;
}
.doc-item-link {
display: inline-block;
float: right;
position: relative;
top: 7px;
right: 1em;
}
.doc-new-item {
width: 100%;
text-align: center;
}
.doc-new-item a {
width: 100%;
}
.document h1 {
margin: 0;
font-size: 24pt;
}
.document h2 {
margin: 2pt 0 2pt 0;
font-style: italic;
font-weight: normal;
font-size: 16pt;
}
.document h3 {
margin: 2pt 0 2pt 0;
font-weight: normal;
font-size: 16pt;
}
.doc-taglist li {
list-style: none;
display: inline-block;
}
.doc-taglist li a {
text-decoration: underline;
}
.doc-taglist li:after {
content: "/";
}
.doc-taglist li:last-of-type:after {
content: "";
}
.doc-description-intro, .doc-link-intro {
font-weight: bold;
}
.doc-link-intro:before {
content: "➜ ";
}
.doc-link a {
text-decoration: underline;
}
.tag-edit-link {
text-align: right;
}
input[type="text"] {
width: 80%;
}
@media screen and (min-width: 64em ) {
#sidebar {
position: absolute;
top: 0;
left: 2rem;
width: 14rem;
}
#content {
margin: 2rem 2rem 0 18rem;
padding: 0 0 2rem 0;
}
}
@media screen and (max-width: 64em ) {
#sidebar-head {
background-position: 14%;
background-size: 4rem;
height: 7rem;
margin-bottom: 1rem;
}
#sidebar-head > h1 {
padding-top: 1.5rem;
font-size: 3rem;
}
#sidebar-taglist {
margin-bottom: 1rem;
}
}
#login {
position: absolute;
bottom: 0;
right: 2rem;
}
#login input {
height: 14pt;
border: 1px solid lightgray;
}
#login button {
height: 14pt;
font-size: 8pt;
border: 1px solid lightgray;
background: #202020;
}
.czech {
font-size: 12pt;
}
.sign {
font-weight: bold;
font-size: 16pt;
margin: 0;
}
input[type="submit"], input[type="file"] {
font-family: Prociono, serif;
}
.button {
-webkit-appearance: button;
-moz-appearance: button;
appearance: button;
text-decoration: none;
color: initial;
padding: 2px;
}

View file

@ -1,10 +0,0 @@
{
"config": {
"vendor-dir": "_vendor"
},
"require": {
"erusev/parsedown": "^1.7",
"mpdf/mpdf": "^8.0",
"ext-sqlite3": "*"
}
}

336
www/composer.lock generated
View file

@ -1,336 +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": "07bcf0ebe745edf9a2aff62e65aae616",
"packages": [
{
"name": "erusev/parsedown",
"version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/erusev/parsedown.git",
"reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/erusev/parsedown/zipball/6d893938171a817f4e9bc9e86f2da1e370b7bcd7",
"reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35"
},
"type": "library",
"autoload": {
"psr-0": {
"Parsedown": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"description": "Parser for Markdown.",
"homepage": "http://parsedown.org",
"keywords": [
"markdown",
"parser"
],
"time": "2019-03-17T18:48:37+00:00"
},
{
"name": "mpdf/mpdf",
"version": "v8.0.2",
"source": {
"type": "git",
"url": "https://github.com/mpdf/mpdf.git",
"reference": "ab0662606cc2396015616633946f3b8918d818a7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mpdf/mpdf/zipball/ab0662606cc2396015616633946f3b8918d818a7",
"reference": "ab0662606cc2396015616633946f3b8918d818a7",
"shasum": ""
},
"require": {
"ext-gd": "*",
"ext-mbstring": "*",
"myclabs/deep-copy": "^1.7",
"paragonie/random_compat": "^1.4|^2.0|9.99.99",
"php": "^5.6 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0",
"psr/log": "^1.0",
"setasign/fpdi": "^2.1"
},
"require-dev": {
"mockery/mockery": "^0.9.5",
"mpdf/qrcode": "^1.0.0",
"phpunit/phpunit": "^5.0",
"squizlabs/php_codesniffer": "^2.7.0",
"tracy/tracy": "^2.4"
},
"suggest": {
"ext-bcmath": "Needed for generation of some types of barcodes",
"ext-xml": "Needed mainly for SVG manipulation",
"ext-zlib": "Needed for compression of embedded resources, such as fonts"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-development": "7.x-dev"
}
},
"autoload": {
"psr-4": {
"Mpdf\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0-only"
],
"authors": [
{
"name": "Matěj Humpál",
"role": "Developer, maintainer"
},
{
"name": "Ian Back",
"role": "Developer (retired)"
}
],
"description": "PHP library generating PDF files from UTF-8 encoded HTML",
"homepage": "https://mpdf.github.io",
"keywords": [
"pdf",
"php",
"utf-8"
],
"time": "2019-06-17T09:03:49+00:00"
},
{
"name": "myclabs/deep-copy",
"version": "1.9.3",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea",
"reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea",
"shasum": ""
},
"require": {
"php": "^7.1"
},
"replace": {
"myclabs/deep-copy": "self.version"
},
"require-dev": {
"doctrine/collections": "^1.0",
"doctrine/common": "^2.6",
"phpunit/phpunit": "^7.1"
},
"type": "library",
"autoload": {
"psr-4": {
"DeepCopy\\": "src/DeepCopy/"
},
"files": [
"src/DeepCopy/deep_copy.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Create deep copies (clones) of your objects",
"keywords": [
"clone",
"copy",
"duplicate",
"object",
"object graph"
],
"time": "2019-08-09T12:45:53+00:00"
},
{
"name": "paragonie/random_compat",
"version": "v9.99.99",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
"reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
"shasum": ""
},
"require": {
"php": "^7"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*",
"vimeo/psalm": "^1"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"polyfill",
"pseudorandom",
"random"
],
"time": "2018-07-02T15:55:56+00:00"
},
{
"name": "psr/log",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"time": "2018-11-20T15:27:04+00:00"
},
{
"name": "setasign/fpdi",
"version": "v2.2.0",
"source": {
"type": "git",
"url": "https://github.com/Setasign/FPDI.git",
"reference": "3c266002f8044f61b17329f7cd702d44d73f0f7f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Setasign/FPDI/zipball/3c266002f8044f61b17329f7cd702d44d73f0f7f",
"reference": "3c266002f8044f61b17329f7cd702d44d73f0f7f",
"shasum": ""
},
"require": {
"ext-zlib": "*",
"php": "^5.6 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "~5.7",
"setasign/fpdf": "~1.8",
"setasign/tfpdf": "1.25",
"tecnickcom/tcpdf": "~6.2"
},
"suggest": {
"setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured.",
"setasign/fpdi-fpdf": "Use this package to automatically evaluate dependencies to FPDF.",
"setasign/fpdi-tcpdf": "Use this package to automatically evaluate dependencies to TCPDF.",
"setasign/fpdi-tfpdf": "Use this package to automatically evaluate dependencies to tFPDF."
},
"type": "library",
"autoload": {
"psr-4": {
"setasign\\Fpdi\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jan Slabon",
"email": "jan.slabon@setasign.com",
"homepage": "https://www.setasign.com"
},
{
"name": "Maximilian Kresse",
"email": "maximilian.kresse@setasign.com",
"homepage": "https://www.setasign.com"
}
],
"description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.",
"homepage": "https://www.setasign.com/fpdi",
"keywords": [
"fpdf",
"fpdi",
"pdf"
],
"time": "2019-01-30T14:11:19+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"ext-sqlite3": "*"
},
"platform-dev": []
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

View file

@ -1,26 +0,0 @@
<?php
require '_util/PileDB.php';
$db = new PileDB();
$recent_docs = $db->getRecentDocs();
header('Content-Type: application/rss+xml');
?>
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>/-\ pile</title>
<link>https://pile.sdbs.cz</link>
<atom:link href="https://pile.sdbs.cz/feed.php" rel="self" type="application/rss+xml" />
<description>A pile of interesting documents.</description>
<?php foreach ($recent_docs as $doc): ?>
<item>
<title><?= $doc['Title'] ?></title>
<guid>https://pile.sdbs.cz/?item=<?= $doc['ID'] ?></guid>
<link>https://pile.sdbs.cz/?item=<?= $doc['ID'] ?></link>
<description><?= $doc['Description'] ?></description>
</item>
<?php endforeach; ?>
</channel>
</rss>

View file

@ -1,55 +0,0 @@
<?php
require '_templates/Template.php';
require '_util/PileDB.php';
$db = new PileDB();
session_start();
$page = new Template();
if (isset($_GET["item"])) {
try {
$doc = $db->fetchDoc($_GET["item"]);
} catch (NotFoundException $e) {
http_response_code(404);
$page->text = "Document not found.";
$page->redirect = "/";
echo $page->render("full_text.php");
die(0);
}
$doc_template = new Template();
$doc_template->doc = $doc;
$selected_doc = $doc;
$content = $doc_template->render('front_doc_overview.php');
} elseif (isset($_GET["tag"])) {
$doc_list_template = new Template();
if ($_GET["tag"] == "*") {
$docs = $db->listDocs();
} elseif ($_GET["tag"] == "_") {
$docs = $db->listDocs(-1);
} else {
$tag = $db->fetchTag($_GET["tag"]);
if (!$tag) {
$tag = $db->findTag($_GET["tag"]);
}
$docs = $db->listDocs($tag["ID"]);
$selected_tag = $tag;
$doc_list_template->tag = $tag;
}
$doc_list_template->docs = $docs;
$content = $doc_list_template->render('front_doc_listing.php');
} else {
$intro_template = new Template();
$intro_template->recent_docs = $db->getRecentDocs();
$content = $intro_template->render('front_intro.php');
}
$page->doc_count = $db->getDocCount();
$page->none_count = $db->getUntaggedDocCount();
$page->tags = $db->getTags();
$page->content = $content;
$page->logged = isset($_SESSION["ID"]);
$page->selected_doc = $selected_doc;
$page->selected_tag = $selected_tag;
echo $page->render('front_wrap.php');
?>

View file

@ -1,65 +0,0 @@
<?php
require '_vendor/autoload.php';
require '_templates/Template.php';
require '_util/PileDB.php';
$db = new PileDB();
try {
$doc = $db->fetchDoc($_GET["id"]);
} catch (NotFoundException $e) {
http_response_code(404);
$page->text = "Document not found.";
$page->redirect = "/";
echo $page->render("full_text.php");
die(0);
}
$front = new Template();
$front->doc = $doc;
$defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults();
$fontDirs = $defaultConfig['fontDir'];
$defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults();
$fontData = $defaultFontConfig['fontdata'];
try {
$mpdf = new \Mpdf\Mpdf([
'format' => 'A4',
'fontDir' => array_merge($fontDirs, [
__DIR__ . '/assets',
]),
'fontdata' => $fontData + [
'prociono' => [
'R' => 'Prociono-Regular.ttf',
]
],
'default_font' => 'prociono'
]);
$mpdf->showImageErrors = true;
$mpdf->WriteHTML($front->render("_templates/label_template.php"));
try {
if (preg_match('#^https?://pile.sdbs.cz/.*\.(pdf|PDF)$#', $doc['URL']) === 1) {
$tempName = tempnam("/tmp/", 'pile-merge');
file_put_contents($tempName, file_get_contents($doc["URL"]));
$pageCount = $mpdf->SetSourceFile($tempName);
for ($page = 1; $page <= $pageCount; $page++) {
$mpdf->AddPage();
$template = $mpdf->ImportPage($page);
$mpdf->UseTemplate($template);
}
}
} catch (Exception $exception) {
// noop
} finally {
unlink($tempName);
}
$mpdf->Output();
} catch (Exception $exception) {
http_response_code(500); ?>
<h1>Something went wrong generating the label.</h1>
<pre><?= $exception->getMessage() ?></pre>
<?php } ?>