Skip to content
Browse files

Created 0.4.0-rc1 release

  • Loading branch information...
1 parent af61e6b commit 84af09c231561f736fd25e88dd402bfef034aec2 @cbarberis cbarberis committed
Showing with 9,462 additions and 0 deletions.
  1. +42 −0 0.4.0-rc1/ChangeLog
  2. +15 −0 0.4.0-rc1/README
  3. +7 −0 0.4.0-rc1/_config.php
  4. +232 −0 0.4.0-rc1/code/BlogEntry.php
  5. +275 −0 0.4.0-rc1/code/BlogHolder.php
  6. +280 −0 0.4.0-rc1/code/BlogTree.php
  7. +106 −0 0.4.0-rc1/code/MetaWeblogController.php
  8. +52 −0 0.4.0-rc1/code/TrackBackDecorator.php
  9. +24 −0 0.4.0-rc1/code/TrackBackPing.php
  10. +152 −0 0.4.0-rc1/code/import/TypoImport.php
  11. +106 −0 0.4.0-rc1/code/widgets/ArchiveWidget.php
  12. +63 −0 0.4.0-rc1/code/widgets/BlogManagementWidget.php
  13. +92 −0 0.4.0-rc1/code/widgets/RSSWidget.php
  14. +31 −0 0.4.0-rc1/code/widgets/SubscribeRSSWidget.php
  15. +145 −0 0.4.0-rc1/code/widgets/TagCloudWidget.php
  16. +9 −0 0.4.0-rc1/css/archivewidget.css
  17. +32 −0 0.4.0-rc1/css/bbcodehelp.css
  18. +11 −0 0.4.0-rc1/css/blog.css
  19. +3 −0 0.4.0-rc1/css/flickrwidget.css
  20. +4 −0 0.4.0-rc1/css/subscribersswidget.css
  21. +6 −0 0.4.0-rc1/css/tagcloud.css
  22. BIN 0.4.0-rc1/images/blogholder-file.gif
  23. BIN 0.4.0-rc1/images/blogpage-file.gif
  24. BIN 0.4.0-rc1/images/feed-icon-14x14.png
  25. BIN 0.4.0-rc1/images/feed-icon-28x28.png
  26. +12 −0 0.4.0-rc1/javascript/bbcodehelp.js
  27. 0 0.4.0-rc1/lang/_manifest_exclude
  28. +81 −0 0.4.0-rc1/lang/ar_SA.php
  29. +63 −0 0.4.0-rc1/lang/bg_BG.php
  30. +61 −0 0.4.0-rc1/lang/da_DK.php
  31. +81 −0 0.4.0-rc1/lang/de_DE.php
  32. +81 −0 0.4.0-rc1/lang/en_GB.php
  33. +140 −0 0.4.0-rc1/lang/en_US.php
  34. +20 −0 0.4.0-rc1/lang/es_419.php
  35. +81 −0 0.4.0-rc1/lang/es_ES.php
  36. +81 −0 0.4.0-rc1/lang/es_MX.php
  37. +81 −0 0.4.0-rc1/lang/et_EE.php
  38. +79 −0 0.4.0-rc1/lang/fr_FR.php
  39. +56 −0 0.4.0-rc1/lang/hr_HR.php
  40. +78 −0 0.4.0-rc1/lang/is_IS.php
  41. +83 −0 0.4.0-rc1/lang/it_IT.php
  42. +34 −0 0.4.0-rc1/lang/ms_MY.php
  43. +81 −0 0.4.0-rc1/lang/nl_NL.php
  44. +71 −0 0.4.0-rc1/lang/pl_PL.php
  45. +41 −0 0.4.0-rc1/lang/pt_PT.php
  46. +63 −0 0.4.0-rc1/lang/ru_RU.php
  47. +58 −0 0.4.0-rc1/lang/sr_RS.php
  48. +81 −0 0.4.0-rc1/lang/tr_TR.php
  49. +21 −0 0.4.0-rc1/templates/ArchiveWidget.ss
  50. +5 −0 0.4.0-rc1/templates/BlogManagementWidget.ss
  51. +27 −0 0.4.0-rc1/templates/Includes/BlogPagination.ss
  52. +3 −0 0.4.0-rc1/templates/Includes/BlogSideBar.ss
  53. +14 −0 0.4.0-rc1/templates/Includes/BlogSummary.ss
  54. +20 −0 0.4.0-rc1/templates/Includes/TrackBacks.ss
  55. +30 −0 0.4.0-rc1/templates/Layout/BlogEntry.ss
  56. +21 −0 0.4.0-rc1/templates/Layout/BlogHolder.ss
  57. +7 −0 0.4.0-rc1/templates/RSSWidget.ss
  58. +5 −0 0.4.0-rc1/templates/SubscribeRSSWidget.ss
  59. +5 −0 0.4.0-rc1/templates/TagCloudWidget.ss
  60. +5 −0 0.4.0-rc1/templates/TrackBackPingReturn.ss
  61. +3 −0 0.4.0-rc1/templates/TrackBackRdf.ss
  62. +52 −0 0.4.0-rc1/tests/BlogHolderFunctionalTest.php
  63. +20 −0 0.4.0-rc1/tests/BlogHolderFunctionalTest.yml
  64. +59 −0 0.4.0-rc1/tests/BlogHolderTest.php
  65. +28 −0 0.4.0-rc1/tests/BlogTest.yml
  66. +30 −0 0.4.0-rc1/tests/BlogTreeTest.php
  67. +53 −0 0.4.0-rc1/tests/BlogTreeTest.yml
  68. +3,718 −0 0.4.0-rc1/thirdparty/xmlrpc/xmlrpc.php
  69. +944 −0 0.4.0-rc1/thirdparty/xmlrpc/xmlrpc_wrappers.php
  70. +1,198 −0 0.4.0-rc1/thirdparty/xmlrpc/xmlrpcs.php
View
42 0.4.0-rc1/ChangeLog
@@ -0,0 +1,42 @@
+ChangeLog
+
+0.2.0
+
+Features
+ - Blogs can now be configured to use HTML instead of BBCode
+ - Tags now follow the rel-tag microformat standard
+ - Blog module is now translatable
+ - The entries shown on the BlogHolder when not browsing by date/tag can now be restricted to only show entries that are younger than a user specified age
+ - The RSS feed name can now be changed in the CMS
+ - Added support for receiving trackback pings
+ - Added SubscribeRSSWidget for linking directly to the blog RSS feed
+ - Tag widget title is now editable
+ - Added empty relationship statics so BlogEntry and BlogHolder can be decorated by a DataObjectDecorator
+ - Use pagination summary, so a full list of pages isnt generated
+ - Added Date variable to RSSWidget feed items, so Date can be used in template if wanted
+ - Cast Title variable on RSSWidget feed items, so Title can have Text functions called in the template if wanted
+
+Bugfixes
+ - Removed deprecated calls to sapphire, and made other fixes to support sapphire 2.3.0
+ - Don't use PHP short tags
+ - Don't display $Content on a BlogHolder, as it isnt editable in the CMS
+ - Prevent infinite loops when an RSSWidget on a blog points to itself
+ - Fix URL segment generation
+ - RSS feed is now sorted by date, newest first
+ - Fixed pagination
+ - Fixed summaries on BlogHolder
+ - Fixed issues with display by month when blog post is on last month of the day
+ - BlogEntry::Tags() was renamed to TagsCollection() to prevent conflicts with the database fields called Tags
+ - Fixed invalid use of single quotes in BlogEntryForm HTML
+ - Fixed extra <p> tags around blog content
+ - Default parent needs to be a string instead of an array
+ - Fixed escaping in BlogHolder
+ - Use themedCSS instead of hardlinking paths
+ - Fixed rss feed caching
+ - Fixed archive widget showing months and years for unpublished posts
+ - SetDate doesn't need to be called, as the date is automatically set
+
+
+0.1
+
+Initial release
View
15 0.4.0-rc1/README
@@ -0,0 +1,15 @@
+####################################################
+Blog Module
+####################################################
+
+# Maintainer Contact
+Andrew O'Neil (Nickname: aoneil)
+<andrew (at) silverstripe (dot) com>
+
+# Requirements
+SilverStripe minimum version 2.3.0
+
+# Documentation
+http://doc.silverstripe.com/doku.php?id=modules:blog
+
+
View
7 0.4.0-rc1/_config.php
@@ -0,0 +1,7 @@
+<?php
+
+Director::addRules(10, array(
+ 'metaweblog' => 'MetaWeblogController'
+));
+
+?>
View
232 0.4.0-rc1/code/BlogEntry.php
@@ -0,0 +1,232 @@
+<?php
+/**
+ * An individual blog entry page type.
+ *
+ * @package blog
+ */
+class BlogEntry extends Page {
+ static $db = array(
+ "Date" => "Datetime",
+ "Author" => "Text",
+ "Tags" => "Text"
+ );
+
+ static $default_parent = 'BlogHolder';
+
+ static $can_be_root = false;
+
+ static $icon = "blog/images/blogpage";
+
+ static $has_one = array();
+
+ static $has_many = array();
+
+ static $many_many = array();
+
+ static $belongs_many_many = array();
+
+ static $defaults = array(
+ "ProvideComments" => true,
+ 'ShowInMenus' => false
+ );
+
+ static $extensions = array(
+ 'Hierarchy',
+ 'TrackBackDecorator',
+ "Versioned('Stage', 'Live')"
+ );
+
+ /**
+ * Is WYSIWYG editing allowed?
+ * @var boolean
+ */
+ static $allow_wysiwyg_editing = true;
+
+ /**
+ * Is WYSIWYG editing enabled?
+ * Used in templates.
+ *
+ * @return boolean
+ */
+ public function IsWYSIWYGEnabled() {
+ return self::$allow_wysiwyg_editing;
+ }
+
+ /**
+ * Overload so that the default date is today.
+ */
+ public function populateDefaults(){
+ parent::populateDefaults();
+
+ $this->setField('Date', date('Y-m-d H:i:s', strtotime('now')));
+ }
+
+ function getCMSFields() {
+ Requirements::javascript('blog/javascript/bbcodehelp.js');
+ Requirements::themedCSS('bbcodehelp');
+
+ $firstName = Member::currentUser() ? Member::currentUser()->FirstName : '';
+ $codeparser = new BBCodeParser();
+
+ SiteTree::disableCMSFieldsExtensions();
+ $fields = parent::getCMSFields();
+ SiteTree::enableCMSFieldsExtensions();
+
+ if(!self::$allow_wysiwyg_editing) {
+ $fields->removeFieldFromTab("Root.Content.Main","Content");
+ $fields->addFieldToTab("Root.Content.Main", new TextareaField("Content", _t("BlogEntry.CN", "Content"), 20));
+ }
+
+ $fields->addFieldToTab("Root.Content.Main", $dateField = new DatetimeField("Date", _t("BlogEntry.DT", "Date")),"Content");
+ $dateField->getDateField()->setConfig('showcalendar', true);
+ $dateField->getTimeField()->setConfig('showdropdown', true);
+ $fields->addFieldToTab("Root.Content.Main", new TextField("Author", _t("BlogEntry.AU", "Author"), $firstName),"Content");
+
+ if(!self::$allow_wysiwyg_editing) {
+ $fields->addFieldToTab("Root.Content.Main", new LiteralField("BBCodeHelper", "<div id='BBCode' class='field'>" .
+ "<a id=\"BBCodeHint\" target='new'>" . _t("BlogEntry.BBH", "BBCode help") . "</a>" .
+ "<div id='BBTagsHolder' style='display:none;'>".$codeparser->useable_tagsHTML()."</div></div>"));
+ }
+
+ $fields->addFieldToTab("Root.Content.Main", new TextField("Tags", _t("BlogEntry.TS", "Tags (comma sep.)")),"Content");
+
+ $this->extend('updateCMSFields', $fields);
+
+ return $fields;
+ }
+
+ /**
+ * Returns the tags added to this blog entry
+ */
+ function TagsCollection() {
+ $tags = split(" *, *", trim($this->Tags));
+ $output = new DataObjectSet();
+
+ $link = $this->getParent() ? $this->getParent()->Link('tag') : '';
+
+ foreach($tags as $tag) {
+ $output->push(new ArrayData(array(
+ 'Tag' => $tag,
+ 'Link' => $link . '/' . urlencode($tag),
+ 'URLTag' => urlencode($tag)
+ )));
+ }
+
+ if($this->Tags) {
+ return $output;
+ }
+ }
+
+ /**
+ * Get the sidebar from the BlogHolder.
+ */
+ function SideBar() {
+ return $this->getParent()->SideBar();
+ }
+
+ /**
+ * Get a bbcode parsed summary of the blog entry
+ */
+ function ParagraphSummary(){
+ if(self::$allow_wysiwyg_editing) {
+ return $this->obj('Content')->FirstParagraph('html');
+ } else {
+ $parser = new BBCodeParser($this->Content);
+ $html = new HTMLText('Content');
+ $html->setValue($parser->parse());
+ return $html->FirstParagraph('html');
+ }
+ }
+
+ /**
+ * Get the bbcode parsed content
+ */
+ function ParsedContent() {
+ if(self::$allow_wysiwyg_editing) {
+ return $this->obj('Content');
+ } else {
+ $parser = new BBCodeParser($this->Content);
+ $content = new Text('Content');
+ $content->value = $parser->parse();
+
+ return $content;
+ }
+ }
+
+ /**
+ * Link for editing this blog entry
+ */
+ function EditURL() {
+ return $this->getParent()->Link('post') . '/' . $this->ID . '/';
+ }
+
+ /**
+ * Check to see if trackbacks are enabled.
+ */
+ function TrackBacksEnabled() {
+ return $this->getParent()->TrackBacksEnabled;
+ }
+
+ function trackbackping() {
+ if($this->TrackBacksEnabled() && $this->hasExtension('TrackBackDecorator')) {
+ return $this->decoratedTrackbackping();
+ } else {
+ Director::redirect($this->Link());
+ }
+ }
+
+ function IsOwner() {
+ if(method_exists($this->Parent(), 'IsOwner')) {
+ return $this->Parent()->IsOwner();
+ }
+ }
+
+ /**
+ * Call this to enable WYSIWYG editing on your blog entries.
+ * By default the blog uses BBCode
+ */
+ static function allow_wysiwyg_editing() {
+ self::$allow_wysiwyg_editing = true;
+ }
+}
+
+class BlogEntry_Controller extends Page_Controller {
+ static $allowed_actions = array(
+ 'index',
+ 'trackbackping',
+ 'unpublishPost',
+ 'PageComments',
+ 'SearchForm'
+ );
+
+ function init() {
+ parent::init();
+
+ Requirements::themedCSS('blog');
+ }
+
+ /**
+ * Gets a link to unpublish the blog entry
+ */
+ 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->deleteFromStage('Live');
+ $page->flushCache();
+
+ $page = DataObject::get_by_id('SiteTree', $SQL_id);
+ $page->Status = 'Unpublished';
+
+ Director::redirect($this->getParent()->Link());
+ }
+ }
+
+}
+?>
View
275 0.4.0-rc1/code/BlogHolder.php
@@ -0,0 +1,275 @@
+<?php
+
+/**
+ * @package blog
+ */
+
+/**
+ * Blog holder to display summarised blog entries.
+ *
+ * A blog holder is the leaf end of a BlogTree, but can also be used standalone in simpler circumstances.
+ * BlogHolders can only hold BlogEntries, BlogTrees can only hold BlogTrees and BlogHolders
+ * BlogHolders have a form on them for easy posting, and an owner that can post to them, BlogTrees don't
+ */
+class BlogHolder extends BlogTree implements PermissionProvider {
+ static $icon = "blog/images/blogholder";
+
+ static $db = array(
+ 'TrackBacksEnabled' => 'Boolean',
+ 'AllowCustomAuthors' => 'Boolean',
+ );
+
+ static $has_one = array(
+ 'Owner' => 'Member',
+ );
+
+ static $allowed_children = array(
+ 'BlogEntry'
+ );
+
+ function getCMSFields() {
+ SiteTree::disableCMSFieldsExtensions();
+ $fields = parent::getCMSFields();
+ SiteTree::enableCMSFieldsExtensions();
+
+ $fields->addFieldToTab('Root.Content.Main', new CheckboxField('TrackBacksEnabled', 'Enable TrackBacks'));
+ $fields->addFieldToTab('Root.Content.Main', new DropdownField('OwnerID', 'Blog owner', DataObject::get('Member')->toDropDownMap('ID', 'Name', 'None')));
+ $fields->addFieldToTab('Root.Content.Main', new CheckboxField('AllowCustomAuthors', 'Allow non-admins to have a custom author field'));
+
+ $this->extend('updateCMSFields', $fields);
+
+ return $fields;
+ }
+
+ public function BlogHolderIDs() {
+ return array( $this->ID );
+ }
+
+ /*
+ * @todo: These next few functions don't really belong in the model. Can we remove them?
+ */
+
+ /**
+ * Only display the blog entries that have the specified tag
+ */
+ function ShowTag() {
+ if(Director::urlParam('Action') == 'tag') {
+ return Convert::raw2xml(Director::urlParam('ID'));
+ }
+ }
+
+ /**
+ * Check if url has "/post"
+ */
+ function isPost() {
+ return Director::urlParam('Action') == 'post';
+ }
+
+ /**
+ * Link for creating a new blog entry
+ */
+ function postURL(){
+ return $this->Link('post');
+ }
+
+ /**
+ * Returns true if the current user is an admin, or is the owner of this blog
+ *
+ * @return Boolean
+ */
+ function IsOwner() {
+ return (Permission::check('BLOGMANAGEMENT') || Permission::check('ADMIN'));
+ }
+
+ /**
+ * Create default blog setup
+ */
+ function requireDefaultRecords() {
+ parent::requireDefaultRecords();
+
+ $blogHolder = DataObject::get_one('BlogHolder');
+ //TODO: This does not check for whether this blogholder is an orphan or not
+ if(!$blogHolder) {
+ $blogholder = new BlogHolder();
+ $blogholder->Title = "Blog";
+ $blogholder->URLSegment = "blog";
+ $blogholder->Status = "Published";
+
+ $widgetarea = new WidgetArea();
+ $widgetarea->write();
+
+ $blogholder->SideBarID = $widgetarea->ID;
+ $blogholder->write();
+ $blogholder->publish("Stage", "Live");
+
+ $managementwidget = new BlogManagementWidget();
+ $managementwidget->ParentID = $widgetarea->ID;
+ $managementwidget->write();
+
+ $tagcloudwidget = new TagCloudWidget();
+ $tagcloudwidget->ParentID = $widgetarea->ID;
+ $tagcloudwidget->write();
+
+ $archivewidget = new ArchiveWidget();
+ $archivewidget->ParentID = $widgetarea->ID;
+ $archivewidget->write();
+
+ $widgetarea->write();
+
+ $blog = new BlogEntry();
+ $blog->Title = _t('BlogHolder.SUCTITLE', "SilverStripe blog module successfully installed");
+ $blog->URLSegment = 'sample-blog-entry';
+ $blog->Tags = _t('BlogHolder.SUCTAGS',"silverstripe, blog");
+ $blog->Content = _t('BlogHolder.SUCCONTENT',"<p>Congratulations, the SilverStripe blog module has been successfully installed. This blog entry can be safely deleted. You can configure aspects of your blog (such as the widgets displayed in the sidebar) in <a href=\"admin\">the CMS</a>.</p>");
+ $blog->Status = "Published";
+ $blog->ParentID = $blogholder->ID;
+ $blog->write();
+ $blog->publish("Stage", "Live");
+
+ DB::alteration_message("Blog page created","created");
+ }
+ }
+}
+
+class BlogHolder_Controller extends BlogTree_Controller {
+ static $allowed_actions = array(
+ 'index',
+ 'tag',
+ 'date',
+ 'metaweblog',
+ 'postblog' => 'BLOGMANAGEMENT',
+ 'post' => 'BLOGMANAGEMENT',
+ 'BlogEntryForm' => 'BLOGMANAGEMENT',
+ );
+
+ function init() {
+ parent::init();
+ Requirements::themedCSS("bbcodehelp");
+ }
+
+ /**
+ * Return list of usable tags for help
+ */
+ function BBTags() {
+ return BBCodeParser::usable_tags();
+ }
+
+ function providePermissions() {
+ return array("BLOGMANAGEMENT" => "Blog management");
+ }
+
+ /**
+ * Post a new blog entry
+ */
+ function post(){
+ if(!Permission::check('BLOGMANAGEMENT')) return Security::permissionFailure();
+
+ $page = $this->customise(array(
+ 'Content' => false,
+ 'Form' => $this->BlogEntryForm()
+ ));
+
+ return $page->renderWith('Page');
+ }
+
+ /**
+ * A simple form for creating blog entries
+ */
+ function BlogEntryForm() {
+ if(!Permission::check('BLOGMANAGEMENT')) return Security::permissionFailure();
+
+ Requirements::javascript('jsparty/behaviour.js');
+ Requirements::javascript('jsparty/prototype.js');
+ Requirements::javascript('jsparty/scriptaculous/effects.js');
+ Requirements::javascript('cms/javascript/PageCommentInterface.js');
+ Requirements::javascript('blog/javascript/bbcodehelp.js');
+
+ $id = 0;
+ if(Director::urlParam('ID')) {
+ $id = (int) Director::urlParam('ID');
+ }
+
+ $codeparser = new BBCodeParser();
+ $membername = Member::currentMember() ? Member::currentMember()->getName() : "";
+
+ if(BlogEntry::$allow_wysiwyg_editing) {
+ $contentfield = new HtmlEditorField("BlogPost", _t("BlogEntry.CN"));
+ } else {
+ $contentfield = new CompositeField(
+ new LiteralField("BBCodeHelper","<a id=\"BBCodeHint\" target='new'>"._t("BlogEntry.BBH")."</a><div class='clear'><!-- --></div>" ),
+ new TextareaField("BlogPost", _t("BlogEntry.CN"),20), // This is called BlogPost as the id #Content is generally used already
+ new LiteralField("BBCodeTags","<div id=\"BBTagsHolder\">".$codeparser->useable_tagsHTML()."</div>")
+ );
+ }
+
+ if(class_exists('TagField')) {
+ $tagfield = new TagField('Tags', null, null, 'BlogEntry');
+ $tagfield->setSeparator(', ');
+ } else {
+ $tagfield = new TextField('Tags');
+ }
+
+ $field = 'TextField';
+ if(!$this->AllowCustomAuthors && !Permission::check('ADMIN')) {
+ $field = 'ReadonlyField';
+ }
+ $fields = new FieldSet(
+ new HiddenField("ID", "ID"),
+ new TextField("Title", _t('BlogHolder.SJ', "Subject")),
+ new $field("Author", _t('BlogEntry.AU'), $membername),
+ $contentfield,
+ $tagfield,
+ new LiteralField("Tagsnote"," <label id='tagsnote'>"._t('BlogHolder.TE', "For example: sport, personal, science fiction")."<br/>" .
+ _t('BlogHolder.SPUC', "Please separate tags using commas.")."</label>")
+ );
+
+ $submitAction = new FormAction('postblog', _t('BlogHolder.POST', 'Post blog entry'));
+ $actions = new FieldSet($submitAction);
+ $validator = new RequiredFields('Title','Content');
+
+ $form = new Form($this, 'BlogEntryForm',$fields, $actions,$validator);
+
+ if($id != 0) {
+ $entry = DataObject::get_by_id('BlogEntry', $id);
+ if($entry->IsOwner()) {
+ $form->loadDataFrom($entry);
+ $form->datafieldByName('BlogPost')->setValue($entry->Content);
+ }
+ } else {
+ $form->loadDataFrom(array("Author" => Cookie::get("BlogHolder_Name")));
+ }
+
+ return $form;
+ }
+
+ 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(!$blogentry) {
+ $blogentry = new BlogEntry();
+ }
+
+ $form->saveInto($blogentry);
+ $blogentry->ParentID = $this->ID;
+ $blogentry->Content = $form->datafieldByName('BlogPost')->dataValue();
+
+ $blogentry->Status = "Published";
+ $blogentry->writeToStage("Stage");
+ $blogentry->publish("Stage", "Live");
+
+ Director::redirect($this->Link());
+ }
+}
+
+
+?>
View
280 0.4.0-rc1/code/BlogTree.php
@@ -0,0 +1,280 @@
+<?php
+
+/**
+ * @package blog
+ */
+
+/**
+ * Blog tree allows a tree of Blog Holders. Viewing branch nodes shows all blog entries from all blog holder children
+ */
+
+class BlogTree extends Page {
+
+ // Default number of blog entries to show
+ static $default_entries_limit = 10;
+
+ static $db = array(
+ 'Name' => 'Varchar',
+ 'InheritSideBar' => 'Boolean',
+ 'LandingPageFreshness' => 'Varchar',
+ );
+
+ static $defaults = array(
+ 'InheritSideBar' => True
+ );
+
+ static $has_one = array(
+ "SideBar" => "WidgetArea",
+ );
+
+ static $allowed_children = array(
+ 'BlogTree', 'BlogHolder'
+ );
+
+ /*
+ * Finds the BlogTree object most related to the current page.
+ * - If this page is a BlogTree, use that
+ * - If this page is a BlogEntry, use the parent Holder
+ * - Otherwise, try and find a 'top-level' BlogTree
+ */
+ static function current() {
+
+ $controller = Controller::curr();
+ if($controller) $page = $controller->data();
+
+ // If we _are_ a BlogTree, use us
+ if ($page 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;
+ }
+
+ // Try to find a top-level BlogTree
+ $top = DataObject::get_one('BlogTree', "\"ParentID\" = '0'");
+ if($top) return $top;
+
+ // Try to find any BlogTree that is not inside another BlogTree
+ foreach(DataObject::get('BlogTree') 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 DataObject::get_one('BlogTree');
+ }
+
+ /* ----------- 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 = '';
+ return $freshness;
+ }
+
+ function SideBar() {
+ if($this->InheritSideBar && $this->getParent()) {
+ if (method_exists($this->getParent(), 'SideBar')) return $this->getParent()->SideBar();
+ }
+
+ if($this->SideBarID){
+ return DataObject::get_by_id('WidgetArea', $this->SideBarID);
+ // @todo: This segfaults - investigate why then fix: return $this->getComponent('SideBar');
+ }
+ }
+
+ /* ----------- CMS CONTROL -------------- */
+
+ function getCMSFields() {
+ $fields = parent::getCMSFields();
+ $fields->addFieldToTab("Root.Content.Main", new TextField("Name", "Name of blog"));
+ $fields->addFieldToTab('Root.Content.Main', new DropdownField('LandingPageFreshness', 'When you first open the blog, how many entries should I show', array(
+ "" => "All entries",
+ "1 MONTH" => "Last month's entries",
+ "2 MONTH" => "Last 2 months' entries",
+ "3 MONTH" => "Last 3 months' entries",
+ "4 MONTH" => "Last 4 months' entries",
+ "5 MONTH" => "Last 5 months' entries",
+ "6 MONTH" => "Last 6 months' entries",
+ "7 MONTH" => "Last 7 months' entries",
+ "8 MONTH" => "Last 8 months' entries",
+ "9 MONTH" => "Last 9 months' entries",
+ "10 MONTH" => "Last 10 months' entries",
+ "11 MONTH" => "Last 11 months' entries",
+ "12 MONTH" => "Last year's entries",
+ "INHERIT" => "Take value from parent Blog Tree"
+ )));
+
+ $fields->addFieldToTab("Root.Content.Widgets", new CheckboxField("InheritSideBar", 'Inherit Sidebar From Parent'));
+ $fields->addFieldToTab("Root.Content.Widgets", new WidgetAreaEditor("SideBar"));
+
+ 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;
+ } elseif($child instanceof BlogTree) {
+ $child->loadDescendantBlogHolderIDListInto($idList);
+ }
+ }
+ }
+ }
+
+ // Build a list of all IDs for BlogHolders that are children of us
+ 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 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 callback retrieveCallback A function to call with pagetype, filter and limit for custom blog sorting or filtering
+ * @param string $where
+ * @return DataObjectSet
+ */
+ 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%'";
+ }
+
+ if($date) {
+ if(strpos($date, '-')) {
+ $year = (int) substr($date, 0, strpos($date, '-'));
+ $month = (int) substr($date, strpos($date, '-') + 1);
+
+ if($year && $month) {
+ if(method_exists(DB::getConn(), 'formattedDatetimeClause')) {
+ $dateCheck = 'AND ' . DB::getConn()->formattedDatetimeClause('"BlogEntry"."Date"', '%m') . " * 1 = $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'";
+ }
+ }
+ }
+ }
+
+ // 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 .= '"ParentID" IN (' . implode(',', $holderIDs) . ") $tagCheck $dateCheck";
+
+ $order = '"BlogEntry"."Date" DESC';
+
+ // 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);
+ else return DataObject::get('BlogEntry', $filter, $order, '', $limit);
+ }
+}
+
+class BlogTree_URL {
+ static function tag() {
+ if (Director::urlParam('Action') == 'tag') return Director::urlParam('ID');
+ return '';
+ }
+
+ static function date() {
+ if(Director::urlParam('Action') == 'date') {
+ $year = Director::urlParam('ID');
+ $month = Director::urlParam('OtherID');
+
+ if($month && is_numeric($month) && $month >= 1 && $month <= 12 && is_numeric($year)) {
+ return "$year-$month";
+ } elseif (is_numeric($year)) {
+ return $year;
+ }
+
+ return '';
+ }
+ }
+}
+
+class BlogTree_Controller extends Page_Controller {
+ static $allowed_actions = array(
+ 'index',
+ 'rss',
+ );
+
+ function init() {
+ parent::init();
+
+ $this->IncludeBlogRSS();
+
+ Requirements::themedCSS("blog");
+ }
+
+ function BlogEntries($limit = null) {
+ if($limit === null) $limit = BlogTree::$default_entries_limit;
+
+ // only use freshness if no action is present (might be displaying tags or rss)
+ if ($this->LandingPageFreshness && !$this->request->param('Action')) {
+ $filter = "\"BlogEntry\".\"Date\" > NOW() - INTERVAL " . $this->LandingPageFreshness;
+ } else {
+ $filter = '';
+ }
+
+ $start = isset($_GET['start']) ? (int) $_GET['start'] : 0;
+ return $this->Entries("$start,$limit", BlogTree_URL::tag(), BlogTree_URL::date(), null, $filter);
+ }
+
+ function IncludeBlogRSS() {
+ // This will create a <link> tag point to the RSS feed
+ RSSFeed::linkToFeed($this->Link() . "rss", _t('BlogHolder.RSSFEED',"RSS feed of these blogs"));
+ }
+
+ /**
+ * Get the rss feed for this blog holder's entries
+ */
+ function rss() {
+ global $project_name;
+
+ $blogName = $this->Name;
+ $altBlogName = $project_name . ' blog';
+
+ $entries = $this->Entries(20);
+
+ if($entries) {
+ $rss = new RSSFeed($entries, $this->Link(), ($blogName ? $blogName : $altBlogName), "", "Title", "ParsedContent");
+ $rss->outputToBrowser();
+ }
+ }
+
+ function defaultAction($action) {
+ // 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 $this->rss();
+
+ return parent::defaultAction($action);
+ }
+}
+
+?>
View
106 0.4.0-rc1/code/MetaWeblogController.php
@@ -0,0 +1,106 @@
+<?php
+
+require_once(BASE_PATH . '/blog/thirdparty/xmlrpc/xmlrpc.php');
+require_once(BASE_PATH . '/blog/thirdparty/xmlrpc/xmlrpcs.php');
+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) {
+ Debug::log(Debug::text($request));
+
+ // Create an xmlrpc server, and set up the method calls
+ $service = new xmlrpc_server(array(
+ "blogger.getUsersBlogs" => array(
+ "function" => array($this, "getUsersBlogs")
+ ),
+ "metaWeblog.getRecentPosts" => array(
+ 'function' => array($this, 'getRecentPosts')
+ ),
+ 'metaWeblog.getCategories' => array(
+ 'function' => array($this, 'getCategories')
+ )
+ ), false);
+
+ // Use nice php functions, and call the service
+ $service->functions_parameters_type = 'phpvals';
+ $service->service();
+
+ // Tell SilverStripe not to try render a template
+ return false;
+ }
+
+ /**
+ * Get a list of BlogHolders the user has access to.
+ */
+ function getUsersBlogs($appkey, $username, $password) {
+ $member = MemberAuthenticator::authenticate(array(
+ 'Email' => $username,
+ 'Password' => $password,
+ ));
+
+ // TODO Throw approriate error.
+ if(!$member) die();
+
+ $blogholders = DataObject::get('BlogHolder');
+
+ $response = array();
+
+ foreach($blogholders as $bh) {
+ if(!$bh->canAddChildren($member)) continue;
+
+ $bgarr = array();
+ $bgarr['url'] = $bh->AbsoluteLink();
+ $bgarr['blogid'] = (int) $bh->ID;
+ $bgarr['blogname'] = $bh->Title;
+
+ $response[] = $bgarr;
+ }
+
+ return $response;
+ }
+
+ /**
+ * Get the most recent posts on a blog.
+ */
+ function getRecentPosts($blogid, $username, $password, $numberOfPosts) {
+ $member = MemberAuthenticator::authenticate(array(
+ 'Email' => $username,
+ 'Password' => $password,
+ ));
+
+ // TODO Throw approriate error.
+ if(!$member) die();
+
+ $posts = DataObject::get('BlogEntry', '"ParentID" = ' . (int) $blogid, '"Date" DESC');
+
+ $res = array();
+ $postsSoFar = 0;
+
+ foreach($posts as $post) {
+ if(!$post->canEdit($member)) continue;
+
+ $parr = array();
+
+ $parr['title'] = $post->Title;
+ $parr['link'] = $post->AbsoluteLink();
+ $parr['description'] = $post->Content;
+ $parr['postid'] = (int) $post->ID;
+
+ $res[] = $parr;
+
+ if(++$postsSoFar >= $numberOfPosts) break;
+ }
+
+ return $res;
+ }
+
+ function getCategories() {
+ //TODO dummy function
+ return array();
+ }
+}
+
+?>
View
52 0.4.0-rc1/code/TrackBackDecorator.php
@@ -0,0 +1,52 @@
+<?php
+
+class TrackBackDecorator extends DataObjectDecorator {
+ function extraStatics() {
+ return array(
+ 'has_many' => array(
+ 'TrackBacks' => 'TrackBackPing'
+ )
+ );
+ }
+
+ function updateMetaTags(&$tags) {
+ $tags .= $this->owner->renderWith('TrackBackRdf');
+ }
+
+ function TrackBackPingLink() {
+ return $this->owner->AbsoluteLink() . 'trackbackping';
+ }
+
+ function decoratedTrackbackping() {
+ $error = 0;
+ $message = '';
+
+ if(!(isset($_POST['url']) && $_POST['url'])) {
+ $error = 1;
+ $message = 'Missing required POST parameter \'url\'.';
+ } else {
+ $trackbackping = new TrackBackPing();
+ $trackbackping->Url = $_POST['url'];
+ if(isset($_POST['title']) && $_POST['title']) {
+ $trackbackping->Title = $_POST['title'];
+ }
+ if(isset($_POST['excerpt']) && $_POST['excerpt']) {
+ $trackbackping->Excerpt = $_POST['excerpt'];
+ }
+ if(isset($_POST['blog_name']) && $_POST['blog_name']) {
+ $trackbackping->BlogName = $_POST['blog_name'];
+ }
+ $trackbackping->PageID = $this->owner->ID;
+ $trackbackping->write();
+ }
+
+ $returnData = new ArrayData(array(
+ 'Error' => $error,
+ 'Message' => $message
+ ));
+
+ return $returnData->renderWith('TrackBackPingReturn');
+ }
+}
+
+?>
View
24 0.4.0-rc1/code/TrackBackPing.php
@@ -0,0 +1,24 @@
+<?php
+
+class TrackBackPing extends DataObject {
+ static $db = array(
+ 'Title' => 'Varchar',
+ 'Excerpt' => 'Text',
+ // 2083 is URL-length limit for IE, AFAIK.
+ // see: http://www.boutell.com/newfaq/misc/urllength.html
+ 'Url' => 'Varchar(2048)',
+ 'BlogName' => 'Varchar'
+ );
+
+ static $has_one = array(
+ 'Page' => 'Page'
+ );
+
+ static $has_many = array();
+
+ static $many_many = array();
+
+ static $belongs_many_many = array();
+}
+
+?>
View
152 0.4.0-rc1/code/import/TypoImport.php
@@ -0,0 +1,152 @@
+<?php
+
+require_once("core/model/DB.php");
+
+class TypoImport extends Controller {
+ /**
+ * Imports product status and price change updates.
+ *
+ */
+
+ function testinstall() {
+ echo "Ok";
+ }
+
+ /**
+ * Imports blog entries and comments from a Potgres-based typo installation into a SilverStripe blog
+ */
+ function import(){
+ // some of the guys in the contents table are articles, some are contents. Distinguished by type = "Article" or "Comment"
+ // fields are: id, title, author, body, body_html, extended, excerpt, keywords, created_at, updated_at, extended_html, user_id, permalink, guid, [13]
+ // text_filter_id, whiteboard, type, article_id, email, url, ip, blog_name, name, published, allow_pings, allow_comments, blog_id
+ // published_at, state, status_confirmed
+
+
+ $dbconn = pg_connect("host=orwell port=5432 dbname=typo_prod user=postgres password=possty");
+
+ // create a new blogholder and call it "imported blog"
+ $bholder = new BlogHolder();
+ $bholder->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 "<br />\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 "<br />\n";
+ }
+
+ $newEntry->flushCache();
+
+ // fix up the specialchars
+ pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"&#215;\", \"x\")");
+ pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"&#8217;\", \"&rsquo;\")");
+ pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"&#8216;\", \"&lsquo;\")");
+ pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"&#151;\", \"&mdash;\")");
+ pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"&#8220;\", \"&ldquo;\")");
+ pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"&#8221;\", \"&rdquo;\")");
+ pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"&#8211;\", \"&ndash;\")");
+ pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"&#8212;\", \"&mdash;\")");
+ pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"&#8230;\", \"&hellip;\")");
+ pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"&#8482;\", \"&trade;\")");
+ pg_query($dbconn, "UPDATE SiteTree SET Content = REPLACE(Content, \"&#38;\", \"&amp;\")");
+
+ pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"&#215;\", \"x\")");
+ pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"&#8217;\", \"&rsquo;\")");
+ pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"&#8216;\", \"&lsquo;\")");
+ pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"&#151;\", \"&mdash;\")");
+ pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"&#8220;\", \"&ldquo;\")");
+ pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"&#8221;\", \"&rdquo;\")");
+ pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"&#8211;\", \"&ndash;\")");
+ pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"&#8212;\", \"&mdash;\")");
+ pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"&#8230;\", \"&hellip;\")");
+ pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"&#8482;\", \"&trade;\")");
+ pg_query($dbconn, "UPDATE PageComment SET Comment = REPLACE(Comment, \"&#38;\", \"&amp;\")");
+
+
+ }
+
+ pg_close($dbconn);
+
+ } // end function
+
+} // end class
+?>
View
106 0.4.0-rc1/code/widgets/ArchiveWidget.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Shows a widget with viewing blog entries
+ * by months or years.
+ *
+ * @package blog
+ */
+class ArchiveWidget extends Widget {
+ static $db = array(
+ 'DisplayMode' => 'Varchar'
+ );
+
+ static $has_one = array();
+
+ static $has_many = array();
+
+ static $many_many = array();
+
+ static $belongs_many_many = array();
+
+ static $defaults = array(
+ 'DisplayMode' => 'month'
+ );
+
+ static $title = 'Browse by Date';
+
+ static $cmsTitle = 'Blog Archive';
+
+ static $description = 'Show a list of months or years in which there are blog posts, and provide links to them.';
+
+ function getCMSFields() {
+ $fields = new FieldSet(
+ 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 Dates() {
+ Requirements::themedCSS('archivewidget');
+
+ $results = new DataObjectSet();
+ $container = BlogTree::current();
+ $ids = $container->BlogHolderIDs();
+
+ $stage = Versioned::current_stage();
+ $suffix = (!$stage || $stage == 'Stage') ? "" : "_$stage";
+
+ $monthclause = method_exists(DB::getConn(), 'formattedDatetimeClause') ? DB::getConn()->formattedDatetimeClause('"Date"', '%m') : 'MONTH("Date")';
+ $yearclause = method_exists(DB::getConn(), 'formattedDatetimeClause') ? DB::getConn()->formattedDatetimeClause('"Date"', '%Y') : '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('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;
+ }
+}
+
+?>
View
63 0.4.0-rc1/code/widgets/BlogManagementWidget.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Blog Management Widget
+ * @package blog
+ */
+class BlogManagementWidget extends Widget implements PermissionProvider {
+ static $db = array();
+
+ static $has_one = array();
+
+ static $has_many = array();
+
+ static $many_many = array();
+
+ static $belongs_many_many = array();
+
+ static $defaults = array();
+
+ static $title = "Blog Management";
+ static $cmsTitle = "Blog Management";
+ static $description = "Provide a number of links useful for administering a blog. Only shown if the user is an admin.";
+
+ function CommentText() {
+ $unmoderatedcount = DB::query("SELECT COUNT(*) FROM \"PageComment\" WHERE \"NeedsModeration\"=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')) {
+ return false;
+ }
+ $unmoderatedcount = DB::query("SELECT COUNT(*) FROM \"PageComment\" WHERE \"NeedsModeration\"=1")->value();
+
+ if($unmoderatedcount > 0) {
+ return "admin/comments/unmoderated";
+ } else {
+ return "admin/comments";
+ }
+ }
+
+ function providePermissions() {
+ return array("BLOGMANAGEMENT" => "Blog management");
+ }
+
+ function WidgetHolder() {
+ if(Permission::check("BLOGMANAGEMENT")) {
+ return $this->renderWith("WidgetHolder");
+ }
+ }
+
+ function PostLink() {
+ $container = BlogTree::current();
+ if ($container) return $container->Link('post');
+ }
+
+}
+?>
View
92 0.4.0-rc1/code/widgets/RSSWidget.php
@@ -0,0 +1,92 @@
+<?php
+
+class RSSWidget extends Widget {
+ static $db = array(
+ "RSSTitle" => "Text",
+ "RssUrl" => "Text",
+ "NumberToShow" => "Int"
+ );
+
+ static $has_one = array();
+
+ static $has_many = array();
+
+ static $many_many = array();
+
+ static $belongs_many_many = array();
+
+ static $defaults = array(
+ "NumberToShow" => 10,
+ "RSSTitle" => 'RSS Feed'
+ );
+ static $cmsTitle = "RSS Feed";
+ 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 = new FieldSet(
+ 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 : 'RSS Feed';
+ }
+
+ function FeedItems() {
+ $output = new DataObjectSet();
+
+ // 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;
+ }
+
+ include_once(Director::getAbsFile(SAPPHIRE_DIR . '/thirdparty/simplepie/simplepie.inc'));
+
+ $t1 = microtime(true);
+ $feed = new SimplePie($this->AbsoluteRssUrl, 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($item->get_title());
+
+ $output->push(new ArrayData(array(
+ 'Title' => $title,
+ 'Date' => $date,
+ 'Link' => $item->get_link()
+ )));
+ }
+ return $output;
+ }
+ }
+}
+
+?>
View
31 0.4.0-rc1/code/widgets/SubscribeRSSWidget.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * A simple widget that just shows a link
+ * to this website's blog RSS, with an RSS
+ * icon.
+ *
+ * @package blog
+ */
+class SubscribeRSSWidget extends Widget {
+
+ static $title = 'Subscribe via RSS';
+
+ static $cmsTitle = 'Subscribe via RSS widget';
+
+ static $description = 'Shows a link allowing a user to subscribe to this blog via RSS.';
+
+ /**
+ * Return an absolute URL based on the BlogHolder
+ * that this widget is located on.
+ *
+ * @return string
+ */
+ function RSSLink() {
+ Requirements::themedCSS('subscribersswidget');
+ $container = BlogTree::current();
+ if ($container) return $container->Link() . 'rss';
+ }
+}
+
+?>
View
145 0.4.0-rc1/code/widgets/TagCloudWidget.php
@@ -0,0 +1,145 @@
+<?php
+
+class TagCloudWidget extends Widget {
+ static $db = array(
+ "Title" => "Varchar",
+ "Limit" => "Int",
+ "Sortby" => "Varchar"
+ );
+
+ static $has_one = array();
+
+ static $has_many = array();
+
+ static $many_many = array();
+
+ static $belongs_many_many = array();
+
+ static $defaults = array(
+ "Title" => "Tag Cloud",
+ "Limit" => "0",
+ "Sortby" => "alphabet"
+ );
+
+ static $cmsTitle = "Tag Cloud";
+ static $description = "Shows a tag cloud of tags on your blog.";
+
+ static $popularities = array( 'not-popular', 'not-very-popular', 'somewhat-popular', 'popular', 'very-popular', 'ultra-popular' );
+
+ function getCMSFields() {
+ $fields = new FieldSet(
+ 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->extend('updateCMSFields', $fields);
+
+ return $fields;
+ }
+
+ function Title() {
+ return $this->Title ? $this->Title : 'Tag Cloud';
+ }
+
+ function TagsCollection() {
+ Requirements::css("blog/css/tagcloud.css");
+
+ $allTags = array();
+ $max = 0;
+ $container = BlogTree::current();
+
+ $entries = $container->Entries();
+
+ if($entries) {
+ foreach($entries as $entry) {
+ $theseTags = split(" *, *", strtolower(trim($entry->Tags)));
+ foreach($theseTags as $tag) {
+ if($tag != "") {
+ $allTags[$tag] = isset($allTags[$tag]) ? $allTags[$tag] + 1 : 1; //getting the count into key => value map
+ $max = ($allTags[$tag] > $max) ? $allTags[$tag] : $max;
+ }
+ }
+ }
+
+ if($allTags) {
+ //TODO: move some or all of the sorts to the database for more efficiency
+ if($this->Limit > 0){
+ uasort($allTags, array($this, "column_sort_by_popularity")); //sort by popularity
+ $allTags = array_slice($allTags, 0, $this->Limit,true);
+ }
+ if($this->Sortby == "alphabet"){
+ $this->natksort($allTags);
+ }
+
+ $sizes = array();
+ foreach ($allTags as $tag => $count) $sizes[$count] = true;
+
+ $offset = 0;
+ $numsizes = count($sizes)-1; //Work out the number of different sizes
+ $buckets = count(self::$popularities)-1;
+
+ // If there are more frequencies then buckets, divide frequencies into buckets
+ if ($numsizes > $buckets) {
+ $numsizes = $buckets;
+ }
+ // Otherwise center use central buckets
+ else {
+ $offset = round(($buckets-$numsizes)/2);
+ }
+
+ foreach($allTags as $tag => $count) {
+ $popularity = round($count / $max * $numsizes) + $offset; $popularity=min($buckets,$popularity);
+ $class = self::$popularities[$popularity];
+
+ $allTags[$tag] = array(
+ "Tag" => $tag,
+ "Count" => $count,
+ "Class" => $class,
+ "Link" => $container->Link('tag') . '/' . urlencode($tag)
+ );
+ }
+ }
+
+ $output = new DataObjectSet();
+ foreach($allTags as $tag => $fields) {
+ $output->push(new ArrayData($fields));
+ }
+
+ return $output;
+ }
+
+ return;
+ }
+
+ /**
+ * Helper method to compare 2 Vars to work out the results.
+ * @param mixed
+ * @param mixed
+ * @return int
+ */
+ private function column_sort_by_popularity($a, $b){
+ if($a == $b) {
+ $result = 0;
+ }
+ else {
+ $result = $b - $a;
+ }
+ return $result;
+ }
+
+ private function natksort(&$aToBeSorted) {
+ $aResult = array();
+ $aKeys = array_keys($aToBeSorted);
+ natcasesort($aKeys);
+ foreach ($aKeys as $sKey) {
+ $aResult[$sKey] = $aToBeSorted[$sKey];
+ }
+ $aToBeSorted = $aResult;
+
+ return true;
+ }
+}
+
+
+?>
View
9 0.4.0-rc1/css/archivewidget.css
@@ -0,0 +1,9 @@
+.archiveMonths{
+
+}
+
+ul.archiveYears li{
+ display: inline;
+ font-size: 1.2em !important;
+ margin:0 !important;
+}
View
32 0.4.0-rc1/css/bbcodehelp.css
@@ -0,0 +1,32 @@
+/*
+ Foundational BBHelper formatting
+*/
+
+ul.bbcodeExamples li {
+ list-style-type:none;
+ font-size: 1em;
+}
+ul.bbcodeExamples li.last {
+ border: none;
+}
+
+ul.bbcodeExamples li span.example {
+
+}
+
+#BBTagsHolder{
+ color: #777;
+ padding: 5px;
+ width: 270px;
+ background-color: #fff;
+ font-size:0.8em;
+}
+
+.bbcodeExamples{
+ margin: 0 !important;
+ padding: 0;
+}
+
+#BBCodeHint{
+ cursor: pointer;
+}
View
11 0.4.0-rc1/css/blog.css
@@ -0,0 +1,11 @@
+.BlogError {
+ text-align: center;
+}
+
+.BlogError p {
+ color: #fff;
+ display: inline;
+ background-color: #f77;
+ padding: 7px;
+ font-weight:bold;
+}
View
3 0.4.0-rc1/css/flickrwidget.css
@@ -0,0 +1,3 @@
+div.flickrwidget {
+ text-align: center;
+}
View
4 0.4.0-rc1/css/subscribersswidget.css
@@ -0,0 +1,4 @@
+.subscribeLink {
+ background: url(../images/feed-icon-14x14.png) no-repeat left center;
+ padding-left: 20px;
+}
View
6 0.4.0-rc1/css/tagcloud.css
@@ -0,0 +1,6 @@
+.tagcloud .not-popular { font-size: 1em; }
+.tagcloud .not-very-popular { font-size: 1.3em; }
+.tagcloud .somewhat-popular { font-size: 1.6em; }
+.tagcloud .popular { font-size: 1.9em; }
+.tagcloud .very-popular { font-size: 2.2em; }
+.tagcloud .ultra-popular { font-size: 2.5em; }
View
BIN 0.4.0-rc1/images/blogholder-file.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN 0.4.0-rc1/images/blogpage-file.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN 0.4.0-rc1/images/feed-icon-14x14.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN 0.4.0-rc1/images/feed-icon-28x28.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
12 0.4.0-rc1/javascript/bbcodehelp.js
@@ -0,0 +1,12 @@
+Behaviour.register({
+ '#BBCodeHint': {
+ onclick: function() {
+ if($('BBTagsHolder').style.display == "none") {
+ Effect.BlindDown('BBTagsHolder');
+ } else{
+ Effect.BlindUp('BBTagsHolder');
+ }
+ return false;
+ }
+ }
+});
View
0 0.4.0-rc1/lang/_manifest_exclude
No changes.
View
81 0.4.0-rc1/lang/ar_SA.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * Arabic (Saudi Arabia) language pack
+ * @package blog
+ * @subpackage i18n
+ */
+
+i18n::include_locale_file('blog', 'en_US');
+
+global $lang;
+
+if(array_key_exists('ar_SA', $lang) && is_array($lang['ar_SA'])) {
+ $lang['ar_SA'] = array_merge($lang['en_US'], $lang['ar_SA']);
+} else {
+ $lang['ar_SA'] = $lang['en_US'];
+}
+
+$lang['ar_SA']['ArchiveWidget']['DispBY'] = 'استعراض بواسطة';
+$lang['ar_SA']['ArchiveWidget']['MONTH'] = 'شهر';
+$lang['ar_SA']['ArchiveWidget']['PLURALNAME'] = 'مربعات الأرشيف';
+$lang['ar_SA']['ArchiveWidget']['SINGULARNAME'] = 'مربع الأرشيف';
+$lang['ar_SA']['ArchiveWidget']['YEAR'] = 'سنة';
+$lang['ar_SA']['BlogEntry']['AU'] = 'الكاتب';
+$lang['ar_SA']['BlogEntry']['BBH'] = 'مساعدة BBCode';
+$lang['ar_SA']['BlogEntry']['CN'] = 'المحتوى';
+$lang['ar_SA']['BlogEntry']['DT'] = 'تاريخ';
+$lang['ar_SA']['BlogEntry']['PLURALNAME'] = 'تدوينات المدونة';
+$lang['ar_SA']['BlogEntry']['SINGULARNAME'] = 'تدوينة المدونة';
+$lang['ar_SA']['BlogEntry.ss']['COMMENTS'] = 'التعليقات';
+$lang['ar_SA']['BlogEntry.ss']['EDITTHIS'] = 'تحرير التدوينة';
+$lang['ar_SA']['BlogEntry.ss']['POSTEDBY'] = 'نشرت بواسطة';
+$lang['ar_SA']['BlogEntry.ss']['POSTEDON'] = 'في';
+$lang['ar_SA']['BlogEntry.ss']['TAGS'] = 'الوسوم:';
+$lang['ar_SA']['BlogEntry.ss']['UNPUBLISHTHIS'] = 'عدم نشر التدوينة';
+$lang['ar_SA']['BlogEntry.ss']['VIEWALLPOSTTAGGED'] = 'عرض جميع التدوينات';
+$lang['ar_SA']['BlogEntry']['TS'] = 'وسوم (فاصلة,بين,الوسوم)';
+$lang['ar_SA']['BlogHolder']['HAVENTPERM'] = 'تدوين المدونات يعتبر مهمة إدارية. فضلاً قم بتسجيل الدخول';
+$lang['ar_SA']['BlogHolder']['PLURALNAME'] = 'حاويات المدونة';
+$lang['ar_SA']['BlogHolder']['POST'] = 'Post blog entry';
+$lang['ar_SA']['BlogHolder']['RSSFEED'] = 'RSS لهذه المدونة';
+$lang['ar_SA']['BlogHolder']['SINGULARNAME'] = 'حاوية المدونة';
+$lang['ar_SA']['BlogHolder']['SJ'] = 'الموضوع';
+$lang['ar_SA']['BlogHolder']['SPUC'] = 'فضلاً افصل بين الوسوم بفاصلة';
+$lang['ar_SA']['BlogHolder.ss']['NOENTRIES'] = 'لا يوجد مدخلات';
+$lang['ar_SA']['BlogHolder.ss']['VIEWINGTAGGED'] = 'عرض المخلات الموسومة بـ';
+$lang['ar_SA']['BlogHolder']['SUCCONTENT'] = 'مبروك, تم تركيب Silverstripe blog بنجاح. هذه المدونة يمكن حذفها بأمان.يمكن تعديل المدونة عبر عبر رابط [url=admin]إدارة المحتوى[/url]';
+$lang['ar_SA']['BlogHolder']['SUCTAGS'] = 'َsilverstripe , blog';
+$lang['ar_SA']['BlogHolder']['SUCTITLE'] = 'تم تركيب SilverStripe Blog بنجاح';
+$lang['ar_SA']['BlogHolder']['TE'] = 'مثال:رياضة,شخصية,علمية';
+$lang['ar_SA']['BlogManagementWidget']['COMADM'] = 'إدارة التعليقات';
+$lang['ar_SA']['BlogManagementWidget']['PLURALNAME'] = 'مربعات إدارة المدونة';
+$lang['ar_SA']['BlogManagementWidget']['SINGULARNAME'] = 'مربع إدارة المدونة';
+$lang['ar_SA']['BlogManagementWidget.ss']['LOGOUT'] = 'خروج';
+$lang['ar_SA']['BlogManagementWidget.ss']['POSTNEW'] = 'نشر تدوينة جديدة';
+$lang['ar_SA']['BlogManagementWidget']['UNM1'] = 'يوجد تعليق واحد لا يحتاج إلى موافقة';
+$lang['ar_SA']['BlogManagementWidget']['UNMM'] = 'يوجد %i تعليقات لا تحتاج إلى موافقة';
+$lang['ar_SA']['BlogSummary.ss']['COMMENTS'] = 'التعليقات';
+$lang['ar_SA']['BlogSummary.ss']['POSTEDBY'] = 'بواسطة';
+$lang['ar_SA']['BlogSummary.ss']['POSTEDON'] = 'في';
+$lang['ar_SA']['BlogSummary.ss']['VIEWFULL'] = 'عرض كامل التدوينة';
+$lang['ar_SA']['RSSWidget']['CT'] = 'العنوان المخصص للخلاصة';
+$lang['ar_SA']['RSSWidget']['NTS'] = 'عدد العناصر لعرضها';
+$lang['ar_SA']['RSSWidget']['PLURALNAME'] = 'مربعات الخلاصات RSS';
+$lang['ar_SA']['RSSWidget']['SINGULARNAME'] = 'مربع الخلاصات RSS';
+$lang['ar_SA']['RSSWidget']['URL'] = 'رابط الخلاصة';
+$lang['ar_SA']['SubscribeRSSWidget']['PLURALNAME'] = 'مربعات الاشتراك في الخلاصات RSS';
+$lang['ar_SA']['SubscribeRSSWidget']['SINGULARNAME'] = 'مربع الاشتراك في الخلاصات RSS';
+$lang['ar_SA']['SubscribeRSSWidget.ss']['SUBSCRIBETEXT'] = 'اشتراك';
+$lang['ar_SA']['SubscribeRSSWidget.ss']['SUBSCRIBETITLE'] = 'الاشتراك في المدونة عن طريق الخلاصات RSS';
+$lang['ar_SA']['TagCloudWidget']['LIMIT'] = 'العدد المحدد للوسوم';
+$lang['ar_SA']['TagCloudWidget']['PLURALNAME'] = 'مربعات الوسوم السحابية';
+$lang['ar_SA']['TagCloudWidget']['SBAL'] = 'هجائي';
+$lang['ar_SA']['TagCloudWidget']['SBFREQ'] = 'تكرار';
+$lang['ar_SA']['TagCloudWidget']['SINGULARNAME'] = 'مربع الوسوم السحابيةَ';
+$lang['ar_SA']['TagCloudWidget']['SORTBY'] = 'ترتيب';
+$lang['ar_SA']['TagCloudWidget']['TILE'] = 'العنوان';
+$lang['ar_SA']['TrackBackPing']['PLURALNAME'] = 'تنبيهات التعقيبات';
+$lang['ar_SA']['TrackBackPing']['SINGULARNAME'] = 'تنبيه التعقيبات';
+
+?>
View
63 0.4.0-rc1/lang/bg_BG.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * Bulgarian (Bulgaria) language pack
+ * @package blog
+ * @subpackage i18n
+ */
+
+i18n::include_locale_file('blog', 'en_US');
+
+global $lang;
+
+if(array_key_exists('bg_BG', $lang) && is_array($lang['bg_BG'])) {
+ $lang['bg_BG'] = array_merge($lang['en_US'], $lang['bg_BG']);
+} else {
+ $lang['bg_BG'] = $lang['en_US'];
+}
+
+$lang['bg_BG']['ArchiveWidget']['DispBY'] = 'Покажи по';
+$lang['bg_BG']['ArchiveWidget']['MONTH'] = 'месец';
+$lang['bg_BG']['ArchiveWidget']['YEAR'] = 'година';
+$lang['bg_BG']['BlogEntry']['AU'] = 'Автор';
+$lang['bg_BG']['BlogEntry']['BBH'] = 'BBCode помощ';
+$lang['bg_BG']['BlogEntry']['CN'] = 'Съдържание';
+$lang['bg_BG']['BlogEntry']['DT'] = 'Дата';
+$lang['bg_BG']['BlogEntry.ss']['COMMENTS'] = 'Коментари';
+$lang['bg_BG']['BlogEntry.ss']['EDITTHIS'] = 'Промени тази статия';
+$lang['bg_BG']['BlogEntry.ss']['POSTEDBY'] = 'Публикувано от';
+$lang['bg_BG']['BlogEntry.ss']['POSTEDON'] = 'на';
+$lang['bg_BG']['BlogEntry.ss']['TAGS'] = 'Марки:';
+$lang['bg_BG']['BlogEntry.ss']['UNPUBLISHTHIS'] = 'Премахни от публикация тази статия';
+$lang['bg_BG']['BlogEntry.ss']['VIEWALLPOSTTAGGED'] = 'Прегледай всички маркирани статий';
+$lang['bg_BG']['BlogEntry']['TS'] = 'Марки (разделени със запетайка)';
+$lang['bg_BG']['BlogHolder']['HAVENTPERM'] = 'Публикуване на блогове е администраторска задача. Моля влезте в системата.';
+$lang['bg_BG']['BlogHolder']['POST'] = 'Публикувай блог статия';
+$lang['bg_BG']['BlogHolder']['RSSFEED'] = 'RSS емисия за този блог';
+$lang['bg_BG']['BlogHolder']['SJ'] = 'Предмет';
+$lang['bg_BG']['BlogHolder']['SPUC'] = 'Моля разделете марките използвайки запетайки.';
+$lang['bg_BG']['BlogHolder.ss']['NOENTRIES'] = 'Няма никакви блог статий';
+$lang['bg_BG']['BlogHolder.ss']['VIEWINGTAGGED'] = 'Разглеждане на статий маркирани с';
+$lang['bg_BG']['BlogHolder']['SUCCONTENT'] = 'Поздравления, SilverStripe blog модула беше инсталиран успешно. Тази блог статия може да бъде изтрита. Сега можете да конфигурирате аспектите на вашият блог (например кои widgets ще се показват) в [url=admin]CMS системата[/url].';
+$lang['bg_BG']['BlogHolder']['SUCTAGS'] = 'silverstripe, блог';
+$lang['bg_BG']['BlogHolder']['SUCTITLE'] = 'SilverStripe блог модул успешно инсталиран';
+$lang['bg_BG']['BlogHolder']['TE'] = 'Например: спорт, наука, здраве';
+$lang['bg_BG']['BlogManagementWidget']['COMADM'] = 'Администрация за коментари';
+$lang['bg_BG']['BlogManagementWidget.ss']['LOGOUT'] = 'Излез';
+$lang['bg_BG']['BlogManagementWidget.ss']['POSTNEW'] = 'Публикувайте нова блог статия';
+$lang['bg_BG']['BlogManagementWidget']['UNM1'] = 'Вие имате 1 непрегледан коментар';
+$lang['bg_BG']['BlogManagementWidget']['UNMM'] = 'Вие имате %i непрегледани коментара';
+$lang['bg_BG']['BlogSummary.ss']['COMMENTS'] = 'Коментари';
+$lang['bg_BG']['BlogSummary.ss']['POSTEDBY'] = 'Публикувано от';
+$lang['bg_BG']['BlogSummary.ss']['POSTEDON'] = 'на';
+$lang['bg_BG']['BlogSummary.ss']['VIEWFULL'] = 'Разгледай цялата статия';
+$lang['bg_BG']['RSSWidget']['CT'] = 'Собствено заглавие за емисията';
+$lang['bg_BG']['RSSWidget']['NTS'] = 'Брой на предмети за показване';
+$lang['bg_BG']['RSSWidget']['URL'] = 'Адрес на RSS емисия