Compare commits

...

45 commits

Author SHA1 Message Date
Simon Vieille c9fee1ea42
[security] fix issue WS-2018-0590 2019-06-14 12:07:08 +02:00
Simon Vieille 196da8cb45
upgrade npm packages 2019-05-04 19:05:52 +02:00
Simon Vieille cb286ce704
fix issue #4 2019-04-25 15:59:26 +02:00
Simon Vieille 6d84cf319c
remove cache on view page 2019-03-13 11:36:19 +01:00
Simon Vieille e5f996705e
add cache controle using createResponse on embedJs action 2019-02-07 10:27:37 +01:00
Simon Vieille 7c32234626
add cache controle using createResponse on embedJs action 2019-02-07 10:26:42 +01:00
Simon Vieille 7195609086
Fix #15: autolinker plugin removed 2019-01-03 14:52:44 +01:00
Simon Vieille 3972513905
Issue template 2018-10-21 15:48:14 +02:00
Simon Vieille 0ff9d4a526
Issue template 2018-10-21 15:45:08 +02:00
Simon Vieille a8a3eadc71
Issue template 2018-10-21 15:43:10 +02:00
Simon Vieille f2e30bbd8f
Issue template 2018-10-21 15:38:57 +02:00
Simon Vieille faab3c4252
API link updated 2018-10-09 16:36:38 +02:00
Simon Vieille f6136404c9
New language highlight: markdown 2018-09-20 10:31:32 +02:00
Simon Vieille 426cf0e1f1
Fix typo in Makefile 2018-09-11 13:41:43 +02:00
Simon Vieille d67ac40e3d
clean-cache rule in Makefile 2018-09-11 13:22:40 +02:00
Simon Vieille 4ee4311c1a
Cache remove on pages with form 2018-09-11 13:15:12 +02:00
Simon Vieille 9eb8a73462
Embed page: line-numbers 2018-09-05 12:28:07 +02:00
Simon Vieille 7f77b92554
Feature: line-numbers, UI: target blank on embed links 2018-09-05 12:16:29 +02:00
Simon Vieille 0eb689b5dc
API's document links updated 2018-09-05 11:08:44 +02:00
Simon Vieille 971812f2d8
Pictures in README 2018-08-21 16:21:23 +02:00
Simon Vieille fbac488f62 New HTTP Cache headers and cache disabled for account page 2018-08-21 16:10:20 +02:00
Simon Vieille f36783b2ce
Cache 2018-08-21 10:24:26 +02:00
Simon Vieille 001901a8f7
Fix issue with ViewController 2018-08-21 10:13:38 +02:00
Simon Vieille 6e848f0982
Cache 2018-08-21 10:01:33 +02:00
Simon Vieille b43baac802
Cache 2018-08-21 09:56:51 +02:00
Simon Vieille 2ac9737409
Cache control 2018-08-20 17:05:36 +02:00
Simon Vieille b7e6d8c69c
HTTP Cache 2018-08-20 16:40:04 +02:00
Simon Vieille 2d8f19437a
Fix issue with prism path on embed page 2018-08-20 16:02:08 +02:00
Simon Vieille af30221295
Documentation moved to the WIKI 2018-08-20 14:00:12 +02:00
Simon Vieille 999b4a25a7
Documentation 2018-08-20 12:50:58 +02:00
Simon Vieille 32f01bcd75
Documentation 2018-08-20 12:34:47 +02:00
Simon Vieille 3b812523a9
Documentation 2018-08-20 12:33:49 +02:00
Simon Vieille 418d150833
Fix issue with SQLite setup 2018-08-20 12:30:33 +02:00
Simon Vieille 9059fe214b
Jquery call fixed 2018-08-09 09:11:48 +02:00
Simon Vieille abed0e4b3c
Symlink from web/app to src 2018-08-08 12:59:40 +02:00
Simon Vieille 8caff598e8
Migration from bower to npm only 2018-08-08 12:41:08 +02:00
Simon Vieille 6c12f92fd1
New configuration process 2018-08-08 11:48:31 +02:00
Simon Vieille 301fca186d
Propel configuration template replaced 2018-08-08 11:47:56 +02:00
Simon Vieille ff4a592203
Mage removed from the project and the documentation 2018-08-08 11:47:37 +02:00
Simon Vieille 2b4625c9b4 Merge branch 'feature/composer' into dev-master 2018-08-08 11:30:52 +02:00
Simon Vieille b25809bda6
propel configration file ignored 2018-08-08 11:30:35 +02:00
Simon Vieille 41d1770dc2
Composer installation handler 2018-08-08 11:29:38 +02:00
Simon Vieille 851dbd282b
Composer installation handler: database configuration templates 2018-08-08 11:29:15 +02:00
Simon Vieille e30571c6b7 Merge branch 'dev-master' into feature/composer 2018-08-08 11:26:09 +02:00
Simon Vieille 2584014d78
Test of post install script 2018-06-25 16:24:23 +02:00
46 changed files with 593 additions and 829 deletions

View file

@ -1,3 +0,0 @@
{
"directory": "web/components/"
}

5
.gitignore vendored
View file

@ -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/

View file

View file

@ -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

View file

@ -1,7 +0,0 @@
# global settings
name: Gist
email:
notifications: false
logging: true
maxlogs: 30
ssh_needs_tty: false

View file

@ -1,2 +0,0 @@
*
!.gitignore

View file

26
ISSUE_TEMPLATE.md Normal file
View file

@ -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
...

View file

@ -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

414
README.md
View file

@ -1,419 +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 <contact@deblan.fr>
**Translators**
* Simon Vieille <contact@deblan.fr>
* Marion Sanchez
* Marjorie Da Silva
* Mélanie Chanat
* Showfom
* Tavin
![Gist](https://upload.deblan.org/u/2018-08/5b7ab81c.png "Embeded Gist")

View file

@ -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;
});

View file

@ -0,0 +1,8 @@
<?php
use Silex\Provider\HttpCacheServiceProvider;
$app->register(new HttpCacheServiceProvider(), array(
'http_cache.cache_dir' => $app['root_path'].'/cache/',
'http_cache.esi' => null,
));

View file

@ -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:

View file

@ -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

View file

@ -44,7 +44,7 @@ date:
format: 'Y-m-d h:i:s'
footer:
text: '<p>Powered by <a href="https://gitnet.fr/deblan/gist">GIST</a>, it''s open source :) - <a href="https://gitnet.fr/deblan/gist#api">API</a></p>'
text: '<p>Powered by <a href="https://gitnet.fr/deblan/gist">GIST</a>, it''s open source :) - <a href="https://gitnet.fr/deblan/gist/wiki/1.4-API/">API</a></p>'
login:
login:
@ -98,6 +98,7 @@ form:
php: 'PHP'
sql: 'SQL'
yaml: 'YAML'
markdown: 'MARKDOWN'
perl: 'PERL'
c: 'C/C++'
asp: 'ASP'

View file

@ -44,7 +44,7 @@ date:
format: 'Y-m-d h:i:s'
footer:
text: '<p>Powered by <a href="https://gitnet.fr/deblan/gist">GIST</a>, it''s open source :) - <a href="https://gitnet.fr/deblan/gist#api">API</a></p>'
text: '<p>Powered by <a href="https://gitnet.fr/deblan/gist">GIST</a>, it''s open source :) - <a href="https://gitnet.fr/deblan/gist/wiki/1.4-API/">API</a></p>'
login:
login:
@ -98,6 +98,7 @@ form:
php: 'PHP'
sql: 'SQL'
yaml: 'YAML'
markdown: 'MARKDOWN'
perl: 'PERL'
c: 'C/C++'
asp: 'ASP'

View file

@ -45,7 +45,7 @@ date:
format: 'Y-m-d h:i:s'
footer:
text: '<p>Powered by <a href="https://gitnet.fr/deblan/gist">GIST</a>, it''s open source :) - <a href="https://gitnet.fr/deblan/gist#api">API</a></p>'
text: '<p>Powered by <a href="https://gitnet.fr/deblan/gist">GIST</a>, it''s open source :) - <a href="https://gitnet.fr/deblan/gist/wiki/1.4-API/">API</a></p>'
login:
login:
@ -99,6 +99,7 @@ form:
php: 'PHP'
sql: 'SQL'
yaml: 'YAML'
markdown: 'MARKDOWN'
perl: 'PERL'
c: 'C/C++'
asp: 'ASP'

View file

@ -44,7 +44,7 @@ date:
format: 'd/m/Y H\hi s\s'
footer:
text: '<p>Impulsado por <a href="https://gitnet.fr/deblan/gist">GIST</a>, es libre :) - <a href="https://gitnet.fr/deblan/gist#api">API</a></p>'
text: '<p>Impulsado por <a href="https://gitnet.fr/deblan/gist">GIST</a>, es libre :) - <a href="https://gitnet.fr/deblan/gist/wiki/1.4-API/">API</a></p>'
login:
login:
@ -98,6 +98,7 @@ form:
php: 'PHP'
sql: 'SQL'
yaml: 'YAML'
markdown: 'MARKDOWN'
perl: 'PERL'
c: 'C/C++'
asp: 'ASP'

View file

@ -44,7 +44,7 @@ date:
format: 'd/m/Y H\hi s\s'
footer:
text: '<p>Propulsé par <a href="https://gitnet.fr/deblan/gist">GIST</a>, c''est libre :) - <a href="https://gitnet.fr/deblan/gist#api">API</a></p>'
text: '<p>Propulsé par <a href="https://gitnet.fr/deblan/gist">GIST</a>, c''est libre :) - <a href="https://gitnet.fr/deblan/gist/wiki/1.4-API/">API</a></p>'
login:
login:
@ -98,6 +98,7 @@ form:
php: 'PHP'
sql: 'SQL'
yaml: 'YAML'
markdown: 'MARKDOWN'
perl: 'PERL'
c: 'C/C++'
asp: 'ASP'

View file

@ -45,7 +45,7 @@ date:
format: 'Y-m-d h:i:s'
footer:
text: '<p>Napędzane przez <a href="https://gitnet.fr/deblan/gist">GIST</a>, to jest open source :) - <a href="https://gitnet.fr/deblan/gist#api">API</a></p>'
text: '<p>Napędzane przez <a href="https://gitnet.fr/deblan/gist">GIST</a>, to jest open source :) - <a href="https://gitnet.fr/deblan/gist/wiki/1.4-API/">API</a></p>'
login:
login:
@ -99,6 +99,7 @@ form:
php: 'PHP'
sql: 'SQL'
yaml: 'YAML'
markdown: 'MARKDOWN'
perl: 'PERL'
c: 'C/C++'
asp: 'ASP'

View file

@ -1,22 +0,0 @@
{
"name": "gist",
"version": "0.0.3",
"authors": [
"Simon Vieille <simon@deblan.fr>"
],
"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"
}
}

View file

@ -15,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/"

9
package.json Normal file
View file

@ -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"
}
}

View file

@ -0,0 +1,211 @@
<?php
namespace Gist\Composer;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Process\Process;
use Composer\Script\Event;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Yaml\Yaml;
/**
* class PostInstallHandler.
*
* @author Simon Vieille <simon@deblan.fr>
*/
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;
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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,
]
);
}

View file

@ -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,
)
);
}

View file

@ -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,

View file

@ -97,6 +97,7 @@ class CreateGistForm extends AbstractForm
'sql' => '',
'xml' => '',
'yaml' => '',
'markdown' => '',
'perl' => '',
'c' => '',
'asp' => '',

View file

@ -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());

1
src/Gist/Resources/public Symbolic link
View file

@ -0,0 +1 @@
../../../web/app/

View file

@ -3,7 +3,7 @@
{% block css %}
{{ parent() }}
<link rel="stylesheet" href="{{ web_path }}components/Prism/themes/prism.css">
<link rel="stylesheet" href="{{ web_path }}app/css/prism.css">
{% if theme_settings.name == 'dark' %}
<link rel="stylesheet" href="{{ web_path }}app/css/prism-okaidia.css">
@ -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 %}
<div class="row">
<div class="col-md-12" id="embed">
<div class="panel panel-default">
@ -47,7 +53,7 @@
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li>
<a target="_blank" href="{{ path('view', app.request.attributes.get('_route_params')) }}" class="cipher-link">
<a target="_blank" href="{{ path('view', routeParams) }}" class="cipher-link">
<span class="btn btn-warning btn-xs">
{{ commit|slice(0, 10) }}
</span>
@ -55,18 +61,18 @@
</li>
{% if not gist.cipher %}
<li>
<a href="{{ path('raw', app.request.attributes.get('_route_params')) }}">
<a target="_blank" href="{{ path('raw', routeParams) }}">
{{ 'gist.action.raw'|trans }}
</a>
</li>
<li>
<a href="{{ path('download', app.request.attributes.get('_route_params')) }}">
<a target="_blank" href="{{ path('download', routeParams) }}">
{{ 'gist.action.download'|trans }}
</a>
</li>
{% endif %}
<li>
<a href="{{ path('clone', app.request.attributes.get('_route_params')) }}" class="cipher-link">
<a target="_blank" href="{{ path('clone', routeParams) }}" class="cipher-link">
{{ 'gist.action.clone'|trans }}
</a>
</li>
@ -75,20 +81,20 @@
</div>
<div class="visible-md visible-lg">
<div class="text-right actions">
<a target="_blank" href="{{ path('view', app.request.attributes.get('_route_params')) }}" class="cipher-link"><span class="btn btn-warning btn-xs">{{ commit|slice(0, 10) }}</span></a>
<a target="_blank" href="{{ path('view', routeParams) }}" class="cipher-link"><span class="btn btn-warning btn-xs">{{ commit|slice(0, 10) }}</span></a>
{% if not gist.cipher %}
<a href="{{ path('raw', app.request.attributes.get('_route_params')) }}" class="btn btn-default btn-sm">
<a target="_blank" href="{{ path('raw', routeParams) }}" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-eye-open"></span>
{{ 'gist.action.raw'|trans }}
</a>
<a href="{{ path('download', app.request.attributes.get('_route_params')) }}" class="btn btn-default btn-sm">
<a target="_blank" href="{{ path('download', routeParams) }}" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-save-file"></span>
{{ 'gist.action.download'|trans }}
</a>
{% endif %}
<a href="{{ path('clone', app.request.attributes.get('_route_params')) }}" class="btn btn-success btn-sm cipher-link">
<a target="_blank" href="{{ path('clone', routeParams) }}" class="btn btn-success btn-sm cipher-link">
<span class="glyphicon glyphicon-copy"></span>
{{ 'gist.action.clone'|trans }}
</a>
@ -101,7 +107,7 @@
<div class="tab-content">
<div id="view" class="tab-pane active in">
<div id="viewer">
<pre><code {% if gist.cipher %}data-cipher{% endif %} class="language-{{ gist.highlightType }}">{{ raw_content }}</code></pre>
<pre><code {% if gist.cipher %}data-cipher{% endif %} class="line-numbers language-{{ gist.highlightType }}">{{ raw_content }}</code></pre>
</div>
</div>
</div>

View file

@ -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('');
}

View file

@ -82,7 +82,7 @@
<script src="{{ web_path }}app/js/prism.js" {% if gist.cipher %}data-manual{% endif %}></script>
{% if gist.cipher %}
<script src="{{ web_path }}components/jsdiff/diff.min.js"></script>
<script src="{{ web_path }}components/diff/diff.min.js"></script>
<script>
var key = getKey();

View file

@ -15,6 +15,12 @@
{% 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 %}
<div class="row">
<div class="col-md-12">
<ul class="nav nav-tabs">
@ -45,7 +51,7 @@
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li>
<a target="_blank" href="{{ path('view', app.request.attributes.get('_route_params')) }}" class="cipher-link">
<a target="_blank" href="{{ path('view', routeParams) }}" class="cipher-link">
<span class="btn btn-warning btn-xs">
{{ commit|slice(0, 10) }}
</span>
@ -53,18 +59,18 @@
</li>
{% if not gist.cipher %}
<li>
<a href="{{ path('raw', app.request.attributes.get('_route_params')) }}">
<a href="{{ path('raw', routeParams) }}">
{{ 'gist.action.raw'|trans }}
</a>
</li>
<li>
<a href="{{ path('download', app.request.attributes.get('_route_params')) }}">
<a href="{{ path('download', routeParams) }}">
{{ 'gist.action.download'|trans }}
</a>
</li>
{% endif %}
<li>
<a href="{{ path('clone', app.request.attributes.get('_route_params')) }}" class="cipher-link">
<a href="{{ path('clone', routeParams) }}" class="cipher-link">
{{ 'gist.action.clone'|trans }}
</a>
</li>
@ -78,17 +84,17 @@
</span>
{% if not gist.cipher %}
<a href="{{ path('raw', app.request.attributes.get('_route_params')) }}" class="btn btn-default btn-sm">
<a href="{{ path('raw', routeParams) }}" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-eye-open"></span>
{{ 'gist.action.raw'|trans }}
</a>
<a href="{{ path('download', app.request.attributes.get('_route_params')) }}" class="btn btn-default btn-sm">
<a href="{{ path('download', routeParams) }}" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-save-file"></span>
{{ 'gist.action.download'|trans }}
</a>
{% endif %}
<a href="{{ path('clone', app.request.attributes.get('_route_params')) }}" class="btn btn-success btn-sm cipher-link">
<a href="{{ path('clone', routeParams) }}" class="btn btn-success btn-sm cipher-link">
<span class="glyphicon glyphicon-copy"></span>
{{ 'gist.action.clone'|trans }}
</a>
@ -101,7 +107,34 @@
<div class="tab-content">
<div id="view" class="tab-pane active in">
<div id="viewer">
<pre><code {% if gist.cipher %}data-cipher{% endif %} class="language-{{ gist.highlightType }}">{{ raw_content }}</code></pre>
<pre><code {% if gist.cipher %}data-cipher{% endif %} class="line-numbers language-{{ gist.highlightType }}">{{ raw_content }}</code></pre>
</div>
</div>
<div class="btn-toolbar">
<div class="pull-right">
<div class="btn-group">
<div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
{{ ('form.type.choice.' ~ gist.type)|trans }}
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right">
{% for item in types %}
{% if gist.type != item %}
<li>
{% set params = app.request.attributes.get('_route_params')|merge({type: item}) %}
<a href="{{ path('view', params) }}">
<label for="type-{{ loop.index }}">
{{ ('form.type.choice.' ~ item)|trans }}
</label>
</a>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
</div>
@ -115,7 +148,7 @@
<div class="tab-content">
<div class="tab-pane active in">
<pre><code class="language-html">{{ '<div%key%id="gist-' ~ gist.file ~ '-' ~ commit ~ '" class="gist-container"></div>
<script src="' ~ app.request.getSchemeAndHttpHost() ~ path('embedjs', app.request.attributes.get('_route_params')) ~ '" async></script>' }}</code></pre>
<script src="' ~ app.request.getSchemeAndHttpHost() ~ path('embedjs', routeParams) ~ '" async></script>' }}</code></pre>
</div>
</div>
</div>

View file

@ -1,4 +1,5 @@
/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+actionscript+c+aspnet+bash+cpp+csharp+css-extras+diff+perl+php+php-extras+python+sql+yaml&plugins=line-highlight+line-numbers+autolinker+previewer-base+previewer-color+previewer-gradient+previewer-easing+previewer-time+previewer-angle */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+actionscript+c+csharp+bash+cpp+aspnet+css-extras+diff+markup-templating+markdown+perl+php+php-extras+sql+python+yaml&plugins=line-highlight+line-numbers */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
@ -105,7 +106,7 @@ pre[class*="language-"] {
.token.url,
.language-css .token.string,
.style .token.string {
color: #a67f59;
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
@ -115,7 +116,8 @@ pre[class*="language-"] {
color: #07a;
}
.token.function {
.token.function,
.token.class-name {
color: #DD4A68;
}
@ -182,15 +184,20 @@ pre[data-line] {
bottom: .4em;
}
pre.line-numbers {
.line-numbers .line-highlight:before,
.line-numbers .line-highlight:after {
content: none;
}
pre[class*="language-"].line-numbers {
position: relative;
padding-left: 3.8em;
counter-reset: linenumber;
}
pre.line-numbers > code {
pre[class*="language-"].line-numbers > code {
position: relative;
white-space: inherit;
white-space: inherit;
}
.line-numbers .line-numbers-rows {
@ -223,245 +230,4 @@ pre.line-numbers > code {
padding-right: 0.8em;
text-align: right;
}
.token a {
color: inherit;
}
.prism-previewer,
.prism-previewer:before,
.prism-previewer:after {
position: absolute;
pointer-events: none;
}
.prism-previewer,
.prism-previewer:after {
left: 50%;
}
.prism-previewer {
margin-top: -48px;
width: 32px;
height: 32px;
margin-left: -16px;
opacity: 0;
-webkit-transition: opacity .25s;
-o-transition: opacity .25s;
transition: opacity .25s;
}
.prism-previewer.flipped {
margin-top: 0;
margin-bottom: -48px;
}
.prism-previewer:before,
.prism-previewer:after {
content: '';
position: absolute;
pointer-events: none;
}
.prism-previewer:before {
top: -5px;
right: -5px;
left: -5px;
bottom: -5px;
border-radius: 10px;
border: 5px solid #fff;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.5) inset, 0 0 10px rgba(0, 0, 0, 0.75);
}
.prism-previewer:after {
top: 100%;
width: 0;
height: 0;
margin: 5px 0 0 -7px;
border: 7px solid transparent;
border-color: rgba(255, 0, 0, 0);
border-top-color: #fff;
}
.prism-previewer.flipped:after {
top: auto;
bottom: 100%;
margin-top: 0;
margin-bottom: 5px;
border-top-color: rgba(255, 0, 0, 0);
border-bottom-color: #fff;
}
.prism-previewer.active {
opacity: 1;
}
.prism-previewer-color {
background-image: linear-gradient(45deg, #bbb 25%, transparent 25%, transparent 75%, #bbb 75%, #bbb), linear-gradient(45deg, #bbb 25%, #eee 25%, #eee 75%, #bbb 75%, #bbb);
background-size: 10px 10px;
background-position: 0 0, 5px 5px;
}
.prism-previewer-color:before {
background-color: inherit;
background-clip: padding-box;
}
.prism-previewer-gradient {
background-image: linear-gradient(45deg, #bbb 25%, transparent 25%, transparent 75%, #bbb 75%, #bbb), linear-gradient(45deg, #bbb 25%, #eee 25%, #eee 75%, #bbb 75%, #bbb);
background-size: 10px 10px;
background-position: 0 0, 5px 5px;
width: 64px;
margin-left: -32px;
}
.prism-previewer-gradient:before {
content: none;
}
.prism-previewer-gradient div {
position: absolute;
top: -5px;
left: -5px;
right: -5px;
bottom: -5px;
border-radius: 10px;
border: 5px solid #fff;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.5) inset, 0 0 10px rgba(0, 0, 0, 0.75);
}
.prism-previewer-easing {
margin-top: -76px;
margin-left: -30px;
width: 60px;
height: 60px;
background: #333;
}
.prism-previewer-easing.flipped {
margin-bottom: -116px;
}
.prism-previewer-easing svg {
width: 60px;
height: 60px;
}
.prism-previewer-easing circle {
fill: hsl(200, 10%, 20%);
stroke: white;
}
.prism-previewer-easing path {
fill: none;
stroke: white;
stroke-linecap: round;
stroke-width: 4;
}
.prism-previewer-easing line {
stroke: white;
stroke-opacity: 0.5;
stroke-width: 2;
}
@-webkit-keyframes prism-previewer-time {
0% {
stroke-dasharray: 0, 500;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 100, 500;
stroke-dashoffset: 0;
}
100% {
stroke-dasharray: 0, 500;
stroke-dashoffset: -100;
}
}
@-o-keyframes prism-previewer-time {
0% {
stroke-dasharray: 0, 500;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 100, 500;
stroke-dashoffset: 0;
}
100% {
stroke-dasharray: 0, 500;
stroke-dashoffset: -100;
}
}
@-moz-keyframes prism-previewer-time {
0% {
stroke-dasharray: 0, 500;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 100, 500;
stroke-dashoffset: 0;
}
100% {
stroke-dasharray: 0, 500;
stroke-dashoffset: -100;
}
}
@keyframes prism-previewer-time {
0% {
stroke-dasharray: 0, 500;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 100, 500;
stroke-dashoffset: 0;
}
100% {
stroke-dasharray: 0, 500;
stroke-dashoffset: -100;
}
}
.prism-previewer-time:before {
border-radius: 50%;
background: #fff;
}
.prism-previewer-time:after {
margin-top: 4px;
}
.prism-previewer-time svg {
width: 32px;
height: 32px;
-webkit-transform: rotate(-90deg);
-moz-transform: rotate(-90deg);
-ms-transform: rotate(-90deg);
-o-transform: rotate(-90deg);
transform: rotate(-90deg);
}
.prism-previewer-time circle {
fill: transparent;
stroke: hsl(200, 10%, 20%);
stroke-opacity: 0.9;
stroke-width: 32;
stroke-dasharray: 0, 500;
stroke-dashoffset: 0;
-webkit-animation: prism-previewer-time linear infinite 3s;
-moz-animation: prism-previewer-time linear infinite 3s;
-o-animation: prism-previewer-time linear infinite 3s;
animation: prism-previewer-time linear infinite 3s;
}
.prism-previewer-angle:before {
border-radius: 50%;
background: #fff;
}
.prism-previewer-angle:after {
margin-top: 4px;
}
.prism-previewer-angle svg {
width: 32px;
height: 32px;
-webkit-transform: rotate(-90deg);
-moz-transform: rotate(-90deg);
-ms-transform: rotate(-90deg);
-o-transform: rotate(-90deg);
transform: rotate(-90deg);
}
.prism-previewer-angle[data-negative] svg {
-webkit-transform: scaleX(-1) rotate(-90deg);
-moz-transform: scaleX(-1) rotate(-90deg);
-ms-transform: scaleX(-1) rotate(-90deg);
-o-transform: scaleX(-1) rotate(-90deg);
transform: scaleX(-1) rotate(-90deg);
}
.prism-previewer-angle circle {
fill: transparent;
stroke: hsl(200, 10%, 20%);
stroke-opacity: 0.9;
stroke-width: 32;
stroke-dasharray: 0, 500;
}

View file

@ -56,3 +56,11 @@ body {
.btn-error:active, .btn-error:hover, .btn-error:focus {
color: #000;
}
pre.line-numbers {
padding-left: 50px !important;
}
.line-numbers-rows {
top: -4px !important;
}

View file

@ -46,3 +46,11 @@ pre, code {
.btn-error:active, .btn-error:hover, .btn-error:focus {
color: #000;
}
pre.line-numbers {
padding-left: 50px !important;
}
.line-numbers-rows {
top: -4px !important;
}

File diff suppressed because one or more lines are too long

1
web/components/bootstrap Symbolic link
View file

@ -0,0 +1 @@
../../node_modules/bootstrap

1
web/components/diff Symbolic link
View file

@ -0,0 +1 @@
../../node_modules/diff

View file

@ -0,0 +1 @@
../../node_modules/flag-icon-css

View file

@ -0,0 +1 @@
../../node_modules/iframe-resizer

1
web/components/jquery Symbolic link
View file

@ -0,0 +1 @@
../../node_modules/jquery

View file

@ -4,4 +4,4 @@ $app = require __DIR__.'/../app/bootstrap.php';
$app['env'] = 'prod';
$app->run();
$app['http_cache']->run();