Base web implementation.

This commit is contained in:
Alexey Skobkin 2015-03-03 23:38:52 +03:00
parent b4ee4312bb
commit d908885e2f
26 changed files with 1023 additions and 121 deletions

View File

@ -1,69 +1,17 @@
Symfony Standard Edition
========================
Welcome to the Symfony Standard Edition - a fully-functional Symfony2
application that you can use as the skeleton for your new applications.
For details on how to download and get started with Symfony, see the
[Installation][1] chapter of the Symfony Documentation.
```shell
npm install -g less
```
What's inside?
--------------
...
The Symfony Standard Edition is configured with the following defaults:
```shell
php app/console braincrafted:bootstrap:install
```
* An AppBundle you can use to start coding;
...
* Twig as the only configured template engine;
* Doctrine ORM/DBAL;
* Swiftmailer;
* Annotations enabled for everything.
It comes pre-configured with the following bundles:
* **FrameworkBundle** - The core Symfony framework bundle
* [**SensioFrameworkExtraBundle**][6] - Adds several enhancements, including
template and routing annotation capability
* [**DoctrineBundle**][7] - Adds support for the Doctrine ORM
* [**TwigBundle**][8] - Adds support for the Twig templating engine
* [**SecurityBundle**][9] - Adds security by integrating Symfony's security
component
* [**SwiftmailerBundle**][10] - Adds support for Swiftmailer, a library for
sending emails
* [**MonologBundle**][11] - Adds support for Monolog, a logging library
* [**AsseticBundle**][12] - Adds support for Assetic, an asset processing
library
* **WebProfilerBundle** (in dev/test env) - Adds profiling functionality and
the web debug toolbar
* **SensioDistributionBundle** (in dev/test env) - Adds functionality for
configuring and working with Symfony distributions
* [**SensioGeneratorBundle**][13] (in dev/test env) - Adds code generation
capabilities
All libraries and bundles included in the Symfony Standard Edition are
released under the MIT or BSD license.
Enjoy!
[1]: http://symfony.com/doc/2.7/book/installation.html
[6]: http://symfony.com/doc/2.7/bundles/SensioFrameworkExtraBundle/index.html
[7]: http://symfony.com/doc/2.7/book/doctrine.html
[8]: http://symfony.com/doc/2.7/book/templating.html
[9]: http://symfony.com/doc/2.7/book/security.html
[10]: http://symfony.com/doc/2.7/cookbook/email.html
[11]: http://symfony.com/doc/2.7/cookbook/logging/monolog.html
[12]: http://symfony.com/doc/2.7/cookbook/assetic/asset_management.html
[13]: http://symfony.com/doc/2.7/bundles/SensioGeneratorBundle/index.html
```shell
php app/console assetic:dump
```

View File

@ -19,6 +19,7 @@ class AppKernel extends Kernel
new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
new DT\Bundle\GeshiBundle\DTGeshiBundle(),
new Braincrafted\Bundle\BootstrapBundle\BraincraftedBootstrapBundle(),
new Skobkin\Bundle\CopyPasteBundle\SkobkinCopyPasteBundle(),
);

View File

@ -0,0 +1,22 @@
<?php
namespace Application\Migrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
/**
* Set secret as NULL where secret = ''
*/
class Version20150303224825 extends AbstractMigration
{
public function up(Schema $schema)
{
$this->addSql('UPDATE copypastes SET secret=NULL WHERE secret=\'\'');
}
public function down(Schema $schema)
{
$this->addSql('UPDATE copypastes SET secret=\'\' WHERE secret=NULL');
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Application\Migrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version20150305184842 extends AbstractMigration
{
public function up(Schema $schema)
{
$this->addSql('UPDATE copypastes SET date_expire=NULL WHERE date_expire=\'0000-00-00 00:00:00\'');
}
public function down(Schema $schema)
{
$this->addSql('UPDATE copypastes SET date_expire=\'0000-00-00 00:00:00\' WHERE date_expire=NULL');
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Application\Migrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
/**
* Copypastes table changes:
* - enabled -> is_enabled
* - is_preferred added
*/
class Version20150316014139 extends AbstractMigration
{
public function up(Schema $schema)
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE languages CHANGE enabled is_enabled TINYINT(1) NOT NULL');
$this->addSql('ALTER TABLE languages ADD is_preferred TINYINT(1) NOT NULL');
$this->addSql('CREATE INDEX idx_preferred ON languages (is_preferred)');
}
public function down(Schema $schema)
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE languages CHANGE is_enabled enabled TINYINT(1) NOT NULL');
$this->addSql('ALTER TABLE languages DROP is_preferred');
$this->addSql('DROP INDEX idx_preferred ON languages');
}
}

View File

@ -0,0 +1,119 @@
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="header_title">
<source>header_title</source>
<target>CopyPaste</target>
</trans-unit>
<trans-unit id="sidebar_title">
<source>sidebar_title</source>
<target>Последние</target>
</trans-unit>
<trans-unit id="header_menu_add">
<source>header_menu_add</source>
<target>Добавить</target>
</trans-unit>
<trans-unit id="header_menu_about">
<source>header_menu_about</source>
<target>О сервисе</target>
</trans-unit>
<trans-unit id="header_menu_api">
<source>header_menu_api</source>
<target>API</target>
</trans-unit>
<trans-unit id="paste_add_form_text">
<source>paste_add_form_text</source>
<target>Текст</target>
</trans-unit>
<trans-unit id="paste_add_form_description">
<source>paste_add_form_description</source>
<target>Описание</target>
</trans-unit>
<trans-unit id="paste_add_form_file_name">
<source>paste_add_form_file_name</source>
<target>Имя файла</target>
</trans-unit>
<trans-unit id="paste_add_form_author">
<source>paste_add_form_author</source>
<target>Автор</target>
</trans-unit>
<trans-unit id="paste_add_form_expire">
<source>paste_add_form_expire</source>
<target>Удалить через</target>
</trans-unit>
<trans-unit id="paste_add_form_private">
<source>paste_add_form_private</source>
<target>Секретно</target>
</trans-unit>
<trans-unit id="paste_add_form_language">
<source>paste_add_form_language</source>
<target>Язык</target>
</trans-unit>
<trans-unit id="paste_add_form_create">
<source>Create</source>
<target>Создать</target>
</trans-unit>
<trans-unit id="paste_view_tab_view">
<source>paste_view_tab_view</source>
<target>Просмотр</target>
</trans-unit>
<trans-unit id="paste_view_tab_edit">
<source>paste_view_tab_edit</source>
<target>Правка</target>
</trans-unit>
<trans-unit id="paste_show_qr_show">
<source>paste_show_qr_show</source>
<target>Показать QR</target>
</trans-unit>
<trans-unit id="paste_show_download">
<source>paste_show_download</source>
<target>Скачать</target>
</trans-unit>
<trans-unit id="paste_new_expire_5_minutes">
<source>5 minutes</source>
<target>5 минут</target>
</trans-unit>
<trans-unit id="paste_new_expire_1_hour">
<source>1 hour</source>
<target>1 час</target>
</trans-unit>
<trans-unit id="paste_new_expire_3_hours">
<source>3 hours</source>
<target>3 часа</target>
</trans-unit>
<trans-unit id="paste_new_expire_12_hours">
<source>12 hours</source>
<target>12 часов</target>
</trans-unit>
<trans-unit id="paste_new_expire_1_day">
<source>1 day</source>
<target>1 день</target>
</trans-unit>
<trans-unit id="paste_new_expire_1_week">
<source>1 week</source>
<target>1 неделя</target>
</trans-unit>
<trans-unit id="paste_new_expire_1_month">
<source>1 month</source>
<target>1 месяц</target>
</trans-unit>
<trans-unit id="paste_new_expire_3_months">
<source>3 months</source>
<target>3 месяца</target>
</trans-unit>
<trans-unit id="paste_new_expire_6_month">
<source>6 months</source>
<target>6 месяцев</target>
</trans-unit>
<trans-unit id="paste_new_expire_1_year">
<source>1 year</source>
<target>1 год</target>
</trans-unit>
<trans-unit id="paste_new_expire_never">
<source>Never</source>
<target>Никогда</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -1,13 +1,52 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>
{% extends '::layout.html.twig' -%}
{%- block css -%}
{{- parent() -}}
<link href="{{ asset('bundles/skobkincopypaste/css/base.css') }}" rel="stylesheet" media="screen">
{% endblock %}
{% block javascript %}
{{- parent() -}}
<script src="{{ asset('bundles/skobkincopypaste/js/copypaste.js') }}"></script>
{% endblock %}
{%- block header -%}
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{ path('copypaste_new') }}">{{ 'header_title' | trans }}</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
{% block header_menu_left_items %}
<li{% if app.request.attributes.get('_route') == 'copypaste_new' %} class="active"{% endif %}>
<a href="{{ path('copypaste_new') }}"><span class="glyphicon glyphicon-plus-sign"></span> {{ 'header_menu_add' | trans }}</a>
</li>
<li>
<a href="{#{{ path('about') }}#}"><span class="glyphicon glyphicon-info-sign"></span> {{ 'header_menu_about' | trans }}</a>
</li>
{% endblock %}
</ul>
<ul class="nav navbar-nav navbar-right">
{% block header_menu_right_items %}
<li><a href="https://bitbucket.org/skobkin/copypaste2/wiki/API" target="_blank">{{ 'header_menu_api' | trans }}</a></li>
{% endblock %}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{%- endblock -%}
{%- block sidebar -%}
{{ render(controller('SkobkinCopyPasteBundle:CopyPaste:sidebar')) }}
{%- endblock -%}
{% block content %}{% endblock %}

View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
{%- block head -%}
<title>{% block title %}CopyPaste{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="{{ asset('bundles/skobkincopypaste/images/favicon.ico') }}">
{%- block css -%}
<link href="{{ asset('bootstrap/css/bootstrap.css') }}" rel="stylesheet" media="screen">
{%- endblock -%}
{# HTML5 Shim and Respond.js add IE8 support of HTML5 elements and media queries #}
{% include 'BraincraftedBootstrapBundle::ie8-support.html.twig' %}
{%- endblock -%}
</head>
<body>
{% block body %}
<div class="container-fluid">
{% block containter %}
<div class="row">
{% block header %}{% endblock %}
</div>
<div class="row">
<div class="col-lg-2 col-md-2 col-xs-0">
{% block sidebar %}{% endblock %}
</div>
<div class="col-lg-10 col-md-10 col-xs-12">
{% block content %}{% endblock %}
</div>
</div>
<div class="row">
{% block footer %}{% endblock %}
</div>
{% endblock %}
</div>
{% block javascript %}
<script src="{{ asset('bootstrap/js/jquery.js') }}"></script>
{# Include all JavaScripts, compiled by Assetic #}
<script src="{{ asset('bootstrap/js/bootstrap.js') }}"></script>
{% endblock %}
{% endblock %}
</body>
</html>

View File

@ -0,0 +1,12 @@
<div>
<div class="panel panel-default">
<div class="panel-heading">{{ 'sidebar_title' | trans() }}</div>
<ul class="list-group">
{% for paste in pastes %}
<a href="{{ path('copypaste_show_public', {'id': paste.id}) }}" class="list-group-item">
#{{ paste.id }}&nbsp;-&nbsp;{% if paste.author %}{{ paste.author }}{% else %}anonymous{% endif %}
</a>
{% endfor %}
</ul>
</div>
</div>

View File

@ -5,7 +5,7 @@ imports:
framework:
#esi: ~
#translator: { fallbacks: ["%locale%"] }
translator: { fallbacks: ["%locale%"] }
secret: "%secret%"
router:
resource: "%kernel.root_dir%/config/routing.yml"
@ -37,12 +37,32 @@ assetic:
bundles: [ ]
#java: /usr/bin/java
filters:
less:
node: "%nodejs_executable%"
node_paths: %nodejs_paths%
apply_to: "\.less$"
cssrewrite: ~
#closure:
# jar: "%kernel.root_dir%/Resources/java/compiler.jar"
#yui_css:
# jar: "%kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar"
braincrafted_bootstrap:
output_dir: bootstrap
assets_dir: %kernel.root_dir%/../vendor/twbs/bootstrap
jquery_path: %kernel.root_dir%/../vendor/components/jquery/jquery.js
less_filter: less
fonts_dir: %kernel.root_dir%/../web/bootstrap/fonts
auto_configure:
assetic: true
twig: true
knp_menu: true
knp_paginator: true
customize:
variables_file: ~
bootstrap_output: %kernel.root_dir%/Resources/less/bootstrap.less
bootstrap_template: BraincraftedBootstrapBundle:Bootstrap:bootstrap.less.twig
# Doctrine Configuration
doctrine:
dbal:

View File

@ -18,3 +18,8 @@ parameters:
# A secret key that's used to generate certain security-related tokens
secret: ThisTokenIsNotSoSecretChangeIt
# Other stuff
nodejs_executable: /usr/bin/node
nodejs_paths: [/usr/lib/node_modules/]

View File

@ -23,7 +23,10 @@
"doctrine/doctrine-migrations-bundle": "2.1.*@dev",
"doctrine/doctrine-fixtures-bundle": "2.2.*",
"theodordiaconu/geshi": "dev-master",
"theodordiaconu/geshi-bundle" : "dev-master"
"theodordiaconu/geshi-bundle" : "dev-master",
"braincrafted/bootstrap-bundle": "dev-master",
"twbs/bootstrap": "3.3.*@dev",
"components/jquery": "dev-master"
},
"require-dev": {
"sensio/generator-bundle": "~2.3"

176
composer.lock generated
View File

@ -4,8 +4,110 @@
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "e36598c346f19a7d683f1c1f3d58399f",
"hash": "06492598a20c478faba66c1691e8490c",
"packages": [
{
"name": "braincrafted/bootstrap-bundle",
"version": "dev-master",
"target-dir": "Braincrafted/Bundle/BootstrapBundle",
"source": {
"type": "git",
"url": "https://github.com/braincrafted/bootstrap-bundle.git",
"reference": "2dc17466f95a035869b51c845024f6a92bc1d6d1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/braincrafted/bootstrap-bundle/zipball/2dc17466f95a035869b51c845024f6a92bc1d6d1",
"reference": "2dc17466f95a035869b51c845024f6a92bc1d6d1",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/console": "~2.3",
"symfony/finder": "~2.3",
"symfony/form": "~2.3",
"symfony/framework-bundle": "~2.3",
"symfony/twig-bundle": "~2.3"
},
"require-dev": {
"knplabs/knp-menu": "~2.0@alpha",
"knplabs/knp-menu-bundle": "~2.0@alpha",
"knplabs/knp-paginator-bundle": "dev-master",
"mockery/mockery": "0.9.*",
"phpunit/phpunit": "3.7.*",
"symfony/assetic-bundle": "~2.3"
},
"suggest": {
"knplabs/knp-menu": "Required to use KnpMenuBundle.",
"knplabs/knp-menu-bundle": "BraincraftedBootstrapBundle styles the menus provided by KnpMenuBundle.",
"knplabs/knp-paginator-bundle": "BraincraftedBootstrapBundle styles the pagination provided by KnpPaginatorBundle.",
"twbs/bootstrap": "Twitter Bootstrap provides the assets (images, CSS and JS)"
},
"type": "symfony-bundle",
"autoload": {
"psr-0": {
"Braincrafted\\Bundle\\BootstrapBundle": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Florian Eckerstorfer",
"email": "florian@eckerstorfer.co",
"homepage": "http://florian.ec"
}
],
"description": "BraincraftedBootstrapBundle integrates Bootstrap into Symfony2 by providing templates, Twig extensions, services and commands.",
"keywords": [
"bootstrap"
],
"time": "2015-01-31 10:32:31"
},
{
"name": "components/jquery",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/components/jquery.git",
"reference": "f8edb647d1833fd5d57410ab70860f03534cb2e8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/components/jquery/zipball/f8edb647d1833fd5d57410ab70860f03534cb2e8",
"reference": "f8edb647d1833fd5d57410ab70860f03534cb2e8",
"shasum": ""
},
"type": "component",
"extra": {
"component": {
"scripts": [
"jquery.js"
],
"files": [
"jquery.min.js",
"jquery.min.map",
"jquery-migrate.js",
"jquery-migrate.min.js"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "John Resig",
"email": "jeresig@gmail.com"
}
],
"description": "jQuery JavaScript Library",
"homepage": "http://jquery.com",
"time": "2014-12-23 06:48:45"
},
{
"name": "doctrine/annotations",
"version": "v1.2.3",
@ -816,12 +918,12 @@
"source": {
"type": "git",
"url": "https://github.com/doctrine/migrations.git",
"reference": "058a4635ac9b80a5b1997df28a754e64b1425a1d"
"reference": "312fb5939d5b9dbd66e515374533933e7c5cc8a6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/migrations/zipball/058a4635ac9b80a5b1997df28a754e64b1425a1d",
"reference": "058a4635ac9b80a5b1997df28a754e64b1425a1d",
"url": "https://api.github.com/repos/doctrine/migrations/zipball/312fb5939d5b9dbd66e515374533933e7c5cc8a6",
"reference": "312fb5939d5b9dbd66e515374533933e7c5cc8a6",
"shasum": ""
},
"require": {
@ -866,7 +968,7 @@
"database",
"migrations"
],
"time": "2015-02-19 08:13:05"
"time": "2015-03-02 18:28:52"
},
{
"name": "doctrine/orm",
@ -1626,12 +1728,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/symfony.git",
"reference": "755ea09a440c1b8ea560e403b442fc9f53e0ae93"
"reference": "12cf04f8f341573a146cdc32722f72ae2deb0579"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/symfony/zipball/755ea09a440c1b8ea560e403b442fc9f53e0ae93",
"reference": "755ea09a440c1b8ea560e403b442fc9f53e0ae93",
"url": "https://api.github.com/repos/symfony/symfony/zipball/12cf04f8f341573a146cdc32722f72ae2deb0579",
"reference": "12cf04f8f341573a146cdc32722f72ae2deb0579",
"shasum": ""
},
"require": {
@ -1735,7 +1837,7 @@
"keywords": [
"framework"
],
"time": "2015-03-02 10:21:01"
"time": "2015-03-03 08:32:45"
},
{
"name": "theodordiaconu/geshi",
@ -1817,6 +1919,57 @@
],
"time": "2013-03-29 12:54:06"
},
{
"name": "twbs/bootstrap",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/twbs/bootstrap.git",
"reference": "ddd09f6fe773c4a99929c169b80882ed955a6227"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twbs/bootstrap/zipball/ddd09f6fe773c4a99929c169b80882ed955a6227",
"reference": "ddd09f6fe773c4a99929c169b80882ed955a6227",
"shasum": ""
},
"replace": {
"twitter/bootstrap": "self.version"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.3.x-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jacob Thornton",
"email": "jacobthornton@gmail.com"
},
{
"name": "Mark Otto",
"email": "markdotto@gmail.com"
}
],
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
"homepage": "http://getbootstrap.com",
"keywords": [
"JS",
"css",
"framework",
"front-end",
"less",
"mobile-first",
"responsive",
"web"
],
"time": "2015-03-03 06:03:42"
},
{
"name": "twig/extensions",
"version": "v1.2.0",
@ -1984,7 +2137,10 @@
"doctrine/migrations": 20,
"doctrine/doctrine-migrations-bundle": 20,
"theodordiaconu/geshi": 20,
"theodordiaconu/geshi-bundle": 20
"theodordiaconu/geshi-bundle": 20,
"braincrafted/bootstrap-bundle": 20,
"twbs/bootstrap": 20,
"components/jquery": 20
},
"prefer-stable": false,
"prefer-lowest": false,

View File

@ -0,0 +1,154 @@
<?php
namespace Skobkin\Bundle\CopyPasteBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use \Symfony\Component\Form\Form;
use Skobkin\Bundle\CopyPasteBundle\Entity\Copypaste;
use Skobkin\Bundle\CopyPasteBundle\Form\CopypasteType;
use DT\Bundle\GeshiBundle\Highlighter\GeshiHighlighter;
use \GeSHi\GeSHi;
/**
* Copypaste controller.
*
*/
class CopypasteController extends Controller
{
/**
* Creates a new Copypaste entity.
*
* @return Response
*/
public function createAction(Request $request)
{
$paste = new Copypaste();
$form = $this->createCreateForm($paste);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
if ($form->get('private')->getData()) {
// Private paste
$paste->setSecret(substr(md5($paste->getText()), 0, 16));
}
$expire = (int) $form->get('expire')->getData();
//var_dump($expire); exit();
if ($expire) {
$dateExpire = new \DateTime();
$dateExpire->add(new \DateInterval('PT' . $expire . 'S'));
$paste->setDateExpire($dateExpire);
}
$paste->setIp($request->getClientIp());
$em->persist($paste);
$em->flush();
if ($paste->isPrivate()) {
return $this->redirect($this->generateUrl('copypaste_show_private', ['id' => $paste->getId(), 'secret' => $paste->getSecret()]));
} else {
return $this->redirect($this->generateUrl('copypaste_show_public', ['id' => $paste->getId()]));
}
}
throw new $this->createAccessDeniedException('Sorry :(');
}
/**
* Creates a form to create a Copypaste entity.
*
* @param Copypaste $entity The entity
*
* @return Form The form
*/
private function createCreateForm(Copypaste $entity)
{
$form = $this->createForm(new CopypasteType(), $entity, [
'action' => $this->generateUrl('copypaste_create'),
'method' => 'POST',
]);
$form->add('submit', 'submit', array('label' => 'Create'));
return $form;
}
/**
* Displays a form to create a new Copypaste entity.
*
* @return Response
*/
public function newAction()
{
$paste = new Copypaste();
$createForm = $this->createCreateForm($paste);
return $this->render('SkobkinCopyPasteBundle:Copypaste:new.html.twig', [
'entity' => $paste,
'form_create' => $createForm->createView(),
]);
}
/**
* Finds and displays a Copypaste entity.
*
* @return Response
*/
public function showAction($id, $secret)
{
$em = $this->getDoctrine()->getManager();
/* @var $paste Copypaste */
$paste = $em->getRepository('SkobkinCopyPasteBundle:Copypaste')->findOneBy([
'id' =>$id,
'secret' => $secret
]);
if (!$paste) {
throw $this->createNotFoundException('Unable to find copypaste.');
}
$editForm = $this->createCreateForm($paste);
/* @var $highlighter GeshiHighlighter */
$highlighter = $this->get('dt_geshi.highlighter');
$highlightedCode = $highlighter->highlight($paste->getText(), $paste->getLanguage()->getCode(), function(GeSHi $geshi) {
$geshi->set_header_type(GESHI_HEADER_PRE);
$geshi->enable_line_numbers(GESHI_NO_LINE_NUMBERS);
});
return $this->render('SkobkinCopyPasteBundle:Copypaste:show.html.twig', [
'paste' => $paste,
'highlighted_text' => $highlightedCode,
'form_create' => $editForm->createView(),
]);
}
/**
* Main page
*
* @return Response
*/
public function sidebarAction()
{
$em = $this->getDoctrine()->getManager();
$pastes = $em->getRepository('SkobkinCopyPasteBundle:Copypaste')->findBy(
['secret' => null],
['id' => 'DESC'],
// @todo move to the config
15
);
return $this->render('::sidebar.html.twig', ['pastes' => $pastes]);
}
}

View File

@ -3,17 +3,16 @@
namespace Skobkin\Bundle\CopyPasteBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Copypaste
*
* @ORM\Table(
* name="copypastes",
* indexes={
* @ORM\Index(name="idx_expire", columns={"date_expire"})
* }
* )
* @ORM\Table(name="copypastes", indexes={
* @ORM\Index(name="idx_expire", columns={"date_expire"})
* })
* @ORM\Entity
* @ORM\HasLifecycleCallbacks()
*/
class Copypaste
{
@ -38,7 +37,7 @@ class Copypaste
*
* @ORM\Column(name="description", type="text", nullable=true)
*/
private $description;
private $description = null;
/**
* @var Language
@ -53,14 +52,14 @@ class Copypaste
*
* @ORM\Column(name="file_name", type="string", length=128, nullable=true)
*/
private $fileName;
private $fileName = null;
/**
* @var string
*
* @ORM\Column(name="author", type="string", length=48, nullable=true)
*/
private $author;
private $author = null;
/**
* @var \DateTime
@ -74,11 +73,12 @@ class Copypaste
*
* @ORM\Column(name="date_expire", type="datetime", nullable=true)
*/
private $dateExpire;
private $dateExpire = null;
/**
* @var string
*
* @Assert\Ip
* @ORM\Column(name="ip", type="string", length=48, nullable=false)
*/
private $ip;
@ -88,9 +88,15 @@ class Copypaste
*
* @ORM\Column(name="secret", type="string", length=16, nullable=true)
*/
private $secret;
private $secret = null;
/**
* @ORM\PrePersist
*/
public function prePersist()
{
$this->datePublished = new \DateTime();
}
/**
* Get id
@ -101,6 +107,11 @@ class Copypaste
{
return $this->id;
}
public function __toString()
{
return $this->id;
}
/**
* Set text

View File

@ -7,13 +7,11 @@ use Doctrine\ORM\Mapping as ORM;
/**
* Language
*
* @ORM\Table(
* name="languages",
* indexes={
* @ORM\Index(name="idx_enabled", columns={"enabled"}),
* @ORM\Index(name="idx_code", columns={"code"})
* }
* )
* @ORM\Table(name="languages", indexes={
* @ORM\Index(name="idx_enabled", columns={"is_enabled"}),
* @ORM\Index(name="idx_preferred", columns={"is_preferred"}),
* @ORM\Index(name="idx_code", columns={"code"})
* })
* @ORM\Entity
*/
class Language
@ -40,13 +38,20 @@ class Language
* @ORM\Column(name="code", type="string", length=24, nullable=false)
*/
private $code;
/**
* @var boolean
*
* @ORM\Column(name="is_preferred", type="boolean", nullable=false)
*/
private $isPreferred = false;
/**
* @var boolean
*
* @ORM\Column(name="enabled", type="boolean", nullable=false)
* @ORM\Column(name="is_enabled", type="boolean", nullable=false)
*/
private $enabled;
private $isEnabled;
@ -59,6 +64,11 @@ class Language
{
return $this->id;
}
public function __toString()
{
return $this->name;
}
/**
* Set name
@ -86,7 +96,7 @@ class Language
/**
* Set code
*
* @param string $file
* @param string $code
* @return Lang
*/
public function setCode($code)
@ -107,35 +117,47 @@ class Language
}
/**
* Set enabled
* Set isEnabled
*
* @param boolean $enabled
* @param boolean $isEnabled
* @return Lang
*/
public function setEnabled($enabled)
public function setIsEnabled($isEnabled)
{
$this->enabled = $enabled;
$this->isEnabled = $isEnabled;
return $this;
}
/**
* Get enabled
* Get isEnabled
*
* @return boolean
*/
public function getEnabled()
public function getIsEnabled()
{
return $this->enabled;
return $this->isEnabled;
}
/**
* Check if language is enabled
* Get isPreferred
*
* @return boolean
*/
public function isEnabled()
function getIsPreferred()
{
return $this->enabled;
return $this->isPreferred;
}
/**
* Set isPreferred
*
* @param boolean $isPreferred
*/
function setIsPreferred($isPreferred)
{
$this->isPreferred = $isPreferred;
return $this;
}
}

View File

@ -0,0 +1,90 @@
<?php
namespace Skobkin\Bundle\CopyPasteBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
class CopypasteType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('text', 'textarea', ['label' => 'paste_add_form_text'])
->add('description', 'textarea', [
'label' => 'paste_add_form_description',
'required' => false,
])
->add('fileName', 'text', [
'label' => 'paste_add_form_file_name',
'required' => false,
])
->add('author', 'text', [
'label' => 'paste_add_form_author',
'required' => false,
])
->add('expire', 'choice', [
'label' => 'paste_add_form_expire',
'mapped' => false,
// @todo move to config
'choices' => [
300 => '5 minutes',
3600 => '1 hour',
10800 => '3 hours',
43200 => '12 hours',
86400 => '1 day',
604800 => '1 week',
2419200 => '1 month',
7257600 => '3 months',
14515200 => '6 months',
29030400 => '1 year',
0 => 'Never',
]
])
->add('private', 'checkbox', [
'label' => 'paste_add_form_private',
'required' => false,
'mapped' => false
])
->add('language', 'entity', [
'label' => 'paste_add_form_language',
'class' => 'Skobkin\Bundle\CopyPasteBundle\Entity\Language',
'query_builder' => function (EntityRepository $repo) {
/* @var $qb QueryBuilder */
return $repo->createQueryBuilder('lang')
->where('lang.isEnabled = :enabled')
->addOrderBy('lang.isPreferred', 'DESC')
->addOrderBy('lang.code')
->setParameter('enabled', true);
},
//'preferred_choices' => []
])
->add('actions', 'form_actions')
;
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults([
'data_class' => 'Skobkin\Bundle\CopyPasteBundle\Entity\Copypaste'
]);
}
/**
* @return string
*/
public function getName()
{
return 'skobkin_bundle_copypastebundle_copypaste';
}
}

View File

@ -1 +1,3 @@
skobkin_copy_paste:
resource: "@SkobkinCopyPasteBundle/Resources/config/routing/copypaste.yml"
prefix: /

View File

@ -0,0 +1,21 @@
copypaste_show_public:
path: /{id}
defaults: { _controller: "SkobkinCopyPasteBundle:Copypaste:show", secret: null }
requirements:
id: \d+
copypaste_show_private:
path: /{id}/{secret}
defaults: { _controller: "SkobkinCopyPasteBundle:Copypaste:show" }
requirements:
id: \d+
secret: \w{16}
copypaste_new:
path: /
defaults: { _controller: "SkobkinCopyPasteBundle:Copypaste:new" }
copypaste_create:
path: /create
defaults: { _controller: "SkobkinCopyPasteBundle:Copypaste:create" }
methods: POST

View File

@ -0,0 +1,44 @@
/*
Created on : Mar 3, 2015, 6:53:42 PM
Author : Alexey Skobkin
*/
html {
position: relative;
min-height: 100%;
}
body {
/* Margin bottom by footer height */
margin-bottom: 60px;
padding-top: 70px;
}
.navbar-brand {
font-weight: bold;
}
/* @todo Fix crutches and bycicles */
.editor-head button[type="submit"] {
margin-top: 24px;
}
.editor-description {
margin-top: 10px;
}
.editor-head .form-group, .editor-head .checkbox {
margin-bottom: 0;
}
.paste-main #tab-paste-edit, .paste-main #tab-paste-view {
padding-top: 10px;
}
.paste-description-content {
margin-top: 10px;
}
.paste-main .paste-tabs {
margin-top: 10px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,9 @@
$(function () {
var $qr_link = $('#paste-qr-code');
$qr_link.popover({
content: '<img src="' + $qr_link.data('qr-url') + '" class="img-rounded"/>',
placement: 'bottom',
trigger: 'focus',
html: true
});
});

View File

@ -0,0 +1,6 @@
{% extends '::base.html.twig' %}
{% block content %}
{# This form recieves form_create object from current context #}
{% include 'SkobkinCopyPasteBundle:Form:form_paste_create.html.twig' %}
{% endblock %}

View File

@ -0,0 +1,76 @@
{% extends '::base.html.twig' %}
{% block content %}
<div class="paste container-fluid">
<div class="row">
<div class="col-md-2 col-xs-6">
{% if paste.author %}
<span class="glyphicon glyphicon-user"></span> {{ paste.author }}
{% else %}
<span class="glyphicon glyphicon-user"></span> anonymous
{% endif %}
</div>
<div class="col-md-2 col-xs-6"><span class="glyphicon glyphicon-font"></span> {{ paste.language }}</div>
<div class="col-md-2 col-xs-6"><span class="glyphicon glyphicon-plus"></span> {{ paste.datePublished | date('Y.m.d H:i') }}</div>
<div class="col-md-2 col-xs-6">
{% if paste.dateExpire %}
<span class="glyphicon glyphicon-remove"></span> {{ paste.dateExpire | date('Y.m.d H:i') }}
{% endif %}
</div>
<div class="col-md-2 col-xs-6">
<span class="glyphicon glyphicon-qrcode"></span>
<a href="#" id="paste-qr-code" data-toggle="popover" data-qr-url="http://chart.apis.google.com/chart?cht=qr&chs=150x150&chl={{
path(app.request.attributes.get('_route'), app.request.attributes.get('_route_params')) | url_encode()
}}">{{ 'paste_show_qr_show' | trans() }}</a>
</div>
<div class="col-md-2 col-xs-6">
<a href="#">
<span class="glyphicon glyphicon-download-alt"></span> {{ 'paste_show_download' | trans() }}
</a>
</div>
</div>
{% if paste.description %}
<div class="paste-description row">
<div class="col-md-12">
<div class="paste-description-content well well-sm">
{{ paste.description | nl2br }}
</div>
</div>
</div>
{% endif %}
<div class="paste-main row">
<div class="col-md-12" role="tabpanel">
<div class="paste-tabs" role="tabpanel">
<!-- Nav tabs -->
<ul class="nav nav-tabs nav-justified" role="tablist">
<li role="presentation" class="active">
<a href="#tab-paste-view" aria-controls="home" role="tab" data-toggle="tab">
<span class="glyphicon {% if paste.isPrivate %}glyphicon-eye-close{% else %}glyphicon-eye-open{% endif %}"></span>
{{ 'paste_view_tab_view' | trans() }}
</a>
</li>
<li role="presentation">
<a href="#tab-paste-edit" aria-controls="profile" role="tab" data-toggle="tab">
<span class="glyphicon glyphicon-edit"></span>
{{ 'paste_view_tab_edit' | trans() }}
</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="tab-paste-view">
<div class="paste-text-content">
{{ highlighted_text | raw }}
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-paste-edit">
{# This form recieves form_create object from current context #}
{% include 'SkobkinCopyPasteBundle:Form:form_paste_create.html.twig' %}
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,42 @@
<div class="paste-form-create">
{{ form_start(form_create) }}
<div class="container-fluid">
<div class="editor-head row">
<div class="form-group col-md-4">
{{ form_row(form_create.author) }}
</div>
<div class="form-group col-md-3">
{{ form_row(form_create.language) }}
</div>
<div class="form-group col-md-3">
{{ form_row(form_create.expire) }}
{{ form_row(form_create.private) }}
</div>
<div class="form-group col-md-2 form-inline">
<div class="inline">
{{ form_widget(form_create.submit) }}
</div>
</div>
</div>
<div class="editor-text row">
<div class="form-group col-md-12">
{{ form_label(form_create.text) }}
{{ form_errors(form_create.text) }}
{{ form_widget(form_create.text, {'attr': {'rows': 10}}) }}
</div>
</div>
<div class="editor-head row">
<div class="form-group col-md-4">
{{ form_row(form_create.fileName) }}
</div>
</div>
<div class="editor-description row">
<div class="form-group col-md-12">
{{ form_label(form_create.description) }}
{{ form_errors(form_create.description) }}
{{ form_widget(form_create.description) }}
</div>
</div>
</div>
{{ form_end(form_create) }}
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB