diff --git a/.bowerrc b/.bowerrc deleted file mode 100644 index 7f6b222..0000000 --- a/.bowerrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "directory": "web/components/" -} diff --git a/.gitignore b/.gitignore index 28eb5fc..6d98c4e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,12 +2,13 @@ /.mage/logs/*.log /composer.lock /vendor/ -/propel.yml +/propel.yaml /src/Gist/Model/Base/ /src/Gist/Model/Map/ -/web/components/ /app/propel/ +/node_modules/ /app/config/config.yml /app/config/propel/ /data/ /trans/ +/cache/ diff --git a/.mage/config/environment/.gitignore b/.mage/config/environment/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/.mage/config/environment/prod.yml.dist b/.mage/config/environment/prod.yml.dist deleted file mode 100644 index a5bcb3b..0000000 --- a/.mage/config/environment/prod.yml.dist +++ /dev/null @@ -1,28 +0,0 @@ -# prod -# Rename this file to "prod.yml" -deployment: - user: gist - from: ./ - to: /var/www/gist/ - excludes: - - "*.svn" - - "*.git" - - "*.swp" - - "app/config/config.yml" - - "app/config/propel/" - - "app/propel/" - - "data/git" - - "deploy" - - "tags" - - "propel.yaml" - - "composer.lock" - - "composer.json" - - "bower.json" - - ".bowerrc" -hosts: - - gist.mycompany.com -tasks: - pre-deploy: - on-deploy: - post-deploy: -verbose_logging: true diff --git a/.mage/config/general.yml b/.mage/config/general.yml deleted file mode 100644 index cff23fa..0000000 --- a/.mage/config/general.yml +++ /dev/null @@ -1,7 +0,0 @@ -# global settings -name: Gist -email: -notifications: false -logging: true -maxlogs: 30 -ssh_needs_tty: false diff --git a/.mage/logs/.gitignore b/.mage/logs/.gitignore deleted file mode 100644 index c96a04f..0000000 --- a/.mage/logs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore \ No newline at end of file diff --git a/.mage/tasks/.gitignore b/.mage/tasks/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..8d29688 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,26 @@ +## Environment + +* GIST version or GIT commit: ... +* PHP Version: ... +* GIT version. ... +* HTTPD (Nginx, Apache2): ... +* DBMS (MySQL, SQLite, PostgreSQL): ... + + +## Description + +... + +### Steps to reproduce + +1. ... +2. ... +3. ... + +#### Observed Results + +... + +#### Expected Results + +... diff --git a/Makefile b/Makefile index 7dd29cc..6b5a389 100644 --- a/Makefile +++ b/Makefile @@ -1,53 +1,58 @@ COMPOSER ?= composer -BOWER ?= bower +NPM ?= npm GIT ?= git MKDIR ?= mkdir PHP ?= php +RM ?= rm -all: update +all: update clean-cache composer: - @echo "Installing application's dependencies" - @echo "-------------------------------------" - @echo + @echo "Installing PHP dependencies" + @echo "---------------------------" + @echo - $(COMPOSER) install $(COMPOSER_INSTALL_FLAGS) -bower: - @echo "Installing application's dependencies" - @echo "-------------------------------------" - @echo + ${COMPOSER} install - $(BOWER) install +npm: + @echo "Installing CSS/JS dependencies" + @echo "------------------------------" + @echo -optimize: - @echo "Optimizing Composer's autoloader, can take some time" - @echo "----------------------------------------------------" - @echo - - $(COMPOSER) dump-autoload --optimize + ${NPM} install + ${NPM} update update: @echo "Updating application's dependencies" @echo "-----------------------------------" - @echo + @echo - $(GIT) pull origin master + ${GIT} pull origin master ${MKDIR} -p data/git - $(COMPOSER) update - $(BOWER) install + ${MKDIR} -p data/cache + ${COMPOSER} update + ${NPM} install + ${NPM} update + +clean-cache: + @echo "Removing cache" + @echo "--------------" + @echo + + ${RM} -fr cache/* run: @echo "Run development server" @echo "----------------------" - @echo + @echo - $(PHP) -S 127.0.0.1:8080 -t web + ${PHP} -S 127.0.0.1:8080 -t web propel: @echo "Propel migration" @echo "----------------" - @echo - + @echo + ./vendor/propel/propel/bin/propel config:convert ./vendor/propel/propel/bin/propel model:build --recursive ./vendor/propel/propel/bin/propel migration:diff --recursive diff --git a/README.md b/README.md index a9e1493..ccbff71 100644 --- a/README.md +++ b/README.md @@ -1,418 +1,23 @@ -Table of Contents -================= - - * [GIST](#gist) - * [Requirements](#requirements) - * [Git](#git) - * [Composer](#composer) - * [Bower](#bower) - * [Installation](#installation) - * [Upgrade](#upgrade) - * [Configuration](#configuration) - * [Makefile](#makefile) - * [API](#api) - * [Console](#console) - * [Deployment](#deployment) - * [Contributors](#contributors) - - GIST ==== GIST is an open-source application to share code. https://www.deblan.io/post/517/gist-est-dans-la-place -![Gist](https://upload.deblan.org/u/2015-11/565b93a5.png "Gist") +**[Read the wiki for more information](https://gitnet.fr/deblan/gist/wiki/_pages).** -![Gist](https://upload.deblan.org/u/2016-06/57655dec.png "Gist") +### Editor +![Gist](https://upload.deblan.org/u/2018-08/5b7ab7a6.png "Gist editor") -Requirements ------------- +### Result -* PHP >= 5.4 -* GIT -* MySQL, PostgreSQL or SQLite -* Composer (php) -* Bower (node) +![Gist](https://upload.deblan.org/u/2018-08/5b7ab7d4.png "Gist result") -### Git +### Account -Git can maybe be downloaded from your system's repositories. +![Gist](https://upload.deblan.org/u/2018-08/5b7aba2d.png "Gist account") - $ git config --global user.email "you@example.com" - $ git config --global user.name "Your Name" +### Embeded Gist -### Composer - -Composer can maybe be downloaded from your system's repositories. -Else, follow the next instructions: - - # With cURL - $ curl -sS https://getcomposer.org/installer | php - - # With Wget - $ wget -O - -q https://getcomposer.org/installer | php - - $ chmod +x composer.phar - - # For a local installation and if the envvar PATH contains "$HOME/bin/" - $ mv composer.phar ~/bin/composer - - # For a global installation - $ sudo mv composer.phar /usr/local/bin/composer - -### Bower - - $ sudo apt-get install npm - $ sudo npm install -g bower - -Installation ------------- - - $ cd /path/to/www/ - $ git clone https://gitnet.fr/deblan/gist - $ cd gist - $ make - $ cp propel-dist.yaml propel.yaml - -Edit `propel.yaml`. **Use spaces instead of tabulations**. - -**MySQL** - - propel: - database: - connections: - default: - adapter: mysql - # http://www.php.net/manual/en/ref.pdo-mysql.connection.php - dsn: "mysql:host=DATABASE_SERVER;dbname=DATABASE_NAME" - user: DATEBASE_USER - password: DATEBASE_PASSWORD - settings: - charset: utf8 - queries: - utf8: "SET NAMES utf8 COLLATE utf8_unicode_ci, COLLATION_CONNECTION = utf8_unicode_ci, COLLATION_DATABASE = utf8_unicode_ci, COLLATION_SERVER = utf8_unicode_ci" - - [...] - -**SQLITE** - - propel: - database: - connections: - default: - adapter: sqlite - # http://www.php.net/manual/en/ref.pdo-sqlite.connection.php - dsn: "sqlite:/PATH/TO/gist.sqlite" - user: ~ - password: ~ - - [...] - -Then `$ make propel`. - -**Versions >= 1.4.4 only**: `$ cp app/config/config.yml.dist app/config/config.yml` - -See the [configuration section](#configuration) for more information about configuration. - ---- - -The web server must have permission to write into `data`. - - $ sudo chown -R www-data:www-data data - -Your webserver must be configured to serve `web/` as document root. If you use nginx, all virtual paths must be rooted with `web/index.php` or `web/app_dev.php` ([documentation](https://www.nginx.com/resources/wiki/start/topics/recipes/symfony/)). If you use apache, you must enable the `rewrite` module and restart: - - $ sudo a2enmod rewrite - $ sudo service apache2 restart - -`app_dev.php` is the development router. Access is granted for an IP range defined in the same file. - -Upgrade -------- - -If your version is less than v1.4.2, run: `test -d app && git add app && git commit -m "Configuration"`. - - $ make update - $ make propel - -If you upgrade to v1.4.1 or more: `app/console migrate:to:v1.4.1`. - -If you upgrade to v1.4.4 or more, the configuration is moved to a `app/config/config.yml`: `$ cp app/config/config.yml.dist app/config/config.yml` and see the [configuration section](#configuration) for more information. - -If you upgrade to v1.7.0 or more, see the [configuration section](#configurationh) for more information about new options. - -Configuration -------------- - -### Version < 1.4.4 - -Edit `app/bootstrap.php.d/70-security.php`. - -* `$app['token']`: the securty token (a strong passphrase). -* `$app['enable_registration']`: defines if the registration is allowed (`true` or `false`) -* `$app['enable_login']`: defines if the login is allowed (`true` or `false`) -* `$app['login_required_to_edit_gist']`: defines if the user must be logged to create or clone a Gist (`true` or `false`) -* `$app['login_required_to_view_gist']`: defines if the user must be logged to view a Gist (`true` or `false`) -* `$app['login_required_to_view_gist']`: defines if the user must be logged to view an embeded Gist (`true` or `false`) - -If you install Gist on your server, you have to modify the `base_uri` of the API. -Edit `app/bootstrap.php.d/60-api.php` and replace `https://gist.deblan.org/`. - -### Version >= 1.4.4 - -Edit `app/config/config.yml`. - -* `security.token`: the securty token (a strong passphrase) -* `security.enable_registration`: defines if the registration is allowed (`true` or `false`) -* `security.enable_login`: defines if the login is allowed (`true` or `false`) -* `security.login_required_to_edit_gist`: defines if the user must be logged to create or clone a Gist (`true` or `false`) -* `security.login_required_to_view_gist`: defines if the user must be logged to view a Gist (`true` or `false`) -* `security.login_required_to_view_gist`: defines if the user must be logged to view an embeded Gist (`true` or `false`) -* `api.base_uri`: The url of your instance. -* `data.path`: the path where the files are saved. -* `git.path`: The path of `git`. -* `theme.name`: the name of the theme (`dark` or `light`) - -### Version >= v1.7.0 - -* `api.enabled`: defines if the API is enabled (`true` or `false`) -* `api.api_key_required`: defines if the API key is required to access the API (`true` or `false`) -* `api.client.api_key`: defines the client API key (`string`) - -Makefile --------- - -A Makefile is provided to automate some tasks. - -* `make` will install application's dependencies via Composer and Bower, -* `make optimize` will run Composer's autoloader dump script with classmap -* `make update` will update the application -* `make propel` will generate propel's files -* `make run` will run development server on http://127.0.0.1:8080/ - -API ---- - -### Version < v1.7.0 - -#### Create a new gist - -**POST** /{locale}/api/create -Params: - -* `form[title]`: String (required, can be empty) -* `form[type]`: String (required) - Values: html, css, javascript, php, sql, xml, yaml, perl, c, asp, python, bash, actionscript3, text -* `form[content]`: String (required) - -**Responses:** - -* Code `405`: Method Not Allowed -* Code `400`: Bad Request -* Code `200`: A json which contains gist's information. Example: - -```javascript -{ - "url": "https:\/\/gist.deblan.org\/en\/view\/55abcfa7771e0\/f4afbf72967dd95e3461490dcaa310d728d6a97d", - "gist": { - "Id": 66, - "Title": "test prod", - "Cipher": false, - "Type": "javascript", - "File": "55abcfa7771e0", - "CreatedAt": "2015-07-19T16:26:15Z", - "UpdatedAt": "2015-07-19T16:26:15Z" - } -} -``` - -#### Update an existing gist - -**POST** /{locale}/api/update/{id} -Params: - -* `{id}`: Gist Id (required) -* `form[content]`: String (required) - -**Responses:** - -* Code `405`: Method Not Allowed -* Code `400`: Bad Request -* Code `200`: A json which contains gist's information. Example: - -```javascript -{ - "url": "https:\/\/gist.deblan.org\/en\/view\/55abcfa7771e0\/abcgi72967dd95e3461490dcaa310d728d6adef", - "gist": { - "Id": 66, - "Title": "test prod", - "Cipher": false, - "Type": "javascript", - "File": "55abcfa7771e0", - "CreatedAt": "2015-07-19T16:26:15Z", - "UpdatedAt": "2015-07-19T16:30:15Z" - } -} -``` - -### Version >= v1.7.0 - -Invalid response codes: - -* Code `401`: Unauthorized -* Code `403`: API not enabled -* Code `405`: Method Not Allowed -* Code `400`: Bad Request - -#### List gists - -**GET** /{locale}/api/list/{apiToken} - -Response example: - -```javascript -[ - { - "id": 66, - "title": "test prod", - "cipher": false, - "type": "javascript", - "file": "55abcfa7771e0", - "createdAt": "2015-07-19T16:26:15Z", - "updatedAt": "2015-07-19T16:30:15Z" - "url": "https:\/\/gist.deblan.org\/en\/view\/55abcfa7771e0\/abcgi72967dd95e3461490dcaa310d728d6adef", - }, - { - "id": 67, - "title": "test prod 2", - "cipher": false, - "type": "javascript", - "file": "xyzbcfa7771e0", - "createdAt": "2015-08-19T16:26:15Z", - "updatedAt": "2015-08-19T16:30:15Z" - "url": "https:\/\/gist.deblan.org\/en\/view\/5byzbcfa7771e0\/def72967dd95e346koq0dcaa310d728d6artu", - }, - ... -] -``` - -#### Create a new gist - -**POST** /{locale}/api/create/{apiToken} -Params: - -* `form[title]`: String (required, can be empty) -* `form[type]`: String (required) - Values: html, css, javascript, php, sql, xml, yaml, perl, c, asp, python, bash, actionscript3, text -* `form[content]`: String (required) - -Response example: - -```javascript -{ - "url": "https:\/\/gist.deblan.org\/en\/view\/55abcfa7771e0\/f4afbf72967dd95e3461490dcaa310d728d6a97d", - "gist": { - "id": 66, - "title": "test prod", - "cipher": false, - "type": "javascript", - "file": "55abcfa7771e0", - "createdAt": "2015-07-19T16:26:15Z", - "updatedAt": "2015-07-19T16:26:15Z" - } -} -``` - -#### Update an existing gist - -**POST** /{locale}/api/update/{id}/{apiToken} -Params: - -* `{id}`: Gist Id (required) -* `form[content]`: String (required) - -Response example: - -```javascript -{ - "url": "https:\/\/gist.deblan.org\/en\/view\/55abcfa7771e0\/abcgi72967dd95e3461490dcaa310d728d6adef", - "gist": { - "id": 66, - "title": "test prod", - "cipher": false, - "type": "javascript", - "file": "55abcfa7771e0", - "createdAt": "2015-07-19T16:26:15Z", - "updatedAt": "2015-07-19T16:30:15Z" - } -} -``` - -#### Delete an existing gist - -**POST** /{locale}/api/delete/{id}/{apiToken} - -Response code `200`: - -```javascript -{"error":false} -``` - -Response code `400`: - -```javascript -{"message":"Invalid Gist", "error":true} -``` - -Console -------- - -* **Create a gist**: `$ app/console --help create` -* **Update a gist**: `$ app/console --help update` -* **Create user**: `app/console --help user:create` -* **Show stats**: `$ app/console --help stats` - -### Version >= v1.7.0 - -* **List your gists**: `$ app/console --help gists` -* **Delete a gist**: `$ app/console --help delete` - -Deployment ----------- - -Gist uses [Magallanes](http://magephp.com/) to manage deployment. - -**Global installation** - - $ composer global require andres-montanez/magallanes - # if the envvar PATH contains "$HOME/bin/" - $ ln -s ~/.composer/vendor/bin/mage ~/bin/mage - -**Local installation** - - $ composer require andres-montanez/magallanes - -There is an example of the configuration of an environment in `.mage/config/environment/prod.yml.dist`. - - # global installation - $ mage deploy to:prod - - # local installation - $ ./vendor/andres-montanez/magallanes/bin/mage deploy to:prod - -Contributors ------------- - -**Developers** - -* Simon Vieille - -**Translators** - -* Simon Vieille -* Marion Sanchez -* Marjorie Da Silva -* Mélanie Chanat -* Showfom +![Gist](https://upload.deblan.org/u/2018-08/5b7ab81c.png "Embeded Gist") diff --git a/app/bootstrap.php.d/20-twig.php b/app/bootstrap.php.d/20-twig.php index 8da7180..e69e1c6 100644 --- a/app/bootstrap.php.d/20-twig.php +++ b/app/bootstrap.php.d/20-twig.php @@ -9,6 +9,6 @@ $app->register(new TwigServiceProvider(), array( $app->extend('twig', function ($twig, $app) { $base = str_replace($app['request']->server->get('SCRIPT_NAME'), '', $app['request']->getBaseUrl()); $twig->addGlobal('web_path', $base.'/'); - + return $twig; }); diff --git a/app/bootstrap.php.d/30-trans.php b/app/bootstrap.php.d/30-trans.php index 9ec8a36..d65aa5d 100644 --- a/app/bootstrap.php.d/30-trans.php +++ b/app/bootstrap.php.d/30-trans.php @@ -11,7 +11,7 @@ use Symfony\Component\HttpFoundation\Cookie; $app->register(new TranslationServiceProvider(), array( 'locale' => 'en', 'locale_fallback' => 'en', - 'locales' => array('en', 'fr', 'es', 'de', 'cn'), + 'locales' => array('en', 'fr', 'es', 'de', 'cn', 'pl'), )); $app['translator'] = $app->extend('translator', function ($translator, $app) { diff --git a/app/bootstrap.php.d/80-cache.php b/app/bootstrap.php.d/80-cache.php new file mode 100644 index 0000000..3a28362 --- /dev/null +++ b/app/bootstrap.php.d/80-cache.php @@ -0,0 +1,8 @@ +register(new HttpCacheServiceProvider(), array( + 'http_cache.cache_dir' => $app['root_path'].'/cache/', + 'http_cache.esi' => null, +)); diff --git a/propel-dist.yaml b/app/config/propel.yaml.dist-mysql similarity index 84% rename from propel-dist.yaml rename to app/config/propel.yaml.dist-mysql index dd6f836..fa13d75 100644 --- a/propel-dist.yaml +++ b/app/config/propel.yaml.dist-mysql @@ -4,9 +4,9 @@ propel: default: adapter: mysql classname: Propel\Runtime\Connection\ConnectionWrapper - dsn: "mysql:host=localhost;dbname=gist" - user: root - password: root + dsn: "mysql:host=DATABASE_HOST;dbname=DATABASE_NAME" + user: "DATABASE_USERNAME" + password: "DATABASE_PASSWORD" settings: charset: utf8 queries: diff --git a/app/config/propel.yaml.dist-sqlite b/app/config/propel.yaml.dist-sqlite new file mode 100644 index 0000000..b11f6c7 --- /dev/null +++ b/app/config/propel.yaml.dist-sqlite @@ -0,0 +1,31 @@ +propel: + database: + connections: + default: + adapter: sqlite + classname: Propel\Runtime\Connection\ConnectionWrapper + dsn: "sqlite:DATABASE_PATH" + user: ~ + password: ~ + settings: + charset: utf8 + queries: + + paths: + projectDir: src/ + schemaDir: src/ + outputDir: src/ + phpDir: src/ + phpConfDir: app/config/propel + sqlDir: app/propel/sql + migrationDir: app/propel/migration + + runtime: + defaultConnection: default + connections: [default] + + generator: + defaultConnection: default + connections: [default] + objectModel: + addClassLevelComment: false diff --git a/app/locales/cn.yml b/app/locales/cn.yml index 9af2b92..c2adf88 100644 --- a/app/locales/cn.yml +++ b/app/locales/cn.yml @@ -44,7 +44,7 @@ date: format: 'Y-m-d h:i:s' footer: - text: '

Powered by GIST, it''s open source :) - API

' + text: '

Powered by GIST, it''s open source :) - API

' login: login: @@ -98,6 +98,7 @@ form: php: 'PHP' sql: 'SQL' yaml: 'YAML' + markdown: 'MARKDOWN' perl: 'PERL' c: 'C/C++' asp: 'ASP' diff --git a/app/locales/de.yml b/app/locales/de.yml index 28a0314..656f71f 100644 --- a/app/locales/de.yml +++ b/app/locales/de.yml @@ -44,7 +44,7 @@ date: format: 'Y-m-d h:i:s' footer: - text: '

Powered by GIST, it''s open source :) - API

' + text: '

Powered by GIST, it''s open source :) - API

' login: login: @@ -98,6 +98,7 @@ form: php: 'PHP' sql: 'SQL' yaml: 'YAML' + markdown: 'MARKDOWN' perl: 'PERL' c: 'C/C++' asp: 'ASP' diff --git a/app/locales/en.yml b/app/locales/en.yml index dd99aba..40e7454 100644 --- a/app/locales/en.yml +++ b/app/locales/en.yml @@ -45,7 +45,7 @@ date: format: 'Y-m-d h:i:s' footer: - text: '

Powered by GIST, it''s open source :) - API

' + text: '

Powered by GIST, it''s open source :) - API

' login: login: @@ -99,6 +99,7 @@ form: php: 'PHP' sql: 'SQL' yaml: 'YAML' + markdown: 'MARKDOWN' perl: 'PERL' c: 'C/C++' asp: 'ASP' diff --git a/app/locales/es.yml b/app/locales/es.yml index 679d0f1..83c0078 100644 --- a/app/locales/es.yml +++ b/app/locales/es.yml @@ -44,7 +44,7 @@ date: format: 'd/m/Y H\hi s\s' footer: - text: '

Impulsado por GIST, es libre :) - API

' + text: '

Impulsado por GIST, es libre :) - API

' login: login: @@ -98,6 +98,7 @@ form: php: 'PHP' sql: 'SQL' yaml: 'YAML' + markdown: 'MARKDOWN' perl: 'PERL' c: 'C/C++' asp: 'ASP' diff --git a/app/locales/fr.yml b/app/locales/fr.yml index 68759cd..63c79ac 100644 --- a/app/locales/fr.yml +++ b/app/locales/fr.yml @@ -44,7 +44,7 @@ date: format: 'd/m/Y H\hi s\s' footer: - text: '

Propulsé par GIST, c''est libre :) - API

' + text: '

Propulsé par GIST, c''est libre :) - API

' login: login: @@ -98,6 +98,7 @@ form: php: 'PHP' sql: 'SQL' yaml: 'YAML' + markdown: 'MARKDOWN' perl: 'PERL' c: 'C/C++' asp: 'ASP' diff --git a/app/locales/pl.yml b/app/locales/pl.yml new file mode 100644 index 0000000..4eaf452 --- /dev/null +++ b/app/locales/pl.yml @@ -0,0 +1,109 @@ +app: + title: '#!GIST' + title_prefix: '#!GIST - ' + + menu: + home: + title: 'Home' + about: + title: 'O projekcie' + my: + login: + title: 'Logowanie' + logout: + title: 'Wylogowanie' + register: + title: 'Rejestracja' + my: + title: 'Konta' + +my: + title: 'Moje wklejki' + nothing: 'Nie ma jeszcze nic!' + api: + title: 'API' + warning: 'Trzymaj to w sekrecie!' + form: + generate: 'Wygeneruj nowy' + + +gist: + untitled: 'Bez tytułu' + 404: + title: 'Oops!' + message: 'Wklejka nie została znaleziona...' + action: + view: 'Podgląd' + history: 'Commit(s)' + raw: 'RAW' + download: 'Pobierz' + clone: 'Sklonuj' + embed: 'Embed' + add: 'Nowa' + +date: + format: 'Y-m-d h:i:s' + +footer: + text: '

Napędzane przez GIST, to jest open source :) - API

' + +login: + login: + title: 'Logowanie' + invalid: 'Nie prawidłowy login lub hasło.' + form: + username: + placeholder: 'Login' + password: + placeholder: 'Hasło' + remember_me: + label: 'Pamiętaj' + register: + title: 'Nowe konto' + already_exists: 'Ta nazwa użytkownika jest już zarejestrowana!' + registred: 'Gratulacje, twoje konto zostało utworzone!' + form: + username: + placeholder: 'Login' + current_password: + placeholder: 'Obecne hasło' + password: + placeholder: 'Hasło' + +form: + error: + password: 'Nie prawidłowe hasło.' + not_blank: 'To pole nie powinno być puste!' + title: + placeholder: 'Tytuł' + cipher: + label: 'Szyfrowanie: %value%' + choice: + anyway: 'Tak czy inaczej' + yes: 'Tak' + no: 'Nie' + submit: 'Wyślij' + filter: 'Filtr' + confirm: 'Potwierdzasz?' + success: + password: 'Hasło zostało zaktualizowane.' + gist: 'Wklejka została usunięta.' + type: + label: 'Język: %value%' + choice: + all: 'All' + html: 'HTML' + xml: 'XML' + css: 'CSS' + javascript: 'JAVASCRIPT' + php: 'PHP' + sql: 'SQL' + yaml: 'YAML' + markdown: 'MARKDOWN' + perl: 'PERL' + c: 'C/C++' + asp: 'ASP' + python: 'PYTHON' + bash: 'BASH' + actionscript3: 'ACTION SCRIPT' + text: 'TEXT' diff --git a/bower.json b/bower.json deleted file mode 100644 index 464be4d..0000000 --- a/bower.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "gist", - "version": "0.0.3", - "authors": [ - "Simon Vieille " - ], - "description": "GIST is an open-source application to share code.", - "main": "web/index.php", - "keywords": [ - "Gist", - "GIT" - ], - "license": "LGPL", - "homepage": "https://gitnet.fr/deblan/gist", - "dependencies": { - "bootstrap": "3.3.4", - "flag-icon-css": "0.7.1", - "iframe-resizer": "2.8.6", - "jsdiff": "~2.2.2", - "Prism": "~1.8.1" - } -} diff --git a/composer.json b/composer.json index 510fa87..14cd8e1 100644 --- a/composer.json +++ b/composer.json @@ -1,4 +1,7 @@ { + "name": "deblan/gist", + "license": "GPL-3.0-only", + "type": "project", "require": { "silex/silex": "1.3.x-dev", "symfony/yaml": "~2.6", @@ -12,6 +15,17 @@ "guzzlehttp/guzzle": "~6.0", "symfony/security": "^2.7" }, + "scripts": { + "gist-scripts": [ + "Gist\\Composer\\PostInstallHandler::execute" + ], + "post-install-cmd": [ + "@gist-scripts" + ], + "post-update-cmd": [ + "@gist-scripts" + ] + }, "autoload": { "psr-0": { "": "src/" diff --git a/package.json b/package.json new file mode 100644 index 0000000..c637d1f --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "dependencies": { + "bootstrap": "^3.4.1", + "diff": "^3.5.0", + "flag-icon-css": "^0.8.6", + "iframe-resizer": "^2.8.10", + "jquery": ">=3.4.0" + } +} diff --git a/src/Gist/Api/Client.php b/src/Gist/Api/Client.php index 82ed6cd..ca17817 100644 --- a/src/Gist/Api/Client.php +++ b/src/Gist/Api/Client.php @@ -88,7 +88,7 @@ class Client extends BaseClient public function update($gist, $content) { $response = $this->post( - str_replace('{gist}', $gist, $this->mergeApiKey(self::LIST)), + str_replace('{gist}', $gist, $this->mergeApiKey(self::UPDATE)), array( 'form_params' => array( 'form' => array( diff --git a/src/Gist/Composer/PostInstallHandler.php b/src/Gist/Composer/PostInstallHandler.php new file mode 100644 index 0000000..366cc24 --- /dev/null +++ b/src/Gist/Composer/PostInstallHandler.php @@ -0,0 +1,211 @@ + + */ +class PostInstallHandler +{ + public static function execute(Event $event) + { + $helper = new QuestionHelper(); + $input = new ArgvInput(); + $output = new ConsoleOutput(); + $filesystem = new Filesystem(); + + $output->writeln('+==============================+'); + $output->writeln('| GIST Setup |'); + $output->writeln('+==============================+'); + $output->writeln(''); + $output->writeln('1. Database'); + $output->writeln('==========='); + + $configure = true; + if ($filesystem->exists('propel.yaml')) { + $output->writeln('The configuration file exists.'); + $question = new ConfirmationQuestion('Your current configuration will not be merged. Do you want to override it? [y/N] ', false); + $configure = $helper->ask($input, $output, $question); + } else { + $configure = true; + } + + if ($configure) { + $choices = ['MySQL/MariaDB', 'SQLite', 'other']; + $question = new ChoiceQuestion('Which DBMS? ', $choices, 0); + $dbms = $helper->ask($input, $output, $question); + $substitutions = []; + + if ($dbms === 'MySQL/MariaDB') { + $templateName = 'propel.yaml.dist-mysql'; + + $question = new Question('Host: [127.0.0.1] ', '127.0.0.1'); + $substitutions['DATABASE_HOST'] = $helper->ask($input, $output, $question); + + $question = new Question('Database (it must exists!): [gist] ', 'gist'); + $substitutions['DATABASE_NAME'] = $helper->ask($input, $output, $question); + + $question = new Question('Username: [root] ', 'root'); + $substitutions['DATABASE_USERNAME'] = $helper->ask($input, $output, $question); + + $question = new Question('Password: [] ', ''); + $substitutions['DATABASE_PASSWORD'] = $helper->ask($input, $output, $question); + } elseif ($dbms === 'SQLite') { + $defaultPath = getcwd().'/data/gist.sqlite'; + $question = new Question("Ok! Where do you want me to save datas? [$defaultPath] ", $defaultPath); + $substitutions['DATABASE_PATH'] = $helper->ask($input, $output, $question); + $templateName = 'propel.yaml.dist-sqlite'; + } else { + $output->writeln('See README.md to perform the configuration.'); + + return; + } + + $template = file_get_contents('app/config/'.$templateName); + $content = str_replace( + array_keys($substitutions), + array_values($substitutions), + $template + ); + + $done = file_put_contents('propel.yaml', $content) !== false; + + if ($done) { + $output->writeln('Running migration...'); + + foreach (['config:convert', 'model:build --recursive', 'migration:diff --recursive', 'migration:migrate --recursive'] as $arg) { + $command = self::getPhp(true).' ./vendor/propel/propel/bin/propel '.$arg; + $process = new Process($command); + $process->run(); + + if (!$process->isSuccessful()) { + $output->writeln('An error occured while executing:'); + $output->writeln($command); + $output->writeln('To perform the configuration. See README.md.'); + + return; + } + } + + $output->writeln('Done!'); + } else { + $output->writeln('An error occured. See README.md to perform the configuration.'); + } + } + + $output->writeln(''); + $output->writeln('2. Application'); + $output->writeln('=============='); + + $configure = true; + if ($filesystem->exists('app/config/config.yml')) { + $output->writeln('The configuration file exists.'); + $question = new ConfirmationQuestion('Your current configuration will not be merged. Do you want to override it? [y/N] ', false); + $configure = $helper->ask($input, $output, $question); + } else { + $configure = true; + } + + if ($configure) { + $output->writeln(''); + $output->writeln(' 2.1 Security'); + $output->writeln('-------------'); + + $token = str_shuffle(sha1(microtime().uniqid())); + + $question = new ConfirmationQuestion('Registration enabled: [Y/n] ', true); + $enableRegistration = $helper->ask($input, $output, $question); + + $question = new ConfirmationQuestion('Login enabled: [Y/n] ', true); + $enableLogin = $helper->ask($input, $output, $question); + + $question = new ConfirmationQuestion('Login required to edit a gist: [y/N] ', false); + $loginRequiredToEditGist = $helper->ask($input, $output, $question); + + $question = new ConfirmationQuestion('Login required to view a gist: [y/N] ', false); + $loginRequiredToViewGist = $helper->ask($input, $output, $question); + + $question = new ConfirmationQuestion('Login required to view an embeded gist: [y/N] ', false); + $loginRequiredToViewEmbededGist = $helper->ask($input, $output, $question); + + $output->writeln(''); + $output->writeln(' 2.2 API'); + $output->writeln('--------'); + + $question = new ConfirmationQuestion('API enabled: [Y/n] ', true); + $apiEnabled = $helper->ask($input, $output, $question); + + if ($apiEnabled) { + $question = new ConfirmationQuestion('API key required: [y/N] ', false); + $apikeyRequired = $helper->ask($input, $output, $question); + } else { + $apikeyRequired = false; + } + + $question = new Question('[Client] API base URL: [https://gist.deblan.org/] ', 'https://gist.deblan.org/'); + $apiBaseUrl = $helper->ask($input, $output, $question); + + $question = new Question('[Client] API key: [] ', ''); + $apiClientApiKey = $helper->ask($input, $output, $question); + + $configuration = [ + 'security' => [ + 'token' => $token, + 'enable_registration' => $enableRegistration, + 'enable_login' => $enableLogin, + 'login_required_to_edit_gist' => $loginRequiredToEditGist, + 'login_required_to_view_gist' => $loginRequiredToViewGist, + 'login_required_to_view_embeded_gist' => $loginRequiredToViewEmbededGist, + ], + 'api' => [ + 'enabled' => $apiEnabled, + 'api_key_required' => $apikeyRequired, + 'base_url' => $apiBaseUrl, + 'client' => [ + 'api_key' => $apiClientApiKey, + ], + ], + 'data' => [ + 'path' => 'data/git', + ], + 'git' => [ + 'path' => '/usr/bin/git', + ], + 'theme' => [ + 'name' => 'dark', + ], + ]; + + $content = (new Yaml())->dump($configuration); + + $done = file_put_contents('app/config/config.yml', $content); + } + + $output->writeln(''); + $output->writeln('Configuration finished!'); + } + + protected static function getPhp($includeArgs = true) + { + $phpFinder = new PhpExecutableFinder(); + if (!$phpPath = $phpFinder->find($includeArgs)) { + throw new \RuntimeException('The php executable could not be found, add it to your PATH environment variable and try again'); + } + + return $phpPath; + } +} diff --git a/src/Gist/Controller/ApiController.php b/src/Gist/Controller/ApiController.php index 89e18e1..283a616 100644 --- a/src/Gist/Controller/ApiController.php +++ b/src/Gist/Controller/ApiController.php @@ -158,12 +158,16 @@ class ApiController extends Controller return $this->invalidMethodResponse('POST method is required.'); } - $gist = GistQuery::create() - ->filterByCipher(false) - ->filterById((int) $gist) - ->_or() - ->filterByFile($gist) - ->findOne(); + $query = GistQuery::create() + ->filterByCipher(false); + + if (ctype_digit($gist)) { + $query->filterById((int) $gist); + } else { + $query->filterByFile($gist); + } + + $gist = $query->findOne(); if (!$gist) { return $this->invalidRequestResponse('Invalid Gist'); diff --git a/src/Gist/Controller/Controller.php b/src/Gist/Controller/Controller.php index 5e71381..c13fd38 100644 --- a/src/Gist/Controller/Controller.php +++ b/src/Gist/Controller/Controller.php @@ -20,6 +20,27 @@ abstract class Controller */ protected $app; + /** + * @var array + */ + protected $types = [ + 'html', + 'css', + 'javascript', + 'php', + 'sql', + 'xml', + 'yaml', + 'markdown', + 'perl', + 'c', + 'asp', + 'python', + 'bash', + 'actionscript3', + 'text', + ]; + /** * __construct. * @@ -88,6 +109,7 @@ abstract class Controller return array( 'gist' => $gist, 'type' => $gist->getType(), + 'types' => $this->types, 'history' => $history, 'commit' => $commit, 'raw_content' => $content, @@ -136,7 +158,6 @@ abstract class Controller $app = $this->getApp(); if (!empty($request)) { - } $securityContext = $app['security.token_storage']; @@ -180,4 +201,32 @@ abstract class Controller $params ); } + + /** + * Creates a Response. + * + * @param string $template + * @param array $params + * + * @return Response + */ + public function createResponse($template, array $params = null) + { + $body = $this->render($template, $params); + + $response = new Response($body); + + if (empty($params['no_cache'])) { + $ttl = 3600 * 24 * 7; + $etag = sha1($response->getContent()); + + $response->setTtl($ttl); + $response->setClientTtl($ttl); + $response->setExpires(new \DateTime('now +7 days')); + $response->setLastModified(new \DateTime('now')); + $response->setEtag($etag, true); + } + + return $response; + } } diff --git a/src/Gist/Controller/EditController.php b/src/Gist/Controller/EditController.php index 1767dce..bafdf49 100644 --- a/src/Gist/Controller/EditController.php +++ b/src/Gist/Controller/EditController.php @@ -9,6 +9,7 @@ use Gist\Model\Gist; use GitWrapper\GitException; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Form\FormError; +use Symfony\Component\HttpFoundation\Response; /** * Class EditController. @@ -22,7 +23,7 @@ class EditController extends Controller * * @param Request $request * - * @return string + * @return Response */ public function createAction(Request $request) { @@ -54,11 +55,12 @@ class EditController extends Controller } } - return $this->render( + return $this->createResponse( 'Edit/index.html.twig', array( 'gist' => isset($gist) ? $gist : null, 'form' => $form->createView(), + 'no_cache' => true, ) ); } @@ -68,7 +70,7 @@ class EditController extends Controller * * @param Request $request * - * @return string + * @return Response */ public function cloneAction(Request $request, $gist, $commit) { @@ -108,7 +110,8 @@ class EditController extends Controller } $viewOptions['form'] = $form->createView(); + $viewOptions['no_cache'] = true; - return $this->render('Edit/clone.html.twig', $viewOptions); + return $this->createResponse('Edit/clone.html.twig', $viewOptions); } } diff --git a/src/Gist/Controller/LoginController.php b/src/Gist/Controller/LoginController.php index f835306..eecff06 100644 --- a/src/Gist/Controller/LoginController.php +++ b/src/Gist/Controller/LoginController.php @@ -20,7 +20,7 @@ class LoginController extends Controller * * @param Request $request * - * @return string + * @return Response */ public function registerAction(Request $request) { @@ -57,12 +57,13 @@ class LoginController extends Controller } } - return $this->render( + return $this->createResponse( 'Login/register.html.twig', [ 'form' => $form->createView(), 'error' => isset($error) ? $error : '', 'success' => isset($success) ? $success : '', + 'no_cache' => true, ] ); } @@ -72,7 +73,7 @@ class LoginController extends Controller * * @param Request $request * - * @return string + * @return Response */ public function loginAction(Request $request) { @@ -97,11 +98,12 @@ class LoginController extends Controller $error = $app['translator']->trans('login.login.invalid'); } - return $this->render( + return $this->createResponse( 'Login/login.html.twig', [ 'form' => $form->createView(), 'error' => isset($error) ? $error : '', + 'no_cache' => true, ] ); } diff --git a/src/Gist/Controller/MyController.php b/src/Gist/Controller/MyController.php index d59f315..b3224a5 100644 --- a/src/Gist/Controller/MyController.php +++ b/src/Gist/Controller/MyController.php @@ -7,6 +7,7 @@ use Gist\Form\DeleteGistForm; use Gist\Form\FilterGistForm; use Gist\Form\UserPasswordForm; use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Response; /** * Class MyController. @@ -21,7 +22,7 @@ class MyController extends Controller * @param Request $request * @param int $page * - * @return string + * @return Response */ public function myAction(Request $request, $page) { @@ -62,7 +63,7 @@ class MyController extends Controller if (empty($apiKey)) { $regenerateApiKey = true; - } + } // FIXME: CSRF issue! elseif ($request->request->get('apiKey') === $apiKey && $request->request->has('generateApiKey')) { $regenerateApiKey = true; @@ -119,7 +120,7 @@ class MyController extends Controller } } - return $this->render( + return $this->createResponse( 'My/my.html.twig', array( 'gists' => $gists, @@ -129,6 +130,7 @@ class MyController extends Controller 'filterForm' => $filterForm->createView(), 'passwordForm' => $passwordForm->createView(), 'deleted' => !empty($deleted), + 'no_cache' => true, ) ); } diff --git a/src/Gist/Controller/ViewController.php b/src/Gist/Controller/ViewController.php index 44aeedc..8ea5002 100644 --- a/src/Gist/Controller/ViewController.php +++ b/src/Gist/Controller/ViewController.php @@ -31,7 +31,18 @@ class ViewController extends Controller $viewOptions = $this->getViewOptions($request, $gist, $commit); if (is_array($viewOptions)) { - return $this->render('View/view.html.twig', $viewOptions); + if ($request->query->has('type')) { + $type = $request->query->get('type'); + + if (in_array($type, $this->types)) { + $viewOptions['gist']->setType($type); + $viewOptions['type_overrided'] = true; + } + } + + $viewOptions['no_cache'] = true; + + return $this->createResponse('View/view.html.twig', $viewOptions); } else { return $this->notFoundResponse(); } @@ -53,7 +64,16 @@ class ViewController extends Controller $viewOptions = $this->getViewOptions($request, $gist, $commit); if (is_array($viewOptions)) { - return $app['twig']->render('View/embed.html.twig', $viewOptions); + if ($request->query->has('type')) { + $type = $request->query->get('type'); + + if (in_array($type, $this->types)) { + $viewOptions['gist']->setType($type); + $viewOptions['type_overrided'] = true; + } + } + + return $this->createResponse('View/embed.html.twig', $viewOptions); } else { return $this->notFoundResponse(); } @@ -66,19 +86,25 @@ class ViewController extends Controller * @param string $gist Gist's ID * @param string $commit The commit * - * @return string|Response + * @return Response */ public function embedJsAction(Request $request, $gist, $commit) { $viewOptions = $this->getViewOptions($request, $gist, $commit); - return new Response( - $this->render('View/embedJs.html.twig', $viewOptions), - 200, - array( - 'Content-Type' => 'text/javascript', - ) - ); + if ($request->query->has('type')) { + $type = $request->query->get('type'); + + if (in_array($type, $this->types)) { + $viewOptions['gist']->setType($type); + $viewOptions['type_overrided'] = true; + } + } + + $response = $this->createResponse('View/embedJs.html.twig', $viewOptions); + $response->headers->set('Content-Type', 'text/javascript'); + + return $response; } /** @@ -88,7 +114,7 @@ class ViewController extends Controller * @param string $gist Gist's ID * @param string $commit The commit * - * @return string|Response + * @return Response */ public function rawAction(Request $request, $gist, $commit) { @@ -114,7 +140,7 @@ class ViewController extends Controller * @param string $gist Gist's ID * @param string $commit The commit * - * @return string|Response + * @return Response */ public function downloadAction(Request $request, $gist, $commit) { @@ -133,7 +159,8 @@ class ViewController extends Controller 'Content-Disposition' => sprintf('filename=%s.%s', $gist->getFile(), $gist->getTypeAsExtension()), 'Content-Length' => mb_strlen($viewOptions['raw_content']), 'Content-Type' => 'application/force-download', - ) + ), + false ); } else { return $this->notFoundResponse($app); @@ -146,7 +173,7 @@ class ViewController extends Controller * @param Request $request * @param string $gist Gist's ID * - * @return string|Response + * @return Response */ public function revisionsAction(Request $request, $gist) { @@ -164,7 +191,7 @@ class ViewController extends Controller return $this->notFoundResponse(); } - return $this->render( + return $this->createResponse( 'View/revisions.html.twig', array( 'gist' => $gist, diff --git a/src/Gist/Form/CreateGistForm.php b/src/Gist/Form/CreateGistForm.php index 060f444..3972796 100644 --- a/src/Gist/Form/CreateGistForm.php +++ b/src/Gist/Form/CreateGistForm.php @@ -97,6 +97,7 @@ class CreateGistForm extends AbstractForm 'sql' => '', 'xml' => '', 'yaml' => '', + 'markdown' => '', 'perl' => '', 'c' => '', 'asp' => '', diff --git a/src/Gist/Model/Gist.php b/src/Gist/Model/Gist.php index 54142bc..90d8259 100644 --- a/src/Gist/Model/Gist.php +++ b/src/Gist/Model/Gist.php @@ -75,6 +75,7 @@ class Gist extends BaseGist 'bash' => 'sh', 'actionscript3' => 'as', 'text' => 'txt', + 'markdown' => 'md', ); return str_replace(array_keys($data), array_values($data), $this->getType()); diff --git a/src/Gist/Resources/public b/src/Gist/Resources/public new file mode 120000 index 0000000..55a9cd7 --- /dev/null +++ b/src/Gist/Resources/public @@ -0,0 +1 @@ +../../../web/app/ \ No newline at end of file diff --git a/src/Gist/Resources/views/View/embed.html.twig b/src/Gist/Resources/views/View/embed.html.twig index 44ff7df..d1ee417 100644 --- a/src/Gist/Resources/views/View/embed.html.twig +++ b/src/Gist/Resources/views/View/embed.html.twig @@ -3,7 +3,7 @@ {% block css %} {{ parent() }} - + {% if theme_settings.name == 'dark' %} @@ -31,6 +31,12 @@ {% block nav %}{% endblock %} {% block body %} + {% set routeParams = app.request.attributes.get('_route_params') %} + + {% if type_overrided is defined %} + {% set routeParams = routeParams|merge({type: gist.type}) %} + {% endif %} +
- {{ commit|slice(0, 10) }} + {{ commit|slice(0, 10) }} {% if not gist.cipher %} - + {{ 'gist.action.raw'|trans }} - + {{ 'gist.action.download'|trans }} {% endif %} - + {{ 'gist.action.clone'|trans }} @@ -101,7 +107,7 @@
-
{{ raw_content }}
+
{{ raw_content }}
diff --git a/src/Gist/Resources/views/View/embedJs.html.twig b/src/Gist/Resources/views/View/embedJs.html.twig index af08fd9..8e55f0b 100644 --- a/src/Gist/Resources/views/View/embedJs.html.twig +++ b/src/Gist/Resources/views/View/embedJs.html.twig @@ -1,10 +1,16 @@ +{% set routeParams = app.request.attributes.get('_route_params') %} + +{% if type_overrided is defined %} + {% set routeParams = routeParams|merge({type: gist.type}) %} +{% endif %} + (function() { var iFrameResizeLoaded = function() { var div = document.getElementById('gist-{{ gist.file }}-{{ commit }}'); var iframeId = 'gist-' + (Math.floor(Math.random() * (10000 - 1)) + 1).toString(); var iframe = document.createElement('iframe'); - var url = '{{ app.request.getSchemeAndHttpHost() ~ path('embed', app.request.attributes.get('_route_params')) }}'; - + var url = '{{ app.request.getSchemeAndHttpHost() ~ path('embed', routeParams) }}'; + if (div.getAttribute('data-key')) { url = [url, div.getAttribute('data-key')].join(''); } diff --git a/src/Gist/Resources/views/View/revisions.html.twig b/src/Gist/Resources/views/View/revisions.html.twig index e1d2766..3b8c2b4 100644 --- a/src/Gist/Resources/views/View/revisions.html.twig +++ b/src/Gist/Resources/views/View/revisions.html.twig @@ -82,7 +82,7 @@ {% if gist.cipher %} - + ' }} +' }}
diff --git a/src/Gist/Resources/views/base.html.twig b/src/Gist/Resources/views/base.html.twig index aedcedb..6bebf64 100644 --- a/src/Gist/Resources/views/base.html.twig +++ b/src/Gist/Resources/views/base.html.twig @@ -81,7 +81,7 @@ {% block langs %}
"})}}(); -!function(){if(("undefined"==typeof self||self.Prism)&&("undefined"==typeof global||global.Prism)){var e={css:!0,less:!0,sass:[{lang:"sass",inside:"inside",before:"punctuation",root:Prism.languages.sass&&Prism.languages.sass["variable-line"]},{lang:"sass",inside:"inside",root:Prism.languages.sass&&Prism.languages.sass["property-line"]}],scss:!0,stylus:[{lang:"stylus",before:"hexcode",inside:"rest",root:Prism.languages.stylus&&Prism.languages.stylus["property-declaration"].inside},{lang:"stylus",before:"hexcode",inside:"rest",root:Prism.languages.stylus&&Prism.languages.stylus["variable-declaration"].inside}]};Prism.hooks.add("before-highlight",function(r){if(r.language&&e[r.language]&&!e[r.language].initialized){var s=e[r.language];"Array"!==Prism.util.type(s)&&(s=[s]),s.forEach(function(s){var i,a,n,t;s===!0?(i="important",a=r.language,s=r.language):(i=s.before||"important",a=s.inside||s.lang,n=s.root||Prism.languages,t=s.skip,s=r.language),!t&&Prism.languages[s]&&(Prism.languages.insertBefore(a,i,{easing:/\bcubic-bezier\((?:-?\d*\.?\d+,\s*){3}-?\d*\.?\d+\)\B|\b(?:linear|ease(?:-in)?(?:-out)?)(?=\s|[;}]|$)/i},n),r.grammar=Prism.languages[s],e[r.language]={initialized:!0})})}}),Prism.plugins.Previewer&&new Prism.plugins.Previewer("easing",function(e){e={linear:"0,0,1,1",ease:".25,.1,.25,1","ease-in":".42,0,1,1","ease-out":"0,0,.58,1","ease-in-out":".42,0,.58,1"}[e]||e;var r=e.match(/-?\d*\.?\d+/g);if(4===r.length){r=r.map(function(e,r){return 100*(r%2?1-e:e)}),this.querySelector("path").setAttribute("d","M0,100 C"+r[0]+","+r[1]+", "+r[2]+","+r[3]+", 100,0");var s=this.querySelectorAll("line");return s[0].setAttribute("x2",r[0]),s[0].setAttribute("y2",r[1]),s[1].setAttribute("x2",r[2]),s[1].setAttribute("y2",r[3]),!0}return!1},"*",function(){this._elt.innerHTML=''})}}(); -!function(){if(("undefined"==typeof self||self.Prism)&&("undefined"==typeof global||global.Prism)){var s={css:!0,less:!0,markup:{lang:"markup",before:"punctuation",inside:"inside",root:Prism.languages.markup&&Prism.languages.markup.tag.inside["attr-value"]},sass:[{lang:"sass",inside:"inside",root:Prism.languages.sass&&Prism.languages.sass["property-line"]},{lang:"sass",before:"operator",inside:"inside",root:Prism.languages.sass&&Prism.languages.sass["variable-line"]}],scss:!0,stylus:[{lang:"stylus",before:"hexcode",inside:"rest",root:Prism.languages.stylus&&Prism.languages.stylus["property-declaration"].inside},{lang:"stylus",before:"hexcode",inside:"rest",root:Prism.languages.stylus&&Prism.languages.stylus["variable-declaration"].inside}]};Prism.hooks.add("before-highlight",function(e){if(e.language&&s[e.language]&&!s[e.language].initialized){var a=s[e.language];"Array"!==Prism.util.type(a)&&(a=[a]),a.forEach(function(a){var i,r,n,l;a===!0?(i="important",r=e.language,a=e.language):(i=a.before||"important",r=a.inside||a.lang,n=a.root||Prism.languages,l=a.skip,a=e.language),!l&&Prism.languages[a]&&(Prism.languages.insertBefore(r,i,{time:/(?:\b|\B-|(?=\B\.))\d*\.?\d+m?s\b/i},n),e.grammar=Prism.languages[a],s[e.language]={initialized:!0})})}}),Prism.plugins.Previewer&&new Prism.plugins.Previewer("time",function(s){var e=parseFloat(s),a=s.match(/[a-z]+$/i);return e&&a?(a=a[0],this.querySelector("circle").style.animationDuration=2*e+a,!0):!1},"*",function(){this._elt.innerHTML=''})}}(); -!function(){if(("undefined"==typeof self||self.Prism)&&("undefined"==typeof global||global.Prism)){var a={css:!0,less:!0,markup:{lang:"markup",before:"punctuation",inside:"inside",root:Prism.languages.markup&&Prism.languages.markup.tag.inside["attr-value"]},sass:[{lang:"sass",inside:"inside",root:Prism.languages.sass&&Prism.languages.sass["property-line"]},{lang:"sass",before:"operator",inside:"inside",root:Prism.languages.sass&&Prism.languages.sass["variable-line"]}],scss:!0,stylus:[{lang:"stylus",before:"func",inside:"rest",root:Prism.languages.stylus&&Prism.languages.stylus["property-declaration"].inside},{lang:"stylus",before:"func",inside:"rest",root:Prism.languages.stylus&&Prism.languages.stylus["variable-declaration"].inside}]};Prism.hooks.add("before-highlight",function(s){if(s.language&&a[s.language]&&!a[s.language].initialized){var e=a[s.language];"Array"!==Prism.util.type(e)&&(e=[e]),e.forEach(function(e){var i,r,n,g;e===!0?(i="important",r=s.language,e=s.language):(i=e.before||"important",r=e.inside||e.lang,n=e.root||Prism.languages,g=e.skip,e=s.language),!g&&Prism.languages[e]&&(Prism.languages.insertBefore(r,i,{angle:/(?:\b|\B-|(?=\B\.))\d*\.?\d+(?:deg|g?rad|turn)\b/i},n),s.grammar=Prism.languages[e],a[s.language]={initialized:!0})})}}),Prism.plugins.Previewer&&new Prism.plugins.Previewer("angle",function(a){var s,e,i=parseFloat(a),r=a.match(/[a-z]+$/i);if(!i||!r)return!1;switch(r=r[0]){case"deg":s=360;break;case"grad":s=400;break;case"rad":s=2*Math.PI;break;case"turn":s=1}return e=100*i/s,e%=100,this[(0>i?"set":"remove")+"Attribute"]("data-negative",""),this.querySelector("circle").style.strokeDasharray=Math.abs(e)+",500",!0},"*",function(){this._elt.innerHTML=''})}}(); +Prism.languages["markup-templating"]={},Object.defineProperties(Prism.languages["markup-templating"],{buildPlaceholders:{value:function(e,t,n,a){e.language===t&&(e.tokenStack=[],e.code=e.code.replace(n,function(n){if("function"==typeof a&&!a(n))return n;for(var r=e.tokenStack.length;-1!==e.code.indexOf("___"+t.toUpperCase()+r+"___");)++r;return e.tokenStack[r]=n,"___"+t.toUpperCase()+r+"___"}),e.grammar=Prism.languages.markup)}},tokenizePlaceholders:{value:function(e,t){if(e.language===t&&e.tokenStack){e.grammar=Prism.languages[t];var n=0,a=Object.keys(e.tokenStack),r=function(o){if(!(n>=a.length))for(var i=0;i-1){++n;var f,u=l.substring(0,p),_=new Prism.Token(t,Prism.tokenize(s,e.grammar,t),"language-"+t,s),k=l.substring(p+("___"+t.toUpperCase()+c+"___").length);if(u||k?(f=[u,_,k].filter(function(e){return!!e}),r(f)):f=_,"string"==typeof g?Array.prototype.splice.apply(o,[i,1].concat(f)):g.content=f,n>=a.length)break}}else g.content&&"string"!=typeof g.content&&r(g.content)}};r(e.tokens)}}}}); +Prism.languages.markdown=Prism.languages.extend("markup",{}),Prism.languages.insertBefore("markdown","prolog",{blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},code:[{pattern:/^(?: {4}|\t).+/m,alias:"keyword"},{pattern:/``.+?``|`[^`\n]+`/,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\r?\n|\r))[\s\S]+?(?=(?:\r?\n|\r)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\r?\n|\r)(?:==+|--+)/,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#+.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:/(^|[^\\])(\*\*|__)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,greedy:!0,inside:{punctuation:/^\*\*|^__|\*\*$|__$/}},italic:{pattern:/(^|[^\\])([*_])(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,greedy:!0,inside:{punctuation:/^[*_]|[*_]$/}},strike:{pattern:/(^|[^\\])(~~?)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,greedy:!0,inside:{punctuation:/^~~?|~~?$/}},url:{pattern:/!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,inside:{variable:{pattern:/(!?\[)[^\]]+(?=\]$)/,lookbehind:!0},string:{pattern:/"(?:\\.|[^"\\])*"(?=\)$)/}}}}),Prism.languages.markdown.bold.inside.url=Prism.languages.markdown.url,Prism.languages.markdown.italic.inside.url=Prism.languages.markdown.url,Prism.languages.markdown.strike.inside.url=Prism.languages.markdown.url,Prism.languages.markdown.bold.inside.italic=Prism.languages.markdown.italic,Prism.languages.markdown.bold.inside.strike=Prism.languages.markdown.strike,Prism.languages.markdown.italic.inside.bold=Prism.languages.markdown.bold,Prism.languages.markdown.italic.inside.strike=Prism.languages.markdown.strike,Prism.languages.markdown.strike.inside.bold=Prism.languages.markdown.bold,Prism.languages.markdown.strike.inside.italic=Prism.languages.markdown.italic,Prism.hooks.add("after-tokenize",function(a){function n(a){if(a&&"string"!=typeof a)for(var e=0,i=a.length;i>e;e++){var r=a[e];if("code"===r.type){var t=r.content[1],s=r.content[3];if(t&&s&&"code-language"===t.type&&"code-block"===s.type&&"string"==typeof t.content){var o="language-"+t.content.trim().split(/\s+/)[0].toLowerCase();s.alias?"string"==typeof s.alias?s.alias=[s.alias,o]:s.alias.push(o):s.alias=[o]}}else n(r.content)}}"markdown"===a.language&&n(a.tokens)}),Prism.hooks.add("wrap",function(a){if("code-block"===a.type){for(var n="",e=0,i=a.classes.length;i>e;e++){var r=a.classes[e],t=/language-(\w+)/.exec(r);if(t){n=t[1];break}}var s=Prism.languages[n];if(s){var o=a.content.replace(/</g,"<").replace(/&/g,"&");a.content=Prism.highlight(o,s,n)}}}),Prism.languages.md=Prism.languages.markdown; +Prism.languages.perl={comment:[{pattern:/(^\s*)=\w+[\s\S]*?=cut.*/m,lookbehind:!0},{pattern:/(^|[^\\$])#.*/,lookbehind:!0}],string:[{pattern:/\b(?:q|qq|qx|qw)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s+([a-zA-Z0-9])(?:(?!\1)[^\\]|\\[\s\S])*\1/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s*\((?:[^()\\]|\\[\s\S])*\)/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s*\{(?:[^{}\\]|\\[\s\S])*\}/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s*\[(?:[^[\]\\]|\\[\s\S])*\]/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s*<(?:[^<>\\]|\\[\s\S])*>/,greedy:!0},{pattern:/("|`)(?:(?!\1)[^\\]|\\[\s\S])*\1/,greedy:!0},{pattern:/'(?:[^'\\\r\n]|\\.)*'/,greedy:!0}],regex:[{pattern:/\b(?:m|qr)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1[msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s+([a-zA-Z0-9])(?:(?!\1)[^\\]|\\[\s\S])*\1[msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s*\((?:[^()\\]|\\[\s\S])*\)[msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s*\{(?:[^{}\\]|\\[\s\S])*\}[msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s*\[(?:[^[\]\\]|\\[\s\S])*\][msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s*<(?:[^<>\\]|\\[\s\S])*>[msixpodualngc]*/,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\2)[^\\]|\\[\s\S])*\2(?:(?!\2)[^\\]|\\[\s\S])*\2[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s+([a-zA-Z0-9])(?:(?!\2)[^\\]|\\[\s\S])*\2(?:(?!\2)[^\\]|\\[\s\S])*\2[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*\((?:[^()\\]|\\[\s\S])*\)\s*\((?:[^()\\]|\\[\s\S])*\)[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*\{(?:[^{}\\]|\\[\s\S])*\}\s*\{(?:[^{}\\]|\\[\s\S])*\}[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*\[(?:[^[\]\\]|\\[\s\S])*\]\s*\[(?:[^[\]\\]|\\[\s\S])*\][msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*<(?:[^<>\\]|\\[\s\S])*>\s*<(?:[^<>\\]|\\[\s\S])*>[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/\/(?:[^\/\\\r\n]|\\.)*\/[msixpodualngc]*(?=\s*(?:$|[\r\n,.;})&|\-+*~<>!?^]|(lt|gt|le|ge|eq|ne|cmp|not|and|or|xor|x)\b))/,greedy:!0}],variable:[/[&*$@%]\{\^[A-Z]+\}/,/[&*$@%]\^[A-Z_]/,/[&*$@%]#?(?=\{)/,/[&*$@%]#?(?:(?:::)*'?(?!\d)[\w$]+)+(?:::)*/i,/[&*$@%]\d+/,/(?!%=)[$@%][!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~]/],filehandle:{pattern:/<(?![<=])\S*>|\b_\b/,alias:"symbol"},vstring:{pattern:/v\d+(?:\.\d+)*|\d+(?:\.\d+){2,}/,alias:"string"},"function":{pattern:/sub [a-z0-9_]+/i,inside:{keyword:/sub/}},keyword:/\b(?:any|break|continue|default|delete|die|do|else|elsif|eval|for|foreach|given|goto|if|last|local|my|next|our|package|print|redo|require|say|state|sub|switch|undef|unless|until|use|when|while)\b/,number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0b[01](?:_?[01])*|(?:\d(?:_?\d)*)?\.?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)\b/,operator:/-[rwxoRWXOezsfdlpSbctugkTBMAC]\b|\+[+=]?|-[-=>]?|\*\*?=?|\/\/?=?|=[=~>]?|~[~=]?|\|\|?=?|&&?=?|<(?:=>?|<=?)?|>>?=?|![~=]?|[%^]=?|\.(?:=|\.\.?)?|[\\?]|\bx(?:=|\b)|\b(?:lt|gt|le|ge|eq|ne|cmp|not|and|or|xor)\b/,punctuation:/[{}[\];(),:]/}; +!function(e){e.languages.php=e.languages.extend("clike",{keyword:/\b(?:and|or|xor|array|as|break|case|cfunction|class|const|continue|declare|default|die|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|for|foreach|function|include|include_once|global|if|new|return|static|switch|use|require|require_once|var|while|abstract|interface|public|implements|private|protected|parent|throw|null|echo|print|trait|namespace|final|yield|goto|instanceof|finally|try|catch)\b/i,constant:/\b[A-Z_][A-Z0-9_]*\b/,comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0}}),e.languages.insertBefore("php","string",{"shell-comment":{pattern:/(^|[^\\])#.*/,lookbehind:!0,alias:"comment"}}),e.languages.insertBefore("php","keyword",{delimiter:{pattern:/\?>|<\?(?:php|=)?/i,alias:"important"},variable:/\$+(?:\w+\b|(?={))/i,"package":{pattern:/(\\|namespace\s+|use\s+)[\w\\]+/,lookbehind:!0,inside:{punctuation:/\\/}}}),e.languages.insertBefore("php","operator",{property:{pattern:/(->)[\w]+/,lookbehind:!0}});var n={pattern:/{\$(?:{(?:{[^{}]+}|[^{}]+)}|[^{}])+}|(^|[^\\{])\$+(?:\w+(?:\[.+?]|->\w+)*)/,lookbehind:!0,inside:{rest:e.languages.php}};e.languages.insertBefore("php","string",{"nowdoc-string":{pattern:/<<<'([^']+)'(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\1;/,greedy:!0,alias:"string",inside:{delimiter:{pattern:/^<<<'[^']+'|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<'?|[';]$/}}}},"heredoc-string":{pattern:/<<<(?:"([^"]+)"(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\1;|([a-z_]\w*)(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\2;)/i,greedy:!0,alias:"string",inside:{delimiter:{pattern:/^<<<(?:"[^"]+"|[a-z_]\w*)|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<"?|[";]$/}},interpolation:n}},"single-quoted-string":{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0,alias:"string"},"double-quoted-string":{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,alias:"string",inside:{interpolation:n}}}),delete e.languages.php.string,e.hooks.add("before-tokenize",function(n){if(/(?:<\?php|<\?)/gi.test(n.code)){var t=/(?:<\?php|<\?)[\s\S]*?(?:\?>|$)/gi;e.languages["markup-templating"].buildPlaceholders(n,"php",t)}}),e.hooks.add("after-tokenize",function(n){e.languages["markup-templating"].tokenizePlaceholders(n,"php")})}(Prism); +Prism.languages.insertBefore("php","variable",{"this":/\$this\b/,global:/\$(?:_(?:SERVER|GET|POST|FILES|REQUEST|SESSION|ENV|COOKIE)|GLOBALS|HTTP_RAW_POST_DATA|argc|argv|php_errormsg|http_response_header)\b/,scope:{pattern:/\b[\w\\]+::/,inside:{keyword:/static|self|parent/,punctuation:/::|\\/}}}); +Prism.languages.sql={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,lookbehind:!0},variable:[{pattern:/@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/,greedy:!0},/@[\w.$]+/],string:{pattern:/(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/,greedy:!0,lookbehind:!0},"function":/\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:_INSERT|COL)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURNS?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i,"boolean":/\b(?:TRUE|FALSE|NULL)\b/i,number:/\b0x[\da-f]+\b|\b\d+\.?\d*|\B\.\d+\b/i,operator:/[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|IN|LIKE|NOT|OR|IS|DIV|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/}; +Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0},"string-interpolation":{pattern:/(?:f|rf|fr)(?:("""|''')[\s\S]+?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:{{)*){(?!{)(?:[^{}]|{(?!{)(?:[^{}]|{(?!{)(?:[^{}])+})+})+}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|rb|br)?("""|''')[\s\S]+?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|rb|br)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},"function":{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^\s*)@\w+(?:\.\w+)*/i,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:and|as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,"boolean":/\b(?:True|False|None)\b/,number:/(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},Prism.languages.python["string-interpolation"].inside.interpolation.inside.rest=Prism.languages.python,Prism.languages.py=Prism.languages.python; +Prism.languages.yaml={scalar:{pattern:/([\-:]\s*(?:![^\s]+)?[ \t]*[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)[^\r\n]+(?:\2[^\r\n]+)*)/,lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:/(\s*(?:^|[:\-,[{\r\n?])[ \t]*(?:![^\s]+)?[ \t]*)[^\r\n{[\]},#\s]+?(?=\s*:\s)/,lookbehind:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:/([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?)?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?)(?=[ \t]*(?:$|,|]|}))/m,lookbehind:!0,alias:"number"},"boolean":{pattern:/([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:true|false)[ \t]*(?=$|,|]|})/im,lookbehind:!0,alias:"important"},"null":{pattern:/([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:null|~)[ \t]*(?=$|,|]|})/im,lookbehind:!0,alias:"important"},string:{pattern:/([:\-,[{]\s*(?:![^\s]+)?[ \t]*)("|')(?:(?!\2)[^\\\r\n]|\\.)*\2(?=[ \t]*(?:$|,|]|}|\s*#))/m,lookbehind:!0,greedy:!0},number:{pattern:/([:\-,[{]\s*(?:![^\s]+)?[ \t]*)[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+\.?\d*|\.?\d+)(?:e[+-]?\d+)?|\.inf|\.nan)[ \t]*(?=$|,|]|})/im,lookbehind:!0},tag:/![^\s]+/,important:/[&*][\w]+/,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},Prism.languages.yml=Prism.languages.yaml; +!function(){function e(e,t){return Array.prototype.slice.call((t||document).querySelectorAll(e))}function t(e,t){return t=" "+t+" ",(" "+e.className+" ").replace(/[\n\t]/g," ").indexOf(t)>-1}function n(e,n,i){n="string"==typeof n?n:e.getAttribute("data-line");for(var o,l=n.replace(/\s+/g,"").split(","),a=+e.getAttribute("data-line-offset")||0,s=r()?parseInt:parseFloat,d=s(getComputedStyle(e).lineHeight),u=t(e,"line-numbers"),c=0;o=l[c++];){var p=o.split("-"),m=+p[0],f=+p[1]||m,h=e.querySelector('.line-highlight[data-range="'+o+'"]')||document.createElement("div");if(h.setAttribute("aria-hidden","true"),h.setAttribute("data-range",o),h.className=(i||"")+" line-highlight",u&&Prism.plugins.lineNumbers){var g=Prism.plugins.lineNumbers.getLine(e,m),y=Prism.plugins.lineNumbers.getLine(e,f);g&&(h.style.top=g.offsetTop+"px"),y&&(h.style.height=y.offsetTop-g.offsetTop+y.offsetHeight+"px")}else h.setAttribute("data-start",m),f>m&&h.setAttribute("data-end",f),h.style.top=(m-a-1)*d+"px",h.textContent=new Array(f-m+2).join(" \n");u?e.appendChild(h):(e.querySelector("code")||e).appendChild(h)}}function i(){var t=location.hash.slice(1);e(".temporary.line-highlight").forEach(function(e){e.parentNode.removeChild(e)});var i=(t.match(/\.([\d,-]+)$/)||[,""])[1];if(i&&!document.getElementById(t)){var r=t.slice(0,t.lastIndexOf(".")),o=document.getElementById(r);o&&(o.hasAttribute("data-line")||o.setAttribute("data-line",""),n(o,i,"temporary "),document.querySelector(".temporary.line-highlight").scrollIntoView())}}if("undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector){var r=function(){var e;return function(){if("undefined"==typeof e){var t=document.createElement("div");t.style.fontSize="13px",t.style.lineHeight="1.5",t.style.padding=0,t.style.border=0,t.innerHTML=" 
 ",document.body.appendChild(t),e=38===t.offsetHeight,document.body.removeChild(t)}return e}}(),o=0;Prism.hooks.add("before-sanity-check",function(t){var n=t.element.parentNode,i=n&&n.getAttribute("data-line");if(n&&i&&/pre/i.test(n.nodeName)){var r=0;e(".line-highlight",n).forEach(function(e){r+=e.textContent.length,e.parentNode.removeChild(e)}),r&&/^( \n)+$/.test(t.code.slice(-r))&&(t.code=t.code.slice(0,-r))}}),Prism.hooks.add("complete",function l(e){var r=e.element.parentNode,a=r&&r.getAttribute("data-line");if(r&&a&&/pre/i.test(r.nodeName)){clearTimeout(o);var s=Prism.plugins.lineNumbers,d=e.plugins&&e.plugins.lineNumbers;t(r,"line-numbers")&&s&&!d?Prism.hooks.add("line-numbers",l):(n(r,a),o=setTimeout(i,1))}}),window.addEventListener("hashchange",i),window.addEventListener("resize",function(){var e=document.querySelectorAll("pre[data-line]");Array.prototype.forEach.call(e,function(e){n(e)})})}}(); +!function(){if("undefined"!=typeof self&&self.Prism&&self.document){var e="line-numbers",t=/\n(?!$)/g,n=function(e){var n=r(e),s=n["white-space"];if("pre-wrap"===s||"pre-line"===s){var l=e.querySelector("code"),i=e.querySelector(".line-numbers-rows"),a=e.querySelector(".line-numbers-sizer"),o=l.textContent.split(t);a||(a=document.createElement("span"),a.className="line-numbers-sizer",l.appendChild(a)),a.style.display="block",o.forEach(function(e,t){a.textContent=e||"\n";var n=a.getBoundingClientRect().height;i.children[t].style.height=n+"px"}),a.textContent="",a.style.display="none"}},r=function(e){return e?window.getComputedStyle?getComputedStyle(e):e.currentStyle||null:null};window.addEventListener("resize",function(){Array.prototype.forEach.call(document.querySelectorAll("pre."+e),n)}),Prism.hooks.add("complete",function(e){if(e.code){var r=e.element.parentNode,s=/\s*\bline-numbers\b\s*/;if(r&&/pre/i.test(r.nodeName)&&(s.test(r.className)||s.test(e.element.className))&&!e.element.querySelector(".line-numbers-rows")){s.test(e.element.className)&&(e.element.className=e.element.className.replace(s," ")),s.test(r.className)||(r.className+=" line-numbers");var l,i=e.code.match(t),a=i?i.length+1:1,o=new Array(a+1);o=o.join(""),l=document.createElement("span"),l.setAttribute("aria-hidden","true"),l.className="line-numbers-rows",l.innerHTML=o,r.hasAttribute("data-start")&&(r.style.counterReset="linenumber "+(parseInt(r.getAttribute("data-start"),10)-1)),e.element.appendChild(l),n(r),Prism.hooks.run("line-numbers",e)}}}),Prism.hooks.add("line-numbers",function(e){e.plugins=e.plugins||{},e.plugins.lineNumbers=!0}),Prism.plugins.lineNumbers={getLine:function(t,n){if("PRE"===t.tagName&&t.classList.contains(e)){var r=t.querySelector(".line-numbers-rows"),s=parseInt(t.getAttribute("data-start"),10)||1,l=s+(r.children.length-1);s>n&&(n=s),n>l&&(n=l);var i=n-s;return r.children[i]}}}}}(); diff --git a/web/components/bootstrap b/web/components/bootstrap new file mode 120000 index 0000000..379a160 --- /dev/null +++ b/web/components/bootstrap @@ -0,0 +1 @@ +../../node_modules/bootstrap \ No newline at end of file diff --git a/web/components/diff b/web/components/diff new file mode 120000 index 0000000..6966ded --- /dev/null +++ b/web/components/diff @@ -0,0 +1 @@ +../../node_modules/diff \ No newline at end of file diff --git a/web/components/flag-icon-css b/web/components/flag-icon-css new file mode 120000 index 0000000..325c10a --- /dev/null +++ b/web/components/flag-icon-css @@ -0,0 +1 @@ +../../node_modules/flag-icon-css \ No newline at end of file diff --git a/web/components/iframe-resizer b/web/components/iframe-resizer new file mode 120000 index 0000000..72ea9a9 --- /dev/null +++ b/web/components/iframe-resizer @@ -0,0 +1 @@ +../../node_modules/iframe-resizer \ No newline at end of file diff --git a/web/components/jquery b/web/components/jquery new file mode 120000 index 0000000..ee57ada --- /dev/null +++ b/web/components/jquery @@ -0,0 +1 @@ +../../node_modules/jquery \ No newline at end of file diff --git a/web/index.php b/web/index.php index 5dd7e92..d66b7a8 100644 --- a/web/index.php +++ b/web/index.php @@ -4,4 +4,4 @@ $app = require __DIR__.'/../app/bootstrap.php'; $app['env'] = 'prod'; -$app->run(); +$app['http_cache']->run();