Initiate repository

This commit is contained in:
Helen Chong 2024-06-20 22:10:42 +08:00
commit 8f61187328
1377 changed files with 133500 additions and 0 deletions

12
.dockerignore Normal file
View File

@ -0,0 +1,12 @@
.dockerignore
Dockerfile
.git
.gitignore
LICENSE.md
README.md
README_de_DE.md
README_it_IT.md
README_ko_KR.md
README_nl_NL.md
README_zh_CN.md
SECURITY.md

14
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,14 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
custom: ["https://paypal.me/djpimley"]

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.sublime-project
*.sublime-workspace
*.DS_Store

14
Dockerfile Normal file
View File

@ -0,0 +1,14 @@
FROM php:8.2-apache
WORKDIR /var/www/html
COPY . .
RUN chown -R www-data ./*
RUN apt-get update
RUN apt-get install -y libonig-dev libpq-dev
RUN docker-php-ext-install pdo_mysql pdo_pgsql
EXPOSE 80
VOLUME /var/www/html
CMD [ "apache2-foreground" ]

28
LICENSE.md Normal file
View File

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

102
README.md Normal file
View File

@ -0,0 +1,102 @@
[English](README.md), [Deutsch](README_de_DE.md), [Italiano](README_it_IT.md), [한국인](README_ko_KR.md), [Nederlands](README_nl_NL.md), [简体中文](README_zh_CN.md).
## What can Chyrp Lite do for me?
Chyrp Lite makes it possible to host a blog on your own web server with minimal fuss. You can have
a traditional blog, a tumbleblog, or you can add oodles of customisation and build a general-purpose
web publishing platform with blogging features on the side. You get five beautiful blog themes and
a friendly administration console, all fully navigable on a broad range of devices, thanks to the
power of responsive HTML5. Semantic markup and comprehensive ARIA labelling ensure your blog will be
accessible to visitors who use assistive technologies.
With a flexible system of Feathers and Pages, you can make your website whatever you want it to be.
Feathers enable different types of blog content you can restrict yourself to absolute textual purity,
or you can create a multimedia rainbow. Pages let you publish articles separate from your blog content
be it a simple colophon or a hierarchy of multiple pages, optionally including a homepage that your
visitors will see when they first arrive at your website.
## What are the key features?
#### Core:
* Easy to install, simple to maintain, extensible by design.
* Built with responsive and accessible W3C-validated HTML5.
* Universal support for plain text, Markdown, or raw markup.
* Personalise your blog using powerful extensions.
* Theme development is easy with the Twig template engine.
* Manage users and visitors with a comprehensive rights model.
#### Feathers:
* Text: write textual blog entries.
* Photo: upload an image.
* Quote: make a quotation.
* Link: link to another website.
* Video: upload a video file.
* Audio: upload an audio file.
* Uploader: upload multiple files.
#### Modules:
* Cacher: cache your blog pages for reduced server load.
* Categorize: give each of your blog entries a category.
* Tags: apply multiple searchable tags to your blog entries.
* Mentionable: register webmentions from blogs that link to yours.
* Comments: a comprehensive comments system for your blog.
* Likes: allow your visitors to show their appreciation.
* Read More: excerpt long blog entries on the blog index.
* Rights: set attribution and copyright/left for your entries.
* Cascade: ajax-powered infinite scrolling for your blog.
* Lightbox: on-page image viewer with image protection.
* Sitemap: index your blog for search engines.
* MAPTCHA: use simple mathematics problems to prevent spam.
* Highlighter: syntax highlighting for your code snippets.
* Easy Embed: the easiest way to embed videos in your blog.
* Post Views: maintain a view count for your blog entries.
* MathJax: a JavaScript display engine for mathematics.
## Requirements
* [PHP 8.0+](https://www.php.net/supported-versions.php) with default extensions (Session, JSON, Ctype, Filter, libxml, SimpleXML)
* [Multibyte String](https://www.php.net/manual/en/book.mbstring.php)
* [PDO](https://www.php.net/manual/en/book.pdo.php)
* [cURL](https://www.php.net/manual/en/book.curl.php)
* MySQL 5.7+
* SQLite 3+
* PostgreSQL 10+
## Installation
You can install Chyrp Lite in three steps:
1. If using MySQL, create a MySQL database with a username and password.
2. Download the [latest release](https://github.com/xenocrat/chyrp-lite/releases), unzip, and upload to your web server.
3. Run the installation process by visiting [install.php](install.php) in your web browser.
## Upgrading
You can upgrade Chyrp Lite in six steps:
1. __Backup your database before proceeding!__
2. Download the latest version of Chyrp Lite.
3. Move your _uploads_ folder and _includes/config.json.php_ somewhere safe.
4. Overwrite your current version with the new one.
5. Restore your _uploads_ folder and _includes/config.json.php_.
6. Run the upgrade process by visiting [upgrade.php](upgrade.php) in your web browser.
## Documentation
The Chyrp Lite [wiki](https://chyrplite.net/wiki/) has comprehensive documentation
for users and developers.
## Authors
Chyrp Lite was created by the following people:
* Lite Developer: Daniel Pimley
* Chyrp Developer: Arian Xhezairi
* Project Founder: Alex Suraci
* Module authors and other contributors.
## Licenses
Chyrp Lite is Copyright 2008-2024 Alex Suraci, Arian Xhezairi, Daniel Pimley, and other contributors,
distributed under the [BSD license](https://raw.githubusercontent.com/xenocrat/chyrp-lite/master/LICENSE.md).
Please see the [licenses](licenses) directory for the full license text of all software packages distributed with Chyrp Lite.

9
SECURITY.md Normal file
View File

@ -0,0 +1,9 @@
# Security Policy
## Supported Versions
The latest version will be supported with security updates.
## Reporting a Vulnerability
You can [report a security vulnerability privately on Github](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability).

View File

@ -0,0 +1,21 @@
{% extends "layouts" ~ DIR ~ "help.twig" %}
{% block title %}{{ "Canonical URL" | translate }}{% endblock %}
{% block content %}
<h1>{{ "Canonical URL" | translate }}</h1>
<p>
{{ "If you enter a canonical URL, your site URLs will point someplace other than your install directory. You can use this feature to have Chyrp Lite installed in its own dedicated directory on your web server and still have your site accessible at your choice of destination directory. There are two requirements for this to work:" | translate }}
</p>
<ol>
<li>
{{ "Create an <em>index.php</em> file in your destination directory with the following in it:" | translate }}
<pre><code>&lt;?php
require "filesystem/path/to/chyrp/index.php";
</code></pre>
</li>
<li>
{{ "Modify your URL rewrite directives to reflect the new destination directory." | translate }}
</li>
</ol>
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "layouts" ~ DIR ~ "help.twig" %}
{% block title %}{{ "Filtering Results" | translate }}{% endblock %}
{% block content %}
<h1>{{ "Filtering Results" | translate }}</h1>
<p>
{{ "Use this search field to filter for specific items by entering plain text or keywords." | translate }}
</p>
<h2>{{ "Keywords" | translate }}</h2>
<p>
{{ "Use the syntax <code>keyword:value;</code> to quickly match specific results where <code>keyword</code> is equal to <code>value</code>. The keyword can be the name of a database column, the name of an author or a group, or a date/time. For example: <code>author:Foo;year:2016;</code> would filter the posts table to display only posts created by Foo in 2016." | translate }}
</p>
<p>
{{ "Use the syntax <code>ASC:column;</code> or <code>DESC:column;</code> to sort the results in ascending or descending order. For example: <code>DESC:user_id;ASC:id;</code> would sort a table of posts in descending order of user ID and then ascending order of post ID." | translate }}
</p>
{% endblock %}

101
admin/help/markdown.twig Normal file
View File

@ -0,0 +1,101 @@
{% extends "layouts" ~ DIR ~ "help.twig" %}
{% block title %}{{ "Markdown" | translate }}{% endblock %}
{% block content %}
<h1>{{ "Markdown" | translate }}</h1>
<p>
{{ "Markdown is a syntax for writing structured documents in plain text. Here are the basics to get you started:" | translate }}
</p>
<table>
<thead>
<tr>
<th>{{ "Markdown" | translate }}</th>
<th>{{ "Result" | translate }}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ "## Heading" | translate }}</td>
<td><h2>{{ "Heading" | translate }}</h2></td>
</tr>
<tr>
<td>{{ "### Heading" | translate }}</td>
<td><h3>{{ "Heading" | translate }}</h3></td>
</tr>
<tr>
<td>{{ "**Strong**" | translate }}</td>
<td><strong>{{ "Strong" | translate }}</strong></td>
</tr>
<tr>
<td>{{ "*Emphasis*" | translate }}</td>
<td><em>{{ "Emphasis" | translate }}</em></td>
</tr>
<tr>
<td>{{ "*_Citation_*" | translate }}</td>
<td><cite>{{ "Citation" | translate }}</cite></td>
</tr>
<tr>
<td>{{ "~~Strikethrough~~" | translate }}</td>
<td><del>{{ "Strikethrough" | translate }}</del></td>
</tr>
<tr>
<td>{{ "`Code`" | translate }}</td>
<td><code>{{ "Code" | translate }}</code></td>
</tr>
<tr>
<td>{{ "==Highlight==" | translate }}</td>
<td><mark>{{ "Highlight" | translate }}</mark></td>
</tr>
</tr>
<tr>
<td>{{ "++Superscript++" | translate }}</td>
<td><sup>{{ "Superscript" | translate }}</sup></td>
</tr>
<tr>
<td>{{ "--Subscript--" | translate }}</td>
<td><sub>{{ "Subscript" | translate }}</sub></td>
</tr>
<tr>
<td>{{ "Blank line" | translate }}</td>
<td>{{ "New paragraph" | translate }}</td>
</tr>
<tr>
<td>{{ "[title](URL)" | translate }}</td>
<td><a href="#">{{ "Hyperlink" | translate }}</a></td>
</tr>
<tr>
<td>{{ "![description](URL)" | translate }}</td>
<td>{{ "Image" | translate }}</td>
</tr>
<tr>
<td>{{ "- List of items" | translate }}</td>
<td><ul><li>{{ "List of items" | translate }}</li></ul></td>
</tr>
<tr>
<td>{{ "1. List of items" | translate }}</td>
<td><ol><li>{{ "List of items" | translate }}</li></ol></td>
</tr>
<tr>
<td>{{ "> Blockquote" | translate }}</td>
<td><blockquote>{{ "Blockquote" | translate }}</blockquote></td>
</tr>
<tr>
<td>{{ "< Aside" | translate }}</td>
<td><aside>{{ "Aside" | translate }}</aside></td>
</tr>
<tr>
<td>{{ ": Figure" | translate }}<br>{{ ":: Caption" | translate }}</td>
<td><figure>{{ "Figure" | translate }}<figcaption>{{ "Caption" | translate }}</figcaption></figure></td>
</tr>
<tr>
<td>{{ "````<br>Code block<br>````" | translate }}</td>
<td><pre><code>{{ "Code block" | translate }}</code></pre></td>
</tr>
<tr>
<td>{{ "A footnote label. [^label]" | translate }}<br><br>{{ "[^label]: The footnote." | translate }}</td>
<td>{{ "A footnote label." | translate }} <sup><a href="#">1</a></sup></td>
</tr>
</tbody>
</table>
{% endblock %}

10
admin/help/slugs.twig Normal file
View File

@ -0,0 +1,10 @@
{% extends "layouts" ~ DIR ~ "help.twig" %}
{% block title %}{{ "Slugs" | translate }}{% endblock %}
{% block content %}
<h1>{{ "Slugs" | translate }}</h1>
<p>
{{ "The slug is the URL-friendly identifying name for this post or page. You can enter the slug yourself or have it auto-generated when the post or page is created. A slug may contain only the letters a-z, the numbers 0-9, and hyphen-minus (&#8220;-&#8221;)." | translate }}
</p>
{% endblock %}

View File

@ -0,0 +1,114 @@
{% extends "layouts" ~ DIR ~ "help.twig" %}
{% block title %}{{ "Unicode Emoticons" | translate }}{% endblock %}
{% block content %}
<h1>{{ "Unicode Emoticons" | translate }}</h1>
<p>
{{ "You can have some emoticons converted to equivalent Unicode emoji when your content is displayed. Your original content is not modified, so you can turn this feature on and off at any time. The following conversions will occur:" | translate }}
</p>
<table>
<thead>
<tr>
<th>{{ "Emoticon" | translate }}</th>
<th>{{ "Emoji" | translate }}</th>
</tr>
</thead>
<tbody>
<tr>
<td>o:-)</td>
<td>{{ "o:-)" | emote }}</td></tr>
<tr>
<td>&gt;:-)</td>
<td>{{ "&gt;:-)" | emote }}</td></tr>
<tr>
<td>:-)</td>
<td>{{ ":-)" | emote }}</td>
</tr>
<tr>
<td>^_^</td>
<td>{{ "^_^" | emote }}</td>
</tr>
<tr>
<td>:-D</td>
<td>{{ ":-D" | emote }}</td>
</tr>
<tr>
<td>;-)</td>
<td>{{ ";-)" | emote }}</td>
</tr>
<tr>
<td>&lt;3</td>
<td>{{ "&lt;3" | emote }}</td>
</tr>
<tr>
<td>B-)</td>
<td>{{ "B-)" | emote }}</td>
</tr>
<tr>
<td>:-></td>
<td>{{ ":->" | emote }}</td>
</tr>
<tr>
<td>:-||</td>
<td>{{ ":-||" | emote }}</td>
</tr>
<tr>
<td>:-|</td>
<td>{{ ":-|" | emote }}</td>
</tr>
<tr>
<td>-_-</td>
<td>{{ "-_-" | emote }}</td>
</tr>
<tr>
<td>:-/</td>
<td>{{ ":-/" | emote }}</td>
</tr>
<tr>
<td>:-s</td>
<td>{{ ":-s" | emote }}</td>
</tr>
<tr>
<td>:-*</td>
<td>{{ ":-*" | emote }}</td>
</tr>
<tr>
<td>:-P</td>
<td>{{ ":-P" | emote }}</td>
</tr>
<tr>
<td>:-(</td>
<td>{{ ":-(" | emote }}</td>
</tr>
<tr>
<td>;_;</td>
<td>{{ ";_;" | emote }}</td>
</tr>
<tr>
<td>:-((</td>
<td>{{ ":-((" | emote }}</td>
</tr>
<tr>
<td>:-o</td>
<td>{{ ":-o" | emote }}</td>
</tr>
<tr>
<td>O_O</td>
<td>{{ "O_O" | emote }}</td>
</tr>
<tr>
<td>:-$</td>
<td>{{ ":-$" | emote }}</td>
</tr>
<tr>
<td>x_x</td>
<td>{{ "x_x" | emote }}</td>
</tr>
<tr>
<td>:-x</td>
<td>{{ ":-x" | emote }}</td>
</tr>
</tbody>
</table>
{% endblock %}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zm.75 4.75a.75.75 0 00-1.5 0v2.5h-2.5a.75.75 0 000 1.5h2.5v2.5a.75.75 0 001.5 0v-2.5h2.5a.75.75 0 000-1.5h-2.5v-2.5z"/></svg>

After

Width:  |  Height:  |  Size: 308 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M3.5 1.75a.25.25 0 01.25-.25h3a.75.75 0 000 1.5h.5a.75.75 0 000-1.5h2.086a.25.25 0 01.177.073l2.914 2.914a.25.25 0 01.073.177v8.586a.25.25 0 01-.25.25h-.5a.75.75 0 000 1.5h.5A1.75 1.75 0 0014 13.25V4.664c0-.464-.184-.909-.513-1.237L10.573.513A1.75 1.75 0 009.336 0H3.75A1.75 1.75 0 002 1.75v11.5c0 .649.353 1.214.874 1.515a.75.75 0 10.752-1.298.25.25 0 01-.126-.217V1.75zM8.75 3a.75.75 0 000 1.5h.5a.75.75 0 000-1.5h-.5zM6 5.25a.75.75 0 01.75-.75h.5a.75.75 0 010 1.5h-.5A.75.75 0 016 5.25zm2 1.5A.75.75 0 018.75 6h.5a.75.75 0 010 1.5h-.5A.75.75 0 018 6.75zm-1.25.75a.75.75 0 000 1.5h.5a.75.75 0 000-1.5h-.5zM8 9.75A.75.75 0 018.75 9h.5a.75.75 0 010 1.5h-.5A.75.75 0 018 9.75zm-.75.75a1.75 1.75 0 00-1.75 1.75v3c0 .414.336.75.75.75h2.5a.75.75 0 00.75-.75v-3a1.75 1.75 0 00-1.75-1.75h-.5zM7 12.25a.25.25 0 01.25-.25h.5a.25.25 0 01.25.25v2.25H7v-2.25z"/></svg>

After

Width:  |  Height:  |  Size: 969 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M7.563 2.069A.75.75 0 018 2.75v10.5a.75.75 0 01-1.238.57L3.472 11H1.75A1.75 1.75 0 010 9.25v-2.5C0 5.784.784 5 1.75 5h1.723l3.289-2.82a.75.75 0 01.801-.111zM6.5 4.38L4.238 6.319a.75.75 0 01-.488.181h-2a.25.25 0 00-.25.25v2.5c0 .138.112.25.25.25h2a.75.75 0 01.488.18L6.5 11.62V4.38zm6.096-2.038a.75.75 0 011.06 0 8 8 0 010 11.314.75.75 0 01-1.06-1.06 6.5 6.5 0 000-9.193.75.75 0 010-1.06v-.001zm-1.06 2.121a.75.75 0 10-1.061 1.061 3.5 3.5 0 010 4.95.75.75 0 101.06 1.06 5 5 0 000-7.07l.001-.001z"/></svg>

After

Width:  |  Height:  |  Size: 615 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M4 2a1 1 0 00-1 1v10a1 1 0 001 1h5.5a3.5 3.5 0 001.852-6.47A3.5 3.5 0 008.5 2H4zm4.5 5a1.5 1.5 0 100-3H5v3h3.5zM5 9v3h4.5a1.5 1.5 0 000-3H5z"/></svg>

After

Width:  |  Height:  |  Size: 261 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"/></svg>

After

Width:  |  Height:  |  Size: 248 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"/></svg>

After

Width:  |  Height:  |  Size: 342 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M4.72 3.22a.75.75 0 011.06 1.06L2.06 8l3.72 3.72a.75.75 0 11-1.06 1.06L.47 8.53a.75.75 0 010-1.06l4.25-4.25zm6.56 0a.75.75 0 10-1.06 1.06L13.94 8l-3.72 3.72a.75.75 0 101.06 1.06l4.25-4.25a.75.75 0 000-1.06l-4.25-4.25z"/></svg>

After

Width:  |  Height:  |  Size: 338 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M6.5 1.75a.25.25 0 01.25-.25h2.5a.25.25 0 01.25.25V3h-3V1.75zm4.5 0V3h2.25a.75.75 0 010 1.5H2.75a.75.75 0 010-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75zM4.496 6.675a.75.75 0 10-1.492.15l.66 6.6A1.75 1.75 0 005.405 15h5.19c.9 0 1.652-.681 1.741-1.576l.66-6.6a.75.75 0 00-1.492-.149l-.66 6.6a.25.25 0 01-.249.225h-5.19a.25.25 0 01-.249-.225l-.66-6.6z"/></svg>

After

Width:  |  Height:  |  Size: 489 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1.75 2.5a.25.25 0 00-.25.25v10.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25v-8.5a.25.25 0 00-.25-.25H7.5c-.55 0-1.07-.26-1.4-.7l-.9-1.2a.25.25 0 00-.2-.1H1.75zM0 2.75C0 1.784.784 1 1.75 1H5c.55 0 1.07.26 1.4.7l.9 1.2a.25.25 0 00.2.1h6.75c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0114.25 15H1.75A1.75 1.75 0 010 13.25V2.75z"/></svg>

After

Width:  |  Height:  |  Size: 446 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5 0v6.69L4.78 5.97a.75.75 0 00-1.06 1.06l3.75 3.75zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"/></svg>

After

Width:  |  Height:  |  Size: 315 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M11.013 1.427a1.75 1.75 0 012.474 0l1.086 1.086a1.75 1.75 0 010 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 01-.927-.928l.929-3.25a1.75 1.75 0 01.445-.758l8.61-8.61zm1.414 1.06a.25.25 0 00-.354 0L10.811 3.75l1.439 1.44 1.263-1.263a.25.25 0 000-.354l-1.086-1.086zM11.189 6.25L9.75 4.81l-6.286 6.287a.25.25 0 00-.064.108l-.558 1.953 1.953-.558a.249.249 0 00.108-.064l6.286-6.286z"/></svg>

After

Width:  |  Height:  |  Size: 515 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M3.404 12.596a6.5 6.5 0 119.192-9.192 6.5 6.5 0 01-9.192 9.192zM2.344 2.343a8 8 0 1011.313 11.314A8 8 0 002.343 2.343zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"/></svg>

After

Width:  |  Height:  |  Size: 404 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M3.75 1.5a.25.25 0 00-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 00.25-.25V6h-2.75A1.75 1.75 0 019 4.25V1.5H3.75zm6.75.062V4.25c0 .138.112.25.25.25h2.688a.252.252 0 00-.011-.013l-2.914-2.914a.272.272 0 00-.013-.011zM2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0113.25 16h-9.5A1.75 1.75 0 012 14.25V1.75z"/></svg>

After

Width:  |  Height:  |  Size: 500 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M3.75 2a.75.75 0 01.75.75V7h7V2.75a.75.75 0 011.5 0v10.5a.75.75 0 01-1.5 0V8.5h-7v4.75a.75.75 0 01-1.5 0V2.75A.75.75 0 013.75 2z"/></svg>

After

Width:  |  Height:  |  Size: 249 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zM6.92 6.085c.081-.16.19-.299.34-.398.145-.097.371-.187.74-.187.28 0 .553.087.738.225A.613.613 0 019 6.25c0 .177-.04.264-.077.318a.956.956 0 01-.277.245c-.076.051-.158.1-.258.161l-.007.004a7.728 7.728 0 00-.313.195 2.416 2.416 0 00-.692.661.75.75 0 001.248.832.956.956 0 01.276-.245 6.3 6.3 0 01.26-.16l.006-.004c.093-.057.204-.123.313-.195.222-.149.487-.355.692-.662.214-.32.329-.702.329-1.15 0-.76-.36-1.348-.863-1.725A2.76 2.76 0 008 4c-.631 0-1.155.16-1.572.438-.413.276-.68.638-.849.977a.75.75 0 101.342.67z"/></svg>

After

Width:  |  Height:  |  Size: 733 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M11.134 1.535C9.722 2.562 8.16 4.057 6.889 5.312 5.8 6.387 5.041 7.401 4.575 8.294a3.745 3.745 0 00-3.227 1.054c-.43.431-.69 1.066-.86 1.657a11.982 11.982 0 00-.358 1.914A21.263 21.263 0 000 15.203v.054l.75-.007-.007.75h.054a14.404 14.404 0 00.654-.012 21.243 21.243 0 001.63-.118c.62-.07 1.3-.18 1.914-.357.592-.17 1.226-.43 1.657-.861a3.745 3.745 0 001.055-3.217c.908-.461 1.942-1.216 3.04-2.3 1.279-1.262 2.764-2.825 3.775-4.249.501-.706.923-1.428 1.125-2.096.2-.659.235-1.469-.368-2.07-.606-.607-1.42-.55-2.069-.34-.66.213-1.376.646-2.076 1.155zm-3.95 8.48a3.76 3.76 0 00-1.19-1.192 9.758 9.758 0 011.161-1.607l1.658 1.658a9.853 9.853 0 01-1.63 1.142zM.742 16l.007-.75-.75.008A.75.75 0 00.743 16zM12.016 2.749c-1.224.89-2.605 2.189-3.822 3.384l1.718 1.718c1.21-1.205 2.51-2.597 3.387-3.833.47-.662.78-1.227.912-1.662.134-.444.032-.551.009-.575h-.001V1.78c-.014-.014-.112-.113-.548.027-.432.14-.995.462-1.655.942zM1.62 13.089a19.56 19.56 0 00-.104 1.395 19.55 19.55 0 001.396-.104 10.528 10.528 0 001.668-.309c.526-.151.856-.325 1.011-.48a2.25 2.25 0 00-3.182-3.182c-.155.155-.329.485-.48 1.01a10.515 10.515 0 00-.309 1.67z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1.75 2.5a.25.25 0 00-.25.25v10.5c0 .138.112.25.25.25h.94a.76.76 0 01.03-.03l6.077-6.078a1.75 1.75 0 012.412-.06L14.5 10.31V2.75a.25.25 0 00-.25-.25H1.75zm12.5 11H4.81l5.048-5.047a.25.25 0 01.344-.009l4.298 3.889v.917a.25.25 0 01-.25.25zm1.75-.25V2.75A1.75 1.75 0 0014.25 1H1.75A1.75 1.75 0 000 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0016 13.25zM5.5 6a.5.5 0 11-1 0 .5.5 0 011 0zM7 6a2 2 0 11-4 0 2 2 0 014 0z"/></svg>

After

Width:  |  Height:  |  Size: 542 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"/></svg>

After

Width:  |  Height:  |  Size: 346 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M6 2.75A.75.75 0 016.75 2h6.5a.75.75 0 010 1.5h-2.505l-3.858 9H9.25a.75.75 0 010 1.5h-6.5a.75.75 0 010-1.5h2.505l3.858-9H6.75A.75.75 0 016 2.75z"/></svg>

After

Width:  |  Height:  |  Size: 265 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"/></svg>

After

Width:  |  Height:  |  Size: 472 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 208 128"><path d="M193 128H15a15 15 0 0 1-15-15V15A15 15 0 0 1 15 0h178a15 15 0 0 1 15 15v98a15 15 0 0 1-15 15zM50 98V59l20 25 20-25v39h20V30H90L70 55 50 30H30v68zm134-34h-20V30h-20v34h-20l30 35z"/></svg>

After

Width:  |  Height:  |  Size: 257 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"/></svg>

After

Width:  |  Height:  |  Size: 150 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M4.25 7.25a.75.75 0 000 1.5h7.5a.75.75 0 000-1.5h-7.5z"/><path fill-rule="evenodd" d="M16 8A8 8 0 110 8a8 8 0 0116 0zm-1.5 0a6.5 6.5 0 11-13 0 6.5 6.5 0 0113 0z"/></svg>

After

Width:  |  Height:  |  Size: 261 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"/></svg>

After

Width:  |  Height:  |  Size: 249 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M12.78 6.22a.75.75 0 010 1.06l-4.25 4.25a.75.75 0 01-1.06 0L3.22 7.28a.75.75 0 011.06-1.06L8 9.94l3.72-3.72a.75.75 0 011.06 0z"/></svg>

After

Width:  |  Height:  |  Size: 247 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1.75 2.5a.25.25 0 00-.25.25v1.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25v-1.5a.25.25 0 00-.25-.25H1.75zM0 2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75v1.5A1.75 1.75 0 0114.25 6H1.75A1.75 1.75 0 010 4.25v-1.5zM1.75 7a.75.75 0 01.75.75v5.5c0 .138.112.25.25.25h10.5a.25.25 0 00.25-.25v-5.5a.75.75 0 111.5 0v5.5A1.75 1.75 0 0113.25 15H2.75A1.75 1.75 0 011 13.25v-5.5A.75.75 0 011.75 7zm4.5 1a.75.75 0 000 1.5h3.5a.75.75 0 100-1.5h-3.5z"/></svg>

After

Width:  |  Height:  |  Size: 568 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M7.581 3.25c-2.036 0-2.778 1.082-2.778 1.786 0 .055.002.107.006.157a.75.75 0 01-1.496.114 3.56 3.56 0 01-.01-.271c0-1.832 1.75-3.286 4.278-3.286 1.418 0 2.721.58 3.514 1.093a.75.75 0 11-.814 1.26c-.64-.414-1.662-.853-2.7-.853zm3.474 5.25h3.195a.75.75 0 000-1.5H1.75a.75.75 0 000 1.5h6.018c.835.187 1.503.464 1.951.81.439.34.647.725.647 1.197 0 .428-.159.895-.594 1.267-.444.38-1.254.726-2.676.726-1.373 0-2.38-.493-2.86-.956a.75.75 0 00-1.042 1.079C3.992 13.393 5.39 14 7.096 14c1.652 0 2.852-.403 3.65-1.085a3.134 3.134 0 001.12-2.408 2.85 2.85 0 00-.811-2.007z"/></svg>

After

Width:  |  Height:  |  Size: 683 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM0 8a8 8 0 1116 0A8 8 0 010 8zm11.78-1.72a.75.75 0 00-1.06-1.06L6.75 9.19 5.28 7.72a.75.75 0 00-1.06 1.06l2 2a.75.75 0 001.06 0l4.5-4.5z"/></svg>

After

Width:  |  Height:  |  Size: 299 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.53 1.22a.75.75 0 00-1.06 0L3.72 4.97a.75.75 0 001.06 1.06l2.47-2.47v6.69a.75.75 0 001.5 0V3.56l2.47 2.47a.75.75 0 101.06-1.06L8.53 1.22zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"/></svg>

After

Width:  |  Height:  |  Size: 312 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1.75 3.5a.25.25 0 00-.25.25v8.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25v-8.5a.25.25 0 00-.25-.25H1.75zM0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0114.25 14H1.75A1.75 1.75 0 010 12.25v-8.5z"/><path d="M6 10.559V5.442a.25.25 0 01.379-.215l4.264 2.559a.25.25 0 010 .428l-4.264 2.559A.25.25 0 016 10.559z"/></svg>

After

Width:  |  Height:  |  Size: 459 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1.679 7.932c.412-.621 1.242-1.75 2.366-2.717C5.175 4.242 6.527 3.5 8 3.5c1.473 0 2.824.742 3.955 1.715 1.124.967 1.954 2.096 2.366 2.717a.119.119 0 010 .136c-.412.621-1.242 1.75-2.366 2.717C10.825 11.758 9.473 12.5 8 12.5c-1.473 0-2.824-.742-3.955-1.715C2.92 9.818 2.09 8.69 1.679 8.068a.119.119 0 010-.136zM8 2c-1.981 0-3.67.992-4.933 2.078C1.797 5.169.88 6.423.43 7.1a1.619 1.619 0 000 1.798c.45.678 1.367 1.932 2.637 3.024C4.329 13.008 6.019 14 8 14c1.981 0 3.67-.992 4.933-2.078 1.27-1.091 2.187-2.345 2.637-3.023a1.619 1.619 0 000-1.798c-.45-.678-1.367-1.932-2.637-3.023C11.671 2.992 9.981 2 8 2zm0 8a2 2 0 100-4 2 2 0 000 4z"/></svg>

After

Width:  |  Height:  |  Size: 752 B

20
admin/index.php Normal file
View File

@ -0,0 +1,20 @@
<?php
define('ADMIN', true);
require_once dirname(__FILE__, 2).
DIRECTORY_SEPARATOR.
"includes".
DIRECTORY_SEPARATOR.
"common.php";
# Prepare the controller.
$admin = AdminController::current();
# Parse the route.
$route = Route::current($admin);
# Respond to the request.
$route->init();
$trigger->call("end");
ob_end_flush();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="{{ site.locale | lang_base }}" dir="{{ site.locale | text_direction }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale = 1.0">
<title>
{%- if pagination is not empty and pagination.page > 1 -%}
{{- "Page %d" | translate | format(pagination.page) -}}
{{- " | " -}}
{%- endif -%}
{%- block title -%}
{{- title | fix -}}
{%- endblock -%}
{{- " | " -}}
{{- site.name | fix -}}
</title>
<link rel="icon" sizes="32x32" type="image/x-icon" href="{{ site.chyrp_url }}/favicon.ico">
<link rel="icon" sizes="any" type="image/svg+xml" href="{{ site.chyrp_url }}/favicon.svg">
<link rel="mask-icon" href="{{ site.chyrp_url }}/favicon.svg" color="#4f4f4f">
<link rel="stylesheet" href="{{ site.chyrp_url }}/admin/stylesheets/all.css" type="text/css" media="all">
{% if site.monospace_font %}
<link rel="stylesheet" href="{{ site.chyrp_url }}/admin/stylesheets/monospace.css" type="text/css" media="all">
{% endif %}
{{ javascripts() }}
{{ trigger.call("admin_head") }}
</head>
<body>
<div id="header" role="banner">
<h1><a href="{{ url('/', 'MainController') }}">{{ site.name }}</a></h1>
<ul id="navigation" role="menubar">
{% block navigation %}
{% for action, attributes in navigation %}
{% if attributes.children is not empty %}
<li role="presentation" class="{{ action }}{{ attributes.selected | selected(true) }}">
<a role="menuitem" href="{{ url(action) }}">{{ attributes.title }}</a>
</li>
{% endif %}
{% endfor %}
{% endblock %}
</ul>
</div>
<ul id="sub_nav" role="menubar" class="{{ route.action }}_nav ">
{% block subnav %}
{% for parent in navigation %}
{% if parent.selected and parent.children is not empty %}
{% for child, attributes in parent.children %}
<li role="presentation" class="{{ child }}{{ attributes.selected | selected(true) }}">
<a role="menuitem" href="{{ url(child) }}">{{ attributes.title }}</a>
</li>
{% endfor %}
{% endif %}
{% endfor %}
{% endblock %}
</ul>
<div id="content" role="main">
{% for notice in flash.notices %}
<div role="alert" class="flash notice">{{ notice }}</div>
{% endfor %}
{% for warning in flash.warnings %}
<div role="alert" class="flash warning">{{ warning }}</div>
{% endfor %}
{% for message in flash.messages %}
<div role="alert" class="flash message">{{ message }}</div>
{% endfor %}
{% block content %}{% endblock %}
</div>
</body>
<!-- Queries: {{ sql_queries }} | Load Time: {{ theme.load_time() }} -->
{% if debug %}
<!--
{% for caller in sql_debug %}
{{ caller.query | replace({"\t": " "}) | fix }}
{{ "Called from %s on line %d at %s seconds." | translate | format(caller.file, caller.line, caller.time) }}
{% endfor %}
-->
{% endif %}
</html>

321
admin/layouts/help.twig Normal file
View File

@ -0,0 +1,321 @@
<!DOCTYPE html>
<html lang="{{ site.locale | lang_base }}" dir="{{ site.locale | text_direction }}">
<head>
<meta charset="UTF-8">
<title>{% block title %}{{ title | fix }}{% endblock %}{{ " | "}}{{ site.name | fix }}</title>
<style type="text/css" nonce="{{ stylesheets_nonce() }}">
@font-face {
font-family: 'Open Sans webfont';
src: url('{{ site.chyrp_url }}/fonts/OpenSans-Regular.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Open Sans webfont';
src: url('{{ site.chyrp_url }}/fonts/OpenSans-SemiBold.woff') format('woff');
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: 'Open Sans webfont';
src: url('{{ site.chyrp_url }}/fonts/OpenSans-Bold.woff') format('woff');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'Open Sans webfont';
src: url('{{ site.chyrp_url }}/fonts/OpenSans-Italic.woff') format('woff');
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: 'Open Sans webfont';
src: url('{{ site.chyrp_url }}/fonts/OpenSans-SemiBoldItalic.woff') format('woff');
font-weight: 600;
font-style: italic;
}
@font-face {
font-family: 'Open Sans webfont';
src: url('{{ site.chyrp_url }}/fonts/OpenSans-BoldItalic.woff') format('woff');
font-weight: bold;
font-style: italic;
}
@font-face {
font-family: 'Cousine webfont';
src: url('{{ site.chyrp_url }}/fonts/Cousine-Regular.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Cousine webfont';
src: url('{{ site.chyrp_url }}/fonts/Cousine-Bold.woff') format('woff');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'Cousine webfont';
src: url('{{ site.chyrp_url }}/fonts/Cousine-Italic.woff') format('woff');
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: 'Cousine webfont';
src: url('{{ site.chyrp_url }}/fonts/Cousine-BoldItalic.woff') format('woff');
font-weight: bold;
font-style: italic;
}
html, body, div, dl, dt, dd, ul, ol, li, p,
h1, h2, h3, h4, h5, h6, img, pre, code,
form, fieldset, input, select, textarea,
table, tbody, tr, th, td, legend, caption,
blockquote, aside, figure, figcaption {
margin: 0em;
padding: 0em;
border: 0em;
}
html {
font-size: 16px;
}
table {
border-collapse: collapse;
border-spacing: 0em;
}
*::selection {
color: #1f1f23;
background-color: #ffdd00;
}
body {
font-family: "Open Sans webfont", sans-serif;
font-size: 1rem;
line-height: 1.5;
color: #1f1f23;
tab-size: 4;
margin: 2rem;
max-width: 1024px;
background-color: #ffffff;
}
h1, h2, h3, h4, h5, h6 {
font-weight: bold;
font-style: normal;
font-variant: normal;
margin: 1rem 0em;
padding: 0rem;
}
h1 {
font-size: 2em;
}
h2 {
font-size: 1.5em;
}
h3, h4, h5, h6 {
font-size: 1em;
font-weight: 600;
}
sup, sub {
line-height: 1;
font-size: 0.75em;
}
em, dfn, cite, var {
font: inherit;
font-style: italic;
}
figure, blockquote {
margin-bottom: 1rem;
font: inherit;
font-size: 1rem;
}
blockquote {
font-style: italic;
}
figcaption {
font-size: 0.75em;
margin-top: 0rem;
}
blockquote em {
font-style: normal;
}
strong {
font: inherit;
font-weight: bold;
}
address {
font: inherit;
}
small {
font-size: 0.75em;
}
mark {
color: #1f1f23;
background-color: #ffdd00;
}
del {
font: inherit;
text-decoration: line-through;
}
ins {
font: inherit;
color: #1f1f23;
background-color: #daf1d0;
}
p {
margin-bottom: 1rem;
}
ul {
list-style: disc outside;
margin: 1rem 0rem;
margin-inline-start: 2rem;
padding: 0rem;
}
ul ul {
list-style-type: circle;
}
ol {
list-style: decimal outside;
margin: 1rem 0rem;
margin-inline-start: 2rem;
padding: 0rem;
}
ol ol {
list-style-type: lower-roman;
}
dl {
list-style: none;
}
dl dt {
font-weight: bold;
}
dl dd {
margin-left: 1rem;
}
hr {
border: none;
clear: both;
border-top: 1px solid #dddddd;
margin: 2rem 0rem;
}
aside {
margin-bottom: 1rem;
padding: 0.5rem 1rem;
border: 1px solid #e5d7a1;
border-radius: 0.25em;
background-color: #fffde6;
}
pre {
font-family: "Cousine webfont", monospace;
font-size: 0.85em;
background-color: #efefef;
margin: 1rem 0rem;
padding: 1rem;
overflow-x: auto;
white-space: pre;
}
code {
font-family: "Cousine webfont", monospace;
font-size: 0.85em;
background-color: #efefef;
padding: 0px 2px;
border: 1px solid #cfcfcf;
vertical-align: bottom;
white-space: break-spaces;
}
pre > code {
font-size: 0.85rem;
display: block;
border: none;
padding: 0px;
white-space: inherit;
}
img {
max-width: 100%;
}
table {
width: 100%;
margin: 0rem 0rem 1rem 0rem;
overflow-x: scroll;
}
table th {
background-color: #dfdfdf;
padding: 0.5rem;
font: inherit;
font-weight: 600;
text-align: center;
vertical-align: middle;
border: 1px solid #afafaf;
}
table td {
padding: 0.5rem;
font: inherit;
vertical-align: middle;
border: 1px solid #cfcfcf;
}
table tbody tr:nth-child(even) td {
background-color: #fbfbfb;
}
table tbody tr td.emblem {
text-align: center;
}
td > *:first-child {
margin-top: 0rem;
}
td > *:last-child {
margin-bottom: 0rem;
}
a:link,
a:visited {
color: #1f1f23;
text-decoration: underline;
text-underline-offset: 0.125em;
}
a:focus {
outline: #ff7f00 dashed 2px;
outline-offset: 1px;
}
a:hover,
a:focus,
a:active {
color: #1e57ba;
text-decoration: underline;
text-underline-offset: 0.125em;
}
img.emblem,
a.emblem > img {
display: inline;
position: relative;
top: 0.1875rem;
height: 1rem;
}
@media screen and (min-width: 1080px) {
body {
margin: 2rem auto;
}
}
@media (prefers-color-scheme: dark) {
body {
background-color: #efefef;
}
hr {
border-color: #afafaf;
}
aside {
border-color: #afafaf;
}
pre {
background-color: #dfdfdf;
}
code {
background-color: #dfdfdf;
border-color: #afafaf;
}
table tbody tr td {
border-color: #afafaf;
}
table tbody tr:nth-child(even) td {
background-color: #dfdfdf;
}
}
</style>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,103 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Content Settings" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Content Settings" | translate }}</h2>
<form id="content_settings" class="split" action="{{ url('content_settings') }}" method="post" accept-charset="utf-8">
<fieldset role="presentation">
<p>
<label for="posts_per_page">{{ "Posts Per Blog Page" | translate }}</label>
{#- -#}
<input class="text digits" type="number" min="1" name="posts_per_page" value="{{ site.posts_per_page | fix(true) }}" size="2" id="posts_per_page">
</p>
<p>
<label for="feed_items">{{ "Posts in Feed" | translate }}</label>
{#- -#}
<input class="text digits" type="number" min="1" name="feed_items" value="{{ site.feed_items | fix(true) }}" size="2" id="feed_items">
</p>
<p>
<label for="admin_per_page">{{ "Items Per Admin Page" | translate }}</label>
{#- -#}
<input class="text digits" type="number" min="1" name="admin_per_page" value="{{ site.admin_per_page | fix(true) }}" size="2" id="admin_per_page">
</p>
<p>
<label for="uploads_path">{{ "Uploads Path" | translate }}</label>
{#- -#}
<input class="text" type="text" name="uploads_path" value="{{ site.uploads_path | fix(true, true) }}" id="uploads_path">
<small>
{{ "The directory to which files are uploaded, relative to your installation directory." | translate }}
</small>
</p>
<p>
<label for="uploads_limit">{{ "Upload Size Limit" | translate }}</label>
{#- -#}
<input class="text digits" type="number" min="1" name="uploads_limit" value="{{ site.uploads_limit | fix(true) }}" size="3" id="uploads_limit">
<span class="sub">{{ "(Megabytes)" | translate }}</span>
</p>
<p>
<label for="feed_format">{{ "Feed Format" | translate }}</label>
{#- -#}
<select name="feed_format" id="feed_format">
{% for feed_format in feed_formats %}
<option value="{{ feed_format.class }}"{{ feed_format.class | selected(site.feed_format) }}>
{{ feed_format.name }}
</option>
{% endfor %}
</select>
</p>
<p>
<label for="search_pages">
{{ "Search Pages" | translate }}
</label>
{#- -#}
<input class="checkbox" type="checkbox" name="search_pages" id="search_pages"{{ site.search_pages | checked }}>
<small>
{{ "Include pages in search results." | translate }}
</small>
</p>
<p>
<label for="send_pingbacks">
{{ "Webmentions" | translate }}
</label>
{#- -#}
<input class="checkbox" type="checkbox" name="send_pingbacks" id="send_pingbacks"{{ site.send_pingbacks | checked }}>
<small>
{{ "Send and receive notifications when URLs are mentioned." | translate }}
</small>
</p>
<p>
<label for="enable_emoji">
{{ "Unicode Emoticons" | translate }}
<a href="{{ url('help/id/unicode_emoticons') }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
</label>
{#- -#}
<input class="checkbox" type="checkbox" name="enable_emoji" id="enable_emoji"{{ site.enable_emoji | checked }}>
<small>
{{ "Display emoticons as Unicode emoji." | translate }}
</small>
</p>
<p>
<label for="enable_markdown">
{{ "Markdown" | translate }}
<a href="{{ url('help/id/markdown') }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
</label>
{#- -#}
<input class="checkbox" type="checkbox" name="enable_markdown" id="enable_markdown"{{ site.enable_markdown | checked }}>
<small>
{{ "Compose blog content using Markdown text formatting." | translate }}
</small>
</p>
<p class="buttons">
<button type="submit" class="yay">
{{ "Update" | translate }}
</button>
</p>
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,63 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Delete Group?" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Delete Group?" | translate }}</h2>
<form class="delete split" action="{{ url('destroy_group') }}" method="post" accept-charset="UTF-8">
<fieldset role="presentation">
<p>
{{ "&#8220;%s&#8221; will be permanently deleted." | translate | format(group.name) }}
</p>
{% if group.users is not empty and groups is not empty %}
<p>
<label for="move_group">{{ "Move members to:" | translate }}</label>
{#- -#}
<select name="move_group" id="move_group">
{% for group in groups %}
<option value="{{ group.id }}"{{ group.id | selected(site.default_group) }}>
{{ group.name }}
</option>
{% endfor %}
</select>
</p>
{% endif %}
{% if group.id == site.default_group and groups is not empty %}
<p>
<label for="default_group">{{ "New default group:" | translate }}</label>
{#- -#}
<select name="default_group" id="default_group">
{% for group in groups %}
<option value="{{ group.id }}"{{ group.id | selected(site.guest_group) }}>
{{ group.name }}
</option>
{% endfor %}
</select>
</p>
{% endif %}
{% if group.id == site.guest_group and groups is not empty %}
<p>
<label for="guest_group">{{ "New &#8220;guest&#8221; group:" | translate }}</label>
{#- -#}
<select name="guest_group" id="guest_group">
{% for group in groups %}
<option value="{{ group.id }}"{{ group.id | selected(site.default_group) }}>
{{ group.name }}
</option>
{% endfor %}
</select>
</p>
{% endif %}
<div class="confirmation">
<button name="destroy" value="indubitably" class="boo">
{{ "Delete!" | translate }}
</button>
<button name="destroy" value="undecidedly" type="submit" class="yay">
{{ "Cancel" | translate }}
</button>
</div>
<input type="hidden" name="id" value="{{ group.id }}" id="id">
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,31 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Delete Page?" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Delete Page?" | translate }}</h2>
<form class="delete split" action="{{ url('destroy_page') }}" method="post" accept-charset="UTF-8">
<fieldset role="presentation">
<p>
{{ "Are you sure you want to delete &#8220;%s&#8221;?" | translate | format(page.title | striptags | oneof("[Untitled]" | translate)) }}
</p>
{% if page.children is not empty %}
<p>
<label for="destroy_children">{{ "Delete children?" | translate }}</label>
{#- -#}
<input type="checkbox" name="destroy_children" id="destroy_children">
</p>
{% endif %}
<div class="confirmation">
<button name="destroy" value="indubitably" class="boo">
{{ "Delete!" | translate }}
</button>
<button name="destroy" value="undecidedly" type="submit" class="yay">
{{ "Cancel" | translate }}
</button>
</div>
<input type="hidden" name="id" value="{{ page.id }}" id="id">
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,24 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Delete Post?" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Delete Post?" | translate }}</h2>
<form class="delete" action="{{ url('destroy_post') }}" method="post" accept-charset="UTF-8">
<fieldset role="presentation">
<p>
{{ "&#8220;%s&#8221; will be permanently deleted." | translate | format(post.title() | striptags | oneof("[Untitled]" | translate)) }}
</p>
<div class="confirmation">
<button name="destroy" value="indubitably" class="boo">
{{ "Delete!" | translate }}
</button>
<button name="destroy" value="undecidedly" type="submit" class="yay">
{{ "Cancel" | translate }}
</button>
</div>
<input type="hidden" name="id" value="{{ post.id }}" id="id">
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,24 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Delete Upload?" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Delete Upload?" | translate }}</h2>
<form class="delete" action="{{ url('destroy_upload') }}" method="post" accept-charset="UTF-8">
<fieldset role="presentation">
<p>
{{ "&#8220;%s&#8221; will be permanently deleted." | translate | format(filename | fix) }}
</p>
<div class="confirmation">
<button name="destroy" value="indubitably" class="boo">
{{ "Delete!" | translate }}
</button>
<button name="destroy" value="undecidedly" type="submit" class="yay">
{{ "Cancel" | translate }}
</button>
</div>
<input type="hidden" name="file" value="{{ filename | fix(true) }}" id="id">
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,57 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Delete User?" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Delete User?" | translate }}</h2>
<form class="delete split" action="{{ url('destroy_user') }}" method="post" accept-charset="UTF-8">
<fieldset role="presentation">
<p>
{{ "&#8220;%s&#8221; will be permanently deleted." | translate | format(user.login) }}
</p>
{% if user.posts is not empty %}
<p>
<label for="move_posts">{{ "Attribute posts to:" | translate }}</label>
{#- -#}
<select name="move_posts" id="move_posts">
<option value="0">{{ "[Delete Posts]" | translate }}</option>
{% if users is not empty %}
{% for user in users %}
<option value="{{ user.id }}">
{{ user.full_name is not empty ? user.full_name ~ " (" ~ user.login ~ ")" : user.login }}
</option>
{% endfor %}
{% endif %}
</select>
</p>
{% endif %}
{% if user.pages is not empty %}
<p>
<label for="move_pages">{{ "Attribute pages to:" | translate }}</label>
{#- -#}
<select name="move_pages" id="move_pages">
<option value="0">{{ "[Delete Pages]" | translate }}</option>
{% if users is not empty %}
{% for user in users %}
<option value="{{ user.id }}">
{{ user.full_name is not empty ? user.full_name ~ " (" ~ user.login ~ ")" : user.login }}
</option>
{% endfor %}
{% endif %}
</select>
</p>
{% endif %}
{{ trigger.call("delete_user_form") }}
<div class="confirmation">
<button name="destroy" value="indubitably" class="boo">
{{ "Delete!" | translate }}
</button>
<button name="destroy" value="undecidedly" type="submit" class="yay">
{{ "Cancel" | translate }}
</button>
</div>
<input type="hidden" name="id" value="{{ user.id }}" id="id">
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,34 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Edit Group" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Editing Group &#8220;%s&#8221;" | translate | format(group.name) }}</h2>
<form id="group_edit" class="split" action="{{ url('update_group') }}" method="post" accept-charset="UTF-8" data-toggler="group_toggler"{% if group.id == visitor.group.id %} data-confirm="{{'Are you sure you want to make these changes to your group?' | translate | fix(true) }}"{% endif %}>
<fieldset role="presentation">
<p>
<label for="name">{{ "Name" | translate }}</label>
{#- -#}
<input class="text" type="text" name="name" value="{{ group.name | fix(true, true) }}" id="name" maxlength="100" required>
</p>
<h3>{{ "Permissions" | translate }}</h3>
<p class="toggler" id="group_toggler">
</p>
<hr>
{% for permission in permissions %}
<p>
<label for="permission_{{ permission.id }}">{{ permission.name }}{# translation is done in Group #}</label>
{#- -#}
<input class="checkbox" type="checkbox" name="permissions[{{ permission.id }}]" id="permission_{{ permission.id }}"{{ group.can(permission.id) | checked }}>
</p>
{% endfor %}
<p class="buttons">
<button type="submit" class="yay">
{{ "Update" | translate }}
</button>
</p>
<input type="hidden" name="id" value="{{ group.id }}" id="id">
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Edit Page" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Editing &#8220;%s&#8221;" | translate | format(page.title | striptags | oneof("[Untitled]" | translate)) }}</h2>
<form id="edit_form" action="{{ url('update_page') }}" method="post" accept-charset="UTF-8" enctype="multipart/form-data">
<fieldset role="presentation">
{% include "partials" ~ DIR ~ "page_fields.twig" %}
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
<input type="hidden" name="id" value="{{ page.id }}" id="page_id">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Edit Post" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Editing &#8220;%s&#8221;" | translate | format(post.title() | striptags | oneof("[Untitled]" | translate)) }}</h2>
<form id="edit_form" action="{{ url('update_post') }}" method="post" accept-charset="UTF-8" enctype="multipart/form-data">
<fieldset role="presentation">
{% include "partials" ~ DIR ~ "post_fields.twig" %}
<input type="hidden" name="id" value="{{ post.id }}" id="post_id">
<input type="hidden" name="feather" value="{{ post.feather | fix(true) }}" id="feather">
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,73 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Edit User" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Editing User &#8220;%s&#8221;" | translate | format(user.login) }}</h2>
<form id="user_edit" class="split" action="{{ url('update_user') }}" method="post" accept-charset="UTF-8"{% if user.id == visitor.id %} data-confirm="{{'Are you sure you want to make these changes to your account?' | translate | fix(true) }}"{% endif %}>
<fieldset role="presentation">
<p>
<label for="login">{{ "Login" | translate }}</label>
{#- -#}
<input class="text" type="text" name="login" value="{{ user.login | fix(true, true) }}" id="login" maxlength="64" required>
</p>
<p>
<label for="email">{{ "Email" | translate }}</label>
{#- -#}
<input class="text" type="email" name="email" value="{{ user.email | fix(true, true) }}" id="email" maxlength="128" required>
</p>
<p>
<label for="group">{{ "Group" | translate }}</label>
{#- -#}
<select name="group" id="group">
{% for group in groups %}
<option value="{{ group.id }}"{{ group.id | selected(user.group_id) }}>
{{ group.name }}
</option>
{% endfor %}
</select>
</p>
<p>
<label for="new_password1">{{ "New Password?" | translate }}</label>
{#- -#}
<input class="text" type="password" name="new_password1" value="" id="new_password1" maxlength="128">
</p>
<p>
<label for="new_password2">{{ "Confirm" | translate }}</label>
{#- -#}
<input class="text" type="password" name="new_password2" value="" id="new_password2" maxlength="128">
</p>
<p>
<label for="full_name">
{{ "Full Name" | translate }}
<span class="sub">{{ "(optional)" | translate }}</span>
</label>
{#- -#}
<input class="text" type="text" name="full_name" value="{{ user.full_name | fix(true, true) }}" id="full_name" maxlength="250">
</p>
<p>
<label for="website">
{{ "Website" | translate }}
<span class="sub">{{ "(optional)" | translate }}</span>
</label>
{#- -#}
<input class="text" type="url" name="website" value="{{ user.website | fix(true, true) }}" id="website" maxlength="128">
</p>
{% if site.email_activation %}
<p>
<label for="activated">{{ "Activated?" | translate }}</label>
{#- -#}
<input class="checkbox" type="checkbox" name="activated" id="activated"{{ user.approved | checked }}>
</p>
{% endif %}
{{ trigger.call("edit_user_fields", user) }}
<p class="buttons">
<button type="submit" class="yay">
{{ "Update" | translate }}
</button>
</p>
<input type="hidden" name="id" value="{{ user.id }}" id="id">
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

108
admin/pages/export.twig Normal file
View File

@ -0,0 +1,108 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Export" | translate }}{% endblock %}
{% block content %}
<h2>{{ "What would you like to export?" | translate }}</h2>
<form id="export_form" class="split" action="{{ url('export') }}" method="post" accept-charset="UTF-8">
<fieldset role="presentation">
<p>
<label for="posts">
{{ "Posts" | translate }}
</label>
{#- -#}
<input class="checkbox" type="checkbox" name="posts" value="" id="posts" checked="checked">
</p>
<p>
<label for="filter_posts">
{{ "Filter Posts" | translate }}
<span class="sub">{{ "(optional)" | translate }}</span>
<a href="{{ url('help/id/filtering_results') }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
</label>
{#- -#}
<input class="text filter_text" type="text" name="filter_posts" value="" id="filter_posts">
</p>
<p>
<label for="pages">
{{ "Pages" | translate }}
</label>
{#- -#}
<input class="checkbox" type="checkbox" name="pages" value="" id="pages" checked="checked">
</p>
<p>
<label for="filter_pages">
{{ "Filter Pages" | translate }}
<span class="sub">{{ "(optional)" | translate }}</span>
<a href="{{ url('help/id/filtering_results') }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
</label>
{#- -#}
<input class="text filter_text" type="text" name="filter_pages" value="" id="filter_pages">
</p>
<p>
<label for="groups">
{{ "Groups" | translate }}
</label>
{#- -#}
<input class="checkbox" type="checkbox" name="groups" value="" id="groups" checked="checked">
</p>
<p>
<label for="filter_groups">
{{ "Filter Groups" | translate }}
<span class="sub">{{ "(optional)" | translate }}</span>
<a href="{{ url('help/id/filtering_results') }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
</label>
{#- -#}
<input class="text filter_text" type="text" name="filter_groups" value="" id="filter_groups">
</p>
<p>
<label for="users">
{{ "Users" | translate }}
</label>
{#- -#}
<input class="checkbox" type="checkbox" name="users" value="" id="users" checked="checked">
</p>
<p>
<label for="filter_users">
{{ "Filter Users" | translate }}
<span class="sub">{{ "(optional)" | translate }}</span>
<a href="{{ url('help/id/filtering_results') }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
</label>
{#- -#}
<input class="text filter_text" type="text" name="filter_users" value="" id="filter_users">
</p>
<aside class="split_block small">
{{ "Users export file will contain the hashed password for each user keep it safe!" | translate }}
</aside>
<p>
<label for="uploads">
{{ "Uploads Manifest" | translate }}
</label>
{#- -#}
<input class="checkbox" type="checkbox" name="uploads" value="" id="uploads" checked="checked">
</p>
<p>
<label for="filter_uploads">
{{ "Filter Uploads Manifest" | translate }}
<span class="sub">{{ "(optional)" | translate }}</span>
</label>
{#- -#}
<input class="text filter_text" type="text" name="filter_uploads" value="" id="filter_uploads">
</p>
{{ trigger.call("export_choose") }}
<p class="buttons">
<button type="submit" class="yay">
{{ "Export" | translate }}
</button>
</p>
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

68
admin/pages/feathers.twig Normal file
View File

@ -0,0 +1,68 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Feathers" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Feathers" | translate }}</h2>
{% if enabled_feathers is not empty %}
<h3>{{ "Enabled" | translate }}</h3>
<ul id="feathers_enabled" class="extend feathers">
{% for safename, feather in enabled_feathers %}
<li class="feather{{ feather.classes is defined ? ' ' ~ feather.classes | join(' ') : '' }}" id="feather_{{ safename }}">
<h4>
{{ feather.name }}{# translation is done in the controller #}
<a class="emblem feather_url" rel="external" href="{{ feather.url }}" target="_blank">
{{ icon_img("info.svg", "info" | translate) }}
</a>
</h4>
<p class="feather_description">
{{ feather.description }}{# translation is done in the controller #}
</p>
<div class="controls">
<form class="feather_disabler" action="{{ url('disable') }}" method="post" accept-charset="UTF-8">
<input type="hidden" name="extension" value="{{ safename }}">
<input type="hidden" name="type" value="feather">
<input type="hidden" name="hash" value="{{ authenticate() }}">
{% if feather.confirm is not empty %}
<button name="confirm" value="1" type="submit" class="boo" data-confirm="{{ feather.confirm | fix(true) }}">
{{ "Uninstall" | translate }}
</button>
{% endif %}
<button type="submit">
{{ "Disable" | translate }}
</button>
</form>
</div>
</li>
{% endfor %}
</ul>
{% endif %}
{% if disabled_feathers is not empty %}
<h3>{{ "Disabled" | translate }}</h3>
<ul id="feathers_disabled" class="extend feathers">
{% for safename, feather in disabled_feathers %}
<li class="feather{{ feather.classes is defined ? ' ' ~ feather.classes | join(' ') : '' }}" id="feather_{{ safename }}">
<h4>
{{ feather.name }}{# translation is done in the controller #}
<a class="emblem feather_url" href="{{ feather.url }}" target="_blank">
{{- icon_img("info.svg", "info" | translate) }}
</a>
</h4>
<p class="feather_description">
{{ feather.description }}{# translation is done in the controller #}
</p>
<div class="controls">
<form class="feather_enabler" action="{{ url('enable') }}" method="post" accept-charset="UTF-8">
<input type="hidden" name="extension" value="{{ safename }}">
<input type="hidden" name="type" value="feather">
<input type="hidden" name="hash" value="{{ authenticate() }}">
<button type="submit">
{{ "Enable" | translate }}
</button>
</form>
</div>
</li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,105 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "General Settings" | translate }}{% endblock %}
{% block content %}
<h2>{{ "General Settings" | translate }}</h2>
<form id="general_settings" class="split" action="{{ url('general_settings') }}" method="post" accept-charset="UTF-8">
<fieldset role="presentation">
<p>
<label for="name">
{{ "Site Name" | translate }}
</label>
{#- -#}
<input class="text" type="text" name="name" value="{{ site.name | fix(true, true) }}" id="name">
</p>
<p>
<label for="description">
{{ "Description" | translate }}
</label>
{#- -#}
<input class="text" type="text" name="description" value="{{ site.description | fix(true, true) }}" id="description">
</p>
<p>
<label for="chyrp_url">
{{ "Chyrp URL" | translate }}
</label>
{#- -#}
<input class="text" type="url" name="chyrp_url" value="{{ site.chyrp_url | fix(true, true) }}" id="chyrp_url" required>
</p>
<p>
<label for="url">
{{ "Canonical URL" | translate }}
<span class="sub">{{ "(optional)" | translate }}</span>
<a href="{{ url('help/id/canonical_url') }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
</label>
{#- -#}
<input class="text" type="url" name="url" value="{{ site.url != site.chyrp_url ? site.url | fix(true, true) : '' }}" id="url">
<small>
{{ "Have your site URLs point someplace other than your install directory." | translate }}
</small>
</p>
<p>
<label for="email">
{{ "Contact Email Address" | translate }}
</label>
{#- -#}
<input class="text" type="email" name="email" value="{{ site.email | fix(true, true) }}" id="email" required>
</p>
<p>
<label for="timezone">
{{ "Time Zone" | translate }}
</label>
{#- -#}
<select name="timezone" id="timezone">
{% for zone in timezones %}
<option value="{{ zone.code }}"{{ zone.code | selected(site.timezone) }}>
{{ zone.name }}
</option>
{% endfor %}
</select>
</p>
<p>
<label for="locale">
{{ "Language" | translate }}
</label>
{#- -#}
<select name="locale" id="locale">
{% for locale in locales %}
<option value="{{ locale.code }}"{{ locale.code | selected(site.locale) }}>
{{ locale.name }}
</option>
{% endfor %}
</select>
</p>
<p>
<label for="monospace_font">
{{ "Monospace Font" | translate }}
</label>
{#- -#}
<input class="checkbox" type="checkbox" name="monospace_font" id="monospace_font"{{ site.monospace_font | checked }}>
<small>
{{ "Write with a monospace font." | translate }}
</small>
</p>
<p>
<label for="check_updates">
{{ "Check for Updates" | translate }}
</label>
{#- -#}
<input class="checkbox" type="checkbox" name="check_updates" id="check_updates"{{ site.check_updates | checked }}>
<small>
{{ "Current version: %s." | translate | format(version) }}
</small>
</p>
<p class="buttons">
<button type="submit" class="yay">
{{ "Update" | translate }}
</button>
</p>
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

54
admin/pages/import.twig Normal file
View File

@ -0,0 +1,54 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Import" | translate }}{% endblock %}
{% block content %}
<h2>{{ "What would you like to import?" | translate }}</h2>
<form id="import_chyrp_form" class="split" action="{{ url('import') }}" method="post" accept-charset="UTF-8" enctype="multipart/form-data">
<fieldset role="presentation">
<p>
<label for="posts_file">{{ "Posts .atom File" | translate }}</label>
{#- -#}
<input type="file" name="posts_file" value="" id="posts_file" accept=".atom,.xml">
</p>
<p>
<label for="pages_file">{{ "Pages .atom File" | translate }}</label>
{#- -#}
<input type="file" name="pages_file" value="" id="pages_file" accept=".atom,.xml">
</p>
<p>
<label for="groups_file">{{ "Groups .json File" | translate }}</label>
{#- -#}
<input type="file" name="groups_file" value="" id="groups_file" accept=".json,.js">
</p>
<p>
<label for="users_file">{{ "Users .json File" | translate }}</label>
{#- -#}
<input type="file" name="users_file" value="" id="users_file" accept=".json,.js">
</p>
<p>
<label for="uploads">{{ "Bulk File Upload" | translate }}</label>
{#- -#}
<input type="file" name="uploads[]" multiple value="" id="uploads">
</p>
<p>
<label for="media_url">
{{ "URL for Embedded Media" | translate }}
<span class="sub">{{ "(optional)" | translate }}</span>
</label>
{#- -#}
<input class="text" type="url" name="media_url" value="" id="media_url">
<small>
{{ "Usually something like <code>%s/uploads/</code>." | translate | format(site.url) }}
</small>
</p>
{{ trigger.call("import_choose") }}
<p class="buttons">
<button type="submit" class="yay">
{{ "Import" | translate }}
</button>
</p>
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,73 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Manage Groups" | translate }}{% endblock %}
{% block content %}
<form class="filters" action="{{ url('manage_groups') }}" method="post" accept-charset="UTF-8" role="search">
<fieldset role="presentation">
<label for="search">{{ "Search all groups for user&hellip;" | translate }}</label>
<input class="text filter_text" type="text" name="search" value="{{ GET.search is defined ? GET.search | fix(true, true) : '' }}" id="search">
<button type="submit">{{ "Search" | translate }}</button>
{% if visitor.group.can("add_group") %}
<a href="{{ url('new_group') }}" class="button yay">
{{ icon_img("add.svg") }}{{ "New Group" | translate }}
</a>
{% endif %}
</fieldset>
</form>
<h2>{{ GET.search is defined ? "Search Results" | translate : "Groups" | translate }}</h2>
<table>
<thead>
<tr class="head">
<th class="group_name name main">{{ "Group" | translate }}</th>
<th class="group_size value">{{ "Members" | translate }}</th>
<th class="group_default emblem">{{ "Default?" | translate }}</th>
<th class="group_guest emblem">{{ "Guests?" | translate }}</th>
{{ trigger.call("manage_groups_column_header") }}
<th class="controls" colspan="2">{{ "Controls" | translate }}</th>
</tr>
</thead>
<tbody>
{% for group in groups.paginated %}
<tr id="group_{{ group.id }}" class="group">
<td class="group_name name main">
{{ group.name }}
</td>
<td class="group_size value">
<a href="{{ url('manage_users/query/' ~ (('group:' ~ group.name) | url_encode)) }}">{{ group.size }}</a>
</td>
<td class="group_default emblem">
{% if group.id == site.default_group %}
{{ icon_img("success.svg", "yes" | translate, "emblem") }}
{% endif %}
</td>
<td class="group_guest emblem">
{% if group.id == site.guest_group %}
{{ icon_img("success.svg", "yes" | translate, "emblem") }}
{% endif %}
</td>
{{ trigger.call("manage_groups_column", groups) }}
<td class="controls">
{{ group.edit_link(icon_img("edit.svg", "edit" | translate), null, null, "emblem") }}
</td>
<td class="controls">
{{ group.delete_link(icon_img("delete.svg", "delete" | translate), null, null, "emblem") }}
</td>
</tr>
{% else %}
<tr>
<td class="placeholder">
{{ icon_img("failure.svg", "", "emblem") }} {{ "No results" | translate }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if groups.paginated is not empty and groups.pages > 1 %}
<div class="pagination">
<span class="pages">{{ "Page %d of %s" | translate | format(groups.page, groups.final_link(groups.pages)) }}</span>
{{ groups.prev_link }}
{{ groups.next_link }}
</div>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,90 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Manage Pages" | translate }}{% endblock %}
{% block content %}
<form class="filters" action="{{ url('manage_pages') }}" method="post" accept-charset="UTF-8" role="search">
<fieldset role="presentation">
<label for="query">
{{ "Search&hellip;" | translate }}
<a href="{{ url('help/id/filtering_results') }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
</label>
<input class="text filter_text" type="text" name="query" value="{{ GET.query | fix(true, true) }}" id="query">
<button type="submit">{{ "Search" | translate }}</button>
{% if visitor.group.can("add_page") %}
<a href="{{ url('write_page') }}" class="button yay">
{{ icon_img("add.svg") }}{{ "New Page" | translate }}
</a>
{% endif %}
</fieldset>
</form>
<h2>{{ GET.query is not empty ? "Search Results" | translate : "Pages" | translate }}</h2>
<table>
<thead>
<tr class="head">
<th class="page_title title main">{{ "Title" | translate }}</th>
<th class="page_created date">{{ "Created" | translate }}</th>
<th class="page_updated date">{{ "Last Updated" | translate }}</th>
<th class="page_public emblem">{{ "Public?" | translate }}</th>
<th class="page_show emblem">{{ "Listed?" | translate }}</th>
<th class="page_author value">{{ "Author" | translate }}</th>
{{ trigger.call("manage_pages_column_header") }}
<th class="controls" colspan="2">{{ "Controls" | translate }}</th>
</tr>
</thead>
<tbody>
{% for page in pages.paginated %}
<tr id="page_{{ page.id }}" class="page">
<td class="page_title title main">
<a href="{{ page.url() }}">{{ page.title | striptags | oneof("[Untitled]" | translate) | truncate(40) }}</a>
</td>
<td class="page_created date">
{{ page.created_at | time }}
</td>
<td class="page_updated date">
{% if page.updated %}
{{ page.updated_at | time }}
{% else %}
<span class="sub">{{ "never" | translate }}</span>
{% endif %}
</td>
<td class="page_public emblem">
{% if page.public %}
{{ icon_img("success.svg", "yes" | translate, "emblem") }}
{% endif %}
</td>
<td class="page_show emblem">
{% if page.show_in_list %}
{{ icon_img("success.svg", "yes" | translate, "emblem") }}
{% endif %}
</td>
<td class="page_author value">
{{ page.author.name }}
</td>
{{ trigger.call("manage_pages_column", page) }}
<td class="controls">
{{ page.edit_link(icon_img("edit.svg", "edit" | translate), null, null, "emblem") }}
</td>
<td class="controls">
{{ page.delete_link(icon_img("delete.svg", "delete" | translate), null, null, "emblem") }}
</td>
</tr>
{% else %}
<tr>
<td class="placeholder">
{{ icon_img("failure.svg", "", "emblem") }} {{ "No results" | translate }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if pages.paginated is not empty and pages.pages > 1 %}
<div class="pagination">
<span class="pages">{{ "Page %d of %s" | translate | format(pages.page, pages.final_link(pages.pages)) }}</span>
{{ pages.prev_link }}
{{ pages.next_link }}
</div>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,75 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Manage Posts" | translate }}{% endblock %}
{% block content %}
<form class="filters" action="{{ url('manage_posts') }}" method="post" accept-charset="UTF-8" role="search">
<fieldset role="presentation">
<label for="query">
{{ "Search&hellip;" | translate }}
<a href="{{ url('help/id/filtering_results') }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
</label>
<input class="text filter_text" type="text" name="query" value="{{ GET.query | fix(true, true) }}" id="query">
<button type="submit">{{ "Search" | translate }}</button>
{% if visitor.group.can("add_post", "add_draft") %}
<a href="{{ url('write_post') }}" class="button yay">
{{ icon_img("add.svg") }}{{ "New Post" | translate }}
</a>
{% endif %}
</fieldset>
</form>
<h2>{{ GET.query is not empty ? "Search Results" | translate : "Posts" | translate }}</h2>
<table>
<thead>
<tr class="head">
<th class="post_title title main">{{ "Title" | translate }}</th>
<th class="post_date date">{{ "Posted" | translate }}</th>
<th class="post_status status">{{ "Status" | translate }}</th>
<th class="post_author value">{{ "Author" | translate }}</th>
{{ trigger.call("manage_posts_column_header") }}
<th class="controls" colspan="2">{{ "Controls" | translate }}</th>
</tr>
</thead>
<tbody>
{% for post in posts.paginated %}
<tr id="post_{{ post.id }}" class="post {{ post.status_class }}">
<td class="post_title title main">
<a href="{{ post.url() }}">{{ post.title() | striptags | oneof("[Untitled]" | translate) | truncate(40) }}</a>
</td>
<td class="post_date date">
{{ post.created_at | time }}
</td>
<td class="post_status status">
{{ post.status_name }}
</td>
<td class="post_author value">
{{ post.author.name }}
</td>
{{ trigger.call("manage_posts_column", post) }}
<td class="controls">
{{ post.edit_link(icon_img("edit.svg", "edit" | translate), null, null, "emblem") }}
</td>
<td class="controls">
{{ post.delete_link(icon_img("delete.svg", "delete" | translate), null, null, "emblem") }}
</td>
</tr>
{% else %}
<tr>
<td class="placeholder">
<img class="emblem" src="{{ site.chyrp_url }}/admin/images/icons/failure.svg" alt="">
{{ "No results" | translate }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if posts.paginated is defined and posts.pages > 1 %}
<div class="pagination">
<span class="pages">{{ "Page %d of %s" | translate | format(posts.page, posts.final_link(posts.pages)) }}</span>
{{ posts.prev_link }}
{{ posts.next_link }}
</div>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,99 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Manage Uploads" | translate }}{% endblock %}
{% block content %}
<form class="filters" action="{{ url('manage_uploads') }}" method="post" accept-charset="UTF-8" role="search">
<fieldset role="presentation">
<label for="search">{{ "Search&hellip;" | translate }}</label>
<input class="text filter_text" type="text" name="search" value="{{ GET.search is defined ? GET.search | fix(true, true) : '' }}" id="search">
<button type="submit">{{ "Search" | translate }}</button>
{% if visitor.group.can("import_content") %}
<a href="{{ url('import') }}" class="button yay">
{{ icon_img("add.svg") }}{{ "Add Files" | translate }}
</a>
{% endif %}
</fieldset>
</form>
<h2>{{ GET.search is defined ? "Search Results" | translate : "Uploads" | translate }}</h2>
<table class="interactive{{ uploads.paginated is empty ? ' empty' : '' }}">
<thead>
<tr class="head">
<th class="uploads_name filename main">{{ "Name" | translate }}</th>
<th class="uploads_modified date">{{ "Last Modified" | translate }}</th>
<th class="uploads_size value">{{ "Size" | translate }}</th>
<th class="uploads_type value">{{ "Type" | translate }}</th>
{{ trigger.call("manage_uploads_column_header") }}
<th class="controls" colspan="2">{{ "Controls" | translate }}</th>
</tr>
</thead>
<tbody>
{% for upload in uploads.paginated %}
<tr id="uploads_{{ loop.index }}" class="uploads">
<td class="uploads_name filename main">
{% if ["jpg", "jpeg", "png", "webp", "gif"] | contains(upload.type) %}
{{ upload.name | thumbnail("", false, ["max_width=70", "quality=60", "square=1"], "70px") }}
{% elseif ["avif", "tif", "tiff", "heif", "bmp"] | contains(upload.type) %}
{{ icon_img("image.svg", "", "placeholder") }}
{% elseif ["mp3", "m4a", "oga", "ogg", "mka", "flac", "wav", "aiff"] | contains(upload.type) %}
{{ icon_img("audio.svg", "", "placeholder") }}
{% elseif ["mpg", "mpeg", "mp2", "mp4", "m4v", "ogv", "mkv", "mov", "avi", "webm", "3gp", "ts"] | contains(upload.type) %}
{{ icon_img("video.svg", "", "placeholder") }}
{% elseif ["zip", "tar", "rar", "gz", "bz2", "7z", "dmg", "cab", "iso", "udf"] | contains(upload.type) %}
{{ icon_img("archive.svg", "", "placeholder") }}
{% else %}
{{ icon_img("file.svg", "", "placeholder") }}
{% endif %}
<a target="_blank" href="{{ upload.name | uploaded }}">{{ upload.name | fix }}</a>
</td>
<td class="uploads_modified value">
{{ upload.modified | time("Y-m-d H:i:s") }}
</td>
<td class="uploads_size value">
{{ upload.size | filesizeformat }}
</td>
<td class="uploads_type value">
{{ upload.type | upper }}
</td>
{{ trigger.call("manage_uploads_column", upload) }}
<td class="controls">
<a class="emblem" href="{{ upload.name | download }}">
{{- icon_img("download.svg", "download" | translate) -}}
</a>
</td>
<td class="controls">
<a class="upload_delete_link delete_link emblem" href="{{ url('delete_upload/file/' ~ (upload.name | url_encode)) }}">
{{- icon_img("delete.svg", "delete" | translate) -}}
</a>
</td>
</tr>
{% else %}
<tr>
<td class="placeholder">
{{ icon_img("failure.svg", "", "emblem") }} {{ "No results" | translate }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="controls">
<form action="{{ self_url() }}" method="post" accept-charset="UTF-8">
<label for="uploads_sort">{{ "Sort results by:" | translate }}</label>
<select name="sort" id="uploads_sort">
{% for value, label in uploads_columns %}
<option value="{{ value }}"{{ value | selected(uploads_sort) }}>
{{ label }}
</option>
{% endfor %}
</select>
<button type="submit">{{ "Sort Results" | translate }}</button>
</form>
</div>
{% if uploads.paginated is not empty and uploads.pages > 1 %}
<div class="pagination">
<span class="pages">{{ "Page %d of %s" | translate | format(uploads.page, uploads.final_link(uploads.pages)) }}</span>
{{ uploads.prev_link }}
{{ uploads.next_link }}
</div>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,85 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Manage Users" | translate }}{% endblock %}
{% block content %}
<form class="filters" action="{{ url('manage_users') }}" method="post" accept-charset="UTF-8" role="search">
<fieldset role="presentation">
<label for="query">
{{ "Search&hellip;" | translate }}
<a href="{{ url('help/id/filtering_results') }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
</label>
<input class="text filter_text" type="text" name="query" value="{{ GET.query | fix(true, true) }}" id="query">
<button type="submit">{{ "Search" | translate }}</button>
{% if visitor.group.can("add_user") %}
<a href="{{ url('new_user') }}" class="button yay">
{{ icon_img("add.svg") }}{{ "New User" | translate }}
</a>
{% endif %}
</fieldset>
</form>
<h2>{{ GET.query is not empty ? "Search Results" | translate : "Users" | translate }}</h2>
<table>
<thead>
<tr class="head">
<th class="user_name name main">{{ "Login (name)" | translate }}</th>
<th class="user_group value">{{ "Group" | translate }}</th>
<th class="user_joined date">{{ "Joined" | translate }}</th>
<th class="user_website name">{{ "Website" | translate }}</th>
{% if site.email_activation %}
<th class="user_activated emblem" title="approved">{{ "Activated?" | translate }}</th>
{% endif %}
{{ trigger.call("manage_users_column_header") }}
<th class="controls" colspan="2">{{ "Controls" | translate }}</th>
</tr>
</thead>
<tbody>
{% for user in users.paginated %}
<tr id="user_{{ user.id }}" class="user">
<td class="user_name name main">
<a href="mailto:{{ user.email | fix(true) }}">{{ user.login }}</a>
{% if user.full_name is not empty %}<span class="sub">({{ user.full_name }})</span>{% endif %}
</td>
<td class="user_group value">
{{ user.group.name }}
</td>
<td class="user_joined date">
{{ user.joined_at | time }}
</td>
<td class="user_website name">
{% if user.website is not empty %}<a href="{{ user.website | fix(true) }}">{{ user.website }}</a>{% endif %}
</td>
{% if site.email_activation %}
<td class="user_activated emblem">
{% if user.approved %}
{{ icon_img("success.svg", "yes" | translate, "emblem") }}
{% endif %}
</td>
{% endif %}
{{ trigger.call("manage_users_column", user) }}
<td class="controls">
{{ user.edit_link(icon_img("edit.svg", "edit" | translate), null, null, "emblem") }}
</td>
<td class="controls">
{{ user.delete_link(icon_img("delete.svg", "delete" | translate), null, null, "emblem") }}
</td>
</tr>
{% else %}
<tr>
<td class="placeholder">
{{ icon_img("failure.svg", "", "emblem") }} {{ "No results" | translate }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if users.paginated is not empty and users.pages > 1 %}
<div class="pagination">
<span class="pages">{{ "Page %d of %s" | translate | format(users.page, users.final_link(users.pages)) }}</span>
{{ users.prev_link }}
{{ users.next_link }}
</div>
{% endif %}
{% endblock %}

110
admin/pages/modules.twig Normal file
View File

@ -0,0 +1,110 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Modules" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Modules" | translate }}</h2>
{% if enabled_modules is not empty %}
<h3>{{ "Enabled" | translate }}</h3>
<ul id="modules_enabled" class="extend modules">
{% for safename, module in enabled_modules %}
<li class="module{{ module.classes is defined ? ' ' ~ module.classes | join(' ') : '' }}" id="module_{{ safename }}">
<h4>
{{ module.name }}{# translation is done in the controller #}
<a class="emblem module_url" rel="external" href="{{ module.url }}" target="_blank">
{{ icon_img("info.svg", "info" | translate) }}
</a>
</h4>
<p class="module_description">
{{ module.description }}{# translation is done in the controller #}
</p>
{% if module.conflicts is not empty %}
<h5 class="module_conflicts_message">
{{ "This module conflicts with the following modules:" | translate }}
</h5>
<ol class="module_conflicts_list">
{% for conflict in module.conflicts %}
<li class="{{ conflict }}">{{ conflict | camelize }}</li>
{% endfor %}
</ol>
{% endif %}
{% if module.dependencies is not empty %}
<h5 class="module_dependencies_message">
{{ "This module requires the following modules to be enabled:" | translate }}
</h5>
<ol class="module_dependencies_list">
{% for dependency in module.dependencies %}
<li class="{{ dependency }}">{{ dependency | camelize }}</li>
{% endfor %}
</ol>
{% endif %}
<div class="controls">
<form class="module_disabler" action="{{ url('disable') }}" method="post" accept-charset="UTF-8">
<input type="hidden" name="extension" value="{{ safename }}">
<input type="hidden" name="type" value="module">
<input type="hidden" name="hash" value="{{ authenticate() }}">
{% if module.confirm is not empty %}
<button name="confirm" value="1" type="submit" class="boo" data-confirm="{{ module.confirm | fix(true) }}">
{{ "Uninstall" | translate }}
</button>
{% endif %}
<button type="submit">
{{ "Disable" | translate }}
</button>
</form>
</div>
</li>
{% endfor %}
</ul>
{% endif %}
{% if disabled_modules is not empty %}
<h3>{{ "Disabled" | translate }}</h3>
<ul id="modules_disabled" class="extend modules">
{% for safename, module in disabled_modules %}
<li class="module{{ module.classes is defined ? ' ' ~ module.classes | join(' ') : '' }}" id="module_{{ safename }}">
<h4>
{{ module.name }}{# translation is done in the controller #}
<a class="emblem module_url" href="{{ module.url }}" target="_blank">
{{ icon_img("info.svg", "info" | translate) }}
</a>
</h4>
<p class="module_description">
{{ module.description }}{# translation is done in the controller #}
</p>
{% if module.conflicts is not empty %}
<h5 class="module_conflicts_message">
{{ "This module conflicts with the following modules:" | translate }}
</h5>
<ol class="module_conflicts_list">
{% for conflict in module.conflicts %}
<li class="{{ conflict }}">{{ conflict | camelize }}</li>
{% endfor %}
</ol>
{% endif %}
{% if module.dependencies is not empty %}
<h5 class="module_dependencies_message">
{{ "This module requires the following modules to be enabled:" | translate }}
</h5>
<ol class="module_dependencies_list">
{% for dependency in module.dependencies %}
<li class="{{ dependency }}">{{ dependency | camelize }}</li>
{% endfor %}
</ol>
{% endif %}
{% if module.classes is defined and not (module.classes | contains("missing_dependency")) %}
<div class="controls">
<form class="module_enabler" action="{{ url('enable') }}" method="post" accept-charset="UTF-8">
<input type="hidden" name="extension" value="{{ safename }}">
<input type="hidden" name="type" value="module">
<input type="hidden" name="hash" value="{{ authenticate() }}">
<button type="submit">
{{ "Enable" | translate }}
</button>
</form>
</div>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,33 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "New Group" | translate }}{% endblock %}
{% block content %}
<h2>{{ "New Group" | translate }}</h2>
<form id="new_group" class="split" action="{{ url('add_group') }}" method="post" accept-charset="UTF-8" data-toggler="group_toggler">
<fieldset role="presentation">
<p>
<label for="name">{{ "Name" | translate }}</label>
{#- -#}
<input class="text" type="text" name="name" value="" id="name" maxlength="100" required>
</p>
<h3>{{ "Permissions" | translate }}</h3>
<p class="toggler" id="group_toggler">
</p>
<hr>
{% for permission in permissions %}
<p>
<label for="permission_{{ permission.id }}">{{ permission.name }}{# translation is done in Group #}</label>
{#- -#}
<input class="checkbox" type="checkbox" name="permissions[{{ permission.id }}]" id="permission_{{ permission.id }}">
</p>
{% endfor %}
<p class="buttons">
<button type="submit" class="yay">
{{ "Add Group" | translate }}
</button>
</p>
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

75
admin/pages/new_user.twig Normal file
View File

@ -0,0 +1,75 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Add User" | translate }}{% endblock %}
{% block content %}
<h2>{{ "New User" | translate }}</h2>
<form id="new_user" class="split" action="{{ url('add_user') }}" method="post" accept-charset="UTF-8">
<fieldset role="presentation">
<p>
<label for="login">{{ "Login" | translate }}</label>
{#- -#}
<input class="text" type="text" name="login" value="" id="login" maxlength="64" required>
</p>
<p>
<label for="email">{{ "Email" | translate }}</label>
{#- -#}
<input class="text" type="email" name="email" value="" id="email" maxlength="128" required>
</p>
<p>
<label for="group">{{ "Group" | translate }}</label>
{#- -#}
<select name="group" id="group">
<option value="{{ default_group.id }}">
{{ default_group.name }}
</option>
{% for group in groups %}
<option value="{{ group.id }}">
{{ group.name }}
</option>
{% endfor %}
</select>
</p>
<p>
<label for="password1">{{ "Password" | translate }}</label>
{#- -#}
<input class="text" type="password" name="password1" value="" id="password1" maxlength="128" required>
</p>
<p>
<label for="password2">{{ "Confirm" | translate }}</label>
{#- -#}
<input class="text" type="password" name="password2" value="" id="password2" maxlength="128" required>
</p>
<p>
<label for="full_name">
{{ "Full Name" | translate }}
<span class="sub">{{ "(optional)" | translate }}</span>
</label>
{#- -#}
<input class="text" type="text" name="full_name" value="" id="full_name" maxlength="250">
</p>
<p>
<label for="website">
{{ "Website" | translate }}
<span class="sub">{{ "(optional)" | translate }}</span>
</label>
{#- -#}
<input class="text" type="url" name="website" value="" id="website" maxlength="128">
</p>
{% if site.email_activation %}
<p>
<label for="activated">{{ "Activated?" | translate }}</label>
{#- -#}
<input class="checkbox" type="checkbox" name="activated" id="activated">
</p>
{% endif %}
{{ trigger.call("new_user_fields") }}
<p class="buttons">
<button type="submit" class="yay">
{{ "Add User" | translate }}
</button>
</p>
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,101 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Route Settings" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Route Settings" | translate }}</h2>
<form id="route_settings" class="split" action="{{ url('route_settings') }}" method="post" accept-charset="UTF-8">
<fieldset role="presentation">
<p>
<label for="clean_urls">
{{ "Clean URLs" | translate }}
<a href="https://chyrplite.net/wiki/Supporting-Clean-URLs.html" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
</label>
{#- -#}
<input type="checkbox" name="clean_urls" id="clean_urls"{{ site.clean_urls | checked }}>
<small>
{{ "Gives your site prettier URLs." | translate }} {{ "Requires URL rewrite support." | translate }}
</small>
</p>
<p>
<label for="enable_homepage">
{{ "Homepage" | translate }}
</label>
{#- -#}
<input class="checkbox" type="checkbox" name="enable_homepage" id="enable_homepage"{{ site.enable_homepage | checked }}>
<small>
{{ "Make the default route a homepage instead of the blog index." | translate }}
</small>
</p>
<p>
<label for="post_url">
{{ "Post View URL" | translate }}
<span class="sub">{{ "(requires clean URLs)" | translate }}</span>
</label>
{#- -#}
<input class="text" type="text" name="post_url" value="{{ site.post_url | fix(true, true) }}" size="30" id="post_url">
</p>
<h3>{{ "Syntax:" | translate }}</h3>
<div class="split_block small">
<ul>
<li>
<code class="syntax">(year)</code>
{{ "Year submitted" | translate }} <span class="sub">{{ "(e.g. 2007)" | translate }}</span>
</li>
<li>
<code class="syntax">(month)</code>
{{ "Month submitted" | translate }} <span class="sub">{{ "(e.g. 12)" | translate }}</span>
</li>
<li>
<code class="syntax">(day)</code>
{{ "Day submitted" | translate }} <span class="sub">{{ "(e.g. 25)" | translate }}</span>
</li>
<li>
<code class="syntax">(hour)</code>
{{ "Hour submitted" | translate }} <span class="sub">{{ "(e.g. 03)" | translate }}</span>
</li>
<li>
<code class="syntax">(minute)</code>
{{ "Minute submitted" | translate }} <span class="sub">{{ "(e.g. 59)" | translate }}</span>
</li>
<li>
<code class="syntax">(second)</code>
{{ "Second submitted" | translate }} <span class="sub">{{ "(e.g. 30)" | translate }}</span>
</li>
<li>
<code class="syntax">(id)</code>
{{ "Post ID" | translate }}
</li>
<li>
<code class="syntax">(author)</code>
{{ "Post author (username)" | translate }} <span class="sub">{{ "(e.g. Alex)" | translate }}</span>
</li>
<li>
<code class="syntax">(clean)</code>
{{ "The non-unique slug" | translate }} <span class="sub">{{ "(e.g. this_is_clean)" | translate }}</span>
</li>
<li>
<code class="syntax">(url)</code>
{{ "The unique form of (clean)" | translate }} <span class="sub">{{ "(e.g. this_one_is_taken_2)" | translate }}</span>
</li>
<li>
<code class="syntax">(feather)</code>
{{ "The post's feather" | translate }} <span class="sub">{{ "(e.g. text)" | translate }}</span>
</li>
<li>
<code class="syntax">(feathers)</code>
{{ "The plural form of the post's feather" | translate }} <span class="sub">{{ "(e.g. links)" | translate }}</span>
</li>
</ul>
</div>
<p class="buttons">
<button type="submit" class="yay">
{{ "Update" | translate }}
</button>
</p>
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

34
admin/pages/themes.twig Normal file
View File

@ -0,0 +1,34 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Themes" | translate }}{% endblock %}
{% block content %}
<h2>{{ "Blog Themes" | translate }}</h2>
<ul id="blog_themes" class="extend themes">
{% for safename, theme in themes %}
<li class="theme{{ safename == site.theme ? ' current' : '' }}" id="{{ safename }}">
<h3>
{{ theme.name }}{# translation is done in the controller #}
<a class="emblem module_author" rel="external" href="{{ theme.url }}" target="_blank">
{{ icon_img("info.svg", "info" | translate) }}
</a>
</h3>
<p class="theme_description">
{{ theme.description }}{# translation is done in the controller #}
</p>
<div class="controls">
<form action="{{ url('change_theme') }}" method="post" accept-charset="UTF-8">
<input type="hidden" name="theme" value="{{ safename }}">
<input type="hidden" name="hash" value="{{ authenticate() }}">
<button name="change" value="undecidedly"{{ safename | disabled(site.theme) }}>
{{ "Preview" | translate }}
</button>
<button name="change" value="indubitably" type="submit"{{ safename | disabled(site.theme) }}>
{{ "Select" | translate }}
</button>
</form>
</div>
</li>
{% endfor %}
</ul>
{% endblock %}

View File

@ -0,0 +1,63 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "User Settings" | translate }}{% endblock %}
{% block content %}
<h2>{{ "User Settings" | translate }}</h2>
<form id="user_settings" class="split" action="{{ url('user_settings') }}" method="post" accept-charset="UTF-8">
<fieldset role="presentation">
<p>
<label for="can_register">{{ "Registration" | translate }}</label>
{#- -#}
<input class="checkbox" type="checkbox" name="can_register" id="can_register"{{ site.can_register | checked }}>
<small>
{{ "Allow people to register." | translate }}
</small>
</p>
<p>
<label for="email_correspondence">{{ "Email Correspondence" | translate }}</label>
{#- -#}
<input class="checkbox" type="checkbox" name="email_correspondence" id="email_correspondence"{{ site.email_correspondence | checked }}>
<small>
{{ "Allow the site to send email correspondence to users?" | translate }}
</small>
</p>
<p>
<label for="email_activation">{{ "Activate by Email" | translate }}</label>
{#- -#}
<input class="checkbox" type="checkbox" name="email_activation" id="email_activation"{{ site.email_activation | checked }}>
<small>
{{ "Should users activate by email?" | translate }}
</small>
</p>
<p>
<label for="default_group">{{ "Default User Group" | translate }}</label>
{#- -#}
<select name="default_group" id="default_group">
{% for group in groups %}
<option value="{{ group.id }}"{{ group.id | selected(site.default_group) }}>
{{ group.name }}
</option>
{% endfor %}
</select>
</p>
<p>
<label for="guest_group">{{ "&#8220;Guest&#8221; Group" | translate }}</label>
{#- -#}
<select name="guest_group" id="guest_group">
{% for group in groups %}
<option value="{{ group.id }}"{{ group.id | selected(site.guest_group) }}>
{{ group.name }}
</option>
{% endfor %}
</select>
</p>
<p class="buttons">
<button type="submit" class="yay">
{{ "Update" | translate }}
</button>
</p>
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Add Page" | translate }}{% endblock %}
{% block content %}
<form id="write_form" action="{{ url('add_page') }}" method="post" accept-charset="UTF-8" enctype="multipart/form-data">
<fieldset role="presentation">
{% include "partials" ~ DIR ~ "page_fields.twig" %}
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends "layouts" ~ DIR ~ "default.twig" %}
{% block title %}{{ "Write" | translate }}{% endblock %}
{% block content %}
<form id="write_form" action="{{ url('add_post') }}" method="post" accept-charset="UTF-8" enctype="multipart/form-data">
<fieldset role="presentation">
{% include "partials" ~ DIR ~ "post_fields.twig" %}
<input type="hidden" name="feather" value="{{ feather.safename | fix(true) }}" id="feather">
<input type="hidden" name="hash" value="{{ authenticate() }}" id="hash">
</fieldset>
</form>
{% endblock %}

View File

@ -0,0 +1,88 @@
{{ trigger.call("before_page_fields") }}
{% set field_markdown = site.enable_markdown ? " data-markdown" : "" %}
<p class="main_options">
<label for="title">{{ "Title" | translate }}</label>
<input class="text" type="text" name="title" value="{{ page is defined ? page.title | fix(true, true) : '' }}" id="title" required>
</p>
<p class="main_options">
<label for="body">
{{ "Body" | translate }}
</label>
<span id="body_toolbar" class="options_toolbar" role="toolbar">{% if field_markdown %}
<a href="{{ url('help/id/markdown') }}" target="_blank" class="help emblem markdown">
{{- icon_img("markdown.svg", "Markdown" | translate) -}}
</a>
{% endif %}</span>
<textarea rows="12" name="body" id="body" data-preview{{ field_markdown }} required>{{ page is defined ? page.body | fix(false, true) : "" }}</textarea>
<span id="body_tray" class="options_tray" role="status"></span>
</p>
{{ trigger.call("after_page_fields") }}
<div id="more_options" class="more_options">
<p class="more_options_option">
<label for="slug">
{{ "Slug" | translate }}
<a href="{{ url('help/id/slugs') }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
</label>
<input class="text" type="text" name="slug" value="{{ page is defined ? page.clean | fix(true) : '' }}" id="slug" maxlength="80">
</p>
<p class="more_options_option">
<label for="status">{{ "Status" | translate }}</label>
<select name="status" id="status">
{% set option_selected = page is defined ? page.status | selected("listed") : " selected" %}
<option value="listed"{{ option_selected }}>{{ "Public and visible in pages list" | translate }}</option>
{% set option_selected = page is defined ? page.status | selected("public") : "" %}
<option value="public"{{ option_selected }}>{{ "Public" | translate }}</option>
{% set option_selected = page is defined ? page.status | selected("teased") : "" %}
<option value="teased"{{ option_selected }}>{{ "Private and visible in pages list" | translate }}</option>
{% set option_selected = page is defined ? page.status | selected("private") : "" %}
<option value="private"{{ option_selected }}>{{ "Private" | translate }}</option>
</select>
</p>
<p class="more_options_option">
<label for="list_priority">{{ "Priority in pages list" | translate }}</label>
<select name="list_priority" id="list_priority">
{% set option_selected = page is defined ? page.list_order | selected(0,1,2) : "" %}
<option value="0"{{ option_selected }}>{{ "High" | translate }}</option>
{% set option_selected = page is defined ? page.list_order | selected(3,4,5) : " selected" %}
<option value="4"{{ option_selected }}>{{ "Medium" | translate }}</option>
{% set option_selected = page is defined ? page.list_order | selected(6,7,8) : "" %}
<option value="8"{{ option_selected }}>{{ "Low" | translate }}</option>
</select>
</p>
<p class="more_options_option">
<label for="parent_id">{{ "Parent" | translate }}</label>
<select name="parent_id" id="parent_id">
<option value="0">{{ "[None]" | translate }}</option>
{% for item in theme.pages_list(0, page is defined ? page.id : null) %}
{% set option_selected = page is defined ? page.parent_id | selected(item.id) : "" %}
{% set option_contents = ("&nbsp;" | repeat((item.depth - 1) * 4)) ~ (item.title | striptags | fix) %}
<option value="{{ item.id }}"{{ option_selected }}>{{ option_contents }}</option>
{% endfor %}
</select>
</p>
{% if route.action == "write_page" %}
{{ trigger.call("new_page_options") }}
{% else %}
{{ trigger.call("edit_page_options", page) }}
{% endif %}
</div>
<div class="buttons confirmation">
{% if route.action == "write_page" %}
<button type="submit" class="yay">
{{ "Publish" | translate }}
</button>
<button type="submit" name="private" value="true">
{{ "Save" | translate }}
</button>
{% else %}
<button type="submit">
{{ "Save" | translate }}
</button>
<button type="submit" class="boo" name="cancel" value="true" data-confirm="">
{{ "Cancel" | translate }}
</button>
{% endif %}
</div>
</div>

View File

@ -0,0 +1,207 @@
{{ trigger.call("before_post_fields", feather) }}
{% for field in feather.fields %}
{% if attribute(post, field.attr) is defined %}
{% set field_value = attribute(post, field.attr) %}
{% else %}
{% set field_value = field.value is defined ? field.value : "" %}
{% endif %}
{% set field_count = field_value is iterable ? field_value | length : 1 %}
{% set field_preview = field.preview is defined and field.preview ? " data-preview" : "" %}
{% set field_multiple = field.multiple is defined and field.multiple ? " multiple" : "" %}
{% set field_accept = field.accept is defined ? ' accept="' ~ (field.accept | fix(true)) ~ '"' : "" %}
{% if site.enable_markdown %}
{% set field_markdown = field.filters is defined and (field.filters | contains("markup_text")) ? " data-markdown" : "" %}
{% else %}
{% set field_markdown = "" %}
{% endif %}
{% if post is not defined %}
{% set field_required = field.optional is defined and field.optional ? "" : " required" %}
{% else %}
{% set field_required = field.type == "file" or field.optional is defined and field.optional ? "" : " required" %}
{% endif %}
{% set field_id = (field.attr ~ "_field") | fix(true) %}
{% set field_name = (field_multiple is not empty ? field.attr ~ "[]" : field.attr) | fix(true) %}
<p class="main_options">
<label for="{{ field_id }}">
{{ field.label }}
{% if field.note is defined %}
{{ field.note }}
{% endif %}
{% if field.help is defined %}
<a href="{{ url('help/id/' ~ (field.help | url_encode)) }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
{% endif %}
</label>
{% if field.type == "text" %}
<input id="{{ field_id }}" class="text" type="text" name="{{ field_name }}"{{ field_markdown ~ field_required }} value="{{ field_value | fix(true, true) }}">
{% elseif field.type == "url" %}
<input id="{{ field_id }}" class="text" type="url" name="{{ field_name }}"{{ field_required }} value="{{ field_value | fix(true, true) }}">
{% elseif field.type == "email" %}
<input id="{{ field_id }}" class="text" type="email" name="{{ field_name }}"{{ field_required }} value="{{ field_value | fix(true, true) }}">
{% elseif field.type == "file" %}
<input id="{{ field_id }}" type="file" name="{{ field_name }}"{{ field_accept ~ field_multiple ~ field_required }} value="">
{% if field_value is not empty %}
<span id="{{ field_name }}_field_tray" class="options_tray" role="note">
{{ "%d file previously uploaded with this post." | translate_plural("%d files previously uploaded with this post.", field_count) | format(field_count) }}
</span>
{% endif %}
{% elseif field.type == "text_block" %}
<span id="{{ field_name }}_field_toolbar" class="options_toolbar" role="toolbar">
{% if field_markdown is not empty %}<a href="{{ url('help/id/markdown') }}" target="_blank" class="help emblem markdown">
{{- icon_img("markdown.svg", "Markdown" | translate) -}}
</a>
{% endif %}</span>
<textarea id="{{ field_id }}" name="{{ field_name }}" rows="12"{{ field_preview ~ field_markdown ~ field_required }}>{{ field_value | fix(false, true) }}</textarea>
<span id="{{ field_name }}_field_tray" class="options_tray" role="status"></span>
{% elseif field.type == "checkbox" %}
<input id="{{ field_id }}" type="checkbox" name="{{ field_name }}"{{ field.checked | checked }}>
{% elseif field.type == "select" %}
<select id="{{ field_id }}" name="{{ field_name }}">
{% for value, name in field.options %}
{% set option_selected = attribute(post, field.attr) is defined ? value | selected(attribute(post, field.attr)) : "" %}
<option value="{{ value | fix(true) }}"{{ option_selected }}>
{{ name | fix }}
</option>
{% endfor %}
</select>
{% endif %}
{% if field.extra is defined %}
{{ field.extra }}
{% endif %}
</p>
{% endfor %}
{{ trigger.call("after_post_fields", feather) }}
<div id="more_options" class="more_options">
{% if visitor.group.can("add_post") %}
<p class="more_options_option">
<label for="status">{{ "Status" | translate }}</label>
<select id="status" name="status">
{% set option_selected = post is defined ? post.status | selected("draft") : "" %}
<option value="draft"{{ option_selected }}>
{{ "Draft" | translate }}
</option>
{% set option_selected = post is defined ? post.status | selected("public") : " selected" %}
<option value="public"{{ option_selected }}>
{{ "Public" | translate }}
</option>
{% set option_selected = post is defined ? post.status | selected("private") : "" %}
<option value="private"{{ option_selected }}>
{{ "Private" | translate }}
</option>
{% set option_selected = post is defined ? post.status | selected("scheduled") : "" %}
<option value="scheduled"{{ option_selected }}>
{{ "Scheduled" | translate }}
</option>
<optgroup label="{{ 'Visible only to a group of users:' | translate }}">
{% set option_selected = post is defined ? post.status | selected("registered_only") : "" %}
<option value="registered_only"{{ option_selected }}>
{{ "All registered users" | translate }}
</option>
{% if groups is not empty %}
{% for group in groups %}
{% set option_selected = post is defined ? post.status | selected("{" ~ group.id ~ "}") : "" %}
<option value="{{ '{' ~ group.id ~ '}' }}"{{ option_selected }}>
{{ group.name | fix }}
</option>
{% endfor %}
{% endif %}
</optgroup>
</select>
</p>
{% endif %}
<p class="more_options_option">
<label for="pinned">
{{ "Pinned?" | translate }}
{{ "(shows this post above all others)" | translate }}
</label>
<input id="pinned" type="checkbox" name="pinned"{{ post is defined ? post.pinned | checked : "" }}>
</p>
<p class="more_options_option">
<label for="slug">
{{ "Slug" | translate }}
<a href="{{ url('help/id/slugs') }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
</label>
<input id="slug" class="text" type="text" name="slug" value="{{ post is defined ? post.clean | fix(true) : '' }}" maxlength="80">
</p>
<p class="more_options_option">
<label for="created_at">{{ "Timestamp" | translate }}</label>
{% set post_created = post is defined and post.status != "draft" ? post.created_at : now %}
<input id="created_at" class="text" type="text" name="created_at" value="{{ post_created | dateformat('Y-m-d H:i:s') | fix(true) }}">
</p>
{% for field in options %}
{% set field_value = field.value is defined ? field.value : "" %}
{% set field_multiple = field.multiple is defined and field.multiple ? " multiple" : "" %}
{% set field_accept = field.accept is defined ? ' accept="' ~ (field.accept | fix(true)) ~ '"' : "" %}
{% set field_id = ((field.attr | replace({"[": "_", "]": ''})) ~ "_field") | fix(true) %}
{% set field_name = (field_multiple is not empty ? field.attr ~ "[]" : field.attr) | fix(true) %}
<p class="more_options_option">
<label for="{{ field_id }}">
{{ field.label }}
{% if field.note is defined %}
{{ field.note }}
{% endif %}
{% if field.help is defined %}
<a href="{{ url('help/id/' ~ (field.help | url_encode)) }}" rel="help" target="_blank" class="help emblem">
{{- icon_img("help.svg", "help" | translate) -}}
</a>
{% endif %}
</label>
{% if field.type == "text" %}
<input id="{{ field_id }}" class="text" type="text" name="{{ field_name }}" value="{{ field_value | fix(true, true) }}">
{% elseif field.type == "url" %}
<input id="{{ field_id }}" class="text" type="url" name="{{ field_name }}" value="{{ field_value | fix(true, true) }}">
{% elseif field.type == "email" %}
<input id="{{ field_id }}" class="text" type="email" name="{{ field_name }}" value="{{ field_value | fix(true, true) }}">
{% elseif field.type == "file" %}
<input id="{{ field_id }}" type="file" name="{{ field_name }}"{{ field_accept ~ field_multiple }} value="">
{% elseif field.type == "text_block" %}
<textarea id="{{ field_id }}" name="{{ field_name }}" rows="12">{{ field_value | fix(false, true) }}</textarea>
{% elseif field.type == "checkbox" %}
<input id="{{ field_id }}" type="checkbox" name="{{ field_name }}"{{ field.checked | checked }}>
{% elseif field.type == "select" %}
<select id="{{ field_id }}" name="{{ field_name }}">
{% for option in field.options %}
{% set option_selected = option.selected | selected(true) %}
<option value="{{ option.value | fix(true) }}"{{ option_selected }}>
{{ option.name | fix }}
</option>
{% endfor %}
</select>
{% endif %}
{% if field.extra is defined %}
{{ field.extra }}
{% endif %}
</p>
{% endfor %}
</div>
<div class="buttons confirmation">
{% if route.action == "edit_post" %}
{% if visitor.group.can("add_post") and post.status == "draft" %}
<button type="submit" class="yay" name="publish" value="true">
{{ "Publish" | translate }}
</button>
<button type="submit">
{{ "Save" | translate }}
</button>
{% else %}
<button type="submit">
{{ "Save" | translate }}
</button>
<button type="submit" class="boo" name="cancel" value="true" data-confirm="">
{{ "Cancel" | translate }}
</button>
{% endif %}
{% else %}
{% if visitor.group.can("add_post") %}
<button type="submit" class="yay">
{{ "Publish" | translate }}
</button>
{% endif %}
<button type="submit" name="draft" value="true">
{{ "Save" | translate }}
</button>
{% endif %}
</div>

View File

@ -0,0 +1,38 @@
<table>
<tbody>
{% for upload in uploads %}
<tr id="uploads_{{ loop.index }}" class="uploads">
<td class="uploads_name filename main">
{% if ["jpg", "jpeg", "png", "gif", "webp"] | contains(upload.type) %}
{{ upload.name | thumbnail("", false, ["max_width=70", "quality=60", "square=1"], "70px") }}
{% set file_url = site.chyrp_url ~ "/includes/thumbnail.php?file=" ~ (upload.name | url_encode) %}
{% elseif ["avif", "tif", "tiff", "bmp"] | contains(upload.type) %}
{{ icon_img("image.svg", "", "placeholder") }}
{% set file_url = site.chyrp_url ~ "/includes/thumbnail.php?file=" ~ (upload.name | url_encode) %}
{% elseif ["mp3", "m4a", "oga", "ogg", "mka", "flac", "wav"] | contains(upload.type) %}
{{ icon_img("audio.svg", "", "placeholder") }}
{% set file_url = upload.name | uploaded %}
{% elseif ["mpg", "mpeg", "mp2", "mp4", "m4v", "ogv", "mkv", "mov", "avi", "webm", "3gp", "ts"] | contains(upload.type) %}
{{ icon_img("video.svg", "", "placeholder") }}
{% set file_url = upload.name | uploaded %}
{% elseif ["zip", "tar", "rar", "gz", "bz2", "7z", "dmg", "cab", "iso", "udf"] | contains(upload.type) %}
{{ icon_img("archive.svg", "", "placeholder") }}
{% set file_url = upload.name | download %}
{% else %}
{{ icon_img("file.svg", "", "placeholder") }}
{% set file_url = upload.name | download %}
{% endif %}
<a data-name="{{ upload.name }}" data-type="{{ upload.type }}" data-size="{{ upload.size }}" href="{{ file_url }}">
{{ upload.name | fix }}
</a>
</td>
</tr>
{% else %}
<tr>
<td class="placeholder">
{{ icon_img("failure.svg", "", "emblem") }} {{ "No results" | translate }}
</td>
</tr>
{% endfor %}
</tbody>
</table>

1453
admin/stylesheets/all.css Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
/* Setting: monospace_font */
textarea {
font-family: "Cousine webfont", monospace;
}

20
ajax/index.php Normal file
View File

@ -0,0 +1,20 @@
<?php
define('AJAX', true);
require_once dirname(__FILE__, 2).
DIRECTORY_SEPARATOR.
"includes".
DIRECTORY_SEPARATOR.
"common.php";
# Prepare the controller.
$ajax = AjaxController::current();
# Parse the route.
$route = Route::current($ajax);
# Respond to the request.
$route->init();
$trigger->call("end");
ob_end_flush();

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

4
favicon.svg Normal file
View File

@ -0,0 +1,4 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0, 0, 24, 24">
<path d="M23,1 C23.019,3.123 22.365,5.157 21.343,6.994 L18,9 L20.209,9 C16.726,13.496 11.893,17.54 6,18 C6,18 11,12 14,8.312 C11,10 5,17 5,17 C3.687,8.688 12.75,1 23,1 z" fill="#4F4F4F"/>
<path d="M2,23 C2,23 2,21 2,21 C2,21 5,17 5,17 C5,17 6,18 6,18 C6,18 2,23 2,23 z" fill="#4F4F4F"/>
</svg>

After

Width:  |  Height:  |  Size: 371 B

237
feathers/audio/audio.php Normal file
View File

@ -0,0 +1,237 @@
<?php
class Audio extends Feathers implements Feather {
public function __init() {
$maximum = Config::current()->uploads_limit;
$this->setField(
array(
"attr" => "title",
"type" => "text",
"label" => __("Title", "audio"),
"optional" => true
)
);
$this->setField(
array(
"attr" => "filename",
"type" => "file",
"label" => __("Audio File", "audio"),
"multiple" => false,
"accept" => ".".implode(",.", $this->audio_extensions())
)
);
$this->setField(
array(
"attr" => "captions",
"type" => "file",
"label" => __("Captions", "audio"),
"optional" => true,
"multiple" => false,
"accept" => ".vtt"
)
);
$this->setField(
array(
"attr" => "description",
"type" => "text_block",
"label" => __("Description", "audio"),
"optional" => true,
"preview" => true
)
);
$this->setFilter(
"title",
array("markup_post_title", "markup_title")
);
$this->setFilter(
"description",
array("markup_post_text", "markup_text")
);
$this->respondTo("feed_item", "enclose_audio");
$this->respondTo("filter_post", "filter_post");
}
public function submit(): Post {
if (isset($_FILES['filename']) and upload_tester($_FILES['filename']))
$filename = upload(
$_FILES['filename'],
$this->audio_extensions()
);
if (!isset($filename))
error(
__("Error"),
__("You did not select any audio to upload.", "audio"),
code:422
);
if (isset($_FILES['captions']) and upload_tester($_FILES['captions']))
$captions = upload(
$_FILES['captions'],
array("vtt")
);
fallback($_POST['title'], "");
fallback($_POST['description'], "");
fallback($_POST['slug'], $_POST['title']);
fallback($_POST['status'], "public");
fallback($_POST['created_at'], datetime());
fallback($_POST['option'], array());
return Post::add(
values:array(
"title" => $_POST['title'],
"filename" => $filename,
"captions" => fallback($captions, ""),
"description" => $_POST['description']
),
clean:sanitize($_POST['slug']),
feather:"audio",
pinned:!empty($_POST['pinned']),
status:$_POST['status'],
created_at:datetime($_POST['created_at']),
pingbacks:true,
options:$_POST['option']
);
}
public function update($post): Post|false {
fallback($_POST['title'], "");
fallback($_POST['description'], "");
fallback($_POST['slug'], $post->clean);
fallback($_POST['status'], $post->status);
fallback($_POST['created_at'], $post->created_at);
fallback($_POST['option'], array());
$filename = $post->filename;
$captions = $post->captions;
if (isset($_FILES['filename']) and upload_tester($_FILES['filename']))
$filename = upload(
$_FILES['filename'],
$this->audio_extensions()
);
return $post->update(
values:array(
"title" => $_POST['title'],
"filename" => $filename,
"captions" => fallback($captions, ""),
"description" => $_POST['description']
),
pinned:!empty($_POST['pinned']),
status:$_POST['status'],
clean:sanitize($_POST['slug']),
created_at:datetime($_POST['created_at']),
options:$_POST['option']
);
}
public function title($post): string {
return oneof(
$post->title,
$post->title_from_excerpt()
);
}
public function excerpt($post): string {
return $post->description;
}
public function feed_content($post): string {
return $post->description;
}
public function enclose_audio($post, $feed) {
if ($post->feather != "audio")
return;
$filepath = uploaded($post->filename, false);
if (file_exists($filepath))
$feed->enclosure(
uploaded($post->filename),
filesize($filepath),
$this->audio_type($post->filename)
);
if (empty($post->captions))
return;
$filepath = uploaded($post->captions, false);
if (file_exists($filepath))
$feed->enclosure(
uploaded($post->captions),
filesize($filepath),
$this->audio_type($post->captions)
);
}
public function filter_post($post): void {
if ($post->feather != "audio")
return;
$post->audio_player = $this->audio_player($post);
}
private function audio_player($post): string {
$config = Config::current();
$trigger = Trigger::current();
if ($trigger->exists("audio_player"))
return $trigger->call("audio_player", $post);
$player = '<audio controls>'.
"\n".
__("Your web browser does not support the <code>audio</code> element.", "audio").
"\n".
'<source src="'.
uploaded($post->filename).
'" type="'.
$this->audio_type($post->filename).
'">'.
"\n";
if (!empty($post->captions))
$player.= '<track kind="captions" src="'.
uploaded($post->captions).
'" srclang="'.
lang_base($config->locale).
'" label="'.
lang_code($config->locale).
'">'."\n";
$player.= '</audio>'."\n";
return $player;
}
private function audio_type($filename): string {
$extension = strtolower(
pathinfo($filename, PATHINFO_EXTENSION)
);
switch($extension) {
case "mp3":
return "audio/mpeg";
case "m4a":
return "audio/mp4";
case "mp4":
return "audio/mp4";
case "oga":
return "audio/ogg";
case "ogg":
return "audio/ogg";
case "webm":
return "audio/webm";
case "mka":
return "audio/x-matroska";
default:
return "application/octet-stream";
}
}
private function audio_extensions(): array {
return array("mp3", "m4a", "mp4", "oga", "ogg", "webm", "mka");
}
}

12
feathers/audio/info.php Normal file
View File

@ -0,0 +1,12 @@
<?php
return array(
"name" => __("Audio", "audio"),
"url" => "http://chyrplite.net/",
"version" => "2024.01",
"description" => __("A feather for audio.", "audio"),
"author" => array(
"name" => "Daniel Pimley",
"url" => "http://pimley.net/"
),
"uploader" => true
);

Binary file not shown.

View File

@ -0,0 +1,45 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: Erwin Maas <achillosilliasos@gmail.com>\n"
"Language-Team: \n"
"Language: de_DE\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.4.2\n"
#. This file is distributed under the same license as the Chyrp Lite package.
#: feathers/audio/audio.php:10
msgid "Title"
msgstr "Titel"
#: feathers/audio/audio.php:18
msgid "Audio File"
msgstr "Audio Datei"
#: feathers/audio/audio.php:27
msgid "Captions"
msgstr "Bildunterschriften"
#: feathers/audio/audio.php:37
msgid "Description"
msgstr "Beschreibung"
#: feathers/audio/audio.php:64
msgid "You did not select any audio to upload."
msgstr "Sie haben kein Audio zum Hochladen ausgewählt."
#: feathers/audio/audio.php:186
msgid "Your web browser does not support the <code>audio</code> element."
msgstr "Ihr Webbrowser unterstützt das <code>Audioelement</code> nicht."
#: feathers/audio/info.php:3
msgid "Audio"
msgstr "Audio"
#: feathers/audio/info.php:6
msgid "A feather for audio."
msgstr "Ein feather für Audio."

View File

@ -0,0 +1,34 @@
#. This file is distributed under the same license as the Chyrp Lite package.
#: feathers/audio/audio.php:10
msgid "Title"
msgstr ""
#: feathers/audio/audio.php:18
msgid "Audio File"
msgstr ""
#: feathers/audio/audio.php:27
msgid "Captions"
msgstr ""
#: feathers/audio/audio.php:37
msgid "Description"
msgstr ""
#: feathers/audio/audio.php:64
msgid "You did not select any audio to upload."
msgstr ""
#: feathers/audio/audio.php:186
msgid "Your web browser does not support the <code>audio</code> element."
msgstr ""
#: feathers/audio/info.php:3
msgid "Audio"
msgstr ""
#: feathers/audio/info.php:6
msgid "A feather for audio."
msgstr ""

Binary file not shown.

View File

@ -0,0 +1,46 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: Cyril MAGUIRE <contact@ecyseo.net>\n"
"Language-Team: \n"
"Language: fr_FR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.0.1\n"
#. This file is distributed under the same license as the Chyrp Lite package.
#: feathers/audio/audio.php:10
msgid "Title"
msgstr "Titre"
#: feathers/audio/audio.php:18
msgid "Audio File"
msgstr "Fichier audio"
#: feathers/audio/audio.php:27
msgid "Captions"
msgstr "Légendes"
#: feathers/audio/audio.php:37
msgid "Description"
msgstr "Description"
#: feathers/audio/audio.php:64
msgid "You did not select any audio to upload."
msgstr "Vous n'avez sélectionné aucun fichier audio à télécharger."
#: feathers/audio/audio.php:186
msgid "Your web browser does not support the <code>audio</code> element."
msgstr "Votre navigateur ne supporte pas cet élément <code>audio</audio>."
#: feathers/audio/info.php:3
msgid "Audio"
msgstr "Audio"
#: feathers/audio/info.php:6
msgid "A feather for audio."
msgstr "Une plume pour l'audio."

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