SlideShare a Scribd company logo
apostrophenow.com / punkave.com
A Symfony-powered CMS
   your clients will love

     Thomas Boutell
What is Apostrophe?

Content Management System

Content Management Framework
On the shoulders of giants...

PHP!

Symfony 1.4
Doctrine
MySQL
Zend Lucene
minify ( http://code.google.com/p/minify/ )


... and ideas from sfSimpleCMSPlugin
Goals of Apostrophe

Easy for clients to update without specialized training

Hard for clients to screw up by accident!

Extensible by any Symfony developer
Making it easy

"When you log in, it just gets awesomer"

Do things in context

When you can't do things in context, keep it simple

Don't require a degree in Drupal-ogy!

Check it out: demo.apostrophenow.com
Demo #1

In-context editing

Button slots

Media repository

Cropping

Version History

... All “free” when you build custom slots
8
... OK, but how do you extend it?

Relax! It's Still Symfony (RISS)

Apostrophe embraces Symfony idioms

Slot = Doctrine inheritance +       Engine = Symfony module +
      Edit view component +                 aRoute & aDoctrineRoute +
      Normal view component +               Apostrophe page as a "host"
      Edit action +
      Edit form


... And layouts, and (page) templates, and plain old Symfony actions
Templates: adding slots and areas

     <?php a_area('link', array( 
   'area_add_content_label' => 'Add Program', 
    'allowed_types' => array(
     'aButton',     
    ),
  'type_options' => array(
     'aButton' => array(
       'itemTemplate' => 'homeButton', 
       'width' => 144, 
       // 'flexHeight' => true, 
       'height' => 124, 
       // 'resizeType' => 'c', 
       'constraints' => array(
         'minimum-width' => 144, 'minimum-height' => 124, 
         'aspect-width' => 144, 'aspect-height' => 124),  
       'title' => true, 
       'description' => false)))) ?>
Feed Slot: schema.yml

aFeedSlot:
 inheritance:
   extends: aSlot
   type: column_aggregation
   keyField: type
   keyValue: 'aFeed'
Feed Slot: edit view component

class BaseaFeedSlotComponents extends aSlotComponents
{
  public function executeEditView()
  {
    $this->setup();
    // If this is the first validation pass make the form
    if (!isset($this->form))
    {
      $this->form = new aFeedForm($this->id, $this->slot-
>getArrayValue());
    }
  }
  ...
}
Feed Slot: normal view component

...
public function executeNormalView()
 {
    $this->setup();
    $this->values = $this->slot->getArrayValue();
     if (!empty($this->values['url']))
     {
       $this->feed = aFeed::fetchCachedFeed(
         $this->url, ...);
       ...
     }
 }
Feed Slot: edit view partial

<?php use_helper('a') ?>
<ul class="a-slot-info a-feed-info">
  <li><?
php echo a_('Paste an RSS feed URL, a Twitter @name (with
the @), ' .
'or the URL of a page that offers a feed. Most blogs do.'
) ?></li>
</ul>
<?php echo $form ?>
Feed Slot: normal view partial

<?php use_helper('a') ?>
<?php if ($editable): ?>
  <?php // Display the edit button ?>
  <?php include_partial('a/
simpleEditWithVariants', ... ) ?>
<?php endif ?>
<ul class="a-feed">
  <?php foreach ($feed->getItems() as $feedItem): ?>
    <?php include_partial('aFeedSlot/'.
$options['itemTemplate'],
      array('feedItem' => $feedItem, ... )) ?>
  <?php endforeach ?>
</ul>
Feed Slot: edit action

class aFeedSlotActions extends aSlotActions
{
  public function executeEdit(sfRequest $request)
  {
    $this->editSetup();
    $value = $this->getRequestParameter('slot-form-' . $this->id);
    $this->form = new aFeedForm($this->id, array());
    $this->form->bind($value);
    if ($this->form->isValid())
    {
      // Serialize usually better than extra db columns
      $this->slot->setArrayValue($this->form->getValues());
      return $this->editSave();
    } else
    {
      // Another validation pass
      return $this->editRetry();
    }
  ...
Feed Slot: aFeedForm

class aFeedForm extends BaseForm
{
  public function __construct($id = 1, $defaults = array())
  {
    $this->id = $id;
    parent::__construct();
    $this->setDefaults($defaults);
  }
  public function configure()
  {
    $this->setWidget('url', new sfWidgetFormInputText(
      array('label' => 'RSS Feed URL'))));
    // Validators for: Twitter handle, lazy URLs,
    // valid URLs, regular pages with feed URLs in meta tags
    $this->widgetSchema->setNameFormat('slot-form-' . $this-
>id . '[%s]');
    $this->widgetSchema->setFormFormatterName('aAdmin');
  }
}
aFeedForm validators

    $this->setValidators(array('url' => new sfValidatorAnd(array(
  // @foo => correct twitter RSS feed URL
  new sfValidatorCallback(
    array('callback' => array($this, 'validateTwitterHandle'))), 
  // www.foo.bar => http://www.foo.bar
  new sfValidatorCallback(
    array('callback' => array($this, 'validateLazyUrl'))), 
  // Must be a valid URL to go past this stage
  new sfValidatorUrl(
    array('required' => true, 'max_length' => 1024)), 
  // Find feeds via meta tags in plain old pages
  new sfValidatorCallback(
    array('callback' => array($this, 'validateFeed')))))));
validateFeed: find feeds in plain pages

    public function validateFeed($validator, $value)
  {
    $content = @file_get_contents($value);
    if ($content)
    {
      $html = new DOMDocument();
      @$html->loadHTML($content);
      $xpath = new DOMXPath($html);
      $arts = $xpath->query('//link[@rel="alternate" and @type="application/rss+xml"]');
      if (isset($arts->length) && $arts->length)
      {
        return $arts->item(0)->getAttribute('href');
      }
    }
    return $value;
  }
apostrophe:generate-slot-type FTW!

./symfony apostrophe:generate-slot-type
     --plugin=myPlugin
     --type=monster

Generates:

schema.yml
Actions and components
monsterForm class

... Everything for a basic form-driven slot
Engines: multiple-page experiences

A Symfony module...

"Grafted" into the page tree

Multiple instances allowed

Easy to distinguish with categorized content

Examples: Bob's blog, Jane's blog, public photo gallery
Media engine: actions class (simplified)

class BaseaMediaActions extends aEngineActions
{
  public function executeIndex(sfWebRequest $request)
  {
    $this->items = Doctrine::getTable('aMediaItem')-
>findAll();
  }
    public function executeShow(sfWebRequest $request)
    {
      $this->item = Doctrine::getTable('aMediaItem')
        ->findOneBySlug($request->getParameter('slug'));
    }
}
Media engine: routing (yml style)

a_media_index:
  url: /
  class: aRoute
  param: { module: aMedia, action: index }

a_media_show:
  url: /view/:slug
  class: aRoute
  param: { module: aMedia, action: show }
  requirements: { slug: ^[w-]+$ }
Media engine: routing examples

1. /admin/media ->
   Engine page /admin/media
   Matches a_media_index route (special case for /)

2. /admin/media/view/iguana ->
   Engine page /admin/media
   Matches a_media_show route, slug is iguana

3. /iguanapix/view/iguana ->
   Engine page /iguanapix
   Matches a_media_show route, slug is iguana
Virtual pages

• Any page object with a slug not starting with /
• Efficiently stores slots & areas that will be needed together
• ... Which is how our blog plugin works
• Created on demand when you save the first slot
• ... Or in advance, paired with another object (blog post)
• Search treats these as routes: @blog_show?id=5, blog/show?id=5
• Search ignores this: not-searchable-57
• a_area(‘blog-body’, array(‘slug’ => “@blog_show?id=$id”))
Demo #2

Various actions of the event engine page

Category filtering (with nice URLs)

Permalink pages (with nice URLs)

Some nice features of the blog (and event) plugin

Virtual pages for blog and event content

The feed slot does its magic
27
Too friendly?

•   “If it’s friendly it must be for small sites”
•   More users = more training?
•   Can you afford to train 100 people?
•   Less training required = more scalable
Demo #3: features for big sites

• Access controls
• Reorganizing the page tree
30
Bonus: safe, efficient JS calls + minifier
layout.php:
<head>
<?php a_use_javascripts() ?>
<?php a_use_stylesheets() ?>
</head>
<body>
... At the very END of the body:
<?php a_include_js_calls() ?>
</body>
_list_footer.php:
<?php a_js_call('apostrophe.enableUserAdmin(?)',
    array('choose-one-label' => a_('Choose One...'))); ?>
Apostrophe 2.0
Apostrophe 2.0
Apostrophe 2.0
Apostrophe 2.0
Apostrophe 2.0
Apostrophe 2.0
Apostrophe 2.0
Conclusions

1.5 is awesome now

2.0 will be awesome later

You are awesome

Let's be awesome together
apostrophenow.com
     apostrophenow.com
http://joind.in/view/talk/2744
        apostrophenow.com

More Related Content

What's hot

The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
Fabien Potencier
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
patter
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
Fabien Potencier
 
WordPress Queries - the right way
WordPress Queries - the right wayWordPress Queries - the right way
WordPress Queries - the right way
Anthony Hortin
 
New in php 7
New in php 7New in php 7
New in php 7
Vic Metcalfe
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il cliente
Leonardo Proietti
 
Dependency Injection with PHP 5.3
Dependency Injection with PHP 5.3Dependency Injection with PHP 5.3
Dependency Injection with PHP 5.3
Fabien Potencier
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
Jeroen van Dijk
 
PhpBB meets Symfony2
PhpBB meets Symfony2PhpBB meets Symfony2
PhpBB meets Symfony2
Fabien Potencier
 
The Enterprise Wor/d/thy/Press
The Enterprise Wor/d/thy/PressThe Enterprise Wor/d/thy/Press
The Enterprise Wor/d/thy/Press
Jeroen van Dijk
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
Jeroen van Dijk
 
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
Fabien Potencier
 
The Enterprise Wor/d/thy/Press
The Enterprise Wor/d/thy/PressThe Enterprise Wor/d/thy/Press
The Enterprise Wor/d/thy/Press
Jeroen van Dijk
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5
Leonardo Proietti
 
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learnedMoving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Baldur Rensch
 
Data Validation models
Data Validation modelsData Validation models
Data Validation models
Marcin Czarnecki
 
Twig tips and tricks
Twig tips and tricksTwig tips and tricks
Twig tips and tricks
Javier Eguiluz
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
Hugo Hamon
 
Developing For The WordPress Customizer
Developing For The WordPress CustomizerDeveloping For The WordPress Customizer
Developing For The WordPress Customizer
Anthony Hortin
 
PHP MVC
PHP MVCPHP MVC

What's hot (20)

The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
 
WordPress Queries - the right way
WordPress Queries - the right wayWordPress Queries - the right way
WordPress Queries - the right way
 
New in php 7
New in php 7New in php 7
New in php 7
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il cliente
 
Dependency Injection with PHP 5.3
Dependency Injection with PHP 5.3Dependency Injection with PHP 5.3
Dependency Injection with PHP 5.3
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
 
PhpBB meets Symfony2
PhpBB meets Symfony2PhpBB meets Symfony2
PhpBB meets Symfony2
 
The Enterprise Wor/d/thy/Press
The Enterprise Wor/d/thy/PressThe Enterprise Wor/d/thy/Press
The Enterprise Wor/d/thy/Press
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
 
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
 
The Enterprise Wor/d/thy/Press
The Enterprise Wor/d/thy/PressThe Enterprise Wor/d/thy/Press
The Enterprise Wor/d/thy/Press
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5
 
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learnedMoving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
 
Data Validation models
Data Validation modelsData Validation models
Data Validation models
 
Twig tips and tricks
Twig tips and tricksTwig tips and tricks
Twig tips and tricks
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
Developing For The WordPress Customizer
Developing For The WordPress CustomizerDeveloping For The WordPress Customizer
Developing For The WordPress Customizer
 
PHP MVC
PHP MVCPHP MVC
PHP MVC
 

Similar to Apostrophe (improved Paris edition)

関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
Hisateru Tanaka
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
Kris Wallsmith
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8
Michelangelo van Dam
 
What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012
D
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2
Shinya Ohyanagi
 
Geek Moot '09 -- Smarty 101
Geek Moot '09 -- Smarty 101Geek Moot '09 -- Smarty 101
Geek Moot '09 -- Smarty 101
Ted Kulp
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
Jonathan Wage
 
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
arcware
 
Web internship Yii Framework
Web internship  Yii FrameworkWeb internship  Yii Framework
Web internship Yii Framework
Noveo
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
Nishan Subedi
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4Developers
Kacper Gunia
 
Symfony2 revealed
Symfony2 revealedSymfony2 revealed
Symfony2 revealed
Fabien Potencier
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friends
Michael Peacock
 
WordPress Theme Design and Development Workshop - Day 3
WordPress Theme Design and Development Workshop - Day 3WordPress Theme Design and Development Workshop - Day 3
WordPress Theme Design and Development Workshop - Day 3
Mizanur Rahaman Mizan
 
Frameworks da nova Era PHP FuelPHP
Frameworks da nova Era PHP FuelPHPFrameworks da nova Era PHP FuelPHP
Frameworks da nova Era PHP FuelPHP
Dan Jesus
 
Services Drupalcamp Stockholm 2009
Services Drupalcamp Stockholm 2009Services Drupalcamp Stockholm 2009
Services Drupalcamp Stockholm 2009
hugowetterberg
 
Unit testing zend framework apps
Unit testing zend framework appsUnit testing zend framework apps
Unit testing zend framework apps
Michelangelo van Dam
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
Daniel Knell
 
Keeping It Small with Slim
Keeping It Small with SlimKeeping It Small with Slim
Keeping It Small with Slim
Raven Tools
 
Building Potent WordPress Websites
Building Potent WordPress WebsitesBuilding Potent WordPress Websites
Building Potent WordPress Websites
Kyle Cearley
 

Similar to Apostrophe (improved Paris edition) (20)

関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8
 
What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2
 
Geek Moot '09 -- Smarty 101
Geek Moot '09 -- Smarty 101Geek Moot '09 -- Smarty 101
Geek Moot '09 -- Smarty 101
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
 
Web internship Yii Framework
Web internship  Yii FrameworkWeb internship  Yii Framework
Web internship Yii Framework
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4Developers
 
Symfony2 revealed
Symfony2 revealedSymfony2 revealed
Symfony2 revealed
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friends
 
WordPress Theme Design and Development Workshop - Day 3
WordPress Theme Design and Development Workshop - Day 3WordPress Theme Design and Development Workshop - Day 3
WordPress Theme Design and Development Workshop - Day 3
 
Frameworks da nova Era PHP FuelPHP
Frameworks da nova Era PHP FuelPHPFrameworks da nova Era PHP FuelPHP
Frameworks da nova Era PHP FuelPHP
 
Services Drupalcamp Stockholm 2009
Services Drupalcamp Stockholm 2009Services Drupalcamp Stockholm 2009
Services Drupalcamp Stockholm 2009
 
Unit testing zend framework apps
Unit testing zend framework appsUnit testing zend framework apps
Unit testing zend framework apps
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Keeping It Small with Slim
Keeping It Small with SlimKeeping It Small with Slim
Keeping It Small with Slim
 
Building Potent WordPress Websites
Building Potent WordPress WebsitesBuilding Potent WordPress Websites
Building Potent WordPress Websites
 

Recently uploaded

June Patch Tuesday
June Patch TuesdayJune Patch Tuesday
June Patch Tuesday
Ivanti
 
Skybuffer SAM4U tool for SAP license adoption
Skybuffer SAM4U tool for SAP license adoptionSkybuffer SAM4U tool for SAP license adoption
Skybuffer SAM4U tool for SAP license adoption
Tatiana Kojar
 
Choosing The Best AWS Service For Your Website + API.pptx
Choosing The Best AWS Service For Your Website + API.pptxChoosing The Best AWS Service For Your Website + API.pptx
Choosing The Best AWS Service For Your Website + API.pptx
Brandon Minnick, MBA
 
Trusted Execution Environment for Decentralized Process Mining
Trusted Execution Environment for Decentralized Process MiningTrusted Execution Environment for Decentralized Process Mining
Trusted Execution Environment for Decentralized Process Mining
LucaBarbaro3
 
Your One-Stop Shop for Python Success: Top 10 US Python Development Providers
Your One-Stop Shop for Python Success: Top 10 US Python Development ProvidersYour One-Stop Shop for Python Success: Top 10 US Python Development Providers
Your One-Stop Shop for Python Success: Top 10 US Python Development Providers
akankshawande
 
Generating privacy-protected synthetic data using Secludy and Milvus
Generating privacy-protected synthetic data using Secludy and MilvusGenerating privacy-protected synthetic data using Secludy and Milvus
Generating privacy-protected synthetic data using Secludy and Milvus
Zilliz
 
Best 20 SEO Techniques To Improve Website Visibility In SERP
Best 20 SEO Techniques To Improve Website Visibility In SERPBest 20 SEO Techniques To Improve Website Visibility In SERP
Best 20 SEO Techniques To Improve Website Visibility In SERP
Pixlogix Infotech
 
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdfUnlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Malak Abu Hammad
 
Deep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStr
Deep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStrDeep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStr
Deep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStr
saastr
 
Operating System Used by Users in day-to-day life.pptx
Operating System Used by Users in day-to-day life.pptxOperating System Used by Users in day-to-day life.pptx
Operating System Used by Users in day-to-day life.pptx
Pravash Chandra Das
 
HCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAUHCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAU
panagenda
 
System Design Case Study: Building a Scalable E-Commerce Platform - Hiike
System Design Case Study: Building a Scalable E-Commerce Platform - HiikeSystem Design Case Study: Building a Scalable E-Commerce Platform - Hiike
System Design Case Study: Building a Scalable E-Commerce Platform - Hiike
Hiike
 
AWS Cloud Cost Optimization Presentation.pptx
AWS Cloud Cost Optimization Presentation.pptxAWS Cloud Cost Optimization Presentation.pptx
AWS Cloud Cost Optimization Presentation.pptx
HarisZaheer8
 
A Comprehensive Guide to DeFi Development Services in 2024
A Comprehensive Guide to DeFi Development Services in 2024A Comprehensive Guide to DeFi Development Services in 2024
A Comprehensive Guide to DeFi Development Services in 2024
Intelisync
 
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdfMonitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Tosin Akinosho
 
Digital Marketing Trends in 2024 | Guide for Staying Ahead
Digital Marketing Trends in 2024 | Guide for Staying AheadDigital Marketing Trends in 2024 | Guide for Staying Ahead
Digital Marketing Trends in 2024 | Guide for Staying Ahead
Wask
 
Taking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdfTaking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdf
ssuserfac0301
 
UI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentationUI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentation
Wouter Lemaire
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
shyamraj55
 
5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides
DanBrown980551
 

Recently uploaded (20)

June Patch Tuesday
June Patch TuesdayJune Patch Tuesday
June Patch Tuesday
 
Skybuffer SAM4U tool for SAP license adoption
Skybuffer SAM4U tool for SAP license adoptionSkybuffer SAM4U tool for SAP license adoption
Skybuffer SAM4U tool for SAP license adoption
 
Choosing The Best AWS Service For Your Website + API.pptx
Choosing The Best AWS Service For Your Website + API.pptxChoosing The Best AWS Service For Your Website + API.pptx
Choosing The Best AWS Service For Your Website + API.pptx
 
Trusted Execution Environment for Decentralized Process Mining
Trusted Execution Environment for Decentralized Process MiningTrusted Execution Environment for Decentralized Process Mining
Trusted Execution Environment for Decentralized Process Mining
 
Your One-Stop Shop for Python Success: Top 10 US Python Development Providers
Your One-Stop Shop for Python Success: Top 10 US Python Development ProvidersYour One-Stop Shop for Python Success: Top 10 US Python Development Providers
Your One-Stop Shop for Python Success: Top 10 US Python Development Providers
 
Generating privacy-protected synthetic data using Secludy and Milvus
Generating privacy-protected synthetic data using Secludy and MilvusGenerating privacy-protected synthetic data using Secludy and Milvus
Generating privacy-protected synthetic data using Secludy and Milvus
 
Best 20 SEO Techniques To Improve Website Visibility In SERP
Best 20 SEO Techniques To Improve Website Visibility In SERPBest 20 SEO Techniques To Improve Website Visibility In SERP
Best 20 SEO Techniques To Improve Website Visibility In SERP
 
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdfUnlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
 
Deep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStr
Deep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStrDeep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStr
Deep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStr
 
Operating System Used by Users in day-to-day life.pptx
Operating System Used by Users in day-to-day life.pptxOperating System Used by Users in day-to-day life.pptx
Operating System Used by Users in day-to-day life.pptx
 
HCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAUHCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAU
 
System Design Case Study: Building a Scalable E-Commerce Platform - Hiike
System Design Case Study: Building a Scalable E-Commerce Platform - HiikeSystem Design Case Study: Building a Scalable E-Commerce Platform - Hiike
System Design Case Study: Building a Scalable E-Commerce Platform - Hiike
 
AWS Cloud Cost Optimization Presentation.pptx
AWS Cloud Cost Optimization Presentation.pptxAWS Cloud Cost Optimization Presentation.pptx
AWS Cloud Cost Optimization Presentation.pptx
 
A Comprehensive Guide to DeFi Development Services in 2024
A Comprehensive Guide to DeFi Development Services in 2024A Comprehensive Guide to DeFi Development Services in 2024
A Comprehensive Guide to DeFi Development Services in 2024
 
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdfMonitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdf
 
Digital Marketing Trends in 2024 | Guide for Staying Ahead
Digital Marketing Trends in 2024 | Guide for Staying AheadDigital Marketing Trends in 2024 | Guide for Staying Ahead
Digital Marketing Trends in 2024 | Guide for Staying Ahead
 
Taking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdfTaking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdf
 
UI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentationUI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentation
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
 
5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides
 

Apostrophe (improved Paris edition)

  • 2. A Symfony-powered CMS your clients will love Thomas Boutell
  • 3. What is Apostrophe? Content Management System Content Management Framework
  • 4. On the shoulders of giants... PHP! Symfony 1.4 Doctrine MySQL Zend Lucene minify ( http://code.google.com/p/minify/ ) ... and ideas from sfSimpleCMSPlugin
  • 5. Goals of Apostrophe Easy for clients to update without specialized training Hard for clients to screw up by accident! Extensible by any Symfony developer
  • 6. Making it easy "When you log in, it just gets awesomer" Do things in context When you can't do things in context, keep it simple Don't require a degree in Drupal-ogy! Check it out: demo.apostrophenow.com
  • 7. Demo #1 In-context editing Button slots Media repository Cropping Version History ... All “free” when you build custom slots
  • 8. 8
  • 9. ... OK, but how do you extend it? Relax! It's Still Symfony (RISS) Apostrophe embraces Symfony idioms Slot = Doctrine inheritance + Engine = Symfony module + Edit view component + aRoute & aDoctrineRoute + Normal view component + Apostrophe page as a "host" Edit action + Edit form ... And layouts, and (page) templates, and plain old Symfony actions
  • 10. Templates: adding slots and areas  <?php a_area('link', array(     'area_add_content_label' => 'Add Program',      'allowed_types' => array(      'aButton',          ),   'type_options' => array(      'aButton' => array(        'itemTemplate' => 'homeButton',         'width' => 144,         // 'flexHeight' => true,         'height' => 124,         // 'resizeType' => 'c',         'constraints' => array( 'minimum-width' => 144, 'minimum-height' => 124,  'aspect-width' => 144, 'aspect-height' => 124),          'title' => true,         'description' => false)))) ?>
  • 11. Feed Slot: schema.yml aFeedSlot: inheritance: extends: aSlot type: column_aggregation keyField: type keyValue: 'aFeed'
  • 12. Feed Slot: edit view component class BaseaFeedSlotComponents extends aSlotComponents { public function executeEditView() { $this->setup(); // If this is the first validation pass make the form if (!isset($this->form)) { $this->form = new aFeedForm($this->id, $this->slot- >getArrayValue()); } } ... }
  • 13. Feed Slot: normal view component ... public function executeNormalView() { $this->setup(); $this->values = $this->slot->getArrayValue(); if (!empty($this->values['url'])) { $this->feed = aFeed::fetchCachedFeed( $this->url, ...); ... } }
  • 14. Feed Slot: edit view partial <?php use_helper('a') ?> <ul class="a-slot-info a-feed-info"> <li><? php echo a_('Paste an RSS feed URL, a Twitter @name (with the @), ' . 'or the URL of a page that offers a feed. Most blogs do.' ) ?></li> </ul> <?php echo $form ?>
  • 15. Feed Slot: normal view partial <?php use_helper('a') ?> <?php if ($editable): ?> <?php // Display the edit button ?> <?php include_partial('a/ simpleEditWithVariants', ... ) ?> <?php endif ?> <ul class="a-feed"> <?php foreach ($feed->getItems() as $feedItem): ?> <?php include_partial('aFeedSlot/'. $options['itemTemplate'], array('feedItem' => $feedItem, ... )) ?> <?php endforeach ?> </ul>
  • 16. Feed Slot: edit action class aFeedSlotActions extends aSlotActions { public function executeEdit(sfRequest $request) { $this->editSetup(); $value = $this->getRequestParameter('slot-form-' . $this->id); $this->form = new aFeedForm($this->id, array()); $this->form->bind($value); if ($this->form->isValid()) { // Serialize usually better than extra db columns $this->slot->setArrayValue($this->form->getValues()); return $this->editSave(); } else { // Another validation pass return $this->editRetry(); } ...
  • 17. Feed Slot: aFeedForm class aFeedForm extends BaseForm { public function __construct($id = 1, $defaults = array()) { $this->id = $id; parent::__construct(); $this->setDefaults($defaults); } public function configure() { $this->setWidget('url', new sfWidgetFormInputText( array('label' => 'RSS Feed URL')))); // Validators for: Twitter handle, lazy URLs, // valid URLs, regular pages with feed URLs in meta tags $this->widgetSchema->setNameFormat('slot-form-' . $this- >id . '[%s]'); $this->widgetSchema->setFormFormatterName('aAdmin'); } }
  • 18. aFeedForm validators $this->setValidators(array('url' => new sfValidatorAnd(array(   // @foo => correct twitter RSS feed URL new sfValidatorCallback( array('callback' => array($this, 'validateTwitterHandle'))),    // www.foo.bar => http://www.foo.bar   new sfValidatorCallback( array('callback' => array($this, 'validateLazyUrl'))),    // Must be a valid URL to go past this stage   new sfValidatorUrl( array('required' => true, 'max_length' => 1024)),    // Find feeds via meta tags in plain old pages   new sfValidatorCallback( array('callback' => array($this, 'validateFeed')))))));
  • 19. validateFeed: find feeds in plain pages public function validateFeed($validator, $value)   {     $content = @file_get_contents($value);     if ($content)     {       $html = new DOMDocument();       @$html->loadHTML($content);       $xpath = new DOMXPath($html);       $arts = $xpath->query('//link[@rel="alternate" and @type="application/rss+xml"]');       if (isset($arts->length) && $arts->length)       {         return $arts->item(0)->getAttribute('href');       }     }     return $value;   }
  • 20. apostrophe:generate-slot-type FTW! ./symfony apostrophe:generate-slot-type --plugin=myPlugin --type=monster Generates: schema.yml Actions and components monsterForm class ... Everything for a basic form-driven slot
  • 21. Engines: multiple-page experiences A Symfony module... "Grafted" into the page tree Multiple instances allowed Easy to distinguish with categorized content Examples: Bob's blog, Jane's blog, public photo gallery
  • 22. Media engine: actions class (simplified) class BaseaMediaActions extends aEngineActions { public function executeIndex(sfWebRequest $request) { $this->items = Doctrine::getTable('aMediaItem')- >findAll(); } public function executeShow(sfWebRequest $request) { $this->item = Doctrine::getTable('aMediaItem') ->findOneBySlug($request->getParameter('slug')); } }
  • 23. Media engine: routing (yml style) a_media_index: url: / class: aRoute param: { module: aMedia, action: index } a_media_show: url: /view/:slug class: aRoute param: { module: aMedia, action: show } requirements: { slug: ^[w-]+$ }
  • 24. Media engine: routing examples 1. /admin/media -> Engine page /admin/media Matches a_media_index route (special case for /) 2. /admin/media/view/iguana -> Engine page /admin/media Matches a_media_show route, slug is iguana 3. /iguanapix/view/iguana -> Engine page /iguanapix Matches a_media_show route, slug is iguana
  • 25. Virtual pages • Any page object with a slug not starting with / • Efficiently stores slots & areas that will be needed together • ... Which is how our blog plugin works • Created on demand when you save the first slot • ... Or in advance, paired with another object (blog post) • Search treats these as routes: @blog_show?id=5, blog/show?id=5 • Search ignores this: not-searchable-57 • a_area(‘blog-body’, array(‘slug’ => “@blog_show?id=$id”))
  • 26. Demo #2 Various actions of the event engine page Category filtering (with nice URLs) Permalink pages (with nice URLs) Some nice features of the blog (and event) plugin Virtual pages for blog and event content The feed slot does its magic
  • 27. 27
  • 28. Too friendly? • “If it’s friendly it must be for small sites” • More users = more training? • Can you afford to train 100 people? • Less training required = more scalable
  • 29. Demo #3: features for big sites • Access controls • Reorganizing the page tree
  • 30. 30
  • 31. Bonus: safe, efficient JS calls + minifier layout.php: <head> <?php a_use_javascripts() ?> <?php a_use_stylesheets() ?> </head> <body> ... At the very END of the body: <?php a_include_js_calls() ?> </body> _list_footer.php: <?php a_js_call('apostrophe.enableUserAdmin(?)', array('choose-one-label' => a_('Choose One...'))); ?>
  • 39. Conclusions 1.5 is awesome now 2.0 will be awesome later You are awesome Let's be awesome together
  • 40. apostrophenow.com apostrophenow.com
  • 41. http://joind.in/view/talk/2744 apostrophenow.com

Editor's Notes

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n