commit 6458f2cb8fddb5c4d5f13006bcdc8e709825e25c Author: Jurij Podgoršek Date: Sat Dec 21 17:42:45 2024 +0100 Prva verzija diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..376e28b --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Syncthing +#.stfolder +#.stignore +.stversions + +# Linux +*~ +*.swp + +# Windows +Thumbs.db +desktop.ini + +# Mac OS X +.DS_Store +._* + +# Composer +/composer.phar +/vendor + + + +/._sync_* +/.owncloudsync.log +/.sync-exclude.lst diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..bf84f7b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,650 @@ +Pico Changelog +============== + +**Note:** This changelog only provides technical information about the changes + introduced with a particular Pico version, and is meant to supplement + the actual code changes. The information in this changelog are often + insufficient to understand the implications of larger changes. Please + refer to both the UPGRADE and NEWS sections of the docs for more + details. + +**Note:** Changes breaking backwards compatibility (BC) are marked with an `!` + (exclamation mark). This doesn't include changes for which BC is + preserved by Pico's official `PicoDeprecated` plugin. If a previously + deprecated feature is later removed in `PicoDeprecated`, this change + is going to be marked as BC-breaking change in both Pico's and + `PicoDeprecated`'s changelog. Please note that BC-breaking changes + are only possible with a new major version. + +### Version 2.1.4 +Released: 2020-08-29 + +``` +* [Changed] Silence PHP errors in Parsedown +* [Fixed] #560: Improve charset guessing for formatted date strings using + `strftime()` (Pico always uses UTF-8, but `strftime()` might not) +``` + +### Version 2.1.3 +Released: 2020-07-10 + +``` +* [New] Add `locale` option to `config/config.yml` +* [Changed] Improve Pico docs +``` + +### Version 2.1.2 +Released: 2020-04-10 + +``` +* [Fixed] Fix DummyPlugin declaring API version 3 +``` + +### Version 2.1.1 +Released: 2019-12-31 + +``` +* [Fixed] Require Parsedown 1.8.0-beta-7 and Parsedown Extra 0.8.0-beta-1 due + to changes in Parsedown and Parsedown Extra breaking BC beyond repair +* [Changed] #523: Check for hidden pages based on page ID instead of full paths +* [Changed] Improve Pico docs +``` + +### Version 2.1.0 +Released: 2019-11-24 + +``` +* [Changed] Add Pico's official logo and tagline to `content-sample/_meta.md` +* [Changed] Improve `content-sample/theme.md` to show Pico's official logo and + the usage of the new image utility classes of Pico's default theme +* [Changed] Improve Pico docs and PHPDoc class docs +``` + +### Version 2.1.0-beta.1 +Released: 2019-11-03 + +``` +* [New] Introduce API version 3 +* [New] Add `assets_dir`, `assets_url` and `plugins_url` config params +* [New] Add `%config.*%` Markdown placeholders for scalar config params and the + `%assets_url%`, `%themes_url%` and `%plugins_url%` placeholders +* [New] Add `content-sample/theme.md` for theme testing purposes +* [New] Introduce API versioning for themes and support theme-specific configs + using the new `pico-theme.yml` in a theme's directory; `pico-theme.yml` + allows a theme to influence Pico's Twig config, to register known meta + headers and to provide defaults for theme config params +* [New] Add `assets_url`, `themes_url` and `plugins_url` Twig variables +* [New] Add `pages` Twig function to deal with Pico's page tree; this function + replaces the raw usage of Pico's `pages` array in themes +* [New] Add `url` Twig filter to replace URL placeholders (e.g. `%base_url%`) + in strings using the new `Pico::substituteUrl()` method +* [New] Add `onThemeLoading` and `onThemeLoaded` events +* [New] Add `debug` config param and the `Pico::isDebugModeEnabled()` method, + checking the `PICO_DEBUG` environment variable, to enable debugging +* [New] Add new `Pico::getNormalizedPath()` method to normalize a path; this + method should be used to prevent content dir breakouts when dealing + with paths provided by user input +* [New] Add new `Pico::getUrlFromPath()` method to guess a URL from a file path +* [New] Add new `Pico::getAbsoluteUrl()` method to make a relative URL absolute +* [New] #505: Create pre-built `.zip` release archives +* [Fixed] #461: Proberly handle content files with a UTF-8 BOM +* [Changed] Rename `theme_url` config param to `themes_url`; the `theme_url` + Twig variable and Markdown placeholder are kept unchanged +* [Changed] Update to Parsedown Extra 0.8 and Parsedown 1.8 (both still beta) +* [Changed] Enable Twig's `autoescape` feature by default; outputting a + variable now causes Twig to escape HTML markup; Pico's `content` + variable is a notable exception, as it is marked as being HTML safe +* [Changed] Rename `prev_page` Twig variable to `previous_page` +* [Changed] Mark `markdown` and `content` Twig filters as well as the `content` + variable as being HTML safe +* [Changed] Add `$singleLine` param to `markdown` Twig filter as well as the + `Pico::parseFileContent()` method to parse just a single line of + Markdown input +* [Changed] Add `AbstractPicoPlugin::configEnabled()` method to check whether + a plugin should be enabled or disabled based on Pico's config +* [Changed] Deprecate the use of `AbstractPicoPlugin::__call()`, use + `PicoPluginInterface::getPico()` instead +* [Changed] Update to Twig 1.36 as last version supporting PHP 5.3, use a + Composer-based installation to use a newer Twig version +* [Changed] Add `$basePath` and `$endSlash` params to `Pico::getAbsolutePath()` +* [Changed] Deprecate `Pico::getBaseThemeUrl()` +* [Changed] Replace various `file_exists` calls with proper `is_file` calls +* [Changed] Refactor release & build system +* [Changed] Improve Pico docs and PHPDoc class docs +* [Changed] Various small improvements +* [Removed] Remove superfluous `base_dir` and `theme_dir` Twig variables +* [Removed] Remove `PicoPluginInterface::__construct()` +``` + +### Version 2.0.5-beta.1 +Released: 2019-01-03 + +``` +* [New] Add PHP 7.3 tests +* [New] Add `2.0.x-dev` alias for master branch to `composer.json` +* [Changed] Update to Parsedown Extra 0.8 and Parsedown 1.8 (both still beta) +* [Changed] Improve release & build process +``` + +### Version 2.0.4 +Released: 2018-12-17 + +``` +* [Fixed] Proberly handle hostnames with ports in `Pico::getBaseUrl()` +* [Changed] Improve documentation +``` + +### Version 2.0.3 +Released: 2018-12-03 + +``` +* [Fixed] Support alternative server ports in `Pico::getBaseUrl()` +* [Changed] Don't require server environment variables to be configured +* [Changed] Improve release & build process +* [Changed] Improve documentation +* [Changed] Improve PHP class docs +* [Changed] Various small improvements +``` + +### Version 2.0.2 +Released: 2018-08-12 + +``` +* [Fixed] Support Windows paths (`\` instead of `/`) in `Pico::evaluateRequestUrl()` +``` + +### Version 2.0.1 +Released: 2018-07-29 + +``` +* [Changed] Improve documentation +* [Changed] Add missing "Formatted Date", "Time" and "Hidden" meta headers; use + the "Hidden" meta header to manually hide a page in the pages list +``` + +### Version 2.0.0 +Released: 2018-07-01 + +``` +* [New] Add Bountysource +* [Changed] Improve documentation +* [Changed] Improve release & build process +* [Changed] Add `Pico::setConfig()` example to `index.php.dist` +* [Fixed] Don't load `config/config.yml` multiple times +``` + +### Version 2.0.0-beta.3 +Released: 2018-04-07 + +``` +* [Changed] Add `README.md`, `CONTRIBUTING.md` and `CHANGELOG.md` of main repo + to pre-bundled releases, keep `.gitignore` +* [Changed] Deny access to a possibly existing `composer.phar` in `.htaccess` +* [Changed] Disallow the use of the `callback` filter for the `url_param` and + `form_param` Twig functions +* [Changed] Improve documentation +* [Fixed] Fix page tree when sorting pages by arbitrary values +* [Fixed] Fix sorting of `Pico::$nativePlugins` +``` + +### Version 2.0.0-beta.2 +Released: 2018-01-21 + +``` +* [New] Improve release & build process and move most build tools to the new + `picocms/ci-tools` repo, allowing them to be used by other projects +* [New] Add page tree; refer to the `Pico::buildPageTree()` method for more + details; also see the `onPageTreeBuilt` event +* [Changed] Update dependencies: Twig 1.35 +* [Changed] ! Improve `.htaccess` and deny access to all dot files by default +* [Changed] ! Throw a `RuntimeException` when non-native plugins are loaded, + but Pico's `PicoDeprecated` plugin is not loaded +* [Changed] ! Change `AbstractPicoPlugin::$enabled`'s behavior: setting it to + TRUE now leads to throwing a `RuntimeException` when the plugin's + dependencies aren't fulfilled; use NULL to maintain old behavior +* [Changed] ! Force themes to use `.twig` as file extension for Twig templates +* [Changed] Improve PHP class docs +* [Changed] Various small improvements +``` + +### Version 2.0.0-beta.1 +Released: 2017-11-05 + +``` +* [New] Pico is on its way to its second major release! +* [New] Improve Pico's release & build process +* [New] Add "Developer Certificate of Origin" to `CONTRIBUTING.md` +* [New] Add license & copyright header to all relevant files +* [New] Add Pico version constants (`Pico::VERSION` and `Pico::VERSION_ID`), + and add a `version` Twig variable and `%version%` Markdown placeholder +* [New] Add Pico API versioning for plugins (see `Pico::API_VERSION` constant); + Pico now triggers events on plugins using the latest API version only + ("native" plugins), `PicoDeprecated` takes care of all other plugins; + as a result, old plugin's always depend on `PicoDeprecated` now +* [New] Add a theme and plugin installer for composer; Pico now additionally + uses the new `vendor/pico-plugin.php` file to discover plugins + installed by composer and loads them using composer's autoloader; + see the `picocms/composer-installer` repo for more details; Pico + loads plugins installed by composer first and ignores conflicting + plugins in Pico's `plugins/` dir +* [New] Add `$enableLocalPlugins` parameter to `Pico::__construct()` to allow + website developers to disable local plugin discovery by scanning the + `plugins/` dir (i.e. load plugins from `vendor/pico-plugin.php` only) +* [New] Add public `AbstractPicoPlugin::getPluginConfig()` method +* [New] Add public `Pico::loadPlugin()` method and the corresponding + `onPluginManuallyLoaded` event +* [New] Add public `Pico::resolveFilePath()` method (replaces the protected + `Pico::discoverRequestFile()` method) +* [New] Add public `Pico::is404Content()` method +* [New] Add public `Pico::getYamlParser()` method and the corresponding + `onYamlParserRegistered` event +* [New] Add public `Pico::substituteFileContent()` method +* [New] Add public `Pico::getPageId()` method +* [New] Add public `Pico::getFilesGlob()` method +* [New] Add public `Pico::getVendorDir()` method, returning Pico's installation + directory (i.e. `/var/www/pico/vendor/picocms/pico`); don't confuse + this with composer's `vendor/` dir! +* [New] Add `$default` parameter to `Pico::getConfig()` method +* [New] Add empty `assets/` and `content/` dirs +* [New] #305: Add `url_param` and `form_param` Twig functions, and the public + `Pico::getUrlParameter()` and `Pico::getFormParameter()` methods, + allowing theme developers to access URL GET and HTTP POST parameters +* [New] Add `$meta` parameter to `markdown` Twig filter +* [New] Add `remove` fallback to `sort_by` Twig filter +* [New] Add `theme_url` config parameter +* [New] Add public `Pico::getBaseThemeUrl()` method +* [New] Add `REQUEST_URI` routing method, allowing one to simply rewrite all + requests to `index.php` (e.g. use `FallbackResource` or `mod_rewrite` + in your `.htaccess` for Apache, or use `try_files` for nginx) +* [New] #299: Add built-in 404 page as fallback when no `404.md` is found +* [New] Allow sorting pages by arbitrary meta values +* [New] Add `onSinglePageLoading` event, allowing one to skip a page +* [New] Add `onSinglePageContent` event +* [New] Add some config parameters to change Parsedown's behavior +* [Changed] ! Disallow running the same Pico instance multiple times by + throwing a `RuntimeException` when calling `Pico::run()` +* [Changed] ! #203: Load plugins from `plugins//.php` + and `plugins/.php` only (directory and file name must + match case-sensitive), and throw a `RuntimeException` when Pico is + unable to load a plugin; also throw a `RuntimeException` when + superfluous files or directories in `plugins/` are found; use a + scope-isolated `require()` to include plugin files +* [Changed] ! Use a plugin dependency topology to sort `Pico::$plugins`, + changing the execution order of plugins so that plugins, on which + other plugins depend, are always executed before their dependants +* [Changed] ! Don't pass `$plugins` parameter to `onPluginsLoaded` event by + reference anymore; use `Pico::loadPlugin()` instead +* [Changed] ! Leave `Pico::$pages` unsorted when a unknown sort method was + configured; this usually means that a plugin wants to sort it +* [Changed] Overhaul page discovery events: add `onPagesDiscovered` event which + is triggered right before `Pico::$pages` is sorted and move the + `$currentPage`, `$previousPage` and `$nextPage` parameters of the + `onPagesLoaded` event to the new `onCurrentPageDiscovered` event +* [Changed] Move the `$twig` parameter of the `onPageRendering` event to the + `onTwigRegistered` event, replacing the `onTwigRegistration` event +* [Changed] Unify the `onParsedownRegistration` event by renaming it to + `onParsedownRegistered` and add the `$parsedown` parameter +* [Changed] #330: Replace `config/config.php` by a modular YAML-based approach; + you can now use a arbitrary number of `config/*.yml` files to + configure Pico +* [Changed] ! When trying to auto-detect Pico's `content` dir, Pico no longer + searches just for a (possibly empty) directory, but rather checks + whether a `index.md` exists in this directory +* [Changed] ! Use the relative path between `index.php` and `Pico::$themesDir` + for Pico's theme URL (also refer to the new `theme_url` config and + the public `Pico::getBaseThemeUrl()` method for more details) +* [Changed] #347: Drop the superfluous trailing "/index" from Pico's URLs +* [Changed] Flip registered meta headers array, so that the array key is used + to search for a meta value and the array value is used to store the + found meta value (previously it was the other way round) +* [Changed] ! Add lazy loading for `Pico::$yamlParser`, `Pico::$parsedown` and + `Pico::$twig`; the corresponding events are no longer part of + Pico's event flow and are triggered on demand +* [Changed] ! Trigger the `onMetaHeaders` event just once; the event is no + longer part of Pico's event flow and is triggered on demand +* [Changed] Don't lower meta headers on the first level of a page's meta data + (i.e. `SomeKey: value` is accessible using `$meta['SomeKey']`) +* [Changed] Don't compare registered meta headers case-insensitive, require + matching case +* [Changed] Allow users to explicitly set values for the `date_formatted` and + `time` meta headers in a page's YAML front matter +* [Changed] Add page siblings for all pages +* [Changed] ! Treat pages or directories that are prefixed by `_` as hidden; + when requesting a hidden page, Pico responds with a 404 page; + hidden pages are still in `Pico::$pages`, but are moved to the end + of the pages array when sorted alphabetically or by date +* [Changed] ! Don't treat explicit requests to a 404 page as successful request +* [Changed] Change method visibility of `Pico::getFiles()` to public +* [Changed] Change method visibility of `Pico::triggerEvent()` to public; + at first glance this method triggers events on native plugins only, + however, `PicoDeprecated` takes care of triggering events for other + plugins, thus you can use this method to trigger (custom) events on + all plugins; never use it to trigger Pico core events! +* [Changed] Move Pico's default theme to the new `picocms/pico-theme` repo; the + theme was completely rewritten from scratch and is a much better + starting point for creating your own theme; refer to the theme's + `CHANGELOG.md` for more details +* [Changed] Move `PicoDeprecated` plugin to the new `picocms/pico-deprecated` + repo; refer to the plugin's `CHANGELOG.md` for more details +* [Changed] Update dependencies: Twig 1.34, Symfony YAML 2.8, Parsedown 1.6 +* [Changed] Improve Pico docs and PHP class docs +* [Changed] A vast number of small improvements and changes... +* [Removed] ! Remove `PicoParsePagesContent` plugin +* [Removed] ! Remove `PicoExcerpt` plugin +* [Removed] Remove `rewrite_url` and `is_front_page` Twig variables +* [Removed] Remove superfluous parameters of various events to reduce Pico's + error-proneness (plugins hopefully interfere with each other less) +``` + +### Version 1.0.6 +Released: 2017-07-25 + +``` +* [Changed] Improve documentation +* [Changed] Improve handling of Pico's Twig config (`$config['twig_config']`) +* [Changed] Improve PHP platform requirement checks +``` + +### Version 1.0.5 +Released: 2017-05-02 + +``` +* [Changed] Improve documentation +* [Fixed] Improve hostname detection with proxies +* [Fixed] Fix detection of Windows-based server environments +* [Removed] Remove Twitter links +``` + +### Version 1.0.4 +Released: 2016-10-04 + +``` +* [New] Add Pico's social icons to default theme +* [Changed] Improve documentation +* [Changed] Add CSS flexbox rules to default theme +* [Fixed] Fix handling of non-YAML 1-line front matters +* [Fixed] Fix responsiveness in default theme +``` + +### Version 1.0.3 +Released: 2016-05-11 + +``` +* [Changed] Improve documentation +* [Changed] Heavily extend nginx configuration docs +* [Changed] Add CSS rules for definition lists to default theme +* [Changed] Always use `on404Content...` execution path when serving a `404.md` +* [Changed] Deny access to `.git` directory, `CHANGELOG.md`, `composer.json` + and `composer.lock` (`.htaccess` file) +* [Changed] Use Pico's `404.md` to deny access to `.git`, `config`, `content`, +* `content-sample`, `lib` and `vendor` dirs (`.htaccess` file) +* [Fixed] #342: Fix responsiveness in default theme +* [Fixed] #344: Improve HTTPS detection with proxies +* [Fixed] #346: Force HTTPS to load Google Fonts in default theme +``` + +### Version 1.0.2 +Released: 2016-03-16 + +``` +* [Changed] Various small improvements and changes... +* [Fixed] Check dependencies when a plugin is enabled by default +* [Fixed] Allow `Pico::$requestFile` to point to somewhere outside `content_dir` +* [Fixed] #336: Fix `Date` meta header parsing with ISO-8601 datetime strings +``` + +### Version 1.0.1 +Released: 2016-02-27 + +``` +* [Changed] Improve documentation +* [Changed] Replace `version_compare()` with `PHP_VERSION_ID` in + `index.php.dist` (available since PHP 5.2.7) +* [Fixed] Suppress PHP warning when using `date_default_timezone_get()` +* [Fixed] #329: Force Apache's `MultiViews` feature to be disabled +``` + +### Version 1.0.0 +Released: 2015-12-24 + +``` +* [New] On Christmas Eve, we are happy to announce Pico's first stable release! + The Pico Community wants to thank all contributors and users who made + this possible. Merry Christmas and a Happy New Year 2016! +* [New] Adding `$queryData` parameter to `Pico::getPageUrl()` method +* [Changed] Improve documentation +* [Changed] Moving `LICENSE` to `LICENSE.md` +* [Changed] Throw `LogicException` instead of `RuntimeException` when calling + `Pico::setConfig()` after processing has started +* [Changed] Default theme now highlights the current page and shows pages with + a title in the navigation only +* [Changed] #292: Ignore YAML parse errors (meta data) in `Pico::readPages()` +* [Changed] Various small improvements and changes... +* [Fixed] Support empty meta header +* [Fixed] #307: Fix path handling on Windows +``` + +### Version 1.0.0-beta.2 +Released: 2015-11-30 + +``` +* [New] Introducing the `PicoTwigExtension` Twig extension +* [New] New `markdown` filter for Twig to parse markdown strings; Note: If you + want to parse the contents of a page, use the `content` filter instead +* [New] New `sort_by` filter to sort an array by a specified key or key path +* [New] New `map` filter to get the values of the given key or key path +* [New] Introducing `index.php.dist` (used for pre-bundled releases) +* [New] Use PHP_CodeSniffer to auto-check source code (see `.phpcs.xml`) +* [New] Use Travis CI to generate phpDocs class docs automatically +* [Changed] Improve documentation +* [Changed] Improve table styling in default theme +* [Changed] Update composer version constraints; almost all dependencies will + have pending updates, run `composer update` +* [Changed] Throw a RuntimeException when the `content` dir isn't accessible +* [Changed] Reuse `ParsedownExtra` object; new `onParsedownRegistration` event +* [Changed] `$config['rewrite_url']` is now always available +* [Changed] `DummyPlugin` class is now final +* [Changed] Remove `.git` dirs from `vendor/` when deploying +* [Changed] Various small improvements and changes... +* [Fixed] `PicoDeprecated`: Sanitize `content_dir` and `base_url` options when + reading `config.php` in Picos root dir +* [Fixed] Replace `urldecode()` (deprecated RFC 1738) with `rawurldecode()` + (RFC 3986) in `Page::evaluateRequestUrl()` +* [Fixed] #272: Encode URLs using `rawurlencode()` in `Pico::getPageUrl()` +* [Fixed] #274: Prevent double slashes in `base_url` +* [Fixed] #285: Make `index.php` work when installed as a composer dependency +* [Fixed] #291: Force `Pico::$requestUrl` to have no leading/trailing slash +``` + +### Version 1.0.0-beta.1 +Released: 2015-11-06 + +``` +* [Security] (9e2604a) Prevent content_dir breakouts using malicious URLs +* [New] Pico is on its way to its first stable release! +* [New] Provide pre-bundled releases +* [New] Heavily expanded documentation (inline code docs, user docs, dev docs) +* [New] New routing system using the QUERY_STRING method; Pico now works + out-of-the-box with any webserver and without URL rewriting; use + `%base_url%?sub/page` in markdown files and `{{ "sub/page"|link }}` + in Twig templates to declare internal links +* [New] Brand new plugin system with dependencies (see `PicoPluginInterface` + and `AbstractPicoPlugin`); if you're plugin dev, you really should + take a look at the UPGRADE section of the docs! +* [New] Introducing the `PicoDeprecated` plugin to maintain full backward + compatibility with Pico 0.9 and Pico 0.8 +* [New] Support YAML-style meta header comments (`---`) +* [New] Various new placeholders to use in content files (e.g. `%site_title%`) +* [New] Provide access to all meta headers in content files (`%meta.*%`) +* [New] Provide access to meta headers in `$page` arrays (`$page['meta']`) +* [New] The file extension of content files is now configurable +* [New] Add `Pico::setConfig()` method to predefine config variables +* [New] Supporting per-directory `404.md` files +* [New] #103: Providing access to `sub.md` even when the `sub` directory + exists, provided that there is no `sub/index.md` +* [New] #249: Support the `.twig` file extension for templates +* [New] #268, 269: Now using Travis CI; performing basic code tests and + implementing an automatic release process +* [Changed] Complete code refactoring +* [Changed] Source code now follows PSR code styling +* [Changed] Replacing constants (e.g. `ROOT_DIR`) with constructor parameters +* [Changed] Paths (e.g. `content_dir`) are now relative to Pico's root dir +* [Changed] Adding `Pico::run()` method that performs Pico's processing and + returns the rendered contents +* [Changed] Renaming all plugin events; adding some new events +* [Changed] `Pico_Plugin` is now the fully documented `DummyPlugin` +* [Changed] Meta data must start on the first line of the file now +* [Changed] Dropping the need to register meta headers for the convenience of + users and pure (!) theme devs; plugin devs are still REQUIRED to + register their meta headers during `onMetaHeaders` +* [Changed] Exclude inaccessible files from pages list +* [Changed] With alphabetical order, index files (e.g. `sub/index.md`) are + now always placed before their sub pages (e.g. `sub/foo.md`) +* [Changed] Pico requires PHP >= 5.3.6 (due to `erusev/parsedown-extra`) +* [Changed] Pico now implicitly uses a existing `content` directory without + the need to configure this in the `config/config.php` explicitly +* [Changed] Composer: Require a v0.7 release of `erusev/parsedown-extra` +* [Changed] Moving `license.txt` to `LICENSE` +* [Changed] Moving and reformatting `changelog.txt` to `CHANGELOG.md` +* [Changed] #116: Parse meta headers using the Symfony YAML component +* [Changed] #244: Replace opendir() with scandir() +* [Changed] #246: Move `config.php` to `config/` directory +* [Changed] #253: Assume HTTPS if page is requested through port 443 +* [Changed] A vast number of small improvements and changes... +* [Fixed] Sorting by date now uses timestamps and works as expected +* [Fixed] Fixing `$currentPage`, `$nextPage` and `$previousPage` +* [Fixed] #99: Support content filenames with spaces +* [Fixed] #140, #241: Use file paths as page identifiers rather than titles +* [Fixed] #248: Always set a timezone; adding `$config['timezone']` option +* [Fixed] A vast number of small bugs... +* [Removed] Removing the default Twig cache dir +* [Removed] Removing various empty `index.html` files +* [Removed] Removing `$pageData['excerpt']`; recoverable with `PicoExcerpt` +* [Removed] #93, #158: Pico doesn't parse all content files anymore; moved to + `PicoParsePagesContent`; i.e. `$pageData['content']` doesn't exist + anymore, use `$pageData['raw_content']` when possible; otherwise + use Twigs new `content` filter (e.g. `{{ "sub/page"|content }}`) +``` + +### Version 0.9 +Released: 2015-04-28 + +``` +* [New] Default theme is now mobile-friendly +* [New] Description meta now available in content areas +* [New] Add description to composer.json +* [Changed] content folder is now content-sample +* [Changed] config.php moved to config.php.template +* [Changed] Updated documentation & wiki +* [Changed] Removed Composer, Twig files in /vendor, you must run composer + install now +* [Changed] Localized date format; strftime() instead of date() +* [Changed] Added ignore for tmp file extensions in the get_files() method +* [Changed] michelf/php-markdown is replaced with erusev/parsedown-extra +* [Changed] $config is no global variable anymore +* [Fixed] Pico now only removes the 1st comment block in .md files +* [Fixed] Issue wherein the alphabetical sorting of pages did not happen +``` + +### Version 0.8 +Released: 2013-10-23 + +``` +* [New] Added ability to set template in content meta +* [New] Added before_parse_content and after_parse_content hooks +* [Changed] content_parsed hook is now deprecated +* [Changed] Moved loading the config to nearer the beginning of the class +* [Changed] Only append ellipsis in limit_words() when word count exceeds max +* [Changed] Made private methods protected for better inheritance +* [Fixed] Fixed get_protocol() method to work in more situations +``` + +### Version 0.7 +Released: 2013-09-04 + +``` +* [New] Added before_read_file_meta and get_page_data plugin hooks to customize + page meta data +* [Changed] Make get_files() ignore dotfiles +* [Changed] Make get_pages() ignore Emacs and temp files +* [Changed] Use composer version of Markdown +* [Changed] Other small tweaks +* [Fixed] Date warnings and other small bugs +``` + +### Version 0.6.2 +Released: 2013-05-07 + +``` +* [Changed] Replaced glob_recursive with get_files +``` + +### Version 0.6.1 +Released: 2013-05-07 + +``` +* [New] Added "content" and "excerpt" fields to pages +* [New] Added excerpt_length config setting +``` + +### Version 0.6 +Released: 2013-05-06 + +``` +* [New] Added plugin functionality +* [Changed] Other small cleanup +``` + +### Version 0.5 +Released: 2013-05-03 + +``` +* [New] Added ability to order pages by "alpha" or "date" (asc or desc) +* [New] Added prev_page, current_page, next_page and is_front_page template vars +* [New] Added "Author" and "Date" title meta fields +* [Changed] Added "twig_config" to settings +* [Changed] Updated documentation +* [Fixed] Query string 404 bug +``` + +### Version 0.4.1 +Released: 2013-05-01 + +``` +* [New] Added CONTENT_EXT global +* [Changed] Use .md files instead of .txt +``` + +### Version 0.4 +Released: 2013-05-01 + +``` +* [New] Add get_pages() function for listing content +* [New] Added changelog.txt +* [Changed] Updated default theme +* [Changed] Updated documentation +``` + +### Version 0.3 +Released: 2013-04-27 + +``` +* [Fixed] get_config() function +``` + +### Version 0.2 +Released: 2013-04-26 + +``` +* [Changed] Updated Twig +* [Changed] Better checking for HTTPS +* [Fixed] Add 404 header to 404 page +* [Fixed] Case sensitive folder bug +``` + +### Version 0.1 +Released: 2012-04-04 + +``` +* Initial release +``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..9d37588 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,209 @@ +Contributing to Pico +==================== + +Pico aims to be a high quality Content Management System (CMS) but at the same time wants to give contributors freedom when submitting fixes or improvements. + +By contributing to Pico, you accept and agree to the *Developer Certificate of Origin* for your present and future contributions submitted to Pico. Please refer to the *Developer Certificate of Origin* section below. + +Aside from this, we want to *encourage*, but not obligate you, the contributor, to follow the following guidelines. The only exception to this are the guidelines elucidated in the *Prevent `merge-hell`* section. Having said that: we really appreciate it when you apply the guidelines in part or wholly as that will save us time which, in turn, we can spend on bugfixes and new features. + +Issues +------ + +If you want to report an *issue* with Pico's core, please create a new [Issue](https://github.com/picocms/Pico/issues) on GitHub. Concerning problems with plugins or themes, please refer to the website of the developer of this plugin or theme. + +Before creating a [new Issue on GitHub](https://github.com/picocms/Pico/issues/new), please make sure the problem wasn't reported yet using [GitHubs search engine](https://github.com/picocms/Pico/search?type=Issues). + +Please describe your issue as clear as possible and always include the *Pico version* you're using. Provided that you're using *plugins*, include a list of them too. We need information about the *actual and expected behavior*, the *steps to reproduce* the problem, and what steps you have taken to resolve the problem by yourself (i.e. *your own troubleshooting*). + +Contributing +------------ + +Once you decide you want to contribute to *Pico's core* (which we really appreciate!) you can fork the project from https://github.com/picocms/Pico. If you're interested in developing a *plugin* or *theme* for Pico, please refer to the [development section](http://picocms.org/development/) of our website. + +### Developer Certificate of Origin + +By contributing to Pico, you accept and agree to the following terms and conditions for your present and future contributions submitted to Pico. Except for the license granted herein to Pico and recipients of software distributed by Pico, you reserve all right, title, and interest in and to your contributions. All contributions are subject to the following DCO + license terms. + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +All contributions to this project are licensed under the following MIT License: + +``` +Copyright (c) + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +``` + +Please note that developing a *plugin* or *theme* for Pico is *not* assumed to be a contribution to Pico itself. By developing a plugin or theme you rather create a 3rd-party project that just uses Pico. Following the spirit of open source, we want to *encourage* you to release your plugin or theme under the terms of a [OSI-approved open source license](https://opensource.org/licenses). After all, Pico is open source, too! + +### Prevent `merge-hell` + +Please do *not* develop your contribution on the `master` branch of your fork, but create a separate feature branch, that is based off the `master` branch, for each feature that you want to contribute. + +> Not doing so means that if you decide to work on two separate features and place a pull request for one of them, that the changes of the other issue that you are working on is also submitted. Even if it is not completely finished. + +To get more information about the usage of Git, please refer to the [Pro Git book](https://git-scm.com/book) written by Scott Chacon and/or [this help page of GitHub](https://help.github.com/articles/using-pull-requests). + +### Pull Requests + +Please keep in mind that pull requests should be small (i.e. one feature per request), stick to existing coding conventions and documentation should be updated if required. It's encouraged to make commits of logical units and check for unnecessary whitespace before committing (try `git diff --check`). Please reference issue numbers in your commit messages where appropriate. + +### Coding Standards + +Pico uses the [PSR-2 Coding Standard](http://www.php-fig.org/psr/psr-2/) as defined by the [PHP Framework Interoperability Group (PHP-FIG)](http://www.php-fig.org/). + +For historical reasons we don't use formal namespaces. Markdown files in the `content-sample` folder (the inline documentation) must follow a hard limit of 80 characters line length. + +It is recommended to check your code using [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) using Pico's `.phpcs.xml` standard. Use the following command: + + $ ./vendor/bin/phpcs --standard=.phpcs.xml [file]... + +With this command you can specify a file or folder to limit which files it will check or omit that argument altogether, in which case the current working directory is checked. + +### Keep documentation in sync + +Pico accepts the problems of having redundant documentation on different places (concretely Pico's inline user docs, the `README.md` and the website) for the sake of a better user experience. When updating the docs, please make sure to keep them in sync. + +If you update the [`README.md`](https://github.com/picocms/Pico/blob/master/README.md) or [`content-sample/index.md`](https://github.com/picocms/Pico/blob/master/content-sample/index.md), please make sure to update the corresponding files in the [`_docs`](https://github.com/picocms/picocms.github.io/tree/master/_docs/) folder of the `picocms.github.io` repo (i.e. [Pico's website](http://picocms.org/docs/)) and vice versa. Unfortunately this involves three (!) different markdown parsers. If you're experiencing problems, use Pico's [`erusev/parsedown-extra`](https://github.com/erusev/parsedown-extra) as a reference. You can try to make the contents compatible to [Kramdown](http://kramdown.gettalong.org/) (Pico's website) and [CommonMarker](https://github.com/gjtorikian/commonmarker) (`README.md`) by yourself, otherwise please address the issues in your pull request message and we'll take care of it. + +Versioning +---------- + +Pico follows [Semantic Versioning 2.0](http://semver.org) and uses version numbers like `MAJOR`.`MINOR`.`PATCH`. We will increment the: + +- `MAJOR` version when we make incompatible API changes, +- `MINOR` version when we add functionality in a backwards-compatible manner, and +- `PATCH` version when we make backwards-compatible bug fixes. + +For more information please refer to the http://semver.org website. + +Branching +--------- + +The `master` branch contains the current development version of Pico. It is likely *unstable* and *not ready for production use*. + +However, the `master` branch always consists of a deployable (but not necessarily deployed) version of Pico. As soon as development of a new `MAJOR` or `MINOR` release starts, a separate branch (e.g. `pico-1.1`) is created and a [pull request](https://github.com/picocms/Pico/pulls) is opened to receive the desired feedback. + +Pico's actual development happens in separate development branches. Development branches are prefixed by: + +- `feature/` for bigger features, +- `enhancement/` for smaller improvements, and +- `bugfix/` for non-trivial bug fixes. + +As soon as development reaches a point where feedback is appreciated, a pull request is opened. After some time (very soon for bug fixes, and other improvements should have a reasonable feedback phase) the pull request is merged and the development branch will be deleted. Trivial bug fixes that will be part of the next `PATCH` version will be merged directly into `master`. + +Build & Release process +----------------------- + +We're using [Travis CI](https://travis-ci.com) to automate the build & release process of Pico. It generates and deploys a [PHP class documentation](http://picocms.org/phpDoc/) (powered by [phpDoc](http://phpdoc.org)) for new releases and on every commit to the `master` branch. Travis also prepares new releases by generating Pico's pre-built release packages, a version badge, code statistics (powered by [cloc](https://github.com/AlDanial/cloc)) and updates our website (the [`picocms.github.io` repo](https://github.com/picocms/picocms.github.io)). Please refer to our [`.travis.yml`](https://github.com/picocms/Pico/blob/master/.travis.yml), the [`picocms/ci-tools` repo](https://github.com/picocms/ci-tools) and the [`.build` directory](https://github.com/picocms/Pico/tree/master/.build) for details. + +As insinuated above, it is important that each commit to `master` is deployable. Once development of a new Pico release is finished, trigger Pico's build & release process by pushing a new Git tag. This tag should reference a (usually empty) commit on `master`, which message should adhere to the following template: + +``` +Version 1.0.0 + +* [Security] ... +* [New] ... +* [Changed] ... +* [Fixed] ... +* [Removed] ... +``` + +Before pushing a new Git tag, make sure to update the `Pico::VERSION` and `Pico::VERSION_ID` constants. The versions of Pico's official [default theme](https://github.com/picocms/pico-theme) and the [`PicoDeprecated` plugin](https://github.com/picocms/pico-deprecated) both strictly follow Pico's version. Since Pico's pre-built release package contains them, you must always create a new release of them (even though nothing has changed) before creating a new Pico release. + +If you're pushing a new major or minor release of Pico, you should also update Pico's `composer.json` to require the latest minor releases of Pico's dependencies. Besides, don't forget to update the `@version` tags in the PHP class docs. + +Travis CI will draft the new [release on GitHub](https://github.com/picocms/Pico/releases) automatically, but will require you to manually amend the descriptions formatting. The latest Pico version is always available at https://github.com/picocms/Pico/releases/latest, so please make sure to publish this URL rather than version-specific URLs. [Packagist](http://packagist.org/packages/picocms/pico) will be updated automatically. + +Labeling of Issues & Pull Requests +---------------------------------- + +Pico makes use of GitHub's label and milestone features, to aide developers in quickly identifying and prioritizing which issues need to be worked on. The starting point for labeling issues and pull requests is the `type` label, which is explained in greater detail below. The `type` label might get combined with a `pri` label, describing the issue's priority, and a `status` label, describing the current status of the issue. + +Issues and pull requests labeled with `info: Feedback Needed` indicate that feedback from others is highly appreciated. We always appreciate feedback at any time and from anyone, but when this label is present, we explicitly *ask* you to give feedback. It would be great if you leave a comment! + +- The `type: Bug` label is assigned to issues or pull requests, which have been identified as bugs or security issues in Pico's core. It might get combined with the `pri: High` label, when the problem was identified as security issue, or as a so-called "show stopper" bug. In contrast, uncritical problems might get labeled with `pri: Low`. `type: Bug` issues and pull requests are usually labeled with one of the following `status` labels when being closed: + - `status: Resolved` is used when the issue has been resolved. + - `status: Conflict` indicates a conflict with another issue or behavior of Pico, making it impossible to resolve the problem at the moment. + - `status: Won't Fix` means, that there is indeed a problem, but for some reason we made the decision that resolving it isn't reasonable, making it intended behavior. + - `status: Rejected` is used when the issue was rejected for another reason. + +- The `type: Enhancement` and `type: Feature` labels are used to tag pull requests, which introduce either a comparatively small enhancement, or a "big" new feature. As with the `type: Bug` label, they might get combined with the `pri: High` or `pri: Low` labels to indicate the pull request's priority. If a pull request isn't mergeable at the moment, it is labeled with `status: Work In Progress` until development of the pull request is finished. After merging or closing the pull request, it is labeled with one of the `status` labels as described above for the `type: Bug` label. + +- The `type: Idea` label is similar to the `type: Enhancement` and `type: Feature` labels, but is used for issues or incomplete and abandoned pull requests. It is otherwise used in the exact same way as `type: Enhancement` and `type: Feature`. + +- The `type: Release` label is used in the exact same way as `type: Feature` and indicates the primary pull request of a new Pico release (please refer to the *Branching* and *Build & Release process* sections above). + +- The `type: Notice`, `type: Question` and `type: Discussion` labels are used to indicate "fyi" issues, issues opened by users or developers asking questions, and issues with disucssions about arbitrary topics related to Pico. They are neither combined with `pri` labels, nor with `status` labels. + +- The `type: Duplicate` label is used when there is already another issue or pull request related to this problem or feature request. Issues labeled with `type: Duplicate` are immediately closed. + +- The `type: Invalid` label is used for everything else, e.g. issues or pull requests not related to Pico, or invalid bug reports. This includes supposed bug reports that concern actually intended behavior. + +The `status: Deferred` label might get added to any open issue or pull request to indicate that it is still unresolved and will be resolved later. This is also true for the `info: Pinned` label: It indicates a important issue or pull request that remains open on purpose. + +After resolving a issue, we usually keep it open for about a week to give users some more time for feedback and further questions. This is especially true for issues with the `type: Notice`, `type: Question`, `type: Discussion` and `type: Invalid` labels. After 7 days with no interaction, [Probot](https://probot.github.io/)'s [Stale](https://github.com/apps/stale) bot adds the `info: Stale` label to the issue to ask the participants whether the issue has been resolved. If no more activity occurs, the issue will be automatically closed by Stale bot 2 days later. + +Issues and pull requests labeled with `info: Information Needed` indicate that we have asked one of the participants for further information and didn't receive any feedback yet. It is usually added after Stale bot adds the `info: Stale` label to give the participants some more days to give the necessary information. + +Issues and pull requests, which are rather related to upstream projects (i.e. projects Pico depends on, like Twig), are additionally labeled with `info: Upstream`. + +When a issue or pull request isn't directly related to Pico's core, but the project as a whole, it is labeled with `info: Meta`. Issues labeled with `info: Website` are related to [Pico's website](http://picocms.org), however, in this case it is usually expedient to move the issue to the [`picocms.github.io` repo](https://github.com/picocms/picocms.github.io) instead. The same applies to the `info: Pico CMS for Nextcloud` label; these issues are related to [Pico CMS for Nextcloud](https://apps.nextcloud.com/apps/cms_pico). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..91a34cf --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2012 The Pico Community + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1987dd0 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +## Link do sajta: + +*[Pico CMS (dokumentacija, plugins, themes)](https://picocms.org/)* + +#### Markdown Guide +[Basic Syntax](https://www.markdownguide.org/basic-syntax/) | [Extended Syntax](https://www.markdownguide.org/extended-syntax/) | [Hitra referenca - "plonklistek"](https://www.markdownguide.org/cheat-sheet/) + +#### Katerikoli video embed + +Deluje povsod v vsebini, razen v stranski vrstici, kjer je še vedno potrebno kopirat posamezne embede, ki so navedeni nižje. + +``` +[video *LINK*] +``` + +Primeri: +``` +[video https://kolektiva.media/videos/embed/a5c6b8e1-6e9e-4a89-ace4-03b6cf97ce07] +[video https://kolektiva.media/w/mtiCL28GP9gzLC2FPKjs6v] +[video https://tube.mfraters.net/videos/watch/9a2b78db-ef05-4770-9196-8a1fcd243f43] +[video https://vimeo.com/566821117] +[video https://player.vimeo.com/video/566821117] +[video https://youtu.be/NkdUs2Dxv4s] +[video https://www.youtube.com/watch?v=NkdUs2Dxv4s] +[video https://www.youtube.com/embed/NkdUs2Dxv4s] +``` + +#### PeerTube Video embed +Primer video. Pod videom klikneš na gumb "Share" in izbereš zavihek "Embed", ki ti generira kodo z za embedanje, ki jo potem vstaviš v pejđev markdown. če rabiš več kostumizacije je v "Embed" zavihku spodaj možnost "More customization". +**Lepši embed čez vso širino elementa** dobiš če iframe oviješ z `
` ter `
` in pa ifram-u NE določiš "width" ter "height". +primer: `
` +#### YouTube Video embed +navodila: +**Lepši embed čez vso širino elementa:** *na enak način kot v zgornjem primeru za PeerTube* + +#### Vimeo Video embed +
+**Primer:**
diff --git a/README_Pico.md b/README_Pico.md new file mode 100644 index 0000000..bca6f45 --- /dev/null +++ b/README_Pico.md @@ -0,0 +1,257 @@ +Pico +==== + +[![License](https://picocms.github.io/badges/pico-license.svg)](https://github.com/picocms/Pico/blob/master/LICENSE.md) +[![Version](https://picocms.github.io/badges/pico-version.svg)](https://github.com/picocms/Pico/releases/latest) +[![Build Status](https://api.travis-ci.org/picocms/Pico.svg?branch=master)](https://travis-ci.org/picocms/Pico) +[![Freenode IRC Webchat](https://picocms.github.io/badges/pico-chat.svg)](https://webchat.freenode.net/?channels=%23picocms) +[![Open Bounties on Bountysource](https://www.bountysource.com/badge/team?team_id=198139&style=bounties_received)](https://www.bountysource.com/teams/picocms) + +Pico is a stupidly simple, blazing fast, flat file CMS. + +Visit us at http://picocms.org/ and see http://picocms.org/about/ for more info. + +Screenshot +---------- + +![Pico Screenshot](https://picocms.github.io/screenshots/pico-21.png) + +Install +------- + +Installing Pico is dead simple - and done in seconds! If you have access to a shell on your server (i.e. SSH access), we recommend using [Composer][]. If not, use a pre-bundled release. If you don't know what "SSH access" is, head over to the pre-bundled release. 😇 + +Pico requires PHP 5.3.6+ and the PHP extensions `dom` and `mbstring` to be enabled. + +### I want to use Composer + +Starting with Pico 2.0 we recommend installing Pico using Composer whenever possible. Trust us, you won't regret it when it comes to upgrading Pico! Anyway, if you don't want to use Composer, or if you simply can't use Composer because you don't have access to a shell on your server, don't despair, installing Pico using a pre-bundled release is still easier than everything you know! + +###### Step 1 + +Open a shell and navigate to the `httpdocs` directory (e.g. `/var/www/html`) of your server. Download Composer and run it with the `create-project` option to install it to the desired directory (e.g. `/var/www/html/pico`): + +```shell +$ curl -sSL https://getcomposer.org/installer | php +$ php composer.phar create-project picocms/pico-composer pico +``` + +###### Step 2 + +What second step? There's no second step. That's it! Open your favorite web browser and navigate to your brand new, stupidly simple, blazing fast, flat file CMS! Pico's sample contents will explain how to create your own contents. 😊 + +### I want to use a pre-bundled release + +Do you know the feeling: You want to install a new website, so you upload all files of your favorite CMS and run the setup script - just to find out that you forgot about creating the SQL database first? Later the setup script tells you that the file permissions are wrong. Heck, what does this even mean? Forget about it, Pico is different! + +###### Step 1 + +[Download the latest Pico release][LatestRelease] and upload all files to the desired install directory of Pico within the `httpdocs` directory (e.g. `/var/www/html/pico`) of your server. + +###### Step 2 + +Okay, here's the catch: There's no catch. That's it! Open your favorite web browser and navigate to your brand new, stupidly simple, blazing fast, flat file CMS! Pico's sample contents will explain how to create your own contents. 😊 + +### I want to manage my website using a Git repository + +Git is a very powerful distributed version-control system - and it can be used to establish a nice workflow around your Pico website. Using a Git repository for your website aids content creation and deployment, including collaborative editing and version control. If you want to manage your website in a Git repository, you use a Composer-based installation. + +1. Fork [Pico's Composer starter project][PicoComposerGit] using [GitHub's fork button][HelpFork]. If you don't want to use GitHub you aren't required to, you can choose whatever Git server you want. Forking manually just requires some extra steps: First clone the Git repository locally, add your Git server as a remote and push the repository to this new remote. + +2. Clone your fork locally and add your contents and assets. You can edit Pico's `composer.json` to include 3rd-party plugins and themes, or simply add your own plugins and themes to Pico's `plugins` resp. `themes` directories. Don't forget to commit your changes and push them to your Git server. + +3. Open a shell on your webserver and navigate to the `httpdocs` directory (e.g. `/var/www/html`). Download Composer, clone your Git repository to the desired directory (e.g. `/var/www/html/pico`) and install Pico's dependencies using Composer's `install` option: + + ```shell + $ curl -sSL https://getcomposer.org/installer | php + $ git clone https://github.com// pico + $ php composer.phar --working-dir=pico install + ``` + +4. If you update your website's contents, simply commit your changes and push them to your Git server. Open a shell on your webserver and navigate to Pico's install directory within the `httpdocs` directory (e.g. `/var/www/html/pico`) of your server. Pull all changes from your Git server and update Pico's dependencies using Composer's `update` option: + + ```shell + $ git pull + $ php composer.phar update + ``` + +### I'm a developer + +So, you're one of these amazing people making all of this possible? We love you folks! As a developer we recommend you to clone [Pico's Git repository][PicoGit] as well as the Git repositories of [Pico's default theme][PicoThemeGit] and the [`PicoDeprecated` plugin][PicoDeprecatedGit]. You can set up your workspace using [Pico's Composer starter project][PicoComposerGit] and include all of Pico's components using local packages. + +Using Pico's Git repositories is different from using one of the installation methods elucidated above. It gives you the current development version of Pico, what is likely *unstable* and *not ready for production use*! + +1. Open a shell and navigate to the desired directory of Pico's development workspace within the `httpdocs` directory (e.g. `/var/www/html/pico`) of your server. Download and extract Pico's Composer starter project into the `workspace` directory: + + ```shell + $ curl -sSL https://github.com/picocms/pico-composer/archive/master.tar.gz | tar xz + $ mv pico-composer-master workspace + ``` + +2. Clone the Git repositories of all Pico components (Pico's core, Pico's default theme and the `PicoDeprecated` plugin) into the `components` directory: + + ```shell + $ mkdir components + $ git clone https://github.com/picocms/Pico.git components/pico + $ git clone https://github.com/picocms/pico-theme.git components/pico-theme + $ git clone https://github.com/picocms/pico-deprecated.git components/pico-deprecated + ``` + +3. Instruct Composer to use the local Git repositories as replacement for the `picocms/pico` (Pico's core), `picocms/pico-theme` (Pico's default theme) and `picocms/pico-deprecated` (the `PicoDeprecated` plugin) packages. Update the `composer.json` of your development workspace (i.e. `workspace/composer.json`) accordingly: + + ```json + { + "repositories": [ + { + "type": "path", + "url": "../components/pico", + "options": { "symlink": true } + }, + { + "type": "path", + "url": "../components/pico-theme", + "options": { "symlink": true } + }, + { + "type": "path", + "url": "../components/pico-deprecated", + "options": { "symlink": true } + } + ], + "require": { + "picocms/pico": "dev-master", + "picocms/pico-theme": "dev-master", + "picocms/pico-deprecated": "dev-master", + "picocms/composer-installer": "^1.0" + } + } + ``` + +4. Download Composer and run it with the `install` option: + + ```shell + $ curl -sSL https://getcomposer.org/installer | php + $ php composer.phar --working-dir=workspace install + ``` + +You can now open your web browser and navigate to Pico's development workspace. All changes you make to Pico's components will automatically be reflected in the development workspace. + +By the way, you can also find all of Pico's components on [Packagist.org][Packagist]: [Pico's core][PicoPackagist], [Pico's default theme][PicoThemePackagist], the [`PicoDeprecated` plugin][PicoDeprecatedPackagist] and [Pico's Composer starter project][PicoComposerPackagist]. + +Upgrade +------- + +Do you remember when you installed Pico? It was ingeniously simple, wasn't it? Upgrading Pico is no difference! The upgrade process differs depending on whether you used [Composer][] or a pre-bundled release to install Pico. Please note that you should *always* create a backup of your Pico installation before upgrading! + +Pico follows [Semantic Versioning 2.0][SemVer] and uses version numbers like `MAJOR`.`MINOR`.`PATCH`. When we update the `PATCH` version (e.g. `2.0.0` to `2.0.1`), we made backwards-compatible bug fixes. If we change the `MINOR` version (e.g. `2.0` to `2.1`), we added functionality in a backwards-compatible manner. Upgrading Pico is dead simple in both cases. Simply head over to the appropiate Upgrade sections below. + +But wait, we forgot to mention what happens when we update the `MAJOR` version (e.g. `2.0` to `3.0`). In this case we made incompatible API changes. We will then provide a appropriate upgrade tutorial, so please head over to the ["Upgrade" page on our website][HelpUpgrade]. + +### I've used Composer to install Pico + +Upgrading Pico is dead simple if you've used Composer to install Pico. Simply open a shell and navigate to Pico's install directory within the `httpdocs` directory (e.g. `/var/www/html/pico`) of your server. You can now upgrade Pico using just a single command: + +```shell +$ php composer.phar update +``` + +That's it! Composer will automatically update Pico and all plugins and themes you've installed using Composer. Please make sure to manually update all plugins and themes you've installed manually. + +### I've used a pre-bundled release to install Pico + +Okay, installing Pico was easy, but upgrading Pico is going to be hard, isn't it? I'm afraid I have to disappoint you. It's just as simple as installing Pico! + +First you'll have to delete the `vendor` directory of your Pico installation (e.g. if you've installed Pico to `/var/www/html/pico`, delete `/var/www/html/pico/vendor`). Then [download the latest Pico release][LatestRelease] and upload all files to your existing Pico installation directory. You will be prompted whether you want to overwrite files like `index.php`, `.htaccess`, ... - simply hit "Yes". + +That's it! Now that Pico is up-to-date, you need to update all plugins and themes you've installed. + +### I'm a developer + +As a developer you should know how to stay up-to-date... 😉 For the sake of completeness, if you want to upgrade Pico, simply open a shell and navigate to Pico's development workspace (e.g. `/var/www/html/pico`). Then pull the latest commits from the Git repositories of [Pico's core][PicoGit], [Pico's default theme][PicoThemeGit] and the [`PicoDeprecated` plugin][PicoDeprecatedGit]. Let Composer update your dependencies and you're ready to go. + +```shell +$ git -C components/pico pull +$ git -C components/pico-theme pull +$ git -C components/pico-deprecated pull +$ php composer.phar --working-dir=workspace update +``` + +Getting Help +------------ + +#### Getting Help as a user + +If you want to get started using Pico, please refer to our [user docs][HelpUserDocs]. Please read the [upgrade notes][HelpUpgrade] if you want to upgrade from Pico 1.0 to Pico 2.0. You can find officially supported [plugins][OfficialPlugins] and [themes][OfficialThemes] on our website. A greater choice of third-party plugins and themes can be found in our [Wiki][] on the [plugins][WikiPlugins] or [themes][WikiThemes] pages respectively. If you want to create your own plugin or theme, please refer to the "Getting Help as a developer" section below. + +#### Getting Help as a developer + +If you're a developer, please refer to the "Contributing" section below and our [contribution guidelines][ContributionGuidelines]. To get you started with creating a plugin or theme, please read the [developer docs on our website][HelpDevDocs]. + +#### You still need help or experience a problem with Pico? + +When the docs can't answer your question, you can get help by joining us on [#picocms on Freenode IRC][Freenode] ([logs][FreenodeLogs]). When you're experiencing problems with Pico, please don't hesitate to create a new [Issue][Issues] on GitHub. Concerning problems with plugins or themes, please refer to the website of the developer of this plugin or theme. + +**Before creating a new Issue,** please make sure the problem wasn't reported yet using [GitHubs search engine][IssuesSearch]. Please describe your issue as clear as possible and always include the *Pico version* you're using. Provided that you're using *plugins*, include a list of them too. We need information about the *actual and expected behavior*, the *steps to reproduce* the problem, and what steps you have taken to resolve the problem by yourself (i.e. *your own troubleshooting*). + +Contributing +------------ + +You want to contribute to Pico? We really appreciate that! You can help make Pico better by [contributing code][PullRequests] or [reporting issues][Issues], but please take note of our [contribution guidelines][ContributionGuidelines]. In general you can contribute in three different areas: + +1. Plugins & Themes: You're a plugin developer or theme designer? We love you folks! You can find tons of information about how to develop plugins and themes at http://picocms.org/development/. If you have created a plugin or theme, please add it to our [Wiki][], either on the [plugins][WikiPlugins] or [themes][WikiThemes] page. You may also [Submit][] it to our website, where it'll be displayed on the official [plugin][OfficialPlugins] or [theme][OfficialThemes] pages! + +2. Documentation: We always appreciate people improving our documentation. You can either improve the [inline user docs][EditInlineDocs] or the more extensive [user docs on our website][EditUserDocs]. You can also improve the [docs for plugin and theme developers][EditDevDocs]. Simply fork our website's Git repository from https://github.com/picocms/picocms.github.io, change the Markdown files and open a [pull request][PullRequestsWebsite]. + +3. Pico's Core: The supreme discipline is to work on Pico's Core. Your contribution should help *every* Pico user to have a better experience with Pico. If this is the case, fork Pico from https://github.com/picocms/Pico and open a [pull request][PullRequests]. We look forward to your contribution! + +By contributing to Pico, you accept and agree to the *Developer Certificate of Origin* for your present and future contributions submitted to Pico. Please refer to the ["Developer Certificate of Origin" section in our `CONTRIBUTING.md`][ContributionGuidelinesDCO]. + +You don't have time to contribute code to Pico, but still want to "stand a coffee" for the ones who do? You can contribute monetary to Pico using [Bountysource][], a crowd funding website that focuses on individual issues and feature requests. Just refer to the "Bounties and Fundraisers" section below for more info. + +Bounties and Fundraisers +------------------------ + +Pico uses [Bountysource][] to allow monetary contributions to the project. Bountysource is a crowd funding website that focuses on individual issues and feature requests in Open Source projects using micropayment. Users, or "Backers", can pledge money for fixing a specific issue, implementing new features, or developing a new plugin or theme. Open source software developers, or "Bounty Hunters", can then pick up and solve these tasks to earn the money. + +Obviously this won't allow a developer to replace a full time job, it's rather aiming to "stand a coffee". However, it helps bringing users and developers closer together, and shows developers what users want and how much they care about certain things. Nevertheless you can still donate money to the project itself, as an easy way to say "Thank You" and to support Pico. + +If you want to encourage developers to [fix a specific issue][Issues] or implement a feature, simply [pledge a new bounty][Bountysource] or back an existing one. + +As a developer you can pick up a bounty by simply contributing to Pico (please refer to the "Contributing" section above). You don't have to be a official Pico Contributor! Pico is a Open Source project, anyone can open [pull requests][PullRequests] and claim bounties. + +Official Pico Contributors won't claim bounties on their own behalf, Pico will never take any money out of Bountysource. All money collected by Pico is used to pledge new bounties or to support projects Pico depends on. + +[Composer]: https://getcomposer.org/ +[LatestRelease]: https://github.com/picocms/Pico/releases/latest +[PicoGit]: https://github.com/picocms/Pico +[PicoThemeGit]: https://github.com/picocms/pico-theme +[PicoDeprecatedGit]: https://github.com/picocms/pico-deprecated +[PicoComposerGit]: https://github.com/picocms/pico-composer +[Packagist]: https://packagist.org/ +[PicoPackagist]: https://packagist.org/packages/picocms/pico +[PicoThemePackagist]: https://packagist.org/packages/picocms/pico-theme +[PicoDeprecatedPackagist]: https://packagist.org/packages/picocms/pico-deprecated +[PicoComposerPackagist]: https://packagist.org/packages/picocms/pico-composer +[SemVer]: http://semver.org +[HelpFork]: https://help.github.com/en/github/getting-started-with-github/fork-a-repo +[HelpUpgrade]: http://picocms.org/in-depth/upgrade/ +[HelpUserDocs]: http://picocms.org/docs/ +[HelpDevDocs]: http://picocms.org/development/ +[Submit]: http://picocms.org/in-depth/submission_guidelines +[OfficialPlugins]: http://picocms.org/plugins/ +[OfficialThemes]: http://picocms.org/themes/ +[Wiki]: https://github.com/picocms/Pico/wiki +[WikiPlugins]: https://github.com/picocms/Pico/wiki/Pico-Plugins +[WikiThemes]: https://github.com/picocms/Pico/wiki/Pico-Themes +[Issues]: https://github.com/picocms/Pico/issues +[IssuesSearch]: https://github.com/picocms/Pico/search?type=Issues +[Freenode]: https://webchat.freenode.net/?channels=%23picocms +[FreenodeLogs]: http://picocms.org/irc-logs +[PullRequests]: https://github.com/picocms/Pico/pulls +[PullRequestsWebsite]: https://github.com/picocms/picocms.github.io/pulls +[ContributionGuidelines]: https://github.com/picocms/Pico/blob/master/CONTRIBUTING.md +[ContributionGuidelinesDCO]: https://github.com/picocms/Pico/blob/master/CONTRIBUTING.md#developer-certificate-of-origin +[EditInlineDocs]: https://github.com/picocms/Pico/edit/master/content-sample/index.md +[EditUserDocs]: https://github.com/picocms/picocms.github.io/tree/master/_docs +[EditDevDocs]: https://github.com/picocms/picocms.github.io/tree/master/_development +[Bountysource]: https://www.bountysource.com/teams/picocms diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..5526b3d --- /dev/null +++ b/composer.json @@ -0,0 +1,41 @@ +{ + "name": "picocms/pico-composer", + "type": "project", + "description": "Pico is a flat file CMS, this means there is no administration backend and database to deal with. You simply create .md files in the \"content\" folder and that becomes a page.", + "keywords": [ "pico", "picocms", "pico-cms", "simple", "flat-file", "cms", "content-management", "website", "markdown-to-html", "php", "markdown", "yaml", "twig", "composer-project" ], + "homepage": "http://picocms.org/", + "license": "MIT", + "authors": [ + { + "name": "Daniel Rudolf", + "email": "picocms.org@daniel-rudolf.de", + "role": "Lead Developer" + }, + { + "name": "The Pico Community", + "homepage": "http://picocms.org/" + }, + { + "name": "Contributors", + "homepage": "https://github.com/picocms/pico-composer/graphs/contributors" + } + ], + "support": { + "docs": "http://picocms.org/docs", + "issues": "https://github.com/picocms/Pico/issues", + "source": "https://github.com/picocms/Pico" + }, + "require": { + "picocms/pico": "^2.1", + "picocms/pico-theme": "^2.1", + "picocms/pico-deprecated": "^2.1", + "picocms/composer-installer": "^1.0" + }, + "minimum-stability": "beta", + "prefer-stable": true, + "config": { + "allow-plugins": { + "picocms/composer-installer": true + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..13bd627 --- /dev/null +++ b/composer.lock @@ -0,0 +1,617 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "c572de982113b017edeaa4b6ec598816", + "packages": [ + { + "name": "erusev/parsedown", + "version": "1.8.0-beta-7", + "source": { + "type": "git", + "url": "https://github.com/erusev/parsedown.git", + "reference": "fe7a50eceb4a3c867cc9fa9c0aa906b1067d1955" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/fe7a50eceb4a3c867cc9fa9c0aa906b1067d1955", + "reference": "fe7a50eceb4a3c867cc9fa9c0aa906b1067d1955", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35" + }, + "type": "library", + "autoload": { + "psr-0": { + "Parsedown": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "description": "Parser for Markdown.", + "homepage": "http://parsedown.org", + "keywords": [ + "markdown", + "parser" + ], + "support": { + "issues": "https://github.com/erusev/parsedown/issues", + "source": "https://github.com/erusev/parsedown/tree/1.8.0-beta-7" + }, + "time": "2019-03-17T18:47:21+00:00" + }, + { + "name": "erusev/parsedown-extra", + "version": "0.8.0-beta-1", + "source": { + "type": "git", + "url": "https://github.com/erusev/parsedown-extra.git", + "reference": "e756b1bf8642ab1091403e902b0503f1cec7527d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/e756b1bf8642ab1091403e902b0503f1cec7527d", + "reference": "e756b1bf8642ab1091403e902b0503f1cec7527d", + "shasum": "" + }, + "require": { + "erusev/parsedown": "^1.8.0|^1.8.0-beta-4", + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=5.3.6" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35" + }, + "type": "library", + "autoload": { + "psr-0": { + "ParsedownExtra": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "description": "An extension of Parsedown that adds support for Markdown Extra.", + "homepage": "https://github.com/erusev/parsedown-extra", + "keywords": [ + "markdown", + "markdown extra", + "parsedown", + "parser" + ], + "support": { + "issues": "https://github.com/erusev/parsedown-extra/issues", + "source": "https://github.com/erusev/parsedown-extra/tree/master" + }, + "time": "2018-05-08T21:54:32+00:00" + }, + { + "name": "picocms/composer-installer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/picocms/composer-installer.git", + "reference": "6b5036c83aa091ed76e2a76ed9335885f95a7db7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/picocms/composer-installer/zipball/6b5036c83aa091ed76e2a76ed9335885f95a7db7", + "reference": "6b5036c83aa091ed76e2a76ed9335885f95a7db7", + "shasum": "" + }, + "type": "composer-installer", + "extra": { + "class": "Pico\\Composer\\Installer\\PluginInstaller" + }, + "autoload": { + "psr-4": { + "Pico\\Composer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Rudolf", + "email": "picocms.org@daniel-rudolf.de", + "role": "Lead Developer" + }, + { + "name": "The Pico Community", + "homepage": "http://picocms.org/" + }, + { + "name": "Contributors", + "homepage": "https://github.com/picocms/composer-installer/graphs/contributors" + } + ], + "description": "A composer plugin responsible for installing plugins and themes for Pico, a stupidly simple, blazing fast, flat file CMS.", + "homepage": "http://picocms.org/", + "keywords": [ + "composer", + "composer-installer", + "composer-plugin", + "pico", + "pico-cms", + "picocms", + "picocms-installer", + "picocms-plugin", + "picocms-theme" + ], + "support": { + "issues": "https://github.com/picocms/composer-installer/issues", + "source": "https://github.com/picocms/composer-installer" + }, + "time": "2019-11-24T22:50:47+00:00" + }, + { + "name": "picocms/pico", + "version": "v2.1.4", + "source": { + "type": "git", + "url": "https://github.com/picocms/Pico.git", + "reference": "7228129cade3f812f22904b503c939b04a75c9dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/picocms/Pico/zipball/7228129cade3f812f22904b503c939b04a75c9dd", + "reference": "7228129cade3f812f22904b503c939b04a75c9dd", + "shasum": "" + }, + "require": { + "erusev/parsedown": "1.8.0-beta-7", + "erusev/parsedown-extra": "0.8.0-beta-1", + "ext-mbstring": "*", + "php": ">=5.3.6", + "symfony/yaml": "^2.8", + "twig/twig": "^1.36" + }, + "suggest": { + "picocms/composer-installer": "This Composer plugin is responsible for installing Pico plugins and themes using the Composer package manager.", + "picocms/pico-deprecated": "PicoDeprecated's purpose is to maintain backward compatibility to older versions of Pico.", + "picocms/pico-theme": "Pico requires a theme to actually display the contents of your website. This is Pico's official default theme." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev", + "dev-pico-3.0": "3.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pico": "lib/", + "AbstractPicoPlugin": "lib/", + "PicoPluginInterface": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gilbert Pellegrom", + "email": "gilbert@pellegrom.me", + "role": "Project Founder" + }, + { + "name": "Daniel Rudolf", + "email": "picocms.org@daniel-rudolf.de", + "role": "Lead Developer" + }, + { + "name": "The Pico Community", + "homepage": "http://picocms.org/" + }, + { + "name": "Contributors", + "homepage": "https://github.com/picocms/Pico/graphs/contributors" + } + ], + "description": "Pico is a flat file CMS, this means there is no administration backend and database to deal with. You simply create .md files in the \"content\" folder and that becomes a page.", + "homepage": "http://picocms.org/", + "keywords": [ + "Simple", + "cms", + "content-management", + "flat-file", + "markdown", + "markdown-to-html", + "php", + "pico", + "pico-cms", + "picocms", + "twig", + "website", + "yaml" + ], + "support": { + "docs": "http://picocms.org/docs", + "issues": "https://github.com/picocms/Pico/issues", + "source": "https://github.com/picocms/Pico" + }, + "funding": [ + { + "url": "https://www.bountysource.com/teams/picocms", + "type": "custom" + } + ], + "time": "2020-08-29T14:15:52+00:00" + }, + { + "name": "picocms/pico-deprecated", + "version": "v2.1.4", + "source": { + "type": "git", + "url": "https://github.com/picocms/pico-deprecated.git", + "reference": "8d1a4056ecc71cc2857e91d50bcb38db91d424e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/picocms/pico-deprecated/zipball/8d1a4056ecc71cc2857e91d50bcb38db91d424e0", + "reference": "8d1a4056ecc71cc2857e91d50bcb38db91d424e0", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "picocms/pico": "self.version" + }, + "type": "pico-plugin", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev", + "dev-pico-3.0": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "PicoDeprecated.php", + "lib/", + "plugins/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Rudolf", + "email": "picocms.org@daniel-rudolf.de", + "role": "Lead Developer" + }, + { + "name": "The Pico Community", + "homepage": "http://picocms.org/" + }, + { + "name": "Contributors", + "homepage": "https://github.com/picocms/pico-deprecated/graphs/contributors" + } + ], + "description": "This is Pico's official PicoDeprecated plugin. Pico is a stupidly simple, blazing fast, flat file CMS.", + "homepage": "http://picocms.org/", + "keywords": [ + "compatibility", + "deprecation", + "pico", + "pico-deprecated", + "picocms", + "picocms-plugin" + ], + "support": { + "docs": "http://picocms.org/plugins/deprecated/", + "issues": "https://github.com/picocms/pico-deprecated/issues", + "source": "https://github.com/picocms/pico-deprecated" + }, + "time": "2020-08-29T15:11:07+00:00" + }, + { + "name": "picocms/pico-theme", + "version": "v2.1.4", + "source": { + "type": "git", + "url": "https://github.com/picocms/pico-theme.git", + "reference": "d4ec8df28356f1e034a97d37327b9aecb3129eed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/picocms/pico-theme/zipball/d4ec8df28356f1e034a97d37327b9aecb3129eed", + "reference": "d4ec8df28356f1e034a97d37327b9aecb3129eed", + "shasum": "" + }, + "require": { + "picocms/pico": "self.version" + }, + "type": "pico-theme", + "extra": { + "installer-name": "default", + "branch-alias": { + "dev-master": "2.1.x-dev", + "dev-pico-3.0": "3.0.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Rudolf", + "email": "picocms.org@daniel-rudolf.de", + "role": "Lead Developer" + }, + { + "name": "The Pico Community", + "homepage": "http://picocms.org/" + }, + { + "name": "Contributors", + "homepage": "https://github.com/picocms/pico-theme/graphs/contributors" + } + ], + "description": "This is Pico's official default theme. Pico is a stupidly simple, blazing fast, flat file CMS.", + "homepage": "http://picocms.org/", + "keywords": [ + "default-theme", + "pico", + "pico-theme", + "picocms", + "picocms-theme" + ], + "support": { + "docs": "http://picocms.org/themes/default/", + "issues": "https://github.com/picocms/pico-theme/issues", + "source": "https://github.com/picocms/pico-theme" + }, + "time": "2020-08-29T15:11:43+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/yaml", + "version": "v2.8.52", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "02c1859112aa779d9ab394ae4f3381911d84052b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/02c1859112aa779d9ab394ae4f3381911d84052b", + "reference": "02c1859112aa779d9ab394ae4f3381911d84052b", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "symfony/polyfill-ctype": "~1.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v2.8.52" + }, + "time": "2018-11-11T11:18:13+00:00" + }, + { + "name": "twig/twig", + "version": "v1.44.7", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "0887422319889e442458e48e2f3d9add1a172ad5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/0887422319889e442458e48e2f3d9add1a172ad5", + "reference": "0887422319889e442458e48e2f3d9add1a172ad5", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8" + }, + "require-dev": { + "psr/container": "^1.0", + "symfony/phpunit-bridge": "^4.4.9|^5.0.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.44-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + }, + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v1.44.7" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2022-09-28T08:38:36+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "beta", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/config/config.yml b/config/config.yml new file mode 100644 index 0000000..98423ad --- /dev/null +++ b/config/config.yml @@ -0,0 +1,61 @@ +## +# Basic +# +site_title: Proti militarizmu # The title of your website +site_subtitle: Za svet brez vojne +base_url: ~ # Pico will try to guess its base URL, if this fails, override it here; + # Example: https://example.com/pico/ +rewrite_url: ~ # A boolean (true or false) indicating whether URL rewriting is forced +debug: ~ # Set this to true to enable Pico's debug mode +timezone: ~ # Your PHP installation might require you to manually specify a timezone +locale: ~ # Your PHP installation might require you to manually specify a locale to use + +## +# Theme +# +theme: Travelify-tr00_popravljena # The name of your custom theme +themes_url: ~ # Pico will try to guess the URL to the themes dir of your installation; + # If this fails, override it here. Example: https://example.com/pico/themes/ +theme_config: # Additional theme-specific config + widescreen: false # Default theme: Use more horizontal space (i.e. make the site container wider) +twig_config: # Twig template engine config + autoescape: html # Let Twig escape variables by default + strict_variables: false # If set to true, Twig will bail out when unset variables are being used + charset: utf-8 # The charset used by Twig templates + debug: ~ # Enable Twig's debug mode + cache: .cache # Enable Twig template caching by specifying a path to a writable directory + auto_reload: true # Recompile Twig templates whenever the source code changes + +## +# Content +# +date_format: %d. %m. %Y # Pico's default date format; + # See https://php.net/manual/en/function.strftime.php for more info +pages_order_by_meta: author # Sort pages by meta value "author" (set "pages_order_by" to "meta") +pages_order_by: date # Change how Pico sorts pages ("alpha" for alphabetical order, "date", or "meta") +pages_order: desc # Sort pages in ascending ("asc") or descending ("desc") order +content_dir: ~ # The path to Pico's content directory +content_ext: .md # The file extension of your Markdown files +content_config: # Parsedown Markdown parser config + extra: true # Use the Parsedown Extra parser to support extended markup; + # See https://michelf.ca/projects/php-markdown/extra/ for more info + breaks: true # A boolean indicating whether breaks in the markup should be reflected in the + # parsed contents of the page + escape: false # Escape HTML markup in your content files; don't confuse this with some sort of + # safe mode, enabling this doesn't allow you to process untrusted user input! + auto_urls: true # Automatically link URLs found in your markup +assets_dir: assets/ # The path to Pico's assets directory +assets_url: ~ # Pico will try to guess the URL to the assets dir of your installation; + # If this fails, override it here. Example: https://example.com/pico/assets/ + +## +# Plugins +# +plugins_url: ~ # Pico will try to guess the URL to the plugins dir of your installation; + # If this fails, override it here. Example: https://example.com/pico/plugins/ +DummyPlugin.enabled: false # Force the plugin "DummyPlugin" to be disabled + +## +# Custom +# +#my_custom_setting: Hello World! # You can access custom settings in themes using {{ config.my_custom_setting }} diff --git a/content/404.md b/content/404.md new file mode 100644 index 0000000..e6ddf30 --- /dev/null +++ b/content/404.md @@ -0,0 +1,9 @@ +--- +Title: Error 404 +Robots: noindex,nofollow +--- + +Error 404 +========= + +Woops. Looks like this page doesn't exist. diff --git a/content/_meta.md b/content/_meta.md new file mode 100644 index 0000000..f98b4c1 --- /dev/null +++ b/content/_meta.md @@ -0,0 +1,14 @@ +--- +Logo: %theme_url%/img/pico-white.svg +Tagline: Pico CMS for Nextcloud in action. +Social: + - title: Visit us on GitHub + url: https://github.com/picocms/Pico + icon: octocat + - title: Join us on Freenode IRC Webchat + url: https://webchat.freenode.net/?channels=%23picocms + icon: chat + - title: Help us by creating/collecting bounties and pledging to fundraisers + url: https://www.bountysource.com/teams/picocms + icon: dollar +--- diff --git a/content/index.md b/content/index.md new file mode 100644 index 0000000..5680ed1 --- /dev/null +++ b/content/index.md @@ -0,0 +1,7 @@ +--- +Title: Pobuda proti militarizmu +Description: Pobuda proti militarizmu +Template: index-novice +--- + + diff --git a/content/materiali.md b/content/materiali.md new file mode 100644 index 0000000..e5e5bb6 --- /dev/null +++ b/content/materiali.md @@ -0,0 +1,18 @@ +--- +Title: Materiali +Description: Plakati, nalepke in drugi materiali za boj proti militarizmu +--- + +**PLAKATI** + + +![Ivo Boskarol](https://protimilitarizmu.kompot.si/assets/IVO-plakat1.png) +
  + + +![Marko Pelhan](https://protimilitarizmu.kompot.si/assets/MARKO-plakat.png) +
+ +![Tim Castagne](https://protimilitarizmu.kompot.si/assets/TIM-plakat.png) +
+ diff --git a/content/navbar.md b/content/navbar.md new file mode 100644 index 0000000..f7abd5c --- /dev/null +++ b/content/navbar.md @@ -0,0 +1,7 @@ +--- +title: Navigation bar +--- + + - [Domov](%base_url%) + - [O pobudi?](%base_url%?pobuda) + - [Materiali](%base_url%?materiali) diff --git a/content/novice/vojna-je-tu.md b/content/novice/vojna-je-tu.md new file mode 100644 index 0000000..659b312 --- /dev/null +++ b/content/novice/vojna-je-tu.md @@ -0,0 +1,40 @@ +--- +Title: Vojna je tu +Description: Izjava in poziv za podporo protimilitarističnemu protestu 4. novembra 2023 v Tržiču/Monfalconeju. +Date: 2023-11-01 +Teaser: krt.jpg +--- +*Izjava in poziv za podporo protimilitarističnemu protestu 4. novembra 2023 v Tržiču/Monfalconeju* + +## Vojna je tu! + +Vojne ta hip potekajo v številnih delih sveta. Omenimo zgolj Sirijo, Južni Sudan, Etiopijo, Mjanmar in seveda še vedno Ukrajino ter Palestino/Izrael. Na smrtonosni seznam praktično vsakih nekaj mesecev dodajamo imena novih geografij. Ne glede na vse poskuse normalizacije, nobena od teh vojn ni naravna katastrofa, ki se pač zgodi in na katero nimamo nobenega vpliva. Vojna nikoli zgolj ne izbruhne, temveč se morajo tisti, ki imajo od nje korist, pogosto precej potruditi, da vzpostavijo pogoje, v katerih postane možna. Velik del vzpostavljanja teh pogojev poteka daleč stran od bojišč, v centrih moči, med drugim v strukturah Evropske unije in zveze NATO. S članstvom v teh interesnih združenjih smo potisnjeni v to, da si priznamo, da je vojna še kako tudi naša stvar. + +Pripadniki in pripadnice slovenske vojske so trenutno na vojaških misijah na večih delih sveta, pretežno na, kot pravijo, danes strateško pomembnem t.i. Zahodnem Balkanu oziroma na področju nekdanje Jugoslavije. V preteklosti so bili v vlogi okupacijskih enot v Iraku in Afganistanu. Bili so v navidez opazovalni vlogi v Libanonu, Siriji, v nejasni vlogi v Maliju in verjetno še kje. Danes so po uradnih virih prisotni na Kosovu, v Bosni in Herecegovini, v Srbiji in v Severni Makedoniji. Prisotnost ima različne značaje, od opazovalnega, obveščevalnega, a vsaj na Kosovu predvideva tudi možnost neposrednega sodelovanja v oboroženih spopadih. Dodatno je slovenska vojska kot del članstva v zvezi NATO razporejena na teritorijih Slovaške in Latvije kot del "NATO vzhodnega boka". Z orožjem je pomagala iraškim in afganistanskim oblastem v službi okupacijskih sil, nazadnje izdatno tudi Ukrajini z obilno pošiljko težkega orožja. Slovenija je v vojni. + +Investicije v vojsko, torej nakupovanje orožja, so redno na sporedu vsake slovenske vlade, ne glede na politične stranke na oblasti. Če je prejšnja vlada pod vodstvom Janeza Janše za obdobje od leta 2021 do 2026 namenila 780 milijonov evrov, jo je aktualna vlada pod vodstvom Roberta Goloba celo prekosila in potrdila nove investicije v obdobju do leta 2026 v višini več kot milijarde evrov. Floskule o zagotavljanju varnosti prikrivajo, da gre v resnici tudi pri nas v prvi vrsti za posel. Čeprav nas poskušajo s pomočjo različnih propagandnih prijemov prepričati v nujnost obstoja vojske, je njen smisel predvsem v vojni industriji, v nekro kapitalu, torej, kot se za kapitalizem spodobi, v obračanju in kopičenju denarja pri elitah, v tem primeru dobesedno preko trupel. + +Tudi v Sloveniji je na tem področju kar nekaj nadebudnih oportunistov, saj se je letos spomladi na javni razpis Ministrstva za obrambo (MORS) odzvalo več kot 40 slovenskih podjetij, ki so pripravljena sodelovati pri proizvodnji streliva. Informacija o tem, katera podjetja so to in ali so bili nato kakšni posli dejansko sklenjeni, je bilo zaenkrat javnosti zamolčano. Orožarski biznis je, kot pravi sam MORS, v domeni tajnosti, gradiva imajo pogosto oznako “interno”, še posebej, ko se javni denar pretaka v žepe zasebnih vojnih dobičkarjev. Po podatkih, ki so javno vendarle dostopni, je eden od njh podjetje Arex defense, katerega direktor Tim Castagne se ponosno hvali, da je bilo leto 2022, torej leto vojne v Ukrajini, za njegovo podjetje "najboljše leto v [naši] zgodovini1". Posli očitno cvetijo tudi drugim lokalnim proizvajalcem in prekupčevalcem, o čemer priča eno od redkih sporočil za javnost Ministrstva za obrambo2, ki tu in tam vendarle odstrejo tančice s tajnih poslov. V letu 2022 je pod postavko “Izvoz in prenos vojaškega orožja in opreme” evidentiran znesekveč kot 36 in pol milijonov evrov, kar predstavlja več kot dvakratno povečanje v primerjavi z letom 2020, ko je ta znašal malo več kot 17 milijonov evrov. V letu 2022 so slovenska podjetja orožje ali dele za njegovo izdelovanje izvažala tudi v Izrael. Vojna je tudi tu, vojni dobičkarji pa tudi. + +Kljub lastnim piarovskim naporom in oblastni podpori militarizmu se Slovenska vojska še vedno spopada z dvomi o upravičenosti njenega obstoja. Ljudje, ki si z delom dnevno skušajo zagotoviti materialne pogoje za preživetje, ne vidijo ugodnosti vojnega razdejanja, katerega prvi glasnik je vojska. Tudi zaradi tega postajata vojna in vojaška propaganda vse bolj agresivna: vojaški sejmi, novačenje v srednjih šolah in na fakultetah, promocijske kampanje, nenazadnje je tudi uporabo vojske na meji v obdobju od 2015 dalje potrebno razumeti v smislu normalizacije vojaške prisotnosti. Nič čudnega ni, da se v zadnjem času toliko govori tudi o razpisu za vojaški resničnostni šov. Ta je nazoren del široke piarovske kampanje, s katero se z javnim denarjem gradi privlačnost vojne za mlade. Veliki oglasni panoji, video spoti, aplikacije, zasedanje javnih prostorov so le posamični izrazi normalizacije ideje militaristične družbe, ki vsepovsod vse bolj agresivno vstopa v vse sfere življenja. Vojna je tudi pri nas, za vojno nas ves čas pripravljajo. + +Vojna je torej vsakdanja realnost za milijone ljudi po svetu in v njej polno sodeluje tudi Slovenija z vso svojo politično, vojaško in orožarsko močjo. Ta je morda v absolutnem merilu minorna, toda vseeno obstaja in vpliva na ostale vidike družbe. Vsaka investicija v vojno namreč pomeni, da sredstva ne gredo v izobraževanje ali socialne transferje, vsaka misija v tujini pomeni dodatna sredstva, ki jih ni v zdravstveni blagajni, praktično vsaka “humanitarna misija” pomeni le uveljavljanje neke interesne sfere, vsaka politična podpora genocidu pomeni normalizacijo sveta, v katerem je ta legitimen, in vsaka kriminalizacija organiziranja proti vojni pomeni korak globlje v avtoritarno družbo. + +Teror okupacijskih oboroženih sil izraelske države nad prebivalci in prebivalkami Palestine je vojno v vseh svojih grozljivih razsežnostih po vsega nekaj tednih relativnega premora ponovno vrnil na naslovnice. Potem ko smo bili priče “preselitvi” armenskega prebivalstva z enega državnega teritorija na drugega (o tem trenutno na naslovnicah ni ne duha ne sluha), zgroženi spremljamo novo in še bolj krvavo epizodo desetletja trajajočega kolonialnega pohoda izraelske države, ki ima vse značilnosti genocida nad palestinskim ljudstvom. Toda množična morija, ki se dogaja na drugi obali tudi našega Sredozemlja, ni zgolj stvar tistih, ki v njej umirajo ali pritiskajo na tipke oziroma sprožilce: vanjo so globoko vpletene ne samo ZDA, temveč tudi Evropska unija in tudi Slovenija pod taktirko predsednika vlade Roberta Goloba in ministrice za zunanje zadeve Tanje Fajon. Vojna, okupacija, etnično čiščenje in genocid v Palestini so tako tudi naša stvar – do nje se je treba opredeliti in pokazati, katere strukture v naših lokalnih okoljih tvorijo pogoje vojne povsod po svetu. + +Širom t.i. zahodnega sveta se vrstijo protesti, obsodbe, javne intervencije, ki od oblasti zahtevajo prekinitev podpore kolonialnemu projektu v Palestini in prekinitev ognja. Toda protesti za prekinitev napadov, za mir in pravico za vse, ki živijo v Palestini/Izraelu, so marsikje prepovedani, mediji in socialna omrežja cenzurirani. Vrstijo se primeri ljudi, ki izgubijo službe zgolj zato, ker izrazijo nekaj tako univerzalnega, kot je podpora premirju ali žalovanje za vsemi žrtvami. Pod krinko boja proti antisemitizmu so tarče napadov tisti, ki svoja imena in telesa javno zastavijo proti nadaljevanju apartheida, okupacije in genocida. Pred tem niso varni niti Judje, ki se povsod po t.i. zahodnem svetu množično oglašajo in zavračajo zločine, ki se vsakodnevno izvajajo v njihovem imenu. Tudi oni so aretirani, kaznovani, utišani, diskreditirani. Država Nemčija, ki je tako obremenjena zaradi svoje zgodovinske odgovornosti za holokavst, si denimo ponovno drzne judovskim organizacijam odvzemati pravico do javnega delovanja, določati, kaj Judje lahko in česa ne smejo. Tokrat ne smejo pozivati k miru in enakem spoštovanju življenj Palestincev in Palestink. Hkrati pa ljudi, ki iz Ruske federacije bežijo pred vojaško obveznostjo, vračamo nazaj, čeprav je njihov beg neposreden izraz nesodelovanja v vojni, v tem primeru proti Ukrajini in posredno proti NATO. To je le še en dokaz več, da v interesu elit ni končanje vojne in ukinitev njenih pogojev, pač nadaljevanje uničevanja in nasilja. + +Tudi v Sloveniji glasove odpora zoper podporo Slovenije vojni tako zaslišuje Sova, Ministrstvo za notranje zadeve ob očitni podpori vlade prebivalstvo tragikomično ustrahuje z domnevno nevarnostjo terorističnega napada in po vzoru Italije ponovno vpostavlja policijski nadzor na mejah. Nikoli zares zaključena psihoza in vojna proti terorizmu se je tako na velika vrata vrnila kot duh iz preteklosti, s katerim želijo elite svoje družbe ponovno podvreči izrednim razmeram. Tudi v Sloveniji smo priča krčevitim naporom za proizvodnjo podob teroristične grožnje. Ti bi bili smešni, če ne bi enkrat več zlovešče pričali o tem, kako zelo se elite požvižgajo na dejstva, ko je potrebno zadostiti svojim interesom. Ministrstvo za notranje zadeve in Ministrstvo za obrambo oz. policija in vojska tu delujeta složno. V kontekstu “zavezniške solidarnosti” slovenska vojska v NATO paktu prispeva svoj košček v mozaik smrtonosne vojne proti migrantom in migrantkam na Sredozemskem morju, slovenska policija pa na mejah države. Hkrati pa oblasti zlorabljajo »novo krizno situacijo« za vzpostavitev novih-starih avtoritarnih regulativ. Vojna je prav zares tudi tu. + +Vojna, orožje, genocid, represija, vse to niso neizogibne stvari. Tako kot kapitalizem ni neizogiben in samoumeven. Oba sta v nasprotju z interesi večine svetovnega prebivalstva in peljeta svet v pogubo. Zato ne želimo biti zgolj nemi opazovalci genocidov, ki se izvajajo na očeh celega sveta. Nasprotujemo porabi javnega denarja za oboroževanje, za vojno, za vojsko in policijo ter njuno infrastrukturo. Mir na svetu bom dosegli šele, ko bomo enkrat za vselej ukinili pogoje, ki vojno omogočajo. Ne želimo pa si kapitalističnega miru, ki za številne med nami prav tako pomeni življenje v pomanjkanju, vsakodnevno nasilje in izkoriščanje, hkrati pa marsikje pomeni tudi pripravo na novo vojno. + +Terjati mir danes konkretno pomeni terjati razgradnjo režima apartheida in okupacije v Palestini/Izraelu, prekinitev vseh vojaških intervencij v Ukrajini, razgradnjo vojaške industrije in razpustitev vojaških organizacij vseh vrst. Na lokalni ravni to med drugim pomeni odsotnost vojne propagande in nenehen boj zoper nacionalizem, patriarhat in vse ostale izraze ideje, da imamo ljudje različne stopnje pravic. + +Zato se 4. novembra 2023 pridružujemo antimilitarističnemu protestu v Tržiču. V regiji, ki jo je pred dobrimi stotimi leti zaznamovala vojna, ki naj bi končala vse vojne. Kjer še danes nekateri opevajo zmago in gradijo diskurz, da je vsaka vojna v zgodovini bila neizbežna in logična. Kjer še danes živijo nacionalizmi. Mi pa stojimo na strani drugačnih idej: smo na strani ukinitve meja, na strani sobivanja in sodelovanja, na strani vizije pokrajine, kjer se bodo v prihodnje lahko govorili številni jeziki, tudi tisti, ki prihajajo na novo, in gradnje skupnosti, ki ne bo temeljila na izkoriščevalskih odnosih. + +STOP MILITARIZACIJI! +PROTI OROŽARSKI INDUSTRIJI! +NE VOJNE, MIR NAM DAJTE! +STOP GENOCIDU V GAZI! + +Pobuda proti militarizmu \ No newline at end of file diff --git a/content/pobuda.md b/content/pobuda.md new file mode 100644 index 0000000..63e1135 --- /dev/null +++ b/content/pobuda.md @@ -0,0 +1,9 @@ +--- +Title: O pobudi +Description: mi smo +--- +Pobuda proti militarizmu je raziskovalno-akcijska platforma. Prvenstveno je posvečena celostnemu razumevanju sistema militarizma, delovanju njegovih lovk v lokalnem okolju in identifikaciji oseb, ki igrajo v njem pomembne vloge. Vse to z namenom njegove odprave. + +Za svet brez vojne, militarizma, za svet mnogoterih svetov. + +Kontakt: pobudaprotimilitarizmu afna riseup.net \ No newline at end of file diff --git a/content/sidebars/1_links.md b/content/sidebars/1_links.md new file mode 100644 index 0000000..eb017fb --- /dev/null +++ b/content/sidebars/1_links.md @@ -0,0 +1,7 @@ +--- +title: Povezave +--- + + +* [RŠ FAQ](https://static.radiostudent.si/faq/) +* [Reci NE NATO](http://aktiviraj-se.net/recinenato/)

\ No newline at end of file diff --git a/content/theme.md b/content/theme.md new file mode 100644 index 0000000..f1e5fe7 --- /dev/null +++ b/content/theme.md @@ -0,0 +1,154 @@ +--- +title: Theme Styling Test +hidden: true +--- + +Theme Styling Test +================== + +This is `theme.md` in Pico's content directory. This page doesn't show up in the website's menu due to `hidden: true` in the page's YAML header. The purpose of this page is to aid theme development - below you'll find basically every format that is possible with Markdown. If you develop a theme, you should make sure that all examples below show decent. + +Text +---- + +**Lorem ipsum dolor sit amet,** consectetur adipisici elit, *sed eiusmod tempor* incidunt ut labore et dolore magna aliqua.[^1] ~~Ut enim ad minim veniam,~~ quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat.[^2] [Quis aute iure reprehenderit][Link] in voluptate velit esse cillum dolore eu fugiat nulla pariatur. `Excepteur` sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +Headings +-------- + +# h1 + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +## h2 + +Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + +### h3 + +Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. + +#### h4 + +Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + +##### h5 + +Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. + +###### h6 + +At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. + +Horizontal line +--------------- + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +--- + +Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + +List +---- + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +* Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. + 1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + 2. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + 3. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. +* Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. + - Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + - Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + 1. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. + 2. At vero eos et accusam et justo duo dolores et ea rebum. + 1. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet + 2. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. + 3. At vero eos et accusam et justo duo dolores et ea rebum. + +Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. + +Definition list +--------------- + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +Duis autem +: Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. + +Lorem ipsum dolor sit amet +: Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + +Ut wisi enim ad minim veniam +: Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. +: Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. + +Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + +Blockquote +---------- + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +> Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse +> molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero +> eros et accumsan et iusto odio dignissim qui blandit praesent luptatum +> zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum +> dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod +> tincidunt ut laoreet dolore magna aliquam erat volutpat. +> +> > Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit +> > lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure +> > dolor in hendrerit in vulputate velit esse molestie consequat, vel illum +> > dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio +> > dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te +> > feugait nulla facilisi. +> +> Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet +> doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, +> consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut +> laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, +> quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex +> ea commodo consequat. + +Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. + +Code block +---------- + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +``` + + + + This is a title + + +

Hello world!

+ + +``` + +Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + +Table +----- + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +Lorem ipsum | Duis autem vel eum | Ut wisi enim ad minim veniam +----------- | ------------------ | ---------------------------- +**Duis autem vel eum iriure dolor** in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. | *Lorem ipsum dolor sit amet,* consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. | ~~Ut wisi enim ad minim veniam,~~ quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. +[Duis autem vel eum iriure dolor][Link] in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. | `Nam liber tempor` cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. | Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. +Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. | | Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. + +At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. + +*[Lorem ipsum]: Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. + +[Link]: %base_url% "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat." + +[^1]: Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +[^2]: Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. diff --git a/index.php b/index.php new file mode 100644 index 0000000..a1f981d --- /dev/null +++ b/index.php @@ -0,0 +1,39 @@ + + * + * SPDX-License-Identifier: MIT + * License-Filename: LICENSE + */ + +// check PHP platform requirements +if (PHP_VERSION_ID < 50306) { + die('Pico requires PHP 5.3.6 or above to run'); +} +if (!extension_loaded('dom')) { + die("Pico requires the PHP extension 'dom' to run"); +} +if (!extension_loaded('mbstring')) { + die("Pico requires the PHP extension 'mbstring' to run"); +} + +// load dependencies +require_once(__DIR__ . '/vendor/autoload.php'); + +// instance Pico +$pico = new Pico( + __DIR__, // root dir + 'config/', // config dir + 'plugins/', // plugins dir + 'themes/' // themes dir +); + +// override configuration? +//$pico->setConfig(array()); + +// run application +echo $pico->run(); diff --git a/plugins/PicoDeprecated/CHANGELOG.md b/plugins/PicoDeprecated/CHANGELOG.md new file mode 100644 index 0000000..4cc5d1d --- /dev/null +++ b/plugins/PicoDeprecated/CHANGELOG.md @@ -0,0 +1,155 @@ +Pico Deprecated Changelog +========================= + +**Note:** This changelog only provides technical information about the changes + introduced with a particular Pico version, and is meant to supplement + the actual code changes. The information in this changelog are often + insufficient to understand the implications of larger changes. Please + refer to both the UPGRADE and NEWS sections of the docs for more + details. + +**Note:** Changes breaking backwards compatibility (BC) are marked with an `!` + (exclamation mark). This doesn't include changes for which BC is + preserved by this plugin. If a previously deprecated feature is later + removed, this change is going to be marked as BC-breaking change. + Please note that BC-breaking changes are only possible with a new + major version. + +**Note:** Many versions of `PicoDeprecated` include changes which are not + explicitly mentioned in this changelog. This primarily concerns + changes in Pico's plugin API. These changes aren't listed separately + because they are already listed in Pico's changelog. Only functional + changes and/or BC-breaking changes are listed below. + +### Version 2.1.4 +Released: 2020-08-29 + +No changes + +### Version 2.1.3 +Released: 2020-07-10 + +No changes + +### Version 2.1.2 +Released: 2020-04-10 + +No changes + +### Version 2.1.1 +Released: 2019-12-31 + +No changes + +### Version 2.1.0 +Released: 2019-11-24 + +No changes + +### Version 2.1.0-beta.1 +Released: 2019-11-03 + +``` +* [New] Add support for the latest API v3 changes +* [New] Support disabled Twig autoescape prior to API v3 +* [New] Re-introduce `theme_url` config variable +* [New] Re-introduce `prev_page`, `base_dir` and `theme_dir` Twig variables +* [New] Support loading additional plugins using API v1 `onPluginsLoaded` event +* [New] Re-introduce Pico v0.9 config constant `CACHE_DIR` +* [New] Add release & build system to test the plugin using PHP_CodeSniffer and + to automatically create pre-built release packages +* [Changed] Split the plugin's functionality into multiple compatibility + plugins (two for each API version, for plugins and themes resp.) + and load the necessary compatibility plugins on demand only; also + allow 3rd-party plugins to load their own compatibility plugins +``` + +### Version 2.0.5-beta.1 +Released: 2019-01-03 + +``` +* [New] Add `2.0.x-dev` alias for master branch to `composer.json` +``` + +### Version 2.0.4 +Released: 2018-12-17 + +No changes + +### Version 2.0.3 +Released: 2018-12-03 + +No changes + +### Version 2.0.2 +Released: 2018-08-12 + +No changes + +### Version 2.0.1 +Released: 2018-07-29 + +No changes + +### Version 2.0.0 +Released: 2018-07-01 + +No changes + +### Version 2.0.0-beta.3 +Released: 2018-04-07 + +No changes + +### Version 2.0.0-beta.2 +Released: 2018-01-21 + +``` +* [New] Add support for the latest API v2 changes +* [New] ! Add support for themes using the old `.html` file extension for Twig + templates; however, starting with API v2 plugins might rely on `.twig` + as file extension, making this a BC-breaking change regardless +``` + +### Version 2.0.0-beta.1 +Released: 2017-11-05 + +**Note:** Pico's official `PicoDeprecated` plugin was moved to this separate + repository in preparation for Pico 2.0. Refer to Pico's changelog for + a list of changes to this plugin before Pico 2.0. + +``` +* [New] Update plugin to API v2 and add support for all API v1 events +* [New] Keep track of all loaded Pico plugins and distinguish them by the API + version they use; deprecated events are only triggered on plugins using + this particular API version (`PicoDeprecated::API_VERSION_*` constants) +* [New] Take care of triggering events on plugins using older API versions; + this includes not only core events, but also all custom events; as a + result, old plugin's always depend on `PicoDeprecated` now +* [New] Use a simple event alias table to keep track of unchanged or just + renamed core events +* [New] Add `rewrite_url` and `is_front_page` Twig variables +* [New] Add support for the `config/config.php` configuration file +* [New] Additionally compare registered meta headers case-insensitive +* [New] Make meta headers on the first level of a page's meta data also + available using a lowered key (as of Pico 1.0; i.e. `SomeKey: value` is + now accessible using both `$meta['SomeKey']` and `$meta['somekey']`) +* [New] Add public `PicoDeprecated::triggersApiEvents()` method +* [New] Add public `PicoDeprecated::triggerEvent()` method (and the additional + `$apiVersion` parameter) as replacement for the previously protected + method of the same name +* [Fixed] ! Don't overwrite the global `$config` variable if it is defined +* [Fixed] ! Improve re-indexing of pages added by the API v0 event `get_pages` +* [Changed] No longer try to guess whether the plugin needs to be enabled or + not, rather enable it by default (guessing was pretty error-prone) +* [Changed] ! Use a scope-isolated `require()` to include configuration files +* [Changed] ! Don't pass `$plugins` parameter to API v1 `onPluginsLoaded` event + by reference anymore; use `Pico::loadPlugin()` instead +* [Changed] ! The API v1 events `onTwigRegistration` and `onMetaHeaders`, as + well as the API v0 event `before_twig_register` are no longer part + of Pico's event flow and are triggered just once on demand +* [Changed] Improve PHP class docs +* [Changed] A vast number of small improvements and changes... +* [Removed] ! Remove support for `PicoParsePagesContent` plugin +* [Removed] ! Remove support for `PicoExcerpt` plugin +``` diff --git a/plugins/PicoDeprecated/LICENSE b/plugins/PicoDeprecated/LICENSE new file mode 100644 index 0000000..91a34cf --- /dev/null +++ b/plugins/PicoDeprecated/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2012 The Pico Community + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/PicoDeprecated/PicoDeprecated.php b/plugins/PicoDeprecated/PicoDeprecated.php new file mode 100644 index 0000000..9334661 --- /dev/null +++ b/plugins/PicoDeprecated/PicoDeprecated.php @@ -0,0 +1,383 @@ + + * + * The file was previously part of the project's main repository; the version + * control history of the original file applies accordingly, available from + * the following original location: + * + * + * + * SPDX-License-Identifier: MIT + * License-Filename: LICENSE + */ + +/** + * Maintain backward compatibility to older Pico releases + * + * `PicoDeprecated`'s purpose is to maintain backward compatibility to older + * versions of Pico, by re-introducing characteristics that were removed from + * Pico's core. + * + * `PicoDeprecated` is basically a mandatory plugin for all Pico installs. + * Without this plugin you can't use plugins which were written for other + * API versions than the one of Pico's core, even when there was just the + * slightest change. + * + * {@see http://picocms.org/plugins/deprecated/} for a full list of features. + * + * @author Daniel Rudolf + * @link http://picocms.org + * @license http://opensource.org/licenses/MIT The MIT License + * @version 2.1 + */ +class PicoDeprecated extends AbstractPicoPlugin +{ + /** + * API version used by this plugin + * + * @var int + */ + const API_VERSION = 3; + + /** + * API version 0, used by Pico 0.9 and earlier + * + * @var int + */ + const API_VERSION_0 = 0; + + /** + * API version 1, used by Pico 1.0 + * + * @var int + */ + const API_VERSION_1 = 1; + + /** + * API version 2, used by Pico 2.0 + * + * @var int + */ + const API_VERSION_2 = 2; + + /** + * API version 3, used by Pico 2.1 + * + * @var int + */ + const API_VERSION_3 = 3; + + /** + * Loaded plugins, indexed by API version + * + * @see PicoDeprecated::getPlugins() + * + * @var object[] + */ + protected $plugins = array(); + + /** + * Loaded compatibility plugins + * + * @see PicoDeprecated::getCompatPlugins() + * + * @var PicoCompatPluginInterface[] + */ + protected $compatPlugins = array(); + + /** + * {@inheritDoc} + */ + public function __construct(Pico $pico) + { + parent::__construct($pico); + + if (is_file(__DIR__ . '/vendor/autoload.php')) { + require(__DIR__ . '/vendor/autoload.php'); + } + + if (!class_exists('PicoMainCompatPlugin')) { + die( + "Cannot find PicoDeprecated's 'vendor/autoload.php'. If you're using a composer-based Pico install, " + . "run `composer update`. If you're rather trying to use one of PicoDeprecated's pre-built release " + . "packages, make sure to download PicoDeprecated's release package matching Pico's version named " + . "'pico-deprecated-release-v*.tar.gz' (don't download a source code package)." + ); + } + + if ($pico::API_VERSION !== static::API_VERSION) { + throw new RuntimeException( + 'PicoDeprecated requires API version ' . static::API_VERSION . ', ' + . 'but Pico is running API version ' . $pico::API_VERSION + ); + } + } + + /** + * {@inheritDoc} + */ + public function handleEvent($eventName, array $params) + { + parent::handleEvent($eventName, $params); + + // trigger events on compatibility plugins + if ($this->isEnabled() || ($eventName === 'onPluginsLoaded')) { + $isCoreEvent = in_array($eventName, $this->getCoreEvents()); + foreach ($this->compatPlugins as $plugin) { + if ($isCoreEvent) { + if ($plugin->getApiVersion() === static::API_VERSION) { + $plugin->handleEvent($eventName, $params); + } + } elseif ($plugin instanceof PicoPluginApiCompatPluginInterface) { + $plugin->handleCustomEvent($eventName, $params); + } + } + } + } + + /** + * Reads all loaded plugins and indexes them by API level, loads the + * necessary compatibility plugins + * + * @see PicoDeprecated::loadPlugin() + * + * @param object[] $plugins loaded plugin instances + */ + public function onPluginsLoaded(array $plugins) + { + $this->loadCompatPlugin('PicoMainCompatPlugin'); + + foreach ($plugins as $plugin) { + $this->loadPlugin($plugin); + } + + $this->getPico()->triggerEvent('onPicoDeprecated', array($this)); + } + + /** + * Adds a manually loaded plugin to PicoDeprecated's plugin index, loads + * the necessary compatibility plugins + * + * @see PicoDeprecated::loadPlugin() + * + * @param object $plugin loaded plugin instance + */ + public function onPluginManuallyLoaded($plugin) + { + $this->loadPlugin($plugin); + } + + /** + * Loads a compatibility plugin if Pico's theme uses a old theme API + * + * @param string $theme name of current theme + * @param int $themeApiVersion API version of the theme + * @param array $themeConfig config array of the theme + */ + public function onThemeLoaded($theme, $themeApiVersion, array &$themeConfig) + { + $this->loadThemeApiCompatPlugin($themeApiVersion); + } + + /** + * Adds a plugin to PicoDeprecated's plugin index + * + * @see PicoDeprecated::onPluginsLoaded() + * @see PicoDeprecated::onPluginManuallyLoaded() + * @see PicoDeprecated::getPlugins() + * + * @param object $plugin loaded plugin instance + */ + protected function loadPlugin($plugin) + { + $pluginName = get_class($plugin); + + $apiVersion = $this->getPluginApiVersion($plugin); + if (!isset($this->plugins[$apiVersion])) { + $this->plugins[$apiVersion] = array(); + $this->loadPluginApiCompatPlugin($apiVersion); + } + + $this->plugins[$apiVersion][$pluginName] = $plugin; + } + + /** + * Returns a list of all loaded Pico plugins using the given API level + * + * @param int $apiVersion API version to match plugins + * + * @return object[] loaded plugin instances + */ + public function getPlugins($apiVersion) + { + return isset($this->plugins[$apiVersion]) ? $this->plugins[$apiVersion] : array(); + } + + /** + * Loads a compatibility plugin + * + * @param PicoCompatPluginInterface|string $plugin either the class name of + * a plugin to instantiate or a plugin instance + * + * @return PicoCompatPluginInterface instance of the loaded plugin + */ + public function loadCompatPlugin($plugin) + { + if (!is_object($plugin)) { + $className = (string) $plugin; + if (class_exists($className)) { + $plugin = new $className($this->getPico(), $this); + } else { + throw new RuntimeException( + "Unable to load PicoDeprecated compatibility plugin '" . $className . "': Class not found" + ); + } + } + + $className = get_class($plugin); + if (isset($this->compatPlugins[$className])) { + return $this->compatPlugins[$className]; + } + + if (!($plugin instanceof PicoCompatPluginInterface)) { + throw new RuntimeException( + "Unable to load PicoDeprecated compatibility plugin '" . $className . "': " + . "Compatibility plugins must implement 'PicoCompatPluginInterface'" + ); + } + + $apiVersion = $plugin->getApiVersion(); + $this->loadPluginApiCompatPlugin($apiVersion); + + $dependsOn = $plugin->getDependencies(); + foreach ($dependsOn as $pluginDependency) { + $this->loadCompatPlugin($pluginDependency); + } + + $this->compatPlugins[$className] = $plugin; + + return $plugin; + } + + /** + * Loads a plugin API compatibility plugin + * + * @param int $apiVersion API version to load the compatibility plugin for + */ + protected function loadPluginApiCompatPlugin($apiVersion) + { + if ($apiVersion !== static::API_VERSION) { + $this->loadCompatPlugin('PicoPluginApi' . $apiVersion . 'CompatPlugin'); + } + } + + /** + * Loads a theme API compatibility plugin + * + * @param int $apiVersion API version to load the compatibility plugin for + */ + protected function loadThemeApiCompatPlugin($apiVersion) + { + if ($apiVersion !== static::API_VERSION) { + $this->loadCompatPlugin('PicoThemeApi' . $apiVersion . 'CompatPlugin'); + } + } + + /** + * Returns all loaded compatibility plugins + * + * @return PicoCompatPluginInterface[] list of loaded compatibility plugins + */ + public function getCompatPlugins() + { + return $this->compatPlugins; + } + + /** + * Triggers deprecated events on plugins of different API versions + * + * You can use this public method in other plugins to trigger custom events + * on plugins using a particular API version. If you want to trigger a + * custom event on all plugins, no matter their API version (except for + * plugins using API v0, which can't handle custom events), use + * {@see Pico::triggerEvent()} instead. + * + * @see Pico::triggerEvent() + * + * @param int $apiVersion API version of the event + * @param string $eventName event to trigger + * @param array $params optional parameters to pass + */ + public function triggerEvent($apiVersion, $eventName, array $params = array()) + { + foreach ($this->getPlugins($apiVersion) as $plugin) { + $plugin->handleEvent($eventName, $params); + } + } + + /** + * Returns the API version of a given plugin + * + * @param object $plugin plugin instance + * + * @return int API version used by the plugin + */ + public function getPluginApiVersion($plugin) + { + $pluginApiVersion = self::API_VERSION_0; + if ($plugin instanceof PicoPluginInterface) { + $pluginApiVersion = self::API_VERSION_1; + if (defined(get_class($plugin) . '::API_VERSION')) { + $pluginApiVersion = $plugin::API_VERSION; + } + } + + return $pluginApiVersion; + } + + /** + * Returns a list of the names of Pico's core events + * + * @return string[] list of Pico's core events + */ + public function getCoreEvents() + { + return array( + 'onPluginsLoaded', + 'onPluginManuallyLoaded', + 'onConfigLoaded', + 'onThemeLoading', + 'onThemeLoaded', + 'onRequestUrl', + 'onRequestFile', + 'onContentLoading', + 'on404ContentLoading', + 'on404ContentLoaded', + 'onContentLoaded', + 'onMetaParsing', + 'onMetaParsed', + 'onContentParsing', + 'onContentPrepared', + 'onContentParsed', + 'onPagesLoading', + 'onSinglePageLoading', + 'onSinglePageContent', + 'onSinglePageLoaded', + 'onPagesDiscovered', + 'onPagesLoaded', + 'onCurrentPageDiscovered', + 'onPageTreeBuilt', + 'onPageRendering', + 'onPageRendered', + 'onMetaHeaders', + 'onYamlParserRegistered', + 'onParsedownRegistered', + 'onTwigRegistered' + ); + } +} diff --git a/plugins/PicoDeprecated/README.md b/plugins/PicoDeprecated/README.md new file mode 100644 index 0000000..4c3091a --- /dev/null +++ b/plugins/PicoDeprecated/README.md @@ -0,0 +1,44 @@ +Pico Deprecated Plugin +====================== + +This is the repository of Pico's official `PicoDeprecated` plugin. + +Pico is a stupidly simple, blazing fast, flat file CMS. See http://picocms.org/ for more info. + +`PicoDeprecated`'s purpose is to maintain backward compatibility to older versions of Pico, by re-introducing characteristics that were removed from Pico's core. It for example triggers old events (like the `before_render` event used before Pico 1.0) and reads config files that were written in PHP (`config/config.php`, used before Pico 2.0). + +Please refer to [`picocms/Pico`](https://github.com/picocms/Pico) to get info about how to contribute or getting help. + +Install +------- + +You usually don't have to install this plugin manually, it's shipped together with [Pico's pre-built release packages](https://github.com/picocms/Pico/releases/latest) and a default dependency of [`picocms/pico-composer`](https://github.com/picocms/pico-composer). + +If you're using plugins and themes that are compatible with Pico's latest API version only, you can safely remove `PicoDeprecated` from your Pico installation or disable the plugin (please refer to the "Usage" section below). However, if you're not sure about this, simply leave it as it is - it won't hurt... :wink: + +If you use a `composer`-based installation of Pico and want to either remove or install `PicoDeprecated`, simply open a shell on your server and navigate to Pico's install directory (e.g. `/var/www/html`). Run `composer remove picocms/pico-deprecated` to remove `PicoDeprecated`, or run `composer require picocms/pico-deprecated` (via [Packagist.org](https://packagist.org/packages/picocms/pico-deprecated)) to install `PicoDeprecated`. + +If you rather use one of Pico's pre-built release packages, it is best to disable `PicoDeprecated` and not to actually remove it. The reason for this is, that `PicoDeprecated` is part of Pico's pre-built release packages, thus it will be automatically re-installed when updating Pico. However, if you really want to remove `PicoDeprecated`, simply delete the `plugins/PicoDeprecated` directory in Pico's install directory (e.g. `/var/www/html`). If you want to install `PicoDeprecated`, you must first create a empty `plugins/PicoDeprecated` directory on your server, [download the version of `PicoDeprecated`](https://github.com/picocms/pico-deprecated/releases) matching the version of your Pico installation and upload all containing files (esp. `PicoDeprecated.php` and the `lib/`, `plugins/` and `vendor/` directories) into said `plugins/PicoDeprecated` directory (resulting in `plugins/PicoDeprecated/PicoDeprecated.php`). + +The versioning of `PicoDeprecated` strictly follows the version of Pico's core. You *must not* use a version of `PicoDeprecated` that doesn't match the version of Pico's core (e.g. PicoDeprecated 2.0.1 is *not compatible* with Pico 2.0.0). If you're using a `composer`-based installation of Pico, simply use a version constaint like `^2.0` - `PicoDeprecated` ensures that its version matches Pico's version. Even if you're using one of Pico's pre-built release packages, you don't have to take care of anything - a matching version of `PicoDeprecated` is part of Pico's pre-built release packages anyway. + +Usage +----- + +You can explicitly disable `PicoDeprecated` by adding `PicoDeprecated.enabled: false` to your `config/config.yml`. If you want to re-enable `PicoDeprecated`, simply remove this line from your `config/config.yml`. `PicoDeprecated` itself has no configuration options, it enables and disables all of its features depending on whether there are plugins and/or themes requiring said characteristics. + +`PicoDeprecated`'s functionality is split into various so-called "compatibility plugins". There are compatibility plugins for every old API version (Pico 0.9 and earlier were using API version 0, Pico 1.0 was using API version 1 and Pico 2.0 was using API version 2; the current API version is version 3, used by Pico 2.1), one for plugins and another one for themes. Their purpose is to re-introduce characteristics plugins and themes using said API version might rely on. For example, plugin API compatibility plugins are responsible for simulating old Pico core events (like the `before_render` event used by Pico 0.9 and earlier). Theme API compatibility plugins will e.g. register old Twig variables (like the `is_front_page` Twig variable used by Pico 1.0). If you install a plugin using API version 2, the corresponding `PicoPluginApi2CompatPlugin` will be loaded. All plugin API compatibility plugins also depend on their theme counterpart, thus `PicoThemeApi2CompatPlugin` will be loaded, too. Furthermore all compatibility plugins depend on their respective API successors. + +The plugin exposes a simple API to allow other plugins to load their own compatibility plugins. As a plugin developer you may use the `PicoDeprecated::loadCompatPlugin(PicoCompatPluginInterface $compatPlugin)` method to load a custom compatibility plugin. Use `PicoDeprecated::getCompatPlugins()` to return a list of all loaded compatibility plugins. You can furthermore use the `PicoDeprecated::getPlugins(int $apiVersion)` method to return a list of all loaded Pico plugins using a particular API version. If you want to trigger a custom event on plugins using a particular API version only, use `PicoDeprecated::triggerEvent(int $apiVersion, string $eventName, array $parameters = [])`. `PicoDeprecated` furthermore triggers the custom `onPicoDeprecated(PicoDeprecated $picoDeprecated)` event. + +Getting Help +------------ + +Please refer to the ["Getting Help" section](https://github.com/picocms/Pico#getting-help) of our main repository. + +Contributing +------------ + +Please refer to the ["Contributing" section](https://github.com/picocms/Pico#contributing) of our main repository. + +By contributing to Pico, you accept and agree to the *Developer Certificate of Origin* for your present and future contributions submitted to Pico. Please refer to the ["Developer Certificate of Origin" section](https://github.com/picocms/Pico/blob/master/CONTRIBUTING.md#developer-certificate-of-origin) in the `CONTRIBUTING.md` of our main repository. diff --git a/plugins/PicoDeprecated/composer.json b/plugins/PicoDeprecated/composer.json new file mode 100644 index 0000000..daf56bc --- /dev/null +++ b/plugins/PicoDeprecated/composer.json @@ -0,0 +1,41 @@ +{ + "name": "picocms/pico-deprecated", + "type": "pico-plugin", + "description": "This is Pico's official PicoDeprecated plugin. Pico is a stupidly simple, blazing fast, flat file CMS.", + "keywords": [ "pico", "picocms", "picocms-plugin", "pico-deprecated", "compatibility", "deprecation" ], + "homepage": "http://picocms.org/", + "license": "MIT", + "authors": [ + { + "name": "Daniel Rudolf", + "email": "picocms.org@daniel-rudolf.de", + "role": "Lead Developer" + }, + { + "name": "The Pico Community", + "homepage": "http://picocms.org/" + }, + { + "name": "Contributors", + "homepage": "https://github.com/picocms/pico-deprecated/graphs/contributors" + } + ], + "support": { + "docs": "http://picocms.org/plugins/deprecated/", + "issues": "https://github.com/picocms/pico-deprecated/issues", + "source": "https://github.com/picocms/pico-deprecated" + }, + "require": { + "php": ">=5.3.0", + "picocms/pico": "self.version" + }, + "autoload": { + "classmap": [ "PicoDeprecated.php", "lib/", "plugins/" ] + }, + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev", + "dev-pico-3.0": "3.0.x-dev" + } + } +} diff --git a/plugins/PicoDeprecated/lib/AbstractPicoCompatPlugin.php b/plugins/PicoDeprecated/lib/AbstractPicoCompatPlugin.php new file mode 100644 index 0000000..1d3961a --- /dev/null +++ b/plugins/PicoDeprecated/lib/AbstractPicoCompatPlugin.php @@ -0,0 +1,101 @@ + + * + * SPDX-License-Identifier: MIT + * License-Filename: LICENSE + */ + +/** + * Abstract class to extend from when implementing a PicoDeprecated + * compatibility plugin + * + * Please refer to {@see PicoCompatPluginInterface} for more information about + * how to develop a PicoDeprecated compatibility plugin. + * + * @see PicoCompatPluginInterface + * + * @author Daniel Rudolf + * @link http://picocms.org + * @license http://opensource.org/licenses/MIT The MIT License + * @version 2.1 + */ +abstract class AbstractPicoCompatPlugin implements PicoCompatPluginInterface +{ + /** + * Current instance of Pico + * + * @see PicoCompatPluginInterface::getPico() + * + * @var Pico + */ + protected $pico; + + /** + * Instance of the main PicoDeprecated plugin + * + * @see PicoCompatPluginInterface::getPicoDeprecated() + * + * @var PicoDeprecated + */ + protected $picoDeprecated; + + /** + * List of plugins which this plugin depends on + * + * @see PicoCompatPluginInterface::getDependencies() + * + * @var string[] + */ + protected $dependsOn = array(); + + /** + * Constructs a new instance of a PicoDeprecated compatibility plugin + * + * @param Pico $pico current instance of Pico + * @param PicoDeprecated $picoDeprecated current instance of PicoDeprecated + */ + public function __construct(Pico $pico, PicoDeprecated $picoDeprecated) + { + $this->pico = $pico; + $this->picoDeprecated = $picoDeprecated; + } + + /** + * {@inheritDoc} + */ + public function handleEvent($eventName, array $params) + { + if (method_exists($this, $eventName)) { + call_user_func_array(array($this, $eventName), $params); + } + } + + /** + * {@inheritDoc} + */ + public function getPico() + { + return $this->pico; + } + + /** + * {@inheritDoc} + */ + public function getPicoDeprecated() + { + return $this->picoDeprecated; + } + + /** + * {@inheritDoc} + */ + public function getDependencies() + { + return (array) $this->dependsOn; + } +} diff --git a/plugins/PicoDeprecated/lib/AbstractPicoPluginApiCompatPlugin.php b/plugins/PicoDeprecated/lib/AbstractPicoPluginApiCompatPlugin.php new file mode 100644 index 0000000..af32b2f --- /dev/null +++ b/plugins/PicoDeprecated/lib/AbstractPicoPluginApiCompatPlugin.php @@ -0,0 +1,83 @@ + + * + * SPDX-License-Identifier: MIT + * License-Filename: LICENSE + */ + +/** + * Abstract class to extend from when implementing a PicoDeprecated plugin API + * compatibility plugin + * + * Please refer to {@see PicoPluginApiCompatPluginInterface} for more information about + * how to develop a PicoDeprecated plugin API compatibility plugin. + * + * @see PicoPluginApiCompatPluginInterface + * + * @author Daniel Rudolf + * @link http://picocms.org + * @license http://opensource.org/licenses/MIT The MIT License + * @version 2.1 + */ +abstract class AbstractPicoPluginApiCompatPlugin extends AbstractPicoCompatPlugin implements + PicoPluginApiCompatPluginInterface +{ + /** + * Map of core events matching event signatures of older API versions + * + * @see AbstractPicoPluginApiCompatPlugin::handleEvent() + * + * @var array + */ + protected $eventAliases = array(); + + /** + * {@inheritDoc} + */ + public function handleEvent($eventName, array $params) + { + parent::handleEvent($eventName, $params); + + // trigger core events matching the event signatures of older API versions + if (isset($this->eventAliases[$eventName])) { + foreach ($this->eventAliases[$eventName] as $eventAlias) { + $this->triggerEvent($eventAlias, $params); + } + } + } + + /** + * {@inheritDoc} + */ + public function handleCustomEvent($eventName, array $params = array()) + { + $this->getPicoDeprecated()->triggerEvent($this->getApiVersionSupport(), $eventName, $params); + } + + /** + * Triggers deprecated events on plugins of the supported API version + * + * @param string $eventName name of the event to trigger + * @param array $params optional parameters to pass + */ + protected function triggerEvent($eventName, array $params = array()) + { + $apiVersion = $this->getApiVersionSupport(); + $picoDeprecated = $this->getPicoDeprecated(); + + if ($apiVersion !== $picoDeprecated::API_VERSION) { + foreach ($picoDeprecated->getCompatPlugins() as $compatPlugin) { + if ($compatPlugin->getApiVersion() === $apiVersion) { + $compatPlugin->handleEvent($eventName, $params); + } + } + } + + $picoDeprecated->triggerEvent($apiVersion, $eventName, $params); + } +} diff --git a/plugins/PicoDeprecated/lib/PicoCompatPluginInterface.php b/plugins/PicoDeprecated/lib/PicoCompatPluginInterface.php new file mode 100644 index 0000000..6ede84f --- /dev/null +++ b/plugins/PicoDeprecated/lib/PicoCompatPluginInterface.php @@ -0,0 +1,62 @@ + + * + * SPDX-License-Identifier: MIT + * License-Filename: LICENSE + */ + +/** + * Common interface for PicoDeprecated compatibility plugins + * + * @author Daniel Rudolf + * @link http://picocms.org + * @license http://opensource.org/licenses/MIT The MIT License + * @version 2.1 + */ +interface PicoCompatPluginInterface +{ + /** + * Handles a Pico event + * + * @param string $eventName name of the triggered event + * @param array $params passed parameters + */ + public function handleEvent($eventName, array $params); + + /** + * Returns a list of names of compat plugins required by this plugin + * + * @return string[] required plugins + */ + public function getDependencies(); + + /** + * Returns the plugin's instance of Pico + * + * @see Pico + * + * @return Pico the plugin's instance of Pico + */ + public function getPico(); + + /** + * Returns the plugin's main PicoDeprecated plugin instance + * + * @see PicoDeprecated + * + * @return PicoDeprecated the plugin's instance of Pico + */ + public function getPicoDeprecated(); + + /** + * Returns the version of the API this plugin uses + * + * @return int the API version used by this plugin + */ + public function getApiVersion(); +} diff --git a/plugins/PicoDeprecated/lib/PicoPluginApiCompatPluginInterface.php b/plugins/PicoDeprecated/lib/PicoPluginApiCompatPluginInterface.php new file mode 100644 index 0000000..5e71768 --- /dev/null +++ b/plugins/PicoDeprecated/lib/PicoPluginApiCompatPluginInterface.php @@ -0,0 +1,37 @@ + + * + * SPDX-License-Identifier: MIT + * License-Filename: LICENSE + */ + +/** + * Common interface for PicoDeprecated plugin API compatibility plugins + * + * @author Daniel Rudolf + * @link http://picocms.org + * @license http://opensource.org/licenses/MIT The MIT License + * @version 2.1 + */ +interface PicoPluginApiCompatPluginInterface extends PicoCompatPluginInterface +{ + /** + * Handles custom events for plugins of the supported API version + * + * @param string $eventName name of the triggered event + * @param array $params passed parameters + */ + public function handleCustomEvent($eventName, array $params = array()); + + /** + * Returns the API version this plugin maintains backward compatibility for + * + * @return int + */ + public function getApiVersionSupport(); +} diff --git a/plugins/PicoDeprecated/plugins/PicoMainCompatPlugin.php b/plugins/PicoDeprecated/plugins/PicoMainCompatPlugin.php new file mode 100644 index 0000000..683aad9 --- /dev/null +++ b/plugins/PicoDeprecated/plugins/PicoMainCompatPlugin.php @@ -0,0 +1,102 @@ + + * + * This file was created by splitting up an original file into multiple files, + * which in turn was previously part of the project's main repository. The + * version control history of these files apply accordingly, available from + * the following original locations: + * + * + * + * + * SPDX-License-Identifier: MIT + * License-Filename: LICENSE + */ + +/** + * Maintains backward compatibility with older Pico versions + * + * @author Daniel Rudolf + * @link http://picocms.org + * @license http://opensource.org/licenses/MIT The MIT License + * @version 2.1 + */ +class PicoMainCompatPlugin extends AbstractPicoCompatPlugin +{ + /** + * Load's config.php from Pico's root and config dir + * + * Since we want to utilize Pico's own code dealing with particular config + * settings (like making paths and URLs absolute), we must call this before + * {@see Pico::loadConfig()}. `onConfigLoaded` is triggered later, thus we + * use the `onPluginsLoaded` event. + * + * @see PicoMainCompatPlugin::loadScriptedConfig() + * + * @param object[] $plugins loaded plugin instances + */ + public function onPluginsLoaded(array $plugins) + { + // deprecated since Pico 1.0 + if (is_file($this->getPico()->getRootDir() . 'config.php')) { + $this->loadScriptedConfig($this->getPico()->getRootDir() . 'config.php'); + } + + // deprecated since Pico 2.0 + if (is_file($this->getPico()->getConfigDir() . 'config.php')) { + $this->loadScriptedConfig($this->getPico()->getConfigDir() . 'config.php'); + } + } + + /** + * Reads a Pico PHP config file and injects the config into Pico + * + * This method injects the config into Pico using PHP's Reflection API + * (i.e. {@see ReflectionClass}). Even though the Reflection API was + * created to aid development and not to do things like this, it's the best + * solution. Otherwise we'd have to copy all of Pico's code dealing with + * special config settings (like making paths and URLs absolute). + * + * @see PicoMainCompatPlugin::onConfigLoaded() + * @see Pico::loadConfig() + * + * @param string $configFile path to the config file to load + */ + protected function loadScriptedConfig($configFile) + { + // scope isolated require() + $includeConfigClosure = function ($configFile) { + require($configFile); + return (isset($config) && is_array($config)) ? $config : array(); + }; + if (PHP_VERSION_ID >= 50400) { + $includeConfigClosure = $includeConfigClosure->bindTo(null); + } + + $scriptedConfig = $includeConfigClosure($configFile); + + if (!empty($scriptedConfig)) { + $picoReflector = new ReflectionObject($this->getPico()); + $picoConfigReflector = $picoReflector->getProperty('config'); + $picoConfigReflector->setAccessible(true); + + $config = $picoConfigReflector->getValue($this->getPico()) ?: array(); + $config += $scriptedConfig; + + $picoConfigReflector->setValue($this->getPico(), $config); + } + } + + /** + * {@inheritDoc} + */ + public function getApiVersion() + { + return PicoDeprecated::API_VERSION_3; + } +} diff --git a/plugins/PicoDeprecated/plugins/PicoPluginApi0CompatPlugin.php b/plugins/PicoDeprecated/plugins/PicoPluginApi0CompatPlugin.php new file mode 100644 index 0000000..d459524 --- /dev/null +++ b/plugins/PicoDeprecated/plugins/PicoPluginApi0CompatPlugin.php @@ -0,0 +1,314 @@ + + * + * This file was created by splitting up an original file into multiple files, + * which in turn was previously part of the project's main repository. The + * version control history of these files apply accordingly, available from + * the following original locations: + * + * + * + * + * SPDX-License-Identifier: MIT + * License-Filename: LICENSE + */ + +/** + * Maintains backward compatibility with plugins using API version 0, written + * for Pico 0.9 and earlier + * + * @author Daniel Rudolf + * @link http://picocms.org + * @license http://opensource.org/licenses/MIT The MIT License + * @version 2.1 + */ +class PicoPluginApi0CompatPlugin extends AbstractPicoPluginApiCompatPlugin +{ + /** + * This plugin extends {@see PicoPluginApi1CompatPlugin} and + * {@see PicoThemeApi0CompatPlugin} + * + * @var string[] + */ + protected $dependsOn = array('PicoPluginApi1CompatPlugin', 'PicoThemeApi0CompatPlugin'); + + /** + * Map of core events matching event signatures of older API versions + * + * @see AbstractPicoPluginApiCompatPlugin::handleEvent() + * + * @var array + */ + protected $eventAliases = array( + 'onConfigLoaded' => array('config_loaded'), + 'onRequestUrl' => array('request_url'), + 'onContentLoading' => array('before_load_content'), + 'on404ContentLoading' => array('before_404_load_content'), + 'onMetaParsed' => array('file_meta'), + 'onContentParsing' => array('before_parse_content'), + 'onContentParsed' => array('after_parse_content', 'content_parsed'), + 'onTwigRegistration' => array('before_twig_register'), + 'onPageRendered' => array('after_render') + ); + + /** + * Pico's request file + * + * @see Pico::$requestFile + * @see PicoPluginApi0CompatPlugin::onRequestFile() + * + * @var string|null + */ + protected $requestFile; + + /** + * Triggers the plugins_loaded event + * + * @param object[] $plugins loaded plugin instances + */ + public function onPluginsLoaded(array &$plugins) + { + $this->triggerEvent('plugins_loaded'); + } + + /** + * Defines various config-related constants and sets the $config global + * + * `ROOT_DIR`, `LIB_DIR`, `PLUGINS_DIR`, `THEMES_DIR`, `CONTENT_EXT` and + * `CACHE_DIR` were removed wih Pico 1.0, `CONTENT_DIR` existed just in + * Pico 0.9 and `CONFIG_DIR` existed just for a short time between Pico 0.9 + * and Pico 1.0. + * + * @param array &$config array of config variables + */ + public function onConfigLoaded(array &$config) + { + $this->defineConfigConstants($config); + + if (!isset($GLOBALS['config'])) { + $GLOBALS['config'] = &$config; + } + } + + /** + * Defines various config-related constants + * + * `ROOT_DIR`, `LIB_DIR`, `PLUGINS_DIR`, `THEMES_DIR`, `CONTENT_EXT` and + * `CACHE_DIR` were removed wih Pico 1.0, `CONTENT_DIR` existed just in + * Pico 0.9 and `CONFIG_DIR` existed just for a short time between Pico 0.9 + * and Pico 1.0. + * + * @param array &$config array of config variables + */ + protected function defineConfigConstants(array &$config) + { + if (!defined('ROOT_DIR')) { + define('ROOT_DIR', $this->getPico()->getRootDir()); + } + if (!defined('CONFIG_DIR')) { + define('CONFIG_DIR', $this->getPico()->getConfigDir()); + } + if (!defined('LIB_DIR')) { + $picoReflector = new ReflectionClass('Pico'); + define('LIB_DIR', dirname($picoReflector->getFileName()) . '/'); + } + if (!defined('PLUGINS_DIR')) { + define('PLUGINS_DIR', $this->getPico()->getPluginsDir()); + } + if (!defined('THEMES_DIR')) { + define('THEMES_DIR', $this->getPico()->getThemesDir()); + } + if (!defined('CONTENT_DIR')) { + define('CONTENT_DIR', $this->getPico()->getConfig('content_dir')); + } + if (!defined('CONTENT_EXT')) { + define('CONTENT_EXT', $this->getPico()->getConfig('content_ext')); + } + if (!defined('CACHE_DIR')) { + $twigConfig = $this->getPico()->getConfig('twig_config'); + define('CACHE_DIR', $twigConfig['cache'] ?: ''); + } + } + + /** + * Sets PicoPluginApi1CompatPlugin::$requestFile + * + * @see PicoPluginApi0CompatPlugin::$requestFile + * + * @param string &$file absolute path to the content file to serve + */ + public function onRequestFile(&$file) + { + $this->requestFile = &$file; + } + + /** + * Triggers the after_404_load_content event + * + * @param string &$rawContent raw file contents + */ + public function on404ContentLoaded(&$rawContent) + { + $this->triggerEvent('after_404_load_content', array(&$this->requestFile, &$rawContent)); + } + + /** + * Triggers the after_load_content event + * + * @param string &$rawContent raw file contents + */ + public function onContentLoaded(&$rawContent) + { + $this->triggerEvent('after_load_content', array(&$this->requestFile, &$rawContent)); + } + + /** + * Triggers the before_read_file_meta event + * + * @param string &$rawContent raw file contents + * @param string[] &$headers list of known meta header fields + */ + public function onMetaParsing(&$rawContent, array &$headers) + { + $this->triggerEvent('before_read_file_meta', array(&$headers)); + } + + /** + * Triggers the get_page_data event + * + * @param array &$pageData data of the loaded page + */ + public function onSinglePageLoaded(array &$pageData) + { + $this->triggerEvent('get_page_data', array(&$pageData, $pageData['meta'])); + } + + /** + * Triggers the get_pages event + * + * Please note that the `get_pages` event gets `$pages` passed without a + * array index. The index is rebuild later using either the `id` array key + * or is derived from the `url` array key. If it isn't possible to derive + * the array key, `~unknown` is being used. Duplicates are prevented by + * adding `~dup` when necessary. + * + * @param array[] &$pages sorted list of all known pages + * @param array|null &$currentPage data of the page being served + * @param array|null &$previousPage data of the previous page + * @param array|null &$nextPage data of the next page + */ + public function onPagesLoaded( + array &$pages, + array &$currentPage = null, + array &$previousPage = null, + array &$nextPage = null + ) { + // remove keys of pages array + $plainPages = array(); + foreach ($pages as &$plainPageData) { + $plainPages[] = &$plainPageData; + } + + // trigger event + $this->triggerEvent('get_pages', array(&$plainPages, &$currentPage, &$previousPage, &$nextPage)); + + // re-index pages array + $baseUrl = $this->getPico()->getBaseUrl(); + $baseUrlLength = strlen($baseUrl); + $urlRewritingEnabled = $this->getPico()->isUrlRewritingEnabled(); + + $pages = array(); + foreach ($plainPages as &$pageData) { + if (!isset($pageData['id'])) { + if (substr($pageData['url'], 0, $baseUrlLength) === $baseUrl) { + if ($urlRewritingEnabled && (substr($pageData['url'], $baseUrlLength, 1) === '?')) { + $pageData['id'] = substr($pageData['url'], $baseUrlLength + 1); + } else { + $pageData['id'] = substr($pageData['url'], $baseUrlLength); + } + } else { + // foreign URLs are indexed by ~unknown, ~unknown~dup1, ~unknown~dup2, … + $pageData['id'] = '~unknown'; + } + } + + // prevent duplicates + $id = $pageData['id']; + for ($i = 1; isset($pages[$id]); $i++) { + $id = $pageData['id'] . '~dup' . $i; + } + + $pages[$id] = &$pageData; + } + } + + /** + * Triggers the before_render event + * + * Please note that the `before_render` event gets `$templateName` passed + * without its file extension. The file extension is re-added later. + * + * @param Twig_Environment &$twig Twig instance + * @param string &$templateName file name of the template + * @param array &$twigVariables template variables + */ + public function onPageRendering(Twig_Environment &$twig, array &$twigVariables, &$templateName) + { + $templateNameInfo = pathinfo($templateName) + array('extension' => ''); + + // the template name hasn't had a file extension in API v0 + $templateName = $templateNameInfo['filename']; + + $this->triggerEvent('before_render', array(&$twigVariables, &$twig, &$templateName)); + + // recover original file extension + // we assume that all templates of a theme use the same file extension + $templateName = $templateName . '.' . $templateNameInfo['extension']; + } + + /** + * {@inheritDoc} + */ + public function handleCustomEvent($eventName, array $params = array()) + { + // never trigger custom events + } + + /** + * {@inheritDoc} + */ + public function triggerEvent($eventName, array $params = array()) + { + // we don't support compat plugins using API v0, so no need to take care of compat plugins here + // API v0 events are also triggered on plugins using API v1 (but not later) + $plugins = $this->getPicoDeprecated()->getPlugins(PicoDeprecated::API_VERSION_0); + $plugins += $this->getPicoDeprecated()->getPlugins(PicoDeprecated::API_VERSION_1); + + foreach ($plugins as $plugin) { + if (method_exists($plugin, $eventName)) { + call_user_func_array(array($plugin, $eventName), $params); + } + } + } + + /** + * {@inheritDoc} + */ + public function getApiVersion() + { + return PicoDeprecated::API_VERSION_1; + } + + /** + * {@inheritDoc} + */ + public function getApiVersionSupport() + { + return PicoDeprecated::API_VERSION_0; + } +} diff --git a/plugins/PicoDeprecated/plugins/PicoPluginApi1CompatPlugin.php b/plugins/PicoDeprecated/plugins/PicoPluginApi1CompatPlugin.php new file mode 100644 index 0000000..ea886df --- /dev/null +++ b/plugins/PicoDeprecated/plugins/PicoPluginApi1CompatPlugin.php @@ -0,0 +1,347 @@ + + * + * This file was created by splitting up an original file into multiple files, + * which in turn was previously part of the project's main repository. The + * version control history of these files apply accordingly, available from + * the following original locations: + * + * + * + * + * SPDX-License-Identifier: MIT + * License-Filename: LICENSE + */ + +/** + * Maintains backward compatibility with plugins using API version 1, written + * for Pico 1.0 + * + * @author Daniel Rudolf + * @link http://picocms.org + * @license http://opensource.org/licenses/MIT The MIT License + * @version 2.1 + */ +class PicoPluginApi1CompatPlugin extends AbstractPicoPluginApiCompatPlugin +{ + /** + * This plugin extends {@see PicoPluginApi2CompatPlugin} and + * {@see PicoThemeApi1CompatPlugin} + * + * @var string[] + */ + protected $dependsOn = array('PicoPluginApi2CompatPlugin', 'PicoThemeApi1CompatPlugin'); + + /** + * Map of core events matching event signatures of older API versions + * + * @see AbstractPicoPluginApiCompatPlugin::handleEvent() + * + * @var array + */ + protected $eventAliases = array( + 'onConfigLoaded' => array('onConfigLoaded'), + 'onRequestUrl' => array('onRequestUrl'), + 'onRequestFile' => array('onRequestFile'), + 'on404ContentLoaded' => array('on404ContentLoaded'), + 'onContentLoaded' => array('onContentLoaded'), + 'onContentPrepared' => array('onContentPrepared'), + 'onContentParsed' => array('onContentParsed'), + 'onPagesLoading' => array('onPagesLoading'), + 'onSinglePageLoaded' => array('onSinglePageLoaded'), + 'onPageRendered' => array('onPageRendered') + ); + + /** + * Pico's request file + * + * @see Pico::$requestFile + * @see PicoPluginApi1CompatPlugin::onRequestFile() + * + * @var string|null + */ + protected $requestFile; + + /** + * Pico's raw contents + * + * @see Pico::$rawContent + * @see PicoPluginApi1CompatPlugin::onContentLoaded() + * + * @var string|null + */ + protected $rawContent; + + /** + * Pico's meta headers array + * + * @see Pico::$metaHeaders + * @see PicoPluginApi1CompatPlugin::onMetaHeaders() + * + * @var array|null + */ + protected $metaHeaders; + + /** + * Pico's pages array + * + * @see Pico::$pages + * @see PicoPluginApi1CompatPlugin::onPagesLoaded() + * + * @var array[]|null + */ + protected $pages; + + /** + * Pico's Twig instance + * + * @see Pico::$twig + * @see PicoPluginApi1CompatPlugin::onTwigRegistered() + * + * @var Twig_Environment|null + */ + protected $twig; + + /** + * Triggers the onPluginsLoaded event + * + * Prior to API v2 the event `onPluginsLoaded` passed the `$plugins` array + * by reference. This is no longer the case. We still pass the parameter by + * reference and use {@see Pico::loadPlugin()} to load additional plugins, + * however, unloading or replacing plugins was removed without a + * replacement. This might be a BC-breaking change for you! + * + * @param object[] $plugins loaded plugin instances + */ + public function onPluginsLoaded(array $plugins) + { + $originalPlugins = $plugins; + + $this->triggerEvent('onPluginsLoaded', array(&$plugins)); + + foreach ($plugins as $pluginName => $plugin) { + if (!isset($originalPlugins[$pluginName])) { + $this->getPico()->loadPlugin($plugin); + } elseif ($plugin !== $originalPlugins[$pluginName]) { + throw new RuntimeException( + "A Pico plugin using API version 1 tried to replace Pico plugin '" . $pluginName . "' using the " + . "onPluginsLoaded() event, however, replacing plugins was removed with API version 2" + ); + } + + unset($originalPlugins[$pluginName]); + } + + if ($originalPlugins) { + $removedPluginsList = implode("', '", array_keys($originalPlugins)); + throw new RuntimeException( + "A Pico plugin using API version 1 tried to unload the Pico plugin(s) '" . $removedPluginsList . "' " + . "using the onPluginsLoaded() event, however, unloading plugins was removed with API version 2" + ); + } + } + + /** + * Sets PicoPluginApi1CompatPlugin::$requestFile + * + * @see PicoPluginApi1CompatPlugin::$requestFile + * + * @param string &$file absolute path to the content file to serve + */ + public function onRequestFile(&$file) + { + $this->requestFile = &$file; + } + + /** + * Triggers the onContentLoading event + */ + public function onContentLoading() + { + $this->triggerEvent('onContentLoading', array(&$this->requestFile)); + } + + /** + * Sets PicoPluginApi1CompatPlugin::$rawContent + * + * @see PicoPluginApi1CompatPlugin::$rawContent + * + * @param string &$rawContent raw file contents + */ + public function onContentLoaded(&$rawContent) + { + $this->rawContent = &$rawContent; + } + + /** + * Triggers the on404ContentLoading event + */ + public function on404ContentLoading() + { + $this->triggerEvent('on404ContentLoading', array(&$this->requestFile)); + } + + /** + * Triggers the onMetaParsing event + * + * @see PicoPluginApi1CompatPlugin::onMetaHeaders() + */ + public function onMetaParsing() + { + $headersFlipped = $this->getFlippedMetaHeaders(); + $this->triggerEvent('onMetaParsing', array(&$this->rawContent, &$headersFlipped)); + $this->updateFlippedMetaHeaders($headersFlipped); + } + + /** + * Triggers the onMetaParsed and onParsedownRegistration events + * + * @param string[] &$meta parsed meta data + */ + public function onMetaParsed(array &$meta) + { + $this->triggerEvent('onMetaParsed', array(&$meta)); + $this->triggerEvent('onParsedownRegistration'); + } + + /** + * Triggers the onContentParsing event + */ + public function onContentParsing() + { + $this->triggerEvent('onContentParsing', array(&$this->rawContent)); + } + + /** + * Sets PicoPluginApi1CompatPlugin::$pages + * + * @see PicoPluginApi1CompatPlugin::$pages + * + * @param array[] &$pages sorted list of all known pages + */ + public function onPagesLoaded(array &$pages) + { + $this->pages = &$pages; + } + + /** + * Triggers the onPagesLoaded and onTwigRegistration events + * + * @param array|null &$currentPage data of the page being served + * @param array|null &$previousPage data of the previous page + * @param array|null &$nextPage data of the next page + */ + public function onCurrentPageDiscovered( + array &$currentPage = null, + array &$previousPage = null, + array &$nextPage = null + ) { + $this->triggerEvent('onPagesLoaded', array(&$this->pages, &$currentPage, &$previousPage, &$nextPage)); + + $this->triggerEvent('onTwigRegistration'); + $this->getPico()->getTwig(); + } + + /** + * Triggers the onPageRendering event + * + * @param string &$templateName file name of the template + * @param array &$twigVariables template variables + */ + public function onPageRendering(&$templateName, array &$twigVariables) + { + $this->triggerEvent('onPageRendering', array(&$this->twig, &$twigVariables, &$templateName)); + } + + /** + * Triggers the onMetaHeaders event with flipped meta headers and sets + * PicoPluginApi1CompatPlugin::$metaHeaders + * + * @see PicoPluginApi1CompatPlugin::$metaHeaders + * + * @param string[] &$headers list of known meta header fields; the array + * key specifies the YAML key to search for, the array value is later + * used to access the found value + */ + public function onMetaHeaders(array &$headers) + { + $this->metaHeaders = &$headers; + + $headersFlipped = $this->getFlippedMetaHeaders(); + $this->triggerEvent('onMetaHeaders', array(&$headersFlipped)); + $this->updateFlippedMetaHeaders($headersFlipped); + } + + /** + * Sets PicoPluginApi1CompatPlugin::$twig + * + * @see PicoPluginApi1CompatPlugin::$twig + * + * @param Twig_Environment &$twig Twig instance + */ + public function onTwigRegistered(Twig_Environment &$twig) + { + $this->twig = $twig; + } + + /** + * Returns the flipped meta headers array + * + * Pico 1.0 and earlier were using the values of the meta headers array to + * match registered meta headers in a page's meta data, and used the keys + * of the meta headers array to store the meta value in the page's meta + * data. However, starting with Pico 2.0 it is the other way round. This + * allows us to specify multiple "search strings" for a single registered + * meta value (e.g. "Nyan Cat" and "Tac Nayn" can be synonmous). + * + * @return array flipped meta headers + */ + protected function getFlippedMetaHeaders() + { + if ($this->metaHeaders === null) { + // make sure to trigger the onMetaHeaders event + $this->getPico()->getMetaHeaders(); + } + + return array_flip($this->metaHeaders ?: array()); + } + + /** + * Syncs PicoPluginApi1CompatPlugin::$metaHeaders with a flipped headers array + * + * @param array $headersFlipped flipped headers array + */ + protected function updateFlippedMetaHeaders(array $headersFlipped) + { + foreach ($this->metaHeaders as $name => $key) { + if (!isset($headersFlipped[$key])) { + unset($this->metaHeaders[$name]); + } + } + + foreach ($headersFlipped as $key => $name) { + $this->metaHeaders[$name] = $key; + } + } + + /** + * {@inheritDoc} + */ + public function getApiVersion() + { + return PicoDeprecated::API_VERSION_2; + } + + /** + * {@inheritDoc} + */ + public function getApiVersionSupport() + { + return PicoDeprecated::API_VERSION_1; + } +} diff --git a/plugins/PicoDeprecated/plugins/PicoPluginApi2CompatPlugin.php b/plugins/PicoDeprecated/plugins/PicoPluginApi2CompatPlugin.php new file mode 100644 index 0000000..a9ebbee --- /dev/null +++ b/plugins/PicoDeprecated/plugins/PicoPluginApi2CompatPlugin.php @@ -0,0 +1,131 @@ + + * + * This file was created by splitting up an original file into multiple files, + * which in turn was previously part of the project's main repository. The + * version control history of these files apply accordingly, available from + * the following original locations: + * + * + * + * + * SPDX-License-Identifier: MIT + * License-Filename: LICENSE + */ + +/** + * Maintains backward compatibility with plugins using API version 2, written + * for Pico 2.0 + * + * @author Daniel Rudolf + * @link http://picocms.org + * @license http://opensource.org/licenses/MIT The MIT License + * @version 2.1 + */ +class PicoPluginApi2CompatPlugin extends AbstractPicoPluginApiCompatPlugin +{ + /** + * This plugin extends {@see PicoThemeApi2CompatPlugin} + * + * @var string[] + */ + protected $dependsOn = array('PicoThemeApi2CompatPlugin'); + + /** + * Map of core events matching event signatures of older API versions + * + * @see AbstractPicoPluginApiCompatPlugin::handleEvent() + * + * @var array + */ + protected $eventAliases = array( + 'onPluginsLoaded' => array('onPluginsLoaded'), + 'onPluginManuallyLoaded' => array('onPluginManuallyLoaded'), + 'onRequestUrl' => array('onRequestUrl'), + 'onRequestFile' => array('onRequestFile'), + 'onContentLoading' => array('onContentLoading'), + 'on404ContentLoading' => array('on404ContentLoading'), + 'on404ContentLoaded' => array('on404ContentLoaded'), + 'onContentLoaded' => array('onContentLoaded'), + 'onMetaParsing' => array('onMetaParsing'), + 'onMetaParsed' => array('onMetaParsed'), + 'onContentParsing' => array('onContentParsing'), + 'onContentPrepared' => array('onContentPrepared'), + 'onContentParsed' => array('onContentParsed'), + 'onPagesLoading' => array('onPagesLoading'), + 'onSinglePageLoading' => array('onSinglePageLoading'), + 'onSinglePageContent' => array('onSinglePageContent'), + 'onSinglePageLoaded' => array('onSinglePageLoaded'), + 'onPagesDiscovered' => array('onPagesDiscovered'), + 'onPagesLoaded' => array('onPagesLoaded'), + 'onCurrentPageDiscovered' => array('onCurrentPageDiscovered'), + 'onPageTreeBuilt' => array('onPageTreeBuilt'), + 'onPageRendering' => array('onPageRendering'), + 'onPageRendered' => array('onPageRendered'), + 'onMetaHeaders' => array('onMetaHeaders'), + 'onYamlParserRegistered' => array('onYamlParserRegistered'), + 'onParsedownRegistered' => array('onParsedownRegistered'), + 'onTwigRegistered' => array('onTwigRegistered') + ); + + /** + * Pico's config array + * + * @see Pico::$config + * @see PicoPluginApi2CompatPlugin::onConfigLoaded() + * + * @var array|null + */ + protected $config; + + /** + * Sets PicoPluginApi1CompatPlugin::$config and handles the theme_url + * config param + * + * @see PicoPluginApi2CompatPlugin::$config + * + * @param array $config + */ + public function onConfigLoaded(array &$config) + { + $this->config = &$config; + + if (!empty($config['theme_url'])) { + $config['themes_url'] = $this->getPico()->getAbsoluteUrl($config['theme_url']); + $config['theme_url'] = &$config['themes_url']; + } + } + + /** + * Triggers the onConfigLoaded event + * + * @param string $theme name of current theme + * @param int $themeApiVersion API version of the theme + * @param array &$themeConfig config array of the theme + */ + public function onThemeLoaded($theme, $themeApiVersion, array &$themeConfig) + { + $this->triggerEvent('onConfigLoaded', array(&$this->config)); + } + + /** + * {@inheritDoc} + */ + public function getApiVersion() + { + return PicoDeprecated::API_VERSION_3; + } + + /** + * {@inheritDoc} + */ + public function getApiVersionSupport() + { + return PicoDeprecated::API_VERSION_2; + } +} diff --git a/plugins/PicoDeprecated/plugins/PicoThemeApi0CompatPlugin.php b/plugins/PicoDeprecated/plugins/PicoThemeApi0CompatPlugin.php new file mode 100644 index 0000000..9ed6a64 --- /dev/null +++ b/plugins/PicoDeprecated/plugins/PicoThemeApi0CompatPlugin.php @@ -0,0 +1,52 @@ + + * + * This file was created by splitting up an original file into multiple files, + * which in turn was previously part of the project's main repository. The + * version control history of these files apply accordingly, available from + * the following original locations: + * + * + * + * + * SPDX-License-Identifier: MIT + * License-Filename: LICENSE + */ + +/** + * Maintains backward compatibility with themes using API version 0, written + * for Pico 0.9 and earlier + * + * Since there were no theme-related changes between Pico 0.9 and Pico 1.0, + * this compat plugin doesn't hold any code itself, it just depends on + * {@see PicoThemeApi1CompatPlugin}. Since themes didn't support API versioning + * until Pico 2.1 (i.e. API version 3), all older themes will appear to use API + * version 0. + * + * @author Daniel Rudolf + * @link http://picocms.org + * @license http://opensource.org/licenses/MIT The MIT License + * @version 2.1 + */ +class PicoThemeApi0CompatPlugin extends AbstractPicoCompatPlugin +{ + /** + * This plugin extends {@see PicoThemeApi1CompatPlugin} + * + * @var string[] + */ + protected $dependsOn = array('PicoThemeApi1CompatPlugin'); + + /** + * {@inheritDoc} + */ + public function getApiVersion() + { + return PicoDeprecated::API_VERSION_3; + } +} diff --git a/plugins/PicoDeprecated/plugins/PicoThemeApi1CompatPlugin.php b/plugins/PicoDeprecated/plugins/PicoThemeApi1CompatPlugin.php new file mode 100644 index 0000000..f4b276b --- /dev/null +++ b/plugins/PicoDeprecated/plugins/PicoThemeApi1CompatPlugin.php @@ -0,0 +1,154 @@ + + * + * This file was created by splitting up an original file into multiple files, + * which in turn was previously part of the project's main repository. The + * version control history of these files apply accordingly, available from + * the following original locations: + * + * + * + * + * SPDX-License-Identifier: MIT + * License-Filename: LICENSE + */ + +/** + * Maintains backward compatibility with themes using API version 1, written + * for Pico 1.0 + * + * @author Daniel Rudolf + * @link http://picocms.org + * @license http://opensource.org/licenses/MIT The MIT License + * @version 2.1 + */ +class PicoThemeApi1CompatPlugin extends AbstractPicoCompatPlugin +{ + /** + * This plugin extends {@see PicoThemeApi2CompatPlugin} + * + * @var string[] + */ + protected $dependsOn = array('PicoThemeApi2CompatPlugin'); + + /** + * Lowers the page's meta headers + * + * @see PicoThemeApi1CompatPlugin::lowerFileMeta() + * + * @param string[] &$meta parsed meta data + */ + public function onMetaParsed(array &$meta) + { + $this->lowerFileMeta($meta); + } + + /** + * Lowers the page's meta headers + * + * @see PicoThemeApi1CompatPlugin::lowerFileMeta() + * + * @param array &$pageData data of the loaded page + */ + public function onSinglePageLoaded(array &$pageData) + { + // don't lower the file meta of the requested page, + // it was already lowered during the onMetaParsed event + $contentDir = $this->getPico()->getConfig('content_dir'); + $contentExt = $this->getPico()->getConfig('content_ext'); + if ($contentDir . $pageData['id'] . $contentExt !== $this->getPico()->getRequestFile()) { + $this->lowerFileMeta($pageData['meta']); + } + } + + /** + * Handles .html Twig templates and re-introcudes the Twig variables + * rewrite_url and is_front_page + * + * @param string &$templateName file name of the template + * @param array &$twigVariables template variables + */ + public function onPageRendering(&$templateName, array &$twigVariables) + { + if (!isset($twigVariables['rewrite_url'])) { + $twigVariables['rewrite_url'] = $this->getPico()->isUrlRewritingEnabled(); + } + + if (!isset($twigVariables['is_front_page'])) { + $contentDir = $this->getPico()->getConfig('content_dir'); + $contentExt = $this->getPico()->getConfig('content_ext'); + $requestFile = $this->getPico()->getRequestFile(); + $twigVariables['is_front_page'] = ($requestFile === $contentDir . 'index' . $contentExt); + } + + // API v2 requires themes to use .twig as file extension + // try to load the template and if this fails, try .html instead (as of API v1) + $templateNameInfo = pathinfo($templateName) + array('extension' => ''); + $twig = $this->getPico()->getTwig(); + + try { + $twig->loadTemplate($templateName); + } catch (Twig_Error_Loader $e) { + if ($templateNameInfo['extension'] === 'twig') { + try { + $twig->loadTemplate($templateNameInfo['filename'] . '.html'); + + $templateName = $templateNameInfo['filename'] . '.html'; + $templateNameInfo['extension'] = 'html'; + } catch (Twig_Error_Loader $e) { + // template doesn't exist, Twig will very likely fail later + } + } + } + } + + /** + * Lowers a page's meta headers as with Pico 1.0 and older + * + * This makes unregistered meta headers available using lowered array keys + * and matches registered meta headers in a case-insensitive manner. + * + * @param array &$meta meta data + */ + protected function lowerFileMeta(array &$meta) + { + $metaHeaders = $this->getPico()->getMetaHeaders(); + + // get unregistered meta + $unregisteredMeta = array(); + foreach ($meta as $key => $value) { + if (!in_array($key, $metaHeaders)) { + $unregisteredMeta[$key] = &$meta[$key]; + } + } + + // Pico 1.0 lowered unregistered meta unsolicited... + if ($unregisteredMeta) { + $metaHeadersLowered = array_change_key_case($metaHeaders, CASE_LOWER); + foreach ($unregisteredMeta as $key => $value) { + $keyLowered = strtolower($key); + if (isset($metaHeadersLowered[$keyLowered])) { + $registeredKey = $metaHeadersLowered[$keyLowered]; + if ($meta[$registeredKey] === '') { + $meta[$registeredKey] = &$unregisteredMeta[$key]; + } + } elseif (!isset($meta[$keyLowered]) || ($meta[$keyLowered] === '')) { + $meta[$keyLowered] = &$unregisteredMeta[$key]; + } + } + } + } + + /** + * {@inheritDoc} + */ + public function getApiVersion() + { + return PicoDeprecated::API_VERSION_3; + } +} diff --git a/plugins/PicoDeprecated/plugins/PicoThemeApi2CompatPlugin.php b/plugins/PicoDeprecated/plugins/PicoThemeApi2CompatPlugin.php new file mode 100644 index 0000000..646925b --- /dev/null +++ b/plugins/PicoDeprecated/plugins/PicoThemeApi2CompatPlugin.php @@ -0,0 +1,205 @@ + + * + * This file was created by splitting up an original file into multiple files, + * which in turn was previously part of the project's main repository. The + * version control history of these files apply accordingly, available from + * the following original locations: + * + * + * + * + * SPDX-License-Identifier: MIT + * License-Filename: LICENSE + */ + +/** + * Maintains backward compatibility with themes using API version 2, written + * for Pico 2.0 + * + * @author Daniel Rudolf + * @link http://picocms.org + * @license http://opensource.org/licenses/MIT The MIT License + * @version 2.1 + */ +class PicoThemeApi2CompatPlugin extends AbstractPicoCompatPlugin +{ + /** + * Manually configured Twig escape strategy + * + * @var mixed|null + */ + protected $twigEscapeStrategy; + + /** + * Directory paths of plugins + * + * @var string[] + */ + protected $pluginPaths = array(); + + /** + * Sets PicoThemeApi2CompatPlugin::$twigEscapeStrategy + * + * @see PicoThemeApi2CompatPlugin::$twigEscapeStrategy + * + * @param array &$config array of config variables + */ + public function onConfigLoaded(array &$config) + { + if (isset($config['twig_config']['autoescape'])) { + $this->twigEscapeStrategy = $config['twig_config']['autoescape']; + } + } + + /** + * Re-introduces the Twig variables prev_page, base_dir and theme_dir + * + * @param string &$templateName file name of the template + * @param array &$twigVariables template variables + */ + public function onPageRendering(&$templateName, array &$twigVariables) + { + $twigVariables['prev_page'] = &$twigVariables['previous_page']; + $twigVariables['base_dir'] = rtrim($this->getPico()->getRootDir(), '/'); + $twigVariables['theme_dir'] = $this->getPico()->getThemesDir() . $this->getPico()->getTheme(); + } + + /** + * Registers PicoPluginApi2CompatPlugin::twigEscapeStrategy() as Twig's + * default escape strategy + * + * @see PicoPluginApi2CompatPlugin::twigEscapeStrategy() + * + * @param Twig_Environment &$twig Twig instance + */ + public function onTwigRegistered(Twig_Environment &$twig) + { + if ($twig->hasExtension('Twig_Extension_Escaper')) { + /** @var Twig_Extension_Escaper $escaperExtension */ + $escaperExtension = $twig->getExtension('Twig_Extension_Escaper'); + $escaperExtension->setDefaultStrategy(array($this, 'twigEscapeStrategy')); + } + } + + /** + * Returns Twig's default escaping strategy for the given template + * + * This escape strategy takes a template name and decides whether Twig's + * global default escape strategy should be used, or escaping should be + * disabled. Escaping is disabled for themes using API v2 and below as well + * as for templates of plugins using API v2 and below. If a escape strategy + * has been configured manually, this method always returns this explicitly + * configured escape strategy. + * + * @param string $templateName template name + * + * @return string|false escape strategy for this template + */ + public function twigEscapeStrategy($templateName) + { + $twigConfig = $this->getPico()->getConfig('twig_config'); + $escapeStrategy = $twigConfig['autoescape']; + + if (($this->twigEscapeStrategy !== null) && ($escapeStrategy === $this->twigEscapeStrategy)) { + return $escapeStrategy; + } + + if (!is_string($escapeStrategy) && ($escapeStrategy !== false)) { + $escapeStrategy = call_user_func($escapeStrategy, $templateName); + } + + if ($escapeStrategy === false) { + return false; + } + + /** @var Twig_SourceContextLoaderInterface $twigLoader */ + $twigLoader = $this->getPico()->getTwig()->getLoader(); + if (!$twigLoader instanceof Twig_SourceContextLoaderInterface) { + throw new RuntimeException( + "PicoDeprecated compat plugin '" . __CLASS__ . "' requires a 'Twig_SourceContextLoaderInterface' " + . "Twig loader, '" . get_class($twigLoader) . "' given" + ); + } + + try { + $templatePath = $twigLoader->getSourceContext($templateName)->getPath(); + } catch (\Twig\Error\LoaderError $e) { + $templatePath = ''; + } + + if ($templatePath) { + $themePath = realpath($this->getPico()->getThemesDir() . $this->getPico()->getTheme()) . '/'; + if (substr($templatePath, 0, strlen($themePath)) === $themePath) { + $themeApiVersion = $this->getPico()->getThemeApiVersion(); + return ($themeApiVersion >= PicoDeprecated::API_VERSION_3) ? $escapeStrategy : false; + } + + $plugin = $this->getPluginFromPath($templatePath); + if ($plugin) { + $pluginApiVersion = $this->getPicoDeprecated()->getPluginApiVersion($plugin); + return ($pluginApiVersion >= PicoDeprecated::API_VERSION_3) ? $escapeStrategy : false; + } + } + + // unknown template path + // to preserve BC we must assume that the template uses an old API version + return false; + } + + /** + * Returns the matching plugin instance when the given path is within a + * plugin's base directory + * + * @param string $path file path to search for + * + * @return object|null either the matching plugin instance or NULL + */ + protected function getPluginFromPath($path) + { + $plugins = $this->getPico()->getPlugins(); + foreach ($this->pluginPaths as $pluginName => $pluginPath) { + if ($pluginPath && (substr($path, 0, strlen($pluginPath)) === $pluginPath)) { + return $plugins[$pluginName]; + } + } + + $rootDir = realpath($this->getPico()->getRootDir()) . '/'; + $vendorDir = realpath($this->getPico()->getVendorDir()) . '/'; + $pluginsDir = realpath($this->getPico()->getPluginsDir()) . '/'; + $themesDir = realpath($this->getPico()->getThemesDir()) . '/'; + foreach ($plugins as $pluginName => $plugin) { + if (isset($this->pluginPaths[$pluginName])) { + continue; + } + + $pluginReflector = new ReflectionObject($plugin); + + $pluginPath = dirname($pluginReflector->getFileName() ?: '') . '/'; + if (in_array($pluginPath, array('/', $rootDir, $vendorDir, $pluginsDir, $themesDir), true)) { + $pluginPath = ''; + } + + $this->pluginPaths[$pluginName] = $pluginPath; + + if ($pluginPath && (substr($path, 0, strlen($pluginPath)) === $pluginPath)) { + return $plugins[$pluginName]; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getApiVersion() + { + return PicoDeprecated::API_VERSION_3; + } +} diff --git a/plugins/PicoPagesList/LICENCE.md b/plugins/PicoPagesList/LICENCE.md new file mode 100644 index 0000000..73eeb80 --- /dev/null +++ b/plugins/PicoPagesList/LICENCE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Nicolas Liautaud + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/plugins/PicoPagesList/PicoPagesList.php b/plugins/PicoPagesList/PicoPagesList.php new file mode 100644 index 0000000..1a8b810 --- /dev/null +++ b/plugins/PicoPagesList/PicoPagesList.php @@ -0,0 +1,245 @@ +items = $this->nestedPages($pages); + } + + /** + * Register `$this` in the Twig `{{ PagesList }}` variable. + * + * + * @see Pico::getTwig() + * @param Twig_Environment &$twig Twig instance + * @return void + */ + public function onTwigRegistered(Twig_Environment &$twig) + { + $twig->addFilter(new Twig_SimpleFilter('navigation', function($pages) { + return $this->output($pages); + }, array('is_safe' => array('html')))); + + $twig->addFilter(new Twig_SimpleFilter('exclude', function($pages, array $paths = array()) { + return $this->filterPages($pages, $paths); + }, array('is_variadic' => true))); + + $twig->addFilter(new Twig_SimpleFilter('only', function($pages, array $paths = array()) { + return $this->filterPages($pages, $paths, true); + }, array('is_variadic' => true))); + } + + /** + * Register `$this` in the Twig `{{ PagesList }}` variable. + * + * Triggered before Pico renders the page + * + * @see Pico::getTwig() + * @see DummyPlugin::onPageRendered() + * @param string &$templateName file name of the template + * @param array &$twigVariables template variables + * @return void + */ + public function onPageRendering(string &$templateName, array &$twigVariables) + { + $twigVariables['nested_pages'] = $this->items; + } + + /** + * Create the nested pages array according to the pages paths. + * + * @see nested_path + * @param array $pages Pico pages flat array + */ + private function nestedPages($pages) + { + $this->items = array(); + foreach ($pages as $page) { + $nested_path = $this->nested_path($page); + $this->items = array_replace_recursive($this->items, $nested_path); + } + return $this->items['_childs']; + } + + /** + * Create a nested array of a given path, with the page at the end. + * Each path fragment is in "_childs" of the parent. + * + * @param array $page the page array + * @return array the nested path + */ + private function nested_path($page) + { + $path = self::rtrim($page['id'], '/index'); + $parts = explode('/', $path); + $count = count($parts); + + $arr = array(); + $parent = &$arr; + foreach($parts as $id => $part) { + $value = []; + if(!$part || $id == $count-1) { + $value = $page; + } else { + $currpath = implode('/', array_slice($parts, 0, $id+1)); + $value['id'] = $currpath; + } + if($path && !$part) { + $parent = $value; + break; + } + + $parent['_childs'][$part] = $value; + $parent = &$parent['_childs'][$part]; + } + return $arr; + } + + /** + * Strip a substring from the end of a string + * + * @param array $str The input string. + * @param array $substr The substring to remove. + * @return string The modified string. + */ + private static function rtrim($str, $substr) + { + $length = strlen($substr); + return (substr($str, -$length) === $substr) ? substr($str, 0, -$length) : $str; + } + + /** + * Filter the pages array according to given paths, as exclusive or inclusive. + * + * @param array $pages The flat or nested pages array. + * @param array $filteredPaths The paths to filter. + * @param boolean $isInclusive If `true` only corresponding paths are kept. + * @return array The filtered array of pages. + */ + public static function filterPages( + $pages, + $filteredPaths, + $isInclusive = false, + $inclusiveOutput = [] + ) { + foreach($pages as $i => $page) { + if (!isset($page['id'])) return; + + $path = self::rtrim($page['id'], '/index'); + $isSubPath = self::isSubPath($path, $filteredPaths); + if ($isSubPath) { + if ($isInclusive) $inclusiveOutput[$i] = $page; + else unset($pages[$i]); + continue; + } + if (isset($page['_childs'])) { + $childs = self::filterPages($page['_childs'], $filteredPaths, $isInclusive, $inclusiveOutput); + if ($isInclusive) $inclusiveOutput = $childs; + else $pages[$i]['_childs'] = $childs; + } + } + return $isInclusive ? $inclusiveOutput : $pages; + } + + /** + * Return if the given path is a subpath of the given parent path(s) + * + * @param string $path + * @param array $parentPaths array of paths + * @return boolean + */ + private static function isSubPath($path, $parentPaths) + { + foreach($parentPaths as $p) { + if (!is_string($p)) continue; + if ($path == $p) return true; + if (strncmp($path, $p, strlen($p)) === 0) + return true; + } + return false; + } + + /** + * Return an html nested list based on a nested pages array. + * + * @param array $pages a nested pages array + * @return string the html list + */ + private function output($pages) + { + if (!is_array($pages)) return; + $html = '
    '; + foreach ($pages as $pageID => $page) + { + if (!empty($page['hidden'])) continue; + + $childsOutput = ''; + if(isset($page['_childs'])) { + $childsOutput = $this->output($page['_childs']); + } + + $url = isset($page['url']) ? $page['url'] : false; + + // use title if the page has one and make a link if the page exists. + if(!$url) $item = "$pageID"; + else { + $name = !empty($page['title']) ? $page['title'] : $pageID; + $item = "$name"; + } + + // add the pageID in class and indicate if it is the current or parent of the current page. + $class = $pageID; + $class .= $url ? ' is-page' : ' is-directory'; + if ($childsOutput) $class .= ' has-childs'; + + $currentPage = $this->getPico()->getCurrentPage(); + if ($currentPage && $currentPage['id']) { + if ($currentPage['id'] === $page['id']) { + $class .= ' is-current is-active'; + } elseif ($page['id'] !== 'index') { + $pagePath = (basename($page['id']) === 'index') ? dirname($page['id']) . '/' : $page['id'] . '/'; + if (substr_compare($pagePath, $currentPage['id'], 0, strlen($pagePath)) === 0) $class .= ' is-active'; + } + } + + $html .= "
  • $item$childsOutput
  • "; + } + $html .= '
'; + return $html; + } +} diff --git a/plugins/PicoPagesList/README.md b/plugins/PicoPagesList/README.md new file mode 100644 index 0000000..f6cfbe3 --- /dev/null +++ b/plugins/PicoPagesList/README.md @@ -0,0 +1,154 @@ +# Pico Pages List + +A flat and nested pages lists plugin for [Pico CMS](http://picocms.org). + +- `{{ nested_pages }}` array +- nested or flat html navigations +- pages links and directories structures +- versatile html/css for dropdown menus, single-line breadcrumbs... +- `exclude` and `only` pages filters + +[![Examples](examples/capture.png)](http://pico.nliautaud.fr/PicoPagesList) + +Demo : http://pico.nliautaud.fr/PicoPagesList + +## Installation + +Copy `PicoPagesList.php` to the `plugins` directory of your Pico Project. + +## Usage + +Create a nested HTML navigation tree with : + +```twig +{{ nested_pages | navigation }} +``` + +The nested navigation will look like that : + +* [A cool page]() +* [Sub-page is coming]() + * [The choosen one]() + * category + * [A page]() +* [untitled]() + +The global `nested_pages` and the filter `navigation` render an HTML navigation. Works on `{{ pages }}` too. + +```twig +{{ pages | navigation }} // output a flat pages list +``` + +## Filtering + +The plugin create two additionnal Twig filters, `exclude()` and `only()`, that filters the given pages array (`pages` or `nested_pages`) by paths. + +```twig +pages | exclude('path/') // exclude the page located under "path/" +pages | only('path/') // return only the page located at "path/" +``` + +Use the leading slath to target index pages or not. + +```twig +pages | exclude('sub/dir/') // exclude the page located under "sub/dir/", but not "sub/dir" (index) +pages | exclude('sub/dir') // exclude "sub/dir" (index) and pages located under "sub/dir/" +``` + +You can specify multiple paths at once by using multiple arguments. + +```twig +exclude('sub/dir', 'page') +only('sub/dir', 'page') +``` + +### Styling + +The default html output is a clean nested list with extra classes that provides the possibility to build hierarchical navigations and to target specific pages and directories. + +```html + +``` + +```css +.foo-item { /* an item named "foo-item" */ } +.foo-item > a { /* the link of a page named "foo-item" */ } +.foo-item > span { /* the name of a directory named "foo-item" */ } +.foo-item > ul { /* the childs of "foo-item" */ } +.foo-item > ul ul { /* the deep childs of "foo-item" */ } + +.is-page { /* the pages, with links */ } +.is-directory { /* the directories, with simple names */ } +.is-current { /* the current page */ } +.is-active { /* the items in the path of the current page */ } +.has-childs { /* the items with childs */ } +``` + +As a simple example, you may show sub-pages only if their parent is active : + +```css +.mymenu li.is-page:not(.is-active) ul { + display: none; +} +``` + +## Custom loop + +The `{{ nested_pages }}` global is an array of pages, similar to `{{ pages }}`, where sub-pages are nested into `_childs`. + +You may want a recursive Twig template or macro to walk trough it, for example : + +```twig +{% macro menu(items) %} +
    + {% for name,item in items %} +
  • + {% if item.url %} + {{ item.title }} : {{ item.description }} + {% else %} + {{ name }} + {% endif %} + {% if item._childs %} + {% import _self as macros %} + {{ macros.menu(item._childs) }} + {% endif %} +
  • + {% endfor %} +
+{% endmacro %} + +{% import _self as macros %} +{{ macros.menu(nested_pages) }} +``` + +## Settings + +The lists are sorted according to the default settings in Pico `config.php`. + +```yml +pages_order_by: date +pages_order: desc +``` diff --git a/plugins/PicoPagesList/composer.json b/plugins/PicoPagesList/composer.json new file mode 100644 index 0000000..39beda0 --- /dev/null +++ b/plugins/PicoPagesList/composer.json @@ -0,0 +1,30 @@ +{ + "name": "nliautaud/pico-pages-list", + "type": "pico-plugin", + "description": "A pages lists plugin for Pico CMS, with nested pages, pages filtering and HTML navigation.", + "keywords": [ "pico", "picocms", "picocms-plugin", "pico-pages-list"], + "homepage": "http://picocms.org/", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Liautaud", + "homepage": "https://github.com/nliautaud/pico-pages-list", + "role": "Lead Developer" + }, + { + "name": "Contributors", + "homepage": "https://github.com/nliautaud/pico-pages-list/graphs/contributors" + } + ], + "support": { + "docs": "https://github.com/nliautaud/pico-pages-list/blob/master/README.md", + "issues": "https://github.com/nliautaud/pico-pages-list/issues", + "source": "https://github.com/nliautaud/pico-pages-list" + }, + "require": { + "php": ">=5.4.0" + }, + "autoload": { + "classmap": [ "PicoPagesList.php" ] + } +} \ No newline at end of file diff --git a/plugins/PicoPagesList/examples/capture.png b/plugins/PicoPagesList/examples/capture.png new file mode 100644 index 0000000..dcf634a Binary files /dev/null and b/plugins/PicoPagesList/examples/capture.png differ diff --git a/plugins/PicoPagesList/examples/demo.twig b/plugins/PicoPagesList/examples/demo.twig new file mode 100644 index 0000000..423d037 --- /dev/null +++ b/plugins/PicoPagesList/examples/demo.twig @@ -0,0 +1,162 @@ + +
+
+
+
+

Generate a flat navigation using the default page array.

+ {% raw %}{{ pages | navigation }}{% endraw %} +
+ {{ pages | navigation }} +
+
+
+

Generate a nested navigation using the nested array.

+ {% raw %}{{ nested_pages | navigation }}{% endraw %} +
+ {{ nested_pages | navigation }} +
+
+
+

The html structure and the css classes allows styling current page, active parent pages, naked directories, getting pages by name, etc. to build any type of navigation : dropdown menus, single-lined breadcrumbs... +

+ {% raw %}.is-page, .is-directory, .is-current, .is-active, .has-childs ...{% endraw %} +
+
+
+
+

Filtering pages paths with the exclude filter :

+ {% raw %}{{ nested_pages | exclude('PicoPagesList') | navigation }}{% endraw %} +
+ {{ nested_pages | exclude('PicoPagesList') | navigation }} +
+
+
+

Filter the inner pages by using a trailing slash :

+ {% raw %}{{ nested_pages | exclude('PicoPagesList/') | navigation }}{% endraw %} +
+ {{ nested_pages | exclude('PicoPagesList/') | navigation }} +
+
+
+

Filter all pages except the given ones with only, and chain filters :

+ {% raw %}{{ nested_pages | only('PicoPagesList') | exclude('PicoPagesList/sub/bar') | navigation }}{% endraw %} +
+ {{ nested_pages | only('PicoPagesList') | exclude('PicoPagesList/sub/bar') | navigation }} +
+
+
+

Filters can be given multiple paths :

+ {% raw %}{{ nested_pages | only('PicoPagesList/sub', 'index') | navigation }}{% endraw %} +
+ {{ nested_pages | only('PicoPagesList/sub', 'index') | navigation }} +
+
+
+

Custom loop :

+ {% raw %}{% macro menu(items) %} + <ul> + {% for name,item in items %} + <li> + {% if item.url %} + <a href="{{ item.url }}">{{ item.title }}</a> : {{ item.description }} + {% else %} + <span>{{ name }}</span> + {% endif %} + {% if item._childs %} + {% import 'macros.twig' as macros %} + {{ macros.menu(item._childs) }} + {% endif %} + </li> + {% endfor %} + </ul> +{% endmacro %}{% endraw %} + +{% raw %}{% import 'macros.twig' as macros %} +{{ macros.menu(nested_pages) }}{% endraw %} +
+ {% macro menu(items) %} +
    + {% for name,item in items %} +
  • + {% if item.url %} + {{ item.title }} : {{ item.description }} + {% else %} + {{ name }} + {% endif %} + {% if item._childs %} + {% import _self as macros %} + {{ macros.menu(item._childs) }} + {% endif %} +
  • + {% endfor %} +
+ {% endmacro %} + + {% import _self as macros %} + {{ macros.menu(nested_pages) }} +
+
+
\ No newline at end of file diff --git a/plugins/PicoVideoEmbed/LICENSE.md b/plugins/PicoVideoEmbed/LICENSE.md new file mode 100644 index 0000000..cba6f6a --- /dev/null +++ b/plugins/PicoVideoEmbed/LICENSE.md @@ -0,0 +1,660 @@ +### GNU AFFERO GENERAL PUBLIC LICENSE + +Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +### Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains +free software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing +under this license. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS + +#### 0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public +License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +#### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +#### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +#### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +#### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +#### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +#### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +#### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +#### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +#### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +#### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +#### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +#### 13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your +version supports such interaction) an opportunity to receive the +Corresponding Source of your version by providing access to the +Corresponding Source from a network server at no charge, through some +standard or customary means of facilitating copying of software. This +Corresponding Source shall include the Corresponding Source for any +work covered by version 3 of the GNU General Public License that is +incorporated pursuant to the following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +#### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU Affero General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever +published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +#### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +#### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +#### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively state +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper +mail. + +If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for +the specific requirements. + +You should also get your employer (if you work as a programmer) or +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU AGPL, see . diff --git a/plugins/PicoVideoEmbed/PicoVideoEmbed.php b/plugins/PicoVideoEmbed/PicoVideoEmbed.php new file mode 100644 index 0000000..0098617 --- /dev/null +++ b/plugins/PicoVideoEmbed/PicoVideoEmbed.php @@ -0,0 +1,116 @@ + + * @license https://www.gnu.org/licenses/agpl-3.0.en.html + * @version 0.1 + */ +class PicoVideoEmbed extends AbstractPicoPlugin +{ + /** + * API version used by this plugin + * + * @var int + */ + const API_VERSION = 3; + + /** + * This plugin depends on ... + * + * @see AbstractPicoPlugin::$dependsOn + * @var string[] + */ + protected $dependsOn = array(); + + + const SHORTCODE = '#\[video .+?\]#i'; + + /** + * Triggered after Pico has prepared the raw file contents for parsing + * + * @see Pico::parseFileContent() + * @see DummyPlugin::onContentParsed() + * @param string &$content prepared file contents for parsing + * @return void + */ + public function onContentParsed(&$content) + { + if (stripos($content, '[video') !== false) { + // Search for Embed shortcodes allover the content + preg_match_all(self::SHORTCODE, $content, $matches); + + // Make sure we found some shortcodes + if (count($matches[0]) > 0) { + + // Walk through shortcodes one by one + foreach ($matches[0] as $match) { + + // First, try youtube + // Get youtube like and video ID (Ref:http://stackoverflow.com/questions/3717115/regular-expression-for-youtube-links/3726073#3726073) + preg_match(self::YOUTUBE_REGEX, $match, $embed_link); + if (count($embed_link) > 1) { + $content = preg_replace(self::SHORTCODE, $this->getYoutubeEmbed($embed_link), $content, 1); + continue; + } + + // Next, we try vimeo + preg_match(self::VIMEO_REGEX, $match, $embed_link); + if (count($embed_link) > 1) { + $content = preg_replace(self::SHORTCODE, $this->getVimeoEmbed($embed_link), $content, 1); + continue; + } + + // Otherwise, it's peertube <3 + preg_match(self::PEERTUBE_REGEX, $match, $embed_link); + if (count($embed_link) > 1) { + $content = preg_replace(self::SHORTCODE, $this->getPeertubeEmbed($embed_link), $content, 1); + continue; + } + } + } + } + } + + const YOUTUBE_REGEX = '#http(?:s)?\:\/\/(?:www\.)?youtu(?:be\.com/watch\?v=|\.be/|be\.com/embed/)([\w\-]+)(&(amp;)?[\w\?=]*)?#s'; + private function getYoutubeEmbed(array $embed_link) { + return '
' + . '' + . '
'; + } + + const VIMEO_REGEX = '#http(?:s)?\:\/\/(?:www\.)?(?:player\.)?vimeo\.com/(?:video/)?([0-9]+)(-:\?.+)?#s'; + private function getVimeoEmbed(array $embed_link) { + return '
' + . '' + . '
'; + } + + const PEERTUBE_REGEX = '#(http(?:s)?)\:\/\/([a-zA-Z0-9](?:(?:[a-zA-Z0-9-]*|(?' + . '' + . ''; + } + + /** + * Triggered after Pico has rendered the page + * + * @param string &$output contents which will be sent to the user + * @return void + */ + public function onPageRendered(&$output) + { + // Add embed css + $output = str_replace('', ($this->getStyleHeader() . ''), $output); + } + + private function getStyleHeader() { + $header = ''; + return $header; + } +} diff --git a/plugins/PicoVideoEmbed/README.md b/plugins/PicoVideoEmbed/README.md new file mode 100644 index 0000000..21e7d66 --- /dev/null +++ b/plugins/PicoVideoEmbed/README.md @@ -0,0 +1,24 @@ +# PicoVideoEmbed + +This plugin for [Pico CMS](https://picocms.org/) adds support for embedding responsive videos from peertube, vimeo and youtube. It supports different link formats, such as: + +``` +[video https://kolektiva.media/videos/embed/a5c6b8e1-6e9e-4a89-ace4-03b6cf97ce07] +[video https://kolektiva.media/w/mtiCL28GP9gzLC2FPKjs6v] +[video https://tube.mfraters.net/videos/watch/9a2b78db-ef05-4770-9196-8a1fcd243f43] +[video https://vimeo.com/566821117] +[video https://player.vimeo.com/video/566821117] +[video https://youtu.be/NkdUs2Dxv4s] +[video https://www.youtube.com/watch?v=NkdUs2Dxv4s] +[video https://www.youtube.com/embed/NkdUs2Dxv4s] +``` + +## TODO + +- add configuration options (peertube IP notice, ...) +- add unit tests for URL parsing +- also support bare video files? + +## Thanks + +I would like to thank s4ad, who built the [spiritual predecessor](https://github.com/s4ad/PicoEmbed) of this plugin. diff --git a/slikce/IVO -plakat .png b/slikce/IVO -plakat .png new file mode 100644 index 0000000..2d95e95 Binary files /dev/null and b/slikce/IVO -plakat .png differ diff --git a/slikce/IVO -plakat1.png b/slikce/IVO -plakat1.png new file mode 100644 index 0000000..e45333f Binary files /dev/null and b/slikce/IVO -plakat1.png differ diff --git a/slikce/MARKO - plakat.png b/slikce/MARKO - plakat.png new file mode 100644 index 0000000..72fe498 Binary files /dev/null and b/slikce/MARKO - plakat.png differ diff --git a/slikce/TIM - plakat.png b/slikce/TIM - plakat.png new file mode 100644 index 0000000..0ad75d1 Binary files /dev/null and b/slikce/TIM - plakat.png differ diff --git a/slikce/TIM-plakat.png b/slikce/TIM-plakat.png new file mode 100644 index 0000000..6c267f7 Binary files /dev/null and b/slikce/TIM-plakat.png differ diff --git a/slikce/Tris Tim - Marko - Ivo mala.png b/slikce/Tris Tim - Marko - Ivo mala.png new file mode 100644 index 0000000..54abadb Binary files /dev/null and b/slikce/Tris Tim - Marko - Ivo mala.png differ diff --git a/slikce/krtstran.JPG b/slikce/krtstran.JPG new file mode 100644 index 0000000..a81d8e0 Binary files /dev/null and b/slikce/krtstran.JPG differ diff --git a/themes/Travelify-tr00_popravljena/favicon/cropped-favicon-180x180.png b/themes/Travelify-tr00_popravljena/favicon/cropped-favicon-180x180.png new file mode 100644 index 0000000..a2af1d0 Binary files /dev/null and b/themes/Travelify-tr00_popravljena/favicon/cropped-favicon-180x180.png differ diff --git a/themes/Travelify-tr00_popravljena/favicon/cropped-favicon-192x192.png b/themes/Travelify-tr00_popravljena/favicon/cropped-favicon-192x192.png new file mode 100644 index 0000000..50001b8 Binary files /dev/null and b/themes/Travelify-tr00_popravljena/favicon/cropped-favicon-192x192.png differ diff --git a/themes/Travelify-tr00_popravljena/favicon/cropped-favicon-270x270.png b/themes/Travelify-tr00_popravljena/favicon/cropped-favicon-270x270.png new file mode 100644 index 0000000..30f0079 Binary files /dev/null and b/themes/Travelify-tr00_popravljena/favicon/cropped-favicon-270x270.png differ diff --git a/themes/Travelify-tr00_popravljena/favicon/cropped-favicon-32x32.png b/themes/Travelify-tr00_popravljena/favicon/cropped-favicon-32x32.png new file mode 100644 index 0000000..5fb1ff8 Binary files /dev/null and b/themes/Travelify-tr00_popravljena/favicon/cropped-favicon-32x32.png differ diff --git a/themes/Travelify-tr00_popravljena/favicon/favicon.twig b/themes/Travelify-tr00_popravljena/favicon/favicon.twig new file mode 100644 index 0000000..47c779e --- /dev/null +++ b/themes/Travelify-tr00_popravljena/favicon/favicon.twig @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/themes/Travelify-tr00_popravljena/images/arrow-right.png b/themes/Travelify-tr00_popravljena/images/arrow-right.png new file mode 100644 index 0000000..cfdfc41 Binary files /dev/null and b/themes/Travelify-tr00_popravljena/images/arrow-right.png differ diff --git a/themes/Travelify-tr00_popravljena/images/belina.jpeg b/themes/Travelify-tr00_popravljena/images/belina.jpeg new file mode 100644 index 0000000..2d62b22 Binary files /dev/null and b/themes/Travelify-tr00_popravljena/images/belina.jpeg differ diff --git a/themes/Travelify-tr00_popravljena/images/prva_slika.jpeg b/themes/Travelify-tr00_popravljena/images/prva_slika.jpeg new file mode 100644 index 0000000..8a03e37 Binary files /dev/null and b/themes/Travelify-tr00_popravljena/images/prva_slika.jpeg differ diff --git a/themes/Travelify-tr00_popravljena/images/rss.png b/themes/Travelify-tr00_popravljena/images/rss.png new file mode 100644 index 0000000..9707305 Binary files /dev/null and b/themes/Travelify-tr00_popravljena/images/rss.png differ diff --git a/themes/Travelify-tr00_popravljena/images/svetlo.png b/themes/Travelify-tr00_popravljena/images/svetlo.png new file mode 100644 index 0000000..7909115 Binary files /dev/null and b/themes/Travelify-tr00_popravljena/images/svetlo.png differ diff --git a/themes/Travelify-tr00_popravljena/images/temno.png b/themes/Travelify-tr00_popravljena/images/temno.png new file mode 100644 index 0000000..e2f89bf Binary files /dev/null and b/themes/Travelify-tr00_popravljena/images/temno.png differ diff --git a/themes/Travelify-tr00_popravljena/index-novice.twig b/themes/Travelify-tr00_popravljena/index-novice.twig new file mode 100644 index 0000000..27454fb --- /dev/null +++ b/themes/Travelify-tr00_popravljena/index-novice.twig @@ -0,0 +1,101 @@ + + + + + {% if meta.title %}{{ meta.title }}{% else %}{{ site_title }}{% endif %} + + + {% if meta.description %} + + {% endif %}{% if meta.robots %} + + {% endif %} +{{ include('favicon/favicon.twig') }} + + + + + + + + + + +
+
+
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+
+

Zadnje novice

+ {% for page in pages("novice/")|sort_by("date")|reverse if not page.hidden %} +
+ {% if page.meta.teaser %} + + {% endif %} + +
+

{{ page.title }}

+

Objavljeno: {{ page.date_formatted }}

+

{{ page.description }} več

+
+
+ {% endfor %} + + {{ content }} +
+
+
+
+
+ {% for page in pages %} + {% if page.id starts with current_page.id|split('/')|slice(0,-1)|join('/') ~ '/sidebars/' %} {# page-specfic sidebars should be placed before global ones #} + + {% endif %} + {% endfor %} + {% for page in pages %} + {% if page.id starts with 'sidebars/' %} {# global sidebars #} + + {% endif %} + {% endfor %} +
+
+
+
+
+
+ + + + diff --git a/themes/Travelify-tr00_popravljena/index.twig b/themes/Travelify-tr00_popravljena/index.twig new file mode 100644 index 0000000..7511011 --- /dev/null +++ b/themes/Travelify-tr00_popravljena/index.twig @@ -0,0 +1,104 @@ + + + + {% if meta.title %}{{ meta.title }}{% else %}{{ site_title }}{% endif %} + + + {% if meta.description %} + + {% endif %}{% if meta.robots %} + + {% endif %} +{{ include('favicon/favicon.twig') }} + + + + + + + + + + +
+
+
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+
+ {{ content }} + + {% if meta.title == "Materiali" %} + + {% for page in pages("materiali/")|sort_by("date")|reverse if not page.hidden %} +
+ {% if page.meta.teaser %} + + {% endif %} + +
+

{{ page.title }}

+ {% if page.date_formatted %} +

Objavljeno: {{ page.date_formatted }}

+ {% endif %} +

{{ page.description }} več

+
+
+ {% endfor %} + {% endif %} +
+
+
+
+
+ {% for page in pages %} + {% if page.id starts with current_page.id|split('/')|slice(0,-1)|join('/') ~ '/sidebars/' %} {# page-specfic sidebars should be placed before global ones #} + + {% endif %} + {% endfor %} + {% for page in pages %} + {% if page.id starts with 'sidebars/' %} {# global sidebars #} + + {% endif %} + {% endfor %} +
+
+
+
+
+
+ + + + diff --git a/themes/Travelify-tr00_popravljena/menu-icon.png b/themes/Travelify-tr00_popravljena/menu-icon.png new file mode 100644 index 0000000..1cb463e Binary files /dev/null and b/themes/Travelify-tr00_popravljena/menu-icon.png differ diff --git a/themes/Travelify-tr00_popravljena/rss_feed.twig b/themes/Travelify-tr00_popravljena/rss_feed.twig new file mode 100644 index 0000000..4f35b15 --- /dev/null +++ b/themes/Travelify-tr00_popravljena/rss_feed.twig @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/themes/Travelify-tr00_popravljena/scripts/jquery-3.1.1.min.js b/themes/Travelify-tr00_popravljena/scripts/jquery-3.1.1.min.js new file mode 100644 index 0000000..4c5be4c --- /dev/null +++ b/themes/Travelify-tr00_popravljena/scripts/jquery-3.1.1.min.js @@ -0,0 +1,4 @@ +/*! jQuery v3.1.1 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R), +a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function Ya(a,b,c,d,e){return new Ya.prototype.init(a,b,c,d,e)}r.Tween=Ya,Ya.prototype={constructor:Ya,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Ya.propHooks[this.prop];return a&&a.get?a.get(this):Ya.propHooks._default.get(this)},run:function(a){var b,c=Ya.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ya.propHooks._default.set(this),this}},Ya.prototype.init.prototype=Ya.prototype,Ya.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Ya.propHooks.scrollTop=Ya.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Ya.prototype.init,r.fx.step={};var Za,$a,_a=/^(?:toggle|show|hide)$/,ab=/queueHooks$/;function bb(){$a&&(a.requestAnimationFrame(bb),r.fx.tick())}function cb(){return a.setTimeout(function(){Za=void 0}),Za=r.now()}function db(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ba[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function eb(a,b,c){for(var d,e=(hb.tweeners[b]||[]).concat(hb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?ib:void 0)), +void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),ib={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=jb[b]||r.find.attr;jb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=jb[g],jb[g]=e,e=null!=c(a,b,d)?g:null,jb[g]=f),e}});var kb=/^(?:input|select|textarea|button)$/i,lb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):kb.test(a.nodeName)||lb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function mb(a){var b=a.match(K)||[];return b.join(" ")}function nb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,nb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,nb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,nb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=nb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(nb(c))+" ").indexOf(b)>-1)return!0;return!1}});var ob=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(ob,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:mb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,"$1"),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r(" + + + + + diff --git a/themes/default/js/modernizr-3.3.1-custom.min.js b/themes/default/js/modernizr-3.3.1-custom.min.js new file mode 100644 index 0000000..33ad568 --- /dev/null +++ b/themes/default/js/modernizr-3.3.1-custom.min.js @@ -0,0 +1,3 @@ +/*! modernizr 3.3.1 (Custom Build) | MIT * + * https://modernizr.com/download/?-classlist-csstransitions-requestanimationframe !*/ +!function(e,n,t){function r(e,n){return typeof e===n}function i(){var e,n,t,i,o,s,a;for(var f in y)if(y.hasOwnProperty(f)){if(e=[],n=y[f],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(t=0;tp;p++)if(v=e[p],h=P.style[v],s(v,"-")&&(v=o(v)),P.style[v]!==t){if(a||r(i,"undefined"))return f(),"pfx"==n?v:!0;try{P.style[v]=i}catch(g){}if(P.style[v]!=h)return f(),"pfx"==n?v:!0}return f(),!1}function v(e,n,t,i,o){var s=e.charAt(0).toUpperCase()+e.slice(1),a=(e+" "+_.join(s+" ")+s).split(" ");return r(n,"string")||r(n,"undefined")?m(a,n,i,o):(a=(e+" "+E.join(s+" ")+s).split(" "),f(a,n,t))}function h(e,n,r){return v(e,t,t,n,r)}var y=[],g={_version:"3.3.1",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,n){var t=this;setTimeout(function(){n(t[e])},0)},addTest:function(e,n,t){y.push({name:e,fn:n,options:t})},addAsyncTest:function(e){y.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=g,Modernizr=new Modernizr;var C=[],w=n.documentElement;Modernizr.addTest("classlist","classList"in w);var x="Moz O ms Webkit",_=g._config.usePrefixes?x.split(" "):[];g._cssomPrefixes=_;var S=function(n){var r,i=prefixes.length,o=e.CSSRule;if("undefined"==typeof o)return t;if(!n)return!1;if(n=n.replace(/^@/,""),r=n.replace(/-/g,"_").toUpperCase()+"_RULE",r in o)return"@"+n;for(var s=0;i>s;s++){var a=prefixes[s],f=a.toUpperCase()+"_"+r;if(f in o)return"@-"+a.toLowerCase()+"-"+n}return!1};g.atRule=S;var E=g._config.usePrefixes?x.toLowerCase().split(" "):[];g._domPrefixes=E;var T="svg"===w.nodeName.toLowerCase(),z={elem:l("modernizr")};Modernizr._q.push(function(){delete z.elem});var P={style:z.elem.style};Modernizr._q.unshift(function(){delete P.style}),g.testAllProps=v;var b=g.prefixed=function(e,n,t){return 0===e.indexOf("@")?S(e):(-1!=e.indexOf("-")&&(e=o(e)),n?v(e,n,t):v(e,"pfx"))};Modernizr.addTest("requestanimationframe",!!b("requestAnimationFrame",e),{aliases:["raf"]}),g.testAllProps=h,Modernizr.addTest("csstransitions",h("transition","all",!0)),i(),delete g.addTest,delete g.addAsyncTest;for(var L=0;L .container > table'); + for (var i = 0; i < tables.length; i++) { + if (!/\btable-responsive\b/.test(tables[i].parentElement.className)) { + var tableWrapper = document.createElement('div'); + tableWrapper.className = 'table-responsive'; + + tables[i].parentElement.insertBefore(tableWrapper, tables[i]); + tableWrapper.appendChild(tables[i]); + } + } + + // responsive menu + var menu = document.getElementById('nav'), + menuToggle = document.getElementById('nav-toggle'); + + if (menu && menuToggle) { + function toggleMenuEvent(event) { + if (event.type === 'keydown') { + if ((event.keyCode !== 13) && (event.keyCode !== 32)) { + return; + } + } + + event.preventDefault(); + + if (menuToggle.getAttribute('aria-expanded') === 'false') { + menuToggle.setAttribute('aria-expanded', 'true'); + utils.slideDown(menu, null, function () { + if (event.type === 'keydown') { + menu.focus(); + } + }); + } else { + menuToggle.setAttribute('aria-expanded', 'false'); + utils.slideUp(menu); + } + } + + function onResizeEvent() { + if (utils.isElementVisible(menuToggle)) { + menu.className = 'hidden'; + menuToggle.addEventListener('click', toggleMenuEvent); + menuToggle.addEventListener('keydown', toggleMenuEvent); + } else { + menu.className = ''; + menu.removeAttribute('data-slide-id'); + menuToggle.removeEventListener('click', toggleMenuEvent); + menuToggle.removeEventListener('keydown', toggleMenuEvent); + } + } + + window.addEventListener('resize', onResizeEvent); + onResizeEvent(); + } +} + +main(); diff --git a/themes/default/js/utils.js b/themes/default/js/utils.js new file mode 100644 index 0000000..62b65cc --- /dev/null +++ b/themes/default/js/utils.js @@ -0,0 +1,134 @@ +/** + * Pico's Default Theme - JavaScript helper + * + * Pico is a stupidly simple, blazing fast, flat file CMS. + * + * @author Daniel Rudolf + * @link http://picocms.org + * @license http://opensource.org/licenses/MIT The MIT License + * @version 2.1 + */ + +utils = {}; + +/** + * Checks whether the client's browser is able to slide elements or not + * + * @return {bool} TRUE when the browser supports sliding, FALSE otherwise + */ +utils.canSlide = function () +{ + return (Modernizr.classlist && Modernizr.requestanimationframe && Modernizr.csstransitions); +}; + +/** + * Slides a element up (i.e. hide a element by changing its height to 0px) + * + * @param {HTMLElement} element the element to slide up + * @param {function} finishCallback function to call when the animation has + * been finished (i.e. the element is hidden) + * @param {function} startCallback function to call when the animation starts + */ +utils.slideUp = function (element, finishCallback, startCallback) +{ + if (!utils.canSlide()) { + if (startCallback) startCallback(); + element.className += (element.className !== '') ? ' hidden' : 'hidden'; + if (finishCallback) window.setTimeout(finishCallback, 16); + return; + } + + element.style.height = element.clientHeight + 'px'; + + var slideId = parseInt(element.getAttribute('data-slide-id')) || 0; + element.setAttribute('data-slide-id', ++slideId); + + window.requestAnimationFrame(function () { + element.classList.add('slide'); + + window.requestAnimationFrame(function () { + element.style.height = '0px'; + + if (startCallback) { + startCallback(); + } + + window.setTimeout(function () { + if (parseInt(element.getAttribute('data-slide-id')) !== slideId) return; + + element.classList.add('hidden'); + element.classList.remove('slide'); + element.style.height = null; + + if (finishCallback) { + window.requestAnimationFrame(finishCallback); + } + }, 500); + }); + }); +}; + +/** + * Slides a element down (i.e. show a hidden element) + * + * @param {HTMLElement} element the element to slide down + * @param {function} finishCallback function to call when the animation has + * been finished (i.e. the element is visible) + * @param {function} startCallback function to call when the animation starts + */ +utils.slideDown = function (element, finishCallback, startCallback) +{ + if (!utils.canSlide()) { + if (startCallback) startCallback(); + element.className = element.className.replace(/\bhidden\b */g, ''); + if (finishCallback) window.setTimeout(finishCallback, 16); + return; + } + + var cssRuleOriginalValue = element.clientHeight + 'px', + slideId = parseInt(element.getAttribute('data-slide-id')) || 0; + + element.setAttribute('data-slide-id', ++slideId); + + element.style.height = null; + element.classList.remove('hidden'); + element.classList.remove('slide'); + var cssRuleValue = element.clientHeight + 'px'; + + element.style.height = cssRuleOriginalValue; + + window.requestAnimationFrame(function () { + element.classList.add('slide'); + + window.requestAnimationFrame(function () { + element.style.height = cssRuleValue; + + if (startCallback) { + startCallback(); + } + + window.setTimeout(function () { + if (parseInt(element.getAttribute('data-slide-id')) !== slideId) return; + + element.classList.remove('slide'); + element.style.height = null; + + if (finishCallback) { + window.requestAnimationFrame(finishCallback); + } + }, 500); + }); + }); +}; + +/** + * Checks whether a element is visible or not + * + * @param {HTMLElement} element the element to check + * + * @return {bool} TRUE when the element is visible, FALSE otherwise + */ +utils.isElementVisible = function (element) +{ + return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length); +}; diff --git a/themes/default/pico-theme.yml b/themes/default/pico-theme.yml new file mode 100644 index 0000000..fc65b28 --- /dev/null +++ b/themes/default/pico-theme.yml @@ -0,0 +1,14 @@ +api_version: 3 # Use Pico's latest API version for themes + +meta: # Register meta headers used by this theme + Logo: logo # The URL to your website's logo (value is passed to Pico's "url" Twig filter) + Tagline: tagline # Your website's tag line, shown right below your site title (supports Markdown) + Social: social # A list of social icons that will be shown in your website's footer; + # You must specify a "title", "url" and "icon" per entry + +twig_config: # Twig template engine config + autoescape: html # Let Twig escape variables by default + strict_variables: false # If set to true, Twig will bail out when unset variables are being used + charset: utf-8 # The charset used by Twig templates + +widescreen: false # Use more horicontal space (i.e. make the site container wider)