diff --git a/resources/sql/autopatches/20160623.phame.blog.fulldomain.1.sql b/resources/sql/autopatches/20160623.phame.blog.fulldomain.1.sql new file mode 100644 index 0000000000..96fc3b27ba --- /dev/null +++ b/resources/sql/autopatches/20160623.phame.blog.fulldomain.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_phame.phame_blog + ADD domainFullURI VARCHAR(128) COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20160623.phame.blog.fulldomain.2.sql b/resources/sql/autopatches/20160623.phame.blog.fulldomain.2.sql new file mode 100644 index 0000000000..a323333c85 --- /dev/null +++ b/resources/sql/autopatches/20160623.phame.blog.fulldomain.2.sql @@ -0,0 +1,3 @@ +UPDATE {$NAMESPACE}_phame.phame_blog + SET domainFullURI = CONCAT('http://', domain, '/') + WHERE domain IS NOT NULL; diff --git a/resources/sql/autopatches/20160623.phame.blog.fulldomain.3.sql b/resources/sql/autopatches/20160623.phame.blog.fulldomain.3.sql new file mode 100644 index 0000000000..05f6009de1 --- /dev/null +++ b/resources/sql/autopatches/20160623.phame.blog.fulldomain.3.sql @@ -0,0 +1,3 @@ +UPDATE {$NAMESPACE}_phame.phame_blogtransaction + SET transactionType = 'phame.blog.full.domain' + WHERE transactionType = 'phame.blog.domain'; diff --git a/src/applications/phame/controller/blog/PhameBlogManageController.php b/src/applications/phame/controller/blog/PhameBlogManageController.php index a87b02070d..4085d3ef6b 100644 --- a/src/applications/phame/controller/blog/PhameBlogManageController.php +++ b/src/applications/phame/controller/blog/PhameBlogManageController.php @@ -1,242 +1,241 @@ getViewer(); $id = $request->getURIData('id'); $blog = id(new PhameBlogQuery()) ->setViewer($viewer) ->withIDs(array($id)) ->needProfileImage(true) ->needHeaderImage(true) ->executeOne(); if (!$blog) { return new Aphront404Response(); } if ($blog->isArchived()) { $header_icon = 'fa-ban'; $header_name = pht('Archived'); $header_color = 'dark'; } else { $header_icon = 'fa-check'; $header_name = pht('Active'); $header_color = 'bluegrey'; } $picture = $blog->getProfileImageURI(); $view = id(new PHUIButtonView()) ->setTag('a') ->setText(pht('View Live')) ->setIcon('fa-external-link') ->setHref($blog->getLiveURI()); $header = id(new PHUIHeaderView()) ->setHeader($blog->getName()) ->setUser($viewer) ->setPolicyObject($blog) ->setImage($picture) ->setStatus($header_icon, $header_color, $header_name) ->addActionLink($view); $curtain = $this->buildCurtain($blog); $properties = $this->buildPropertyView($blog); $file = $this->buildFileView($blog); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb( pht('Blogs'), $this->getApplicationURI('blog/')); $crumbs->addTextCrumb( $blog->getName(), $this->getApplicationURI('blog/view/'.$id)); $crumbs->addTextCrumb(pht('Manage Blog')); $crumbs->setBorder(true); $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) ->addPropertyList($properties); $timeline = $this->buildTransactionTimeline( $blog, new PhameBlogTransactionQuery()); $timeline->setShouldTerminate(true); $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) ->addPropertySection(pht('Details'), $properties) ->addPropertySection(pht('Header'), $file) ->setMainColumn( array( $timeline, )); return $this->newPage() ->setTitle($blog->getName()) ->setCrumbs($crumbs) ->appendChild( array( $view, )); } private function buildPropertyView(PhameBlog $blog) { $viewer = $this->getViewer(); require_celerity_resource('aphront-tooltip-css'); Javelin::initBehavior('phabricator-tooltips'); $properties = id(new PHUIPropertyListView()) ->setUser($viewer); - $domain = $blog->getDomain(); - if (!$domain) { - $domain = phutil_tag('em', array(), pht('No external domain')); + $full_domain = $blog->getDomainFullURI(); + if (!$full_domain) { + $full_domain = phutil_tag('em', array(), pht('No external domain')); } - - $properties->addProperty(pht('Domain'), $domain); + $properties->addProperty(pht('Full Domain'), $full_domain); $parent_site = $blog->getParentSite(); if (!$parent_site) { $parent_site = phutil_tag('em', array(), pht('No parent site')); } $properties->addProperty(pht('Parent Site'), $parent_site); $parent_domain = $blog->getParentDomain(); if (!$parent_domain) { $parent_domain = phutil_tag('em', array(), pht('No parent domain')); } $properties->addProperty(pht('Parent Domain'), $parent_domain); $feed_uri = PhabricatorEnv::getProductionURI( $this->getApplicationURI('blog/feed/'.$blog->getID().'/')); $properties->addProperty( pht('Atom URI'), javelin_tag('a', array( 'href' => $feed_uri, 'sigil' => 'has-tooltip', 'meta' => array( 'tip' => pht('Atom URI does not support custom domains.'), 'size' => 320, ), ), $feed_uri)); $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( $viewer, $blog); $properties->addProperty( pht('Editable By'), $descriptions[PhabricatorPolicyCapability::CAN_EDIT]); $engine = id(new PhabricatorMarkupEngine()) ->setViewer($viewer) ->addObject($blog, PhameBlog::MARKUP_FIELD_DESCRIPTION) ->process(); $description = $blog->getDescription(); if (strlen($description)) { $description = new PHUIRemarkupView($viewer, $description); $properties->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $properties->addTextContent($description); } return $properties; } private function buildCurtain(PhameBlog $blog) { $viewer = $this->getViewer(); $curtain = $this->newCurtainView($blog); $actions = id(new PhabricatorActionListView()) ->setObject($blog) ->setUser($viewer); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $blog, PhabricatorPolicyCapability::CAN_EDIT); $curtain->addAction( id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setHref($this->getApplicationURI('blog/edit/'.$blog->getID().'/')) ->setName(pht('Edit Blog')) ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); $curtain->addAction( id(new PhabricatorActionView()) ->setIcon('fa-camera') ->setHref($this->getApplicationURI('blog/header/'.$blog->getID().'/')) ->setName(pht('Edit Blog Header')) ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); $curtain->addAction( id(new PhabricatorActionView()) ->setIcon('fa-picture-o') ->setHref($this->getApplicationURI('blog/picture/'.$blog->getID().'/')) ->setName(pht('Edit Blog Picture')) ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); if ($blog->isArchived()) { $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Activate Blog')) ->setIcon('fa-check') ->setHref( $this->getApplicationURI('blog/archive/'.$blog->getID().'/')) ->setDisabled(!$can_edit) ->setWorkflow(true)); } else { $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Archive Blog')) ->setIcon('fa-ban') ->setHref( $this->getApplicationURI('blog/archive/'.$blog->getID().'/')) ->setDisabled(!$can_edit) ->setWorkflow(true)); } return $curtain; } private function buildFileView( PhameBlog $blog) { $viewer = $this->getViewer(); $view = id(new PHUIPropertyListView()) ->setUser($viewer); if ($blog->getHeaderImagePHID()) { $view->addImageContent( phutil_tag( 'img', array( 'src' => $blog->getHeaderImageURI(), 'class' => 'phabricator-image-macro-hero', ))); return $view; } return null; } } diff --git a/src/applications/phame/editor/PhameBlogEditEngine.php b/src/applications/phame/editor/PhameBlogEditEngine.php index 70d878e15a..5f96309e8b 100644 --- a/src/applications/phame/editor/PhameBlogEditEngine.php +++ b/src/applications/phame/editor/PhameBlogEditEngine.php @@ -1,133 +1,133 @@ getViewer()); } protected function newObjectQuery() { return id(new PhameBlogQuery()) ->needProfileImage(true); } protected function getObjectCreateTitleText($object) { return pht('Create New Blog'); } protected function getObjectEditTitleText($object) { return pht('Edit %s', $object->getName()); } protected function getObjectEditShortText($object) { return $object->getName(); } protected function getObjectCreateShortText() { return pht('Create Blog'); } protected function getObjectName() { return pht('Blog'); } protected function getObjectCreateCancelURI($object) { return $this->getApplication()->getApplicationURI('blog/'); } protected function getEditorURI() { return $this->getApplication()->getApplicationURI('blog/edit/'); } protected function getObjectViewURI($object) { return $object->getManageURI(); } protected function getCreateNewObjectPolicy() { return $this->getApplication()->getPolicy( PhameBlogCreateCapability::CAPABILITY); } protected function buildCustomEditFields($object) { return array( id(new PhabricatorTextEditField()) ->setKey('name') ->setLabel(pht('Name')) ->setDescription(pht('Blog name.')) ->setConduitDescription(pht('Retitle the blog.')) ->setConduitTypeDescription(pht('New blog title.')) ->setTransactionType(PhameBlogTransaction::TYPE_NAME) ->setValue($object->getName()), id(new PhabricatorTextEditField()) ->setKey('subtitle') ->setLabel(pht('Subtitle')) ->setDescription(pht('Blog subtitle.')) ->setConduitDescription(pht('Change the blog subtitle.')) ->setConduitTypeDescription(pht('New blog subtitle.')) ->setTransactionType(PhameBlogTransaction::TYPE_SUBTITLE) ->setValue($object->getSubtitle()), id(new PhabricatorRemarkupEditField()) ->setKey('description') ->setLabel(pht('Description')) ->setDescription(pht('Blog description.')) ->setConduitDescription(pht('Change the blog description.')) ->setConduitTypeDescription(pht('New blog description.')) ->setTransactionType(PhameBlogTransaction::TYPE_DESCRIPTION) ->setValue($object->getDescription()), id(new PhabricatorTextEditField()) - ->setKey('domain') - ->setLabel(pht('Custom Domain')) - ->setDescription(pht('Blog domain name.')) - ->setConduitDescription(pht('Change the blog domain.')) - ->setConduitTypeDescription(pht('New blog domain.')) - ->setValue($object->getDomain()) - ->setTransactionType(PhameBlogTransaction::TYPE_DOMAIN), + ->setKey('domainFullURI') + ->setLabel(pht('Full Domain URI')) + ->setDescription(pht('Blog full domain URI.')) + ->setConduitDescription(pht('Change the blog full domain URI.')) + ->setConduitTypeDescription(pht('New blog full domain URI.')) + ->setValue($object->getDomainFullURI()) + ->setTransactionType(PhameBlogTransaction::TYPE_FULLDOMAIN), id(new PhabricatorTextEditField()) ->setKey('parentSite') ->setLabel(pht('Parent Site')) ->setDescription(pht('Blog parent site name.')) ->setConduitDescription(pht('Change the blog parent site name.')) ->setConduitTypeDescription(pht('New blog parent site name.')) ->setValue($object->getParentSite()) ->setTransactionType(PhameBlogTransaction::TYPE_PARENTSITE), id(new PhabricatorTextEditField()) ->setKey('parentDomain') ->setLabel(pht('Parent Domain')) ->setDescription(pht('Blog parent domain name.')) ->setConduitDescription(pht('Change the blog parent domain.')) ->setConduitTypeDescription(pht('New blog parent domain.')) ->setValue($object->getParentDomain()) ->setTransactionType(PhameBlogTransaction::TYPE_PARENTDOMAIN), id(new PhabricatorSelectEditField()) ->setKey('status') ->setLabel(pht('Status')) ->setTransactionType(PhameBlogTransaction::TYPE_STATUS) ->setIsConduitOnly(true) ->setOptions(PhameBlog::getStatusNameMap()) ->setDescription(pht('Active or archived status.')) ->setConduitDescription(pht('Active or archive the blog.')) ->setConduitTypeDescription(pht('New blog status constant.')) ->setValue($object->getStatus()), ); } } diff --git a/src/applications/phame/editor/PhameBlogEditor.php b/src/applications/phame/editor/PhameBlogEditor.php index d2a9fcbc1f..197387985b 100644 --- a/src/applications/phame/editor/PhameBlogEditor.php +++ b/src/applications/phame/editor/PhameBlogEditor.php @@ -1,287 +1,298 @@ getTransactionType()) { case PhameBlogTransaction::TYPE_NAME: return $object->getName(); case PhameBlogTransaction::TYPE_SUBTITLE: return $object->getSubtitle(); case PhameBlogTransaction::TYPE_DESCRIPTION: return $object->getDescription(); - case PhameBlogTransaction::TYPE_DOMAIN: - return $object->getDomain(); + case PhameBlogTransaction::TYPE_FULLDOMAIN: + return $object->getDomainFullURI(); case PhameBlogTransaction::TYPE_PARENTSITE: return $object->getParentSite(); case PhameBlogTransaction::TYPE_PARENTDOMAIN: return $object->getParentDomain(); case PhameBlogTransaction::TYPE_STATUS: return $object->getStatus(); } } protected function getCustomTransactionNewValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhameBlogTransaction::TYPE_NAME: case PhameBlogTransaction::TYPE_SUBTITLE: case PhameBlogTransaction::TYPE_DESCRIPTION: case PhameBlogTransaction::TYPE_STATUS: case PhameBlogTransaction::TYPE_PARENTSITE: case PhameBlogTransaction::TYPE_PARENTDOMAIN: return $xaction->getNewValue(); - case PhameBlogTransaction::TYPE_DOMAIN: + case PhameBlogTransaction::TYPE_FULLDOMAIN: $domain = $xaction->getNewValue(); if (!strlen($xaction->getNewValue())) { return null; } return $domain; } } protected function applyCustomInternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhameBlogTransaction::TYPE_NAME: return $object->setName($xaction->getNewValue()); case PhameBlogTransaction::TYPE_SUBTITLE: return $object->setSubtitle($xaction->getNewValue()); case PhameBlogTransaction::TYPE_DESCRIPTION: return $object->setDescription($xaction->getNewValue()); - case PhameBlogTransaction::TYPE_DOMAIN: - return $object->setDomain($xaction->getNewValue()); + case PhameBlogTransaction::TYPE_FULLDOMAIN: + $new_value = $xaction->getNewValue(); + if (strlen($new_value)) { + $uri = new PhutilURI($new_value); + $domain = $uri->getDomain(); + $object->setDomain($domain); + } else { + $object->setDomain(null); + } + $object->setDomainFullURI($new_value); + return; case PhameBlogTransaction::TYPE_STATUS: return $object->setStatus($xaction->getNewValue()); case PhameBlogTransaction::TYPE_PARENTSITE: return $object->setParentSite($xaction->getNewValue()); case PhameBlogTransaction::TYPE_PARENTDOMAIN: return $object->setParentDomain($xaction->getNewValue()); } return parent::applyCustomInternalTransaction($object, $xaction); } protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhameBlogTransaction::TYPE_NAME: case PhameBlogTransaction::TYPE_SUBTITLE: case PhameBlogTransaction::TYPE_DESCRIPTION: - case PhameBlogTransaction::TYPE_DOMAIN: + case PhameBlogTransaction::TYPE_FULLDOMAIN: case PhameBlogTransaction::TYPE_PARENTSITE: case PhameBlogTransaction::TYPE_PARENTDOMAIN: case PhameBlogTransaction::TYPE_STATUS: return; } return parent::applyCustomExternalTransaction($object, $xaction); } protected function validateTransaction( PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhameBlogTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField( $object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError( $type, pht('Required'), pht('Name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case PhameBlogTransaction::TYPE_PARENTDOMAIN: if (!$xactions) { continue; } $parent_domain = last($xactions)->getNewValue(); if (empty($parent_domain)) { continue; } try { PhabricatorEnv::requireValidRemoteURIForLink($parent_domain); } catch (Exception $ex) { $error = new PhabricatorApplicationTransactionValidationError( $type, pht('Invalid URI'), pht('Parent Domain must be set to a valid Remote URI.'), nonempty(last($xactions), null)); $errors[] = $error; } break; - case PhameBlogTransaction::TYPE_DOMAIN: + case PhameBlogTransaction::TYPE_FULLDOMAIN: if (!$xactions) { continue; } $custom_domain = last($xactions)->getNewValue(); if (empty($custom_domain)) { continue; } list($error_label, $error_text) = $object->validateCustomDomain($custom_domain); if ($error_label) { $error = new PhabricatorApplicationTransactionValidationError( $type, $error_label, $error_text, nonempty(last($xactions), null)); $errors[] = $error; } if ($object->getViewPolicy() != PhabricatorPolicies::POLICY_PUBLIC) { $error_text = pht( 'For custom domains to work, the blog must have a view policy of '. 'public.'); $error = new PhabricatorApplicationTransactionValidationError( PhabricatorTransactions::TYPE_VIEW_POLICY, pht('Invalid Policy'), $error_text, nonempty(last($xactions), null)); $errors[] = $error; } + $domain = new PhutilURI($custom_domain); + $domain = $domain->getDomain(); $duplicate_blog = id(new PhameBlogQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withDomain($custom_domain) + ->withDomain($domain) ->executeOne(); if ($duplicate_blog && $duplicate_blog->getID() != $object->getID()) { $error = new PhabricatorApplicationTransactionValidationError( $type, pht('Not Unique'), pht('Domain must be unique; another blog already has this domain.'), nonempty(last($xactions), null)); $errors[] = $error; } break; } return $errors; } protected function shouldSendMail( PhabricatorLiskDAO $object, array $xactions) { return true; } protected function shouldPublishFeedStory( PhabricatorLiskDAO $object, array $xactions) { return true; } protected function getMailTo(PhabricatorLiskDAO $object) { $phids = array(); $phids[] = $this->requireActor()->getPHID(); $phids[] = $object->getCreatorPHID(); return $phids; } protected function buildMailTemplate(PhabricatorLiskDAO $object) { $phid = $object->getPHID(); $name = $object->getName(); return id(new PhabricatorMetaMTAMail()) ->setSubject($name) ->addHeader('Thread-Topic', $phid); } protected function buildReplyHandler(PhabricatorLiskDAO $object) { return id(new PhameBlogReplyHandler()) ->setMailReceiver($object); } protected function buildMailBody( PhabricatorLiskDAO $object, array $xactions) { $body = parent::buildMailBody($object, $xactions); $body->addLinkSection( pht('BLOG DETAIL'), PhabricatorEnv::getProductionURI($object->getViewURI())); return $body; } public function getMailTagsMap() { return array( PhameBlogTransaction::MAILTAG_DETAILS => pht("A blog's details change."), PhameBlogTransaction::MAILTAG_SUBSCRIBERS => pht("A blog's subscribers change."), PhameBlogTransaction::MAILTAG_OTHER => pht('Other blog activity not listed above occurs.'), ); } protected function getMailSubjectPrefix() { return '[Phame]'; } protected function supportsSearch() { return true; } protected function shouldApplyHeraldRules( PhabricatorLiskDAO $object, array $xactions) { return true; } protected function buildHeraldAdapter( PhabricatorLiskDAO $object, array $xactions) { return id(new HeraldPhameBlogAdapter()) ->setBlog($object); } } diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php index 4f092cd3f1..6d9c51071a 100644 --- a/src/applications/phame/storage/PhameBlog.php +++ b/src/applications/phame/storage/PhameBlog.php @@ -1,407 +1,405 @@ true, self::CONFIG_SERIALIZATION => array( 'configData' => self::SERIALIZATION_JSON, ), self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'text64', 'subtitle' => 'text64', 'description' => 'text', 'domain' => 'text128?', + 'domainFullURI' => 'text128?', 'parentSite' => 'text128', 'parentDomain' => 'text128', 'status' => 'text32', 'mailKey' => 'bytes20', 'profileImagePHID' => 'phid?', 'headerImagePHID' => 'phid?', // T6203/NULLABILITY // These policies should always be non-null. 'editPolicy' => 'policy?', 'viewPolicy' => 'policy?', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, 'phid' => array( 'columns' => array('phid'), 'unique' => true, ), 'domain' => array( 'columns' => array('domain'), 'unique' => true, ), ), ) + parent::getConfiguration(); } public function save() { if (!$this->getMailKey()) { $this->setMailKey(Filesystem::readRandomCharacters(20)); } return parent::save(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( PhabricatorPhameBlogPHIDType::TYPECONST); } public static function initializeNewBlog(PhabricatorUser $actor) { $blog = id(new PhameBlog()) ->setCreatorPHID($actor->getPHID()) ->setStatus(self::STATUS_ACTIVE) ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) ->setEditPolicy(PhabricatorPolicies::POLICY_USER); return $blog; } public function isArchived() { return ($this->getStatus() == self::STATUS_ARCHIVED); } public static function getStatusNameMap() { return array( self::STATUS_ACTIVE => pht('Active'), self::STATUS_ARCHIVED => pht('Archived'), ); } /** * 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 = 'blog.example.com'; + public function validateCustomDomain($domain_full_uri) { + $example_domain = 'http://blog.example.com/'; $label = pht('Invalid'); // 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 array( - $label, - pht( - 'The custom domain should not include a protocol. Just provide '. - 'the bare domain name (for example, "%s").', - $example_domain), - ); - } + $uri = new PhutilURI($domain_full_uri); + $domain = $uri->getDomain(); + $protocol = $uri->getProtocol(); + $path = $uri->getPath(); + $supported_protocols = array('http', 'https'); - if ($uri->getPort()) { + if (!in_array($protocol, $supported_protocols)) { return array( $label, pht( - 'The custom domain should not include a port number. Just provide '. - 'the bare domain name (for example, "%s").', + 'The custom domain should include a valid protocol in the URI '. + '(for example, "%s"). Valid protocols are "http" or "https".', $example_domain), - ); + ); } - if (strpos($custom_domain, '/') !== false) { + if (strlen($path) && $path != '/') { return array( $label, pht( 'The custom domain should not specify a path (hosting a Phame '. 'blog at a path is currently not supported). Instead, just provide '. 'the bare domain name (for example, "%s").', $example_domain), ); } - if (strpos($custom_domain, '.') === false) { + if (strpos($domain, '.') === false) { return array( $label, pht( 'The custom domain should contain at least one dot (.) because '. 'some browsers fail to set cookies on domains without a dot. '. 'Instead, use a normal looking domain name like "%s".', $example_domain), ); } if (!PhabricatorEnv::getEnvConfig('policy.allow-public')) { $href = PhabricatorEnv::getProductionURI( '/config/edit/policy.allow-public/'); return array( pht('Fix Configuration'), pht( 'For custom domains to work, this Phabricator instance must be '. 'configured to allow the public access policy. Configure this '. 'setting %s, or ask an administrator to configure this setting. '. 'The domain can be specified later once this setting has been '. 'changed.', phutil_tag( 'a', array('href' => $href), pht('here'))), ); } return null; } public function getLiveURI() { if (strlen($this->getDomain())) { return $this->getExternalLiveURI(); } else { return $this->getInternalLiveURI(); } } public function getExternalLiveURI() { - $uri = new PhutilURI('http://'.$this->getDomain().'/'); + $uri = new PhutilURI($this->getDomainFullURI()); + PhabricatorEnv::requireValidRemoteURIForLink($uri); return (string)$uri; } public function getExternalParentURI() { $uri = $this->getParentDomain(); PhabricatorEnv::requireValidRemoteURIForLink($uri); return (string)$uri; } public function getInternalLiveURI() { return '/phame/live/'.$this->getID().'/'; } public function getViewURI() { return '/phame/blog/view/'.$this->getID().'/'; } public function getManageURI() { return '/phame/blog/manage/'.$this->getID().'/'; } public function getProfileImageURI() { return $this->getProfileImageFile()->getBestURI(); } public function attachProfileImageFile(PhabricatorFile $file) { $this->profileImageFile = $file; return $this; } public function getProfileImageFile() { return $this->assertAttached($this->profileImageFile); } public function getHeaderImageURI() { return $this->getHeaderImageFile()->getBestURI(); } public function attachHeaderImageFile(PhabricatorFile $file) { $this->headerImageFile = $file; return $this; } public function getHeaderImageFile() { return $this->assertAttached($this->headerImageFile); } /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $user) { $can_edit = PhabricatorPolicyCapability::CAN_EDIT; switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: // Users who can edit or post to a blog can always view it. if (PhabricatorPolicyFilter::hasCapability($user, $this, $can_edit)) { return true; } break; } return false; } public function describeAutomaticCapability($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return pht( 'Users who can edit a blog can always view it.'); } return null; } /* -( PhabricatorMarkupInterface Implementation )-------------------------- */ public function getMarkupFieldKey($field) { $hash = PhabricatorHash::digest($this->getMarkupText($field)); return $this->getPHID().':'.$field.':'.$hash; } public function newMarkupEngine($field) { return PhabricatorMarkupEngine::newPhameMarkupEngine(); } public function getMarkupText($field) { return $this->getDescription(); } public function didMarkupText( $field, $output, PhutilMarkupEngine $engine) { return $output; } public function shouldUseMarkupCache($field) { return (bool)$this->getPHID(); } /* -( PhabricatorDestructibleInterface )----------------------------------- */ public function destroyObjectPermanently( PhabricatorDestructionEngine $engine) { $this->openTransaction(); $posts = id(new PhamePost()) ->loadAllWhere('blogPHID = %s', $this->getPHID()); foreach ($posts as $post) { $post->delete(); } $this->delete(); $this->saveTransaction(); } /* -( PhabricatorApplicationTransactionInterface )------------------------- */ public function getApplicationTransactionEditor() { return new PhameBlogEditor(); } public function getApplicationTransactionObject() { return $this; } public function getApplicationTransactionTemplate() { return new PhameBlogTransaction(); } public function willRenderTimeline( PhabricatorApplicationTransactionView $timeline, AphrontRequest $request) { return $timeline; } /* -( PhabricatorSubscribableInterface Implementation )-------------------- */ public function isAutomaticallySubscribed($phid) { return ($this->creatorPHID == $phid); } /* -( PhabricatorConduitResultInterface )---------------------------------- */ public function getFieldSpecificationsForConduit() { return array( id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('name') ->setType('string') ->setDescription(pht('The name of the blog.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('description') ->setType('string') ->setDescription(pht('Blog description.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('status') ->setType('string') ->setDescription(pht('Archived or active status.')), ); } public function getFieldValuesForConduit() { return array( 'name' => $this->getName(), 'description' => $this->getDescription(), 'status' => $this->getStatus(), ); } public function getConduitSearchAttachments() { return array(); } /* -( PhabricatorFulltextInterface )--------------------------------------- */ public function newFulltextEngine() { return new PhameBlogFulltextEngine(); } } diff --git a/src/applications/phame/storage/PhameBlogTransaction.php b/src/applications/phame/storage/PhameBlogTransaction.php index 91c6552666..2d74ca5cc6 100644 --- a/src/applications/phame/storage/PhameBlogTransaction.php +++ b/src/applications/phame/storage/PhameBlogTransaction.php @@ -1,295 +1,295 @@ getOldValue(); switch ($this->getTransactionType()) { case self::TYPE_DESCRIPTION: if ($old === null) { return true; } } return parent::shouldHide(); } public function getIcon() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_NAME: if ($old === null) { return 'fa-plus'; } else { return 'fa-pencil'; } break; case self::TYPE_DESCRIPTION: - case self::TYPE_DOMAIN: + case self::TYPE_FULLDOMAIN: return 'fa-pencil'; case self::TYPE_STATUS: if ($new == PhameBlog::STATUS_ARCHIVED) { return 'fa-ban'; } else { return 'fa-check'; } break; } return parent::getIcon(); } public function getColor() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_STATUS: if ($new == PhameBlog::STATUS_ARCHIVED) { return 'violet'; } else { return 'green'; } } return parent::getColor(); } public function getMailTags() { $tags = parent::getMailTags(); switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_SUBSCRIBERS: $tags[] = self::MAILTAG_SUBSCRIBERS; break; case self::TYPE_NAME: case self::TYPE_SUBTITLE: case self::TYPE_DESCRIPTION: - case self::TYPE_DOMAIN: + case self::TYPE_FULLDOMAIN: case self::TYPE_PARENTSITE: case self::TYPE_PARENTDOMAIN: $tags[] = self::MAILTAG_DETAILS; break; default: $tags[] = self::MAILTAG_OTHER; break; } return $tags; } public function getTitle() { $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); $type = $this->getTransactionType(); switch ($type) { case PhabricatorTransactions::TYPE_CREATE: return pht( '%s created this blog.', $this->renderHandleLink($author_phid)); case self::TYPE_NAME: if ($old === null) { return pht( '%s created this blog.', $this->renderHandleLink($author_phid)); } else { return pht( '%s updated the blog\'s name to "%s".', $this->renderHandleLink($author_phid), $new); } break; case self::TYPE_SUBTITLE: if ($old === null) { return pht( '%s set this blog\'s subtitle to "%s".', $this->renderHandleLink($author_phid), $new); } else { return pht( '%s updated the blog\'s subtitle to "%s".', $this->renderHandleLink($author_phid), $new); } break; case self::TYPE_DESCRIPTION: return pht( '%s updated the blog\'s description.', $this->renderHandleLink($author_phid)); break; - case self::TYPE_DOMAIN: + case self::TYPE_FULLDOMAIN: return pht( - '%s updated the blog\'s domain to "%s".', + '%s updated the blog\'s full domain to "%s".', $this->renderHandleLink($author_phid), $new); break; case self::TYPE_PARENTSITE: if ($old === null) { return pht( '%s set this blog\'s parent site to "%s".', $this->renderHandleLink($author_phid), $new); } else { return pht( '%s updated the blog\'s parent site to "%s".', $this->renderHandleLink($author_phid), $new); } break; case self::TYPE_PARENTDOMAIN: if ($old === null) { return pht( '%s set this blog\'s parent domain to "%s".', $this->renderHandleLink($author_phid), $new); } else { return pht( '%s updated the blog\'s parent domain to "%s".', $this->renderHandleLink($author_phid), $new); } break; case self::TYPE_STATUS: switch ($new) { case PhameBlog::STATUS_ACTIVE: return pht( '%s published this blog.', $this->renderHandleLink($author_phid)); case PhameBlog::STATUS_ARCHIVED: return pht( '%s archived this blog.', $this->renderHandleLink($author_phid)); } } return parent::getTitle(); } public function getTitleForFeed() { $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); $type = $this->getTransactionType(); switch ($type) { case self::TYPE_NAME: if ($old === null) { return pht( '%s created %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } else { return pht( '%s updated the name for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } break; case self::TYPE_SUBTITLE: if ($old === null) { return pht( '%s set the subtitle for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } else { return pht( '%s updated the subtitle for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } break; case self::TYPE_DESCRIPTION: return pht( '%s updated the description for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); break; - case self::TYPE_DOMAIN: + case self::TYPE_FULLDOMAIN: return pht( - '%s updated the domain for %s.', + '%s updated the full domain for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); break; case self::TYPE_PARENTSITE: return pht( '%s updated the parent site for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); break; case self::TYPE_PARENTDOMAIN: return pht( '%s updated the parent domain for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); break; case self::TYPE_STATUS: switch ($new) { case PhameBlog::STATUS_ACTIVE: return pht( '%s published the blog %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case PhameBlog::STATUS_ARCHIVED: return pht( '%s archived the blog %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } break; } return parent::getTitleForFeed(); } public function hasChangeDetails() { switch ($this->getTransactionType()) { case self::TYPE_DESCRIPTION: return ($this->getOldValue() !== null); } return parent::hasChangeDetails(); } public function renderChangeDetails(PhabricatorUser $viewer) { switch ($this->getTransactionType()) { case self::TYPE_DESCRIPTION: $old = $this->getOldValue(); $new = $this->getNewValue(); return $this->renderTextCorpusChangeDetails( $viewer, $old, $new); } return parent::renderChangeDetails($viewer); } }