@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorProjectUpdateController
4 extends PhabricatorProjectController {
5
6 public function handleRequest(AphrontRequest $request) {
7 $viewer = $request->getViewer();
8 $id = $request->getURIData('id');
9 $action = $request->getURIData('action');
10
11 $capabilities = array(
12 PhabricatorPolicyCapability::CAN_VIEW,
13 );
14
15 switch ($action) {
16 case 'join':
17 $capabilities[] = PhabricatorPolicyCapability::CAN_JOIN;
18 break;
19 case 'leave':
20 break;
21 default:
22 return new Aphront404Response();
23 }
24
25 $project = id(new PhabricatorProjectQuery())
26 ->setViewer($viewer)
27 ->withIDs(array($id))
28 ->needMembers(true)
29 ->requireCapabilities($capabilities)
30 ->executeOne();
31 if (!$project) {
32 return new Aphront404Response();
33 }
34
35 $done_uri = "/project/members/{$id}/";
36
37 if (!$project->supportsEditMembers()) {
38 $copy = pht('Parent projects and milestones do not support adding '.
39 'members. You can add members directly to any non-parent subproject.');
40
41 $subprojects_uri = "/project/subprojects/{$id}/";
42
43 return $this->newDialog()
44 ->setTitle(pht('Unsupported Project'))
45 ->appendParagraph($copy)
46 ->setSubmitURI($subprojects_uri)
47 ->addSubmitButton(pht('See Subprojects'))
48 ->setDisableWorkflowOnSubmit(true)
49 ->addCancelButton($done_uri);
50 }
51
52 if ($request->isFormPost()) {
53 $edge_action = null;
54 switch ($action) {
55 case 'join':
56 $edge_action = '+';
57 break;
58 case 'leave':
59 $edge_action = '-';
60 break;
61 }
62
63 $type_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST;
64
65 $member_spec = array(
66 $edge_action => array($viewer->getPHID() => $viewer->getPHID()),
67 );
68
69 $xactions = array();
70 $xactions[] = id(new PhabricatorProjectTransaction())
71 ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
72 ->setMetadataValue('edge:type', $type_member)
73 ->setNewValue($member_spec);
74
75 $editor = id(new PhabricatorProjectTransactionEditor())
76 ->setActor($viewer)
77 ->setContentSourceFromRequest($request)
78 ->setContinueOnNoEffect(true)
79 ->setContinueOnMissingFields(true)
80 ->applyTransactions($project, $xactions);
81
82 return id(new AphrontRedirectResponse())->setURI($done_uri);
83 }
84
85 $is_locked = $project->getIsMembershipLocked();
86 $can_edit = PhabricatorPolicyFilter::hasCapability(
87 $viewer,
88 $project,
89 PhabricatorPolicyCapability::CAN_EDIT);
90 $can_leave = ($can_edit || !$is_locked);
91
92 $button = null;
93 if ($action == 'leave') {
94 if ($can_leave) {
95 $title = pht('Leave Project');
96 $body = pht(
97 'Your tremendous contributions to this project will be sorely '.
98 'missed. Are you sure you want to leave?');
99 $button = pht('Leave Project');
100 } else {
101 $title = pht('Membership Locked');
102 $body = pht(
103 'Membership for this project is locked. You can not leave.');
104 }
105 } else {
106 $title = pht('Join Project');
107 $body = pht(
108 'Join this project? You will become a member and enjoy whatever '.
109 'benefits membership may confer.');
110 $button = pht('Join Project');
111 }
112
113 $dialog = $this->newDialog()
114 ->setTitle($title)
115 ->appendParagraph($body)
116 ->addCancelButton($done_uri);
117
118 if ($button) {
119 $dialog->addSubmitButton($button);
120 }
121
122 return $dialog;
123 }
124
125}