• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Apostrophe (improved Paris edition)
 

Apostrophe (improved Paris edition)

on

  • 8,828 views

The Apostrophe content management system. Open source, built on the Symfony framework

The Apostrophe content management system. Open source, built on the Symfony framework

Statistics

Views

Total Views
8,828
Views on SlideShare
3,834
Embed Views
4,994

Actions

Likes
3
Downloads
58
Comments
0

24 Embeds 4,994

http://www.lafermeduweb.net 2689
http://www.symfony.it 1259
http://www.symfony.es 571
http://punkave.com 278
http://pocky.github.com 71
http://window.punkave.com 39
http://symfony.es 16
http://www.emmanuelpereira.com 14
http://punkave 11
http://pocky.github.io 8
http://webcache.googleusercontent.com 8
http://feeds.feedburner.com 8
http://dev2-linguisticsprogram.trinity.duke.edu 6
http://translate.googleusercontent.com 3
http://paper.li 2
http://www.sfexception.com 2
https://si0.twimg.com 2
http://feedproxy.google.com 1
https://mariacollege.blackboard.com 1
http://dev2-literature.trinity.duke.edu 1
http://ccbcmdtest.blackboard.com 1
http://m.lafermeduweb.net 1
http://twitter.com 1
https://ccbcmd-bb.blackboard.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

Apostrophe (improved Paris edition) Apostrophe (improved Paris edition) Presentation Transcript

  • apostrophenow.com / punkave.com
  • A Symfony-powered CMS your clients will love Thomas Boutell
  • What is Apostrophe?Content Management SystemContent Management Framework
  • On the shoulders of giants...PHP!Symfony 1.4DoctrineMySQLZend Luceneminify ( http://code.google.com/p/minify/ )... and ideas from sfSimpleCMSPlugin
  • Goals of ApostropheEasy for clients to update without specialized trainingHard 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 contextWhen you cant do things in context, keep it simpleDont require a degree in Drupal-ogy!Check it out: demo.apostrophenow.com
  • Demo #1In-context editingButton slotsMedia repositoryCroppingVersion History... All “free” when you build custom slots
  • 8
  • ... OK, but how do you extend it?Relax! Its Still Symfony (RISS)Apostrophe embraces Symfony idiomsSlot = 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.ymlaFeedSlot: inheritance: extends: aSlot type: column_aggregation keyField: type keyValue: aFeed
  • Feed Slot: edit view componentclass 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 (withthe @), .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 actionclass 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: aFeedFormclass 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=monsterGenerates:schema.ymlActions and componentsmonsterForm class... Everything for a basic form-driven slot
  • Engines: multiple-page experiencesA Symfony module..."Grafted" into the page treeMultiple instances allowedEasy to distinguish with categorized contentExamples: Bobs blog, Janes 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 examples1. /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 iguana3. /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 #2Various actions of the event engine pageCategory filtering (with nice URLs)Permalink pages (with nice URLs)Some nice features of the blog (and event) pluginVirtual pages for blog and event contentThe 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 + minifierlayout.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
  • Conclusions1.5 is awesome now2.0 will be awesome laterYou are awesomeLets be awesome together
  • apostrophenow.com apostrophenow.com
  • http://joind.in/view/talk/2744 apostrophenow.com