diff --git a/src/applications/feed/query/PhabricatorFeedQuery.php b/src/applications/feed/query/PhabricatorFeedQuery.php index a4034a652..d84b0201d 100644 --- a/src/applications/feed/query/PhabricatorFeedQuery.php +++ b/src/applications/feed/query/PhabricatorFeedQuery.php @@ -1,180 +1,174 @@ filterPHIDs = $phids; return $this; } - // c4science custo - public function withFilterOutPHIDs(array $phids) { - $this->filterOutPHIDs = $phids; - return $this; - } - public function withChronologicalKeys(array $keys) { $this->chronologicalKeys = $keys; return $this; } public function withEpochInRange($range_min, $range_max) { $this->rangeMin = $range_min; $this->rangeMax = $range_max; return $this; } public function newResultObject() { return new PhabricatorFeedStoryData(); } protected function loadPage() { // NOTE: We return raw rows from this method, which is a little unusual. return $this->loadStandardPageRows($this->newResultObject()); } protected function willFilterPage(array $data) { return PhabricatorFeedStory::loadAllFromRows($data, $this->getViewer()); } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); // NOTE: We perform this join unconditionally (even if we have no filter // PHIDs) to omit rows which have no story references. These story data // rows are notifications or realtime alerts. $ref_table = new PhabricatorFeedStoryReference(); $joins[] = qsprintf( $conn, 'JOIN %T ref ON ref.chronologicalKey = story.chronologicalKey', $ref_table->getTableName()); return $joins; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->filterPHIDs) { // c4science custo $where[] = qsprintf( $conn, 'ref.objectPHID IN (%Ls)', $this->filterPHIDs); } // C4science customization if ($this->filterOutPHIDs !== null) { $where[] = qsprintf( $conn, 'ref.objectPHID NOT IN (%Ls)', $this->filterOutPHIDs); } if ($this->chronologicalKeys !== null) { // NOTE: We want to use integers in the query so we can take advantage // of keys, but can't use %d on 32-bit systems. Make sure all the keys // are integers and then format them raw. $keys = $this->chronologicalKeys; foreach ($keys as $key) { if (!ctype_digit($key)) { throw new Exception( pht("Key '%s' is not a valid chronological key!", $key)); } } $where[] = qsprintf( $conn, 'ref.chronologicalKey IN (%Q)', implode(', ', $keys)); } // NOTE: We may not have 64-bit PHP, so do the shifts in MySQL instead. // From EXPLAIN, it appears like MySQL is smart enough to compute the // result and make use of keys to execute the query. if ($this->rangeMin !== null) { $where[] = qsprintf( $conn, 'ref.chronologicalKey >= (%d << 32)', $this->rangeMin); } if ($this->rangeMax !== null) { $where[] = qsprintf( $conn, 'ref.chronologicalKey < (%d << 32)', $this->rangeMax); } return $where; } protected function buildGroupClause(AphrontDatabaseConnection $conn) { if ($this->filterPHIDs !== null) { return qsprintf($conn, 'GROUP BY ref.chronologicalKey'); } else { return qsprintf($conn, 'GROUP BY story.chronologicalKey'); } } protected function getDefaultOrderVector() { return array('key'); } public function getBuiltinOrders() { return array( 'newest' => array( 'vector' => array('key'), 'name' => pht('Creation (Newest First)'), 'aliases' => array('created'), ), 'oldest' => array( 'vector' => array('-key'), 'name' => pht('Creation (Oldest First)'), ), ); } public function getOrderableColumns() { $table = ($this->filterPHIDs ? 'ref' : 'story'); return array( 'key' => array( 'table' => $table, 'column' => 'chronologicalKey', 'type' => 'string', 'unique' => true, ), ); } protected function getPagingValueMap($cursor, array $keys) { return array( 'key' => $cursor, ); } protected function getResultCursor($item) { if ($item instanceof PhabricatorFeedStory) { return $item->getChronologicalKey(); } return $item['chronologicalKey']; } protected function getPrimaryTableAlias() { return 'story'; } public function getQueryApplicationClass() { return 'PhabricatorFeedApplication'; } } diff --git a/src/applications/feed/query/PhabricatorFeedSearchEngine.php b/src/applications/feed/query/PhabricatorFeedSearchEngine.php index cb71694c5..5c640b181 100644 --- a/src/applications/feed/query/PhabricatorFeedSearchEngine.php +++ b/src/applications/feed/query/PhabricatorFeedSearchEngine.php @@ -1,191 +1,177 @@ setLabel(pht('Include Users')) ->setKey('userPHIDs'), // NOTE: This query is not executed with EdgeLogic, so we can't use // a fancy logical datasource. id(new PhabricatorSearchDatasourceField()) ->setDatasource(new PhabricatorProjectDatasource()) ->setLabel(pht('Include Projects')) ->setKey('projectPHIDs'), id(new PhabricatorSearchDatasourceField()) // C4science customization ->setDatasource(new DiffusionRepositoryDatasource()) ->setLabel(pht('Include Repositories')) ->setKey('repositoryPHIDs'), id(new PhabricatorSearchDateControlField()) ->setLabel(pht('Occurs After')) ->setKey('rangeStart'), id(new PhabricatorSearchDateControlField()) ->setLabel(pht('Occurs Before')) ->setKey('rangeEnd'), // NOTE: This is a legacy field retained only for backward // compatibility. If the projects field used EdgeLogic, we could use // `viewerprojects()` to execute an equivalent query. id(new PhabricatorSearchCheckboxesField()) ->setKey('viewerProjects') ->setOptions( array( 'self' => pht('Include stories about projects I am a member of.'), )), ); } protected function buildQueryFromParameters(array $map) { $query = $this->newQuery(); $phids = array(); if ($map['userPHIDs']) { $phids += array_fuse($map['userPHIDs']); } // C4science customization if ($map['repositoryPHIDs']) { $viewer = $this->requireViewer(); $commits = id(new DiffusionCommitQuery()) ->setViewer($viewer) ->withRepositoryPHIDs($map['repositoryPHIDs']) ->execute(); $commits_phids = mpull($commits, 'getPHID'); $phids += array_fuse($commits_phids); } if ($map['projectPHIDs']) { $phids += array_fuse($map['projectPHIDs']); } // NOTE: This value may be `true` for older saved queries, or // `array('self')` for newer ones. $viewer_projects = $map['viewerProjects']; if ($viewer_projects) { $viewer = $this->requireViewer(); $projects = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->withMemberPHIDs(array($viewer->getPHID())) ->execute(); $phids += array_fuse(mpull($projects, 'getPHID')); } if ($phids) { $query->withFilterPHIDs($phids); } - // c4science customization - - // Get Shibboleth groups - $provider = PhabricatorAuthProviderShibboleth::getShibbolethProvider(); - $adapter = $provider->getAdapter(); - $projects = $adapter->getUserProject(); - - // Filter Shibboleth groups from feeds - if($projects){ - $query->withFilterOutPHIDs($projects); - } - - // end of c4s custo - $range_min = $map['rangeStart']; if ($range_min) { $range_min = $range_min->getEpoch(); } $range_max = $map['rangeEnd']; if ($range_max) { $range_max = $range_max->getEpoch(); } if ($range_min && $range_max) { if ($range_min > $range_max) { throw new PhabricatorSearchConstraintException( pht( 'The specified "Occurs Before" date is earlier in time than the '. 'specified "Occurs After" date, so this query can never match '. 'any results.')); } } if ($range_min || $range_max) { $query->withEpochInRange($range_min, $range_max); } return $query; } protected function getURI($path) { return '/feed/'.$path; } protected function getBuiltinQueryNames() { $names = array( 'all' => pht('All Stories'), ); if ($this->requireViewer()->isLoggedIn()) { $names['projects'] = pht('Tags'); } return $names; } public function buildSavedQueryFromBuiltin($query_key) { $query = $this->newSavedQuery(); $query->setQueryKey($query_key); switch ($query_key) { case 'all': return $query; case 'projects': return $query->setParameter('viewerProjects', array('self')); } return parent::buildSavedQueryFromBuiltin($query_key); } protected function renderResultList( array $objects, PhabricatorSavedQuery $query, array $handles) { $builder = new PhabricatorFeedBuilder($objects); if ($this->isPanelContext()) { $builder->setShowHovercards(false); } else { $builder->setShowHovercards(true); } $builder->setUser($this->requireViewer()); $view = $builder->buildView(); $list = phutil_tag_div('phabricator-feed-frame', $view); $result = new PhabricatorApplicationSearchResultView(); $result->setContent($list); return $result; } } diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index adf4a3138..e6033dcd6 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -1,486 +1,496 @@ shouldPublish = $shouldPublish; + return $this; + } private function setIsMilestone($is_milestone) { $this->isMilestone = $is_milestone; return $this; } public function getIsMilestone() { return $this->isMilestone; } public function getEditorApplicationClass() { return 'PhabricatorProjectApplication'; } public function getEditorObjectsDescription() { return pht('Projects'); } public function getCreateObjectTitle($author, $object) { return pht('%s created this project.', $author); } public function getCreateObjectTitleForFeed($author, $object) { return pht('%s created %s.', $author, $object); } public function getTransactionTypes() { $types = parent::getTransactionTypes(); $types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_JOIN_POLICY; return $types; } protected function validateAllTransactions( PhabricatorLiskDAO $object, array $xactions) { $errors = array(); // Prevent creating projects which are both subprojects and milestones, // since this does not make sense, won't work, and will break everything. $parent_xaction = null; foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorProjectParentTransaction::TRANSACTIONTYPE: case PhabricatorProjectMilestoneTransaction::TRANSACTIONTYPE: if ($xaction->getNewValue() === null) { continue; } if (!$parent_xaction) { $parent_xaction = $xaction; continue; } $errors[] = new PhabricatorApplicationTransactionValidationError( $xaction->getTransactionType(), pht('Invalid'), pht( 'When creating a project, specify a maximum of one parent '. 'project or milestone project. A project can not be both a '. 'subproject and a milestone.'), $xaction); break; break; } } $is_milestone = $this->getIsMilestone(); $is_parent = $object->getHasSubprojects(); foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorTransactions::TYPE_EDGE: $type = $xaction->getMetadataValue('edge:type'); if ($type != PhabricatorProjectProjectHasMemberEdgeType::EDGECONST) { break; } if ($is_parent) { $errors[] = new PhabricatorApplicationTransactionValidationError( $xaction->getTransactionType(), pht('Invalid'), pht( 'You can not change members of a project with subprojects '. 'directly. Members of any subproject are automatically '. 'members of the parent project.'), $xaction); } if ($is_milestone) { $errors[] = new PhabricatorApplicationTransactionValidationError( $xaction->getTransactionType(), pht('Invalid'), pht( 'You can not change members of a milestone. Members of the '. 'parent project are automatically members of the milestone.'), $xaction); } break; } } return $errors; } protected function requireCapabilities( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorProjectNameTransaction::TRANSACTIONTYPE: case PhabricatorProjectStatusTransaction::TRANSACTIONTYPE: case PhabricatorProjectImageTransaction::TRANSACTIONTYPE: case PhabricatorProjectIconTransaction::TRANSACTIONTYPE: case PhabricatorProjectColorTransaction::TRANSACTIONTYPE: PhabricatorPolicyFilter::requireCapability( $this->requireActor(), $object, PhabricatorPolicyCapability::CAN_EDIT); return; case PhabricatorProjectLockTransaction::TRANSACTIONTYPE: PhabricatorPolicyFilter::requireCapability( $this->requireActor(), newv($this->getEditorApplicationClass(), array()), ProjectCanLockProjectsCapability::CAPABILITY); return; case PhabricatorTransactions::TYPE_EDGE: switch ($xaction->getMetadataValue('edge:type')) { case PhabricatorProjectProjectHasMemberEdgeType::EDGECONST: $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); $add = array_keys(array_diff_key($new, $old)); $rem = array_keys(array_diff_key($old, $new)); $actor_phid = $this->requireActor()->getPHID(); $is_join = (($add === array($actor_phid)) && !$rem); $is_leave = (($rem === array($actor_phid)) && !$add); if ($is_join) { // You need CAN_JOIN to join a project. PhabricatorPolicyFilter::requireCapability( $this->requireActor(), $object, PhabricatorPolicyCapability::CAN_JOIN); } else if ($is_leave) { // You usually don't need any capabilities to leave a project. if ($object->getIsMembershipLocked()) { // you must be able to edit though to leave locked projects PhabricatorPolicyFilter::requireCapability( $this->requireActor(), $object, PhabricatorPolicyCapability::CAN_EDIT); } } else { // You need CAN_EDIT to change members other than yourself. PhabricatorPolicyFilter::requireCapability( $this->requireActor(), $object, PhabricatorPolicyCapability::CAN_EDIT); } return; } break; } return parent::requireCapabilities($object, $xaction); } protected function willPublish(PhabricatorLiskDAO $object, array $xactions) { // NOTE: We're using the omnipotent user here because the original actor // may no longer have permission to view the object. return id(new PhabricatorProjectQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withPHIDs(array($object->getPHID())) ->needAncestorMembers(true) ->executeOne(); } protected function shouldSendMail( PhabricatorLiskDAO $object, array $xactions) { return true; } protected function getMailSubjectPrefix() { return pht('[Project]'); } protected function getMailTo(PhabricatorLiskDAO $object) { return array( $this->getActingAsPHID(), ); } protected function getMailCc(PhabricatorLiskDAO $object) { return array(); } public function getMailTagsMap() { return array( PhabricatorProjectTransaction::MAILTAG_METADATA => pht('Project name, hashtags, icon, image, or color changes.'), PhabricatorProjectTransaction::MAILTAG_MEMBERS => pht('Project membership changes.'), PhabricatorProjectTransaction::MAILTAG_WATCHERS => pht('Project watcher list changes.'), PhabricatorProjectTransaction::MAILTAG_OTHER => pht('Other project activity not listed above occurs.'), ); } protected function buildReplyHandler(PhabricatorLiskDAO $object) { return id(new ProjectReplyHandler()) ->setMailReceiver($object); } protected function buildMailTemplate(PhabricatorLiskDAO $object) { $id = $object->getID(); $name = $object->getName(); return id(new PhabricatorMetaMTAMail()) ->setSubject("{$name}") ->addHeader('Thread-Topic', "Project {$id}"); } protected function buildMailBody( PhabricatorLiskDAO $object, array $xactions) { $body = parent::buildMailBody($object, $xactions); $uri = '/project/profile/'.$object->getID().'/'; $body->addLinkSection( pht('PROJECT DETAIL'), PhabricatorEnv::getProductionURI($uri)); return $body; } protected function shouldPublishFeedStory( PhabricatorLiskDAO $object, array $xactions) { + if(isset($shouldPublish)) { // c4science custo + return $shouldPublish; + } return true; } protected function supportsSearch() { return true; } protected function applyFinalEffects( PhabricatorLiskDAO $object, array $xactions) { $materialize = false; $new_parent = null; foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorTransactions::TYPE_EDGE: switch ($xaction->getMetadataValue('edge:type')) { case PhabricatorProjectProjectHasMemberEdgeType::EDGECONST: $materialize = true; break; } break; case PhabricatorProjectParentTransaction::TRANSACTIONTYPE: case PhabricatorProjectMilestoneTransaction::TRANSACTIONTYPE: $materialize = true; $new_parent = $object->getParentProject(); break; } } if ($new_parent) { // If we just created the first subproject of this parent, we want to // copy all of the real members to the subproject. if (!$new_parent->getHasSubprojects()) { $member_type = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST; $project_members = PhabricatorEdgeQuery::loadDestinationPHIDs( $new_parent->getPHID(), $member_type); if ($project_members) { $editor = id(new PhabricatorEdgeEditor()); foreach ($project_members as $phid) { $editor->addEdge($object->getPHID(), $member_type, $phid); } $editor->save(); } } } // TODO: We should dump an informational transaction onto the parent // project to show that we created the sub-thing. if ($materialize) { id(new PhabricatorProjectsMembershipIndexEngineExtension()) ->rematerialize($object); } if ($new_parent) { id(new PhabricatorProjectsMembershipIndexEngineExtension()) ->rematerialize($new_parent); } return parent::applyFinalEffects($object, $xactions); } public function addSlug(PhabricatorProject $project, $slug, $force) { $slug = PhabricatorSlug::normalizeProjectSlug($slug); $table = new PhabricatorProjectSlug(); $project_phid = $project->getPHID(); if ($force) { // If we have the `$force` flag set, we only want to ignore an existing // slug if it's for the same project. We'll error on collisions with // other projects. $current = $table->loadOneWhere( 'slug = %s AND projectPHID = %s', $slug, $project_phid); } else { // Without the `$force` flag, we'll just return without doing anything // if any other project already has the slug. $current = $table->loadOneWhere( 'slug = %s', $slug); } if ($current) { return; } return id(new PhabricatorProjectSlug()) ->setSlug($slug) ->setProjectPHID($project_phid) ->save(); } public function removeSlugs(PhabricatorProject $project, array $slugs) { if (!$slugs) { return; } // We're going to try to delete both the literal and normalized versions // of all slugs. This allows us to destroy old slugs that are no longer // valid. foreach ($this->normalizeSlugs($slugs) as $slug) { $slugs[] = $slug; } $objects = id(new PhabricatorProjectSlug())->loadAllWhere( 'projectPHID = %s AND slug IN (%Ls)', $project->getPHID(), $slugs); foreach ($objects as $object) { $object->delete(); } } public function normalizeSlugs(array $slugs) { foreach ($slugs as $key => $slug) { $slugs[$key] = PhabricatorSlug::normalizeProjectSlug($slug); } $slugs = array_unique($slugs); $slugs = array_values($slugs); return $slugs; } protected function adjustObjectForPolicyChecks( PhabricatorLiskDAO $object, array $xactions) { $copy = parent::adjustObjectForPolicyChecks($object, $xactions); $type_edge = PhabricatorTransactions::TYPE_EDGE; $edgetype_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST; $member_xaction = null; foreach ($xactions as $xaction) { if ($xaction->getTransactionType() !== $type_edge) { continue; } $edgetype = $xaction->getMetadataValue('edge:type'); if ($edgetype !== $edgetype_member) { continue; } $member_xaction = $xaction; } if ($member_xaction) { $object_phid = $object->getPHID(); if ($object_phid) { $project = id(new PhabricatorProjectQuery()) ->setViewer($this->getActor()) ->withPHIDs(array($object_phid)) ->needMembers(true) ->executeOne(); $members = $project->getMemberPHIDs(); } else { $members = array(); } $clone_xaction = clone $member_xaction; $hint = $this->getPHIDTransactionNewValue($clone_xaction, $members); $rule = new PhabricatorProjectMembersPolicyRule(); $hint = array_fuse($hint); PhabricatorPolicyRule::passTransactionHintToRule( $copy, $rule, $hint); } return $copy; } protected function expandTransactions( PhabricatorLiskDAO $object, array $xactions) { $actor = $this->getActor(); $actor_phid = $actor->getPHID(); $results = parent::expandTransactions($object, $xactions); $is_milestone = $object->isMilestone(); foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorProjectMilestoneTransaction::TRANSACTIONTYPE: if ($xaction->getNewValue() !== null) { $is_milestone = true; } break; } } $this->setIsMilestone($is_milestone); return $results; } protected function shouldApplyHeraldRules( PhabricatorLiskDAO $object, array $xactions) { return true; } protected function buildHeraldAdapter( PhabricatorLiskDAO $object, array $xactions) { // Herald rules may run on behalf of other users and need to execute // membership checks against ancestors. $project = id(new PhabricatorProjectQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withPHIDs(array($object->getPHID())) ->needAncestorMembers(true) ->executeOne(); return id(new PhabricatorProjectHeraldAdapter()) ->setProject($project); } } diff --git a/src/extensions/PhabricatorAuthProviderShibboleth.php b/src/extensions/PhabricatorAuthProviderShibboleth.php index af0ca4dca..7e0c0f14c 100644 --- a/src/extensions/PhabricatorAuthProviderShibboleth.php +++ b/src/extensions/PhabricatorAuthProviderShibboleth.php @@ -1,529 +1,530 @@ adapter) { $conf = $this->getProviderConfig(); $adapter = id(new PhutilAuthAdapterShibboleth()) ->setShibSessionIdField( $conf->getProperty(self::KEY_SHIB_SESSION_ID_FIELD)) ->setShibApplicationIdField( $conf->getProperty(self::KEY_SHIB_APPLICATION_ID_FIELD)) ->setUseridField( $conf->getProperty(self::KEY_USERID_FIELD)) ->setUsernameField( $conf->getProperty(self::KEY_USERNAME_FIELD)) ->setRealnameField( $conf->getProperty(self::KEY_REALNAME_FIELD)) ->setFirstnameField( $conf->getProperty(self::KEY_FIRSTNAME_FIELD)) ->setLastnameField( $conf->getProperty(self::KEY_LASTNAME_FIELD)) ->setEmailField( $conf->getProperty(self::KEY_EMAIL_FIELD)) ->setPageURIPattern( $conf->getProperty(self::KEY_PAGE_URI_PATTERN)) ->setImageURIPattern( $conf->getProperty(self::KEY_IMAGE_URI_PATTERN)) ->setAddUserToProject( $conf->getProperty(self::KEY_ADD_USER_TO_PROJECT)) ->setUserProject( $conf->getProperty(self::KEY_USER_PROJECT)) ->setIsGeneratedUsername( $conf->getProperty(self::KEY_USERNAME_FROM_REALNAME)) ->setOrgField( $conf->getProperty(self::KEY_ORG_FIELD)) ->setOrgCustomField( $conf->getProperty(self::KEY_ORG_CUSTOM_FIELD)) ->setOrgTypeField( $conf->getProperty(self::KEY_ORG_TYPE_FIELD)); $this->adapter = $adapter; } return $this->adapter; } protected function renderLoginForm(AphrontRequest $request, $mode) { $viewer = $request->getUser(); $dialog = id(new AphrontDialogView()) ->setSubmitURI($this->getLoginURI()) ->setUser($viewer) ->setTitle(pht('Login for Swiss Universities')) ->addSubmitButton(pht('Login or Register')); $remarkup = new PHUIRemarkupView($viewer, pht(<<addPadding(PHUI::PADDING_LARGE) ->appendChild($remarkup); $dialog->appendChild($frame); return $dialog; } public function isLoginFormAButton() { return false; } private function populateAdapter() { $adapter = $this->getAdapter(); $env = array(); $env_names = $adapter->getEnvNames(); foreach ($env_names as $h) { if(array_key_exists($h, $_SERVER)) { $env[$h] = $_SERVER[$h]; } } return $adapter->setUserDataFromRequest($env); } public function processLoginRequest( PhabricatorAuthLoginController $controller) { $adapter = $this->getAdapter(); $request = $controller->getRequest(); $response = null; $account = null; if (!$this->populateAdapter()) { $response = $controller->buildProviderPageResponse( $this, id(new PHUIInfoView()) ->setErrors(array(pht('Invalid Shibboleth session.'))) ->addButton(id(new PHUIButtonView()) ->setTag('a') ->setText(pht('Return to home')) ->setHref('/') ) ); return array($account, $response); } $account_id = $adapter->getAccountID(); return array($this->loadOrCreateAccount_custom($account_id, $request), $response); } private function updateOrganization($userPHID, $adapter) { $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); if($adapter == null){ $adapter = $this->getAdapter(); } if(strlen($adapter->getOrg()) && strlen($adapter->getOrgType()) && strlen($adapter->getOrgCustom()) ) { $user = id(new PhabricatorUser()) ->loadOneWhere('phid = %s', $userPHID); $admin = id(new PhabricatorUser()) ->loadOneWhere('username = %s', 'admin'); $field_list = PhabricatorCustomField::getObjectFields( $user, PhabricatorCustomField::ROLE_VIEW); $field_list->readFieldsFromStorage($user); $orgvalue = null; foreach($field_list->getFields() as $field){ try { if($field->getFieldKey()=='std:user:' . $adapter->getOrgCustom()){ $orgvalue = $field->getValueForStorage(); break; } } catch (Exception $e) { } } // Create projects for organization $org = $adapter->getOrg(); $proj = id(new PhabricatorProjectQuery()) ->setViewer($user) ->withNames(array($org)) ->executeOne(); // Create the project if(!$proj) { $proj = PhabricatorProject::initializeNewProject($user) ->setViewPolicy('public') ->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN); $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) ->setTransactionType(PhabricatorProjectNameTransaction::TRANSACTIONTYPE) ->setNewValue($org); $xactions[] = id(new PhabricatorProjectTransaction()) ->setTransactionType(PhabricatorProjectIconTransaction::TRANSACTIONTYPE) ->setNewValue('organization'); $xactions[] = id(new PhabricatorProjectTransaction()) ->setTransactionType(PhabricatorProjectStatusTransaction::TRANSACTIONTYPE) ->setNewValue(PhabricatorProjectStatus::STATUS_ACTIVE); $editor = id(new PhabricatorProjectTransactionEditor()) ->setActor($admin) ->setContinueOnNoEffect(true) ->setContentSource(PhabricatorContentSource::newForSource('web')) ->applyTransactions($proj, $xactions); } // Add the user to the project $xactions = array(); $type_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST; $xactions[] = id(new PhabricatorProjectTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) ->setMetadataValue('edge:type', $type_member) ->setNewValue( array( '+' => array_fuse(array($userPHID)), )); $editor = id(new PhabricatorProjectTransactionEditor()) ->setActor($admin) + ->setShouldPublish(false) ->setContentSource(PhabricatorContentSource::newForSource('web')) ->setContinueOnNoEffect(true) ->setContinueOnMissingFields(true) ->applyTransactions($proj, $xactions); // Add organization to custom field in user profile if(empty($orgvalue)) { $xactions = array(); $xactions[] = id(new PhabricatorUserTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD) ->setMetadataValue('customfield:key', 'std:user:' . $adapter->getOrgCustom()) ->setOldValue('') ->setNewValue($adapter->getOrg() . ' (' . $adapter->getOrgType() . ')'); $xactions[] = id(new PhabricatorUserTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD) ->setMetadataValue('customfield:key', 'user:title') ->setOldValue('') ->setNewValue(' '); $xactions[] = id(new PhabricatorUserTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD) ->setMetadataValue('customfield:key', 'user:blurb') ->setOldValue('') ->setNewValue(' '); id(new PhabricatorUserProfileEditor()) ->setActor($user) ->setContentSource(PhabricatorContentSource::newForSource('console')) ->setContinueOnNoEffect(true) ->applyTransactions($user, $xactions); } } unset($unguarded); } private function updateProjects($userPHID, $adapter) { $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); if($adapter == null){ $adapter = $this->getAdapter(); } $projects = $adapter->getUserProject(); if(count($projects)) { $admin = id(new PhabricatorUser()) ->loadOneWhere('username = %s', 'admin'); $user_projects = id(new PhabricatorProjectQuery()) ->setViewer($admin) ->withMemberPHIDs(array($userPHID)) ->withPHIDs($projects) ->execute(); $check_proj = array(); foreach($user_projects as $r){ $check_proj[] = $r->getPHID(); } $results = id(new PhabricatorProjectQuery()) ->setViewer($admin) ->withPHIDs($projects) ->execute(); // Add user to project foreach($results as $project) { if(in_array($project->getPHID(), $check_proj)){ continue; } $spec = array( '+' => array($userPHID => $userPHID), ); $edge_type = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST; $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) ->setMetadataValue('edge:type', $edge_type) ->setNewValue($spec); $editor = id(new PhabricatorProjectTransactionEditor()) ->setActor($admin) ->setContentSource(new PhabricatorConsoleContentSource()) ->setContinueOnNoEffect(true) ->applyTransactions($project, $xactions); } } unset($unguarded); } protected function loadOrCreateAccount_custom($account_id, $request) { $account = parent::loadOrCreateAccount($account_id); $adapter = $this->getAdapter(); if(!$adapter) { return $account; } if(!$account->getUserPHID()){ if ($account instanceof PhabricatorExternalAccount && $request->getViewer()->getPHID()) { $this->updateProjects($request->getViewer()->getPHID(), $adapter); } // User account not yet created return $account; } $this->updateOrganization($account->getUserPHID(), $adapter); $this->updateProjects($account->getUserPHID(), $adapter); return $account; } public function willRegisterAccount(PhabricatorExternalAccount $account) { parent::willRegisterAccount($account); //$this->updateOrganization($account); // no shib env variables here :/ $this->updateProjects($account->getUserPHID(), null); } const KEY_SHIB_SESSION_ID_FIELD = 'shibboleth:session_id_field'; const KEY_SHIB_APPLICATION_ID_FIELD = 'shibboleth:application_id_field'; const KEY_USERID_FIELD = 'shibboleth:userid_field'; const KEY_USERNAME_FIELD = 'shibboleth:username_field'; const KEY_REALNAME_FIELD = 'shibboleth:realname_field'; const KEY_FIRSTNAME_FIELD = 'shibboleth:firstname_field'; const KEY_LASTNAME_FIELD = 'shibboleth:lastname_field'; const KEY_EMAIL_FIELD = 'shibboleth:email_field'; const KEY_ORG_FIELD = 'shibboleth:org'; const KEY_ORG_TYPE_FIELD = 'shibboleth:org_type'; const KEY_ORG_CUSTOM_FIELD = 'shibboleth:org_custom'; const KEY_PAGE_URI_PATTERN = 'shibboleth:page_uri_pattern'; const KEY_IMAGE_URI_PATTERN = 'shibboleth:image_uri_pattern'; const KEY_USERNAME_FROM_REALNAME = 'shibboleth:username_from_realname'; const KEY_ADD_USER_TO_PROJECT = 'shibboleth:add_user_to_project'; const KEY_USER_PROJECT = 'shibboleth:user_project'; private function getPropertyKeys() { return array( self::KEY_SHIB_SESSION_ID_FIELD, self::KEY_SHIB_APPLICATION_ID_FIELD, self::KEY_USERID_FIELD, self::KEY_USERNAME_FIELD, self::KEY_REALNAME_FIELD, self::KEY_FIRSTNAME_FIELD, self::KEY_LASTNAME_FIELD, self::KEY_EMAIL_FIELD, self::KEY_ORG_FIELD, self::KEY_ORG_CUSTOM_FIELD, self::KEY_ORG_TYPE_FIELD, self::KEY_PAGE_URI_PATTERN, self::KEY_IMAGE_URI_PATTERN, self::KEY_USERNAME_FROM_REALNAME, self::KEY_ADD_USER_TO_PROJECT, self::KEY_USER_PROJECT ); } private function getPropertyLabels() { return array( self::KEY_SHIB_SESSION_ID_FIELD => pht('Session ID'), self::KEY_SHIB_APPLICATION_ID_FIELD => pht('Application ID'), self::KEY_USERID_FIELD => pht('User ID'), self::KEY_USERNAME_FIELD => pht('Username'), self::KEY_REALNAME_FIELD => pht('Real name'), self::KEY_FIRSTNAME_FIELD => pht('Firstname'), self::KEY_LASTNAME_FIELD => pht('Lastname'), self::KEY_EMAIL_FIELD => pht('User emailname'), self::KEY_ORG_FIELD => pht('Organization'), self::KEY_ORG_CUSTOM_FIELD => pht('Organization custom field'), self::KEY_ORG_TYPE_FIELD => pht('Organization type'), self::KEY_PAGE_URI_PATTERN => pht('User page URI pattern'), self::KEY_IMAGE_URI_PATTERN => pht('User image URI pattern'), ); } public function readFormValuesFromProvider() { $properties = array(); foreach ($this->getPropertyKeys() as $key) { $properties[$key] = $this->getProviderConfig()->getProperty($key); } return $properties; } public function readFormValuesFromRequest(AphrontRequest $request) { $values = array(); foreach ($this->getPropertyKeys() as $key) { if($key == self::KEY_USER_PROJECT) { $values[$key] = $request->getArr($key); } else { $values[$key] = $request->getStr($key); } } return $values; } public function processEditForm( AphrontRequest $request, array $values) { $errors = array(); $issues = array(); return array($errors, $issues, $values); } public function extendEditForm( AphrontRequest $request, AphrontFormView $form, array $values, array $issues) { $labels = $this->getPropertyLabels(); $captions = array( self::KEY_SHIB_SESSION_ID_FIELD => pht('Shibboleth Session ID, e.g.: Shib-Session-ID'), self::KEY_SHIB_APPLICATION_ID_FIELD => pht('Shibboleth application id, e.g.: Shib-Application-ID'), self::KEY_USERID_FIELD => pht('Unique user id for internal Phabricator use. e.g.: uniqueID'), self::KEY_USERNAME_FIELD => pht('Visible username, can be left empty if you choose to autogenerate it. e.g.: username'), self::KEY_REALNAME_FIELD => pht('Visible in the user profile. e.g.: displayName'), self::KEY_FIRSTNAME_FIELD => pht('Use this only when you autogenerate username. e.g.: givenName'), self::KEY_LASTNAME_FIELD => pht('Use this only when you autogenerate username. e.g.: surname'), self::KEY_EMAIL_FIELD => pht('Unique email address. e.g.: email'), self::KEY_ORG_FIELD => pht('Organization name. e.g.: homeOrganization'), self::KEY_ORG_CUSTOM_FIELD => pht('Organization name custom field in Phabricator. e.g.: mycompany:org'), self::KEY_ORG_TYPE_FIELD => pht('Organization type. e.g.: homeOrganizationType'), self::KEY_PAGE_URI_PATTERN => pht('URI pattern to a user pag. Add %%s for replacement with the username'), self::KEY_IMAGE_URI_PATTERN => pht('URI pattern to an image for the user. Add %%s for replacement with the username'), self::KEY_USER_PROJECT => pht('Project ID to add the user to. e.g.: PHID-PROJ-itwdg3fgxutsrdnqjklb'), ); // Text fields foreach ($labels as $key => $label) { $caption = idx($captions, $key); $value = idx($values, $key); $control = null; $control = id(new AphrontFormTextControl()) ->setName($key) ->setLabel($label) ->setCaption($caption) ->setValue($value); $form->appendChild($control); } // Add to project $form->appendChild( id(new AphrontFormCheckboxControl()) ->addCheckbox( self::KEY_ADD_USER_TO_PROJECT, 1, hsprintf('%s: %s', "Add to default Project", "Automatically add the new user to a default project"), idx($values, self::KEY_ADD_USER_TO_PROJECT)) ); $projects = idx($values, self::KEY_USER_PROJECT, array()); $viewer = $request->getViewer(); $form->appendChild( id(new AphrontFormTokenizerControl()) ->setLabel(pht('Projects')) ->setName(self::KEY_USER_PROJECT) ->setValue($projects) ->setUser($viewer) //->setDisabled(!idx($values, self::KEY_ADD_USER_TO_PROJECT)) ->setDatasource(new PhabricatorProjectDatasource()) ); // Generate username $form->appendChild( id(new AphrontFormCheckboxControl()) ->addCheckbox( self::KEY_USERNAME_FROM_REALNAME, 1, hsprintf('%s: %s', "Generated username", "Create a unique username from the surname and firstname which complies with Phabricator policies."), idx($values, self::KEY_USERNAME_FROM_REALNAME)) ); } public function renderConfigPropertyTransactionTitle( PhabricatorAuthProviderConfigTransaction $xaction) { $author_phid = $xaction->getAuthorPHID(); $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); $key = $xaction->getMetadataValue( PhabricatorAuthProviderConfigTransaction::PROPERTY_KEY); $labels = $this->getPropertyLabels(); if (isset($labels[$key])) { $label = $labels[$key]; if (!strlen($old)) { return pht( '%s set the "%s" value to "%s".', $xaction->renderHandleLink($author_phid), $label, $new); } else { return pht( '%s changed the "%s" value from "%s" to "%s".', $xaction->renderHandleLink($author_phid), $label, $old, $new); } } return parent::renderConfigPropertyTransactionTitle($xaction); } public static function getShibbolethProvider() { $providers = self::getAllEnabledProviders(); foreach ($providers as $provider) { if ($provider instanceof PhabricatorAuthProviderShibboleth) { return $provider; } } return null; } } diff --git a/src/extensions/PhabricatorC4sHome.php b/src/extensions/PhabricatorC4sHome.php index ca857076f..162cb627a 100644 --- a/src/extensions/PhabricatorC4sHome.php +++ b/src/extensions/PhabricatorC4sHome.php @@ -1,700 +1,694 @@ getViewer(); if($request->getPath() == '/home/render/feed') { require_celerity_resource('phabricator-dashboard-css'); $content = $this->getFeedContent($request); return id(new AphrontAjaxResponse()) ->setContent(array( 'panelMarkup' => hsprintf('%s', $content), )); } else if(!$viewer->getUsername() || $request->getPath() == '/public') { $view = $this->publicPage($request, $viewer); } else { $view = $this->userPage($request, $viewer); } $navigation = $this->buildSideNavView($request); $page = $this->newPage() ->setNavigation($navigation) ->appendChild($view); // On mobile, / is the menu and /home is the content $is_content = $request->getURIData('content'); if (!$is_content) { $page->addClass('phabricator-home'); } return $page; } private function userPage($request, $viewer) { $tab_key = $request->getStr('tab'); $order_key = $request->getStr('order', 'committed'); // Get and check order key $repo_order = id(new PhabricatorRepositoryQuery()) ->getBuiltinOrders(); unset($repo_order['callsign']); //FIXME: for some reason, ordering with // the calllsign field doesn't work // and crashes, not very usefull though if(!in_array($order_key, array_keys($repo_order))) { $order_key = 'committed'; } // My repo tab (can_edit + created) $view_my = $this->buildRepositoryPanel( $request, 'my', null, 5, false, $order_key, 'author'); $tab_my = id(new PHUITabView()) ->setName(pht('Your repositories')) ->setKey('my') ->appendChild($view_my); // Contribution tab (can_push) $view_contrib = $this->buildRepositoryPanel( $request, 'contrib', null, 5, false, $order_key, 'contrib'); $tab_contrib = id(new PHUITabView()) ->setName(pht('Contribution')) ->setKey('contrib') ->appendChild($view_contrib); // Tab Repositories $tab_group_repo = id(new PHUITabGroupView()) ->addTab($tab_my) ->addTab($tab_contrib); if(in_array($tab_key, array('my', 'contrib'))) { $tab_group_repo->selectTab($tab_key); } // Box header $actions = id(new PhabricatorActionListView()) ->setViewer($viewer); foreach($repo_order as $orderKey => $orderValue) { $action = id(new PhabricatorActionView()) ->setName($orderValue['name']) ->setHref('?order=' . $orderKey . '#box_repo'); if($order_key == $orderKey) { $action->setSelected(true); } $actions->addAction($action); } $order_link = id(new PHUIButtonView()) ->setText('Order By: ' . $order_key) ->setTag('a') ->setIcon('fa-sort') ->setDropdown(true) ->setDropdownMenu($actions); $header = id(new PHUIHeaderView()) ->setHeader(pht('Repositories')) ->addActionLink($order_link); // Box $anchor = id(new PhabricatorAnchorView()) ->setAnchorName('box_repo'); $tab_repo = id(new PHUIObjectBoxView()) ->setHeader($header) ->setAnchor($anchor) ->addTabGroup($tab_group_repo); // Projects member $view_member = $this->buildProjectsPanel($request, 'member', 'member'); $tab_member = id(new PHUITabView()) ->setName(pht('Member')) ->setKey('member') ->appendChild($view_member); // Projects watcher $view_watcher = $this->buildProjectsPanel($request, 'watcher', 'watcher'); $tab_watcher = id(new PHUITabView()) ->setName(pht('Watcher')) ->setKey('watcher') ->appendChild($view_watcher); // Tab Projects $tab_group_proj = id(new PHUITabGroupView()) ->addTab($tab_member) ->addTab($tab_watcher); if(in_array($tab_key, array('member', 'watcher'))) { $tab_group_proj->selectTab($tab_key); } $anchor = id(new PhabricatorAnchorView()) ->setAnchorName('box_proj'); $tab_proj = id(new PHUIObjectBoxView()) ->setHeaderText('Projects') ->setAnchor($anchor) ->addTabGroup($tab_group_proj); // Columns $col_repo = phutil_tag( 'div', array( 'class' => 'homepage-panel', ), array( $tab_repo, $tab_proj, )); $col_feed = phutil_tag( 'div', array( 'class' => 'homepage-panel', ), array( //$this->buildShortcuts(), $this->buildAnnouncement(), $this->buildFeedPanel($request, false), )); $view = id(new AphrontMultiColumnView()) ->addColumn($col_repo) ->addColumn($col_feed) ->setFluidLayout(true); return $view; } private function publicPage($request, $viewer) { $col_feed = phutil_tag( 'div', array( 'class' => 'homepage-panel', ), array( $this->buildAnnouncement(), $this->buildFeedPanel($request), )); $view_active = $this->buildRepositoryPanel($request, 'active', null, 5); $tab_active = id(new PHUITabView()) ->setName(pht('Recently Active')) ->setKey('active') ->appendChild($view_active); $view_commits = $this->buildRepositoryPanel($request, 'commits', null, 5, true, 'size'); $tab_commits = id(new PHUITabView()) ->setName(pht('Most commits')) ->setKey('commits') ->appendChild($view_commits); $view_users = $this->buildRepositoryPanel($request, 'users', null, 5, true, 'users'); $tab_users = id(new PHUITabView()) ->setName(pht('Most contributors')) ->setKey('users') ->appendChild($view_users); $tab_group = id(new PHUITabGroupView()) ->addTab($tab_active) ->addTab($tab_commits) ->addTab($tab_users); $tab_key = $request->getStr('tab'); if(in_array($tab_key, array('my', 'contrib'))) { $tab_group->selectTab($tab_key); } $anchor = id(new PhabricatorAnchorView()) ->setAnchorName('box_repo'); $tab_view = id(new PHUIObjectBoxView()) ->setHeaderText('Repositories') ->setAnchor($anchor) ->addTabGroup($tab_group); $col_repo = phutil_tag( 'div', array( 'class' => 'homepage-panel', ), array( $this->buildPresentationPanel(), $tab_view, )); $view = id(new AphrontMultiColumnView()) ->addColumn($col_repo) ->addColumn($col_feed) ->setFluidLayout(true); return $view; } private function buildAnnouncement() { $viewer = $this->getViewer(); $dashboard = id(new PhabricatorDashboard()) ->loadOneWhere('name = "Service Announcement" && editPolicy = "admin"'); if(!$dashboard) { return; } $dashboard = id(new PhabricatorDashboardQuery()) ->setViewer($viewer) ->withPHIDs(array($dashboard->getPHID())) ->needPanels(true) ->executeOne(); if(!$dashboard->getPanelPHIDs()) { return; } $panels = id(new PhabricatorDashboardPanelQuery()) ->setViewer($viewer) ->withPHIDs($dashboard->getPanelPHIDs()) ->execute(); $boxes = array(); foreach($panels as $panel) { if($panel->getPanelType() != 'text') { continue; } $remarkup = new PHUIRemarkupView($viewer, $panel->getProperty('text')); $layout = id(new PHUIBoxView()) ->appendChild($remarkup) ->addMargin(PHUI::MARGIN_MEDIUM); $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText($panel->getName()) ->appendChild($layout); } return $boxes; } private function buildShortcuts() { $icons = array( id(new PHUIIconView()) ->setIcon('fa-briefcase') ->setBackground('bg-grey') ->setTooltip('Create Project') ->addClass('mmr') ->setHref('/project/edit/form/default/'), id(new PHUIIconView()) ->setIcon('fa-code') ->setBackground('bg-grey') ->setTooltip('Create Repository') ->addClass('mmr') ->setHref('/diffusion/edit/form/default/'), id(new PHUIIconView()) ->setIcon('fa-bug') ->setBackground('bg-grey') ->setTooltip('Report a bug') ->addClass('mmr') ->setHref('/maniphest/task/edit/form/8/'), id(new PHUIIconView()) ->setIcon('fa-book') ->setBackground('bg-grey') ->setTooltip('Documentation') ->addClass('mmr') ->setHref('/w/c4science') ); $layout = id(new PHUIBoxView()) ->appendChild($icons) ->addMargin(PHUI::MARGIN_MEDIUM); $box = id(new PHUIObjectBoxView()) ->setHeaderText('Shortcuts') ->appendChild($layout); return $box; } private function getFeedContent($request) { // hack to have global in the request $global = $request->getBool('dashboardID', true); $viewer = $this->getViewer(); $phids = array(); $epoch_min = time() - 7*24*3600; // 7 days $epoch_max = time(); if(!$global) { //User $phids += array($viewer->getPHID()); // Projects $projects = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->withMemberPHIDs(array($viewer->getPHID())) ->execute(); $project_phids = mpull($projects, 'getPHID'); $phids += array_fuse($project_phids); // Repositories (created by user) $repo_transaction = id(new PhabricatorRepositoryTransactionQuery()) ->setViewer($viewer) ->withAuthorPHIDs(array($viewer->getPHID())) ->withTransactionTypes(array(PhabricatorTransactions::TYPE_CREATE)) ->needComments(false) ->needHandles(false) ->execute(); $repo = array_fuse(mpull($repo_transaction, 'getObjectPHID')); // Repositories (in project user is member of) $any = array(); foreach($project_phids as $phid) { $any[] = 'any(' . $phid . ')'; } $datasource = id(new PhabricatorProjectLogicalDatasource()) ->setViewer($viewer); $constraints = $datasource->evaluateTokens($any); $query = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) ->withEdgeLogicConstraints( PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, $constraints) ->execute(); $repo += array_fuse(mpull($query, 'getPHID')); // Commits for selected Repositories if(!empty($repo)) { $commits = id(new DiffusionCommitQuery()) ->setViewer($viewer) ->withRepositoryPHIDs($repo) ->withEpochRange($epoch_min, $epoch_max) ->execute(); $phids += array_fuse(mpull($commits, 'getPHID')); } // Phriction pages $slugs = array(); foreach($projects as $p) { $slugs[] = PhabricatorProjectWikiCreate::getAllSlugs($p); } $wiki = id(new PhrictionDocumentQuery()) ->setViewer($viewer) ->withSlugsPrefix($slugs) ->withStatus(PhrictionDocumentQuery::STATUS_OPEN) ->execute(); $phids += array_fuse(mpull($wiki, 'getPHID')); } - // Remove Shibolleth groups - $provider = PhabricatorAuthProviderShibboleth::getShibbolethProvider(); - $adapter = $provider->getAdapter(); - $remove_projects = $adapter->getUserProject(); - // Build Feed stories $stories = id(new PhabricatorFeedQuery()) ->setViewer($viewer) ->withFilterPHIDs($phids) - ->withFilterOutPHIDs($remove_projects) ->withEpochInRange($epoch_min, $epoch_max) ->setLimit(12) ->execute(); $view = id(new PhabricatorFeedBuilder($stories)) ->setUser($viewer) ->buildView(); return $view; } private function buildFeedPanel($request, $global=true) { $panel_id = celerity_generate_unique_node_id(); Javelin::initBehavior( 'dashboard-async-panel', array( 'panelID' => $panel_id, 'parentPanelPHIDs' => array(), 'headerMode' => 'none', 'dashboardID' => $global, 'uri' => '/home/render/feed', )); $view_all = id(new PHUIButtonView()) ->setTag('a') ->setIcon(id(new PHUIIconView())->setIcon('fa-list-ul')) ->setText(pht('View All')) ->setHref('/feed/'); $header = id(new PHUIHeaderView()) ->setHeader(pht('Recent Activity')) ->addActionLink($view_all); $feed_box = id(new PHUIBoxView()) ->setID($panel_id) ->addPadding(PHUI::PADDING_LARGE) ->appendChild(pht('Loading...')); $feed = id(new PHUIObjectBoxView()) ->setHeader($header) ->appendChild($feed_box); return $feed; } private function buildProjectsPanel($request, $tab_key, $filter='member', $proj_count=5) { $viewer = $this->getViewer(); $pager = id(new AphrontCursorPagerView()) ->setURI(new PhutilURI('?tab=' . $tab_key . '#box_proj')) ->setPageSize($proj_count); if($tab_key == $request->getStr('tab') && in_array($tab_key, array('member', 'watcher'))) { $pager ->setAfterID($request->getStr('after')) ->setBeforeID($request->getStr('before')); } $projects = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->setOrder('newest') ->needImages(true) ->withStatuses(array(PhabricatorProjectStatus::STATUS_ACTIVE)); if($filter == 'member') { $projects->withMemberPHIDs(array($viewer->getPHID())); } else if($filter == 'watcher') { $projects->withWatcherPHIDs(array($viewer->getPHID())); } $projects = $projects->executeWithCursorPager($pager); if (!empty($projects)) { $list = id(new PhabricatorProjectListView()) ->setUser($viewer) ->setProjects($projects); } else { if($filter == 'member') { $str = 'No project found. [[/project/edit/form/default/ | Create a new project]].'; } else if($filter == 'watcher') { $str = 'No project found. [[/project/query/active/ | Watch a project]].'; } $remarkup = new PHUIRemarkupView($viewer, $str); $list = id(new PHUIBoxView()) ->addPadding(PHUI::PADDING_LARGE) ->appendChild($remarkup); } $box = id(new PHUIObjectBoxView()) ->appendChild($list); if($pager->willShowPagingControls()) { $box->appendChild($pager); } return $box; } private function buildRepositoryPanel( $request, $tab_key, $title, $repo_count=5, $compact=false, $order='committed', $filter=null) { $viewer = $this->getViewer(); $pager = id(new AphrontCursorPagerView()) ->setURI(new PhutilURI('?tab=' . $tab_key . '&order=' . $order . '#box_repo')) ->setPageSize($repo_count); if($tab_key == $request->getStr('tab') && in_array($tab_key, array('my', 'contrib'))) { $pager ->setAfterID($request->getStr('after')) ->setBeforeID($request->getStr('before')); } // Get repositories if($order == 'users') { $table = id(new PhabricatorRepositoryCommit()); $repo = queryfx_all( $table->establishConnection('r'), 'SELECT a.name, a.rid, count(*) AS cnt, a.phid FROM (SELECT r.phid as phid, r.name AS name, repositoryID AS rid, authorPHID AS aid FROM repository_commit JOIN repository AS r ON r.id=repositoryID WHERE viewPolicy="public" AND authorPHID IS NOT NULL AND importStatus=15 GROUP BY aid)a GROUP BY a.rid ORDER BY cnt DESC LIMIT %d', $repo_count); } else { $repo = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) ->setOrder($order) ->needCommitCounts(true); if(!$compact) { $repo ->needMostRecentCommits(true) ->needProjectPHIDs(true) ->needProfileImage(true); } if($filter == 'contrib') { $repo->requireCapabilities(array(DiffusionPushCapability::CAPABILITY)); $repo->withCanPush(); } if($filter == 'author') { $repo->withAuthors(array($viewer->getPHID())); } else if($viewer->getUsername()) { $repo->withoutAuthors(array($viewer->getPHID())); } $repo = $repo->executeWithCursorPager($pager); } // Create List if(!empty($repo)) { $list = new PHUIObjectItemListView(); } else { $remarkup = new PHUIRemarkupView($viewer, 'No repository found. [[/diffusion/edit/form/default/ | Create a new repository]].'); $list = id(new PHUIBoxView()) ->addPadding(PHUI::PADDING_LARGE) ->appendChild($remarkup); } foreach($repo as $r){ $item = id(new PHUIObjectItemView()); if($order == 'users') { $item ->setHeader($r['name']) ->addIcon('', pht('%s Users', new PhutilNumber($r['cnt']))) ->setHref(pht('/diffusion/%d/', $r['rid'])); } else { if($r->getStatus() == PhabricatorRepository::STATUS_INACTIVE) { $item->setDisabled(true); } $item ->setHeader($r->getName()) ->addIcon('', pht('%s Commit(s)', new PhutilNumber($r->getCommitCount()))) ->setHref($r->getURI()); } if(!$compact) { $item->setImageURI($r->getProfileImageURI()); $desc = id(new PhutilUTF8StringTruncator()) ->setMaximumGlyphs(120) ->truncateString($r->getDetail('description')); $desc = new PHUIRemarkupView($viewer, $desc); $item->setSubHead($desc); if($r->getMostRecentCommit()) { $item->addIcon('', phabricator_date( $r->getMostRecentCommit()->getDateCreated(), $viewer)); } if($r->getProjectPHIDs()) { $handles = id(new PhabricatorHandleQuery()) ->setViewer($viewer) ->witHPHIDs($r->getProjectPHIDs()) ->execute(); $project_handles = array_select_keys( $handles, $r->getProjectPHIDs()); if ($project_handles) { $item->addAttribute( id(new PHUIHandleTagListView()) ->setSlim(true) ->setHandles($project_handles)); } } } $list->addItem($item); } if($pager->willShowPagingControls()) { $pager_item = id(new PHUIObjectItemView()) ->appendChild($pager); $list->addItem($pager_item); } $box = id(new PHUIObjectBoxView()) ->setObjectList($list); if($title) { $header = id(new PHUIHeaderView()) ->setHeader($title); $box->setHeader($header); } if($order == 'users') { $repo_phids = ipull($repo, 'phid'); } else { $repo_phids = mpull($repo, 'getPHID'); } return $box; } private function buildPresentationPanel() { $viewer = $this->getViewer(); $content = pht(<<appendChild($remarkup); return $box; } private function buildSideNavView(AphrontRequest $request) { $viewer = $this->getViewer(); $home = id(new PhabricatorHomeApplication()); $engine = id(new PhabricatorHomeProfileMenuEngine()) ->setViewer($viewer) ->setProfileObject($home) ->setCustomPHID($viewer->getPHID()); $is_content = $request->getURIData('content'); if (!$is_content) { $engine->addContentPageClass('phabricator-home'); } return $engine->buildNavigation(); } }