]> git.mxchange.org Git - friendica-addons.git/blob - dav/SabreDAV/bin/migrateto17.php
Initial Release of the calendar plugin
[friendica-addons.git] / dav / SabreDAV / bin / migrateto17.php
1 <?php
2
3 echo "SabreDAV migrate script for version 1.7\n";
4
5 if ($argc<2) {
6
7     echo <<<HELLO
8
9 This script help you migrate from a pre-1.7 database to 1.7 and later\n
10 It is important to note, that this script only touches the 'calendarobjects'
11 table.
12
13 If you do not have this table, or don't use the default PDO CalDAV backend
14 it's pointless to run this script.
15
16 Keep in mind that some processing will be done on every single record of this
17 table and in addition, ALTER TABLE commands will be executed.
18 If you have a large calendarobjects table, this may mean that this process
19 takes a while.
20
21 Usage:
22
23 {$argv[0]} [pdo-dsn] [username] [password]
24
25 For example:
26
27 {$argv[0]} mysql:host=localhost;dbname=sabredav root password
28 {$argv[0]} sqlite:data/sabredav.db
29
30 HELLO;
31
32     exit();
33
34 }
35
36 if (file_exists(__DIR__ . '/../lib/Sabre/VObject/includes.php')) {
37     include __DIR__ . '/../lib/Sabre/VObject/includes.php';
38 } else {
39     // If, for some reason VObject was not found in the vicinity,
40     // we'll try to grab it from the default path.
41     require 'Sabre/VObject/includes.php';
42 }
43
44 $dsn = $argv[1];
45 $user = isset($argv[2])?$argv[2]:null;
46 $pass = isset($argv[3])?$argv[3]:null;
47
48 echo "Connecting to database: " . $dsn . "\n";
49
50 $pdo = new PDO($dsn, $user, $pass);
51 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
52 $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
53
54 echo "Validating existing table layout\n";
55
56 // The only cross-db way to do this, is to just fetch a single record.
57 $row = $pdo->query("SELECT * FROM calendarobjects LIMIT 1")->fetch();
58
59 if (!$row) {
60     echo "Error: This database did not have any records in the calendarobjects table, you should just recreate the table.\n";
61     exit(-1);
62 }
63
64 $requiredFields = array(
65     'id', 
66     'calendardata',
67     'uri',
68     'calendarid', 
69     'lastmodified',
70 );
71
72 foreach($requiredFields as $requiredField) {
73     if (!array_key_exists($requiredField,$row)) {
74         echo "Error: The current 'calendarobjects' table was missing a field we expected to exist.\n";
75         echo "For safety reasons, this process is stopped.\n";
76         exit(-1);
77     }
78 }
79
80 $fields17 = array(
81     'etag',
82     'size',
83     'componenttype',
84     'firstoccurence',
85     'lastoccurence',
86 );
87
88 $found = 0;
89 foreach($fields17 as $field) {
90     if (array_key_exists($field, $row)) {
91         $found++;
92     }
93 }
94
95 if ($found === 0) {
96     echo "The database had the 1.6 schema. Table will now be altered.\n";
97     echo "This may take some time for large tables\n";
98     $pdo->exec(<<<SQL
99 ALTER TABLE calendarobjects 
100 ADD etag VARCHAR(32),
101 ADD size INT(11) UNSIGNED,
102 ADD componenttype VARCHAR(8),
103 ADD firstoccurence INT(11) UNSIGNED,
104 ADD lastoccurence INT(11) UNSIGNED
105 SQL
106 );
107     echo "Database schema upgraded.\n";
108
109 } elseif ($found === 5) {
110
111     echo "Database already had the 1.7 schema\n";
112
113 } else {
114
115     echo "The database had $found out of 5 from the changes for 1.7. This is scary and unusual, so we have to abort.\n";
116     echo "You can manually try to upgrade the schema, and then run this script again.\n";
117     exit(-1);
118
119 }
120
121 echo "Now, we need to parse every record and pull out some information.\n";
122
123 $result = $pdo->query('SELECT id, calendardata FROM calendarobjects');
124 $stmt = $pdo->prepare('UPDATE calendarobjects SET etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ? WHERE id = ?'); 
125
126 echo "Total records found: " . $result->rowCount() . "\n";
127 $done = 0;
128 $total = $result->rowCount();
129 while($row = $result->fetch()) {
130
131     try {
132         $newData = getDenormalizedData($row['calendardata']);
133     } catch (Exception $e) {
134         echo "===\nException caught will trying to parser calendarobject.\n";
135         echo "Error message: " . $e->getMessage() . "\n";
136         echo "Record id: " . $row['id'] . "\n";
137         echo "This record is ignored, you should inspect it to see if there's anything wrong.\n===\n";
138         continue;
139     }
140     $stmt->execute(array(
141         $newData['etag'],
142         $newData['size'],
143         $newData['componentType'],
144         $newData['firstOccurence'],
145         $newData['lastOccurence'],
146         $row['id'],
147     ));
148     $done++;
149
150     if ($done % 500 === 0) {
151         echo "Completed: $done / $total\n";
152     } 
153 }
154
155 echo "Process completed!\n";
156
157 /**
158  * Parses some information from calendar objects, used for optimized
159  * calendar-queries.
160  *
161  * Blantently copied from Sabre_CalDAV_Backend_PDO
162  *
163  * Returns an array with the following keys:
164  *   * etag
165  *   * size
166  *   * componentType
167  *   * firstOccurence
168  *   * lastOccurence
169  *
170  * @param string $calendarData
171  * @return array
172  */
173 function getDenormalizedData($calendarData) {
174
175     $vObject = Sabre_VObject_Reader::read($calendarData);
176     $componentType = null;
177     $component = null;
178     $firstOccurence = null;
179     $lastOccurence = null;
180     foreach($vObject->getComponents() as $component) {
181         if ($component->name!=='VTIMEZONE') {
182             $componentType = $component->name;
183             break;
184         }
185     }
186     if (!$componentType) {
187         throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
188     }
189     if ($componentType === 'VEVENT') {
190         $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
191         // Finding the last occurence is a bit harder
192         if (!isset($component->RRULE)) {
193             if (isset($component->DTEND)) {
194                 $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
195             } elseif (isset($component->DURATION)) {
196                 $endDate = clone $component->DTSTART->getDateTime();
197                 $endDate->add(Sabre_VObject_DateTimeParser::parse($component->DURATION->value));
198                 $lastOccurence = $endDate->getTimeStamp();
199             } elseif ($component->DTSTART->getDateType()===Sabre_VObject_Property_DateTime::DATE) {
200                 $endDate = clone $component->DTSTART->getDateTime();
201                 $endDate->modify('+1 day');
202                 $lastOccurence = $endDate->getTimeStamp();
203             } else {
204                 $lastOccurence = $firstOccurence;
205             }
206         } else {
207             $it = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->UID);
208             $maxDate = new DateTime(self::MAX_DATE);
209             if ($it->isInfinite()) {
210                 $lastOccurence = $maxDate->getTimeStamp();
211             } else {
212                 $end = $it->getDtEnd();
213                 while($it->valid() && $end < $maxDate) {
214                     $end = $it->getDtEnd();
215                     $it->next();
216
217                 }
218                 $lastOccurence = $end->getTimeStamp();
219             }
220
221         }
222     }
223
224     return array(
225         'etag' => md5($calendarData),
226         'size' => strlen($calendarData),
227         'componentType' => $componentType,
228         'firstOccurence' => $firstOccurence,
229         'lastOccurence'  => $lastOccurence,
230     );
231
232 }