142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 | = $message->text ?>
151 |
152 |
153 |
154 |
155 | = $content ?>
156 |
157 |
158 |
159 |
160 |
161 |
166 |
167 |
168 |
169 |
170 |
171 |
204 |
205 | = View::stack('javascript') ?>
206 |
207 | = \Vesta\VestaUtils::get(ModuleService::class)->findByInterface(ModuleGlobalInterface::class)->map(static function (ModuleGlobalInterface $module): string {
208 | return $module->bodyContent();
209 | })->implode('') ?>
210 |
211 |
212 |
--------------------------------------------------------------------------------
/patchedWebtrees/Services/GedcomEditServiceExt2.php:
--------------------------------------------------------------------------------
1 | forNewIndividual = $forNewIndividual;
34 | $this->vesta_symbol = json_decode('"\u26B6"');
35 | $this->house_symbol = json_decode('"\u2302"');
36 | }
37 |
38 | protected function insertMissingLevels(Tree $tree, string $tag, string $gedcom, bool $include_hidden): string
39 | {
40 | $next_level = substr_count($tag, ':') + 1;
41 | $factory = Registry::elementFactory();
42 | $subtags = $factory->make($tag)->subtags();
43 |
44 | // The first part is level N. The remainder are level N+1.
45 | $parts = preg_split('/\n(?=' . $next_level . ')/', $gedcom);
46 | $return = array_shift($parts) ?? '';
47 |
48 | foreach ($subtags as $subtag => $occurrences) {
49 | if (!$include_hidden) {
50 |
51 | if ("MAP" == $subtag) {
52 | //Issue #168: MAP is special (it has no own edit control)
53 | //hide only if both its subtags are hidden
54 | //(cannot unhide unconditionally: that strategy would show PLAC with empty dropdown if all others are hidden)
55 | $longHidden = $this->isHiddenTagExt(
56 | $tree,
57 | $tag . ':' . $subtag . ':LONG',
58 | false);
59 |
60 | $latiHidden = $this->isHiddenTagExt(
61 | $tree,
62 | $tag . ':' . $subtag . ':LATI',
63 | false);
64 |
65 | $hidden = $longHidden && $latiHidden;
66 | } else {
67 | $hidden = $this->isHiddenTagExt(
68 | $tree,
69 | $tag . ':' . $subtag,
70 | str_ends_with($occurrences, ':?'));
71 | }
72 |
73 |
74 | if ($hidden) {
75 |
76 | //we want to preserve overall order in any case
77 | //(in particular in the 'edit main facts and events' dialog)
78 | //therefore show existing hidden at its 'normal' position, rather than at the end
79 | $existing = false;
80 | foreach ($parts as $n => $part) {
81 | if (str_starts_with($part, $next_level . ' ' . $subtag)) {
82 | $existing = true;
83 | break;
84 | }
85 | }
86 |
87 | if (!$existing) {
88 | continue;
89 | }
90 | }
91 | }
92 |
93 | [$min, $max] = explode(':', $occurrences);
94 |
95 | $min = (int) $min;
96 |
97 | if ($max === 'M') {
98 | $max = PHP_INT_MAX;
99 | } else {
100 | $max = (int) $max;
101 | }
102 |
103 | $count = 0;
104 |
105 | // Add expected subtags in our preferred order.
106 | foreach ($parts as $n => $part) {
107 | if (str_starts_with($part, $next_level . ' ' . $subtag)) {
108 | $return .= "\n" . $this->insertMissingLevels($tree, $tag . ':' . $subtag, $part, $include_hidden);
109 | $count++;
110 | unset($parts[$n]);
111 | }
112 | }
113 |
114 | // Allowed to have more of this subtag?
115 | if ($count < $max) {
116 | // Create a new one.
117 | $gedcom = $next_level . ' ' . $subtag;
118 | $default = $factory->make($tag . ':' . $subtag)->default($tree);
119 | if ($default !== '') {
120 | $gedcom .= ' ' . $default;
121 | }
122 |
123 | $number_to_add = max(1, $min - $count);
124 | $gedcom_to_add = "\n" . $this->insertMissingLevels($tree, $tag . ':' . $subtag, $gedcom, $include_hidden);
125 |
126 | $return .= str_repeat($gedcom_to_add, $number_to_add);
127 | }
128 | }
129 |
130 | // Now add any unexpected/existing data.
131 | if ($parts !== []) {
132 | $return .= "\n" . implode("\n", $parts);
133 | }
134 |
135 | return $return;
136 | }
137 |
138 | public function getPreference(
139 | Tree $tree,
140 | string $tag): string {
141 |
142 | $tag = $this->compressTag($tag);
143 | $pref = $this->vesta_symbol . $this->house_symbol . $tag;
144 | $override = $tree->getPreference($pref);
145 | return $override;
146 | }
147 |
148 | public function setPreference(
149 | Tree $tree,
150 | string $tag,
151 | string $value) {
152 |
153 | $tag = $this->compressTag($tag);
154 | $pref = $this->vesta_symbol . $this->house_symbol . $tag;
155 | //TODO would be better to delete pref in case of empty string
156 | $tree->setPreference($pref, $value);
157 | }
158 |
159 | protected function isHiddenTagExt(
160 | Tree $tree,
161 | string $tag,
162 | bool $hiddenViaOccurrences): bool {
163 |
164 | $r = new ReflectionMethod(parent::class, 'isHiddenTag');
165 | $r->setAccessible(true);
166 | $originalRet = $hiddenViaOccurrences || $r->invokeArgs($this, [$tag]);
167 |
168 | //error_log("hidden? " . $tag . '=' . $originalRet);
169 |
170 | //check for specific override
171 | $tag = $this->compressTag($tag);
172 | $pref = $this->vesta_symbol . $this->house_symbol . $tag;
173 |
174 | //error_log("override? " . $pref);
175 |
176 | $override = $tree->getPreference($pref);
177 | if ($override !== '') {
178 | switch ($override) {
179 | //hide always
180 | case 'LEVEL0':
181 | $overrideRet = true;
182 | break;
183 | //hide for new individual, show otherwise
184 | case 'LEVEL1':
185 | case 'LEVEL1a':
186 | if ($this->forNewIndividual) {
187 | $overrideRet = true;
188 | } else {
189 | $overrideRet = false;
190 | }
191 | break;
192 | //show always
193 | case 'LEVEL2':
194 | case 'LEVEL2a':
195 | $overrideRet = false;
196 | break;
197 | //(unexpected; hide always)
198 | default:
199 | $overrideRet = true;
200 | break;
201 | }
202 |
203 | if ($originalRet !== $overrideRet) {
204 | //error_log($tag . "override specific to ".$overrideRet);
205 | }
206 |
207 | return $overrideRet;
208 | }
209 |
210 | //check for generic override
211 | $tag2parts = explode(':',$tag);
212 | $tag2parts[1] = '*';
213 | $tag2 = implode(':',$tag2parts);
214 |
215 | $tag2 = $this->compressTag($tag2);
216 | $pref = $this->vesta_symbol . $this->house_symbol . $tag2;
217 |
218 | //error_log("override? " . $pref);
219 |
220 | $override = $tree->getPreference($pref);
221 | if ($override !== '') {
222 | switch ($override) {
223 | //hide always
224 | case 'LEVEL0':
225 | $overrideRet = true;
226 | break;
227 | //hide for new individual, show otherwise
228 | case 'LEVEL1':
229 | case 'LEVEL1a':
230 | if ($this->forNewIndividual) {
231 | $overrideRet = true;
232 | } else {
233 | $overrideRet = false;
234 | }
235 | break;
236 | //show always
237 | case 'LEVEL2':
238 | case 'LEVEL2a':
239 | $overrideRet = false;
240 | break;
241 | //(unexpected; hide always)
242 | default:
243 | $overrideRet = true;
244 | break;
245 | }
246 |
247 | if ($originalRet !== $overrideRet) {
248 | //error_log($tag2 . " override generic to ".$overrideRet);
249 | }
250 |
251 | return $overrideRet;
252 | }
253 |
254 | return $originalRet;
255 | }
256 |
257 | public function alwaysExpandSubtags(
258 | Tree $tree,
259 | string $tag): bool {
260 |
261 | //check for specific override
262 | $tag = $this->compressTag($tag);
263 | $pref = $this->vesta_symbol . $this->house_symbol . $tag;
264 |
265 | //error_log("override? " . $pref);
266 |
267 | $override = $tree->getPreference($pref);
268 | if ($override !== '') {
269 | switch ($override) {
270 | case 'LEVEL1a':
271 | case 'LEVEL2a':
272 | return true;
273 | default:
274 | break;
275 | }
276 | }
277 |
278 | //check for generic override
279 | $tag2parts = explode(':',$tag);
280 | $tag2parts[1] = '*';
281 | $tag2 = implode(':',$tag2parts);
282 |
283 | $tag2 = $this->compressTag($tag2);
284 | $pref = $this->vesta_symbol . $this->house_symbol . $tag2;
285 |
286 | //error_log("override? " . $pref);
287 |
288 | $override = $tree->getPreference($pref);
289 | if ($override !== '') {
290 | switch ($override) {
291 | case 'LEVEL1a':
292 | case 'LEVEL2a':
293 | return true;
294 | default:
295 | break;
296 | }
297 | }
298 |
299 | return false;
300 | }
301 |
302 | protected function compressTag(string $tag): string {
303 | //we only have 32 chars in table column!
304 | //(first 2 of those are used for pref outside tag, i.e. 30 left
305 | //note that this may still be problematic for custom tags)
306 | $tagCompressed = $tag;
307 | $tagCompressed = str_replace("INDI","I", $tagCompressed);
308 | $tagCompressed = str_replace("FAM","F", $tagCompressed);
309 | $tagCompressed = str_replace("ASSO","A", $tagCompressed);
310 | $tagCompressed = str_replace("SOUR","S", $tagCompressed);
311 | $tagCompressed = str_replace("PLAC","P", $tagCompressed);
312 |
313 | return $tagCompressed;
314 | }
315 | }
316 |
--------------------------------------------------------------------------------