1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-30 17:30:59 +01:00

Phame - allow blogs to specify custom URIs

Summary: this then enables people to create blog.theircompany.com. And for us, blog.phacility.com...!

Test Plan:
 - created custom URIs of various goodness and verified the error messages were sensical.
 - verified if "false" in configuration then custom uri stuff disappears

Reviewers: epriestley

Reviewed By: epriestley

CC: aran, Korvin

Maniphest Tasks: T1373

Differential Revision: https://secure.phabricator.com/D3542
This commit is contained in:
Bob Trahan 2012-09-30 17:10:27 -07:00
parent f29804a62a
commit 9e1b643896
8 changed files with 147 additions and 26 deletions

View file

@ -735,15 +735,6 @@ 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 ------------------------------------------------------------- //
@ -1070,6 +1061,16 @@ return array(
'phriction.enabled' => true, 'phriction.enabled' => true,
// -- Phame ----------------------------------------------------------------- //
// 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,
// -- Remarkup -------------------------------------------------------------- // // -- Remarkup -------------------------------------------------------------- //
// If you enable this, linked YouTube videos will be embeded inline. This has // If you enable this, linked YouTube videos will be embeded inline. This has

View file

@ -0,0 +1,4 @@
ALTER TABLE `{$NAMESPACE}_phame`.`phame_blog`
ADD COLUMN `domain` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin
AFTER `description`,
ADD UNIQUE KEY (`domain`);

View file

@ -91,9 +91,11 @@ abstract class AphrontApplicationConfiguration {
/** /**
* Using builtin and application routes, build the appropriate * Using builtin and application routes, build the appropriate
* @{class:AphrontController} class for the request. To route a request, we * @{class:AphrontController} class for the request. To route a request, we
* test the URI against all builtin routes from @{method:getURIMap}, then * first test if the HTTP_HOST is configured as a valid Phabricator URI. If
* against all application routes from installed * it isn't, we do a special check to see if it's a custom domain for a blog
* @{class:PhabricatorApplication}s. * in the Phame application and if that fails we error. Otherwise, we test
* the URI against all builtin routes from @{method:getURIMap}, then against
* all application routes from installed @{class:PhabricatorApplication}s.
* *
* If we match a route, we construct the controller it points at, build it, * If we match a route, we construct the controller it points at, build it,
* and return it. * and return it.
@ -117,7 +119,6 @@ abstract class AphrontApplicationConfiguration {
*/ */
final public function buildController() { final public function buildController() {
$request = $this->getRequest(); $request = $this->getRequest();
$path = $request->getPath();
if (PhabricatorEnv::getEnvConfig('security.require-https')) { if (PhabricatorEnv::getEnvConfig('security.require-https')) {
if (!$request->isHTTPS()) { if (!$request->isHTTPS()) {
@ -128,6 +129,41 @@ abstract class AphrontApplicationConfiguration {
} }
} }
$path = $request->getPath();
$host = $request->getHost();
$base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
$prod_uri = PhabricatorEnv::getEnvConfig('phabricator.production-uri');
$file_uri = PhabricatorEnv::getEnvConfig('security.alternate-file-domain');
if ($host != id(new PhutilURI($base_uri))->getDomain() &&
$host != id(new PhutilURI($prod_uri))->getDomain() &&
$host != id(new PhutilURI($file_uri))->getDomain()) {
$blogs = id(new PhameBlogQuery())->withDomain($host)->execute();
$blog = reset($blogs);
if (!$blog) {
if ($prod_uri) {
$prod_str = ' or '.$prod_uri;
} else {
$prod_str = '';
}
throw new Exception(
'Specified domain '.$host.' is not configured for Phabricator '.
'requests. Please use '.$base_uri.$prod_str.' to visit this instance.'
);
}
// 2 basic cases
// -- looking at a list of blog posts, path is nothing or '/'
// -- looking at an actual blog post, path is like /btrahan/post_title
if (!$path || $path == '/') {
$path = $blog->getViewURI();
} else {
$path = '/phame/posts/'.trim($path, '/').'/';
}
// TODO - now we need to tell Celerity to render static resources with
// full URIs like secure.phabricator.org/rsrc/blahblah
}
list($controller, $uri_data) = $this->buildControllerForPath($path); list($controller, $uri_data) = $this->buildControllerForPath($path);
if (!$controller) { if (!$controller) {
if (!preg_match('@/$@', $path)) { if (!preg_match('@/$@', $path)) {

View file

@ -74,11 +74,12 @@ final class PhameBlogEditController
} }
public function processRequest() { public function processRequest() {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $user = $request->getUser();
$e_name = null; $e_name = null;
$e_bloggers = null; $e_bloggers = null;
$errors = array(); $e_custom_domain = null;
$errors = array();
if ($this->isBlogEdit()) { if ($this->isBlogEdit()) {
$blogs = id(new PhameBlogQuery()) $blogs = id(new PhameBlogQuery())
@ -117,10 +118,11 @@ final class PhameBlogEditController
} }
if ($request->isFormPost()) { if ($request->isFormPost()) {
$saved = true; $saved = true;
$name = $request->getStr('name'); $name = $request->getStr('name');
$description = $request->getStr('description'); $description = $request->getStr('description');
$blogger_arr = $request->getArr('bloggers'); $blogger_arr = $request->getArr('bloggers');
$custom_domain = $request->getStr('custom_domain');
if (empty($blogger_arr)) { if (empty($blogger_arr)) {
$error = 'Bloggers must be nonempty.'; $error = 'Bloggers must be nonempty.';
@ -145,6 +147,14 @@ final class PhameBlogEditController
} }
$blog->setName($name); $blog->setName($name);
$blog->setDescription($description); $blog->setDescription($description);
if (!empty($custom_domain)) {
$error = $blog->validateCustomDomain($custom_domain);
if ($error) {
$errors[] = $error;
$e_custom_domain = 'Invalid';
}
$blog->setDomain($custom_domain);
}
if (empty($errors)) { if (empty($errors)) {
$blog->save(); $blog->save();
@ -168,7 +178,11 @@ final class PhameBlogEditController
if ($saved) { if ($saved) {
$uri = new PhutilURI($blog->getViewURI()); $uri = new PhutilURI($blog->getViewURI());
$uri->setQueryParam('new', true); if ($this->isBlogEdit()) {
$uri->setQueryParam('edit', true);
} else {
$uri->setQueryParam('new', true);
}
return id(new AphrontRedirectResponse()) return id(new AphrontRedirectResponse())
->setURI($uri); ->setURI($uri);
} }
@ -208,8 +222,17 @@ final class PhameBlogEditController
->setDatasource('/typeahead/common/users/') ->setDatasource('/typeahead/common/users/')
->setError($e_bloggers) ->setError($e_bloggers)
) )
->appendChild( ->appendChild(
id(new AphrontFormSubmitControl()) id(new AphrontFormTextControl())
->setLabel('Custom Domain')
->setName('custom_domain')
->setValue($blog->getDomain())
->setCaption('Must include at least one dot (.), e.g. '.
'blog.example.com')
->setError($e_custom_domain)
)
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/phame/blog/') ->addCancelButton('/phame/blog/')
->setValue($submit_button) ->setValue($submit_button)
); );

View file

@ -111,6 +111,10 @@ final class PhameBlogViewController
$notice = $this->buildNoticeView() $notice = $this->buildNoticeView()
->setTitle('Successfully created your blog.') ->setTitle('Successfully created your blog.')
->appendChild('Time to write some posts.'); ->appendChild('Time to write some posts.');
} else if ($request->getExists('edit')) {
$notice = $this->buildNoticeView()
->setTitle('Successfully edited your blog.')
->appendChild('Time to write some posts.');
} else { } else {
$notice = null; $notice = null;
} }

View file

@ -22,6 +22,7 @@
final class PhameBlogQuery extends PhabricatorOffsetPagedQuery { final class PhameBlogQuery extends PhabricatorOffsetPagedQuery {
private $phids; private $phids;
private $domain;
private $needBloggers; private $needBloggers;
public function withPHIDs($phids) { public function withPHIDs($phids) {
@ -29,6 +30,11 @@ final class PhameBlogQuery extends PhabricatorOffsetPagedQuery {
return $this; return $this;
} }
public function withDomain($domain) {
$this->domain = $domain;
return $this;
}
public function needBloggers($need_bloggers) { public function needBloggers($need_bloggers) {
$this->needBloggers = $need_bloggers; $this->needBloggers = $need_bloggers;
return $this; return $this;
@ -96,7 +102,16 @@ final class PhameBlogQuery extends PhabricatorOffsetPagedQuery {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn_r,
'phid IN (%Ls)', 'phid IN (%Ls)',
$this->phids); $this->phids
);
}
if ($this->domain) {
$where[] = qsprintf(
$conn_r,
'domain = %s',
$this->domain
);
} }
return $this->formatWhereClause($where); return $this->formatWhereClause($where);

View file

@ -25,6 +25,7 @@ final class PhameBlog extends PhameDAO {
protected $phid; protected $phid;
protected $name; protected $name;
protected $description; protected $description;
protected $domain;
protected $configData; protected $configData;
protected $creatorPHID; protected $creatorPHID;
@ -45,6 +46,38 @@ final class PhameBlog extends PhameDAO {
PhabricatorPHIDConstants::PHID_TYPE_BLOG); PhabricatorPHIDConstants::PHID_TYPE_BLOG);
} }
/**
* Makes sure a given custom blog uri is properly configured in DNS
* to point at this Phabricator instance. If there is an error in
* the configuration, return a string describing the error and how
* to fix it. If there is no error, return an empty string.
*
* @return string
*/
public function validateCustomDomain($custom_domain) {
$example_domain = '(e.g. blog.example.com)';
$valid = '';
// note this "uri" should be pretty busted given the desired input
// so just use it to test if there's a protocol specified
$uri = new PhutilURI($custom_domain);
if ($uri->getProtocol()) {
return 'Do not specify a protocol, just the domain. '.$example_domain;
}
if (strpos($custom_domain, '/') !== false) {
return 'Do not specify a path, just the domain. '.$example_domain;
}
if (strpos($custom_domain, '.') === false) {
return 'Custom domain must contain at least one dot (.) because '.
'some browsers fail to set cookies on domains such as '.
'http://example. '.$example_domain;
}
return $valid;
}
public function loadBloggerPHIDs() { public function loadBloggerPHIDs() {
if (!$this->getPHID()) { if (!$this->getPHID()) {
return $this; return $this;

View file

@ -988,6 +988,11 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'type' => 'sql', 'type' => 'sql',
'name' => $this->getPatchPath('draft-metadata.sql'), 'name' => $this->getPatchPath('draft-metadata.sql'),
), ),
'phamedomain.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('phamedomain.sql'),
),
); );
} }