@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 PhabricatorApplicationPolicyChangeTransaction
4 extends PhabricatorApplicationTransactionType {
5
6 const TRANSACTIONTYPE = 'application.policy';
7 const METADATA_ATTRIBUTE = 'capability.name';
8
9 private $policies;
10
11 public function generateOldValue($object) {
12 $application = $object;
13 $capability = $this->getCapabilityName();
14 return $application->getPolicy($capability);
15 }
16
17 public function applyExternalEffects($object, $value) {
18 $application = $object;
19 $user = $this->getActor();
20
21 $key = 'phabricator.application-settings';
22 $config_entry = PhabricatorConfigEntry::loadConfigEntry($key);
23 $current_value = $config_entry->getValue();
24
25 $phid = $application->getPHID();
26 if (empty($current_value[$phid])) {
27 $current_value[$application->getPHID()] = array();
28 }
29 if (empty($current_value[$phid]['policy'])) {
30 $current_value[$phid]['policy'] = array();
31 }
32
33 $new = array($this->getCapabilityName() => $value);
34 $current_value[$phid]['policy'] = $new + $current_value[$phid]['policy'];
35
36 $editor = $this->getEditor();
37 $content_source = $editor->getContentSource();
38
39 // NOTE: We allow applications to have custom edit policies, but they are
40 // currently stored in the Config application. The ability to edit Config
41 // values is always restricted to administrators, today. Empower this
42 // particular edit to punch through possible stricter policies, so normal
43 // users can change application configuration if the application allows
44 // them to do so.
45
46 PhabricatorConfigEditor::storeNewValue(
47 PhabricatorUser::getOmnipotentUser(),
48 $config_entry,
49 $current_value,
50 $content_source,
51 $user->getPHID());
52 }
53
54 public function getTitle() {
55 return pht(
56 '%s changed the %s policy from %s to %s.',
57 $this->renderAuthor(),
58 $this->renderCapability(),
59 $this->renderOldPolicy(),
60 $this->renderNewPolicy());
61 }
62
63 public function getTitleForFeed() {
64 return pht(
65 '%s changed the %s policy for application %s from %s to %s.',
66 $this->renderAuthor(),
67 $this->renderCapability(),
68 $this->renderObject(),
69 $this->renderOldPolicy(),
70 $this->renderNewPolicy());
71 }
72
73 public function validateTransactions($object, array $xactions) {
74 $user = $this->getActor();
75 $application = $object;
76 $policies = id(new PhabricatorPolicyQuery())
77 ->setViewer($user)
78 ->setObject($application)
79 ->execute();
80
81 $errors = array();
82 foreach ($xactions as $xaction) {
83 $new = $xaction->getNewValue();
84 $capability = $xaction->getMetadataValue(self::METADATA_ATTRIBUTE);
85
86 if (empty($policies[$new])) {
87 // Not a standard policy, check for a custom policy.
88 $policy = id(new PhabricatorPolicyQuery())
89 ->setViewer($user)
90 ->withPHIDs(array($new))
91 ->executeOne();
92 if (!$policy) {
93 $errors[] = $this->newInvalidError(
94 pht('Policy does not exist.'));
95 continue;
96 }
97 } else {
98 $policy = idx($policies, $new);
99 }
100
101 if (!$policy->isValidPolicyForEdit()) {
102 $errors[] = $this->newInvalidError(
103 pht('Can\'t set the policy to a policy you can\'t view!'));
104 continue;
105 }
106
107 if ($new == PhabricatorPolicies::POLICY_PUBLIC) {
108 $capobj = PhabricatorPolicyCapability::getCapabilityByKey(
109 $capability);
110 if (!$capobj || !$capobj->shouldAllowPublicPolicySetting()) {
111 $errors[] = $this->newInvalidError(
112 pht('Can\'t set non-public policies to public.'));
113 continue;
114 }
115 }
116
117 if (!$application->isCapabilityEditable($capability)) {
118 $errors[] = $this->newInvalidError(
119 pht('Capability "%s" is not editable for this application.',
120 $capability));
121 continue;
122 }
123 }
124
125 // If we're changing these policies, the viewer needs to still be able to
126 // view or edit the application under the new policy.
127 $validate_map = array(
128 PhabricatorPolicyCapability::CAN_VIEW,
129 PhabricatorPolicyCapability::CAN_EDIT,
130 );
131 $validate_map = array_fill_keys($validate_map, array());
132
133 foreach ($xactions as $xaction) {
134 $capability = $xaction->getMetadataValue(self::METADATA_ATTRIBUTE);
135 if (!isset($validate_map[$capability])) {
136 continue;
137 }
138
139 $validate_map[$capability][] = $xaction;
140 }
141
142 foreach ($validate_map as $capability => $cap_xactions) {
143 if (!$cap_xactions) {
144 continue;
145 }
146
147 $editor = $this->getEditor();
148 $policy_errors = $editor->validatePolicyTransaction(
149 $object,
150 $cap_xactions,
151 self::TRANSACTIONTYPE,
152 $capability);
153
154 foreach ($policy_errors as $error) {
155 $errors[] = $error;
156 }
157 }
158
159 return $errors;
160 }
161
162 private function renderCapability() {
163 $application = $this->getObject();
164 $capability = $this->getCapabilityName();
165 $label = $application->getCapabilityLabel($capability);
166 return $this->renderValue($label);
167 }
168
169 private function getCapabilityName() {
170 return $this->getMetadataValue(self::METADATA_ATTRIBUTE);
171 }
172
173}