forked from deblan/gist
Compare commits
89 commits
Author | SHA1 | Date | |
---|---|---|---|
75297b565b | |||
Simon Vieille | d73e4df326 | ||
Simon Vieille | b74960ecd3 | ||
Simon Vieille | ebc0ea2bf8 | ||
Simon Vieille | 3a78e500df | ||
Simon Vieille | db8b512670 | ||
Simon Vieille | 411447ae3e | ||
Simon Vieille | d56c33c29f | ||
Simon Vieille | 37751f51a6 | ||
Simon Vieille | b83e1655b5 | ||
Simon Vieille | 1a79e4520f | ||
Simon Vieille | 21a85c0db4 | ||
Simon Vieille | 88ce6d66db | ||
Simon Vieille | 43fd509d69 | ||
Simon Vieille | d441d7e834 | ||
Simon Vieille | 9fe6510fc9 | ||
Simon Vieille | 4d2cf30de1 | ||
Simon Vieille | ab58bdb82d | ||
Simon Vieille | 87348c8335 | ||
Simon Vieille | f6e4837776 | ||
Simon Vieille | a478fb3de5 | ||
Simon Vieille | 91cdb2341c | ||
Simon Vieille | ffe3540565 | ||
Simon Vieille | 80b3c0bbdb | ||
Simon Vieille | a9435906fe | ||
Simon Vieille | 7a3ea9d060 | ||
Simon Vieille | 99635e8e3c | ||
Simon Vieille | 50025705d1 | ||
Simon Vieille | c46cf0fdb7 | ||
Simon Vieille | 79ba41a983 | ||
Simon Vieille | 8a7de8a107 | ||
Simon Vieille | bc67a2ce1c | ||
Simon Vieille | 0f127a0920 | ||
Simon Vieille | 834faedc1f | ||
Simon Vieille | 1898c31e21 | ||
Simon Vieille | fb2d387fef | ||
Simon Vieille | 0060b18842 | ||
Showfom | 3d1a6f8158 | ||
Simon Vieille | 1c52988222 | ||
Simon Vieille | 3fcdae9270 | ||
Simon Vieille | 2f1d57d36b | ||
Simon Vieille | 0b6c4d7ac1 | ||
Simon Vieille | 4a035ba7ed | ||
Simon Vieille | 706823c04a | ||
Simon Vieille | 87bd9e9db5 | ||
Simon Vieille | 48eacc5cb8 | ||
Simon Vieille | df86035225 | ||
Simon Vieille | 43db48a715 | ||
Simon Vieille | c7a03c4bf9 | ||
Simon Vieille | 61b071feb8 | ||
Simon Vieille | 36f4fb98ff | ||
Simon Vieille | 9dfbde6730 | ||
Simon Vieille | 74b23a59b5 | ||
Simon Vieille | 2702a1d987 | ||
Simon Vieille | 221e28832f | ||
Simon Vieille | ab47699731 | ||
Simon Vieille | eb1f4f1df3 | ||
Simon Vieille | 0e02add6e6 | ||
Simon Vieille | df3035f2f1 | ||
Simon Vieille | a79b443b36 | ||
Simon Vieille | 82790998d4 | ||
Simon Vieille | 63ba878e80 | ||
Simon Vieille | b0f74d8dc8 | ||
Simon Vieille | aa73578efe | ||
Simon Vieille | 3f29ab2574 | ||
Simon Vieille | 22cf9ab3e5 | ||
Simon Vieille | b7b93460b1 | ||
Simon Vieille | f27f74e0e9 | ||
Simon Vieille | 7bba890025 | ||
Simon Vieille | 3e297ec85f | ||
Simon Vieille | 41fefddc16 | ||
Simon Vieille | 67c674c52e | ||
Simon Vieille | 724fee29bb | ||
Simon Vieille | be31903aaa | ||
Simon Vieille | 12e6efc471 | ||
Simon Vieille | 2f65088c5e | ||
Simon Vieille | 1b961403ad | ||
Simon Vieille | 841211a63a | ||
Simon Vieille | 1006e079f6 | ||
Simon Vieille | da7bb48893 | ||
Simon Vieille | 0529ec16d0 | ||
Simon Vieille | 2ddfea60cc | ||
Simon Vieille | f25e427dcc | ||
Simon Vieille | 96f199adaa | ||
Simon Vieille | 6ab827bf94 | ||
Simon Vieille | c8d35c23bb | ||
Simon Vieille | 229f532ac0 | ||
Simon Vieille | 71bfa8f7cb | ||
Simon Vieille | 8e8a8a2b42 |
20
.gitignore
vendored
20
.gitignore
vendored
|
@ -1,7 +1,13 @@
|
|||
.mage/config/environment/*.yml
|
||||
.mage/logs/*.log
|
||||
composer.lock
|
||||
vendor/
|
||||
tags
|
||||
*.swp
|
||||
propel.yml
|
||||
/.mage/config/environment/*.yml
|
||||
/.mage/logs/*.log
|
||||
/composer.lock
|
||||
/vendor/
|
||||
/propel.yml
|
||||
/src/Gist/Model/Base/
|
||||
/src/Gist/Model/Map/
|
||||
/web/components/
|
||||
/app/propel/
|
||||
/app/config/config.yml
|
||||
/app/config/propel/
|
||||
/data/
|
||||
/trans/
|
||||
|
|
|
@ -8,6 +8,7 @@ deployment:
|
|||
- "*.svn"
|
||||
- "*.git"
|
||||
- "*.swp"
|
||||
- "app/config/config.yml"
|
||||
- "app/config/propel/"
|
||||
- "app/propel/"
|
||||
- "data/git"
|
407
README.md
407
README.md
|
@ -1,3 +1,21 @@
|
|||
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
|
||||
====
|
||||
|
||||
|
@ -8,6 +26,7 @@ https://www.deblan.io/post/517/gist-est-dans-la-place
|
|||
|
||||
![Gist](https://upload.deblan.org/u/2016-06/57655dec.png "Gist")
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
|
@ -17,20 +36,6 @@ Requirements
|
|||
* Composer (php)
|
||||
* Bower (node)
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
$ git clone https://gitnet.fr/deblan/gist
|
||||
$ cd gist
|
||||
$ make
|
||||
$ mv propel-dist.yaml propel.yaml
|
||||
$ # EDIT propel.yaml (dsn)
|
||||
$ make propel
|
||||
|
||||
Edit `app/bootstrap.php.d/70-security.php` and modify the valye of `$app['token']` with a strong secret phrase.
|
||||
|
||||
Screencast: https://asciinema.org/a/19814
|
||||
|
||||
### Git
|
||||
|
||||
Git can maybe be downloaded from your system's repositories.
|
||||
|
@ -43,57 +48,139 @@ Git can maybe be downloaded from your system's repositories.
|
|||
Composer can maybe be downloaded from your system's repositories.
|
||||
Else, follow the next instructions:
|
||||
|
||||
#### Download
|
||||
# With cURL
|
||||
$ curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
# With cURL
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
# With Wget
|
||||
$ wget -O - -q https://getcomposer.org/installer | php
|
||||
|
||||
# With Wget
|
||||
wget -O - -q https://getcomposer.org/installer | php
|
||||
$ chmod +x composer.phar
|
||||
|
||||
You can now use it with `php composer.phar [arguments]`.
|
||||
# For a local installation and if the envvar PATH contains "$HOME/bin/"
|
||||
$ mv composer.phar ~/bin/composer
|
||||
|
||||
#### Executable
|
||||
|
||||
mv composer.phar composer
|
||||
chmod +x composer
|
||||
|
||||
Use it with `./composer [arguments]`.
|
||||
|
||||
#### Install
|
||||
|
||||
Assuming `~/bin` exists ans is in `$PATH`.
|
||||
|
||||
mv composer ~/bin
|
||||
|
||||
#### Dependencies Installation (from `composer.lock`)
|
||||
|
||||
composer install
|
||||
|
||||
#### Dependencies Update (will change `composer.lock`)
|
||||
|
||||
composer update
|
||||
# For a global installation
|
||||
$ sudo mv composer.phar /usr/local/bin/composer
|
||||
|
||||
### Bower
|
||||
|
||||
$ sudo apt-get install npm
|
||||
$ sudo npm install -g bower
|
||||
|
||||
#### Install
|
||||
Installation
|
||||
------------
|
||||
|
||||
npm install -g bower
|
||||
$ cd /path/to/www/
|
||||
$ git clone https://gitnet.fr/deblan/gist
|
||||
$ cd gist
|
||||
$ make
|
||||
$ cp propel-dist.yaml propel.yaml
|
||||
|
||||
#### Dependencies Installation (from `bower.json`)
|
||||
Edit `propel.yaml`. **Use spaces instead of tabulations**.
|
||||
|
||||
bower install
|
||||
**MySQL**
|
||||
|
||||
#### Dependencies Update
|
||||
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"
|
||||
|
||||
bower install
|
||||
[...]
|
||||
|
||||
**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"`.
|
||||
|
||||
### Upgrade
|
||||
|
||||
$ 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
|
||||
--------
|
||||
|
||||
|
@ -108,20 +195,25 @@ A Makefile is provided to automate some tasks.
|
|||
API
|
||||
---
|
||||
|
||||
### Create a new gist
|
||||
### Version < v1.7.0
|
||||
|
||||
#### Create a new gist
|
||||
|
||||
**POST** /{locale}/api/create
|
||||
Params:
|
||||
|
||||
* ```form[title]```: String (required, can be empty)
|
||||
* ```form[type]```: String (required)
|
||||
* `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)
|
||||
* `form[content]`: String (required)
|
||||
|
||||
#### Responses:
|
||||
**Responses:**
|
||||
|
||||
* Code ```200```: A json which contains gist's information. Example:
|
||||
```javascript
|
||||
* 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": {
|
||||
|
@ -134,22 +226,23 @@ Params:
|
|||
"UpdatedAt": "2015-07-19T16:26:15Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
* Code ```405```: Method Not Allowed
|
||||
* Code ```400```: Bad Request
|
||||
```
|
||||
|
||||
### Update an existing Gist
|
||||
#### Update an existing gist
|
||||
|
||||
**POST** /{locale}/api/update/{id}
|
||||
Params:
|
||||
|
||||
* ```{id}```: Gist Id (required)
|
||||
* ```form[content]```: String (required)
|
||||
* `{id}`: Gist Id (required)
|
||||
* `form[content]`: String (required)
|
||||
|
||||
#### Responses:
|
||||
**Responses:**
|
||||
|
||||
* Code ```200```: A json which contains gist's information. Example:
|
||||
```javascript
|
||||
* 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": {
|
||||
|
@ -162,76 +255,164 @@ Params:
|
|||
"UpdatedAt": "2015-07-19T16:30:15Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
* Code ```405```: Method Not Allowed
|
||||
* Code ```400```: Bad Request
|
||||
```
|
||||
|
||||
### 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 and update gists:
|
||||
* **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`
|
||||
|
||||
```
|
||||
$ app/console --help create
|
||||
$ app/console --help update
|
||||
```
|
||||
|
||||
### Create user
|
||||
|
||||
```
|
||||
$ app/console --help user:create
|
||||
```
|
||||
|
||||
### Show stats
|
||||
|
||||
```
|
||||
$ app/console --help stats
|
||||
```
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
### API
|
||||
|
||||
#### Personal instance
|
||||
|
||||
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/```.
|
||||
|
||||
### Authentication
|
||||
|
||||
#### Disabling login
|
||||
|
||||
Edit `app/bootstrap.php.d/70-security.php` and modify the value of `$app['enable_login']` with `false`.
|
||||
|
||||
#### Disabling registration
|
||||
|
||||
Edit `app/bootstrap.php.d/70-security.php` and modify the value of `$app['enable_registration']` with `false`.
|
||||
|
||||
### Debug
|
||||
|
||||
`app_dev.php` is the development router. Access is granted for an IP range defined in the same file.
|
||||
### 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.
|
||||
Gist uses [Magallanes](http://magephp.com/) to manage deployment.
|
||||
|
||||
### Global installation
|
||||
**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
|
||||
|
||||
**Local installation**
|
||||
|
||||
$ composer require andres-montanez/magallanes
|
||||
|
||||
There is an example of the configuration of an environment in `.mage/config/environment/prod.yml-dist`.
|
||||
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
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
<?php
|
||||
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
$app['config.locator.path'] = $app['root_path'].'/app/config/';
|
||||
|
||||
$app['config.locator'] = function ($app) {
|
||||
return new FileLocator($app['config.locator.path']);
|
||||
};
|
||||
|
||||
$app['settings'] = $app->share(function ($app) {
|
||||
return Yaml::parse($app['config.locator']->locate('config.yml'));
|
||||
});
|
||||
|
|
|
@ -12,11 +12,3 @@ $app->extend('twig', function ($twig, $app) {
|
|||
|
||||
return $twig;
|
||||
});
|
||||
|
||||
$app['geshi'] = $app->share(function ($app) {
|
||||
$geshi = new GeSHi();
|
||||
$geshi->enable_classes();
|
||||
$geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
|
||||
|
||||
return $geshi;
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ use Symfony\Component\HttpFoundation\Cookie;
|
|||
$app->register(new TranslationServiceProvider(), array(
|
||||
'locale' => 'en',
|
||||
'locale_fallback' => 'en',
|
||||
'locales' => array('en', 'fr'),
|
||||
'locales' => array('en', 'fr', 'es', 'de', 'cn'),
|
||||
));
|
||||
|
||||
$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;
|
||||
}
|
||||
|
|
|
@ -3,10 +3,16 @@
|
|||
use GitWrapper\GitWrapper;
|
||||
use Gist\Service\Gist;
|
||||
|
||||
$app['gist_path'] = $app['root_path'].'/data/git';
|
||||
$dataPath = $app['settings']['data']['path'];
|
||||
|
||||
if ($dataPath[0] !== '/') {
|
||||
$app['gist_path'] = $app['root_path'].'/'.$dataPath;
|
||||
} else {
|
||||
$app['gist_path'] = $dataPath;
|
||||
}
|
||||
|
||||
$app['git_wrapper'] = $app->share(function ($app) {
|
||||
return new GitWrapper('/usr/bin/git');
|
||||
return new GitWrapper($app['settings']['git']['path']);
|
||||
});
|
||||
|
||||
$app['git_working_copy'] = $app->share(function ($app) {
|
||||
|
@ -14,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']);
|
||||
});
|
||||
|
|
|
@ -3,5 +3,11 @@
|
|||
use Gist\Api\Client;
|
||||
|
||||
$app['api_client'] = $app->share(function ($app) {
|
||||
return new Client(['base_uri' => 'https://gist.deblan.org/']);
|
||||
$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;
|
||||
});
|
||||
|
|
|
@ -6,16 +6,14 @@ use Silex\Provider\RememberMeServiceProvider;
|
|||
use Gist\Service\SaltGenerator;
|
||||
use Gist\Security\AuthenticationProvider;
|
||||
use Gist\Security\AuthenticationListener;
|
||||
use Gist\Security\AuthenticationEntryPoint;
|
||||
use Gist\Security\LogoutSuccessHandler;
|
||||
use Silex\Provider\SessionServiceProvider;
|
||||
use Symfony\Component\Security\Http\HttpUtils;
|
||||
|
||||
$app['enable_registration'] = true;
|
||||
$app['enable_login'] = true;
|
||||
$app['token'] = 'ThisTokenIsNotSoSecretChangeIt';
|
||||
$securitySettings = $app['settings']['security'];
|
||||
|
||||
$app['salt_generator'] = $app->share(function($app) {
|
||||
$app['token'] = $securitySettings['token'];
|
||||
|
||||
$app['salt_generator'] = $app->share(function ($app) {
|
||||
return new SaltGenerator();
|
||||
});
|
||||
|
||||
|
@ -30,10 +28,10 @@ $app['security.authentication_listener.factory.form'] = $app->protect(function (
|
|||
$app['security.authentication_provider.'.$name.'.form'] = $app->share(function ($app) {
|
||||
return new AuthenticationProvider($app['user.provider']);
|
||||
});
|
||||
|
||||
|
||||
$app['security.authentication_listener.'.$name.'.form'] = $app->share(function ($app) use ($name) {
|
||||
return new AuthenticationListener(
|
||||
$app['security.token_storage'],
|
||||
$app['security.token_storage'],
|
||||
$app['security.authentication_provider.'.$name.'.form']
|
||||
);
|
||||
});
|
||||
|
@ -42,42 +40,62 @@ $app['security.authentication_listener.factory.form'] = $app->protect(function (
|
|||
'security.authentication_provider.'.$name.'.form',
|
||||
'security.authentication_listener.'.$name.'.form',
|
||||
null,
|
||||
'pre_auth'
|
||||
'pre_auth',
|
||||
];
|
||||
});
|
||||
|
||||
$app->register(
|
||||
new SecurityServiceProvider(),
|
||||
[
|
||||
'security.firewalls' => [
|
||||
'default' => [
|
||||
'pattern' => '^/',
|
||||
'anonymous' => true,
|
||||
'form' => [
|
||||
'login_path' => '_login',
|
||||
'check_path' => '/login_check',
|
||||
'always_use_default_target_path' => false,
|
||||
'default_target_path' => '/',
|
||||
],
|
||||
'logout' => [
|
||||
'path' => '/logout',
|
||||
],
|
||||
'users' => $app->share(function () use ($app) {
|
||||
return $app['user.provider'];
|
||||
}),
|
||||
'remember_me' => [
|
||||
'key' => $app['token'],
|
||||
'path' => '/',
|
||||
'always_remember_me' => false,
|
||||
],
|
||||
|
||||
$firewall = [
|
||||
'security.firewalls' => [
|
||||
'default' => [
|
||||
'pattern' => '^/',
|
||||
'anonymous' => true,
|
||||
'form' => [
|
||||
'login_path' => '_login',
|
||||
'check_path' => '/login_check',
|
||||
'always_use_default_target_path' => false,
|
||||
'default_target_path' => '/',
|
||||
],
|
||||
'logout' => [
|
||||
'path' => '/logout',
|
||||
],
|
||||
'users' => $app->share(function () use ($app) {
|
||||
return $app['user.provider'];
|
||||
}),
|
||||
'remember_me' => [
|
||||
'key' => $app['token'],
|
||||
'path' => '/',
|
||||
'always_remember_me' => false,
|
||||
],
|
||||
],
|
||||
'security.access_rules' => [
|
||||
['^/[a-z]{2}/my.*$', 'ROLE_USER'],
|
||||
]
|
||||
]
|
||||
);
|
||||
],
|
||||
'security.access_rules' => [
|
||||
['^/[a-z]{2}/my.*$', 'ROLE_USER'],
|
||||
],
|
||||
];
|
||||
|
||||
if ($securitySettings['login_required_to_edit_gist'] || $securitySettings['login_required_to_view_gist'] || $securitySettings['login_required_to_view_embeded_gist']) {
|
||||
$exceptedUriPattern = ['login', 'register'];
|
||||
|
||||
if ($securitySettings['login_required_to_view_gist'] === true) {
|
||||
$firewall['security.access_rules'][] = ['^/[a-z]{2}/view.*$', 'ROLE_USER'];
|
||||
$firewall['security.access_rules'][] = ['^/[a-z]{2}/revs.*$', 'ROLE_USER'];
|
||||
} else {
|
||||
$exceptedUriPattern[] = 'view';
|
||||
$exceptedUriPattern[] = 'revs';
|
||||
}
|
||||
|
||||
if ($securitySettings['login_required_to_view_embeded_gist'] === true) {
|
||||
$firewall['security.access_rules'][] = ['^/[a-z]{2}/embed.*$', 'ROLE_USER'];
|
||||
} else {
|
||||
$exceptedUriPattern[] = 'embed';
|
||||
}
|
||||
|
||||
if ($securitySettings['login_required_to_edit_gist'] === true) {
|
||||
$firewall['security.access_rules'][] = ['^/[a-z]{2}/(?!('.implode('|', $exceptedUriPattern).')).*$', 'ROLE_USER'];
|
||||
}
|
||||
}
|
||||
|
||||
$app->register(new SecurityServiceProvider(), $firewall);
|
||||
$app->register(new SessionServiceProvider());
|
||||
$app->register(new RememberMeServiceProvider());
|
||||
|
||||
|
|
19
app/config/config.yml.dist
Normal file
19
app/config/config.yml.dist
Normal file
|
@ -0,0 +1,19 @@
|
|||
security:
|
||||
token: ThisTokenIsNotSoSecretChangeIt
|
||||
enable_registration: true
|
||||
enable_login: true
|
||||
login_required_to_edit_gist: false
|
||||
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:
|
||||
path: /usr/bin/git
|
||||
theme:
|
||||
name: dark
|
|
@ -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}
|
||||
|
|
|
@ -2,15 +2,21 @@
|
|||
<?php
|
||||
|
||||
use Gist\Command\CreateCommand;
|
||||
use Gist\Command\ListCommand;
|
||||
use Gist\Command\UpdateCommand;
|
||||
use Gist\Command\StatsCommand;
|
||||
use Gist\Command\DeleteCommand;
|
||||
use Gist\Command\UserCreateCommand;
|
||||
use Gist\Command\Migration\UpgradeTo1p4p1Command;
|
||||
|
||||
$app = require __DIR__.'/bootstrap.php';
|
||||
|
||||
$app['console']->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());
|
||||
|
||||
$app['console']->run();
|
||||
|
|
107
app/locales/cn.yml
Normal file
107
app/locales/cn.yml
Normal file
|
@ -0,0 +1,107 @@
|
|||
app:
|
||||
title: '#!GIST'
|
||||
title_prefix: '#!GIST - '
|
||||
|
||||
menu:
|
||||
home:
|
||||
title: '首页'
|
||||
about:
|
||||
title: '关于'
|
||||
my:
|
||||
login:
|
||||
title: '登陆'
|
||||
logout:
|
||||
title: '注销'
|
||||
register:
|
||||
title: '注册'
|
||||
my:
|
||||
title: '账号'
|
||||
|
||||
my:
|
||||
title: '我的 Gist'
|
||||
nothing: '这家伙很懒,暂时没有内容'
|
||||
api:
|
||||
title: 'API'
|
||||
warning: 'Keep it <strong>secret!</strong>'
|
||||
form:
|
||||
generate: 'Regenerate'
|
||||
|
||||
gist:
|
||||
untitled: '未命名'
|
||||
404:
|
||||
title: '哇!'
|
||||
message: '这个 gist 未找到...'
|
||||
action:
|
||||
view: '查看'
|
||||
history: '历史版本'
|
||||
raw: '源文件'
|
||||
download: '下载'
|
||||
clone: '克隆'
|
||||
embed: '引用'
|
||||
add: '新建'
|
||||
|
||||
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>'
|
||||
|
||||
login:
|
||||
login:
|
||||
title: '登陆'
|
||||
invalid: '用户名或密码错误'
|
||||
form:
|
||||
username:
|
||||
placeholder: '用户名'
|
||||
password:
|
||||
placeholder: '密码'
|
||||
remember_me:
|
||||
label: '记住我'
|
||||
register:
|
||||
title: '注册新账号'
|
||||
already_exists: '改用户名已被使用'
|
||||
registred: '恭喜您,您的账号已经注册成功!'
|
||||
form:
|
||||
username:
|
||||
placeholder: '用户名'
|
||||
current_password:
|
||||
placeholder: '当前密码'
|
||||
password:
|
||||
placeholder: '新密码'
|
||||
|
||||
form:
|
||||
error:
|
||||
password: '密码错误'
|
||||
not_blank: '该选项不可为空'
|
||||
title:
|
||||
placeholder: '标题'
|
||||
cipher:
|
||||
label: '加密: %value%'
|
||||
choice:
|
||||
anyway: '随意'
|
||||
yes: '是'
|
||||
no: '否'
|
||||
submit: '提交'
|
||||
filter: '过滤器'
|
||||
confirm: '确认此次操作?'
|
||||
success:
|
||||
password: '密码已更新'
|
||||
gist: 'Gist 已移除'
|
||||
type:
|
||||
label: '语言: %value%'
|
||||
choice:
|
||||
all: 'All'
|
||||
html: 'HTML'
|
||||
xml: 'XML'
|
||||
css: 'CSS'
|
||||
javascript: 'JAVASCRIPT'
|
||||
php: 'PHP'
|
||||
sql: 'SQL'
|
||||
yaml: 'YAML'
|
||||
perl: 'PERL'
|
||||
c: 'C/C++'
|
||||
asp: 'ASP'
|
||||
python: 'PYTHON'
|
||||
bash: 'BASH'
|
||||
actionscript3: 'ACTION SCRIPT'
|
||||
text: 'TEXT'
|
107
app/locales/de.yml
Normal file
107
app/locales/de.yml
Normal file
|
@ -0,0 +1,107 @@
|
|||
app:
|
||||
title: '#!GIST'
|
||||
title_prefix: '#!GIST - '
|
||||
|
||||
menu:
|
||||
home:
|
||||
title: 'Home'
|
||||
about:
|
||||
title: 'Über uns'
|
||||
my:
|
||||
login:
|
||||
title: 'Anmelden'
|
||||
logout:
|
||||
title: 'Abmelden'
|
||||
register:
|
||||
title: 'Konto öffnen'
|
||||
my:
|
||||
title: 'Mein Konto'
|
||||
|
||||
my:
|
||||
title: 'Meine Gists'
|
||||
nothing: 'Nichts zu finden (momentan)!'
|
||||
api:
|
||||
title: 'API'
|
||||
warning: 'Keep it <strong>secret!</strong>'
|
||||
form:
|
||||
generate: 'Regenerate'
|
||||
|
||||
gist:
|
||||
untitled: 'Ohne Titel'
|
||||
404:
|
||||
title: 'Tja...'
|
||||
message: 'Dieser Gist existiert nicht.'
|
||||
action:
|
||||
view: 'Anzeigen'
|
||||
history: 'Commit(s)'
|
||||
raw: 'RAW'
|
||||
download: 'Herunterladen'
|
||||
clone: 'Klonen'
|
||||
embed: 'Einfügen'
|
||||
add: 'Hinzufügen'
|
||||
|
||||
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>'
|
||||
|
||||
login:
|
||||
login:
|
||||
title: 'Login'
|
||||
invalid: 'Anmeldung festgeschlagen.'
|
||||
form:
|
||||
username:
|
||||
placeholder: 'Benutzername'
|
||||
password:
|
||||
placeholder: 'Passwort'
|
||||
remember_me:
|
||||
label: 'Remember me'
|
||||
register:
|
||||
title: 'New account'
|
||||
already_exists: 'This username is already registred!'
|
||||
registred: 'Congratulations, your account is created!'
|
||||
form:
|
||||
username:
|
||||
placeholder: 'Username'
|
||||
current_password:
|
||||
placeholder: 'Aktuelles Passwort'
|
||||
password:
|
||||
placeholder: 'Password'
|
||||
|
||||
form:
|
||||
error:
|
||||
password: 'Ungültiges Passwort.'
|
||||
not_blank: 'Dieser Wert darf nicht leer sein.'
|
||||
title:
|
||||
placeholder: 'Titel'
|
||||
cipher:
|
||||
label: 'Cipher: %value%'
|
||||
choice:
|
||||
anyway: 'Anyway'
|
||||
yes: 'Yes'
|
||||
no: 'No'
|
||||
submit: 'Senden'
|
||||
filter: 'Filter'
|
||||
confirm: 'Bestätigen Sie?'
|
||||
success:
|
||||
password: 'Passwort aktualisiert.'
|
||||
gist: 'Gist removed.'
|
||||
type:
|
||||
label: 'Language: %value%'
|
||||
choice:
|
||||
all: 'All'
|
||||
html: 'HTML'
|
||||
xml: 'XML'
|
||||
css: 'CSS'
|
||||
javascript: 'JAVASCRIPT'
|
||||
php: 'PHP'
|
||||
sql: 'SQL'
|
||||
yaml: 'YAML'
|
||||
perl: 'PERL'
|
||||
c: 'C/C++'
|
||||
asp: 'ASP'
|
||||
python: 'PYTHON'
|
||||
bash: 'BASH'
|
||||
actionscript3: 'ACTION SCRIPT'
|
||||
text: 'TEXT'
|
|
@ -20,6 +20,12 @@ app:
|
|||
my:
|
||||
title: 'My gists'
|
||||
nothing: 'Nothing yet!'
|
||||
api:
|
||||
title: 'API'
|
||||
warning: 'Keep it <strong>secret!</strong>'
|
||||
form:
|
||||
generate: 'Regenerate'
|
||||
|
||||
|
||||
gist:
|
||||
untitled: 'Untitled'
|
||||
|
@ -32,7 +38,7 @@ gist:
|
|||
raw: 'RAW'
|
||||
download: 'Download'
|
||||
clone: 'Clone'
|
||||
embed: 'Embed:'
|
||||
embed: 'Embed'
|
||||
add: 'New'
|
||||
|
||||
date:
|
||||
|
@ -59,16 +65,18 @@ login:
|
|||
form:
|
||||
username:
|
||||
placeholder: 'Username'
|
||||
current_password:
|
||||
placeholder: 'Current password'
|
||||
password:
|
||||
placeholder: 'Password'
|
||||
|
||||
form:
|
||||
error:
|
||||
password: 'Invalid password.'
|
||||
not_blank: 'This value should not be blank bro!'
|
||||
title:
|
||||
placeholder: 'Title'
|
||||
cipher:
|
||||
alert: 'By enabling cipher, fork will be not possible.'
|
||||
label: 'Cipher: %value%'
|
||||
choice:
|
||||
anyway: 'Anyway'
|
||||
|
@ -76,7 +84,9 @@ form:
|
|||
no: 'No'
|
||||
submit: 'Send'
|
||||
filter: 'Filter'
|
||||
confirm: 'Do you confirm?'
|
||||
success:
|
||||
password: 'Password updated.'
|
||||
gist: 'Gist removed.'
|
||||
type:
|
||||
label: 'Language: %value%'
|
||||
|
|
107
app/locales/es.yml
Normal file
107
app/locales/es.yml
Normal file
|
@ -0,0 +1,107 @@
|
|||
app:
|
||||
title: '#!GIST'
|
||||
title_prefix: '#!GIST - '
|
||||
|
||||
menu:
|
||||
home:
|
||||
title: 'Inicio'
|
||||
about:
|
||||
title: 'A propósito'
|
||||
my:
|
||||
login:
|
||||
title: 'Conexión'
|
||||
logout:
|
||||
title: 'Desconexión'
|
||||
register:
|
||||
title: 'Apuntarse'
|
||||
my:
|
||||
title: 'Mi cuenta'
|
||||
|
||||
my:
|
||||
title: 'Mis Gists'
|
||||
nothing: 'Nada por ahora.'
|
||||
api:
|
||||
title: 'API'
|
||||
warning: 'Keep it <strong>secret!</strong>'
|
||||
form:
|
||||
generate: 'Regenerate'
|
||||
|
||||
gist:
|
||||
untitled: 'Sin título'
|
||||
404:
|
||||
title: '¡Uy!'
|
||||
message: "Este gist no existe."
|
||||
action:
|
||||
view: 'Visualizar'
|
||||
history: 'Confirmacion(es)'
|
||||
raw: 'RAW'
|
||||
download: 'Descargar'
|
||||
clone: 'Clonar'
|
||||
embed: 'Insertar'
|
||||
add: 'Añadir'
|
||||
|
||||
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>'
|
||||
|
||||
login:
|
||||
login:
|
||||
title: 'Identificación'
|
||||
invalid: 'Nombre de usuario o contraseña incorrecta'
|
||||
form:
|
||||
username:
|
||||
placeholder: 'Nombre de usuario'
|
||||
password:
|
||||
placeholder: 'Contraseña'
|
||||
remember_me:
|
||||
label: 'Recordar mis datos'
|
||||
register:
|
||||
title: 'Nueva cuenta'
|
||||
already_exists: 'Este nombre de usuario ya está en uso'
|
||||
registred: '¡ Enhorabuena, su cuenta fue creada !'
|
||||
form:
|
||||
username:
|
||||
placeholder: 'Nombre de usuario'
|
||||
current_password:
|
||||
placeholder: 'Contraseña actual'
|
||||
password:
|
||||
placeholder: 'Contraseña'
|
||||
|
||||
form:
|
||||
error:
|
||||
password: 'Contraseña no válida.'
|
||||
not_blank: 'Se necesita introducir este dato.'
|
||||
title:
|
||||
placeholder: 'Título'
|
||||
cipher:
|
||||
label: 'Cifrar : %value%'
|
||||
choice:
|
||||
anyway: 'Da igual'
|
||||
yes: 'Sí'
|
||||
no: 'No'
|
||||
submit: 'Enviar'
|
||||
filter: 'Filtrar'
|
||||
confirm: '¿Confirmas?'
|
||||
success:
|
||||
password: 'Contraseña cambiada.'
|
||||
gist: 'Su Gist ha sido eliminado.'
|
||||
type:
|
||||
label: 'Lenguaje : %value%'
|
||||
choice:
|
||||
all: 'Todos'
|
||||
html: 'HTML'
|
||||
xml: 'XML'
|
||||
css: 'CSS'
|
||||
javascript: 'JAVASCRIPT'
|
||||
php: 'PHP'
|
||||
sql: 'SQL'
|
||||
yaml: 'YAML'
|
||||
perl: 'PERL'
|
||||
c: 'C/C++'
|
||||
asp: 'ASP'
|
||||
python: 'PYTHON'
|
||||
bash: 'BASH'
|
||||
actionscript3: 'ACTION SCRIPT'
|
||||
text: 'TEXTE'
|
|
@ -20,6 +20,11 @@ app:
|
|||
my:
|
||||
title: 'Mes Gists'
|
||||
nothing: 'Rien pour le moment !'
|
||||
api:
|
||||
title: 'API'
|
||||
warning: 'Gardez-la <strong>secrète !</strong>'
|
||||
form:
|
||||
generate: 'Regénérer'
|
||||
|
||||
gist:
|
||||
untitled: 'Sans titre'
|
||||
|
@ -32,7 +37,7 @@ gist:
|
|||
raw: 'RAW'
|
||||
download: 'Télécharger'
|
||||
clone: 'Clôner'
|
||||
embed: 'Insérer : '
|
||||
embed: 'Insérer'
|
||||
add: 'Ajouter'
|
||||
|
||||
date:
|
||||
|
@ -59,17 +64,18 @@ login:
|
|||
form:
|
||||
username:
|
||||
placeholder: 'Nom d''utilisateur'
|
||||
current_password:
|
||||
placeholder: 'Mot de passe courrant'
|
||||
password:
|
||||
placeholder: 'Mot de passe'
|
||||
|
||||
|
||||
form:
|
||||
error:
|
||||
password: 'Mot de passe invalide.'
|
||||
not_blank: 'Vous devez saisir cette donnée.'
|
||||
title:
|
||||
placeholder: 'Titre'
|
||||
cipher:
|
||||
alert: 'En activant le chiffrement, le fork deviendra impossible.'
|
||||
label: 'Chiffrer : %value%'
|
||||
choice:
|
||||
anyway: 'Peu importe'
|
||||
|
@ -77,7 +83,9 @@ form:
|
|||
no: 'Non'
|
||||
submit: 'Envoyer'
|
||||
filter: 'Filtrer'
|
||||
confirm: 'Confirmez-vous ?'
|
||||
success:
|
||||
password: 'Mot de passe modifié.'
|
||||
gist: 'Votre Gist a bien a été supprimé.'
|
||||
type:
|
||||
label: 'Langage : %value%'
|
||||
|
|
108
app/locales/pl.yml
Normal file
108
app/locales/pl.yml
Normal file
|
@ -0,0 +1,108 @@
|
|||
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 <strong>sekrecie!</strong>'
|
||||
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: '<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>'
|
||||
|
||||
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'
|
||||
perl: 'PERL'
|
||||
c: 'C/C++'
|
||||
asp: 'ASP'
|
||||
python: 'PYTHON'
|
||||
bash: 'BASH'
|
||||
actionscript3: 'ACTION SCRIPT'
|
||||
text: 'TEXT'
|
|
@ -11,11 +11,12 @@
|
|||
"GIT"
|
||||
],
|
||||
"license": "LGPL",
|
||||
"homepage": "https://gitlab.deblan.org/deblan/gist.deblan.org",
|
||||
"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"
|
||||
"iframe-resizer": "2.8.6",
|
||||
"jsdiff": "~2.2.2",
|
||||
"Prism": "~1.8.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"require": {
|
||||
"geshi/geshi": "dev-master",
|
||||
"silex/silex": "1.3.x-dev",
|
||||
"symfony/yaml": "~2.6",
|
||||
"symfony/twig-bridge": "~2.6",
|
||||
|
|
|
@ -7,7 +7,6 @@ propel:
|
|||
dsn: "mysql:host=localhost;dbname=gist"
|
||||
user: root
|
||||
password: root
|
||||
attributes:
|
||||
settings:
|
||||
charset: utf8
|
||||
queries:
|
||||
|
|
|
@ -5,18 +5,60 @@ namespace Gist\Api;
|
|||
use GuzzleHttp\Client as BaseClient;
|
||||
|
||||
/**
|
||||
* Class Client
|
||||
* Class Client.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class Client extends BaseClient
|
||||
{
|
||||
/**
|
||||
* URI of creation.
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const CREATE = '/en/api/create';
|
||||
|
||||
/**
|
||||
* URI of update.
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const UPDATE = '/en/api/update/{gist}';
|
||||
|
||||
/**
|
||||
* URI of delete.
|
||||
*
|
||||
* @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(
|
||||
|
@ -34,11 +76,19 @@ class Client extends BaseClient
|
|||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clones and update a gist.
|
||||
*
|
||||
* @param string $gist Gist's ID
|
||||
* @param string $content The content
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function update($gist, $content)
|
||||
{
|
||||
$response = $this->post(
|
||||
str_replace('{gist}', $gist, self::UPDATE),
|
||||
str_replace('{gist}', $gist, $this->mergeApiKey(self::LIST)),
|
||||
array(
|
||||
'form_params' => array(
|
||||
'form' => array(
|
||||
|
@ -54,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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,16 +5,23 @@ namespace Gist;
|
|||
use Silex\Application as SilexApplication;
|
||||
|
||||
/**
|
||||
* @deprecated The static version should be avoided, use DI instead.
|
||||
* @deprecated The static version should be avoided, use DI instead
|
||||
*/
|
||||
class Application extends SilexApplication
|
||||
{
|
||||
/**
|
||||
* Creates an instance of Application.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @return Application
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
static $app;
|
||||
|
||||
if (null === $app) {
|
||||
$app = new static;
|
||||
$app = new static();
|
||||
}
|
||||
|
||||
return $app;
|
||||
|
|
|
@ -7,9 +7,19 @@ 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.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class CreateCommand extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$types = implode(', ', $this->getTypes());
|
||||
|
@ -19,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(<<<EOF
|
||||
Provides a client to create a gist using the API.
|
||||
|
||||
|
@ -35,23 +46,28 @@ Arguments:
|
|||
Options:
|
||||
<info>--title</info>, <info>-t</info>
|
||||
Defines a title
|
||||
|
||||
<info>--show-id</info>, <info>-i</info>
|
||||
Display only the Id of the gist
|
||||
|
||||
<info>--show-url</info>, <info>-u</info>
|
||||
Display only the url of the gist
|
||||
<info>--id</info>, <info>-i</info>
|
||||
Display only the id of the gist
|
||||
|
||||
<info>--all</info>, <info>-a</info>
|
||||
Display all the response
|
||||
|
||||
<info>--json</info>, <info>-j</info>
|
||||
Format the response to json
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
//$output->writeln(sprintf('<comment>%s</comment> 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');
|
||||
|
@ -83,21 +99,24 @@ EOF
|
|||
|
||||
$gist = $this->getSilexApplication()['api_client']->create($title, $type, $content);
|
||||
|
||||
if ($input->getOption('show-url')) {
|
||||
$output->writeln($gist['url']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($input->getOption('show-id')) {
|
||||
$output->writeln($gist['gist']['Id']);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$output->writeln(json_encode($gist));
|
||||
$output->writeln($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTypes()
|
||||
{
|
||||
$types = array(
|
||||
|
|
48
src/Gist/Command/DeleteCommand.php
Normal file
48
src/Gist/Command/DeleteCommand.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Gist\Command;
|
||||
|
||||
use Knp\Command\Command;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* class DeleteCommand.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
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:
|
||||
<info>--gist</info>
|
||||
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' : '<error>An error occured.</error>');
|
||||
}
|
||||
}
|
59
src/Gist/Command/ListCommand.php
Normal file
59
src/Gist/Command/ListCommand.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Gist\Command;
|
||||
|
||||
use Knp\Command\Command;
|
||||
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;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* class ListCommand.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
44
src/Gist/Command/Migration/UpgradeTo1p4p1Command.php
Normal file
44
src/Gist/Command/Migration/UpgradeTo1p4p1Command.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Gist\Command\Migration;
|
||||
|
||||
use Knp\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Gist\Model\GistQuery;
|
||||
|
||||
/**
|
||||
* class UpgradeTo1p4p1Command.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class UpgradeTo1p4p1Command extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('migrate:to:v1.4.1')
|
||||
->setDescription('Migrates database entries to >= v1.4.1')
|
||||
->setHelp('The <info>%command.name%</info> migrates database entries to >= v1.4.1');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$app = $this->getSilexApplication();
|
||||
$gists = GistQuery::create()
|
||||
->filterByCommits(0)
|
||||
->find();
|
||||
|
||||
foreach ($gists as $gist) {
|
||||
$commits = $app['gist']->getNumberOfCommits($gist);
|
||||
$gist->setCommits($commits);
|
||||
$gist->save();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,8 +10,16 @@ use Symfony\Component\Console\Input\InputOption;
|
|||
use Symfony\Component\Console\Helper\Table;
|
||||
use GitWrapper\GitException;
|
||||
|
||||
/**
|
||||
* class StatsCommand;
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class StatsCommand extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
|
@ -23,6 +31,9 @@ EOF
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$gistService = $this->getSilexApplication()['gist'];
|
||||
|
@ -41,18 +52,13 @@ EOF
|
|||
$withEncryption[$gist->getType()] = 0;
|
||||
$commits[$gist->getType()] = 0;
|
||||
}
|
||||
|
||||
|
||||
if ($gist->getCipher()) {
|
||||
$withEncryption[$gist->getType()]++;
|
||||
}
|
||||
|
||||
|
||||
$languages[$gist->getType()]++;
|
||||
try {
|
||||
$count = count($gistService->getHistory($gist));
|
||||
$commits[$gist->getType()] += $count;
|
||||
} catch(GitException $e) {
|
||||
|
||||
}
|
||||
$commits[$gist->getType()] += $gist->getCommits();
|
||||
}
|
||||
|
||||
$output->writeln(['<comment>Gists statistics</comment>', '']);
|
||||
|
@ -62,9 +68,9 @@ EOF
|
|||
->setHeaders(array('Without encryption', 'With encryption', 'Commits', 'Total'))
|
||||
->setRows(array(
|
||||
array(
|
||||
$total - $v = array_sum($withEncryption),
|
||||
$v,
|
||||
array_sum($commits),
|
||||
$total - $v = array_sum($withEncryption),
|
||||
$v,
|
||||
array_sum($commits),
|
||||
$total
|
||||
),
|
||||
))
|
||||
|
@ -79,9 +85,9 @@ EOF
|
|||
|
||||
$table->setHeaders(array(
|
||||
'Type',
|
||||
'Without encryption',
|
||||
'With encryption',
|
||||
'Commits',
|
||||
'Without encryption',
|
||||
'With encryption',
|
||||
'Commits',
|
||||
'Total',
|
||||
));
|
||||
|
||||
|
|
|
@ -7,9 +7,18 @@ 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.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class UpdateCommand extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$types = implode(', ', $this->getTypes());
|
||||
|
@ -18,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(<<<EOF
|
||||
Provides a client to create a gist using the API.
|
||||
|
||||
|
@ -35,21 +45,26 @@ Options:
|
|||
<info>--gist</info>
|
||||
Defines the Gist to update by using its Id or its File
|
||||
|
||||
<info>--show-id</info>, <info>-i</info>
|
||||
Display only the Id of the gist
|
||||
<info>--id</info>, <info>-i</info>
|
||||
Display only the id of the gist
|
||||
|
||||
<info>--show-url</info>, <info>-u</info>
|
||||
Display only the url of the gist
|
||||
<info>--all</info>, <info>-a</info>
|
||||
Display all the response
|
||||
|
||||
<info>--json</info>, <info>-j</info>
|
||||
Format the response to json
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
//$output->writeln(sprintf('<comment>%s</comment> bar.', 'test'));
|
||||
|
||||
$file = $input->getArgument('input');
|
||||
$gist = $input->getOption('gist');
|
||||
$json = $input->getOption('json');
|
||||
|
||||
if ($file === '-') {
|
||||
$content = file_get_contents('php://stdin');
|
||||
|
@ -75,21 +90,24 @@ EOF
|
|||
|
||||
$gist = $this->getSilexApplication()['api_client']->update($gist, $content);
|
||||
|
||||
if ($input->getOption('show-url')) {
|
||||
$output->writeln($gist['url']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($input->getOption('show-id')) {
|
||||
$output->writeln($gist['gist']['Id']);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$output->writeln(json_encode($gist));
|
||||
$output->writeln($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTypes()
|
||||
{
|
||||
$types = array(
|
||||
|
|
|
@ -7,16 +7,27 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||
use Knp\Command\Command;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
|
||||
/**
|
||||
* Class UserCreateCommand.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class UserCreateCommand extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('user:create')
|
||||
->setDescription('Create a user')
|
||||
->setHelp("");
|
||||
->setHelp('');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$helper = $this->getHelper('question');
|
||||
|
@ -28,7 +39,7 @@ class UserCreateCommand extends Command
|
|||
while (trim($username) === '') {
|
||||
$question = new Question('Username: ', '');
|
||||
$username = $helper->ask($input, $output, $question);
|
||||
|
||||
|
||||
if ($userProvider->userExists($username)) {
|
||||
$output->writeln('<error>This username is already used.</error>');
|
||||
$username = '';
|
||||
|
|
|
@ -8,17 +8,91 @@ 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
|
||||
* Class ApiController.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
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.');
|
||||
}
|
||||
|
@ -35,30 +109,51 @@ 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.');
|
||||
}
|
||||
|
@ -90,21 +185,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 = [
|
||||
|
@ -115,6 +277,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 = [
|
||||
|
@ -124,4 +293,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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,23 +9,42 @@ use Gist\Model\GistQuery;
|
|||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Class Controller
|
||||
* Class Controller.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class Controller
|
||||
abstract class Controller
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* __construct.
|
||||
*
|
||||
* @param Application $app
|
||||
*/
|
||||
public function __construct(Application $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the application.
|
||||
*
|
||||
* @return Application
|
||||
*/
|
||||
public function getApp()
|
||||
{
|
||||
return $this->app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a 404 response.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function notFoundResponse()
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
@ -38,7 +57,16 @@ class Controller
|
|||
404
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the default options of a gist view.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $gist Gist's ID
|
||||
* @param string $commit The commit ID
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getViewOptions(Request $request, $gist, $commit)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
@ -63,10 +91,16 @@ class Controller
|
|||
'history' => $history,
|
||||
'commit' => $commit,
|
||||
'raw_content' => $content,
|
||||
'content' => $app['gist']->highlight($gist->getGeshiType(), $content),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content of the gist depending of the commit and its history.
|
||||
*
|
||||
* @param Gist $gist
|
||||
* @param mixed $commit
|
||||
* @param mixed $history
|
||||
*/
|
||||
protected function getContentByCommit(Gist $gist, &$commit, $history)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
@ -90,10 +124,21 @@ class Controller
|
|||
return $app['gist']->getContent($gist, $commit);
|
||||
}
|
||||
|
||||
public function getUser()
|
||||
/**
|
||||
* Returns the connected user.
|
||||
*
|
||||
* @param Request $request An API request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUser(Request $request = null)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
||||
if (!empty($request)) {
|
||||
|
||||
}
|
||||
|
||||
$securityContext = $app['security.token_storage'];
|
||||
$securityToken = $securityContext->getToken();
|
||||
|
||||
|
@ -110,6 +155,14 @@ class Controller
|
|||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a view.
|
||||
*
|
||||
* @param string $template
|
||||
* @param array $params
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render($template, array $params = null)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
|
|
@ -8,13 +8,22 @@ use Gist\Form\CloneGistForm;
|
|||
use Gist\Model\Gist;
|
||||
use GitWrapper\GitException;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\Form\FormError;
|
||||
|
||||
/**
|
||||
* Class EditController
|
||||
* Class EditController.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class EditController extends Controller
|
||||
{
|
||||
/**
|
||||
* Creation page.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function createAction(Request $request)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
@ -29,9 +38,19 @@ 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +63,13 @@ class EditController extends Controller
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cloning page.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function cloneAction(Request $request, $gist, $commit)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
|
|
@ -7,20 +7,26 @@ use Gist\Model\User;
|
|||
use Gist\Form\UserRegisterForm;
|
||||
use Gist\Form\UserLoginForm;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\SecurityContext;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
/**
|
||||
* Class LoginController
|
||||
* Class LoginController.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class LoginController extends Controller
|
||||
{
|
||||
/**
|
||||
* Registration page.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function registerAction(Request $request)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
||||
if (false === $app['enable_registration']) {
|
||||
if (false === $app['settings']['security']['enable_registration']) {
|
||||
return new Response('', 403);
|
||||
}
|
||||
|
||||
|
@ -54,18 +60,25 @@ class LoginController extends Controller
|
|||
return $this->render(
|
||||
'Login/register.html.twig',
|
||||
[
|
||||
'form' => $form->createView(),
|
||||
'error' => isset($error) ? $error : '',
|
||||
'form' => $form->createView(),
|
||||
'error' => isset($error) ? $error : '',
|
||||
'success' => isset($success) ? $success : '',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Login page.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function loginAction(Request $request)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
||||
if (false === $app['enable_login']) {
|
||||
if (false === $app['settings']['security']['enable_login']) {
|
||||
return new Response('', 403);
|
||||
}
|
||||
|
||||
|
@ -87,7 +100,7 @@ class LoginController extends Controller
|
|||
return $this->render(
|
||||
'Login/login.html.twig',
|
||||
[
|
||||
'form' => $form->createView(),
|
||||
'form' => $form->createView(),
|
||||
'error' => isset($error) ? $error : '',
|
||||
]
|
||||
);
|
||||
|
|
|
@ -3,26 +3,36 @@
|
|||
namespace Gist\Controller;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Gist\Model\GistQuery;
|
||||
use Gist\Form\DeleteGistForm;
|
||||
use Gist\Form\FilterGistForm;
|
||||
use Gist\Form\UserPasswordForm;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
/**
|
||||
* Class MyController
|
||||
* Class MyController.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class MyController extends Controller
|
||||
{
|
||||
/**
|
||||
* "My" page.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param int $page
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function myAction(Request $request, $page)
|
||||
{
|
||||
$page = (int) $page;
|
||||
$page = (int) $page;
|
||||
$app = $this->getApp();
|
||||
|
||||
$deleteForm = new DeleteGistForm($app['form.factory'], $app['translator']);
|
||||
$deleteForm = $deleteForm->build()->getForm();
|
||||
|
||||
|
||||
$options = array(
|
||||
'type' => 'all',
|
||||
'type' => 'all',
|
||||
'cipher' => 'anyway',
|
||||
);
|
||||
|
||||
|
@ -35,6 +45,9 @@ class MyController extends Controller
|
|||
|
||||
$filterForm = $filterForm->build()->getForm();
|
||||
|
||||
$passwordForm = new UserPasswordForm($app['form.factory'], $app['translator']);
|
||||
$passwordForm = $passwordForm->build()->getForm();
|
||||
|
||||
if ($request->query->has('filter')) {
|
||||
$filterForm->submit($request);
|
||||
|
||||
|
@ -42,21 +55,34 @@ class MyController extends Controller
|
|||
$options = $filterForm->getData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$gists = $this->getUser()->getGistsPager($page, $options);
|
||||
|
||||
if ($request->isMethod('post')) {
|
||||
$form->submit($request);
|
||||
$apiKey = $this->getUser()->getApiKey();
|
||||
|
||||
if ($form->isValid()) {
|
||||
$gist = $app['gist']->create(new Gist(), $form->getData(), $this->getUser());
|
||||
}
|
||||
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->submit($request);
|
||||
$deleteForm->handleRequest($request);
|
||||
$passwordForm->handleRequest($request);
|
||||
|
||||
if ($deleteForm->isValid()) {
|
||||
if ($deleteForm->isSubmitted() && $deleteForm->isValid()) {
|
||||
$id = (int) $deleteForm->getData()['id'];
|
||||
|
||||
foreach ($gists as $gist) {
|
||||
|
@ -67,16 +93,42 @@ class MyController extends Controller
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($passwordForm->isSubmitted() && $passwordForm->isValid()) {
|
||||
$currentPassword = $passwordForm->getData()['currentPassword'];
|
||||
$newPassword = $passwordForm->getData()['newPassword'];
|
||||
$passwordUpdated = 0;
|
||||
|
||||
if ($app['user.provider']->isCurrentUserPassword($this->getUser(), $currentPassword)) {
|
||||
$app['user.provider']->updateUserPassword(
|
||||
$this->getUser(),
|
||||
$newPassword
|
||||
);
|
||||
|
||||
$passwordUpdated = 1;
|
||||
}
|
||||
|
||||
return new RedirectResponse(
|
||||
$app['url_generator']->generate(
|
||||
'my',
|
||||
[
|
||||
'passwordUpdated' => $passwordUpdated,
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
'My/my.html.twig',
|
||||
array(
|
||||
'gists' => $gists,
|
||||
'page' => $page,
|
||||
'deleteForm' => $deleteForm->createView(),
|
||||
'filterForm' => $filterForm->createView(),
|
||||
'deleted' => !empty($deleted),
|
||||
'gists' => $gists,
|
||||
'page' => $page,
|
||||
'apiKey' => $apiKey,
|
||||
'deleteForm' => $deleteForm->createView(),
|
||||
'filterForm' => $filterForm->createView(),
|
||||
'passwordForm' => $passwordForm->createView(),
|
||||
'deleted' => !empty($deleted),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,11 +9,21 @@ use Gist\Model\Gist;
|
|||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Class ViewController
|
||||
* Class ViewController.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class ViewController extends Controller
|
||||
{
|
||||
/**
|
||||
* View action.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $gist Gist's ID
|
||||
* @param string $commit The commit
|
||||
*
|
||||
* @return string|Response
|
||||
*/
|
||||
public function viewAction(Request $request, $gist, $commit)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
@ -27,6 +37,15 @@ class ViewController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Embed action.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $gist Gist's ID
|
||||
* @param string $commit The commit
|
||||
*
|
||||
* @return string|Response
|
||||
*/
|
||||
public function embedAction(Request $request, $gist, $commit)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
@ -40,6 +59,15 @@ class ViewController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JS embed action.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $gist Gist's ID
|
||||
* @param string $commit The commit
|
||||
*
|
||||
* @return string|Response
|
||||
*/
|
||||
public function embedJsAction(Request $request, $gist, $commit)
|
||||
{
|
||||
$viewOptions = $this->getViewOptions($request, $gist, $commit);
|
||||
|
@ -53,6 +81,15 @@ class ViewController extends Controller
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw action.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $gist Gist's ID
|
||||
* @param string $commit The commit
|
||||
*
|
||||
* @return string|Response
|
||||
*/
|
||||
public function rawAction(Request $request, $gist, $commit)
|
||||
{
|
||||
$viewOptions = $this->getViewOptions($request, $gist, $commit);
|
||||
|
@ -70,6 +107,15 @@ class ViewController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download action.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $gist Gist's ID
|
||||
* @param string $commit The commit
|
||||
*
|
||||
* @return string|Response
|
||||
*/
|
||||
public function downloadAction(Request $request, $gist, $commit)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
@ -85,7 +131,7 @@ 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',
|
||||
)
|
||||
);
|
||||
|
@ -94,6 +140,14 @@ class ViewController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revisions action.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $gist Gist's ID
|
||||
*
|
||||
* @return string|Response
|
||||
*/
|
||||
public function revisionsAction(Request $request, $gist)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
|
|
@ -3,17 +3,23 @@
|
|||
namespace Gist;
|
||||
|
||||
use Silex\ControllerResolver as BaseControllerResolver;
|
||||
use Gist\Application;
|
||||
|
||||
/**
|
||||
* Class DecoratorControllerResolver
|
||||
* Class DecoratorControllerResolver.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class ControllerResolver extends BaseControllerResolver
|
||||
{
|
||||
/**
|
||||
* Instanciates a controller.
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @return Gist\Controller
|
||||
*/
|
||||
protected function instantiateController($class)
|
||||
{
|
||||
return new $class($this->app);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,15 +6,34 @@ use Symfony\Component\Form\FormFactory;
|
|||
use Symfony\Component\Translation\Translator;
|
||||
|
||||
/**
|
||||
* Class AbstractForm
|
||||
* Class AbstractForm.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
abstract class AbstractForm
|
||||
{
|
||||
/**
|
||||
* The builder.
|
||||
*
|
||||
* @var Symfony\Component\Form\FormBuilder
|
||||
*/
|
||||
protected $builder;
|
||||
|
||||
/**
|
||||
* The translator.
|
||||
*
|
||||
* @var Translator
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
/**
|
||||
* __construct.
|
||||
*
|
||||
* @param FormFactory $formFactory
|
||||
* @param Translator $translator
|
||||
* @param mixed $data
|
||||
* @param array $formFactoryOptions
|
||||
*/
|
||||
public function __construct(FormFactory $formFactory, Translator $translator, $data = null, $formFactoryOptions = array())
|
||||
{
|
||||
$this->translator = $translator;
|
||||
|
@ -22,15 +41,32 @@ abstract class AbstractForm
|
|||
$this->builder = $formFactory->createNamedBuilder($this->getName(), 'form', $data, $formFactoryOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the form from the builder.
|
||||
*
|
||||
* @return Symfony\Component\Form\Form
|
||||
*/
|
||||
public function getForm()
|
||||
{
|
||||
return $this->builder->getForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the form's name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'form';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the form.
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return Symfony\Component\Form\FormBuilder
|
||||
*/
|
||||
abstract public function build(array $options = array());
|
||||
}
|
||||
|
|
|
@ -3,16 +3,22 @@
|
|||
namespace Gist\Form;
|
||||
|
||||
/**
|
||||
* Class ApiCreateGistForm
|
||||
* Class ApiCreateGistForm.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class ApiCreateGistForm extends CreateGistForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(array $options = array())
|
||||
{
|
||||
parent::build($options);
|
||||
|
||||
$this->builder->remove('cipher');
|
||||
$this->builder
|
||||
->remove('cipher')
|
||||
->remove('file');
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
|
|
|
@ -3,17 +3,22 @@
|
|||
namespace Gist\Form;
|
||||
|
||||
/**
|
||||
* Class ApiUpdateGistForm
|
||||
* Class ApiUpdateGistForm.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class ApiUpdateGistForm extends ApiCreateGistForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(array $options = array())
|
||||
{
|
||||
parent::build($options);
|
||||
|
||||
$this->builder
|
||||
->remove('title')
|
||||
->remove('file')
|
||||
->remove('type');
|
||||
|
||||
return $this->builder;
|
||||
|
|
|
@ -3,11 +3,15 @@
|
|||
namespace Gist\Form;
|
||||
|
||||
/**
|
||||
* Class CreateGistForm
|
||||
* Class CreateGistForm.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class CloneGistForm extends CreateGistForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(array $options = array())
|
||||
{
|
||||
parent::build($options);
|
||||
|
|
|
@ -5,11 +5,15 @@ namespace Gist\Form;
|
|||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* Class CreateGistForm
|
||||
* Class CreateGistForm.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class CreateGistForm extends AbstractForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(array $options = array())
|
||||
{
|
||||
$this->builder->add(
|
||||
|
@ -28,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(
|
||||
),
|
||||
)
|
||||
);
|
||||
|
@ -69,6 +82,11 @@ class CreateGistForm extends AbstractForm
|
|||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the types for generating the form.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTypes()
|
||||
{
|
||||
$types = array(
|
||||
|
@ -78,7 +96,7 @@ class CreateGistForm extends AbstractForm
|
|||
'php' => '',
|
||||
'sql' => '',
|
||||
'xml' => '',
|
||||
'yaml'=> '',
|
||||
'yaml' => '',
|
||||
'perl' => '',
|
||||
'c' => '',
|
||||
'asp' => '',
|
||||
|
|
|
@ -5,11 +5,15 @@ namespace Gist\Form;
|
|||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* Class DeleteGistForm
|
||||
* Class DeleteGistForm.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class DeleteGistForm extends AbstractForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(array $options = array())
|
||||
{
|
||||
$this->builder->add(
|
||||
|
@ -22,12 +26,15 @@ class DeleteGistForm extends AbstractForm
|
|||
),
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
$this->builder->setMethod('POST');
|
||||
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'delete';
|
||||
|
|
|
@ -5,11 +5,15 @@ namespace Gist\Form;
|
|||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* Class CreateGistForm
|
||||
* Class CreateGistForm.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class FilterGistForm extends AbstractForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(array $options = array())
|
||||
{
|
||||
$this->builder->add(
|
||||
|
@ -40,11 +44,28 @@ class FilterGistForm extends AbstractForm
|
|||
)
|
||||
);
|
||||
|
||||
$this->builder->add(
|
||||
'title',
|
||||
'text',
|
||||
array(
|
||||
'required' => false,
|
||||
'attr' => array(
|
||||
'placeholder' => $this->translator->trans('form.title.placeholder'),
|
||||
'class' => 'form-control',
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$this->builder->setMethod('GET');
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the types for generating the form.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTypes()
|
||||
{
|
||||
$types = array(
|
||||
|
@ -55,7 +76,7 @@ class FilterGistForm extends AbstractForm
|
|||
'php' => '',
|
||||
'sql' => '',
|
||||
'xml' => '',
|
||||
'yaml'=> '',
|
||||
'yaml' => '',
|
||||
'perl' => '',
|
||||
'c' => '',
|
||||
'asp' => '',
|
||||
|
@ -72,6 +93,9 @@ class FilterGistForm extends AbstractForm
|
|||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'filter';
|
||||
|
|
|
@ -5,11 +5,15 @@ namespace Gist\Form;
|
|||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* Class UserLoginForm
|
||||
* Class UserLoginForm.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class UserLoginForm extends AbstractForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(array $options = array())
|
||||
{
|
||||
$this->builder->add(
|
||||
|
@ -45,7 +49,7 @@ class UserLoginForm extends AbstractForm
|
|||
),
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
$this->builder->add(
|
||||
'_remember_me',
|
||||
'checkbox',
|
||||
|
@ -63,6 +67,9 @@ class UserLoginForm extends AbstractForm
|
|||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return '';
|
||||
|
|
65
src/Gist/Form/UserPasswordForm.php
Normal file
65
src/Gist/Form/UserPasswordForm.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace Gist\Form;
|
||||
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* Class UserPasswordForm.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class UserPasswordForm extends AbstractForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(array $options = array())
|
||||
{
|
||||
$this->builder->add(
|
||||
'currentPassword',
|
||||
'password',
|
||||
array(
|
||||
'required' => true,
|
||||
'attr' => array(
|
||||
'class' => 'form-control',
|
||||
'placeholder' => $this->translator->trans('login.register.form.current_password.placeholder'),
|
||||
),
|
||||
'trim' => false,
|
||||
'constraints' => array(
|
||||
new NotBlank(array(
|
||||
'message' => $this->translator->trans('form.error.not_blank'),
|
||||
)),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$this->builder->add(
|
||||
'newPassword',
|
||||
'password',
|
||||
array(
|
||||
'required' => true,
|
||||
'attr' => array(
|
||||
'class' => 'form-control',
|
||||
'placeholder' => $this->translator->trans('login.register.form.password.placeholder'),
|
||||
),
|
||||
'trim' => false,
|
||||
'constraints' => array(
|
||||
new NotBlank(array(
|
||||
'message' => $this->translator->trans('form.error.not_blank'),
|
||||
)),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'password';
|
||||
}
|
||||
}
|
|
@ -5,11 +5,15 @@ namespace Gist\Form;
|
|||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* Class UserRegisterForm
|
||||
* Class UserRegisterForm.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class UserRegisterForm extends AbstractForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(array $options = array())
|
||||
{
|
||||
$this->builder->add(
|
||||
|
|
|
@ -3,9 +3,22 @@
|
|||
namespace Gist\Model;
|
||||
|
||||
use Gist\Model\Base\Gist as BaseGist;
|
||||
use Propel\Runtime\Map\TableMap;
|
||||
|
||||
/**
|
||||
* Class Gist.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class Gist extends BaseGist
|
||||
{
|
||||
/**
|
||||
* Hydrates the gist with array data.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return Gist
|
||||
*/
|
||||
public function hydrateWith(array $data)
|
||||
{
|
||||
if (isset($data['title'])) {
|
||||
|
@ -21,25 +34,42 @@ class Gist extends BaseGist
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique filename.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateFilename()
|
||||
{
|
||||
$this->setFile(uniqid());
|
||||
}
|
||||
|
||||
public function getGeshiType()
|
||||
/**
|
||||
* Returns the type for highlighting.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHighlightType()
|
||||
{
|
||||
$data = array(
|
||||
'html' => 'xml',
|
||||
'asp' => 'aspnet',
|
||||
'actionscript3' => 'actionscript',
|
||||
);
|
||||
|
||||
return str_replace(array_keys($data), array_values($data), $this->getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extension depending of the type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTypeAsExtension()
|
||||
{
|
||||
$data = array(
|
||||
'javascript' => 'js',
|
||||
'yaml'=> 'yml',
|
||||
'yaml' => 'yml',
|
||||
'perl' => 'pl',
|
||||
'python' => 'py',
|
||||
'bash' => 'sh',
|
||||
|
@ -49,4 +79,35 @@ class Gist extends BaseGist
|
|||
|
||||
return str_replace(array_keys($data), array_values($data), $this->getType());
|
||||
}
|
||||
|
||||
/*
|
||||
* Increments the number of commits.
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$this->setCommits($this->getCommits() + 1);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,9 @@ namespace Gist\Model;
|
|||
use Gist\Model\Base\GistQuery as BaseGistQuery;
|
||||
|
||||
/**
|
||||
* Skeleton subclass for performing query and update operations on the 'gist' table.
|
||||
*
|
||||
*
|
||||
*
|
||||
* You should add additional methods to this class to meet the
|
||||
* application requirements. This class will only be generated as
|
||||
* long as it does not already exist in the output directory.
|
||||
* Class GistQuery.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class GistQuery extends BaseGistQuery
|
||||
{
|
||||
|
|
|
@ -7,17 +7,35 @@ use Symfony\Component\Security\Core\User\UserInterface;
|
|||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
/**
|
||||
* Class User.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class User extends BaseUser implements UserInterface
|
||||
{
|
||||
/**
|
||||
* Erases credentials.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function eraseCredentials()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns roles.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRoles()
|
||||
{
|
||||
return explode(',', parent::getRoles());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getGists(Criteria $criteria = null, ConnectionInterface $con = null)
|
||||
{
|
||||
if ($criteria === null) {
|
||||
|
@ -27,7 +45,16 @@ class User extends BaseUser implements UserInterface
|
|||
return parent::getGists($criteria, $con);
|
||||
}
|
||||
|
||||
public function getGistsPager($page, $options = array(), $maxPerPage = 10)
|
||||
/**
|
||||
* Generates a pager of the user's gists.
|
||||
*
|
||||
* @param int $page
|
||||
* @param array $options
|
||||
* @param int $maxPerPage
|
||||
*
|
||||
* @return Propel\Runtime\Util\PropelModelPager
|
||||
*/
|
||||
public function getGistsPager($page, $options = array(), $maxPerPage = 10)
|
||||
{
|
||||
$query = GistQuery::create()
|
||||
->filterByUser($this)
|
||||
|
@ -36,7 +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,
|
||||
|
|
|
@ -5,14 +5,9 @@ namespace Gist\Model;
|
|||
use Gist\Model\Base\UserQuery as BaseUserQuery;
|
||||
|
||||
/**
|
||||
* Skeleton subclass for performing query and update operations on the 'user' table.
|
||||
*
|
||||
*
|
||||
*
|
||||
* You should add additional methods to this class to meet the
|
||||
* application requirements. This class will only be generated as
|
||||
* long as it does not already exist in the output directory.
|
||||
* Class UserQuery.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class UserQuery extends BaseUserQuery
|
||||
{
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
<column name="type" type="VARCHAR" size="30" required="true" />
|
||||
<column name="file" type="VARCHAR" size="30" required="true" />
|
||||
<column name="user_id" type="INTEGER" required="false" />
|
||||
|
||||
<column name="commits" type="INTEGER" required="true" defaultValue="0" />
|
||||
|
||||
<foreign-key foreignTable="user" onDelete="setnull" onUpdate="cascade">
|
||||
<reference local="user_id" foreign="id"/>
|
||||
</foreign-key>
|
||||
|
@ -21,6 +22,7 @@
|
|||
<column name="password" type="VARCHAR" size="255" required="true" />
|
||||
<column name="roles" type="VARCHAR" size="255" required="true" />
|
||||
<column name="salt" type="VARCHAR" size="64" required="true" />
|
||||
<column name="api_key" type="VARCHAR" size="32" required="true" />
|
||||
|
||||
<behavior name="timestampable"/>
|
||||
</table>
|
||||
|
|
|
@ -17,7 +17,14 @@
|
|||
</p>
|
||||
<p>
|
||||
{{ form_errors(form.content) }}
|
||||
{{ form_widget(form.content) }}
|
||||
|
||||
{% set class = 'form-control' %}
|
||||
|
||||
{% if gist.cipher %}
|
||||
{% set class = class ~ ' cipher-editor' %}
|
||||
{% endif %}
|
||||
|
||||
{{ form_widget(form.content, {attr: {class: class}}) }}
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" class="btn btn-primary" value="{{ 'form.submit'|trans }}">
|
||||
|
@ -29,3 +36,13 @@
|
|||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{% if gist.cipher %}
|
||||
<script>
|
||||
var cipherGistClone = true;
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
|
|
|
@ -4,17 +4,13 @@
|
|||
|
||||
{% block body %}
|
||||
<div class="row">
|
||||
<form action="{{ path('home') }}" method="post" id="main-form">
|
||||
<form action="{{ path('home') }}" method="post" id="main-form" enctype="multipart/form-data">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{ form_widget(form.title) }}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="text-primary hide" id="cipher-alert">
|
||||
<span class="glyphicon glyphicon-info-sign"></span>
|
||||
{{ 'form.cipher.alert'|trans }}
|
||||
</p>
|
||||
<div class="btn-toolbar">
|
||||
<div class="btn-group" id="options">
|
||||
<div class="btn-group">
|
||||
|
@ -75,6 +71,10 @@
|
|||
{{ form_errors(form.content) }}
|
||||
{{ form_widget(form.content) }}
|
||||
</p>
|
||||
<p>
|
||||
{{ form_errors(form.file) }}
|
||||
{{ form_widget(form.file) }}
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" class="btn btn-primary" value="{{ 'form.submit'|trans }}">
|
||||
</p>
|
||||
|
|
|
@ -24,19 +24,19 @@
|
|||
<p>
|
||||
{{ form_errors(form._username) }}
|
||||
{{ form_widget(form._username) }}
|
||||
</p>
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ form_errors(form._password) }}
|
||||
{{ form_widget(form._password) }}
|
||||
</p>
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ form_errors(form._remember_me) }}
|
||||
{{ form_widget(form._remember_me) }}
|
||||
{{ form_label(form._remember_me) }}
|
||||
</p>
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="submit" class="btn btn-primary" value="{{ 'form.submit'|trans }}">
|
||||
</p>
|
||||
|
|
|
@ -1,52 +1,52 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{{ 'login.register.title'|trans }}
|
||||
{{ 'login.register.title'|trans }}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="row">
|
||||
{% if error %}
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-warning">
|
||||
{{ error }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if success %}
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-success">
|
||||
{{ success }}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<form action="{{ path('register') }}" method="post" id="main-form">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{ 'login.register.title'|trans }}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
{{ form_errors(form.username) }}
|
||||
{{ form_widget(form.username) }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ form_errors(form.password) }}
|
||||
{{ form_widget(form.password) }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="submit" class="btn btn-primary" value="{{ 'form.submit'|trans }}">
|
||||
</p>
|
||||
{% if error %}
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-warning">
|
||||
{{ error }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{{ form_rest(form) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if success %}
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-success">
|
||||
{{ success }}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<form action="{{ path('register') }}" method="post" id="main-form">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{ 'login.register.title'|trans }}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
{{ form_errors(form.username) }}
|
||||
{{ form_widget(form.username) }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ form_errors(form.password) }}
|
||||
{{ form_widget(form.password) }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="submit" class="btn btn-primary" value="{{ 'form.submit'|trans }}">
|
||||
</p>
|
||||
|
||||
{{ form_rest(form) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -4,6 +4,20 @@
|
|||
|
||||
{% block body %}
|
||||
<div class="row">
|
||||
{% if app.request.query.has('passwordUpdated') %}
|
||||
<div class="col-md-12">
|
||||
{% if app.request.query.get('passwordUpdated') %}
|
||||
<div class="alert alert-success">
|
||||
<p>{{ 'form.success.password'|trans }}</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<p>{{ 'form.error.password'|trans }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if deleted %}
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-success">
|
||||
|
@ -11,17 +25,18 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{ 'my.title'|trans }}
|
||||
{{ 'my.title'|trans }}
|
||||
<div class="pull-right actions">
|
||||
<a href="{{ path('home', app.request.attributes.get('_route_params')) }}" class="btn btn-success btn-sm">
|
||||
<span class="glyphicon glyphicon-copy"></span>
|
||||
{{ 'gist.action.add'|trans }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="tab-content">
|
||||
<div id="form-deletion">
|
||||
|
@ -30,7 +45,10 @@
|
|||
|
||||
{% set params = app.request.attributes.get('_route_params')|merge({page: 1}) %}
|
||||
|
||||
<form action="{{ path('my', params) }}" method="GET">
|
||||
<form action="{{ path('my', params) }}" method="GET" class="form-inline">
|
||||
<div class="form-group pull-left">
|
||||
{{ form_widget(filterForm.title) }}
|
||||
</div>
|
||||
<div class="btn-toolbar">
|
||||
<div class="btn-group" id="options">
|
||||
<div class="btn-group">
|
||||
|
@ -42,15 +60,15 @@
|
|||
<ul class="dropdown-menu">
|
||||
{% for item in filterForm.type.vars.choices %}
|
||||
<li>
|
||||
<input
|
||||
{% if item.value == filterForm.type.vars.value %}checked{% endif %}
|
||||
data-id="#type-label" type="radio" class="hide"
|
||||
data-title="{{ item.label }}"
|
||||
value="{{ item.value }}"
|
||||
name="filter[type]"
|
||||
<input
|
||||
{% if item.value == filterForm.type.vars.value %}checked{% endif %}
|
||||
data-id="#type-label" type="radio" class="hide"
|
||||
data-title="{{ item.label }}"
|
||||
value="{{ item.value }}"
|
||||
name="filter[type]"
|
||||
id="type-{{ loop.index }}" />
|
||||
|
||||
<a href="#">
|
||||
<a href="#">
|
||||
<label for="type-{{ loop.index }}">
|
||||
{{ item.label }}
|
||||
</label>
|
||||
|
@ -68,15 +86,15 @@
|
|||
<ul class="dropdown-menu" role="menu">
|
||||
{% for item in filterForm.cipher.vars.choices %}
|
||||
<li>
|
||||
<input
|
||||
{% if item.value == filterForm.cipher.vars.value %}checked{% endif %}
|
||||
data-id="#cipher-label" type="radio" class="hide cipher-input"
|
||||
data-title="{{ item.label }}"
|
||||
value="{{ item.value }}"
|
||||
<input
|
||||
{% if item.value == filterForm.cipher.vars.value %}checked{% endif %}
|
||||
data-id="#cipher-label" type="radio" class="hide cipher-input"
|
||||
data-title="{{ item.label }}"
|
||||
value="{{ item.value }}"
|
||||
name="filter[cipher]"
|
||||
id="cipher-{{ loop.index }}" />
|
||||
|
||||
<a href="#">
|
||||
<a href="#">
|
||||
<label for="cipher-{{ loop.index }}">
|
||||
{{ item.label }}
|
||||
</label>
|
||||
|
@ -91,9 +109,9 @@
|
|||
</div>
|
||||
</form>
|
||||
|
||||
{% if gists.nbResults == 0 %}
|
||||
{{ 'my.nothing'|trans }}
|
||||
{% else %}
|
||||
{% if gists.nbResults == 0 %}
|
||||
{{ 'my.nothing'|trans }}
|
||||
{% else %}
|
||||
{% set pager %}
|
||||
{% if gists.haveToPaginate %}
|
||||
{% set params = app.request.attributes.get('_route_params')|merge({filter: app.request.query.get('filter', [])}) %}
|
||||
|
@ -118,7 +136,7 @@
|
|||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
{% for p in gists.links(10) %}
|
||||
<li {% if p == page %}class="active"{% endif %}>
|
||||
{% set params = params|merge({page: p}) %}
|
||||
|
@ -126,7 +144,7 @@
|
|||
<a href="{{ path('my', params) }}">{{ p }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
<li>
|
||||
{% set params = params|merge({page: gists.nextPage}) %}
|
||||
|
||||
|
@ -136,7 +154,7 @@
|
|||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
{% set params = params|merge({page: gists.lastPage}) %}
|
||||
|
||||
|
@ -149,43 +167,100 @@
|
|||
</ul>
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
|
||||
|
||||
{{ pager }}
|
||||
|
||||
{% for gist in gists %}
|
||||
<div class="commit">
|
||||
<p>
|
||||
<strong>{{ gist.title ? gist.title : 'gist.untitled'|trans }}</strong>,
|
||||
{{ gist.createdAt|date('date.format'|trans) }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<button class="btn btn-info btn-sm">
|
||||
{{ gist.type }}
|
||||
</button>
|
||||
{% for gist in gists %}
|
||||
<div class="commit">
|
||||
<p>
|
||||
<strong>{{ gist.title ? gist.title : 'gist.untitled'|trans }}</strong>,
|
||||
{{ gist.createdAt|date('date.format'|trans) }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<button class="btn btn-info btn-sm">
|
||||
{{ gist.type }}
|
||||
</button>
|
||||
|
||||
{% if not gist.cipher %}
|
||||
<a href="{{ path('view', {gist: gist.file}) }}" class="btn btn-warning btn-sm">
|
||||
{{ 'gist.action.view'|trans }}
|
||||
</a>
|
||||
{% else %}
|
||||
<button class="btn btn-error btn-sm">
|
||||
<span class="glyphicon glyphicon-lock"></span>
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
{% if not gist.cipher %}
|
||||
<a href="{{ path('view', {gist: gist.file}) }}" class="btn btn-warning btn-sm">
|
||||
{{ 'gist.action.view'|trans }}
|
||||
</a>
|
||||
{% else %}
|
||||
<button class="btn btn-error btn-sm">
|
||||
<span class="glyphicon glyphicon-lock"></span>
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
<button class="btn btn-delete btn-sm" data-id="{{ gist.id }}">
|
||||
<span class="glyphicon btn-delete glyphicon-remove" data-id="{{ gist.id }}"></span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{{ pager }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% set apiEnabled = app.settings.api.enabled %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-{{ apiEnabled ? 6 : 12 }}">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{ 'login.login.form.password.placeholder'|trans }}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="tab-content">
|
||||
<form action="{{ path('my', params) }}" method="post">
|
||||
<p>
|
||||
{{ form_errors(passwordForm.currentPassword) }}
|
||||
{{ form_widget(passwordForm.currentPassword) }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ form_errors(passwordForm.newPassword) }}
|
||||
{{ form_widget(passwordForm.newPassword) }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ form_rest(passwordForm) }}
|
||||
<input type="submit" class="btn btn-primary" value="{{ 'form.submit'|trans }}">
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if apiEnabled %}
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{ 'my.api.title'|trans }}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="tab-content">
|
||||
<p>{{ 'my.api.warning'|trans|raw }}</p>
|
||||
|
||||
<form action="{{ path('my', params) }}" method="post">
|
||||
<div class="row">
|
||||
<p class="col-md-12">
|
||||
<input type="text" name="apiKey" id="form-api-key" class="form-control" value="{{ apiKey }}" data-key="{{ apiKey }}">
|
||||
</p>
|
||||
<p class="col-md-12">
|
||||
<input type="submit" name="generateApiKey" value="{{ 'my.api.form.generate'|trans }}" class="btn btn-primary">
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
30
src/Gist/Resources/views/View/cipherJs.html.twig
Normal file
30
src/Gist/Resources/views/View/cipherJs.html.twig
Normal file
|
@ -0,0 +1,30 @@
|
|||
<script type="text/javascript" src="{{ web_path }}components/SyntaxHighlighter/scripts/XRegExp.js"></script> <!-- XRegExp is bundled with the final shCore.js during build -->
|
||||
<script type="text/javascript" src="{{ web_path }}components/SyntaxHighlighter/scripts/shCore.js"></script>
|
||||
<script type="text/javascript" src="{{ web_path }}components/SyntaxHighlighter/scripts/shAutoloader.js"></script>
|
||||
<script type="text/javascript">
|
||||
SyntaxHighlighter.autoloader(
|
||||
['applescript', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushAppleScript.js' ],
|
||||
['actionscript3', 'as3', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushAS3.js' ],
|
||||
['bash', 'shell', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushBash.js' ],
|
||||
['coldfusion', 'cf', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushColdFusion.js' ],
|
||||
['cpp', 'c', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushCpp.js' ],
|
||||
['c#', 'c-sharp', 'csharp', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushCSharp.js' ],
|
||||
['css', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushCss.js' ],
|
||||
['delphi', 'pascal', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushDelphi.js' ],
|
||||
['diff', 'patch', 'pas', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushDiff.js' ],
|
||||
['erl', 'erlang', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushErlang.js' ],
|
||||
['groovy', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushGroovy.js' ],
|
||||
['java', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushJava.js' ],
|
||||
['jfx', 'javafx', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushJavaFX.js' ],
|
||||
['js', 'jscript', 'javascript', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushJScript.js' ],
|
||||
['perl', 'pl', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushPerl.js' ],
|
||||
['php', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushPhp.js' ],
|
||||
['text', 'plain', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushPlain.js' ],
|
||||
['py', 'python', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushPython.js' ],
|
||||
['ruby', 'rails', 'ror', 'rb', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushRuby.js' ],
|
||||
['scala', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushScala.js' ],
|
||||
['sql', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushSql.js' ],
|
||||
['vb', 'vbnet', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushVb.js' ],
|
||||
['xml', 'xhtml', 'xslt', 'html', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushXml.js' ]
|
||||
);
|
||||
</script>
|
|
@ -1,31 +1,17 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block css %}
|
||||
{% if gist.cipher %}
|
||||
<link type="text/css" rel="Stylesheet" href="{{ web_path }}components/SyntaxHighlighter/styles/shCoreRDark.css" />
|
||||
<link type="text/css" rel="Stylesheet" href="{{ web_path }}components/SyntaxHighlighter/styles/shThemeRDark.css" />
|
||||
{{ parent() }}
|
||||
|
||||
<link rel="stylesheet" href="{{ web_path }}components/Prism/themes/prism.css">
|
||||
|
||||
{% if theme_settings.name == 'dark' %}
|
||||
<link rel="stylesheet" href="{{ web_path }}app/css/prism-okaidia.css">
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="{{ web_path }}app/css/geshi/vibrant-ink.css" />
|
||||
<link rel="stylesheet" href="{{ web_path }}app/css/prism-solarizedlight.css">
|
||||
{% endif %}
|
||||
|
||||
{{ parent() }}
|
||||
|
||||
<style type="text/css">
|
||||
#embed {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#viewer {
|
||||
background: #222;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-size: 14px;
|
||||
padding: 5px;
|
||||
margin-bottom: 0;
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -34,9 +20,8 @@
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
pre ol {
|
||||
padding-left: 0 !important;
|
||||
list-style: none;
|
||||
.container-fluid {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
@ -50,40 +35,73 @@
|
|||
<div class="col-md-12" id="embed">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{% if not gist.cipher %}
|
||||
<div class="pull-right actions">
|
||||
<a target="_blank" href="{{ path('view', app.request.attributes.get('_route_params')) }}" class="btn btn-default btn-sm">
|
||||
<span class="btn btn-warning btn-xs">
|
||||
{{ commit|slice(0, 10) }}
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<a target="_blank" href="{{ path('raw', app.request.attributes.get('_route_params')) }}" class="btn btn-default btn-sm">
|
||||
<span class="glyphicon glyphicon-eye-open"></span>
|
||||
{{ 'gist.action.raw'|trans }}
|
||||
</a>
|
||||
<a target="_blank" href="{{ path('download', app.request.attributes.get('_route_params')) }}" class="btn btn-default btn-sm">
|
||||
<span class="glyphicon glyphicon-save-file"></span>
|
||||
{{ 'gist.action.download'|trans }}
|
||||
</a>
|
||||
<a target="_blank" href="{{ path('clone', app.request.attributes.get('_route_params')) }}" class="btn btn-success btn-sm">
|
||||
<span class="glyphicon glyphicon-copy"></span>
|
||||
{{ 'gist.action.clone'|trans }}
|
||||
</a>
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-xs-9">
|
||||
{{ gist.title ? gist.title : 'gist.untitled'|trans }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-md-6 col-xs-3">
|
||||
<div class="visible-sm visible-xs pull-right">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="glyphicon glyphicon-menu-hamburger"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li>
|
||||
<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>
|
||||
</li>
|
||||
{% if not gist.cipher %}
|
||||
<li>
|
||||
<a href="{{ path('raw', app.request.attributes.get('_route_params')) }}">
|
||||
{{ 'gist.action.raw'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('download', app.request.attributes.get('_route_params')) }}">
|
||||
{{ 'gist.action.download'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<a href="{{ path('clone', app.request.attributes.get('_route_params')) }}" class="cipher-link">
|
||||
{{ 'gist.action.clone'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
{{ gist.title ? gist.title : 'gist.untitled'|trans }}
|
||||
{% if not gist.cipher %}
|
||||
<a href="{{ path('raw', app.request.attributes.get('_route_params')) }}" 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">
|
||||
<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">
|
||||
<span class="glyphicon glyphicon-copy"></span>
|
||||
{{ 'gist.action.clone'|trans }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="tab-content">
|
||||
<div id="view" class="tab-pane active in">
|
||||
<div id="viewer">
|
||||
{% if gist.cipher %}
|
||||
<pre class="brush: {{ gist.type }}; syntaxhighlighter">{{ raw_content|raw }}</pre>
|
||||
{% else %}
|
||||
{{ content|raw }}
|
||||
{% endif %}
|
||||
<pre><code {% if gist.cipher %}data-cipher{% endif %} class="language-{{ gist.highlightType }}">{{ raw_content }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -99,37 +117,5 @@
|
|||
{{ parent() }}
|
||||
|
||||
<script type="text/javascript" src="{{ web_path }}components/iframe-resizer/js/iframeResizer.contentWindow.min.js"></script>
|
||||
|
||||
{% if gist.cipher %}
|
||||
<script type="text/javascript" src="{{ web_path }}components/SyntaxHighlighter/scripts/XRegExp.js"></script> <!-- XRegExp is bundled with the final shCore.js during build -->
|
||||
<script type="text/javascript" src="{{ web_path }}components/SyntaxHighlighter/scripts/shCore.js"></script>
|
||||
<script type="text/javascript" src="{{ web_path }}components/SyntaxHighlighter/scripts/shAutoloader.js"></script>
|
||||
<script type="text/javascript">
|
||||
SyntaxHighlighter.autoloader(
|
||||
['applescript', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushAppleScript.js' ],
|
||||
['actionscript3', 'as3', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushAS3.js' ],
|
||||
['bash', 'shell', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushBash.js' ],
|
||||
['coldfusion', 'cf', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushColdFusion.js' ],
|
||||
['cpp', 'c', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushCpp.js' ],
|
||||
['c#', 'c-sharp', 'csharp', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushCSharp.js' ],
|
||||
['css', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushCss.js' ],
|
||||
['delphi', 'pascal', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushDelphi.js' ],
|
||||
['diff', 'patch', 'pas', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushDiff.js' ],
|
||||
['erl', 'erlang', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushErlang.js' ],
|
||||
['groovy', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushGroovy.js' ],
|
||||
['java', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushJava.js' ],
|
||||
['jfx', 'javafx', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushJavaFX.js' ],
|
||||
['js', 'jscript', 'javascript', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushJScript.js' ],
|
||||
['perl', 'pl', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushPerl.js' ],
|
||||
['php', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushPhp.js' ],
|
||||
['text', 'plain', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushPlain.js' ],
|
||||
['py', 'python', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushPython.js' ],
|
||||
['ruby', 'rails', 'ror', 'rb', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushRuby.js' ],
|
||||
['scala', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushScala.js' ],
|
||||
['sql', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushSql.js' ],
|
||||
['vb', 'vbnet', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushVb.js' ],
|
||||
['xml', 'xhtml', 'xslt', 'html', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushXml.js' ]
|
||||
);
|
||||
</script>
|
||||
{% endif %}
|
||||
<script src="{{ web_path }}app/js/prism.js" {% if gist.cipher %}data-manual{% endif %}></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block css %}
|
||||
<link rel="stylesheet" href="{{ web_path }}app/css/geshi/vibrant-ink.css" />
|
||||
|
||||
{{ parent() }}
|
||||
|
||||
<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">
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="{{ web_path }}app/css/prism-solarizedlight.css">
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}{{ gist.title ? gist.title : 'gist.untitled'|trans }} - {{ 'gist.action.history'|trans }}{% endblock %}
|
||||
|
@ -11,21 +17,19 @@
|
|||
{% block body %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% if not gist.cipher %}
|
||||
<ul class="nav nav-tabs">
|
||||
<li>
|
||||
<a href="{{ path('view', {gist: gist.file}) }}">
|
||||
{{ 'gist.action.view'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<a href="{{ path('revisions', {gist: gist.file}) }}">
|
||||
{{ 'gist.action.history'|trans }}
|
||||
<span class="badge">{{ history|length }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
<ul class="nav nav-tabs">
|
||||
<li>
|
||||
<a href="{{ path('view', {gist: gist.file}) }}" class="cipher-link">
|
||||
{{ 'gist.action.view'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<a href="{{ path('revisions', {gist: gist.file}) }}" class="cipher-link">
|
||||
{{ 'gist.action.history'|trans }}
|
||||
<span class="badge">{{ history|length }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
|
@ -33,42 +37,38 @@
|
|||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="tab-content">
|
||||
{% if not gist.cipher %}
|
||||
<div id="revisions" class="tab-pane in active">
|
||||
{% for commit in history %}
|
||||
<div class="commit">
|
||||
<p>
|
||||
<a href="{{ path('view', {gist: gist.file, commit: commit.commit}) }}" class="btn btn-warning btn-sm">
|
||||
{{ commit.commit|slice(0, 10) }}
|
||||
<div id="revisions" class="tab-pane in active">
|
||||
{% for commit in history %}
|
||||
<div class="commit">
|
||||
<p>
|
||||
<a href="{{ path('view', {gist: gist.file, commit: commit.commit}) }}" class="btn btn-warning btn-sm cipher-link">
|
||||
{{ commit.commit|slice(0, 10) }}
|
||||
</a>
|
||||
|
||||
{% if loop.first %}<span class="btn btn-info btn-sm">init</span>{% endif %}
|
||||
|
||||
{% if not loop.first %}
|
||||
<a href="#diff-{{ loop.index }}" data-target="#diff-{{ loop.index }}" class="btn btn-default btn-sm show-diff">
|
||||
diff
|
||||
</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>
|
||||
{{ commit.date|date('date.format'|trans) }}
|
||||
</p>
|
||||
<div>
|
||||
{% if not loop.first %}
|
||||
<pre><code class="diff language-diff" id="diff-{{ loop.index }}">{% if not gist.cipher %}{{ commit.diff }}{% endif %}</code></pre>
|
||||
|
||||
{% if loop.first %}<span class="btn btn-info btn-sm">init</span>{% endif %}
|
||||
|
||||
{% if not loop.first %}
|
||||
<a href="#diff-{{ loop.index }}" data-target="#diff-{{ loop.index }}" class="btn btn-default btn-sm show-diff">
|
||||
diff
|
||||
</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>
|
||||
{{ commit.date|date('date.format'|trans) }}
|
||||
</p>
|
||||
<div>
|
||||
{% if not loop.first %}
|
||||
|
||||
<div class="diff" id="diff-{{ loop.index }}">
|
||||
{{ commit.diff|raw }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if not loop.last %}
|
||||
<hr />
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not loop.last %}
|
||||
<hr />
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -78,4 +78,59 @@
|
|||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
|
||||
<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>
|
||||
var key = getKey();
|
||||
|
||||
var decrypt = function(content) {
|
||||
var decrypted = CryptoJS.AES.decrypt(content, key, {
|
||||
format: JsonFormatter
|
||||
});
|
||||
|
||||
return decrypted.toString(CryptoJS.enc.Utf8);
|
||||
}
|
||||
|
||||
var commits = [];
|
||||
|
||||
{% for commit in history %}
|
||||
try {
|
||||
var content = decrypt('{{ commit.content|raw }}');
|
||||
commits[{{ loop.index - 1 }}] = content;
|
||||
} catch(e) {
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
for (var u = commits.length - 1; u > 0; u--) {
|
||||
if (commits.hasOwnProperty(u) && commits.hasOwnProperty(u - 1)) {
|
||||
var previous = commits[u - 1];
|
||||
var current = commits[u];
|
||||
var diff = JsDiff.diffLines(previous, current);
|
||||
var diffContent = [];
|
||||
|
||||
for (var v = 0, c = diff.length; v < c; v++) {
|
||||
var value = diff[v].value;
|
||||
var sign = diff[v].added ? '+' : '-';
|
||||
var lines = value.split("\n");
|
||||
|
||||
for (var i = 0, l = lines.length; i < l; i++) {
|
||||
if (lines[i] != '\ No newline at end of file') {
|
||||
diffContent.push(sign + lines[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diffContent = diffContent.join("\n");
|
||||
|
||||
$('#diff-' + (u + 1).toString()).text(diffContent);
|
||||
}
|
||||
}
|
||||
|
||||
Prism.highlightAll();
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block css %}
|
||||
{% if gist.cipher %}
|
||||
<link type="text/css" rel="Stylesheet" href="{{ web_path }}components/SyntaxHighlighter/styles/shCoreRDark.css" />
|
||||
<link type="text/css" rel="Stylesheet" href="{{ web_path }}components/SyntaxHighlighter/styles/shThemeRDark.css" />
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="{{ web_path }}app/css/geshi/vibrant-ink.css" />
|
||||
{% endif %}
|
||||
|
||||
{{ parent() }}
|
||||
|
||||
<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">
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="{{ web_path }}app/css/prism-solarizedlight.css">
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}{{ gist.title ? gist.title : 'gist.untitled'|trans }} - {{ commit|slice(0, 10) }}{% endblock %}
|
||||
|
@ -16,62 +17,105 @@
|
|||
{% block body %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% if not gist.cipher %}
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active">
|
||||
<a href="{{ path('view', {gist: gist.file}) }}">
|
||||
{{ 'gist.action.view'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('revisions', {gist: gist.file}) }}">
|
||||
{{ 'gist.action.history'|trans }}
|
||||
<span class="badge">{{ history|length }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active">
|
||||
<a href="{{ path('view', {gist: gist.file}) }}" class="cipher-link">
|
||||
{{ 'gist.action.view'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('revisions', {gist: gist.file}) }}" class="cipher-link">
|
||||
{{ 'gist.action.history'|trans }}
|
||||
<span class="badge">{{ history|length }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{% if not gist.cipher %}
|
||||
<div class="pull-right actions">
|
||||
<span class="btn btn-warning btn-xs">
|
||||
{{ commit|slice(0, 10) }}
|
||||
</span>
|
||||
|
||||
<a href="{{ path('raw', app.request.attributes.get('_route_params')) }}" 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">
|
||||
<span class="glyphicon glyphicon-save-file"></span>
|
||||
{{ 'gist.action.download'|trans }}
|
||||
</a>
|
||||
<a href="{{ path('clone', app.request.attributes.get('_route_params')) }}" class="btn btn-success btn-sm">
|
||||
<span class="glyphicon glyphicon-copy"></span>
|
||||
{{ 'gist.action.clone'|trans }}
|
||||
</a>
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-xs-9">
|
||||
{{ gist.title ? gist.title : 'gist.untitled'|trans }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-md-6 col-xs-3">
|
||||
<div class="visible-sm visible-xs pull-right">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="glyphicon glyphicon-menu-hamburger"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li>
|
||||
<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>
|
||||
</li>
|
||||
{% if not gist.cipher %}
|
||||
<li>
|
||||
<a href="{{ path('raw', app.request.attributes.get('_route_params')) }}">
|
||||
{{ 'gist.action.raw'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('download', app.request.attributes.get('_route_params')) }}">
|
||||
{{ 'gist.action.download'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<a href="{{ path('clone', app.request.attributes.get('_route_params')) }}" class="cipher-link">
|
||||
{{ 'gist.action.clone'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="visible-md visible-lg">
|
||||
<div class="text-right actions">
|
||||
<span class="btn btn-warning btn-xs">
|
||||
{{ commit|slice(0, 10) }}
|
||||
</span>
|
||||
|
||||
{{ gist.title ? gist.title : 'gist.untitled'|trans }}
|
||||
{% if not gist.cipher %}
|
||||
<a href="{{ path('raw', app.request.attributes.get('_route_params')) }}" 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">
|
||||
<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">
|
||||
<span class="glyphicon glyphicon-copy"></span>
|
||||
{{ 'gist.action.clone'|trans }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="tab-content">
|
||||
<div id="view" class="tab-pane active in">
|
||||
<div id="viewer">
|
||||
{% if gist.cipher %}
|
||||
<pre class="brush: {{ gist.type }}; syntaxhighlighter">{{ raw_content|raw }}</pre>
|
||||
{% else %}
|
||||
{{ content|raw }}
|
||||
{% endif %}
|
||||
<pre><code {% if gist.cipher %}data-cipher{% endif %} class="language-{{ gist.highlightType }}">{{ raw_content }}</code></pre>
|
||||
</div>
|
||||
|
||||
<p id="embed" class="pull-right">
|
||||
{{ 'gist.action.embed'|trans }}
|
||||
<input type="text" class="form-control" id="embed-input" value="{{ '<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>' }}" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{ 'gist.action.embed'|trans }}
|
||||
</div>
|
||||
<div class="panel-body" id="embed">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -82,37 +126,6 @@
|
|||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
|
||||
{% if gist.cipher %}
|
||||
<script type="text/javascript" src="{{ web_path }}components/SyntaxHighlighter/scripts/XRegExp.js"></script> <!-- XRegExp is bundled with the final shCore.js during build -->
|
||||
<script type="text/javascript" src="{{ web_path }}components/SyntaxHighlighter/scripts/shCore.js"></script>
|
||||
<script type="text/javascript" src="{{ web_path }}components/SyntaxHighlighter/scripts/shAutoloader.js"></script>
|
||||
<script type="text/javascript">
|
||||
SyntaxHighlighter.autoloader(
|
||||
['applescript', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushAppleScript.js' ],
|
||||
['actionscript3', 'as3', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushAS3.js' ],
|
||||
['bash', 'shell', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushBash.js' ],
|
||||
['coldfusion', 'cf', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushColdFusion.js' ],
|
||||
['cpp', 'c', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushCpp.js' ],
|
||||
['c#', 'c-sharp', 'csharp', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushCSharp.js' ],
|
||||
['css', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushCss.js' ],
|
||||
['delphi', 'pascal', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushDelphi.js' ],
|
||||
['diff', 'patch', 'pas', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushDiff.js' ],
|
||||
['erl', 'erlang', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushErlang.js' ],
|
||||
['groovy', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushGroovy.js' ],
|
||||
['java', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushJava.js' ],
|
||||
['jfx', 'javafx', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushJavaFX.js' ],
|
||||
['js', 'jscript', 'javascript', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushJScript.js' ],
|
||||
['perl', 'pl', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushPerl.js' ],
|
||||
['php', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushPhp.js' ],
|
||||
['text', 'plain', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushPlain.js' ],
|
||||
['py', 'python', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushPython.js' ],
|
||||
['ruby', 'rails', 'ror', 'rb', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushRuby.js' ],
|
||||
['scala', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushScala.js' ],
|
||||
['sql', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushSql.js' ],
|
||||
['vb', 'vbnet', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushVb.js' ],
|
||||
['xml', 'xhtml', 'xslt', 'html', '{{ web_path }}components/SyntaxHighlighter/scripts/shBrushXml.js' ]
|
||||
);
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
<script src="{{ web_path }}app/js/prism.js" {% if gist.cipher %}data-manual{% endif %}></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,23 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
{% set theme_settings = app.settings.theme %}
|
||||
{% set security_settings = app.settings.security %}
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
{% block css %}
|
||||
<link rel="stylesheet" href="{{ web_path }}components/bootstrap/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="{{ web_path }}components/flag-icon-css/css/flag-icon.min.css" />
|
||||
<link rel="stylesheet" href="{{ web_path }}app/css/bootstrap/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="{{ web_path }}app/css/app.css" />
|
||||
<link rel="stylesheet" href="{{ web_path }}components/bootstrap/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="{{ web_path }}components/flag-icon-css/css/flag-icon.min.css">
|
||||
|
||||
{% if theme_settings.name == 'dark' %}
|
||||
<link rel="stylesheet" href="{{ web_path }}app/css/bootstrap/bootstrap.min.css">
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="{{ web_path }}components/bootstrap/dist/css/bootstrap-theme.min.css">
|
||||
{% endif %}
|
||||
|
||||
<link rel="stylesheet" href="{{ web_path }}app/css/themes/{{ theme_settings.name }}.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block metas %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
{% endblock %}
|
||||
|
||||
<link rel="shortcut icon" href="{{ web_path }}favicon.png">
|
||||
|
||||
<title>{{ 'app.title_prefix'|trans }}{% block title %}{% endblock %}</title>
|
||||
</head>
|
||||
<body>
|
||||
{% block nav %}
|
||||
<nav class="navbar navbar-inverse">
|
||||
<nav class="navbar navbar-{{ theme_settings.name == 'dark' ? 'inverse' : 'default' }}">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#main-menu">
|
||||
|
@ -36,47 +46,46 @@
|
|||
{{ 'app.menu.home.title'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if user %}
|
||||
<li>
|
||||
<a href="{{ path('my') }}">
|
||||
{{ 'app.menu.my.my.title'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('logout', {target_url: path('home')}) }}">
|
||||
{{ 'app.menu.my.logout.title'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% elseif app.enable_login %}
|
||||
<li>
|
||||
<a href="{{ path('login') }}">
|
||||
{{ 'app.menu.my.login.title'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if app.enable_registration %}
|
||||
<li>
|
||||
<a href="{{ path('register') }}">
|
||||
{{ 'app.menu.my.register.title'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if user %}
|
||||
<li>
|
||||
<a href="{{ path('my') }}">
|
||||
{{ 'app.menu.my.my.title'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('logout', {target_url: path('home')}) }}">
|
||||
{{ 'app.menu.my.logout.title'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% elseif security_settings.enable_login %}
|
||||
<li>
|
||||
<a href="{{ path('login') }}">
|
||||
{{ 'app.menu.my.login.title'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if security_settings.enable_registration %}
|
||||
<li>
|
||||
<a href="{{ path('register') }}">
|
||||
{{ 'app.menu.my.register.title'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li>
|
||||
<a href="https://gitlab.deblan.org/deblan/gist/src/master/README.md">
|
||||
<a href="https://gitnet.fr/deblan/gist/src/master/README.md">
|
||||
{{ 'app.menu.about.title'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% block langs %}
|
||||
<p class="navbar-text navbar-right">
|
||||
<a class="lang btn btn-xs" href="{{ path(app.request.attributes.get('_route'), app.request.attributes.get('_route_params')|merge({_locale: 'en'})) }}">
|
||||
<span class="flag-icon flag-icon-gb"></span>
|
||||
</a>
|
||||
<a class="lang btn btn-xs" href="{{ path(app.request.attributes.get('_route'), app.request.attributes.get('_route_params')|merge({_locale: 'fr'})) }}">
|
||||
<span class="flag-icon flag-icon-fr"></span>
|
||||
</a>
|
||||
{% for locale, flag in {'en': 'gb', 'fr': 'fr', 'es': 'es', 'de': 'de', 'cn': 'cn'} %}
|
||||
<a class="lang btn btn-xs cipher-link" href="{{ path(app.request.attributes.get('_route'), app.request.attributes.get('_route_params')|merge({_locale: locale})) }}">
|
||||
<span class="flag-icon flag-icon-{{ flag }}"></span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
@ -92,9 +101,22 @@
|
|||
<footer>
|
||||
{{ 'footer.text'|trans|raw }}
|
||||
</footer>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% block js %}
|
||||
<script>
|
||||
var trans = function(key) {
|
||||
var translations = {
|
||||
'form.confirm': '{{ 'form.confirm'|trans }}',
|
||||
};
|
||||
|
||||
if (translations.hasOwnProperty(key)) {
|
||||
return translations[key];
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
</script>
|
||||
<script src="{{ web_path }}components/jquery/dist/jquery.min.js"></script>
|
||||
<script src="{{ web_path }}components/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||
<script src="{{ web_path }}app/js/cipher.js"></script>
|
||||
|
|
|
@ -7,36 +7,48 @@ use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
|||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
|
||||
use Symfony\Component\Security\Http\HttpUtils;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\Routing\Generator\UrlGenerator;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Class AuthenticationListener
|
||||
* Class AuthenticationListener.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class AuthenticationListener implements ListenerInterface
|
||||
{
|
||||
/**
|
||||
* @var TokenStorageInterface
|
||||
*/
|
||||
protected $tokenStorage;
|
||||
|
||||
/**
|
||||
* @var AuthenticationManagerInterface
|
||||
*/
|
||||
protected $authenticationManager;
|
||||
|
||||
/**
|
||||
* __construct.
|
||||
*
|
||||
* @param TokenStorageInterface $tokenStorage
|
||||
* @param AuthenticationManagerInterface $authenticationManager
|
||||
*/
|
||||
public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager)
|
||||
{
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
$this->authenticationManager = $authenticationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GetResponseEvent $event
|
||||
*/
|
||||
public function handle(GetResponseEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
$request = $event->getRequest();
|
||||
$username = $request->get('_username');
|
||||
$password = $request->get('_password');
|
||||
|
||||
if (!empty($username)) {
|
||||
$token = new UsernamePasswordToken($username, $password, 'default');
|
||||
|
||||
|
||||
try {
|
||||
$authToken = $this->authenticationManager->authenticate($token);
|
||||
$this->tokenStorage->setToken($token);
|
||||
|
@ -46,7 +58,7 @@ class AuthenticationListener implements ListenerInterface
|
|||
$this->tokenStorage->setToken(null);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,18 +9,32 @@ use Gist\Service\UserProvider;
|
|||
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
|
||||
|
||||
/**
|
||||
* Class AuthenticationProvider
|
||||
* Class AuthenticationProvider.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class AuthenticationProvider implements AuthenticationProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var UserProvider
|
||||
*/
|
||||
protected $userProvider;
|
||||
|
||||
/**
|
||||
* __construct.
|
||||
*
|
||||
* @param UserProvider $userProvider
|
||||
*/
|
||||
public function __construct(UserProvider $userProvider)
|
||||
{
|
||||
$this->userProvider = $userProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates.
|
||||
*
|
||||
* @param TokenInterface $token
|
||||
*/
|
||||
public function authenticate(TokenInterface $token)
|
||||
{
|
||||
$user = $this->userProvider->loadUserByUsername($token->getUser());
|
||||
|
@ -29,7 +43,7 @@ class AuthenticationProvider implements AuthenticationProviderInterface
|
|||
$isValid = $this->userProvider->getEncoder()->isPasswordValid(
|
||||
$user->getPassword(),
|
||||
$token->getCredentials(),
|
||||
$user->getSalt()
|
||||
$user->getSalt()
|
||||
);
|
||||
|
||||
if (!$isValid) {
|
||||
|
@ -42,6 +56,13 @@ class AuthenticationProvider implements AuthenticationProviderInterface
|
|||
throw new AuthenticationException('Authentication failed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the token instance is supported.
|
||||
*
|
||||
* @param TokenInterface $token
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports(TokenInterface $token)
|
||||
{
|
||||
return $token instanceof UsernamePasswordToken;
|
||||
|
|
|
@ -7,11 +7,17 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
|
|||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Class LogoutSuccessHandler
|
||||
* Class LogoutSuccessHandler.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class LogoutSuccessHandler implements LogoutSuccessHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function onLogoutSuccess(Request $request)
|
||||
{
|
||||
$targetUrl = $request->query->get('target_url') ? $request->query->get('target_url') : '/';
|
||||
|
@ -19,4 +25,3 @@ class LogoutSuccessHandler implements LogoutSuccessHandlerInterface
|
|||
return new RedirectResponse($targetUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,32 +11,58 @@ use Gist\Model\GistQuery;
|
|||
use Gist\Model\User;
|
||||
|
||||
/**
|
||||
* Class Gist
|
||||
* Class Gist.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class Gist
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $gistPath;
|
||||
|
||||
/**
|
||||
* @var GitWrapper
|
||||
*/
|
||||
protected $gitWrapper;
|
||||
|
||||
/**
|
||||
* @var GitWorkingCopy
|
||||
*/
|
||||
protected $gitWorkingCopy;
|
||||
|
||||
protected $geshi;
|
||||
|
||||
public function __construct($gistPath, GitWrapper $gitWrapper, GitWorkingCopy $gitWorkingCopy, GeSHi $geshi)
|
||||
/**
|
||||
* __construct.
|
||||
*
|
||||
* @param mixed $gistPath
|
||||
* @param GitWrapper $gitWrapper
|
||||
* @param GitWorkingCopy $gitWorkingCopy
|
||||
*/
|
||||
public function __construct($gistPath, GitWrapper $gitWrapper, GitWorkingCopy $gitWorkingCopy)
|
||||
{
|
||||
$this->gistPath = $gistPath;
|
||||
$this->gitWrapper = $gitWrapper;
|
||||
$this->gitWorkingCopy = $gitWorkingCopy;
|
||||
$this->geshi = $geshi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of gists.
|
||||
*
|
||||
* @return Propel\Runtime\Collection\ObjectCollection
|
||||
*/
|
||||
public function getGists()
|
||||
{
|
||||
return GistQuery::create()->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the history of a Gist.
|
||||
*
|
||||
* @param GistModel $gist
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHistory(GistModel $gist)
|
||||
{
|
||||
$command = GitCommand::getInstance('log', '--format=medium', $gist->getFile());
|
||||
|
@ -49,7 +75,7 @@ class Gist
|
|||
|
||||
$history = [];
|
||||
|
||||
for ($i = count($commits) - 1; $i >= 0; $i--) {
|
||||
for ($i = count($commits) - 1; $i >= 0; --$i) {
|
||||
$commit = trim($commits[$i][1]);
|
||||
|
||||
$command = GitCommand::getInstance('show', '--no-color', $commit);
|
||||
|
@ -62,15 +88,27 @@ class Gist
|
|||
$data = array(
|
||||
'commit' => trim($commits[$i][1]),
|
||||
'date' => new \DateTime(trim($dates[$i][1])),
|
||||
'diff' => $this->highlight('diff', $diff),
|
||||
'diff' => str_replace('\ No newline at end of file', '', $diff),
|
||||
);
|
||||
|
||||
if ($gist->isCipher()) {
|
||||
$data['content'] = $this->getContent($gist, $commit);
|
||||
}
|
||||
|
||||
$history[] = $data;
|
||||
}
|
||||
|
||||
return $history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content of a gist.
|
||||
*
|
||||
* @param GistModel $gist
|
||||
* @param string $commit
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContent(GistModel $gist, $commit)
|
||||
{
|
||||
$command = GitCommand::getInstance('cat-file', '-p', $commit.':'.$gist->getFile());
|
||||
|
@ -80,6 +118,15 @@ class Gist
|
|||
return str_replace("\r\n", "\n", $this->gitWrapper->run($command));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a gist.
|
||||
*
|
||||
* @param GistModel $gist
|
||||
* @param array $data
|
||||
* @param mixed $user
|
||||
*
|
||||
* @return GistModel
|
||||
*/
|
||||
public function create(GistModel $gist, array $data, $user = null)
|
||||
{
|
||||
$gist->hydrateWith($data);
|
||||
|
@ -95,11 +142,19 @@ class Gist
|
|||
$gist->setUser($user);
|
||||
}
|
||||
|
||||
$gist->save();
|
||||
$gist->commit()->save();
|
||||
|
||||
return $gist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a commit.
|
||||
*
|
||||
* @param GistModel $gist
|
||||
* @param array $data
|
||||
*
|
||||
* @return GistModel
|
||||
*/
|
||||
public function commit(GistModel $gist, array $data)
|
||||
{
|
||||
file_put_contents($this->gistPath.'/'.$gist->getFile(), $data['content']);
|
||||
|
@ -108,14 +163,27 @@ class Gist
|
|||
->add($gist->getFile())
|
||||
->commit('Update');
|
||||
|
||||
$gist->commit()->save();
|
||||
|
||||
return $gist;
|
||||
}
|
||||
|
||||
public function highlight($type, $content)
|
||||
/*
|
||||
* Returns the number of commits.
|
||||
*
|
||||
* @param GistModel $gist
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNumberOfCommits(GistModel $gist)
|
||||
{
|
||||
$this->geshi->set_source($content);
|
||||
$this->geshi->set_language($type);
|
||||
$command = GitCommand::getInstance('log', '--oneline', '--', $gist->getFile());
|
||||
$command->setDirectory($this->gistPath);
|
||||
$command->bypass(false);
|
||||
|
||||
return $this->geshi->parse_code();
|
||||
$content = trim($this->gitWrapper->run($command));
|
||||
$content = str_replace("\r\n", "\n", $content);
|
||||
|
||||
return count(explode("\n", $content));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,23 +5,43 @@ namespace Gist\Service;
|
|||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class SaltGenerator
|
||||
* Class SaltGenerator.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class SaltGenerator
|
||||
{
|
||||
public function generate($length = 32)
|
||||
/**
|
||||
* Generates a random salt.
|
||||
*
|
||||
* @param int $length
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generate($length = 32, $isApiKey = false)
|
||||
{
|
||||
if (!is_numeric($length)) {
|
||||
throw new InvalidArgumentException('Paramter length must be a valid integer.');
|
||||
}
|
||||
|
||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
return substr(base64_encode(openssl_random_pseudo_bytes($length)), 0, $length);
|
||||
$string = base64_encode(openssl_random_pseudo_bytes(256));
|
||||
}
|
||||
|
||||
if (function_exists('mcrypt_create_iv')) {
|
||||
return substr(base64_encode(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)), 0, $length);
|
||||
$string = base64_encode(mcrypt_create_iv(256, MCRYPT_DEV_URANDOM));
|
||||
}
|
||||
|
||||
if (!empty($string)) {
|
||||
if (true === $isApiKey) {
|
||||
$string = str_replace(
|
||||
array('+', '%', '/', '#', '&'),
|
||||
'',
|
||||
$string
|
||||
);
|
||||
}
|
||||
|
||||
return substr($string, 0, $length);
|
||||
}
|
||||
|
||||
throw new RuntimeException('You must enable openssl or mcrypt modules.');
|
||||
|
|
|
@ -9,24 +9,43 @@ use Symfony\Component\Security\Core\User\UserProviderInterface;
|
|||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
|
||||
use Gist\Service\SaltGenerator;
|
||||
|
||||
/**
|
||||
* Class UserProvider
|
||||
* Class UserProvider.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class UserProvider implements UserProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var MessageDigestPasswordEncoder
|
||||
*/
|
||||
protected $encoder;
|
||||
|
||||
|
||||
/**
|
||||
* @var SaltGenerator
|
||||
*/
|
||||
protected $saltGenerator;
|
||||
|
||||
/**
|
||||
* __construct.
|
||||
*
|
||||
* @param MessageDigestPasswordEncoder $encoder
|
||||
* @param SaltGenerator $saltGenerator
|
||||
*/
|
||||
public function __construct(MessageDigestPasswordEncoder $encoder, SaltGenerator $saltGenerator)
|
||||
{
|
||||
$this->encoder = $encoder;
|
||||
$this->saltGenerator = $saltGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of encoder.
|
||||
*
|
||||
* @param MessageDigestPasswordEncoder $encoder
|
||||
*
|
||||
* @return UserProvider
|
||||
*/
|
||||
public function setEncoder(MessageDigestPasswordEncoder $encoder)
|
||||
{
|
||||
$this->encoder = $encoder;
|
||||
|
@ -34,11 +53,23 @@ class UserProvider implements UserProviderInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of encoder.
|
||||
*
|
||||
* @return MessageDigestPasswordEncoder
|
||||
*/
|
||||
public function getEncoder()
|
||||
{
|
||||
return $this->encoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of saltGenerator.
|
||||
*
|
||||
* @param SaltGenerator $saltGenerator
|
||||
*
|
||||
* @return UserProvider
|
||||
*/
|
||||
public function setSaltGenerator(SaltGenerator $saltGenerator)
|
||||
{
|
||||
$this->saltGenerator = $saltGenerator;
|
||||
|
@ -46,11 +77,23 @@ class UserProvider implements UserProviderInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of saltGenerator.
|
||||
*
|
||||
* @return SaltGenerator
|
||||
*/
|
||||
public function getSaltGenerator()
|
||||
{
|
||||
return $this->saltGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given username is a user.
|
||||
*
|
||||
* @param string $username
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function userExists($username)
|
||||
{
|
||||
return UserQuery::create()
|
||||
|
@ -58,11 +101,24 @@ class UserProvider implements UserProviderInterface
|
|||
->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a User.
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function createUser()
|
||||
{
|
||||
return new User();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an user.
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $password
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function registerUser(User $user, $password)
|
||||
{
|
||||
$user->setSalt($this->saltGenerator->generate());
|
||||
|
@ -70,11 +126,20 @@ class UserProvider implements UserProviderInterface
|
|||
$user
|
||||
->setRoles('ROLE_USER')
|
||||
->setPassword($this->encoder->encodePassword($password, $user->getSalt()))
|
||||
->setApiKey($this->saltGenerator->generate(32, true))
|
||||
->save();
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an user.
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $password
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function updateUserPassword(User $user, $password)
|
||||
{
|
||||
$user
|
||||
|
@ -84,6 +149,13 @@ class UserProvider implements UserProviderInterface
|
|||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a user by his username.
|
||||
*
|
||||
* @param string $username
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function loadUserByUsername($username)
|
||||
{
|
||||
$user = UserQuery::create()->findOneByUsername($username);
|
||||
|
@ -95,6 +167,40 @@ class UserProvider implements UserProviderInterface
|
|||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a user by his api key.
|
||||
*
|
||||
* @param string $apiKey
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function loadUserByApiKey($apiKey)
|
||||
{
|
||||
$user = UserQuery::create()->findOneByApiKey($apiKey);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the given password is the current user password.
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $password
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCurrentUserPassword(User $user, $password)
|
||||
{
|
||||
return $this->encoder->encodePassword($password, $user->getSalt()) === $user->getPassword();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh an user.
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function refreshUser(UserInterface $user)
|
||||
{
|
||||
if (!$user instanceof User) {
|
||||
|
@ -104,6 +210,13 @@ class UserProvider implements UserProviderInterface
|
|||
return $this->loadUserByUsername($user->getUsername());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the class is supported.
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supportsClass($class)
|
||||
{
|
||||
return $class === 'Gist\Model\User';
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
# Determine the RewriteBase automatically and set it as environment variable.
|
||||
# If you are using Apache aliases to do mass virtual hosting or installed the
|
||||
# project in a subdirectory, the base path will be prepended to allow proper
|
||||
# resolution of the app.php file and to redirect to the correct URI. It will
|
||||
# resolution of the index.php file and to redirect to the correct URI. It will
|
||||
# work in environments without path prefix as well, providing a safe, one-size
|
||||
# fits all solution. But as you do not need it in this case, you can comment
|
||||
# the following 2 lines to eliminate the overhead.
|
||||
|
@ -16,7 +16,7 @@
|
|||
RewriteRule ^(.*) - [E=BASE:%1]
|
||||
|
||||
# Redirect to URI without front controller to prevent duplicate content
|
||||
# (with and without `/app.php`). Only do this redirect on the initial
|
||||
# (with and without `/index.php`). Only do this redirect on the initial
|
||||
# rewrite by Apache and not on subsequent cycles. Otherwise we would get an
|
||||
# endless redirect loop (request -> rewrite to front controller ->
|
||||
# redirect -> request -> ...).
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
/* @override http://localhost/mark_story2/site/css/geshi.css */
|
||||
/**
|
||||
* GeSHi CSS Inspired by
|
||||
* TextMate Theme Dawn
|
||||
*
|
||||
* Copyright 2008 Mark Story
|
||||
*
|
||||
* This work is licensed under the Creative Commons Attribution-Share Alike 2.5 Canada License.
|
||||
* To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/2.5/ca/
|
||||
* or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
|
||||
*
|
||||
* @copyright Copyright 2008, Mark Story.
|
||||
* @link http://mark-story.com
|
||||
* @license http://creativecommons.org/licenses/by-sa/2.5/ca/
|
||||
*/
|
||||
|
||||
/*
|
||||
* Global geshi styles
|
||||
**********************/
|
||||
#main pre {
|
||||
line-height: 1.5em;
|
||||
font-size: 10px;
|
||||
white-space: normal;
|
||||
padding: 0;
|
||||
background: #E8EDF4;
|
||||
border: 1px solid #222;
|
||||
}
|
||||
pre ol {
|
||||
list-style: decimal;
|
||||
list-style-position: outside;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
#main pre ol li {
|
||||
margin: 0 0 0 35px;
|
||||
padding: 0;
|
||||
color: #333;
|
||||
clear: none;
|
||||
}
|
||||
pre ol li div {
|
||||
color:#000;
|
||||
}
|
||||
|
||||
/* Line highlights */
|
||||
.li1 {
|
||||
background: #E4E8EF;
|
||||
}
|
||||
|
||||
|
||||
/* comments */
|
||||
.co1,
|
||||
.coMULTI {
|
||||
color:#5A526E;
|
||||
}
|
||||
/* methods */
|
||||
.me1{
|
||||
color:#000;
|
||||
}
|
||||
.me0 {
|
||||
|
||||
}
|
||||
.me2 {
|
||||
color:#000;
|
||||
}
|
||||
|
||||
/* brackets */
|
||||
.br0 {
|
||||
color:#000;
|
||||
}
|
||||
|
||||
/* strings */
|
||||
.st0 {
|
||||
color:#0B6125;
|
||||
}
|
||||
|
||||
/* keywords */
|
||||
.kw1 {
|
||||
color: #794938;
|
||||
}
|
||||
.kw2 {
|
||||
color:#A71D5D;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.kw3 {
|
||||
color:#693A17;
|
||||
}
|
||||
|
||||
/* numbers */
|
||||
.nu0 {
|
||||
color:#811F24;
|
||||
}
|
||||
|
||||
/* vars */
|
||||
.re0 {
|
||||
color:#434A97;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CSS selectors
|
||||
*****************/
|
||||
/* classnames */
|
||||
|
||||
[lang=css] .kw2,
|
||||
.css .kw2 {
|
||||
color:#C24F24;
|
||||
}
|
||||
[lang=css] .kw1,
|
||||
.css .kw1 {
|
||||
color:#691C97;
|
||||
}
|
||||
[lang=css] .re0,
|
||||
.css .re0 {
|
||||
color: #C24F24;
|
||||
}
|
||||
.re1 {
|
||||
color: #C24F24;
|
||||
}
|
||||
/* px values */
|
||||
[lang=css] .re3,
|
||||
.css .re3 {
|
||||
color:#84252A;
|
||||
}
|
||||
|
||||
/*
|
||||
* Python
|
||||
****************/
|
||||
[lang=python] ol li div,
|
||||
.python ol li div {
|
||||
color: #000;
|
||||
}
|
||||
[lang=python] .kw2,
|
||||
.python .kw2 {
|
||||
font-style: normal;
|
||||
}
|
||||
[lang=python] .kw1 {
|
||||
color: #A91D5D;
|
||||
}
|
||||
/*
|
||||
* Javascript
|
||||
****************/
|
||||
[lang=javascript] .me1,
|
||||
.javascript .me1 {
|
||||
color: #794938;
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
/**
|
||||
* GeSHi CSS Inspired by TextMate
|
||||
* Theme Mac Classic
|
||||
*
|
||||
* Copyright 2008 Mark Story
|
||||
*
|
||||
* This work is licensed under the Creative Commons Attribution-Share Alike 2.5 Canada License.
|
||||
* To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/2.5/ca/
|
||||
* or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
|
||||
*
|
||||
* @copyright Copyright 2008, Mark Story.
|
||||
* @link http://mark-story.com
|
||||
* @license http://creativecommons.org/licenses/by-sa/2.5/ca/
|
||||
*/
|
||||
|
||||
/*
|
||||
* Global geshi styles
|
||||
**********************/
|
||||
#main pre {
|
||||
line-height: 1.5em;
|
||||
font-size: 10px;
|
||||
white-space: normal;
|
||||
padding: 0;
|
||||
background: #eae9e5;
|
||||
border: 1px solid #c0c0c0;
|
||||
}
|
||||
pre ol {
|
||||
list-style: decimal;
|
||||
list-style-position: outside;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
#main pre ol li {
|
||||
margin: 0 0 0 35px;
|
||||
padding: 0;
|
||||
color: #000;
|
||||
clear: none;
|
||||
}
|
||||
pre ol li div {
|
||||
color:#000;
|
||||
}
|
||||
|
||||
|
||||
/* php */
|
||||
pre[lang=php] ol div,
|
||||
pre.php ol div {
|
||||
color:#fd1435;
|
||||
}
|
||||
/* Line highlights */
|
||||
.li1 {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
/* Line highlights */
|
||||
.li1 {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
|
||||
/* comments */
|
||||
.co1,
|
||||
.coMULTI {
|
||||
color:#5c9dff;
|
||||
}
|
||||
/* methods */
|
||||
.me1{
|
||||
color:#000;
|
||||
}
|
||||
.me0 {
|
||||
|
||||
}
|
||||
.me2 {
|
||||
color:#000;
|
||||
}
|
||||
|
||||
/* brackets */
|
||||
.br0 {
|
||||
color:#222;
|
||||
}
|
||||
|
||||
/* strings */
|
||||
.st0 {
|
||||
color:#b701bb;
|
||||
}
|
||||
|
||||
/* keywords */
|
||||
.kw1 {
|
||||
color: #fd1435;
|
||||
}
|
||||
.kw2 {
|
||||
color:#6266f5;
|
||||
}
|
||||
|
||||
.kw3 {
|
||||
color:#505e80;
|
||||
}
|
||||
|
||||
/* numbers */
|
||||
.nu0 {
|
||||
color:#0a0aa5;
|
||||
}
|
||||
|
||||
/* vars */
|
||||
.re0 {
|
||||
color:#059532;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CSS selectors
|
||||
*****************/
|
||||
/* classnames */
|
||||
|
||||
[lang=css] .kw2,
|
||||
.css .kw2 {
|
||||
color:#0a0aa5;
|
||||
}
|
||||
.re1 {
|
||||
color: #b701bb;
|
||||
}
|
||||
/* px values */
|
||||
[lang=css] .re3,
|
||||
.css .re3 {
|
||||
color:#059532;
|
||||
}
|
||||
|
||||
/*
|
||||
* Javascript
|
||||
****************/
|
||||
[lang=javascript] .me1,
|
||||
.javascript .me1 {
|
||||
color:#505e80;
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
/* @override http://localhost/mark_story2/site/css/geshi.css */
|
||||
/**
|
||||
* GeSHi CSS Inspired by TextMate
|
||||
* Theme Twilight
|
||||
*
|
||||
* Copyright 2008 Mark Story
|
||||
*
|
||||
* This work is licensed under the Creative Commons Attribution-Share Alike 2.5 Canada License.
|
||||
* To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/2.5/ca/
|
||||
* or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
|
||||
*
|
||||
* @filesource
|
||||
* @copyright Copyright 2008, Mark Story.
|
||||
* @link http://mark-story.com
|
||||
* @license http://creativecommons.org/licenses/by-sa/2.5/ca/
|
||||
*/
|
||||
/*
|
||||
* Global geshi styles
|
||||
**********************/
|
||||
#main pre {
|
||||
line-height: 1.5em;
|
||||
font-size: 10px;
|
||||
white-space: normal;
|
||||
padding: 0;
|
||||
background: #222223;
|
||||
border: 1px solid #222;
|
||||
}
|
||||
pre ol {
|
||||
list-style: decimal;
|
||||
list-style-position: outside;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
#main pre ol li {
|
||||
margin: 0 0 0 35px;
|
||||
padding: 0;
|
||||
color: #000;
|
||||
clear: none;
|
||||
}
|
||||
pre ol li div {
|
||||
color:#f8f8f8;
|
||||
}
|
||||
|
||||
/* Line highlights */
|
||||
.li1 {
|
||||
background: #202021;
|
||||
}
|
||||
|
||||
|
||||
/* comments */
|
||||
.co1,
|
||||
.coMULTI {
|
||||
color:#5F5A60;
|
||||
}
|
||||
/* methods */
|
||||
.me1{
|
||||
color:#fff;
|
||||
}
|
||||
.me0 {
|
||||
|
||||
}
|
||||
.me2 {
|
||||
color:#000;
|
||||
}
|
||||
|
||||
/* brackets */
|
||||
.br0 {
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
/* strings */
|
||||
.st0 {
|
||||
color:#8F9657;
|
||||
}
|
||||
|
||||
/* keywords */
|
||||
.kw1 {
|
||||
color: #CDA869;
|
||||
}
|
||||
.kw2 {
|
||||
color:#F9EE98;
|
||||
}
|
||||
|
||||
.kw3 {
|
||||
color:#505e80;
|
||||
}
|
||||
|
||||
/* numbers */
|
||||
.nu0 {
|
||||
color:#CF6745;
|
||||
}
|
||||
|
||||
/* vars */
|
||||
.re0 {
|
||||
color:#7587A6;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CSS selectors
|
||||
*****************/
|
||||
/* classnames */
|
||||
|
||||
[lang=css] .kw2,
|
||||
.css .kw2 {
|
||||
color:#F9EE7E;
|
||||
}
|
||||
.re1 {
|
||||
color: #96703D;
|
||||
}
|
||||
/* px values */
|
||||
[lang=css] .re3,
|
||||
.css .re3 {
|
||||
color:#CA7840;
|
||||
}
|
||||
|
||||
/*
|
||||
* Javascript
|
||||
****************/
|
||||
[lang=javascript] .me1,
|
||||
.javascript .me1 {
|
||||
color:#505e80;
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
/* @override http://localhost/mark_story2/site/css/geshi.css */
|
||||
/**
|
||||
* GeSHi CSS Inspired by TextMate
|
||||
* Theme Vibrant Ink
|
||||
*
|
||||
* Copyright 2008 Mark Story
|
||||
*
|
||||
* This work is licensed under the Creative Commons Attribution-Share Alike 2.5 Canada License.
|
||||
* To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/2.5/ca/
|
||||
* or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
|
||||
*
|
||||
* @copyright Copyright 2008, Mark Story.
|
||||
* @link http://mark-story.com
|
||||
* @license http://creativecommons.org/licenses/by-sa/2.5/ca/
|
||||
*/
|
||||
|
||||
/*
|
||||
* Global geshi styles
|
||||
**********************/
|
||||
#main pre {
|
||||
line-height: 1.5em;
|
||||
font-size: 10px;
|
||||
white-space: normal;
|
||||
padding: 0;
|
||||
background: #000;
|
||||
border: 1px solid #222;
|
||||
}
|
||||
pre ol {
|
||||
list-style: decimal;
|
||||
list-style-position: outside;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
#main pre ol li {
|
||||
margin: 0 0 0 35px;
|
||||
padding: 0;
|
||||
color: #fff;
|
||||
clear: none;
|
||||
}
|
||||
pre ol li div {
|
||||
/*color: #F26100;*/
|
||||
color: #D2C0B4;
|
||||
}
|
||||
|
||||
/* Line highlights */
|
||||
.li1 {
|
||||
background: #030303;
|
||||
}
|
||||
|
||||
|
||||
/* comments */
|
||||
.co1,
|
||||
.coMULTI {
|
||||
color:#7830CC;
|
||||
}
|
||||
/* methods */
|
||||
.me1{
|
||||
color:#fff;
|
||||
}
|
||||
.me0 {
|
||||
|
||||
}
|
||||
.me2 {
|
||||
color:#000;
|
||||
}
|
||||
|
||||
/* brackets */
|
||||
.br0 {
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
/* strings */
|
||||
.st0 {
|
||||
color:#52F700;
|
||||
}
|
||||
|
||||
/* keywords */
|
||||
.kw1 {
|
||||
color: #C6C765;
|
||||
}
|
||||
.kw2 {
|
||||
color:#FFC500;
|
||||
}
|
||||
|
||||
.kw3 {
|
||||
color:#505e80;
|
||||
}
|
||||
|
||||
/* numbers */
|
||||
.nu0 {
|
||||
color:#319994;
|
||||
}
|
||||
|
||||
/* vars */
|
||||
.re0 {
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CSS selectors
|
||||
*****************/
|
||||
/* classnames */
|
||||
|
||||
[lang=css] .kw2,
|
||||
.css .kw2 {
|
||||
color:#fff;
|
||||
}
|
||||
[lang=css] .kw1,
|
||||
.css .kw1 {
|
||||
color:#999966;
|
||||
}
|
||||
[lang=css] .re0,
|
||||
.css .re0 {
|
||||
color: #F26100;
|
||||
}
|
||||
.re1 {
|
||||
color: #96703D;
|
||||
}
|
||||
/* px values */
|
||||
[lang=css] .re3,
|
||||
.css .re3 {
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Python
|
||||
****************/
|
||||
[lang=python] ol li div,
|
||||
.python ol li div {
|
||||
color: #fff;
|
||||
}
|
||||
[lang=python] .kw1,
|
||||
.python .kw1 {
|
||||
color:#F26100;
|
||||
}
|
||||
|
||||
/*
|
||||
* Javascript
|
||||
****************/
|
||||
[lang=javascript] .me1,
|
||||
.javascript .me1 {
|
||||
color:#fff;
|
||||
}
|
||||
pre[lang=javascript] ol li div,
|
||||
.javascript ol li div {
|
||||
color: #fff;
|
||||
}
|
121
web/app/css/prism-okaidia.css
Normal file
121
web/app/css/prism-okaidia.css
Normal file
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* okaidia theme for JavaScript, CSS and HTML
|
||||
* Loosely based on Monokai textmate theme by http://www.monokai.nl/
|
||||
* @author ocodia
|
||||
*/
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: #f8f8f2;
|
||||
background: none;
|
||||
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #272822;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #f92672;
|
||||
}
|
||||
|
||||
.token.boolean,
|
||||
.token.number {
|
||||
color: #ae81ff;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #a6e22e;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string,
|
||||
.token.variable {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.function {
|
||||
color: #e6db74;
|
||||
}
|
||||
|
||||
.token.keyword {
|
||||
color: #66d9ef;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important {
|
||||
color: #fd971f;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
148
web/app/css/prism-solarizedlight.css
Normal file
148
web/app/css/prism-solarizedlight.css
Normal file
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
Solarized Color Schemes originally by Ethan Schoonover
|
||||
http://ethanschoonover.com/solarized
|
||||
|
||||
Ported for PrismJS by Hector Matos
|
||||
Website: https://krakendev.io
|
||||
Twitter Handle: https://twitter.com/allonsykraken)
|
||||
*/
|
||||
|
||||
/*
|
||||
SOLARIZED HEX
|
||||
--------- -------
|
||||
base03 #002b36
|
||||
base02 #073642
|
||||
base01 #586e75
|
||||
base00 #657b83
|
||||
base0 #839496
|
||||
base1 #93a1a1
|
||||
base2 #eee8d5
|
||||
base3 #fdf6e3
|
||||
yellow #b58900
|
||||
orange #cb4b16
|
||||
red #dc322f
|
||||
magenta #d33682
|
||||
violet #6c71c4
|
||||
blue #268bd2
|
||||
cyan #2aa198
|
||||
green #859900
|
||||
*/
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: #657b83; /* base00 */
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
||||
background: #073642; /* base02 */
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
||||
background: #073642; /* base02 */
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background-color: #fdf6e3; /* base3 */
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: #93a1a1; /* base1 */
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #586e75; /* base01 */
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #268bd2; /* blue */
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.url,
|
||||
.token.inserted {
|
||||
color: #2aa198; /* cyan */
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
color: #657b83; /* base00 */
|
||||
background: #eee8d5; /* base2 */
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #859900; /* green */
|
||||
}
|
||||
|
||||
.token.function {
|
||||
color: #b58900; /* yellow */
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #cb4b16; /* orange */
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
467
web/app/css/prism.css
Normal file
467
web/app/css/prism.css
Normal file
|
@ -0,0 +1,467 @@
|
|||
/* 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 */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #a67f59;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
pre[data-line] {
|
||||
position: relative;
|
||||
padding: 1em 0 1em 3em;
|
||||
}
|
||||
|
||||
.line-highlight {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: inherit 0;
|
||||
margin-top: 1em; /* Same as .prism’s padding-top */
|
||||
|
||||
background: hsla(24, 20%, 50%,.08);
|
||||
background: linear-gradient(to right, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
line-height: inherit;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.line-highlight:before,
|
||||
.line-highlight[data-end]:after {
|
||||
content: attr(data-start);
|
||||
position: absolute;
|
||||
top: .4em;
|
||||
left: .6em;
|
||||
min-width: 1em;
|
||||
padding: 0 .5em;
|
||||
background-color: hsla(24, 20%, 50%,.4);
|
||||
color: hsl(24, 20%, 95%);
|
||||
font: bold 65%/1.5 sans-serif;
|
||||
text-align: center;
|
||||
vertical-align: .3em;
|
||||
border-radius: 999px;
|
||||
text-shadow: none;
|
||||
box-shadow: 0 1px white;
|
||||
}
|
||||
|
||||
.line-highlight[data-end]:after {
|
||||
content: attr(data-end);
|
||||
top: auto;
|
||||
bottom: .4em;
|
||||
}
|
||||
|
||||
pre.line-numbers {
|
||||
position: relative;
|
||||
padding-left: 3.8em;
|
||||
counter-reset: linenumber;
|
||||
}
|
||||
|
||||
pre.line-numbers > code {
|
||||
position: relative;
|
||||
white-space: inherit;
|
||||
}
|
||||
|
||||
.line-numbers .line-numbers-rows {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
top: 0;
|
||||
font-size: 100%;
|
||||
left: -3.8em;
|
||||
width: 3em; /* works for line-numbers below 1000 lines */
|
||||
letter-spacing: -1px;
|
||||
border-right: 1px solid #999;
|
||||
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
}
|
||||
|
||||
.line-numbers-rows > span {
|
||||
pointer-events: none;
|
||||
display: block;
|
||||
counter-increment: linenumber;
|
||||
}
|
||||
|
||||
.line-numbers-rows > span:before {
|
||||
content: counter(linenumber);
|
||||
color: #999;
|
||||
display: block;
|
||||
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;
|
||||
}
|
|
@ -26,54 +26,24 @@ body {
|
|||
margin-right: 4px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #222;
|
||||
border: #222;
|
||||
color: #ddd;
|
||||
#viewer pre, #viewer code, #revisions pre, #revisions code, #embed pre, #embed code {
|
||||
background: none !important;
|
||||
white-space: pre-wrap;
|
||||
white-space: -moz-pre-wrap;
|
||||
white-space: -pre-wrap;
|
||||
white-space: -o-pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
pre ol {
|
||||
padding-left: 50px !important;
|
||||
}
|
||||
|
||||
pre li:hover {
|
||||
background: #444;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.panel-heading .actions {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
div.diff {
|
||||
.diff {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.de1 {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.li1 {
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.re8 {
|
||||
color: #52F700;
|
||||
}
|
||||
|
||||
.kw3 {
|
||||
color: #C6C765;
|
||||
}
|
||||
|
||||
#viewer .syntaxhighlighter td {
|
||||
vertical-align: top !important;
|
||||
}
|
||||
|
||||
#options {
|
||||
margin-bottom: 17px;
|
||||
}
|
48
web/app/css/themes/light.css
Normal file
48
web/app/css/themes/light.css
Normal file
|
@ -0,0 +1,48 @@
|
|||
.navbar {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#form_content {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#languages {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
#languages .btn-group:first-child {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
background: none !important;
|
||||
white-space: pre-wrap !important;
|
||||
white-space: -moz-pre-wrap !important;
|
||||
white-space: -pre-wrap !important;
|
||||
white-space: -o-pre-wrap !important;
|
||||
word-wrap: break-word !important;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.panel-heading .actions {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.diff {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#options {
|
||||
margin-bottom: 17px;
|
||||
}
|
||||
|
||||
.btn-delete {
|
||||
background: #DE3336;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-error:active, .btn-error:hover, .btn-error:focus {
|
||||
color: #000;
|
||||
}
|
|
@ -66,36 +66,55 @@ var editorEvents = function() {
|
|||
$(this).trigger('change');
|
||||
});
|
||||
|
||||
var key = getKey();
|
||||
|
||||
if (key) {
|
||||
$('.show-diff').each(function() {
|
||||
var href = $(this).attr('href');
|
||||
href = href.replace('#', '#key=' + key + '&');
|
||||
|
||||
$(this).attr('href', href);
|
||||
});
|
||||
}
|
||||
|
||||
$('.show-diff').click(function() {
|
||||
$($(this).data('target')).toggle();
|
||||
});
|
||||
|
||||
if ((document.location.href).indexOf('#diff-') !== -1) {
|
||||
var diffLinkTest1 = (document.location.href).indexOf('#diff-') !== -1;
|
||||
var diffLinkTest2 = (document.location.href).indexOf('&diff-') !== -1;
|
||||
|
||||
if (diffLinkTest1 || diffLinkTest2) {
|
||||
var anchor = '#' + (document.location.href).toString().split('#')[1];
|
||||
|
||||
$('.show-diff[href="' + anchor + '"]').click();
|
||||
document.location.href = anchor;
|
||||
}
|
||||
}
|
||||
|
||||
var myEvents = function() {
|
||||
$('.btn-delete').click(function() {
|
||||
$('#delete_id').val($(this).data('id'));
|
||||
$('#form-deletion form').submit();
|
||||
if (confirm(trans('form.confirm'))) {
|
||||
$('#delete_id').val($(this).data('id'));
|
||||
$('#form-deletion form').submit();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('change keyup keydown', '#form-api-key', function() {
|
||||
$(this).val($(this).data('key'));
|
||||
});
|
||||
}
|
||||
|
||||
var mainEditorEvents = function() {
|
||||
$('.cipher-input').change(function() {
|
||||
if ($('.cipher-input:checked').val() === 'yes') {
|
||||
$('#cipher-alert').removeClass('hide');
|
||||
} else {
|
||||
$('#cipher-alert').addClass('hide');
|
||||
}
|
||||
});
|
||||
|
||||
$('#main-form').submit(function(e) {
|
||||
if ($('.cipher-input:checked').val() === 'yes') {
|
||||
var passphrase = randomString(256, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
|
||||
if ($('.cipher-input:checked').val() === 'yes' || typeof cipherGistClone !== 'undefined') {
|
||||
var key = getKey();
|
||||
|
||||
if (key) {
|
||||
var passphrase = key;
|
||||
} else {
|
||||
var passphrase = randomString(256, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
|
||||
}
|
||||
|
||||
var content = $('#form_content').val();
|
||||
var encrypted = CryptoJS.AES.encrypt(content, passphrase, {
|
||||
format: JsonFormatter
|
||||
|
@ -112,36 +131,55 @@ var getKey = function() {
|
|||
var parts = url.split('#key=');
|
||||
|
||||
if (parts.length === 2) {
|
||||
return parts[1];
|
||||
return parts[1].split('&')[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var viewerEvents = function() {
|
||||
var $render = $('.syntaxhighlighter');
|
||||
var $render = $('#viewer code[data-cipher]');
|
||||
|
||||
$(document).ready(function() {
|
||||
var key = getKey();
|
||||
var $embedInput = $('#embed-input');
|
||||
|
||||
var $cipherEditor = $('.cipher-editor');
|
||||
var $embedCode = $('#embed code');
|
||||
|
||||
var to = ' ';
|
||||
|
||||
if (0 !== $render.length && key) {
|
||||
var decrypted = CryptoJS.AES.decrypt($render.html(), key, {
|
||||
format: JsonFormatter
|
||||
});
|
||||
$render.text(decrypted.toString(CryptoJS.enc.Utf8));
|
||||
SyntaxHighlighter.all();
|
||||
if (key) {
|
||||
$('.cipher-link').each(function() {
|
||||
var href = $(this).attr('href');
|
||||
href = href + '#key=' + key;
|
||||
|
||||
to = ' data-key="#key=' + key + '" ';
|
||||
|
||||
$('.lang').each(function() {
|
||||
$(this).attr('href', $(this).attr('href') + '#key=' + key);
|
||||
$(this).attr('href', href);
|
||||
});
|
||||
|
||||
if (0 !== $render.length || $cipherEditor.length !== 0) {
|
||||
if ($render.length !== 0) {
|
||||
var decrypted = CryptoJS.AES.decrypt($render.html(), key, {
|
||||
format: JsonFormatter
|
||||
});
|
||||
|
||||
$render.text(decrypted.toString(CryptoJS.enc.Utf8));
|
||||
$render.attr('class', $render.data('class'));
|
||||
Prism.highlightAll();
|
||||
|
||||
to = ' data-key="#key=' + key + '" ';
|
||||
} else {
|
||||
var decrypted = CryptoJS.AES.decrypt($cipherEditor.val(), key, {
|
||||
format: JsonFormatter
|
||||
});
|
||||
|
||||
$cipherEditor.val(decrypted.toString(CryptoJS.enc.Utf8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($embedInput.length) {
|
||||
$embedInput.val($embedInput.val().replace('%key%', to));
|
||||
if ($embedCode.length) {
|
||||
$embedCode.html($embedCode.html().replace('%key%', to));
|
||||
Prism.highlightAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
29
web/app/js/prism.js
Normal file
29
web/app/js/prism.js
Normal file
File diff suppressed because one or more lines are too long
BIN
web/favicon.png
Normal file
BIN
web/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 570 B |
Loading…
Reference in a new issue