@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
3abstract class PhabricatorApplicationConfigOptions extends Phobject {
4
5 abstract public function getName();
6 abstract public function getDescription();
7 abstract public function getGroup();
8 /**
9 * @return array<PhabricatorConfigOption>
10 */
11 abstract public function getOptions();
12
13 public function getIcon() {
14 return 'fa-sliders';
15 }
16
17 /**
18 * Get corresponding application class for configuration options. Child
19 * classes returning a classname should also have getGroup() return 'apps'.
20 *
21 * @return class-string|null Application class name, or null if the config
22 * options are not related to a specific application.
23 */
24 public function getApplicationClassName() {
25 return null;
26 }
27
28 public function validateOption(PhabricatorConfigOption $option, $value) {
29 if ($value === $option->getDefault()) {
30 return;
31 }
32
33 if ($value === null) {
34 return;
35 }
36
37 $type = $option->newOptionType();
38 if ($type) {
39 try {
40 $type->validateStoredValue($option, $value);
41 $this->didValidateOption($option, $value);
42 } catch (PhabricatorConfigValidationException $ex) {
43 throw $ex;
44 } catch (Exception $ex) {
45 // If custom validators threw exceptions other than validation
46 // exceptions, convert them to validation exceptions so we repair the
47 // configuration and raise an error.
48 throw new PhabricatorConfigValidationException($ex->getMessage());
49 }
50
51 return;
52 }
53
54 if ($option->isCustomType()) {
55 try {
56 return $option->getCustomObject()->validateOption($option, $value);
57 } catch (Exception $ex) {
58 throw new PhabricatorConfigValidationException($ex->getMessage());
59 }
60 } else {
61 throw new Exception(
62 pht(
63 'Unknown configuration option type "%s".',
64 $option->getType()));
65 }
66
67 $this->didValidateOption($option, $value);
68 }
69
70 protected function didValidateOption(
71 PhabricatorConfigOption $option,
72 $value) {
73 // Hook for subclasses to do complex validation.
74 return;
75 }
76
77 /**
78 * Hook to render additional hints based on, e.g., the viewing user, request,
79 * or other context. For example, this is used to show workspace IDs when
80 * configuring `asana.workspace-id`.
81 *
82 * @param PhabricatorConfigOption $option Option being rendered.
83 * @param AphrontRequest $request Active request.
84 * @return PhutilSafeHTMLProducerInterface|array|null Additional contextual
85 * description information.
86 */
87 public function renderContextualDescription(
88 PhabricatorConfigOption $option,
89 AphrontRequest $request) {
90 return null;
91 }
92
93 public function getKey() {
94 $class = get_class($this);
95 $matches = null;
96 if (preg_match('/^Phabricator(.*)ConfigOptions$/', $class, $matches)) {
97 return strtolower($matches[1]);
98 }
99 return strtolower(get_class($this));
100 }
101
102 final protected function newOption($key, $type, $default) {
103 return id(new PhabricatorConfigOption())
104 ->setKey($key)
105 ->setType($type)
106 ->setDefault($default)
107 ->setGroup($this);
108 }
109
110 final public static function loadAll($external_only = false) {
111 $symbols = id(new PhutilSymbolLoader())
112 ->setAncestorClass(self::class)
113 ->setConcreteOnly(true)
114 ->selectAndLoadSymbols();
115
116 $groups = array();
117 foreach ($symbols as $symbol) {
118 if ($external_only && $symbol['library'] == 'phabricator') {
119 continue;
120 }
121
122 $obj = newv($symbol['name'], array());
123 $key = $obj->getKey();
124 if (isset($groups[$key])) {
125 $pclass = get_class($groups[$key]);
126 $nclass = $symbol['name'];
127
128 throw new Exception(
129 pht(
130 "Multiple %s subclasses have the same key ('%s'): %s, %s.",
131 self::class,
132 $key,
133 $pclass,
134 $nclass));
135 }
136 $groups[$key] = $obj;
137 }
138
139 return $groups;
140 }
141
142 final public static function loadAllOptions($external_only = false) {
143 $groups = self::loadAll($external_only);
144
145 $options = array();
146 foreach ($groups as $group) {
147 foreach ($group->getOptions() as $option) {
148 $key = $option->getKey();
149 if (isset($options[$key])) {
150 throw new Exception(
151 pht(
152 "Multiple %s subclasses contain an option named '%s'!",
153 self::class,
154 $key));
155 }
156 $options[$key] = $option;
157 }
158 }
159
160 return $options;
161 }
162
163 /**
164 * Deformat a HEREDOC for use in remarkup by converting line breaks to
165 * spaces.
166 */
167 final protected function deformat($string) {
168 return preg_replace('/(?<=\S)\n(?=\S)/', ' ', $string);
169 }
170
171}