1
0
Fork 0
forked from deblan/gist

Compare commits

...

47 commits

Author SHA1 Message Date
Showfom 3d1a6f8158 Added Simplified Chinese Translation
Added Simplified Chinese Translation
2017-06-03 16:59:28 +02:00
Simon Vieille 3fcdae9270 Default configuration values 2017-04-24 01:23:08 +02:00
Simon Vieille 2f1d57d36b Merge branch 'dev-master' 2017-04-24 01:19:15 +02:00
Simon Vieille 0b6c4d7ac1 Fix theme name typo 2017-04-24 01:18:21 +02:00
Simon Vieille 4a035ba7ed Mage conf file renamed 2017-04-24 01:16:22 +02:00
Simon Vieille 706823c04a Config file ignored 2017-04-24 01:15:04 +02:00
Simon Vieille 87bd9e9db5 Documentation 2017-04-24 01:11:54 +02:00
Simon Vieille 48eacc5cb8 New configuration file 2017-04-24 01:11:39 +02:00
Simon Vieille df86035225 Default and light themes 2017-04-24 01:11:09 +02:00
Simon Vieille 43db48a715 Merge branch 'dev-master' 2017-04-23 16:59:29 +02:00
Simon Vieille c7a03c4bf9 Indent 2017-04-23 16:57:27 +02:00
Simon Vieille 61b071feb8 Merge branch 'dev-master' 2017-04-23 16:31:11 +02:00
Simon Vieille 36f4fb98ff Title filter in account page 2017-04-23 16:30:26 +02:00
Simon Vieille 9dfbde6730 Title filter in account page 2017-04-23 16:29:41 +02:00
Simon Vieille 74b23a59b5 README updated to explain upgrade (issue #9) 2017-02-16 10:51:39 +01:00
Simon Vieille 2702a1d987 README updated to explain upgrade (issue #9) 2017-02-16 10:50:21 +01:00
Simon Vieille 221e28832f README updated to explain upgrade (issue #9) 2017-02-15 16:18:29 +01:00
Simon Vieille ab47699731 Fix issue #9 2017-02-15 16:13:34 +01:00
Simon Vieille eb1f4f1df3 README: upgrade 2016-12-23 11:23:58 +01:00
Simon Vieille 0e02add6e6 Adding of field that contains the number of commits 2016-12-23 11:17:56 +01:00
Simon Vieille df3035f2f1 Form to change the password 2016-12-23 10:28:09 +01:00
Simon Vieille a79b443b36 DE translations 2016-12-21 22:55:06 +01:00
Simon Vieille 82790998d4 DE translations 2016-12-21 22:52:23 +01:00
Simon Vieille 63ba878e80 Fix typo 2016-11-17 00:13:09 +01:00
Simon Vieille b0f74d8dc8 PHP Documentation 2016-11-13 00:44:23 +01:00
Simon Vieille aa73578efe Gitignore 2016-11-13 00:43:42 +01:00
Simon Vieille 3f29ab2574 gitignore 2016-10-29 16:12:08 +02:00
Simon Vieille 22cf9ab3e5 gitignore 2016-10-29 16:10:22 +02:00
Simon Vieille b7b93460b1 Contributors 2016-10-03 23:35:26 +02:00
Simon Vieille f27f74e0e9 Webserver documentation 2016-10-03 23:32:01 +02:00
Simon Vieille 7bba890025 Webserver documentation 2016-10-03 23:29:49 +02:00
Simon Vieille 3e297ec85f German disabled 2016-10-03 19:23:29 +02:00
Simon Vieille 41fefddc16 Issue fixed 2016-10-03 19:11:26 +02:00
Simon Vieille 67c674c52e Spanish translations 2016-10-03 19:10:34 +02:00
Simon Vieille 724fee29bb Contributors 2016-10-03 17:41:01 +02:00
Simon Vieille be31903aaa README 2016-10-02 20:31:08 +02:00
Simon Vieille 12e6efc471 README 2016-09-28 09:18:33 +02:00
Simon Vieille 2f65088c5e README 2016-09-28 00:49:50 +02:00
Simon Vieille 1b961403ad README 2016-09-28 00:43:54 +02:00
Simon Vieille 841211a63a README 2016-09-28 00:25:47 +02:00
Simon Vieille 1006e079f6 Indent 2016-09-26 17:42:39 +02:00
Simon Vieille da7bb48893 Cipher: translation removed 2016-09-26 17:41:57 +02:00
Simon Vieille 0529ec16d0 [security] XSS injection patch 2016-09-26 01:41:08 +02:00
Simon Vieille 2ddfea60cc Cipher: embeded links updated 2016-09-24 16:42:13 +02:00
Simon Vieille f25e427dcc Cipher alert removed 2016-09-24 16:10:32 +02:00
Simon Vieille 96f199adaa Fix issue #1 2016-09-24 16:07:35 +02:00
Simon Vieille 6ab827bf94 Ciphered GIST cloning 2016-09-24 14:43:15 +02:00
67 changed files with 1928 additions and 558 deletions

22
.gitignore vendored
View file

@ -1,9 +1,13 @@
.mage/config/environment/*.yml
.mage/logs/*.log
composer.lock
vendor/
tags
*.swp
propel.yml
src/Gist/Model/Base/
src/Gist/Model/Map/
/.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/

View file

@ -8,6 +8,7 @@ deployment:
- "*.svn"
- "*.git"
- "*.swp"
- "app/config/config.yml"
- "app/config/propel/"
- "app/propel/"
- "data/git"

View file

@ -31,6 +31,7 @@ update:
@echo "-----------------------------------"
@echo
sh -c 'test -d app && $(GIT) add app && $(GIT) commit -m "Configuration"'
$(GIT) pull origin master
${MKDIR} -p data/git
$(COMPOSER) update

287
README.md
View file

@ -3,16 +3,18 @@ Table of Contents
* [GIST](#gist)
* [Requirements](#requirements)
* [Installation](#installation)
* [Git](#git)
* [Composer](#composer)
* [Bower](#bower)
* [Upgrade](#upgrade)
* [Installation](#installation)
* [Upgrade](#upgrade)
* [Configuration](#configuration)
* [Makefile](#makefile)
* [API](#api)
* [Console](#console)
* [Configuration](#configuration)
* [Deployment](#deployment)
* [Contributors](#contributors)
GIST
====
@ -34,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.
@ -60,57 +48,131 @@ 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, run: `app/console migrate:to:v1.4.1`.
If you upgrade to v1.4.4 or more, the configuration is moved to a `app/config/config.yml`: `$ cp app/config/config.yml.dist app/config/config.yml` and see the [configuration section](#configuration) for more information.
Configuration
-------------
### Version < 1.4.4
Edit `app/bootstrap.php.d/70-security.php`.
* `$app['token']`: the securty token (a strong passphrase).
* `$app['enable_registration']`: defines if the registration is allowed (`true` or `false`)
* `$app['enable_login']`: defines if the login is allowed (`true` or `false`)
* `$app['login_required_to_edit_gist']`: defines if the user must be logged to create or clone a Gist (`true` or `false`)
* `$app['login_required_to_view_gist']`: defines if the user must be logged to view a Gist (`true` or `false`)
* `$app['login_required_to_view_gist']`: defines if the user must be logged to view an embeded Gist (`true` or `false`)
If you install Gist on your server, you have to modify the `base_uri` of the API.
Edit `app/bootstrap.php.d/60-api.php` and replace `https://gist.deblan.org/`.
### Version >= 1.4.4
Edit `app/config/config.yml`.
* `security.token`: the securty token (a strong passphrase)
* `security.enable_registration`: defines if the registration is allowed (`true` or `false`)
* `security.enable_login`: defines if the login is allowed (`true` or `false`)
* `security.login_required_to_edit_gist`: defines if the user must be logged to create or clone a Gist (`true` or `false`)
* `security.login_required_to_view_gist`: defines if the user must be logged to view a Gist (`true` or `false`)
* `security.login_required_to_view_gist`: defines if the user must be logged to view an embeded Gist (`true` or `false`)
* `api.base_uri`: The url of your instance.
* `data.path`: the path where the files are saved.
* `git.path`: The path of `git`.
* `theme.name`: the name of the theme (`dark` or `light`)
Makefile
--------
@ -130,15 +192,18 @@ API
**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": {
@ -151,22 +216,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": {
@ -179,90 +245,49 @@ Params:
"UpdatedAt": "2015-07-19T16:30:15Z"
}
}
```
* Code ```405```: Method Not Allowed
* Code ```400```: Bad Request
```
Console
-------
### Create and update gists:
```
$ 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`.
#### Force registration/login
##### Login required to edit a gist
Edit `app/bootstrap.php.d/70-security.php` and modify the value of `$app['login_required_to_edit_gist']` with `true`.
##### Login required to view a gist
Edit `app/bootstrap.php.d/70-security.php` and modify the value of `$app['login_required_to_view_gist']` with `true`.
##### Login required to view an embeded gist
Edit `app/bootstrap.php.d/70-security.php` and modify the value of `$app['login_required_to_view_embeded_gist']` with `true`.
### Debug
`app_dev.php` is the development router. Access is granted for an IP range defined in the same file.
* **Create a gist**: `$ app/console --help create`
* **Update a gist**: `$ app/console --help update`
* **Create user**: `app/console --help user:create`
* **Show stats**: `$ app/console --help stats`
Deployment
----------
Gist uses [Magallanes](http://magephp.com/) to manage deployment.
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

View file

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

View file

@ -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'),
));
$app['translator'] = $app->extend('translator', function ($translator, $app) {

View file

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

View file

@ -3,5 +3,5 @@
use Gist\Api\Client;
$app['api_client'] = $app->share(function ($app) {
return new Client(['base_uri' => 'https://gist.deblan.org/']);
return new Client(['base_uri' => $app['settings']['api']['base_uri']]);
});

View file

@ -6,20 +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['login_required_to_edit_gist'] = false;
$app['login_required_to_view_gist'] = false;
$app['login_required_to_view_embeded_gist'] = false;
$securitySettings = $app['settings']['security'];
$app['token'] = 'ThisTokenIsNotSoSecretChangeIt';
$app['token'] = $securitySettings['token'];
$app['salt_generator'] = $app->share(function($app) {
$app['salt_generator'] = $app->share(function ($app) {
return new SaltGenerator();
});
@ -34,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']
);
});
@ -46,7 +40,7 @@ $app['security.authentication_listener.factory.form'] = $app->protect(function (
'security.authentication_provider.'.$name.'.form',
'security.authentication_listener.'.$name.'.form',
null,
'pre_auth'
'pre_auth',
];
});
@ -76,32 +70,31 @@ $firewall = [
],
'security.access_rules' => [
['^/[a-z]{2}/my.*$', 'ROLE_USER'],
]
],
];
if ($app['login_required_to_edit_gist'] || $app['login_required_to_view_gist'] || $app['login_required_to_view_embeded_gist']) {
$securityRegexp = '^/[a-z]{2}';
if ($securitySettings['login_required_to_edit_gist'] || $securitySettings['login_required_to_view_gist'] || $securitySettings['login_required_to_view_embeded_gist']) {
$exceptedUriPattern = ['login', 'register'];
if ($app['login_required_to_view_gist'] === true) {
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 ($app['login_required_to_view_embeded_gist'] === true) {
if ($securitySettings['login_required_to_view_embeded_gist'] === true) {
$firewall['security.access_rules'][] = ['^/[a-z]{2}/embed.*$', 'ROLE_USER'];
} else {
$exceptedUriPattern[] = 'embed';
}
if ($app['login_required_to_edit_gist'] === true) {
$firewall['security.access_rules'][] = ['^/[a-z]{2}/(?!('.implode('|', $exceptedUriPattern).')).*$', 'ROLE_USER'];
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());

View file

@ -0,0 +1,15 @@
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:
base_url: 'https://gist.deblan.org/'
data:
path: data/git
git:
path: /usr/bin/git
theme:
name: dark

View file

@ -5,6 +5,7 @@ use Gist\Command\CreateCommand;
use Gist\Command\UpdateCommand;
use Gist\Command\StatsCommand;
use Gist\Command\UserCreateCommand;
use Gist\Command\Migration\UpgradeTo1p4p1Command;
$app = require __DIR__.'/bootstrap.php';
@ -12,5 +13,6 @@ $app['console']->add(new CreateCommand());
$app['console']->add(new UpdateCommand());
$app['console']->add(new StatsCommand());
$app['console']->add(new UserCreateCommand());
$app['console']->add(new UpgradeTo1p4p1Command());
$app['console']->run();

102
app/locales/cn.yml Normal file
View file

@ -0,0 +1,102 @@
app:
title: '#!GIST'
title_prefix: '#!GIST - '
menu:
home:
title: '首页'
about:
title: '关于'
my:
login:
title: '登陆'
logout:
title: '注销'
register:
title: '注册'
my:
title: '账号'
my:
title: '我的 Gist'
nothing: '这家伙很懒,暂时没有内容'
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'

102
app/locales/de.yml Normal file
View file

@ -0,0 +1,102 @@
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)!'
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'

View file

@ -59,16 +59,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 +78,9 @@ form:
no: 'No'
submit: 'Send'
filter: 'Filter'
confirm: 'Do you confirm?'
success:
password: 'Password updated.'
gist: 'Gist removed.'
type:
label: 'Language: %value%'

102
app/locales/es.yml Normal file
View file

@ -0,0 +1,102 @@
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.'
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'

View file

@ -59,17 +59,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 +78,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%'

View file

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

View file

@ -7,7 +7,6 @@ propel:
dsn: "mysql:host=localhost;dbname=gist"
user: root
password: root
attributes:
settings:
charset: utf8
queries:

View file

@ -5,14 +5,34 @@ 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 updating
*
* @const string
*/
const UPDATE = '/en/api/update/{gist}';
/**
* 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(
@ -34,7 +54,15 @@ 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(

View file

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

View file

@ -8,8 +8,16 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
/**
* class CreateCommand.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class CreateCommand extends Command
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$types = implode(', ', $this->getTypes());
@ -45,6 +53,9 @@ EOF
);
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
//$output->writeln(sprintf('<comment>%s</comment> bar.', 'test'));
@ -88,7 +99,7 @@ EOF
return true;
}
if ($input->getOption('show-id')) {
$output->writeln($gist['gist']['Id']);
@ -98,6 +109,11 @@ EOF
$output->writeln(json_encode($gist));
}
/**
* Returns the list of types.
*
* @return array
*/
protected function getTypes()
{
$types = array(

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

View file

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

View file

@ -8,8 +8,16 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
/**
* class UpdateCommand.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class UpdateCommand extends Command
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$types = implode(', ', $this->getTypes());
@ -44,6 +52,9 @@ EOF
);
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
//$output->writeln(sprintf('<comment>%s</comment> bar.', 'test'));
@ -80,7 +91,7 @@ EOF
return true;
}
if ($input->getOption('show-id')) {
$output->writeln($gist['gist']['Id']);
@ -90,6 +101,11 @@ EOF
$output->writeln(json_encode($gist));
}
/**
* Returns the list of types.
*
* @return array
*/
protected function getTypes()
{
$types = array(

View file

@ -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 = '';

View file

@ -10,7 +10,8 @@ use Gist\Model\GistQuery;
use Gist\Form\ApiUpdateGistForm;
/**
* Class ApiController
* Class ApiController.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class ApiController extends Controller

View file

@ -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
{
/**
* @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();
@ -67,6 +95,13 @@ class Controller
);
}
/**
* 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,6 +125,11 @@ class Controller
return $app['gist']->getContent($gist, $commit);
}
/**
* Returns the connected user.
*
* @return mixed
*/
public function getUser()
{
$app = $this->getApp();
@ -110,6 +150,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();

View file

@ -10,11 +10,19 @@ use GitWrapper\GitException;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* 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();
@ -44,6 +52,13 @@ class EditController extends Controller
);
}
/**
* Cloning page.
*
* @param Request $request
*
* @return string
*/
public function cloneAction(Request $request, $gist, $commit)
{
$app = $this->getApp();

View file

@ -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']['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']['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 : '',
]
);

View file

@ -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,13 +55,14 @@ class MyController extends Controller
$options = $filterForm->getData();
}
}
$gists = $this->getUser()->getGistsPager($page, $options);
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) {
@ -59,16 +73,41 @@ 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,
'deleteForm' => $deleteForm->createView(),
'filterForm' => $filterForm->createView(),
'passwordForm' => $passwordForm->createView(),
'deleted' => !empty($deleted),
)
);
}

View file

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

View file

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

View file

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

View file

@ -3,11 +3,15 @@
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);

View file

@ -3,11 +3,15 @@
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);

View file

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

View file

@ -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(
@ -69,6 +73,11 @@ class CreateGistForm extends AbstractForm
return $this->builder;
}
/**
* Returns the types for generating the form.
*
* @return array
*/
protected function getTypes()
{
$types = array(
@ -78,7 +87,7 @@ class CreateGistForm extends AbstractForm
'php' => '',
'sql' => '',
'xml' => '',
'yaml'=> '',
'yaml' => '',
'perl' => '',
'c' => '',
'asp' => '',

View file

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

View file

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

View file

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

View 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';
}
}

View file

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

View file

@ -4,8 +4,20 @@ namespace Gist\Model;
use Gist\Model\Base\Gist as BaseGist;
/**
* 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,11 +33,21 @@ class Gist extends BaseGist
return $this;
}
/**
* Generates a unique filename.
*
* @return string
*/
public function generateFilename()
{
$this->setFile(uniqid());
}
/**
* Returns the type for Geshi.
*
* @return string
*/
public function getGeshiType()
{
$data = array(
@ -35,11 +57,16 @@ class Gist extends BaseGist
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 +76,14 @@ 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;
}
}

View file

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

View file

@ -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,6 +45,15 @@ class User extends BaseUser implements UserInterface
return parent::getGists($criteria, $con);
}
/**
* 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()
@ -37,6 +64,10 @@ class User extends BaseUser implements UserInterface
$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,

View file

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

View file

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

View file

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

View file

@ -11,10 +11,6 @@
{{ 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">

View file

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

View file

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

View file

@ -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,69 @@
</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>
<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>
</div>
{% endblock %}

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

View file

@ -50,14 +50,14 @@
<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>
<div class="pull-right actions">
<a target="_blank" href="{{ path('view', app.request.attributes.get('_route_params')) }}" class="btn btn-default btn-sm cipher-link">
<span class="btn btn-warning btn-xs">
{{ commit|slice(0, 10) }}
</span>
</a>
{% if not gist.cipher %}
<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 }}
@ -66,12 +66,13 @@
<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>
{% endif %}
{% endif %}
<a target="_blank" 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>
{{ gist.title ? gist.title : 'gist.untitled'|trans }}
</div>

View file

@ -11,21 +11,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 +31,41 @@
</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>
{% 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 }}">
{% endif %}
</p>
<p>
{{ commit.date|date('date.format'|trans) }}
</p>
<div>
{% if not loop.first %}
<div class="diff" id="diff-{{ loop.index }}">
{% if not gist.cipher %}
{{ commit.diff|raw }}
</div>
{% endif %}
</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 +75,61 @@
{% block js %}
{{ parent() }}
{% if gist.cipher %}
{{ include('View/cipherJs.html.twig') }}
<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++) {
diffContent.push(sign + lines[i]);
}
}
diffContent = diffContent.join("\n");
var $pre = $('<pre>')
.attr('class', 'brush: diff; syntaxhighlighter')
.text(diffContent);
$('#diff-' + (u + 1).toString()).append($pre);
}
}
SyntaxHighlighter.all();
</script>
{% endif %}
{% endblock %}

View file

@ -16,29 +16,28 @@
{% 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>
<div class="pull-right actions">
<span class="btn btn-warning btn-xs">
{{ commit|slice(0, 10) }}
</span>
{% 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 }}
@ -47,12 +46,13 @@
<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>
{% endif %}
{% 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>
{{ gist.title ? gist.title : 'gist.untitled'|trans }}
@ -84,35 +84,6 @@
{{ 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>
{{ include('View/cipherJs.html.twig') }}
{% endif %}
{% endblock %}

View file

@ -1,11 +1,19 @@
<!DOCTYPE html>
{% set theme_settings = app.settings.theme %}
{% set security_dettings = 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" />
{% 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 %}
@ -17,7 +25,7 @@
</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,33 +44,33 @@
{{ '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_dettings.enable_login %}
<li>
<a href="{{ path('login') }}">
{{ 'app.menu.my.login.title'|trans }}
</a>
</li>
{% if security_dettings.enable_registration %}
<li>
<a href="{{ path('register') }}">
{{ 'app.menu.my.register.title'|trans }}
</a>
</li>
{% endif %}
{% endif %}
<li>
<a href="https://gitnet.fr/deblan/gist/src/master/README.md">
{{ 'app.menu.about.title'|trans }}
@ -71,12 +79,11 @@
</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'} %}
<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 +99,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>

View file

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

View file

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

View file

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

View file

@ -11,19 +11,40 @@ 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;
/**
* @var GeSHi
*/
protected $geshi;
/**
* __construct.
*
* @param mixed $gistPath
* @param GitWrapper $gitWrapper
* @param GitWorkingCopy $gitWorkingCopy
* @param GeSHi $geshi
*/
public function __construct($gistPath, GitWrapper $gitWrapper, GitWorkingCopy $gitWorkingCopy, GeSHi $geshi)
{
$this->gistPath = $gistPath;
@ -32,11 +53,23 @@ class Gist
$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 +82,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);
@ -65,12 +98,24 @@ class Gist
'diff' => $this->highlight('diff', $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 +125,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 +149,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,9 +170,38 @@ class Gist
->add($gist->getFile())
->commit('Update');
$gist->commit()->save();
return $gist;
}
/*
* Returns the number of commits.
*
* @param GistModel $gist
*
* @return int
*/
public function getNumberOfCommits(GistModel $gist)
{
$command = GitCommand::getInstance('log', '--oneline', '--', $gist->getFile());
$command->setDirectory($this->gistPath);
$command->bypass(false);
$content = trim($this->gitWrapper->run($command));
$content = str_replace("\r\n", "\n", $content);
return count(explode("\n", $content));
}
/**
* Highlight the content.
*
* @param string $type
* @param string $content
*
* @return string
*/
public function highlight($type, $content)
{
$this->geshi->set_source($content);

View file

@ -5,11 +5,19 @@ namespace Gist\Service;
use InvalidArgumentException;
/**
* Class SaltGenerator
* Class SaltGenerator.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class SaltGenerator
{
/**
* Generates a random salt.
*
* @param int $length
*
* @return string
*/
public function generate($length = 32)
{
if (!is_numeric($length)) {

View file

@ -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());
@ -75,6 +131,14 @@ class UserProvider implements UserProviderInterface
return $user;
}
/**
* Updates an user.
*
* @param User $user
* @param string $password
*
* @return User
*/
public function updateUserPassword(User $user, $password)
{
$user
@ -84,6 +148,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 +166,26 @@ class UserProvider implements UserProviderInterface
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 +195,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';

View file

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

View file

@ -0,0 +1,78 @@
.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 {
background: #222;
border: #222;
color: #ddd;
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;
}
.panel-heading .actions {
margin-top: -5px;
}
div.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;
}
.btn-delete {
background: #DE3336;
color: #fff;
}
.btn-error:active, .btn-error:hover, .btn-error:focus {
color: #000;
}

View file

@ -66,36 +66,51 @@ 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();
}
});
}
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,7 +127,7 @@ var getKey = function() {
var parts = url.split('#key=');
if (parts.length === 2) {
return parts[1];
return parts[1].split('&')[0];
}
return null;
@ -123,21 +138,38 @@ var viewerEvents = function() {
$(document).ready(function() {
var key = getKey();
var $cipherEditor = $('.cipher-editor');
var $embedInput = $('#embed-input');
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));
SyntaxHighlighter.all();
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) {