├── .gitignore ├── .platform.app.yaml ├── .platform └── routes.yaml ├── .styleci.yml ├── .travis.yml ├── LICENSE ├── README.md ├── app ├── .htaccess ├── AppCache.php ├── AppKernel.php ├── Resources │ ├── CmfSimpleCmsBundle │ │ └── views │ │ │ └── Page │ │ │ └── index.html.twig │ ├── TwigBundle │ │ └── views │ │ │ └── Exception │ │ │ └── error.html.twig │ ├── slides │ │ ├── diagram_sourcefiles │ │ │ ├── components.dia │ │ │ ├── request_flow.dia │ │ │ ├── request_flow_cmf.dia │ │ │ ├── routing.dia │ │ │ └── routing_and_menu.dia │ │ ├── routing_and_menu │ │ │ ├── menubundle.png │ │ │ ├── routing.png │ │ │ ├── routing_and_menu.png │ │ │ └── routingandmenu.html │ │ ├── the_big_picture │ │ │ ├── adminbundle.png │ │ │ ├── bigpicture.html │ │ │ ├── blockbundle.png │ │ │ ├── components.png │ │ │ ├── halloeditor.png │ │ │ ├── menubundle.png │ │ │ ├── phpcr_concept.png │ │ │ ├── request_flow.png │ │ │ └── request_flow_cmf.png │ │ └── why_symfony_cmf │ │ │ ├── JCRvsRDBMSvsNoSQL.png │ │ │ ├── companies.png │ │ │ ├── content_graph.png │ │ │ ├── square_in_a_circle.png │ │ │ ├── versioning.png │ │ │ └── why_symfony_cmf.html │ └── views │ │ ├── block │ │ ├── block_events.html.twig │ │ └── block_talk.html.twig │ │ ├── cms │ │ ├── home.html.twig │ │ ├── news_detail.html.twig │ │ ├── news_overview.html.twig │ │ ├── news_overview.rss.twig │ │ └── sidebar.html.twig │ │ ├── error │ │ └── index.html.twig │ │ ├── layout.html.twig │ │ └── sitemap │ │ ├── index.html.twig │ │ └── index.xml.twig ├── autoload.php ├── config │ ├── config.yml │ ├── config_dev.yml │ ├── config_prod.yml │ ├── config_test.yml │ ├── parameters.yml │ ├── routing.yml │ ├── routing_dev.yml │ └── services.yml ├── console ├── phpunit.xml.dist └── tests │ ├── StaticPageTest.php │ ├── WebTestCase.php │ └── bootstrap.php ├── composer.json ├── composer.lock ├── src ├── .htaccess └── AppBundle │ ├── AppBundle.php │ ├── Block │ └── EventsBlockService.php │ ├── Composer │ └── ScriptHandler.php │ ├── DataFixtures │ └── PHPCR │ │ └── LoadStaticData.php │ ├── Document │ ├── EventsBlock.php │ ├── SeoPage.php │ └── TalkBlock.php │ ├── Initializer │ └── HomePageInitializer.php │ ├── Menu │ └── MenuProvider.php │ └── Resources │ └── data │ ├── block.yml │ ├── external.yml │ ├── page.yml │ └── posts │ ├── 2015-02-01.yml │ ├── 2015-03-20.yml │ ├── 2015-04-01.yml │ ├── 2015-05-02.yml │ ├── 2015-12-25.yml │ ├── 2016-02-10.yml │ ├── 2016-04-04.yml │ ├── 2017-01-21.yml │ └── 2017-08-23.yml └── web ├── .htaccess ├── app.php ├── app_dev.php ├── css └── style.css ├── favicon.ico ├── images ├── badge_open_source.png ├── bg.gif ├── bullet.gif ├── clock.gif ├── cmf-best-os-2013.jpg ├── cmf-ch-open-source-award-2014.jpg ├── comments.gif ├── doc.gif ├── edit-doc.gif ├── firefox-gray.jpg ├── footer-bottom.gif ├── gravatar.jpg ├── header.jpg ├── left-tab.gif ├── logo-horizontal.png ├── nav.gif ├── nav.psd ├── qsearch.gif ├── quote.gif ├── right-tab.gif ├── search.gif ├── top-menu.gif └── wrap.gif └── robots.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /app/cache/* 2 | /app/logs/* 3 | /app/bootstrap.php.cache 4 | /app/app.sqlite 5 | 6 | /vendor/ 7 | /bin/ 8 | 9 | /web/config.php 10 | /web/slides/ 11 | /web/bundles/ 12 | /app/SymfonyRequirements.php 13 | /app/check.php 14 | -------------------------------------------------------------------------------- /.platform.app.yaml: -------------------------------------------------------------------------------- 1 | # This file describes an application. You can have multiple applications 2 | # in the same project. 3 | 4 | # The name of this app. Must be unique within a project. 5 | name: php 6 | 7 | # The toolstack used to build the application. 8 | type: php:5.6 9 | build: 10 | flavor: symfony 11 | 12 | # The relationships of the application with services or other applications. 13 | # The left-hand side is the name of the relationship as it will be exposed 14 | # to the application in the PLATFORM_RELATIONSHIPS variable. The right-hand 15 | # side is in the form `:`. 16 | #relationships: 17 | # database: "mysql:mysql" 18 | 19 | # The configuration of app when it is exposed to the web. 20 | web: 21 | # The public directory of the app, relative to its root. 22 | document_root: "/web" 23 | # The front-controller script to send non-static requests to. 24 | passthru: "/app.php" 25 | whitelist: 26 | # slides 27 | - ^/slides/.*\.html$ 28 | 29 | # CSS and Javascript. 30 | - \.css$ 31 | - \.js$ 32 | 33 | # image/* types. 34 | - \.gif$ 35 | - \.jpe?g$ 36 | - \.png$ 37 | - \.ico 38 | 39 | # robots.txt. 40 | - /robots\.txt$ 41 | 42 | # The size of the persistent disk of the application (in MB). 43 | disk: 2048 44 | 45 | # The mounts that will be performed when the package is deployed. 46 | mounts: 47 | "/app/cache": "shared:files/cache" 48 | "/app/logs": "shared:files/logs" 49 | 50 | # The hooks that will be performed when the package is deployed. 51 | hooks: 52 | build: | 53 | rm web/app_dev.php 54 | # place sqlite file into /tmp so that we can write to it during deploy 55 | sed -i 's@%kernel.root_dir%/app.sqlite@/tmp/app.sqlite@' app/config/parameters.yml 56 | # install platform.sh cli to .platformsh/bin 57 | curl -sS https://platform.sh/cli/installer | php 58 | deploy: | 59 | # force clearing the cache 60 | rm -rf app/cache/prod 61 | app/console --env=prod cache:warmup 62 | app/console --env=prod doctrine:phpcr:init:dbal --drop --force 63 | app/console --env=prod doctrine:phpcr:repository:init 64 | app/console --env=prod doctrine:phpcr:fixtures:load -n 65 | 66 | runtime: 67 | extensions: 68 | - name: blackfire 69 | configuration: 70 | server_id: "ce95eba9-2d90-48b7-ad75-fcad42e1bcab" 71 | server_token: "ca7e32ac2d466ecfdf30b8ac43bdfef1304fc3feeb66e8906c1192b435e206f0" 72 | 73 | cron: 74 | symfony: 75 | spec: '0 1 * * *' 76 | cmd: | 77 | # force clearing the cache 78 | rm -rf app/cache/prod 79 | app/console --env=prod cache:warmup 80 | app/console --env=prod doctrine:phpcr:init:dbal --drop --force 81 | app/console --env=prod doctrine:phpcr:repository:init 82 | app/console --env=prod doctrine:phpcr:fixtures:load -n 83 | 84 | renewcert: 85 | # Force a redeploy at 1 am (UTC) on the 1st and 15th of every month. 86 | spec: '0 1 1,15 * *' 87 | cmd: | 88 | if [ "$PLATFORM_BRANCH" = master ]; then 89 | platform redeploy --yes --no-wait 90 | fi 91 | -------------------------------------------------------------------------------- /.platform/routes.yaml: -------------------------------------------------------------------------------- 1 | "http://{default}/": 2 | type: upstream 3 | upstream: "php:php" 4 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: symfony 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.6 5 | 6 | sudo: false 7 | 8 | cache: 9 | directories: 10 | - $HOME/.composer/cache/files 11 | 12 | before_script: 13 | - composer install 14 | - php app/console doctrine:database:create 15 | - php app/console doctrine:phpcr:init:dbal --force 16 | - php app/console doctrine:phpcr:workspace:create standard_test 17 | - php app/console doctrine:phpcr:repository:init -e=test 18 | 19 | script: phpunit -c app 20 | 21 | notifications: 22 | irc: "irc.freenode.org#symfony-cmf" 23 | email: "symfony-cmf-devs@googlegroups.com" 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | symfony-cmf-website 2 | 3 | The MIT License 4 | 5 | Copyright (c) Symfony CMF 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/symfony-cmf/symfony-cmf-website.svg?branch=master)](https://travis-ci.org/symfony-cmf/symfony-cmf-website) 2 | 3 | # Symfony CMF Website 4 | 5 | ### You will need: 6 | * Git 1.6+ 7 | * PHP 5.3.3+ 8 | * php5-intl 9 | * phpunit 3.6+ (optional) 10 | * composer 11 | 12 | ## Installation 13 | To get the website running, first clone the repository: 14 | 15 | $ git clone git://github.com/symfony-cmf/symfony-cmf-website.git 16 | $ cd symfony-cmf-website 17 | 18 | ## Get the code 19 | 20 | $ curl -s http://getcomposer.org/installer | php -- 21 | $ php composer.phar install 22 | 23 | This will fetch the vendors and all it's dependencies. 24 | 25 | The next step is to setup the database: 26 | 27 | app/console doctrine:database:create 28 | app/console doctrine:phpcr:init:dbal --force 29 | app/console doctrine:phpcr:repository:init 30 | app/console doctrine:phpcr:fixtures:load -n 31 | 32 | ### Setup your permissions - see [Setting up Permissions](http://symfony.com/doc/current/book/installation.html#configuration-and-setup). 33 | [![StyleCI](https://styleci.io/repos/806312/shield)](https://styleci.io/repos/806312) 34 | For Mac Os X users, when changing the owner of the cache folder, use '_www' instead of www-data. 35 | 36 | ## Access by web browser 37 | 38 | Create an apache virtual host entry along the lines of 39 | 40 | 41 | Servername http://cmf-website.lo 42 | DocumentRoot /path/to/symfony-cmf/symfony-cmf-website/web 43 | 44 | AllowOverride All 45 | 46 | 47 | 48 | And add an entry to your hosts file for "cmf-website.lo" 49 | 50 | If you are running Symfony for the first time, run http://cmf-website.lo/config.php to ensure your 51 | system settings have been setup inline with the expected behaviour of the Symfony framework. 52 | 53 | Then point your browser to http://cmf-website.lo/app_dev.php 54 | 55 | ## Run tests 56 | 57 | Functional tests are written with PHPUnit. Note that Bundles and Components are tested independently. 58 | 59 | app/console doctrine:phpcr:workspace:create standard_test 60 | phpunit -c app 61 | -------------------------------------------------------------------------------- /app/.htaccess: -------------------------------------------------------------------------------- 1 | deny from all -------------------------------------------------------------------------------- /app/AppCache.php: -------------------------------------------------------------------------------- 1 | getEnvironment(), array('dev', 'test'))) { 41 | $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); 42 | $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); 43 | } 44 | 45 | return $bundles; 46 | } 47 | 48 | public function registerContainerConfiguration(LoaderInterface $loader) 49 | { 50 | $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/Resources/CmfSimpleCmsBundle/views/Page/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends "layout.html.twig" %} 2 | 3 | {% block title %}{{ cmfMainContent.title }}{% endblock %} 4 | {% block content %} 5 | 6 |
7 | {{ cmfMainContent.body|raw }} 8 |
9 | 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /app/Resources/TwigBundle/views/Exception/error.html.twig: -------------------------------------------------------------------------------- 1 | {% extends "layout.html.twig" %} 2 | 3 | {% block title %}An Error Occurred: {{ status_text }}{% endblock %} 4 | 5 | {% block content %} 6 |

Oops! An Error Occurred

7 |

The server returned a "{{ status_code }} {{ status_text }}".

8 | 9 |

10 | Something is broken. Please e-mail us and let us know 11 | what you were doing when this error occurred. We will fix it as soon 12 | as possible. Sorry for any inconvenience caused. 13 |

14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /app/Resources/slides/diagram_sourcefiles/components.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/diagram_sourcefiles/components.dia -------------------------------------------------------------------------------- /app/Resources/slides/diagram_sourcefiles/request_flow.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/diagram_sourcefiles/request_flow.dia -------------------------------------------------------------------------------- /app/Resources/slides/diagram_sourcefiles/request_flow_cmf.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/diagram_sourcefiles/request_flow_cmf.dia -------------------------------------------------------------------------------- /app/Resources/slides/diagram_sourcefiles/routing.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/diagram_sourcefiles/routing.dia -------------------------------------------------------------------------------- /app/Resources/slides/diagram_sourcefiles/routing_and_menu.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/diagram_sourcefiles/routing_and_menu.dia -------------------------------------------------------------------------------- /app/Resources/slides/routing_and_menu/menubundle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/routing_and_menu/menubundle.png -------------------------------------------------------------------------------- /app/Resources/slides/routing_and_menu/routing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/routing_and_menu/routing.png -------------------------------------------------------------------------------- /app/Resources/slides/routing_and_menu/routing_and_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/routing_and_menu/routing_and_menu.png -------------------------------------------------------------------------------- /app/Resources/slides/routing_and_menu/routingandmenu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Symfony2 CMF - Routing and Navigation Menu 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 64 | 65 | 75 | 76 | 77 | 78 |
79 |

Symfony2 CMF Project

80 |

Routing and navigation menu components

81 |

cmf.symfony.com

82 |

An introduction to the routing and menu components.

83 |
84 | 85 |
86 |

87 | Symfony2 routing 88 |

89 |
    90 |
  • determine controller to handle this request
  • 91 |
  • parse url parameters
  • 92 |
  • 93 | default: based on configured set of routes 94 |
      95 |
    • loaded from configuration files
    • 96 |
    • generated by bundles
    • 97 |
    98 |
  • 99 |
  • does not support editing routes
  • 100 |
  • large number of routes?
  • 101 |
  • in a cms, users want to define their URLs
  • 102 |
103 |
104 | 105 |
106 |

Solution: First Take

107 |
    108 |
  • catch-all route /{url} with requirement url: .*
  • 109 |
  • one controller for all requests (forward to specific controllers?)
  • 110 |
  • two-step-routing tends to reinvent the wheel and creating a mess
  • 111 |
  • does not play nicely with other routes
  • 112 |
113 |

114 |
115 |             navigation:
116 |                 pattern: "/{url}"
117 |                 defaults: { _controller: service.controller:indexAction }
118 |                 requirements:
119 |                     url: .*
120 |             
121 |
122 | 123 |
124 |

Solution: Cmf Routing component

125 |
    126 |
  • The new DynamicRouter 127 |
      128 |
    • full flexibility
    • 129 |
    • lookup in database
    • 130 |
    131 |
  • 132 |
  • ChainRouter 133 |
      134 |
    • to combine both routing concepts
    • 135 |
    • and along the way allow you to add your own routers
    • 136 |
    137 |
  • 138 |
139 |
140 | 141 |
142 |

ChainRouter

143 |
    144 |
  • find router services by tagging
  • 145 |
  • tries to match with each configured router
  • 146 |
  • with priorities to control order
  • 147 |
148 |

149 |
150 |                 
152 |                     
153 |                 
154 |             
155 |
156 | 157 |
158 |

DynamicRouter: Match url

159 |
    160 |
  • find route at runtime based on current request
  • 161 |
  • write your own route loader by implementing a simple interface
  • 162 |
  • provided implementation with PHPCR ODM
  • 163 |
  • built on top of the standard symfony router
  • 164 |
  • route may explicitly specify controller
  • 165 |
  • otherwise enhancers to determine values
  • 166 |
  • redirection route (to other route or absolute url)
  • 167 |
168 |
169 | 170 |
171 |

172 | DynamicRouter: PHPCR-ODM 173 |

174 |
    175 |
  • translate request URL to repository path
  • 176 |
  • routing tree consists of references to the content tree
  • 177 |
  • possible to separate content tree from routing tree 178 |
      179 |
    • multi site: f.e. desktop, tablet, phone ..
    • 180 |
    • multi language: f.e. english, french, german ..
    • 181 |
    182 |
  • 183 |
  • reworking the routing requires cloning the routing tree
  • 184 |
  • merge back into the production routing tree once ready
  • 185 |
186 |
187 | 188 |
189 |

DynamicRouter: Route Enhancers

190 |
    191 |
  • infer some information from the route information
  • 192 |
  • i.e. decide which controller to use based on content document class
  • 193 |
  • useful enhancers provided:
  • 194 |
      195 |
    • map content class to controller
    • 196 |
    • map content class to template and use generic controller
    • 197 |
    • use a default controller for any DynamicRouter route
    • 198 |
    199 | 200 |
  • you can add your own enhancers
  • 201 |
202 |

203 |
204 |                 interface ControllerEnhancerInterface
205 |                 {
206 |                     public function enhance(array $defaults, Request $request);
207 |                 }
208 |             
209 |
210 | 211 |
212 |

Routing and Content

213 |
214 |

215 |
216 | 217 |
218 |

DynamicRouter: Generate url from referenced content

219 |
220 |                 $this->router->generate($content);
221 |             
222 |
    223 |
  • DynamicRouter can generate url from content document, route document as well as content or route repository id
  • 224 |
225 |
226 | 227 |
228 |

Menu

229 |
    230 |
  • provide Knp\Menu\NodeInterface with PHPCR ODM documents
  • 231 |
  • menu renderer: if MenuItem specifies content 232 |
      233 |
    • generate route from content
    • 234 |
    • determine active menu item from content in request
    • 235 |
    236 |
  • 237 |
  • otherwise let standard menu renderer handle this entry
  • 238 |
  • with PHPCR ODM, multi language menus are a breeze
  • 239 |
240 |
241 | 242 |
243 | 244 |
245 |

Routing and Content and Menu

246 |
247 |

248 |
249 | 250 |
251 |

Conclusions

252 |
    253 |
  • very flexible way to handle things
  • 254 |
  • building on top of the symfony router
  • 255 |
  • routing parameters, locale support
  • 256 |
  • ready to use standalone or with other Symfony CMF components
  • 257 |
  • if you don't need the flexibility, just make menu, route and content the same class :-) 258 | [ As demonstrated in the SimpleCmsBundle ] 259 |
  • 260 |
261 |
262 | 263 |
264 |

Github projects

265 | 271 |
272 | 273 |
274 |

Resources

275 | 281 |
282 | 283 | 288 | 289 | 290 | 291 | -------------------------------------------------------------------------------- /app/Resources/slides/the_big_picture/adminbundle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/the_big_picture/adminbundle.png -------------------------------------------------------------------------------- /app/Resources/slides/the_big_picture/bigpicture.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Symfony2 CMF - The Big Picture 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 64 | 65 | 75 | 76 | 77 | 78 |
79 |

Symfony2 CMF Project

80 |

The Big Picture

81 |

cmf.symfony.com

82 |

An overview of the Symfony2 content management framework with lots of pictures.

83 |
84 | 85 |
86 |

The Symfony CMF project makes it easier for developers to add CMS functionality to 87 | applications built with the Symfony2 PHP framework. Key development principles for the provided 88 | set of bundles are scalability, usability, 89 | documentation and testing 90 |

91 |
92 | 93 |
94 |

95 | Content management framework,
not content management system 96 |

97 |
    98 |
  • Use what you can
  • 99 |
  • Replace what you need
  • 100 |
  • Ignore what you do not need

  • 101 |
  • Collection of modular tools to do content management
  • 102 |
  • Not a ready-to-use application for end users
  • 103 |
104 |
105 | 106 |
107 |

Content Management means user controlled:

108 |
    109 |
  • Content: Createjs frontend editing, Sonata admin
  • 110 |
  • Menu structure: Provider for KnpMenuBundle
  • 111 |
  • Routes (URLs): RoutingBundle together with Drupal
  • 112 |
  • Page structure (to some extent): Block bundle based on Sonata BlockBundle
  • 113 |
114 |
115 | 116 |
117 |

Symfony2 Control Flow

118 |

 

119 |
120 |

121 |
122 | 123 |
124 |

Symfony2 Control Flow

125 |

CMF additions

126 |
127 |

128 |
129 | 130 | 131 |
132 |

At a glance

133 |
134 |

135 |
136 | 137 |
138 |

PHPCR and the ODM

139 | 140 | 141 |
    142 |
  • PHP Content Repositories API specification
  • 143 |
  • Graph-based, schema-less, versioning document database

  • 144 |
  • Doctrine PHPCR ODM: map nodes to your objects
  • 145 |
  • Add features like multilanguage
  • 146 |
147 |
148 | 149 |
150 |

Routing

151 |
    152 |
  • ChainRouter allows to have several routers in action
  • 153 |
  • DynamicRouter for user-defined routes from a data store
  • 154 |
  • Specifies controller and page content to use
  • 155 |
  • Or just the template instead of controller (with the ContentBundle)
  • 156 |
157 |
158 | 159 |
160 |

Core Bundle

161 |
    162 |
  • Provides common infrastructure for all CMF Bundles
  • 163 |
  • Unifies several configuration options for Bundles
  • 164 |
  • Simple publish workflow
  • 165 |
  • Twig extension for interaction with PHPCR ODM
  • 166 |
  • Some BC helpers to maintain Symfony 2.2 compatibility
  • 167 |
168 |
169 | 170 |
171 |

Content Bundle

172 |
    173 |
  • Provides generic controller so you need only a custom template
  • 174 |
  • Simple StaticContent model and sonata admin integration
  • 175 |
176 |
177 | 178 |
179 |

Menu Bundle

180 |
    181 |
  • Integrate KnpMenuBundle with Doctrine
  • 182 |
  • Menu documents pointing to content documents
  • 183 |
  • Generates URL for content
  • 184 |
185 |
186 | 187 |
188 | 189 |
190 |

Block Bundle

191 |
    192 |
  • Integration between SonataBlockBundle and PHPCR ODM
  • 193 |
  • Multiple blocks per page
  • 194 |
  • Blocks can be reused on different pages
  • 195 |
196 |
197 | 198 |
199 | 200 |
201 |

Media Bundle

202 |
    203 |
  • Abstracting storage of media data
  • 204 |
  • Integration with LiipImagineBundle, elfinder for WYSIWYG editors, ...
  • 205 |
206 |
207 | 208 |
209 |

Create Bundle

210 |
    211 |
  • Integration with create.js
  • 212 |
  • backbone.js based editing framework
  • 213 |
  • Uses RDF annotations to detect editable data
  • 214 |
  • Supports hallo.js, CKEditor, Aloha and others via contentEditable
  • 215 |
216 |
217 | 218 |
219 | 220 |
221 |

Sonata PHPCR ODM AdminBundle

222 |
    223 |
  • Integration between SonataAdminBundle and PHPCR ODM
  • 224 |
  • Supports tree oriented data
  • 225 |
226 |
227 | 228 |
229 | 230 |
231 |

Other related Bundles

232 |
    233 |
  • CmfTreeBrowserBundle (tree UI for sonata admin)
  • 234 |
  • CmfMediaBundle (interfaces and models for media assets)
  • 235 |
  • CmfBlogBundle (simple blog solution)
  • 236 |
  • CmfSearchBundle (content search, extends LiipSearchBundle)
  • 237 |
  • CmfSimpleCms (a simple CMS incl. content authoring)
  • 238 |
  • LuneticsLocaleBundle (automatic locale detection)
  • 239 |
  • Integration with several more Sonata Bundles
  • 240 |
241 |
242 | 243 |
244 |

Github projects

245 | 254 |
255 | 256 |
257 |

Resources

258 | 264 |
265 | 266 | 271 | 272 | 273 | 274 | -------------------------------------------------------------------------------- /app/Resources/slides/the_big_picture/blockbundle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/the_big_picture/blockbundle.png -------------------------------------------------------------------------------- /app/Resources/slides/the_big_picture/components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/the_big_picture/components.png -------------------------------------------------------------------------------- /app/Resources/slides/the_big_picture/halloeditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/the_big_picture/halloeditor.png -------------------------------------------------------------------------------- /app/Resources/slides/the_big_picture/menubundle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/the_big_picture/menubundle.png -------------------------------------------------------------------------------- /app/Resources/slides/the_big_picture/phpcr_concept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/the_big_picture/phpcr_concept.png -------------------------------------------------------------------------------- /app/Resources/slides/the_big_picture/request_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/the_big_picture/request_flow.png -------------------------------------------------------------------------------- /app/Resources/slides/the_big_picture/request_flow_cmf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/the_big_picture/request_flow_cmf.png -------------------------------------------------------------------------------- /app/Resources/slides/why_symfony_cmf/JCRvsRDBMSvsNoSQL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/why_symfony_cmf/JCRvsRDBMSvsNoSQL.png -------------------------------------------------------------------------------- /app/Resources/slides/why_symfony_cmf/companies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/why_symfony_cmf/companies.png -------------------------------------------------------------------------------- /app/Resources/slides/why_symfony_cmf/content_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/why_symfony_cmf/content_graph.png -------------------------------------------------------------------------------- /app/Resources/slides/why_symfony_cmf/square_in_a_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/why_symfony_cmf/square_in_a_circle.png -------------------------------------------------------------------------------- /app/Resources/slides/why_symfony_cmf/versioning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/app/Resources/slides/why_symfony_cmf/versioning.png -------------------------------------------------------------------------------- /app/Resources/slides/why_symfony_cmf/why_symfony_cmf.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Symfony2 CMF Motivation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 62 | 63 | 70 | 71 | 72 | 73 |
74 |

Symfony2 CMF Project

75 |

Why does the world need this?

76 |

cmf.symfony.com

77 |
78 | 79 |
80 |

Drupal is awesome!

81 |
82 | 83 |
84 |

No REALLY!

85 |

Drupal is awesome!

86 |
87 | 88 |
89 |

Drupal is awesome if you are an end user!

90 |

Powerful admin interface

91 |
92 | 93 |
94 |

Drupal is awesome if you are a sales guy!

95 |

Well known brand

96 |
97 | 98 |
99 |
100 |

Drupal sucks if you are a developer!

101 |
102 |
103 | 104 |
105 |
106 |

Drupal is CMS 1st, development framework 2nd

107 |

This is a legitimate prioritization for their users

108 |
109 |
110 | 111 |
112 |

Drupal developer nightmares ..

113 |
    114 |
  • .. no clean separation of configuration, logic and content
  • 115 |
  • .. no clean deployment and staging concept
  • 116 |
  • .. inconsistent cache layers
  • 117 |
  • .. function callback AOP is not every ones cup of tea
  • 118 |
  • .. lots of legacy baggage
  • 119 |
  • .. NIH (not invented here) syndrom
  • 120 |
121 |
122 | 123 |
124 |
125 |

Note the Drupal guys agree and are actually collaborating with Symfony2 to improve

126 | 127 |
    128 |
  • The "sucks for developers" slide criticisms are all valid. And we're trying to fix them in D8. Larry Garfield (aka Crell)
  • 129 |
130 | 131 |
132 |
133 | 134 |
135 |

Do we also suffer from NIH?

136 |
    137 |
  • Collaboration with Drupal and others to share basic content management code
  • 138 |
  • Based ourselves as much on standard tools and specs
  • 139 |
  • Leverage existing bundles and technologies of the Symfony and web developer community
  • 140 |
  • Deliver value within a reasonable time
  • 141 |
  • Both Drupal 8 and ezPublish 5 use the CMF routing component
  • 142 |
143 |
144 | 145 |
146 |

CMF = Content Management Framework

147 |
    148 |
  • In other words: its a toolbox to create your own custom CMS
  • 149 |
  • Not a one size fits all, but increase code sharing
  • 150 |
  • Imagine Diem, Sympal, Apostrophe all build on the same content foundation
  • 151 |
152 |
153 | 154 |
155 |

The Symfony CMF project makes it easier for developers to add CMS functionality to 156 | applications built with the Symfony2 PHP framework. Key development principles for the provided 157 | set of bundles are scalability, usability, 158 | documentation and testing 159 |

160 |
161 | 162 |
163 |

What is Content Management?

164 |

User controlled

165 |
    166 |
  • Content
  • 167 |
  • Content structure
  • 168 |
  • Navigation structure
  • 169 |
  • Routes (URLs)
  • 170 |
171 |

The CMF solutions to these requirements are discussed in the "Big picture" slide set

172 |
173 | 174 |
175 |

Data in a CMS is mostly unstructured

176 |

RDBMS are not a good fit, hurray for NoSQL

177 |

like fitting a square into a circle

178 |
179 | 180 |
181 |

CMS often organize content as a tree/graph

182 |

Most NoSQL not a good fit, hurray for Graph DBs

183 |

content graph

184 |
185 | 186 |
187 |

CMS should be able to store content versions

188 |

multiple versions

189 |
190 | 191 |
192 |

Complexity shouldn't overwhelm developers

193 |

Need a solution that can scale both from small to large projects and we want to get somewhere within 12 months!

194 |
195 | 196 |
197 |

Enter Doctrine PHPCR ODM

198 |

PHP Content Repository + Doctrine ODM

199 |

PHPCR (aka PHP-ified JCR specification)

200 |
201 | 202 |
203 |

Works like MongoDB or CouchDB ODM, but also includes a tree/graph, versioning API

204 |

JCR vs. RDBMS/NoSQL

205 |
206 | 207 |
208 |

PHPCR provides a standardized API that can be used by any PHP content management system to interface with any content repository.

209 |
210 | 211 |
212 |

PHPCR implementations

213 |
    214 |
  • Jackalope 215 |
      216 |
    • Jackrabbit (via Http WebDav/Davex) transport layer
    • 217 |
    • Doctrine DBAL transport layer
    • 218 |
    219 |
  • 220 |
  • Midgard2 PHPCR
  • 221 |
  • ..
  • 222 |
223 |
224 | 225 |
226 |

PHPCR has been submitted to the JCR spec at the request of David Nüschler, JCR spec lead

227 |
228 | 229 |
230 |

Not all data fits well in PHPCR/JCR

231 |
    232 |
  • For example aggregation is better done in an RDBMS
  • 233 |
  • Store web store product description in PHPCR/JCR
  • 234 |
  • Store web store inventory and orders in RDBMS
  • 235 |
236 |
237 | 238 |
239 |

Door swings both ways, so remember

240 |

like fitting a square into a circle

241 |
242 | 243 | 244 |
245 |

Welcome to the real world

246 |
    247 |
  • Quiet Storm Solutions is utilizing the CMF and to drive a newly launched Saas CRM service
  • 248 |
  • ServerGrove is using the PHP content repository in their KnowledgeBase tool.
  • 249 |
  • Liip integrated the CMF as a simple editing solution into Symfony web applications like www.barometredesprix.ch.
  • 250 |
  • Eat your own dog food: the Liip website and VDW website are both built with the CMF
  • 251 |
  • Joiz uses it for their social media based TV station
  • 252 |
  • See our references for a more complete overview
  • 253 |
254 |
255 | 256 |
257 |

Interest is huge!

258 |
    259 |
  • Over 350 people on the developer mailinglist
  • 260 |
  • About 5 more per month
  • 261 |
  • About half a dozen core contributors with weekly contributions
  • 262 |
  • Community is very willing to teach new comers
  • 263 |
  • All key decisions have been made in public forums
  • 264 |
  • Multiple people are working and committing to all parts of the code
  • 265 |
266 |
267 | 268 |
269 |

Several companies and organisations are investing into the effort

270 | Liip, Ideato, Nemein, IKS, Vermont Design Works 271 |
272 | 273 |
274 |

Play with it today!

275 |

CMF sandbox online demo

276 |

Get the Symfony CMF Standard Edition on github

277 |

Get the CMF sandbox on github

278 |
279 | 280 |
281 |

Roadmap

282 |
    283 |
  • Follow about one month after Symfony2 core releases
  • 284 |
  • Respect deadline over cramming in more features
  • 285 |
  • But regular releases every half a year
  • 286 |
287 |
288 | 289 |
290 |

Github projects

291 | 298 |
299 | 300 | 310 | 311 |
312 |
313 | 314 |
315 |
316 | 317 | 322 | 323 | 324 | -------------------------------------------------------------------------------- /app/Resources/views/block/block_events.html.twig: -------------------------------------------------------------------------------- 1 | {% if talkBlocks %} 2 |
3 |

Upcoming CMF talks

4 |
    5 | {% for talkBlock in talkBlocks %} 6 | {{ sonata_block_render(talkBlock) }} 7 | {% endfor %} 8 |
9 |
10 | {% endif %} -------------------------------------------------------------------------------- /app/Resources/views/block/block_talk.html.twig: -------------------------------------------------------------------------------- 1 | {% block block %} 2 |
  • 3 | {{ block.title|raw }}
    4 | {{ block.publishEndDate|date('Y-m-d H:i') }}
    5 | {{ block.body|raw }} 6 |
    7 |
  • 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /app/Resources/views/cms/home.html.twig: -------------------------------------------------------------------------------- 1 | {% extends "CmfSimpleCmsBundle:Page:index.html.twig" %} 2 | 3 | {% block content %} 4 |
    5 | {{ cmfMainContent.body|raw }} 6 |
    7 | {% endblock %} 8 | 9 | {% block sidebar %} 10 | {% include "cms/sidebar.html.twig" %} 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /app/Resources/views/cms/news_detail.html.twig: -------------------------------------------------------------------------------- 1 | {% extends "CmfSimpleCmsBundle:Page:index.html.twig" %} 2 | 3 | {% block content %} 4 |
    5 |

    {{ cmfMainContent.title }}

    6 | 7 |
    Date: {{ cmfMainContent.publishStartDate|date('Y-m-d') }}
    8 |

    {{ cmfMainContent.body|raw }}

    9 | 10 | {% set prev = cmf_prev(cmfMainContent) %} 11 | {% if prev and cmf_is_linkable(prev) %} 12 | prev 13 | {% endif %} 14 | {% set next = cmf_next(cmfMainContent) %} 15 | {% if next %} 16 | next 17 | {% endif %} 18 |
    19 | 20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /app/Resources/views/cms/news_overview.html.twig: -------------------------------------------------------------------------------- 1 | {% extends "CmfSimpleCmsBundle:Page:index.html.twig" %} 2 | 3 | {% block title %}{{ cmfMainContent.title }}{% endblock %} 4 | 5 | {% block content %} 6 |
    7 |

    {{ cmfMainContent.title }}

    8 | 9 |
    10 |
    {{ cmfMainContent.body|raw }}
    11 | 12 |
      13 | {% for news in cmf_children(cmfMainContent)|reverse %} 14 | {% if cmf_is_linkable(news) %} 15 |
    • {{ news.title }} ({{ news.publishStartDate|date('Y-m-d') }})
    • 16 | {% endif %} 17 | {% endfor %} 18 |
    19 | 20 |
    21 |
    22 | {% endblock %} 23 | 24 | {% block sidebar %} 25 | {% include "cms/sidebar.html.twig" %} 26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /app/Resources/views/cms/news_overview.rss.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Symfony CMF 5 | {{ app.request.getSchemeAndHttpHost }} 6 | The Symfony CMF project makes it easier for developers to add CMS functionality to applications built with the Symfony PHP framework. Key development principles for the provided set of bundles are scalability, usability, documentation and testing. 7 | {{ app.request.locale }} 8 | Symfony CMF 9 | symfony-cmf-devs@googlegroups.com (Symfony CMF) 10 | symfony-cmf-devs@googlegroups.com (Symfony CMF) 11 | 1440 12 | {% for news in cmf_children(cmfMainContent, false, true)|reverse %} 13 | {% if cmf_is_linkable(news) %} 14 | 15 | {{ news.title }} 16 | {{ url(news) }} 17 | {{ url(news) }} 18 | General 19 | {{ news.body|striptags }} 20 | 21 | {{ news.publishStartDate|date('r') }} 22 | symfony-cmf-devs@googlegroups.com (Symfony CMF) 23 | 24 | {% endif %} 25 | {% endfor %} 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/Resources/views/cms/sidebar.html.twig: -------------------------------------------------------------------------------- 1 | {{ sonata_block_render({ 'name': '/cms/content/talks' }, { 2 | maxItems: 10 3 | }) }} 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/Resources/views/error/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends '::layout.html.twig' %} 2 | 3 | {% block content %} 4 |

    Oops! An Error Occurred

    5 |

    The server returned a "{{ status_code }} {{ status_text }}".

    6 | 7 |

    8 | Something is broken. Please let us know what you were doing when this error occurred. 9 | We will fix it as soon as possible. Sorry for any inconvenience caused. 10 |

    11 | 12 |

    Suggested pages

    13 | 14 | {% for group, list in best_matches if list is not empty %} 15 |

    {{ group|capitalize }}

    16 | 21 | {% else %} 22 |

    No suggestions found

    23 | {% endfor %} 24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /app/Resources/views/layout.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ sonata_seo_metadatas() }} 7 | {{ sonata_seo_link_canonical() }} 8 | {{ sonata_seo_lang_alternates() }} 9 | {{ sonata_seo_title() }} 10 | 11 | {% block includes %} 12 | 13 | 14 | 15 | 16 | 19 | {% endblock %} 20 | 21 | 22 | 23 |
    24 | 38 | 39 | 42 | 43 |
    44 | {% set _sidebar = block('sidebar') %} 45 | 46 |
    47 | {% block content %}{% endblock %} 48 |
    49 | {% if _sidebar is not empty %} 50 | 53 | {% endif %} 54 |
    55 | 56 | 99 |
    100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /app/Resources/views/sitemap/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends '::layout.html.twig' %} 2 | 3 | {% block content %} 4 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /app/Resources/views/sitemap/index.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% for url in urls %} 4 | 5 | {{ url.location }} 6 | {% if url.lastModification %} 7 | {{ url.lastModification }} 8 | {% endif %} 9 | {{ url.changeFrequency }} 10 | {% if url.alternateLocales is defined and url.alternateLocales|length > 0 %} 11 | {% for locale in url.alternateLocales %} 12 | 13 | {% endfor %} 14 | {% endif %} 15 | 16 | {% endfor %} 17 | 18 | -------------------------------------------------------------------------------- /app/autoload.php: -------------------------------------------------------------------------------- 1 | '; 5 | echo "$nl$nl"; 6 | die('You must set up the project dependencies.'.$nl. 7 | 'Run the following commands in '.dirname(__DIR__).':'.$nl.$nl. 8 | 'curl -s http://getcomposer.org/installer | php'.$nl. 9 | 'php composer.phar install'.$nl); 10 | } 11 | 12 | use Doctrine\Common\Annotations\AnnotationRegistry; 13 | use Composer\Autoload\ClassLoader; 14 | 15 | /* 16 | * @var ClassLoader 17 | */ 18 | $loader = require __DIR__.'/../vendor/autoload.php'; 19 | 20 | AnnotationRegistry::registerLoader(array($loader, 'loadClass')); 21 | 22 | return $loader; 23 | -------------------------------------------------------------------------------- /app/config/config.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: parameters.yml } 3 | - { resource: services.yml } 4 | 5 | framework: 6 | secret: false 7 | router: { resource: "%kernel.root_dir%/config/routing.yml" } 8 | form: false 9 | templating: { engines: ['twig'] } #assets_version: SomeVersionScheme 10 | default_locale: en 11 | session: false 12 | 13 | # Twig Configuration 14 | twig: 15 | exception_controller: cmf_seo.error.suggestion_provider.controller:showAction 16 | debug: %kernel.debug% 17 | strict_variables: %kernel.debug% 18 | 19 | # for jackalope-doctrine-dbal 20 | doctrine: 21 | dbal: 22 | driver: %database_driver% 23 | path: %database_path% 24 | charset: UTF8 25 | 26 | # cmf configuration 27 | doctrine_phpcr: 28 | # configure the PHPCR session 29 | session: 30 | backend: %phpcr_backend% 31 | workspace: %phpcr_workspace% 32 | username: %phpcr_user% 33 | password: %phpcr_pass% 34 | # enable the ODM layer 35 | odm: 36 | auto_mapping: true 37 | auto_generate_proxy_classes: %kernel.debug% 38 | cmf_core: 39 | persistence: 40 | phpcr: 41 | enabled: true 42 | 43 | cmf_routing: 44 | dynamic: 45 | templates_by_class: 46 | Symfony\Cmf\Bundle\SimpleCmsBundle\Doctrine\Phpcr\Page: CmfSimpleCmsBundle:Page:index.html.twig 47 | 48 | knp_menu: 49 | twig: true 50 | 51 | cmf_menu: 52 | voters: 53 | uri_prefix: true 54 | 55 | cmf_seo: 56 | title: %%content_title%% | Symfony CMF 57 | description: The Symfony Content Management Framework. %%content_description%% 58 | error: 59 | enable_parent_provider: true 60 | enable_sibling_provider: true 61 | templates: 62 | html: ":error:index.html.twig" 63 | exclusion_rules: 64 | - { path: 'excluded' } 65 | sitemap: 66 | defaults: 67 | default_change_frequency: never 68 | templates: 69 | xml: ':sitemap:index.xml.twig' 70 | html: ':sitemap:index.html.twig' 71 | configurations: 72 | sitemap: ~ 73 | frequent: 74 | default_change_frequency: always 75 | doctrine_cache: 76 | providers: 77 | phpcr_meta: 78 | type: file_system 79 | phpcr_nodes: 80 | type: file_system 81 | 82 | fos_rest: 83 | view: 84 | formats: 85 | rss: true 86 | templating_formats: 87 | rss: true 88 | html: true 89 | mime_types: 90 | rss: 'application/rss+xml' 91 | format_listener: 92 | rules: 93 | - { path: ^/news.rss, priorities: [ rss ], fallback_format: ~, prefer_extension: true } 94 | - { path: ^/, priorities: [ html ], fallback_format: html, prefer_extension: true } 95 | 96 | sonata_block: 97 | default_context: [cms] 98 | 99 | sonata_seo: 100 | page: 101 | title: Welcome | Symfony CMF 102 | metas: 103 | name: 104 | description: The Symfony Content Management Framework. 105 | keywords: "CMF, Symfony, PHPCR" 106 | -------------------------------------------------------------------------------- /app/config/config_dev.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: config.yml } 3 | 4 | framework: 5 | router: { resource: "%kernel.root_dir%/config/routing_dev.yml" } 6 | profiler: { only_exceptions: false } 7 | 8 | web_profiler: 9 | toolbar: true 10 | intercept_redirects: false 11 | 12 | monolog: 13 | handlers: 14 | main: 15 | type: stream 16 | path: %kernel.logs_dir%/%kernel.environment%.log 17 | level: debug 18 | firephp: 19 | type: firephp 20 | level: info 21 | -------------------------------------------------------------------------------- /app/config/config_prod.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: config.yml } 3 | 4 | monolog: 5 | handlers: 6 | main: 7 | type: fingers_crossed 8 | action_level: error 9 | handler: nested 10 | nested: 11 | type: stream 12 | path: %kernel.logs_dir%/%kernel.environment%.log 13 | level: debug 14 | -------------------------------------------------------------------------------- /app/config/config_test.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: config_dev.yml } 3 | 4 | parameters: 5 | phpcr_workspace: standard_test 6 | 7 | framework: 8 | test: ~ 9 | session: 10 | storage_id: session.storage.mock_file 11 | 12 | web_profiler: 13 | toolbar: false 14 | intercept_redirects: false 15 | -------------------------------------------------------------------------------- /app/config/parameters.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | database_driver: pdo_sqlite 3 | database_path: '%kernel.root_dir%/app.sqlite' 4 | phpcr_backend: 5 | type: doctrinedbal 6 | connection: default 7 | caches: 8 | meta: doctrine_cache.providers.phpcr_meta 9 | nodes: doctrine_cache.providers.phpcr_nodes 10 | parameters: 11 | jackalope.check_login_on_server: false 12 | phpcr_workspace: default 13 | phpcr_user: admin 14 | phpcr_pass: admin 15 | database_host: null 16 | database_port: null 17 | database_name: null 18 | database_user: null 19 | database_password: null 20 | -------------------------------------------------------------------------------- /app/config/routing.yml: -------------------------------------------------------------------------------- 1 | # Internal routing configuration to handle ESI 2 | #_internal: 3 | # resource: "@FrameworkBundle/Resources/config/routing/internal.xml" 4 | # prefix: /_internal 5 | 6 | sitemaps: 7 | prefix: /sitemaps 8 | resource: "@CmfSeoBundle/Resources/config/routing/sitemap.xml" 9 | -------------------------------------------------------------------------------- /app/config/routing_dev.yml: -------------------------------------------------------------------------------- 1 | _wdt: 2 | resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" 3 | prefix: /_wdt 4 | 5 | _profiler: 6 | resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" 7 | prefix: /_profiler 8 | 9 | _configurator: 10 | resource: "@SensioDistributionBundle/Resources/config/routing/webconfigurator.xml" 11 | prefix: /_configurator 12 | 13 | _main: 14 | resource: routing.yml 15 | -------------------------------------------------------------------------------- /app/config/services.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | cmf_simple_cms.persistence.phpcr.menu_provider.class: AppBundle\Menu\MenuProvider 3 | 4 | services: 5 | app.block.events: 6 | class: AppBundle\Block\EventsBlockService 7 | arguments: 8 | - app.block.events 9 | - '@templating' 10 | - null 11 | - '@doctrine_phpcr.odm.document_manager' 12 | - '@cmf_core.publish_workflow.checker' 13 | tags: 14 | - {name: "sonata.block"} 15 | cmf_simple_cms.initializer: 16 | class: AppBundle\Initializer\HomePageInitializer 17 | arguments: 18 | - '%cmf_simple_cms.persistence.phpcr.basepath%' 19 | - AppBundle\Document\SeoPage 20 | tags: 21 | - { name: doctrine_phpcr.initializer} 22 | cmf_seo.error.suggestion_provider.parent: 23 | class: Symfony\Cmf\Bundle\SeoBundle\Doctrine\Phpcr\ParentSuggestionProvider 24 | arguments: 25 | - '@doctrine_phpcr' 26 | - '%cmf_simple_cms.persistence.phpcr.basepath%' 27 | tags: 28 | - { name: 'cmf_seo.suggestion_provider', group: 'parent'} 29 | 30 | cmf_seo.error.suggestion_provider.sibling: 31 | class: Symfony\Cmf\Bundle\SeoBundle\Doctrine\Phpcr\SiblingSuggestionProvider 32 | arguments: 33 | - '@doctrine_phpcr' 34 | - '%cmf_simple_cms.persistence.phpcr.basepath%' 35 | tags: 36 | - { name: 'cmf_seo.suggestion_provider', group: 'sibling'} 37 | -------------------------------------------------------------------------------- /app/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV') ?: 'dev'); 18 | $debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(array('--no-debug', '')) && $env !== 'prod'; 19 | 20 | $kernel = new AppKernel($env, $debug); 21 | $application = new Application($kernel); 22 | $application->run($input); 23 | -------------------------------------------------------------------------------- /app/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | 17 | 18 | tests 19 | ../vendor/symfony-cmf/src 20 | ../src/*/*Bundle/Tests 21 | ../src/*/Bundle/*Bundle/Tests 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | ../src 34 | 35 | ../src/*/*Bundle/Resources 36 | ../src/*/*Bundle/Tests 37 | ../src/*/Bundle/*Bundle/Resources 38 | ../src/*/Bundle/*Bundle/Tests 39 | 40 | ../vendor/symfony-cmf/src 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/tests/StaticPageTest.php: -------------------------------------------------------------------------------- 1 | createClient(); 13 | 14 | $crawler = $client->request('GET', $url); 15 | 16 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); 17 | 18 | $this->assertCount(1, $crawler->filter(sprintf('h2:contains("%s")', $title)), 'Page does not contain an h2 tag with: '.$title); 19 | } 20 | 21 | public function contentDataProvider() 22 | { 23 | return array( 24 | array('/', 'The Symfony CMF Project'), 25 | array('/news', 'News'), 26 | array('/news/cmf-featured-on-symfony-com', 'Symfony CMF featured on symfony.com'), 27 | array('/get-started', 'Get started'), 28 | array('/get-involved', 'Get involved'), 29 | array('/about', 'About'), 30 | ); 31 | } 32 | 33 | public function testAboutShowsTableOfSponsors() 34 | { 35 | $client = $this->createClient(); 36 | $crawler = $client->request('GET', '/about'); 37 | $this->assertTrue($client->getResponse()->isSuccessful()); 38 | $this->assertEquals(1, $crawler->filter('table thead th:contains("Company")')->count()); 39 | $this->assertEquals(1, $crawler->filter('table tbody tr:contains("Liip AG")')->count()); 40 | } 41 | 42 | public function testGetInvolvedShowsALinkToGithubWiki() 43 | { 44 | $client = $this->createClient(); 45 | $crawler = $client->request('GET', '/get-involved'); 46 | $this->assertTrue($client->getResponse()->isSuccessful()); 47 | $this->assertEquals(1, $crawler->filter('a:contains("Github Wiki")')->count()); 48 | } 49 | 50 | public function testClickSiteTitleGoToHomepage() 51 | { 52 | $client = $this->createClient(); 53 | $crawler = $client->request('GET', '/get-started'); 54 | $this->assertTrue($client->getResponse()->isSuccessful()); 55 | $crawler = $client->click($crawler->filter('a[href="/"]')->link()); 56 | $this->assertCount(1, $crawler->filter(sprintf('h2:contains("%s")', 'The Symfony CMF Project'))); 57 | } 58 | 59 | public function testOnlyCurrentNavItemIsCurrent() 60 | { 61 | $client = $this->createClient(); 62 | $crawler = $client->request('GET', '/get-involved'); 63 | $this->assertTrue($client->getResponse()->isSuccessful()); 64 | $this->assertEquals(1, $crawler->filter('#nav li.current a:contains("Get Involved")')->count()); 65 | $this->assertEquals(0, $crawler->filter('#nav li.current a:contains("Home")')->count()); 66 | $this->assertEquals(0, $crawler->filter('#nav li.current a:contains("About")')->count()); 67 | } 68 | 69 | public function testRssFeed() 70 | { 71 | $client = $this->createClient(); 72 | $client->request('GET', '/news.rss'); 73 | $this->assertTrue($client->getResponse()->isSuccessful()); 74 | $this->assertContains('Symfony CMF featured on symfony.com', $client->getResponse()->getContent()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/tests/WebTestCase.php: -------------------------------------------------------------------------------- 1 | loadFixtures(array( 18 | 'AppBundle\DataFixtures\PHPCR\LoadStaticData', 19 | ), null, 'doctrine_phpcr'); 20 | 21 | self::$fixturesLoaded = true; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace AppBundle; 13 | 14 | use Symfony\Component\HttpKernel\Bundle\Bundle; 15 | 16 | class AppBundle extends Bundle 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /src/AppBundle/Block/EventsBlockService.php: -------------------------------------------------------------------------------- 1 | template = $template; 26 | } 27 | parent::__construct($name, $templating); 28 | 29 | $this->dm = $dm; 30 | $this->publishWorkflowChecker = $publishWorkflowChecker; 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function buildEditForm(FormMapper $form, BlockInterface $block) 37 | { 38 | throw new \RuntimeException('Not used at the moment, editing using a frontend or backend UI could be changed here'); 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | public function validateBlock(ErrorElement $errorElement, BlockInterface $block) 45 | { 46 | throw new \RuntimeException('Not used at the moment, validation for editing using a frontend or backend UI could be changed here'); 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function execute(BlockContextInterface $blockContext, Response $response = null) 53 | { 54 | if (!$response) { 55 | $response = new Response(); 56 | } 57 | 58 | if ($blockContext->getBlock()->getEnabled()) { 59 | $qb = $this->dm->createQueryBuilder(); 60 | 61 | $qb->from() 62 | ->document('AppBundle\Document\TalkBlock', 't') 63 | ->end() 64 | // TODO https://github.com/jackalope/jackalope-doctrine-dbal/issues/153 65 | // generates invalid sql-2 query: (t.publishEndDate > CAST('2013-11-11T10:19:42.000+01:00' AS DATE) 66 | // ->where() 67 | // ->gt()->field('t.publishEndDate')->literal(new \DateTime()) 68 | // ->end() 69 | ->orderBy()->desc()->field('t.publishEndDate') 70 | ; 71 | 72 | if ($maxItems = $blockContext->getSetting('maxItems')) { 73 | $qb->setMaxResults($maxItems); 74 | } 75 | 76 | // filter by published 77 | $talkBlocks = array(); 78 | foreach ($qb->getQuery()->execute() as $document) { 79 | if ($this->publishWorkflowChecker->isGranted(PublishWorkflowChecker::VIEW_ANONYMOUS_ATTRIBUTE, $document)) { 80 | $talkBlocks[] = $document; 81 | } 82 | } 83 | $talkBlocks = array_reverse($talkBlocks); 84 | 85 | $response = $this->renderResponse($blockContext->getTemplate(), array( 86 | 'block' => $blockContext->getBlock(), 87 | 'talkBlocks' => $talkBlocks, 88 | ), $response); 89 | } 90 | 91 | return $response; 92 | } 93 | 94 | /** 95 | * {@inheritdoc} 96 | */ 97 | public function setDefaultSettings(OptionsResolverInterface $resolver) 98 | { 99 | $resolver->setDefaults(array( 100 | 'template' => $this->template, 101 | 'maxItems' => 10, 102 | )); 103 | } 104 | 105 | /** 106 | * @param string $template 107 | */ 108 | public function setTemplate($template) 109 | { 110 | $this->template = $template; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/AppBundle/Composer/ScriptHandler.php: -------------------------------------------------------------------------------- 1 | getComposer()->getPackage()->getExtra(); 17 | 18 | $event->getIO()->write('Compiling slides'); 19 | 20 | if (!isset($extra['slippy-source-dir'])) { 21 | throw new \Exception('Do not run this script without defining slippy-source-dir pointing to the slides directory, relative to the project directory'); 22 | } 23 | $sourceDir = getcwd().'/'.$extra['slippy-source-dir']; 24 | if (!isset($extra['slippy-target-dir'])) { 25 | throw new \Exception('Do not run this script without defining slippy-target-dir, relative to the project directory'); 26 | } 27 | $targetDir = getcwd().'/'.$extra['slippy-target-dir']; 28 | 29 | if (!is_dir($sourceDir)) { 30 | throw new \Exception("$sourceDir not existing"); 31 | } 32 | if (!is_dir($targetDir)) { 33 | if (!mkdir($targetDir, 0755, true)) { 34 | throw new \Exception("Failed to create $targetDir, please check permissions"); 35 | } 36 | } 37 | $files = array(); 38 | foreach (scandir($sourceDir) as $sub) { 39 | $sub = "$sourceDir/$sub"; 40 | if (!substr_compare($sub, '.html', -5, 5)) { 41 | $files[] = $sub; 42 | } elseif (is_dir($sub) && strncmp($sub, '.', 1)) { 43 | foreach (scandir($sub) as $file) { 44 | if ('.' != $file && !substr_compare($file, '.html', -5, 5)) { 45 | $files[] = "$sub/$file"; 46 | } 47 | } 48 | } 49 | } 50 | if (empty($files)) { 51 | throw new \Exception("Found no slides in subfolders of $sourceDir"); 52 | } 53 | 54 | $pwd = getcwd(); 55 | chdir('vendor/seld/slippy/src'); 56 | $fs = new Filesystem(); 57 | 58 | foreach ($files as $file) { 59 | $event->getIO()->write("Slide $file"); 60 | // copy all source files into src so that index.php can find them 61 | foreach (glob(dirname($file).'/*.*') as $f) { 62 | $fs->copy($f, basename($f), true); 63 | } 64 | 65 | // make sure the target file does not exist 66 | $targetFile = $targetDir.'/'.basename($file); 67 | if ($fs->exists($targetFile)) { 68 | $fs->remove($targetFile); 69 | } 70 | 71 | exec("php index.php $file $targetFile", $output, $status); 72 | if ($status) { 73 | throw new \RuntimeException("Compiling failed with $status\n".implode("\n", $output)); 74 | } 75 | } 76 | chdir($pwd); 77 | $event->getIO()->write('All done'); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/AppBundle/DataFixtures/PHPCR/LoadStaticData.php: -------------------------------------------------------------------------------- 1 | parsedown = new Parsedown(); 27 | } 28 | 29 | public function getOrder() 30 | { 31 | return 5; 32 | } 33 | 34 | public function load(ObjectManager $manager) 35 | { 36 | $dataDir = __DIR__.'/../../Resources/data'; 37 | $session = $manager->getPhpcrSession(); 38 | $yaml = new Parser(); 39 | 40 | $basepath = $this->container->getParameter('cmf_simple_cms.persistence.phpcr.basepath'); 41 | NodeHelper::createPath($session, preg_replace('#/[^/]*$#', '', $basepath)); 42 | 43 | $data = $yaml->parse(file_get_contents($dataDir.'/page.yml')); 44 | foreach ($data['static'] as $overview) { 45 | $this->loadPage($manager, $basepath, $overview); 46 | } 47 | 48 | // load single pages 49 | $finder = new Finder(); 50 | $finder 51 | ->files() 52 | ->name('*.yml') 53 | ->in($dataDir.'/posts') 54 | ->sortByName() 55 | ; 56 | 57 | foreach ($finder as $pageFile) { 58 | $post = array_merge(array( 59 | 'parent' => '/news', 60 | 'label' => false, 61 | 'format' => 'markdown', 62 | 'template' => 'cms/news_detail.html.twig', 63 | ), $yaml->parse(file_get_contents($pageFile))); 64 | 65 | $this->loadPage($manager, $basepath, $post); 66 | } 67 | 68 | $data = $yaml->parse(file_get_contents(__DIR__.'/../../Resources/data/external.yml')); 69 | 70 | $basepath = $this->container->getParameter('cmf_core.persistence.phpcr.basepath'); 71 | $home = $manager->find(null, $basepath); 72 | 73 | foreach ($data['static'] as $name => $overview) { 74 | $item = new MenuNode(); 75 | $item->setName($name); 76 | $item->setLabel($overview['label']); 77 | $item->setUri($overview['uri']); 78 | $item->setParentDocument($home); 79 | $manager->persist($item); 80 | } 81 | 82 | $blocks = $yaml->parse(file_get_contents(__DIR__.'/../../Resources/data/block.yml')); 83 | $blockBasepath = $this->container->getParameter('cmf_block.persistence.phpcr.block_basepath'); 84 | NodeHelper::createPath($session, $blockBasepath); 85 | $blocksHome = $manager->find(null, $blockBasepath); 86 | 87 | foreach ($blocks['static'] as $name => $block) { 88 | $this->loadBlock($manager, $blocksHome, $name, $block); 89 | } 90 | 91 | $manager->flush(); 92 | } 93 | 94 | private function loadPage(ObjectManager $manager, $basepath, $pageData) 95 | { 96 | $class = isset($pageData['class']) ? $pageData['class'] : SeoPage::class; 97 | $format = isset($pageData['format']) ? $pageData['format'] : 'html'; 98 | 99 | $parent = (isset($pageData['parent']) ? trim($pageData['parent'], '/') : ''); 100 | $name = (isset($pageData['name']) ? trim($pageData['name'], '/') : ''); 101 | 102 | $path = $basepath 103 | .(empty($parent) ? '' : '/'.$parent) 104 | .(empty($name) ? '' : '/'.$name); 105 | 106 | $page = $manager->find($class, $path); 107 | if (!$page) { 108 | $page = new $class(); 109 | $page->setId($path); 110 | } 111 | 112 | if (isset($pageData['formats'])) { 113 | $page->setDefault('_format', reset($pageData['formats'])); 114 | $page->setRequirement('_format', implode('|', $pageData['formats'])); 115 | } 116 | 117 | if (!empty($pageData['template'])) { 118 | $page->setDefault(RouteObjectInterface::TEMPLATE_NAME, $pageData['template']); 119 | } 120 | 121 | if (!empty($pageData['controller'])) { 122 | $page->setDefault(RouteObjectInterface::CONTROLLER_NAME, $pageData['controller']); 123 | } 124 | 125 | if (!empty($pageData['options'])) { 126 | $page->setOptions($pageData['options']); 127 | } 128 | 129 | if (!empty($pageData['seo-metadata']) && $page instanceof SeoAwareInterface) { 130 | $seoMetadata = new SeoMetadata(); 131 | $seoMetadata->setMetaDescription( 132 | !empty($pageData['seo-metadata']['description']) ? $pageData['seo-metadata']['description'] : '' 133 | ); 134 | $seoMetadata->setMetaKeywords( 135 | !empty($pageData['seo-metadata']['keywords']) ? $pageData['seo-metadata']['keywords'] : '' 136 | ); 137 | $seoMetadata->setOriginalUrl( 138 | !empty($pageData['seo-metadata']['original-url']) ? $pageData['seo-metadata']['original-url'] : '' 139 | ); 140 | $page->setSeoMetadata($seoMetadata); 141 | } 142 | 143 | if ($page instanceof SitemapAwareInterface) { 144 | $page->setIsVisibleForSitemap(true); 145 | } 146 | 147 | $manager->persist($page); 148 | 149 | if (is_array($pageData['title'])) { 150 | foreach ($pageData['title'] as $locale => $title) { 151 | $page->setTitle($title); 152 | if (isset($pageData['label'][$locale]) && $pageData['label'][$locale]) { 153 | $page->setLabel($pageData['label'][$locale]); 154 | } elseif (!isset($pageData['label'][$locale])) { 155 | $page->setLabel($title); 156 | } 157 | 158 | $page->setBody($this->parseBody($pageData['body'][$locale], $format)); 159 | $manager->bindTranslation($page, $locale); 160 | } 161 | } else { 162 | $page->setTitle($pageData['title']); 163 | if (isset($pageData['label'])) { 164 | if ($pageData['label']) { 165 | $page->setLabel($pageData['label']); 166 | } 167 | } elseif (!isset($pageData['label'])) { 168 | $page->setLabel($pageData['title']); 169 | } 170 | $page->setBody($this->parseBody($pageData['body'], $format)); 171 | } 172 | 173 | if (isset($pageData['create_date'])) { 174 | $page->setCreateDate(date_create_from_format('U', strtotime($pageData['create_date']))); 175 | } 176 | 177 | if (isset($pageData['publish_start_date'])) { 178 | $page->setPublishStartDate(date_create_from_format('U', strtotime($pageData['publish_start_date']))); 179 | } 180 | 181 | if (isset($pageData['publish_end_date'])) { 182 | $page->setPublishEndDate(date_create_from_format('U', strtotime($pageData['publish_end_date']))); 183 | } 184 | } 185 | 186 | private function parseBody($body, $format) 187 | { 188 | switch ($format) { 189 | case 'html': 190 | return $body; 191 | case 'markdown': 192 | return $this->parsedown->text($body); 193 | default: 194 | throw new \InvalidArgumentException(sprintf( 195 | 'Unknown format "%s"', $format 196 | )); 197 | } 198 | } 199 | 200 | /** 201 | * Load a block from the fixtures and create / update the node. Recurse if there are children. 202 | * 203 | * @param ObjectManager $manager the document manager 204 | * @param string $parentPath the parent of the block 205 | * @param string $name the name of the block 206 | * @param array $block the block definition 207 | */ 208 | private function loadBlock(ObjectManager $manager, $parent, $name, $block) 209 | { 210 | $className = $block['class']; 211 | $document = $manager->find(null, $this->getIdentifier($manager, $parent).'/'.$name); 212 | $class = $manager->getClassMetadata($className); 213 | if ($document && get_class($document) != $className) { 214 | $manager->remove($document); 215 | $document = null; 216 | } 217 | if (!$document) { 218 | $document = $class->newInstance(); 219 | 220 | // $document needs to be an instance of BaseBlock ... 221 | $document->setParentDocument($parent); 222 | $document->setName($name); 223 | } 224 | 225 | if ($className == 'Symfony\Cmf\Bundle\BlockBundle\Doctrine\Phpcr\ReferenceBlock') { 226 | $referencedBlock = $manager->find(null, $block['referencedBlock']); 227 | if (null == $referencedBlock) { 228 | throw new \Exception('did not find '.$block['referencedBlock']); 229 | } 230 | $document->setReferencedBlock($referencedBlock); 231 | } elseif ($className == 'Symfony\Cmf\Bundle\BlockBundle\Doctrine\Phpcr\ActionBlock') { 232 | $document->setActionName($block['actionName']); 233 | } 234 | 235 | // set properties 236 | if (isset($block['properties'])) { 237 | foreach ($block['properties'] as $propName => $prop) { 238 | $class->reflFields[$propName]->setValue($document, $prop); 239 | } 240 | } 241 | 242 | $manager->persist($document); 243 | 244 | // create children 245 | if (isset($block['children'])) { 246 | foreach ($block['children'] as $childName => $child) { 247 | $this->loadBlock($manager, $document, $childName, $child); 248 | } 249 | } 250 | } 251 | 252 | private function getIdentifier($manager, $document) 253 | { 254 | $class = $manager->getClassMetadata(get_class($document)); 255 | 256 | return $class->getIdentifierValue($document); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/AppBundle/Document/EventsBlock.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class SeoPage extends Page implements SeoAwareInterface, SitemapAwareInterface 18 | { 19 | /** 20 | * @var SeoMetadata 21 | * 22 | * @PHPCR\Child 23 | */ 24 | protected $seoMetadata; 25 | 26 | /** 27 | * @var bool 28 | * 29 | * @PHPCR\Boolean(property="visible_for_sitemap") 30 | */ 31 | private $isVisibleForSitemap; 32 | 33 | public function __construct(array $options = array()) 34 | { 35 | parent::__construct($options); 36 | $this->seoMetadata = new SeoMetadata(); 37 | } 38 | 39 | /** 40 | * Gets the SEO metadata for this content. 41 | * 42 | * @return SeoMetadataInterface 43 | */ 44 | public function getSeoMetadata() 45 | { 46 | return $this->seoMetadata; 47 | } 48 | 49 | /** 50 | * Sets the SEO metadata for this content. 51 | * 52 | * This method is used by a listener, which converts the metadata to a 53 | * plain array in order to persist it and converts it back when the content 54 | * is fetched. 55 | * 56 | * @param array|SeoMetadataInterface $metadata 57 | */ 58 | public function setSeoMetadata($metadata) 59 | { 60 | $this->seoMetadata = $metadata; 61 | } 62 | 63 | /** 64 | * Decision whether a document should be visible 65 | * in sitemap or not. 66 | * 67 | * @param $sitemap 68 | * 69 | * @return bool 70 | */ 71 | public function isVisibleInSitemap($sitemap) 72 | { 73 | return $this->isVisibleForSitemap; 74 | } 75 | 76 | /** 77 | * @param bool $isVisibleForSitemap 78 | */ 79 | public function setIsVisibleForSitemap($isVisibleForSitemap) 80 | { 81 | $this->isVisibleForSitemap = $isVisibleForSitemap; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/AppBundle/Document/TalkBlock.php: -------------------------------------------------------------------------------- 1 | setSetting('template', 'block/block_talk.html.twig'); 24 | } 25 | 26 | /** 27 | * @param string $url 28 | */ 29 | public function setLink($url) 30 | { 31 | $this->link = $url; 32 | } 33 | 34 | /** 35 | * @return string|null 36 | */ 37 | public function getLink() 38 | { 39 | return $this->link; 40 | } 41 | 42 | /** 43 | * Set the date and time when the talk will start. 44 | * 45 | * @param \DateTime $date 46 | */ 47 | public function setWhen(\DateTime $date) 48 | { 49 | $this->publishEndDate = $date; 50 | } 51 | 52 | /** 53 | * Get the date and time when the talk will start. 54 | * 55 | * @return \DateTime|null 56 | */ 57 | public function getWhen() 58 | { 59 | return $this->publishEndDate; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/AppBundle/Initializer/HomePageInitializer.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class HomePageInitializer implements InitializerInterface 17 | { 18 | private $basePath; 19 | private $documentClass; 20 | 21 | public function __construct($basePath, $documentClass) 22 | { 23 | $this->basePath = $basePath; 24 | $this->documentClass = $documentClass; 25 | } 26 | 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public function init(ManagerRegistry $registry) 31 | { 32 | /** @var $dm DocumentManager */ 33 | $dm = $registry->getManagerForClass('AppBundle\Document\SeoPage'); 34 | if ($dm->find(null, $this->basePath)) { 35 | return; 36 | } 37 | 38 | $session = $dm->getPhpcrSession(); 39 | NodeHelper::createPath($session, PathHelper::getParentPath($this->basePath)); 40 | 41 | /** @var \AppBundle\Document\SeoPage $page */ 42 | $page = new $this->documentClass(); 43 | $page->setId($this->basePath); 44 | $page->setLabel('Home'); 45 | $page->setTitle('Homepage'); 46 | $page->setBody('Autocreated Homepage'); 47 | $page->setIsVisibleForSitemap(true); 48 | 49 | $dm->persist($page); 50 | $dm->flush(); 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public function getName() 57 | { 58 | return 'AppBundle Homepage'; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/AppBundle/Menu/MenuProvider.php: -------------------------------------------------------------------------------- 1 | factory); 18 | $item->setUri($menu->getUri()); 19 | $menu->addChild($item); 20 | $item->moveToFirstPosition(); 21 | } 22 | 23 | return $menu; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/data/block.yml: -------------------------------------------------------------------------------- 1 | static: 2 | talks: 3 | class: AppBundle\Document\EventsBlock 4 | # example-talk: 5 | # class: AppBundle\Document\TalkBlock 6 | # properties: 7 | # title: The event were the talk is given 8 | # publishEndDate: "2014-11-21 13:00" # when the talk is given, after this date and time it will be unpublished 9 | # link: http://www.event.com/my-talk 10 | # body: "The title of your talk and other information" 11 | forum-php-paris-2013: 12 | class: AppBundle\Document\TalkBlock 13 | properties: 14 | title: Forum PHP Paris 2013 15 | publishEndDate: "2013-11-21 16:45" 16 | link: http://www.afup.org/pages/forumphp2013/sessions.php#867 17 | body: "Symfony2 CMF - la gestion de contenu bas-niveau (in french)" 18 | symfonycon-2013-warszaw: 19 | class: AppBundle\Document\TalkBlock 20 | properties: 21 | title: SymfonyCon 2013 Warszaw 22 | publishEndDate: "2013-12-21 14:30" 23 | link: http://warsaw2013.symfony.com/speakers#session-959 24 | body: "Symfony2 Content Management in 40 minutes" 25 | php-benelux-2014: 26 | class: AppBundle\Document\TalkBlock 27 | properties: 28 | title: PHP Benelux 2014, Antwerp 29 | publishEndDate: "2014-01-24 09:00" 30 | link: http://conference.phpbenelux.eu/2014/sessions/#getting-started-content-management-in-symfony2 31 | body: "3 hour tutorial - Getting started with content management in Symfony2" 32 | drupalcamp-2013-vienna: 33 | class: AppBundle\Document\TalkBlock 34 | properties: 35 | title: DrupalCamp in Vienna 36 | publishEndDate: "2013-11-23 15:00" 37 | link: http://2013.drupalcamp.at/session/stone-masons-vs-3d-printer-cms-world 38 | body: "Stone masons vs. 3d printer in the cms world" 39 | 010php-2013-11-14-rotterdam: 40 | class: AppBundle\Document\TalkBlock 41 | properties: 42 | title: 010PHP User Group, Rotterdam 43 | publishEndDate: "2013-11-14 20:00" 44 | link: http://www.meetup.com/010PHP/events/143957872/ 45 | body: "Symfony CMF: A decoupled Content Management Framework" 46 | phpday-2015-verona: 47 | class: AppBundle\Document\TalkBlock 48 | properties: 49 | title: PHP Day Italy, Verona 50 | publishEndDate: "2015-05-14 09:00" 51 | link: http://2015.phpday.it/workshops/ 52 | body: "Symfony CMF - Workshop in Italian language!" 53 | dpc-2015-amsterdam: 54 | class: AppBundle\Document\TalkBlock 55 | properties: 56 | title: Dutch PHP Conference, Amsterdam 57 | publishEndDate: "2015-06-25 9:30" 58 | link: http://www.phpconference.nl/schedule 59 | body: "Symfony CMF Tutorial (3 hour workshop)" 60 | phpfrl-2015-leeuwarden: 61 | class: AppBundle\Document\TalkBlock 62 | properties: 63 | title: PHP.FRL Leeuwarden 64 | publishEndDate: "2015-06-23 19:00" 65 | link: http://www.meetup.com/PHP-FRL/events/222784422/ 66 | body: Symfony CMF talk at the PHP user group Friesland 67 | symfony-live-2015-berlin: 68 | class: AppBundle\Document\TalkBlock 69 | properties: 70 | title: SymfonyLive Berlin 2015 71 | publishEndDate: "2015-10-16 12:15" 72 | link: http://berlin2015.live.symfony.com/programm/#session-014 73 | body: "Sulu and the other guys - Wie Sulu mit anderen Applikationen zusammen spielt." 74 | symfony-usergroup-meetup-munich-2015-09-29: 75 | class: AppBundle\Document\TalkBlock 76 | properties: 77 | title: Symfony User Group Munich 78 | publishEndDate: "2015-09-29 18:30" 79 | link: http://www.meetup.com/de/sfugmunich/events/224029031/ 80 | body: Symfony CMF and Sulu talk at the Symfony user group Munich 81 | ipc-2015-talk-in-munic: 82 | class: AppBundle\Document\TalkBlock 83 | properties: 84 | title: IPC Talk 2015 by @ElectricMaxxx in munich 85 | publishEndDate: "2015-10-26 17:00" 86 | link: https://phpconference.com/2015/en/sessions/eine-symfony-application-um-cms-funktionen-erweitern 87 | body: | 88 | Maximilian Berghoff (@ElectricMaxxx) will give a talk about the CMF 89 | on the international php conference 2015 in munich. He will suggest the 90 | CMF as a way to serve CMS features to a given Symfony application. The 91 | talk will be in german only. 92 | sfu-june-2016-talk: 93 | class: AppBundle\Document\TalkBlock 94 | properties: 95 | title: Symfony Usergroup Talk by @ElectricMaxxx in Frankfurt 96 | publishEndDate: "2016-06-23 19:00" 97 | link: https://joind.in/event/sfugfra-iii2016/details 98 | body: | 99 | Maximilian Berghoff (@ElectricMaxxx) will give a talk about the CMF 100 | on symfony usergroup frankfurt on the 23th of june 3016. He will suggest the 101 | CMF as a way to serve CMS features to a given Symfony application. The 102 | talk will be in german only. 103 | froscon_talk_2016: 104 | class: AppBundle\Document\TalkBlock 105 | properties: 106 | title: FrOSCon Talk by @ElectricMaxxx 107 | publishEndDate: "2016-08-20 19:00" 108 | link: https://programm.froscon.de/2016/events/1897.html 109 | body: | 110 | Maximilian Berghoff (@ElectricMaxxx) will give a talk about the CMF 111 | on this years frOSCon in Bonn/Germany on the 20th of august 2016. He will suggest the 112 | CMF as a way to serve CMS features to a given Symfony application. The location of the 113 | conference is the university of Siegburg next to the former german capital Bonn. 114 | phpruhr-2016-talk: 115 | class: AppBundle\Document\TalkBlock 116 | properties: 117 | title: PHPRUHR 2016 Talk by @Electricmaxx 118 | publishEndDate: "2016-11-10 19:00" 119 | link: https://2016.php.ruhr/session/extending-a-symfony-application-by-cms-features/ 120 | body: | 121 | Maximilian Berghoff (@ElectricMaxxx) will give a talk about the CMF 122 | at the PHPRUHR conference on the 10th of november 2016 in Dortmund. 123 | He will suggest the CMF as a way to serve CMS features to a given Symfony application. 124 | never-code-alone-2017-live-coding: 125 | class: AppBundle\Document\TalkBlock 126 | properties: 127 | title: Live Coding - NeverCodeAlon Event by @ElectricMaxxx 128 | publishEndDate: "2017-09-24 19:00" 129 | link: https://blog.nevercodealone.de/moveelevator-never-code-alone-event/ 130 | body: | 131 | Maximilian Berghoff (@ElectricMaxxx) will give a indroduction into the world of Symfony CMF by a 132 | live coding session at the next NeverCodeAlone Event on the 24th of september 2017. 133 | It is free and you will see how to extend a brand new Symfony application with CMS features 134 | like routing, structuring and seo. So if you live next to Oberhausen (Germany) 135 | grab one of the last free tickets and join my session. 136 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/data/external.yml: -------------------------------------------------------------------------------- 1 | static: 2 | docs: 3 | label: "Docs" 4 | uri: "http://symfony.com/doc/master/cmf/index.html" 5 | wiki: 6 | label: "Wiki" 7 | uri: "http://wiki.github.com/symfony-cmf/symfony-cmf" 8 | demo: 9 | label: "Sandbox" 10 | uri: "http://sandbox.cmf.symfony.com/" -------------------------------------------------------------------------------- /src/AppBundle/Resources/data/posts/2015-02-01.yml: -------------------------------------------------------------------------------- 1 | name: "cmf-news-january-2015" 2 | title: "CMF News January 2015" 3 | publish_start_date: "2015-02-01" 4 | body: | 5 | Its quite an exciting time for the CMF, there is talk about the next 6 | generation of Jackalope, the Resource stack, rewriting the 7 | TreeBrowserBundle and true multi-site support. 8 | 9 | We also had a CMF hackday in December at the Mayflower offices in Würzburg, Germany and we are 10 | hoping to have another one in the coming months. If you are interested make your self known 11 | on the [CMF Mailinglist](https://groups.google.com/forum/#!forum/symfony-cmf-devs), otherwise 12 | we will announce it on the [@SymfonyCMF](http://twitter.com/SymfonyCMF) Twitter account. 13 | 14 |
    15 | 16 |
    CMF hackday in Würzburg, Germany
    17 |
    18 | 19 | ### Jackalope 2.0 20 | 21 | It has been generally agreed that the current Jackalope implementation 22 | could be improved upon, and to this end the architecture should be adapted. 23 | 24 | The current Jackalope implementation was originally intended only to be a 25 | client for Apache Jackrabbit, the `doctrine-dbal` implementation was an 26 | afterthought. 27 | 28 | Jackalope will be refactored with the aim of providing a modular framework which 29 | will support multiple native persistence backends (Doctrine DBAL, filesystem, MongoDB) in 30 | addition to support for Jackrabbit. 31 | 32 | Modules will also be provided for versioning and search. For versioning there will be support 33 | for GIT, database storage and Jackrabbit. Search modules will initially be provided for 34 | Elastic Search and Zend Lucene. 35 | 36 | Work will probably begin at sometime in the second quarter of this year. 37 | 38 | ### Resources 39 | 40 | Resources are a new and experimental concept in the CMF. Resources are a 41 | common interface to disparate entities within the system. For example a 42 | resource could represent a file on the local filesystem, a Doctrine Entity 43 | or a PHPCR Node. 44 | 45 | The resource stack is built upon [Puli](https://github.com/puli/repository). 46 | 47 | Two of the greatest apparent benefits will be nice ways to support multiple 48 | sites and dynamic theming. 49 | 50 | See [this blog 51 | post](http://www.dantleech.com/post/2015/01/18/cmf-resource-s-and-puli-and-why-they-are-good) 52 | for more information. 53 | 54 | ### TreeBrowserBundle 55 | 56 | [WouterJ](https://twitter.com/WouterJNL) has been working on a new tree 57 | browser implementation for the 58 | [TreeBrowserBundle](https://github.com/symfony-cmf/TreeBrowserBundle). 59 | 60 | - The trees will be implemented as pure JS adapters to existing libraries. 61 | - Data will be retrieved from the 62 | [ResourceRestBundle](https://github.com/symfony-cmf/ResourceRestBundle). 63 | - The tree packages will be kept in a javascript package manager (e.g. 64 | [bower](http://bower.io)). 65 | 66 | As a result the code in the TreeBrowserBundle will be limited to 67 | bootstrapping the adapters. 68 | 69 | Integration with the resource API will also enable users to freely mix both 70 | Doctrine ORM resources and PHPCR-ODM resources in the same tree. 71 | 72 | ### SiteContextBundle 73 | 74 | [dantleech](https://twitter.com/dantleech) recently started working on a new SiteContextBundle. 75 | 76 | All CMS's that support multiple sites will undoubtably have some "site manager" service 77 | which listens to the incoming Request and determines which Site should be "loaded". This decision 78 | will normally be based upon the hostname. 79 | 80 | The SiteContextBundle will aim to provide a CMF solution to this problem and also provide an 81 | integration with the SonataAdminBundle. 82 | 83 | This bundle will provide the contextual information required by the resource stack to determine where 84 | to locate a sites resources (e.g. the routes, content, blocks, etc.). 85 | 86 | ### Other news 87 | 88 | [Beta version](http://www.dantleech.com/post/2015/01/05/phpcrsh-beta) of PHPCR Shell released 89 | 90 |
    91 | 92 |
    PHPCR Shell Beta 1
    93 |
    94 | 95 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/data/posts/2015-03-20.yml: -------------------------------------------------------------------------------- 1 | name: "cmf-news-march-2015" 2 | title: "CMF News March 2015" 3 | publish_start_date: "2015-03-20" 4 | body: | 5 | Main efforts last months focus on TreeBrowserBundle 2.0 and the related Sonata 6 | refactoring, and the ResourceBundle. 7 | 8 | Jackalope should see release 1.2 very soon, currently we are aware of only 9 | [one issue](https://github.com/jackalope/jackalope-doctrine-dbal/pull/223) 10 | that needs to be fixed first. 11 | 12 | If you use Twitter, you might want to follow 13 | [https://twitter.com/SymfonyCMF](@SymfonyCMF) - we will tweet important news 14 | about the Symfony CMF on that account. 15 | 16 | ### Events 17 | 18 | #### May Hackday 19 | 20 | Come to Dornbirn, Austria for a joint Symfony CMF / Sulu hackday on 1/2 of 21 | May! More details will be announced soon. Follow 22 | [https://twitter.com/SymfonyCMF](@SymfonyCMF) on twitter or subscribe to the 23 | [https://groups.google.com/forum/#!forum/symfony-cmf-users](symfony-cmf-users) 24 | google group to not miss the announcement. 25 | 26 | #### Conference Presence 27 | 28 | Manuel Baldassarri does a CMF tutorial **in Italian language** at 29 | [PHPDay Italy](http://2015.phpday.it/workshops/) in Italy, on 14th of May. 30 | 31 | David Buchmann gives a CMF tutorial at the [Dutch PHP Conference](http://www.phpconference.nl/) 32 | in Amsterdam, end of June. 33 | 34 | David will also be present at [Symfony Live Paris](http://paris2015.live.symfony.com/) on the 9. 35 | of April. There is no talk about the CMF scheduled but of course David is always happy to discuss 36 | the CMF if you approach him. 37 | 38 | ### Development updates 39 | 40 | #### Decoupling Sonata Admin 41 | 42 | We are discussing about moving the [Sonata PHPCR-ODM admin classes into its 43 | own bundle](https://github.com/symfony-cmf/symfony-cmf/issues/216). 44 | 45 | This will decouple the the CMF Bundles from the Sonata Admin and clear the 46 | way for integrating other admin systems such as 47 | [ng-admin](https://github.com/marmelab/ng-admin), 48 | [EasyAdmin](https://github.com/javiereguiluz/EasyAdminBundle) etc. 49 | 50 | #### Routing Auto 51 | 52 | The RoutingAuto component now supports [DI container 53 | parameters](https://github.com/symfony-cmf/RoutingAutoBundle/pull/153) to be 54 | used as tokens and a 55 | [PR](https://github.com/symfony-cmf/RoutingAutoBundle/pull/150) will soon be 56 | merged allowing users to register their own adapters. 57 | 58 | Also the URI generator now does some basic validation on generated routes. 59 | 60 | #### PHPCR-ODM 61 | 62 | PHPCR-ODM now supports a new "depth" mapping which allows you to map the depth of the underlying 63 | PHPCR node to a property in the document. 64 | 65 | It is also now possible to specify a custom locale chooser and configure the namespace alias used 66 | for translations. 67 | 68 | Support was also added to the bundle for [configuring 69 | Memcache](https://github.com/doctrine/DoctrinePHPCRBundle/pull/184/files). 70 | 71 | #### PHPCR Shell 72 | 73 | Another Beta version of PHPCR-Shell will shortly be released, most notably it 74 | now supports updating fields with expressions in queries, enabling you to 75 | update fields based on other fields within the node for example. 76 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/data/posts/2015-04-01.yml: -------------------------------------------------------------------------------- 1 | name: "cmf-hackday-may-2015" 2 | title: "CMF Hackday 1st/2nd May 2015" 3 | publish_start_date: "2015-04-02" 4 | body: | 5 | We are happy to announce a joint [Symfony CMF](http://cmf.symfony.com) / 6 | [Sulu](http://sulu.io) hackday in Dornbirn, Austria on the 1st and 2nd of 7 | May. Join us for a hacking, discussion and fun. In case there is interest 8 | workshops will be available for Symfony CMF, Sulu or both. 9 | 10 | If you would like to come, please **[register 11 | here](https://docs.google.com/forms/d/16nd-hHJ140RtE-MQyC1FcEMYwPAihbdB6kzhU1y5R2c/viewform?usp=send_form)**, 12 | if for some reason you cannot complete the google form, send a mail to our 13 | [mailing list](https://groups.google.com/forum/#!forum/symfony-cmf-devs). 14 | 15 | ### Schedule 16 | 17 | #### Friday 1st May 18 | 19 | - **09:00** - Breakfast 20 | - **09:30** - *workshop* / *talk* / *discussion* / *hacking* 21 | - **12:00** - Lunch 22 | - **12:30** - *workshop* / *talk* / *discussion* / *hacking* 23 | - **18:00** - Mountain dinner at the [Karren](http://www.karren.at/?id=1800) 24 | 25 | #### Saturday 2nd May 26 | 27 | - **09:00** - Breakfast 28 | - **09:30** - *workshop* / *talk* / *discussion* / *hacking* 29 | - **12:00** - Lunch 30 | - **12:30** - *workshop* / *talk* / *discussion* / *hacking* 31 | 32 | ### Getting there 33 | 34 | The hackday will take place in the [Massive Art](http://www.massiveart.com/de/kontakt/) office: 35 | 36 | 39 | 40 | #### From an airport 41 | 42 | Follow the instructions on how to get to Dornbirn train station from one of 43 | the closer airports on the [homepage of the local 44 | university](http://www.fhv.at/fhv-campus/location-directions/directions-nearby-airports?set_language=en). 45 | 46 | #### From Dornbirn train station 47 | 48 | From the train station you can take a bus: 49 | 50 | - Bus line 9 to Steinebach (walk 1 minute) 51 | - Bus line 8 to Dornbirn Oberdorf (walk 13 minutes) 52 | - Bus line 38 to Gasthaus Engel (walk 10 minutes) 53 | 54 | Or it is a 30 minute walk, or we can collect people from the train station if 55 | necessary, contact us via. one of the methods below. 56 | 57 | ### Accomodation 58 | 59 | - [Harrys Home](http://www.harrys-home.com/), Hotel, Dornbirn 75,50 Euro inc. breakfast 60 | - [Junges Hotel Hard](http://www.jugendherberge-hard.at/index.php?page=page&id=6) - Jugendherberge, Hard 33 Euro per night, inc. breakfast 61 | - [Jufa Bregenz](https://www.jufa.eu/jufa-bregenz-am-bodensee/zimmerausstattung/)- Hotel, Bregenz 61,40 Euro per night 62 | - [Motel Hohenems](http://www.motel-hohenems.at/home.html) 95 Euro per night 63 | - [Airbnb](https://www.airbnb.at/s/Dornbirn?cdn_spdy=1&checkin=30.04.2015&checkout=01.05.2015&source=bb&ss_id=6u9431bx) Rooms from 30 Euro 64 | - [Couchsurfing](https://www.couchsurfing.com) 65 | 66 | In addition there are couches available from the MassiveArt team, let us know. 67 | 68 | ### Contact 69 | 70 | - Twitter: [@SymfonyCMF](https://twitter.com/symfonycmf) 71 | - CMF Mailing List: symfony-cmf-devs@googlegroups.com 72 | - MassiveArt website: [http://www.massiveart.com/de/kontakt](http://www.massiveart.com/de/kontakt) 73 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/data/posts/2015-05-02.yml: -------------------------------------------------------------------------------- 1 | name: "jackalope-1-2" 2 | title: "Jackalope 1.2" 3 | publish_start_date: "2015-05-02" 4 | body: | 5 | One of the result of the May [hackday](http://cmf.symfony.com/news/cmf-hackday-may-2015) 6 | was that we finally released [Jackalope 1.2](https://github.com/jackalope/jackalope/releases/tag/1.2.0) 7 | including new releases of the [Jackrabbit](https://github.com/jackalope/jackalope-jackrabbit/releases/tag/1.2.0) 8 | and [Doctrine DBAL](https://github.com/jackalope/jackalope-doctrine-dbal/releases/tag/1.2.0) transports. 9 | 10 | All users should see performance and reliability improvements. This is especially true for users 11 | of the Doctrine DBAL transport where performance should have made a significant jump forward. 12 | Note that in order to use Doctrine DBAL transport in the new version you will need to update 13 | your database schema. Aside from this we also improved the maintainability and code-reuse 14 | between the transports. Of course we also always expand the test suite when we do any work 15 | on the code base. As we did fix several edge cases, it is recommend to review the CHANGELOG.md 16 | and UPGRADE.md when encountering issues after the update. 17 | 18 | At the hackday we also discussed about a more radical refactoring of Jackalope to create a 19 | [version 2 of Jackalope](https://github.com/jackalope2) that will be more modular and have 20 | a cleaner architecture, allowing better code reuse and performance. Adding new storage or 21 | search solutions should become really simple. 22 | 23 | We started drafting the concept in the [Jackalope2 wiki](https://github.com/Jackalope2/jackalope/wiki). 24 | If you want to get involved, please get in contact with us on the 25 | [mailinglist](https://groups.google.com/forum/#!forum/jackalope-dev) or in the Jackalope2 issue 26 | tracker on github. 27 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/data/posts/2015-12-25.yml: -------------------------------------------------------------------------------- 1 | name: "symfony-cmf-status-and-phpcr-odm-1-3" 2 | title: "Symfony CMF status update and PHPCR ODM 1.3 release" 3 | publish_start_date: "2015-12-25" 4 | body: | 5 | Progress has not been as steady as we had hoped this year. Behind the scenes we have added several 6 | tweaks and improvements to the various Bundles and components and are working on Symfony 2.8 7 | and 3.0 support. Its not yet entirely clear if we will manage to support 3.0 right away though 8 | as it might be more feasible for us to do this only in new major versions which will require 2.8+. 9 | However for now we want to maintain compatibility with Symfony 2.3+ if possible until next spring. 10 | What is becoming clear however is that PHP 5.3 support is no longer a hard requirement for us. We 11 | will simply keep PHP 5.3 compatibility as a best as we can but will switch to PHP 5.4+ if we run 12 | into any tricky issues with 5.3. 13 | 14 | That being said, we also wanted to share the news that PHPCR ODM and DoctrinePHPCRBundle 1.3 have 15 | been released. The most important improvements are Symfony 3.0 compatibility while maintaining 16 | full compatibility with Symfony 2.3. Furthermore PHP 7.0 is fully supported. However this 17 | required a significant change: "Deprecated Binary, Boolean, Date, Decimal, Double, Float, Int, 18 | Long, Name, Path, String and Uri annotations in favor of `@Field(type="...")`." This means in 19 | order to make code compatible with PHP 7.0, one needs to switch all field annotation mappings 20 | to this new syntax as otherwise one will receive a fatal error as "string" etc are now resvered 21 | keywords. 22 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/data/posts/2016-02-10.yml: -------------------------------------------------------------------------------- 1 | name: "first-1-3-release-candidate" 2 | title: "First Symfony CMF 1.3 Release Candidate Available" 3 | publish_start_date: "2016-02-10" 4 | body: | 5 | The release of Symfony CMF 1.3 is coming closer with the availability of 6 | release candidates for all packages. Stable releases can be expected in the 7 | coming weeks. 8 | 9 | After the 1.2 release, the activity of the Symfony CMF project has dropped, 10 | resulting in no new release in 2015. With this new release, the CMF is back 11 | in the running with full Symfony 2.8 and PHP 7 support. 12 | 13 | ### Important new Features 14 | 15 | Included in this release are the following packages: 16 | 17 | * Version 1.0 of SlugifierApi; 18 | * Version 1.1 of RoutingAutoBundle and RoutingAuto; 19 | * Version 1.2 of SearchBundle, SeoBundle and TreeBrowserBundle; 20 | * Version 1.3 of BlockBundle, CoreBundle, ContentBundle, MediaBundle, 21 | SimpleCmsBundle, Testing; 22 | * Version 1.4 of RoutingBundle and Routing; 23 | * Version 2.0 of MenuBundle; 24 | 25 | The most important features included in these new versions are: 26 | 27 | * PHP 7 support for all CMF packages; 28 | * The Routing and Testing components support Symfony 3; 29 | * The CmfMenuBundle added KnpMenu 2 support and dropped support for KnpMenu 1; 30 | * The CmfSeoBundle added support for generating sitemaps and an exception 31 | controller for better error pages; 32 | * The CmfMediaBundle added a `cmf_media_file` form type to upload simple files; 33 | * Improved admin experience for most Sonata Admin classes, including integration 34 | of the [SonataTranslationBundle](https://sonata-project.org/bundles/translation/master/doc/index.html) 35 | for multi-language documents; 36 | * A ``symfony-cmf/slugifier-api`` package was introduced, containing a 37 | generic sluggifier interface to be used when a sluggifier is needed; 38 | * Integration of CKeditor in the CmfSimpleCmsBundle and CmfContentBundle, 39 | providing the admin with better tooling to edit page contents. 40 | 41 | You can read about all new features [master version](http://symfony.com/doc/master/cmf) 42 | of the documentation. 43 | 44 | Please update your project and to test the release candidates. This will 45 | help making the 1.3 release more stable. 46 | 47 | ### The Future 48 | 49 | Symfony CMF 1.3 will be the last release in the 1.x series. After this 50 | release, work will start on Symfony CMF 2.0. This version targets Symfony 51 | 2.8 and 3.0+. In 1.3, most deprecated features already trigger deprecation 52 | notices. However, this does not mean the upgrade to 2.0 will not contain 53 | more breaking changes. We do our best to reduce the breaking changes as 54 | much as possible. 55 | 56 | For 2.0, we will also try to reduce the number of bundles included in the 57 | CMF so that we can focus on the important parts. A first thing we already 58 | deprecated is the BlogBundle that never saw a stable release. 59 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/data/posts/2016-04-04.yml: -------------------------------------------------------------------------------- 1 | name: "release-1-3" 2 | title: "Symfony CMF 1.3 Released" 3 | publish_start_date: "2016-04-04" 4 | body: | 5 | We released new stable versions of all CMF bundles, the standard edition 6 | and the sandbox. Most importantly, this release brings full support for 7 | up to Symfony 2.8 and PHP 7 support. 8 | 9 | ### Released Versions 10 | 11 | Included in this release are the following packages: 12 | 13 | * Version 1.0 of SlugifierApi; 14 | * Version 1.1 of RoutingAutoBundle and RoutingAuto; 15 | * Version 1.2 of SearchBundle, SeoBundle and TreeBrowserBundle; 16 | * Version 1.3 of BlockBundle, CoreBundle, ContentBundle, MediaBundle, 17 | SimpleCmsBundle, Testing; 18 | * Version 1.4 of RoutingBundle and Routing; 19 | * Version 2.0 of MenuBundle; 20 | 21 | The version numbers for each bundle are tracked separately, which explains 22 | why not all of them are synchronized. 23 | 24 | ### Important new Features 25 | 26 | The most important features included in these new versions are: 27 | 28 | * PHP 7 support for all CMF packages; 29 | * The Routing and Testing components support Symfony 3; 30 | * The CmfMenuBundle added KnpMenu 2 support and dropped support for KnpMenu 1; 31 | * The CmfSeoBundle added support for generating sitemaps and an exception 32 | controller for better error pages; 33 | * The CmfMediaBundle added a `cmf_media_file` form type to upload simple files; 34 | * Improved admin experience for most Sonata Admin classes, including integration 35 | of the [SonataTranslationBundle](https://sonata-project.org/bundles/translation/master/doc/index.html) 36 | for multi-language documents; 37 | * A ``symfony-cmf/slugifier-api`` package was introduced, containing a 38 | generic sluggifier interface to be used when a sluggifier is needed; 39 | * Integration of CKeditor in the CmfSimpleCmsBundle and CmfContentBundle, 40 | providing the admin with better tooling to edit page contents. 41 | 42 | You can read about all new features [master version](http://symfony.com/doc/master/cmf) 43 | of the documentation. 44 | 45 | ### The Future 46 | 47 | Symfony CMF 1.3 will be the last release in the 1.x series. After this 48 | release, work will start on Symfony CMF 2.0. This version targets Symfony 49 | 2.8 and 3.0+. In 1.3, most deprecated features already trigger deprecation 50 | notices. However, this does not mean the upgrade to 2.0 will not contain 51 | more breaking changes. We do our best to reduce the breaking changes as 52 | much as possible. 53 | 54 | For 2.0, we will also try to reduce the number of bundles included in the 55 | CMF so that we can focus on the important parts. A first thing we already 56 | deprecated is the BlogBundle that never saw a stable release. 57 | 58 | A rough plan for 2.0 is in the [project wiki](https://github.com/symfony-cmf/symfony-cmf/wiki/Release-2.0). 59 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/data/posts/2017-01-21.yml: -------------------------------------------------------------------------------- 1 | name: "release-2.0.0-RC1" 2 | title: "Symfony CMF 2.0.0 First Release Candidate" 3 | publish_start_date: "2017-01-21" 4 | body: | 5 | We are getting ready with the CMF 2.0 release! We are in the process of 6 | tagging release candidates and hope to gather feedback in the next weeks, 7 | before tagging stable versions. The most important feature is support for 8 | Symfony 3. 9 | 10 | We've also created a `#symfony_cmf` channel on the 11 | [Symfony Slack](https://slackinvite.me/to/symfony-devs) to improve 12 | collaboration and user support. Move in there, report an error or give us a 13 | high five! 14 | 15 | ### What is new? 16 | 17 | The upcoming versions will support Symfony 3.0 and higher, as well as 18 | the 2.8 LTS version. They require PHP 5.6 or 7. Deprecated code from the 1.x 19 | versions has been removed. 20 | 21 | If you use the Sonata Admin in your project, note that we moved the admins 22 | out of the individual bundles into the CmfSonataPhpcrAdminIntegrationBundle. 23 | 24 | MenuBundle will be released as 2.1, because there already was a 2.0. Note 25 | that the sonata admin classes for the menu bundle were also moved to the 26 | separate admin integration bundle. 27 | 28 | Then, some of the bundles also have new features: 29 | 30 | * RoutingAuto supports multiple routes per document, e.g. to create REST APIs. 31 | * TreeBrowserBundle got completely rebuilt, providing a much nicer tree 32 | with extensibility and better JavaScript libraries. It now also provides 33 | a form type for the tree widget. 34 | * SeoBundle supports guessing the modified date and depth from the content. 35 | 36 | Alongside this, three new packages have been released: Resource, ResourceBundle 37 | and ResourceRestBundle. These packages provide a PHPCR and Doctrine PHPCR 38 | ODM repository for [Puli](http://puli.io/). This way, you can access your 39 | documents like they are files. As Puli is not yet released stable, the 40 | bundles use a very limited set of Puli classes included in the Resource 41 | component. These packages will contain compatibility breaks when Puli is stable. 42 | 43 | ### Some Packages no Longer Maintained 44 | 45 | To focus our resources, the CMF core team decided to stop maintaining some 46 | packages for the time being. While they all have their merit, we just don't 47 | have enough time to work on all that has been created over the last five 48 | years. Rather than having badly maintained packages confuse users, we 49 | want to be transparent. 50 | 51 | The affected bundles are: CreateBundle, MediaBundle, SearchBundle and 52 | SimpleCmsBundle. 53 | 54 | If you want to help co-maintaining any of those packages, tell us in a 55 | GitHub issue or in `#symfony_cmf` of [Symfony slack](https://slackinvite.me/to/symfony-devs). 56 | 57 | ### How to upgrade? 58 | 59 | A lot of deprecated code was removed. Before upgrading, run your tests and 60 | check for deprecation warnings in the test log. 61 | 62 | Each repository contains a `UPGRADE-2.0.md` file that should explain the 63 | breaking changes when migrating to version 2. The instructions can be 64 | incomplete at this point. If you have time to test the migration, please 65 | reach out to us on GitHub or Slack if you find inaccuracies or information 66 | is missing. 67 | 68 | ### Documentation, Documentation, Documentation! 69 | 70 | Quite a bit of the changes are not yet reflected in the 71 | [documentation](https://symfony.com/doc/master/cmf/). This will prove the 72 | largest part of the remaining work. 73 | 74 | You can become a new CMF hero by helping us with this task. Just a quick 75 | proof read of open pull requests or the documentation will already be very 76 | great! 77 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/data/posts/2017-08-23.yml: -------------------------------------------------------------------------------- 1 | name: "release-2.0.0-stable" 2 | title: "Symfony CMF 2.0.0 Stable Release" 3 | publish_start_date: "2017-08-23" 4 | body: | 5 | Over the summer, we have been tagging the 2.0.0 releases of the Symfony 6 | CMF components and bundles. This week, we managed to wrap up the last 7 | bits and pieces and tagged 2.0.0 of all parts of the CMF. 8 | 9 | CMF version 2 runs on PHP 5.6 and 7, and is compatible with Symfony 2.8 LTS 10 | and all maintained versions of Symfony 3. The sonata admin integration has 11 | been moved into its own separate bundle to not clutter up the bundles with 12 | an optional integration. We created a resource component inspired by puli. 13 | The tree browser bundle was rebuilt on top of the new resource 14 | infrastructure, and uses new Javascript libraries. Sonata PHPCR-ODM Admin 15 | has also been updated to use the new tree bundle and been released as 16 | version 2.0.0. 17 | 18 | For a detailed list of changes, see the [CMF 2 RC1 blogpost](release-2.0.0-RC1) 19 | and the changelog of each component. 20 | 21 | As already mentioned in the RC blogpost, we had to abandon some packages to 22 | focus our efforts. CreateBundle, MediaBundle, SearchBundle and 23 | SimpleCmsBundle are currently not maintained. If you want to help 24 | co-maintaining any of those packages, tell us in a GitHub issue or in 25 | `#symfony_cmf` of [Symfony slack](https://slackinvite.me/to/symfony-devs). 26 | 27 | ### How to upgrade? 28 | 29 | A lot of deprecated code was removed. Before upgrading, run your tests and 30 | check for deprecation warnings in the test log. 31 | 32 | Each repository contains a `UPGRADE-2.0.md` file that should explain the 33 | breaking changes when migrating to version 2. The instructions can be 34 | incomplete at this point. If you have time to test the migration, please 35 | reach out to us on GitHub or Slack if you find inaccuracies or information 36 | is missing. 37 | 38 | ### Next steps 39 | 40 | We are happy with the current release, but there is of course always more 41 | that could be done. When Symfony 4 starts to take shape, we will have to 42 | make sure all components are compatible with it. 43 | 44 | For CMF 2.1, there is a [tentative roadmap in the CMF wiki](https://github.com/symfony-cmf/symfony-cmf/wiki/[Proposal]-Roadmap-2.1). 45 | Another topic we discuss is whether to use a [mono repository](https://github.com/symfony-cmf/symfony-cmf/issues/248) 46 | aproach or a tool to [synchronize files into all our repositories](https://github.com/symfony-cmf/symfony-cmf/issues/246), 47 | similar to the Sonata dev-kit. 48 | 49 | If you want to get involved, comment on the github issues or join us at 50 | `#symfony_cmf` in the [Symfony slack](https://slackinvite.me/to/symfony-devs). 51 | -------------------------------------------------------------------------------- /web/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine On 3 | 4 | # 5 | # RewriteBase / 6 | # 7 | 8 | RewriteCond %{REQUEST_FILENAME} !-f 9 | RewriteRule ^(.*)$ app.php [QSA,L] 10 | 11 | -------------------------------------------------------------------------------- /web/app.php: -------------------------------------------------------------------------------- 1 | loadClassCache(); 11 | //$kernel = new AppCache($kernel); 12 | $request = Request::createFromGlobals(); 13 | $response = $kernel->handle($request); 14 | $response->send(); 15 | $kernel->terminate($request, $response); 16 | -------------------------------------------------------------------------------- /web/app_dev.php: -------------------------------------------------------------------------------- 1 | loadClassCache(); 27 | $request = Request::createFromGlobals(); 28 | $response = $kernel->handle($request); 29 | $response->send(); 30 | $kernel->terminate($request, $response); 31 | -------------------------------------------------------------------------------- /web/css/style.css: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------- 2 | Template Name : VectorLover 3 | Template Code : S-0026 4 | Version : 1.1 5 | Author : Erwin Aligam 6 | Author URI : http://www.styleshout.com/ 7 | Last Date Modified : January 17, 2010 8 | ------------------------------------------------ */ 9 | 10 | /* ---------------------------------------------- 11 | HTML ELEMENTS 12 | ------------------------------------------------- */ 13 | 14 | /* Top Elements */ 15 | * { margin: 0; padding: 0; outline: 0 } 16 | 17 | body { 18 | background: #CCC url(../images/bg.gif) repeat-x; 19 | font: 12px/170% 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; 20 | color: #6B6B6B; 21 | text-align: center; 22 | } 23 | 24 | /* links */ 25 | a, a:visited { 26 | color: #60aa2e; 27 | text-decoration: none; 28 | } 29 | a:hover { 30 | color: #73471B; 31 | text-decoration: underline; 32 | } 33 | 34 | /* headers */ 35 | h1, h2, h3 { 36 | font-family: 'Trebuchet MS', 'Helvetica Neue', Arial, Sans-serif; 37 | font-weight: Bold; 38 | padding: 10px; 39 | color: #444; 40 | } 41 | h1 { 42 | font-size: 2.9em; 43 | } 44 | h2 { 45 | font-size: 2.5em; 46 | color: #88AC0B; 47 | } 48 | h3 { 49 | font-size: 1.8em; 50 | font-weight: normal; 51 | padding: 16px 10px 4px 10px; 52 | } 53 | h4 { 54 | font-size: 1.6em; 55 | font-weight: normal; 56 | padding: 16px 10px 4px 10px; 57 | font-style: italic; 58 | } 59 | 60 | /* Paragraph */ 61 | p { padding: 12px 10px; margin-right: 30px; } 62 | 63 | /* Lists */ 64 | ul, ol { 65 | margin: 10px 20px; 66 | padding: 0 20px; 67 | } 68 | ul { list-style: none; } 69 | 70 | dl { padding: 10px; margin: 0; } 71 | 72 | dt { 73 | font-weight: bold; 74 | color: #79A62E; 75 | } 76 | dd { 77 | padding-left: 25px; 78 | } 79 | 80 | /* images */ 81 | img { 82 | border: 1px solid #E4E4E4; 83 | padding: 8px; 84 | } 85 | img.float-right { 86 | margin: 5px 0px 10px 10px; 87 | } 88 | img.float-left { 89 | margin: 5px 10px 10px 0px; 90 | } 91 | 92 | pre { 93 | margin: 5px 0; 94 | padding: 15px; 95 | text-align: left; 96 | display: block; 97 | overflow: auto; 98 | font: 500 1em/1.5em 'Lucida Console', 'Courier New', monospace; 99 | /* white-space: pre; */ 100 | border: 1px solid #EEE8E1; 101 | background: #FAF7F5; 102 | } 103 | 104 | code { 105 | display: inline; 106 | color: #666; 107 | font-weight: bold; 108 | } 109 | 110 | figure { 111 | text-align: center; 112 | } 113 | 114 | figcaption { 115 | font-style: italic; 116 | } 117 | 118 | 119 | acronym { 120 | cursor: help; 121 | border-bottom: 1px dotted #895F30; 122 | } 123 | blockquote { 124 | margin: 15px 10px; 125 | padding: 5px 7px 5px 40px; 126 | font-weight: normal; 127 | font-size: 17px; 128 | line-height: 1.6em; 129 | font-style: italic; 130 | font-family: Georgia, 'Times New Roman', Times, serif; 131 | color: #60aa2e; 132 | border: 1px solid #EEE8E1; 133 | background: #FAF7F5 url(../images/quote.gif) no-repeat 15px 15px; 134 | } 135 | 136 | /* start - table */ 137 | table { 138 | border-collapse: collapse; 139 | margin: 15px 10px; 140 | } 141 | th { 142 | height: 38px; 143 | padding-left: 12px; 144 | padding-right: 12px; 145 | color: #fff; 146 | text-align: left; 147 | background: #60aa2e; 148 | border-width: 1px; 149 | border-style: solid; 150 | border-color: #C4DE8F #93C138 #93C138 #C4DE8F; 151 | } 152 | tr { 153 | height: 34px; 154 | background: #fff; 155 | } 156 | td { 157 | padding-left: 11px; 158 | padding-right: 11px; 159 | border: 1px solid #E7F0CC; 160 | } 161 | /* end - table */ 162 | 163 | /* form elements */ 164 | form { 165 | margin: 20px 10px; 166 | padding: 15px 25px 25px 20px; 167 | border: 1px solid #EEE8E1; 168 | background: #FAF7F5; 169 | } 170 | form p { 171 | border-bottom: 1px solid #ECE5DD; 172 | padding: 12px 0 5px 0; margin: 0; 173 | color: #9D6D37; 174 | } 175 | label { 176 | font-weight: bold; 177 | color: #A7743B; 178 | } 179 | input, select, textarea { 180 | margin: 5px 0; 181 | padding: 5px; 182 | color: #6A6969; 183 | border-width: 1px; 184 | border-style: solid; 185 | border-color: #d4d4d4 #ebebeb #ebebeb #d4d4d4; 186 | } 187 | option { padding-right: 0.5em; } 188 | 189 | #name, #email, #message, #website{ 190 | width: 455px; 191 | } 192 | input.button { 193 | font: bold 12px Arial, Sans-serif; 194 | height: 30px; 195 | margin: 0; 196 | padding: 2px 3px; 197 | color: #fff; 198 | background: #A6CD56; 199 | 200 | border-width: 1px; 201 | border-style: solid; 202 | border-color: #C4DE8F #8DB836 #8DB836 #C4DE8F; 203 | } 204 | 205 | 206 | /*------------------------------------------- 207 | LAYOUT 208 | --------------------------------------------*/ 209 | #wrap { 210 | position: relative; 211 | width: 952px; 212 | background: #FFF url(../images/wrap.gif) repeat-y center top; 213 | margin: 40px auto 30px auto; 214 | text-align: left; 215 | } 216 | #content { 217 | clear: both; 218 | float: left; 219 | width: 952px; 220 | padding: 25px 0 60px 0; 221 | } 222 | #header { 223 | position: relative; 224 | width: 952px; 225 | height: 175px; 226 | background: #fff url(../images/header.jpg) no-repeat; 227 | color: #fff; 228 | padding: 0; 229 | margin: 0; 230 | } 231 | #header img { 232 | margin-left: -25px; 233 | margin-top: -25px; 234 | 235 | border-style: none; 236 | } 237 | #header h1#logo-text a { 238 | position: absolute; 239 | margin: 0; padding: 0; 240 | font: bold 66px 'Trebuchet MS', 'Helvetica Neue', Arial, Sans-serif; 241 | letter-spacing: -5px; 242 | text-decoration: none; 243 | color: #fff; 244 | 245 | /* change the values of top and left to adjust the position of the logo*/ 246 | top: 50px; left: 55px; 247 | } 248 | #header p#slogan { 249 | position: absolute; 250 | margin-left: 0; padding: 0; 251 | font: bold 15px 'Trebuchet MS', 'Helvetica Neue', Arial, Sans-serif; 252 | color: #333333; 253 | letter-spacing: -0.5px; 254 | 255 | /* change the values of top and left to adjust the position of the slogan*/ 256 | top: 125px; left: 142px; 257 | } 258 | 259 | /* top-menu */ 260 | #top-menu { 261 | position: absolute; 262 | margin: 0; 263 | padding: 10px 15px 8px 15px; 264 | background: #333; 265 | font-family: 'Trebuchet MS', Arial, Sans-serif; 266 | color: #FFF; 267 | 268 | top: 21px; right: 70px 269 | } 270 | #top-menu p { 271 | margin: 0; padding: 0; 272 | } 273 | #top-menu a { 274 | font-weight: bold; 275 | color: #fff; 276 | } 277 | 278 | /* stamp */ 279 | #stamp { 280 | position: absolute; 281 | margin: 0; 282 | color: #FFF; 283 | 284 | top: 105px; right: 5px 285 | } 286 | 287 | /* Navigation */ 288 | #nav { 289 | clear: both; 290 | padding: 0; margin: 0; 291 | width: 952px; 292 | height: 50px; 293 | background: url(../images/nav.gif) no-repeat; 294 | } 295 | #nav ul { 296 | float: left; 297 | list-style: none; 298 | margin: 5px 0 0 0; 299 | height: 45px; 300 | padding: 0 0 0 40px; 301 | } 302 | #nav ul li { 303 | float: left; 304 | margin: 0; padding: 0 0 0 10px; 305 | } 306 | #nav ul li a { 307 | float: left; 308 | margin: 0; 309 | padding: 0 15px 0 5px; 310 | color: #FFF; 311 | font: bold 15px/45px 'Trebuchet MS', 'Helvetica Neue', Arial, sans-serif; 312 | text-decoration: none; 313 | } 314 | #nav ul li a:hover, 315 | #nav ul li a:active { 316 | color: #E7FAA9; 317 | } 318 | #nav ul li.current { 319 | background: transparent url(../images/left-tab.gif) no-repeat; 320 | } 321 | #nav ul li.current a { 322 | color: #4B3D2C; 323 | background: transparent url(../images/right-tab.gif) no-repeat right top; 324 | } 325 | 326 | /* Main Column */ 327 | #main { 328 | float: left; 329 | padding: 0; margin: 0 0 0 40px; 330 | display: inline; 331 | } 332 | #main.sidebar { 333 | width: 535px; 334 | } 335 | #main h2 { 336 | font: bold 2.9em 'Trebuchet MS', 'Helvetica Neue', Arial, Sans-serif; 337 | color: #333; 338 | padding: 10px 0 5px 5px; 339 | margin-left: 5px; 340 | letter-spacing: -2px; 341 | } 342 | #main h2 a { 343 | color: #51432f; 344 | text-decoration: none; 345 | } 346 | #main ul li { 347 | list-style-image: url(../images/bullet.gif); 348 | } 349 | 350 | #sidebar { 351 | float: right; 352 | width: 285px; 353 | padding: 0; 354 | margin: 0 45px 0 0; 355 | display: inline; 356 | } 357 | 358 | #sidebar .events { 359 | border: solid 1px #e4f2c8; 360 | margin-bottom: 16px; 361 | padding: 0 10px 0 10px; 362 | } 363 | 364 | #sidebar h3 { 365 | color: #51432F; 366 | font: bold 1.9em 'Trebuchet MS', 'Helvetica Neue', Arial, Sans-serif; 367 | text-transform: none; 368 | letter-spacing: -0.5px; 369 | padding: 10px 0 10px 0; 370 | border-bottom: 1px solid #E4F2C8; 371 | } 372 | 373 | /* sidebar menu */ 374 | #sidebar .sidemenu { 375 | margin: 0; 376 | padding: 10px 0 10px 0; 377 | text-decoration: none; 378 | } 379 | #sidebar .sidemenu li { 380 | list-style: none; 381 | margin: 0; 382 | border-bottom: 1px solid #E8E8E8; 383 | } 384 | 385 | #sidebar .sidemenu li:last-child { 386 | border-bottom: none; 387 | } 388 | 389 | * html body #sidebar .sidemenu li { 390 | height: 1%; 391 | } 392 | #sidebar .sidemenu li a { 393 | text-decoration: none; 394 | color: #739A23; 395 | background: none; 396 | font-weight: bold; 397 | } 398 | #sidebar .sidemenu li a span { 399 | color: #7F7F7F; 400 | 401 | font-weight: normal; 402 | } 403 | #sidebar .sidemenu li a span.info{ color: #A98D76; } 404 | #sidebar .sidemenu li a:hover { 405 | color: #000; 406 | background: none; 407 | } 408 | #sidebar .sidemenu ul { margin: 0 0 0 5px; padding: 0; } 409 | #sidebar .sidemenu ul li { border: none; } 410 | /* header quick search */ 411 | #sidebar form#qsearch { 412 | padding: 0; margin: 20px 0 30px 10px; 413 | border: none; 414 | width: 270px; height: 33px; 415 | background: #F2F0F0 url(../images/qsearch.gif) no-repeat; 416 | } 417 | #sidebar form#qsearch p { 418 | margin: 0; padding: 0; 419 | } 420 | #sidebar form#qsearch input { 421 | float: left; 422 | border: none; 423 | background: transparent; 424 | color: #B8B8B8; 425 | margin: 0; 426 | } 427 | #sidebar form#qsearch .tbox { 428 | margin: 5px 0 0 5px; 429 | width: 220px; 430 | display: inline; 431 | font: 1em 'Trebuchet MS', 'Helvetica Neue', Arial, Sans-serif; 432 | } 433 | #search form#qsearch .btn{ 434 | width: 24px; height: 24px; 435 | } 436 | #sidebar form#qsearch label { 437 | display: none; 438 | } 439 | 440 | /* footer */ 441 | #footer { 442 | clear: both; 443 | width: 952px; 444 | margin: 0; 445 | padding: 30px 0 25px 0; 446 | background: #CCC url(../images/footer-bottom.gif) no-repeat left top; 447 | font-family: 'Trebuchet MS', 'Helvetica Neue', Arial, Sans-serif; 448 | font-size: 0.95em; 449 | color: #777; 450 | text-align: center; 451 | letter-spacing: .2px; 452 | } 453 | #footer p { 454 | padding:10px 0; 455 | } 456 | #footer a:hover { 457 | color: #555; 458 | text-decoration: none; 459 | } 460 | 461 | #footer .links { 462 | text-align: left; 463 | width: 100%; 464 | } 465 | #footer .links h2 { 466 | font-size: 15px; 467 | } 468 | 469 | #footer .links .link-col { 470 | width: 25%; 471 | float: left; 472 | display: inline; 473 | min-height: 130px; 474 | } 475 | 476 | #footer .links .link-col ul { 477 | margin: 10px 0; 478 | padding: 0 10px; 479 | } 480 | 481 | .cmf-sitemap .indent-1 { 482 | margin-left: 20px; 483 | } 484 | 485 | .cmf-sitemap .indent-2 { 486 | margin-left: 40px; 487 | } 488 | 489 | .cmf-sitemap .indent-3 { 490 | margin-left: 60px; 491 | } 492 | 493 | .cmf-sitemap .indent-4 { 494 | margin-left: 80px; 495 | } 496 | 497 | /* postmeta */ 498 | .post-footer { 499 | padding: 8px 10px; 500 | margin: 15px 10px 10px 10px; 501 | color: #977C57; 502 | border: 1px solid #EEE8E1; 503 | background: #FAF7F5; 504 | } 505 | .post-footer .date{ 506 | margin: 0 10px 0 5px; 507 | padding: 7px; 508 | background: #FAF7F5 url(../images/clock.gif) no-repeat 0 2px; 509 | padding-left: 30px; 510 | } 511 | .post-footer a.comments { 512 | margin: 0 10px 0 5px; 513 | padding: 7px 5px; 514 | background: #FAF7F5 url(../images/comments.gif) no-repeat 0 2px; 515 | padding-left: 30px; 516 | color: #977C57; 517 | } 518 | .post-footer a.readmore { 519 | margin: 0 10px 0 5px; 520 | padding: 7px 5px; 521 | background: #FAF7F5 url(../images/doc.gif) no-repeat 0 2px; 522 | padding-left: 30px; 523 | color: #977C57; 524 | } 525 | .post-footer a.edit { 526 | margin: 0 10px 0 5px; 527 | padding: 7px 5px; 528 | background: #FAF7F5 url(../images/edit-doc.gif) no-repeat 0 2px; 529 | padding-left: 30px; 530 | color: #977C57; 531 | } 532 | 533 | .post-info { font-size: .95em; padding: 0 10px; margin-left: 2px; color: #A98D76; } 534 | .post-info a, .post-info a:visited { color: #333; } 535 | 536 | /* comments list */ 537 | ol.commentlist { 538 | margin: 12px 10px; 539 | padding: 0; 540 | border-style: solid; 541 | border-color: #EEE8E1; 542 | border-width: 1px 1px 0 1px; 543 | } 544 | .commentlist li { 545 | margin: 0; 546 | padding: 10px; 547 | list-style: none; 548 | border-bottom: 1px solid #EEE8E1; 549 | } 550 | .commentlist li cite { 551 | display: block; 552 | font-style: normal; 553 | font-weight: bold; 554 | padding: 7px; 555 | } 556 | .commentlist li cite img { 557 | float: left; 558 | margin-right: 10px; 559 | } 560 | .commentlist li cite .comment-data { 561 | font-size: .8em; 562 | font-weight: normal; 563 | } 564 | .commentlist li .comment-text { 565 | clear: both; 566 | margin: 0; padding: 0; 567 | } 568 | .commentlist li.alt { 569 | background: #FAF7F5; 570 | } 571 | 572 | /* alignment classes */ 573 | .float-left { float: left; } 574 | .float-right { float: right; } 575 | .align-left { text-align: left; } 576 | .align-right { text-align: right; } 577 | 578 | /* display and additional classes */ 579 | .no-border { border: none; } 580 | .clearer { clear: both; } 581 | .clear { display:inline-block; } 582 | .clear:after { 583 | display:block; 584 | visibility:hidden; 585 | clear:both; 586 | height:0; 587 | content: "."; 588 | } 589 | 590 | .subtitle { margin-left: 15px; } 591 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/favicon.ico -------------------------------------------------------------------------------- /web/images/badge_open_source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/badge_open_source.png -------------------------------------------------------------------------------- /web/images/bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/bg.gif -------------------------------------------------------------------------------- /web/images/bullet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/bullet.gif -------------------------------------------------------------------------------- /web/images/clock.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/clock.gif -------------------------------------------------------------------------------- /web/images/cmf-best-os-2013.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/cmf-best-os-2013.jpg -------------------------------------------------------------------------------- /web/images/cmf-ch-open-source-award-2014.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/cmf-ch-open-source-award-2014.jpg -------------------------------------------------------------------------------- /web/images/comments.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/comments.gif -------------------------------------------------------------------------------- /web/images/doc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/doc.gif -------------------------------------------------------------------------------- /web/images/edit-doc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/edit-doc.gif -------------------------------------------------------------------------------- /web/images/firefox-gray.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/firefox-gray.jpg -------------------------------------------------------------------------------- /web/images/footer-bottom.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/footer-bottom.gif -------------------------------------------------------------------------------- /web/images/gravatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/gravatar.jpg -------------------------------------------------------------------------------- /web/images/header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/header.jpg -------------------------------------------------------------------------------- /web/images/left-tab.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/left-tab.gif -------------------------------------------------------------------------------- /web/images/logo-horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/logo-horizontal.png -------------------------------------------------------------------------------- /web/images/nav.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/nav.gif -------------------------------------------------------------------------------- /web/images/nav.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/nav.psd -------------------------------------------------------------------------------- /web/images/qsearch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/qsearch.gif -------------------------------------------------------------------------------- /web/images/quote.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/quote.gif -------------------------------------------------------------------------------- /web/images/right-tab.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/right-tab.gif -------------------------------------------------------------------------------- /web/images/search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/search.gif -------------------------------------------------------------------------------- /web/images/top-menu.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/top-menu.gif -------------------------------------------------------------------------------- /web/images/wrap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symfony-cmf/symfony-cmf-website/bee26316cf80e715781cdec835e82e47de4c8200/web/images/wrap.gif -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------