@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 PhabricatorProjectNameTransaction
4 extends PhabricatorProjectTransactionType {
5
6 const TRANSACTIONTYPE = 'project:name';
7
8 public function generateOldValue($object) {
9 return $object->getName();
10 }
11
12 public function applyInternalEffects($object, $value) {
13 $object->setName($value);
14 if (!$this->getEditor()->getIsMilestone()) {
15 $object->setPrimarySlug(PhabricatorSlug::normalizeProjectSlug($value));
16 }
17 }
18
19 public function applyExternalEffects($object, $value) {
20 $old = $this->getOldValue();
21
22 // First, add the old name as a secondary slug; this is helpful
23 // for renames and generally a good thing to do.
24 if (!$this->getEditor()->getIsMilestone()) {
25 if ($old !== null) {
26 $this->getEditor()->addSlug($object, $old, false);
27 }
28 $this->getEditor()->addSlug($object, $value, false);
29 }
30 return;
31 }
32
33 public function getTitle() {
34 $old = $this->getOldValue();
35 if ($old === null) {
36 return pht(
37 '%s created this project.',
38 $this->renderAuthor());
39 } else {
40 return pht(
41 '%s renamed this project from %s to %s.',
42 $this->renderAuthor(),
43 $this->renderOldValue(),
44 $this->renderNewValue());
45 }
46 }
47
48 public function getTitleForFeed() {
49 $old = $this->getOldValue();
50 if ($old === null) {
51 return pht(
52 '%s created %s.',
53 $this->renderAuthor(),
54 $this->renderObject());
55 } else {
56 return pht(
57 '%s renamed %s from %s to %s.',
58 $this->renderAuthor(),
59 $this->renderObject(),
60 $this->renderOldValue(),
61 $this->renderNewValue());
62 }
63 }
64
65 public function validateTransactions($object, array $xactions) {
66 $errors = array();
67
68 if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
69 $errors[] = $this->newRequiredError(pht('Projects must have a name.'));
70 }
71
72 $max_length = $object->getColumnMaximumByteLength('name');
73 foreach ($xactions as $xaction) {
74 $new_value = $xaction->getNewValue() ?? '';
75 $new_value = trim($new_value); // Strip surrounding whitespace
76 $xaction->setNewValue($new_value);
77 $new_length = strlen($new_value);
78 if ($new_length > $max_length) {
79 $errors[] = $this->newInvalidError(
80 pht(
81 'Project names must not be longer than %s character(s).',
82 new PhutilNumber($max_length)));
83 }
84 }
85
86 if ($this->getEditor()->getIsMilestone() || !$xactions) {
87 return $errors;
88 }
89
90 $name = last($xactions)->getNewValue();
91
92 if (!PhabricatorSlug::isValidProjectSlug($name)) {
93 $errors[] = $this->newInvalidError(
94 pht('Project names must contain at least one letter or number.'));
95 }
96
97 $slug = PhabricatorSlug::normalizeProjectSlug($name);
98
99 $slug_used_already = id(new PhabricatorProjectSlug())
100 ->loadOneWhere('slug = %s', $slug);
101 if ($slug_used_already &&
102 $slug_used_already->getProjectPHID() != $object->getPHID()) {
103
104 $errors[] = $this->newInvalidError(
105 pht(
106 'Project name generates the same hashtag ("%s") as another '.
107 'existing project. Choose a unique name.',
108 '#'.$slug));
109 }
110
111 return $errors;
112 }
113
114}