diff --git a/.travis.yml b/.travis.yml index 4227177..007d81a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,17 @@ # See https://github.com/silverstripe-labs/silverstripe-travis-support for setup details language: php -php: +php: - 5.3 + - 5.4 env: - - DB=MYSQL CORE_RELEASE=3.1 - - DB=PGSQL CORE_RELEASE=master + - DB=MYSQL CORE_RELEASE=master matrix: include: - php: 5.4 - env: DB=MYSQL CORE_RELEASE=master + env: DB=PGSQL CORE_RELEASE=master before_script: - git clone git://github.com/silverstripe-labs/silverstripe-travis-support.git ~/travis-support @@ -19,4 +19,4 @@ before_script: - cd ~/builds/ss script: - - phpunit blog/tests/ \ No newline at end of file + - vendor/bin/phpunit blog/tests diff --git a/_config.php b/_config.php index e69de29..fbabff6 100644 --- a/_config.php +++ b/_config.php @@ -0,0 +1,4 @@ + true, @@ -41,7 +34,7 @@ class BlogEntry extends Page { * Is WYSIWYG editing allowed? * @var boolean */ - static $allow_wysiwyg_editing = true; + public static $allow_wysiwyg_editing = true; /** * Overload so that the default date is today. @@ -49,41 +42,55 @@ class BlogEntry extends Page { public function populateDefaults(){ parent::populateDefaults(); - $this->setField('Date', date('Y-m-d H:i:s', strtotime('now'))); + $this->setField('Date', SS_DateTime::now()->getValue()); } - function getCMSFields() { + public function getCMSFields() { Requirements::javascript('blog/javascript/bbcodehelp.js'); - Requirements::themedCSS('bbcodehelp'); + Requirements::themedCSS('bbcodehelp', 'blog'); + + // Add fields prior to extension + $this->beforeUpdateCMSFields(function($fields) { + // Disable HTML editing if wysiwyg is disabled + if(!BlogEntry::$allow_wysiwyg_editing) { + $fields->FieldList('Content', TextareaField::create("Content", _t("BlogEntry.CN", "Content"), 20)); + } + + // Date field + $dateField = DatetimeField::create("Date", _t("BlogEntry.DT", "Date"));; + $dateField->getDateField()->setConfig('showcalendar', true); + $dateField->getTimeField()->setConfig('timeformat', 'H:m:s'); + $fields->addFieldToTab("Root.Main", $dateField, "Content"); + + // Author field + $firstName = Member::currentUser() ? Member::currentUser()->FirstName : ''; + $fields->addFieldToTab( + "Root.Main", + TextField::create("Author", _t("BlogEntry.AU", "Author"), $firstName), + "Content" + ); + + // BB code hints + if(!BlogEntry::$allow_wysiwyg_editing) { + $codeparser = BBCodeParser::create(); + $hintField = new LiteralField( + "BBCodeHelper", + "
" . + "" . _t("BlogEntry.BBH", "BBCode help") . "" . + "
" + ); + $fields->addFieldToTab("Root.Main", $hintField); + } + + // Tags + $fields->addFieldToTab( + "Root.Main", + TextField::create("Tags", _t("BlogEntry.TS", "Tags (comma sep.)")), + "Content" + ); + }); - $firstName = Member::currentUser() ? Member::currentUser()->FirstName : ''; - $codeparser = new BBCodeParser(); - - SiteTree::disableCMSFieldsExtensions(); - $fields = parent::getCMSFields(); - SiteTree::enableCMSFieldsExtensions(); - - if(!self::$allow_wysiwyg_editing) { - $fields->removeFieldFromTab("Root.Main","Content"); - $fields->addFieldToTab("Root.Main", new TextareaField("Content", _t("BlogEntry.CN", "Content"), 20)); - } - - $fields->addFieldToTab("Root.Main", $dateField = new DatetimeField("Date", _t("BlogEntry.DT", "Date")),"Content"); - $dateField->getDateField()->setConfig('showcalendar', true); - $dateField->getTimeField()->setConfig('timeformat', 'H:m:s'); - $fields->addFieldToTab("Root.Main", new TextField("Author", _t("BlogEntry.AU", "Author"), $firstName),"Content"); - - if(!self::$allow_wysiwyg_editing) { - $fields->addFieldToTab("Root.Main", new LiteralField("BBCodeHelper", "
" . - "" . _t("BlogEntry.BBH", "BBCode help") . "" . - "
")); - } - - $fields->addFieldToTab("Root.Main", new TextField("Tags", _t("BlogEntry.TS", "Tags (comma sep.)")),"Content"); - - $this->extend('updateCMSFields', $fields); - - return $fields; + return parent::getCMSFields(); } /** @@ -106,7 +113,6 @@ class BlogEntry extends Page { * @return ArrayList List of ArrayData with Tag, Link, and URLTag keys */ public function TagsCollection() { - $tags = $this->TagNames(); $output = new ArrayList(); @@ -123,11 +129,11 @@ class BlogEntry extends Page { return $output; } - function Content() { + public function Content() { if(self::$allow_wysiwyg_editing) { return $this->getField('Content'); } else { - $parser = new BBCodeParser($this->Content); + $parser = BBCodeParser::create($this->Content); $content = new HTMLText('Content'); $content->value = $parser->parse(); return $content; @@ -135,87 +141,72 @@ class BlogEntry extends Page { } /** - * To be used by RSSFeed. If RSSFeed uses Content field, it doesn't pull in correctly parsed content. + * To be used by RSSFeed. If RSSFeed uses Content field, it doesn't pull in correctly parsed content. + * + * @return string */ - function RSSContent() { - return $this->Content(); - } - - /** - * Get a bbcode parsed summary of the blog entry - * @deprecated - */ - function ParagraphSummary(){ - user_error("BlogEntry::ParagraphSummary() is deprecated; use BlogEntry::Content()", E_USER_NOTICE); - - $val = $this->Content(); - $content = $val; - - if(!($content instanceof HTMLText)) { - $content = new HTMLText('Content'); - $content->value = $val; - } - - return $content->FirstParagraph('html'); - } - - /** - * Get the bbcode parsed content - * @deprecated - */ - function ParsedContent() { - user_error("BlogEntry::ParsedContent() is deprecated; use BlogEntry::Content()", E_USER_NOTICE); + public function RSSContent() { return $this->Content(); } /** * Link for editing this blog entry + * + * @return string Edit URL */ - function EditURL() { - return ($this->getParent()) ? $this->getParent()->Link('post') . '/' . $this->ID . '/' : false; - } - - function IsOwner() { - if(method_exists($this->Parent(), 'IsOwner')) { - return $this->Parent()->IsOwner(); + public function EditURL() { + $parent = $this->getParent(); + if($parent) { + return Controller::join_links($parent->Link('post'), $this->ID); } } + + /** + * Returns true if the current user is an admin, or is the owner of this blog + * {@see BlogHolder::IsOwner} + * + * @return bool + */ + public function IsOwner() { + $parent = $this->Parent(); + return $parent && $parent->hasMethod('IsOwner') && $parent->IsOwner(); + } /** * Call this to enable WYSIWYG editing on your blog entries. * By default the blog uses BBCode */ - static function allow_wysiwyg_editing() { + public static function allow_wysiwyg_editing() { self::$allow_wysiwyg_editing = true; } /** - * Get the previous blog entry from this section of blog pages. + * Get the next oldest blog entry from this section of blog pages. * * @return BlogEntry */ - function PreviousBlogEntry() { - return DataObject::get_one( - 'BlogEntry', - "\"SiteTree\".\"ParentID\" = '$this->ParentID' AND \"BlogEntry\".\"Date\" < '$this->Date'", - true, - 'Date DESC' - ); + public function PreviousBlogEntry() { + return BlogEntry::get() + ->filter('ParentID', $this->ParentID) + ->exclude('ID', $this->ID) + ->filter('Date:LessThanOrEqual', $this->Date) + ->sort('"BlogEntry"."Date" DESC') + ->first(); } /** - * Get the next blog entry from this section of blog pages. + * Get the next most recent blog entry from this section of blog pages. * * @return BlogEntry */ - function NextBlogEntry() { - return DataObject::get_one( - 'BlogEntry', - "\"SiteTree\".\"ParentID\" = '$this->ParentID' AND \"BlogEntry\".\"Date\" > '$this->Date'", - true, - 'Date ASC' - ); + public function NextBlogEntry() { + return BlogEntry::get() + ->filter('ParentID', $this->ParentID) + ->exclude('ID', $this->ID) + ->filter('Date:GreaterThanOrEqual', $this->Date) + ->sort('"BlogEntry"."Date" ASC') + ->first(); } /** @@ -223,7 +214,7 @@ class BlogEntry extends Page { * * @return BlogHolder */ - function getBlogHolder() { + public function getBlogHolder() { $holder = null; if($this->ParentID && $this->Parent()->ClassName == 'BlogHolder') { $holder = $this->Parent(); @@ -236,47 +227,31 @@ class BlogEntry extends Page { class BlogEntry_Controller extends Page_Controller { private static $allowed_actions = array( - 'index', - 'unpublishPost', - 'PageComments', - 'SearchForm' + 'unpublishPost' ); - function init() { + public function init() { parent::init(); - Requirements::themedCSS("blog","blog"); + Requirements::themedCSS("blog", "blog"); } /** * Gets a link to unpublish the blog entry */ - function unpublishPost() { + public function unpublishPost() { if(!$this->IsOwner()) { Security::permissionFailure( $this, 'Unpublishing blogs is an administrator task. Please log in.' ); } else { - $SQL_id = (int) $this->ID; - - $page = DataObject::get_by_id('SiteTree', $SQL_id); + $page = BlogEntry::get()->byID($this->data()->ID); $page->deleteFromStage('Live'); $page->flushCache(); - $this->redirect($this->getParent()->Link()); + return $this->redirect($this->getParent()->Link()); } } - - /** - * Temporary workaround for compatibility with 'comments' module - * (has been extracted from sapphire/trunk in 12/2010). - * - * @return Form - */ - function PageComments() { - if($this->hasMethod('CommentsForm')) return $this->CommentsForm(); - else if(method_exists('Page_Controller', 'PageComments')) return parent::PageComments(); - } } diff --git a/code/BlogHolder.php b/code/BlogHolder.php index 340c48a..e8dc1c0 100644 --- a/code/BlogHolder.php +++ b/code/BlogHolder.php @@ -34,40 +34,37 @@ class BlogHolder extends BlogTree implements PermissionProvider { 'BlogEntry' ); - function getCMSFields() { + public function getCMSFields() { $blogOwners = $this->blogOwners(); - SiteTree::disableCMSFieldsExtensions(); - $fields = parent::getCMSFields(); - SiteTree::enableCMSFieldsExtensions(); + // Add holder fields prior to extensions being called + $this->beforeUpdateCMSFields(function($fields) use ($blogOwners) { + $fields->addFieldsToTab( + 'Root.Main', + array( + DropdownField::create('OwnerID', 'Blog owner', $blogOwners->map('ID', 'Name')->toArray()) + ->setEmptyString('(None)') + ->setHasEmptyDefault(true), + CheckboxField::create('AllowCustomAuthors', 'Allow non-admins to have a custom author field'), + CheckboxField::create("ShowFullEntry", "Show Full Entry") + ->setDescription('Show full content in overviews rather than summary') + ), + "Content" + ); + }); - $fields->addFieldToTab( - 'Root.Main', - DropdownField::create('OwnerID', 'Blog owner', $blogOwners->map('ID', 'Name')->toArray()) - ->setEmptyString('(None)') - ->setHasEmptyDefault(true), - "Content" - ); - $fields->addFieldToTab('Root.Main', new CheckboxField('AllowCustomAuthors', 'Allow non-admins to have a custom author field'), "Content"); - $fields->addFieldToTab( - "Root.Main", - CheckboxField::create("ShowFullEntry", "Show Full Entry") - ->setDescription('Show full content in overviews rather than summary'), - "Content" - ); - - $this->extend('updateCMSFields', $fields); - - return $fields; + return parent::getCMSFields(); } /** * Get members who have BLOGMANAGEMENT and ADMIN permission - */ - - function blogOwners($sort = array('FirstName'=>'ASC','Surname'=>'ASC'), $direction = null) { - - $members = Permission::get_members_by_permission(array('ADMIN','BLOGMANAGEMENT')); + * + * @param array $sort + * @param string $direction + * @return SS_List + */ + public function blogOwners($sort = array('FirstName'=>'ASC','Surname'=>'ASC'), $direction = null) { + $members = Permission::get_members_by_permission(array('ADMIN', 'BLOGMANAGEMENT')); $members->sort($sort); $this->extend('extendBlogOwners', $members); @@ -86,7 +83,7 @@ class BlogHolder extends BlogTree implements PermissionProvider { /** * Only display the blog entries that have the specified tag */ - function ShowTag() { + public function ShowTag() { if($this->request->latestParam('Action') == 'tag') { return Convert::raw2xml(Director::urlParam('ID')); } @@ -95,30 +92,30 @@ class BlogHolder extends BlogTree implements PermissionProvider { /** * Check if url has "/post" */ - function isPost() { + public function isPost() { return $this->request->latestParam('Action') == 'post'; } /** * Link for creating a new blog entry */ - function postURL(){ + public function postURL() { return $this->Link('post'); } /** * Returns true if the current user is an admin, or is the owner of this blog * - * @return Boolean + * @return bool */ - function IsOwner() { + public function IsOwner() { return (Permission::check('BLOGMANAGEMENT') || Permission::check('ADMIN')); } /** * Create default blog setup */ - function requireDefaultRecords() { + public function requireDefaultRecords() { parent::requireDefaultRecords(); // Skip creation of default records @@ -179,7 +176,7 @@ class BlogHolder extends BlogTree implements PermissionProvider { } } - function providePermissions() { + public function providePermissions() { return array("BLOGMANAGEMENT" => "Blog management"); } } @@ -196,7 +193,7 @@ class BlogHolder_Controller extends BlogTree_Controller { 'BlogEntryForm' => 'BLOGMANAGEMENT', ); - function init() { + public function init() { parent::init(); Requirements::themedCSS("bbcodehelp"); } @@ -204,14 +201,14 @@ class BlogHolder_Controller extends BlogTree_Controller { /** * Return list of usable tags for help */ - function BBTags() { + public function BBTags() { return BBCodeParser::usable_tags(); } /** * Post a new blog entry */ - function post(){ + public function post(){ if(!Permission::check('BLOGMANAGEMENT')) return Security::permissionFailure(); $page = $this->customise(array( 'Content' => false, @@ -223,62 +220,74 @@ class BlogHolder_Controller extends BlogTree_Controller { /** * A simple form for creating blog entries + * + * @return Form */ - function BlogEntryForm() { + public function BlogEntryForm() { if(!Permission::check('BLOGMANAGEMENT')) return Security::permissionFailure(); - $id = 0; - if($this->request->latestParam('ID')) { - $id = (int) $this->request->latestParam('ID'); - } - - $codeparser = new BBCodeParser(); + $codeparser = BBCodeParser::create(); $membername = Member::currentUser() ? Member::currentUser()->getName() : ""; if(BlogEntry::$allow_wysiwyg_editing) { - $contentfield = new HtmlEditorField("BlogPost", _t("BlogEntry.CN")); + $contentfield = HtmlEditorField::create("BlogPost", _t("BlogEntry.CN")); } else { - $contentfield = new CompositeField( - new LiteralField("BBCodeHelper",""._t("BlogEntry.BBH")."
" ), - new TextareaField("BlogPost", _t("BlogEntry.CN"),20), // This is called BlogPost as the id #Content is generally used already - new LiteralField("BBCodeTags","
".$codeparser->useable_tagsHTML()."
") + $contentfield = CompositeField::create( + LiteralField::create( + "BBCodeHelper", + ""._t("BlogEntry.BBH")."
" + ), + TextareaField::create( + "BlogPost", + _t("BlogEntry.CN"), + 20 + ), // This is called BlogPost as the id #Content is generally used already + LiteralField::create( + "BBCodeTags", + "
".$codeparser->useable_tagsHTML()."
" + ) ); } + + // Support for https://github.com/chillu/silverstripe-tagfield if(class_exists('TagField')) { - $tagfield = new TagField('Tags', null, null, 'BlogEntry'); + $tagfield = TagField::create('Tags', null, null, 'BlogEntry'); $tagfield->setSeparator(', '); } else { - $tagfield = new TextField('Tags'); + $tagfield = TextField::create('Tags'); } $field = 'TextField'; if(!$this->AllowCustomAuthors && !Permission::check('ADMIN')) { $field = 'ReadonlyField'; } - $fields = new FieldList( - new HiddenField("ID", "ID"), - new TextField("Title", _t('BlogHolder.SJ', "Subject")), - new $field("Author", _t('BlogEntry.AU'), $membername), + $fields = FieldList::create( + HiddenField::create("ID", "ID"), + TextField::create("Title", _t('BlogHolder.SJ', "Subject")), + $field::create("Author", _t('BlogEntry.AU'), $membername), $contentfield, $tagfield, - new LiteralField("Tagsnote"," ") + LiteralField::create( + "Tagsnote", + " " + ) ); - $submitAction = new FormAction('postblog', _t('BlogHolder.POST', 'Post blog entry')); + $submitAction = FormAction::create('postblog', _t('BlogHolder.POST', 'Post blog entry')); - $actions = new FieldList($submitAction); - $validator = new RequiredFields('Title','BlogPost'); + $actions = FieldList::create($submitAction); + $validator = RequiredFields::create('Title','BlogPost'); - $form = new Form($this, 'BlogEntryForm',$fields, $actions,$validator); + $form = Form::create($this, 'BlogEntryForm', $fields, $actions, $validator); - if($id != 0) { - $entry = DataObject::get_by_id('BlogEntry', $id); + $id = (int) $this->request->latestParam('ID'); + if($id) { + $entry = BlogEntry::get()->byID($id); if($entry->IsOwner()) { $form->loadDataFrom($entry); $form->Fields()->fieldByName('BlogPost')->setValue($entry->Content); - } } else { $form->loadDataFrom(array("Author" => Cookie::get("BlogHolder_Name"))); @@ -287,22 +296,18 @@ class BlogHolder_Controller extends BlogTree_Controller { return $form; } - function postblog($data, $form) { + public function postblog($data, $form) { if(!Permission::check('BLOGMANAGEMENT')) return Security::permissionFailure(); Cookie::set("BlogHolder_Name", $data['Author']); $blogentry = false; - if(isset($data['ID']) && $data['ID']) { - $blogentry = DataObject::get_by_id("BlogEntry", $data['ID']); - if(!$blogentry->IsOwner()) { - unset($blogentry); - } + if(!empty($data['ID'])) { + $candidate = BlogEntry::get()->byID($data['ID']); + if($candidate->IsOwner()) $blogentry = $candidate; } - if(!$blogentry) { - $blogentry = new BlogEntry(); - } + if(!$blogentry) $blogentry = BlogEntry::create(); $form->saveInto($blogentry); $blogentry->ParentID = $this->ID; @@ -313,8 +318,11 @@ class BlogHolder_Controller extends BlogTree_Controller { $blogentry->Locale = $this->Locale; } - $blogentry->writeToStage("Stage"); + $oldMode = Versioned::get_reading_mode(); + Versioned::reading_stage('Stage'); + $blogentry->write(); $blogentry->publish("Stage", "Live"); + Versioned::set_reading_mode($oldMode); $this->redirect($this->Link()); } diff --git a/code/BlogLeftMainExtension.php b/code/BlogLeftMainExtension.php index 32fa014..cf2fc4f 100644 --- a/code/BlogLeftMainExtension.php +++ b/code/BlogLeftMainExtension.php @@ -4,36 +4,44 @@ * Adds author and "post date" fields. */ class BlogLeftMainExtension extends Extension { - function updateListView($listView) { + + /** + * {@see CMSMain::ListViewForm} + * + * @param type $listView + * @return type + */ + public function updateListView($listView) { $parentId = $listView->getController()->getRequest()->requestVar('ParentID'); - $parent = ($parentId) ? Page::get()->byId($parentId) : new Page(); + if(!$parentId) return; // Only apply logic for this page type - if($parent && $parent instanceof BlogHolder) { - $gridField = $listView->Fields()->dataFieldByName('Page'); - if($gridField) { - // Sort by post date - $list = $gridField->getList(); - $list = $list->leftJoin('BlogEntry', '"BlogEntry"."ID" = "SiteTree"."ID"'); - $gridField->setList($list->sort('Date', 'DESC')); + $parent = BlogHolder::get()->byId($parentId); + if(!$parent) return; - // Change columns - $cols = $gridField->getConfig()->getComponentByType('GridFieldDataColumns'); - if($cols) { - $fields = $cols->getDisplayFields($gridField); - $castings = $cols->getFieldCasting($gridField); - - // Add author to columns - $fields['Author'] = _t("BlogEntry.AU", "Author"); - // Add post date and remove duplicate "created" date - $fields['Date'] = _t("BlogEntry.DT", "Date"); - $castings['Date'] = 'SS_Datetime->Ago'; - if(isset($fields['Created'])) unset($fields['Created']); - - $cols->setDisplayFields($fields); - $cols->setFieldCasting($castings); - } - } - } + $gridField = $listView->Fields()->dataFieldByName('Page'); + if(!$gridField) return; + + // Sort by post date + $list = $gridField->getList(); + $list = $list->leftJoin('BlogEntry', '"BlogEntry"."ID" = "SiteTree"."ID"'); + $gridField->setList($list->sort('Date', 'DESC')); + + // Change columns + $cols = $gridField->getConfig()->getComponentByType('GridFieldDataColumns'); + if(!$cols) return; + + $fields = $cols->getDisplayFields($gridField); + $castings = $cols->getFieldCasting($gridField); + + // Add author to columns + $fields['Author'] = _t("BlogEntry.AU", "Author"); + // Add post date and remove duplicate "created" date + $fields['Date'] = _t("BlogEntry.DT", "Date"); + $castings['Date'] = 'SS_Datetime->Ago'; + if(isset($fields['Created'])) unset($fields['Created']); + + $cols->setDisplayFields($fields); + $cols->setFieldCasting($castings); } } \ No newline at end of file diff --git a/code/BlogTree.php b/code/BlogTree.php index e5310ad..7cc2775 100644 --- a/code/BlogTree.php +++ b/code/BlogTree.php @@ -1,11 +1,11 @@ - 'Varchar(255)', 'LandingPageFreshness' => 'Varchar', ); - - private static $defaults = array( - ); - - private static $has_one = array(); - private static $has_many = array(); - private static $allowed_children = array( - 'BlogTree', 'BlogHolder' + 'BlogTree', + 'BlogHolder' ); /* @@ -48,179 +47,181 @@ class BlogTree extends Page { * uses current * @return BlogTree */ - static function current($page = null) { - + public static function current($page = null) { + // extract page from current request if not specified if (!$page && Controller::has_curr()) { $controller = Controller::curr(); if ($controller->hasMethod('data')) { $page = $controller->data(); } } - + if ($page) { // If we _are_ a BlogTree, use us if ($page instanceof BlogTree) return $page; - + // If page is a virtual page use that - if($page instanceof VirtualPage && $page->CopyContentFrom() instanceof BlogTree) return $page; + if($page instanceof VirtualPage && $page->CopyContentFrom() instanceof BlogTree) { + return $page; + } // Or, if we a a BlogEntry underneath a BlogTree, use our parent - if($page->is_a("BlogEntry")) { - $parent = $page->getParent(); - if($parent instanceof BlogTree) return $parent; + if($page instanceof BlogEntry && $page->getParent() instanceof BlogTree) { + return $page->getParent(); } } - + // Try to find a top-level BlogTree - $top = DataObject::get_one('BlogTree', "\"ParentID\" = '0'"); + $top = BlogTree::get()->filter("ParentID", 0)->first(); if($top) return $top; - + // Try to find any BlogTree that is not inside another BlogTree - if($blogTrees=DataObject::get('BlogTree')) foreach($blogTrees as $tree) { + $blogTrees = BlogTree::get(); + foreach($blogTrees as $tree) { if(!($tree->getParent() instanceof BlogTree)) return $tree; } - + // This shouldn't be possible, but assuming the above fails, just return anything you can get return $blogTrees->first(); } - /* ----------- ACCESSOR OVERRIDES -------------- */ - - public function getLandingPageFreshness() { - $freshness = $this->getField('LandingPageFreshness'); - // If we want to inherit freshness, try that first - if ($freshness == "INHERIT" && $this->getParent()) $freshness = $this->getParent()->LandingPageFreshness; - // If we don't have a parent, or the inherited result was still inherit, use default - if ($freshness == "INHERIT") $freshness = ''; + /** + * Calculates number of months of landing page freshness to show + * + * @return int Number of months, if filtered + */ + public function getLandingPageFreshnessMonths() { + $freshness = $this->LandingPageFreshness; + + // Substitute 'INHERIT' for parent freshness, if available + if ($freshness === "INHERIT") { + $freshness = (($parent = $this->getParent()) && $parent instanceof BlogTree) + ? $parent->getLandingPageFreshnessMonths() + : null; + } return $freshness; } - + /* ----------- CMS CONTROL -------------- */ - - function getSettingsFields() { + + public function getSettingsFields() { $fields = parent::getSettingsFields(); $fields->addFieldToTab( - 'Root.Settings', + 'Root.Settings', new DropdownField( - 'LandingPageFreshness', - 'When you first open the blog, how many entries should I show', - array( - "" => "All entries", - "1" => "Last month's entries", - "2" => "Last 2 months' entries", - "3" => "Last 3 months' entries", - "4" => "Last 4 months' entries", - "5" => "Last 5 months' entries", - "6" => "Last 6 months' entries", - "7" => "Last 7 months' entries", - "8" => "Last 8 months' entries", - "9" => "Last 9 months' entries", - "10" => "Last 10 months' entries", - "11" => "Last 11 months' entries", - "12" => "Last year's entries", + 'LandingPageFreshness', + 'When you first open the blog, how many entries should I show', + array( + "" => "All entries", + "1" => "Last month's entries", + "2" => "Last 2 months' entries", + "3" => "Last 3 months' entries", + "4" => "Last 4 months' entries", + "5" => "Last 5 months' entries", + "6" => "Last 6 months' entries", + "7" => "Last 7 months' entries", + "8" => "Last 8 months' entries", + "9" => "Last 9 months' entries", + "10" => "Last 10 months' entries", + "11" => "Last 11 months' entries", + "12" => "Last year's entries", "INHERIT" => "Take value from parent Blog Tree" ) ) - ); + ); return $fields; } - + /* ----------- New accessors -------------- */ - + public function loadDescendantBlogHolderIDListInto(&$idList) { if ($children = $this->AllChildren()) { foreach($children as $child) { if(in_array($child->ID, $idList)) continue; - + if($child instanceof BlogHolder) { - $idList[] = $child->ID; + $idList[] = $child->ID; } elseif($child instanceof BlogTree) { $child->loadDescendantBlogHolderIDListInto($idList); - } + } } } } - - // Build a list of all IDs for BlogHolders that are children of us + + /** + * Build a list of all IDs for BlogHolders that are children of us + * + * @return array + */ public function BlogHolderIDs() { $holderIDs = array(); $this->loadDescendantBlogHolderIDListInto($holderIDs); return $holderIDs; } - + /** * Get entries in this blog. - * - * @param string $limit A clause to insert into the limit clause. + * + * @param string $limit Page size of paginated list * @param string $tag Only get blog entries with this tag * @param string $date Only get blog entries on this date - either a year, or a year-month eg '2008' or '2008-02' - * @param callable $retrieveCallback A function to call with pagetype, filter and limit for custom blog - * sorting or filtering - * @param string $filter Filter condition + * @param array $filters A list of DataList compatible filters + * @param mixed $where Raw SQL WHERE condition(s) * @return PaginatedList The list of entries in a paginated list */ - public function Entries($limit = '', $tag = '', $date = '', $retrieveCallback = null, $filter = '') { - - $tagCheck = ''; - $dateCheck = ''; - - if($tag) { - $SQL_tag = Convert::raw2sql($tag); - $tagCheck = "AND \"BlogEntry\".\"Tags\" LIKE '%$SQL_tag%'"; - } + public function Entries($limit = '', $tag = '', $date = '', $filters = array(), $where = '') { + // Filter by all current blog holder parents, if any are available + $holderIDs = $this->BlogHolderIDs(); + if(empty($holderIDs)) return false; - if($date) { - // Some systems still use the / seperator for date presentation - if( strpos($date, '-') ) $seperator = '-'; - elseif( strpos($date, '/') ) $seperator = '/'; - - if(isset($seperator) && !empty($seperator)) { - // The 2 in the explode argument will tell it to only create 2 elements - // i.e. in this instance the $year and $month fields respectively - list($year,$month) = explode( $seperator, $date, 2); - - $year = (int)$year; - $month = (int)$month; + // Build filtered list + $entries = BlogEntry::get() + ->filter('ParentID', $holderIDs) + ->sort($order = '"BlogEntry"."Date" DESC'); - if($year && $month) { - if(method_exists(DB::getConn(), 'formattedDatetimeClause')) { - $db_date=DB::getConn()->formattedDatetimeClause('"BlogEntry"."Date"', '%m'); - $dateCheck = "AND CAST($db_date AS " . DB::getConn()->dbDataType('unsigned integer') . ") = $month AND " . DB::getConn()->formattedDatetimeClause('"BlogEntry"."Date"', '%Y') . " = '$year'"; - } else { - $dateCheck = "AND MONTH(\"BlogEntry\".\"Date\") = '$month' AND YEAR(\"BlogEntry\".\"Date\") = '$year'"; - } - } - } else { - $year = (int) $date; - if($year) { - if(method_exists(DB::getConn(), 'formattedDatetimeClause')) { - $dateCheck = "AND " . DB::getConn()->formattedDatetimeClause('"BlogEntry"."Date"', '%Y') . " = '$year'"; - } else { - $dateCheck = "AND YEAR(\"BlogEntry\".\"Date\") = '$year'"; - } - } + // Apply where condition + if($where) $entries = $entries->where($where); + + // Add tag condition + if($tag) $entries = $entries->filter('Tags:PartialMatch', $tag); + + // Add date condition + if($date && preg_match('/^(?\d+)([-\\/](?\d+))?/', $date, $matches)) { + // Add year filter + $yearExpression = DB::get_conn()->formattedDatetimeClause('"BlogEntry"."Date"', '%Y'); + $uintExpression = DB::get_schema()->dbDataType('unsigned integer'); + $entries = $entries->where(array( + "CAST($yearExpression AS $uintExpression) = ?" => $matches['year'] + )); + + // Add month filter + if(!empty($matches['month'])) { + $monthExpression = DB::get_conn()->formattedDatetimeClause('"BlogEntry"."Date"', '%m'); + $entries = $entries->where(array( + "CAST($monthExpression AS $uintExpression) = ?" => $matches['month'] + )); } } - - // Build a list of all IDs for BlogHolders that are children of us - $holderIDs = $this->BlogHolderIDs(); - // If no BlogHolders, no BlogEntries. So return false - if(empty($holderIDs)) return false; - - // Otherwise, do the actual query - if($filter) $filter .= ' AND '; - $filter .= '"SiteTree"."ParentID" IN (' . implode(',', $holderIDs) . ") $tagCheck $dateCheck"; + // Deprecate old $retrieveCallback parameter + if($filters && (is_string($filters) || is_callable($filters))) { + Deprecation::notice( + '0.8', + '$retrieveCallback parameter is deprecated. Use updateEntries in an extension instead.' + ); + $callbackWhere = $entries->dataQuery()->query()->getWhere(); + return call_user_func($filters, 'BlogEntry', $callbackWhere, $limit, $order); + } - $order = '"BlogEntry"."Date" DESC'; + // Apply filters + if($filters) $entries = $entries->filter($filters); - // By specifying a callback, you can alter the SQL, or sort on something other than date. - if($retrieveCallback) return call_user_func($retrieveCallback, 'BlogEntry', $filter, $limit, $order); - - $entries = BlogEntry::get()->where($filter)->sort($order); + // Extension point + $this->extend('updateEntries', $entries, $limit, $tag, $date, $filters, $where); + // Paginate results $list = new PaginatedList($entries, Controller::curr()->request); $list->setPageLength($limit); return $list; @@ -228,66 +229,57 @@ class BlogTree extends Page { } class BlogTree_Controller extends Page_Controller { - + private static $allowed_actions = array( 'index', 'rss', 'tag', 'date' ); - + private static $casting = array( 'SelectedTag' => 'Text', 'SelectedAuthor' => 'Text' ); - - function init() { + + public function init() { parent::init(); - + $this->IncludeBlogRSS(); - + Requirements::themedCSS("blog","blog"); } /** * Determine selected BlogEntry items to show on this page - * + * * @param int $limit * @return PaginatedList */ public function BlogEntries($limit = null) { require_once('Zend/Date.php'); - - if($limit === null) $limit = BlogTree::$default_entries_limit; + $filter = array(); + + // Defaults for limit + if($limit === null) $limit = BlogTree::config()->default_entries_limit; // only use freshness if no action is present (might be displaying tags or rss) - if ($this->LandingPageFreshness && !$this->request->param('Action')) { - $d = new Zend_Date(SS_Datetime::now()->getValue()); - $d->sub($this->LandingPageFreshness, Zend_Date::MONTH); - $date = $d->toString('YYYY-MM-dd'); - - $filter = "\"BlogEntry\".\"Date\" > '$date'"; - } else { - $filter = ''; + $landingPageFreshness = $this->getLandingPageFreshnessMonths(); + if ($landingPageFreshness && !$this->request->param('Action')) { + $date = new Zend_Date(SS_Datetime::now()->getValue()); + $date->sub($landingPageFreshness, Zend_Date::MONTH); + $date = $date->toString('YYYY-MM-dd'); + + $filter["Date:GreaterThan"] = $date; } - // allow filtering by author field and some blogs have an authorID field which - // may allow filtering by id - if(isset($_GET['author']) && isset($_GET['authorID'])) { - $author = Convert::raw2sql($_GET['author']); - $id = Convert::raw2sql($_GET['authorID']); - - $filter .= " \"BlogEntry\".\"Author\" LIKE '". $author . "' OR \"BlogEntry\".\"AuthorID\" = '". $id ."'"; - } - else if(isset($_GET['author'])) { - $filter .= " \"BlogEntry\".\"Author\" LIKE '". Convert::raw2sql($_GET['author']) . "'"; - } - else if(isset($_GET['authorID'])) { - $filter .= " \"BlogEntry\".\"AuthorID\" = '". Convert::raw2sql($_GET['authorID']). "'"; + + // Allow filtering by author field + if($author = $this->SelectedAuthor()) { + $filter['Author:PartialMatch'] = $author; } - $date = $this->SelectedDate(); - - return $this->Entries($limit, $this->SelectedTag(), ($date) ? $date : '', null, $filter); + // Return filtered items + return $this->Entries($limit, $this->SelectedTag(), $this->SelectedDate(), $filter); } /** @@ -296,7 +288,7 @@ class BlogTree_Controller extends Page_Controller { public function IncludeBlogRSS() { RSSFeed::linkToFeed($this->Link('rss'), _t('BlogHolder.RSSFEED',"RSS feed of these blogs")); } - + /** * Get the rss feed for this blog holder's entries */ @@ -305,7 +297,7 @@ class BlogTree_Controller extends Page_Controller { $blogName = $this->Title; $altBlogName = $project_name . ' blog'; - + $entries = $this->Entries(20); if($entries) { @@ -313,18 +305,18 @@ class BlogTree_Controller extends Page_Controller { return $rss->outputToBrowser(); } } - + /** * Protection against infinite loops when an RSS widget pointing to this page is added to this page */ public function defaultAction($action) { if(stristr($_SERVER['HTTP_USER_AGENT'], 'SimplePie')) return $this->rss(); - + return parent::defaultAction($action); } - + /** - * Return the currently viewing tag used in the template as $Tag + * Return the currently viewing tag used in the template as $Tag * * @return string */ @@ -335,68 +327,51 @@ class BlogTree_Controller extends Page_Controller { } return ''; } - + /** * Return the selected date from the blog tree * - * @return string + * @return string Date in format 'year-month', 'year', or false if not a date */ public function SelectedDate() { - if($this->request->latestParam('Action') == 'date') { - $year = $this->request->latestParam('ID'); - $month = $this->request->latestParam('OtherID'); - - if(is_numeric($year) && is_numeric($month) && $month < 13) { - - $date = $year .'-'. $month; - return $date; - - } else { - - if(is_numeric($year)) return $year; - } + if($this->request->latestParam('Action') !== 'date') return false; + + // Check year + $year = $this->request->latestParam('ID'); + if(!is_numeric($year)) return false; + + // Check month + $month = $this->request->latestParam('OtherID'); + if(is_numeric($month) && $month < 13) { + return $year . '-' . $month; + } else { + return $year; } - - return false; } /** * @return string */ public function SelectedAuthor() { - if($this->request->getVar('author')) { - $hasAuthor = BlogEntry::get()->filter('Author', $this->request->getVar('author'))->Count(); - return $hasAuthor - ? $this->request->getVar('author') - : null; - } elseif($this->request->getVar('authorID')) { - $hasAuthor = BlogEntry::get()->filter('AuthorID', $this->request->getVar('authorID'))->Count(); - if($hasAuthor) { - $member = Member::get()->byId($this->request->getVar('authorID')); - if($member) { - if($member->hasMethod('BlogAuthorTitle')) { - return $member->BlogAuthorTitle; - } else { - return $member->Title; - } - } else { - return null; - } - } + if($author = $this->request->getVar('author')) { + $hasAuthor = BlogEntry::get() + ->filter('Author:PartialMatch', $author) + ->Count(); + if($hasAuthor) return $author; } } - + /** - * + * * @return string */ public function SelectedNiceDate(){ $date = $this->SelectedDate(); - + if(strpos($date, '-')) { $date = explode("-",$date); return date("F", mktime(0, 0, 0, $date[1], 1, date('Y'))). " " .date("Y", mktime(0, 0, 0, date('m'), 1, $date[0])); - + } else { return date("Y", mktime(0, 0, 0, date('m'), 1, $date)); } diff --git a/code/MetaWeblogController.php b/code/MetaWeblogController.php index f890a75..5be2007 100644 --- a/code/MetaWeblogController.php +++ b/code/MetaWeblogController.php @@ -7,8 +7,9 @@ require_once(BASE_PATH . '/blog/thirdparty/xmlrpc/xmlrpc_wrappers.php'); /** * MetaWeblogController provides the MetaWeblog API for SilverStripe blogs. */ -class MetaWeblogController extends Controller { - function index($request) { +class MetaWeblogController extends Controller { + + public function index($request) { // Create an xmlrpc server, and set up the method calls $service = new xmlrpc_server(array( @@ -34,16 +35,16 @@ class MetaWeblogController extends Controller { /** * Get a list of BlogHolders the user has access to. */ - function getUsersBlogs($appkey, $username, $password) { + public function getUsersBlogs($appkey, $username, $password) { $member = MemberAuthenticator::authenticate(array( - 'Email' => $username, - 'Password' => $password, + 'Email' => $username, + 'Password' => $password, )); // TODO Throw approriate error. if(!$member) die(); - $blogholders = DataObject::get('BlogHolder'); + $blogholders = SearchForm::get(); $response = array(); @@ -64,7 +65,7 @@ class MetaWeblogController extends Controller { /** * Get the most recent posts on a blog. */ - function getRecentPosts($blogid, $username, $password, $numberOfPosts) { + public function getRecentPosts($blogid, $username, $password, $numberOfPosts) { $member = MemberAuthenticator::authenticate(array( 'Email' => $username, 'Password' => $password, @@ -96,10 +97,8 @@ class MetaWeblogController extends Controller { return $res; } - function getCategories() { + public function getCategories() { //TODO dummy function return array(); } } - -?> diff --git a/code/import/TypoImport.php b/code/import/TypoImport.php deleted file mode 100644 index 2b3b21f..0000000 --- a/code/import/TypoImport.php +++ /dev/null @@ -1,152 +0,0 @@ -Title = "imported blog"; - - // write it! - $bholder->write(); - $bholder->publish("Stage", "Live"); - - // get the typo articles - $result = pg_query($dbconn, "SELECT * FROM contents WHERE type='Article'"); - - while ($row = pg_fetch_row($result)) { - - // title [1] - // author [2] - // body [3] - // body_html [4] (type rendered and cached the html here. This is the preferred blog entry content for migration) - // keywords (space separated) [7] (tags table is just a list of the unique variants of these keywords) - // created_at [8] - // permalink [12] (this is like the url in sitetree, prolly not needed) - // email [18] (address of the commenter) - // url [19] (url of the commenter) - - $title = $row[1]; - $author = $row[2]; - $blog_entry = $row[4]; - $keywords = $row[7]; - $created_at = $row[8]; - - // sometimes it's empty. If it is, grab the body - if ($blog_entry == ""){ - // use "body" - $blog_entry = $row[3]; - } - echo "blog_entry: $blog_entry"; - echo "
\n"; - - // put the typo blog entry in the SS database - $newEntry = new BlogEntry(); - $newEntry->Title = $title; - $newEntry->Author = $author; - $newEntry->Content = $blog_entry; - $newEntry->Tags = $keywords; - $newEntry->Date = $created_at; - - // tie each blog entry back to the blogholder we created initially - $newEntry->ParentID = $bholder->ID; - - // write it! - $newEntry->write(); - $newEntry->publish("Stage", "Live"); - - // grab the id so we can get the comments - $old_article_id = $row[0]; - - // get the comments - $result2 = pg_query($dbconn, "SELECT * FROM contents WHERE type = 'Comment' AND article_id = $old_article_id"); - - while ($row2 = pg_fetch_row($result2)) { - // grab the body_html - $comment = $row2[4]; - - // sometimes it's empty. If it is, grab the body - if ($comment == ""){ - // use "body" - $comment = $row2[3]; - } - - - - - $Cauthor = $row2[2]; - $Ccreated_at = $row2[8]; - - // put the typo blog comment in the SS database - $newCEntry = new PageComment(); - $newCEntry->Name = $Cauthor; - $newCEntry->Comment = $comment; - $newCEntry->Created = $created_at; - - // need to grab the newly inserted blog entry's id - $newCEntry->ParentID = $newEntry->ID; - - // write it! - $newCEntry->write(); - - echo "comment: $comment"; - echo "
\n"; - } - - $newEntry->flushCache(); - - // fix up the specialchars - pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"×\", \"x\")"); - pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"’\", \"’\")"); - pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"‘\", \"‘\")"); - pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"—\", \"—\")"); - pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"“\", \"“\")"); - pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"”\", \"”\")"); - pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"–\", \"–\")"); - pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"—\", \"—\")"); - pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"…\", \"…\")"); - pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"™\", \"™\")"); - pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"&\", \"&\")"); - - pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"×\", \"x\")"); - pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"’\", \"’\")"); - pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"‘\", \"‘\")"); - pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"—\", \"—\")"); - pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"“\", \"“\")"); - pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"”\", \"”\")"); - pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"–\", \"–\")"); - pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"—\", \"—\")"); - pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"…\", \"…\")"); - pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"™\", \"™\")"); - pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"&\", \"&\")"); - - - } - - pg_close($dbconn); - - } // end function - -} // end class -?> diff --git a/code/widgets/ArchiveWidget.php b/code/widgets/ArchiveWidget.php index 21c465d..b8b7624 100644 --- a/code/widgets/ArchiveWidget.php +++ b/code/widgets/ArchiveWidget.php @@ -1,117 +1,115 @@ 'Varchar' - ); - - private static $defaults = array( - 'DisplayMode' => 'month' - ); - - private static $title = 'Browse by Date'; - - private static $cmsTitle = 'Blog Archive'; - - private static $description = - 'Show a list of months or years in which there are blog posts, and provide links to them.'; - - function getCMSFields() { - $fields = parent::getCMSFields(); - - $fields->merge( +/** + * Shows a widget with viewing blog entries + * by months or years. + * + * @package blog + */ +class ArchiveWidget extends Widget { - new FieldList( - new OptionsetField( - 'DisplayMode', - _t('ArchiveWidget.DispBY', 'Display by'), - array( - 'month' => _t('ArchiveWidget.MONTH', 'month'), - 'year' => _t('ArchiveWidget.YEAR', 'year') - ) + private static $db = array( + 'DisplayMode' => 'Varchar' + ); + + private static $defaults = array( + 'DisplayMode' => 'month' + ); + + private static $title = 'Browse by Date'; + + private static $cmsTitle = 'Blog Archive'; + + private static $description = + 'Show a list of months or years in which there are blog posts, and provide links to them.'; + + public function getCMSFields() { + $fields = parent::getCMSFields(); + + $fields->merge( + + new FieldList( + new OptionsetField( + 'DisplayMode', + _t('ArchiveWidget.DispBY', 'Display by'), + array( + 'month' => _t('ArchiveWidget.MONTH', 'month'), + 'year' => _t('ArchiveWidget.YEAR', 'year') ) - ) - ); - - $this->extend('updateCMSFields', $fields); - - return $fields; - } - - function getDates() { - Requirements::themedCSS('archivewidget'); - - $results = new ArrayList(); - $container = BlogTree::current(); - $ids = $container->BlogHolderIDs(); - - $stage = Versioned::current_stage(); - $suffix = (!$stage || $stage == 'Stage') ? "" : "_$stage"; + ) + ) + ); - if(method_exists(DB::getConn(), 'formattedDatetimeClause')) { - $monthclause = DB::getConn()->formattedDatetimeClause('"Date"', '%m'); - $yearclause = DB::getConn()->formattedDatetimeClause('"Date"', '%Y'); - } else { - $monthclause = 'MONTH("Date")'; - $yearclause = 'YEAR("Date")'; - } - - if($this->DisplayMode == 'month') { - $sqlResults = DB::query(" - SELECT DISTINCT CAST($monthclause AS " . DB::getConn()->dbDataType('unsigned integer') . ") - AS \"Month\", - $yearclause AS \"Year\" - FROM \"SiteTree$suffix\" INNER JOIN \"BlogEntry$suffix\" - ON \"SiteTree$suffix\".\"ID\" = \"BlogEntry$suffix\".\"ID\" - WHERE \"ParentID\" IN (" . implode(', ', $ids) . ") - ORDER BY \"Year\" DESC, \"Month\" DESC;" - ); - } else { - $sqlResults = DB::query(" - SELECT DISTINCT $yearclause AS \"Year\" - FROM \"SiteTree$suffix\" INNER JOIN \"BlogEntry$suffix\" - ON \"SiteTree$suffix\".\"ID\" = \"BlogEntry$suffix\".\"ID\" - WHERE \"ParentID\" IN (" . implode(', ', $ids) . ") - ORDER BY \"Year\" DESC" - ); - } - - if($sqlResults) foreach($sqlResults as $sqlResult) { - $isMonthDisplay = $this->DisplayMode == 'month'; - - $monthVal = (isset($sqlResult['Month'])) ? (int) $sqlResult['Month'] : 1; - $month = ($isMonthDisplay) ? $monthVal : 1; - $year = ($sqlResult['Year']) ? (int) $sqlResult['Year'] : date('Y'); - - $date = DBField::create_field('Date', array( - 'Day' => 1, - 'Month' => $month, - 'Year' => $year - )); - - if($isMonthDisplay) { - $link = $container->Link('date') . '/' . $sqlResult['Year'] . '/' . sprintf("%'02d", $monthVal); - } else { - $link = $container->Link('date') . '/' . $sqlResult['Year']; - } - - $results->push(new ArrayData(array( - 'Date' => $date, - 'Link' => $link - ))); - } - - return $results; - } + $this->extend('updateCMSFields', $fields); + + return $fields; } + public function getDates() { + Requirements::themedCSS('archivewidget'); + + $results = new ArrayList(); + $container = BlogTree::current(); + $ids = $container->BlogHolderIDs(); + + $stage = Versioned::current_stage(); + $suffix = (!$stage || $stage == 'Stage') ? "" : "_$stage"; + + if(method_exists(DB::getConn(), 'formattedDatetimeClause')) { + $monthclause = DB::getConn()->formattedDatetimeClause('"Date"', '%m'); + $yearclause = DB::getConn()->formattedDatetimeClause('"Date"', '%Y'); + } else { + $monthclause = 'MONTH("Date")'; + $yearclause = 'YEAR("Date")'; + } + + if($this->DisplayMode == 'month') { + $sqlResults = DB::query(" + SELECT DISTINCT CAST($monthclause AS " . DB::getConn()->dbDataType('unsigned integer') . ") + AS \"Month\", + $yearclause AS \"Year\" + FROM \"SiteTree$suffix\" INNER JOIN \"BlogEntry$suffix\" + ON \"SiteTree$suffix\".\"ID\" = \"BlogEntry$suffix\".\"ID\" + WHERE \"ParentID\" IN (" . implode(', ', $ids) . ") + ORDER BY \"Year\" DESC, \"Month\" DESC;" + ); + } else { + $sqlResults = DB::query(" + SELECT DISTINCT $yearclause AS \"Year\" + FROM \"SiteTree$suffix\" INNER JOIN \"BlogEntry$suffix\" + ON \"SiteTree$suffix\".\"ID\" = \"BlogEntry$suffix\".\"ID\" + WHERE \"ParentID\" IN (" . implode(', ', $ids) . ") + ORDER BY \"Year\" DESC" + ); + } + + if($sqlResults) foreach($sqlResults as $sqlResult) { + $isMonthDisplay = $this->DisplayMode == 'month'; + + $monthVal = (isset($sqlResult['Month'])) ? (int) $sqlResult['Month'] : 1; + $month = ($isMonthDisplay) ? $monthVal : 1; + $year = ($sqlResult['Year']) ? (int) $sqlResult['Year'] : date('Y'); + + $date = DBField::create_field('Date', array( + 'Day' => 1, + 'Month' => $month, + 'Year' => $year + )); + + if($isMonthDisplay) { + $link = $container->Link('date') . '/' . $sqlResult['Year'] . '/' . sprintf("%'02d", $monthVal); + } else { + $link = $container->Link('date') . '/' . $sqlResult['Year']; + } + + $results->push(new ArrayData(array( + 'Date' => $date, + 'Link' => $link + ))); + } + + return $results; + } } diff --git a/code/widgets/BlogManagementWidget.php b/code/widgets/BlogManagementWidget.php index 8f3c2fa..a2dc2ef 100644 --- a/code/widgets/BlogManagementWidget.php +++ b/code/widgets/BlogManagementWidget.php @@ -1,60 +1,66 @@ value(); - if($unmoderatedcount == 1) { - return _t("BlogManagementWidget.UNM1", "You have 1 unmoderated comment"); - } else if($unmoderatedcount > 1) { - return sprintf(_t("BlogManagementWidget.UNMM", "You have %i unmoderated comments"), $unmoderatedcount); - } else { - return _t("BlogManagementWidget.COMADM", "Comment administration"); - } + public function CommentText() { + if(!class_exists('Comment')) return false; + $unmoderatedcount = DB::query("SELECT COUNT(*) FROM \"Comment\" WHERE \"Moderated\"=1")->value(); + if($unmoderatedcount == 1) { + return _t("BlogManagementWidget.UNM1", "You have 1 unmoderated comment"); + } else if($unmoderatedcount > 1) { + return sprintf(_t("BlogManagementWidget.UNMM", "You have %i unmoderated comments"), $unmoderatedcount); + } else { + return _t("BlogManagementWidget.COMADM", "Comment administration"); } - - function CommentLink() { - - if(!Permission::check('BLOGMANAGEMENT') || !class_exists('Comment')) return false; - $unmoderatedcount = DB::query("SELECT COUNT(*) FROM \"Comment\" WHERE \"Moderated\"=1")->value(); - - if($unmoderatedcount > 0) { - return "admin/comments/unmoderated"; - } else { - return "admin/comments"; - } - } - } - class BlogManagementWidget_Controller extends Widget_Controller { - - function WidgetHolder() { - if(Permission::check("BLOGMANAGEMENT")) { - return $this->renderWith("WidgetHolder"); - } - } - - function PostLink() { - $container = BlogTree::current(); - return ($container && $container->ClassName != "BlogTree") ? $container->Link('post') : false; + /** + * Link to edit comment + * + * @return string + */ + public function CommentLink() { + if(!Permission::check('BLOGMANAGEMENT') || !class_exists('Comment')) return false; + $unmoderatedcount = DB::query("SELECT COUNT(*) FROM \"Comment\" WHERE \"Moderated\"=1")->value(); + + if($unmoderatedcount > 0) { + return "admin/comments/unmoderated"; + } else { + return "admin/comments"; } } } + +class BlogManagementWidget_Controller extends Widget_Controller { + + public function WidgetHolder() { + if(Permission::check("BLOGMANAGEMENT")) { + return $this->renderWith("WidgetHolder"); + } + } + + public function PostLink() { + $container = BlogTree::current(); + return ($container && $container->ClassName != "BlogTree") ? $container->Link('post') : false; + } +} diff --git a/code/widgets/RSSWidget.php b/code/widgets/RSSWidget.php index 5615436..2b9e6d9 100644 --- a/code/widgets/RSSWidget.php +++ b/code/widgets/RSSWidget.php @@ -1,109 +1,107 @@ "Text", + "RssUrl" => "Text", + "NumberToShow" => "Int" + ); + + private static $defaults = array( + "NumberToShow" => 10, + "RSSTitle" => 'RSS Feed' + ); + + private static $cmsTitle = "RSS Feed"; + + private static $description = "Downloads another page's RSS feed and displays items in a list."; + /** - * Presents a list of items from an RSS feed url - * - * @package blog + * If the RssUrl is relative, convert it to absolute with the + * current baseURL to avoid confusing simplepie. + * Passing relative URLs to simplepie will result + * in strange DNS lookups and request timeouts. + * + * @return string */ - class RSSWidget extends Widget { - - private static $db = array( - "RSSTitle" => "Text", - "RssUrl" => "Text", - "NumberToShow" => "Int" - ); - - private static $defaults = array( - "NumberToShow" => 10, - "RSSTitle" => 'RSS Feed' - ); - - private static $cmsTitle = "RSS Feed"; - - private static $description = "Downloads another page's RSS feed and displays items in a list."; - - /** - * If the RssUrl is relative, convert it to absolute with the - * current baseURL to avoid confusing simplepie. - * Passing relative URLs to simplepie will result - * in strange DNS lookups and request timeouts. - * - * @return string - */ - function getAbsoluteRssUrl() { - $urlParts = parse_url($this->RssUrl); - if(!isset($urlParts['host']) || !$urlParts['host']) { - return Director::absoluteBaseURL() . $this->RssUrl; - } else { - return $this->RssUrl; - } - } - - function getCMSFields() { - $fields = parent::getCMSFields(); - - $fields->merge( - new FieldList( - new TextField("RSSTitle", _t('RSSWidget.CT', "Custom title for the feed")), - new TextField("RssUrl", _t( - 'RSSWidget.URL', - "URL of the other page's RSS feed. Please make sure this URL points to an RSS feed." - )), - new NumericField("NumberToShow", _t('RSSWidget.NTS', "Number of Items to show")) - ) - ); - - $this->extend('updateCMSFields', $fields); - - return $fields; - } - - function Title() { - return ($this->RSSTitle) ? $this->RSSTitle : _t('RSSWidget.DEFAULTTITLE', 'RSS Feed'); - } - - function getFeedItems() { - $output = new ArrayList(); - - // Protection against infinite loops when an RSS widget pointing to this page is added to this page - if(stristr($_SERVER['HTTP_USER_AGENT'], 'SimplePie')) { - return $output; - } - - if(!class_exists('SimplePie')) { - throw new LogicException( - 'Please install the "simplepie/simplepie" library by adding it to the "require" ' - + 'section of your composer.json' - ); - } - - $t1 = microtime(true); - $feed = new SimplePie(); - $feed->set_feed_url($this->AbsoluteRssUrl); - $feed->set_cache_location(TEMP_FOLDER); - $feed->init(); - if($items = $feed->get_items(0, $this->NumberToShow)) { - foreach($items as $item) { - - // Cast the Date - $date = new Date('Date'); - $date->setValue($item->get_date()); - - // Cast the Title - $title = new Text('Title'); - $title->setValue(html_entity_decode($item->get_title())); - - $output->push(new ArrayData(array( - 'Title' => $title, - 'Date' => $date, - 'Link' => $item->get_link() - ))); - } - return $output; - } + public function getAbsoluteRssUrl() { + $urlParts = parse_url($this->RssUrl); + if(!isset($urlParts['host']) || !$urlParts['host']) { + return Director::absoluteBaseURL() . $this->RssUrl; + } else { + return $this->RssUrl; } } + public function getCMSFields() { + $fields = parent::getCMSFields(); + + $fields->merge( + new FieldList( + new TextField("RSSTitle", _t('RSSWidget.CT', "Custom title for the feed")), + new TextField("RssUrl", _t( + 'RSSWidget.URL', + "URL of the other page's RSS feed. Please make sure this URL points to an RSS feed." + )), + new NumericField("NumberToShow", _t('RSSWidget.NTS', "Number of Items to show")) + ) + ); + + $this->extend('updateCMSFields', $fields); + + return $fields; + } + + public function Title() { + return $this->RSSTitle ?: _t('RSSWidget.DEFAULTTITLE', 'RSS Feed'); + } + + public function getFeedItems() { + $output = new ArrayList(); + + // Protection against infinite loops when an RSS widget pointing to this page is added to this page + if(stristr($_SERVER['HTTP_USER_AGENT'], 'SimplePie')) { + return $output; + } + + if(!class_exists('SimplePie')) { + throw new LogicException( + 'Please install the "simplepie/simplepie" library by adding it to the "require" ' + + 'section of your composer.json' + ); + } + + $t1 = microtime(true); + $feed = new SimplePie(); + $feed->set_feed_url($this->AbsoluteRssUrl); + $feed->set_cache_location(TEMP_FOLDER); + $feed->init(); + if($items = $feed->get_items(0, $this->NumberToShow)) { + foreach($items as $item) { + + // Cast the Date + $date = new Date('Date'); + $date->setValue($item->get_date()); + + // Cast the Title + $title = new Text('Title'); + $title->setValue(html_entity_decode($item->get_title())); + + $output->push(new ArrayData(array( + 'Title' => $title, + 'Date' => $date, + 'Link' => $item->get_link() + ))); + } + return $output; + } + } } diff --git a/code/widgets/SubscribeRSSWidget.php b/code/widgets/SubscribeRSSWidget.php index 8190c47..26f288c 100644 --- a/code/widgets/SubscribeRSSWidget.php +++ b/code/widgets/SubscribeRSSWidget.php @@ -1,33 +1,31 @@ Link('rss'); - } + public function getRSSLink() { + Requirements::themedCSS('subscribersswidget'); + $container = BlogTree::current(); + if ($container) return $container->Link('rss'); } - } diff --git a/code/widgets/TagCloudWidget.php b/code/widgets/TagCloudWidget.php index a07e3d9..6aaf282 100644 --- a/code/widgets/TagCloudWidget.php +++ b/code/widgets/TagCloudWidget.php @@ -1,155 +1,154 @@ "Varchar", + "Limit" => "Int", + "Sortby" => "Varchar" + ); + + private static $defaults = array( + "Title" => "Tag Cloud", + "Limit" => "0", + "Sortby" => "alphabet" + ); + + private static $cmsTitle = "Tag Cloud"; + + private static $description = "Shows a tag cloud of tags on your blog."; + /** - * A list of tags associated with blog posts - * - * @package blog + * List of popularity classes in order of least to most popular + * + * @config + * @var array */ - class TagCloudWidget extends Widget { - - private static $db = array( - "Title" => "Varchar", - "Limit" => "Int", - "Sortby" => "Varchar" - ); + private static $popularities = array( + 'not-popular', + 'not-very-popular', + 'somewhat-popular', + 'popular', + 'very-popular', + 'ultra-popular' + ); - private static $defaults = array( - "Title" => "Tag Cloud", - "Limit" => "0", - "Sortby" => "alphabet" - ); + public function getCMSFields() { - private static $cmsTitle = "Tag Cloud"; - - private static $description = "Shows a tag cloud of tags on your blog."; - - /** - * List of popularity classes in order of least to most popular - * - * @config - * @var array - */ - private static $popularities = array( - 'not-popular', - 'not-very-popular', - 'somewhat-popular', - 'popular', - 'very-popular', - 'ultra-popular' - ); - - public function getCMSFields() { - - $this->beforeUpdateCMSFields(function($fields) { - $fields->merge( - new FieldList( - new TextField("Title", _t("TagCloudWidget.TILE", "Title")), - new TextField("Limit", _t("TagCloudWidget.LIMIT", "Limit number of tags")), - new OptionsetField( - "Sortby", - _t("TagCloudWidget.SORTBY", "Sort by"), - array( - "alphabet" => _t("TagCloudWidget.SBAL", "alphabet"), - "frequency" => _t("TagCloudWidget.SBFREQ", "frequency") - ) + $this->beforeUpdateCMSFields(function($fields) { + $fields->merge( + new FieldList( + new TextField("Title", _t("TagCloudWidget.TILE", "Title")), + new TextField("Limit", _t("TagCloudWidget.LIMIT", "Limit number of tags")), + new OptionsetField( + "Sortby", + _t("TagCloudWidget.SORTBY", "Sort by"), + array( + "alphabet" => _t("TagCloudWidget.SBAL", "alphabet"), + "frequency" => _t("TagCloudWidget.SBFREQ", "frequency") ) ) - ); + ) + ); + }); + + return parent::getCMSFields(); + } + + public function Title() { + return $this->Title ?: _t('TagCloudWidget.DEFAULTTITLE', 'Tag Cloud'); + } + + /** + * Current BlogTree used as the container for this tagcloud. + * Used by {@link TagCloudWidgetTest} for testing + * + * @var BlogTree + */ + public static $container = null; + + /** + * Return all sorted tags in the system + * + * @return ArrayList + */ + public function getTagsCollection() { + Requirements::themedCSS("tagcloud"); + + // Ensure there is a valid BlogTree with entries + $container = BlogTree::current(self::$container); + if( !$container + || !($entries = $container->Entries()) + || $entries->count() == 0 + ) return null; + + // Extract all tags from each entry + $tagCounts = array(); // Mapping of tag => frequency + $tagLabels = array(); // Mapping of tag => label + foreach($entries as $entry) { + $theseTags = $entry->TagNames(); + foreach($theseTags as $tag => $tagLabel) { + $tagLabels[$tag] = $tagLabel; + //getting the count into key => value map + $tagCounts[$tag] = isset($tagCounts[$tag]) ? $tagCounts[$tag] + 1 : 1; + } + } + if(empty($tagCounts)) return null; + $minCount = min($tagCounts); + $maxCount = max($tagCounts); + + // Apply sorting mechanism + if($this->Sortby == "alphabet") { + // Sort by name + ksort($tagCounts); + } else { + // Sort by frequency + uasort($tagCounts, function($a, $b) { + return $b - $a; }); - - return parent::getCMSFields(); } - function Title() { - return $this->Title ? $this->Title : _t('TagCloudWidget.DEFAULTTITLE', 'Tag Cloud'); - } - - /** - * Current BlogTree used as the container for this tagcloud. - * Used by {@link TagCloudWidgetTest} for testing - * - * @var BlogTree - */ - public static $container = null; + // Apply limiting + if($this->Limit > 0) $tagCounts = array_slice($tagCounts, 0, $this->Limit, true); - /** - * Return all sorted tags in the system - * - * @return ArrayList - */ - function getTagsCollection() { - Requirements::themedCSS("tagcloud"); + // Calculate buckets of popularities + $numsizes = count(array_unique($tagCounts)); //Work out the number of different sizes + $popularities = self::config()->popularities; + $buckets = count($popularities); - // Ensure there is a valid BlogTree with entries - $container = BlogTree::current(self::$container); - if( !$container - || !($entries = $container->Entries()) - || $entries->count() == 0 - ) return null; + // If there are more frequencies than buckets, divide frequencies into buckets + if ($numsizes > $buckets) $numsizes = $buckets; - // Extract all tags from each entry - $tagCounts = array(); // Mapping of tag => frequency - $tagLabels = array(); // Mapping of tag => label - foreach($entries as $entry) { - $theseTags = $entry->TagNames(); - foreach($theseTags as $tag => $tagLabel) { - $tagLabels[$tag] = $tagLabel; - //getting the count into key => value map - $tagCounts[$tag] = isset($tagCounts[$tag]) ? $tagCounts[$tag] + 1 : 1; - } - } - if(empty($tagCounts)) return null; - $minCount = min($tagCounts); - $maxCount = max($tagCounts); + // Adjust offset to use central buckets (if using a subset of available buckets) + $offset = round(($buckets - $numsizes)/2); - // Apply sorting mechanism - if($this->Sortby == "alphabet") { - // Sort by name - ksort($tagCounts); + $output = new ArrayList(); + foreach($tagCounts as $tag => $count) { + + // Find position of $count in the selected range, adjusted for bucket range used + if($maxCount == $minCount) { + $popularity = $offset; } else { - // Sort by frequency - uasort($tagCounts, function($a, $b) { - return $b - $a; - }); + $popularity = round( + ($count-$minCount) / ($maxCount-$minCount) * ($numsizes-1) + ) + $offset; } - - // Apply limiting - if($this->Limit > 0) $tagCounts = array_slice($tagCounts, 0, $this->Limit, true); + $class = $popularities[$popularity]; - // Calculate buckets of popularities - $numsizes = count(array_unique($tagCounts)); //Work out the number of different sizes - $popularities = self::config()->popularities; - $buckets = count($popularities); - - // If there are more frequencies than buckets, divide frequencies into buckets - if ($numsizes > $buckets) $numsizes = $buckets; - - // Adjust offset to use central buckets (if using a subset of available buckets) - $offset = round(($buckets - $numsizes)/2); - - $output = new ArrayList(); - foreach($tagCounts as $tag => $count) { - - // Find position of $count in the selected range, adjusted for bucket range used - if($maxCount == $minCount) { - $popularity = $offset; - } else { - $popularity = round( - ($count-$minCount) / ($maxCount-$minCount) * ($numsizes-1) - ) + $offset; - } - $class = $popularities[$popularity]; - - $output->push(new ArrayData(array( - "Tag" => $tagLabels[$tag], - "Count" => $count, - "Class" => $class, - "Link" => Controller::join_links($container->Link('tag'), urlencode($tag)) - ))); - } - return $output; + $output->push(new ArrayData(array( + "Tag" => $tagLabels[$tag], + "Count" => $count, + "Class" => $class, + "Link" => Controller::join_links($container->Link('tag'), urlencode($tag)) + ))); } + return $output; } } diff --git a/composer.json b/composer.json index 7afc4a1..f3abe6a 100644 --- a/composer.json +++ b/composer.json @@ -4,22 +4,22 @@ "type": "silverstripe-module", "keywords": ["silverstripe", "blog"], "authors": [ - { - "name": "Saophalkun Ponlu", - "email": "phalkunz@silverstripe.com" - }, - { - "name": "Carlos Barberis", - "email": "carlos@silverstripe.com" - } + { + "name": "Saophalkun Ponlu", + "email": "phalkunz@silverstripe.com" + }, + { + "name": "Carlos Barberis", + "email": "carlos@silverstripe.com" + } ], - - "require": - { - "silverstripe/cms": "~3.1" + "require": { + "silverstripe/cms": "~3.2" }, - "suggest": - { + "require-dev": { + "phpunit/PHPUnit": "~3.7@stable" + }, + "suggest": { "silverstripe/widgets": "Additional 'sidebar features', e.g. a list of recent posts and a tagcloud", "silverstripe/comments": "Enable user comments on any page type, including blog posts", "simplepie/simplepie": "Parse RSS feeds, required for the RSS widget" diff --git a/tests/BlogTreeTest.php b/tests/BlogTreeTest.php index 6c14845..86be699 100644 --- a/tests/BlogTreeTest.php +++ b/tests/BlogTreeTest.php @@ -72,11 +72,12 @@ class BlogTreeTest extends SapphireTest { function testLandingPageFreshness() { $node = $this->objFromFixture('BlogTree', 'root'); - $this->assertEquals($node->LandingPageFreshness, '7 DAYS'); + $this->assertEquals('7', $node->LandingPageFreshness); $node = $this->objFromFixture('BlogTree', 'levela'); - $this->assertEquals($node->LandingPageFreshness, '2 DAYS'); + $this->assertEquals('2', $node->LandingPageFreshness); $node = $this->objFromFixture('BlogTree', 'levelb'); - $this->assertEquals($node->LandingPageFreshness, '7 DAYS'); + $this->assertEquals('INHERIT', $node->LandingPageFreshness); + $this->assertEquals('7', $node->getLandingPageFreshnessMonths()); } function testGettingAssociatedBlogTree() { diff --git a/tests/BlogTreeTest.yml b/tests/BlogTreeTest.yml index b66730d..2d354e8 100644 --- a/tests/BlogTreeTest.yml +++ b/tests/BlogTreeTest.yml @@ -1,13 +1,13 @@ BlogTree: root: Title: Root BlogTree - LandingPageFreshness: 7 DAYS + LandingPageFreshness: 7 otherroot: Title: Other root BlogTree levela: Title: Level A Parent: =>BlogTree.root - LandingPageFreshness: 2 DAYS + LandingPageFreshness: 2 levelb: Title: Level B Parent: =>BlogTree.root @@ -27,7 +27,7 @@ BlogHolder: levelaa_blog1: Title: Level AA Blog 1 Parent: =>BlogTree.levelaa - LandingPageFreshness: 1 DAY + LandingPageFreshness: 1 levelaa_blog2: Title: Level AA Blog 2 Parent: =>BlogTree.levelaa