@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
3$conn_w = id(new PhabricatorMetaMTAMail())->establishConnection('w');
4$lists = new LiskRawMigrationIterator($conn_w, 'metamta_mailinglist');
5
6echo pht('Migrating mailing lists...')."\n";
7
8foreach ($lists as $list) {
9 $name = $list['name'];
10 $email = $list['email'];
11 $uri = $list['uri'];
12 $old_phid = $list['phid'];
13
14 $username = preg_replace('/[^a-zA-Z0-9_-]+/', '-', $name);
15 $username = preg_replace('/-{2,}/', '-', $username);
16 $username = trim($username, '-');
17 if (!strlen($username)) {
18 $username = 'mailinglist';
19 }
20 $username .= '-list';
21
22 $username_okay = false;
23 for ($suffix = 1; $suffix <= 9; $suffix++) {
24 if ($suffix == 1) {
25 $effective_username = $username;
26 } else {
27 $effective_username = $username.$suffix;
28 }
29
30 $collision = id(new PhabricatorPeopleQuery())
31 ->setViewer(PhabricatorUser::getOmnipotentUser())
32 ->withUsernames(array($effective_username))
33 ->executeOne();
34 if (!$collision) {
35 $username_okay = true;
36 break;
37 }
38 }
39
40 if (!$username_okay) {
41 echo pht(
42 'Failed to migrate mailing list "%s": unable to generate a unique '.
43 'username for it.',
44 $name)."\n";
45 continue;
46 }
47
48 $username = $effective_username;
49 if (!PhabricatorUser::validateUsername($username)) {
50 echo pht(
51 'Failed to migrate mailing list "%s": unable to generate a valid '.
52 'username for it.',
53 $name)."\n";
54 continue;
55 }
56
57 $address = id(new PhabricatorUserEmail())->loadOneWhere(
58 'address = %s',
59 $email);
60 if ($address) {
61 echo pht(
62 'Failed to migrate mailing list "%s": an existing user already '.
63 'has the email address "%s".',
64 $name,
65 $email)."\n";
66 continue;
67 }
68
69 $user = id(new PhabricatorUser())
70 ->setUsername($username)
71 ->setRealName(pht('Mailing List "%s"', $name))
72 ->setIsApproved(1)
73 ->setIsMailingList(1);
74
75 $email_object = id(new PhabricatorUserEmail())
76 ->setAddress($email)
77 ->setIsVerified(1);
78
79 try {
80 id(new PhabricatorUserEditor())
81 ->setActor($user)
82 ->createNewUser($user, $email_object);
83 } catch (Exception $ex) {
84 echo pht(
85 'Failed to migrate mailing list "%s": %s.',
86 $name,
87 $ex->getMessage())."\n";
88 continue;
89 }
90
91 $new_phid = $user->getPHID();
92
93 // NOTE: After the PHID type is removed we can't use any Edge code to
94 // modify edges.
95
96 $edge_type = PhabricatorSubscribedToObjectEdgeType::EDGECONST;
97 $edge_inverse = PhabricatorObjectHasSubscriberEdgeType::EDGECONST;
98
99 $map = PhabricatorPHIDType::getAllTypes();
100 foreach ($map as $type => $spec) {
101 try {
102 $object = $spec->newObject();
103 if (!$object) {
104 continue;
105 }
106 $object_conn_w = $object->establishConnection('w');
107 queryfx(
108 $object_conn_w,
109 'UPDATE %T SET dst = %s WHERE dst = %s AND type = %s',
110 PhabricatorEdgeConfig::TABLE_NAME_EDGE,
111 $new_phid,
112 $old_phid,
113 $edge_inverse);
114 } catch (Exception $ex) {
115 // Just ignore these; they're mostly tables not existing.
116 continue;
117 }
118 }
119
120 try {
121 $dst_phids = queryfx_all(
122 $conn_w,
123 'SELECT dst FROM %T WHERE src = %s AND type = %s',
124 PhabricatorEdgeConfig::TABLE_NAME_EDGE,
125 $old_phid,
126 $edge_type);
127 if ($dst_phids) {
128 $editor = new PhabricatorEdgeEditor();
129 foreach ($dst_phids as $dst_phid) {
130 $editor->addEdge($new_phid, $edge_type, $dst_phid['dst']);
131 }
132 $editor->save();
133 }
134 } catch (Exception $ex) {
135 echo pht(
136 'Unable to migrate some inverse edges for mailing list "%s": %s.',
137 $name,
138 $ex->getMessage())."\n";
139 continue;
140 }
141
142 echo pht(
143 'Migrated mailing list "%s" to mailing list user "%s".',
144 $name,
145 $user->getUsername())."\n";
146}