mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 16:52:41 +01:00
Phame V1 - Phabricator blogging software
Summary: 'cuz we need to be phamous! V1 feature set - posts -- standard thing you'd expect - a title and a remarkup-powered body and... -- "phame" title - a short string that can be used to reference the story. this gets auto-updated when you mess with the title. -- configuration - for now, do you want Facebook, Disqus or no comments? this is a per-post thing but feeds from an instance-wide configuration Please do toss out any must have features or changes. Test Plan: played around with this bad boy like whoa Reviewers: epriestley Reviewed By: epriestley CC: aran, vrana Maniphest Tasks: T1111 Differential Revision: https://secure.phabricator.com/D2202
This commit is contained in:
parent
3d6b8bff34
commit
51418900f7
35 changed files with 1832 additions and 0 deletions
|
@ -489,6 +489,16 @@ return array(
|
||||||
// The Phabricator "Client Secret" to use for Phabricator API access.
|
// The Phabricator "Client Secret" to use for Phabricator API access.
|
||||||
'phabricator.application-secret' => null,
|
'phabricator.application-secret' => null,
|
||||||
|
|
||||||
|
// -- Disqus Comments ------------------------------------------------------- //
|
||||||
|
|
||||||
|
// Should Phame users have Disqus comment widget, and if so what's the
|
||||||
|
// website shortname to use? For example, secure.phabricator.org uses
|
||||||
|
// "phabricator", which we registered with Disqus. If you aren't familiar
|
||||||
|
// with Disqus, see:
|
||||||
|
// Disqus quick start guide - http://docs.disqus.com/help/4/
|
||||||
|
// Information on shortnames - http://docs.disqus.com/help/68/
|
||||||
|
'disqus.shortname' => null,
|
||||||
|
|
||||||
// -- Recaptcha ------------------------------------------------------------- //
|
// -- Recaptcha ------------------------------------------------------------- //
|
||||||
|
|
||||||
// Is Recaptcha enabled? If disabled, captchas will not appear. You should
|
// Is Recaptcha enabled? If disabled, captchas will not appear. You should
|
||||||
|
|
18
resources/sql/patches/132.phame.sql
Normal file
18
resources/sql/patches/132.phame.sql
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
CREATE DATABASE IF NOT EXISTS `phabricator_phame` COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
CREATE TABLE `phabricator_phame`.`phame_post` (
|
||||||
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`phid` VARCHAR(64) BINARY NOT NULL COLLATE utf8_bin,
|
||||||
|
`bloggerPHID` VARCHAR(64) BINARY NOT NULL COLLATE utf8_bin,
|
||||||
|
`title` VARCHAR(255) NOT NULL,
|
||||||
|
`phameTitle` VARCHAR(64) NOT NULL,
|
||||||
|
`body` LONGTEXT COLLATE utf8_general_ci,
|
||||||
|
`visibility` INT UNSIGNED NOT NULL DEFAULT 0,
|
||||||
|
`configData` LONGTEXT COLLATE utf8_general_ci,
|
||||||
|
`datePublished` INT UNSIGNED NOT NULL,
|
||||||
|
`dateCreated` INT UNSIGNED NOT NULL,
|
||||||
|
`dateModified` INT UNSIGNED NOT NULL,
|
||||||
|
KEY `bloggerPosts` (`bloggerPHID`, `visibility`, `datePublished`, `id`),
|
||||||
|
UNIQUE KEY `phid` (`phid`),
|
||||||
|
UNIQUE KEY `phameTitle` (`bloggerPHID`, `phameTitle`)
|
||||||
|
) ENGINE=InnoDB;
|
|
@ -1281,6 +1281,19 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'disk' => '/rsrc/js/application/core/behavior-watch-anchor.js',
|
'disk' => '/rsrc/js/application/core/behavior-watch-anchor.js',
|
||||||
),
|
),
|
||||||
|
'javelin-behavior-phame-post-preview' =>
|
||||||
|
array(
|
||||||
|
'uri' => '/res/ac4c503a/rsrc/js/application/phame/phame-post-preview.js',
|
||||||
|
'type' => 'js',
|
||||||
|
'requires' =>
|
||||||
|
array(
|
||||||
|
0 => 'javelin-behavior',
|
||||||
|
1 => 'javelin-dom',
|
||||||
|
2 => 'javelin-util',
|
||||||
|
3 => 'phabricator-shaped-request',
|
||||||
|
),
|
||||||
|
'disk' => '/rsrc/js/application/phame/phame-post-preview.js',
|
||||||
|
),
|
||||||
'javelin-behavior-phriction-document-preview' =>
|
'javelin-behavior-phriction-document-preview' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/f1665ecd/rsrc/js/application/phriction/phriction-document-preview.js',
|
'uri' => '/res/f1665ecd/rsrc/js/application/phriction/phriction-document-preview.js',
|
||||||
|
|
|
@ -929,6 +929,19 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/symbol',
|
'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/symbol',
|
||||||
'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/toplevel',
|
'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/toplevel',
|
||||||
'PhabricatorXHProfProfileView' => 'applications/xhprof/view/base',
|
'PhabricatorXHProfProfileView' => 'applications/xhprof/view/base',
|
||||||
|
'PhameController' => 'applications/phame/controller/base',
|
||||||
|
'PhameDAO' => 'applications/phame/storage/base',
|
||||||
|
'PhameDraftListController' => 'applications/phame/controller/post/list/drafts',
|
||||||
|
'PhamePost' => 'applications/phame/storage/post',
|
||||||
|
'PhamePostDeleteController' => 'applications/phame/controller/post/delete',
|
||||||
|
'PhamePostDetailView' => 'applications/phame/view/postdetail',
|
||||||
|
'PhamePostEditController' => 'applications/phame/controller/post/edit',
|
||||||
|
'PhamePostListBaseController' => 'applications/phame/controller/post/list/base',
|
||||||
|
'PhamePostListController' => 'applications/phame/controller/post/list/posts',
|
||||||
|
'PhamePostListView' => 'applications/phame/view/postlist',
|
||||||
|
'PhamePostPreviewController' => 'applications/phame/controller/post/preview',
|
||||||
|
'PhamePostQuery' => 'applications/phame/query/post',
|
||||||
|
'PhamePostViewController' => 'applications/phame/controller/post/view',
|
||||||
'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/monthyearexpiry',
|
'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/monthyearexpiry',
|
||||||
'PhortuneStripeBaseController' => 'applications/phortune/stripe/controller/base',
|
'PhortuneStripeBaseController' => 'applications/phortune/stripe/controller/base',
|
||||||
'PhortuneStripePaymentFormView' => 'applications/phortune/stripe/view/paymentform',
|
'PhortuneStripePaymentFormView' => 'applications/phortune/stripe/view/paymentform',
|
||||||
|
@ -1743,6 +1756,19 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorXHProfProfileSymbolView' => 'PhabricatorXHProfProfileView',
|
'PhabricatorXHProfProfileSymbolView' => 'PhabricatorXHProfProfileView',
|
||||||
'PhabricatorXHProfProfileTopLevelView' => 'PhabricatorXHProfProfileView',
|
'PhabricatorXHProfProfileTopLevelView' => 'PhabricatorXHProfProfileView',
|
||||||
'PhabricatorXHProfProfileView' => 'AphrontView',
|
'PhabricatorXHProfProfileView' => 'AphrontView',
|
||||||
|
'PhameController' => 'PhabricatorController',
|
||||||
|
'PhameDAO' => 'PhabricatorLiskDAO',
|
||||||
|
'PhameDraftListController' => 'PhamePostListBaseController',
|
||||||
|
'PhamePost' => 'PhameDAO',
|
||||||
|
'PhamePostDeleteController' => 'PhameController',
|
||||||
|
'PhamePostDetailView' => 'AphrontView',
|
||||||
|
'PhamePostEditController' => 'PhameController',
|
||||||
|
'PhamePostListBaseController' => 'PhameController',
|
||||||
|
'PhamePostListController' => 'PhamePostListBaseController',
|
||||||
|
'PhamePostListView' => 'AphrontView',
|
||||||
|
'PhamePostPreviewController' => 'PhameController',
|
||||||
|
'PhamePostQuery' => 'PhabricatorOffsetPagedQuery',
|
||||||
|
'PhamePostViewController' => 'PhameController',
|
||||||
'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
|
'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
|
||||||
'PhortuneStripeBaseController' => 'PhabricatorController',
|
'PhortuneStripeBaseController' => 'PhabricatorController',
|
||||||
'PhortuneStripePaymentFormView' => 'AphrontView',
|
'PhortuneStripePaymentFormView' => 'AphrontView',
|
||||||
|
|
|
@ -385,6 +385,28 @@ class AphrontDefaultApplicationConfiguration
|
||||||
'diff/(?P<id>\d+)/' => 'PhrictionDiffController',
|
'diff/(?P<id>\d+)/' => 'PhrictionDiffController',
|
||||||
),
|
),
|
||||||
|
|
||||||
|
'/phame/' => array(
|
||||||
|
'' => 'PhamePostListController',
|
||||||
|
'post/' => array(
|
||||||
|
'' => 'PhamePostListController',
|
||||||
|
'delete/(?P<phid>[^/]+)/' => 'PhamePostDeleteController',
|
||||||
|
'edit/(?P<phid>[^/]+)/' => 'PhamePostEditController',
|
||||||
|
'new/' => 'PhamePostEditController',
|
||||||
|
'preview/' => 'PhamePostPreviewController',
|
||||||
|
'view/(?P<phid>[^/]+)/' => 'PhamePostViewController',
|
||||||
|
),
|
||||||
|
'draft/' => array(
|
||||||
|
'' => 'PhameDraftListController',
|
||||||
|
'new/' => 'PhamePostEditController',
|
||||||
|
),
|
||||||
|
'posts/' => array(
|
||||||
|
'' => 'PhamePostListController',
|
||||||
|
'(?P<bloggername>\w+)/' => 'PhamePostListController',
|
||||||
|
'(?P<bloggername>\w+)/(?P<phametitle>.+/)'
|
||||||
|
=> 'PhamePostViewController',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
'/calendar/' => array(
|
'/calendar/' => array(
|
||||||
'' => 'PhabricatorCalendarBrowseController',
|
'' => 'PhabricatorCalendarBrowseController',
|
||||||
),
|
),
|
||||||
|
|
|
@ -49,6 +49,13 @@ class PhabricatorMarkupEngine {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function newPhameMarkupEngine() {
|
||||||
|
return self::newMarkupEngine(array(
|
||||||
|
'macros' => false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static function newDifferentialMarkupEngine(array $options = array()) {
|
public static function newDifferentialMarkupEngine(array $options = array()) {
|
||||||
return self::newMarkupEngine(array(
|
return self::newMarkupEngine(array(
|
||||||
'custom-inline' => PhabricatorEnv::getEnvConfig(
|
'custom-inline' => PhabricatorEnv::getEnvConfig(
|
||||||
|
|
91
src/applications/phame/controller/base/PhameController.php
Normal file
91
src/applications/phame/controller/base/PhameController.php
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group phame
|
||||||
|
*/
|
||||||
|
abstract class PhameController extends PhabricatorController {
|
||||||
|
private $showSideNav;
|
||||||
|
|
||||||
|
protected function setShowSideNav($value) {
|
||||||
|
$this->showSideNav = (bool) $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function showSideNav() {
|
||||||
|
return $this->showSideNav;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildStandardPageResponse($view, array $data) {
|
||||||
|
|
||||||
|
$page = $this->buildStandardPageView();
|
||||||
|
|
||||||
|
$page->setApplicationName('Phame');
|
||||||
|
$page->setBaseURI('/phame/');
|
||||||
|
$page->setTitle(idx($data, 'title'));
|
||||||
|
$page->setGlyph("\xe2\x9c\xa9");
|
||||||
|
|
||||||
|
$tabs = array(
|
||||||
|
'help' => array(
|
||||||
|
'name' => 'Help',
|
||||||
|
'href' =>
|
||||||
|
PhabricatorEnv::getDoclink('article/Phame_User_Guide.html'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$page->setTabs($tabs, idx($data, 'tab'));
|
||||||
|
if ($this->showSideNav()) {
|
||||||
|
$nav = $this->renderSideNavFilterView($this->getSideNavFilter());
|
||||||
|
$nav->appendChild($view);
|
||||||
|
$page->appendChild($nav);
|
||||||
|
} else {
|
||||||
|
$page->appendChild($view);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = new AphrontWebpageResponse();
|
||||||
|
return $response->setContent($page->render());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderSideNavFilterView($filter) {
|
||||||
|
$nav = new AphrontSideNavFilterView();
|
||||||
|
$nav->setBaseURI(new PhutilURI('/phame/'));
|
||||||
|
$nav->addLabel('Drafts');
|
||||||
|
$nav->addFilter('post/new',
|
||||||
|
'New Draft');
|
||||||
|
$nav->addFilter('draft',
|
||||||
|
'My Drafts');
|
||||||
|
$nav->addSpacer();
|
||||||
|
$nav->addLabel('Posts');
|
||||||
|
$nav->addFilter('post',
|
||||||
|
'My Posts');
|
||||||
|
foreach ($this->getSideNavExtraPostFilters() as $post_filter) {
|
||||||
|
$nav->addFilter($post_filter['key'],
|
||||||
|
$post_filter['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$nav->selectFilter($filter, 'post');
|
||||||
|
|
||||||
|
return $nav;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSideNavExtraPostFilters() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
protected function getSideNavFilter() {
|
||||||
|
return 'post';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
18
src/applications/phame/controller/base/__init__.php
Normal file
18
src/applications/phame/controller/base/__init__.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/webpage');
|
||||||
|
phutil_require_module('phabricator', 'applications/base/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
|
phutil_require_module('phabricator', 'view/layout/sidenavfilter');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'parser/uri');
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhameController.php');
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group phame
|
||||||
|
*/
|
||||||
|
final class PhamePostDeleteController
|
||||||
|
extends PhameController {
|
||||||
|
|
||||||
|
private $phid;
|
||||||
|
|
||||||
|
private function setPostPHID($phid) {
|
||||||
|
$this->phid = $phid;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function getPostPHID() {
|
||||||
|
return $this->phid;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSideNavFilter() {
|
||||||
|
return 'post/delete/'.$this->getPostPHID();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSideNavExtraPostFilters() {
|
||||||
|
$filters = array(
|
||||||
|
array('key' => $this->getSideNavFilter(),
|
||||||
|
'name' => 'Delete Post')
|
||||||
|
);
|
||||||
|
|
||||||
|
return $filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$phid = $data['phid'];
|
||||||
|
$this->setPostPHID($phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$user = $request->getUser();
|
||||||
|
$post = id(new PhamePost())->loadOneWhere(
|
||||||
|
'phid = %s',
|
||||||
|
$this->getPostPHID());
|
||||||
|
if (empty($post)) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
if ($post->getBloggerPHID() != $user->getPHID()) {
|
||||||
|
return new Aphront403Response();
|
||||||
|
}
|
||||||
|
$edit_uri = $post->getEditURI();
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
$post->delete();
|
||||||
|
return id(new AphrontRedirectResponse())->setURI('/phame/?deleted');
|
||||||
|
}
|
||||||
|
|
||||||
|
$dialog = id(new AphrontDialogView())
|
||||||
|
->setUser($user)
|
||||||
|
->setTitle('Delete post?')
|
||||||
|
->appendChild('Really delete this post? It will be gone forever.')
|
||||||
|
->addSubmitButton('Delete')
|
||||||
|
->addCancelButton($edit_uri);
|
||||||
|
|
||||||
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
|
}
|
||||||
|
}
|
20
src/applications/phame/controller/post/delete/__init__.php
Normal file
20
src/applications/phame/controller/post/delete/__init__.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/403');
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/404');
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/dialog');
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/storage/post');
|
||||||
|
phutil_require_module('phabricator', 'view/dialog');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhamePostDeleteController.php');
|
|
@ -0,0 +1,269 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group phame
|
||||||
|
*/
|
||||||
|
final class PhamePostEditController
|
||||||
|
extends PhameController {
|
||||||
|
|
||||||
|
private $phid;
|
||||||
|
private $isPostEdit;
|
||||||
|
|
||||||
|
private function setPostPHID($phid) {
|
||||||
|
$this->phid = $phid;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function getPostPHID() {
|
||||||
|
return $this->phid;
|
||||||
|
}
|
||||||
|
private function setIsPostEdit($is_post_edit) {
|
||||||
|
$this->isPostEdit = $is_post_edit;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function isPostEdit() {
|
||||||
|
return $this->isPostEdit;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSideNavFilter() {
|
||||||
|
if ($this->isPostEdit()) {
|
||||||
|
$filter = 'post/edit/'.$this->getPostPHID();
|
||||||
|
} else {
|
||||||
|
$filter = 'post/new';
|
||||||
|
}
|
||||||
|
return $filter;
|
||||||
|
}
|
||||||
|
protected function getSideNavExtraPostFilters() {
|
||||||
|
if ($this->isPostEdit()) {
|
||||||
|
$filters = array(
|
||||||
|
array('key' => 'post/edit/'.$this->getPostPHID(),
|
||||||
|
'name' => 'Edit Post')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$filters = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$phid = idx($data, 'phid');
|
||||||
|
$this->setPostPHID($phid);
|
||||||
|
$this->setIsPostEdit((bool) $phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$user = $request->getUser();
|
||||||
|
$e_phame_title = null;
|
||||||
|
$e_title = null;
|
||||||
|
$errors = array();
|
||||||
|
|
||||||
|
if ($this->isPostEdit()) {
|
||||||
|
$post = id(new PhamePost())->loadOneWhere(
|
||||||
|
'phid = %s',
|
||||||
|
$this->getPostPHID());
|
||||||
|
if (empty($post)) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
if ($post->getBloggerPHID() != $user->getPHID()) {
|
||||||
|
return new Aphront403Response();
|
||||||
|
}
|
||||||
|
$cancel_uri = $post->getViewURI($user->getUsername());
|
||||||
|
$submit_button = 'Save Changes';
|
||||||
|
$delete_button = javelin_render_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $post->getDeleteURI(),
|
||||||
|
'class' => 'grey button',
|
||||||
|
'sigil' => 'workflow',
|
||||||
|
),
|
||||||
|
'Delete Post');
|
||||||
|
$page_title = 'Edit Post';
|
||||||
|
} else {
|
||||||
|
$post = id(new PhamePost())
|
||||||
|
->setBloggerPHID($user->getPHID())
|
||||||
|
->setVisibility(PhamePost::VISIBILITY_DRAFT);
|
||||||
|
$cancel_uri = '/phame';
|
||||||
|
$submit_button = 'Create Post';
|
||||||
|
$delete_button = null;
|
||||||
|
$page_title = 'Create Post';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
$saved = true;
|
||||||
|
$visibility = $request->getInt('visibility');
|
||||||
|
$comments = $request->getStr('comments_widget');
|
||||||
|
$data = array('comments_widget' => $comments);
|
||||||
|
$phame_title = $request->getStr('phame_title');
|
||||||
|
$phame_title = PhabricatorSlug::normalize($phame_title);
|
||||||
|
$title = $request->getStr('title');
|
||||||
|
$post->setTitle($title);
|
||||||
|
$post->setPhameTitle($phame_title);
|
||||||
|
$post->setBody($request->getStr('body'));
|
||||||
|
$post->setVisibility($visibility);
|
||||||
|
$post->setConfigData($data);
|
||||||
|
// only publish once...!
|
||||||
|
if ($visibility == PhamePost::VISIBILITY_PUBLISHED) {
|
||||||
|
if (!$post->getDatePublished()) {
|
||||||
|
$post->setDatePublished(time());
|
||||||
|
}
|
||||||
|
// this is basically a cast of null to 0 if its a new post
|
||||||
|
} else if (!$post->getDatePublished()) {
|
||||||
|
$post->setDatePublished(0);
|
||||||
|
}
|
||||||
|
if ($phame_title == '/') {
|
||||||
|
$errors[] = 'Phame title must be nonempty.';
|
||||||
|
$e_phame_title = 'Required';
|
||||||
|
}
|
||||||
|
if (empty($title)) {
|
||||||
|
$errors[] = 'Title must be nonempty.';
|
||||||
|
$e_title = 'Required';
|
||||||
|
}
|
||||||
|
if (empty($errors)) {
|
||||||
|
try {
|
||||||
|
$post->save();
|
||||||
|
} catch (AphrontQueryDuplicateKeyException $e) {
|
||||||
|
$saved = false;
|
||||||
|
$e_phame_title = 'Not Unique';
|
||||||
|
$errors[] = 'Another post already uses this slug. '.
|
||||||
|
'Each post must have a unique slug.';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$saved = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($saved) {
|
||||||
|
$uri = new PhutilURI($post->getViewURI($user->getUsername()));
|
||||||
|
return id(new AphrontRedirectResponse())
|
||||||
|
->setURI($uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$panel = new AphrontPanelView();
|
||||||
|
$panel->setHeader($page_title);
|
||||||
|
$panel->setWidth(AphrontPanelView::WIDTH_FULL);
|
||||||
|
if ($delete_button) {
|
||||||
|
$panel->addButton($delete_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
$remarkup_reference = phutil_render_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' =>
|
||||||
|
PhabricatorEnv::getDoclink('article/Remarkup_Reference.html'),
|
||||||
|
'tabindex' => '-1',
|
||||||
|
'target' => '_blank',
|
||||||
|
),
|
||||||
|
'Formatting Reference');
|
||||||
|
|
||||||
|
$form = id(new AphrontFormView())
|
||||||
|
->setUser($user)
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormTextControl())
|
||||||
|
->setLabel('Title')
|
||||||
|
->setName('title')
|
||||||
|
->setValue($post->getTitle())
|
||||||
|
->setID('post-title')
|
||||||
|
->setError($e_title)
|
||||||
|
)
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormTextControl())
|
||||||
|
->setLabel('Phame Title')
|
||||||
|
->setName('phame_title')
|
||||||
|
->setValue(rtrim($post->getPhameTitle(), '/'))
|
||||||
|
->setID('post-phame-title')
|
||||||
|
->setCaption('Up to 64 alphanumeric characters '.
|
||||||
|
'with underscores for spaces. '.
|
||||||
|
'Formatting is enforced.')
|
||||||
|
->setError($e_phame_title)
|
||||||
|
)
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormTextAreaControl())
|
||||||
|
->setLabel('Body')
|
||||||
|
->setName('body')
|
||||||
|
->setValue($post->getBody())
|
||||||
|
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
|
||||||
|
->setEnableDragAndDropFileUploads(true)
|
||||||
|
->setID('post-body')
|
||||||
|
->setCaption($remarkup_reference)
|
||||||
|
)
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSelectControl())
|
||||||
|
->setLabel('Visibility')
|
||||||
|
->setName('visibility')
|
||||||
|
->setValue($post->getVisibility())
|
||||||
|
->setOptions(PhamePost::getVisibilityOptionsForSelect())
|
||||||
|
)
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSelectControl())
|
||||||
|
->setLabel('Comments Widget')
|
||||||
|
->setName('comments_widget')
|
||||||
|
->setvalue($post->getCommentsWidget())
|
||||||
|
->setOptions($post->getCommentsWidgetOptionsForSelect())
|
||||||
|
)
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSubmitControl())
|
||||||
|
->addCancelButton($cancel_uri)
|
||||||
|
->setValue($submit_button)
|
||||||
|
);
|
||||||
|
|
||||||
|
$panel->appendChild($form);
|
||||||
|
|
||||||
|
$preview_panel =
|
||||||
|
'<div class="aphront-panel-preview ">
|
||||||
|
<div class="phame-post-preview-header">
|
||||||
|
Post Preview
|
||||||
|
</div>
|
||||||
|
<div id="post-preview">
|
||||||
|
<div class="aphront-panel-preview-loading-text">
|
||||||
|
Loading preview...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>';
|
||||||
|
|
||||||
|
Javelin::initBehavior(
|
||||||
|
'phame-post-preview',
|
||||||
|
array(
|
||||||
|
'preview' => 'post-preview',
|
||||||
|
'body' => 'post-body',
|
||||||
|
'title' => 'post-title',
|
||||||
|
'phame_title' => 'post-phame-title',
|
||||||
|
'uri' => '/phame/post/preview/',
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($errors) {
|
||||||
|
$error_view = id(new AphrontErrorView())
|
||||||
|
->setTitle('Errors saving post.')
|
||||||
|
->setErrors($errors);
|
||||||
|
} else {
|
||||||
|
$error_view = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setShowSideNav(true);
|
||||||
|
return $this->buildStandardPageResponse(
|
||||||
|
array(
|
||||||
|
$error_view,
|
||||||
|
$panel,
|
||||||
|
$preview_panel,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => $page_title,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
31
src/applications/phame/controller/post/edit/__init__.php
Normal file
31
src/applications/phame/controller/post/edit/__init__.php
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/403');
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/404');
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/storage/post');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/util/slug');
|
||||||
|
phutil_require_module('phabricator', 'view/form/base');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/select');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/submit');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/text');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/textarea');
|
||||||
|
phutil_require_module('phabricator', 'view/form/error');
|
||||||
|
phutil_require_module('phabricator', 'view/layout/panel');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'markup');
|
||||||
|
phutil_require_module('phutil', 'parser/uri');
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhamePostEditController.php');
|
|
@ -0,0 +1,127 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group phame
|
||||||
|
*/
|
||||||
|
abstract class PhamePostListBaseController
|
||||||
|
extends PhameController {
|
||||||
|
|
||||||
|
private $bloggerName;
|
||||||
|
private $isDraft;
|
||||||
|
|
||||||
|
private function setBloggerName($blogger_name) {
|
||||||
|
$this->bloggerName = $blogger_name;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function getBloggerName() {
|
||||||
|
return $this->bloggerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSideNavExtraPostFilters() {
|
||||||
|
if ($this->isDraft() || !$this->getBloggerName()) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
array(array('key' => $this->getSideNavFilter(),
|
||||||
|
'name' => 'Posts by '.$this->getBloggerName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSideNavFilter() {
|
||||||
|
if ($this->getBloggerName()) {
|
||||||
|
$filter = 'posts/'.$this->getBloggerName();
|
||||||
|
} else if ($this->isDraft()) {
|
||||||
|
$filter = 'draft';
|
||||||
|
} else {
|
||||||
|
$filter = 'posts';
|
||||||
|
}
|
||||||
|
return $filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isDraft() {
|
||||||
|
return (bool) $this->isDraft;
|
||||||
|
}
|
||||||
|
protected function setIsDraft($is_draft) {
|
||||||
|
$this->isDraft = $is_draft;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$this->setBloggerName(idx($data, 'bloggername'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$user = $request->getUser();
|
||||||
|
$pager = new AphrontPagerView();
|
||||||
|
$page_size = 50;
|
||||||
|
$pager->setURI($request->getRequestURI(), 'offset');
|
||||||
|
$pager->setPageSize($page_size);
|
||||||
|
$pager->setOffset($request->getInt('offset'));
|
||||||
|
|
||||||
|
if ($this->getBloggerName()) {
|
||||||
|
$blogger = id(new PhabricatorUser())->loadOneWhere(
|
||||||
|
'username = %s',
|
||||||
|
$this->getBloggerName());
|
||||||
|
if (!$blogger) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
$page_title = 'Posts by '.$this->getBloggerName();
|
||||||
|
if ($blogger->getPHID() == $user->getPHID()) {
|
||||||
|
$actions = array('view', 'edit');
|
||||||
|
} else {
|
||||||
|
$actions = array('view');
|
||||||
|
}
|
||||||
|
$this->setShowSideNav(false);
|
||||||
|
} else {
|
||||||
|
$blogger = $user;
|
||||||
|
$page_title = 'Posts by '.$user->getUserName();
|
||||||
|
$actions = array('view', 'edit');
|
||||||
|
$this->setShowSideNav(true);
|
||||||
|
}
|
||||||
|
$phid = $blogger->getPHID();
|
||||||
|
// user gets to see their own unpublished stuff
|
||||||
|
if ($phid == $user->getPHID() && $this->isDraft()) {
|
||||||
|
$post_visibility = PhamePost::VISIBILITY_DRAFT;
|
||||||
|
} else {
|
||||||
|
$post_visibility = PhamePost::VISIBILITY_PUBLISHED;
|
||||||
|
}
|
||||||
|
$query = new PhamePostQuery();
|
||||||
|
$query->withBloggerPHID($phid);
|
||||||
|
$query->withVisibility($post_visibility);
|
||||||
|
$posts = $query->executeWithPager($pager);
|
||||||
|
$bloggers = array($blogger->getPHID() => $blogger);
|
||||||
|
|
||||||
|
$panel = id(new PhamePostListView())
|
||||||
|
->setUser($user)
|
||||||
|
->setBloggers($bloggers)
|
||||||
|
->setPosts($posts)
|
||||||
|
->setActions($actions)
|
||||||
|
->setDraftList($this->isDraft());
|
||||||
|
|
||||||
|
return $this->buildStandardPageResponse(
|
||||||
|
array(
|
||||||
|
$panel,
|
||||||
|
$pager
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => $page_title,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/404');
|
||||||
|
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/query/post');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/storage/post');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/view/postlist');
|
||||||
|
phutil_require_module('phabricator', 'view/control/pager');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhamePostListBaseController.php');
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group phame
|
||||||
|
*/
|
||||||
|
final class PhameDraftListController
|
||||||
|
extends PhamePostListBaseController {
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$this->setIsDraft(true);
|
||||||
|
return parent::processRequest();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/controller/post/list/base');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhameDraftListController.php');
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group phame
|
||||||
|
*/
|
||||||
|
final class PhamePostListController
|
||||||
|
extends PhamePostListBaseController {
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$this->setIsDraft(false);
|
||||||
|
return parent::processRequest();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/controller/post/list/base');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhamePostListController.php');
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group phame
|
||||||
|
*/
|
||||||
|
final class PhamePostPreviewController
|
||||||
|
extends PhameController {
|
||||||
|
|
||||||
|
protected function getSideNavFilter() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$user = $request->getUser();
|
||||||
|
$body = $request->getStr('body');
|
||||||
|
$title = $request->getStr('title');
|
||||||
|
$phame_title = $request->getStr('phame_title');
|
||||||
|
|
||||||
|
$post = id(new PhamePost())
|
||||||
|
->setBody($body)
|
||||||
|
->setTitle($title)
|
||||||
|
->setPhameTitle($phame_title)
|
||||||
|
->setDateModified(time());
|
||||||
|
|
||||||
|
$post_html = id(new PhamePostDetailView())
|
||||||
|
->setUser($user)
|
||||||
|
->setBlogger($user)
|
||||||
|
->setPost($post)
|
||||||
|
->setIsPreview(true)
|
||||||
|
->render();
|
||||||
|
|
||||||
|
return id(new AphrontAjaxResponse())->setContent($post_html);
|
||||||
|
}
|
||||||
|
}
|
17
src/applications/phame/controller/post/preview/__init__.php
Normal file
17
src/applications/phame/controller/post/preview/__init__.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/ajax');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/storage/post');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/view/postdetail');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhamePostPreviewController.php');
|
|
@ -0,0 +1,151 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group phame
|
||||||
|
*/
|
||||||
|
final class PhamePostViewController
|
||||||
|
extends PhameController {
|
||||||
|
|
||||||
|
private $postPHID;
|
||||||
|
private $phameTitle;
|
||||||
|
private $bloggerName;
|
||||||
|
|
||||||
|
private function setPostPHID($post_phid) {
|
||||||
|
$this->postPHID = $post_phid;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function getPostPHID() {
|
||||||
|
return $this->postPHID;
|
||||||
|
}
|
||||||
|
private function setPhameTitle($phame_title) {
|
||||||
|
$this->phameTitle = $phame_title;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function getPhameTitle() {
|
||||||
|
return $this->phameTitle;
|
||||||
|
}
|
||||||
|
private function setBloggerName($blogger_name) {
|
||||||
|
$this->bloggerName = $blogger_name;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function getBloggerName() {
|
||||||
|
return $this->bloggerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSideNavFilter() {
|
||||||
|
$filter = 'post/view/'.$this->getPostPHID();
|
||||||
|
return $filter;
|
||||||
|
}
|
||||||
|
protected function getSideNavExtraPostFilters() {
|
||||||
|
$filters = array(
|
||||||
|
array('key' => $this->getSideNavFilter(),
|
||||||
|
'name' => $this->getPhameTitle())
|
||||||
|
);
|
||||||
|
return $filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$this->setPostPHID(idx($data, 'phid'));
|
||||||
|
$this->setPhameTitle(idx($data, 'phametitle'));
|
||||||
|
$this->setBloggerName(idx($data, 'bloggername'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$user = $request->getUser();
|
||||||
|
$post_phid = null;
|
||||||
|
|
||||||
|
if ($this->getPostPHID()) {
|
||||||
|
$post_phid = $this->getPostPHID();
|
||||||
|
if (!$post_phid) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$post = id(new PhamePost())->loadOneWhere(
|
||||||
|
'phid = %s',
|
||||||
|
$post_phid);
|
||||||
|
|
||||||
|
if ($post) {
|
||||||
|
$this->setPhameTitle($post->getPhameTitle());
|
||||||
|
}
|
||||||
|
|
||||||
|
$blogger = id(new PhabricatorUser())->loadOneWhere(
|
||||||
|
'phid = %s', $post->getBloggerPHID());
|
||||||
|
if (!$blogger) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ($this->getBloggerName() && $this->getPhameTitle()) {
|
||||||
|
$phame_title = $this->getPhameTitle();
|
||||||
|
$phame_title = PhabricatorSlug::normalize($phame_title);
|
||||||
|
if ($phame_title != $this->getPhameTitle()) {
|
||||||
|
$uri = $post->getViewURI($this->getBloggerName());
|
||||||
|
return id(new AphrontRedirectResponse())->setURI($uri);
|
||||||
|
}
|
||||||
|
$blogger = id(new PhabricatorUser())->loadOneWhere(
|
||||||
|
'username = %s',
|
||||||
|
$this->getBloggerName());
|
||||||
|
if (!$blogger) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
$post = id(new PhamePost())->loadOneWhere(
|
||||||
|
'bloggerPHID = %s AND phameTitle = %s',
|
||||||
|
$blogger->getPHID(),
|
||||||
|
$this->getPhameTitle());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$post) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($post->isDraft() &&
|
||||||
|
$post->getBloggerPHID() != $user->getPHID()) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($post->isDraft()) {
|
||||||
|
$notice = id(new AphrontErrorView())
|
||||||
|
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
|
||||||
|
->setTitle('You are previewing a draft.')
|
||||||
|
->setErrors(array(
|
||||||
|
'Only you can see this draft until you publish it.',
|
||||||
|
'If you chose a comment widget it will show up when you publish.',
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
$notice = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$page_title = $this->getPhameTitle();
|
||||||
|
$page = id(new PhamePostDetailView())
|
||||||
|
->setUser($user)
|
||||||
|
->setRequestURI($request->getRequestURI())
|
||||||
|
->setBlogger($blogger)
|
||||||
|
->setPost($post);
|
||||||
|
|
||||||
|
$this->setShowSideNav(false);
|
||||||
|
return $this->buildStandardPageResponse(
|
||||||
|
array(
|
||||||
|
$notice,
|
||||||
|
$page,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => $page_title,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
21
src/applications/phame/controller/post/view/__init__.php
Normal file
21
src/applications/phame/controller/post/view/__init__.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/404');
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
|
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/storage/post');
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/view/postdetail');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/util/slug');
|
||||||
|
phutil_require_module('phabricator', 'view/form/error');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhamePostViewController.php');
|
79
src/applications/phame/query/post/PhamePostQuery.php
Normal file
79
src/applications/phame/query/post/PhamePostQuery.php
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
final class PhamePostQuery extends PhabricatorOffsetPagedQuery {
|
||||||
|
|
||||||
|
private $bloggerPHID;
|
||||||
|
private $visibility;
|
||||||
|
|
||||||
|
public function withBloggerPHID($blogger_phid) {
|
||||||
|
$this->bloggerPHID = $blogger_phid;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function withVisibility($visibility) {
|
||||||
|
$this->visibility = $visibility;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() {
|
||||||
|
$table = new PhamePost();
|
||||||
|
$conn_r = $table->establishConnection('r');
|
||||||
|
|
||||||
|
$where_clause = $this->buildWhereClause($conn_r);
|
||||||
|
$order_clause = $this->buildOrderClause($conn_r);
|
||||||
|
$limit_clause = $this->buildLimitClause($conn_r);
|
||||||
|
|
||||||
|
$data = queryfx_all(
|
||||||
|
$conn_r,
|
||||||
|
'SELECT * FROM %T e %Q %Q %Q',
|
||||||
|
$table->getTableName(),
|
||||||
|
$where_clause,
|
||||||
|
$order_clause,
|
||||||
|
$limit_clause);
|
||||||
|
|
||||||
|
$posts = $table->loadAllFromArray($data);
|
||||||
|
|
||||||
|
return $posts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildWhereClause($conn_r) {
|
||||||
|
$where = array();
|
||||||
|
|
||||||
|
if ($this->bloggerPHID) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'bloggerPHID = %s',
|
||||||
|
$this->bloggerPHID
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->visibility !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'visibility = %d',
|
||||||
|
$this->visibility
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->formatWhereClause($where);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildOrderClause($conn_r) {
|
||||||
|
return 'ORDER BY datePublished DESC, id DESC';
|
||||||
|
}
|
||||||
|
}
|
15
src/applications/phame/query/post/__init__.php
Normal file
15
src/applications/phame/query/post/__init__.php
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/storage/post');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/query/offsetpaged');
|
||||||
|
phutil_require_module('phabricator', 'storage/qsprintf');
|
||||||
|
phutil_require_module('phabricator', 'storage/queryfx');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhamePostQuery.php');
|
28
src/applications/phame/storage/base/PhameDAO.php
Normal file
28
src/applications/phame/storage/base/PhameDAO.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group phame
|
||||||
|
*/
|
||||||
|
abstract class PhameDAO extends PhabricatorLiskDAO {
|
||||||
|
|
||||||
|
public function getApplicationName() {
|
||||||
|
return 'phame';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
src/applications/phame/storage/base/__init__.php
Normal file
12
src/applications/phame/storage/base/__init__.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/base/storage/lisk');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhameDAO.php');
|
106
src/applications/phame/storage/post/PhamePost.php
Normal file
106
src/applications/phame/storage/post/PhamePost.php
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group phame
|
||||||
|
*/
|
||||||
|
final class PhamePost extends PhameDAO {
|
||||||
|
|
||||||
|
const VISIBILITY_DRAFT = 0;
|
||||||
|
const VISIBILITY_PUBLISHED = 1;
|
||||||
|
|
||||||
|
protected $id;
|
||||||
|
protected $phid;
|
||||||
|
protected $bloggerPHID;
|
||||||
|
protected $title;
|
||||||
|
protected $phameTitle;
|
||||||
|
protected $body;
|
||||||
|
protected $visibility;
|
||||||
|
protected $configData;
|
||||||
|
protected $datePublished;
|
||||||
|
|
||||||
|
public function getViewURI($blogger_name = '') {
|
||||||
|
// go for the pretty uri if we can
|
||||||
|
if ($blogger_name) {
|
||||||
|
$phame_title = PhabricatorSlug::normalize($this->getPhameTitle());
|
||||||
|
$uri = phutil_escape_uri('/phame/posts/'.$blogger_name.'/'.$phame_title);
|
||||||
|
} else {
|
||||||
|
$uri = $this->getActionURI('view');
|
||||||
|
}
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
public function getEditURI() {
|
||||||
|
return $this->getActionURI('edit');
|
||||||
|
}
|
||||||
|
public function getDeleteURI() {
|
||||||
|
return $this->getActionURI('delete');
|
||||||
|
}
|
||||||
|
private function getActionURI($action) {
|
||||||
|
return '/phame/post/'.$action.'/'.$this->getPHID().'/';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isDraft() {
|
||||||
|
return $this->getVisibility() == self::VISIBILITY_DRAFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommentsWidget() {
|
||||||
|
$config_data = $this->getConfigData();
|
||||||
|
if (empty($config_data)) {
|
||||||
|
return 'none';
|
||||||
|
}
|
||||||
|
return idx($config_data, 'comments_widget', 'none');
|
||||||
|
}
|
||||||
|
public function getConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::CONFIG_AUX_PHID => true,
|
||||||
|
self::CONFIG_SERIALIZATION => array(
|
||||||
|
'configData' => self::SERIALIZATION_JSON,
|
||||||
|
),
|
||||||
|
) + parent::getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generatePHID() {
|
||||||
|
return PhabricatorPHID::generateNewPHID(
|
||||||
|
PhabricatorPHIDConstants::PHID_TYPE_POST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getVisibilityOptionsForSelect() {
|
||||||
|
return array(
|
||||||
|
self::VISIBILITY_DRAFT => 'Draft: visible only to me.',
|
||||||
|
self::VISIBILITY_PUBLISHED => 'Published: visible to the whole world.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommentsWidgetOptionsForSelect() {
|
||||||
|
$current = $this->getCommentsWidget();
|
||||||
|
$options = array();
|
||||||
|
|
||||||
|
if ($current == 'facebook' ||
|
||||||
|
PhabricatorEnv::getEnvConfig('facebook.application-id')) {
|
||||||
|
$options['facebook'] = 'Facebook';
|
||||||
|
}
|
||||||
|
if ($current == 'disqus' ||
|
||||||
|
PhabricatorEnv::getEnvConfig('disqus.shortname')) {
|
||||||
|
$options['disqus'] = 'Disqus';
|
||||||
|
}
|
||||||
|
$options['none'] = 'None';
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
19
src/applications/phame/storage/post/__init__.php
Normal file
19
src/applications/phame/storage/post/__init__.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/phame/storage/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/phid/constants');
|
||||||
|
phutil_require_module('phabricator', 'applications/phid/storage/phid');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/util/slug');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'markup');
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhamePost.php');
|
221
src/applications/phame/view/postdetail/PhamePostDetailView.php
Normal file
221
src/applications/phame/view/postdetail/PhamePostDetailView.php
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group phame
|
||||||
|
*/
|
||||||
|
final class PhamePostDetailView extends AphrontView {
|
||||||
|
private $post;
|
||||||
|
private $blogger;
|
||||||
|
private $requestURI;
|
||||||
|
private $user;
|
||||||
|
private $isPreview;
|
||||||
|
|
||||||
|
public function setIsPreview($is_preview) {
|
||||||
|
$this->isPreview = $is_preview;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function isPreview() {
|
||||||
|
return $this->isPreview;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUser(PhabricatorUser $user) {
|
||||||
|
$this->user = $user;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function getUser() {
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRequestURI(PhutilURI $uri) {
|
||||||
|
$uri = PhabricatorEnv::getProductionURI($uri->setQueryParams(array()));
|
||||||
|
$this->requestURI = $uri;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function getRequestURI() {
|
||||||
|
return $this->requestURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBlogger(PhabricatorUser $blogger) {
|
||||||
|
$this->blogger = $blogger;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function getBlogger() {
|
||||||
|
return $this->blogger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPost(PhamePost $post) {
|
||||||
|
$this->post = $post;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function getPost() {
|
||||||
|
return $this->post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() {
|
||||||
|
require_celerity_resource('phabricator-remarkup-css');
|
||||||
|
|
||||||
|
$user = $this->getUser();
|
||||||
|
$blogger = $this->getBlogger();
|
||||||
|
$post = $this->getPost();
|
||||||
|
$engine = PhabricatorMarkupEngine::newPhameMarkupEngine();
|
||||||
|
$body = $engine->markupText($post->getBody());
|
||||||
|
if ($post->isDraft()) {
|
||||||
|
$uri = '/phame/draft/';
|
||||||
|
$label = 'Back to Your Drafts';
|
||||||
|
} else {
|
||||||
|
$uri = '/phame/posts/'.$blogger->getUsername();
|
||||||
|
$label = 'More Posts by '.phutil_escape_html($blogger->getUsername());
|
||||||
|
}
|
||||||
|
$button = phutil_render_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $uri,
|
||||||
|
'class' => 'grey button',
|
||||||
|
),
|
||||||
|
$label
|
||||||
|
);
|
||||||
|
|
||||||
|
$publish_date = $post->getDatePublished();
|
||||||
|
if ($publish_date) {
|
||||||
|
$caption = 'Published '.
|
||||||
|
phabricator_datetime($publish_date,
|
||||||
|
$user);
|
||||||
|
} else {
|
||||||
|
$caption = 'Last edited '.
|
||||||
|
phabricator_datetime($post->getDateModified(),
|
||||||
|
$user);
|
||||||
|
}
|
||||||
|
if ($this->isPreview()) {
|
||||||
|
$width = AphrontPanelView::WIDTH_FULL;
|
||||||
|
} else {
|
||||||
|
$width = AphrontPanelView::WIDTH_WIDE;
|
||||||
|
}
|
||||||
|
$panel = id(new AphrontPanelView())
|
||||||
|
->setHeader(phutil_escape_html($post->getTitle()))
|
||||||
|
->appendChild('<div class="phabricator-remarkup">'.$body.'</div>')
|
||||||
|
->setWidth($width)
|
||||||
|
->addButton($button)
|
||||||
|
->setCaption($caption);
|
||||||
|
if ($user->getPHID() == $post->getBloggerPHID()) {
|
||||||
|
if ($post->isDraft()) {
|
||||||
|
$label = 'Edit Draft';
|
||||||
|
} else {
|
||||||
|
$label = 'Edit Post';
|
||||||
|
}
|
||||||
|
$button = phutil_render_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $post->getEditURI(),
|
||||||
|
'class' => 'grey button',
|
||||||
|
),
|
||||||
|
$label);
|
||||||
|
$panel->addButton($button);
|
||||||
|
}
|
||||||
|
switch ($post->getCommentsWidget()) {
|
||||||
|
case 'facebook':
|
||||||
|
$comments = $this->renderFacebookComments();
|
||||||
|
break;
|
||||||
|
case 'disqus':
|
||||||
|
$comments = $this->renderDisqusComments();
|
||||||
|
break;
|
||||||
|
case 'none':
|
||||||
|
default:
|
||||||
|
$comments = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$panel->appendChild($comments);
|
||||||
|
|
||||||
|
return $panel->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderFacebookComments() {
|
||||||
|
$fb_id = PhabricatorEnv::getEnvConfig('facebook.application-id');
|
||||||
|
if (!$fb_id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fb_root = phutil_render_tag('div',
|
||||||
|
array(
|
||||||
|
'id' => 'fb-root',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$c_uri = '//connect.facebook.net/en_US/all.js#xfbml=1&appId='.$fb_id;
|
||||||
|
$fb_js = jsprintf(
|
||||||
|
'<script>(function(d, s, id) {'.
|
||||||
|
' var js, fjs = d.getElementsByTagName(s)[0];'.
|
||||||
|
' if (d.getElementById(id)) return;'.
|
||||||
|
' js = d.createElement(s); js.id = id;'.
|
||||||
|
' js.src = %s;'.
|
||||||
|
' fjs.parentNode.insertBefore(js, fjs);'.
|
||||||
|
'}(document, \'script\', \'facebook-jssdk\'));</script>',
|
||||||
|
$c_uri
|
||||||
|
);
|
||||||
|
|
||||||
|
$fb_comments = phutil_render_tag('div',
|
||||||
|
array(
|
||||||
|
'class' => 'fb-comments',
|
||||||
|
'data-href' => $this->getRequestURI(),
|
||||||
|
'data-num-posts' => 5,
|
||||||
|
'data-width' => 1080,
|
||||||
|
'data-colorscheme' => 'dark',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return '<hr />' . $fb_root . $fb_js . $fb_comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderDisqusComments() {
|
||||||
|
$disqus_shortname = PhabricatorEnv::getEnvConfig('disqus.shortname');
|
||||||
|
if (!$disqus_shortname) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$post = $this->getPost();
|
||||||
|
|
||||||
|
$disqus_thread = phutil_render_tag('div',
|
||||||
|
array(
|
||||||
|
'id' => 'disqus_thread'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// protip - try some var disqus_developer = 1; action to test locally
|
||||||
|
$disqus_js = jsprintf(
|
||||||
|
'<script>'.
|
||||||
|
' var disqus_shortname = "phabricator";'.
|
||||||
|
' var disqus_identifier = %s;'.
|
||||||
|
' var disqus_url = %s;'.
|
||||||
|
' var disqus_title = %s;'.
|
||||||
|
'(function() {'.
|
||||||
|
' var dsq = document.createElement("script");'.
|
||||||
|
' dsq.type = "text/javascript";'.
|
||||||
|
' dsq.async = true;'.
|
||||||
|
' dsq.src = "http://" + disqus_shortname + ".disqus.com/embed.js";'.
|
||||||
|
'(document.getElementsByTagName("head")[0] ||'.
|
||||||
|
' document.getElementsByTagName("body")[0]).appendChild(dsq);'.
|
||||||
|
'})(); </script>',
|
||||||
|
$post->getPHID(),
|
||||||
|
$this->getRequestURI(),
|
||||||
|
$post->getTitle()
|
||||||
|
);
|
||||||
|
|
||||||
|
return '<hr />' . $disqus_thread . $disqus_js;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
21
src/applications/phame/view/postdetail/__init__.php
Normal file
21
src/applications/phame/view/postdetail/__init__.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/markup/engine');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
|
phutil_require_module('phabricator', 'view/base');
|
||||||
|
phutil_require_module('phabricator', 'view/layout/panel');
|
||||||
|
phutil_require_module('phabricator', 'view/utils');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'markup');
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
phutil_require_module('phutil', 'xsprintf/jsprintf');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhamePostDetailView.php');
|
137
src/applications/phame/view/postlist/PhamePostListView.php
Normal file
137
src/applications/phame/view/postlist/PhamePostListView.php
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group phame
|
||||||
|
*/
|
||||||
|
final class PhamePostListView extends AphrontView {
|
||||||
|
|
||||||
|
private $user;
|
||||||
|
private $posts;
|
||||||
|
private $bloggers;
|
||||||
|
private $actions;
|
||||||
|
private $draftList;
|
||||||
|
|
||||||
|
public function setDraftList($draft_list) {
|
||||||
|
$this->draftList = $draft_list;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function isDraftList() {
|
||||||
|
return (bool) $this->draftList;
|
||||||
|
}
|
||||||
|
private function getPostNoun() {
|
||||||
|
if ($this->isDraftList()) {
|
||||||
|
$noun = 'Draft';
|
||||||
|
} else {
|
||||||
|
$noun = 'Post';
|
||||||
|
}
|
||||||
|
return $noun;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUser(PhabricatorUser $user) {
|
||||||
|
$this->user = $user;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function getUser() {
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
public function setPosts(array $posts) {
|
||||||
|
assert_instances_of($posts, 'PhamePost');
|
||||||
|
$this->posts = $posts;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function getPosts() {
|
||||||
|
return $this->posts;
|
||||||
|
}
|
||||||
|
public function setBloggers(array $bloggers) {
|
||||||
|
assert_instances_of($bloggers, 'PhabricatorUser');
|
||||||
|
$this->bloggers = $bloggers;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function getBloggers() {
|
||||||
|
return $this->bloggers;
|
||||||
|
}
|
||||||
|
public function setActions(array $actions) {
|
||||||
|
$this->actions = $actions;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
private function getActions() {
|
||||||
|
if ($this->actions) {
|
||||||
|
return $this->actions;
|
||||||
|
}
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() {
|
||||||
|
$user = $this->getUser();
|
||||||
|
$posts = $this->getPosts();
|
||||||
|
$bloggers = $this->getBloggers();
|
||||||
|
$noun = $this->getPostNoun();
|
||||||
|
|
||||||
|
if (empty($posts)) {
|
||||||
|
$panel = id(new AphrontPanelView())
|
||||||
|
->setHeader(sprintf('No %ss... Yet!', $noun))
|
||||||
|
->setCaption('Will you answer the call to phame?')
|
||||||
|
->setCreateButton(sprintf('New %s', $noun),
|
||||||
|
sprintf('/phame/%s/new', strtolower($noun)));
|
||||||
|
return $panel->render();
|
||||||
|
}
|
||||||
|
require_celerity_resource('phabricator-remarkup-css');
|
||||||
|
|
||||||
|
$engine = PhabricatorMarkupEngine::newPhameMarkupEngine();
|
||||||
|
$html = array();
|
||||||
|
$actions = $this->getActions();
|
||||||
|
foreach ($posts as $post) {
|
||||||
|
$blogger_phid = $post->getBloggerPHID();
|
||||||
|
$blogger = $bloggers[$blogger_phid];
|
||||||
|
$updated = phabricator_datetime($post->getDateModified(),
|
||||||
|
$user);
|
||||||
|
$body = $engine->markupText($post->getBody());
|
||||||
|
$panel = id(new AphrontPanelView())
|
||||||
|
->setHeader(phutil_escape_html($post->getTitle()))
|
||||||
|
->setCaption('Last updated '.$updated)
|
||||||
|
->appendChild('<div class="phabricator-remarkup">'.$body.'</div>');
|
||||||
|
foreach ($actions as $action) {
|
||||||
|
switch ($action) {
|
||||||
|
case 'view':
|
||||||
|
$uri = $post->getViewURI($blogger->getUsername());
|
||||||
|
$label = 'View '.$noun;
|
||||||
|
break;
|
||||||
|
case 'edit':
|
||||||
|
$uri = $post->getEditURI();
|
||||||
|
$label = 'Edit '.$noun;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$button = phutil_render_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $uri,
|
||||||
|
'class' => 'grey button',
|
||||||
|
),
|
||||||
|
$label);
|
||||||
|
$panel->addButton($button);
|
||||||
|
}
|
||||||
|
|
||||||
|
$html[] = $panel->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode('', $html);
|
||||||
|
}
|
||||||
|
}
|
19
src/applications/phame/view/postlist/__init__.php
Normal file
19
src/applications/phame/view/postlist/__init__.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/markup/engine');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||||
|
phutil_require_module('phabricator', 'view/base');
|
||||||
|
phutil_require_module('phabricator', 'view/layout/panel');
|
||||||
|
phutil_require_module('phabricator', 'view/utils');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'markup');
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhamePostListView.php');
|
|
@ -39,4 +39,5 @@ final class PhabricatorPHIDConstants {
|
||||||
const PHID_TYPE_DRYL = 'DRYL';
|
const PHID_TYPE_DRYL = 'DRYL';
|
||||||
const PHID_TYPE_OASC = 'OASC';
|
const PHID_TYPE_OASC = 'OASC';
|
||||||
const PHID_TYPE_OASA = 'OASA';
|
const PHID_TYPE_OASA = 'OASA';
|
||||||
|
const PHID_TYPE_POST = 'POST';
|
||||||
}
|
}
|
||||||
|
|
18
src/docs/userguide/phame.diviner
Normal file
18
src/docs/userguide/phame.diviner
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
@title Phame User Guide
|
||||||
|
@group userguide
|
||||||
|
|
||||||
|
Journal about your thoughts and feelings. Share with others. Profit.
|
||||||
|
|
||||||
|
= Overview =
|
||||||
|
|
||||||
|
Phame is a simple blogging platform. You can write drafts which only you can
|
||||||
|
see. Later, you can publish these drafts as posts which anyone can see.
|
||||||
|
|
||||||
|
= Comment Widgets =
|
||||||
|
|
||||||
|
Phame supports comment widgets from Facebook and Disqus. The adminstrator
|
||||||
|
of the Phabricator instance must properly configure Phabricator to enable
|
||||||
|
this functionality.
|
||||||
|
|
||||||
|
NOTE: Phame is extremely new and very basic for now. Give us feedback on
|
||||||
|
what you'd like to see improve!
|
81
webroot/rsrc/js/application/phame/phame-post-preview.js
Normal file
81
webroot/rsrc/js/application/phame/phame-post-preview.js
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
* @provides javelin-behavior-phame-post-preview
|
||||||
|
* @requires javelin-behavior
|
||||||
|
* javelin-dom
|
||||||
|
* javelin-util
|
||||||
|
* phabricator-shaped-request
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.behavior('phame-post-preview', function(config) {
|
||||||
|
|
||||||
|
var preview = JX.$(config.preview);
|
||||||
|
var body = JX.$(config.body);
|
||||||
|
var title = JX.$(config.title);
|
||||||
|
var phame_title = JX.$(config.phame_title);
|
||||||
|
var sync_titles = true;
|
||||||
|
|
||||||
|
var titleCallback = function(e) {
|
||||||
|
if (!sync_titles) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var title_string = new String(title.value);
|
||||||
|
phame_title.value = normalizeSlug(title_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
var phameTitleKeyupCallback = function (e) {
|
||||||
|
// stop sync'ing once user edits phame_title directly
|
||||||
|
sync_titles = false;
|
||||||
|
var normalized = normalizeSlug(phame_title.value, true);
|
||||||
|
if (normalized == phame_title.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var position = phame_title.value.length;
|
||||||
|
if ('selectionStart' in phame_title) {
|
||||||
|
position = phame_title.selectionStart;
|
||||||
|
}
|
||||||
|
phame_title.value = normalized;
|
||||||
|
if ('setSelectionRange' in phame_title) {
|
||||||
|
phame_title.focus();
|
||||||
|
phame_title.setSelectionRange(position, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var phameTitleBlurCallback = function (e) {
|
||||||
|
phame_title.value = normalizeSlug(phame_title.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a sort of implementation of PhabricatorSlug::normalize
|
||||||
|
var normalizeSlug = function (slug, spare_trailing_underscore) {
|
||||||
|
var s = slug.toLowerCase().replace(/[^a-z0-9/]+/g, '_').substr(0, 63);
|
||||||
|
if (spare_trailing_underscore) {
|
||||||
|
// do nothing
|
||||||
|
} else {
|
||||||
|
s = s.replace(/_$/g, '');
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
var callback = function(r) {
|
||||||
|
JX.DOM.setContent(JX.$(config.preview), JX.$H(r));
|
||||||
|
};
|
||||||
|
|
||||||
|
var getdata = function() {
|
||||||
|
return {
|
||||||
|
body : body.value,
|
||||||
|
title : title.value,
|
||||||
|
phame_title : phame_title.value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new JX.PhabricatorShapedRequest(config.uri, callback, getdata);
|
||||||
|
var trigger = JX.bind(request, request.trigger);
|
||||||
|
|
||||||
|
JX.DOM.listen(body, 'keydown', null, trigger);
|
||||||
|
JX.DOM.listen(title, 'keydown', null, trigger);
|
||||||
|
JX.DOM.listen(title, 'keyup', null, titleCallback);
|
||||||
|
JX.DOM.listen(phame_title, 'keydown', null, trigger);
|
||||||
|
JX.DOM.listen(phame_title, 'keyup', null, phameTitleKeyupCallback);
|
||||||
|
JX.DOM.listen(phame_title, 'blur', null, phameTitleBlurCallback);
|
||||||
|
request.start();
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in a new issue