├── .bowerrc
├── .gitignore
├── .scrutinizer.yml
├── .tarignore
├── .travis.yml
├── LICENSE
├── README.md
├── Vagrantfile
├── ansible
├── development
├── group_vars
│ ├── all
│ ├── development
│ └── frontends
├── playbook.yml
├── production
└── roles
│ ├── frontend
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ │ └── parameters.yml.j2
│ ├── mysql
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ │ └── my.cnf.j2
│ ├── nginx
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ │ ├── nginx.conf.j2
│ │ └── sites
│ │ └── frontend.conf.j2
│ ├── php-fpm
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ │ ├── php-fpm.conf.j2
│ │ └── php.ini.j2
│ ├── redis
│ └── tasks
│ │ └── main.yml
│ ├── skel
│ └── tasks
│ │ └── main.yml
│ ├── supervisor
│ └── tasks
│ │ └── main.yml
│ └── user
│ └── tasks
│ └── main.yml
├── app
├── AppCache.php
├── AppKernel.php
├── Resources
│ ├── bin
│ │ └── reload
│ ├── docker
│ │ └── init.sh
│ ├── gif
│ │ ├── README.md
│ │ └── star.html
│ ├── jwt
│ │ ├── README.md
│ │ ├── private.pem
│ │ └── public.pem
│ └── views
│ │ ├── admin.html.twig
│ │ ├── base.html.twig
│ │ └── menu.html.twig
├── autoload.php
├── config
│ ├── config.yml
│ ├── config_dev.yml
│ ├── config_prod.yml
│ ├── config_test.yml
│ ├── parameters.yml.dist
│ ├── routing.yml
│ ├── routing_dev.yml
│ ├── security.yml
│ └── service_aliases.yml
├── console
├── logs
│ └── .gitkeep
└── phpunit.xml.dist
├── assets
├── img
│ ├── colors
│ │ ├── txture.png
│ │ ├── txture_lighter.png
│ │ └── txture_trans.png
│ ├── homepage
│ │ ├── carousel
│ │ │ ├── banner-1.jpg
│ │ │ ├── banner-2.jpg
│ │ │ └── banner-3.jpg
│ │ └── testimonials
│ │ │ ├── chewbacca.jpg
│ │ │ ├── grievous.png
│ │ │ ├── vader.jpg
│ │ │ └── yoda.jpg
│ └── icons
│ │ ├── darth-vader-icon.jpeg
│ │ ├── deathstar-ico-32.png
│ │ ├── luke-skywalker-icon.png
│ │ └── yoda-icon.jpg
├── js
│ ├── admin
│ │ └── scripts.js
│ └── app
│ │ └── scripts.js
└── less
│ ├── admin.less
│ ├── admin
│ ├── bootswatch.less
│ ├── layout.less
│ ├── navs.less
│ ├── pagination.less
│ ├── panels.less
│ ├── tables.less
│ └── variables.less
│ ├── app.less
│ └── app
│ ├── colors.less
│ ├── forms.less
│ ├── pages
│ └── homepage.less
│ └── theme.less
├── behat.yml.dist
├── bower.json
├── composer.json
├── composer.lock
├── docker-compose.yml.dist
├── features
├── account
│ ├── confirm.feature
│ └── profile.feature
├── admin
│ └── login.feature
├── api
│ ├── authentication.feature
│ ├── cms-blocks
│ │ └── list.feature
│ └── version.feature
└── guest
│ ├── login.feature
│ └── signup.feature
├── gruntfile.js
├── package.json
├── src
├── AdminBundle
│ ├── AdminBundle.php
│ ├── Controller
│ │ ├── AuditController.php
│ │ ├── CmsBlockController.php
│ │ ├── DashboardController.php
│ │ ├── MailTemplateController.php
│ │ └── UserController.php
│ ├── DependencyInjection
│ │ └── AdminExtension.php
│ ├── Form
│ │ ├── CmsBlockType.php
│ │ ├── MailTemplateType.php
│ │ └── UserType.php
│ ├── Menu
│ │ └── MenuBuilder.php
│ ├── Resources
│ │ ├── config
│ │ │ ├── routing.yml
│ │ │ └── twig.yml
│ │ ├── translations
│ │ │ ├── menu.en.yml
│ │ │ └── messages.en.yml
│ │ └── views
│ │ │ ├── Audit
│ │ │ ├── assoc.html.twig
│ │ │ ├── associate.html.twig
│ │ │ ├── blame.html.twig
│ │ │ ├── diff.html.twig
│ │ │ ├── dissociate.html.twig
│ │ │ ├── index.html.twig
│ │ │ ├── insert.html.twig
│ │ │ ├── remove.html.twig
│ │ │ └── update.html.twig
│ │ │ ├── CmsBlock
│ │ │ ├── edit.html.twig
│ │ │ ├── index.html.twig
│ │ │ └── new.html.twig
│ │ │ ├── Dashboard
│ │ │ └── index.html.twig
│ │ │ ├── MailTemplate
│ │ │ ├── edit.html.twig
│ │ │ ├── index.html.twig
│ │ │ └── new.html.twig
│ │ │ ├── User
│ │ │ ├── edit.html.twig
│ │ │ ├── index.html.twig
│ │ │ └── new.html.twig
│ │ │ └── layout.html.twig
│ └── Twig
│ │ └── AuditExtension.php
├── ApiBundle
│ ├── ApiBundle.php
│ ├── Behat
│ │ └── APIContext.php
│ ├── Controller
│ │ ├── CmsBlockController.php
│ │ └── VersionController.php
│ ├── DependencyInjection
│ │ ├── ApiExtension.php
│ │ └── Security
│ │ │ └── Factory
│ │ │ ├── JWTAuthFactory.php
│ │ │ └── JWTFactory.php
│ ├── EventListener
│ │ └── ApiResponseListener.php
│ ├── Resource
│ │ ├── CmsBlock
│ │ │ ├── ListResource.php
│ │ │ └── SingleResource.php
│ │ └── VersionResource.php
│ ├── ResourceInterface.php
│ ├── Resources
│ │ └── config
│ │ │ ├── listeners
│ │ │ └── kernel.yml
│ │ │ ├── routing.yml
│ │ │ └── security.yml
│ └── Security
│ │ ├── Authentication
│ │ ├── Provider
│ │ │ └── JWTProvider.php
│ │ └── Token
│ │ │ └── JWTUserToken.php
│ │ └── Firewall
│ │ ├── JWTAuthListener.php
│ │ └── JWTListener.php
└── AppBundle
│ ├── AppBundle.php
│ ├── Behat
│ ├── BaseContext.php
│ ├── DatabaseContext.php
│ ├── Doctrine
│ │ └── PlaceholderListener.php
│ ├── MailerContext.php
│ ├── PageContext.php
│ ├── PlaceholderContext.php
│ ├── Swiftmailer
│ │ └── MemorySpool.php
│ └── UserContext.php
│ ├── Cache
│ ├── RedisCache.php
│ └── Session
│ │ └── RedisSessionHandler.php
│ ├── Command
│ ├── CacheClearCommand.php
│ ├── FixturesCommand.php
│ └── RouterAnonymousCommand.php
│ ├── Composer.php
│ ├── Controller
│ ├── DoctrineController.php
│ ├── HomeController.php
│ └── UserController.php
│ ├── DependencyInjection
│ └── AppExtension.php
│ ├── Entity
│ ├── CmsBlock.php
│ ├── Internal
│ │ └── Fixture.php
│ ├── MailTemplate.php
│ └── User.php
│ ├── EventListener
│ ├── DoctrineExtensionsListener.php
│ ├── FlushListener.php
│ └── FlushSubscriber.php
│ ├── Fixture
│ ├── Cms
│ │ └── LayoutBlocks.php
│ ├── Mail
│ │ └── RegistrationConfirm.php
│ └── Users
│ │ └── DevUsers.php
│ ├── Form
│ └── Type
│ │ └── User
│ │ ├── ConfirmType.php
│ │ ├── ProfileType.php
│ │ ├── ResetType.php
│ │ └── SignupType.php
│ ├── Mailer
│ ├── ContactInterface.php
│ └── Mailer.php
│ ├── Menu
│ ├── MenuBuilder.php
│ └── RequestVoter.php
│ ├── Migration
│ └── Version20150813073404.php
│ ├── Resources
│ ├── config
│ │ ├── cache
│ │ │ ├── dev.yml
│ │ │ ├── prod.yml
│ │ │ └── test.yml
│ │ ├── listeners
│ │ │ ├── doctrine.yml
│ │ │ └── kernel.yml
│ │ ├── mailer.yml
│ │ ├── menu.yml
│ │ ├── routing.yml
│ │ ├── security.yml
│ │ └── twig.yml
│ ├── translations
│ │ ├── menu.en.yml
│ │ ├── messages.en.yml
│ │ └── time.en.yml
│ └── views
│ │ ├── Home
│ │ ├── about.html.twig
│ │ └── homepage.html.twig
│ │ ├── Mail
│ │ └── template.html.twig
│ │ ├── User
│ │ ├── confirm.html.twig
│ │ ├── login.html.twig
│ │ ├── profile.html.twig
│ │ ├── reset.html.twig
│ │ └── signup.html.twig
│ │ ├── blocks
│ │ └── layout
│ │ │ └── footer.html.twig
│ │ ├── flashes.html.twig
│ │ ├── forms.html.twig
│ │ └── layout.html.twig
│ ├── Security
│ └── Core
│ │ └── Encoder
│ │ └── BCryptPasswordEncoder.php
│ ├── Tests
│ └── Entity
│ │ └── UserTest.php
│ └── Twig
│ ├── CMSBlockExtension.php
│ └── TimeExtension.php
└── web
├── app.php
├── app_dev.php
├── app_test.php
├── apple-touch-icon.png
├── favicon.ico
└── robots.txt
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "assets/vendor"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.vagrant
2 | /behat.yml
3 | /ansible/*.tar.gz
4 | /app/bootstrap.php.cache
5 | /app/cache/*
6 | /app/config/parameters.yml
7 | /app/logs/*
8 | !app/cache/.gitkeep
9 | !app/logs/.gitkeep
10 | /app/phpunit.xml
11 | /assets/vendor/
12 | /bin/
13 | /node_modules
14 | /vendor/
15 | /web/build/
16 | /web/bundles/
17 | /docker-compose.yml
18 | /app/session
19 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | checks:
2 | php: { }
3 |
4 | coding_style:
5 | php:
6 | indentation:
7 | switch:
8 | indent_case: false
9 |
10 | filter:
11 | paths:
12 | - src/*
13 | excluded_paths:
14 | - src/*/Behat/*
15 |
--------------------------------------------------------------------------------
/.tarignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .drone.yml
3 | .travis.yml
4 | .jshintrc
5 | .travis.yml
6 | .tarignore
7 | .gitignore
8 | .gitmodules
9 | .bowerrc
10 | .editorconfig
11 | .vagrant
12 | *.tar.gz
13 | *.log
14 | */Behat
15 | */Tests
16 | ansible
17 | app/cache/*
18 | app/phpunit.*
19 | app/logs/*
20 | app/spool/*
21 | app/check.php
22 | app/SymfonyRequirements.php
23 | app/config/parameters.*
24 | app/config/routing_*.yml
25 | app/config/config_dev.yml
26 | app/config/config_test.yml
27 | app/Resources/views/*.dist.html.twig
28 | app/Resources/gif
29 | app/Resources/docker
30 | assets
31 | starwars.gif
32 | screenshot.png
33 | behat.yml*
34 | bin
35 | bower.json
36 | composer.*
37 | docker-compose.*
38 | features
39 | gruntfile.js
40 | node_modules
41 | package.json
42 | tests
43 | README.md
44 | Vagrantfile
45 | web/app_*.php
46 | web/bundles
47 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.6
5 | - 7.0
6 |
7 | services:
8 | - mysql
9 | - redis-server
10 |
11 | install:
12 | - composer install --prefer-dist --no-interaction
13 | - bin/reload test
14 |
15 | script:
16 | - bin/phpunit -c app
17 | - bin/behat -fprogress
18 | - app/console security:check
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 DATA-DOG TEAM
2 |
3 | The MIT license, reference http://www.opensource.org/licenses/mit-license.php
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is furnished
10 | to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | # -*- mode: ruby -*-
2 | # vi: set ft=ruby ts=2 sw=2 sts=2 :
3 |
4 | VAGRANTFILE_API_VERSION = "2"
5 |
6 | Vagrant.require_version '>= 1.7.0'
7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
8 |
9 | config.vm.define 'front1' do |front|
10 | front.vm.box = 'chef/centos-7.0'
11 | front.vm.hostname = 'deathstar'
12 | front.vm.network :private_network, ip: '192.168.50.100'
13 | end
14 |
15 | # Fix for slow external network connections
16 | config.vm.provider :virtualbox do |vb|
17 | vb.customize ['modifyvm', :id, '--natdnshostresolver1', 'on']
18 | vb.customize ['modifyvm', :id, '--natdnsproxy1', 'on']
19 | vb.memory = 1024
20 | vb.cpus = 2
21 | end
22 |
23 | config.vm.provision :ansible do |ansible|
24 | ansible.playbook = 'ansible/playbook.yml'
25 | ansible.inventory_path = 'ansible/development'
26 | end
27 | end
28 |
29 |
--------------------------------------------------------------------------------
/ansible/development:
--------------------------------------------------------------------------------
1 | # vi: set ft=dosini ts=2 sw=2 sts=2 :
2 |
3 | front1 ansible_ssh_host=192.168.50.100 ansible_ssh_user=vagrant ansible_ssh_private_key_file=.vagrant/machines/front1/virtualbox/private_key
4 |
5 | [frontends]
6 | front1
7 |
8 | [development:children]
9 | frontends
10 |
--------------------------------------------------------------------------------
/ansible/group_vars/all:
--------------------------------------------------------------------------------
1 | # vi: set ft=yaml ts=2 sw=2 sts=2 :
2 |
3 | # user - is current application owner
4 | home: "/home/{{ user }}"
5 | # name - is an application currently played, like frontend
6 | app: "{{ home }}/{{ name }}"
7 | releases: "{{ home }}/{{ name }}/releases"
8 | current: "{{ home }}/{{ name }}/current"
9 | builds: "{{ home }}/{{ name }}/builds"
10 | shared: "{{ home }}/{{ name }}/shared"
11 |
12 | ansible_python_interpreter: "/usr/bin/python"
13 |
--------------------------------------------------------------------------------
/ansible/group_vars/development:
--------------------------------------------------------------------------------
1 | # vi: set ft=yaml ts=2 sw=2 sts=2 :
2 |
3 | hostname: deathstar.dev
4 | https: false
5 |
6 | db_user: root
7 | db_pass: S3cretpassword
8 | db_name: deathstar
9 |
10 | secret_salt: 679khgkkj776iyi7i667765ftfhfcvbxxzss
11 |
--------------------------------------------------------------------------------
/ansible/group_vars/frontends:
--------------------------------------------------------------------------------
1 | # vi: set ft=yaml ts=2 sw=2 sts=2 :
2 |
3 | user: www
4 | name: frontend
5 |
6 |
--------------------------------------------------------------------------------
/ansible/playbook.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # given our server OS is centos, install package repositories
3 | - hosts: all
4 | sudo: yes
5 | tasks:
6 | - name: ensure epel repo present
7 | yum: pkg=epel-release state=installed
8 |
9 | - name: ensure remi repo present
10 | yum: name=http://rpms.famillecollet.com/enterprise/remi-release-7.rpm state=installed
11 |
12 | # provision and deploy frontends
13 | - hosts: frontends
14 | roles:
15 | - { role: user, sudo: yes }
16 | - { role: mysql, sudo: yes }
17 | - { role: redis, sudo: yes }
18 | - { role: php-fpm, sudo: yes }
19 | - { role: nginx, sudo: yes }
20 | - { role: skel, sudo: yes, sudo_user: "{{ user }}" }
21 | - { role: frontend, sudo: yes, sudo_user: "{{ user }}" }
22 |
--------------------------------------------------------------------------------
/ansible/production:
--------------------------------------------------------------------------------
1 | # vi: set ft=dosini ts=2 sw=2 sts=2 :
2 |
3 | front1 ansible_ssh_host=85.67.77.66 ansible_ssh_user=deployer ansible_ssh_pass=S3cretpassword
4 |
5 | [frontends]
6 | front1
7 |
8 | [production:children]
9 | frontends
10 |
--------------------------------------------------------------------------------
/ansible/roles/frontend/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: ensure shared directories
3 | file: dest={{ shared }}/{{ item }} state=directory
4 | with_items:
5 | - logs
6 | tags: [ frontend ]
7 |
8 | - name: limit stored releases and builds by removing oldest
9 | shell: "ls -1dt {{ item.dir }}/* | tail -n +{{ item.keep }} | xargs rm -rf"
10 | with_items:
11 | - { dir: "{{ builds }}", keep: 3 }
12 | - { dir: "{{ releases }}", keep: 5 }
13 | tags: [ frontend ]
14 |
15 | - name: new build name
16 | shell: date '+%Y%m%d%H%M%S'
17 | register: next_release
18 | tags: [ frontend ]
19 |
20 | - name: previous release
21 | shell: "[ -d '{{ current }}' ] && readlink {{ current }} || echo ''"
22 | register: prev_release
23 | tags: [ frontend ]
24 |
25 | - name: prepare a release directory
26 | file: path={{ builds }}/{{ next_release.stdout }} state=directory
27 | tags: [ frontend ]
28 |
29 | - name: extract release archive
30 | unarchive: src={{ name }}.tar.gz dest={{ builds }}/{{ next_release.stdout }}
31 | tags: [ frontend ]
32 |
33 | - name: symlink logs
34 | file: src={{ shared }}/logs dest={{ builds }}/{{ next_release.stdout }}/app/logs state=link
35 | tags: [ frontend ]
36 |
37 | - name: copy parameters.yml to build
38 | template: src=parameters.yml.j2 dest={{ builds }}/{{ next_release.stdout }}/app/config/parameters.yml
39 | tags: [ frontend ]
40 |
41 | - name: ensure temp directories are available
42 | file: path={{ builds }}/{{ next_release.stdout }}/app/{{ item }} state=directory
43 | with_items:
44 | - cache
45 | tags: [ frontend ]
46 |
47 | - name: warmup cache
48 | shell: chdir={{ builds }}/{{ next_release.stdout }} php app/console cache:warmup --env=prod
49 | tags: [ frontend ]
50 |
51 | - name: run migrations
52 | shell: chdir={{ builds }}/{{ next_release.stdout }} php app/console doctrine:migrations:migrate --no-interaction --env=prod
53 | tags: [ frontend ]
54 |
55 | - name: run fixtures
56 | shell: chdir={{ builds }}/{{ next_release.stdout }} php app/console app:fixtures --env=prod
57 | tags: [ frontend ]
58 |
59 | - name: move successful build to releases
60 | shell: mv {{ builds }}/{{ next_release.stdout }} {{ releases }}/{{ next_release.stdout }}
61 | tags: [ frontend ]
62 |
63 | - name: link successful release
64 | file: src={{ releases }}/{{ next_release.stdout }} dest={{ current }} state=link force=yes
65 | tags: [ frontend ]
66 |
67 | - name: clear previous release cache
68 | shell: chdir={{ prev_release.stdout.strip() }} php app/console app:cache:clear --env=prod
69 | when: prev_release.stdout.strip() != ""
70 | ignore_errors: true
71 | tags: [ frontend ]
72 |
73 |
--------------------------------------------------------------------------------
/ansible/roles/frontend/templates/parameters.yml.j2:
--------------------------------------------------------------------------------
1 | # vi: set ft=yaml ts=2 sw=2 sts=2 :
2 |
3 | parameters:
4 | database_driver: pdo_mysql
5 | database_host: 127.0.0.1
6 | database_port: null
7 | database_name: "{{ db_name }}"
8 | database_user: "{{ db_user }}"
9 | database_password: "{{ db_pass }}"
10 |
11 | locale: en
12 | secret: "{{ secret_salt }}"
13 | cache_namespace: "{{ next_release.stdout }}"
14 |
15 | mailer_transport: smtp
16 | mailer_host: mailtrap.io
17 | mailer_user: 33434104a686874df
18 | mailer_password: 70905ac4f06417
19 | mailer_sender: { noreply@deathstart.dev: "Death Star" }
20 | mailer_auth_mode: cram-md5
21 | mailer_port: 2525
22 |
23 | redis_host: 127.0.0.1
24 | redis_port: 6379
25 |
--------------------------------------------------------------------------------
/ansible/roles/mysql/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: install MySQL
3 | yum: pkg={{ item }} state=installed
4 | with_items:
5 | - mariadb-server
6 | - mysql
7 | - MySQL-python
8 | tags: [ mysql ]
9 |
10 | - name: ensure mysql is enabled on boot and started
11 | service: name=mariadb state=started enabled=yes
12 | tags: [ mysql ]
13 |
14 | - name: update mysql root passwd
15 | mysql_user: name=root host={{ item }} password={{ db_pass }}
16 | with_items:
17 | - 127.0.0.1
18 | - ::1
19 | - localhost
20 | tags: [ mysql ]
21 |
22 | - name: copy user my.cnf file with root passwd credentials
23 | template: src=my.cnf.j2 dest=/root/.my.cnf owner=root group=root mode=0600
24 | tags: [ mysql ]
25 |
26 | - name: root from any host
27 | mysql_user: name=root password={{ db_pass }} host="%" priv=*.*:ALL,GRANT state=present
28 | tags: [ mysql ]
29 |
30 | - name: delete anonymous mysql user
31 | mysql_user: name="" state=absent
32 | tags: [ mysql ]
33 |
34 | - name: remove mysql test database
35 | mysql_db: name=test state=absent
36 | tags: [ mysql ]
37 |
38 | - name: create default database
39 | mysql_db: name={{ db_name }} state=present
40 | tags: [ mysql ]
41 |
--------------------------------------------------------------------------------
/ansible/roles/mysql/templates/my.cnf.j2:
--------------------------------------------------------------------------------
1 | [client]
2 | user=root
3 | password={{ db_pass }}
4 |
--------------------------------------------------------------------------------
/ansible/roles/nginx/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: nginx reload
3 | service: name=nginx state=reloaded
4 |
5 | - name: nginx restart
6 | service: name=nginx state=restarted
7 |
--------------------------------------------------------------------------------
/ansible/roles/nginx/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: install nginx
3 | yum: name=http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm state=installed
4 | tags: [ nginx ]
5 |
6 | - name: install nginx
7 | yum: name=nginx state=latest
8 | tags: [ nginx ]
9 |
10 | - name: ensure nginx is running and enabled on boot
11 | service: name=nginx.service enabled=yes state=started
12 | tags: [ nginx ]
13 |
14 | - name: ensure nginx has sites-available/enabled directories
15 | file: dest=/etc/nginx/{{ item }} state=directory owner=root mode=0755
16 | with_items:
17 | - sites-available
18 | - sites-enabled
19 | tags: [ nginx ]
20 |
21 | - name: ensure main nginx configuration file is present
22 | template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf owner=root mode=0644 backup=yes
23 | notify:
24 | - nginx restart
25 | tags: [ nginx ]
26 |
27 | - name: ensure that nginx log dir is writable by user
28 | file: path=/var/log/nginx state=directory owner={{ user }} group={{ user }}
29 | tags: [ nginx ]
30 |
31 | - name: install nginx site
32 | template: src="sites/{{ name }}.conf.j2"
33 | dest=/etc/nginx/sites-available/{{ name }}.conf
34 | owner=root mode=0644 backup=yes
35 | tags: [ nginx ]
36 |
37 | - name: ensure nginx vhost enabled
38 | file: src=/etc/nginx/sites-available/{{ name }}.conf
39 | dest=/etc/nginx/sites-enabled/{{ name }}.conf
40 | owner=root state=link
41 | notify:
42 | - nginx restart
43 | tags: [ nginx ]
44 |
--------------------------------------------------------------------------------
/ansible/roles/nginx/templates/nginx.conf.j2:
--------------------------------------------------------------------------------
1 | user {{ user }};
2 | worker_processes 2; # number of cpus * 2
3 |
4 | error_log /var/log/nginx/error.log warn;
5 | pid /var/run/nginx.pid;
6 |
7 | events {
8 | worker_connections 1024;
9 | }
10 |
11 | http {
12 | include /etc/nginx/mime.types;
13 | default_type application/octet-stream;
14 |
15 | # max request header size (file uploads needs more space)
16 | # seems it is ignored on old nginx versions (putting to server as well)
17 | client_max_body_size 32M;
18 |
19 | log_format main '$remote_addr - $remote_user [$time_local] "$request" '
20 | '$status $body_bytes_sent "$http_referer" '
21 | '"$http_user_agent" "$http_x_forwarded_for"';
22 |
23 | log_format time_combined '$remote_addr - $remote_user [$time_local] "$request" '
24 | '$status $body_bytes_sent "$http_referer" '
25 | '"$http_user_agent" "$http_x_forwarded_for" '
26 | '$request_time $upstream_response_time $pipe';
27 |
28 | access_log /var/log/nginx/access.log main;
29 |
30 | sendfile on;
31 |
32 | keepalive_timeout 65;
33 |
34 | gzip on;
35 | gzip_disable "MSIE [1-6]\.(?!.*SV1)";
36 |
37 | ##
38 | # Virtual Hosts
39 | ##
40 | include /etc/nginx/sites-enabled/*.conf;
41 |
42 | include /etc/nginx/conf.d/*.conf;
43 | }
44 |
--------------------------------------------------------------------------------
/ansible/roles/nginx/templates/sites/frontend.conf.j2:
--------------------------------------------------------------------------------
1 | server {
2 | listen {% if https %}443{% else %}80{% endif %};
3 |
4 | server_name {{ hostname }};
5 | root {{ current }}/web;
6 |
7 | # max header size for uploads need more space
8 | client_max_body_size 32M;
9 |
10 | add_header X-Frame-Options SAMEORIGIN;
11 |
12 | location / {
13 | try_files $uri @handler;
14 | expires 1h; # cache static files
15 | }
16 |
17 | location @handler {
18 | rewrite ^(.*)$ /app.php/$1 last;
19 | }
20 |
21 | location ~ ^/app\.php(/|$) {
22 | fastcgi_pass 127.0.0.1:9000;
23 | fastcgi_split_path_info ^(.+\.php)(/.*)$;
24 | include fastcgi_params;
25 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
26 | fastcgi_param DOCUMENT_ROOT $realpath_root;
27 | fastcgi_param HTTPS {% if https %}on{% else %}off{% endif %};
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ansible/roles/php-fpm/handlers/main.yml:
--------------------------------------------------------------------------------
1 | - name: php-fpm restart
2 | service: name=php-fpm state=restarted
3 |
--------------------------------------------------------------------------------
/ansible/roles/php-fpm/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: ensure remi repo is enabled
3 | ini_file: dest=/etc/yum.repos.d/remi.repo section=remi option=enabled value=1
4 | tags: [ php ]
5 |
6 | - name: ensure php56 remi repo is enabled
7 | ini_file: dest=/etc/yum.repos.d/remi.repo section=remi-php56 option=enabled value=1
8 | tags: [ php ]
9 |
10 | - name: ensure php modules are installed
11 | action: yum name={{ item }} state=latest
12 | with_items:
13 | - php-fpm
14 | - php-cli
15 | - php-pdo
16 | - php-mysql
17 | - php-redis
18 | - php-mcrypt
19 | - php-bcmath
20 | - php-mbstring
21 | - php-intl
22 | - php-gd
23 | - php-opcache
24 | notify:
25 | - php-fpm restart
26 | tags: [ php ]
27 |
28 | - name: unsure php-fpm is started and enabled on boot
29 | service: name=php-fpm enabled=yes state=started
30 | tags: [ php ]
31 |
32 | - name: ensure php configuration files are present
33 | template: src={{ item.source }} dest={{ item.destination }} owner=root mode=0644 backup=yes
34 | with_items:
35 | - { source: 'php.ini.j2', destination: '/etc/php.ini' }
36 | - { source: 'php-fpm.conf.j2', destination: '/etc/php-fpm.d/www.conf' }
37 | notify:
38 | - php-fpm restart
39 | tags: [ php ]
40 |
41 | - name: ensure php session and wsdl directories are writable
42 | file: dest=/var/lib/php/{{ item }} state=directory owner={{ user }} mode=0770
43 | with_items:
44 | - session
45 | - wsdlcache
46 | tags: [ php ]
47 |
48 | - name: ensure that php-fpm log dir is writable by wwwdata
49 | file: path=/var/log/php-fpm state=directory owner={{ user }} group={{ user }}
50 | tags: [ php ]
51 |
--------------------------------------------------------------------------------
/ansible/roles/php-fpm/templates/php-fpm.conf.j2:
--------------------------------------------------------------------------------
1 | ; vi: set ft=dosini ts=2 sw=2 sts=2 :
2 |
3 | [www]
4 | listen = 127.0.0.1:9000
5 | listen.allowed_clients = 127.0.0.1
6 |
7 | user = {{ user }}
8 | group = {{ user }}
9 |
10 | pm = dynamic
11 | pm.max_children = 50
12 | pm.start_servers = 2
13 | pm.min_spare_servers = 2
14 | pm.max_spare_servers = 2
15 |
16 | php_admin_value[error_log] = /var/log/php-fpm/www-error.log
17 | php_admin_flag[log_errors] = on
18 |
19 | ; Set session path to a directory owned by process user
20 | ;php_value[session.save_handler] = redis
21 | php_value[session.save_path] = /var/lib/php/session
22 | php_value[soap.wsdl_cache_dir] = /var/lib/php/wsdlcache
23 |
--------------------------------------------------------------------------------
/ansible/roles/php-fpm/templates/php.ini.j2:
--------------------------------------------------------------------------------
1 | ; vi: set ft=dosini ts=2 sw=2 sts=2 :
2 |
3 | [PHP]
4 |
5 | ; Only override settings which may be different from defaults
6 |
7 | max_execution_time = 30
8 | max_input_time = 60
9 | memory_limit = 256M
10 |
11 | error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
12 | display_errors = Off
13 | display_startup_errors = Off
14 | log_errors = On
15 |
16 | variables_order = "GPCS"
17 | request_order = "GP"
18 |
19 | post_max_size = 32M
20 | upload_max_filesize = 32M
21 | max_file_uploads = 10
22 |
23 | default_socket_timeout = 60
24 |
25 | ; COOKIES
26 | session.cookie_secure = On
27 | session.cookie_httponly = On
28 |
29 | ;;;;;;;;;;;;;;;;;
30 | ; Miscellaneous ;
31 | ;;;;;;;;;;;;;;;;;
32 |
33 | ; Decides whether PHP may expose the fact that it is installed on the server
34 | ; (e.g. by adding its signature to the Web server header). It is no security
35 | ; threat in any way, but it makes it possible to determine whether you use PHP
36 | ; on your server or not.
37 | ; http://php.net/expose-php
38 | expose_php = Off
39 |
40 | [Date]
41 | date.timezone=UTC
42 |
43 | [opcache]
44 | opcache.enable=1
45 | opcache.memory_consumption=128
46 | opcache.interned_strings_buffer=8
47 | opcache.max_accelerated_files=4000
48 | opcache.revalidate_freq=0
49 | opcache.fast_shutdown=1
50 | opcache.enable_cli=1
51 |
--------------------------------------------------------------------------------
/ansible/roles/redis/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: install redis
3 | yum: name=redis state=latest
4 | tags: [ redis ]
5 |
6 | - name: ensure redis is started
7 | service: name=redis enabled=yes state=started
8 | tags: [ redis ]
9 |
--------------------------------------------------------------------------------
/ansible/roles/skel/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: ensure app directories
3 | file: dest={{ item }} state=directory owner={{ user }} group={{ user }} mode=0755
4 | with_items:
5 | - "{{ app }}"
6 | - "{{ releases }}"
7 | - "{{ current }}"
8 | - "{{ shared }}"
9 | tags: [ skel ]
10 |
--------------------------------------------------------------------------------
/ansible/roles/supervisor/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: install supervisor
3 | yum: name=supervisor state=latest
4 | tags: [ supervisor ]
5 |
6 | - name: ensure supervisord is started
7 | service: name=supervisord enabled=yes state=started
8 | tags: [ supervisor ]
9 |
10 |
--------------------------------------------------------------------------------
/ansible/roles/user/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: ensure user is present
3 | user: name={{ user }} shell=/bin/bash comment="{{ app }} user" state=present
4 | tags: [ user ]
5 |
--------------------------------------------------------------------------------
/app/AppCache.php:
--------------------------------------------------------------------------------
1 | getEnvironment(), ['dev', 'test'])) {
29 | $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
30 | $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
31 | $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
32 | $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
33 | }
34 |
35 | return $bundles;
36 | }
37 |
38 | public function registerContainerConfiguration(LoaderInterface $loader)
39 | {
40 | $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/Resources/bin/reload:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # takes argument as environment
4 |
5 | DIR="$( cd "$( dirname "$0" )" && pwd )"
6 |
7 | ROOT="$DIR/.."
8 | CLI="$ROOT/app/console"
9 | if [ ! -f "$CLI" ]; then
10 | echo "Can be run only from sf-project-root/bin directory. Run composer install first."
11 | exit 1
12 | fi
13 |
14 | ENV=${1:-"dev"}
15 |
16 | # Bootstrap cache
17 | php $ROOT/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/bin/build_bootstrap.php
18 |
19 | # Symfony cache
20 | rm -rf $ROOT/app/cache/* # force cache cleanup - yeah yeah, sometimes it may not work as expected
21 | php $CLI app:cache:clear --env=$ENV --ansi -vvv
22 | php $CLI cache:clear --env=$ENV --ansi
23 | php $CLI cache:warmup --env=$ENV --ansi
24 |
25 | # Database
26 | php $CLI doctrine:database:drop --force --quiet --env=$ENV --ansi
27 | php $CLI doctrine:database:create --env=$ENV --ansi
28 | php $CLI doctrine:migrations:migrate --no-interaction --env=$ENV --ansi
29 | echo ""
30 |
31 | # Fixtures
32 | php $CLI app:fixtures --env=$ENV --ansi
33 | echo ""
34 |
--------------------------------------------------------------------------------
/app/Resources/docker/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "[info] copy default parameters.yml"
4 | cp -n /var/www/app/config/parameters.yml.dist /var/www/app/config/parameters.yml
5 |
6 | sed -i "s/database_host:.*$/database_host: mysql/" /var/www/app/config/parameters.yml
7 | sed -i "s/database_user:.*$/database_user: $MYSQL_ENV_MYSQL_USER/" /var/www/app/config/parameters.yml
8 | sed -i "s/database_password:.*$/database_password: $MYSQL_ENV_MYSQL_PASSWORD/" /var/www/app/config/parameters.yml
9 | sed -i "s/database_port:.*$/database_port: 3306/" /var/www/app/config/parameters.yml
10 | sed -i "s/redis_host:.*$/redis_host: redis/" /var/www/app/config/parameters.yml
11 |
12 | echo "[info] Running composer"
13 | composer install --optimize-autoloader --working-dir=/var/www
14 |
15 | echo "[info] Changing permissions for storage/"
16 | chmod -R 777 /var/www/app/cache /var/www/app/logs /var/www/app/session
17 |
18 | echo "[info] Waiting for mysql"
19 | sleep 10
20 |
21 | echo "[info] Migrating database"
22 | php /var/www/app/console cache:clear
23 | php /var/www/app/console doctrine:schema:update --force
24 |
25 | chown -R www:www /var/www
26 |
--------------------------------------------------------------------------------
/app/Resources/gif/README.md:
--------------------------------------------------------------------------------
1 | # How to create starwars gif
2 |
3 | You will need: `recordmydesktop ffmpeg`
4 |
5 | - run `recordmydesktop` in shell, keep it running..
6 | - open **star.html** in your browser: `chromium star.html` make it fullscreen and refresh to run through.
7 | - cancel `recordmydesktop` CTRL+C
8 | - convert ogv to gif: `ffmpeg -ss 00:00:05.20 -i out.ogv -r 65 -s 640x480 starwars.gif`
9 |
10 | **NOTE:** the `-ss` option sets the start time offset.
11 |
12 | You may download latest ffmpeg from here:
13 |
14 | wget http://ffmpeg.gusari.org/static/64bit/ffmpeg.static.64bit.latest.tar.gz
15 | tar -xzf ffmpeg.static.64bit.latest.tar.gz
16 |
--------------------------------------------------------------------------------
/app/Resources/jwt/README.md:
--------------------------------------------------------------------------------
1 | # JWT keys
2 |
3 | To regenerate:
4 |
5 | openssl genrsa -out private.pem -aes256 4096
6 | openssl rsa -pubout -in private.pem -out public.pem
7 |
--------------------------------------------------------------------------------
/app/Resources/jwt/private.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | Proc-Type: 4,ENCRYPTED
3 | DEK-Info: AES-256-CBC,C053F8A95528470B0D4A9C4E778BD01B
4 |
5 | l6sHgBBWP4c/lT2N8OD8fUgdLT6LbWW2Bb3MpYwiRXbu2T1jyUf9NjewFtzunCsR
6 | 3r6AwSuJB4Z0KWYPHZs8pNHn1M3eqhKuStJclfJDJ4G5t8EqquoZaWHsTGO/FZU/
7 | JMt3C0MgmpZckkH1jiHaiLPC0O/CDx7eAG7OjTD6feZEJJGj+w23RomB6816J+AY
8 | UQNCn8QZfAr7Z+UY2bnjDx/Y7pO8eUknf8PTshbkrNw7b+/agwQ/O9t4Qibo4vJ5
9 | QDYhJsikDDUtQF+a1w5QNNtgbtTZMoQnEb+ZkfLFKJJVLctELYjSQ4tqKmmQcHtL
10 | Dg+chaB0BxABinh6f/yx1rRQCyWPKo6IKu4aExdHP1qNk7pPcvBp/dbQk1w8J6Le
11 | GcWqX2eNBLyp363Bu8RU5Cnqxd+VZ+GRnia4RO+Ow9NkuHDs0YsKZJ/uz59lxMTh
12 | Msws/wwb+ItuKHjvUNB5cB+li7apOcOi7VpzrP7BwgvQ6ZZZRBA0BdyFIEUnMwdk
13 | EjyBuVUNUA+gwg565Y+odZm5yY920O1UJW2RPBX0ZXjaS/hSxsI1GzlaxWg8wzOe
14 | lZ3K2tmWg2KRq4kBtIUZmUMJu0rbf7NtYK5PFBg01kF2dSV+LE/EfUXIkIY5flo/
15 | IamIUUHBnj2S2CrrR0VK7gZ13/z5p0AXeO3L7aCJsBmMpSgdsfj30sUpSj7JASZ+
16 | LUMS6iQUCZuL8DTIZf5W1Va1RfJklcw0hkBd/YDGUo6uk6hCQFNcuZnsEmn6lkh7
17 | cgsrOnCzEtFMG/t1T7792Wb+DdjHq6Fm0oUUK66+hcHrAVvtdxe85Sagxk0SLILy
18 | Zo7LjulET6DrmrhG9nHKcfMREVD2ZrHcRhXJlQKUW3TQ+a/Ab0WamuaEa8+Vbjgi
19 | CHktxEnFJgcmeZK31u/ryTatXlepByIlEH/YnAqaPZWfBlQcMyAriHmw4WHc5n3B
20 | +FTBaODhaB/LOg6zYmPlXYZ4+BOaI2puDIqWUV62Z6HYHZ5AN0xCMSJegTKqbhl9
21 | NLHx5xmc4Hdz5awM8tDDKHifw2v1YBNusjBPORdMpNoE4W+TQjML4wFqm/JwXwdx
22 | Qdy6M/CBdnHhN3JFpFVHaXz2NsvfBvyO37NRutEZfow3WRf8FYyYaq6Z3Se/TfII
23 | XjctHTnWgUEiqPEybxTvESId+RR9FR3fVsS7vnasylEunYtC2aPn2R1iBjR2nYyj
24 | uKnKsUWjMfpUKLZ+DPSIjR2u7X3BAnlPHd7DdTQv7DSVB76uoXxg8FN0KhLg9So3
25 | jo3o/j1vhC79ehpp6S6sRRh6swXQo+r3229sgixVknisiwehGkSUABZeWJkS2Npe
26 | pffcgnzqzvU4ug9BYeP2lQk8S8fNJL6qAtwN6589qE+Jgdw//uO0W0dd9MA5zyJ3
27 | TTidV0aYC8gGN+fdEQBRl7tlL0Eg1ueto/TKLXL5Z2+z4Gtb2utqpndVyU1xaqeU
28 | xJWtTpotZUU6cwL+VR4J3UOa/9p5SCCcZIk5R3Fam5pPErzJXXOoCaigB1MMxddk
29 | WjYJHfZvSWW+xbhXsPDuKc+cI7o1Uhc8IULaYf9OmKjDmzp1AL4ZC8xNxigdvs25
30 | OZLZJv2A71wjlqkyKO9DU7an843O3rL3Uj0Gl/cX8NlOXVXQ/zZ515gizndwNrMC
31 | Z9yZn/2QeHeeJ2Eetj+mVQ8Gxjkn+WpH54wCiCTJR4vgQb6YoPskFYqXISgcQ3F1
32 | jlmTkusRDkyTHjklJ3FgWfgmvMuv1yfMrHUkAgurNvg+0pfy6LMvGwA2A7dz3M4N
33 | e+eh9crMUXuT8VTUgbj2kzwUhhWlRDSVFe7YYGwCdC/+YLGYEPYt11w6WQKu0HW5
34 | Ju4Lak0H5j6qXi4VY+C1NBwffsmt/pI0cM1hO6AuPuYOsfW6Wq2AB4VDZbpBTdL1
35 | Uvenc/1hHbRrIPnikjyNFKkIR32Z3L9L1FLk9ClsUDuPcfOTlk4HQ7DcSaXQ2jBA
36 | ndCCh0zVWv/Wm5Iu/HLD9f9q69Mj8SIQxgAfxSRiikLUFAUguetyklcHkXjDqbcT
37 | I2jGhcGwj8baO4o2SQUk0nB4AhydxfMGPzcjpGubRokS/b/4L1QXUC5pPUUmrTtJ
38 | BbDLIKh0undyf7FL0io2BHQd6BA7J/lU2eGYNf6e2Wj03/Qi3j6vBCSQtbeY1A/3
39 | aMALQtJWdR9txKUJaY9qdgVNPvJy2TsvX6coXuA9f45RkEhGqGjqKxp8+sRBnKW0
40 | ipdCSu1qklP6E7czyRZCBnwd5LNsHIRgQI8RAO7yQRpLHOS5OAZf5caMFJXhxgrP
41 | FykeA4sntvVoeYGSHqLw8w3Fb9l2bof2pGl1Js0Y/VhZgtFEAyMQIYIFpf+D/Bp/
42 | tjwfNwuu8BAbZeWggLGg5s9+c1f/g6LtGszDVPMg9prgTd1LWC836FgrEpmvD/GA
43 | 5pmuptMWRrIxek9OLJeU06r7QhEEZ//zSKaMtW4w1WI9yEcbOsdyfo0SRoW42SeM
44 | bGsG6kSCj+lFLwfFMuoMimNQ5w+N/munbnyP7XmE5Ij8dccup8djINwkrvtR/42z
45 | r0bbcjOK7Sn8NlEFw+4TqHHoYHONo1hgQOPILq6Knh/T9fxokGcd8l9S+DLoFRNb
46 | qXkjxNr/u4+iW1OwzA4BT2UwhlWzp0bJGfuO4oYhnKDDS9DgZdW5qNQpFD4EVJFf
47 | UQnRBny8ExX+cgXn6BLPUwrP7U/rlp410hIkHNfnexzoYC7NDH2Wn7iNUep/zpgQ
48 | w1TslkpsFuKTs8dS6aVcRhwEsN5w80z1LOU/skviX3MYQOw1m/uIU5dPDnDzPJ/r
49 | KSn2tDbLOD3qCIom5C6X2O9QrokF1jCKlm2ck+uxanjXx8s4GYjI0+7mgi+6OJiK
50 | w6SIfD3v+EOjkigUEMG4pyo2ByK4BNs6Nx4bnpRKgsLlaKYvXRvi+w6FDshgn2Oc
51 | wSorluiFMofyg+GtAbuutD9/0FF3Ln14b4efGltRhdI+RqTOP6Xbe9rdDpz6qdGZ
52 | DZf9uZsdAvwTbugX+s3ieznXZdvfWoPLqqAyv12HCuG8qTxjkEhlzlB1OA9K1Zkf
53 | cHYMIKx2LU85iKt6cjIA+VTuPY38QZE3DUEW82vpkOPv3vLuD9K7YCK2eQQ4Q38Z
54 | -----END RSA PRIVATE KEY-----
55 |
--------------------------------------------------------------------------------
/app/Resources/jwt/public.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyWl2FFvRGBJ/Kbz8mfLT
3 | AQ9NWzKpBQGGfoYuPhIaEj9V/sf7bjwQ1SXprLvoG8PItds81nbOoFStPOMwEvY2
4 | x8RliZEGWwq+HCsveFgcYOjbq/rT3gkOn82FZ2lLpfA1QaPWH97CH4dP1aV2ZsiA
5 | 8lHGXvRTUhQTdUZdHgk7VFqft+xkcDztHYr1LG/T1UfnDZgVY/pvlrslgV273PSw
6 | FT1nEPUKQUWkALvkHZfIffaqdDUEKNTavKeYFcLyvCbTttkgvgdEa6a33Eef5Y6p
7 | UeXxB0ZJw8JJx3XyHqlkIxAnn3YK+3aX27ux4G/1gaFrDi5atY/A+4bEs8yt+gzp
8 | eypWAAcoZSUiBijWpHdCVwX0e6niKxyld/NZ0Ub2u0kjLkGxtjZJXC+wVJ4os+Sy
9 | 2klLAyawdJMjMDvBdxH1iE9+NhrpzcgYQkt1kg8GBRUDDRUxleKcuJmscjlY7qHX
10 | lEOG94cErd2Mt34x4i6oGPLDQpriVi3nCqMo28UAbLilfcjmYKsTc9uNuNdsDp8F
11 | mPgTZ1NcMKU7ocs7TOFwEPARfqryLOPsf9yfdvGQH7CzLnztDDCybjTlqi9lwexF
12 | dmG7FKnZPf9vxCnH1+9x4j6bBmKMujypFSxuRbhbMysu9Q4HhFR0OFyN7ChgN9xy
13 | bz1VJUXrL/GaK/bXkPASojMCAwEAAQ==
14 | -----END PUBLIC KEY-----
15 |
--------------------------------------------------------------------------------
/app/Resources/views/admin.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {% if title is defined %}{{ title }}{% else %}Symfony2 application skeleton{% endif %} | Deathstar
8 |
9 |
10 | {% block css %}{% endblock %}
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 | {% block body %}{% endblock %}
24 |
25 |
26 |
27 | {% block js %}{% endblock %}
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/Resources/views/base.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {% if title is defined %}{{ title }}{% else %}Symfony Force Edition{% endif %}
8 |
9 |
10 | {% block css %}{% endblock %}
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 | {% block body %}{% endblock %}
25 |
26 |
27 |
28 | {% block js %}{% endblock %}
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/autoload.php:
--------------------------------------------------------------------------------
1 | getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV') ?: 'dev');
19 | $debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(array('--no-debug', '')) && $env !== 'prod';
20 |
21 | if ($debug) {
22 | Debug::enable();
23 | }
24 |
25 | $kernel = new AppKernel($env, $debug);
26 | $application = new Application($kernel);
27 | $application->run($input);
28 |
--------------------------------------------------------------------------------
/app/logs/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/app/logs/.gitkeep
--------------------------------------------------------------------------------
/app/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 | ../src/*/*Bundle/Tests
13 | ../src/*/Bundle/*Bundle/Tests
14 | ../src/*Bundle/Tests
15 |
16 |
17 |
18 |
23 |
24 |
25 |
26 | ../src
27 |
28 | ../src/*Bundle/Resources
29 | ../src/*Bundle/Tests
30 | ../src/*/*Bundle/Resources
31 | ../src/*/*Bundle/Tests
32 | ../src/*/Bundle/*Bundle/Resources
33 | ../src/*/Bundle/*Bundle/Tests
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/assets/img/colors/txture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/colors/txture.png
--------------------------------------------------------------------------------
/assets/img/colors/txture_lighter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/colors/txture_lighter.png
--------------------------------------------------------------------------------
/assets/img/colors/txture_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/colors/txture_trans.png
--------------------------------------------------------------------------------
/assets/img/homepage/carousel/banner-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/homepage/carousel/banner-1.jpg
--------------------------------------------------------------------------------
/assets/img/homepage/carousel/banner-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/homepage/carousel/banner-2.jpg
--------------------------------------------------------------------------------
/assets/img/homepage/carousel/banner-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/homepage/carousel/banner-3.jpg
--------------------------------------------------------------------------------
/assets/img/homepage/testimonials/chewbacca.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/homepage/testimonials/chewbacca.jpg
--------------------------------------------------------------------------------
/assets/img/homepage/testimonials/grievous.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/homepage/testimonials/grievous.png
--------------------------------------------------------------------------------
/assets/img/homepage/testimonials/vader.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/homepage/testimonials/vader.jpg
--------------------------------------------------------------------------------
/assets/img/homepage/testimonials/yoda.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/homepage/testimonials/yoda.jpg
--------------------------------------------------------------------------------
/assets/img/icons/darth-vader-icon.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/icons/darth-vader-icon.jpeg
--------------------------------------------------------------------------------
/assets/img/icons/deathstar-ico-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/icons/deathstar-ico-32.png
--------------------------------------------------------------------------------
/assets/img/icons/luke-skywalker-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/icons/luke-skywalker-icon.png
--------------------------------------------------------------------------------
/assets/img/icons/yoda-icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/img/icons/yoda-icon.jpg
--------------------------------------------------------------------------------
/assets/js/admin/scripts.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 | $('[data-toggle="tooltip"]').tooltip();
3 |
4 | $('a.js-confirm').click(function(e) {
5 | e.preventDefault();
6 |
7 | if (confirm('Are you sure?')) {
8 | window.location.href = this.href;
9 | }
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/assets/js/app/scripts.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/assets/js/app/scripts.js
--------------------------------------------------------------------------------
/assets/less/admin.less:
--------------------------------------------------------------------------------
1 | @import "../vendor/bootstrap/less/bootstrap";
2 | @import "../vendor/fontawesome/less/font-awesome";
3 | @import "admin/layout";
4 | @import "admin/pagination";
5 | @import "admin/tables";
6 | @import "admin/bootswatch";
7 | @import "admin/variables";
8 | @import "admin/panels";
9 | @import "admin/navs";
10 |
11 | @fa-font-path: "/build/fonts";
12 |
--------------------------------------------------------------------------------
/assets/less/admin/layout.less:
--------------------------------------------------------------------------------
1 | .container.container-content {
2 | margin-top: @navbar-height;
3 | }
4 | .container-content {
5 | > h1:first-child {
6 | margin-bottom: 25px;
7 | margin-top: 30px;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/assets/less/admin/navs.less:
--------------------------------------------------------------------------------
1 | @navbar-inverse-link-color: #fff;
2 | @navbar-inverse-link-active-bg: transparent;
3 | @navbar-link-active-border-width: 3px;
4 |
5 | .navbar {
6 | box-shadow: 0 0 11px #444;
7 | border: 0;
8 |
9 | &.navbar-inverse {
10 | #gradient > .horizontal(@brand-success, #B5AC49, 10%, 90%);
11 |
12 | .nav li {
13 | &.active a {
14 |
15 | border-bottom: @navbar-link-active-border-width solid rgba(255, 255, 255, 0.4);
16 | padding-bottom: @navbar-padding-vertical - @navbar-link-active-border-width;
17 | }
18 | a:hover {
19 | transition: background .3s;
20 | background: rgba(255, 255, 255, 0.13);
21 | }
22 | }
23 | }
24 |
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/assets/less/admin/pagination.less:
--------------------------------------------------------------------------------
1 | .panel-footer {
2 | .pagination {
3 | float: right;
4 | margin: 0;
5 | }
6 | .pagination-summary {
7 | // btn height + padding + border
8 | line-height: @line-height-computed + @padding-base-vertical*2 + 2;
9 | margin: 0;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/assets/less/admin/panels.less:
--------------------------------------------------------------------------------
1 |
2 |
3 | .panel {
4 | box-shadow: 1px 1px 3px #aaa;
5 | border: 0;
6 | }
7 |
8 |
9 | .panel {
10 | > .panel-heading {
11 | color: #fff;
12 | min-height: @padding-base-vertical*4+@line-height-computed + 2;
13 | .panel-title {
14 | line-height: 22px;
15 | }
16 |
17 | .toolbar {
18 | float: right;
19 | margin-top: -3px;
20 | .btn {
21 | .btn-sm();
22 | }
23 | .btn-default {
24 | background: rgba(255, 255, 255, 0.45);
25 | color: #fff;
26 | transition: background .5s;
27 | &:hover {
28 | background: rgba(255, 255, 255, 0.65);
29 | }
30 | }
31 | }
32 |
33 | }
34 |
35 | &-primary > .panel-heading {
36 | border-bottom: 4px solid lighten(@brand-primary, 10%);
37 | #gradient > .horizontal(@brand-primary, @brand-primary-light, 10%, 90%);
38 | }
39 | &-success > .panel-heading {
40 | border-bottom: 4px solid lighten(@brand-success-light, 15%);
41 | #gradient > .horizontal(@brand-success, @brand-success-light, 10%, 90%);
42 | }
43 | &-danger > .panel-heading {
44 | border-bottom: 4px solid lighten(@brand-danger-light, 15%);
45 | #gradient > .horizontal(@brand-danger, @brand-danger-light, 10%, 90%);
46 | }
47 | &-info > .panel-heading {
48 | border-bottom: 4px solid lighten(@brand-info-light, 15%);
49 | #gradient > .horizontal(@brand-info, @brand-info-light, 10%, 90%);
50 | }
51 | &-warning > .panel-heading {
52 | border-bottom: 4px solid lighten(@brand-warning-light, 15%);
53 | #gradient > .horizontal(@brand-warning, @brand-warning-light, 10%, 90%);
54 | }
55 | &-default > .panel-heading {
56 | border-bottom: 4px solid #ccc;
57 | #gradient > .horizontal(#aaa, #ddd, 10%, 70%);
58 | }
59 |
60 | > .table {
61 | thead th {
62 |
63 | background: #f7f7f7;
64 | a {
65 | text-decoration: none;
66 | color: @gray-light;
67 | }
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/assets/less/admin/tables.less:
--------------------------------------------------------------------------------
1 | .table {
2 | tr.filters {
3 | .btn-group-justified {
4 |
5 | }
6 | }
7 | td.col-min {
8 | width: 1%;
9 | white-space: nowrap;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/assets/less/admin/variables.less:
--------------------------------------------------------------------------------
1 | @body-bg: #eee;
2 | @font-family-base: 'Open Sans', sans-serif;
3 |
4 | @brand-primary: #3a7bd5;
5 | @brand-primary-light: #00B1D7;
6 | @brand-success: #3CA55C;
7 | @brand-info: #56B4D3;
8 | @brand-info-light: #348F50;
9 | @brand-warning: #F09819;
10 | @brand-warning-light: #EDDE5D;
11 | @brand-danger: #f83600;
12 | @brand-danger-light: #fe8c00;
13 | @brand-success: #3CA55C;
14 | @brand-success-light: #B5AC49;
15 |
16 | @panel-border-radius: @border-radius-small;
17 |
18 | @alert-primary-bg: @brand-primary;
19 | @alert-primary-border: @brand-primary-light;
20 | @alert-primary-text: #fff;
21 |
22 | @alert-success-bg: @brand-success;
23 | @alert-success-border: @brand-success-light;
24 | @alert-success-text: #fff;
25 |
26 | @alert-warning-bg: @brand-warning;
27 | @alert-warning-border: @brand-warning-light;
28 | @alert-warning-text: #fff;
29 |
30 | @alert-info-bg: @brand-info;
31 | @alert-info-border: @brand-info-light;
32 | @alert-info-text: #fff;
33 |
34 | @alert-danger-bg: @brand-danger;
35 | @alert-danger-border: @brand-danger-light;
36 | @alert-danger-text: #fff;
37 |
38 |
39 | @panel-default-border: @table-border-color;
40 |
--------------------------------------------------------------------------------
/assets/less/app.less:
--------------------------------------------------------------------------------
1 | @import "../vendor/bootstrap/less/bootstrap";
2 | @import "../vendor/fontawesome/less/font-awesome";
3 | @import "app/colors";
4 | @import "app/forms";
5 | @import "app/theme";
6 | @import "app/pages/homepage";
7 |
8 | @fa-font-path: "/build/fonts";
9 |
--------------------------------------------------------------------------------
/assets/less/app/colors.less:
--------------------------------------------------------------------------------
1 | .yellow {
2 | background-color: #f1c40f;
3 | color: #fff;
4 | background-image: url(../img/colors/txture_trans.png);
5 |
6 | &.btn:hover {
7 | background-color: #f4d313;
8 | color: #fff;
9 | border-color: #f4d313;
10 | }
11 | }
12 |
13 | .red {
14 | background-color: #e74c3c;
15 | color: #fff;
16 | background-image: url(../img/colors/txture_trans.png);
17 |
18 | &.btn:hover {
19 | background-color: #ec7063;
20 | color: #fff;
21 | border-color: #ec7063;
22 | }
23 | }
24 |
25 | .green {
26 | background-color: #1abc9c;
27 | color: #fff;
28 | background-image: url(../img/colors/txture_trans.png);
29 |
30 | &.btn:hover {
31 | background-color: #58d68d;
32 | color: #fff;
33 | border-color: #58d68d;
34 | }
35 | }
36 |
37 | .blue {
38 | background-color: #3498db;
39 | color: #fff;
40 | background-image: url(../img/colors/txture_trans.png);
41 |
42 | &.btn:hover {
43 | background-color: #5dade2;
44 | color: #fff;
45 | border-color: #5dade2;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/assets/less/app/forms.less:
--------------------------------------------------------------------------------
1 | .form-group .control-label.required:after {
2 | color: #d00;
3 | content: "*";
4 | position: absolute;
5 | margin-left: 3px;
6 | top: 7px;
7 | }
8 |
--------------------------------------------------------------------------------
/assets/less/app/pages/homepage.less:
--------------------------------------------------------------------------------
1 |
2 | #carousel {
3 | .carousel-inner img {
4 | height: 350px;
5 | width: 100%;
6 | object-fit: cover;
7 | }
8 | }
9 |
10 | #testimonials {
11 |
12 | margin-top: 25px;
13 |
14 | blockquote {
15 | border-left: none;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/assets/less/app/theme.less:
--------------------------------------------------------------------------------
1 | // theme can be removed or changed completely
2 | .header {
3 | margin-bottom: 30px;
4 | }
5 |
6 |
--------------------------------------------------------------------------------
/behat.yml.dist:
--------------------------------------------------------------------------------
1 | # vi: set ft=yaml ts=2 sw=2 sts=2 :
2 |
3 | default:
4 | formatters:
5 | pretty:
6 | verbose: true
7 | paths: true
8 | snippets: true
9 |
10 | extensions:
11 | Behat\MinkExtension:
12 | sessions:
13 | default:
14 | symfony2: ~
15 | Behat\Symfony2Extension:
16 | kernel:
17 | env: test
18 | debug: false
19 |
20 | suites:
21 | default:
22 | contexts:
23 | - AppBundle\Behat\PlaceholderContext
24 | - AppBundle\Behat\DatabaseContext
25 | - AppBundle\Behat\MailerContext
26 | - AppBundle\Behat\PageContext
27 | - AppBundle\Behat\UserContext
28 | - Behat\MinkExtension\Context\MinkContext
29 | filters:
30 | tags: @user
31 |
32 | api:
33 | contexts:
34 | - ApiBundle\Behat\APIContext
35 | - AppBundle\Behat\PlaceholderContext
36 | - AppBundle\Behat\DatabaseContext
37 | - AppBundle\Behat\UserContext
38 | filters:
39 | role: "api user"
40 |
41 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "symfony2-skeleton",
3 | "private": true,
4 | "dependencies": {
5 | "bootstrap": "~3.3.5",
6 | "fontawesome": "~4.4.0"
7 | },
8 | "devDependencies": {}
9 | }
10 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "data-dog/symfony-force",
3 | "license": "MIT",
4 | "type": "project",
5 | "description": "Symfony force edition. Pragmatic symfony2 application bootstrap guide.",
6 |
7 | "autoload": {
8 | "psr-0": { "": "src/" }
9 | },
10 |
11 | "autoload-dev": {},
12 |
13 | "require": {
14 | "php": ">=5.6.0",
15 | "ext-curl": "*",
16 | "ext-mbstring": "*",
17 | "ext-mcrypt": "*",
18 | "ext-pdo": "*",
19 | "ext-pdo_mysql": "*",
20 | "ext-intl": "*",
21 | "ext-openssl": "*",
22 |
23 | "symfony/symfony": "~2.7.0",
24 | "symfony/monolog-bundle": "~2.7",
25 | "symfony/swiftmailer-bundle": "~2.3",
26 |
27 | "sensio/distribution-bundle": "~4.0.0",
28 | "sensio/framework-extra-bundle": "~3.0",
29 |
30 | "twig/extensions": "~1.0",
31 | "incenteev/composer-parameter-handler": "~2.0",
32 | "knplabs/knp-menu-bundle": "~2.0.0",
33 | "excelwebzone/recaptcha-bundle": "^1.4",
34 |
35 | "doctrine/orm": "~2.5.0",
36 | "doctrine/doctrine-bundle": "~1.4",
37 | "doctrine/doctrine-migrations-bundle": "~1.1.0",
38 | "doctrine/migrations": "~1.2.2",
39 | "doctrine/data-fixtures": "~1.0",
40 | "data-dog/pager-bundle": "^0.2.0",
41 | "data-dog/audit-bundle": "^0.1.0",
42 | "gedmo/doctrine-extensions": "~2.4.0",
43 |
44 | "predis/predis": "^1.0"
45 | },
46 |
47 | "require-dev": {
48 | "sensio/generator-bundle": "~2.3",
49 | "fzaninotto/faker": "~1.4.0",
50 |
51 | "phpunit/phpunit": "^4.7",
52 |
53 | "behat/behat": "~3.0.0",
54 | "behat/symfony2-extension": "~2.0.0",
55 | "behat/mink-extension": "~2.0.0",
56 | "behat/mink-browserkit-driver": "~1.2.0",
57 | "behat/mink": "~1.6.0"
58 | },
59 |
60 | "scripts": {
61 | "post-install-cmd": [
62 | "AppBundle\\Composer::misc",
63 | "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
64 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
65 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
66 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets"
67 | ],
68 | "post-update-cmd": [
69 | "AppBundle\\Composer::misc",
70 | "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
71 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
72 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
73 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets"
74 | ]
75 | },
76 |
77 | "config": {
78 | "bin-dir": "bin"
79 | },
80 |
81 | "extra": {
82 | "symfony-app-dir": "app",
83 | "symfony-web-dir": "web",
84 | "symfony-assets-install": "relative",
85 | "incenteev-parameters": {
86 | "file": "app/config/parameters.yml"
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/docker-compose.yml.dist:
--------------------------------------------------------------------------------
1 | php:
2 | image: datadoglt/php56
3 | links: [mysql, redis]
4 | volumes: [ ".:/var/www", "./app/Resources/docker/init.sh:/init.sh", "./app/session:/var/lib/php/session" ]
5 |
6 | web:
7 | image: datadoglt/nginx-sf
8 | volumes: [".:/var/www"]
9 | ports: [80]
10 | links: [php]
11 | environment: { VIRTUAL_HOST: symfony.dev,admin.symfony.dev }
12 |
13 | redis:
14 | image: redis
15 |
16 | mysql:
17 | image: mysql
18 | hostname: mysql
19 | ports: ["3306"]
20 | environment:
21 | MYSQL_DATABASE: symfony2
22 | MYSQL_USER: symfony2
23 | MYSQL_PASSWORD: symfony2
24 | MYSQL_ROOT_PASSWORD: mysecretpw
25 |
--------------------------------------------------------------------------------
/features/account/confirm.feature:
--------------------------------------------------------------------------------
1 | @user
2 | Feature: Confirming an account
3 | In order to use application
4 | As an unconfirmed user
5 | I need to be able to confirm my account
6 |
7 | Scenario: should be able to confirm an account
8 | Given I have signed up as "luke.skywalker@datadog.lt"
9 | When I follow the confirmation link in my email
10 | Then I should see "Account details" on page headline
11 | # Then my account "luke.skywalker@datadog.lt" should be confirmed
12 |
13 | Scenario: confirm an account
14 | Given I have signed up as "luke.skywalker@datadog.lt"
15 | When I follow the confirmation link in my email
16 | And I fill in my personal details
17 | Then I should see success notification "The user Luke Skywalker was successfully confirmed"
18 |
--------------------------------------------------------------------------------
/features/account/profile.feature:
--------------------------------------------------------------------------------
1 | @user
2 | Feature: Profile management
3 | In order to keep account up to date
4 | As a confirmed user
5 | I need to be able to update my profile
6 |
7 | Background:
8 | Given confirmed user named "General Grievous"
9 | And I'm logged in as "general.grievous@datadog.lt"
10 |
11 | Scenario: can update without filling in password
12 | Given I am on "profile" page
13 | When I fill in "First name" with "Dark"
14 | And I press "Update"
15 | Then I should see success notification "Your profile was updated successfully"
16 |
17 | Scenario: can change password
18 | Given I am on "profile" page
19 | When I fill in "Password" with "S3cretpass"
20 | And I fill in "Repeat password" with "S3cretpass"
21 | And I press "Update"
22 | Then I should see success notification "Your profile was updated successfully"
23 |
--------------------------------------------------------------------------------
/features/admin/login.feature:
--------------------------------------------------------------------------------
1 | @user
2 | Feature: Logging in to administrator panel
3 | In order to use manage site
4 | As a administrator
5 | I need to be able to login
6 |
7 | Scenario: can't login with unprivileged user
8 | Given confirmed user named "Chewbacca Wookiee"
9 | And I'm logged in as "chewbacca.wookiee@datadog.lt"
10 | When I visit "admin" page
11 | Then the response code should be 403
12 |
13 | Scenario: admin user is able to login
14 | Given confirmed admin named "Chewbacca Wookiee"
15 | And I'm logged in as "chewbacca.wookiee@datadog.lt"
16 | When I visit "admin" page
17 | Then I should see "Deathstar:Admin"
18 |
19 |
--------------------------------------------------------------------------------
/features/api/authentication.feature:
--------------------------------------------------------------------------------
1 | Feature: protected API resources
2 |
3 | In order to protect resources
4 | As an api user
5 | I should not be able to access certain API resources without authentication
6 |
7 | Scenario: request without an authentication token should result in error
8 | When I send GET request to "/api/cms-blocks"
9 | Then the response code should be 401
10 |
11 | Scenario: authentication should allow only POST method
12 | When I send GET request to "/api/authenticate"
13 | Then the response code should be 405
14 |
15 | Scenario: authentication requires credentials
16 | When I send POST request to "/api/authenticate" with:
17 | """
18 | {
19 | "please": "log me in"
20 | }
21 | """
22 | Then the response code should be 401
23 | And there should be an error message "Username or password is not valid." in response
24 |
25 | Scenario: try to authenticate when there is no such user
26 | When I try to authenticate as "unavailable@datadog.lt"
27 | Then the response code should be 401
28 |
29 | Scenario: should be able to authenticate a confirmed user
30 | Given confirmed user named "John Doe"
31 | When I try to authenticate as "john.doe@datadog.lt"
32 | Then the response code should be 200
33 | And the response should contain json:
34 | """
35 | {
36 | "id": %john.doe@datadog.lt%,
37 | "roles": ["ROLE_USER"],
38 | "token": "/(.+)/"
39 | }
40 | """
41 |
42 | Scenario: should not be able to authenticate an unconfirmed user
43 | Given unconfirmed user named "John Doe"
44 | When I try to authenticate as "john.doe@datadog.lt"
45 | Then the response code should be 401
46 |
--------------------------------------------------------------------------------
/features/api/cms-blocks/list.feature:
--------------------------------------------------------------------------------
1 | Feature: get cms blocks
2 |
3 | In order to see cms blocks
4 | As an api user
5 | I should be able to get list of cms blocks
6 |
7 | Scenario: api resource is protected
8 | When I send GET request to "/api/cms-blocks"
9 | Then the response code should be 401
10 |
11 | Scenario: can list cms blocks
12 | Given confirmed user named "Chewbacca Wookiee"
13 | And I'm authenticated as "chewbacca.wookiee@datadog.lt"
14 | When I send GET request to "/api/cms-blocks"
15 | Then the response code should be 200
16 | And the response should contain json:
17 | """
18 | {
19 | "blocks": [
20 | {
21 | "id": 1,
22 | "name": "Footer"
23 | },
24 | {
25 | "id": 2,
26 | "name": "Css"
27 | },
28 | {
29 | "id": 3,
30 | "name": "Js"
31 | }
32 | ]
33 | }
34 | """
35 |
36 | Scenario: can get paged cms blocks
37 | Given confirmed user named "Chewbacca Wookiee"
38 | And I'm authenticated as "chewbacca.wookiee@datadog.lt"
39 | When I send GET request to "/api/cms-blocks?page=2&limit=1"
40 | Then the response code should be 200
41 | And the response should match json:
42 | """
43 | {
44 | "blocks": [
45 | {
46 | "id": 2,
47 | "alias": "css",
48 | "name": "Css",
49 | "content": "\/* cms block for css *\/",
50 | "createdAt": "/(.+)/",
51 | "updatedAt": "/(.+)/"
52 | }
53 | ]
54 | }
55 | """
56 |
--------------------------------------------------------------------------------
/features/api/version.feature:
--------------------------------------------------------------------------------
1 | Feature: see api version
2 |
3 | In order to know api version
4 | As an api user
5 | I should be able to get version details anonymously
6 |
7 | Scenario: anonymous user can view api version details
8 | When I send GET request to "/api/version"
9 | Then the response code should be 200
10 | And the response should contain json:
11 | """
12 | {
13 | "name": "symfony-force",
14 | "version": "0.1.0",
15 | "description": "Pragmatic symfony2 application bootstrap guide."
16 | }
17 | """
18 |
--------------------------------------------------------------------------------
/features/guest/login.feature:
--------------------------------------------------------------------------------
1 | @user
2 | Feature: Logging in
3 | In order to use application and manage private resources
4 | As a registered user
5 | I need to be able to login
6 |
7 | Background:
8 | Given confirmed user named "Chewbacca Wookiee"
9 |
10 | Scenario: can't login with incorrect credentials
11 | Given I am on "login" page
12 | When I try to login as "chewbacca.wookiee@datadog.lt" using password "any"
13 | Then I should see error notification "Email or password is incorrect"
14 |
15 | Scenario: confirmed user is able to login
16 | Given I am on "login" page
17 | When I try to login as "chewbacca.wookiee@datadog.lt" using password "S3cretpassword"
18 | Then I should see "Symfony Force Edition" on page headline
19 | And I should be logged in
20 |
21 | Scenario: unconfirmed user cannot login
22 | Given unconfirmed user named "Darth Vader"
23 | And I am on "login" page
24 | When I try to login as "darth.vader@datadog.lt" using password "S3cretpassword"
25 | Then I should see error notification "Email or password is incorrect"
26 |
--------------------------------------------------------------------------------
/features/guest/signup.feature:
--------------------------------------------------------------------------------
1 | @user
2 | Feature: Signing up
3 | In order to use application
4 | As an anonymous user
5 | I need to be able to signup
6 |
7 | Scenario: signup with valid email
8 | Given I am on "signup" page
9 | When I signup as "luke.skywalker@datadog.lt"
10 | Then I should see success notification "You should receive confirmation email shortly"
11 | And I should see "Login" on page headline
12 | And I should receive an email to "luke.skywalker@datadog.lt"
13 |
14 | Scenario: cannot signup with a confirmed email address
15 | Given confirmed user named "Luke Skywalker"
16 | And I am on "signup" page
17 | When I attempt to signup as "luke.skywalker@datadog.lt"
18 | Then I should see a form field error "Email luke.skywalker@datadog.lt is already confirmed"
19 |
20 | Scenario: signing up with unconfirmed email resends confirmation token
21 | Given unconfirmed user named "Luke Skywalker"
22 | And I am on "signup" page
23 | When I attempt to signup as "luke.skywalker@datadog.lt"
24 | Then I should see info notification "Confirmation email was successfully resent to luke.skywalker@datadog.lt"
25 |
26 | Scenario: try to signup with invalid email
27 | Given I am on "signup" page
28 | When I signup as "luke"
29 | Then I should see a form field error "Email address is not valid"
30 |
31 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "symfony-force",
3 | "version": "0.1.0",
4 | "description": "Pragmatic symfony2 application bootstrap guide.",
5 | "license": "MIT",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/DATA-DOG/symfony-force.git"
9 | },
10 | "dependencies": {
11 | "grunt": "~0.4.0",
12 | "grunt-contrib-clean": "^0.6.0",
13 | "grunt-contrib-concat": "^0.5.0",
14 | "grunt-contrib-copy": "^0.7.0",
15 | "grunt-contrib-less": "^1.0.1",
16 | "grunt-contrib-uglify": "^0.6.0",
17 | "grunt-contrib-watch": "^0.6.1",
18 | "grunt-remove-logging": "^0.2.0",
19 | "grunt-shell": "^1.1.2",
20 | "load-grunt-tasks": "^1.0.0",
21 | "time-grunt": "^1.0.0"
22 | },
23 | "devDependencies": {}
24 | }
25 |
--------------------------------------------------------------------------------
/src/AdminBundle/AdminBundle.php:
--------------------------------------------------------------------------------
1 | load('twig.yml');
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/AdminBundle/Form/CmsBlockType.php:
--------------------------------------------------------------------------------
1 | add('alias', 'text', ['label' => 'cms_block.label.alias']);
29 | $builder->add('name', 'text', ['label' => 'cms_block.label.name']);
30 | $builder->add('content', 'textarea', ['label' => 'cms_block.label.content']);
31 | }
32 |
33 | /**
34 | * Configures the options for this type.
35 | *
36 | * @param OptionsResolver $resolver The resolver for the options.
37 | */
38 | public function configureOptions(OptionsResolver $resolver)
39 | {
40 | $resolver->setDefaults([
41 | 'data_class' => CmsBlock::class
42 | ]);
43 | }
44 |
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/AdminBundle/Form/MailTemplateType.php:
--------------------------------------------------------------------------------
1 | add('alias', 'text', [
30 | 'label' => 'mail_template.label.alias'
31 | ])
32 | ->add('subject', 'text', [
33 | 'label' => 'mail_template.label.subject'
34 | ])
35 | ->add('content', 'textarea', [
36 | 'label' => 'mail_template.label.content'
37 | ])
38 | ;
39 | }
40 |
41 | /**
42 | * Configures the options for this type.
43 | *
44 | * @param OptionsResolver $resolver The resolver for the options.
45 | */
46 | public function configureOptions(OptionsResolver $resolver)
47 | {
48 | $resolver->setDefaults([
49 | 'data_class' => MailTemplate::class
50 | ]);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/AdminBundle/Form/UserType.php:
--------------------------------------------------------------------------------
1 | add('email', 'email', [
20 | 'label' => 'user.label.email',
21 | ])
22 | ->add('firstname', 'text', [
23 | 'label' => 'user.label.firstname',
24 | ])
25 | ->add('lastname', 'text', [
26 | 'label' => 'user.label.lastname',
27 | ])
28 | ->add('plainPassword', 'repeated', [
29 | 'type' => 'password',
30 | 'first_options' => ['label' => 'user.label.password'],
31 | 'second_options' => ['label' => 'user.label.repeat_password'],
32 | ])
33 | ;
34 | }
35 |
36 | /**
37 | * Configures the options for this type.
38 | *
39 | * @param OptionsResolver $resolver The resolver for the options.
40 | */
41 | public function configureOptions(OptionsResolver $resolver)
42 | {
43 | $resolver->setDefaults([
44 | 'data_class' => User::class,
45 | ]);
46 | }
47 |
48 | /**
49 | * @return string
50 | */
51 | public function getName()
52 | {
53 | return 'user';
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/AdminBundle/Menu/MenuBuilder.php:
--------------------------------------------------------------------------------
1 | createItem('root');
21 | $menu->setChildrenAttribute('class', 'nav navbar-nav pull-right');
22 |
23 | $child = function($label, $route) use($menu) {
24 | $attributes = ['role' => 'presentation'];
25 | $menu->addChild($this->container->get('translator')->trans($label, [], 'menu'), compact('route', 'attributes'));
26 | };
27 |
28 | if ($this->getToken()->getUser() instanceof UserInterface) {
29 | $child($this->getToken()->getUser(), 'app_user_profile');
30 | $child('logout', 'app_user_logout');
31 | }
32 |
33 | return $menu;
34 | }
35 |
36 | /**
37 | * @param FactoryInterface $factory
38 | * @return \Knp\Menu\ItemInterface
39 | */
40 | public function main(FactoryInterface $factory)
41 | {
42 | $menu = $factory->createItem('root');
43 | $menu->setChildrenAttribute('class', 'nav navbar-nav');
44 |
45 | $child = function($label, $route) use($menu) {
46 | $attributes = ['role' => 'presentation'];
47 | $menu->addChild($this->container->get('translator')->trans($label, [], 'menu'), compact('route', 'attributes'));
48 | };
49 |
50 | $child('users', 'admin_user_index');
51 | $child('templates', 'admin_mailtemplate_index');
52 | $child('audit', 'admin_audit_index');
53 | $child('cms', 'admin_cmsblock_index');
54 |
55 | return $menu;
56 | }
57 |
58 | /**
59 | * Get Security Token Storage.
60 | * @return TokenInterface
61 | */
62 | private function getToken()
63 | {
64 | if (!$this->container->has('security.token_storage')) {
65 | throw new \LogicException('The SecurityBundle is not registered in your application.');
66 | }
67 |
68 | $token = $this->container->get('security.token_storage')->getToken();
69 | if (!$token instanceof TokenInterface) {
70 | return null;
71 | }
72 |
73 | return $token;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/config/routing.yml:
--------------------------------------------------------------------------------
1 | admin:
2 | resource: "@AdminBundle/Controller/"
3 | type: annotation
4 | prefix: /
5 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/config/twig.yml:
--------------------------------------------------------------------------------
1 | services:
2 | twig.audit.extension:
3 | class: AdminBundle\Twig\AuditExtension
4 | tags:
5 | - { name: twig.extension }
6 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/translations/menu.en.yml:
--------------------------------------------------------------------------------
1 | users: "Users"
2 | templates: "Templates"
3 | audit: "Audit"
4 | cms: "CMS"
5 | logout: "Logout"
6 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/translations/messages.en.yml:
--------------------------------------------------------------------------------
1 | admin_project_name: "Deathstar:Admin"
2 |
3 | title:
4 | edit: "Edit"
5 | delete: 'Delete'
6 |
7 | button:
8 | save: "Save"
9 | cancel: "Cancel"
10 |
11 | user:
12 | label:
13 | email: 'Email'
14 | name: 'Name'
15 | registered_on: 'Registered on'
16 | firstname: 'First name'
17 | lastname: 'Last name'
18 | password: 'Password'
19 | repeat_password: 'Repeat password'
20 | new:
21 | title: 'Create new user'
22 | enter_details: 'Enter user details'
23 | index:
24 | title: 'Users'
25 | new: 'Add new user'
26 | edit:
27 | title: 'Edit user'
28 | flash:
29 | created: 'User created successfully'
30 | updated: 'User updated successfully'
31 | removed: 'User removed successfully'
32 |
33 | mail_template:
34 | label:
35 | alias: "Alias"
36 | subject: "Subject"
37 | content: "Content"
38 | last_updated: 'Last updated'
39 | new:
40 | title: 'New email template'
41 | enter_details: 'Enter template details'
42 | index:
43 | title: 'Email templates'
44 | new: 'Add new template'
45 | edit:
46 | title: 'Edit template'
47 | flash:
48 | created: 'Mail template created successfully'
49 | updated: 'Mail template updated successfully'
50 | removed: 'Mail template removed successfully'
51 |
52 | cms_block:
53 | label:
54 | alias: "Alias"
55 | name: "Name"
56 | content: "Content"
57 | last_update: 'Last update'
58 | new:
59 | title: 'Create CMS Block'
60 | enter_details: 'Enter block info'
61 | edit:
62 | title: 'Edit CMS block'
63 | index:
64 | title: 'CMS blocks'
65 | new: 'Add new'
66 | flash:
67 | created: 'CMS block created successfully'
68 | updated: 'CMS block updated successfully'
69 | removed: 'CMS block removed successfully'
70 |
71 | audit:
72 | index:
73 | title: 'Audit'
74 | search_by_pk: 'Search by primary key..'
75 | show_history: 'Show all resource history'
76 | was_associated: 'was associated with'
77 | was_disassociated: 'was dissociated from'
78 | was_updated: 'was updated'
79 | was_removed: 'was removed'
80 | was_inserted: 'was inserted'
81 | by_user: 'by'
82 | by_system: 'by power user or task runner.'
83 | diff: 'audit diff'
84 | field: 'Field'
85 | old: 'Old'
86 | new: 'New'
87 | pagination:
88 | any_source: 'Any source class'
89 | any_user: 'Any user'
90 | unknown: 'Unknown'
91 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/Audit/assoc.html.twig:
--------------------------------------------------------------------------------
1 |
2 | {{ assoc.label }}
3 |
4 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/Audit/associate.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | |
5 |
6 | {{ log.source.typLabel }} {{ audit_assoc(log.source) }} {{ 'audit.index.was_associated'|trans }}
7 |
8 | {{ log.target.typLabel }} {{ audit_assoc(log.target) }}, {{ audit_blame(log.blame) }}
9 | |
10 | {{ time_diff(log.loggedAt) }} |
11 | |
12 |
13 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/Audit/blame.html.twig:
--------------------------------------------------------------------------------
1 | {% if blame %}
2 | {{ 'audit.index.by_user'|trans }} {{ audit_assoc(blame) }}
3 | {% else %}
4 | {{ 'audit.index.by_system'|trans|raw }}
5 | {% endif %}
6 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/Audit/diff.html.twig:
--------------------------------------------------------------------------------
1 | {% extends "AdminBundle::layout.html.twig" %}
2 |
3 | {% block content %}
4 | {{ audit_assoc(log.source) }}{{ 'audit.index.diff'|trans }}
5 |
6 | {% include 'AppBundle::flashes.html.twig' %}
7 |
8 |
9 |
10 |
{{ audit_assoc(log.source) }}
11 |
12 |
13 | {% if log.action == 'insert' %}
14 |
15 | {% for field, change in log.diff %}
16 | -
17 | {{ audit_value(change.new) }}
18 | {{ field }}
19 |
20 | {% endfor %}
21 |
22 | {% else %}
23 |
24 |
25 | {{ 'audit.index.field'|trans }} |
26 | {{ 'audit.index.old'|trans }} |
27 | {{ 'audit.index.new'|trans }} |
28 |
29 | {% for field, change in log.diff %}
30 |
31 | {{ field }} |
32 | {{ audit_value(change.old) }} |
33 | {{ audit_value(change.new) }} |
34 |
35 | {% endfor %}
36 |
37 | {% endif %}
38 |
39 |
40 | {% endblock %}
41 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/Audit/dissociate.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | |
5 |
6 | {{ log.source.typLabel }} {{ audit_assoc(log.source) }} {{ 'audit.index.was_disassociated'|trans }}
7 | {{ log.target.typLabel }} {{ audit_assoc(log.target) }}, {{ audit_blame(log.blame) }}
8 | |
9 | {{ time_diff(log.loggedAt) }} |
10 | |
11 |
12 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/Audit/index.html.twig:
--------------------------------------------------------------------------------
1 | {% extends "AdminBundle::layout.html.twig" %}
2 |
3 | {% block content %}
4 | {{ 'audit.index.title'|trans }}
5 |
6 | {% include 'AppBundle::flashes.html.twig' %}
7 |
8 |
9 |
10 |
{{ 'audit.index.title'|trans }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {{ filter_search(logs, "history", "audit.index.search_by_pk"|trans) }}
21 |
22 |
23 | {{ filter_dropdown(logs, "blamed", users) }}
24 |
25 |
26 | {{ filter_dropdown(logs, "class", sourceClasses) }}
27 |
28 |
29 | |
30 |
31 |
32 |
33 | {% for log in logs %}
34 | {{ audit(log) }}
35 | {% endfor %}
36 |
37 |
38 |
39 |
42 |
43 | {% endblock %}
44 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/Audit/insert.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | |
5 |
6 | {{ log.source.typLabel }} {{ audit_assoc(log.source) }} {{ 'audit.index.was_inserted'|trans }},
7 | {{ audit_blame(log.blame) }}
8 | |
9 | {{ time_diff(log.loggedAt) }} |
10 |
11 |
12 |
13 |
14 | |
15 |
16 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/Audit/remove.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | |
5 |
6 | {{ log.source.typLabel }} {{ audit_assoc(log.source) }} {{ 'audit.index.was_removed'|trans }}
7 | {{ audit_blame(log.blame) }}
8 | |
9 | {{ time_diff(log.loggedAt) }} |
10 |
11 | |
12 |
13 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/Audit/update.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | |
5 |
6 | {{ log.source.typLabel }} {{ audit_assoc(log.source) }} {{ 'audit.index.was_updated'|trans }},
7 | {{ audit_blame(log.blame) }}
8 | |
9 | {{ time_diff(log.loggedAt) }} |
10 |
11 |
12 |
13 |
14 | |
15 |
16 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/CmsBlock/edit.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AdminBundle::layout.html.twig' %}
2 |
3 | {% block content %}
4 | {{ 'cms_block.edit.title'|trans }}
5 |
6 | {% include 'AppBundle::flashes.html.twig' %}
7 |
8 | {{ form_errors(form) }}
9 | {{ form_start(form) }}
10 |
11 |
12 |
{{ entity.alias }}
13 |
14 |
15 | {{ form_rest(form) }}
16 |
17 |
21 |
22 | {{ form_end(form) }}
23 | {% endblock %}
24 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/CmsBlock/index.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AdminBundle::layout.html.twig' %}
2 |
3 | {% block content %}
4 |
9 |
10 | {% include 'AppBundle::flashes.html.twig' %}
11 |
12 |
13 |
14 |
{{ 'cms_block.index.title'|trans }}
15 |
16 |
17 |
18 |
19 | # |
20 | {{ sorter_link(blocks, 't.alias', 'cms_block.label.alias'|trans) }} |
21 | {{ sorter_link(blocks, 't.name', 'cms_block.label.name'|trans) }} |
22 | {{ sorter_link(blocks, 't.updatedAt', 'cms_block.label.last_update'|trans) }} |
23 | |
24 |
25 |
26 | |
27 | {{ filter_search(blocks, "t.alias") }} |
28 | {{ filter_search(blocks, "t.name") }} |
29 | {{ filter_search(blocks, "t.updatedAt") }} |
30 |
31 |
32 |
33 | {% for block in blocks %}
34 |
35 | {{ block.id }} |
36 | {{ block.alias }} |
37 | {{ block.name }} |
38 | {{ block.createdAt|date }} |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | |
47 |
48 | {% endfor %}
49 |
50 |
51 |
54 |
55 | {% endblock %}
56 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/CmsBlock/new.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AdminBundle::layout.html.twig' %}
2 |
3 | {% block content %}
4 | {{ 'cms_block.new.title'|trans }}
5 |
6 | {% include 'AppBundle::flashes.html.twig' %}
7 |
8 | {{ form_errors(form) }}
9 | {{ form_start(form) }}
10 |
11 |
12 |
{{ 'cms_block.new.enter_details'|trans }}
13 |
14 |
15 | {{ form_rest(form) }}
16 |
17 |
21 |
22 | {{ form_end(form) }}
23 | {% endblock %}
24 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/MailTemplate/edit.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AdminBundle::layout.html.twig' %}
2 |
3 | {% block content %}
4 | {{ 'mail_template.edit.title'|trans }}
5 |
6 | {% include 'AppBundle::flashes.html.twig' %}
7 |
8 | {{ form_errors(form) }}
9 | {{ form_start(form) }}
10 |
11 |
12 |
{{ entity.alias }}
13 |
14 |
15 | {{ form_rest(form) }}
16 |
17 |
21 |
22 | {{ form_end(form) }}
23 | {% endblock %}
24 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/MailTemplate/index.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AdminBundle::layout.html.twig' %}
2 |
3 | {% block content %}
4 |
8 |
9 | {% include 'AppBundle::flashes.html.twig' %}
10 |
11 |
12 |
13 |
{{ 'mail_template.index.title'|trans }}
14 |
15 |
16 |
17 |
18 | # |
19 | {{ sorter_link(templates, 't.alias', 'mail_template.label.alias'|trans) }} |
20 | {{ sorter_link(templates, 't.subject', 'mail_template.label.subject'|trans) }} |
21 | {{ sorter_link(templates, 't.updatedAt', 'mail_template.label.last_updated'|trans) }} |
22 | |
23 |
24 |
25 | |
26 | {{ filter_search(templates, "t.alias") }} |
27 | {{ filter_search(templates, "t.subject") }} |
28 | {{ filter_search(templates, "t.updatedAt") }} |
29 |
30 |
31 |
32 | {% for template in templates %}
33 |
34 | {{ template.id }} |
35 | {{ template.alias }} |
36 | {{ template.subject }} |
37 | {{ template.createdAt|date }} |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | |
46 |
47 | {% endfor %}
48 |
49 |
50 |
53 |
54 | {% endblock %}
55 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/MailTemplate/new.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AdminBundle::layout.html.twig' %}
2 |
3 | {% block content %}
4 | {{ 'mail_template.new.title'|trans }}
5 |
6 | {% include 'AppBundle::flashes.html.twig' %}
7 |
8 | {{ form_errors(form) }}
9 | {{ form_start(form) }}
10 |
11 |
12 |
{{ 'mail_template.new.enter_details'|trans }}
13 |
14 |
15 | {{ form_rest(form) }}
16 |
17 |
21 |
22 | {{ form_end(form) }}
23 | {% endblock %}
24 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/User/edit.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AdminBundle::layout.html.twig' %}
2 |
3 | {% block content %}
4 | {{ 'user.edit.title'|trans }}
5 |
6 | {% include 'AppBundle::flashes.html.twig' %}
7 |
8 | {{ form_errors(form) }}
9 | {{ form_start(form) }}
10 |
11 |
12 |
{{ entity.email }}
13 |
14 |
15 | {{ form_rest(form) }}
16 |
17 |
21 |
22 | {{ form_end(form) }}
23 | {% endblock %}
24 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/User/index.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AdminBundle::layout.html.twig' %}
2 |
3 | {% block content %}
4 |
8 |
9 | {% include 'AppBundle::flashes.html.twig' %}
10 |
11 |
12 |
13 |
{{ 'user.index.title'|trans }}
14 |
15 |
16 |
17 |
18 | # |
19 | {{ sorter_link(users, 'u.email', 'user.label.email'|trans) }} |
20 | {{ sorter_link(users, 'u.firstname', 'user.label.name'|trans) }} |
21 | {{ sorter_link(users, 'u.createdAt', 'user.label.registered_on'|trans) }} |
22 | |
23 |
24 |
25 | |
26 | {{ filter_search(users, "u.email") }} |
27 | {{ filter_search(users, "u.firstname") }} |
28 | {{ filter_search(users, "u.createdAt") }} |
29 |
30 |
31 |
32 | {% for user in users %}
33 |
34 | {{ user.id }} |
35 | {{ user.email }} |
36 | {{ user.fullName }} |
37 | {{ user.createdAt|date }} |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | |
46 |
47 | {% endfor %}
48 |
49 |
50 |
53 |
54 | {% endblock %}
55 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/User/new.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AdminBundle::layout.html.twig' %}
2 |
3 | {% block content %}
4 | {{ 'user.new.title'|trans }}
5 |
6 | {% include 'AppBundle::flashes.html.twig' %}
7 |
8 | {{ form_errors(form) }}
9 | {{ form_start(form) }}
10 |
11 |
12 |
{{ 'user.new.enter_details'|trans }}
13 |
14 |
15 | {{ form_rest(form) }}
16 |
17 |
21 |
22 | {{ form_end(form) }}
23 | {% endblock %}
24 |
--------------------------------------------------------------------------------
/src/AdminBundle/Resources/views/layout.html.twig:
--------------------------------------------------------------------------------
1 | {% extends '::admin.html.twig' %}
2 |
3 | {% block body %}
4 |
15 |
16 |
17 | {% block content %}{% endblock %}
18 |
19 |
20 | {% endblock body %}
21 |
22 | {% block css %}
23 |
24 | {% endblock %}
25 |
--------------------------------------------------------------------------------
/src/AdminBundle/Twig/AuditExtension.php:
--------------------------------------------------------------------------------
1 | ['html'],
17 | 'needs_environment' => true,
18 | ];
19 |
20 | return [
21 | new \Twig_SimpleFunction('audit', [$this, 'audit'], $defaults),
22 | new \Twig_SimpleFunction('audit_value', [$this, 'value'], $defaults),
23 | new \Twig_SimpleFunction('audit_assoc', [$this, 'assoc'], $defaults),
24 | new \Twig_SimpleFunction('audit_blame', [$this, 'blame'], $defaults),
25 | ];
26 | }
27 |
28 | public function audit(\Twig_Environment $twig, AuditLog $log)
29 | {
30 | return $twig->render("AdminBundle:Audit:{$log->getAction()}.html.twig", compact('log'));
31 | }
32 |
33 | public function assoc(\Twig_Environment $twig, $assoc)
34 | {
35 | return $twig->render("AdminBundle:Audit:assoc.html.twig", compact('assoc'));
36 | }
37 |
38 | public function blame(\Twig_Environment $twig, $blame)
39 | {
40 | return $twig->render("AdminBundle:Audit:blame.html.twig", compact('blame'));
41 | }
42 |
43 | public function value(\Twig_Environment $twig, $val)
44 | {
45 | switch (true) {
46 | case is_bool($val):
47 | return $val ? 'true' : 'false';
48 | case is_array($val) && isset($val['fk']):
49 | return $this->assoc($twig, $val);
50 | case is_array($val):
51 | return json_encode($val);
52 | case is_string($val):
53 | return strlen($val) > 60 ? substr($val, 0, 60) . '...' : $val;
54 | case is_null($val):
55 | return 'NULL';
56 | default:
57 | return $val;
58 | }
59 | }
60 |
61 | public function getName()
62 | {
63 | return 'admin_audit_extension';
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/ApiBundle/ApiBundle.php:
--------------------------------------------------------------------------------
1 | getExtension('security');
15 | $extension->addSecurityListenerFactory(new JWTFactory());
16 | $extension->addSecurityListenerFactory(new JWTAuthFactory());
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/ApiBundle/Controller/CmsBlockController.php:
--------------------------------------------------------------------------------
1 | repo('AppBundle:CmsBlock')->createQueryBuilder('t');
30 | $paged = new Pagination($qb, $request);
31 |
32 | return new ListResource(iterator_to_array($paged, false));
33 | }
34 |
35 | /**
36 | * @Route("/{id}")
37 | * @Method("GET")
38 | */
39 | public function viewAction(Request $request, CmsBlock $block)
40 | {
41 | return new SingleResource($block);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/ApiBundle/Controller/VersionController.php:
--------------------------------------------------------------------------------
1 | getParameter('kernel.root_dir') . '/../package.json');
22 | if (!$pkg) {
23 | throw $this->createNotFoundException("package.json was not found in project root.");
24 | }
25 |
26 | return new VersionResource(json_decode(file_get_contents($pkg), true));
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/ApiBundle/DependencyInjection/ApiExtension.php:
--------------------------------------------------------------------------------
1 | load('listeners/kernel.yml');
19 | $loader->load('security.yml');
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/ApiBundle/DependencyInjection/Security/Factory/JWTAuthFactory.php:
--------------------------------------------------------------------------------
1 | setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.dao'))
21 | ->replaceArgument(0, new Reference($userProvider))
22 | ->replaceArgument(2, $id)
23 | ;
24 | $listenerId = 'security.authentication.listener.jwt_auth.'.$id;
25 | $container
26 | ->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.jwt_auth'))
27 | ->replaceArgument(1, $id)
28 | ->replaceArgument(2, $config)
29 | ;
30 | return [$providerId, $listenerId, $defaultEntryPoint];
31 | }
32 |
33 | /**
34 | * {@inheritdoc}
35 | */
36 | public function getPosition()
37 | {
38 | return 'pre_auth';
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | */
44 | public function getKey()
45 | {
46 | return 'jwt_auth';
47 | }
48 |
49 | /**
50 | * {@inheritdoc}
51 | */
52 | public function addConfiguration(NodeDefinition $node)
53 | {
54 | $node
55 | ->children()
56 | ->scalarNode('priv_key')->defaultValue('')->end()
57 | ->scalarNode('passphrase')->defaultValue('')->end()
58 | ->end();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/ApiBundle/DependencyInjection/Security/Factory/JWTFactory.php:
--------------------------------------------------------------------------------
1 | setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.jwt'))
21 | ->replaceArgument(0, new Reference($userProvider))
22 | ->replaceArgument(1, $config);
23 |
24 | $listenerId = 'security.authentication.listener.jwt.' . $id;
25 | $container
26 | ->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.jwt'));
27 |
28 | return [$providerId, $listenerId, $defaultEntryPoint];
29 | }
30 |
31 | /**
32 | * {@inheritdoc}
33 | */
34 | public function getPosition()
35 | {
36 | return 'pre_auth';
37 | }
38 |
39 | /**
40 | * {@inheritdoc}
41 | */
42 | public function getKey()
43 | {
44 | return 'jwt';
45 | }
46 |
47 | /**
48 | * {@inheritdoc}
49 | */
50 | public function addConfiguration(NodeDefinition $node)
51 | {
52 | $node
53 | ->children()
54 | ->scalarNode('pub_key')->defaultValue('')->end()
55 | ->end();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/ApiBundle/Resource/CmsBlock/ListResource.php:
--------------------------------------------------------------------------------
1 | blocks = $blocks;
15 | }
16 |
17 | public function jsonSerialize()
18 | {
19 | $blocks = array_map(function(CmsBlock $block) {
20 | return (new SingleResource($block))->jsonSerialize();
21 | }, $this->blocks);
22 | return compact('blocks');
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/ApiBundle/Resource/CmsBlock/SingleResource.php:
--------------------------------------------------------------------------------
1 | block = $block;
15 | }
16 |
17 | public function jsonSerialize()
18 | {
19 | return [
20 | 'id' => $this->block->getId(),
21 | 'alias' => $this->block->getAlias(),
22 | 'name' => $this->block->getName(),
23 | 'content' => $this->block->getContent(),
24 | 'createdAt' => $this->block->getCreatedAt()->getTimestamp(),
25 | 'updatedAt' => $this->block->getUpdatedAt()->getTimestamp(),
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/ApiBundle/Resource/VersionResource.php:
--------------------------------------------------------------------------------
1 | pkg = $pkg;
14 | }
15 |
16 | public function jsonSerialize()
17 | {
18 | return [
19 | 'name' => $this->pkg['name'],
20 | 'version' => $this->pkg['version'],
21 | 'description' => $this->pkg['description'],
22 | ];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/ApiBundle/ResourceInterface.php:
--------------------------------------------------------------------------------
1 | userProvider = $userProvider;
26 | $this->pubkey = 'file://' . realpath($config['pub_key']);
27 | }
28 |
29 | /**
30 | * {@inheritdoc}
31 | */
32 | public function authenticate(TokenInterface $token)
33 | {
34 | $parts = explode('.', $token->getCredentials());
35 | if (count($parts) != 3) {
36 | throw new AuthenticationException("Incorrect JWT token, does not match required number of parts");
37 | }
38 |
39 | // base64 decode jwt token parts
40 | list($header, $payload, $signature) = array_map('base64_decode', $parts);
41 |
42 | // signable parts
43 | $signup = implode('.', [$parts[0], $parts[1]]);
44 |
45 | // init openssl public key resource
46 | $key = $key = openssl_pkey_get_public($this->pubkey);
47 | if (!is_resource($key)) {
48 | throw new AuthenticationException("Not valid pub key: {$this->pubkey}");
49 | }
50 |
51 | // ensure key is valid RSA public key
52 | if (openssl_pkey_get_details($key)['type'] !== JWTUserToken::KEY_TYPE) {
53 | throw new AuthenticationException("Only RSA keys are supported.");
54 | }
55 |
56 | // verify signature
57 | if (!openssl_verify($signup, $signature, $key, JWTUserToken::ALGO)) {
58 | throw new AuthenticationException("Could not verify signature.");
59 | }
60 |
61 | // check expiration
62 | if (isset($payload['exp']) && is_numeric($payload['exp'])) {
63 | if ((new \DateTime('now'))->format('U') < $payload['exp']) {
64 | throw new AuthenticationException("Token has expired");
65 | }
66 | }
67 |
68 | // decode payload and header json
69 | list($payload, $header) = array_map(function($json) {
70 | return json_decode($json, true);
71 | }, [$payload, $header]);
72 |
73 | // validate header if necessary
74 | // ...
75 |
76 | // load user
77 | if (!$user = $this->userProvider->loadUserByUsername($payload['username'])) {
78 | throw new AuthenticationException("user does not exist");
79 | }
80 |
81 | $authToken = new JWTUserToken($user->getRoles());
82 | $authToken->setUser($user);
83 | $authToken->setRawToken($token->getCredentials());
84 | return $authToken;
85 | }
86 |
87 | /**
88 | * {@inheritdoc}
89 | */
90 | public function supports(TokenInterface $token)
91 | {
92 | return $token instanceof JWTUserToken;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/ApiBundle/Security/Authentication/Token/JWTUserToken.php:
--------------------------------------------------------------------------------
1 | setAuthenticated(count($roles) > 0);
24 | }
25 |
26 | /**
27 | * @param string $rawToken
28 | */
29 | public function setRawToken($rawToken)
30 | {
31 | $this->rawToken = $rawToken;
32 | }
33 |
34 | /**
35 | * {@inheritdoc}
36 | */
37 | public function getCredentials()
38 | {
39 | return $this->rawToken;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/ApiBundle/Security/Firewall/JWTAuthListener.php:
--------------------------------------------------------------------------------
1 | authenticationManager = $authenticationManager;
34 | $this->providerKey = $providerKey;
35 | $this->privkey = 'file://' . realpath($config['priv_key']);
36 | $this->passphrase = $config['passphrase'];
37 | }
38 |
39 | /**
40 | * {@inheritdoc}
41 | */
42 | public function handle(GetResponseEvent $event)
43 | {
44 | $request = $event->getRequest();
45 | if (!$request->isMethod('POST')) {
46 | throw new HttpException(405, "Only POST method is allowed for JWT authentication");
47 | }
48 |
49 | $username = $request->request->get('username', null);
50 | $password = $request->request->get('password', null);
51 |
52 | try {
53 | $token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
54 | } catch (\InvalidArgumentException $e) {
55 | // most probably failed to find user by these credentials
56 | // let other unexpected exceptions pass through
57 | throw new HttpException(JsonResponse::HTTP_UNAUTHORIZED, "Username or password is not valid.", $e);
58 | }
59 |
60 | $user = $token->getUser();
61 |
62 | $header = [];
63 |
64 | // jwt token data
65 | $payload = [
66 | 'username' => $user->getUsername(),
67 | 'exp' => (new \DateTime('+1 day'))->format('U'),
68 | 'iat' => (new \DateTime('now'))->format('U'),
69 | ];
70 |
71 | // build jwt data to sign
72 | $toSign = implode('.', array_map('base64_encode', array_map('json_encode', [$header, $payload])));
73 |
74 | // init openssl private key resource
75 | $key = openssl_pkey_get_private($this->privkey, $this->passphrase);
76 | if (!is_resource($key)) {
77 | throw new HttpException(500, "not valid private key, {$this->privkey}");
78 | }
79 |
80 | // ensure key is valid RSA private key
81 | if (openssl_pkey_get_details($key)['type'] !== JWTUserToken::KEY_TYPE) {
82 | throw new HttpException(500, "Only RSA keys are supported.");
83 | }
84 |
85 | // create signature
86 | $signature = null;
87 | if (!openssl_sign($toSign, $signature, $key, JWTUserToken::ALGO)) {
88 | throw new HttpException(500, "could not sign JWT.");
89 | }
90 |
91 | // create jwt token
92 | $jwt = implode('.', [$toSign, base64_encode($signature)]);
93 |
94 | // finally create response
95 | $event->setResponse(new JsonResponse([
96 | 'token' => $jwt,
97 | 'id' => $user->getId(),
98 | 'roles' => $user->getRoles(),
99 | ]));
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/ApiBundle/Security/Firewall/JWTListener.php:
--------------------------------------------------------------------------------
1 | tokenStorage = $tokenStorage;
33 | $this->authenticationManager = $authenticationManager;
34 | }
35 |
36 | /**
37 | * {@inheritdoc}
38 | */
39 | public function handle(GetResponseEvent $event)
40 | {
41 | $request = $event->getRequest();
42 | // note, if you want to allow token from query parameters or cookie, act accordingly
43 | if (!$request->headers->has('Authorization')) {
44 | throw new AuthenticationException("Authorization header is missing");
45 | }
46 |
47 | // extract parts from authorization header: prefix - jwt
48 | $parts = explode(' ', $request->headers->get('Authorization'));
49 | if (count($parts) !== 2) {
50 | throw new AuthenticationException("Authorization header is not valid");
51 | }
52 |
53 | // match authorization header prefix
54 | list($prefix, $jwt) = $parts;
55 | if (self::HEADER_PREFIX !== $prefix) {
56 | throw new AuthenticationException("Authorization header prefix is not valid");
57 | }
58 |
59 | $token = new JWTUserToken();
60 | $token->setRawToken($jwt);
61 |
62 | $authToken = $this->authenticationManager->authenticate($token);
63 | $this->tokenStorage->setToken($authToken);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/AppBundle/AppBundle.php:
--------------------------------------------------------------------------------
1 | getEnvironment()->getContext('AppBundle\Behat\PlaceholderContext');
16 | $this->get('em')->getEventManager()->addEventSubscriber(new PlaceholderListener($placeholders));
17 |
18 | $this->get('db')->beginTransaction();
19 | }
20 |
21 | /**
22 | * @AfterScenario
23 | */
24 | function rollback()
25 | {
26 | $this->get('db')->rollback();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/AppBundle/Behat/Doctrine/PlaceholderListener.php:
--------------------------------------------------------------------------------
1 | placeholders = $placeholders;
19 | }
20 |
21 | public function postPersist(LifecycleEventArgs $args)
22 | {
23 | $em = $args->getEntityManager();
24 | $entity = $args->getEntity();
25 | $meta = $em->getClassMetadata(get_class($entity));
26 |
27 | $id = current(array_values($meta->getIdentifierValues($entity)));
28 | $this->placeholders->set($this->label($entity), $id);
29 | }
30 |
31 | public function getSubscribedEvents()
32 | {
33 | return [Events::postPersist];
34 | }
35 |
36 | private function label($entity)
37 | {
38 | switch (true) {
39 | case $entity instanceof Entity\User:
40 | return $entity->getEmail();
41 | case $entity instanceof Entity\MailTemplate:
42 | return $entity->getSubject();
43 | case method_exists($entity, 'getName'):
44 | return $entity->getName();
45 | case method_exists($entity, 'getTitle'):
46 | return $entity->getTitle();
47 | case method_exists($entity, 'getLabel'):
48 | return $entity->getTitle();
49 | case method_exists($entity, '__toString'):
50 | return (string) $entity;
51 | default:
52 | return spl_object_hash($entity) . '-' . get_class($entity);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/AppBundle/Behat/MailerContext.php:
--------------------------------------------------------------------------------
1 | get('mailer')->getTransport()->getSpool()->messages = [];
13 | }
14 |
15 | /**
16 | * @Then /^I should receive an email to "([^"]*)"$/
17 | */
18 | function iShouldHaveReceivedAnEmailTo($email)
19 | {
20 | foreach ($this->get('mailer')->getTransport()->getSpool()->messages as $message) {
21 | foreach ($message->getTo() as $address => $name) {
22 | if ($address === $email) {
23 | return;
24 | }
25 | }
26 | }
27 | throw new \Exception("An email to '$email' was never sent");
28 | }
29 |
30 | /**
31 | * @When /^I follow the confirmation link in my email$/
32 | */
33 | function iFollowTheConfirmationLinkInMyEmail()
34 | {
35 | foreach ($this->get('mailer')->getTransport()->getSpool()->messages as $message) {
36 | if (preg_match('/href="([^"]+)/smi', $message->getBody(), $m)) {
37 | return $this->getSession()->visit($m[1]);
38 | }
39 | }
40 |
41 | throw new \Exception("A confirmation link was not found in any email");
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/AppBundle/Behat/PageContext.php:
--------------------------------------------------------------------------------
1 | visit("homepage");
16 | case "login":
17 | return $this->visit("app_user_login");
18 | case "signup":
19 | return $this->visit("app_user_signup");
20 | case "profile":
21 | return $this->visit("app_user_profile");
22 | case "admin":
23 | return $this->visit("admin_dashboard_index");
24 | default:
25 | throw new \InvalidArgumentException("Page: {$name} route is not defined yet.");
26 | }
27 | }
28 |
29 | /**
30 | * @Then /^I should see "([^"]+)" on page headline$/
31 | * @Then /^I should see "([^"]+)" in page headline$/
32 | */
33 | function iShouldSeeTextOnPageHeadline($text)
34 | {
35 | $this->notNull(
36 | $this->find('xpath', '//h1[contains(., "' . $text . '")] | //h2[contains(., "' . $text . '")] | //h3[contains(., "' . $text . '")]'),
37 | "Text '$text' was not found on page headline"
38 | );
39 | }
40 |
41 | /**
42 | * @Then /^the response code should be (\d+)$/
43 | */
44 | function theResponseCodeShouldBe($code)
45 | {
46 | $this->same(intval($code), $actual = $this->getSession()->getStatusCode(), "Invalid response code, expected $code, got $actual");
47 | }
48 |
49 | /**
50 | * @Then /^I should see (error|danger|success|info|notice) notification "([^"]+)"$/
51 | */
52 | function iShouldSeeNotification($type, $text)
53 | {
54 | switch ($type) {
55 | case 'error':
56 | $type = 'danger';
57 | break;
58 | case 'notice':
59 | $type = 'info';
60 | break;
61 | }
62 |
63 | $q = '//div[contains(@class, "alert") and contains(@class, "alert-' . $type . '") and contains(., "' . $text . '")]';
64 | $this->notNull($this->find('xpath', $q), "Notification of type '$type' with message '$text' was not found on page");
65 | }
66 |
67 | /**
68 | * @Then /^I should see a form field error "([^"]+)"$/
69 | */
70 | function iShouldSeeAFormFieldError($text)
71 | {
72 | $q = '//div[contains(@class, "has-error")]//span[contains(@class, "help-block") and contains(., "' . $text . '")]';
73 | $this->notNull($this->find('xpath', $q), "Form field error '$text' was not found on page");
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/AppBundle/Behat/PlaceholderContext.php:
--------------------------------------------------------------------------------
1 | placeholders = [];
19 | }
20 |
21 | /**
22 | * @AfterStep
23 | */
24 | function showRecentPlaceholders(AfterStepScope $scope)
25 | {
26 | if ($scope->getTestResult()->getResultCode() == TestResult::FAILED) {
27 | if (!empty($this->placeholders)) {
28 | echo "\nPlaceholders:";
29 | foreach ($this->placeholders as $name => $value) {
30 | echo sprintf("\n %s: %s", $name, $value);
31 | }
32 | echo "\n\n";
33 | }
34 | }
35 | }
36 |
37 | public function all()
38 | {
39 | return $this->placeholders;
40 | }
41 |
42 | public function get($name)
43 | {
44 | if (array_key_exists($name, $this->placeholders)) {
45 | throw new \Exception("The placeholder: \"{$name}\" was not set..");
46 | }
47 | return $this->placeholders[$name];
48 | }
49 |
50 | public function set($name, $value)
51 | {
52 | if (array_key_exists($name, $this->placeholders)) {
53 | throw new \Exception("The placeholder: \"{$name}\" was already set..");
54 | }
55 | $this->placeholders[$name] = (string)$value;
56 | }
57 |
58 | public function replace($text)
59 | {
60 | return preg_replace_callback("#%([^%]+)%#", function ($m) {
61 | return isset($this->placeholders[$m[1]]) ? $this->placeholders[$m[1]] : $m[0];
62 | }, $text);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/AppBundle/Behat/Swiftmailer/MemorySpool.php:
--------------------------------------------------------------------------------
1 | messages[] = clone $message;
44 | return true;
45 | }
46 |
47 | /**
48 | * Sends messages using the given transport instance.
49 | *
50 | * @param Swift_Transport $transport A transport instance
51 | * @param string[] $failedRecipients An array of failures by-reference
52 | *
53 | * @return int The number of sent emails
54 | */
55 | public function flushQueue(\Swift_Transport $transport, &$failedRecipients = null)
56 | {
57 | if (empty($this->messages)) {
58 | return 0;
59 | }
60 |
61 | if (!$transport->isStarted()) {
62 | $transport->start();
63 | }
64 |
65 | $count = 0;
66 | while ($message = array_pop($this->messages)) {
67 | $count += $transport->send($message, $failedRecipients);
68 | }
69 |
70 | return $count;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/AppBundle/Behat/UserContext.php:
--------------------------------------------------------------------------------
1 | get('security.context')->setToken(null);
15 | }
16 |
17 | /**
18 | * @Given /^(confirmed|unconfirmed) (user|admin) named "([^"]+)"$/
19 | */
20 | function userNamed($status, $type, $name)
21 | {
22 | $names = explode(' ', $name);
23 | list ($firstname, $lastname) = $names;
24 |
25 | $em = $this->get('em');
26 | $user = new User();
27 | if ('confirmed' === $status) {
28 | $user->setFirstname($firstname);
29 | $user->setLastname($lastname);
30 | }
31 | $user->setEmail(strtolower(implode('.', $names)) . '@datadog.lt');
32 | $user->setRoles($type == 'user' ? ['ROLE_USER'] : ['ROLE_ADMIN']);
33 |
34 | if ('unconfirmed' === $status) {
35 | $user->setConfirmationToken(implode('-', array_map('strtolower', $names)) . '-token');
36 | } else {
37 | $encoder = $this->get('security.encoder_factory')->getEncoder($user);
38 | $user->setPassword($encoder->encodePassword('S3cretpassword', $user->getSalt()));
39 | }
40 | $em->persist($user);
41 | $em->flush();
42 | return $user;
43 | }
44 |
45 | /**
46 | * @Given /^I have signed up as "([^"]*)"$/
47 | */
48 | function iHaveSignedUpAs($email)
49 | {
50 | $this->visit('app_user_signup');
51 | $this->mink->fillField('Email', $email);
52 | $this->mink->pressButton('Signup');
53 | }
54 |
55 | /**
56 | * @When /^I fill in my personal details$/
57 | */
58 | function iFillInMyPersonalDetails()
59 | {
60 | // look in placeholders
61 | $user = null;
62 | foreach ($this->placeholders->all() as $key => $id) {
63 | if (strpos($key, '@') !== false) {
64 | $user = $this->repo('AppBundle:User')->findOneById($id);
65 | }
66 | }
67 | $this->true($user instanceof User, "User must be on confirm page");
68 | $name = substr($user->getEmail(), 0, strpos($user->getEmail(), '@'));
69 | list($first, $last) = array_map('ucfirst', explode('.', $name));
70 |
71 | $this->mink->fillField('First name', $first);
72 | $this->mink->fillField('Last name', $last);
73 | $this->mink->fillField('Password', 'S3cretpassword');
74 | $this->mink->fillField('Repeat password', 'S3cretpassword');
75 | $this->mink->pressButton('Confirm');
76 | }
77 |
78 | /**
79 | * @Given /^I'm logged in as "([^"]*)"$/
80 | * @When /^I login as "([^"]*)" using password "([^"]*)"$/
81 | * @When /^I try to login as "([^"]*)" using password "([^"]*)"$/
82 | */
83 | function iTryToLoginAsUsingPassword($email, $password = 'S3cretpassword')
84 | {
85 | $this->visit('app_user_login');
86 | $this->mink->fillField("Username", $email);
87 | $this->mink->fillField("Password", $password);
88 | $this->mink->pressButton("Login");
89 | }
90 |
91 | /**
92 | * @When /^I attempt to signup as "([^"]*)"$/
93 | * @When /^I signup as "([^"]*)"$/
94 | */
95 | function iSignupAs($email)
96 | {
97 | $this->mink->fillField('Email', $email);
98 | $this->mink->pressButton('Signup');
99 | }
100 |
101 | /**
102 | * @Then /^I should be logged in$/
103 | */
104 | function iShouldBeLoggedIn()
105 | {
106 | $this->true($this->get('security.context')->getToken()->getUser() instanceof User);
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/AppBundle/Cache/RedisCache.php:
--------------------------------------------------------------------------------
1 | redis = $redis;
23 | $this->setNamespace($this->ns = $ns);
24 | }
25 |
26 | /**
27 | * {@inheritdoc}
28 | */
29 | protected function doFetch($id)
30 | {
31 | $result = $this->redis->get($id);
32 |
33 | return null === $result ? false : unserialize($result);
34 | }
35 |
36 | /**
37 | * {@inheritdoc}
38 | */
39 | protected function doContains($id)
40 | {
41 | return (bool) $this->redis->exists($id);
42 | }
43 |
44 | /**
45 | * {@inheritdoc}
46 | */
47 | protected function doSave($id, $data, $lifeTime = false)
48 | {
49 | if (0 < $lifeTime) {
50 | $result = $this->redis->setex($id, (int) $lifeTime, serialize($data));
51 | } else {
52 | $result = $this->redis->set($id, serialize($data));
53 | }
54 |
55 | return (bool) $result;
56 | }
57 |
58 | /**
59 | * {@inheritdoc}
60 | */
61 | protected function doDelete($id)
62 | {
63 | return (bool) $this->redis->del($id);
64 | }
65 |
66 | /**
67 | * {@inheritdoc}
68 | */
69 | protected function doFlush()
70 | {
71 | return (bool) $this->redis->flushDB();
72 | }
73 |
74 | public function flushDB()
75 | {
76 | return (bool) $this->redis->flushDB();
77 | }
78 |
79 | public function flushNamespacedKeys()
80 | {
81 | $params = $this->redis->getConnection()->getParameters();
82 | // redis cli connection string
83 | $conn = sprintf("redis-cli -h %s -p %s -n %s --raw", $params->host, $params->port, $params->database);
84 | // remove namespaced keys only
85 | $proc = new Process(sprintf("%s KEYS '*%s*' | xargs --delim='\\n' %s DEL", $conn, $this->ns, $conn));
86 | $proc->run();
87 | return $proc->getOutput();
88 | }
89 |
90 | /**
91 | * {@inheritdoc}
92 | */
93 | protected function doGetStats()
94 | {
95 | $stats = $this->redis->info();
96 |
97 | return array(
98 | Cache::STATS_HITS => isset($stats['keyspace_hits']) ? $stats['keyspace_hits'] : $stats['Stats']['keyspace_hits'],
99 | Cache::STATS_MISSES => isset($stats['keyspace_misses']) ? $stats['keyspace_misses'] : $stats['Stats']['keyspace_misses'],
100 | Cache::STATS_UPTIME => isset($stats['uptime_in_seconds']) ? $stats['uptime_in_seconds'] : $stats['Server']['uptime_in_seconds'],
101 | Cache::STATS_MEMORY_USAGE => isset($stats['used_memory']) ? $stats['used_memory'] : $stats['Memory']['used_memory'],
102 | Cache::STATS_MEMORY_AVAILIABLE => null,
103 | );
104 | }
105 |
106 | /**
107 | * {@inheritdoc}
108 | */
109 | public function has($class)
110 | {
111 | return $this->contains($class);
112 | }
113 |
114 | /**
115 | * {@inheritdoc}
116 | */
117 | public function read($class)
118 | {
119 | return $this->fetch($class);
120 | }
121 |
122 | /**
123 | * {@inheritdoc}
124 | */
125 | public function write(ClassMetadata $metadata)
126 | {
127 | $this->save($metadata->getClassName(), $metadata);
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/AppBundle/Command/CacheClearCommand.php:
--------------------------------------------------------------------------------
1 | setName('app:cache:clear')
16 | ->setDescription('Clears application cache.')
17 | ->setHelp(<<%command.name% clears application cache.
19 |
20 | php %command.full_name%
21 | php %command.full_name% --env=prod
22 |
23 | EOT
24 | );
25 | }
26 |
27 | protected function execute(InputInterface $input, OutputInterface $output)
28 | {
29 | $env = $this->getContainer()->getParameter('kernel.environment');
30 | $output->writeLn("Clearing app cache for environment {$env}...");
31 |
32 | $cache = $this->getContainer()->get('cache.default');
33 | if ($cache instanceof RedisCache) {
34 | if ($env === 'test') {
35 | $cache->flushDB();
36 | $output->writeln('Flushed all redis cache');
37 | } else {
38 | // flush only namespaced keys for redis cache
39 | $output->writeLn(sprintf(
40 | "Flushed %s cache entries",
41 | trim($cache->flushNamespacedKeys())
42 | ));
43 | }
44 | } else {
45 | // do not mind and remove all cache (array cache)
46 | $cache->flushAll();
47 | }
48 |
49 | $output->writeLn("Cache was cleared.");
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/AppBundle/Command/FixturesCommand.php:
--------------------------------------------------------------------------------
1 | setName('app:fixtures')
21 | ->setDescription('Load data fixtures to your database.')
22 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'The entity manager to use for this command.')
23 | ->setHelp(<<%command.name% command loads data fixtures from your bundles:
25 | It loads only ones which were not appended already.
26 |
27 | php %command.full_name%
28 | php %command.full_name% --env=prod
29 |
30 | EOT
31 | );
32 | }
33 |
34 | protected function execute(InputInterface $input, OutputInterface $output)
35 | {
36 | $doctrine = $this->getContainer()->get('doctrine');
37 | $em = $doctrine->getManager($input->getOption('em'));
38 |
39 | $alreadyLoaded = $em->getRepository("AppBundle:Internal\Fixture")->findAll();
40 | // may support more managers
41 | $loaded = $this->loadFixtures($output, $em, $alreadyLoaded);
42 |
43 | foreach ($loaded as $fixture) {
44 | $added = new Fixture();
45 | $added->setName(get_class($fixture));
46 | $em->persist($added);
47 | }
48 | $em->flush();
49 | }
50 |
51 | private function loadFixtures(OutputInterface $output, EntityManager $em, array $loaded)
52 | {
53 | $logger = function($message) use ($output) {
54 | $output->writeln(sprintf(' > %s', $message));
55 | };
56 | $executor = new ORMExecutor($em);
57 |
58 | $paths = [];
59 | foreach ($this->getApplication()->getKernel()->getBundles() as $bundle) {
60 | $paths[] = $bundle->getPath() . '/Fixture';
61 | }
62 |
63 | $loader = new DataFixturesLoader($this->getContainer());
64 | foreach ($paths as $path) {
65 | if (is_dir($path)) {
66 | $loader->loadFromDirectory($path);
67 | }
68 | }
69 | $env = $this->getContainer()->getParameter('kernel.environment');
70 | $output->writeln("Loading fixtures...");
71 |
72 | $has = array_map(function(Fixture $fixture) {
73 | return $fixture->getName();
74 | }, $loaded);
75 |
76 | $fixtures = array_filter($loader->getFixtures(), function(FixtureInterface $fixture) use($has) {
77 | return !in_array(get_class($fixture), $has);
78 | });
79 |
80 | if (!$fixtures) {
81 | $output->writeln(" Could not find any new fixtures to load..");
82 | return [];
83 | }
84 |
85 | $executor->setLogger($logger);
86 | $executor->execute($fixtures, true);
87 | return $fixtures;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/AppBundle/Composer.php:
--------------------------------------------------------------------------------
1 | getIO()->write(sprintf('The "bin" directory was not found in %s.', getcwd()));
19 | return;
20 | }
21 | foreach (glob('app/Resources/bin/*') as $binary) {
22 | $src = '../app/Resources/bin/' . basename($binary);
23 | $dst = $dst = 'bin/' . basename($binary);
24 | @unlink($dst);
25 | if (@symlink($src, $dst) === false) {
26 | if (!file_exists($dst)) {
27 | $event->getIO()->write(sprintf('Failed to symlink %s from %s.', $src, $dst));
28 | return;
29 | }
30 | continue;
31 | }
32 | $event->getIO()->write(sprintf('Installed binary %s.', $dst));
33 | }
34 | }
35 |
36 | private static function spacingParametersYml(CommandEvent $event)
37 | {
38 | if (!file_exists($file = 'vendor/incenteev/composer-parameter-handler/Processor.php')) {
39 | return;
40 | }
41 |
42 | $content = file_get_contents($file);
43 | $matches = 0;
44 | $content = str_replace('Yaml::dump($actualValues, 99)', 'Yaml::dump($actualValues, 99, 2)', $content, $matches);
45 | if ($matches) {
46 | file_put_contents($file, $content, LOCK_EX);
47 | $event->getIO()->write('Updated spacing for incenteev parameters');
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/AppBundle/Controller/DoctrineController.php:
--------------------------------------------------------------------------------
1 | getDoctrine()->getManager()->persist($entity);
11 | }
12 | }
13 |
14 | protected function remove(...$entities)
15 | {
16 | foreach ($entities as $entity) {
17 | $this->getDoctrine()->getManager()->remove($entity);
18 | }
19 | }
20 |
21 | protected function flush($class = null)
22 | {
23 | $this->getDoctrine()->getManager()->flush($class);
24 | }
25 |
26 | /**
27 | * @param string $class
28 | */
29 | protected function repo($class)
30 | {
31 | return $this->getDoctrine()->getManager()->getRepository($class);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/AppBundle/Controller/HomeController.php:
--------------------------------------------------------------------------------
1 | load('security.yml');
19 | $loader->load('twig.yml');
20 | $loader->load('mailer.yml');
21 | $loader->load('menu.yml');
22 | $loader->load('listeners/doctrine.yml');
23 | $loader->load('listeners/kernel.yml');
24 |
25 | $loader->load(sprintf('cache/%s.yml', $container->getParameter('kernel.environment')));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/AppBundle/Entity/Internal/Fixture.php:
--------------------------------------------------------------------------------
1 | name = $name;
22 | return $this;
23 | }
24 |
25 | public function getName()
26 | {
27 | return $this->name;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/AppBundle/Entity/MailTemplate.php:
--------------------------------------------------------------------------------
1 | id;
62 | }
63 |
64 | /**
65 | * Set alias
66 | *
67 | * @param string $alias
68 | *
69 | * @return MailTemplate
70 | */
71 | public function setAlias($alias)
72 | {
73 | $this->alias = $alias;
74 |
75 | return $this;
76 | }
77 |
78 | /**
79 | * Get alias
80 | *
81 | * @return string
82 | */
83 | public function getAlias()
84 | {
85 | return $this->alias;
86 | }
87 |
88 | /**
89 | * @return \DateTime
90 | */
91 | public function getCreatedAt()
92 | {
93 | return $this->createdAt;
94 | }
95 |
96 | /**
97 | * @param \DateTime $createdAt
98 | */
99 | public function setCreatedAt($createdAt)
100 | {
101 | $this->createdAt = $createdAt;
102 | }
103 |
104 | /**
105 | * @return \DateTime
106 | */
107 | public function getUpdatedAt()
108 | {
109 | return $this->updatedAt;
110 | }
111 |
112 | /**
113 | * @param \DateTime $updatedAt
114 | */
115 | public function setUpdatedAt($updatedAt)
116 | {
117 | $this->updatedAt = $updatedAt;
118 | }
119 |
120 | /**
121 | * Set subject
122 | *
123 | * @param string $subject
124 | *
125 | * @return MailTemplate
126 | */
127 | public function setSubject($subject)
128 | {
129 | $this->subject = $subject;
130 |
131 | return $this;
132 | }
133 |
134 | /**
135 | * Get subject
136 | *
137 | * @return string
138 | */
139 | public function getSubject()
140 | {
141 | return $this->subject;
142 | }
143 |
144 | /**
145 | * Set content
146 | *
147 | * @param string $content
148 | *
149 | * @return MailTemplate
150 | */
151 | public function setContent($content)
152 | {
153 | $this->content = $content;
154 |
155 | return $this;
156 | }
157 |
158 | /**
159 | * Get content
160 | *
161 | * @return string
162 | */
163 | public function getContent()
164 | {
165 | return $this->content;
166 | }
167 | }
168 |
169 |
--------------------------------------------------------------------------------
/src/AppBundle/EventListener/DoctrineExtensionsListener.php:
--------------------------------------------------------------------------------
1 | translatable = $translatable;
15 | }
16 |
17 | public function onLateKernelRequest(GetResponseEvent $event)
18 | {
19 | $this->translatable->setTranslatableLocale($event->getRequest()->getLocale());
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/AppBundle/EventListener/FlushListener.php:
--------------------------------------------------------------------------------
1 | em = $em;
17 | $this->sub = $sub;
18 | }
19 |
20 | public function onEarlyKernelRequest(GetResponseEvent $event)
21 | {
22 | if (!$event->isMasterRequest()) {
23 | return;
24 | }
25 | // reset flushed state on each request, since kernel may not be rebooted
26 | $this->sub->flushed = false;
27 | $this->sub->inRequest = true;
28 | }
29 |
30 | public function onLateKernelResponse(FilterResponseEvent $event)
31 | {
32 | if (!$event->isMasterRequest()) {
33 | return;
34 | }
35 |
36 | $this->sub->inRequest = false;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/AppBundle/EventListener/FlushSubscriber.php:
--------------------------------------------------------------------------------
1 | inRequest) {
17 | // let console commands handle flushes anyway they want
18 | return;
19 | }
20 |
21 | $em = $args->getEntityManager();
22 | if ($em->getConnection()->isTransactionActive()) {
23 | // the transaction is managed manually and was already started
24 | // probably it won't be handled since it is the end of response
25 | // but anyways, it won't cause trouble
26 | return;
27 | }
28 |
29 | if ($this->flushed) {
30 | throw new \BadMethodCallException("The flush can be run only once and is run automatically in the end of each request to prevent data inconsistencies and bad design.");
31 | }
32 |
33 | $this->flushed = true;
34 | }
35 |
36 | public function getSubscribedEvents()
37 | {
38 | return [Events::preFlush];
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/AppBundle/Fixture/Cms/LayoutBlocks.php:
--------------------------------------------------------------------------------
1 | container = $container;
22 | }
23 |
24 | /**
25 | * {@inheritDoc}
26 | */
27 | public function getOrder()
28 | {
29 | return 0;
30 | }
31 |
32 | private function resource($name)
33 | {
34 | $location = $this->container->get('kernel')->locateResource("@AppBundle/Resources/views/blocks/layout/{$name}.html.twig");
35 | return file_get_contents($location);
36 | }
37 |
38 | public function load(ObjectManager $manager)
39 | {
40 | $blocks = [
41 | 'footer',
42 | ];
43 |
44 | foreach ($blocks as $alias) {
45 | $block = new CmsBlock();
46 | $block->setAlias($alias);
47 | $block->setName(implode(' ', array_map('ucfirst', explode('_', $alias))));
48 | $block->setContent($this->resource($alias));
49 | $manager->persist($block);
50 | }
51 |
52 | foreach (['css', 'js'] as $alias) {
53 | $block = new CmsBlock();
54 | $block->setAlias($alias);
55 | $block->setName(implode(' ', array_map('ucfirst', explode('_', $alias))));
56 | $block->setContent("/* cms block for {$alias} */");
57 | $manager->persist($block);
58 | }
59 |
60 | $manager->flush();
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/AppBundle/Fixture/Mail/RegistrationConfirm.php:
--------------------------------------------------------------------------------
1 | [
24 | 'subject'=>'Activate Email',
25 | 'content'=>'click here {{ link }}',
26 | ],
27 | ];
28 |
29 | foreach ($emails as $alias => $emailData) {
30 | $email = new MailTemplate();
31 | $email->setAlias($alias);
32 | $email->setSubject($emailData['subject']);
33 | $email->setContent($emailData['content']);
34 | $manager->persist($email);
35 | }
36 |
37 | $manager->flush();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/AppBundle/Fixture/Users/DevUsers.php:
--------------------------------------------------------------------------------
1 | container = $container;
23 | }
24 |
25 | /**
26 | * {@inheritDoc}
27 | */
28 | public function getOrder()
29 | {
30 | return 10; // may need some groups or over related stuff created before
31 | }
32 |
33 | /**
34 | * @param ObjectManager $em
35 | */
36 | public function load(ObjectManager $em)
37 | {
38 | if (!in_array($this->container->getParameter('kernel.environment'), ['dev'])) {
39 | return; // only for dev environment
40 | }
41 |
42 | $faker = Factory::create();
43 | $users = [
44 | 'yoda' => ['ROLE_ADMIN'],
45 | 'luke' => ['ROLE_USER'],
46 | ];
47 | foreach ($users as $username => $roles) {
48 | $user = new User();
49 | $user->setFirstname($faker->firstname);
50 | $user->setLastname($faker->lastname);
51 | $user->setEmail($username . '@datadog.lt');
52 | $user->setRoles($roles);
53 |
54 | $encoder = $this->container->get('security.encoder_factory')->getEncoder($user);
55 | $user->setPassword($encoder->encodePassword('S3cretpassword', $user->getSalt()));
56 |
57 | $em->persist($user);
58 | }
59 | $em->flush();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/AppBundle/Form/Type/User/ConfirmType.php:
--------------------------------------------------------------------------------
1 | add('firstname', 'text', [
16 | 'label' => 'user.label.firstname',
17 | 'required' => true,
18 | ])
19 | ->add('lastname', 'text', [
20 | 'label' => 'user.label.lastname',
21 | 'required' => true,
22 | ])
23 | ->add('plainPassword', 'repeated', [
24 | 'type' => 'password',
25 | 'invalid_message' => 'Passwords does not match',
26 | 'required' => true,
27 | 'first_options' => ['label' => 'user.label.password'],
28 | 'second_options' => ['label' => 'user.label.repeat_password'],
29 | ]);
30 | }
31 |
32 | /**
33 | * {@inheritdoc}
34 | */
35 | public function configureOptions(OptionsResolver $resolver)
36 | {
37 | $resolver->setDefaults([
38 | 'data_class' => 'AppBundle\Entity\User',
39 | 'validation_groups' => 'confirm',
40 | 'intention' => 'confirm',
41 | ]);
42 | }
43 |
44 | public function getName()
45 | {
46 | return 'confirm';
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/AppBundle/Form/Type/User/ProfileType.php:
--------------------------------------------------------------------------------
1 | add('firstname', 'text', [
16 | 'label' => 'user.label.firstname',
17 | 'required' => true,
18 | ])
19 | ->add('lastname', 'text', [
20 | 'label' => 'user.label.lastname',
21 | 'required' => true,
22 | ])
23 | ->add('plainPassword', 'repeated', [
24 | 'type' => 'password',
25 | 'invalid_message' => 'user.label.password_mismatch',
26 | 'required' => true,
27 | 'first_options' => ['label' => 'user.label.password'],
28 | 'second_options' => ['label' => 'user.label.repeat_password'],
29 | ]);
30 | }
31 |
32 | /**
33 | * {@inheritdoc}
34 | */
35 | public function configureOptions(OptionsResolver $resolver)
36 | {
37 | $resolver->setDefaults([
38 | 'data_class' => 'AppBundle\Entity\User',
39 | 'validation_groups' => 'profile',
40 | 'intention' => 'profile',
41 | ]);
42 | }
43 |
44 | public function getName()
45 | {
46 | return 'profile';
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/AppBundle/Form/Type/User/ResetType.php:
--------------------------------------------------------------------------------
1 | add('email', 'email', [
19 | 'label' => 'user.label.email',
20 | 'required' => true,
21 | 'constraints' => [
22 | new NotBlank(['message' => 'Email address cannot be empty']),
23 | new Email(['message' => 'Email address is not valid']),
24 | ],
25 | ])
26 | ->add('captcha', 'ewz_recaptcha', [
27 | 'label' => 'user.reset.verification',
28 | 'constraints' => [
29 | new RecaptchaTrue(['message'=>'Invalid verification code'])
30 | ],
31 | ])
32 | ;
33 |
34 | }
35 |
36 | /**
37 | * {@inheritdoc}
38 | */
39 | public function configureOptions(OptionsResolver $resolver)
40 | {
41 | $resolver->setDefaults([
42 | 'data_class' => null,
43 | 'intention' => 'reset_password',
44 | ]);
45 | }
46 |
47 | public function getName()
48 | {
49 | return 'reset';
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/AppBundle/Form/Type/User/SignupType.php:
--------------------------------------------------------------------------------
1 | add('email', 'email', [
15 | 'label' => 'user.label.email',
16 | 'required' => true,
17 | ]);
18 | }
19 |
20 | /**
21 | * {@inheritdoc}
22 | */
23 | public function configureOptions(OptionsResolver $resolver)
24 | {
25 | $resolver->setDefaults([
26 | 'data_class' => 'AppBundle\Entity\User',
27 | 'validation_groups' => 'signup',
28 | 'intention' => 'signup',
29 | ]);
30 | }
31 |
32 | public function getName()
33 | {
34 | return 'signup';
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/AppBundle/Mailer/ContactInterface.php:
--------------------------------------------------------------------------------
1 | mailer = $mailer;
43 | $this->twig = $twig;
44 | $this->em = $em;
45 | $this->sender = $sender;
46 | }
47 |
48 | /**
49 | * @param ContactInterface $contact
50 | * @param string $alias
51 | * @param array $data
52 | */
53 | public function user(ContactInterface $contact, $alias, $data = [])
54 | {
55 | /** @var MailTemplate $template */
56 | $template = $this->em->getRepository('AppBundle:MailTemplate')->findOneBy(['alias'=>$alias]);
57 |
58 | if (!$template) {
59 | throw new \InvalidArgumentException(sprintf("Template %s does not exist", $alias));
60 | }
61 |
62 | $this->send([$contact->getEmail() => $contact->getFullName()], $template, ['user' => $contact] + $data);
63 | }
64 |
65 | /**
66 | * @param MailTemplate $template
67 | * @param array $data
68 | * @return string
69 | */
70 | protected function render(MailTemplate $template, array $data)
71 | {
72 | return $this->twig->render("AppBundle:Mail:template.html.twig", compact('template') + $data);
73 | }
74 |
75 | /**
76 | * @param string|array $to
77 | * @param MailTemplate $template
78 | * @param array $data
79 | */
80 | private function send($to, MailTemplate $template, array $data = [])
81 | {
82 | $body = $this->render($template, $data);
83 |
84 | $message = new \Swift_Message($template->getSubject(), $body, "text/html", "utf-8");
85 | $message->setFrom($this->sender);
86 | $message->setTo($to);
87 |
88 | $this->mailer->send($message);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/AppBundle/Menu/MenuBuilder.php:
--------------------------------------------------------------------------------
1 | createItem('root');
19 | $menu->setChildrenAttribute('class', 'nav navbar-nav pull-right');
20 |
21 | // about
22 | $menu->addChild($this->trans('about'), ['route' => 'app_home_about', 'attributes' => [
23 | 'role' => 'presentation',
24 | 'icon' => 'fa fa-book',
25 | ]]);
26 | $user = $this->getUser();
27 |
28 | if ($user instanceof UserInterface) {
29 | // dropdown
30 | $dropdown = $menu->addChild($user, ['attributes' => [
31 | 'role' => 'presentation',
32 | 'dropdown' => true,
33 | 'icon' => 'fa fa-user',
34 | ]]);
35 | // profile
36 | $dropdown->addChild($this->trans('profile'), ['route' => 'app_user_profile', 'attributes' => [
37 | 'role' => 'presentation',
38 | 'icon' => 'fa fa-child',
39 | ]]);
40 | // administration
41 | if ($user->hasRole('ROLE_ADMIN')) {
42 | $dropdown->addChild($this->trans('admin'), ['route' => 'admin', 'attributes' => [
43 | 'role' => 'presentation',
44 | 'icon' => 'fa fa-beer',
45 | ]]);
46 | }
47 | // logout
48 | $dropdown->addChild($this->trans('logout'), ['route' => 'app_user_logout', 'attributes' => [
49 | 'role' => 'presentation',
50 | 'icon' => 'fa fa-sign-out',
51 | ]]);
52 | }
53 |
54 | if (!$user instanceof UserInterface) {
55 | // signin
56 | $menu->addChild($this->trans('login'), ['route' => 'app_user_login', 'attributes' => [
57 | 'role' => 'presentation',
58 | 'icon' => 'fa fa-sign-in',
59 | ]]);
60 | // signup
61 | $menu->addChild($this->trans('sign_up'), ['route' => 'app_user_signup', 'attributes' => [
62 | 'role' => 'presentation',
63 | 'icon' => 'fa fa-user-plus',
64 | ]]);
65 | }
66 |
67 | return $menu;
68 | }
69 |
70 | /**
71 | * @return UserInterface
72 | */
73 | private function getUser()
74 | {
75 | if (!$this->container->has('security.token_storage')) {
76 | throw new \LogicException('The SecurityBundle is not registered in your application.');
77 | }
78 |
79 | $token = $this->container->get('security.token_storage')->getToken();
80 | if (!$token instanceof TokenInterface) {
81 | return null;
82 | }
83 |
84 | return $token->getUser();
85 | }
86 |
87 | /**
88 | * @param string $label
89 | * @return string
90 | */
91 | private function trans($label)
92 | {
93 | return $this->container->get('translator')->trans($label, [], 'menu');
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/AppBundle/Menu/RequestVoter.php:
--------------------------------------------------------------------------------
1 | isMasterRequest()) {
16 | return;
17 | }
18 | $this->request = $event->getRequest();
19 | }
20 |
21 | public function matchItem(ItemInterface $item)
22 | {
23 | if (null === $this->request) {
24 | return null;
25 | }
26 |
27 | if ($item->getUri() === $this->request->getRequestUri()) {
28 | // URL's completely match
29 | return true;
30 | }
31 |
32 | if ($item->getUri() !== $this->request->getBaseUrl() . '/' && (substr($this->request->getRequestUri(), 0, strlen($item->getUri())) === $item->getUri())) {
33 | // URL isn't just "/" and the first part of the URL match
34 | return true;
35 | }
36 |
37 | return null;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/config/cache/dev.yml:
--------------------------------------------------------------------------------
1 | services:
2 | cache.default: @doctrine.orm.default_query_cache # array cache for dev
3 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/config/cache/prod.yml:
--------------------------------------------------------------------------------
1 | parameters:
2 | redis_timeout: 5
3 | redis_db_default: 0
4 | redis_db_session: 1
5 |
6 | services:
7 | cache.connection.default:
8 | class: Predis\Client
9 | arguments:
10 | - { host: %redis_host%, port: %redis_port%, database: %redis_db_default% }
11 |
12 | cache.connection.session:
13 | class: Predis\Client
14 | arguments:
15 | - { host: %redis_host%, port: %redis_port%, database: %redis_db_session% }
16 |
17 | cache.default:
18 | class: AppBundle\Cache\RedisCache
19 | arguments:
20 | - @cache.connection.default
21 | - %cache_namespace%
22 |
23 | session.handler.redis:
24 | class: AppBundle\Cache\Session\RedisSessionHandler
25 | arguments:
26 | - @cache.connection.session
27 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/config/cache/test.yml:
--------------------------------------------------------------------------------
1 | imports:
2 | - { resource: prod.yml }
3 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/config/listeners/doctrine.yml:
--------------------------------------------------------------------------------
1 | services:
2 |
3 | doctrine.flush.listener:
4 | class: AppBundle\EventListener\FlushSubscriber
5 | tags:
6 | - { name: doctrine.event_subscriber, connection: default }
7 |
8 | doctrine.timestampable.listener:
9 | class: Gedmo\Timestampable\TimestampableListener
10 | tags:
11 | - { name: doctrine.event_subscriber, connection: default }
12 | calls:
13 | - [ setAnnotationReader, [ @annotation_reader ] ]
14 |
15 | # sluggable:
16 | # class: Gedmo\Sluggable\SluggableListener
17 | # tags:
18 | # - { name: doctrine.event_subscriber, connection: default }
19 | # calls:
20 | # - [ setAnnotationReader, [ @annotation_reader ] ]
21 |
22 | # doctrine_extensions.listener:
23 | # class: AppBundle\EventListener\DoctrineExtensionsListener
24 | # arguments: [ @translatable ]
25 | # tags:
26 | # # translatable sets locale after router processing
27 | # - { name: kernel.event_listener, event: kernel.request, method: onLateKernelRequest, priority: -10 }
28 |
29 | # translatable:
30 | # class: Gedmo\Translatable\TranslatableListener
31 | # tags:
32 | # - { name: doctrine.event_subscriber, connection: default }
33 | # calls:
34 | # - [ setAnnotationReader, [ @annotation_reader ] ]
35 | # - [ setDefaultLocale, [ %locale% ] ]
36 | # - [ setTranslatableLocale, [ %locale% ] ]
37 | # - [ setTranslationFallback, [ true ] ]
38 | # - [ setPersistDefaultLocaleTranslation, [false] ]
39 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/config/listeners/kernel.yml:
--------------------------------------------------------------------------------
1 | services:
2 |
3 | kernel.flush.listener:
4 | class: AppBundle\EventListener\FlushListener
5 | arguments: [@doctrine.orm.entity_manager, @doctrine.flush.listener]
6 | tags:
7 | - { name: kernel.event_listener, event: kernel.response, method: onLateKernelResponse, priority: -255 }
8 | - { name: kernel.event_listener, event: kernel.request, method: onEarlyKernelRequest, priority: 255 }
9 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/config/mailer.yml:
--------------------------------------------------------------------------------
1 | services:
2 | mail:
3 | class: AppBundle\Mailer\Mailer
4 | arguments:
5 | - @mailer
6 | - @templating
7 | - @em
8 | - %mailer_sender%
9 |
10 | twig.extension.string_loader:
11 | class: Twig_Extension_StringLoader
12 | tags:
13 | - { name: twig.extension }
14 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/config/menu.yml:
--------------------------------------------------------------------------------
1 | services:
2 | bootstrap.menu.voter:
3 | class: AppBundle\Menu\RequestVoter
4 | tags:
5 | - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
6 | - { name: knp_menu.voter }
7 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/config/routing.yml:
--------------------------------------------------------------------------------
1 | app:
2 | resource: @AppBundle/Controller/
3 | type: annotation
4 |
5 | app_user_check:
6 | path: /login_check
7 |
8 | app_user_logout:
9 | path: /logout
10 |
11 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/config/security.yml:
--------------------------------------------------------------------------------
1 | services:
2 | app.password_encoder:
3 | class: AppBundle\Security\Core\Encoder\BCryptPasswordEncoder
4 | arguments: [12] # cost
5 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/config/twig.yml:
--------------------------------------------------------------------------------
1 | services:
2 | twig.extension.cms_block:
3 | class: AppBundle\Twig\CMSBlockExtension
4 | arguments: [@doctrine.orm.entity_manager]
5 | tags:
6 | - { name: twig.extension }
7 |
8 | twig.extension.time_ago:
9 | class: AppBundle\Twig\TimeExtension
10 | arguments: [@translator]
11 | tags:
12 | - { name: twig.extension }
13 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/translations/menu.en.yml:
--------------------------------------------------------------------------------
1 | about: "About"
2 | profile: "Profile"
3 | admin: "Admin area"
4 | logout: "Logout"
5 | login: "Login"
6 | sign_up: "Sign-up"
7 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/translations/messages.en.yml:
--------------------------------------------------------------------------------
1 | project_name: "Symfony Force Edition"
2 |
3 | user:
4 | confirm:
5 | title: "Account details"
6 | submit: 'Confirm'
7 |
8 | login:
9 | title: "Login"
10 | username: "Username"
11 | password: "Password"
12 | forgot: "Forgot username or password?"
13 | submit: "Login"
14 | incorrect_credentials: 'Email or password is incorrect'
15 | account_disabled: 'Account is disabled'
16 |
17 | reset:
18 | title: "Reset password"
19 | submit: 'Reset'
20 | verification: 'Verification'
21 | confirmation_sent: 'Confirmation email was successfully resent to %email%'
22 | user_not_found: 'User with this email not found'
23 | flash:
24 | email_sent: 'You should receive confirmation email shortly'
25 | password_sent: 'Confirmation email was successfully resent to %email%'
26 |
27 | signup:
28 | title: 'Signup'
29 | submit: 'Signup'
30 | already_confirmed: 'Email %email% is already confirmed'
31 |
32 | profile:
33 | title: 'Account profile'
34 | change_password: 'Change password'
35 | submit: 'Update'
36 | details: 'Details'
37 | flash:
38 | updated: 'Your profile was updated successfully'
39 |
40 | label:
41 | password_mismatch: 'Passwords does not match'
42 |
43 | flash:
44 | confirmed: 'The user %user% was successfully confirmed'
45 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/translations/time.en.yml:
--------------------------------------------------------------------------------
1 | ago:
2 | year: "1 year ago|%count% years ago"
3 | month: "1 month ago|%count% months ago"
4 | day: "1 day ago|%count% days ago"
5 | hour: "1 hour ago|%count% hours ago"
6 | minute: "1 minute ago|%count% minutes ago"
7 | second: "1 second ago|%count% seconds ago"
8 |
9 | empty: now
10 |
11 | in:
12 | second: "in 1 second|in %count% seconds"
13 | minute: "in 1 minute|in %count% minutes"
14 | hour: "in 1 hour|in %count% hours"
15 | day: "in 1 day|in %count% days"
16 | month: "in 1 month|in %count% months"
17 | year: "in 1 year|in %count% years"
18 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/views/Home/about.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AppBundle::layout.html.twig' %}
2 |
3 | {% block content %}
4 |
5 | {% include 'AppBundle::flashes.html.twig' %}
6 |
7 |
8 |
9 | Could not resist.. Want some background sound?
10 |
11 |
12 |
13 |
14 |

15 |
16 |
17 |
18 |
19 |
20 | {% endblock %}
21 |
22 | {% block js %}
23 |
38 | {% endblock %}
39 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/views/Home/homepage.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AppBundle::layout.html.twig' %}
2 |
3 | {% block content %}
4 |
5 | {% include 'AppBundle::flashes.html.twig' %}
6 |
7 |
8 |
9 |
10 | {% for banner in range(0, 0) %}
11 |
12 | {% endfor %}
13 |
14 |
15 |
16 |
17 | {% for banner in range(1, 1) %}
18 |
19 |
 }})
20 | {% if banner == 1 %}
21 |
Symfony Force Edition
22 | {% endif %}
23 |
24 | {% endfor %}
25 |
26 |
27 |
28 |
29 | Previous
30 |
31 |
32 |
33 | Next
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
 }})
43 |
44 |
45 |
Making repository interfaces I was to run my Behat tests faster before. Without any change 85% faster
46 | now they run.
47 |
Yoda
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
it is not big enough for Deathstar enterprise software and it does not run on Windows!?
56 |
Darth Vader
57 |
58 |
59 |
 }})
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
 }})
68 |
69 |
70 |
RRRAARRWHHGWWR sherlock. And there can be only a single main transaction during the request.
71 |
Chewbacca
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
My drones cannot smuggle space heroin, all database changes are audited.
80 |
General Grievous
81 |
82 |
83 |
 }})
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | {% endblock %}
92 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/views/Mail/template.html.twig:
--------------------------------------------------------------------------------
1 | {{ template.subject }}
2 |
3 | {% include(template_from_string(template.content)) %}
4 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/views/User/confirm.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AppBundle::layout.html.twig' %}
2 |
3 | {% set title = "user.confirm.title"|trans %}
4 |
5 | {% block content %}
6 |
7 | {% include 'AppBundle::flashes.html.twig' %}
8 |
9 |
10 |
11 |
14 |
15 | {{ form_start(form, {action: path('app_user_confirm', {token: token})}) }}
16 | {{ form_row(form.firstname) }}
17 | {{ form_row(form.lastname) }}
18 | {{ form_row(form.plainPassword) }}
19 |
20 |
25 | {{ form_end(form) }}
26 |
27 | {% endblock %}
28 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/views/User/login.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AppBundle::layout.html.twig' %}
2 |
3 | {% set title = 'user.login.title'|trans %}
4 |
5 | {% block content %}
6 |
7 | {% include 'AppBundle::flashes.html.twig' %}
8 | {% if error %}
9 |
10 | {{ error }}
11 |
12 | {% endif %}
13 |
14 |
44 | {% endblock %}
45 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/views/User/profile.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AppBundle::layout.html.twig' %}
2 |
3 | {% set title = "user.profile.title"|trans %}
4 |
5 | {% block content %}
6 |
7 | {% include 'AppBundle::flashes.html.twig' %}
8 |
9 |
10 |
11 |
14 |
15 | {{ form_start(form, {action: path('app_user_profile')}) }}
16 |
{{ 'user.profile.details'|trans }}
17 |
18 | {{ form_row(form.firstname) }}
19 | {{ form_row(form.lastname) }}
20 |
21 |
{{ 'user.profile.change_password'|trans }}
22 |
23 | {{ form_row(form.plainPassword) }}
24 |
25 |
30 | {{ form_end(form) }}
31 |
32 | {% endblock %}
33 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/views/User/reset.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AppBundle::layout.html.twig' %}
2 |
3 | {% set title = 'user.reset.title'|trans %}
4 |
5 | {% block content %}
6 |
7 | {% include 'AppBundle::flashes.html.twig' %}
8 |
9 |
10 |
11 |
14 |
15 | {{ form_start(form, {action: path('app_user_reset')}) }}
16 | {{ form_row(form.email) }}
17 | {{ form_row(form.captcha) }}
18 |
19 |
24 | {{ form_end(form) }}
25 |
26 | {% endblock %}
27 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/views/User/signup.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'AppBundle::layout.html.twig' %}
2 |
3 | {% set title = "user.signup.title"|trans %}
4 |
5 | {% block content %}
6 |
7 | {% include 'AppBundle::flashes.html.twig' %}
8 |
9 |
10 |
11 |
14 |
15 | {{ form_start(form, {action: path('app_user_signup')}) }}
16 | {{ form_row(form.email) }}
17 |
18 |
23 | {{ form_end(form) }}
24 |
25 | {% endblock %}
26 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/views/blocks/layout/footer.html.twig:
--------------------------------------------------------------------------------
1 | © {{ "now"|date("Y") }} DATA-DOG
2 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/views/flashes.html.twig:
--------------------------------------------------------------------------------
1 | {% set flashIconMap = {danger: 'exclamation-circle', success: 'check-circle', info: 'info-circle'} %}
2 | {% for type, bag in app.session.flashbag.all() %}
3 | {% for message in bag %}
4 |
5 |
6 | {{ message }}
7 |
8 | {% endfor %}
9 | {% endfor %}
10 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/views/forms.html.twig:
--------------------------------------------------------------------------------
1 | {% extends "bootstrap_3_horizontal_layout.html.twig" %}
2 |
3 | {# adds novalidate to prevent html5 validation#}
4 | {% block form_start -%}
5 | {% set attr = attr|merge({novalidate: null}) %}
6 | {{- parent() -}}
7 | {%- endblock form_start %}
8 |
9 |
--------------------------------------------------------------------------------
/src/AppBundle/Resources/views/layout.html.twig:
--------------------------------------------------------------------------------
1 | {% extends '::base.html.twig' %}
2 |
3 | {% block body %}
4 |
5 |
9 |
10 | {% block content %}{% endblock %}
11 |
12 |
15 |
16 | {% endblock body %}
17 |
--------------------------------------------------------------------------------
/src/AppBundle/Security/Core/Encoder/BCryptPasswordEncoder.php:
--------------------------------------------------------------------------------
1 | assertSame([], $user->getRoles());
16 | }
17 |
18 | /**
19 | * @test
20 | */
21 | public function should_be_able_to_add_defined_role()
22 | {
23 | $user = new User;
24 | $user->addRole('ROLE_ADMIN');
25 | $this->assertSame($user->getRoles(), ['ROLE_ADMIN']);
26 | }
27 |
28 | /**
29 | * @test
30 | */
31 | public function should_skip_adding_undefined_role()
32 | {
33 | $user = new User;
34 | $user->addRole('ROLE_UNDEFINED');
35 | $this->assertSame([], $user->getRoles());
36 | }
37 |
38 | /**
39 | * @test
40 | */
41 | public function should_be_able_to_remove_defined_role()
42 | {
43 | $user = new User;
44 | $user->setRoles(['ROLE_ADMIN', 'ROLE_USER']);
45 | $user->removeRole('ROLE_ADMIN');
46 |
47 | $this->assertSame(['ROLE_USER'], $user->getRoles());
48 | }
49 |
50 | /**
51 | * @test
52 | */
53 | public function should_not_be_able_to_remove_undefined_role()
54 | {
55 | $user = new User;
56 | $user->setRoles(['ROLE_ADMIN', 'ROLE_USER']);
57 | $user->removeRole('ROLE_UNDEFINED');
58 |
59 | $this->assertSame(['ROLE_USER', 'ROLE_ADMIN'], $user->getRoles());
60 | }
61 |
62 | /**
63 | * @test
64 | */
65 | public function should_add_user_role_when_confirmed()
66 | {
67 | $user = new User;
68 | $user->confirm();
69 | $this->assertSame(['ROLE_USER'], $user->getRoles());
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/AppBundle/Twig/CMSBlockExtension.php:
--------------------------------------------------------------------------------
1 | repo = $em->getRepository('AppBundle:CmsBlock');
22 | }
23 |
24 | /**
25 | * Returns the name of the extension.
26 | *
27 | * @return string The extension name
28 | */
29 | public function getName()
30 | {
31 | return 'cms_block';
32 | }
33 |
34 | /**
35 | * Returns a list of functions to add to the existing list.
36 | *
37 | * @return array An array of functions
38 | */
39 | public function getFunctions()
40 | {
41 | return [
42 | new \Twig_SimpleFunction('cms_block', [$this, 'renderBlock'], ['needs_environment' => true]),
43 | ];
44 | }
45 |
46 | /**
47 | * @param \Twig_Environment $twig
48 | * @param string $alias
49 | * @return \Twig_Template
50 | * @throws \InvalidArgumentException
51 | */
52 | public function renderBlock(\Twig_Environment $twig, $alias)
53 | {
54 | $block = $this->repo->createQueryBuilder('b')
55 | ->where('b.alias = :alias')
56 | ->setParameters(compact('alias'))
57 | ->setMaxResults(1)
58 | ->getQuery()
59 | ->useResultCache(true)
60 | ->setResultCacheId('cms_block.' . $alias)
61 | ->getResult();
62 |
63 | $block = current($block);
64 | if (!$block) {
65 | throw new \InvalidArgumentException(sprintf("CMS block '%s' could not be found", $alias));
66 | }
67 |
68 | return $twig->createTemplate($block->getContent());
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/AppBundle/Twig/TimeExtension.php:
--------------------------------------------------------------------------------
1 | translator = $translator;
19 | }
20 |
21 | public function getFunctions()
22 | {
23 | return [
24 | new \Twig_SimpleFunction('time_diff', [$this, 'diff'], ['is_safe' => ['html']])
25 | ];
26 | }
27 |
28 | public function diff($since = null, $to = null)
29 | {
30 | foreach (['since', 'to'] as $var) {
31 | if ($$var instanceof \DateTime) {
32 | continue;
33 | }
34 | if (is_integer($$var)) {
35 | $$var = date('Y-m-d H:i:s', $$var);
36 | }
37 | $$var = new \DateTime($$var);
38 | }
39 |
40 | static $units = [
41 | 'y' => 'year',
42 | 'm' => 'month',
43 | 'd' => 'day',
44 | 'h' => 'hour',
45 | 'i' => 'minute',
46 | 's' => 'second'
47 | ];
48 |
49 | $diff = $to->diff($since);
50 | foreach ($units as $attr => $unit) {
51 | $count = $diff->{$attr};
52 | if (0 !== $count) {
53 | $id = sprintf('%s.%s', $diff->invert ? 'ago' : 'in', $unit);
54 | return $this->translator->transChoice($id, $count, ['%count%' => $count], 'time');
55 | }
56 | }
57 | return $this->translator->trans('empty', [], 'time');
58 | }
59 |
60 | public function getName()
61 | {
62 | return 'time';
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/web/app.php:
--------------------------------------------------------------------------------
1 | unregister();
15 | $apcLoader->register(true);
16 | */
17 |
18 | require_once __DIR__.'/../app/AppKernel.php';
19 | //require_once __DIR__.'/../app/AppCache.php';
20 |
21 | $kernel = new AppKernel('prod', false);
22 | $kernel->loadClassCache();
23 | //$kernel = new AppCache($kernel);
24 |
25 | // When using the HttpCache, you need to call the method in your front controller instead of relying on the configuration parameter
26 | //Request::enableHttpMethodParameterOverride();
27 | $request = Request::createFromGlobals();
28 | $response = $kernel->handle($request);
29 | $response->send();
30 | $kernel->terminate($request, $response);
31 |
--------------------------------------------------------------------------------
/web/app_dev.php:
--------------------------------------------------------------------------------
1 | loadClassCache();
20 | $request = Request::createFromGlobals();
21 | $response = $kernel->handle($request);
22 | $response->send();
23 | $kernel->terminate($request, $response);
24 |
--------------------------------------------------------------------------------
/web/app_test.php:
--------------------------------------------------------------------------------
1 | loadClassCache();
15 |
16 | $request = Request::createFromGlobals();
17 | $response = $kernel->handle($request);
18 | $response->send();
19 | $kernel->terminate($request, $response);
20 |
--------------------------------------------------------------------------------
/web/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/web/apple-touch-icon.png
--------------------------------------------------------------------------------
/web/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DATA-DOG/symfony-force/bbb8b070acdd2446e16672c2e491ff67a10fc3b6/web/favicon.ico
--------------------------------------------------------------------------------
/web/robots.txt:
--------------------------------------------------------------------------------
1 | # www.robotstxt.org/
2 | # www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449
3 |
4 | User-agent: *
5 |
--------------------------------------------------------------------------------