Passed
Push — master ( aeb32e...81302f )
by Christoph
15:20 queued 10s
created
lib/private/DB/Migrator.php 2 patches
Indentation   +250 added lines, -250 removed lines patch added patch discarded remove patch
@@ -48,254 +48,254 @@
 block discarded – undo
48 48
 
49 49
 class Migrator {
50 50
 
51
-	/** @var \Doctrine\DBAL\Connection */
52
-	protected $connection;
53
-
54
-	/** @var ISecureRandom */
55
-	private $random;
56
-
57
-	/** @var IConfig */
58
-	protected $config;
59
-
60
-	/** @var EventDispatcherInterface  */
61
-	private $dispatcher;
62
-
63
-	/** @var bool */
64
-	private $noEmit = false;
65
-
66
-	/**
67
-	 * @param \Doctrine\DBAL\Connection $connection
68
-	 * @param ISecureRandom $random
69
-	 * @param IConfig $config
70
-	 * @param EventDispatcherInterface $dispatcher
71
-	 */
72
-	public function __construct(\Doctrine\DBAL\Connection $connection,
73
-								ISecureRandom $random,
74
-								IConfig $config,
75
-								EventDispatcherInterface $dispatcher = null) {
76
-		$this->connection = $connection;
77
-		$this->random = $random;
78
-		$this->config = $config;
79
-		$this->dispatcher = $dispatcher;
80
-	}
81
-
82
-	/**
83
-	 * @param \Doctrine\DBAL\Schema\Schema $targetSchema
84
-	 */
85
-	public function migrate(Schema $targetSchema) {
86
-		$this->noEmit = true;
87
-		$this->applySchema($targetSchema);
88
-	}
89
-
90
-	/**
91
-	 * @param \Doctrine\DBAL\Schema\Schema $targetSchema
92
-	 * @return string
93
-	 */
94
-	public function generateChangeScript(Schema $targetSchema) {
95
-		$schemaDiff = $this->getDiff($targetSchema, $this->connection);
96
-
97
-		$script = '';
98
-		$sqls = $schemaDiff->toSql($this->connection->getDatabasePlatform());
99
-		foreach ($sqls as $sql) {
100
-			$script .= $this->convertStatementToScript($sql);
101
-		}
102
-
103
-		return $script;
104
-	}
105
-
106
-	/**
107
-	 * Create a unique name for the temporary table
108
-	 *
109
-	 * @param string $name
110
-	 * @return string
111
-	 */
112
-	protected function generateTemporaryTableName($name) {
113
-		return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
114
-	}
115
-
116
-	/**
117
-	 * Check the migration of a table on a copy so we can detect errors before messing with the real table
118
-	 *
119
-	 * @param \Doctrine\DBAL\Schema\Table $table
120
-	 * @throws \OC\DB\MigrationException
121
-	 */
122
-	protected function checkTableMigrate(Table $table) {
123
-		$name = $table->getName();
124
-		$tmpName = $this->generateTemporaryTableName($name);
125
-
126
-		$this->copyTable($name, $tmpName);
127
-
128
-		//create the migration schema for the temporary table
129
-		$tmpTable = $this->renameTableSchema($table, $tmpName);
130
-		$schemaConfig = new SchemaConfig();
131
-		$schemaConfig->setName($this->connection->getDatabase());
132
-		$schema = new Schema([$tmpTable], [], $schemaConfig);
133
-
134
-		try {
135
-			$this->applySchema($schema);
136
-			$this->dropTable($tmpName);
137
-		} catch (Exception $e) {
138
-			// pgsql needs to commit it's failed transaction before doing anything else
139
-			if ($this->connection->isTransactionActive()) {
140
-				$this->connection->commit();
141
-			}
142
-			$this->dropTable($tmpName);
143
-			throw new MigrationException($table->getName(), $e->getMessage());
144
-		}
145
-	}
146
-
147
-	/**
148
-	 * @param \Doctrine\DBAL\Schema\Table $table
149
-	 * @param string $newName
150
-	 * @return \Doctrine\DBAL\Schema\Table
151
-	 */
152
-	protected function renameTableSchema(Table $table, $newName) {
153
-		/**
154
-		 * @var \Doctrine\DBAL\Schema\Index[] $indexes
155
-		 */
156
-		$indexes = $table->getIndexes();
157
-		$newIndexes = [];
158
-		foreach ($indexes as $index) {
159
-			if ($index->isPrimary()) {
160
-				// do not rename primary key
161
-				$indexName = $index->getName();
162
-			} else {
163
-				// avoid conflicts in index names
164
-				$indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER);
165
-			}
166
-			$newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary());
167
-		}
168
-
169
-		// foreign keys are not supported so we just set it to an empty array
170
-		return new Table($newName, $table->getColumns(), $newIndexes, [], [], $table->getOptions());
171
-	}
172
-
173
-	public function createSchema() {
174
-		$this->connection->getConfiguration()->setSchemaAssetsFilter(function ($asset) {
175
-			/** @var string|AbstractAsset $asset */
176
-			$filterExpression = $this->getFilterExpression();
177
-			if ($asset instanceof AbstractAsset) {
178
-				return preg_match($filterExpression, $asset->getName()) !== false;
179
-			}
180
-			return preg_match($filterExpression, $asset) !== false;
181
-		});
182
-		return $this->connection->getSchemaManager()->createSchema();
183
-	}
184
-
185
-	/**
186
-	 * @param Schema $targetSchema
187
-	 * @param \Doctrine\DBAL\Connection $connection
188
-	 * @return \Doctrine\DBAL\Schema\SchemaDiff
189
-	 */
190
-	protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) {
191
-		// adjust varchar columns with a length higher then getVarcharMaxLength to clob
192
-		foreach ($targetSchema->getTables() as $table) {
193
-			foreach ($table->getColumns() as $column) {
194
-				if ($column->getType() instanceof StringType) {
195
-					if ($column->getLength() > $connection->getDatabasePlatform()->getVarcharMaxLength()) {
196
-						$column->setType(Type::getType('text'));
197
-						$column->setLength(null);
198
-					}
199
-				}
200
-			}
201
-		}
202
-
203
-		$this->connection->getConfiguration()->setSchemaAssetsFilter(function ($asset) {
204
-			/** @var string|AbstractAsset $asset */
205
-			$filterExpression = $this->getFilterExpression();
206
-			if ($asset instanceof AbstractAsset) {
207
-				return preg_match($filterExpression, $asset->getName()) !== false;
208
-			}
209
-			return preg_match($filterExpression, $asset) !== false;
210
-		});
211
-		$sourceSchema = $connection->getSchemaManager()->createSchema();
212
-
213
-		// remove tables we don't know about
214
-		foreach ($sourceSchema->getTables() as $table) {
215
-			if (!$targetSchema->hasTable($table->getName())) {
216
-				$sourceSchema->dropTable($table->getName());
217
-			}
218
-		}
219
-		// remove sequences we don't know about
220
-		foreach ($sourceSchema->getSequences() as $table) {
221
-			if (!$targetSchema->hasSequence($table->getName())) {
222
-				$sourceSchema->dropSequence($table->getName());
223
-			}
224
-		}
225
-
226
-		$comparator = new Comparator();
227
-		return $comparator->compare($sourceSchema, $targetSchema);
228
-	}
229
-
230
-	/**
231
-	 * @param \Doctrine\DBAL\Schema\Schema $targetSchema
232
-	 * @param \Doctrine\DBAL\Connection $connection
233
-	 */
234
-	protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) {
235
-		if (is_null($connection)) {
236
-			$connection = $this->connection;
237
-		}
238
-
239
-		$schemaDiff = $this->getDiff($targetSchema, $connection);
240
-
241
-		$connection->beginTransaction();
242
-		$sqls = $schemaDiff->toSql($connection->getDatabasePlatform());
243
-		$step = 0;
244
-		foreach ($sqls as $sql) {
245
-			$this->emit($sql, $step++, count($sqls));
246
-			$connection->query($sql);
247
-		}
248
-		$connection->commit();
249
-	}
250
-
251
-	/**
252
-	 * @param string $sourceName
253
-	 * @param string $targetName
254
-	 */
255
-	protected function copyTable($sourceName, $targetName) {
256
-		$quotedSource = $this->connection->quoteIdentifier($sourceName);
257
-		$quotedTarget = $this->connection->quoteIdentifier($targetName);
258
-
259
-		$this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')');
260
-		$this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource);
261
-	}
262
-
263
-	/**
264
-	 * @param string $name
265
-	 */
266
-	protected function dropTable($name) {
267
-		$this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name));
268
-	}
269
-
270
-	/**
271
-	 * @param $statement
272
-	 * @return string
273
-	 */
274
-	protected function convertStatementToScript($statement) {
275
-		$script = $statement . ';';
276
-		$script .= PHP_EOL;
277
-		$script .= PHP_EOL;
278
-		return $script;
279
-	}
280
-
281
-	protected function getFilterExpression() {
282
-		return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
283
-	}
284
-
285
-	protected function emit($sql, $step, $max) {
286
-		if ($this->noEmit) {
287
-			return;
288
-		}
289
-		if (is_null($this->dispatcher)) {
290
-			return;
291
-		}
292
-		$this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step + 1, $max]));
293
-	}
294
-
295
-	private function emitCheckStep($tableName, $step, $max) {
296
-		if (is_null($this->dispatcher)) {
297
-			return;
298
-		}
299
-		$this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step + 1, $max]));
300
-	}
51
+    /** @var \Doctrine\DBAL\Connection */
52
+    protected $connection;
53
+
54
+    /** @var ISecureRandom */
55
+    private $random;
56
+
57
+    /** @var IConfig */
58
+    protected $config;
59
+
60
+    /** @var EventDispatcherInterface  */
61
+    private $dispatcher;
62
+
63
+    /** @var bool */
64
+    private $noEmit = false;
65
+
66
+    /**
67
+     * @param \Doctrine\DBAL\Connection $connection
68
+     * @param ISecureRandom $random
69
+     * @param IConfig $config
70
+     * @param EventDispatcherInterface $dispatcher
71
+     */
72
+    public function __construct(\Doctrine\DBAL\Connection $connection,
73
+                                ISecureRandom $random,
74
+                                IConfig $config,
75
+                                EventDispatcherInterface $dispatcher = null) {
76
+        $this->connection = $connection;
77
+        $this->random = $random;
78
+        $this->config = $config;
79
+        $this->dispatcher = $dispatcher;
80
+    }
81
+
82
+    /**
83
+     * @param \Doctrine\DBAL\Schema\Schema $targetSchema
84
+     */
85
+    public function migrate(Schema $targetSchema) {
86
+        $this->noEmit = true;
87
+        $this->applySchema($targetSchema);
88
+    }
89
+
90
+    /**
91
+     * @param \Doctrine\DBAL\Schema\Schema $targetSchema
92
+     * @return string
93
+     */
94
+    public function generateChangeScript(Schema $targetSchema) {
95
+        $schemaDiff = $this->getDiff($targetSchema, $this->connection);
96
+
97
+        $script = '';
98
+        $sqls = $schemaDiff->toSql($this->connection->getDatabasePlatform());
99
+        foreach ($sqls as $sql) {
100
+            $script .= $this->convertStatementToScript($sql);
101
+        }
102
+
103
+        return $script;
104
+    }
105
+
106
+    /**
107
+     * Create a unique name for the temporary table
108
+     *
109
+     * @param string $name
110
+     * @return string
111
+     */
112
+    protected function generateTemporaryTableName($name) {
113
+        return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
114
+    }
115
+
116
+    /**
117
+     * Check the migration of a table on a copy so we can detect errors before messing with the real table
118
+     *
119
+     * @param \Doctrine\DBAL\Schema\Table $table
120
+     * @throws \OC\DB\MigrationException
121
+     */
122
+    protected function checkTableMigrate(Table $table) {
123
+        $name = $table->getName();
124
+        $tmpName = $this->generateTemporaryTableName($name);
125
+
126
+        $this->copyTable($name, $tmpName);
127
+
128
+        //create the migration schema for the temporary table
129
+        $tmpTable = $this->renameTableSchema($table, $tmpName);
130
+        $schemaConfig = new SchemaConfig();
131
+        $schemaConfig->setName($this->connection->getDatabase());
132
+        $schema = new Schema([$tmpTable], [], $schemaConfig);
133
+
134
+        try {
135
+            $this->applySchema($schema);
136
+            $this->dropTable($tmpName);
137
+        } catch (Exception $e) {
138
+            // pgsql needs to commit it's failed transaction before doing anything else
139
+            if ($this->connection->isTransactionActive()) {
140
+                $this->connection->commit();
141
+            }
142
+            $this->dropTable($tmpName);
143
+            throw new MigrationException($table->getName(), $e->getMessage());
144
+        }
145
+    }
146
+
147
+    /**
148
+     * @param \Doctrine\DBAL\Schema\Table $table
149
+     * @param string $newName
150
+     * @return \Doctrine\DBAL\Schema\Table
151
+     */
152
+    protected function renameTableSchema(Table $table, $newName) {
153
+        /**
154
+         * @var \Doctrine\DBAL\Schema\Index[] $indexes
155
+         */
156
+        $indexes = $table->getIndexes();
157
+        $newIndexes = [];
158
+        foreach ($indexes as $index) {
159
+            if ($index->isPrimary()) {
160
+                // do not rename primary key
161
+                $indexName = $index->getName();
162
+            } else {
163
+                // avoid conflicts in index names
164
+                $indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER);
165
+            }
166
+            $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary());
167
+        }
168
+
169
+        // foreign keys are not supported so we just set it to an empty array
170
+        return new Table($newName, $table->getColumns(), $newIndexes, [], [], $table->getOptions());
171
+    }
172
+
173
+    public function createSchema() {
174
+        $this->connection->getConfiguration()->setSchemaAssetsFilter(function ($asset) {
175
+            /** @var string|AbstractAsset $asset */
176
+            $filterExpression = $this->getFilterExpression();
177
+            if ($asset instanceof AbstractAsset) {
178
+                return preg_match($filterExpression, $asset->getName()) !== false;
179
+            }
180
+            return preg_match($filterExpression, $asset) !== false;
181
+        });
182
+        return $this->connection->getSchemaManager()->createSchema();
183
+    }
184
+
185
+    /**
186
+     * @param Schema $targetSchema
187
+     * @param \Doctrine\DBAL\Connection $connection
188
+     * @return \Doctrine\DBAL\Schema\SchemaDiff
189
+     */
190
+    protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) {
191
+        // adjust varchar columns with a length higher then getVarcharMaxLength to clob
192
+        foreach ($targetSchema->getTables() as $table) {
193
+            foreach ($table->getColumns() as $column) {
194
+                if ($column->getType() instanceof StringType) {
195
+                    if ($column->getLength() > $connection->getDatabasePlatform()->getVarcharMaxLength()) {
196
+                        $column->setType(Type::getType('text'));
197
+                        $column->setLength(null);
198
+                    }
199
+                }
200
+            }
201
+        }
202
+
203
+        $this->connection->getConfiguration()->setSchemaAssetsFilter(function ($asset) {
204
+            /** @var string|AbstractAsset $asset */
205
+            $filterExpression = $this->getFilterExpression();
206
+            if ($asset instanceof AbstractAsset) {
207
+                return preg_match($filterExpression, $asset->getName()) !== false;
208
+            }
209
+            return preg_match($filterExpression, $asset) !== false;
210
+        });
211
+        $sourceSchema = $connection->getSchemaManager()->createSchema();
212
+
213
+        // remove tables we don't know about
214
+        foreach ($sourceSchema->getTables() as $table) {
215
+            if (!$targetSchema->hasTable($table->getName())) {
216
+                $sourceSchema->dropTable($table->getName());
217
+            }
218
+        }
219
+        // remove sequences we don't know about
220
+        foreach ($sourceSchema->getSequences() as $table) {
221
+            if (!$targetSchema->hasSequence($table->getName())) {
222
+                $sourceSchema->dropSequence($table->getName());
223
+            }
224
+        }
225
+
226
+        $comparator = new Comparator();
227
+        return $comparator->compare($sourceSchema, $targetSchema);
228
+    }
229
+
230
+    /**
231
+     * @param \Doctrine\DBAL\Schema\Schema $targetSchema
232
+     * @param \Doctrine\DBAL\Connection $connection
233
+     */
234
+    protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) {
235
+        if (is_null($connection)) {
236
+            $connection = $this->connection;
237
+        }
238
+
239
+        $schemaDiff = $this->getDiff($targetSchema, $connection);
240
+
241
+        $connection->beginTransaction();
242
+        $sqls = $schemaDiff->toSql($connection->getDatabasePlatform());
243
+        $step = 0;
244
+        foreach ($sqls as $sql) {
245
+            $this->emit($sql, $step++, count($sqls));
246
+            $connection->query($sql);
247
+        }
248
+        $connection->commit();
249
+    }
250
+
251
+    /**
252
+     * @param string $sourceName
253
+     * @param string $targetName
254
+     */
255
+    protected function copyTable($sourceName, $targetName) {
256
+        $quotedSource = $this->connection->quoteIdentifier($sourceName);
257
+        $quotedTarget = $this->connection->quoteIdentifier($targetName);
258
+
259
+        $this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')');
260
+        $this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource);
261
+    }
262
+
263
+    /**
264
+     * @param string $name
265
+     */
266
+    protected function dropTable($name) {
267
+        $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name));
268
+    }
269
+
270
+    /**
271
+     * @param $statement
272
+     * @return string
273
+     */
274
+    protected function convertStatementToScript($statement) {
275
+        $script = $statement . ';';
276
+        $script .= PHP_EOL;
277
+        $script .= PHP_EOL;
278
+        return $script;
279
+    }
280
+
281
+    protected function getFilterExpression() {
282
+        return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
283
+    }
284
+
285
+    protected function emit($sql, $step, $max) {
286
+        if ($this->noEmit) {
287
+            return;
288
+        }
289
+        if (is_null($this->dispatcher)) {
290
+            return;
291
+        }
292
+        $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step + 1, $max]));
293
+    }
294
+
295
+    private function emitCheckStep($tableName, $step, $max) {
296
+        if (is_null($this->dispatcher)) {
297
+            return;
298
+        }
299
+        $this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step + 1, $max]));
300
+    }
301 301
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -110,7 +110,7 @@  discard block
 block discarded – undo
110 110
 	 * @return string
111 111
 	 */
112 112
 	protected function generateTemporaryTableName($name) {
113
-		return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
113
+		return $this->config->getSystemValue('dbtableprefix', 'oc_').$name.'_'.$this->random->generate(13, ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
114 114
 	}
115 115
 
116 116
 	/**
@@ -161,7 +161,7 @@  discard block
 block discarded – undo
161 161
 				$indexName = $index->getName();
162 162
 			} else {
163 163
 				// avoid conflicts in index names
164
-				$indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER);
164
+				$indexName = $this->config->getSystemValue('dbtableprefix', 'oc_').$this->random->generate(13, ISecureRandom::CHAR_LOWER);
165 165
 			}
166 166
 			$newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary());
167 167
 		}
@@ -171,7 +171,7 @@  discard block
 block discarded – undo
171 171
 	}
172 172
 
173 173
 	public function createSchema() {
174
-		$this->connection->getConfiguration()->setSchemaAssetsFilter(function ($asset) {
174
+		$this->connection->getConfiguration()->setSchemaAssetsFilter(function($asset) {
175 175
 			/** @var string|AbstractAsset $asset */
176 176
 			$filterExpression = $this->getFilterExpression();
177 177
 			if ($asset instanceof AbstractAsset) {
@@ -200,7 +200,7 @@  discard block
 block discarded – undo
200 200
 			}
201 201
 		}
202 202
 
203
-		$this->connection->getConfiguration()->setSchemaAssetsFilter(function ($asset) {
203
+		$this->connection->getConfiguration()->setSchemaAssetsFilter(function($asset) {
204 204
 			/** @var string|AbstractAsset $asset */
205 205
 			$filterExpression = $this->getFilterExpression();
206 206
 			if ($asset instanceof AbstractAsset) {
@@ -256,15 +256,15 @@  discard block
 block discarded – undo
256 256
 		$quotedSource = $this->connection->quoteIdentifier($sourceName);
257 257
 		$quotedTarget = $this->connection->quoteIdentifier($targetName);
258 258
 
259
-		$this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')');
260
-		$this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource);
259
+		$this->connection->exec('CREATE TABLE '.$quotedTarget.' (LIKE '.$quotedSource.')');
260
+		$this->connection->exec('INSERT INTO '.$quotedTarget.' SELECT * FROM '.$quotedSource);
261 261
 	}
262 262
 
263 263
 	/**
264 264
 	 * @param string $name
265 265
 	 */
266 266
 	protected function dropTable($name) {
267
-		$this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name));
267
+		$this->connection->exec('DROP TABLE '.$this->connection->quoteIdentifier($name));
268 268
 	}
269 269
 
270 270
 	/**
@@ -272,14 +272,14 @@  discard block
 block discarded – undo
272 272
 	 * @return string
273 273
 	 */
274 274
 	protected function convertStatementToScript($statement) {
275
-		$script = $statement . ';';
275
+		$script = $statement.';';
276 276
 		$script .= PHP_EOL;
277 277
 		$script .= PHP_EOL;
278 278
 		return $script;
279 279
 	}
280 280
 
281 281
 	protected function getFilterExpression() {
282
-		return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
282
+		return '/^'.preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')).'/';
283 283
 	}
284 284
 
285 285
 	protected function emit($sql, $step, $max) {
Please login to merge, or discard this patch.
lib/private/DB/MDB2SchemaReader.php 1 patch
Indentation   +300 added lines, -300 removed lines patch added patch discarded remove patch
@@ -36,320 +36,320 @@
 block discarded – undo
36 36
 
37 37
 class MDB2SchemaReader {
38 38
 
39
-	/**
40
-	 * @var string $DBTABLEPREFIX
41
-	 */
42
-	protected $DBTABLEPREFIX;
39
+    /**
40
+     * @var string $DBTABLEPREFIX
41
+     */
42
+    protected $DBTABLEPREFIX;
43 43
 
44
-	/**
45
-	 * @var \Doctrine\DBAL\Platforms\AbstractPlatform $platform
46
-	 */
47
-	protected $platform;
44
+    /**
45
+     * @var \Doctrine\DBAL\Platforms\AbstractPlatform $platform
46
+     */
47
+    protected $platform;
48 48
 
49
-	/** @var IConfig */
50
-	protected $config;
49
+    /** @var IConfig */
50
+    protected $config;
51 51
 
52
-	/**
53
-	 * @param \OCP\IConfig $config
54
-	 * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
55
-	 */
56
-	public function __construct(IConfig $config, AbstractPlatform $platform) {
57
-		$this->platform = $platform;
58
-		$this->config = $config;
59
-		$this->DBTABLEPREFIX = $config->getSystemValue('dbtableprefix', 'oc_');
60
-	}
52
+    /**
53
+     * @param \OCP\IConfig $config
54
+     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
55
+     */
56
+    public function __construct(IConfig $config, AbstractPlatform $platform) {
57
+        $this->platform = $platform;
58
+        $this->config = $config;
59
+        $this->DBTABLEPREFIX = $config->getSystemValue('dbtableprefix', 'oc_');
60
+    }
61 61
 
62
-	/**
63
-	 * @param string $file
64
-	 * @param Schema $schema
65
-	 * @return Schema
66
-	 * @throws \DomainException
67
-	 */
68
-	public function loadSchemaFromFile($file, Schema $schema) {
69
-		$loadEntities = libxml_disable_entity_loader(false);
70
-		$xml = simplexml_load_file($file);
71
-		libxml_disable_entity_loader($loadEntities);
72
-		foreach ($xml->children() as $child) {
73
-			/**
74
-			 * @var \SimpleXMLElement $child
75
-			 */
76
-			switch ($child->getName()) {
77
-				case 'name':
78
-				case 'create':
79
-				case 'overwrite':
80
-				case 'charset':
81
-					break;
82
-				case 'table':
83
-					$this->loadTable($schema, $child);
84
-					break;
85
-				default:
86
-					throw new \DomainException('Unknown element: ' . $child->getName());
62
+    /**
63
+     * @param string $file
64
+     * @param Schema $schema
65
+     * @return Schema
66
+     * @throws \DomainException
67
+     */
68
+    public function loadSchemaFromFile($file, Schema $schema) {
69
+        $loadEntities = libxml_disable_entity_loader(false);
70
+        $xml = simplexml_load_file($file);
71
+        libxml_disable_entity_loader($loadEntities);
72
+        foreach ($xml->children() as $child) {
73
+            /**
74
+             * @var \SimpleXMLElement $child
75
+             */
76
+            switch ($child->getName()) {
77
+                case 'name':
78
+                case 'create':
79
+                case 'overwrite':
80
+                case 'charset':
81
+                    break;
82
+                case 'table':
83
+                    $this->loadTable($schema, $child);
84
+                    break;
85
+                default:
86
+                    throw new \DomainException('Unknown element: ' . $child->getName());
87 87
 
88
-			}
89
-		}
90
-		return $schema;
91
-	}
88
+            }
89
+        }
90
+        return $schema;
91
+    }
92 92
 
93
-	/**
94
-	 * @param \Doctrine\DBAL\Schema\Schema $schema
95
-	 * @param \SimpleXMLElement $xml
96
-	 * @throws \DomainException
97
-	 */
98
-	private function loadTable($schema, $xml) {
99
-		$table = null;
100
-		foreach ($xml->children() as $child) {
101
-			/**
102
-			 * @var \SimpleXMLElement $child
103
-			 */
104
-			switch ($child->getName()) {
105
-				case 'name':
106
-					$name = (string)$child;
107
-					$name = str_replace('*dbprefix*', $this->DBTABLEPREFIX, $name);
108
-					$name = $this->platform->quoteIdentifier($name);
109
-					$table = $schema->createTable($name);
110
-					break;
111
-				case 'create':
112
-				case 'overwrite':
113
-				case 'charset':
114
-					break;
115
-				case 'declaration':
116
-					if (is_null($table)) {
117
-						throw new \DomainException('Table declaration before table name');
118
-					}
119
-					$this->loadDeclaration($table, $child);
120
-					break;
121
-				default:
122
-					throw new \DomainException('Unknown element: ' . $child->getName());
93
+    /**
94
+     * @param \Doctrine\DBAL\Schema\Schema $schema
95
+     * @param \SimpleXMLElement $xml
96
+     * @throws \DomainException
97
+     */
98
+    private function loadTable($schema, $xml) {
99
+        $table = null;
100
+        foreach ($xml->children() as $child) {
101
+            /**
102
+             * @var \SimpleXMLElement $child
103
+             */
104
+            switch ($child->getName()) {
105
+                case 'name':
106
+                    $name = (string)$child;
107
+                    $name = str_replace('*dbprefix*', $this->DBTABLEPREFIX, $name);
108
+                    $name = $this->platform->quoteIdentifier($name);
109
+                    $table = $schema->createTable($name);
110
+                    break;
111
+                case 'create':
112
+                case 'overwrite':
113
+                case 'charset':
114
+                    break;
115
+                case 'declaration':
116
+                    if (is_null($table)) {
117
+                        throw new \DomainException('Table declaration before table name');
118
+                    }
119
+                    $this->loadDeclaration($table, $child);
120
+                    break;
121
+                default:
122
+                    throw new \DomainException('Unknown element: ' . $child->getName());
123 123
 
124
-			}
125
-		}
126
-	}
124
+            }
125
+        }
126
+    }
127 127
 
128
-	/**
129
-	 * @param \Doctrine\DBAL\Schema\Table $table
130
-	 * @param \SimpleXMLElement $xml
131
-	 * @throws \DomainException
132
-	 */
133
-	private function loadDeclaration($table, $xml) {
134
-		foreach ($xml->children() as $child) {
135
-			/**
136
-			 * @var \SimpleXMLElement $child
137
-			 */
138
-			switch ($child->getName()) {
139
-				case 'field':
140
-					$this->loadField($table, $child);
141
-					break;
142
-				case 'index':
143
-					$this->loadIndex($table, $child);
144
-					break;
145
-				default:
146
-					throw new \DomainException('Unknown element: ' . $child->getName());
128
+    /**
129
+     * @param \Doctrine\DBAL\Schema\Table $table
130
+     * @param \SimpleXMLElement $xml
131
+     * @throws \DomainException
132
+     */
133
+    private function loadDeclaration($table, $xml) {
134
+        foreach ($xml->children() as $child) {
135
+            /**
136
+             * @var \SimpleXMLElement $child
137
+             */
138
+            switch ($child->getName()) {
139
+                case 'field':
140
+                    $this->loadField($table, $child);
141
+                    break;
142
+                case 'index':
143
+                    $this->loadIndex($table, $child);
144
+                    break;
145
+                default:
146
+                    throw new \DomainException('Unknown element: ' . $child->getName());
147 147
 
148
-			}
149
-		}
150
-	}
148
+            }
149
+        }
150
+    }
151 151
 
152
-	/**
153
-	 * @param \Doctrine\DBAL\Schema\Table $table
154
-	 * @param \SimpleXMLElement $xml
155
-	 * @throws \DomainException
156
-	 */
157
-	private function loadField($table, $xml) {
158
-		$options = [ 'notnull' => false ];
159
-		foreach ($xml->children() as $child) {
160
-			/**
161
-			 * @var \SimpleXMLElement $child
162
-			 */
163
-			switch ($child->getName()) {
164
-				case 'name':
165
-					$name = (string)$child;
166
-					$name = $this->platform->quoteIdentifier($name);
167
-					break;
168
-				case 'type':
169
-					$type = (string)$child;
170
-					switch ($type) {
171
-						case 'text':
172
-							$type = 'string';
173
-							break;
174
-						case 'clob':
175
-							$type = 'text';
176
-							break;
177
-						case 'timestamp':
178
-							$type = 'datetime';
179
-							break;
180
-						case 'numeric':
181
-							$type = 'decimal';
182
-							break;
183
-					}
184
-					break;
185
-				case 'length':
186
-					$length = (string)$child;
187
-					$options['length'] = $length;
188
-					break;
189
-				case 'unsigned':
190
-					$unsigned = $this->asBool($child);
191
-					$options['unsigned'] = $unsigned;
192
-					break;
193
-				case 'notnull':
194
-					$notnull = $this->asBool($child);
195
-					$options['notnull'] = $notnull;
196
-					break;
197
-				case 'autoincrement':
198
-					$autoincrement = $this->asBool($child);
199
-					$options['autoincrement'] = $autoincrement;
200
-					break;
201
-				case 'default':
202
-					$default = (string)$child;
203
-					$options['default'] = $default;
204
-					break;
205
-				case 'comments':
206
-					$comment = (string)$child;
207
-					$options['comment'] = $comment;
208
-					break;
209
-				case 'primary':
210
-					$primary = $this->asBool($child);
211
-					$options['primary'] = $primary;
212
-					break;
213
-				case 'precision':
214
-					$precision = (string)$child;
215
-					$options['precision'] = $precision;
216
-					break;
217
-				case 'scale':
218
-					$scale = (string)$child;
219
-					$options['scale'] = $scale;
220
-					break;
221
-				default:
222
-					throw new \DomainException('Unknown element: ' . $child->getName());
152
+    /**
153
+     * @param \Doctrine\DBAL\Schema\Table $table
154
+     * @param \SimpleXMLElement $xml
155
+     * @throws \DomainException
156
+     */
157
+    private function loadField($table, $xml) {
158
+        $options = [ 'notnull' => false ];
159
+        foreach ($xml->children() as $child) {
160
+            /**
161
+             * @var \SimpleXMLElement $child
162
+             */
163
+            switch ($child->getName()) {
164
+                case 'name':
165
+                    $name = (string)$child;
166
+                    $name = $this->platform->quoteIdentifier($name);
167
+                    break;
168
+                case 'type':
169
+                    $type = (string)$child;
170
+                    switch ($type) {
171
+                        case 'text':
172
+                            $type = 'string';
173
+                            break;
174
+                        case 'clob':
175
+                            $type = 'text';
176
+                            break;
177
+                        case 'timestamp':
178
+                            $type = 'datetime';
179
+                            break;
180
+                        case 'numeric':
181
+                            $type = 'decimal';
182
+                            break;
183
+                    }
184
+                    break;
185
+                case 'length':
186
+                    $length = (string)$child;
187
+                    $options['length'] = $length;
188
+                    break;
189
+                case 'unsigned':
190
+                    $unsigned = $this->asBool($child);
191
+                    $options['unsigned'] = $unsigned;
192
+                    break;
193
+                case 'notnull':
194
+                    $notnull = $this->asBool($child);
195
+                    $options['notnull'] = $notnull;
196
+                    break;
197
+                case 'autoincrement':
198
+                    $autoincrement = $this->asBool($child);
199
+                    $options['autoincrement'] = $autoincrement;
200
+                    break;
201
+                case 'default':
202
+                    $default = (string)$child;
203
+                    $options['default'] = $default;
204
+                    break;
205
+                case 'comments':
206
+                    $comment = (string)$child;
207
+                    $options['comment'] = $comment;
208
+                    break;
209
+                case 'primary':
210
+                    $primary = $this->asBool($child);
211
+                    $options['primary'] = $primary;
212
+                    break;
213
+                case 'precision':
214
+                    $precision = (string)$child;
215
+                    $options['precision'] = $precision;
216
+                    break;
217
+                case 'scale':
218
+                    $scale = (string)$child;
219
+                    $options['scale'] = $scale;
220
+                    break;
221
+                default:
222
+                    throw new \DomainException('Unknown element: ' . $child->getName());
223 223
 
224
-			}
225
-		}
226
-		if (isset($name) && isset($type)) {
227
-			if (isset($options['default']) && empty($options['default'])) {
228
-				if (empty($options['notnull']) || !$options['notnull']) {
229
-					unset($options['default']);
230
-					$options['notnull'] = false;
231
-				} else {
232
-					$options['default'] = '';
233
-				}
234
-				if ($type == 'integer' || $type == 'decimal') {
235
-					$options['default'] = 0;
236
-				} elseif ($type == 'boolean') {
237
-					$options['default'] = false;
238
-				}
239
-				if (!empty($options['autoincrement']) && $options['autoincrement']) {
240
-					unset($options['default']);
241
-				}
242
-			}
243
-			if ($type === 'integer' && isset($options['default'])) {
244
-				$options['default'] = (int)$options['default'];
245
-			}
246
-			if ($type === 'integer' && isset($options['length'])) {
247
-				$length = $options['length'];
248
-				if ($length < 4) {
249
-					$type = 'smallint';
250
-				} elseif ($length > 4) {
251
-					$type = 'bigint';
252
-				}
253
-			}
254
-			if ($type === 'boolean' && isset($options['default'])) {
255
-				$options['default'] = $this->asBool($options['default']);
256
-			}
257
-			if (!empty($options['autoincrement'])
258
-				&& !empty($options['notnull'])
259
-			) {
260
-				$options['primary'] = true;
261
-			}
224
+            }
225
+        }
226
+        if (isset($name) && isset($type)) {
227
+            if (isset($options['default']) && empty($options['default'])) {
228
+                if (empty($options['notnull']) || !$options['notnull']) {
229
+                    unset($options['default']);
230
+                    $options['notnull'] = false;
231
+                } else {
232
+                    $options['default'] = '';
233
+                }
234
+                if ($type == 'integer' || $type == 'decimal') {
235
+                    $options['default'] = 0;
236
+                } elseif ($type == 'boolean') {
237
+                    $options['default'] = false;
238
+                }
239
+                if (!empty($options['autoincrement']) && $options['autoincrement']) {
240
+                    unset($options['default']);
241
+                }
242
+            }
243
+            if ($type === 'integer' && isset($options['default'])) {
244
+                $options['default'] = (int)$options['default'];
245
+            }
246
+            if ($type === 'integer' && isset($options['length'])) {
247
+                $length = $options['length'];
248
+                if ($length < 4) {
249
+                    $type = 'smallint';
250
+                } elseif ($length > 4) {
251
+                    $type = 'bigint';
252
+                }
253
+            }
254
+            if ($type === 'boolean' && isset($options['default'])) {
255
+                $options['default'] = $this->asBool($options['default']);
256
+            }
257
+            if (!empty($options['autoincrement'])
258
+                && !empty($options['notnull'])
259
+            ) {
260
+                $options['primary'] = true;
261
+            }
262 262
 
263
-			# not used anymore in the options argument
264
-			# see https://github.com/doctrine/dbal/commit/138eb85234a1faeaa2e6a32cd7bcc66bb51c64e8#diff-300f55366adb50a32a40882ebdc95c163b141f64cba5f45f20bda04a907b3eb3L82
265
-			# therefore it's read before and then unset right before the addColumn call
266
-			$setPrimaryKey = false;
267
-			if (!empty($options['primary']) && $options['primary']) {
268
-				$setPrimaryKey = true;
269
-			}
270
-			unset($options['primary']);
271
-			$table->addColumn($name, $type, $options);
272
-			if ($setPrimaryKey) {
273
-				$table->setPrimaryKey([$name]);
274
-			}
275
-		}
276
-	}
263
+            # not used anymore in the options argument
264
+            # see https://github.com/doctrine/dbal/commit/138eb85234a1faeaa2e6a32cd7bcc66bb51c64e8#diff-300f55366adb50a32a40882ebdc95c163b141f64cba5f45f20bda04a907b3eb3L82
265
+            # therefore it's read before and then unset right before the addColumn call
266
+            $setPrimaryKey = false;
267
+            if (!empty($options['primary']) && $options['primary']) {
268
+                $setPrimaryKey = true;
269
+            }
270
+            unset($options['primary']);
271
+            $table->addColumn($name, $type, $options);
272
+            if ($setPrimaryKey) {
273
+                $table->setPrimaryKey([$name]);
274
+            }
275
+        }
276
+    }
277 277
 
278
-	/**
279
-	 * @param \Doctrine\DBAL\Schema\Table $table
280
-	 * @param \SimpleXMLElement $xml
281
-	 * @throws \DomainException
282
-	 */
283
-	private function loadIndex($table, $xml) {
284
-		$name = null;
285
-		$fields = [];
286
-		foreach ($xml->children() as $child) {
287
-			/**
288
-			 * @var \SimpleXMLElement $child
289
-			 */
290
-			switch ($child->getName()) {
291
-				case 'name':
292
-					$name = (string)$child;
293
-					break;
294
-				case 'primary':
295
-					$primary = $this->asBool($child);
296
-					break;
297
-				case 'unique':
298
-					$unique = $this->asBool($child);
299
-					break;
300
-				case 'field':
301
-					foreach ($child->children() as $field) {
302
-						/**
303
-						 * @var \SimpleXMLElement $field
304
-						 */
305
-						switch ($field->getName()) {
306
-							case 'name':
307
-								$field_name = (string)$field;
308
-								$field_name = $this->platform->quoteIdentifier($field_name);
309
-								$fields[] = $field_name;
310
-								break;
311
-							case 'sorting':
312
-								break;
313
-							default:
314
-								throw new \DomainException('Unknown element: ' . $field->getName());
278
+    /**
279
+     * @param \Doctrine\DBAL\Schema\Table $table
280
+     * @param \SimpleXMLElement $xml
281
+     * @throws \DomainException
282
+     */
283
+    private function loadIndex($table, $xml) {
284
+        $name = null;
285
+        $fields = [];
286
+        foreach ($xml->children() as $child) {
287
+            /**
288
+             * @var \SimpleXMLElement $child
289
+             */
290
+            switch ($child->getName()) {
291
+                case 'name':
292
+                    $name = (string)$child;
293
+                    break;
294
+                case 'primary':
295
+                    $primary = $this->asBool($child);
296
+                    break;
297
+                case 'unique':
298
+                    $unique = $this->asBool($child);
299
+                    break;
300
+                case 'field':
301
+                    foreach ($child->children() as $field) {
302
+                        /**
303
+                         * @var \SimpleXMLElement $field
304
+                         */
305
+                        switch ($field->getName()) {
306
+                            case 'name':
307
+                                $field_name = (string)$field;
308
+                                $field_name = $this->platform->quoteIdentifier($field_name);
309
+                                $fields[] = $field_name;
310
+                                break;
311
+                            case 'sorting':
312
+                                break;
313
+                            default:
314
+                                throw new \DomainException('Unknown element: ' . $field->getName());
315 315
 
316
-						}
317
-					}
318
-					break;
319
-				default:
320
-					throw new \DomainException('Unknown element: ' . $child->getName());
316
+                        }
317
+                    }
318
+                    break;
319
+                default:
320
+                    throw new \DomainException('Unknown element: ' . $child->getName());
321 321
 
322
-			}
323
-		}
324
-		if (!empty($fields)) {
325
-			if (isset($primary) && $primary) {
326
-				if ($table->hasPrimaryKey()) {
327
-					return;
328
-				}
329
-				$table->setPrimaryKey($fields, $name);
330
-			} else {
331
-				if (isset($unique) && $unique) {
332
-					$table->addUniqueIndex($fields, $name);
333
-				} else {
334
-					$table->addIndex($fields, $name);
335
-				}
336
-			}
337
-		} else {
338
-			throw new \DomainException('Empty index definition: ' . $name . ' options:' . print_r($fields, true));
339
-		}
340
-	}
322
+            }
323
+        }
324
+        if (!empty($fields)) {
325
+            if (isset($primary) && $primary) {
326
+                if ($table->hasPrimaryKey()) {
327
+                    return;
328
+                }
329
+                $table->setPrimaryKey($fields, $name);
330
+            } else {
331
+                if (isset($unique) && $unique) {
332
+                    $table->addUniqueIndex($fields, $name);
333
+                } else {
334
+                    $table->addIndex($fields, $name);
335
+                }
336
+            }
337
+        } else {
338
+            throw new \DomainException('Empty index definition: ' . $name . ' options:' . print_r($fields, true));
339
+        }
340
+    }
341 341
 
342
-	/**
343
-	 * @param \SimpleXMLElement|string $xml
344
-	 * @return bool
345
-	 */
346
-	private function asBool($xml) {
347
-		$result = (string)$xml;
348
-		if ($result == 'true') {
349
-			$result = true;
350
-		} elseif ($result == 'false') {
351
-			$result = false;
352
-		}
353
-		return (bool)$result;
354
-	}
342
+    /**
343
+     * @param \SimpleXMLElement|string $xml
344
+     * @return bool
345
+     */
346
+    private function asBool($xml) {
347
+        $result = (string)$xml;
348
+        if ($result == 'true') {
349
+            $result = true;
350
+        } elseif ($result == 'false') {
351
+            $result = false;
352
+        }
353
+        return (bool)$result;
354
+    }
355 355
 }
Please login to merge, or discard this patch.
lib/private/DB/SchemaWrapper.php 1 patch
Indentation   +101 added lines, -101 removed lines patch added patch discarded remove patch
@@ -29,105 +29,105 @@
 block discarded – undo
29 29
 
30 30
 class SchemaWrapper implements ISchemaWrapper {
31 31
 
32
-	/** @var Connection */
33
-	protected $connection;
34
-
35
-	/** @var Schema */
36
-	protected $schema;
37
-
38
-	/** @var array */
39
-	protected $tablesToDelete = [];
40
-
41
-	public function __construct(Connection $connection) {
42
-		$this->connection = $connection;
43
-		$this->schema = $this->connection->createSchema();
44
-	}
45
-
46
-	public function getWrappedSchema() {
47
-		return $this->schema;
48
-	}
49
-
50
-	public function performDropTableCalls() {
51
-		foreach ($this->tablesToDelete as $tableName => $true) {
52
-			$this->connection->dropTable($tableName);
53
-			unset($this->tablesToDelete[$tableName]);
54
-		}
55
-	}
56
-
57
-	/**
58
-	 * Gets all table names
59
-	 *
60
-	 * @return array
61
-	 */
62
-	public function getTableNamesWithoutPrefix() {
63
-		$tableNames = $this->schema->getTableNames();
64
-		return array_map(function ($tableName) {
65
-			if (strpos($tableName, $this->connection->getPrefix()) === 0) {
66
-				return substr($tableName, strlen($this->connection->getPrefix()));
67
-			}
68
-
69
-			return $tableName;
70
-		}, $tableNames);
71
-	}
72
-
73
-	// Overwritten methods
74
-
75
-	/**
76
-	 * @return array
77
-	 */
78
-	public function getTableNames() {
79
-		return $this->schema->getTableNames();
80
-	}
81
-
82
-	/**
83
-	 * @param string $tableName
84
-	 *
85
-	 * @return \Doctrine\DBAL\Schema\Table
86
-	 * @throws \Doctrine\DBAL\Schema\SchemaException
87
-	 */
88
-	public function getTable($tableName) {
89
-		return $this->schema->getTable($this->connection->getPrefix() . $tableName);
90
-	}
91
-
92
-	/**
93
-	 * Does this schema have a table with the given name?
94
-	 *
95
-	 * @param string $tableName
96
-	 *
97
-	 * @return boolean
98
-	 */
99
-	public function hasTable($tableName) {
100
-		return $this->schema->hasTable($this->connection->getPrefix() . $tableName);
101
-	}
102
-
103
-	/**
104
-	 * Creates a new table.
105
-	 *
106
-	 * @param string $tableName
107
-	 * @return \Doctrine\DBAL\Schema\Table
108
-	 */
109
-	public function createTable($tableName) {
110
-		unset($this->tablesToDelete[$tableName]);
111
-		return $this->schema->createTable($this->connection->getPrefix() . $tableName);
112
-	}
113
-
114
-	/**
115
-	 * Drops a table from the schema.
116
-	 *
117
-	 * @param string $tableName
118
-	 * @return \Doctrine\DBAL\Schema\Schema
119
-	 */
120
-	public function dropTable($tableName) {
121
-		$this->tablesToDelete[$tableName] = true;
122
-		return $this->schema->dropTable($this->connection->getPrefix() . $tableName);
123
-	}
124
-
125
-	/**
126
-	 * Gets all tables of this schema.
127
-	 *
128
-	 * @return \Doctrine\DBAL\Schema\Table[]
129
-	 */
130
-	public function getTables() {
131
-		return $this->schema->getTables();
132
-	}
32
+    /** @var Connection */
33
+    protected $connection;
34
+
35
+    /** @var Schema */
36
+    protected $schema;
37
+
38
+    /** @var array */
39
+    protected $tablesToDelete = [];
40
+
41
+    public function __construct(Connection $connection) {
42
+        $this->connection = $connection;
43
+        $this->schema = $this->connection->createSchema();
44
+    }
45
+
46
+    public function getWrappedSchema() {
47
+        return $this->schema;
48
+    }
49
+
50
+    public function performDropTableCalls() {
51
+        foreach ($this->tablesToDelete as $tableName => $true) {
52
+            $this->connection->dropTable($tableName);
53
+            unset($this->tablesToDelete[$tableName]);
54
+        }
55
+    }
56
+
57
+    /**
58
+     * Gets all table names
59
+     *
60
+     * @return array
61
+     */
62
+    public function getTableNamesWithoutPrefix() {
63
+        $tableNames = $this->schema->getTableNames();
64
+        return array_map(function ($tableName) {
65
+            if (strpos($tableName, $this->connection->getPrefix()) === 0) {
66
+                return substr($tableName, strlen($this->connection->getPrefix()));
67
+            }
68
+
69
+            return $tableName;
70
+        }, $tableNames);
71
+    }
72
+
73
+    // Overwritten methods
74
+
75
+    /**
76
+     * @return array
77
+     */
78
+    public function getTableNames() {
79
+        return $this->schema->getTableNames();
80
+    }
81
+
82
+    /**
83
+     * @param string $tableName
84
+     *
85
+     * @return \Doctrine\DBAL\Schema\Table
86
+     * @throws \Doctrine\DBAL\Schema\SchemaException
87
+     */
88
+    public function getTable($tableName) {
89
+        return $this->schema->getTable($this->connection->getPrefix() . $tableName);
90
+    }
91
+
92
+    /**
93
+     * Does this schema have a table with the given name?
94
+     *
95
+     * @param string $tableName
96
+     *
97
+     * @return boolean
98
+     */
99
+    public function hasTable($tableName) {
100
+        return $this->schema->hasTable($this->connection->getPrefix() . $tableName);
101
+    }
102
+
103
+    /**
104
+     * Creates a new table.
105
+     *
106
+     * @param string $tableName
107
+     * @return \Doctrine\DBAL\Schema\Table
108
+     */
109
+    public function createTable($tableName) {
110
+        unset($this->tablesToDelete[$tableName]);
111
+        return $this->schema->createTable($this->connection->getPrefix() . $tableName);
112
+    }
113
+
114
+    /**
115
+     * Drops a table from the schema.
116
+     *
117
+     * @param string $tableName
118
+     * @return \Doctrine\DBAL\Schema\Schema
119
+     */
120
+    public function dropTable($tableName) {
121
+        $this->tablesToDelete[$tableName] = true;
122
+        return $this->schema->dropTable($this->connection->getPrefix() . $tableName);
123
+    }
124
+
125
+    /**
126
+     * Gets all tables of this schema.
127
+     *
128
+     * @return \Doctrine\DBAL\Schema\Table[]
129
+     */
130
+    public function getTables() {
131
+        return $this->schema->getTables();
132
+    }
133 133
 }
Please login to merge, or discard this patch.
lib/private/DB/AdapterSqlite.php 1 patch
Indentation   +61 added lines, -61 removed lines patch added patch discarded remove patch
@@ -31,70 +31,70 @@
 block discarded – undo
31 31
 
32 32
 class AdapterSqlite extends Adapter {
33 33
 
34
-	/**
35
-	 * @param string $tableName
36
-	 */
37
-	public function lockTable($tableName) {
38
-		$this->conn->executeUpdate('BEGIN EXCLUSIVE TRANSACTION');
39
-	}
34
+    /**
35
+     * @param string $tableName
36
+     */
37
+    public function lockTable($tableName) {
38
+        $this->conn->executeUpdate('BEGIN EXCLUSIVE TRANSACTION');
39
+    }
40 40
 
41
-	public function unlockTable() {
42
-		$this->conn->executeUpdate('COMMIT TRANSACTION');
43
-	}
41
+    public function unlockTable() {
42
+        $this->conn->executeUpdate('COMMIT TRANSACTION');
43
+    }
44 44
 
45
-	public function fixupStatement($statement) {
46
-		$statement = preg_replace('/`(\w+)` ILIKE \?/', 'LOWER($1) LIKE LOWER(?)', $statement);
47
-		$statement = str_replace('`', '"', $statement);
48
-		$statement = str_ireplace('NOW()', 'datetime(\'now\')', $statement);
49
-		$statement = str_ireplace('GREATEST(', 'MAX(', $statement);
50
-		$statement = str_ireplace('UNIX_TIMESTAMP()', 'strftime(\'%s\',\'now\')', $statement);
51
-		return $statement;
52
-	}
45
+    public function fixupStatement($statement) {
46
+        $statement = preg_replace('/`(\w+)` ILIKE \?/', 'LOWER($1) LIKE LOWER(?)', $statement);
47
+        $statement = str_replace('`', '"', $statement);
48
+        $statement = str_ireplace('NOW()', 'datetime(\'now\')', $statement);
49
+        $statement = str_ireplace('GREATEST(', 'MAX(', $statement);
50
+        $statement = str_ireplace('UNIX_TIMESTAMP()', 'strftime(\'%s\',\'now\')', $statement);
51
+        return $statement;
52
+    }
53 53
 
54
-	/**
55
-	 * Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
56
-	 * it is needed that there is also a unique constraint on the values. Then this method will
57
-	 * catch the exception and return 0.
58
-	 *
59
-	 * @param string $table The table name (will replace *PREFIX* with the actual prefix)
60
-	 * @param array $input data that should be inserted into the table  (column name => value)
61
-	 * @param array|null $compare List of values that should be checked for "if not exists"
62
-	 *				If this is null or an empty array, all keys of $input will be compared
63
-	 *				Please note: text fields (clob) must not be used in the compare array
64
-	 * @return int number of inserted rows
65
-	 * @throws \Doctrine\DBAL\Exception
66
-	 * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371
67
-	 */
68
-	public function insertIfNotExist($table, $input, array $compare = null) {
69
-		if (empty($compare)) {
70
-			$compare = array_keys($input);
71
-		}
72
-		$fieldList = '`' . implode('`,`', array_keys($input)) . '`';
73
-		$query = "INSERT INTO `$table` ($fieldList) SELECT "
74
-			. str_repeat('?,', count($input) - 1).'? '
75
-			. " WHERE NOT EXISTS (SELECT 1 FROM `$table` WHERE ";
54
+    /**
55
+     * Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
56
+     * it is needed that there is also a unique constraint on the values. Then this method will
57
+     * catch the exception and return 0.
58
+     *
59
+     * @param string $table The table name (will replace *PREFIX* with the actual prefix)
60
+     * @param array $input data that should be inserted into the table  (column name => value)
61
+     * @param array|null $compare List of values that should be checked for "if not exists"
62
+     *				If this is null or an empty array, all keys of $input will be compared
63
+     *				Please note: text fields (clob) must not be used in the compare array
64
+     * @return int number of inserted rows
65
+     * @throws \Doctrine\DBAL\Exception
66
+     * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371
67
+     */
68
+    public function insertIfNotExist($table, $input, array $compare = null) {
69
+        if (empty($compare)) {
70
+            $compare = array_keys($input);
71
+        }
72
+        $fieldList = '`' . implode('`,`', array_keys($input)) . '`';
73
+        $query = "INSERT INTO `$table` ($fieldList) SELECT "
74
+            . str_repeat('?,', count($input) - 1).'? '
75
+            . " WHERE NOT EXISTS (SELECT 1 FROM `$table` WHERE ";
76 76
 
77
-		$inserts = array_values($input);
78
-		foreach ($compare as $key) {
79
-			$query .= '`' . $key . '`';
80
-			if (is_null($input[$key])) {
81
-				$query .= ' IS NULL AND ';
82
-			} else {
83
-				$inserts[] = $input[$key];
84
-				$query .= ' = ? AND ';
85
-			}
86
-		}
87
-		$query = substr($query, 0, -5);
88
-		$query .= ')';
77
+        $inserts = array_values($input);
78
+        foreach ($compare as $key) {
79
+            $query .= '`' . $key . '`';
80
+            if (is_null($input[$key])) {
81
+                $query .= ' IS NULL AND ';
82
+            } else {
83
+                $inserts[] = $input[$key];
84
+                $query .= ' = ? AND ';
85
+            }
86
+        }
87
+        $query = substr($query, 0, -5);
88
+        $query .= ')';
89 89
 
90
-		try {
91
-			return $this->conn->executeUpdate($query, $inserts);
92
-		} catch (UniqueConstraintViolationException $e) {
93
-			// if this is thrown then a concurrent insert happened between the insert and the sub-select in the insert, that should have avoided it
94
-			// it's fine to ignore this then
95
-			//
96
-			// more discussions about this can be found at https://github.com/nextcloud/server/pull/12315
97
-			return 0;
98
-		}
99
-	}
90
+        try {
91
+            return $this->conn->executeUpdate($query, $inserts);
92
+        } catch (UniqueConstraintViolationException $e) {
93
+            // if this is thrown then a concurrent insert happened between the insert and the sub-select in the insert, that should have avoided it
94
+            // it's fine to ignore this then
95
+            //
96
+            // more discussions about this can be found at https://github.com/nextcloud/server/pull/12315
97
+            return 0;
98
+        }
99
+    }
100 100
 }
Please login to merge, or discard this patch.
lib/private/Updater.php 1 patch
Indentation   +528 added lines, -528 removed lines patch added patch discarded remove patch
@@ -58,532 +58,532 @@
 block discarded – undo
58 58
  */
59 59
 class Updater extends BasicEmitter {
60 60
 
61
-	/** @var ILogger $log */
62
-	private $log;
63
-
64
-	/** @var IConfig */
65
-	private $config;
66
-
67
-	/** @var Checker */
68
-	private $checker;
69
-
70
-	/** @var Installer */
71
-	private $installer;
72
-
73
-	private $logLevelNames = [
74
-		0 => 'Debug',
75
-		1 => 'Info',
76
-		2 => 'Warning',
77
-		3 => 'Error',
78
-		4 => 'Fatal',
79
-	];
80
-
81
-	/**
82
-	 * @param IConfig $config
83
-	 * @param Checker $checker
84
-	 * @param ILogger $log
85
-	 * @param Installer $installer
86
-	 */
87
-	public function __construct(IConfig $config,
88
-								Checker $checker,
89
-								ILogger $log = null,
90
-								Installer $installer) {
91
-		$this->log = $log;
92
-		$this->config = $config;
93
-		$this->checker = $checker;
94
-		$this->installer = $installer;
95
-	}
96
-
97
-	/**
98
-	 * runs the update actions in maintenance mode, does not upgrade the source files
99
-	 * except the main .htaccess file
100
-	 *
101
-	 * @return bool true if the operation succeeded, false otherwise
102
-	 */
103
-	public function upgrade() {
104
-		$this->emitRepairEvents();
105
-		$this->logAllEvents();
106
-
107
-		$logLevel = $this->config->getSystemValue('loglevel', ILogger::WARN);
108
-		$this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
109
-		$this->config->setSystemValue('loglevel', ILogger::DEBUG);
110
-
111
-		$wasMaintenanceModeEnabled = $this->config->getSystemValueBool('maintenance');
112
-
113
-		if (!$wasMaintenanceModeEnabled) {
114
-			$this->config->setSystemValue('maintenance', true);
115
-			$this->emit('\OC\Updater', 'maintenanceEnabled');
116
-		}
117
-
118
-		// Clear CAN_INSTALL file if not on git
119
-		if (\OC_Util::getChannel() !== 'git' && is_file(\OC::$configDir.'/CAN_INSTALL')) {
120
-			if (!unlink(\OC::$configDir . '/CAN_INSTALL')) {
121
-				$this->log->error('Could not cleanup CAN_INSTALL from your config folder. Please remove this file manually.');
122
-			}
123
-		}
124
-
125
-		$installedVersion = $this->config->getSystemValue('version', '0.0.0');
126
-		$currentVersion = implode('.', \OCP\Util::getVersion());
127
-
128
-		$this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, ['app' => 'core']);
129
-
130
-		$success = true;
131
-		try {
132
-			$this->doUpgrade($currentVersion, $installedVersion);
133
-		} catch (HintException $exception) {
134
-			$this->log->logException($exception, ['app' => 'core']);
135
-			$this->emit('\OC\Updater', 'failure', [$exception->getMessage() . ': ' .$exception->getHint()]);
136
-			$success = false;
137
-		} catch (\Exception $exception) {
138
-			$this->log->logException($exception, ['app' => 'core']);
139
-			$this->emit('\OC\Updater', 'failure', [get_class($exception) . ': ' .$exception->getMessage()]);
140
-			$success = false;
141
-		}
142
-
143
-		$this->emit('\OC\Updater', 'updateEnd', [$success]);
144
-
145
-		if (!$wasMaintenanceModeEnabled && $success) {
146
-			$this->config->setSystemValue('maintenance', false);
147
-			$this->emit('\OC\Updater', 'maintenanceDisabled');
148
-		} else {
149
-			$this->emit('\OC\Updater', 'maintenanceActive');
150
-		}
151
-
152
-		$this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
153
-		$this->config->setSystemValue('loglevel', $logLevel);
154
-		$this->config->setSystemValue('installed', true);
155
-
156
-		return $success;
157
-	}
158
-
159
-	/**
160
-	 * Return version from which this version is allowed to upgrade from
161
-	 *
162
-	 * @return array allowed previous versions per vendor
163
-	 */
164
-	private function getAllowedPreviousVersions() {
165
-		// this should really be a JSON file
166
-		require \OC::$SERVERROOT . '/version.php';
167
-		/** @var array $OC_VersionCanBeUpgradedFrom */
168
-		return $OC_VersionCanBeUpgradedFrom;
169
-	}
170
-
171
-	/**
172
-	 * Return vendor from which this version was published
173
-	 *
174
-	 * @return string Get the vendor
175
-	 */
176
-	private function getVendor() {
177
-		// this should really be a JSON file
178
-		require \OC::$SERVERROOT . '/version.php';
179
-		/** @var string $vendor */
180
-		return (string) $vendor;
181
-	}
182
-
183
-	/**
184
-	 * Whether an upgrade to a specified version is possible
185
-	 * @param string $oldVersion
186
-	 * @param string $newVersion
187
-	 * @param array $allowedPreviousVersions
188
-	 * @return bool
189
-	 */
190
-	public function isUpgradePossible($oldVersion, $newVersion, array $allowedPreviousVersions) {
191
-		$version = explode('.', $oldVersion);
192
-		$majorMinor = $version[0] . '.' . $version[1];
193
-
194
-		$currentVendor = $this->config->getAppValue('core', 'vendor', '');
195
-
196
-		// Vendor was not set correctly on install, so we have to white-list known versions
197
-		if ($currentVendor === '' && (
198
-			isset($allowedPreviousVersions['owncloud'][$oldVersion]) ||
199
-			isset($allowedPreviousVersions['owncloud'][$majorMinor])
200
-		)) {
201
-			$currentVendor = 'owncloud';
202
-			$this->config->setAppValue('core', 'vendor', $currentVendor);
203
-		}
204
-
205
-		if ($currentVendor === 'nextcloud') {
206
-			return isset($allowedPreviousVersions[$currentVendor][$majorMinor])
207
-				&& (version_compare($oldVersion, $newVersion, '<=') ||
208
-					$this->config->getSystemValue('debug', false));
209
-		}
210
-
211
-		// Check if the instance can be migrated
212
-		return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) ||
213
-			isset($allowedPreviousVersions[$currentVendor][$oldVersion]);
214
-	}
215
-
216
-	/**
217
-	 * runs the update actions in maintenance mode, does not upgrade the source files
218
-	 * except the main .htaccess file
219
-	 *
220
-	 * @param string $currentVersion current version to upgrade to
221
-	 * @param string $installedVersion previous version from which to upgrade from
222
-	 *
223
-	 * @throws \Exception
224
-	 */
225
-	private function doUpgrade($currentVersion, $installedVersion) {
226
-		// Stop update if the update is over several major versions
227
-		$allowedPreviousVersions = $this->getAllowedPreviousVersions();
228
-		if (!$this->isUpgradePossible($installedVersion, $currentVersion, $allowedPreviousVersions)) {
229
-			throw new \Exception('Updates between multiple major versions and downgrades are unsupported.');
230
-		}
231
-
232
-		// Update .htaccess files
233
-		try {
234
-			Setup::updateHtaccess();
235
-			Setup::protectDataDirectory();
236
-		} catch (\Exception $e) {
237
-			throw new \Exception($e->getMessage());
238
-		}
239
-
240
-		// create empty file in data dir, so we can later find
241
-		// out that this is indeed an ownCloud data directory
242
-		// (in case it didn't exist before)
243
-		file_put_contents($this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', '');
244
-
245
-		// pre-upgrade repairs
246
-		$repair = new Repair(Repair::getBeforeUpgradeRepairSteps(), \OC::$server->getEventDispatcher());
247
-		$repair->run();
248
-
249
-		$this->doCoreUpgrade();
250
-
251
-		try {
252
-			// TODO: replace with the new repair step mechanism https://github.com/owncloud/core/pull/24378
253
-			Setup::installBackgroundJobs();
254
-		} catch (\Exception $e) {
255
-			throw new \Exception($e->getMessage());
256
-		}
257
-
258
-		// update all shipped apps
259
-		$this->checkAppsRequirements();
260
-		$this->doAppUpgrade();
261
-
262
-		// Update the appfetchers version so it downloads the correct list from the appstore
263
-		\OC::$server->getAppFetcher()->setVersion($currentVersion);
264
-
265
-		// upgrade appstore apps
266
-		$this->upgradeAppStoreApps(\OC::$server->getAppManager()->getInstalledApps());
267
-		$autoDisabledApps = \OC::$server->getAppManager()->getAutoDisabledApps();
268
-		$this->upgradeAppStoreApps($autoDisabledApps, true);
269
-
270
-		// install new shipped apps on upgrade
271
-		OC_App::loadApps(['authentication']);
272
-		$errors = Installer::installShippedApps(true);
273
-		foreach ($errors as $appId => $exception) {
274
-			/** @var \Exception $exception */
275
-			$this->log->logException($exception, ['app' => $appId]);
276
-			$this->emit('\OC\Updater', 'failure', [$appId . ': ' . $exception->getMessage()]);
277
-		}
278
-
279
-		// post-upgrade repairs
280
-		$repair = new Repair(Repair::getRepairSteps(), \OC::$server->getEventDispatcher());
281
-		$repair->run();
282
-
283
-		//Invalidate update feed
284
-		$this->config->setAppValue('core', 'lastupdatedat', 0);
285
-
286
-		// Check for code integrity if not disabled
287
-		if (\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) {
288
-			$this->emit('\OC\Updater', 'startCheckCodeIntegrity');
289
-			$this->checker->runInstanceVerification();
290
-			$this->emit('\OC\Updater', 'finishedCheckCodeIntegrity');
291
-		}
292
-
293
-		// only set the final version if everything went well
294
-		$this->config->setSystemValue('version', implode('.', Util::getVersion()));
295
-		$this->config->setAppValue('core', 'vendor', $this->getVendor());
296
-	}
297
-
298
-	protected function doCoreUpgrade() {
299
-		$this->emit('\OC\Updater', 'dbUpgradeBefore');
300
-
301
-		// execute core migrations
302
-		$ms = new MigrationService('core', \OC::$server->get(Connection::class));
303
-		$ms->migrate();
304
-
305
-		$this->emit('\OC\Updater', 'dbUpgrade');
306
-	}
307
-
308
-	/**
309
-	 * upgrades all apps within a major ownCloud upgrade. Also loads "priority"
310
-	 * (types authentication, filesystem, logging, in that order) afterwards.
311
-	 *
312
-	 * @throws NeedsUpdateException
313
-	 */
314
-	protected function doAppUpgrade() {
315
-		$apps = \OC_App::getEnabledApps();
316
-		$priorityTypes = ['authentication', 'filesystem', 'logging'];
317
-		$pseudoOtherType = 'other';
318
-		$stacks = [$pseudoOtherType => []];
319
-
320
-		foreach ($apps as $appId) {
321
-			$priorityType = false;
322
-			foreach ($priorityTypes as $type) {
323
-				if (!isset($stacks[$type])) {
324
-					$stacks[$type] = [];
325
-				}
326
-				if (\OC_App::isType($appId, [$type])) {
327
-					$stacks[$type][] = $appId;
328
-					$priorityType = true;
329
-					break;
330
-				}
331
-			}
332
-			if (!$priorityType) {
333
-				$stacks[$pseudoOtherType][] = $appId;
334
-			}
335
-		}
336
-		foreach ($stacks as $type => $stack) {
337
-			foreach ($stack as $appId) {
338
-				if (\OC_App::shouldUpgrade($appId)) {
339
-					$this->emit('\OC\Updater', 'appUpgradeStarted', [$appId, \OC_App::getAppVersion($appId)]);
340
-					\OC_App::updateApp($appId);
341
-					$this->emit('\OC\Updater', 'appUpgrade', [$appId, \OC_App::getAppVersion($appId)]);
342
-				}
343
-				if ($type !== $pseudoOtherType) {
344
-					// load authentication, filesystem and logging apps after
345
-					// upgrading them. Other apps my need to rely on modifying
346
-					// user and/or filesystem aspects.
347
-					\OC_App::loadApp($appId);
348
-				}
349
-			}
350
-		}
351
-	}
352
-
353
-	/**
354
-	 * check if the current enabled apps are compatible with the current
355
-	 * ownCloud version. disable them if not.
356
-	 * This is important if you upgrade ownCloud and have non ported 3rd
357
-	 * party apps installed.
358
-	 *
359
-	 * @return array
360
-	 * @throws \Exception
361
-	 */
362
-	private function checkAppsRequirements() {
363
-		$isCoreUpgrade = $this->isCodeUpgrade();
364
-		$apps = OC_App::getEnabledApps();
365
-		$version = implode('.', Util::getVersion());
366
-		$disabledApps = [];
367
-		$appManager = \OC::$server->getAppManager();
368
-		foreach ($apps as $app) {
369
-			// check if the app is compatible with this version of Nextcloud
370
-			$info = OC_App::getAppInfo($app);
371
-			if ($info === null || !OC_App::isAppCompatible($version, $info)) {
372
-				if ($appManager->isShipped($app)) {
373
-					throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update');
374
-				}
375
-				\OC::$server->getAppManager()->disableApp($app, true);
376
-				$this->emit('\OC\Updater', 'incompatibleAppDisabled', [$app]);
377
-			}
378
-			// no need to disable any app in case this is a non-core upgrade
379
-			if (!$isCoreUpgrade) {
380
-				continue;
381
-			}
382
-			// shipped apps will remain enabled
383
-			if ($appManager->isShipped($app)) {
384
-				continue;
385
-			}
386
-			// authentication and session apps will remain enabled as well
387
-			if (OC_App::isType($app, ['session', 'authentication'])) {
388
-				continue;
389
-			}
390
-		}
391
-		return $disabledApps;
392
-	}
393
-
394
-	/**
395
-	 * @return bool
396
-	 */
397
-	private function isCodeUpgrade() {
398
-		$installedVersion = $this->config->getSystemValue('version', '0.0.0');
399
-		$currentVersion = implode('.', Util::getVersion());
400
-		if (version_compare($currentVersion, $installedVersion, '>')) {
401
-			return true;
402
-		}
403
-		return false;
404
-	}
405
-
406
-	/**
407
-	 * @param array $disabledApps
408
-	 * @param bool $reenable
409
-	 * @throws \Exception
410
-	 */
411
-	private function upgradeAppStoreApps(array $disabledApps, $reenable = false) {
412
-		foreach ($disabledApps as $app) {
413
-			try {
414
-				$this->emit('\OC\Updater', 'checkAppStoreAppBefore', [$app]);
415
-				if ($this->installer->isUpdateAvailable($app)) {
416
-					$this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]);
417
-					$this->installer->updateAppstoreApp($app);
418
-				}
419
-				$this->emit('\OC\Updater', 'checkAppStoreApp', [$app]);
420
-
421
-				if ($reenable) {
422
-					$ocApp = new \OC_App();
423
-					$ocApp->enable($app);
424
-				}
425
-			} catch (\Exception $ex) {
426
-				$this->log->logException($ex, ['app' => 'core']);
427
-			}
428
-		}
429
-	}
430
-
431
-	/**
432
-	 * Forward messages emitted by the repair routine
433
-	 */
434
-	private function emitRepairEvents() {
435
-		$dispatcher = \OC::$server->getEventDispatcher();
436
-		$dispatcher->addListener('\OC\Repair::warning', function ($event) {
437
-			if ($event instanceof GenericEvent) {
438
-				$this->emit('\OC\Updater', 'repairWarning', $event->getArguments());
439
-			}
440
-		});
441
-		$dispatcher->addListener('\OC\Repair::error', function ($event) {
442
-			if ($event instanceof GenericEvent) {
443
-				$this->emit('\OC\Updater', 'repairError', $event->getArguments());
444
-			}
445
-		});
446
-		$dispatcher->addListener('\OC\Repair::info', function ($event) {
447
-			if ($event instanceof GenericEvent) {
448
-				$this->emit('\OC\Updater', 'repairInfo', $event->getArguments());
449
-			}
450
-		});
451
-		$dispatcher->addListener('\OC\Repair::step', function ($event) {
452
-			if ($event instanceof GenericEvent) {
453
-				$this->emit('\OC\Updater', 'repairStep', $event->getArguments());
454
-			}
455
-		});
456
-	}
457
-
458
-	private function logAllEvents() {
459
-		$log = $this->log;
460
-
461
-		$dispatcher = \OC::$server->getEventDispatcher();
462
-		$dispatcher->addListener('\OC\DB\Migrator::executeSql', function ($event) use ($log) {
463
-			if (!$event instanceof GenericEvent) {
464
-				return;
465
-			}
466
-			$log->info('\OC\DB\Migrator::executeSql: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
467
-		});
468
-		$dispatcher->addListener('\OC\DB\Migrator::checkTable', function ($event) use ($log) {
469
-			if (!$event instanceof GenericEvent) {
470
-				return;
471
-			}
472
-			$log->info('\OC\DB\Migrator::checkTable: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
473
-		});
474
-
475
-		$repairListener = function ($event) use ($log) {
476
-			if (!$event instanceof GenericEvent) {
477
-				return;
478
-			}
479
-			switch ($event->getSubject()) {
480
-				case '\OC\Repair::startProgress':
481
-					$log->info('\OC\Repair::startProgress: Starting ... ' . $event->getArgument(1) .  ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
482
-					break;
483
-				case '\OC\Repair::advance':
484
-					$desc = $event->getArgument(1);
485
-					if (empty($desc)) {
486
-						$desc = '';
487
-					}
488
-					$log->info('\OC\Repair::advance: ' . $desc . ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
489
-
490
-					break;
491
-				case '\OC\Repair::finishProgress':
492
-					$log->info('\OC\Repair::finishProgress', ['app' => 'updater']);
493
-					break;
494
-				case '\OC\Repair::step':
495
-					$log->info('\OC\Repair::step: Repair step: ' . $event->getArgument(0), ['app' => 'updater']);
496
-					break;
497
-				case '\OC\Repair::info':
498
-					$log->info('\OC\Repair::info: Repair info: ' . $event->getArgument(0), ['app' => 'updater']);
499
-					break;
500
-				case '\OC\Repair::warning':
501
-					$log->warning('\OC\Repair::warning: Repair warning: ' . $event->getArgument(0), ['app' => 'updater']);
502
-					break;
503
-				case '\OC\Repair::error':
504
-					$log->error('\OC\Repair::error: Repair error: ' . $event->getArgument(0), ['app' => 'updater']);
505
-					break;
506
-			}
507
-		};
508
-
509
-		$dispatcher->addListener('\OC\Repair::startProgress', $repairListener);
510
-		$dispatcher->addListener('\OC\Repair::advance', $repairListener);
511
-		$dispatcher->addListener('\OC\Repair::finishProgress', $repairListener);
512
-		$dispatcher->addListener('\OC\Repair::step', $repairListener);
513
-		$dispatcher->addListener('\OC\Repair::info', $repairListener);
514
-		$dispatcher->addListener('\OC\Repair::warning', $repairListener);
515
-		$dispatcher->addListener('\OC\Repair::error', $repairListener);
516
-
517
-
518
-		$this->listen('\OC\Updater', 'maintenanceEnabled', function () use ($log) {
519
-			$log->info('\OC\Updater::maintenanceEnabled: Turned on maintenance mode', ['app' => 'updater']);
520
-		});
521
-		$this->listen('\OC\Updater', 'maintenanceDisabled', function () use ($log) {
522
-			$log->info('\OC\Updater::maintenanceDisabled: Turned off maintenance mode', ['app' => 'updater']);
523
-		});
524
-		$this->listen('\OC\Updater', 'maintenanceActive', function () use ($log) {
525
-			$log->info('\OC\Updater::maintenanceActive: Maintenance mode is kept active', ['app' => 'updater']);
526
-		});
527
-		$this->listen('\OC\Updater', 'updateEnd', function ($success) use ($log) {
528
-			if ($success) {
529
-				$log->info('\OC\Updater::updateEnd: Update successful', ['app' => 'updater']);
530
-			} else {
531
-				$log->error('\OC\Updater::updateEnd: Update failed', ['app' => 'updater']);
532
-			}
533
-		});
534
-		$this->listen('\OC\Updater', 'dbUpgradeBefore', function () use ($log) {
535
-			$log->info('\OC\Updater::dbUpgradeBefore: Updating database schema', ['app' => 'updater']);
536
-		});
537
-		$this->listen('\OC\Updater', 'dbUpgrade', function () use ($log) {
538
-			$log->info('\OC\Updater::dbUpgrade: Updated database', ['app' => 'updater']);
539
-		});
540
-		$this->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use ($log) {
541
-			$log->info('\OC\Updater::dbSimulateUpgradeBefore: Checking whether the database schema can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
542
-		});
543
-		$this->listen('\OC\Updater', 'dbSimulateUpgrade', function () use ($log) {
544
-			$log->info('\OC\Updater::dbSimulateUpgrade: Checked database schema update', ['app' => 'updater']);
545
-		});
546
-		$this->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use ($log) {
547
-			$log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: ' . $app, ['app' => 'updater']);
548
-		});
549
-		$this->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use ($log) {
550
-			$log->info('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "' . $app . '" in appstore', ['app' => 'updater']);
551
-		});
552
-		$this->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use ($log) {
553
-			$log->info('\OC\Updater::upgradeAppStoreApp: Update app "' . $app . '" from appstore', ['app' => 'updater']);
554
-		});
555
-		$this->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use ($log) {
556
-			$log->info('\OC\Updater::checkAppStoreApp: Checked for update of app "' . $app . '" in appstore', ['app' => 'updater']);
557
-		});
558
-		$this->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($log) {
559
-			$log->info('\OC\Updater::appUpgradeCheckBefore: Checking updates of apps', ['app' => 'updater']);
560
-		});
561
-		$this->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($log) {
562
-			$log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <' . $app . '> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
563
-		});
564
-		$this->listen('\OC\Updater', 'appUpgradeCheck', function () use ($log) {
565
-			$log->info('\OC\Updater::appUpgradeCheck: Checked database schema update for apps', ['app' => 'updater']);
566
-		});
567
-		$this->listen('\OC\Updater', 'appUpgradeStarted', function ($app) use ($log) {
568
-			$log->info('\OC\Updater::appUpgradeStarted: Updating <' . $app . '> ...', ['app' => 'updater']);
569
-		});
570
-		$this->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($log) {
571
-			$log->info('\OC\Updater::appUpgrade: Updated <' . $app . '> to ' . $version, ['app' => 'updater']);
572
-		});
573
-		$this->listen('\OC\Updater', 'failure', function ($message) use ($log) {
574
-			$log->error('\OC\Updater::failure: ' . $message, ['app' => 'updater']);
575
-		});
576
-		$this->listen('\OC\Updater', 'setDebugLogLevel', function () use ($log) {
577
-			$log->info('\OC\Updater::setDebugLogLevel: Set log level to debug', ['app' => 'updater']);
578
-		});
579
-		$this->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use ($log) {
580
-			$log->info('\OC\Updater::resetLogLevel: Reset log level to ' . $logLevelName . '(' . $logLevel . ')', ['app' => 'updater']);
581
-		});
582
-		$this->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use ($log) {
583
-			$log->info('\OC\Updater::startCheckCodeIntegrity: Starting code integrity check...', ['app' => 'updater']);
584
-		});
585
-		$this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use ($log) {
586
-			$log->info('\OC\Updater::finishedCheckCodeIntegrity: Finished code integrity check', ['app' => 'updater']);
587
-		});
588
-	}
61
+    /** @var ILogger $log */
62
+    private $log;
63
+
64
+    /** @var IConfig */
65
+    private $config;
66
+
67
+    /** @var Checker */
68
+    private $checker;
69
+
70
+    /** @var Installer */
71
+    private $installer;
72
+
73
+    private $logLevelNames = [
74
+        0 => 'Debug',
75
+        1 => 'Info',
76
+        2 => 'Warning',
77
+        3 => 'Error',
78
+        4 => 'Fatal',
79
+    ];
80
+
81
+    /**
82
+     * @param IConfig $config
83
+     * @param Checker $checker
84
+     * @param ILogger $log
85
+     * @param Installer $installer
86
+     */
87
+    public function __construct(IConfig $config,
88
+                                Checker $checker,
89
+                                ILogger $log = null,
90
+                                Installer $installer) {
91
+        $this->log = $log;
92
+        $this->config = $config;
93
+        $this->checker = $checker;
94
+        $this->installer = $installer;
95
+    }
96
+
97
+    /**
98
+     * runs the update actions in maintenance mode, does not upgrade the source files
99
+     * except the main .htaccess file
100
+     *
101
+     * @return bool true if the operation succeeded, false otherwise
102
+     */
103
+    public function upgrade() {
104
+        $this->emitRepairEvents();
105
+        $this->logAllEvents();
106
+
107
+        $logLevel = $this->config->getSystemValue('loglevel', ILogger::WARN);
108
+        $this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
109
+        $this->config->setSystemValue('loglevel', ILogger::DEBUG);
110
+
111
+        $wasMaintenanceModeEnabled = $this->config->getSystemValueBool('maintenance');
112
+
113
+        if (!$wasMaintenanceModeEnabled) {
114
+            $this->config->setSystemValue('maintenance', true);
115
+            $this->emit('\OC\Updater', 'maintenanceEnabled');
116
+        }
117
+
118
+        // Clear CAN_INSTALL file if not on git
119
+        if (\OC_Util::getChannel() !== 'git' && is_file(\OC::$configDir.'/CAN_INSTALL')) {
120
+            if (!unlink(\OC::$configDir . '/CAN_INSTALL')) {
121
+                $this->log->error('Could not cleanup CAN_INSTALL from your config folder. Please remove this file manually.');
122
+            }
123
+        }
124
+
125
+        $installedVersion = $this->config->getSystemValue('version', '0.0.0');
126
+        $currentVersion = implode('.', \OCP\Util::getVersion());
127
+
128
+        $this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, ['app' => 'core']);
129
+
130
+        $success = true;
131
+        try {
132
+            $this->doUpgrade($currentVersion, $installedVersion);
133
+        } catch (HintException $exception) {
134
+            $this->log->logException($exception, ['app' => 'core']);
135
+            $this->emit('\OC\Updater', 'failure', [$exception->getMessage() . ': ' .$exception->getHint()]);
136
+            $success = false;
137
+        } catch (\Exception $exception) {
138
+            $this->log->logException($exception, ['app' => 'core']);
139
+            $this->emit('\OC\Updater', 'failure', [get_class($exception) . ': ' .$exception->getMessage()]);
140
+            $success = false;
141
+        }
142
+
143
+        $this->emit('\OC\Updater', 'updateEnd', [$success]);
144
+
145
+        if (!$wasMaintenanceModeEnabled && $success) {
146
+            $this->config->setSystemValue('maintenance', false);
147
+            $this->emit('\OC\Updater', 'maintenanceDisabled');
148
+        } else {
149
+            $this->emit('\OC\Updater', 'maintenanceActive');
150
+        }
151
+
152
+        $this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
153
+        $this->config->setSystemValue('loglevel', $logLevel);
154
+        $this->config->setSystemValue('installed', true);
155
+
156
+        return $success;
157
+    }
158
+
159
+    /**
160
+     * Return version from which this version is allowed to upgrade from
161
+     *
162
+     * @return array allowed previous versions per vendor
163
+     */
164
+    private function getAllowedPreviousVersions() {
165
+        // this should really be a JSON file
166
+        require \OC::$SERVERROOT . '/version.php';
167
+        /** @var array $OC_VersionCanBeUpgradedFrom */
168
+        return $OC_VersionCanBeUpgradedFrom;
169
+    }
170
+
171
+    /**
172
+     * Return vendor from which this version was published
173
+     *
174
+     * @return string Get the vendor
175
+     */
176
+    private function getVendor() {
177
+        // this should really be a JSON file
178
+        require \OC::$SERVERROOT . '/version.php';
179
+        /** @var string $vendor */
180
+        return (string) $vendor;
181
+    }
182
+
183
+    /**
184
+     * Whether an upgrade to a specified version is possible
185
+     * @param string $oldVersion
186
+     * @param string $newVersion
187
+     * @param array $allowedPreviousVersions
188
+     * @return bool
189
+     */
190
+    public function isUpgradePossible($oldVersion, $newVersion, array $allowedPreviousVersions) {
191
+        $version = explode('.', $oldVersion);
192
+        $majorMinor = $version[0] . '.' . $version[1];
193
+
194
+        $currentVendor = $this->config->getAppValue('core', 'vendor', '');
195
+
196
+        // Vendor was not set correctly on install, so we have to white-list known versions
197
+        if ($currentVendor === '' && (
198
+            isset($allowedPreviousVersions['owncloud'][$oldVersion]) ||
199
+            isset($allowedPreviousVersions['owncloud'][$majorMinor])
200
+        )) {
201
+            $currentVendor = 'owncloud';
202
+            $this->config->setAppValue('core', 'vendor', $currentVendor);
203
+        }
204
+
205
+        if ($currentVendor === 'nextcloud') {
206
+            return isset($allowedPreviousVersions[$currentVendor][$majorMinor])
207
+                && (version_compare($oldVersion, $newVersion, '<=') ||
208
+                    $this->config->getSystemValue('debug', false));
209
+        }
210
+
211
+        // Check if the instance can be migrated
212
+        return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) ||
213
+            isset($allowedPreviousVersions[$currentVendor][$oldVersion]);
214
+    }
215
+
216
+    /**
217
+     * runs the update actions in maintenance mode, does not upgrade the source files
218
+     * except the main .htaccess file
219
+     *
220
+     * @param string $currentVersion current version to upgrade to
221
+     * @param string $installedVersion previous version from which to upgrade from
222
+     *
223
+     * @throws \Exception
224
+     */
225
+    private function doUpgrade($currentVersion, $installedVersion) {
226
+        // Stop update if the update is over several major versions
227
+        $allowedPreviousVersions = $this->getAllowedPreviousVersions();
228
+        if (!$this->isUpgradePossible($installedVersion, $currentVersion, $allowedPreviousVersions)) {
229
+            throw new \Exception('Updates between multiple major versions and downgrades are unsupported.');
230
+        }
231
+
232
+        // Update .htaccess files
233
+        try {
234
+            Setup::updateHtaccess();
235
+            Setup::protectDataDirectory();
236
+        } catch (\Exception $e) {
237
+            throw new \Exception($e->getMessage());
238
+        }
239
+
240
+        // create empty file in data dir, so we can later find
241
+        // out that this is indeed an ownCloud data directory
242
+        // (in case it didn't exist before)
243
+        file_put_contents($this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', '');
244
+
245
+        // pre-upgrade repairs
246
+        $repair = new Repair(Repair::getBeforeUpgradeRepairSteps(), \OC::$server->getEventDispatcher());
247
+        $repair->run();
248
+
249
+        $this->doCoreUpgrade();
250
+
251
+        try {
252
+            // TODO: replace with the new repair step mechanism https://github.com/owncloud/core/pull/24378
253
+            Setup::installBackgroundJobs();
254
+        } catch (\Exception $e) {
255
+            throw new \Exception($e->getMessage());
256
+        }
257
+
258
+        // update all shipped apps
259
+        $this->checkAppsRequirements();
260
+        $this->doAppUpgrade();
261
+
262
+        // Update the appfetchers version so it downloads the correct list from the appstore
263
+        \OC::$server->getAppFetcher()->setVersion($currentVersion);
264
+
265
+        // upgrade appstore apps
266
+        $this->upgradeAppStoreApps(\OC::$server->getAppManager()->getInstalledApps());
267
+        $autoDisabledApps = \OC::$server->getAppManager()->getAutoDisabledApps();
268
+        $this->upgradeAppStoreApps($autoDisabledApps, true);
269
+
270
+        // install new shipped apps on upgrade
271
+        OC_App::loadApps(['authentication']);
272
+        $errors = Installer::installShippedApps(true);
273
+        foreach ($errors as $appId => $exception) {
274
+            /** @var \Exception $exception */
275
+            $this->log->logException($exception, ['app' => $appId]);
276
+            $this->emit('\OC\Updater', 'failure', [$appId . ': ' . $exception->getMessage()]);
277
+        }
278
+
279
+        // post-upgrade repairs
280
+        $repair = new Repair(Repair::getRepairSteps(), \OC::$server->getEventDispatcher());
281
+        $repair->run();
282
+
283
+        //Invalidate update feed
284
+        $this->config->setAppValue('core', 'lastupdatedat', 0);
285
+
286
+        // Check for code integrity if not disabled
287
+        if (\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) {
288
+            $this->emit('\OC\Updater', 'startCheckCodeIntegrity');
289
+            $this->checker->runInstanceVerification();
290
+            $this->emit('\OC\Updater', 'finishedCheckCodeIntegrity');
291
+        }
292
+
293
+        // only set the final version if everything went well
294
+        $this->config->setSystemValue('version', implode('.', Util::getVersion()));
295
+        $this->config->setAppValue('core', 'vendor', $this->getVendor());
296
+    }
297
+
298
+    protected function doCoreUpgrade() {
299
+        $this->emit('\OC\Updater', 'dbUpgradeBefore');
300
+
301
+        // execute core migrations
302
+        $ms = new MigrationService('core', \OC::$server->get(Connection::class));
303
+        $ms->migrate();
304
+
305
+        $this->emit('\OC\Updater', 'dbUpgrade');
306
+    }
307
+
308
+    /**
309
+     * upgrades all apps within a major ownCloud upgrade. Also loads "priority"
310
+     * (types authentication, filesystem, logging, in that order) afterwards.
311
+     *
312
+     * @throws NeedsUpdateException
313
+     */
314
+    protected function doAppUpgrade() {
315
+        $apps = \OC_App::getEnabledApps();
316
+        $priorityTypes = ['authentication', 'filesystem', 'logging'];
317
+        $pseudoOtherType = 'other';
318
+        $stacks = [$pseudoOtherType => []];
319
+
320
+        foreach ($apps as $appId) {
321
+            $priorityType = false;
322
+            foreach ($priorityTypes as $type) {
323
+                if (!isset($stacks[$type])) {
324
+                    $stacks[$type] = [];
325
+                }
326
+                if (\OC_App::isType($appId, [$type])) {
327
+                    $stacks[$type][] = $appId;
328
+                    $priorityType = true;
329
+                    break;
330
+                }
331
+            }
332
+            if (!$priorityType) {
333
+                $stacks[$pseudoOtherType][] = $appId;
334
+            }
335
+        }
336
+        foreach ($stacks as $type => $stack) {
337
+            foreach ($stack as $appId) {
338
+                if (\OC_App::shouldUpgrade($appId)) {
339
+                    $this->emit('\OC\Updater', 'appUpgradeStarted', [$appId, \OC_App::getAppVersion($appId)]);
340
+                    \OC_App::updateApp($appId);
341
+                    $this->emit('\OC\Updater', 'appUpgrade', [$appId, \OC_App::getAppVersion($appId)]);
342
+                }
343
+                if ($type !== $pseudoOtherType) {
344
+                    // load authentication, filesystem and logging apps after
345
+                    // upgrading them. Other apps my need to rely on modifying
346
+                    // user and/or filesystem aspects.
347
+                    \OC_App::loadApp($appId);
348
+                }
349
+            }
350
+        }
351
+    }
352
+
353
+    /**
354
+     * check if the current enabled apps are compatible with the current
355
+     * ownCloud version. disable them if not.
356
+     * This is important if you upgrade ownCloud and have non ported 3rd
357
+     * party apps installed.
358
+     *
359
+     * @return array
360
+     * @throws \Exception
361
+     */
362
+    private function checkAppsRequirements() {
363
+        $isCoreUpgrade = $this->isCodeUpgrade();
364
+        $apps = OC_App::getEnabledApps();
365
+        $version = implode('.', Util::getVersion());
366
+        $disabledApps = [];
367
+        $appManager = \OC::$server->getAppManager();
368
+        foreach ($apps as $app) {
369
+            // check if the app is compatible with this version of Nextcloud
370
+            $info = OC_App::getAppInfo($app);
371
+            if ($info === null || !OC_App::isAppCompatible($version, $info)) {
372
+                if ($appManager->isShipped($app)) {
373
+                    throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update');
374
+                }
375
+                \OC::$server->getAppManager()->disableApp($app, true);
376
+                $this->emit('\OC\Updater', 'incompatibleAppDisabled', [$app]);
377
+            }
378
+            // no need to disable any app in case this is a non-core upgrade
379
+            if (!$isCoreUpgrade) {
380
+                continue;
381
+            }
382
+            // shipped apps will remain enabled
383
+            if ($appManager->isShipped($app)) {
384
+                continue;
385
+            }
386
+            // authentication and session apps will remain enabled as well
387
+            if (OC_App::isType($app, ['session', 'authentication'])) {
388
+                continue;
389
+            }
390
+        }
391
+        return $disabledApps;
392
+    }
393
+
394
+    /**
395
+     * @return bool
396
+     */
397
+    private function isCodeUpgrade() {
398
+        $installedVersion = $this->config->getSystemValue('version', '0.0.0');
399
+        $currentVersion = implode('.', Util::getVersion());
400
+        if (version_compare($currentVersion, $installedVersion, '>')) {
401
+            return true;
402
+        }
403
+        return false;
404
+    }
405
+
406
+    /**
407
+     * @param array $disabledApps
408
+     * @param bool $reenable
409
+     * @throws \Exception
410
+     */
411
+    private function upgradeAppStoreApps(array $disabledApps, $reenable = false) {
412
+        foreach ($disabledApps as $app) {
413
+            try {
414
+                $this->emit('\OC\Updater', 'checkAppStoreAppBefore', [$app]);
415
+                if ($this->installer->isUpdateAvailable($app)) {
416
+                    $this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]);
417
+                    $this->installer->updateAppstoreApp($app);
418
+                }
419
+                $this->emit('\OC\Updater', 'checkAppStoreApp', [$app]);
420
+
421
+                if ($reenable) {
422
+                    $ocApp = new \OC_App();
423
+                    $ocApp->enable($app);
424
+                }
425
+            } catch (\Exception $ex) {
426
+                $this->log->logException($ex, ['app' => 'core']);
427
+            }
428
+        }
429
+    }
430
+
431
+    /**
432
+     * Forward messages emitted by the repair routine
433
+     */
434
+    private function emitRepairEvents() {
435
+        $dispatcher = \OC::$server->getEventDispatcher();
436
+        $dispatcher->addListener('\OC\Repair::warning', function ($event) {
437
+            if ($event instanceof GenericEvent) {
438
+                $this->emit('\OC\Updater', 'repairWarning', $event->getArguments());
439
+            }
440
+        });
441
+        $dispatcher->addListener('\OC\Repair::error', function ($event) {
442
+            if ($event instanceof GenericEvent) {
443
+                $this->emit('\OC\Updater', 'repairError', $event->getArguments());
444
+            }
445
+        });
446
+        $dispatcher->addListener('\OC\Repair::info', function ($event) {
447
+            if ($event instanceof GenericEvent) {
448
+                $this->emit('\OC\Updater', 'repairInfo', $event->getArguments());
449
+            }
450
+        });
451
+        $dispatcher->addListener('\OC\Repair::step', function ($event) {
452
+            if ($event instanceof GenericEvent) {
453
+                $this->emit('\OC\Updater', 'repairStep', $event->getArguments());
454
+            }
455
+        });
456
+    }
457
+
458
+    private function logAllEvents() {
459
+        $log = $this->log;
460
+
461
+        $dispatcher = \OC::$server->getEventDispatcher();
462
+        $dispatcher->addListener('\OC\DB\Migrator::executeSql', function ($event) use ($log) {
463
+            if (!$event instanceof GenericEvent) {
464
+                return;
465
+            }
466
+            $log->info('\OC\DB\Migrator::executeSql: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
467
+        });
468
+        $dispatcher->addListener('\OC\DB\Migrator::checkTable', function ($event) use ($log) {
469
+            if (!$event instanceof GenericEvent) {
470
+                return;
471
+            }
472
+            $log->info('\OC\DB\Migrator::checkTable: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
473
+        });
474
+
475
+        $repairListener = function ($event) use ($log) {
476
+            if (!$event instanceof GenericEvent) {
477
+                return;
478
+            }
479
+            switch ($event->getSubject()) {
480
+                case '\OC\Repair::startProgress':
481
+                    $log->info('\OC\Repair::startProgress: Starting ... ' . $event->getArgument(1) .  ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
482
+                    break;
483
+                case '\OC\Repair::advance':
484
+                    $desc = $event->getArgument(1);
485
+                    if (empty($desc)) {
486
+                        $desc = '';
487
+                    }
488
+                    $log->info('\OC\Repair::advance: ' . $desc . ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
489
+
490
+                    break;
491
+                case '\OC\Repair::finishProgress':
492
+                    $log->info('\OC\Repair::finishProgress', ['app' => 'updater']);
493
+                    break;
494
+                case '\OC\Repair::step':
495
+                    $log->info('\OC\Repair::step: Repair step: ' . $event->getArgument(0), ['app' => 'updater']);
496
+                    break;
497
+                case '\OC\Repair::info':
498
+                    $log->info('\OC\Repair::info: Repair info: ' . $event->getArgument(0), ['app' => 'updater']);
499
+                    break;
500
+                case '\OC\Repair::warning':
501
+                    $log->warning('\OC\Repair::warning: Repair warning: ' . $event->getArgument(0), ['app' => 'updater']);
502
+                    break;
503
+                case '\OC\Repair::error':
504
+                    $log->error('\OC\Repair::error: Repair error: ' . $event->getArgument(0), ['app' => 'updater']);
505
+                    break;
506
+            }
507
+        };
508
+
509
+        $dispatcher->addListener('\OC\Repair::startProgress', $repairListener);
510
+        $dispatcher->addListener('\OC\Repair::advance', $repairListener);
511
+        $dispatcher->addListener('\OC\Repair::finishProgress', $repairListener);
512
+        $dispatcher->addListener('\OC\Repair::step', $repairListener);
513
+        $dispatcher->addListener('\OC\Repair::info', $repairListener);
514
+        $dispatcher->addListener('\OC\Repair::warning', $repairListener);
515
+        $dispatcher->addListener('\OC\Repair::error', $repairListener);
516
+
517
+
518
+        $this->listen('\OC\Updater', 'maintenanceEnabled', function () use ($log) {
519
+            $log->info('\OC\Updater::maintenanceEnabled: Turned on maintenance mode', ['app' => 'updater']);
520
+        });
521
+        $this->listen('\OC\Updater', 'maintenanceDisabled', function () use ($log) {
522
+            $log->info('\OC\Updater::maintenanceDisabled: Turned off maintenance mode', ['app' => 'updater']);
523
+        });
524
+        $this->listen('\OC\Updater', 'maintenanceActive', function () use ($log) {
525
+            $log->info('\OC\Updater::maintenanceActive: Maintenance mode is kept active', ['app' => 'updater']);
526
+        });
527
+        $this->listen('\OC\Updater', 'updateEnd', function ($success) use ($log) {
528
+            if ($success) {
529
+                $log->info('\OC\Updater::updateEnd: Update successful', ['app' => 'updater']);
530
+            } else {
531
+                $log->error('\OC\Updater::updateEnd: Update failed', ['app' => 'updater']);
532
+            }
533
+        });
534
+        $this->listen('\OC\Updater', 'dbUpgradeBefore', function () use ($log) {
535
+            $log->info('\OC\Updater::dbUpgradeBefore: Updating database schema', ['app' => 'updater']);
536
+        });
537
+        $this->listen('\OC\Updater', 'dbUpgrade', function () use ($log) {
538
+            $log->info('\OC\Updater::dbUpgrade: Updated database', ['app' => 'updater']);
539
+        });
540
+        $this->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use ($log) {
541
+            $log->info('\OC\Updater::dbSimulateUpgradeBefore: Checking whether the database schema can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
542
+        });
543
+        $this->listen('\OC\Updater', 'dbSimulateUpgrade', function () use ($log) {
544
+            $log->info('\OC\Updater::dbSimulateUpgrade: Checked database schema update', ['app' => 'updater']);
545
+        });
546
+        $this->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use ($log) {
547
+            $log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: ' . $app, ['app' => 'updater']);
548
+        });
549
+        $this->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use ($log) {
550
+            $log->info('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "' . $app . '" in appstore', ['app' => 'updater']);
551
+        });
552
+        $this->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use ($log) {
553
+            $log->info('\OC\Updater::upgradeAppStoreApp: Update app "' . $app . '" from appstore', ['app' => 'updater']);
554
+        });
555
+        $this->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use ($log) {
556
+            $log->info('\OC\Updater::checkAppStoreApp: Checked for update of app "' . $app . '" in appstore', ['app' => 'updater']);
557
+        });
558
+        $this->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($log) {
559
+            $log->info('\OC\Updater::appUpgradeCheckBefore: Checking updates of apps', ['app' => 'updater']);
560
+        });
561
+        $this->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($log) {
562
+            $log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <' . $app . '> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
563
+        });
564
+        $this->listen('\OC\Updater', 'appUpgradeCheck', function () use ($log) {
565
+            $log->info('\OC\Updater::appUpgradeCheck: Checked database schema update for apps', ['app' => 'updater']);
566
+        });
567
+        $this->listen('\OC\Updater', 'appUpgradeStarted', function ($app) use ($log) {
568
+            $log->info('\OC\Updater::appUpgradeStarted: Updating <' . $app . '> ...', ['app' => 'updater']);
569
+        });
570
+        $this->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($log) {
571
+            $log->info('\OC\Updater::appUpgrade: Updated <' . $app . '> to ' . $version, ['app' => 'updater']);
572
+        });
573
+        $this->listen('\OC\Updater', 'failure', function ($message) use ($log) {
574
+            $log->error('\OC\Updater::failure: ' . $message, ['app' => 'updater']);
575
+        });
576
+        $this->listen('\OC\Updater', 'setDebugLogLevel', function () use ($log) {
577
+            $log->info('\OC\Updater::setDebugLogLevel: Set log level to debug', ['app' => 'updater']);
578
+        });
579
+        $this->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use ($log) {
580
+            $log->info('\OC\Updater::resetLogLevel: Reset log level to ' . $logLevelName . '(' . $logLevel . ')', ['app' => 'updater']);
581
+        });
582
+        $this->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use ($log) {
583
+            $log->info('\OC\Updater::startCheckCodeIntegrity: Starting code integrity check...', ['app' => 'updater']);
584
+        });
585
+        $this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use ($log) {
586
+            $log->info('\OC\Updater::finishedCheckCodeIntegrity: Finished code integrity check', ['app' => 'updater']);
587
+        });
588
+    }
589 589
 }
Please login to merge, or discard this patch.
lib/private/Tags.php 2 patches
Indentation   +784 added lines, -784 removed lines patch added patch discarded remove patch
@@ -51,788 +51,788 @@
 block discarded – undo
51 51
 
52 52
 class Tags implements ITags {
53 53
 
54
-	/**
55
-	 * Tags
56
-	 *
57
-	 * @var array
58
-	 */
59
-	private $tags = [];
60
-
61
-	/**
62
-	 * Used for storing objectid/categoryname pairs while rescanning.
63
-	 *
64
-	 * @var array
65
-	 */
66
-	private static $relations = [];
67
-
68
-	/**
69
-	 * Type
70
-	 *
71
-	 * @var string
72
-	 */
73
-	private $type;
74
-
75
-	/**
76
-	 * User
77
-	 *
78
-	 * @var string
79
-	 */
80
-	private $user;
81
-
82
-	/**
83
-	 * Are we including tags for shared items?
84
-	 *
85
-	 * @var bool
86
-	 */
87
-	private $includeShared = false;
88
-
89
-	/**
90
-	 * The current user, plus any owners of the items shared with the current
91
-	 * user, if $this->includeShared === true.
92
-	 *
93
-	 * @var array
94
-	 */
95
-	private $owners = [];
96
-
97
-	/**
98
-	 * The Mapper we're using to communicate our Tag objects to the database.
99
-	 *
100
-	 * @var TagMapper
101
-	 */
102
-	private $mapper;
103
-
104
-	/**
105
-	 * The sharing backend for objects of $this->type. Required if
106
-	 * $this->includeShared === true to determine ownership of items.
107
-	 *
108
-	 * @var \OCP\Share_Backend
109
-	 */
110
-	private $backend;
111
-
112
-	public const TAG_TABLE = '*PREFIX*vcategory';
113
-	public const RELATION_TABLE = '*PREFIX*vcategory_to_object';
114
-
115
-	/**
116
-	 * Constructor.
117
-	 *
118
-	 * @param TagMapper $mapper Instance of the TagMapper abstraction layer.
119
-	 * @param string $user The user whose data the object will operate on.
120
-	 * @param string $type The type of items for which tags will be loaded.
121
-	 * @param array $defaultTags Tags that should be created at construction.
122
-	 *
123
-	 * since 20.0.0 $includeShared isn't used anymore
124
-	 */
125
-	public function __construct(TagMapper $mapper, $user, $type, $defaultTags = []) {
126
-		$this->mapper = $mapper;
127
-		$this->user = $user;
128
-		$this->type = $type;
129
-		$this->owners = [$this->user];
130
-		$this->tags = $this->mapper->loadTags($this->owners, $this->type);
131
-
132
-		if (count($defaultTags) > 0 && count($this->tags) === 0) {
133
-			$this->addMultiple($defaultTags, true);
134
-		}
135
-	}
136
-
137
-	/**
138
-	 * Check if any tags are saved for this type and user.
139
-	 *
140
-	 * @return boolean
141
-	 */
142
-	public function isEmpty() {
143
-		return count($this->tags) === 0;
144
-	}
145
-
146
-	/**
147
-	 * Returns an array mapping a given tag's properties to its values:
148
-	 * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
149
-	 *
150
-	 * @param string $id The ID of the tag that is going to be mapped
151
-	 * @return array|false
152
-	 */
153
-	public function getTag($id) {
154
-		$key = $this->getTagById($id);
155
-		if ($key !== false) {
156
-			return $this->tagMap($this->tags[$key]);
157
-		}
158
-		return false;
159
-	}
160
-
161
-	/**
162
-	 * Get the tags for a specific user.
163
-	 *
164
-	 * This returns an array with maps containing each tag's properties:
165
-	 * [
166
-	 * 	['id' => 0, 'name' = 'First tag', 'owner' = 'User', 'type' => 'tagtype'],
167
-	 * 	['id' => 1, 'name' = 'Shared tag', 'owner' = 'Other user', 'type' => 'tagtype'],
168
-	 * ]
169
-	 *
170
-	 * @return array
171
-	 */
172
-	public function getTags() {
173
-		if (!count($this->tags)) {
174
-			return [];
175
-		}
176
-
177
-		usort($this->tags, function ($a, $b) {
178
-			return strnatcasecmp($a->getName(), $b->getName());
179
-		});
180
-		$tagMap = [];
181
-
182
-		foreach ($this->tags as $tag) {
183
-			if ($tag->getName() !== ITags::TAG_FAVORITE) {
184
-				$tagMap[] = $this->tagMap($tag);
185
-			}
186
-		}
187
-		return $tagMap;
188
-	}
189
-
190
-	/**
191
-	 * Return only the tags owned by the given user, omitting any tags shared
192
-	 * by other users.
193
-	 *
194
-	 * @param string $user The user whose tags are to be checked.
195
-	 * @return array An array of Tag objects.
196
-	 */
197
-	public function getTagsForUser($user) {
198
-		return array_filter($this->tags,
199
-			function ($tag) use ($user) {
200
-				return $tag->getOwner() === $user;
201
-			}
202
-		);
203
-	}
204
-
205
-	/**
206
-	 * Get the list of tags for the given ids.
207
-	 *
208
-	 * @param array $objIds array of object ids
209
-	 * @return array|boolean of tags id as key to array of tag names
210
-	 * or false if an error occurred
211
-	 */
212
-	public function getTagsForObjects(array $objIds) {
213
-		$entries = [];
214
-
215
-		try {
216
-			$conn = \OC::$server->getDatabaseConnection();
217
-			$chunks = array_chunk($objIds, 900, false);
218
-			foreach ($chunks as $chunk) {
219
-				$result = $conn->executeQuery(
220
-					'SELECT `category`, `categoryid`, `objid` ' .
221
-					'FROM `' . self::RELATION_TABLE . '` r, `' . self::TAG_TABLE . '` ' .
222
-					'WHERE `categoryid` = `id` AND `uid` = ? AND r.`type` = ? AND `objid` IN (?)',
223
-					[$this->user, $this->type, $chunk],
224
-					[null, null, IQueryBuilder::PARAM_INT_ARRAY]
225
-				);
226
-				while ($row = $result->fetch()) {
227
-					$objId = (int)$row['objid'];
228
-					if (!isset($entries[$objId])) {
229
-						$entries[$objId] = [];
230
-					}
231
-					$entries[$objId][] = $row['category'];
232
-				}
233
-			}
234
-		} catch (\Exception $e) {
235
-			\OC::$server->getLogger()->logException($e, [
236
-				'message' => __METHOD__,
237
-				'level' => ILogger::ERROR,
238
-				'app' => 'core',
239
-			]);
240
-			return false;
241
-		}
242
-
243
-		return $entries;
244
-	}
245
-
246
-	/**
247
-	 * Get the a list if items tagged with $tag.
248
-	 *
249
-	 * Throws an exception if the tag could not be found.
250
-	 *
251
-	 * @param string $tag Tag id or name.
252
-	 * @return array|false An array of object ids or false on error.
253
-	 * @throws \Exception
254
-	 */
255
-	public function getIdsForTag($tag) {
256
-		$result = null;
257
-		$tagId = false;
258
-		if (is_numeric($tag)) {
259
-			$tagId = $tag;
260
-		} elseif (is_string($tag)) {
261
-			$tag = trim($tag);
262
-			if ($tag === '') {
263
-				\OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
264
-				return false;
265
-			}
266
-			$tagId = $this->getTagId($tag);
267
-		}
268
-
269
-		if ($tagId === false) {
270
-			$l10n = \OC::$server->getL10N('core');
271
-			throw new \Exception(
272
-				$l10n->t('Could not find category "%s"', [$tag])
273
-			);
274
-		}
275
-
276
-		$ids = [];
277
-		$sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE
278
-			. '` WHERE `categoryid` = ?';
279
-
280
-		try {
281
-			$stmt = \OC_DB::prepare($sql);
282
-			$result = $stmt->execute([$tagId]);
283
-			if ($result === null) {
284
-				$stmt->closeCursor();
285
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
286
-				return false;
287
-			}
288
-		} catch (\Exception $e) {
289
-			\OC::$server->getLogger()->logException($e, [
290
-				'message' => __METHOD__,
291
-				'level' => ILogger::ERROR,
292
-				'app' => 'core',
293
-			]);
294
-			return false;
295
-		}
296
-
297
-		if (!is_null($result)) {
298
-			while ($row = $result->fetchRow()) {
299
-				$ids[] = (int)$row['objid'];
300
-			}
301
-			$result->closeCursor();
302
-		}
303
-
304
-		return $ids;
305
-	}
306
-
307
-	/**
308
-	 * Checks whether a tag is saved for the given user,
309
-	 * disregarding the ones shared with him or her.
310
-	 *
311
-	 * @param string $name The tag name to check for.
312
-	 * @param string $user The user whose tags are to be checked.
313
-	 * @return bool
314
-	 */
315
-	public function userHasTag($name, $user) {
316
-		$key = $this->array_searchi($name, $this->getTagsForUser($user));
317
-		return ($key !== false) ? $this->tags[$key]->getId() : false;
318
-	}
319
-
320
-	/**
321
-	 * Checks whether a tag is saved for or shared with the current user.
322
-	 *
323
-	 * @param string $name The tag name to check for.
324
-	 * @return bool
325
-	 */
326
-	public function hasTag($name) {
327
-		return $this->getTagId($name) !== false;
328
-	}
329
-
330
-	/**
331
-	 * Add a new tag.
332
-	 *
333
-	 * @param string $name A string with a name of the tag
334
-	 * @return false|int the id of the added tag or false on error.
335
-	 */
336
-	public function add($name) {
337
-		$name = trim($name);
338
-
339
-		if ($name === '') {
340
-			\OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
341
-			return false;
342
-		}
343
-		if ($this->userHasTag($name, $this->user)) {
344
-			\OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', ILogger::DEBUG);
345
-			return false;
346
-		}
347
-		try {
348
-			$tag = new Tag($this->user, $this->type, $name);
349
-			$tag = $this->mapper->insert($tag);
350
-			$this->tags[] = $tag;
351
-		} catch (\Exception $e) {
352
-			\OC::$server->getLogger()->logException($e, [
353
-				'message' => __METHOD__,
354
-				'level' => ILogger::ERROR,
355
-				'app' => 'core',
356
-			]);
357
-			return false;
358
-		}
359
-		\OCP\Util::writeLog('core', __METHOD__.', id: ' . $tag->getId(), ILogger::DEBUG);
360
-		return $tag->getId();
361
-	}
362
-
363
-	/**
364
-	 * Rename tag.
365
-	 *
366
-	 * @param string|integer $from The name or ID of the existing tag
367
-	 * @param string $to The new name of the tag.
368
-	 * @return bool
369
-	 */
370
-	public function rename($from, $to) {
371
-		$from = trim($from);
372
-		$to = trim($to);
373
-
374
-		if ($to === '' || $from === '') {
375
-			\OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
376
-			return false;
377
-		}
378
-
379
-		if (is_numeric($from)) {
380
-			$key = $this->getTagById($from);
381
-		} else {
382
-			$key = $this->getTagByName($from);
383
-		}
384
-		if ($key === false) {
385
-			\OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', ILogger::DEBUG);
386
-			return false;
387
-		}
388
-		$tag = $this->tags[$key];
389
-
390
-		if ($this->userHasTag($to, $tag->getOwner())) {
391
-			\OCP\Util::writeLog('core', __METHOD__.', A tag named ' . $to. ' already exists for user ' . $tag->getOwner() . '.', ILogger::DEBUG);
392
-			return false;
393
-		}
394
-
395
-		try {
396
-			$tag->setName($to);
397
-			$this->tags[$key] = $this->mapper->update($tag);
398
-		} catch (\Exception $e) {
399
-			\OC::$server->getLogger()->logException($e, [
400
-				'message' => __METHOD__,
401
-				'level' => ILogger::ERROR,
402
-				'app' => 'core',
403
-			]);
404
-			return false;
405
-		}
406
-		return true;
407
-	}
408
-
409
-	/**
410
-	 * Add a list of new tags.
411
-	 *
412
-	 * @param string[] $names A string with a name or an array of strings containing
413
-	 * the name(s) of the tag(s) to add.
414
-	 * @param bool $sync When true, save the tags
415
-	 * @param int|null $id int Optional object id to add to this|these tag(s)
416
-	 * @return bool Returns false on error.
417
-	 */
418
-	public function addMultiple($names, $sync = false, $id = null) {
419
-		if (!is_array($names)) {
420
-			$names = [$names];
421
-		}
422
-		$names = array_map('trim', $names);
423
-		array_filter($names);
424
-
425
-		$newones = [];
426
-		foreach ($names as $name) {
427
-			if (!$this->hasTag($name) && $name !== '') {
428
-				$newones[] = new Tag($this->user, $this->type, $name);
429
-			}
430
-			if (!is_null($id)) {
431
-				// Insert $objectid, $categoryid  pairs if not exist.
432
-				self::$relations[] = ['objid' => $id, 'tag' => $name];
433
-			}
434
-		}
435
-		$this->tags = array_merge($this->tags, $newones);
436
-		if ($sync === true) {
437
-			$this->save();
438
-		}
439
-
440
-		return true;
441
-	}
442
-
443
-	/**
444
-	 * Save the list of tags and their object relations
445
-	 */
446
-	protected function save() {
447
-		if (is_array($this->tags)) {
448
-			foreach ($this->tags as $tag) {
449
-				try {
450
-					if (!$this->mapper->tagExists($tag)) {
451
-						$this->mapper->insert($tag);
452
-					}
453
-				} catch (\Exception $e) {
454
-					\OC::$server->getLogger()->logException($e, [
455
-						'message' => __METHOD__,
456
-						'level' => ILogger::ERROR,
457
-						'app' => 'core',
458
-					]);
459
-				}
460
-			}
461
-
462
-			// reload tags to get the proper ids.
463
-			$this->tags = $this->mapper->loadTags($this->owners, $this->type);
464
-			\OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true),
465
-				ILogger::DEBUG);
466
-			// Loop through temporarily cached objectid/tagname pairs
467
-			// and save relations.
468
-			$tags = $this->tags;
469
-			// For some reason this is needed or array_search(i) will return 0..?
470
-			ksort($tags);
471
-			$dbConnection = \OC::$server->getDatabaseConnection();
472
-			foreach (self::$relations as $relation) {
473
-				$tagId = $this->getTagId($relation['tag']);
474
-				\OCP\Util::writeLog('core', __METHOD__ . 'catid, ' . $relation['tag'] . ' ' . $tagId, ILogger::DEBUG);
475
-				if ($tagId) {
476
-					try {
477
-						$dbConnection->insertIfNotExist(self::RELATION_TABLE,
478
-							[
479
-								'objid' => $relation['objid'],
480
-								'categoryid' => $tagId,
481
-								'type' => $this->type,
482
-							]);
483
-					} catch (\Exception $e) {
484
-						\OC::$server->getLogger()->logException($e, [
485
-							'message' => __METHOD__,
486
-							'level' => ILogger::ERROR,
487
-							'app' => 'core',
488
-						]);
489
-					}
490
-				}
491
-			}
492
-			self::$relations = []; // reset
493
-		} else {
494
-			\OCP\Util::writeLog('core', __METHOD__.', $this->tags is not an array! '
495
-				. print_r($this->tags, true), ILogger::ERROR);
496
-		}
497
-	}
498
-
499
-	/**
500
-	 * Delete tags and tag/object relations for a user.
501
-	 *
502
-	 * For hooking up on post_deleteUser
503
-	 *
504
-	 * @param array $arguments
505
-	 */
506
-	public static function post_deleteUser($arguments) {
507
-		// Find all objectid/tagId pairs.
508
-		$result = null;
509
-		try {
510
-			$stmt = \OC_DB::prepare('SELECT `id` FROM `' . self::TAG_TABLE . '` '
511
-				. 'WHERE `uid` = ?');
512
-			$result = $stmt->execute([$arguments['uid']]);
513
-			if ($result === null) {
514
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
515
-			}
516
-		} catch (\Exception $e) {
517
-			\OC::$server->getLogger()->logException($e, [
518
-				'message' => __METHOD__,
519
-				'level' => ILogger::ERROR,
520
-				'app' => 'core',
521
-			]);
522
-		}
523
-
524
-		if (!is_null($result)) {
525
-			try {
526
-				$stmt = \OC_DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` '
527
-					. 'WHERE `categoryid` = ?');
528
-				while ($row = $result->fetchRow()) {
529
-					try {
530
-						$stmt->execute([$row['id']]);
531
-					} catch (\Exception $e) {
532
-						\OC::$server->getLogger()->logException($e, [
533
-							'message' => __METHOD__,
534
-							'level' => ILogger::ERROR,
535
-							'app' => 'core',
536
-						]);
537
-					}
538
-				}
539
-				$result->closeCursor();
540
-			} catch (\Exception $e) {
541
-				\OC::$server->getLogger()->logException($e, [
542
-					'message' => __METHOD__,
543
-					'level' => ILogger::ERROR,
544
-					'app' => 'core',
545
-				]);
546
-			}
547
-		}
548
-		try {
549
-			$stmt = \OC_DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` '
550
-				. 'WHERE `uid` = ?');
551
-			$result = $stmt->execute([$arguments['uid']]);
552
-			if ($result === null) {
553
-				\OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
554
-			}
555
-		} catch (\Exception $e) {
556
-			\OC::$server->getLogger()->logException($e, [
557
-				'message' => __METHOD__,
558
-				'level' => ILogger::ERROR,
559
-				'app' => 'core',
560
-			]);
561
-		}
562
-	}
563
-
564
-	/**
565
-	 * Delete tag/object relations from the db
566
-	 *
567
-	 * @param array $ids The ids of the objects
568
-	 * @return boolean Returns false on error.
569
-	 */
570
-	public function purgeObjects(array $ids) {
571
-		if (count($ids) === 0) {
572
-			// job done ;)
573
-			return true;
574
-		}
575
-		$updates = $ids;
576
-		try {
577
-			$query = 'DELETE FROM `' . self::RELATION_TABLE . '` ';
578
-			$query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids) - 1) . '?) ';
579
-			$query .= 'AND `type`= ?';
580
-			$updates[] = $this->type;
581
-			$stmt = \OC_DB::prepare($query);
582
-			$result = $stmt->execute($updates);
583
-			if ($result === null) {
584
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
585
-				return false;
586
-			}
587
-		} catch (\Exception $e) {
588
-			\OC::$server->getLogger()->logException($e, [
589
-				'message' => __METHOD__,
590
-				'level' => ILogger::ERROR,
591
-				'app' => 'core',
592
-			]);
593
-			return false;
594
-		}
595
-		return true;
596
-	}
597
-
598
-	/**
599
-	 * Get favorites for an object type
600
-	 *
601
-	 * @return array|false An array of object ids.
602
-	 */
603
-	public function getFavorites() {
604
-		if (!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
605
-			return [];
606
-		}
607
-
608
-		try {
609
-			return $this->getIdsForTag(ITags::TAG_FAVORITE);
610
-		} catch (\Exception $e) {
611
-			\OC::$server->getLogger()->logException($e, [
612
-				'message' => __METHOD__,
613
-				'level' => ILogger::ERROR,
614
-				'app' => 'core',
615
-			]);
616
-			return [];
617
-		}
618
-	}
619
-
620
-	/**
621
-	 * Add an object to favorites
622
-	 *
623
-	 * @param int $objid The id of the object
624
-	 * @return boolean
625
-	 */
626
-	public function addToFavorites($objid) {
627
-		if (!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
628
-			$this->add(ITags::TAG_FAVORITE);
629
-		}
630
-		return $this->tagAs($objid, ITags::TAG_FAVORITE);
631
-	}
632
-
633
-	/**
634
-	 * Remove an object from favorites
635
-	 *
636
-	 * @param int $objid The id of the object
637
-	 * @return boolean
638
-	 */
639
-	public function removeFromFavorites($objid) {
640
-		return $this->unTag($objid, ITags::TAG_FAVORITE);
641
-	}
642
-
643
-	/**
644
-	 * Creates a tag/object relation.
645
-	 *
646
-	 * @param int $objid The id of the object
647
-	 * @param string $tag The id or name of the tag
648
-	 * @return boolean Returns false on error.
649
-	 */
650
-	public function tagAs($objid, $tag) {
651
-		if (is_string($tag) && !is_numeric($tag)) {
652
-			$tag = trim($tag);
653
-			if ($tag === '') {
654
-				\OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
655
-				return false;
656
-			}
657
-			if (!$this->hasTag($tag)) {
658
-				$this->add($tag);
659
-			}
660
-			$tagId = $this->getTagId($tag);
661
-		} else {
662
-			$tagId = $tag;
663
-		}
664
-		try {
665
-			\OC::$server->getDatabaseConnection()->insertIfNotExist(self::RELATION_TABLE,
666
-				[
667
-					'objid' => $objid,
668
-					'categoryid' => $tagId,
669
-					'type' => $this->type,
670
-				]);
671
-		} catch (\Exception $e) {
672
-			\OC::$server->getLogger()->logException($e, [
673
-				'message' => __METHOD__,
674
-				'level' => ILogger::ERROR,
675
-				'app' => 'core',
676
-			]);
677
-			return false;
678
-		}
679
-		return true;
680
-	}
681
-
682
-	/**
683
-	 * Delete single tag/object relation from the db
684
-	 *
685
-	 * @param int $objid The id of the object
686
-	 * @param string $tag The id or name of the tag
687
-	 * @return boolean
688
-	 */
689
-	public function unTag($objid, $tag) {
690
-		if (is_string($tag) && !is_numeric($tag)) {
691
-			$tag = trim($tag);
692
-			if ($tag === '') {
693
-				\OCP\Util::writeLog('core', __METHOD__.', Tag name is empty', ILogger::DEBUG);
694
-				return false;
695
-			}
696
-			$tagId = $this->getTagId($tag);
697
-		} else {
698
-			$tagId = $tag;
699
-		}
700
-
701
-		try {
702
-			$sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
703
-					. 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?';
704
-			$stmt = \OC_DB::prepare($sql);
705
-			$stmt->execute([$objid, $tagId, $this->type]);
706
-		} catch (\Exception $e) {
707
-			\OC::$server->getLogger()->logException($e, [
708
-				'message' => __METHOD__,
709
-				'level' => ILogger::ERROR,
710
-				'app' => 'core',
711
-			]);
712
-			return false;
713
-		}
714
-		return true;
715
-	}
716
-
717
-	/**
718
-	 * Delete tags from the database.
719
-	 *
720
-	 * @param string[]|integer[] $names An array of tags (names or IDs) to delete
721
-	 * @return bool Returns false on error
722
-	 */
723
-	public function delete($names) {
724
-		if (!is_array($names)) {
725
-			$names = [$names];
726
-		}
727
-
728
-		$names = array_map('trim', $names);
729
-		array_filter($names);
730
-
731
-		\OCP\Util::writeLog('core', __METHOD__ . ', before: '
732
-			. print_r($this->tags, true), ILogger::DEBUG);
733
-		foreach ($names as $name) {
734
-			$id = null;
735
-
736
-			if (is_numeric($name)) {
737
-				$key = $this->getTagById($name);
738
-			} else {
739
-				$key = $this->getTagByName($name);
740
-			}
741
-			if ($key !== false) {
742
-				$tag = $this->tags[$key];
743
-				$id = $tag->getId();
744
-				unset($this->tags[$key]);
745
-				$this->mapper->delete($tag);
746
-			} else {
747
-				\OCP\Util::writeLog('core', __METHOD__ . 'Cannot delete tag ' . $name
748
-					. ': not found.', ILogger::ERROR);
749
-			}
750
-			if (!is_null($id) && $id !== false) {
751
-				try {
752
-					$sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
753
-							. 'WHERE `categoryid` = ?';
754
-					$stmt = \OC_DB::prepare($sql);
755
-					$result = $stmt->execute([$id]);
756
-					if ($result === null) {
757
-						\OCP\Util::writeLog('core',
758
-							__METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(),
759
-							ILogger::ERROR);
760
-						return false;
761
-					}
762
-				} catch (\Exception $e) {
763
-					\OC::$server->getLogger()->logException($e, [
764
-						'message' => __METHOD__,
765
-						'level' => ILogger::ERROR,
766
-						'app' => 'core',
767
-					]);
768
-					return false;
769
-				}
770
-			}
771
-		}
772
-		return true;
773
-	}
774
-
775
-	// case-insensitive array_search
776
-	protected function array_searchi($needle, $haystack, $mem = 'getName') {
777
-		if (!is_array($haystack)) {
778
-			return false;
779
-		}
780
-		return array_search(strtolower($needle), array_map(
781
-			function ($tag) use ($mem) {
782
-				return strtolower(call_user_func([$tag, $mem]));
783
-			}, $haystack)
784
-		);
785
-	}
786
-
787
-	/**
788
-	 * Get a tag's ID.
789
-	 *
790
-	 * @param string $name The tag name to look for.
791
-	 * @return string|bool The tag's id or false if no matching tag is found.
792
-	 */
793
-	private function getTagId($name) {
794
-		$key = $this->array_searchi($name, $this->tags);
795
-		if ($key !== false) {
796
-			return $this->tags[$key]->getId();
797
-		}
798
-		return false;
799
-	}
800
-
801
-	/**
802
-	 * Get a tag by its name.
803
-	 *
804
-	 * @param string $name The tag name.
805
-	 * @return integer|bool The tag object's offset within the $this->tags
806
-	 *                      array or false if it doesn't exist.
807
-	 */
808
-	private function getTagByName($name) {
809
-		return $this->array_searchi($name, $this->tags, 'getName');
810
-	}
811
-
812
-	/**
813
-	 * Get a tag by its ID.
814
-	 *
815
-	 * @param string $id The tag ID to look for.
816
-	 * @return integer|bool The tag object's offset within the $this->tags
817
-	 *                      array or false if it doesn't exist.
818
-	 */
819
-	private function getTagById($id) {
820
-		return $this->array_searchi($id, $this->tags, 'getId');
821
-	}
822
-
823
-	/**
824
-	 * Returns an array mapping a given tag's properties to its values:
825
-	 * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
826
-	 *
827
-	 * @param Tag $tag The tag that is going to be mapped
828
-	 * @return array
829
-	 */
830
-	private function tagMap(Tag $tag) {
831
-		return [
832
-			'id' => $tag->getId(),
833
-			'name' => $tag->getName(),
834
-			'owner' => $tag->getOwner(),
835
-			'type' => $tag->getType()
836
-		];
837
-	}
54
+    /**
55
+     * Tags
56
+     *
57
+     * @var array
58
+     */
59
+    private $tags = [];
60
+
61
+    /**
62
+     * Used for storing objectid/categoryname pairs while rescanning.
63
+     *
64
+     * @var array
65
+     */
66
+    private static $relations = [];
67
+
68
+    /**
69
+     * Type
70
+     *
71
+     * @var string
72
+     */
73
+    private $type;
74
+
75
+    /**
76
+     * User
77
+     *
78
+     * @var string
79
+     */
80
+    private $user;
81
+
82
+    /**
83
+     * Are we including tags for shared items?
84
+     *
85
+     * @var bool
86
+     */
87
+    private $includeShared = false;
88
+
89
+    /**
90
+     * The current user, plus any owners of the items shared with the current
91
+     * user, if $this->includeShared === true.
92
+     *
93
+     * @var array
94
+     */
95
+    private $owners = [];
96
+
97
+    /**
98
+     * The Mapper we're using to communicate our Tag objects to the database.
99
+     *
100
+     * @var TagMapper
101
+     */
102
+    private $mapper;
103
+
104
+    /**
105
+     * The sharing backend for objects of $this->type. Required if
106
+     * $this->includeShared === true to determine ownership of items.
107
+     *
108
+     * @var \OCP\Share_Backend
109
+     */
110
+    private $backend;
111
+
112
+    public const TAG_TABLE = '*PREFIX*vcategory';
113
+    public const RELATION_TABLE = '*PREFIX*vcategory_to_object';
114
+
115
+    /**
116
+     * Constructor.
117
+     *
118
+     * @param TagMapper $mapper Instance of the TagMapper abstraction layer.
119
+     * @param string $user The user whose data the object will operate on.
120
+     * @param string $type The type of items for which tags will be loaded.
121
+     * @param array $defaultTags Tags that should be created at construction.
122
+     *
123
+     * since 20.0.0 $includeShared isn't used anymore
124
+     */
125
+    public function __construct(TagMapper $mapper, $user, $type, $defaultTags = []) {
126
+        $this->mapper = $mapper;
127
+        $this->user = $user;
128
+        $this->type = $type;
129
+        $this->owners = [$this->user];
130
+        $this->tags = $this->mapper->loadTags($this->owners, $this->type);
131
+
132
+        if (count($defaultTags) > 0 && count($this->tags) === 0) {
133
+            $this->addMultiple($defaultTags, true);
134
+        }
135
+    }
136
+
137
+    /**
138
+     * Check if any tags are saved for this type and user.
139
+     *
140
+     * @return boolean
141
+     */
142
+    public function isEmpty() {
143
+        return count($this->tags) === 0;
144
+    }
145
+
146
+    /**
147
+     * Returns an array mapping a given tag's properties to its values:
148
+     * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
149
+     *
150
+     * @param string $id The ID of the tag that is going to be mapped
151
+     * @return array|false
152
+     */
153
+    public function getTag($id) {
154
+        $key = $this->getTagById($id);
155
+        if ($key !== false) {
156
+            return $this->tagMap($this->tags[$key]);
157
+        }
158
+        return false;
159
+    }
160
+
161
+    /**
162
+     * Get the tags for a specific user.
163
+     *
164
+     * This returns an array with maps containing each tag's properties:
165
+     * [
166
+     * 	['id' => 0, 'name' = 'First tag', 'owner' = 'User', 'type' => 'tagtype'],
167
+     * 	['id' => 1, 'name' = 'Shared tag', 'owner' = 'Other user', 'type' => 'tagtype'],
168
+     * ]
169
+     *
170
+     * @return array
171
+     */
172
+    public function getTags() {
173
+        if (!count($this->tags)) {
174
+            return [];
175
+        }
176
+
177
+        usort($this->tags, function ($a, $b) {
178
+            return strnatcasecmp($a->getName(), $b->getName());
179
+        });
180
+        $tagMap = [];
181
+
182
+        foreach ($this->tags as $tag) {
183
+            if ($tag->getName() !== ITags::TAG_FAVORITE) {
184
+                $tagMap[] = $this->tagMap($tag);
185
+            }
186
+        }
187
+        return $tagMap;
188
+    }
189
+
190
+    /**
191
+     * Return only the tags owned by the given user, omitting any tags shared
192
+     * by other users.
193
+     *
194
+     * @param string $user The user whose tags are to be checked.
195
+     * @return array An array of Tag objects.
196
+     */
197
+    public function getTagsForUser($user) {
198
+        return array_filter($this->tags,
199
+            function ($tag) use ($user) {
200
+                return $tag->getOwner() === $user;
201
+            }
202
+        );
203
+    }
204
+
205
+    /**
206
+     * Get the list of tags for the given ids.
207
+     *
208
+     * @param array $objIds array of object ids
209
+     * @return array|boolean of tags id as key to array of tag names
210
+     * or false if an error occurred
211
+     */
212
+    public function getTagsForObjects(array $objIds) {
213
+        $entries = [];
214
+
215
+        try {
216
+            $conn = \OC::$server->getDatabaseConnection();
217
+            $chunks = array_chunk($objIds, 900, false);
218
+            foreach ($chunks as $chunk) {
219
+                $result = $conn->executeQuery(
220
+                    'SELECT `category`, `categoryid`, `objid` ' .
221
+                    'FROM `' . self::RELATION_TABLE . '` r, `' . self::TAG_TABLE . '` ' .
222
+                    'WHERE `categoryid` = `id` AND `uid` = ? AND r.`type` = ? AND `objid` IN (?)',
223
+                    [$this->user, $this->type, $chunk],
224
+                    [null, null, IQueryBuilder::PARAM_INT_ARRAY]
225
+                );
226
+                while ($row = $result->fetch()) {
227
+                    $objId = (int)$row['objid'];
228
+                    if (!isset($entries[$objId])) {
229
+                        $entries[$objId] = [];
230
+                    }
231
+                    $entries[$objId][] = $row['category'];
232
+                }
233
+            }
234
+        } catch (\Exception $e) {
235
+            \OC::$server->getLogger()->logException($e, [
236
+                'message' => __METHOD__,
237
+                'level' => ILogger::ERROR,
238
+                'app' => 'core',
239
+            ]);
240
+            return false;
241
+        }
242
+
243
+        return $entries;
244
+    }
245
+
246
+    /**
247
+     * Get the a list if items tagged with $tag.
248
+     *
249
+     * Throws an exception if the tag could not be found.
250
+     *
251
+     * @param string $tag Tag id or name.
252
+     * @return array|false An array of object ids or false on error.
253
+     * @throws \Exception
254
+     */
255
+    public function getIdsForTag($tag) {
256
+        $result = null;
257
+        $tagId = false;
258
+        if (is_numeric($tag)) {
259
+            $tagId = $tag;
260
+        } elseif (is_string($tag)) {
261
+            $tag = trim($tag);
262
+            if ($tag === '') {
263
+                \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
264
+                return false;
265
+            }
266
+            $tagId = $this->getTagId($tag);
267
+        }
268
+
269
+        if ($tagId === false) {
270
+            $l10n = \OC::$server->getL10N('core');
271
+            throw new \Exception(
272
+                $l10n->t('Could not find category "%s"', [$tag])
273
+            );
274
+        }
275
+
276
+        $ids = [];
277
+        $sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE
278
+            . '` WHERE `categoryid` = ?';
279
+
280
+        try {
281
+            $stmt = \OC_DB::prepare($sql);
282
+            $result = $stmt->execute([$tagId]);
283
+            if ($result === null) {
284
+                $stmt->closeCursor();
285
+                \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
286
+                return false;
287
+            }
288
+        } catch (\Exception $e) {
289
+            \OC::$server->getLogger()->logException($e, [
290
+                'message' => __METHOD__,
291
+                'level' => ILogger::ERROR,
292
+                'app' => 'core',
293
+            ]);
294
+            return false;
295
+        }
296
+
297
+        if (!is_null($result)) {
298
+            while ($row = $result->fetchRow()) {
299
+                $ids[] = (int)$row['objid'];
300
+            }
301
+            $result->closeCursor();
302
+        }
303
+
304
+        return $ids;
305
+    }
306
+
307
+    /**
308
+     * Checks whether a tag is saved for the given user,
309
+     * disregarding the ones shared with him or her.
310
+     *
311
+     * @param string $name The tag name to check for.
312
+     * @param string $user The user whose tags are to be checked.
313
+     * @return bool
314
+     */
315
+    public function userHasTag($name, $user) {
316
+        $key = $this->array_searchi($name, $this->getTagsForUser($user));
317
+        return ($key !== false) ? $this->tags[$key]->getId() : false;
318
+    }
319
+
320
+    /**
321
+     * Checks whether a tag is saved for or shared with the current user.
322
+     *
323
+     * @param string $name The tag name to check for.
324
+     * @return bool
325
+     */
326
+    public function hasTag($name) {
327
+        return $this->getTagId($name) !== false;
328
+    }
329
+
330
+    /**
331
+     * Add a new tag.
332
+     *
333
+     * @param string $name A string with a name of the tag
334
+     * @return false|int the id of the added tag or false on error.
335
+     */
336
+    public function add($name) {
337
+        $name = trim($name);
338
+
339
+        if ($name === '') {
340
+            \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
341
+            return false;
342
+        }
343
+        if ($this->userHasTag($name, $this->user)) {
344
+            \OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', ILogger::DEBUG);
345
+            return false;
346
+        }
347
+        try {
348
+            $tag = new Tag($this->user, $this->type, $name);
349
+            $tag = $this->mapper->insert($tag);
350
+            $this->tags[] = $tag;
351
+        } catch (\Exception $e) {
352
+            \OC::$server->getLogger()->logException($e, [
353
+                'message' => __METHOD__,
354
+                'level' => ILogger::ERROR,
355
+                'app' => 'core',
356
+            ]);
357
+            return false;
358
+        }
359
+        \OCP\Util::writeLog('core', __METHOD__.', id: ' . $tag->getId(), ILogger::DEBUG);
360
+        return $tag->getId();
361
+    }
362
+
363
+    /**
364
+     * Rename tag.
365
+     *
366
+     * @param string|integer $from The name or ID of the existing tag
367
+     * @param string $to The new name of the tag.
368
+     * @return bool
369
+     */
370
+    public function rename($from, $to) {
371
+        $from = trim($from);
372
+        $to = trim($to);
373
+
374
+        if ($to === '' || $from === '') {
375
+            \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
376
+            return false;
377
+        }
378
+
379
+        if (is_numeric($from)) {
380
+            $key = $this->getTagById($from);
381
+        } else {
382
+            $key = $this->getTagByName($from);
383
+        }
384
+        if ($key === false) {
385
+            \OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', ILogger::DEBUG);
386
+            return false;
387
+        }
388
+        $tag = $this->tags[$key];
389
+
390
+        if ($this->userHasTag($to, $tag->getOwner())) {
391
+            \OCP\Util::writeLog('core', __METHOD__.', A tag named ' . $to. ' already exists for user ' . $tag->getOwner() . '.', ILogger::DEBUG);
392
+            return false;
393
+        }
394
+
395
+        try {
396
+            $tag->setName($to);
397
+            $this->tags[$key] = $this->mapper->update($tag);
398
+        } catch (\Exception $e) {
399
+            \OC::$server->getLogger()->logException($e, [
400
+                'message' => __METHOD__,
401
+                'level' => ILogger::ERROR,
402
+                'app' => 'core',
403
+            ]);
404
+            return false;
405
+        }
406
+        return true;
407
+    }
408
+
409
+    /**
410
+     * Add a list of new tags.
411
+     *
412
+     * @param string[] $names A string with a name or an array of strings containing
413
+     * the name(s) of the tag(s) to add.
414
+     * @param bool $sync When true, save the tags
415
+     * @param int|null $id int Optional object id to add to this|these tag(s)
416
+     * @return bool Returns false on error.
417
+     */
418
+    public function addMultiple($names, $sync = false, $id = null) {
419
+        if (!is_array($names)) {
420
+            $names = [$names];
421
+        }
422
+        $names = array_map('trim', $names);
423
+        array_filter($names);
424
+
425
+        $newones = [];
426
+        foreach ($names as $name) {
427
+            if (!$this->hasTag($name) && $name !== '') {
428
+                $newones[] = new Tag($this->user, $this->type, $name);
429
+            }
430
+            if (!is_null($id)) {
431
+                // Insert $objectid, $categoryid  pairs if not exist.
432
+                self::$relations[] = ['objid' => $id, 'tag' => $name];
433
+            }
434
+        }
435
+        $this->tags = array_merge($this->tags, $newones);
436
+        if ($sync === true) {
437
+            $this->save();
438
+        }
439
+
440
+        return true;
441
+    }
442
+
443
+    /**
444
+     * Save the list of tags and their object relations
445
+     */
446
+    protected function save() {
447
+        if (is_array($this->tags)) {
448
+            foreach ($this->tags as $tag) {
449
+                try {
450
+                    if (!$this->mapper->tagExists($tag)) {
451
+                        $this->mapper->insert($tag);
452
+                    }
453
+                } catch (\Exception $e) {
454
+                    \OC::$server->getLogger()->logException($e, [
455
+                        'message' => __METHOD__,
456
+                        'level' => ILogger::ERROR,
457
+                        'app' => 'core',
458
+                    ]);
459
+                }
460
+            }
461
+
462
+            // reload tags to get the proper ids.
463
+            $this->tags = $this->mapper->loadTags($this->owners, $this->type);
464
+            \OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true),
465
+                ILogger::DEBUG);
466
+            // Loop through temporarily cached objectid/tagname pairs
467
+            // and save relations.
468
+            $tags = $this->tags;
469
+            // For some reason this is needed or array_search(i) will return 0..?
470
+            ksort($tags);
471
+            $dbConnection = \OC::$server->getDatabaseConnection();
472
+            foreach (self::$relations as $relation) {
473
+                $tagId = $this->getTagId($relation['tag']);
474
+                \OCP\Util::writeLog('core', __METHOD__ . 'catid, ' . $relation['tag'] . ' ' . $tagId, ILogger::DEBUG);
475
+                if ($tagId) {
476
+                    try {
477
+                        $dbConnection->insertIfNotExist(self::RELATION_TABLE,
478
+                            [
479
+                                'objid' => $relation['objid'],
480
+                                'categoryid' => $tagId,
481
+                                'type' => $this->type,
482
+                            ]);
483
+                    } catch (\Exception $e) {
484
+                        \OC::$server->getLogger()->logException($e, [
485
+                            'message' => __METHOD__,
486
+                            'level' => ILogger::ERROR,
487
+                            'app' => 'core',
488
+                        ]);
489
+                    }
490
+                }
491
+            }
492
+            self::$relations = []; // reset
493
+        } else {
494
+            \OCP\Util::writeLog('core', __METHOD__.', $this->tags is not an array! '
495
+                . print_r($this->tags, true), ILogger::ERROR);
496
+        }
497
+    }
498
+
499
+    /**
500
+     * Delete tags and tag/object relations for a user.
501
+     *
502
+     * For hooking up on post_deleteUser
503
+     *
504
+     * @param array $arguments
505
+     */
506
+    public static function post_deleteUser($arguments) {
507
+        // Find all objectid/tagId pairs.
508
+        $result = null;
509
+        try {
510
+            $stmt = \OC_DB::prepare('SELECT `id` FROM `' . self::TAG_TABLE . '` '
511
+                . 'WHERE `uid` = ?');
512
+            $result = $stmt->execute([$arguments['uid']]);
513
+            if ($result === null) {
514
+                \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
515
+            }
516
+        } catch (\Exception $e) {
517
+            \OC::$server->getLogger()->logException($e, [
518
+                'message' => __METHOD__,
519
+                'level' => ILogger::ERROR,
520
+                'app' => 'core',
521
+            ]);
522
+        }
523
+
524
+        if (!is_null($result)) {
525
+            try {
526
+                $stmt = \OC_DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` '
527
+                    . 'WHERE `categoryid` = ?');
528
+                while ($row = $result->fetchRow()) {
529
+                    try {
530
+                        $stmt->execute([$row['id']]);
531
+                    } catch (\Exception $e) {
532
+                        \OC::$server->getLogger()->logException($e, [
533
+                            'message' => __METHOD__,
534
+                            'level' => ILogger::ERROR,
535
+                            'app' => 'core',
536
+                        ]);
537
+                    }
538
+                }
539
+                $result->closeCursor();
540
+            } catch (\Exception $e) {
541
+                \OC::$server->getLogger()->logException($e, [
542
+                    'message' => __METHOD__,
543
+                    'level' => ILogger::ERROR,
544
+                    'app' => 'core',
545
+                ]);
546
+            }
547
+        }
548
+        try {
549
+            $stmt = \OC_DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` '
550
+                . 'WHERE `uid` = ?');
551
+            $result = $stmt->execute([$arguments['uid']]);
552
+            if ($result === null) {
553
+                \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
554
+            }
555
+        } catch (\Exception $e) {
556
+            \OC::$server->getLogger()->logException($e, [
557
+                'message' => __METHOD__,
558
+                'level' => ILogger::ERROR,
559
+                'app' => 'core',
560
+            ]);
561
+        }
562
+    }
563
+
564
+    /**
565
+     * Delete tag/object relations from the db
566
+     *
567
+     * @param array $ids The ids of the objects
568
+     * @return boolean Returns false on error.
569
+     */
570
+    public function purgeObjects(array $ids) {
571
+        if (count($ids) === 0) {
572
+            // job done ;)
573
+            return true;
574
+        }
575
+        $updates = $ids;
576
+        try {
577
+            $query = 'DELETE FROM `' . self::RELATION_TABLE . '` ';
578
+            $query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids) - 1) . '?) ';
579
+            $query .= 'AND `type`= ?';
580
+            $updates[] = $this->type;
581
+            $stmt = \OC_DB::prepare($query);
582
+            $result = $stmt->execute($updates);
583
+            if ($result === null) {
584
+                \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
585
+                return false;
586
+            }
587
+        } catch (\Exception $e) {
588
+            \OC::$server->getLogger()->logException($e, [
589
+                'message' => __METHOD__,
590
+                'level' => ILogger::ERROR,
591
+                'app' => 'core',
592
+            ]);
593
+            return false;
594
+        }
595
+        return true;
596
+    }
597
+
598
+    /**
599
+     * Get favorites for an object type
600
+     *
601
+     * @return array|false An array of object ids.
602
+     */
603
+    public function getFavorites() {
604
+        if (!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
605
+            return [];
606
+        }
607
+
608
+        try {
609
+            return $this->getIdsForTag(ITags::TAG_FAVORITE);
610
+        } catch (\Exception $e) {
611
+            \OC::$server->getLogger()->logException($e, [
612
+                'message' => __METHOD__,
613
+                'level' => ILogger::ERROR,
614
+                'app' => 'core',
615
+            ]);
616
+            return [];
617
+        }
618
+    }
619
+
620
+    /**
621
+     * Add an object to favorites
622
+     *
623
+     * @param int $objid The id of the object
624
+     * @return boolean
625
+     */
626
+    public function addToFavorites($objid) {
627
+        if (!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
628
+            $this->add(ITags::TAG_FAVORITE);
629
+        }
630
+        return $this->tagAs($objid, ITags::TAG_FAVORITE);
631
+    }
632
+
633
+    /**
634
+     * Remove an object from favorites
635
+     *
636
+     * @param int $objid The id of the object
637
+     * @return boolean
638
+     */
639
+    public function removeFromFavorites($objid) {
640
+        return $this->unTag($objid, ITags::TAG_FAVORITE);
641
+    }
642
+
643
+    /**
644
+     * Creates a tag/object relation.
645
+     *
646
+     * @param int $objid The id of the object
647
+     * @param string $tag The id or name of the tag
648
+     * @return boolean Returns false on error.
649
+     */
650
+    public function tagAs($objid, $tag) {
651
+        if (is_string($tag) && !is_numeric($tag)) {
652
+            $tag = trim($tag);
653
+            if ($tag === '') {
654
+                \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
655
+                return false;
656
+            }
657
+            if (!$this->hasTag($tag)) {
658
+                $this->add($tag);
659
+            }
660
+            $tagId = $this->getTagId($tag);
661
+        } else {
662
+            $tagId = $tag;
663
+        }
664
+        try {
665
+            \OC::$server->getDatabaseConnection()->insertIfNotExist(self::RELATION_TABLE,
666
+                [
667
+                    'objid' => $objid,
668
+                    'categoryid' => $tagId,
669
+                    'type' => $this->type,
670
+                ]);
671
+        } catch (\Exception $e) {
672
+            \OC::$server->getLogger()->logException($e, [
673
+                'message' => __METHOD__,
674
+                'level' => ILogger::ERROR,
675
+                'app' => 'core',
676
+            ]);
677
+            return false;
678
+        }
679
+        return true;
680
+    }
681
+
682
+    /**
683
+     * Delete single tag/object relation from the db
684
+     *
685
+     * @param int $objid The id of the object
686
+     * @param string $tag The id or name of the tag
687
+     * @return boolean
688
+     */
689
+    public function unTag($objid, $tag) {
690
+        if (is_string($tag) && !is_numeric($tag)) {
691
+            $tag = trim($tag);
692
+            if ($tag === '') {
693
+                \OCP\Util::writeLog('core', __METHOD__.', Tag name is empty', ILogger::DEBUG);
694
+                return false;
695
+            }
696
+            $tagId = $this->getTagId($tag);
697
+        } else {
698
+            $tagId = $tag;
699
+        }
700
+
701
+        try {
702
+            $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
703
+                    . 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?';
704
+            $stmt = \OC_DB::prepare($sql);
705
+            $stmt->execute([$objid, $tagId, $this->type]);
706
+        } catch (\Exception $e) {
707
+            \OC::$server->getLogger()->logException($e, [
708
+                'message' => __METHOD__,
709
+                'level' => ILogger::ERROR,
710
+                'app' => 'core',
711
+            ]);
712
+            return false;
713
+        }
714
+        return true;
715
+    }
716
+
717
+    /**
718
+     * Delete tags from the database.
719
+     *
720
+     * @param string[]|integer[] $names An array of tags (names or IDs) to delete
721
+     * @return bool Returns false on error
722
+     */
723
+    public function delete($names) {
724
+        if (!is_array($names)) {
725
+            $names = [$names];
726
+        }
727
+
728
+        $names = array_map('trim', $names);
729
+        array_filter($names);
730
+
731
+        \OCP\Util::writeLog('core', __METHOD__ . ', before: '
732
+            . print_r($this->tags, true), ILogger::DEBUG);
733
+        foreach ($names as $name) {
734
+            $id = null;
735
+
736
+            if (is_numeric($name)) {
737
+                $key = $this->getTagById($name);
738
+            } else {
739
+                $key = $this->getTagByName($name);
740
+            }
741
+            if ($key !== false) {
742
+                $tag = $this->tags[$key];
743
+                $id = $tag->getId();
744
+                unset($this->tags[$key]);
745
+                $this->mapper->delete($tag);
746
+            } else {
747
+                \OCP\Util::writeLog('core', __METHOD__ . 'Cannot delete tag ' . $name
748
+                    . ': not found.', ILogger::ERROR);
749
+            }
750
+            if (!is_null($id) && $id !== false) {
751
+                try {
752
+                    $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
753
+                            . 'WHERE `categoryid` = ?';
754
+                    $stmt = \OC_DB::prepare($sql);
755
+                    $result = $stmt->execute([$id]);
756
+                    if ($result === null) {
757
+                        \OCP\Util::writeLog('core',
758
+                            __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(),
759
+                            ILogger::ERROR);
760
+                        return false;
761
+                    }
762
+                } catch (\Exception $e) {
763
+                    \OC::$server->getLogger()->logException($e, [
764
+                        'message' => __METHOD__,
765
+                        'level' => ILogger::ERROR,
766
+                        'app' => 'core',
767
+                    ]);
768
+                    return false;
769
+                }
770
+            }
771
+        }
772
+        return true;
773
+    }
774
+
775
+    // case-insensitive array_search
776
+    protected function array_searchi($needle, $haystack, $mem = 'getName') {
777
+        if (!is_array($haystack)) {
778
+            return false;
779
+        }
780
+        return array_search(strtolower($needle), array_map(
781
+            function ($tag) use ($mem) {
782
+                return strtolower(call_user_func([$tag, $mem]));
783
+            }, $haystack)
784
+        );
785
+    }
786
+
787
+    /**
788
+     * Get a tag's ID.
789
+     *
790
+     * @param string $name The tag name to look for.
791
+     * @return string|bool The tag's id or false if no matching tag is found.
792
+     */
793
+    private function getTagId($name) {
794
+        $key = $this->array_searchi($name, $this->tags);
795
+        if ($key !== false) {
796
+            return $this->tags[$key]->getId();
797
+        }
798
+        return false;
799
+    }
800
+
801
+    /**
802
+     * Get a tag by its name.
803
+     *
804
+     * @param string $name The tag name.
805
+     * @return integer|bool The tag object's offset within the $this->tags
806
+     *                      array or false if it doesn't exist.
807
+     */
808
+    private function getTagByName($name) {
809
+        return $this->array_searchi($name, $this->tags, 'getName');
810
+    }
811
+
812
+    /**
813
+     * Get a tag by its ID.
814
+     *
815
+     * @param string $id The tag ID to look for.
816
+     * @return integer|bool The tag object's offset within the $this->tags
817
+     *                      array or false if it doesn't exist.
818
+     */
819
+    private function getTagById($id) {
820
+        return $this->array_searchi($id, $this->tags, 'getId');
821
+    }
822
+
823
+    /**
824
+     * Returns an array mapping a given tag's properties to its values:
825
+     * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
826
+     *
827
+     * @param Tag $tag The tag that is going to be mapped
828
+     * @return array
829
+     */
830
+    private function tagMap(Tag $tag) {
831
+        return [
832
+            'id' => $tag->getId(),
833
+            'name' => $tag->getName(),
834
+            'owner' => $tag->getOwner(),
835
+            'type' => $tag->getType()
836
+        ];
837
+    }
838 838
 }
Please login to merge, or discard this patch.
Spacing   +28 added lines, -28 removed lines patch added patch discarded remove patch
@@ -174,7 +174,7 @@  discard block
 block discarded – undo
174 174
 			return [];
175 175
 		}
176 176
 
177
-		usort($this->tags, function ($a, $b) {
177
+		usort($this->tags, function($a, $b) {
178 178
 			return strnatcasecmp($a->getName(), $b->getName());
179 179
 		});
180 180
 		$tagMap = [];
@@ -196,7 +196,7 @@  discard block
 block discarded – undo
196 196
 	 */
197 197
 	public function getTagsForUser($user) {
198 198
 		return array_filter($this->tags,
199
-			function ($tag) use ($user) {
199
+			function($tag) use ($user) {
200 200
 				return $tag->getOwner() === $user;
201 201
 			}
202 202
 		);
@@ -217,14 +217,14 @@  discard block
 block discarded – undo
217 217
 			$chunks = array_chunk($objIds, 900, false);
218 218
 			foreach ($chunks as $chunk) {
219 219
 				$result = $conn->executeQuery(
220
-					'SELECT `category`, `categoryid`, `objid` ' .
221
-					'FROM `' . self::RELATION_TABLE . '` r, `' . self::TAG_TABLE . '` ' .
220
+					'SELECT `category`, `categoryid`, `objid` '.
221
+					'FROM `'.self::RELATION_TABLE.'` r, `'.self::TAG_TABLE.'` '.
222 222
 					'WHERE `categoryid` = `id` AND `uid` = ? AND r.`type` = ? AND `objid` IN (?)',
223 223
 					[$this->user, $this->type, $chunk],
224 224
 					[null, null, IQueryBuilder::PARAM_INT_ARRAY]
225 225
 				);
226 226
 				while ($row = $result->fetch()) {
227
-					$objId = (int)$row['objid'];
227
+					$objId = (int) $row['objid'];
228 228
 					if (!isset($entries[$objId])) {
229 229
 						$entries[$objId] = [];
230 230
 					}
@@ -274,7 +274,7 @@  discard block
 block discarded – undo
274 274
 		}
275 275
 
276 276
 		$ids = [];
277
-		$sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE
277
+		$sql = 'SELECT `objid` FROM `'.self::RELATION_TABLE
278 278
 			. '` WHERE `categoryid` = ?';
279 279
 
280 280
 		try {
@@ -282,7 +282,7 @@  discard block
 block discarded – undo
282 282
 			$result = $stmt->execute([$tagId]);
283 283
 			if ($result === null) {
284 284
 				$stmt->closeCursor();
285
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
285
+				\OCP\Util::writeLog('core', __METHOD__.'DB error: '.\OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
286 286
 				return false;
287 287
 			}
288 288
 		} catch (\Exception $e) {
@@ -296,7 +296,7 @@  discard block
 block discarded – undo
296 296
 
297 297
 		if (!is_null($result)) {
298 298
 			while ($row = $result->fetchRow()) {
299
-				$ids[] = (int)$row['objid'];
299
+				$ids[] = (int) $row['objid'];
300 300
 			}
301 301
 			$result->closeCursor();
302 302
 		}
@@ -341,7 +341,7 @@  discard block
 block discarded – undo
341 341
 			return false;
342 342
 		}
343 343
 		if ($this->userHasTag($name, $this->user)) {
344
-			\OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', ILogger::DEBUG);
344
+			\OCP\Util::writeLog('core', __METHOD__.', name: '.$name.' exists already', ILogger::DEBUG);
345 345
 			return false;
346 346
 		}
347 347
 		try {
@@ -356,7 +356,7 @@  discard block
 block discarded – undo
356 356
 			]);
357 357
 			return false;
358 358
 		}
359
-		\OCP\Util::writeLog('core', __METHOD__.', id: ' . $tag->getId(), ILogger::DEBUG);
359
+		\OCP\Util::writeLog('core', __METHOD__.', id: '.$tag->getId(), ILogger::DEBUG);
360 360
 		return $tag->getId();
361 361
 	}
362 362
 
@@ -382,13 +382,13 @@  discard block
 block discarded – undo
382 382
 			$key = $this->getTagByName($from);
383 383
 		}
384 384
 		if ($key === false) {
385
-			\OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', ILogger::DEBUG);
385
+			\OCP\Util::writeLog('core', __METHOD__.', tag: '.$from.' does not exist', ILogger::DEBUG);
386 386
 			return false;
387 387
 		}
388 388
 		$tag = $this->tags[$key];
389 389
 
390 390
 		if ($this->userHasTag($to, $tag->getOwner())) {
391
-			\OCP\Util::writeLog('core', __METHOD__.', A tag named ' . $to. ' already exists for user ' . $tag->getOwner() . '.', ILogger::DEBUG);
391
+			\OCP\Util::writeLog('core', __METHOD__.', A tag named '.$to.' already exists for user '.$tag->getOwner().'.', ILogger::DEBUG);
392 392
 			return false;
393 393
 		}
394 394
 
@@ -461,7 +461,7 @@  discard block
 block discarded – undo
461 461
 
462 462
 			// reload tags to get the proper ids.
463 463
 			$this->tags = $this->mapper->loadTags($this->owners, $this->type);
464
-			\OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true),
464
+			\OCP\Util::writeLog('core', __METHOD__.', tags: '.print_r($this->tags, true),
465 465
 				ILogger::DEBUG);
466 466
 			// Loop through temporarily cached objectid/tagname pairs
467 467
 			// and save relations.
@@ -471,7 +471,7 @@  discard block
 block discarded – undo
471 471
 			$dbConnection = \OC::$server->getDatabaseConnection();
472 472
 			foreach (self::$relations as $relation) {
473 473
 				$tagId = $this->getTagId($relation['tag']);
474
-				\OCP\Util::writeLog('core', __METHOD__ . 'catid, ' . $relation['tag'] . ' ' . $tagId, ILogger::DEBUG);
474
+				\OCP\Util::writeLog('core', __METHOD__.'catid, '.$relation['tag'].' '.$tagId, ILogger::DEBUG);
475 475
 				if ($tagId) {
476 476
 					try {
477 477
 						$dbConnection->insertIfNotExist(self::RELATION_TABLE,
@@ -507,11 +507,11 @@  discard block
 block discarded – undo
507 507
 		// Find all objectid/tagId pairs.
508 508
 		$result = null;
509 509
 		try {
510
-			$stmt = \OC_DB::prepare('SELECT `id` FROM `' . self::TAG_TABLE . '` '
510
+			$stmt = \OC_DB::prepare('SELECT `id` FROM `'.self::TAG_TABLE.'` '
511 511
 				. 'WHERE `uid` = ?');
512 512
 			$result = $stmt->execute([$arguments['uid']]);
513 513
 			if ($result === null) {
514
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
514
+				\OCP\Util::writeLog('core', __METHOD__.'DB error: '.\OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
515 515
 			}
516 516
 		} catch (\Exception $e) {
517 517
 			\OC::$server->getLogger()->logException($e, [
@@ -523,7 +523,7 @@  discard block
 block discarded – undo
523 523
 
524 524
 		if (!is_null($result)) {
525 525
 			try {
526
-				$stmt = \OC_DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` '
526
+				$stmt = \OC_DB::prepare('DELETE FROM `'.self::RELATION_TABLE.'` '
527 527
 					. 'WHERE `categoryid` = ?');
528 528
 				while ($row = $result->fetchRow()) {
529 529
 					try {
@@ -546,11 +546,11 @@  discard block
 block discarded – undo
546 546
 			}
547 547
 		}
548 548
 		try {
549
-			$stmt = \OC_DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` '
549
+			$stmt = \OC_DB::prepare('DELETE FROM `'.self::TAG_TABLE.'` '
550 550
 				. 'WHERE `uid` = ?');
551 551
 			$result = $stmt->execute([$arguments['uid']]);
552 552
 			if ($result === null) {
553
-				\OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
553
+				\OCP\Util::writeLog('core', __METHOD__.', DB error: '.\OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
554 554
 			}
555 555
 		} catch (\Exception $e) {
556 556
 			\OC::$server->getLogger()->logException($e, [
@@ -574,14 +574,14 @@  discard block
 block discarded – undo
574 574
 		}
575 575
 		$updates = $ids;
576 576
 		try {
577
-			$query = 'DELETE FROM `' . self::RELATION_TABLE . '` ';
578
-			$query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids) - 1) . '?) ';
577
+			$query = 'DELETE FROM `'.self::RELATION_TABLE.'` ';
578
+			$query .= 'WHERE `objid` IN ('.str_repeat('?,', count($ids) - 1).'?) ';
579 579
 			$query .= 'AND `type`= ?';
580 580
 			$updates[] = $this->type;
581 581
 			$stmt = \OC_DB::prepare($query);
582 582
 			$result = $stmt->execute($updates);
583 583
 			if ($result === null) {
584
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
584
+				\OCP\Util::writeLog('core', __METHOD__.'DB error: '.\OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
585 585
 				return false;
586 586
 			}
587 587
 		} catch (\Exception $e) {
@@ -699,7 +699,7 @@  discard block
 block discarded – undo
699 699
 		}
700 700
 
701 701
 		try {
702
-			$sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
702
+			$sql = 'DELETE FROM `'.self::RELATION_TABLE.'` '
703 703
 					. 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?';
704 704
 			$stmt = \OC_DB::prepare($sql);
705 705
 			$stmt->execute([$objid, $tagId, $this->type]);
@@ -728,7 +728,7 @@  discard block
 block discarded – undo
728 728
 		$names = array_map('trim', $names);
729 729
 		array_filter($names);
730 730
 
731
-		\OCP\Util::writeLog('core', __METHOD__ . ', before: '
731
+		\OCP\Util::writeLog('core', __METHOD__.', before: '
732 732
 			. print_r($this->tags, true), ILogger::DEBUG);
733 733
 		foreach ($names as $name) {
734 734
 			$id = null;
@@ -744,18 +744,18 @@  discard block
 block discarded – undo
744 744
 				unset($this->tags[$key]);
745 745
 				$this->mapper->delete($tag);
746 746
 			} else {
747
-				\OCP\Util::writeLog('core', __METHOD__ . 'Cannot delete tag ' . $name
747
+				\OCP\Util::writeLog('core', __METHOD__.'Cannot delete tag '.$name
748 748
 					. ': not found.', ILogger::ERROR);
749 749
 			}
750 750
 			if (!is_null($id) && $id !== false) {
751 751
 				try {
752
-					$sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
752
+					$sql = 'DELETE FROM `'.self::RELATION_TABLE.'` '
753 753
 							. 'WHERE `categoryid` = ?';
754 754
 					$stmt = \OC_DB::prepare($sql);
755 755
 					$result = $stmt->execute([$id]);
756 756
 					if ($result === null) {
757 757
 						\OCP\Util::writeLog('core',
758
-							__METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(),
758
+							__METHOD__.'DB error: '.\OC::$server->getDatabaseConnection()->getError(),
759 759
 							ILogger::ERROR);
760 760
 						return false;
761 761
 					}
@@ -778,7 +778,7 @@  discard block
 block discarded – undo
778 778
 			return false;
779 779
 		}
780 780
 		return array_search(strtolower($needle), array_map(
781
-			function ($tag) use ($mem) {
781
+			function($tag) use ($mem) {
782 782
 				return strtolower(call_user_func([$tag, $mem]));
783 783
 			}, $haystack)
784 784
 		);
Please login to merge, or discard this patch.
lib/private/Group/Database.php 1 patch
Indentation   +441 added lines, -441 removed lines patch added patch discarded remove patch
@@ -61,445 +61,445 @@
 block discarded – undo
61 61
  * Class for group management in a SQL Database (e.g. MySQL, SQLite)
62 62
  */
63 63
 class Database extends ABackend implements
64
-	IAddToGroupBackend,
65
-			   ICountDisabledInGroup,
66
-			   ICountUsersBackend,
67
-			   ICreateGroupBackend,
68
-			   IDeleteGroupBackend,
69
-			   IGetDisplayNameBackend,
70
-			   IGroupDetailsBackend,
71
-			   IRemoveFromGroupBackend,
72
-			   ISetDisplayNameBackend {
73
-
74
-	/** @var string[] */
75
-	private $groupCache = [];
76
-
77
-	/** @var IDBConnection */
78
-	private $dbConn;
79
-
80
-	/**
81
-	 * \OC\Group\Database constructor.
82
-	 *
83
-	 * @param IDBConnection|null $dbConn
84
-	 */
85
-	public function __construct(IDBConnection $dbConn = null) {
86
-		$this->dbConn = $dbConn;
87
-	}
88
-
89
-	/**
90
-	 * FIXME: This function should not be required!
91
-	 */
92
-	private function fixDI() {
93
-		if ($this->dbConn === null) {
94
-			$this->dbConn = \OC::$server->getDatabaseConnection();
95
-		}
96
-	}
97
-
98
-	/**
99
-	 * Try to create a new group
100
-	 * @param string $gid The name of the group to create
101
-	 * @return bool
102
-	 *
103
-	 * Tries to create a new group. If the group name already exists, false will
104
-	 * be returned.
105
-	 */
106
-	public function createGroup(string $gid): bool {
107
-		$this->fixDI();
108
-
109
-		try {
110
-			// Add group
111
-			$builder = $this->dbConn->getQueryBuilder();
112
-			$result = $builder->insert('groups')
113
-				->setValue('gid', $builder->createNamedParameter($gid))
114
-				->setValue('displayname', $builder->createNamedParameter($gid))
115
-				->execute();
116
-		} catch (UniqueConstraintViolationException $e) {
117
-			$result = 0;
118
-		}
119
-
120
-		// Add to cache
121
-		$this->groupCache[$gid] = [
122
-			'gid' => $gid,
123
-			'displayname' => $gid
124
-		];
125
-
126
-		return $result === 1;
127
-	}
128
-
129
-	/**
130
-	 * delete a group
131
-	 * @param string $gid gid of the group to delete
132
-	 * @return bool
133
-	 *
134
-	 * Deletes a group and removes it from the group_user-table
135
-	 */
136
-	public function deleteGroup(string $gid): bool {
137
-		$this->fixDI();
138
-
139
-		// Delete the group
140
-		$qb = $this->dbConn->getQueryBuilder();
141
-		$qb->delete('groups')
142
-			->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
143
-			->execute();
144
-
145
-		// Delete the group-user relation
146
-		$qb = $this->dbConn->getQueryBuilder();
147
-		$qb->delete('group_user')
148
-			->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
149
-			->execute();
150
-
151
-		// Delete the group-groupadmin relation
152
-		$qb = $this->dbConn->getQueryBuilder();
153
-		$qb->delete('group_admin')
154
-			->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
155
-			->execute();
156
-
157
-		// Delete from cache
158
-		unset($this->groupCache[$gid]);
159
-
160
-		return true;
161
-	}
162
-
163
-	/**
164
-	 * is user in group?
165
-	 * @param string $uid uid of the user
166
-	 * @param string $gid gid of the group
167
-	 * @return bool
168
-	 *
169
-	 * Checks whether the user is member of a group or not.
170
-	 */
171
-	public function inGroup($uid, $gid) {
172
-		$this->fixDI();
173
-
174
-		// check
175
-		$qb = $this->dbConn->getQueryBuilder();
176
-		$cursor = $qb->select('uid')
177
-			->from('group_user')
178
-			->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
179
-			->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
180
-			->execute();
181
-
182
-		$result = $cursor->fetch();
183
-		$cursor->closeCursor();
184
-
185
-		return $result ? true : false;
186
-	}
187
-
188
-	/**
189
-	 * Add a user to a group
190
-	 * @param string $uid Name of the user to add to group
191
-	 * @param string $gid Name of the group in which add the user
192
-	 * @return bool
193
-	 *
194
-	 * Adds a user to a group.
195
-	 */
196
-	public function addToGroup(string $uid, string $gid): bool {
197
-		$this->fixDI();
198
-
199
-		// No duplicate entries!
200
-		if (!$this->inGroup($uid, $gid)) {
201
-			$qb = $this->dbConn->getQueryBuilder();
202
-			$qb->insert('group_user')
203
-				->setValue('uid', $qb->createNamedParameter($uid))
204
-				->setValue('gid', $qb->createNamedParameter($gid))
205
-				->execute();
206
-			return true;
207
-		} else {
208
-			return false;
209
-		}
210
-	}
211
-
212
-	/**
213
-	 * Removes a user from a group
214
-	 * @param string $uid Name of the user to remove from group
215
-	 * @param string $gid Name of the group from which remove the user
216
-	 * @return bool
217
-	 *
218
-	 * removes the user from a group.
219
-	 */
220
-	public function removeFromGroup(string $uid, string $gid): bool {
221
-		$this->fixDI();
222
-
223
-		$qb = $this->dbConn->getQueryBuilder();
224
-		$qb->delete('group_user')
225
-			->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
226
-			->andWhere($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
227
-			->execute();
228
-
229
-		return true;
230
-	}
231
-
232
-	/**
233
-	 * Get all groups a user belongs to
234
-	 * @param string $uid Name of the user
235
-	 * @return array an array of group names
236
-	 *
237
-	 * This function fetches all groups a user belongs to. It does not check
238
-	 * if the user exists at all.
239
-	 */
240
-	public function getUserGroups($uid) {
241
-		//guests has empty or null $uid
242
-		if ($uid === null || $uid === '') {
243
-			return [];
244
-		}
245
-
246
-		$this->fixDI();
247
-
248
-		// No magic!
249
-		$qb = $this->dbConn->getQueryBuilder();
250
-		$cursor = $qb->select('gu.gid', 'g.displayname')
251
-			->from('group_user', 'gu')
252
-			->leftJoin('gu', 'groups', 'g', $qb->expr()->eq('gu.gid', 'g.gid'))
253
-			->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
254
-			->execute();
255
-
256
-		$groups = [];
257
-		while ($row = $cursor->fetch()) {
258
-			$groups[] = $row['gid'];
259
-			$this->groupCache[$row['gid']] = [
260
-				'gid' => $row['gid'],
261
-				'displayname' => $row['displayname'],
262
-			];
263
-		}
264
-		$cursor->closeCursor();
265
-
266
-		return $groups;
267
-	}
268
-
269
-	/**
270
-	 * get a list of all groups
271
-	 * @param string $search
272
-	 * @param int $limit
273
-	 * @param int $offset
274
-	 * @return array an array of group names
275
-	 *
276
-	 * Returns a list with all groups
277
-	 */
278
-	public function getGroups($search = '', $limit = null, $offset = null) {
279
-		$this->fixDI();
280
-
281
-		$query = $this->dbConn->getQueryBuilder();
282
-		$query->select('gid')
283
-			->from('groups')
284
-			->orderBy('gid', 'ASC');
285
-
286
-		if ($search !== '') {
287
-			$query->where($query->expr()->iLike('gid', $query->createNamedParameter(
288
-				'%' . $this->dbConn->escapeLikeParameter($search) . '%'
289
-			)));
290
-			$query->orWhere($query->expr()->iLike('displayname', $query->createNamedParameter(
291
-				'%' . $this->dbConn->escapeLikeParameter($search) . '%'
292
-			)));
293
-		}
294
-
295
-		$query->setMaxResults($limit)
296
-			->setFirstResult($offset);
297
-		$result = $query->execute();
298
-
299
-		$groups = [];
300
-		while ($row = $result->fetch()) {
301
-			$groups[] = $row['gid'];
302
-		}
303
-		$result->closeCursor();
304
-
305
-		return $groups;
306
-	}
307
-
308
-	/**
309
-	 * check if a group exists
310
-	 * @param string $gid
311
-	 * @return bool
312
-	 */
313
-	public function groupExists($gid) {
314
-		$this->fixDI();
315
-
316
-		// Check cache first
317
-		if (isset($this->groupCache[$gid])) {
318
-			return true;
319
-		}
320
-
321
-		$qb = $this->dbConn->getQueryBuilder();
322
-		$cursor = $qb->select('gid', 'displayname')
323
-			->from('groups')
324
-			->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
325
-			->execute();
326
-		$result = $cursor->fetch();
327
-		$cursor->closeCursor();
328
-
329
-		if ($result !== false) {
330
-			$this->groupCache[$gid] = [
331
-				'gid' => $gid,
332
-				'displayname' => $result['displayname'],
333
-			];
334
-			return true;
335
-		}
336
-		return false;
337
-	}
338
-
339
-	/**
340
-	 * get a list of all users in a group
341
-	 * @param string $gid
342
-	 * @param string $search
343
-	 * @param int $limit
344
-	 * @param int $offset
345
-	 * @return array an array of user ids
346
-	 */
347
-	public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
348
-		$this->fixDI();
349
-
350
-		$query = $this->dbConn->getQueryBuilder();
351
-		$query->select('g.uid')
352
-			->from('group_user', 'g')
353
-			->where($query->expr()->eq('gid', $query->createNamedParameter($gid)))
354
-			->orderBy('g.uid', 'ASC');
355
-
356
-		if ($search !== '') {
357
-			$query->leftJoin('g', 'users', 'u', $query->expr()->eq('g.uid', 'u.uid'))
358
-				->leftJoin('u', 'preferences', 'p', $query->expr()->andX(
359
-					$query->expr()->eq('p.userid', 'u.uid'),
360
-					$query->expr()->eq('p.appid', $query->expr()->literal('settings')),
361
-					$query->expr()->eq('p.configkey', $query->expr()->literal('email')))
362
-				)
363
-				// sqlite doesn't like re-using a single named parameter here
364
-				->andWhere(
365
-					$query->expr()->orX(
366
-						$query->expr()->ilike('g.uid', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')),
367
-						$query->expr()->ilike('u.displayname', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')),
368
-						$query->expr()->ilike('p.configvalue', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%'))
369
-					)
370
-				)
371
-				->orderBy('u.uid_lower', 'ASC');
372
-		}
373
-
374
-		if ($limit !== -1) {
375
-			$query->setMaxResults($limit);
376
-		}
377
-		if ($offset !== 0) {
378
-			$query->setFirstResult($offset);
379
-		}
380
-
381
-		$result = $query->execute();
382
-
383
-		$users = [];
384
-		while ($row = $result->fetch()) {
385
-			$users[] = $row['uid'];
386
-		}
387
-		$result->closeCursor();
388
-
389
-		return $users;
390
-	}
391
-
392
-	/**
393
-	 * get the number of all users matching the search string in a group
394
-	 * @param string $gid
395
-	 * @param string $search
396
-	 * @return int
397
-	 */
398
-	public function countUsersInGroup(string $gid, string $search = ''): int {
399
-		$this->fixDI();
400
-
401
-		$query = $this->dbConn->getQueryBuilder();
402
-		$query->select($query->func()->count('*', 'num_users'))
403
-			->from('group_user')
404
-			->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
405
-
406
-		if ($search !== '') {
407
-			$query->andWhere($query->expr()->like('uid', $query->createNamedParameter(
408
-				'%' . $this->dbConn->escapeLikeParameter($search) . '%'
409
-			)));
410
-		}
411
-
412
-		$result = $query->execute();
413
-		$count = $result->fetchOne();
414
-		$result->closeCursor();
415
-
416
-		if ($count !== false) {
417
-			$count = (int)$count;
418
-		} else {
419
-			$count = 0;
420
-		}
421
-
422
-		return $count;
423
-	}
424
-
425
-	/**
426
-	 * get the number of disabled users in a group
427
-	 *
428
-	 * @param string $search
429
-	 *
430
-	 * @return int
431
-	 */
432
-	public function countDisabledInGroup(string $gid): int {
433
-		$this->fixDI();
434
-
435
-		$query = $this->dbConn->getQueryBuilder();
436
-		$query->select($query->createFunction('COUNT(DISTINCT ' . $query->getColumnName('uid') . ')'))
437
-			->from('preferences', 'p')
438
-			->innerJoin('p', 'group_user', 'g', $query->expr()->eq('p.userid', 'g.uid'))
439
-			->where($query->expr()->eq('appid', $query->createNamedParameter('core')))
440
-			->andWhere($query->expr()->eq('configkey', $query->createNamedParameter('enabled')))
441
-			->andWhere($query->expr()->eq('configvalue', $query->createNamedParameter('false'), IQueryBuilder::PARAM_STR))
442
-			->andWhere($query->expr()->eq('gid', $query->createNamedParameter($gid), IQueryBuilder::PARAM_STR));
443
-
444
-		$result = $query->execute();
445
-		$count = $result->fetchOne();
446
-		$result->closeCursor();
447
-
448
-		if ($count !== false) {
449
-			$count = (int)$count;
450
-		} else {
451
-			$count = 0;
452
-		}
453
-
454
-		return $count;
455
-	}
456
-
457
-	public function getDisplayName(string $gid): string {
458
-		if (isset($this->groupCache[$gid])) {
459
-			return $this->groupCache[$gid]['displayname'];
460
-		}
461
-
462
-		$this->fixDI();
463
-
464
-		$query = $this->dbConn->getQueryBuilder();
465
-		$query->select('displayname')
466
-			->from('groups')
467
-			->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
468
-
469
-		$result = $query->execute();
470
-		$displayName = $result->fetchOne();
471
-		$result->closeCursor();
472
-
473
-		return (string) $displayName;
474
-	}
475
-
476
-	public function getGroupDetails(string $gid): array {
477
-		$displayName = $this->getDisplayName($gid);
478
-		if ($displayName !== '') {
479
-			return ['displayName' => $displayName];
480
-		}
481
-
482
-		return [];
483
-	}
484
-
485
-	public function setDisplayName(string $gid, string $displayName): bool {
486
-		if (!$this->groupExists($gid)) {
487
-			return false;
488
-		}
489
-
490
-		$this->fixDI();
491
-
492
-		$displayName = trim($displayName);
493
-		if ($displayName === '') {
494
-			$displayName = $gid;
495
-		}
496
-
497
-		$query = $this->dbConn->getQueryBuilder();
498
-		$query->update('groups')
499
-			->set('displayname', $query->createNamedParameter($displayName))
500
-			->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
501
-		$query->execute();
502
-
503
-		return true;
504
-	}
64
+    IAddToGroupBackend,
65
+                ICountDisabledInGroup,
66
+                ICountUsersBackend,
67
+                ICreateGroupBackend,
68
+                IDeleteGroupBackend,
69
+                IGetDisplayNameBackend,
70
+                IGroupDetailsBackend,
71
+                IRemoveFromGroupBackend,
72
+                ISetDisplayNameBackend {
73
+
74
+    /** @var string[] */
75
+    private $groupCache = [];
76
+
77
+    /** @var IDBConnection */
78
+    private $dbConn;
79
+
80
+    /**
81
+     * \OC\Group\Database constructor.
82
+     *
83
+     * @param IDBConnection|null $dbConn
84
+     */
85
+    public function __construct(IDBConnection $dbConn = null) {
86
+        $this->dbConn = $dbConn;
87
+    }
88
+
89
+    /**
90
+     * FIXME: This function should not be required!
91
+     */
92
+    private function fixDI() {
93
+        if ($this->dbConn === null) {
94
+            $this->dbConn = \OC::$server->getDatabaseConnection();
95
+        }
96
+    }
97
+
98
+    /**
99
+     * Try to create a new group
100
+     * @param string $gid The name of the group to create
101
+     * @return bool
102
+     *
103
+     * Tries to create a new group. If the group name already exists, false will
104
+     * be returned.
105
+     */
106
+    public function createGroup(string $gid): bool {
107
+        $this->fixDI();
108
+
109
+        try {
110
+            // Add group
111
+            $builder = $this->dbConn->getQueryBuilder();
112
+            $result = $builder->insert('groups')
113
+                ->setValue('gid', $builder->createNamedParameter($gid))
114
+                ->setValue('displayname', $builder->createNamedParameter($gid))
115
+                ->execute();
116
+        } catch (UniqueConstraintViolationException $e) {
117
+            $result = 0;
118
+        }
119
+
120
+        // Add to cache
121
+        $this->groupCache[$gid] = [
122
+            'gid' => $gid,
123
+            'displayname' => $gid
124
+        ];
125
+
126
+        return $result === 1;
127
+    }
128
+
129
+    /**
130
+     * delete a group
131
+     * @param string $gid gid of the group to delete
132
+     * @return bool
133
+     *
134
+     * Deletes a group and removes it from the group_user-table
135
+     */
136
+    public function deleteGroup(string $gid): bool {
137
+        $this->fixDI();
138
+
139
+        // Delete the group
140
+        $qb = $this->dbConn->getQueryBuilder();
141
+        $qb->delete('groups')
142
+            ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
143
+            ->execute();
144
+
145
+        // Delete the group-user relation
146
+        $qb = $this->dbConn->getQueryBuilder();
147
+        $qb->delete('group_user')
148
+            ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
149
+            ->execute();
150
+
151
+        // Delete the group-groupadmin relation
152
+        $qb = $this->dbConn->getQueryBuilder();
153
+        $qb->delete('group_admin')
154
+            ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
155
+            ->execute();
156
+
157
+        // Delete from cache
158
+        unset($this->groupCache[$gid]);
159
+
160
+        return true;
161
+    }
162
+
163
+    /**
164
+     * is user in group?
165
+     * @param string $uid uid of the user
166
+     * @param string $gid gid of the group
167
+     * @return bool
168
+     *
169
+     * Checks whether the user is member of a group or not.
170
+     */
171
+    public function inGroup($uid, $gid) {
172
+        $this->fixDI();
173
+
174
+        // check
175
+        $qb = $this->dbConn->getQueryBuilder();
176
+        $cursor = $qb->select('uid')
177
+            ->from('group_user')
178
+            ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
179
+            ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
180
+            ->execute();
181
+
182
+        $result = $cursor->fetch();
183
+        $cursor->closeCursor();
184
+
185
+        return $result ? true : false;
186
+    }
187
+
188
+    /**
189
+     * Add a user to a group
190
+     * @param string $uid Name of the user to add to group
191
+     * @param string $gid Name of the group in which add the user
192
+     * @return bool
193
+     *
194
+     * Adds a user to a group.
195
+     */
196
+    public function addToGroup(string $uid, string $gid): bool {
197
+        $this->fixDI();
198
+
199
+        // No duplicate entries!
200
+        if (!$this->inGroup($uid, $gid)) {
201
+            $qb = $this->dbConn->getQueryBuilder();
202
+            $qb->insert('group_user')
203
+                ->setValue('uid', $qb->createNamedParameter($uid))
204
+                ->setValue('gid', $qb->createNamedParameter($gid))
205
+                ->execute();
206
+            return true;
207
+        } else {
208
+            return false;
209
+        }
210
+    }
211
+
212
+    /**
213
+     * Removes a user from a group
214
+     * @param string $uid Name of the user to remove from group
215
+     * @param string $gid Name of the group from which remove the user
216
+     * @return bool
217
+     *
218
+     * removes the user from a group.
219
+     */
220
+    public function removeFromGroup(string $uid, string $gid): bool {
221
+        $this->fixDI();
222
+
223
+        $qb = $this->dbConn->getQueryBuilder();
224
+        $qb->delete('group_user')
225
+            ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
226
+            ->andWhere($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
227
+            ->execute();
228
+
229
+        return true;
230
+    }
231
+
232
+    /**
233
+     * Get all groups a user belongs to
234
+     * @param string $uid Name of the user
235
+     * @return array an array of group names
236
+     *
237
+     * This function fetches all groups a user belongs to. It does not check
238
+     * if the user exists at all.
239
+     */
240
+    public function getUserGroups($uid) {
241
+        //guests has empty or null $uid
242
+        if ($uid === null || $uid === '') {
243
+            return [];
244
+        }
245
+
246
+        $this->fixDI();
247
+
248
+        // No magic!
249
+        $qb = $this->dbConn->getQueryBuilder();
250
+        $cursor = $qb->select('gu.gid', 'g.displayname')
251
+            ->from('group_user', 'gu')
252
+            ->leftJoin('gu', 'groups', 'g', $qb->expr()->eq('gu.gid', 'g.gid'))
253
+            ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
254
+            ->execute();
255
+
256
+        $groups = [];
257
+        while ($row = $cursor->fetch()) {
258
+            $groups[] = $row['gid'];
259
+            $this->groupCache[$row['gid']] = [
260
+                'gid' => $row['gid'],
261
+                'displayname' => $row['displayname'],
262
+            ];
263
+        }
264
+        $cursor->closeCursor();
265
+
266
+        return $groups;
267
+    }
268
+
269
+    /**
270
+     * get a list of all groups
271
+     * @param string $search
272
+     * @param int $limit
273
+     * @param int $offset
274
+     * @return array an array of group names
275
+     *
276
+     * Returns a list with all groups
277
+     */
278
+    public function getGroups($search = '', $limit = null, $offset = null) {
279
+        $this->fixDI();
280
+
281
+        $query = $this->dbConn->getQueryBuilder();
282
+        $query->select('gid')
283
+            ->from('groups')
284
+            ->orderBy('gid', 'ASC');
285
+
286
+        if ($search !== '') {
287
+            $query->where($query->expr()->iLike('gid', $query->createNamedParameter(
288
+                '%' . $this->dbConn->escapeLikeParameter($search) . '%'
289
+            )));
290
+            $query->orWhere($query->expr()->iLike('displayname', $query->createNamedParameter(
291
+                '%' . $this->dbConn->escapeLikeParameter($search) . '%'
292
+            )));
293
+        }
294
+
295
+        $query->setMaxResults($limit)
296
+            ->setFirstResult($offset);
297
+        $result = $query->execute();
298
+
299
+        $groups = [];
300
+        while ($row = $result->fetch()) {
301
+            $groups[] = $row['gid'];
302
+        }
303
+        $result->closeCursor();
304
+
305
+        return $groups;
306
+    }
307
+
308
+    /**
309
+     * check if a group exists
310
+     * @param string $gid
311
+     * @return bool
312
+     */
313
+    public function groupExists($gid) {
314
+        $this->fixDI();
315
+
316
+        // Check cache first
317
+        if (isset($this->groupCache[$gid])) {
318
+            return true;
319
+        }
320
+
321
+        $qb = $this->dbConn->getQueryBuilder();
322
+        $cursor = $qb->select('gid', 'displayname')
323
+            ->from('groups')
324
+            ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
325
+            ->execute();
326
+        $result = $cursor->fetch();
327
+        $cursor->closeCursor();
328
+
329
+        if ($result !== false) {
330
+            $this->groupCache[$gid] = [
331
+                'gid' => $gid,
332
+                'displayname' => $result['displayname'],
333
+            ];
334
+            return true;
335
+        }
336
+        return false;
337
+    }
338
+
339
+    /**
340
+     * get a list of all users in a group
341
+     * @param string $gid
342
+     * @param string $search
343
+     * @param int $limit
344
+     * @param int $offset
345
+     * @return array an array of user ids
346
+     */
347
+    public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
348
+        $this->fixDI();
349
+
350
+        $query = $this->dbConn->getQueryBuilder();
351
+        $query->select('g.uid')
352
+            ->from('group_user', 'g')
353
+            ->where($query->expr()->eq('gid', $query->createNamedParameter($gid)))
354
+            ->orderBy('g.uid', 'ASC');
355
+
356
+        if ($search !== '') {
357
+            $query->leftJoin('g', 'users', 'u', $query->expr()->eq('g.uid', 'u.uid'))
358
+                ->leftJoin('u', 'preferences', 'p', $query->expr()->andX(
359
+                    $query->expr()->eq('p.userid', 'u.uid'),
360
+                    $query->expr()->eq('p.appid', $query->expr()->literal('settings')),
361
+                    $query->expr()->eq('p.configkey', $query->expr()->literal('email')))
362
+                )
363
+                // sqlite doesn't like re-using a single named parameter here
364
+                ->andWhere(
365
+                    $query->expr()->orX(
366
+                        $query->expr()->ilike('g.uid', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')),
367
+                        $query->expr()->ilike('u.displayname', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')),
368
+                        $query->expr()->ilike('p.configvalue', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%'))
369
+                    )
370
+                )
371
+                ->orderBy('u.uid_lower', 'ASC');
372
+        }
373
+
374
+        if ($limit !== -1) {
375
+            $query->setMaxResults($limit);
376
+        }
377
+        if ($offset !== 0) {
378
+            $query->setFirstResult($offset);
379
+        }
380
+
381
+        $result = $query->execute();
382
+
383
+        $users = [];
384
+        while ($row = $result->fetch()) {
385
+            $users[] = $row['uid'];
386
+        }
387
+        $result->closeCursor();
388
+
389
+        return $users;
390
+    }
391
+
392
+    /**
393
+     * get the number of all users matching the search string in a group
394
+     * @param string $gid
395
+     * @param string $search
396
+     * @return int
397
+     */
398
+    public function countUsersInGroup(string $gid, string $search = ''): int {
399
+        $this->fixDI();
400
+
401
+        $query = $this->dbConn->getQueryBuilder();
402
+        $query->select($query->func()->count('*', 'num_users'))
403
+            ->from('group_user')
404
+            ->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
405
+
406
+        if ($search !== '') {
407
+            $query->andWhere($query->expr()->like('uid', $query->createNamedParameter(
408
+                '%' . $this->dbConn->escapeLikeParameter($search) . '%'
409
+            )));
410
+        }
411
+
412
+        $result = $query->execute();
413
+        $count = $result->fetchOne();
414
+        $result->closeCursor();
415
+
416
+        if ($count !== false) {
417
+            $count = (int)$count;
418
+        } else {
419
+            $count = 0;
420
+        }
421
+
422
+        return $count;
423
+    }
424
+
425
+    /**
426
+     * get the number of disabled users in a group
427
+     *
428
+     * @param string $search
429
+     *
430
+     * @return int
431
+     */
432
+    public function countDisabledInGroup(string $gid): int {
433
+        $this->fixDI();
434
+
435
+        $query = $this->dbConn->getQueryBuilder();
436
+        $query->select($query->createFunction('COUNT(DISTINCT ' . $query->getColumnName('uid') . ')'))
437
+            ->from('preferences', 'p')
438
+            ->innerJoin('p', 'group_user', 'g', $query->expr()->eq('p.userid', 'g.uid'))
439
+            ->where($query->expr()->eq('appid', $query->createNamedParameter('core')))
440
+            ->andWhere($query->expr()->eq('configkey', $query->createNamedParameter('enabled')))
441
+            ->andWhere($query->expr()->eq('configvalue', $query->createNamedParameter('false'), IQueryBuilder::PARAM_STR))
442
+            ->andWhere($query->expr()->eq('gid', $query->createNamedParameter($gid), IQueryBuilder::PARAM_STR));
443
+
444
+        $result = $query->execute();
445
+        $count = $result->fetchOne();
446
+        $result->closeCursor();
447
+
448
+        if ($count !== false) {
449
+            $count = (int)$count;
450
+        } else {
451
+            $count = 0;
452
+        }
453
+
454
+        return $count;
455
+    }
456
+
457
+    public function getDisplayName(string $gid): string {
458
+        if (isset($this->groupCache[$gid])) {
459
+            return $this->groupCache[$gid]['displayname'];
460
+        }
461
+
462
+        $this->fixDI();
463
+
464
+        $query = $this->dbConn->getQueryBuilder();
465
+        $query->select('displayname')
466
+            ->from('groups')
467
+            ->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
468
+
469
+        $result = $query->execute();
470
+        $displayName = $result->fetchOne();
471
+        $result->closeCursor();
472
+
473
+        return (string) $displayName;
474
+    }
475
+
476
+    public function getGroupDetails(string $gid): array {
477
+        $displayName = $this->getDisplayName($gid);
478
+        if ($displayName !== '') {
479
+            return ['displayName' => $displayName];
480
+        }
481
+
482
+        return [];
483
+    }
484
+
485
+    public function setDisplayName(string $gid, string $displayName): bool {
486
+        if (!$this->groupExists($gid)) {
487
+            return false;
488
+        }
489
+
490
+        $this->fixDI();
491
+
492
+        $displayName = trim($displayName);
493
+        if ($displayName === '') {
494
+            $displayName = $gid;
495
+        }
496
+
497
+        $query = $this->dbConn->getQueryBuilder();
498
+        $query->update('groups')
499
+            ->set('displayname', $query->createNamedParameter($displayName))
500
+            ->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
501
+        $query->execute();
502
+
503
+        return true;
504
+    }
505 505
 }
Please login to merge, or discard this patch.
lib/private/Repair/Collation.php 1 patch
Indentation   +115 added lines, -115 removed lines patch added patch discarded remove patch
@@ -36,119 +36,119 @@
 block discarded – undo
36 36
 use OCP\Migration\IRepairStep;
37 37
 
38 38
 class Collation implements IRepairStep {
39
-	/**  @var IConfig */
40
-	protected $config;
41
-
42
-	/** @var ILogger */
43
-	protected $logger;
44
-
45
-	/** @var IDBConnection */
46
-	protected $connection;
47
-
48
-	/** @var bool */
49
-	protected $ignoreFailures;
50
-
51
-	/**
52
-	 * @param IConfig $config
53
-	 * @param ILogger $logger
54
-	 * @param IDBConnection $connection
55
-	 * @param bool $ignoreFailures
56
-	 */
57
-	public function __construct(IConfig $config, ILogger $logger, IDBConnection $connection, $ignoreFailures) {
58
-		$this->connection = $connection;
59
-		$this->config = $config;
60
-		$this->logger = $logger;
61
-		$this->ignoreFailures = $ignoreFailures;
62
-	}
63
-
64
-	public function getName() {
65
-		return 'Repair MySQL collation';
66
-	}
67
-
68
-	/**
69
-	 * Fix mime types
70
-	 */
71
-	public function run(IOutput $output) {
72
-		if (!$this->connection->getDatabasePlatform() instanceof MySQLPlatform) {
73
-			$output->info('Not a mysql database -> nothing to do');
74
-			return;
75
-		}
76
-
77
-		$characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
78
-
79
-		$tables = $this->getAllNonUTF8BinTables($this->connection);
80
-		foreach ($tables as $table) {
81
-			$output->info("Change row format for $table ...");
82
-			$query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT = DYNAMIC;');
83
-			try {
84
-				$query->execute();
85
-			} catch (DriverException $e) {
86
-				// Just log this
87
-				$this->logger->logException($e);
88
-				if (!$this->ignoreFailures) {
89
-					throw $e;
90
-				}
91
-			}
92
-
93
-			$output->info("Change collation for $table ...");
94
-			if ($characterSet === 'utf8mb4') {
95
-				// need to set row compression first
96
-				$query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT=COMPRESSED;');
97
-				$query->execute();
98
-			}
99
-			$query = $this->connection->prepare('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET ' . $characterSet . ' COLLATE ' . $characterSet . '_bin;');
100
-			try {
101
-				$query->execute();
102
-			} catch (DriverException $e) {
103
-				// Just log this
104
-				$this->logger->logException($e);
105
-				if (!$this->ignoreFailures) {
106
-					throw $e;
107
-				}
108
-			}
109
-		}
110
-		if (empty($tables)) {
111
-			$output->info('All tables already have the correct collation -> nothing to do');
112
-		}
113
-	}
114
-
115
-	/**
116
-	 * @param IDBConnection $connection
117
-	 * @return string[]
118
-	 */
119
-	protected function getAllNonUTF8BinTables(IDBConnection $connection) {
120
-		$dbName = $this->config->getSystemValue("dbname");
121
-		$characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
122
-
123
-		// fetch tables by columns
124
-		$statement = $connection->executeQuery(
125
-			"SELECT DISTINCT(TABLE_NAME) AS `table`" .
126
-			"	FROM INFORMATION_SCHEMA . COLUMNS" .
127
-			"	WHERE TABLE_SCHEMA = ?" .
128
-			"	AND (COLLATION_NAME <> '" . $characterSet . "_bin' OR CHARACTER_SET_NAME <> '" . $characterSet . "')" .
129
-			"	AND TABLE_NAME LIKE '*PREFIX*%'",
130
-			[$dbName]
131
-		);
132
-		$rows = $statement->fetchAll();
133
-		$result = [];
134
-		foreach ($rows as $row) {
135
-			$result[$row['table']] = true;
136
-		}
137
-
138
-		// fetch tables by collation
139
-		$statement = $connection->executeQuery(
140
-			"SELECT DISTINCT(TABLE_NAME) AS `table`" .
141
-			"	FROM INFORMATION_SCHEMA . TABLES" .
142
-			"	WHERE TABLE_SCHEMA = ?" .
143
-			"	AND TABLE_COLLATION <> '" . $characterSet . "_bin'" .
144
-			"	AND TABLE_NAME LIKE '*PREFIX*%'",
145
-			[$dbName]
146
-		);
147
-		$rows = $statement->fetchAll();
148
-		foreach ($rows as $row) {
149
-			$result[$row['table']] = true;
150
-		}
151
-
152
-		return array_keys($result);
153
-	}
39
+    /**  @var IConfig */
40
+    protected $config;
41
+
42
+    /** @var ILogger */
43
+    protected $logger;
44
+
45
+    /** @var IDBConnection */
46
+    protected $connection;
47
+
48
+    /** @var bool */
49
+    protected $ignoreFailures;
50
+
51
+    /**
52
+     * @param IConfig $config
53
+     * @param ILogger $logger
54
+     * @param IDBConnection $connection
55
+     * @param bool $ignoreFailures
56
+     */
57
+    public function __construct(IConfig $config, ILogger $logger, IDBConnection $connection, $ignoreFailures) {
58
+        $this->connection = $connection;
59
+        $this->config = $config;
60
+        $this->logger = $logger;
61
+        $this->ignoreFailures = $ignoreFailures;
62
+    }
63
+
64
+    public function getName() {
65
+        return 'Repair MySQL collation';
66
+    }
67
+
68
+    /**
69
+     * Fix mime types
70
+     */
71
+    public function run(IOutput $output) {
72
+        if (!$this->connection->getDatabasePlatform() instanceof MySQLPlatform) {
73
+            $output->info('Not a mysql database -> nothing to do');
74
+            return;
75
+        }
76
+
77
+        $characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
78
+
79
+        $tables = $this->getAllNonUTF8BinTables($this->connection);
80
+        foreach ($tables as $table) {
81
+            $output->info("Change row format for $table ...");
82
+            $query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT = DYNAMIC;');
83
+            try {
84
+                $query->execute();
85
+            } catch (DriverException $e) {
86
+                // Just log this
87
+                $this->logger->logException($e);
88
+                if (!$this->ignoreFailures) {
89
+                    throw $e;
90
+                }
91
+            }
92
+
93
+            $output->info("Change collation for $table ...");
94
+            if ($characterSet === 'utf8mb4') {
95
+                // need to set row compression first
96
+                $query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT=COMPRESSED;');
97
+                $query->execute();
98
+            }
99
+            $query = $this->connection->prepare('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET ' . $characterSet . ' COLLATE ' . $characterSet . '_bin;');
100
+            try {
101
+                $query->execute();
102
+            } catch (DriverException $e) {
103
+                // Just log this
104
+                $this->logger->logException($e);
105
+                if (!$this->ignoreFailures) {
106
+                    throw $e;
107
+                }
108
+            }
109
+        }
110
+        if (empty($tables)) {
111
+            $output->info('All tables already have the correct collation -> nothing to do');
112
+        }
113
+    }
114
+
115
+    /**
116
+     * @param IDBConnection $connection
117
+     * @return string[]
118
+     */
119
+    protected function getAllNonUTF8BinTables(IDBConnection $connection) {
120
+        $dbName = $this->config->getSystemValue("dbname");
121
+        $characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
122
+
123
+        // fetch tables by columns
124
+        $statement = $connection->executeQuery(
125
+            "SELECT DISTINCT(TABLE_NAME) AS `table`" .
126
+            "	FROM INFORMATION_SCHEMA . COLUMNS" .
127
+            "	WHERE TABLE_SCHEMA = ?" .
128
+            "	AND (COLLATION_NAME <> '" . $characterSet . "_bin' OR CHARACTER_SET_NAME <> '" . $characterSet . "')" .
129
+            "	AND TABLE_NAME LIKE '*PREFIX*%'",
130
+            [$dbName]
131
+        );
132
+        $rows = $statement->fetchAll();
133
+        $result = [];
134
+        foreach ($rows as $row) {
135
+            $result[$row['table']] = true;
136
+        }
137
+
138
+        // fetch tables by collation
139
+        $statement = $connection->executeQuery(
140
+            "SELECT DISTINCT(TABLE_NAME) AS `table`" .
141
+            "	FROM INFORMATION_SCHEMA . TABLES" .
142
+            "	WHERE TABLE_SCHEMA = ?" .
143
+            "	AND TABLE_COLLATION <> '" . $characterSet . "_bin'" .
144
+            "	AND TABLE_NAME LIKE '*PREFIX*%'",
145
+            [$dbName]
146
+        );
147
+        $rows = $statement->fetchAll();
148
+        foreach ($rows as $row) {
149
+            $result[$row['table']] = true;
150
+        }
151
+
152
+        return array_keys($result);
153
+    }
154 154
 }
Please login to merge, or discard this patch.
lib/private/Repair/RepairMimeTypes.php 2 patches
Indentation   +206 added lines, -206 removed lines patch added patch discarded remove patch
@@ -41,210 +41,210 @@
 block discarded – undo
41 41
 use OCP\Migration\IRepairStep;
42 42
 
43 43
 class RepairMimeTypes implements IRepairStep {
44
-	/** @var IConfig */
45
-	protected $config;
46
-	/** @var IDBConnection */
47
-	protected $connection;
48
-
49
-	/** @var int */
50
-	protected $folderMimeTypeId;
51
-
52
-	public function __construct(IConfig $config,
53
-								IDBConnection $connection) {
54
-		$this->config = $config;
55
-		$this->connection = $connection;
56
-	}
57
-
58
-	public function getName() {
59
-		return 'Repair mime types';
60
-	}
61
-
62
-	private function updateMimetypes($updatedMimetypes) {
63
-		$query = $this->connection->getQueryBuilder();
64
-		$query->select('id')
65
-			->from('mimetypes')
66
-			->where($query->expr()->eq('mimetype', $query->createParameter('mimetype'), IQueryBuilder::PARAM_INT));
67
-		$insert = $this->connection->getQueryBuilder();
68
-		$insert->insert('mimetypes')
69
-			->setValue('mimetype', $insert->createParameter('mimetype'));
70
-
71
-		if (empty($this->folderMimeTypeId)) {
72
-			$query->setParameter('mimetype', 'httpd/unix-directory');
73
-			$result = $query->execute();
74
-			$this->folderMimeTypeId = (int)$result->fetchOne();
75
-			$result->closeCursor();
76
-		}
77
-
78
-		$update = $this->connection->getQueryBuilder();
79
-		$update->update('filecache')
80
-			->set('mimetype', $update->createParameter('mimetype'))
81
-			->where($update->expr()->neq('mimetype', $update->createParameter('mimetype'), IQueryBuilder::PARAM_INT))
82
-			->andWhere($update->expr()->neq('mimetype', $update->createParameter('folder'), IQueryBuilder::PARAM_INT))
83
-			->andWhere($update->expr()->iLike('name', $update->createParameter('name')))
84
-			->setParameter('folder', $this->folderMimeTypeId);
85
-
86
-		$count = 0;
87
-		foreach ($updatedMimetypes as $extension => $mimetype) {
88
-			// get target mimetype id
89
-			$query->setParameter('mimetype', $mimetype);
90
-			$result = $query->execute();
91
-			$mimetypeId = (int)$result->fetchOne();
92
-			$result->closeCursor();
93
-
94
-			if (!$mimetypeId) {
95
-				// insert mimetype
96
-				$insert->setParameter('mimetype', $mimetype);
97
-				$insert->execute();
98
-				$mimetypeId = $insert->getLastInsertId();
99
-			}
100
-
101
-			// change mimetype for files with x extension
102
-			$update->setParameter('mimetype', $mimetypeId)
103
-				->setParameter('name', '%' . $this->connection->escapeLikeParameter('.' . $extension));
104
-			$count += $update->execute();
105
-		}
106
-
107
-		return $count;
108
-	}
109
-
110
-	private function introduceImageTypes() {
111
-		$updatedMimetypes = [
112
-			'jp2' => 'image/jp2',
113
-			'webp' => 'image/webp',
114
-		];
115
-
116
-		return $this->updateMimetypes($updatedMimetypes);
117
-	}
118
-
119
-	private function introduceWindowsProgramTypes() {
120
-		$updatedMimetypes = [
121
-			'htaccess' => 'text/plain',
122
-			'bat' => 'application/x-msdos-program',
123
-			'cmd' => 'application/cmd',
124
-		];
125
-
126
-		return $this->updateMimetypes($updatedMimetypes);
127
-	}
128
-
129
-	private function introduceLocationTypes() {
130
-		$updatedMimetypes = [
131
-			'gpx' => 'application/gpx+xml',
132
-			'kml' => 'application/vnd.google-earth.kml+xml',
133
-			'kmz' => 'application/vnd.google-earth.kmz',
134
-			'tcx' => 'application/vnd.garmin.tcx+xml',
135
-		];
136
-
137
-		return $this->updateMimetypes($updatedMimetypes);
138
-	}
139
-
140
-	private function introduceInternetShortcutTypes() {
141
-		$updatedMimetypes = [
142
-			'url' => 'application/internet-shortcut',
143
-			'webloc' => 'application/internet-shortcut'
144
-		];
145
-
146
-		return $this->updateMimetypes($updatedMimetypes);
147
-	}
148
-
149
-	private function introduceStreamingTypes() {
150
-		$updatedMimetypes = [
151
-			'm3u' => 'audio/mpegurl',
152
-			'm3u8' => 'audio/mpegurl',
153
-			'pls' => 'audio/x-scpls'
154
-		];
155
-
156
-		return $this->updateMimetypes($updatedMimetypes);
157
-	}
158
-
159
-	private function introduceVisioTypes() {
160
-		$updatedMimetypes = [
161
-			'vsdm' => 'application/vnd.visio',
162
-			'vsdx' => 'application/vnd.visio',
163
-			'vssm' => 'application/vnd.visio',
164
-			'vssx' => 'application/vnd.visio',
165
-			'vstm' => 'application/vnd.visio',
166
-			'vstx' => 'application/vnd.visio',
167
-		];
168
-
169
-		return $this->updateMimetypes($updatedMimetypes);
170
-	}
171
-
172
-	private function introduceComicbookTypes() {
173
-		$updatedMimetypes = [
174
-			'cb7' => 'application/comicbook+7z',
175
-			'cba' => 'application/comicbook+ace',
176
-			'cbr' => 'application/comicbook+rar',
177
-			'cbt' => 'application/comicbook+tar',
178
-			'cbtc' => 'application/comicbook+truecrypt',
179
-			'cbz' => 'application/comicbook+zip',
180
-		];
181
-
182
-		return $this->updateMimetypes($updatedMimetypes);
183
-	}
184
-
185
-	private function introduceOpenDocumentTemplates() {
186
-		$updatedMimetypes = [
187
-			'ott' => 'application/vnd.oasis.opendocument.text-template',
188
-			'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
189
-			'otp' => 'application/vnd.oasis.opendocument.presentation-template',
190
-			'otg' => 'application/vnd.oasis.opendocument.graphics-template',
191
-		];
192
-
193
-		return $this->updateMimetypes($updatedMimetypes);
194
-	}
195
-
196
-	private function introduceOrgModeType() {
197
-		$updatedMimetypes = [
198
-			'org' => 'text/org'
199
-		];
200
-
201
-		return $this->updateMimetypes($updatedMimetypes);
202
-	}
203
-
204
-
205
-	/**
206
-	 * Fix mime types
207
-	 */
208
-	public function run(IOutput $out) {
209
-		$ocVersionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0');
210
-
211
-		// NOTE TO DEVELOPERS: when adding new mime types, please make sure to
212
-		// add a version comparison to avoid doing it every time
213
-
214
-		if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.14', '<') && $this->introduceImageTypes()) {
215
-			$out->info('Fixed image mime types');
216
-		}
217
-
218
-		if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.13', '<') && $this->introduceWindowsProgramTypes()) {
219
-			$out->info('Fixed windows program mime types');
220
-		}
221
-
222
-		if (version_compare($ocVersionFromBeforeUpdate, '13.0.0.0', '<') && $this->introduceLocationTypes()) {
223
-			$out->info('Fixed geospatial mime types');
224
-		}
225
-
226
-		if (version_compare($ocVersionFromBeforeUpdate, '13.0.0.3', '<') && $this->introduceInternetShortcutTypes()) {
227
-			$out->info('Fixed internet-shortcut mime types');
228
-		}
229
-
230
-		if (version_compare($ocVersionFromBeforeUpdate, '13.0.0.6', '<') && $this->introduceStreamingTypes()) {
231
-			$out->info('Fixed streaming mime types');
232
-		}
233
-
234
-		if (version_compare($ocVersionFromBeforeUpdate, '14.0.0.8', '<') && $this->introduceVisioTypes()) {
235
-			$out->info('Fixed visio mime types');
236
-		}
237
-
238
-		if (version_compare($ocVersionFromBeforeUpdate, '14.0.0.10', '<') && $this->introduceComicbookTypes()) {
239
-			$out->info('Fixed comicbook mime types');
240
-		}
241
-
242
-		if (version_compare($ocVersionFromBeforeUpdate, '20.0.0.5', '<') && $this->introduceOpenDocumentTemplates()) {
243
-			$out->info('Fixed OpenDocument template mime types');
244
-		}
245
-
246
-		if (version_compare($ocVersionFromBeforeUpdate, '21.0.0.7', '<') && $this->introduceOrgModeType()) {
247
-			$out->info('Fixed orgmode mime types');
248
-		}
249
-	}
44
+    /** @var IConfig */
45
+    protected $config;
46
+    /** @var IDBConnection */
47
+    protected $connection;
48
+
49
+    /** @var int */
50
+    protected $folderMimeTypeId;
51
+
52
+    public function __construct(IConfig $config,
53
+                                IDBConnection $connection) {
54
+        $this->config = $config;
55
+        $this->connection = $connection;
56
+    }
57
+
58
+    public function getName() {
59
+        return 'Repair mime types';
60
+    }
61
+
62
+    private function updateMimetypes($updatedMimetypes) {
63
+        $query = $this->connection->getQueryBuilder();
64
+        $query->select('id')
65
+            ->from('mimetypes')
66
+            ->where($query->expr()->eq('mimetype', $query->createParameter('mimetype'), IQueryBuilder::PARAM_INT));
67
+        $insert = $this->connection->getQueryBuilder();
68
+        $insert->insert('mimetypes')
69
+            ->setValue('mimetype', $insert->createParameter('mimetype'));
70
+
71
+        if (empty($this->folderMimeTypeId)) {
72
+            $query->setParameter('mimetype', 'httpd/unix-directory');
73
+            $result = $query->execute();
74
+            $this->folderMimeTypeId = (int)$result->fetchOne();
75
+            $result->closeCursor();
76
+        }
77
+
78
+        $update = $this->connection->getQueryBuilder();
79
+        $update->update('filecache')
80
+            ->set('mimetype', $update->createParameter('mimetype'))
81
+            ->where($update->expr()->neq('mimetype', $update->createParameter('mimetype'), IQueryBuilder::PARAM_INT))
82
+            ->andWhere($update->expr()->neq('mimetype', $update->createParameter('folder'), IQueryBuilder::PARAM_INT))
83
+            ->andWhere($update->expr()->iLike('name', $update->createParameter('name')))
84
+            ->setParameter('folder', $this->folderMimeTypeId);
85
+
86
+        $count = 0;
87
+        foreach ($updatedMimetypes as $extension => $mimetype) {
88
+            // get target mimetype id
89
+            $query->setParameter('mimetype', $mimetype);
90
+            $result = $query->execute();
91
+            $mimetypeId = (int)$result->fetchOne();
92
+            $result->closeCursor();
93
+
94
+            if (!$mimetypeId) {
95
+                // insert mimetype
96
+                $insert->setParameter('mimetype', $mimetype);
97
+                $insert->execute();
98
+                $mimetypeId = $insert->getLastInsertId();
99
+            }
100
+
101
+            // change mimetype for files with x extension
102
+            $update->setParameter('mimetype', $mimetypeId)
103
+                ->setParameter('name', '%' . $this->connection->escapeLikeParameter('.' . $extension));
104
+            $count += $update->execute();
105
+        }
106
+
107
+        return $count;
108
+    }
109
+
110
+    private function introduceImageTypes() {
111
+        $updatedMimetypes = [
112
+            'jp2' => 'image/jp2',
113
+            'webp' => 'image/webp',
114
+        ];
115
+
116
+        return $this->updateMimetypes($updatedMimetypes);
117
+    }
118
+
119
+    private function introduceWindowsProgramTypes() {
120
+        $updatedMimetypes = [
121
+            'htaccess' => 'text/plain',
122
+            'bat' => 'application/x-msdos-program',
123
+            'cmd' => 'application/cmd',
124
+        ];
125
+
126
+        return $this->updateMimetypes($updatedMimetypes);
127
+    }
128
+
129
+    private function introduceLocationTypes() {
130
+        $updatedMimetypes = [
131
+            'gpx' => 'application/gpx+xml',
132
+            'kml' => 'application/vnd.google-earth.kml+xml',
133
+            'kmz' => 'application/vnd.google-earth.kmz',
134
+            'tcx' => 'application/vnd.garmin.tcx+xml',
135
+        ];
136
+
137
+        return $this->updateMimetypes($updatedMimetypes);
138
+    }
139
+
140
+    private function introduceInternetShortcutTypes() {
141
+        $updatedMimetypes = [
142
+            'url' => 'application/internet-shortcut',
143
+            'webloc' => 'application/internet-shortcut'
144
+        ];
145
+
146
+        return $this->updateMimetypes($updatedMimetypes);
147
+    }
148
+
149
+    private function introduceStreamingTypes() {
150
+        $updatedMimetypes = [
151
+            'm3u' => 'audio/mpegurl',
152
+            'm3u8' => 'audio/mpegurl',
153
+            'pls' => 'audio/x-scpls'
154
+        ];
155
+
156
+        return $this->updateMimetypes($updatedMimetypes);
157
+    }
158
+
159
+    private function introduceVisioTypes() {
160
+        $updatedMimetypes = [
161
+            'vsdm' => 'application/vnd.visio',
162
+            'vsdx' => 'application/vnd.visio',
163
+            'vssm' => 'application/vnd.visio',
164
+            'vssx' => 'application/vnd.visio',
165
+            'vstm' => 'application/vnd.visio',
166
+            'vstx' => 'application/vnd.visio',
167
+        ];
168
+
169
+        return $this->updateMimetypes($updatedMimetypes);
170
+    }
171
+
172
+    private function introduceComicbookTypes() {
173
+        $updatedMimetypes = [
174
+            'cb7' => 'application/comicbook+7z',
175
+            'cba' => 'application/comicbook+ace',
176
+            'cbr' => 'application/comicbook+rar',
177
+            'cbt' => 'application/comicbook+tar',
178
+            'cbtc' => 'application/comicbook+truecrypt',
179
+            'cbz' => 'application/comicbook+zip',
180
+        ];
181
+
182
+        return $this->updateMimetypes($updatedMimetypes);
183
+    }
184
+
185
+    private function introduceOpenDocumentTemplates() {
186
+        $updatedMimetypes = [
187
+            'ott' => 'application/vnd.oasis.opendocument.text-template',
188
+            'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
189
+            'otp' => 'application/vnd.oasis.opendocument.presentation-template',
190
+            'otg' => 'application/vnd.oasis.opendocument.graphics-template',
191
+        ];
192
+
193
+        return $this->updateMimetypes($updatedMimetypes);
194
+    }
195
+
196
+    private function introduceOrgModeType() {
197
+        $updatedMimetypes = [
198
+            'org' => 'text/org'
199
+        ];
200
+
201
+        return $this->updateMimetypes($updatedMimetypes);
202
+    }
203
+
204
+
205
+    /**
206
+     * Fix mime types
207
+     */
208
+    public function run(IOutput $out) {
209
+        $ocVersionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0');
210
+
211
+        // NOTE TO DEVELOPERS: when adding new mime types, please make sure to
212
+        // add a version comparison to avoid doing it every time
213
+
214
+        if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.14', '<') && $this->introduceImageTypes()) {
215
+            $out->info('Fixed image mime types');
216
+        }
217
+
218
+        if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.13', '<') && $this->introduceWindowsProgramTypes()) {
219
+            $out->info('Fixed windows program mime types');
220
+        }
221
+
222
+        if (version_compare($ocVersionFromBeforeUpdate, '13.0.0.0', '<') && $this->introduceLocationTypes()) {
223
+            $out->info('Fixed geospatial mime types');
224
+        }
225
+
226
+        if (version_compare($ocVersionFromBeforeUpdate, '13.0.0.3', '<') && $this->introduceInternetShortcutTypes()) {
227
+            $out->info('Fixed internet-shortcut mime types');
228
+        }
229
+
230
+        if (version_compare($ocVersionFromBeforeUpdate, '13.0.0.6', '<') && $this->introduceStreamingTypes()) {
231
+            $out->info('Fixed streaming mime types');
232
+        }
233
+
234
+        if (version_compare($ocVersionFromBeforeUpdate, '14.0.0.8', '<') && $this->introduceVisioTypes()) {
235
+            $out->info('Fixed visio mime types');
236
+        }
237
+
238
+        if (version_compare($ocVersionFromBeforeUpdate, '14.0.0.10', '<') && $this->introduceComicbookTypes()) {
239
+            $out->info('Fixed comicbook mime types');
240
+        }
241
+
242
+        if (version_compare($ocVersionFromBeforeUpdate, '20.0.0.5', '<') && $this->introduceOpenDocumentTemplates()) {
243
+            $out->info('Fixed OpenDocument template mime types');
244
+        }
245
+
246
+        if (version_compare($ocVersionFromBeforeUpdate, '21.0.0.7', '<') && $this->introduceOrgModeType()) {
247
+            $out->info('Fixed orgmode mime types');
248
+        }
249
+    }
250 250
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -71,7 +71,7 @@  discard block
 block discarded – undo
71 71
 		if (empty($this->folderMimeTypeId)) {
72 72
 			$query->setParameter('mimetype', 'httpd/unix-directory');
73 73
 			$result = $query->execute();
74
-			$this->folderMimeTypeId = (int)$result->fetchOne();
74
+			$this->folderMimeTypeId = (int) $result->fetchOne();
75 75
 			$result->closeCursor();
76 76
 		}
77 77
 
@@ -88,7 +88,7 @@  discard block
 block discarded – undo
88 88
 			// get target mimetype id
89 89
 			$query->setParameter('mimetype', $mimetype);
90 90
 			$result = $query->execute();
91
-			$mimetypeId = (int)$result->fetchOne();
91
+			$mimetypeId = (int) $result->fetchOne();
92 92
 			$result->closeCursor();
93 93
 
94 94
 			if (!$mimetypeId) {
@@ -100,7 +100,7 @@  discard block
 block discarded – undo
100 100
 
101 101
 			// change mimetype for files with x extension
102 102
 			$update->setParameter('mimetype', $mimetypeId)
103
-				->setParameter('name', '%' . $this->connection->escapeLikeParameter('.' . $extension));
103
+				->setParameter('name', '%'.$this->connection->escapeLikeParameter('.'.$extension));
104 104
 			$count += $update->execute();
105 105
 		}
106 106
 
Please login to merge, or discard this patch.