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 599de5e..6b5a389 100644 --- a/Makefile +++ b/Makefile @@ -1,54 +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 - sh -c 'test -d app && $(GIT) add app && $(GIT) commit -m "Configuration"' - $(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 5da7741..ccbff71 100644 --- a/README.md +++ b/README.md @@ -1,293 +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, run: `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. - -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`) - -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 ---- - -### 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" - } -} -``` - -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` - -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 +![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 54fc2ab..e69e1c6 100644 --- a/app/bootstrap.php.d/20-twig.php +++ b/app/bootstrap.php.d/20-twig.php @@ -9,14 +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; }); - -$app['geshi'] = $app->share(function ($app) { - $geshi = new GeSHi(); - $geshi->enable_classes(); - $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS); - - return $geshi; -}); diff --git a/app/bootstrap.php.d/30-trans.php b/app/bootstrap.php.d/30-trans.php index 527de85..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'), + 'locales' => array('en', 'fr', 'es', 'de', 'cn', 'pl'), )); $app['translator'] = $app->extend('translator', function ($translator, $app) { @@ -50,7 +50,7 @@ $app->get('/', function (Request $request) use ($app) { $foundLocale = $app['translator']->getLocale(); foreach ($app['locales'] as $locale) { - if ($cookie === $locale || $accept->has($locale)) { + if ($cookieValue === $locale || $accept->has($locale)) { $foundLocale = $locale; break; } diff --git a/app/bootstrap.php.d/50-git.php b/app/bootstrap.php.d/50-git.php index 9511253..7afa121 100644 --- a/app/bootstrap.php.d/50-git.php +++ b/app/bootstrap.php.d/50-git.php @@ -6,7 +6,7 @@ use Gist\Service\Gist; $dataPath = $app['settings']['data']['path']; if ($dataPath[0] !== '/') { - $app['gist_path'] = $app['root_path'].$dataPath; + $app['gist_path'] = $app['root_path'].'/'.$dataPath; } else { $app['gist_path'] = $dataPath; } @@ -20,5 +20,5 @@ $app['git_working_copy'] = $app->share(function ($app) { }); $app['gist'] = $app->share(function ($app) { - return new Gist($app['gist_path'], $app['git_wrapper'], $app['git_working_copy'], $app['geshi']); + return new Gist($app['gist_path'], $app['git_wrapper'], $app['git_working_copy']); }); diff --git a/app/bootstrap.php.d/60-api.php b/app/bootstrap.php.d/60-api.php index c66683b..d630058 100644 --- a/app/bootstrap.php.d/60-api.php +++ b/app/bootstrap.php.d/60-api.php @@ -3,5 +3,11 @@ use Gist\Api\Client; $app['api_client'] = $app->share(function ($app) { - return new Client(['base_uri' => $app['settings']['api']['base_uri']]); + $client = new Client(['base_uri' => rtrim($app['settings']['api']['base_url'], '/')]); + + if (!empty($app['settings']['api']['client']['api_key'])) { + $client->setApiKey($app['settings']['api']['client']['api_key']); + } + + return $client; }); 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/app/config/config.yml.dist b/app/config/config.yml.dist index b8af280..c9845ec 100644 --- a/app/config/config.yml.dist +++ b/app/config/config.yml.dist @@ -6,7 +6,11 @@ security: login_required_to_view_gist: false login_required_to_view_embeded_gist: false api: + enabled: true + api_key_required: false base_url: 'https://gist.deblan.org/' + client: + api_key: data: path: data/git git: 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/config/routing.yml b/app/config/routing.yml index 666ca81..49ad3f4 100644 --- a/app/config/routing.yml +++ b/app/config/routing.yml @@ -57,10 +57,18 @@ revisions: path: /revs/{gist} defaults: {_controller: Gist\Controller\ViewController::revisionsAction, _locale: en} +api_list: + path: /api/list/{apiKey} + defaults: {_controller: Gist\Controller\ApiController::listAction, _locale: en, apiKey: null} + api_create: - path: /api/create - defaults: {_controller: Gist\Controller\ApiController::createAction, _locale: en} + path: /api/create/{apiKey} + defaults: {_controller: Gist\Controller\ApiController::createAction, _locale: en, apiKey: null} api_update: - path: /api/update/{gist} - defaults: {_controller: Gist\Controller\ApiController::updateAction, _locale: en} + path: /api/update/{gist}/{apiKey} + defaults: {_controller: Gist\Controller\ApiController::updateAction, _locale: en, apiKey: null} + +api_delete: + path: /api/delete/{gist}/{apiKey} + defaults: {_controller: Gist\Controller\ApiController::deleteAction, _locale: en, apiKey: null} diff --git a/app/console b/app/console index eef6670..b1e12fb 100755 --- a/app/console +++ b/app/console @@ -2,15 +2,19 @@ add(new CreateCommand()); +$app['console']->add(new ListCommand()); $app['console']->add(new UpdateCommand()); +$app['console']->add(new DeleteCommand()); $app['console']->add(new StatsCommand()); $app['console']->add(new UserCreateCommand()); $app['console']->add(new UpgradeTo1p4p1Command()); diff --git a/app/locales/cn.yml b/app/locales/cn.yml index 30c5aa7..c2adf88 100644 --- a/app/locales/cn.yml +++ b/app/locales/cn.yml @@ -20,6 +20,11 @@ app: my: title: '我的 Gist' nothing: '这家伙很懒,暂时没有内容' + api: + title: 'API' + warning: 'Keep it secret!' + form: + generate: 'Regenerate' gist: untitled: '未命名' @@ -32,14 +37,14 @@ gist: raw: '源文件' download: '下载' clone: '克隆' - embed: '引用:' + embed: '引用' add: '新建' 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: @@ -93,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 42cc012..656f71f 100644 --- a/app/locales/de.yml +++ b/app/locales/de.yml @@ -20,6 +20,11 @@ app: my: title: 'Meine Gists' nothing: 'Nichts zu finden (momentan)!' + api: + title: 'API' + warning: 'Keep it secret!' + form: + generate: 'Regenerate' gist: untitled: 'Ohne Titel' @@ -32,14 +37,14 @@ gist: raw: 'RAW' download: 'Herunterladen' clone: 'Klonen' - embed: 'Einfügen:' + embed: 'Einfügen' add: 'Hinzufügen' 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: @@ -93,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 51630d3..40e7454 100644 --- a/app/locales/en.yml +++ b/app/locales/en.yml @@ -20,6 +20,12 @@ app: my: title: 'My gists' nothing: 'Nothing yet!' + api: + title: 'API' + warning: 'Keep it secret!' + form: + generate: 'Regenerate' + gist: untitled: 'Untitled' @@ -32,14 +38,14 @@ gist: raw: 'RAW' download: 'Download' clone: 'Clone' - embed: 'Embed:' + embed: 'Embed' add: 'New' 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: @@ -93,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 75cfe90..83c0078 100644 --- a/app/locales/es.yml +++ b/app/locales/es.yml @@ -20,6 +20,11 @@ app: my: title: 'Mis Gists' nothing: 'Nada por ahora.' + api: + title: 'API' + warning: 'Keep it secret!' + form: + generate: 'Regenerate' gist: untitled: 'Sin título' @@ -32,14 +37,14 @@ gist: raw: 'RAW' download: 'Descargar' clone: 'Clonar' - embed: 'Insertar : ' + embed: 'Insertar' add: 'Añadir' 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: @@ -93,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 7674d1b..63c79ac 100644 --- a/app/locales/fr.yml +++ b/app/locales/fr.yml @@ -20,6 +20,11 @@ app: my: title: 'Mes Gists' nothing: 'Rien pour le moment !' + api: + title: 'API' + warning: 'Gardez-la secrète !' + form: + generate: 'Regénérer' gist: untitled: 'Sans titre' @@ -32,14 +37,14 @@ gist: raw: 'RAW' download: 'Télécharger' clone: 'Clôner' - embed: 'Insérer : ' + embed: 'Insérer' add: 'Ajouter' 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: @@ -93,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 201d38d..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", - "SyntaxHighlighter": "3.0.83", - "iframe-resizer": "2.8.6", - "jsdiff": "~2.2.2" - } -} diff --git a/composer.json b/composer.json index 2f25fac..14cd8e1 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,8 @@ { + "name": "deblan/gist", + "license": "GPL-3.0-only", + "type": "project", "require": { - "geshi/geshi": "dev-master", "silex/silex": "1.3.x-dev", "symfony/yaml": "~2.6", "symfony/twig-bridge": "~2.6", @@ -13,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 29c26a0..ca17817 100644 --- a/src/Gist/Api/Client.php +++ b/src/Gist/Api/Client.php @@ -12,31 +12,53 @@ use GuzzleHttp\Client as BaseClient; class Client extends BaseClient { /** - * URI of creation - * + * URI of creation. + * * @const string */ const CREATE = '/en/api/create'; /** - * URI of updating + * URI of update. * * @const string */ const UPDATE = '/en/api/update/{gist}'; /** - * Creates a gist + * URI of delete. * - * @param string $title The title - * @param string $type The type + * @const string + */ + const DELETE = '/en/api/delete/{gist}'; + + /** + * URI of list. + * + * @const string + */ + const LIST = '/en/api/list'; + + /** + * The API key. + * + * @var string|null + */ + protected $apiKey; + + /** + * Creates a gist. + * + * @param string $title The title + * @param string $type The type * @param string $content The content + * * @return array */ public function create($title, $type, $content) { $response = $this->post( - self::CREATE, + $this->mergeApiKey(self::CREATE), array( 'form_params' => array( 'form' => array( @@ -56,9 +78,9 @@ class Client extends BaseClient } /** - * Clones and update a gist + * Clones and update a gist. * - * @param string $gist Gist's ID + * @param string $gist Gist's ID * @param string $content The content * * @return array @@ -66,7 +88,7 @@ class Client extends BaseClient public function update($gist, $content) { $response = $this->post( - str_replace('{gist}', $gist, self::UPDATE), + str_replace('{gist}', $gist, $this->mergeApiKey(self::UPDATE)), array( 'form_params' => array( 'form' => array( @@ -82,4 +104,81 @@ class Client extends BaseClient return []; } + + /** + * Deletes a gist. + * + * @param string $gist Gist's ID + * + * @return array + */ + public function delete($gist) + { + $response = $this->post(str_replace('{gist}', $gist, $this->mergeApiKey(self::DELETE))); + + if ($response->getStatusCode() === 200) { + return json_decode($response->getBody()->getContents(), true); + } + + return []; + } + + /** + * Lists the user's gists. + * + * @param string $gist Gist's ID + * @param string $content The content + * + * @return array + */ + public function list() + { + $response = $this->get($this->mergeApiKey(self::LIST)); + + if ($response->getStatusCode() === 200) { + return json_decode($response->getBody()->getContents(), true); + } + + return []; + } + + /* + * Merges the API key with the given url. + * + * @param string $url + * + * @return string + */ + public function mergeApiKey($url) + { + if (empty($this->apiKey)) { + return $url; + } + + return rtrim($url, '/').'/'.$this->apiKey; + } + + /* + * Set the value of "apiKey". + * + * @param string|null $apiKey + * + * @return Client + */ + public function setApiKey($apiKey) + { + $this->apiKey = $apiKey; + + return $this; + } + + /* + * Get the value of "apiKey". + * + * @return string|null + */ + public function getApiKey() + { + return $this->apiKey; + } } diff --git a/src/Gist/Command/CreateCommand.php b/src/Gist/Command/CreateCommand.php index 7aa7027..14fdd46 100644 --- a/src/Gist/Command/CreateCommand.php +++ b/src/Gist/Command/CreateCommand.php @@ -7,6 +7,8 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; +use Propel\Runtime\Parser\YamlParser; +use Symfony\Component\Yaml\Yaml; /** * class CreateCommand. @@ -27,8 +29,9 @@ class CreateCommand extends Command ->addArgument('input', InputArgument::REQUIRED, 'Input') ->addArgument('type', InputArgument::OPTIONAL, 'Type', 'text') ->addOption('title', 't', InputOption::VALUE_REQUIRED, 'Title of the gist') - ->addOption('show-url', 'u', InputOption::VALUE_NONE, 'Display only the gist url') - ->addOption('show-id', 'i', InputOption::VALUE_NONE, 'Display only the gist Id') + ->addOption('all', 'a', InputOption::VALUE_NONE, 'Display all the response') + ->addOption('id', 'i', InputOption::VALUE_NONE, 'Display only the id of the gist') + ->addOption('json', 'j', InputOption::VALUE_NONE, 'Format the response to json') ->setHelp(<<--title, -t Defines a title - - --show-id, -i - Display only the Id of the gist - --show-url, -u - Display only the url of the gist + --id, -i + Display only the id of the gist + + --all, -a + Display all the response + + --json, -j + Format the response to json EOF ); } @@ -58,11 +64,10 @@ EOF */ protected function execute(InputInterface $input, OutputInterface $output) { - //$output->writeln(sprintf('%s bar.', 'test')); - $file = $input->getArgument('input'); $type = $input->getArgument('type'); $title = $input->getOption('title'); + $json = $input->getOption('json'); if ($file === '-') { $content = file_get_contents('php://stdin'); @@ -94,19 +99,17 @@ EOF $gist = $this->getSilexApplication()['api_client']->create($title, $type, $content); - if ($input->getOption('show-url')) { - $output->writeln($gist['url']); - - return true; + if ($input->getOption('id')) { + $id = isset($gist['gist']['id']) ? $gist['gist']['id'] : $gist['gist']['Id']; + $result = $json ? json_encode(array('id' => $id)) : $id; + } elseif ($input->getOption('all')) { + $result = $json ? json_encode($gist) : Yaml::dump($gist); + } else { + $url = $gist['url']; + $result = $json ? json_encode(array('url' => $url)) : $url; } - if ($input->getOption('show-id')) { - $output->writeln($gist['gist']['Id']); - - return true; - } - - $output->writeln(json_encode($gist)); + $output->writeln($result); } /** diff --git a/src/Gist/Command/DeleteCommand.php b/src/Gist/Command/DeleteCommand.php new file mode 100644 index 0000000..ec0ca31 --- /dev/null +++ b/src/Gist/Command/DeleteCommand.php @@ -0,0 +1,48 @@ + + */ +class DeleteCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('delete') + ->setDescription('Delete a gist using the API') + ->addOption('gist', null, InputOption::VALUE_REQUIRED, 'Id or File of the gist') + ->setHelp(<<<'EOF' +Provides a client to delete a gist using the API. + +Arguments: + none. + +Options: + --gist + Defines the Gist to delete by using its Id or its File +EOF + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $result = $this->getSilexApplication()['api_client']->delete($input->getOption('gist')); + + $output->writeln(empty($result['error']) ? 'OK' : 'An error occured.'); + } +} diff --git a/src/Gist/Command/ListCommand.php b/src/Gist/Command/ListCommand.php new file mode 100644 index 0000000..878b443 --- /dev/null +++ b/src/Gist/Command/ListCommand.php @@ -0,0 +1,59 @@ + + */ +class ListCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('gists') + ->setDescription('List gists using the API'); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $gists = $this->getSilexApplication()['api_client']->list(); + $rows = []; + + foreach ($gists as $gist) { + $rows[] = array( + $gist['id'], + $gist['title'], + $gist['cipher'] ? 'y' : 'n', + $gist['type'], + (new DateTime($gist['createdAt']))->format('Y-m-d H:i:s'), + (new DateTime($gist['updatedAt']))->format('Y-m-d H:i:s'), + $gist['url'], + ); + } + + $table = new Table($output); + $table + ->setHeaders(array('ID', 'Title', 'Cipher', 'Type', 'Created At', 'Updated At', 'url')) + ->setRows($rows); + + $table->render(); + } +} diff --git a/src/Gist/Command/UpdateCommand.php b/src/Gist/Command/UpdateCommand.php index ef09a00..a48ec14 100644 --- a/src/Gist/Command/UpdateCommand.php +++ b/src/Gist/Command/UpdateCommand.php @@ -7,6 +7,7 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Yaml\Yaml; /** * class UpdateCommand. @@ -26,8 +27,9 @@ class UpdateCommand extends Command ->setDescription('Update a gist using the API') ->addArgument('input', InputArgument::REQUIRED, 'Input') ->addOption('gist', null, InputOption::VALUE_REQUIRED, 'Id or File of the gist') - ->addOption('show-url', 'u', InputOption::VALUE_NONE, 'Display only the gist url') - ->addOption('show-id', 'i', InputOption::VALUE_NONE, 'Display only the gist Id') + ->addOption('all', 'a', InputOption::VALUE_NONE, 'Display all the response') + ->addOption('id', 'i', InputOption::VALUE_NONE, 'Display only the id of the gist') + ->addOption('json', 'j', InputOption::VALUE_NONE, 'Format the response to json') ->setHelp(<<--gist Defines the Gist to update by using its Id or its File - --show-id, -i - Display only the Id of the gist + --id, -i + Display only the id of the gist - --show-url, -u - Display only the url of the gist + --all, -a + Display all the response + + --json, -j + Format the response to json EOF ); } @@ -57,10 +62,9 @@ EOF */ protected function execute(InputInterface $input, OutputInterface $output) { - //$output->writeln(sprintf('%s bar.', 'test')); - $file = $input->getArgument('input'); $gist = $input->getOption('gist'); + $json = $input->getOption('json'); if ($file === '-') { $content = file_get_contents('php://stdin'); @@ -86,19 +90,17 @@ EOF $gist = $this->getSilexApplication()['api_client']->update($gist, $content); - if ($input->getOption('show-url')) { - $output->writeln($gist['url']); - - return true; + if ($input->getOption('id')) { + $id = isset($gist['gist']['id']) ? $gist['gist']['id'] : $gist['gist']['Id']; + $result = $json ? json_encode(array('id' => $id)) : $id; + } elseif ($input->getOption('all')) { + $result = $json ? json_encode($gist) : Yaml::dump($gist); + } else { + $url = $gist['url']; + $result = $json ? json_encode(array('url' => $url)) : $url; } - if ($input->getOption('show-id')) { - $output->writeln($gist['gist']['Id']); - - return true; - } - - $output->writeln(json_encode($gist)); + $output->writeln($result); } /** 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 5b86bbe..283a616 100644 --- a/src/Gist/Controller/ApiController.php +++ b/src/Gist/Controller/ApiController.php @@ -8,6 +8,9 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Gist\Form\ApiCreateGistForm; use Gist\Model\GistQuery; use Gist\Form\ApiUpdateGistForm; +use GitWrapper\GitException; +use Gist\Model\UserQuery; +use Propel\Runtime\ActiveQuery\Criteria; /** * Class ApiController. @@ -16,10 +19,80 @@ use Gist\Form\ApiUpdateGistForm; */ class ApiController extends Controller { - public function createAction(Request $request) + /** + * Lists gists. + * + * @param Request $request + * @param string $apiKey + * + * @return JsonResponse + */ + public function listAction(Request $request, $apiKey) { $app = $this->getApp(); + if (false === $app['settings']['api']['enabled']) { + return new Response('', 403); + } + + if (false === $this->isValidApiKey($apiKey, true)) { + return $this->invalidApiKeyResponse(); + } + + if (false === $request->isMethod('get')) { + return $this->invalidMethodResponse('GET method is required.'); + } + + $user = $app['user.provider']->loadUserByApiKey($apiKey); + + $criteria = GistQuery::create() + ->limit(15) + ->orderById(Criteria::DESC); + + $gists = $user->getGists($criteria); + $data = array(); + + foreach ($gists as $gist) { + try { + $history = $app['gist']->getHistory($gist); + + $value = $gist->toArray(); + $value['url'] = $request->getSchemeAndHttpHost().$app['url_generator']->generate( + 'view', + array( + 'gist' => $gist->getFile(), + 'commit' => array_pop($history)['commit'], + ) + ); + + $data[] = $value; + } catch (GitException $e) { + } + } + + return new JsonResponse($data); + } + + /** + * Creates a gist. + * + * @param Request $request + * @param string $apiKey + * + * @return JsonResponse + */ + public function createAction(Request $request, $apiKey) + { + $app = $this->getApp(); + + if (false === $app['settings']['api']['enabled']) { + return new Response('', 403); + } + + if (false === $this->isValidApiKey($apiKey, (bool) $app['settings']['api']['api_key_required'])) { + return $this->invalidApiKeyResponse(); + } + if (false === $request->isMethod('post')) { return $this->invalidMethodResponse('POST method is required.'); } @@ -36,40 +109,65 @@ class ApiController extends Controller $form->submit($request); if ($form->isValid()) { + $user = !empty($apiKey) ? $app['user.provider']->loadUserByApiKey($apiKey) : null; $gist = $app['gist']->create(new Gist(), $form->getData()); - $gist->setCipher(false)->save(); + $gist + ->setCipher(false) + ->setUser($user) + ->save(); $history = $app['gist']->getHistory($gist); - return new JsonResponse(array( - 'url' => $request->getSchemeAndHttpHost().$app['url_generator']->generate( - 'view', - array( - 'gist' => $gist->getFile(), - 'commit' => array_pop($history)['commit'], - ) - ), - 'gist' => $gist->toArray(), - )); + $data = $gist->toArray(); + $data['url'] = $request->getSchemeAndHttpHost().$app['url_generator']->generate( + 'view', + array( + 'gist' => $gist->getFile(), + 'commit' => array_pop($history)['commit'], + ) + ); + + return new JsonResponse($data); } return $this->invalidRequestResponse('Invalid field(s)'); } - public function updateAction(Request $request, $gist) + /** + * Updates a gist. + * + * @param Request $request + * @param string $gist + * @param string $apiKey + * + * @return JsonResponse + */ + public function updateAction(Request $request, $gist, $apiKey) { $app = $this->getApp(); + if (false === $app['settings']['api']['enabled']) { + return new Response('', 403); + } + + if (false === $this->isValidApiKey($apiKey, (bool) $app['settings']['api']['api_key_required'])) { + return $this->invalidApiKeyResponse(); + } + if (false === $request->isMethod('post')) { 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'); @@ -91,21 +189,88 @@ class ApiController extends Controller $history = $app['gist']->getHistory($gist); - return new JsonResponse(array( - 'url' => $request->getSchemeAndHttpHost().$app['url_generator']->generate( - 'view', - array( - 'gist' => $gist->getFile(), - 'commit' => array_pop($history)['commit'], - ) - ), - 'gist' => $gist->toArray(), - )); + $data = $gist->toArray(); + $data['url'] = $request->getSchemeAndHttpHost().$app['url_generator']->generate( + 'view', + array( + 'gist' => $gist->getFile(), + 'commit' => array_pop($history)['commit'], + ) + ); + + return new JsonResponse($data); } return $this->invalidRequestResponse('Invalid field(s)'); } + /** + * Deletes a gist. + * + * @param Request $request + * @param string $gist + * @param string $apiKey + * + * @return JsonResponse + */ + public function deleteAction(Request $request, $gist, $apiKey) + { + $app = $this->getApp(); + + if (false === $app['settings']['api']['enabled']) { + return new Response('', 403); + } + + if (false === $this->isValidApiKey($apiKey, true)) { + return $this->invalidApiKeyResponse(); + } + + if (false === $request->isMethod('post')) { + return $this->invalidMethodResponse('POST method is required.'); + } + + $user = $app['user.provider']->loadUserByApiKey($apiKey); + + $gist = GistQuery::create() + ->filterById((int) $gist) + ->_or() + ->filterByFile($gist) + ->filterByUser($user) + ->findOne(); + + if (!$gist) { + return $this->invalidRequestResponse('Invalid Gist'); + } + + $gist->delete(); + + return new JsonResponse(['error' => false]); + } + + /** + * Builds an invalid api key response. + * + * @param mixed $message + * + * @return JsonResponse + */ + protected function invalidApiKeyResponse() + { + $data = [ + 'error' => ' Unauthorized', + 'message' => 'Invalid API KEY', + ]; + + return new JsonResponse($data, 401); + } + + /** + * Builds an invalid method response. + * + * @param mixed $message + * + * @return JsonResponse + */ protected function invalidMethodResponse($message = null) { $data = [ @@ -116,6 +281,13 @@ class ApiController extends Controller return new JsonResponse($data, 405); } + /** + * Builds an invalid request response. + * + * @param mixed $message + * + * @return JsonResponse + */ protected function invalidRequestResponse($message = null) { $data = [ @@ -125,4 +297,24 @@ class ApiController extends Controller return new JsonResponse($data, 400); } + + /** + * Checks if the given api key is valid + * depending of the requirement. + * + * @param mixed $apiKey + * @param mixed $required + * + * @return bool + */ + protected function isValidApiKey($apiKey, $required = false) + { + if (empty($apiKey)) { + return !$required; + } + + return UserQuery::create() + ->filterByApiKey($apiKey) + ->count() === 1; + } } diff --git a/src/Gist/Controller/Controller.php b/src/Gist/Controller/Controller.php index c96d8d0..c13fd38 100644 --- a/src/Gist/Controller/Controller.php +++ b/src/Gist/Controller/Controller.php @@ -13,13 +13,34 @@ use Symfony\Component\HttpFoundation\Response; * * @author Simon Vieille */ -class Controller +abstract class Controller { /** * @var Application */ protected $app; + /** + * @var array + */ + protected $types = [ + 'html', + 'css', + 'javascript', + 'php', + 'sql', + 'xml', + 'yaml', + 'markdown', + 'perl', + 'c', + 'asp', + 'python', + 'bash', + 'actionscript3', + 'text', + ]; + /** * __construct. * @@ -88,10 +109,10 @@ class Controller return array( 'gist' => $gist, 'type' => $gist->getType(), + 'types' => $this->types, 'history' => $history, 'commit' => $commit, 'raw_content' => $content, - 'content' => $app['gist']->highlight($gist->getGeshiType(), $content), ); } @@ -128,12 +149,17 @@ class Controller /** * Returns the connected user. * + * @param Request $request An API request + * * @return mixed */ - public function getUser() + public function getUser(Request $request = null) { $app = $this->getApp(); + if (!empty($request)) { + } + $securityContext = $app['security.token_storage']; $securityToken = $securityContext->getToken(); @@ -175,4 +201,32 @@ 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 348e989..bafdf49 100644 --- a/src/Gist/Controller/EditController.php +++ b/src/Gist/Controller/EditController.php @@ -8,6 +8,8 @@ use Gist\Form\CloneGistForm; use Gist\Model\Gist; use GitWrapper\GitException; use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\Form\FormError; +use Symfony\Component\HttpFoundation\Response; /** * Class EditController. @@ -21,7 +23,7 @@ class EditController extends Controller * * @param Request $request * - * @return string + * @return Response */ public function createAction(Request $request) { @@ -37,17 +39,28 @@ class EditController extends Controller if ($request->isMethod('post')) { $form->submit($request); + $data = $form->getData(); + + if (empty($form->getData()['content']) && $form->get('file')->getData()) { + $data['content'] = file_get_contents($form->get('file')->getData()->getPathName()); + unset($data['file']); + } + + if (empty($data['content'])) { + $form->get('content')->addError(new FormError($app['translator']->trans('form.error.not_blank'))); + } if ($form->isValid()) { - $gist = $app['gist']->create(new Gist(), $form->getData(), $this->getUser()); + $gist = $app['gist']->create(new Gist(), $data, $this->getUser()); } } - return $this->render( + return $this->createResponse( 'Edit/index.html.twig', array( 'gist' => isset($gist) ? $gist : null, 'form' => $form->createView(), + 'no_cache' => true, ) ); } @@ -57,7 +70,7 @@ class EditController extends Controller * * @param Request $request * - * @return string + * @return Response */ public function cloneAction(Request $request, $gist, $commit) { @@ -97,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 a45d1a3..eecff06 100644 --- a/src/Gist/Controller/LoginController.php +++ b/src/Gist/Controller/LoginController.php @@ -20,13 +20,13 @@ class LoginController extends Controller * * @param Request $request * - * @return string + * @return Response */ public function registerAction(Request $request) { $app = $this->getApp(); - if (false === $app['settings']['enable_registration']) { + if (false === $app['settings']['security']['enable_registration']) { return new Response('', 403); } @@ -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,13 +73,13 @@ class LoginController extends Controller * * @param Request $request * - * @return string + * @return Response */ public function loginAction(Request $request) { $app = $this->getApp(); - if (false === $app['settings']['enable_login']) { + if (false === $app['settings']['security']['enable_login']) { return new Response('', 403); } @@ -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 aa113c8..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) { @@ -58,6 +59,26 @@ class MyController extends Controller $gists = $this->getUser()->getGistsPager($page, $options); + $apiKey = $this->getUser()->getApiKey(); + + if (empty($apiKey)) { + $regenerateApiKey = true; + } + // FIXME: CSRF issue! + elseif ($request->request->get('apiKey') === $apiKey && $request->request->has('generateApiKey')) { + $regenerateApiKey = true; + } else { + $regenerateApiKey = false; + } + + if ($regenerateApiKey) { + $apiKey = $app['salt_generator']->generate(32, true); + + $this->getUser() + ->setApiKey($apiKey) + ->save(); + } + if ($request->isMethod('post')) { $deleteForm->handleRequest($request); $passwordForm->handleRequest($request); @@ -99,15 +120,17 @@ class MyController extends Controller } } - return $this->render( + return $this->createResponse( 'My/my.html.twig', array( 'gists' => $gists, 'page' => $page, + 'apiKey' => $apiKey, 'deleteForm' => $deleteForm->createView(), '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 fd9f262..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) { @@ -131,9 +157,10 @@ class ViewController extends Controller 200, array( 'Content-Disposition' => sprintf('filename=%s.%s', $gist->getFile(), $gist->getTypeAsExtension()), - 'Content-Length' => filesize($file), + '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/ApiCreateGistForm.php b/src/Gist/Form/ApiCreateGistForm.php index 27f3e4b..436f78a 100644 --- a/src/Gist/Form/ApiCreateGistForm.php +++ b/src/Gist/Form/ApiCreateGistForm.php @@ -16,7 +16,9 @@ class ApiCreateGistForm extends CreateGistForm { parent::build($options); - $this->builder->remove('cipher'); + $this->builder + ->remove('cipher') + ->remove('file'); return $this->builder; } diff --git a/src/Gist/Form/ApiUpdateGistForm.php b/src/Gist/Form/ApiUpdateGistForm.php index 2594d59..bc058e3 100644 --- a/src/Gist/Form/ApiUpdateGistForm.php +++ b/src/Gist/Form/ApiUpdateGistForm.php @@ -18,6 +18,7 @@ class ApiUpdateGistForm extends ApiCreateGistForm $this->builder ->remove('title') + ->remove('file') ->remove('type'); return $this->builder; diff --git a/src/Gist/Form/CreateGistForm.php b/src/Gist/Form/CreateGistForm.php index a30e4f8..3972796 100644 --- a/src/Gist/Form/CreateGistForm.php +++ b/src/Gist/Form/CreateGistForm.php @@ -32,16 +32,25 @@ class CreateGistForm extends AbstractForm 'content', 'textarea', array( - 'required' => true, + 'required' => false, 'attr' => array( 'class' => 'form-control', 'rows' => 10, ), 'trim' => false, 'constraints' => array( - new NotBlank(array( - 'message' => $this->translator->trans('form.error.not_blank'), - )), + ), + ) + ); + + $this->builder->add( + 'file', + 'file', + array( + 'required' => false, + 'attr' => array( + ), + 'constraints' => array( ), ) ); @@ -88,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 23a4809..90d8259 100644 --- a/src/Gist/Model/Gist.php +++ b/src/Gist/Model/Gist.php @@ -3,6 +3,7 @@ namespace Gist\Model; use Gist\Model\Base\Gist as BaseGist; +use Propel\Runtime\Map\TableMap; /** * Class Gist. @@ -44,14 +45,16 @@ class Gist extends BaseGist } /** - * Returns the type for Geshi. + * Returns the type for highlighting. * * @return string */ - public function getGeshiType() + public function getHighlightType() { $data = array( 'html' => 'xml', + 'asp' => 'aspnet', + 'actionscript3' => 'actionscript', ); return str_replace(array_keys($data), array_values($data), $this->getType()); @@ -72,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()); @@ -86,4 +90,25 @@ class Gist extends BaseGist return $this; } + + /** + * {@inheritdoc} + */ + public function toArray($keyType = TableMap::TYPE_PHPNAME, $includeLazyLoadColumns = true, $alreadyDumpedObjects = array(), $includeForeignObjects = false) + { + $data = parent::toArray( + $keyType, + $includeLazyLoadColumns, + $alreadyDumpedObjects, + $includeForeignObjects + ); + + foreach ($data as $key => $value) { + $newKey = lcfirst($key); + unset($data[$key]); + $data[$newKey] = $value; + } + + return $data; + } } diff --git a/src/Gist/Model/User.php b/src/Gist/Model/User.php index e5d483b..b57d03e 100644 --- a/src/Gist/Model/User.php +++ b/src/Gist/Model/User.php @@ -54,7 +54,7 @@ class User extends BaseUser implements UserInterface * * @return Propel\Runtime\Util\PropelModelPager */ - public function getGistsPager($page, $options = array(), $maxPerPage = 10) + public function getGistsPager($page, $options = array(), $maxPerPage = 10) { $query = GistQuery::create() ->filterByUser($this) @@ -63,11 +63,11 @@ class User extends BaseUser implements UserInterface if (!empty($options['type']) && $options['type'] !== 'all') { $query->filterByType($options['type']); } - + if (!empty($options['title'])) { $query->filterByTitle('%'.$options['title'].'%', Criteria::LIKE); } - + if (!empty($options['cipher']) && $options['cipher'] !== 'anyway') { $bools = array( 'yes' => true, diff --git a/src/Gist/Resources/config/propel/schema.xml b/src/Gist/Resources/config/propel/schema.xml index ce79ebb..8caac4e 100644 --- a/src/Gist/Resources/config/propel/schema.xml +++ b/src/Gist/Resources/config/propel/schema.xml @@ -22,6 +22,7 @@ + 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/Edit/index.html.twig b/src/Gist/Resources/views/Edit/index.html.twig index e557467..eb06868 100644 --- a/src/Gist/Resources/views/Edit/index.html.twig +++ b/src/Gist/Resources/views/Edit/index.html.twig @@ -4,7 +4,7 @@ {% block body %}
-
+
@@ -71,6 +71,10 @@ {{ form_errors(form.content) }} {{ form_widget(form.content) }}

+

+ {{ form_errors(form.file) }} + {{ form_widget(form.file) }} +

diff --git a/src/Gist/Resources/views/My/my.html.twig b/src/Gist/Resources/views/My/my.html.twig index 2e65a3b..9ccbbc7 100644 --- a/src/Gist/Resources/views/My/my.html.twig +++ b/src/Gist/Resources/views/My/my.html.twig @@ -205,30 +205,61 @@
-
-
- {{ 'login.login.form.password.placeholder'|trans }} -
-
-
- -

- {{ form_errors(passwordForm.currentPassword) }} - {{ form_widget(passwordForm.currentPassword) }} -

+ {% set apiEnabled = app.settings.api.enabled %} -

- {{ form_errors(passwordForm.newPassword) }} - {{ form_widget(passwordForm.newPassword) }} -

+
+
+
+
+ {{ 'login.login.form.password.placeholder'|trans }} +
+
+
+ +

+ {{ form_errors(passwordForm.currentPassword) }} + {{ form_widget(passwordForm.currentPassword) }} +

-

- {{ form_rest(passwordForm) }} - -

- +

+ {{ form_errors(passwordForm.newPassword) }} + {{ form_widget(passwordForm.newPassword) }} +

+ +

+ {{ form_rest(passwordForm) }} + +

+ +
+
+ {% if apiEnabled %} +
+
+
+ {{ 'my.api.title'|trans }} +
+
+
+

{{ 'my.api.warning'|trans|raw }}

+ +
+
+

+ +

+

+ +

+
+
+
+
+
+
+ {% endif %}
diff --git a/src/Gist/Resources/views/View/embed.html.twig b/src/Gist/Resources/views/View/embed.html.twig index 4dd7ad1..d1ee417 100644 --- a/src/Gist/Resources/views/View/embed.html.twig +++ b/src/Gist/Resources/views/View/embed.html.twig @@ -1,31 +1,17 @@ {% extends 'base.html.twig' %} {% block css %} - {% if gist.cipher %} - - + {{ parent() }} + + + + {% if theme_settings.name == 'dark' %} + {% else %} - + {% endif %} - {{ parent() }} - {% endblock %} @@ -46,45 +31,83 @@ {% 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) }} - - + - - {{ gist.title ? gist.title : 'gist.untitled'|trans }}
- {% if gist.cipher %} -
{{ raw_content|raw }}
- {% else %} - {{ content|raw }} - {% endif %} +
{{ raw_content }}
@@ -100,37 +123,5 @@ {{ parent() }} - - {% if gist.cipher %} - - - - - {% endif %} + {% endblock %} 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 bcf205f..3b8c2b4 100644 --- a/src/Gist/Resources/views/View/revisions.html.twig +++ b/src/Gist/Resources/views/View/revisions.html.twig @@ -1,9 +1,15 @@ {% extends 'base.html.twig' %} {% block css %} - - {{ parent() }} + + + + {% if theme_settings.name == 'dark' %} + + {% else %} + + {% endif %} {% endblock %} {% block title %}{{ gist.title ? gist.title : 'gist.untitled'|trans }} - {{ 'gist.action.history'|trans }}{% endblock %} @@ -23,7 +29,7 @@ {{ history|length }} - + @@ -76,10 +79,10 @@ {% block js %} {{ parent() }} - {% if gist.cipher %} - {{ include('View/cipherJs.html.twig') }} + - + {% if gist.cipher %} + {% endif %} {% endblock %} diff --git a/src/Gist/Resources/views/View/view.html.twig b/src/Gist/Resources/views/View/view.html.twig index 97db4ca..0aed8e4 100644 --- a/src/Gist/Resources/views/View/view.html.twig +++ b/src/Gist/Resources/views/View/view.html.twig @@ -1,19 +1,26 @@ {% extends 'base.html.twig' %} {% block css %} - {% if gist.cipher %} - - - {% else %} - - {% endif %} - {{ parent() }} + + + + {% if theme_settings.name == 'dark' %} + + {% else %} + + {% endif %} {% endblock %} {% block title %}{{ gist.title ? gist.title : 'gist.untitled'|trans }} - {{ commit|slice(0, 10) }}{% endblock %} {% block body %} + {% set routeParams = app.request.attributes.get('_route_params') %} + + {% if type_overrided is defined %} + {% set routeParams = routeParams|merge({type: gist.type}) %} + {% endif %} +
+
+
+
+
+
+
+
+
+
+ {{ 'gist.action.embed'|trans }} +
+
+
+
+
{{ '
+' }}
@@ -82,8 +159,6 @@ {% block js %} {{ parent() }} - - {% if gist.cipher %} - {{ include('View/cipherJs.html.twig') }} - {% endif %} + + {% endblock %} diff --git a/src/Gist/Resources/views/base.html.twig b/src/Gist/Resources/views/base.html.twig index 2410dd7..6bebf64 100644 --- a/src/Gist/Resources/views/base.html.twig +++ b/src/Gist/Resources/views/base.html.twig @@ -1,26 +1,28 @@ {% set theme_settings = app.settings.theme %} -{% set security_dettings = app.settings.security %} +{% set security_settings = app.settings.security %} {% block css %} - - + + {% if theme_settings.name == 'dark' %} - + {% else %} - + {% endif %} - + {% endblock %} {% block metas %} - + {% endblock %} + + {{ 'app.title_prefix'|trans }}{% block title %}{% endblock %} @@ -56,14 +58,14 @@ {{ 'app.menu.my.logout.title'|trans }} - {% elseif security_dettings.enable_login %} + {% elseif security_settings.enable_login %}
  • {{ 'app.menu.my.login.title'|trans }}
  • - {% if security_dettings.enable_registration %} + {% if security_settings.enable_registration %}
  • {{ 'app.menu.my.register.title'|trans }} @@ -79,7 +81,7 @@ {% block langs %}