]> git.mxchange.org Git - friendica-addons.git/blob - dav/SabreDAV/bin/migrateto17.php
Merge pull request #585 from annando/nicer-cat
[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
99     switch($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) {
100
101         case 'mysql' :
102
103             $pdo->exec(<<<SQL
104 ALTER TABLE calendarobjects 
105 ADD etag VARCHAR(32),
106 ADD size INT(11) UNSIGNED,
107 ADD componenttype VARCHAR(8),
108 ADD firstoccurence INT(11) UNSIGNED,
109 ADD lastoccurence INT(11) UNSIGNED
110 SQL
111         );
112             break;
113             case 'sqlite' :
114                 $pdo->exec('ALTER TABLE calendarobjects ADD etag text');
115                 $pdo->exec('ALTER TABLE calendarobjects ADD size integer');
116                 $pdo->exec('ALTER TABLE calendarobjects ADD componenttype TEXT');
117                 $pdo->exec('ALTER TABLE calendarobjects ADD firstoccurence integer');
118                 $pdo->exec('ALTER TABLE calendarobjects ADD lastoccurence integer');
119                 break;
120
121         default :
122             die('This upgrade script does not support this driver (' . $pdo->getAttribute(PDO::ATTR_DRIVER_NAME) . ")\n");
123
124     } 
125     echo "Database schema upgraded.\n";
126
127 } elseif ($found === 5) {
128
129     echo "Database already had the 1.7 schema\n";
130
131 } else {
132
133     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";
134     echo "You can manually try to upgrade the schema, and then run this script again.\n";
135     exit(-1);
136
137 }
138
139 echo "Now, we need to parse every record and pull out some information.\n";
140
141 $result = $pdo->query('SELECT id, calendardata FROM calendarobjects');
142 $stmt = $pdo->prepare('UPDATE calendarobjects SET etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ? WHERE id = ?'); 
143
144 echo "Total records found: " . $result->rowCount() . "\n";
145 $done = 0;
146 $total = $result->rowCount();
147 while($row = $result->fetch()) {
148
149     try {
150         $newData = getDenormalizedData($row['calendardata']);
151     } catch (Exception $e) {
152         echo "===\nException caught will trying to parser calendarobject.\n";
153         echo "Error message: " . $e->getMessage() . "\n";
154         echo "Record id: " . $row['id'] . "\n";
155         echo "This record is ignored, you should inspect it to see if there's anything wrong.\n===\n";
156         continue;
157     }
158     $stmt->execute(array(
159         $newData['etag'],
160         $newData['size'],
161         $newData['componentType'],
162         $newData['firstOccurence'],
163         $newData['lastOccurence'],
164         $row['id'],
165     ));
166     $done++;
167
168     if ($done % 500 === 0) {
169         echo "Completed: $done / $total\n";
170     } 
171 }
172
173 echo "Process completed!\n";
174
175 /**
176  * Parses some information from calendar objects, used for optimized
177  * calendar-queries.
178  *
179  * Blantently copied from Sabre_CalDAV_Backend_PDO
180  *
181  * Returns an array with the following keys:
182  *   * etag
183  *   * size
184  *   * componentType
185  *   * firstOccurence
186  *   * lastOccurence
187  *
188  * @param string $calendarData
189  * @return array
190  */
191 function getDenormalizedData($calendarData) {
192
193     $vObject = Sabre_VObject_Reader::read($calendarData);
194     $componentType = null;
195     $component = null;
196     $firstOccurence = null;
197     $lastOccurence = null;
198     foreach($vObject->getComponents() as $component) {
199         if ($component->name!=='VTIMEZONE') {
200             $componentType = $component->name;
201             break;
202         }
203     }
204     if (!$componentType) {
205         throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
206     }
207     if ($componentType === 'VEVENT') {
208         $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
209         // Finding the last occurence is a bit harder
210         if (!isset($component->RRULE)) {
211             if (isset($component->DTEND)) {
212                 $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
213             } elseif (isset($component->DURATION)) {
214                 $endDate = clone $component->DTSTART->getDateTime();
215                 $endDate->add(Sabre_VObject_DateTimeParser::parse($component->DURATION->value));
216                 $lastOccurence = $endDate->getTimeStamp();
217             } elseif ($component->DTSTART->getDateType()===Sabre_VObject_Property_DateTime::DATE) {
218                 $endDate = clone $component->DTSTART->getDateTime();
219                 $endDate->modify('+1 day');
220                 $lastOccurence = $endDate->getTimeStamp();
221             } else {
222                 $lastOccurence = $firstOccurence;
223             }
224         } else {
225             $it = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->UID);
226             $maxDate = new DateTime(Sabre_CalDAV_Backend_PDO::MAX_DATE);
227             if ($it->isInfinite()) {
228                 $lastOccurence = $maxDate->getTimeStamp();
229             } else {
230                 $end = $it->getDtEnd();
231                 while($it->valid() && $end < $maxDate) {
232                     $end = $it->getDtEnd();
233                     $it->next();
234
235                 }
236                 $lastOccurence = $end->getTimeStamp();
237             }
238
239         }
240     }
241
242     return array(
243         'etag' => md5($calendarData),
244         'size' => strlen($calendarData),
245         'componentType' => $componentType,
246         'firstOccurence' => $firstOccurence,
247         'lastOccurence'  => $lastOccurence,
248     );
249
250 }