Completed
Pull Request — master (#3838)
by Vars
12:18
created
apps/dav/lib/CardDAV/AddressBookRoot.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -30,7 +30,7 @@
 block discarded – undo
30 30
 
31 31
 	/**
32 32
 	 * @param \Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend
33
-	 * @param \Sabre\CardDAV\Backend\BackendInterface $carddavBackend
33
+	 * @param CardDavBackend $carddavBackend
34 34
 	 * @param string $principalPrefix
35 35
 	 */
36 36
 	public function __construct(\Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend, \Sabre\CardDAV\Backend\BackendInterface $carddavBackend, $principalPrefix = 'principals') {
Please login to merge, or discard this patch.
Indentation   +33 added lines, -33 removed lines patch added patch discarded remove patch
@@ -25,46 +25,46 @@
 block discarded – undo
25 25
 
26 26
 class AddressBookRoot extends \Sabre\CardDAV\AddressBookRoot {
27 27
 
28
-	/** @var IL10N */
29
-	protected $l10n;
28
+    /** @var IL10N */
29
+    protected $l10n;
30 30
 
31
-	/**
32
-	 * @param \Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend
33
-	 * @param \Sabre\CardDAV\Backend\BackendInterface $carddavBackend
34
-	 * @param string $principalPrefix
35
-	 */
36
-	public function __construct(\Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend, \Sabre\CardDAV\Backend\BackendInterface $carddavBackend, $principalPrefix = 'principals') {
37
-		parent::__construct($principalBackend, $carddavBackend, $principalPrefix);
38
-		$this->l10n = \OC::$server->getL10N('dav');
39
-	}
31
+    /**
32
+     * @param \Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend
33
+     * @param \Sabre\CardDAV\Backend\BackendInterface $carddavBackend
34
+     * @param string $principalPrefix
35
+     */
36
+    public function __construct(\Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend, \Sabre\CardDAV\Backend\BackendInterface $carddavBackend, $principalPrefix = 'principals') {
37
+        parent::__construct($principalBackend, $carddavBackend, $principalPrefix);
38
+        $this->l10n = \OC::$server->getL10N('dav');
39
+    }
40 40
 
41
-	/**
42
-	 * This method returns a node for a principal.
43
-	 *
44
-	 * The passed array contains principal information, and is guaranteed to
45
-	 * at least contain a uri item. Other properties may or may not be
46
-	 * supplied by the authentication backend.
47
-	 *
48
-	 * @param array $principal
49
-	 * @return \Sabre\DAV\INode
50
-	 */
51
-	function getChildForPrincipal(array $principal) {
41
+    /**
42
+     * This method returns a node for a principal.
43
+     *
44
+     * The passed array contains principal information, and is guaranteed to
45
+     * at least contain a uri item. Other properties may or may not be
46
+     * supplied by the authentication backend.
47
+     *
48
+     * @param array $principal
49
+     * @return \Sabre\DAV\INode
50
+     */
51
+    function getChildForPrincipal(array $principal) {
52 52
 
53
-		return new UserAddressBooks($this->carddavBackend, $principal['uri'], $this->l10n);
53
+        return new UserAddressBooks($this->carddavBackend, $principal['uri'], $this->l10n);
54 54
 
55
-	}
55
+    }
56 56
 
57
-	function getName() {
57
+    function getName() {
58 58
 
59
-		if ($this->principalPrefix === 'principals') {
60
-			return parent::getName();
61
-		}
62
-		// Grabbing all the components of the principal path.
63
-		$parts = explode('/', $this->principalPrefix);
59
+        if ($this->principalPrefix === 'principals') {
60
+            return parent::getName();
61
+        }
62
+        // Grabbing all the components of the principal path.
63
+        $parts = explode('/', $this->principalPrefix);
64 64
 
65
-		// We are only interested in the second part.
66
-		return $parts[1];
65
+        // We are only interested in the second part.
66
+        return $parts[1];
67 67
 
68
-	}
68
+    }
69 69
 
70 70
 }
Please login to merge, or discard this patch.
lib/private/Share20/DefaultShareProvider.php 4 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -770,7 +770,7 @@
 block discarded – undo
770 770
 
771 771
 	/**
772 772
 	 * @param Share[] $shares
773
-	 * @param $userId
773
+	 * @param string $userId
774 774
 	 * @return Share[] The updates shares if no update is found for a share return the original
775 775
 	 */
776 776
 	private function resolveGroupShares($shares, $userId) {
Please login to merge, or discard this patch.
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -24,7 +24,6 @@
 block discarded – undo
24 24
 namespace OC\Share20;
25 25
 
26 26
 use OC\Files\Cache\Cache;
27
-use OC\Files\Cache\CacheEntry;
28 27
 use OCP\Files\File;
29 28
 use OCP\Files\Folder;
30 29
 use OCP\Share\IShareProvider;
Please login to merge, or discard this patch.
Indentation   +982 added lines, -982 removed lines patch added patch discarded remove patch
@@ -47,1019 +47,1019 @@
 block discarded – undo
47 47
  */
48 48
 class DefaultShareProvider implements IShareProvider {
49 49
 
50
-	// Special share type for user modified group shares
51
-	const SHARE_TYPE_USERGROUP = 2;
52
-
53
-	/** @var IDBConnection */
54
-	private $dbConn;
55
-
56
-	/** @var IUserManager */
57
-	private $userManager;
58
-
59
-	/** @var IGroupManager */
60
-	private $groupManager;
61
-
62
-	/** @var IRootFolder */
63
-	private $rootFolder;
64
-
65
-	/**
66
-	 * DefaultShareProvider constructor.
67
-	 *
68
-	 * @param IDBConnection $connection
69
-	 * @param IUserManager $userManager
70
-	 * @param IGroupManager $groupManager
71
-	 * @param IRootFolder $rootFolder
72
-	 */
73
-	public function __construct(
74
-			IDBConnection $connection,
75
-			IUserManager $userManager,
76
-			IGroupManager $groupManager,
77
-			IRootFolder $rootFolder) {
78
-		$this->dbConn = $connection;
79
-		$this->userManager = $userManager;
80
-		$this->groupManager = $groupManager;
81
-		$this->rootFolder = $rootFolder;
82
-	}
83
-
84
-	/**
85
-	 * Return the identifier of this provider.
86
-	 *
87
-	 * @return string Containing only [a-zA-Z0-9]
88
-	 */
89
-	public function identifier() {
90
-		return 'ocinternal';
91
-	}
92
-
93
-	/**
94
-	 * Share a path
95
-	 *
96
-	 * @param \OCP\Share\IShare $share
97
-	 * @return \OCP\Share\IShare The share object
98
-	 * @throws ShareNotFound
99
-	 * @throws \Exception
100
-	 */
101
-	public function create(\OCP\Share\IShare $share) {
102
-		$qb = $this->dbConn->getQueryBuilder();
103
-
104
-		$qb->insert('share');
105
-		$qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()));
106
-
107
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
108
-			//Set the UID of the user we share with
109
-			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
110
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
111
-			//Set the GID of the group we share with
112
-			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
113
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
114
-			//Set the token of the share
115
-			$qb->setValue('token', $qb->createNamedParameter($share->getToken()));
116
-
117
-			//If a password is set store it
118
-			if ($share->getPassword() !== null) {
119
-				$qb->setValue('share_with', $qb->createNamedParameter($share->getPassword()));
120
-			}
121
-
122
-			//If an expiration date is set store it
123
-			if ($share->getExpirationDate() !== null) {
124
-				$qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
125
-			}
126
-
127
-			if (method_exists($share, 'getParent')) {
128
-				$qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
129
-			}
130
-		} else {
131
-			throw new \Exception('invalid share type!');
132
-		}
133
-
134
-		// Set what is shares
135
-		$qb->setValue('item_type', $qb->createParameter('itemType'));
136
-		if ($share->getNode() instanceof \OCP\Files\File) {
137
-			$qb->setParameter('itemType', 'file');
138
-		} else {
139
-			$qb->setParameter('itemType', 'folder');
140
-		}
141
-
142
-		// Set the file id
143
-		$qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId()));
144
-		$qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId()));
145
-
146
-		// set the permissions
147
-		$qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
148
-
149
-		// Set who created this share
150
-		$qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
151
-
152
-		// Set who is the owner of this file/folder (and this the owner of the share)
153
-		$qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()));
154
-
155
-		// Set the file target
156
-		$qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
157
-
158
-		// Set the time this share was created
159
-		$qb->setValue('stime', $qb->createNamedParameter(time()));
160
-
161
-		// insert the data and fetch the id of the share
162
-		$this->dbConn->beginTransaction();
163
-		$qb->execute();
164
-		$id = $this->dbConn->lastInsertId('*PREFIX*share');
165
-
166
-		// Now fetch the inserted share and create a complete share object
167
-		$qb = $this->dbConn->getQueryBuilder();
168
-		$qb->select('*')
169
-			->from('share')
170
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
171
-
172
-		$cursor = $qb->execute();
173
-		$data = $cursor->fetch();
174
-		$this->dbConn->commit();
175
-		$cursor->closeCursor();
176
-
177
-		if ($data === false) {
178
-			throw new ShareNotFound();
179
-		}
180
-
181
-		$share = $this->createShare($data);
182
-		return $share;
183
-	}
184
-
185
-	/**
186
-	 * Update a share
187
-	 *
188
-	 * @param \OCP\Share\IShare $share
189
-	 * @return \OCP\Share\IShare The share object
190
-	 */
191
-	public function update(\OCP\Share\IShare $share) {
192
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
193
-			/*
50
+    // Special share type for user modified group shares
51
+    const SHARE_TYPE_USERGROUP = 2;
52
+
53
+    /** @var IDBConnection */
54
+    private $dbConn;
55
+
56
+    /** @var IUserManager */
57
+    private $userManager;
58
+
59
+    /** @var IGroupManager */
60
+    private $groupManager;
61
+
62
+    /** @var IRootFolder */
63
+    private $rootFolder;
64
+
65
+    /**
66
+     * DefaultShareProvider constructor.
67
+     *
68
+     * @param IDBConnection $connection
69
+     * @param IUserManager $userManager
70
+     * @param IGroupManager $groupManager
71
+     * @param IRootFolder $rootFolder
72
+     */
73
+    public function __construct(
74
+            IDBConnection $connection,
75
+            IUserManager $userManager,
76
+            IGroupManager $groupManager,
77
+            IRootFolder $rootFolder) {
78
+        $this->dbConn = $connection;
79
+        $this->userManager = $userManager;
80
+        $this->groupManager = $groupManager;
81
+        $this->rootFolder = $rootFolder;
82
+    }
83
+
84
+    /**
85
+     * Return the identifier of this provider.
86
+     *
87
+     * @return string Containing only [a-zA-Z0-9]
88
+     */
89
+    public function identifier() {
90
+        return 'ocinternal';
91
+    }
92
+
93
+    /**
94
+     * Share a path
95
+     *
96
+     * @param \OCP\Share\IShare $share
97
+     * @return \OCP\Share\IShare The share object
98
+     * @throws ShareNotFound
99
+     * @throws \Exception
100
+     */
101
+    public function create(\OCP\Share\IShare $share) {
102
+        $qb = $this->dbConn->getQueryBuilder();
103
+
104
+        $qb->insert('share');
105
+        $qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()));
106
+
107
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
108
+            //Set the UID of the user we share with
109
+            $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
110
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
111
+            //Set the GID of the group we share with
112
+            $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
113
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
114
+            //Set the token of the share
115
+            $qb->setValue('token', $qb->createNamedParameter($share->getToken()));
116
+
117
+            //If a password is set store it
118
+            if ($share->getPassword() !== null) {
119
+                $qb->setValue('share_with', $qb->createNamedParameter($share->getPassword()));
120
+            }
121
+
122
+            //If an expiration date is set store it
123
+            if ($share->getExpirationDate() !== null) {
124
+                $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
125
+            }
126
+
127
+            if (method_exists($share, 'getParent')) {
128
+                $qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
129
+            }
130
+        } else {
131
+            throw new \Exception('invalid share type!');
132
+        }
133
+
134
+        // Set what is shares
135
+        $qb->setValue('item_type', $qb->createParameter('itemType'));
136
+        if ($share->getNode() instanceof \OCP\Files\File) {
137
+            $qb->setParameter('itemType', 'file');
138
+        } else {
139
+            $qb->setParameter('itemType', 'folder');
140
+        }
141
+
142
+        // Set the file id
143
+        $qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId()));
144
+        $qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId()));
145
+
146
+        // set the permissions
147
+        $qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
148
+
149
+        // Set who created this share
150
+        $qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
151
+
152
+        // Set who is the owner of this file/folder (and this the owner of the share)
153
+        $qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()));
154
+
155
+        // Set the file target
156
+        $qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
157
+
158
+        // Set the time this share was created
159
+        $qb->setValue('stime', $qb->createNamedParameter(time()));
160
+
161
+        // insert the data and fetch the id of the share
162
+        $this->dbConn->beginTransaction();
163
+        $qb->execute();
164
+        $id = $this->dbConn->lastInsertId('*PREFIX*share');
165
+
166
+        // Now fetch the inserted share and create a complete share object
167
+        $qb = $this->dbConn->getQueryBuilder();
168
+        $qb->select('*')
169
+            ->from('share')
170
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
171
+
172
+        $cursor = $qb->execute();
173
+        $data = $cursor->fetch();
174
+        $this->dbConn->commit();
175
+        $cursor->closeCursor();
176
+
177
+        if ($data === false) {
178
+            throw new ShareNotFound();
179
+        }
180
+
181
+        $share = $this->createShare($data);
182
+        return $share;
183
+    }
184
+
185
+    /**
186
+     * Update a share
187
+     *
188
+     * @param \OCP\Share\IShare $share
189
+     * @return \OCP\Share\IShare The share object
190
+     */
191
+    public function update(\OCP\Share\IShare $share) {
192
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
193
+            /*
194 194
 			 * We allow updating the recipient on user shares.
195 195
 			 */
196
-			$qb = $this->dbConn->getQueryBuilder();
197
-			$qb->update('share')
198
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
199
-				->set('share_with', $qb->createNamedParameter($share->getSharedWith()))
200
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
201
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
202
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
203
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
204
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
205
-				->execute();
206
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
207
-			$qb = $this->dbConn->getQueryBuilder();
208
-			$qb->update('share')
209
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
210
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
211
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
212
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
213
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
214
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
215
-				->execute();
216
-
217
-			/*
196
+            $qb = $this->dbConn->getQueryBuilder();
197
+            $qb->update('share')
198
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
199
+                ->set('share_with', $qb->createNamedParameter($share->getSharedWith()))
200
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
201
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
202
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
203
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
204
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
205
+                ->execute();
206
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
207
+            $qb = $this->dbConn->getQueryBuilder();
208
+            $qb->update('share')
209
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
210
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
211
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
212
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
213
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
214
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
215
+                ->execute();
216
+
217
+            /*
218 218
 			 * Update all user defined group shares
219 219
 			 */
220
-			$qb = $this->dbConn->getQueryBuilder();
221
-			$qb->update('share')
222
-				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
223
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
224
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
225
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
226
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
227
-				->execute();
228
-
229
-			/*
220
+            $qb = $this->dbConn->getQueryBuilder();
221
+            $qb->update('share')
222
+                ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
223
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
224
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
225
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
226
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
227
+                ->execute();
228
+
229
+            /*
230 230
 			 * Now update the permissions for all children that have not set it to 0
231 231
 			 */
232
-			$qb = $this->dbConn->getQueryBuilder();
233
-			$qb->update('share')
234
-				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
235
-				->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
236
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
237
-				->execute();
238
-
239
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
240
-			$qb = $this->dbConn->getQueryBuilder();
241
-			$qb->update('share')
242
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
243
-				->set('share_with', $qb->createNamedParameter($share->getPassword()))
244
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
245
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
246
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
247
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
248
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
249
-				->set('token', $qb->createNamedParameter($share->getToken()))
250
-				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
251
-				->execute();
252
-		}
253
-
254
-		return $share;
255
-	}
256
-
257
-	/**
258
-	 * Get all children of this share
259
-	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
260
-	 *
261
-	 * @param \OCP\Share\IShare $parent
262
-	 * @return \OCP\Share\IShare[]
263
-	 */
264
-	public function getChildren(\OCP\Share\IShare $parent) {
265
-		$children = [];
266
-
267
-		$qb = $this->dbConn->getQueryBuilder();
268
-		$qb->select('*')
269
-			->from('share')
270
-			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
271
-			->andWhere(
272
-				$qb->expr()->in(
273
-					'share_type',
274
-					$qb->createNamedParameter([
275
-						\OCP\Share::SHARE_TYPE_USER,
276
-						\OCP\Share::SHARE_TYPE_GROUP,
277
-						\OCP\Share::SHARE_TYPE_LINK,
278
-					], IQueryBuilder::PARAM_INT_ARRAY)
279
-				)
280
-			)
281
-			->andWhere($qb->expr()->orX(
282
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
283
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
284
-			))
285
-			->orderBy('id');
286
-
287
-		$cursor = $qb->execute();
288
-		while($data = $cursor->fetch()) {
289
-			$children[] = $this->createShare($data);
290
-		}
291
-		$cursor->closeCursor();
292
-
293
-		return $children;
294
-	}
295
-
296
-	/**
297
-	 * Delete a share
298
-	 *
299
-	 * @param \OCP\Share\IShare $share
300
-	 */
301
-	public function delete(\OCP\Share\IShare $share) {
302
-		$qb = $this->dbConn->getQueryBuilder();
303
-		$qb->delete('share')
304
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
305
-
306
-		/*
232
+            $qb = $this->dbConn->getQueryBuilder();
233
+            $qb->update('share')
234
+                ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
235
+                ->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
236
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
237
+                ->execute();
238
+
239
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
240
+            $qb = $this->dbConn->getQueryBuilder();
241
+            $qb->update('share')
242
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
243
+                ->set('share_with', $qb->createNamedParameter($share->getPassword()))
244
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
245
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
246
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
247
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
248
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
249
+                ->set('token', $qb->createNamedParameter($share->getToken()))
250
+                ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
251
+                ->execute();
252
+        }
253
+
254
+        return $share;
255
+    }
256
+
257
+    /**
258
+     * Get all children of this share
259
+     * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
260
+     *
261
+     * @param \OCP\Share\IShare $parent
262
+     * @return \OCP\Share\IShare[]
263
+     */
264
+    public function getChildren(\OCP\Share\IShare $parent) {
265
+        $children = [];
266
+
267
+        $qb = $this->dbConn->getQueryBuilder();
268
+        $qb->select('*')
269
+            ->from('share')
270
+            ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
271
+            ->andWhere(
272
+                $qb->expr()->in(
273
+                    'share_type',
274
+                    $qb->createNamedParameter([
275
+                        \OCP\Share::SHARE_TYPE_USER,
276
+                        \OCP\Share::SHARE_TYPE_GROUP,
277
+                        \OCP\Share::SHARE_TYPE_LINK,
278
+                    ], IQueryBuilder::PARAM_INT_ARRAY)
279
+                )
280
+            )
281
+            ->andWhere($qb->expr()->orX(
282
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
283
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
284
+            ))
285
+            ->orderBy('id');
286
+
287
+        $cursor = $qb->execute();
288
+        while($data = $cursor->fetch()) {
289
+            $children[] = $this->createShare($data);
290
+        }
291
+        $cursor->closeCursor();
292
+
293
+        return $children;
294
+    }
295
+
296
+    /**
297
+     * Delete a share
298
+     *
299
+     * @param \OCP\Share\IShare $share
300
+     */
301
+    public function delete(\OCP\Share\IShare $share) {
302
+        $qb = $this->dbConn->getQueryBuilder();
303
+        $qb->delete('share')
304
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
305
+
306
+        /*
307 307
 		 * If the share is a group share delete all possible
308 308
 		 * user defined groups shares.
309 309
 		 */
310
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
311
-			$qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
312
-		}
313
-
314
-		$qb->execute();
315
-	}
316
-
317
-	/**
318
-	 * Unshare a share from the recipient. If this is a group share
319
-	 * this means we need a special entry in the share db.
320
-	 *
321
-	 * @param \OCP\Share\IShare $share
322
-	 * @param string $recipient UserId of recipient
323
-	 * @throws BackendError
324
-	 * @throws ProviderException
325
-	 */
326
-	public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) {
327
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
328
-
329
-			$group = $this->groupManager->get($share->getSharedWith());
330
-			$user = $this->userManager->get($recipient);
331
-
332
-			if (!$group->inGroup($user)) {
333
-				throw new ProviderException('Recipient not in receiving group');
334
-			}
335
-
336
-			// Try to fetch user specific share
337
-			$qb = $this->dbConn->getQueryBuilder();
338
-			$stmt = $qb->select('*')
339
-				->from('share')
340
-				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
341
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
342
-				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
343
-				->andWhere($qb->expr()->orX(
344
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
345
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
346
-				))
347
-				->execute();
348
-
349
-			$data = $stmt->fetch();
350
-
351
-			/*
310
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
311
+            $qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
312
+        }
313
+
314
+        $qb->execute();
315
+    }
316
+
317
+    /**
318
+     * Unshare a share from the recipient. If this is a group share
319
+     * this means we need a special entry in the share db.
320
+     *
321
+     * @param \OCP\Share\IShare $share
322
+     * @param string $recipient UserId of recipient
323
+     * @throws BackendError
324
+     * @throws ProviderException
325
+     */
326
+    public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) {
327
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
328
+
329
+            $group = $this->groupManager->get($share->getSharedWith());
330
+            $user = $this->userManager->get($recipient);
331
+
332
+            if (!$group->inGroup($user)) {
333
+                throw new ProviderException('Recipient not in receiving group');
334
+            }
335
+
336
+            // Try to fetch user specific share
337
+            $qb = $this->dbConn->getQueryBuilder();
338
+            $stmt = $qb->select('*')
339
+                ->from('share')
340
+                ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
341
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
342
+                ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
343
+                ->andWhere($qb->expr()->orX(
344
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
345
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
346
+                ))
347
+                ->execute();
348
+
349
+            $data = $stmt->fetch();
350
+
351
+            /*
352 352
 			 * Check if there already is a user specific group share.
353 353
 			 * If there is update it (if required).
354 354
 			 */
355
-			if ($data === false) {
356
-				$qb = $this->dbConn->getQueryBuilder();
357
-
358
-				$type = $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder';
359
-
360
-				//Insert new share
361
-				$qb->insert('share')
362
-					->values([
363
-						'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
364
-						'share_with' => $qb->createNamedParameter($recipient),
365
-						'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
366
-						'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
367
-						'parent' => $qb->createNamedParameter($share->getId()),
368
-						'item_type' => $qb->createNamedParameter($type),
369
-						'item_source' => $qb->createNamedParameter($share->getNode()->getId()),
370
-						'file_source' => $qb->createNamedParameter($share->getNode()->getId()),
371
-						'file_target' => $qb->createNamedParameter($share->getTarget()),
372
-						'permissions' => $qb->createNamedParameter(0),
373
-						'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
374
-					])->execute();
375
-
376
-			} else if ($data['permissions'] !== 0) {
377
-
378
-				// Update existing usergroup share
379
-				$qb = $this->dbConn->getQueryBuilder();
380
-				$qb->update('share')
381
-					->set('permissions', $qb->createNamedParameter(0))
382
-					->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
383
-					->execute();
384
-			}
385
-
386
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
387
-
388
-			if ($share->getSharedWith() !== $recipient) {
389
-				throw new ProviderException('Recipient does not match');
390
-			}
391
-
392
-			// We can just delete user and link shares
393
-			$this->delete($share);
394
-		} else {
395
-			throw new ProviderException('Invalid shareType');
396
-		}
397
-	}
398
-
399
-	/**
400
-	 * @inheritdoc
401
-	 */
402
-	public function move(\OCP\Share\IShare $share, $recipient) {
403
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
404
-			// Just update the target
405
-			$qb = $this->dbConn->getQueryBuilder();
406
-			$qb->update('share')
407
-				->set('file_target', $qb->createNamedParameter($share->getTarget()))
408
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
409
-				->execute();
410
-
411
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
412
-
413
-			// Check if there is a usergroup share
414
-			$qb = $this->dbConn->getQueryBuilder();
415
-			$stmt = $qb->select('id')
416
-				->from('share')
417
-				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
418
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
419
-				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
420
-				->andWhere($qb->expr()->orX(
421
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
422
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
423
-				))
424
-				->setMaxResults(1)
425
-				->execute();
426
-
427
-			$data = $stmt->fetch();
428
-			$stmt->closeCursor();
429
-
430
-			if ($data === false) {
431
-				// No usergroup share yet. Create one.
432
-				$qb = $this->dbConn->getQueryBuilder();
433
-				$qb->insert('share')
434
-					->values([
435
-						'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
436
-						'share_with' => $qb->createNamedParameter($recipient),
437
-						'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
438
-						'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
439
-						'parent' => $qb->createNamedParameter($share->getId()),
440
-						'item_type' => $qb->createNamedParameter($share->getNode() instanceof File ? 'file' : 'folder'),
441
-						'item_source' => $qb->createNamedParameter($share->getNode()->getId()),
442
-						'file_source' => $qb->createNamedParameter($share->getNode()->getId()),
443
-						'file_target' => $qb->createNamedParameter($share->getTarget()),
444
-						'permissions' => $qb->createNamedParameter($share->getPermissions()),
445
-						'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
446
-					])->execute();
447
-			} else {
448
-				// Already a usergroup share. Update it.
449
-				$qb = $this->dbConn->getQueryBuilder();
450
-				$qb->update('share')
451
-					->set('file_target', $qb->createNamedParameter($share->getTarget()))
452
-					->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
453
-					->execute();
454
-			}
455
-		}
456
-
457
-		return $share;
458
-	}
459
-
460
-	public function getSharesInFolder($userId, Folder $node, $reshares) {
461
-		$qb = $this->dbConn->getQueryBuilder();
462
-		$qb->select('*')
463
-			->from('share', 's')
464
-			->andWhere($qb->expr()->orX(
465
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
466
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
467
-			));
468
-
469
-		$qb->andWhere($qb->expr()->orX(
470
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
471
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
472
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))
473
-		));
474
-
475
-		/**
476
-		 * Reshares for this user are shares where they are the owner.
477
-		 */
478
-		if ($reshares === false) {
479
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
480
-		} else {
481
-			$qb->andWhere(
482
-				$qb->expr()->orX(
483
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
484
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
485
-				)
486
-			);
487
-		}
488
-
489
-		$qb->innerJoin('s', 'filecache' ,'f', 's.file_source = f.fileid');
490
-		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
491
-
492
-		$qb->orderBy('id');
493
-
494
-		$cursor = $qb->execute();
495
-		$shares = [];
496
-		while ($data = $cursor->fetch()) {
497
-			$shares[$data['fileid']][] = $this->createShare($data);
498
-		}
499
-		$cursor->closeCursor();
500
-
501
-		return $shares;
502
-	}
503
-
504
-	/**
505
-	 * @inheritdoc
506
-	 */
507
-	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
508
-		$qb = $this->dbConn->getQueryBuilder();
509
-		$qb->select('*')
510
-			->from('share')
511
-			->andWhere($qb->expr()->orX(
512
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
513
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
514
-			));
515
-
516
-		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
517
-
518
-		/**
519
-		 * Reshares for this user are shares where they are the owner.
520
-		 */
521
-		if ($reshares === false) {
522
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
523
-		} else {
524
-			$qb->andWhere(
525
-				$qb->expr()->orX(
526
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
527
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
528
-				)
529
-			);
530
-		}
531
-
532
-		if ($node !== null) {
533
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
534
-		}
535
-
536
-		if ($limit !== -1) {
537
-			$qb->setMaxResults($limit);
538
-		}
539
-
540
-		$qb->setFirstResult($offset);
541
-		$qb->orderBy('id');
542
-
543
-		$cursor = $qb->execute();
544
-		$shares = [];
545
-		while($data = $cursor->fetch()) {
546
-			$shares[] = $this->createShare($data);
547
-		}
548
-		$cursor->closeCursor();
549
-
550
-		return $shares;
551
-	}
552
-
553
-	/**
554
-	 * @inheritdoc
555
-	 */
556
-	public function getShareById($id, $recipientId = null) {
557
-		$qb = $this->dbConn->getQueryBuilder();
558
-
559
-		$qb->select('*')
560
-			->from('share')
561
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
562
-			->andWhere(
563
-				$qb->expr()->in(
564
-					'share_type',
565
-					$qb->createNamedParameter([
566
-						\OCP\Share::SHARE_TYPE_USER,
567
-						\OCP\Share::SHARE_TYPE_GROUP,
568
-						\OCP\Share::SHARE_TYPE_LINK,
569
-					], IQueryBuilder::PARAM_INT_ARRAY)
570
-				)
571
-			)
572
-			->andWhere($qb->expr()->orX(
573
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
574
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
575
-			));
576
-
577
-		$cursor = $qb->execute();
578
-		$data = $cursor->fetch();
579
-		$cursor->closeCursor();
580
-
581
-		if ($data === false) {
582
-			throw new ShareNotFound();
583
-		}
584
-
585
-		try {
586
-			$share = $this->createShare($data);
587
-		} catch (InvalidShare $e) {
588
-			throw new ShareNotFound();
589
-		}
590
-
591
-		// If the recipient is set for a group share resolve to that user
592
-		if ($recipientId !== null && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
593
-			$share = $this->resolveGroupShares([$share], $recipientId)[0];
594
-		}
595
-
596
-		return $share;
597
-	}
598
-
599
-	/**
600
-	 * Get shares for a given path
601
-	 *
602
-	 * @param \OCP\Files\Node $path
603
-	 * @return \OCP\Share\IShare[]
604
-	 */
605
-	public function getSharesByPath(Node $path) {
606
-		$qb = $this->dbConn->getQueryBuilder();
607
-
608
-		$cursor = $qb->select('*')
609
-			->from('share')
610
-			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
611
-			->andWhere(
612
-				$qb->expr()->orX(
613
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
614
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))
615
-				)
616
-			)
617
-			->andWhere($qb->expr()->orX(
618
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
619
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
620
-			))
621
-			->execute();
622
-
623
-		$shares = [];
624
-		while($data = $cursor->fetch()) {
625
-			$shares[] = $this->createShare($data);
626
-		}
627
-		$cursor->closeCursor();
628
-
629
-		return $shares;
630
-	}
631
-
632
-	/**
633
-	 * Returns whether the given database result can be interpreted as
634
-	 * a share with accessible file (not trashed, not deleted)
635
-	 */
636
-	private function isAccessibleResult($data) {
637
-		// exclude shares leading to deleted file entries
638
-		if ($data['fileid'] === null) {
639
-			return false;
640
-		}
641
-
642
-		// exclude shares leading to trashbin on home storages
643
-		$pathSections = explode('/', $data['path'], 2);
644
-		// FIXME: would not detect rare md5'd home storage case properly
645
-		if ($pathSections[0] !== 'files' && explode(':', $data['storage_string_id'], 2)[0] === 'home') {
646
-			return false;
647
-		}
648
-		return true;
649
-	}
650
-
651
-	/**
652
-	 * @inheritdoc
653
-	 */
654
-	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
655
-		/** @var Share[] $shares */
656
-		$shares = [];
657
-
658
-		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
659
-			//Get shares directly with this user
660
-			$qb = $this->dbConn->getQueryBuilder();
661
-			$qb->select('s.*',
662
-				'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
663
-				'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
664
-				'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
665
-			)
666
-				->selectAlias('st.id', 'storage_string_id')
667
-				->from('share', 's')
668
-				->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
669
-				->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
670
-
671
-			// Order by id
672
-			$qb->orderBy('s.id');
673
-
674
-			// Set limit and offset
675
-			if ($limit !== -1) {
676
-				$qb->setMaxResults($limit);
677
-			}
678
-			$qb->setFirstResult($offset);
679
-
680
-			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)))
681
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
682
-				->andWhere($qb->expr()->orX(
683
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
684
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
685
-				));
686
-
687
-			// Filter by node if provided
688
-			if ($node !== null) {
689
-				$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
690
-			}
691
-
692
-			$cursor = $qb->execute();
693
-
694
-			while($data = $cursor->fetch()) {
695
-				if ($this->isAccessibleResult($data)) {
696
-					$shares[] = $this->createShare($data);
697
-				}
698
-			}
699
-			$cursor->closeCursor();
700
-
701
-		} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
702
-			$user = $this->userManager->get($userId);
703
-			$allGroups = $this->groupManager->getUserGroups($user);
704
-
705
-			/** @var Share[] $shares2 */
706
-			$shares2 = [];
707
-
708
-			$start = 0;
709
-			while(true) {
710
-				$groups = array_slice($allGroups, $start, 100);
711
-				$start += 100;
712
-
713
-				if ($groups === []) {
714
-					break;
715
-				}
716
-
717
-				$qb = $this->dbConn->getQueryBuilder();
718
-				$qb->select('s.*',
719
-					'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
720
-					'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
721
-					'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
722
-				)
723
-					->selectAlias('st.id', 'storage_string_id')
724
-					->from('share', 's')
725
-					->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
726
-					->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
727
-					->orderBy('s.id')
728
-					->setFirstResult(0);
729
-
730
-				if ($limit !== -1) {
731
-					$qb->setMaxResults($limit - count($shares));
732
-				}
733
-
734
-				// Filter by node if provided
735
-				if ($node !== null) {
736
-					$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
737
-				}
738
-
739
-				$groups = array_map(function(IGroup $group) { return $group->getGID(); }, $groups);
740
-
741
-				$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
742
-					->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
743
-						$groups,
744
-						IQueryBuilder::PARAM_STR_ARRAY
745
-					)))
746
-					->andWhere($qb->expr()->orX(
747
-						$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
748
-						$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
749
-					));
750
-
751
-				$cursor = $qb->execute();
752
-				while($data = $cursor->fetch()) {
753
-					if ($offset > 0) {
754
-						$offset--;
755
-						continue;
756
-					}
757
-
758
-					if ($this->isAccessibleResult($data)) {
759
-						$shares2[] = $this->createShare($data);
760
-					}
761
-				}
762
-				$cursor->closeCursor();
763
-			}
764
-
765
-			/*
355
+            if ($data === false) {
356
+                $qb = $this->dbConn->getQueryBuilder();
357
+
358
+                $type = $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder';
359
+
360
+                //Insert new share
361
+                $qb->insert('share')
362
+                    ->values([
363
+                        'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
364
+                        'share_with' => $qb->createNamedParameter($recipient),
365
+                        'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
366
+                        'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
367
+                        'parent' => $qb->createNamedParameter($share->getId()),
368
+                        'item_type' => $qb->createNamedParameter($type),
369
+                        'item_source' => $qb->createNamedParameter($share->getNode()->getId()),
370
+                        'file_source' => $qb->createNamedParameter($share->getNode()->getId()),
371
+                        'file_target' => $qb->createNamedParameter($share->getTarget()),
372
+                        'permissions' => $qb->createNamedParameter(0),
373
+                        'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
374
+                    ])->execute();
375
+
376
+            } else if ($data['permissions'] !== 0) {
377
+
378
+                // Update existing usergroup share
379
+                $qb = $this->dbConn->getQueryBuilder();
380
+                $qb->update('share')
381
+                    ->set('permissions', $qb->createNamedParameter(0))
382
+                    ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
383
+                    ->execute();
384
+            }
385
+
386
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
387
+
388
+            if ($share->getSharedWith() !== $recipient) {
389
+                throw new ProviderException('Recipient does not match');
390
+            }
391
+
392
+            // We can just delete user and link shares
393
+            $this->delete($share);
394
+        } else {
395
+            throw new ProviderException('Invalid shareType');
396
+        }
397
+    }
398
+
399
+    /**
400
+     * @inheritdoc
401
+     */
402
+    public function move(\OCP\Share\IShare $share, $recipient) {
403
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
404
+            // Just update the target
405
+            $qb = $this->dbConn->getQueryBuilder();
406
+            $qb->update('share')
407
+                ->set('file_target', $qb->createNamedParameter($share->getTarget()))
408
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
409
+                ->execute();
410
+
411
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
412
+
413
+            // Check if there is a usergroup share
414
+            $qb = $this->dbConn->getQueryBuilder();
415
+            $stmt = $qb->select('id')
416
+                ->from('share')
417
+                ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
418
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
419
+                ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
420
+                ->andWhere($qb->expr()->orX(
421
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
422
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
423
+                ))
424
+                ->setMaxResults(1)
425
+                ->execute();
426
+
427
+            $data = $stmt->fetch();
428
+            $stmt->closeCursor();
429
+
430
+            if ($data === false) {
431
+                // No usergroup share yet. Create one.
432
+                $qb = $this->dbConn->getQueryBuilder();
433
+                $qb->insert('share')
434
+                    ->values([
435
+                        'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
436
+                        'share_with' => $qb->createNamedParameter($recipient),
437
+                        'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
438
+                        'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
439
+                        'parent' => $qb->createNamedParameter($share->getId()),
440
+                        'item_type' => $qb->createNamedParameter($share->getNode() instanceof File ? 'file' : 'folder'),
441
+                        'item_source' => $qb->createNamedParameter($share->getNode()->getId()),
442
+                        'file_source' => $qb->createNamedParameter($share->getNode()->getId()),
443
+                        'file_target' => $qb->createNamedParameter($share->getTarget()),
444
+                        'permissions' => $qb->createNamedParameter($share->getPermissions()),
445
+                        'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
446
+                    ])->execute();
447
+            } else {
448
+                // Already a usergroup share. Update it.
449
+                $qb = $this->dbConn->getQueryBuilder();
450
+                $qb->update('share')
451
+                    ->set('file_target', $qb->createNamedParameter($share->getTarget()))
452
+                    ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
453
+                    ->execute();
454
+            }
455
+        }
456
+
457
+        return $share;
458
+    }
459
+
460
+    public function getSharesInFolder($userId, Folder $node, $reshares) {
461
+        $qb = $this->dbConn->getQueryBuilder();
462
+        $qb->select('*')
463
+            ->from('share', 's')
464
+            ->andWhere($qb->expr()->orX(
465
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
466
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
467
+            ));
468
+
469
+        $qb->andWhere($qb->expr()->orX(
470
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
471
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
472
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))
473
+        ));
474
+
475
+        /**
476
+         * Reshares for this user are shares where they are the owner.
477
+         */
478
+        if ($reshares === false) {
479
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
480
+        } else {
481
+            $qb->andWhere(
482
+                $qb->expr()->orX(
483
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
484
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
485
+                )
486
+            );
487
+        }
488
+
489
+        $qb->innerJoin('s', 'filecache' ,'f', 's.file_source = f.fileid');
490
+        $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
491
+
492
+        $qb->orderBy('id');
493
+
494
+        $cursor = $qb->execute();
495
+        $shares = [];
496
+        while ($data = $cursor->fetch()) {
497
+            $shares[$data['fileid']][] = $this->createShare($data);
498
+        }
499
+        $cursor->closeCursor();
500
+
501
+        return $shares;
502
+    }
503
+
504
+    /**
505
+     * @inheritdoc
506
+     */
507
+    public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
508
+        $qb = $this->dbConn->getQueryBuilder();
509
+        $qb->select('*')
510
+            ->from('share')
511
+            ->andWhere($qb->expr()->orX(
512
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
513
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
514
+            ));
515
+
516
+        $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
517
+
518
+        /**
519
+         * Reshares for this user are shares where they are the owner.
520
+         */
521
+        if ($reshares === false) {
522
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
523
+        } else {
524
+            $qb->andWhere(
525
+                $qb->expr()->orX(
526
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
527
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
528
+                )
529
+            );
530
+        }
531
+
532
+        if ($node !== null) {
533
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
534
+        }
535
+
536
+        if ($limit !== -1) {
537
+            $qb->setMaxResults($limit);
538
+        }
539
+
540
+        $qb->setFirstResult($offset);
541
+        $qb->orderBy('id');
542
+
543
+        $cursor = $qb->execute();
544
+        $shares = [];
545
+        while($data = $cursor->fetch()) {
546
+            $shares[] = $this->createShare($data);
547
+        }
548
+        $cursor->closeCursor();
549
+
550
+        return $shares;
551
+    }
552
+
553
+    /**
554
+     * @inheritdoc
555
+     */
556
+    public function getShareById($id, $recipientId = null) {
557
+        $qb = $this->dbConn->getQueryBuilder();
558
+
559
+        $qb->select('*')
560
+            ->from('share')
561
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
562
+            ->andWhere(
563
+                $qb->expr()->in(
564
+                    'share_type',
565
+                    $qb->createNamedParameter([
566
+                        \OCP\Share::SHARE_TYPE_USER,
567
+                        \OCP\Share::SHARE_TYPE_GROUP,
568
+                        \OCP\Share::SHARE_TYPE_LINK,
569
+                    ], IQueryBuilder::PARAM_INT_ARRAY)
570
+                )
571
+            )
572
+            ->andWhere($qb->expr()->orX(
573
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
574
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
575
+            ));
576
+
577
+        $cursor = $qb->execute();
578
+        $data = $cursor->fetch();
579
+        $cursor->closeCursor();
580
+
581
+        if ($data === false) {
582
+            throw new ShareNotFound();
583
+        }
584
+
585
+        try {
586
+            $share = $this->createShare($data);
587
+        } catch (InvalidShare $e) {
588
+            throw new ShareNotFound();
589
+        }
590
+
591
+        // If the recipient is set for a group share resolve to that user
592
+        if ($recipientId !== null && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
593
+            $share = $this->resolveGroupShares([$share], $recipientId)[0];
594
+        }
595
+
596
+        return $share;
597
+    }
598
+
599
+    /**
600
+     * Get shares for a given path
601
+     *
602
+     * @param \OCP\Files\Node $path
603
+     * @return \OCP\Share\IShare[]
604
+     */
605
+    public function getSharesByPath(Node $path) {
606
+        $qb = $this->dbConn->getQueryBuilder();
607
+
608
+        $cursor = $qb->select('*')
609
+            ->from('share')
610
+            ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
611
+            ->andWhere(
612
+                $qb->expr()->orX(
613
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
614
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))
615
+                )
616
+            )
617
+            ->andWhere($qb->expr()->orX(
618
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
619
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
620
+            ))
621
+            ->execute();
622
+
623
+        $shares = [];
624
+        while($data = $cursor->fetch()) {
625
+            $shares[] = $this->createShare($data);
626
+        }
627
+        $cursor->closeCursor();
628
+
629
+        return $shares;
630
+    }
631
+
632
+    /**
633
+     * Returns whether the given database result can be interpreted as
634
+     * a share with accessible file (not trashed, not deleted)
635
+     */
636
+    private function isAccessibleResult($data) {
637
+        // exclude shares leading to deleted file entries
638
+        if ($data['fileid'] === null) {
639
+            return false;
640
+        }
641
+
642
+        // exclude shares leading to trashbin on home storages
643
+        $pathSections = explode('/', $data['path'], 2);
644
+        // FIXME: would not detect rare md5'd home storage case properly
645
+        if ($pathSections[0] !== 'files' && explode(':', $data['storage_string_id'], 2)[0] === 'home') {
646
+            return false;
647
+        }
648
+        return true;
649
+    }
650
+
651
+    /**
652
+     * @inheritdoc
653
+     */
654
+    public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
655
+        /** @var Share[] $shares */
656
+        $shares = [];
657
+
658
+        if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
659
+            //Get shares directly with this user
660
+            $qb = $this->dbConn->getQueryBuilder();
661
+            $qb->select('s.*',
662
+                'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
663
+                'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
664
+                'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
665
+            )
666
+                ->selectAlias('st.id', 'storage_string_id')
667
+                ->from('share', 's')
668
+                ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
669
+                ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
670
+
671
+            // Order by id
672
+            $qb->orderBy('s.id');
673
+
674
+            // Set limit and offset
675
+            if ($limit !== -1) {
676
+                $qb->setMaxResults($limit);
677
+            }
678
+            $qb->setFirstResult($offset);
679
+
680
+            $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)))
681
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
682
+                ->andWhere($qb->expr()->orX(
683
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
684
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
685
+                ));
686
+
687
+            // Filter by node if provided
688
+            if ($node !== null) {
689
+                $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
690
+            }
691
+
692
+            $cursor = $qb->execute();
693
+
694
+            while($data = $cursor->fetch()) {
695
+                if ($this->isAccessibleResult($data)) {
696
+                    $shares[] = $this->createShare($data);
697
+                }
698
+            }
699
+            $cursor->closeCursor();
700
+
701
+        } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
702
+            $user = $this->userManager->get($userId);
703
+            $allGroups = $this->groupManager->getUserGroups($user);
704
+
705
+            /** @var Share[] $shares2 */
706
+            $shares2 = [];
707
+
708
+            $start = 0;
709
+            while(true) {
710
+                $groups = array_slice($allGroups, $start, 100);
711
+                $start += 100;
712
+
713
+                if ($groups === []) {
714
+                    break;
715
+                }
716
+
717
+                $qb = $this->dbConn->getQueryBuilder();
718
+                $qb->select('s.*',
719
+                    'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
720
+                    'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
721
+                    'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
722
+                )
723
+                    ->selectAlias('st.id', 'storage_string_id')
724
+                    ->from('share', 's')
725
+                    ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
726
+                    ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
727
+                    ->orderBy('s.id')
728
+                    ->setFirstResult(0);
729
+
730
+                if ($limit !== -1) {
731
+                    $qb->setMaxResults($limit - count($shares));
732
+                }
733
+
734
+                // Filter by node if provided
735
+                if ($node !== null) {
736
+                    $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
737
+                }
738
+
739
+                $groups = array_map(function(IGroup $group) { return $group->getGID(); }, $groups);
740
+
741
+                $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
742
+                    ->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
743
+                        $groups,
744
+                        IQueryBuilder::PARAM_STR_ARRAY
745
+                    )))
746
+                    ->andWhere($qb->expr()->orX(
747
+                        $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
748
+                        $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
749
+                    ));
750
+
751
+                $cursor = $qb->execute();
752
+                while($data = $cursor->fetch()) {
753
+                    if ($offset > 0) {
754
+                        $offset--;
755
+                        continue;
756
+                    }
757
+
758
+                    if ($this->isAccessibleResult($data)) {
759
+                        $shares2[] = $this->createShare($data);
760
+                    }
761
+                }
762
+                $cursor->closeCursor();
763
+            }
764
+
765
+            /*
766 766
  			 * Resolve all group shares to user specific shares
767 767
  			 */
768
-			$shares = $this->resolveGroupShares($shares2, $userId);
769
-		} else {
770
-			throw new BackendError('Invalid backend');
771
-		}
772
-
773
-
774
-		return $shares;
775
-	}
776
-
777
-	/**
778
-	 * Get a share by token
779
-	 *
780
-	 * @param string $token
781
-	 * @return \OCP\Share\IShare
782
-	 * @throws ShareNotFound
783
-	 */
784
-	public function getShareByToken($token) {
785
-		$qb = $this->dbConn->getQueryBuilder();
786
-
787
-		$cursor = $qb->select('*')
788
-			->from('share')
789
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)))
790
-			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
791
-			->andWhere($qb->expr()->orX(
792
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
793
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
794
-			))
795
-			->execute();
796
-
797
-		$data = $cursor->fetch();
798
-
799
-		if ($data === false) {
800
-			throw new ShareNotFound();
801
-		}
802
-
803
-		try {
804
-			$share = $this->createShare($data);
805
-		} catch (InvalidShare $e) {
806
-			throw new ShareNotFound();
807
-		}
808
-
809
-		return $share;
810
-	}
811
-
812
-	/**
813
-	 * Create a share object from an database row
814
-	 *
815
-	 * @param mixed[] $data
816
-	 * @return \OCP\Share\IShare
817
-	 * @throws InvalidShare
818
-	 */
819
-	private function createShare($data) {
820
-		$share = new Share($this->rootFolder, $this->userManager);
821
-		$share->setId((int)$data['id'])
822
-			->setShareType((int)$data['share_type'])
823
-			->setPermissions((int)$data['permissions'])
824
-			->setTarget($data['file_target'])
825
-			->setMailSend((bool)$data['mail_send']);
826
-
827
-		$shareTime = new \DateTime();
828
-		$shareTime->setTimestamp((int)$data['stime']);
829
-		$share->setShareTime($shareTime);
830
-
831
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
832
-			$share->setSharedWith($data['share_with']);
833
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
834
-			$share->setSharedWith($data['share_with']);
835
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
836
-			$share->setPassword($data['share_with']);
837
-			$share->setToken($data['token']);
838
-		}
839
-
840
-		$share->setSharedBy($data['uid_initiator']);
841
-		$share->setShareOwner($data['uid_owner']);
842
-
843
-		$share->setNodeId((int)$data['file_source']);
844
-		$share->setNodeType($data['item_type']);
845
-
846
-		if ($data['expiration'] !== null) {
847
-			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
848
-			$share->setExpirationDate($expiration);
849
-		}
850
-
851
-		if (isset($data['f_permissions'])) {
852
-			$entryData = $data;
853
-			$entryData['permissions'] = $entryData['f_permissions'];
854
-			$entryData['parent'] = $entryData['f_parent'];;
855
-			$share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
856
-				\OC::$server->getMimeTypeLoader()));
857
-		}
858
-
859
-		$share->setProviderId($this->identifier());
860
-
861
-		return $share;
862
-	}
863
-
864
-	/**
865
-	 * @param Share[] $shares
866
-	 * @param $userId
867
-	 * @return Share[] The updates shares if no update is found for a share return the original
868
-	 */
869
-	private function resolveGroupShares($shares, $userId) {
870
-		$result = [];
871
-
872
-		$start = 0;
873
-		while(true) {
874
-			/** @var Share[] $shareSlice */
875
-			$shareSlice = array_slice($shares, $start, 100);
876
-			$start += 100;
877
-
878
-			if ($shareSlice === []) {
879
-				break;
880
-			}
881
-
882
-			/** @var int[] $ids */
883
-			$ids = [];
884
-			/** @var Share[] $shareMap */
885
-			$shareMap = [];
886
-
887
-			foreach ($shareSlice as $share) {
888
-				$ids[] = (int)$share->getId();
889
-				$shareMap[$share->getId()] = $share;
890
-			}
891
-
892
-			$qb = $this->dbConn->getQueryBuilder();
893
-
894
-			$query = $qb->select('*')
895
-				->from('share')
896
-				->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
897
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
898
-				->andWhere($qb->expr()->orX(
899
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
900
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
901
-				));
902
-
903
-			$stmt = $query->execute();
904
-
905
-			while($data = $stmt->fetch()) {
906
-				$shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
907
-				$shareMap[$data['parent']]->setTarget($data['file_target']);
908
-			}
909
-
910
-			$stmt->closeCursor();
911
-
912
-			foreach ($shareMap as $share) {
913
-				$result[] = $share;
914
-			}
915
-		}
916
-
917
-		return $result;
918
-	}
919
-
920
-	/**
921
-	 * A user is deleted from the system
922
-	 * So clean up the relevant shares.
923
-	 *
924
-	 * @param string $uid
925
-	 * @param int $shareType
926
-	 */
927
-	public function userDeleted($uid, $shareType) {
928
-		$qb = $this->dbConn->getQueryBuilder();
929
-
930
-		$qb->delete('share');
931
-
932
-		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
933
-			/*
768
+            $shares = $this->resolveGroupShares($shares2, $userId);
769
+        } else {
770
+            throw new BackendError('Invalid backend');
771
+        }
772
+
773
+
774
+        return $shares;
775
+    }
776
+
777
+    /**
778
+     * Get a share by token
779
+     *
780
+     * @param string $token
781
+     * @return \OCP\Share\IShare
782
+     * @throws ShareNotFound
783
+     */
784
+    public function getShareByToken($token) {
785
+        $qb = $this->dbConn->getQueryBuilder();
786
+
787
+        $cursor = $qb->select('*')
788
+            ->from('share')
789
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)))
790
+            ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
791
+            ->andWhere($qb->expr()->orX(
792
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
793
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
794
+            ))
795
+            ->execute();
796
+
797
+        $data = $cursor->fetch();
798
+
799
+        if ($data === false) {
800
+            throw new ShareNotFound();
801
+        }
802
+
803
+        try {
804
+            $share = $this->createShare($data);
805
+        } catch (InvalidShare $e) {
806
+            throw new ShareNotFound();
807
+        }
808
+
809
+        return $share;
810
+    }
811
+
812
+    /**
813
+     * Create a share object from an database row
814
+     *
815
+     * @param mixed[] $data
816
+     * @return \OCP\Share\IShare
817
+     * @throws InvalidShare
818
+     */
819
+    private function createShare($data) {
820
+        $share = new Share($this->rootFolder, $this->userManager);
821
+        $share->setId((int)$data['id'])
822
+            ->setShareType((int)$data['share_type'])
823
+            ->setPermissions((int)$data['permissions'])
824
+            ->setTarget($data['file_target'])
825
+            ->setMailSend((bool)$data['mail_send']);
826
+
827
+        $shareTime = new \DateTime();
828
+        $shareTime->setTimestamp((int)$data['stime']);
829
+        $share->setShareTime($shareTime);
830
+
831
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
832
+            $share->setSharedWith($data['share_with']);
833
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
834
+            $share->setSharedWith($data['share_with']);
835
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
836
+            $share->setPassword($data['share_with']);
837
+            $share->setToken($data['token']);
838
+        }
839
+
840
+        $share->setSharedBy($data['uid_initiator']);
841
+        $share->setShareOwner($data['uid_owner']);
842
+
843
+        $share->setNodeId((int)$data['file_source']);
844
+        $share->setNodeType($data['item_type']);
845
+
846
+        if ($data['expiration'] !== null) {
847
+            $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
848
+            $share->setExpirationDate($expiration);
849
+        }
850
+
851
+        if (isset($data['f_permissions'])) {
852
+            $entryData = $data;
853
+            $entryData['permissions'] = $entryData['f_permissions'];
854
+            $entryData['parent'] = $entryData['f_parent'];;
855
+            $share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
856
+                \OC::$server->getMimeTypeLoader()));
857
+        }
858
+
859
+        $share->setProviderId($this->identifier());
860
+
861
+        return $share;
862
+    }
863
+
864
+    /**
865
+     * @param Share[] $shares
866
+     * @param $userId
867
+     * @return Share[] The updates shares if no update is found for a share return the original
868
+     */
869
+    private function resolveGroupShares($shares, $userId) {
870
+        $result = [];
871
+
872
+        $start = 0;
873
+        while(true) {
874
+            /** @var Share[] $shareSlice */
875
+            $shareSlice = array_slice($shares, $start, 100);
876
+            $start += 100;
877
+
878
+            if ($shareSlice === []) {
879
+                break;
880
+            }
881
+
882
+            /** @var int[] $ids */
883
+            $ids = [];
884
+            /** @var Share[] $shareMap */
885
+            $shareMap = [];
886
+
887
+            foreach ($shareSlice as $share) {
888
+                $ids[] = (int)$share->getId();
889
+                $shareMap[$share->getId()] = $share;
890
+            }
891
+
892
+            $qb = $this->dbConn->getQueryBuilder();
893
+
894
+            $query = $qb->select('*')
895
+                ->from('share')
896
+                ->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
897
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
898
+                ->andWhere($qb->expr()->orX(
899
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
900
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
901
+                ));
902
+
903
+            $stmt = $query->execute();
904
+
905
+            while($data = $stmt->fetch()) {
906
+                $shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
907
+                $shareMap[$data['parent']]->setTarget($data['file_target']);
908
+            }
909
+
910
+            $stmt->closeCursor();
911
+
912
+            foreach ($shareMap as $share) {
913
+                $result[] = $share;
914
+            }
915
+        }
916
+
917
+        return $result;
918
+    }
919
+
920
+    /**
921
+     * A user is deleted from the system
922
+     * So clean up the relevant shares.
923
+     *
924
+     * @param string $uid
925
+     * @param int $shareType
926
+     */
927
+    public function userDeleted($uid, $shareType) {
928
+        $qb = $this->dbConn->getQueryBuilder();
929
+
930
+        $qb->delete('share');
931
+
932
+        if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
933
+            /*
934 934
 			 * Delete all user shares that are owned by this user
935 935
 			 * or that are received by this user
936 936
 			 */
937 937
 
938
-			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)));
938
+            $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)));
939 939
 
940
-			$qb->andWhere(
941
-				$qb->expr()->orX(
942
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
943
-					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
944
-				)
945
-			);
946
-		} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
947
-			/*
940
+            $qb->andWhere(
941
+                $qb->expr()->orX(
942
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
943
+                    $qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
944
+                )
945
+            );
946
+        } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
947
+            /*
948 948
 			 * Delete all group shares that are owned by this user
949 949
 			 * Or special user group shares that are received by this user
950 950
 			 */
951
-			$qb->where(
952
-				$qb->expr()->andX(
953
-					$qb->expr()->orX(
954
-						$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
955
-						$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))
956
-					),
957
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
958
-				)
959
-			);
960
-
961
-			$qb->orWhere(
962
-				$qb->expr()->andX(
963
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)),
964
-					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
965
-				)
966
-			);
967
-		} else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
968
-			/*
951
+            $qb->where(
952
+                $qb->expr()->andX(
953
+                    $qb->expr()->orX(
954
+                        $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
955
+                        $qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))
956
+                    ),
957
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
958
+                )
959
+            );
960
+
961
+            $qb->orWhere(
962
+                $qb->expr()->andX(
963
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)),
964
+                    $qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
965
+                )
966
+            );
967
+        } else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
968
+            /*
969 969
 			 * Delete all link shares owned by this user.
970 970
 			 * And all link shares initiated by this user (until #22327 is in)
971 971
 			 */
972
-			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)));
973
-
974
-			$qb->andWhere(
975
-				$qb->expr()->orX(
976
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
977
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
978
-				)
979
-			);
980
-		}
981
-
982
-		$qb->execute();
983
-	}
984
-
985
-	/**
986
-	 * Delete all shares received by this group. As well as any custom group
987
-	 * shares for group members.
988
-	 *
989
-	 * @param string $gid
990
-	 */
991
-	public function groupDeleted($gid) {
992
-		/*
972
+            $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)));
973
+
974
+            $qb->andWhere(
975
+                $qb->expr()->orX(
976
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
977
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
978
+                )
979
+            );
980
+        }
981
+
982
+        $qb->execute();
983
+    }
984
+
985
+    /**
986
+     * Delete all shares received by this group. As well as any custom group
987
+     * shares for group members.
988
+     *
989
+     * @param string $gid
990
+     */
991
+    public function groupDeleted($gid) {
992
+        /*
993 993
 		 * First delete all custom group shares for group members
994 994
 		 */
995
-		$qb = $this->dbConn->getQueryBuilder();
996
-		$qb->select('id')
997
-			->from('share')
998
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
999
-			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1000
-
1001
-		$cursor = $qb->execute();
1002
-		$ids = [];
1003
-		while($row = $cursor->fetch()) {
1004
-			$ids[] = (int)$row['id'];
1005
-		}
1006
-		$cursor->closeCursor();
1007
-
1008
-		if (!empty($ids)) {
1009
-			$chunks = array_chunk($ids, 100);
1010
-			foreach ($chunks as $chunk) {
1011
-				$qb->delete('share')
1012
-					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
1013
-					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1014
-				$qb->execute();
1015
-			}
1016
-		}
1017
-
1018
-		/*
995
+        $qb = $this->dbConn->getQueryBuilder();
996
+        $qb->select('id')
997
+            ->from('share')
998
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
999
+            ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1000
+
1001
+        $cursor = $qb->execute();
1002
+        $ids = [];
1003
+        while($row = $cursor->fetch()) {
1004
+            $ids[] = (int)$row['id'];
1005
+        }
1006
+        $cursor->closeCursor();
1007
+
1008
+        if (!empty($ids)) {
1009
+            $chunks = array_chunk($ids, 100);
1010
+            foreach ($chunks as $chunk) {
1011
+                $qb->delete('share')
1012
+                    ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
1013
+                    ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1014
+                $qb->execute();
1015
+            }
1016
+        }
1017
+
1018
+        /*
1019 1019
 		 * Now delete all the group shares
1020 1020
 		 */
1021
-		$qb = $this->dbConn->getQueryBuilder();
1022
-		$qb->delete('share')
1023
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
1024
-			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1025
-		$qb->execute();
1026
-	}
1027
-
1028
-	/**
1029
-	 * Delete custom group shares to this group for this user
1030
-	 *
1031
-	 * @param string $uid
1032
-	 * @param string $gid
1033
-	 */
1034
-	public function userDeletedFromGroup($uid, $gid) {
1035
-		/*
1021
+        $qb = $this->dbConn->getQueryBuilder();
1022
+        $qb->delete('share')
1023
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
1024
+            ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1025
+        $qb->execute();
1026
+    }
1027
+
1028
+    /**
1029
+     * Delete custom group shares to this group for this user
1030
+     *
1031
+     * @param string $uid
1032
+     * @param string $gid
1033
+     */
1034
+    public function userDeletedFromGroup($uid, $gid) {
1035
+        /*
1036 1036
 		 * Get all group shares
1037 1037
 		 */
1038
-		$qb = $this->dbConn->getQueryBuilder();
1039
-		$qb->select('id')
1040
-			->from('share')
1041
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
1042
-			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1043
-
1044
-		$cursor = $qb->execute();
1045
-		$ids = [];
1046
-		while($row = $cursor->fetch()) {
1047
-			$ids[] = (int)$row['id'];
1048
-		}
1049
-		$cursor->closeCursor();
1050
-
1051
-		if (!empty($ids)) {
1052
-			$chunks = array_chunk($ids, 100);
1053
-			foreach ($chunks as $chunk) {
1054
-				/*
1038
+        $qb = $this->dbConn->getQueryBuilder();
1039
+        $qb->select('id')
1040
+            ->from('share')
1041
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
1042
+            ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1043
+
1044
+        $cursor = $qb->execute();
1045
+        $ids = [];
1046
+        while($row = $cursor->fetch()) {
1047
+            $ids[] = (int)$row['id'];
1048
+        }
1049
+        $cursor->closeCursor();
1050
+
1051
+        if (!empty($ids)) {
1052
+            $chunks = array_chunk($ids, 100);
1053
+            foreach ($chunks as $chunk) {
1054
+                /*
1055 1055
 				 * Delete all special shares wit this users for the found group shares
1056 1056
 				 */
1057
-				$qb->delete('share')
1058
-					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
1059
-					->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
1060
-					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1061
-				$qb->execute();
1062
-			}
1063
-		}
1064
-	}
1057
+                $qb->delete('share')
1058
+                    ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
1059
+                    ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
1060
+                    ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1061
+                $qb->execute();
1062
+            }
1063
+        }
1064
+    }
1065 1065
 }
Please login to merge, or discard this patch.
Spacing   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -285,7 +285,7 @@  discard block
 block discarded – undo
285 285
 			->orderBy('id');
286 286
 
287 287
 		$cursor = $qb->execute();
288
-		while($data = $cursor->fetch()) {
288
+		while ($data = $cursor->fetch()) {
289 289
 			$children[] = $this->createShare($data);
290 290
 		}
291 291
 		$cursor->closeCursor();
@@ -486,7 +486,7 @@  discard block
 block discarded – undo
486 486
 			);
487 487
 		}
488 488
 
489
-		$qb->innerJoin('s', 'filecache' ,'f', 's.file_source = f.fileid');
489
+		$qb->innerJoin('s', 'filecache', 'f', 's.file_source = f.fileid');
490 490
 		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
491 491
 
492 492
 		$qb->orderBy('id');
@@ -542,7 +542,7 @@  discard block
 block discarded – undo
542 542
 
543 543
 		$cursor = $qb->execute();
544 544
 		$shares = [];
545
-		while($data = $cursor->fetch()) {
545
+		while ($data = $cursor->fetch()) {
546 546
 			$shares[] = $this->createShare($data);
547 547
 		}
548 548
 		$cursor->closeCursor();
@@ -621,7 +621,7 @@  discard block
 block discarded – undo
621 621
 			->execute();
622 622
 
623 623
 		$shares = [];
624
-		while($data = $cursor->fetch()) {
624
+		while ($data = $cursor->fetch()) {
625 625
 			$shares[] = $this->createShare($data);
626 626
 		}
627 627
 		$cursor->closeCursor();
@@ -691,7 +691,7 @@  discard block
 block discarded – undo
691 691
 
692 692
 			$cursor = $qb->execute();
693 693
 
694
-			while($data = $cursor->fetch()) {
694
+			while ($data = $cursor->fetch()) {
695 695
 				if ($this->isAccessibleResult($data)) {
696 696
 					$shares[] = $this->createShare($data);
697 697
 				}
@@ -706,7 +706,7 @@  discard block
 block discarded – undo
706 706
 			$shares2 = [];
707 707
 
708 708
 			$start = 0;
709
-			while(true) {
709
+			while (true) {
710 710
 				$groups = array_slice($allGroups, $start, 100);
711 711
 				$start += 100;
712 712
 
@@ -749,7 +749,7 @@  discard block
 block discarded – undo
749 749
 					));
750 750
 
751 751
 				$cursor = $qb->execute();
752
-				while($data = $cursor->fetch()) {
752
+				while ($data = $cursor->fetch()) {
753 753
 					if ($offset > 0) {
754 754
 						$offset--;
755 755
 						continue;
@@ -818,14 +818,14 @@  discard block
 block discarded – undo
818 818
 	 */
819 819
 	private function createShare($data) {
820 820
 		$share = new Share($this->rootFolder, $this->userManager);
821
-		$share->setId((int)$data['id'])
822
-			->setShareType((int)$data['share_type'])
823
-			->setPermissions((int)$data['permissions'])
821
+		$share->setId((int) $data['id'])
822
+			->setShareType((int) $data['share_type'])
823
+			->setPermissions((int) $data['permissions'])
824 824
 			->setTarget($data['file_target'])
825
-			->setMailSend((bool)$data['mail_send']);
825
+			->setMailSend((bool) $data['mail_send']);
826 826
 
827 827
 		$shareTime = new \DateTime();
828
-		$shareTime->setTimestamp((int)$data['stime']);
828
+		$shareTime->setTimestamp((int) $data['stime']);
829 829
 		$share->setShareTime($shareTime);
830 830
 
831 831
 		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
@@ -840,7 +840,7 @@  discard block
 block discarded – undo
840 840
 		$share->setSharedBy($data['uid_initiator']);
841 841
 		$share->setShareOwner($data['uid_owner']);
842 842
 
843
-		$share->setNodeId((int)$data['file_source']);
843
+		$share->setNodeId((int) $data['file_source']);
844 844
 		$share->setNodeType($data['item_type']);
845 845
 
846 846
 		if ($data['expiration'] !== null) {
@@ -851,7 +851,7 @@  discard block
 block discarded – undo
851 851
 		if (isset($data['f_permissions'])) {
852 852
 			$entryData = $data;
853 853
 			$entryData['permissions'] = $entryData['f_permissions'];
854
-			$entryData['parent'] = $entryData['f_parent'];;
854
+			$entryData['parent'] = $entryData['f_parent']; ;
855 855
 			$share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
856 856
 				\OC::$server->getMimeTypeLoader()));
857 857
 		}
@@ -870,7 +870,7 @@  discard block
 block discarded – undo
870 870
 		$result = [];
871 871
 
872 872
 		$start = 0;
873
-		while(true) {
873
+		while (true) {
874 874
 			/** @var Share[] $shareSlice */
875 875
 			$shareSlice = array_slice($shares, $start, 100);
876 876
 			$start += 100;
@@ -885,7 +885,7 @@  discard block
 block discarded – undo
885 885
 			$shareMap = [];
886 886
 
887 887
 			foreach ($shareSlice as $share) {
888
-				$ids[] = (int)$share->getId();
888
+				$ids[] = (int) $share->getId();
889 889
 				$shareMap[$share->getId()] = $share;
890 890
 			}
891 891
 
@@ -902,8 +902,8 @@  discard block
 block discarded – undo
902 902
 
903 903
 			$stmt = $query->execute();
904 904
 
905
-			while($data = $stmt->fetch()) {
906
-				$shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
905
+			while ($data = $stmt->fetch()) {
906
+				$shareMap[$data['parent']]->setPermissions((int) $data['permissions']);
907 907
 				$shareMap[$data['parent']]->setTarget($data['file_target']);
908 908
 			}
909 909
 
@@ -1000,8 +1000,8 @@  discard block
 block discarded – undo
1000 1000
 
1001 1001
 		$cursor = $qb->execute();
1002 1002
 		$ids = [];
1003
-		while($row = $cursor->fetch()) {
1004
-			$ids[] = (int)$row['id'];
1003
+		while ($row = $cursor->fetch()) {
1004
+			$ids[] = (int) $row['id'];
1005 1005
 		}
1006 1006
 		$cursor->closeCursor();
1007 1007
 
@@ -1043,8 +1043,8 @@  discard block
 block discarded – undo
1043 1043
 
1044 1044
 		$cursor = $qb->execute();
1045 1045
 		$ids = [];
1046
-		while($row = $cursor->fetch()) {
1047
-			$ids[] = (int)$row['id'];
1046
+		while ($row = $cursor->fetch()) {
1047
+			$ids[] = (int) $row['id'];
1048 1048
 		}
1049 1049
 		$cursor->closeCursor();
1050 1050
 
Please login to merge, or discard this patch.
lib/private/Repair/Collation.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -90,7 +90,7 @@
 block discarded – undo
90 90
 	}
91 91
 
92 92
 	/**
93
-	 * @param \Doctrine\DBAL\Connection $connection
93
+	 * @param IDBConnection $connection
94 94
 	 * @return string[]
95 95
 	 */
96 96
 	protected function getAllNonUTF8BinTables($connection) {
Please login to merge, or discard this patch.
Indentation   +79 added lines, -79 removed lines patch added patch discarded remove patch
@@ -33,94 +33,94 @@
 block discarded – undo
33 33
 use OCP\Migration\IRepairStep;
34 34
 
35 35
 class Collation implements IRepairStep {
36
-	/**  @var IConfig */
37
-	protected $config;
36
+    /**  @var IConfig */
37
+    protected $config;
38 38
 
39
-	/** @var ILogger */
40
-	protected $logger;
39
+    /** @var ILogger */
40
+    protected $logger;
41 41
 
42
-	/** @var IDBConnection */
43
-	protected $connection;
42
+    /** @var IDBConnection */
43
+    protected $connection;
44 44
 
45
-	/** @var bool */
46
-	protected $ignoreFailures;
45
+    /** @var bool */
46
+    protected $ignoreFailures;
47 47
 
48
-	/**
49
-	 * @param IConfig $config
50
-	 * @param ILogger $logger
51
-	 * @param IDBConnection $connection
52
-	 * @param bool $ignoreFailures
53
-	 */
54
-	public function __construct(IConfig $config, ILogger $logger, IDBConnection $connection, $ignoreFailures) {
55
-		$this->connection = $connection;
56
-		$this->config = $config;
57
-		$this->logger = $logger;
58
-		$this->ignoreFailures = $ignoreFailures;
59
-	}
48
+    /**
49
+     * @param IConfig $config
50
+     * @param ILogger $logger
51
+     * @param IDBConnection $connection
52
+     * @param bool $ignoreFailures
53
+     */
54
+    public function __construct(IConfig $config, ILogger $logger, IDBConnection $connection, $ignoreFailures) {
55
+        $this->connection = $connection;
56
+        $this->config = $config;
57
+        $this->logger = $logger;
58
+        $this->ignoreFailures = $ignoreFailures;
59
+    }
60 60
 
61
-	public function getName() {
62
-		return 'Repair MySQL collation';
63
-	}
61
+    public function getName() {
62
+        return 'Repair MySQL collation';
63
+    }
64 64
 
65
-	/**
66
-	 * Fix mime types
67
-	 */
68
-	public function run(IOutput $output) {
69
-		if (!$this->connection->getDatabasePlatform() instanceof MySqlPlatform) {
70
-			$output->info('Not a mysql database -> nothing to no');
71
-			return;
72
-		}
65
+    /**
66
+     * Fix mime types
67
+     */
68
+    public function run(IOutput $output) {
69
+        if (!$this->connection->getDatabasePlatform() instanceof MySqlPlatform) {
70
+            $output->info('Not a mysql database -> nothing to no');
71
+            return;
72
+        }
73 73
 
74
-		$characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
74
+        $characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
75 75
 
76
-		$tables = $this->getAllNonUTF8BinTables($this->connection);
77
-		foreach ($tables as $table) {
78
-			$output->info("Change row format for $table ...");
79
-			$query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT = DYNAMIC;');
80
-			try {
81
-				$query->execute();
82
-			} catch (DriverException $e) {
83
-				// Just log this
84
-				$this->logger->logException($e);
85
-				if (!$this->ignoreFailures) {
86
-					throw $e;
87
-				}
88
-			}
76
+        $tables = $this->getAllNonUTF8BinTables($this->connection);
77
+        foreach ($tables as $table) {
78
+            $output->info("Change row format for $table ...");
79
+            $query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT = DYNAMIC;');
80
+            try {
81
+                $query->execute();
82
+            } catch (DriverException $e) {
83
+                // Just log this
84
+                $this->logger->logException($e);
85
+                if (!$this->ignoreFailures) {
86
+                    throw $e;
87
+                }
88
+            }
89 89
 
90
-			$output->info("Change collation for $table ...");
91
-			$query = $this->connection->prepare('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET ' . $characterSet . ' COLLATE ' . $characterSet . '_bin;');
92
-			try {
93
-				$query->execute();
94
-			} catch (DriverException $e) {
95
-				// Just log this
96
-				$this->logger->logException($e);
97
-				if (!$this->ignoreFailures) {
98
-					throw $e;
99
-				}
100
-			}
101
-		}
102
-	}
90
+            $output->info("Change collation for $table ...");
91
+            $query = $this->connection->prepare('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET ' . $characterSet . ' COLLATE ' . $characterSet . '_bin;');
92
+            try {
93
+                $query->execute();
94
+            } catch (DriverException $e) {
95
+                // Just log this
96
+                $this->logger->logException($e);
97
+                if (!$this->ignoreFailures) {
98
+                    throw $e;
99
+                }
100
+            }
101
+        }
102
+    }
103 103
 
104
-	/**
105
-	 * @param \Doctrine\DBAL\Connection $connection
106
-	 * @return string[]
107
-	 */
108
-	protected function getAllNonUTF8BinTables($connection) {
109
-		$dbName = $this->config->getSystemValue("dbname");
110
-		$characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
111
-		$rows = $connection->fetchAll(
112
-			"SELECT DISTINCT(TABLE_NAME) AS `table`" .
113
-			"	FROM INFORMATION_SCHEMA . COLUMNS" .
114
-			"	WHERE TABLE_SCHEMA = ?" .
115
-			"	AND (COLLATION_NAME <> '" . $characterSet . "_bin' OR CHARACTER_SET_NAME <> '" . $characterSet . "')" .
116
-			"	AND TABLE_NAME LIKE \"*PREFIX*%\"",
117
-			array($dbName)
118
-		);
119
-		$result = array();
120
-		foreach ($rows as $row) {
121
-			$result[] = $row['table'];
122
-		}
123
-		return $result;
124
-	}
104
+    /**
105
+     * @param \Doctrine\DBAL\Connection $connection
106
+     * @return string[]
107
+     */
108
+    protected function getAllNonUTF8BinTables($connection) {
109
+        $dbName = $this->config->getSystemValue("dbname");
110
+        $characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
111
+        $rows = $connection->fetchAll(
112
+            "SELECT DISTINCT(TABLE_NAME) AS `table`" .
113
+            "	FROM INFORMATION_SCHEMA . COLUMNS" .
114
+            "	WHERE TABLE_SCHEMA = ?" .
115
+            "	AND (COLLATION_NAME <> '" . $characterSet . "_bin' OR CHARACTER_SET_NAME <> '" . $characterSet . "')" .
116
+            "	AND TABLE_NAME LIKE \"*PREFIX*%\"",
117
+            array($dbName)
118
+        );
119
+        $result = array();
120
+        foreach ($rows as $row) {
121
+            $result[] = $row['table'];
122
+        }
123
+        return $result;
124
+    }
125 125
 }
126 126
 
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -76,7 +76,7 @@  discard block
 block discarded – undo
76 76
 		$tables = $this->getAllNonUTF8BinTables($this->connection);
77 77
 		foreach ($tables as $table) {
78 78
 			$output->info("Change row format for $table ...");
79
-			$query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT = DYNAMIC;');
79
+			$query = $this->connection->prepare('ALTER TABLE `'.$table.'` ROW_FORMAT = DYNAMIC;');
80 80
 			try {
81 81
 				$query->execute();
82 82
 			} catch (DriverException $e) {
@@ -88,7 +88,7 @@  discard block
 block discarded – undo
88 88
 			}
89 89
 
90 90
 			$output->info("Change collation for $table ...");
91
-			$query = $this->connection->prepare('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET ' . $characterSet . ' COLLATE ' . $characterSet . '_bin;');
91
+			$query = $this->connection->prepare('ALTER TABLE `'.$table.'` CONVERT TO CHARACTER SET '.$characterSet.' COLLATE '.$characterSet.'_bin;');
92 92
 			try {
93 93
 				$query->execute();
94 94
 			} catch (DriverException $e) {
@@ -109,10 +109,10 @@  discard block
 block discarded – undo
109 109
 		$dbName = $this->config->getSystemValue("dbname");
110 110
 		$characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
111 111
 		$rows = $connection->fetchAll(
112
-			"SELECT DISTINCT(TABLE_NAME) AS `table`" .
113
-			"	FROM INFORMATION_SCHEMA . COLUMNS" .
114
-			"	WHERE TABLE_SCHEMA = ?" .
115
-			"	AND (COLLATION_NAME <> '" . $characterSet . "_bin' OR CHARACTER_SET_NAME <> '" . $characterSet . "')" .
112
+			"SELECT DISTINCT(TABLE_NAME) AS `table`".
113
+			"	FROM INFORMATION_SCHEMA . COLUMNS".
114
+			"	WHERE TABLE_SCHEMA = ?".
115
+			"	AND (COLLATION_NAME <> '".$characterSet."_bin' OR CHARACTER_SET_NAME <> '".$characterSet."')".
116 116
 			"	AND TABLE_NAME LIKE \"*PREFIX*%\"",
117 117
 			array($dbName)
118 118
 		);
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/ShareTypeList.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -61,7 +61,7 @@
 block discarded – undo
61 61
 	 * The deserialize method is called during xml parsing.
62 62
 	 *
63 63
 	 * @param Reader $reader
64
-	 * @return mixed
64
+	 * @return null|ShareTypeList
65 65
 	 */
66 66
 	static function xmlDeserialize(Reader $reader) {
67 67
 		$shareTypes = [];
Please login to merge, or discard this patch.
Indentation   +51 added lines, -51 removed lines patch added patch discarded remove patch
@@ -32,61 +32,61 @@
 block discarded – undo
32 32
  * This property contains multiple "share-type" elements, each containing a share type.
33 33
  */
34 34
 class ShareTypeList implements Element {
35
-	const NS_OWNCLOUD = 'http://owncloud.org/ns';
35
+    const NS_OWNCLOUD = 'http://owncloud.org/ns';
36 36
 
37
-	/**
38
-	 * Share types
39
-	 *
40
-	 * @var int[]
41
-	 */
42
-	private $shareTypes;
37
+    /**
38
+     * Share types
39
+     *
40
+     * @var int[]
41
+     */
42
+    private $shareTypes;
43 43
 
44
-	/**
45
-	 * @param int[] $shareTypes
46
-	 */
47
-	public function __construct($shareTypes) {
48
-		$this->shareTypes = $shareTypes;
49
-	}
44
+    /**
45
+     * @param int[] $shareTypes
46
+     */
47
+    public function __construct($shareTypes) {
48
+        $this->shareTypes = $shareTypes;
49
+    }
50 50
 
51
-	/**
52
-	 * Returns the share types
53
-	 *
54
-	 * @return int[]
55
-	 */
56
-	public function getShareTypes() {
57
-		return $this->shareTypes;
58
-	}
51
+    /**
52
+     * Returns the share types
53
+     *
54
+     * @return int[]
55
+     */
56
+    public function getShareTypes() {
57
+        return $this->shareTypes;
58
+    }
59 59
 
60
-	/**
61
-	 * The deserialize method is called during xml parsing.
62
-	 *
63
-	 * @param Reader $reader
64
-	 * @return mixed
65
-	 */
66
-	static function xmlDeserialize(Reader $reader) {
67
-		$shareTypes = [];
60
+    /**
61
+     * The deserialize method is called during xml parsing.
62
+     *
63
+     * @param Reader $reader
64
+     * @return mixed
65
+     */
66
+    static function xmlDeserialize(Reader $reader) {
67
+        $shareTypes = [];
68 68
 
69
-		$tree = $reader->parseInnerTree();
70
-		if ($tree === null) {
71
-			return null;
72
-		}
73
-		foreach ($tree as $elem) {
74
-			if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}share-type') {
75
-				$shareTypes[] = (int)$elem['value'];
76
-			}
77
-		}
78
-		return new self($shareTypes);
79
-	}
69
+        $tree = $reader->parseInnerTree();
70
+        if ($tree === null) {
71
+            return null;
72
+        }
73
+        foreach ($tree as $elem) {
74
+            if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}share-type') {
75
+                $shareTypes[] = (int)$elem['value'];
76
+            }
77
+        }
78
+        return new self($shareTypes);
79
+    }
80 80
 
81
-	/**
82
-	 * The xmlSerialize metod is called during xml writing.
83
-	 *
84
-	 * @param Writer $writer
85
-	 * @return void
86
-	 */
87
-	function xmlSerialize(Writer $writer) {
88
-		foreach ($this->shareTypes as $shareType) {
89
-			$writer->writeElement('{' . self::NS_OWNCLOUD . '}share-type', $shareType);
90
-		}
91
-	}
81
+    /**
82
+     * The xmlSerialize metod is called during xml writing.
83
+     *
84
+     * @param Writer $writer
85
+     * @return void
86
+     */
87
+    function xmlSerialize(Writer $writer) {
88
+        foreach ($this->shareTypes as $shareType) {
89
+            $writer->writeElement('{' . self::NS_OWNCLOUD . '}share-type', $shareType);
90
+        }
91
+    }
92 92
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -71,8 +71,8 @@  discard block
 block discarded – undo
71 71
 			return null;
72 72
 		}
73 73
 		foreach ($tree as $elem) {
74
-			if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}share-type') {
75
-				$shareTypes[] = (int)$elem['value'];
74
+			if ($elem['name'] === '{'.self::NS_OWNCLOUD.'}share-type') {
75
+				$shareTypes[] = (int) $elem['value'];
76 76
 			}
77 77
 		}
78 78
 		return new self($shareTypes);
@@ -86,7 +86,7 @@  discard block
 block discarded – undo
86 86
 	 */
87 87
 	function xmlSerialize(Writer $writer) {
88 88
 		foreach ($this->shareTypes as $shareType) {
89
-			$writer->writeElement('{' . self::NS_OWNCLOUD . '}share-type', $shareType);
89
+			$writer->writeElement('{'.self::NS_OWNCLOUD.'}share-type', $shareType);
90 90
 		}
91 91
 	}
92 92
 }
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/TagList.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -80,7 +80,7 @@
 block discarded – undo
80 80
 	 * the next element.
81 81
 	 *
82 82
 	 * @param Reader $reader
83
-	 * @return mixed
83
+	 * @return null|TagList
84 84
 	 */
85 85
 	static function xmlDeserialize(Reader $reader) {
86 86
 		$tags = [];
Please login to merge, or discard this patch.
Indentation   +79 added lines, -79 removed lines patch added patch discarded remove patch
@@ -34,92 +34,92 @@
 block discarded – undo
34 34
  * This property contains multiple "tag" elements, each containing a tag name.
35 35
  */
36 36
 class TagList implements Element {
37
-	const NS_OWNCLOUD = 'http://owncloud.org/ns';
37
+    const NS_OWNCLOUD = 'http://owncloud.org/ns';
38 38
 
39
-	/**
40
-	 * tags
41
-	 *
42
-	 * @var array
43
-	 */
44
-	private $tags;
39
+    /**
40
+     * tags
41
+     *
42
+     * @var array
43
+     */
44
+    private $tags;
45 45
 
46
-	/**
47
-	 * @param array $tags
48
-	 */
49
-	public function __construct(array $tags) {
50
-		$this->tags = $tags;
51
-	}
46
+    /**
47
+     * @param array $tags
48
+     */
49
+    public function __construct(array $tags) {
50
+        $this->tags = $tags;
51
+    }
52 52
 
53
-	/**
54
-	 * Returns the tags
55
-	 *
56
-	 * @return array
57
-	 */
58
-	public function getTags() {
53
+    /**
54
+     * Returns the tags
55
+     *
56
+     * @return array
57
+     */
58
+    public function getTags() {
59 59
 
60
-		return $this->tags;
60
+        return $this->tags;
61 61
 
62
-	}
62
+    }
63 63
 
64
-	/**
65
-	 * The deserialize method is called during xml parsing.
66
-	 *
67
-	 * This method is called statictly, this is because in theory this method
68
-	 * may be used as a type of constructor, or factory method.
69
-	 *
70
-	 * Often you want to return an instance of the current class, but you are
71
-	 * free to return other data as well.
72
-	 *
73
-	 * You are responsible for advancing the reader to the next element. Not
74
-	 * doing anything will result in a never-ending loop.
75
-	 *
76
-	 * If you just want to skip parsing for this element altogether, you can
77
-	 * just call $reader->next();
78
-	 *
79
-	 * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
80
-	 * the next element.
81
-	 *
82
-	 * @param Reader $reader
83
-	 * @return mixed
84
-	 */
85
-	static function xmlDeserialize(Reader $reader) {
86
-		$tags = [];
64
+    /**
65
+     * The deserialize method is called during xml parsing.
66
+     *
67
+     * This method is called statictly, this is because in theory this method
68
+     * may be used as a type of constructor, or factory method.
69
+     *
70
+     * Often you want to return an instance of the current class, but you are
71
+     * free to return other data as well.
72
+     *
73
+     * You are responsible for advancing the reader to the next element. Not
74
+     * doing anything will result in a never-ending loop.
75
+     *
76
+     * If you just want to skip parsing for this element altogether, you can
77
+     * just call $reader->next();
78
+     *
79
+     * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
80
+     * the next element.
81
+     *
82
+     * @param Reader $reader
83
+     * @return mixed
84
+     */
85
+    static function xmlDeserialize(Reader $reader) {
86
+        $tags = [];
87 87
 
88
-		$tree = $reader->parseInnerTree();
89
-		if ($tree === null) {
90
-			return null;
91
-		}
92
-		foreach ($tree as $elem) {
93
-			if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}tag') {
94
-				$tags[] = $elem['value'];
95
-			}
96
-		}
97
-		return new self($tags);
98
-	}
88
+        $tree = $reader->parseInnerTree();
89
+        if ($tree === null) {
90
+            return null;
91
+        }
92
+        foreach ($tree as $elem) {
93
+            if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}tag') {
94
+                $tags[] = $elem['value'];
95
+            }
96
+        }
97
+        return new self($tags);
98
+    }
99 99
 
100
-	/**
101
-	 * The xmlSerialize metod is called during xml writing.
102
-	 *
103
-	 * Use the $writer argument to write its own xml serialization.
104
-	 *
105
-	 * An important note: do _not_ create a parent element. Any element
106
-	 * implementing XmlSerializble should only ever write what's considered
107
-	 * its 'inner xml'.
108
-	 *
109
-	 * The parent of the current element is responsible for writing a
110
-	 * containing element.
111
-	 *
112
-	 * This allows serializers to be re-used for different element names.
113
-	 *
114
-	 * If you are opening new elements, you must also close them again.
115
-	 *
116
-	 * @param Writer $writer
117
-	 * @return void
118
-	 */
119
-	function xmlSerialize(Writer $writer) {
100
+    /**
101
+     * The xmlSerialize metod is called during xml writing.
102
+     *
103
+     * Use the $writer argument to write its own xml serialization.
104
+     *
105
+     * An important note: do _not_ create a parent element. Any element
106
+     * implementing XmlSerializble should only ever write what's considered
107
+     * its 'inner xml'.
108
+     *
109
+     * The parent of the current element is responsible for writing a
110
+     * containing element.
111
+     *
112
+     * This allows serializers to be re-used for different element names.
113
+     *
114
+     * If you are opening new elements, you must also close them again.
115
+     *
116
+     * @param Writer $writer
117
+     * @return void
118
+     */
119
+    function xmlSerialize(Writer $writer) {
120 120
 
121
-		foreach ($this->tags as $tag) {
122
-			$writer->writeElement('{' . self::NS_OWNCLOUD . '}tag', $tag);
123
-		}
124
-	}
121
+        foreach ($this->tags as $tag) {
122
+            $writer->writeElement('{' . self::NS_OWNCLOUD . '}tag', $tag);
123
+        }
124
+    }
125 125
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -90,7 +90,7 @@  discard block
 block discarded – undo
90 90
 			return null;
91 91
 		}
92 92
 		foreach ($tree as $elem) {
93
-			if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}tag') {
93
+			if ($elem['name'] === '{'.self::NS_OWNCLOUD.'}tag') {
94 94
 				$tags[] = $elem['value'];
95 95
 			}
96 96
 		}
@@ -119,7 +119,7 @@  discard block
 block discarded – undo
119 119
 	function xmlSerialize(Writer $writer) {
120 120
 
121 121
 		foreach ($this->tags as $tag) {
122
-			$writer->writeElement('{' . self::NS_OWNCLOUD . '}tag', $tag);
122
+			$writer->writeElement('{'.self::NS_OWNCLOUD.'}tag', $tag);
123 123
 		}
124 124
 	}
125 125
 }
Please login to merge, or discard this patch.
lib/private/Share/MailNotifications.php 3 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -30,7 +30,6 @@
 block discarded – undo
30 30
 
31 31
 namespace OC\Share;
32 32
 
33
-use DateTime;
34 33
 use OCP\IL10N;
35 34
 use OCP\IURLGenerator;
36 35
 use OCP\IUser;
Please login to merge, or discard this patch.
Indentation   +100 added lines, -100 removed lines patch added patch discarded remove patch
@@ -46,113 +46,113 @@
 block discarded – undo
46 46
  */
47 47
 class MailNotifications {
48 48
 
49
-	/** @var IUser sender userId */
50
-	private $user;
51
-	/** @var string sender email address */
52
-	private $replyTo;
53
-	/** @var string */
54
-	private $senderDisplayName;
55
-	/** @var IL10N */
56
-	private $l;
57
-	/** @var IMailer */
58
-	private $mailer;
59
-	/** @var Defaults */
60
-	private $defaults;
61
-	/** @var ILogger */
62
-	private $logger;
63
-	/** @var IURLGenerator */
64
-	private $urlGenerator;
49
+    /** @var IUser sender userId */
50
+    private $user;
51
+    /** @var string sender email address */
52
+    private $replyTo;
53
+    /** @var string */
54
+    private $senderDisplayName;
55
+    /** @var IL10N */
56
+    private $l;
57
+    /** @var IMailer */
58
+    private $mailer;
59
+    /** @var Defaults */
60
+    private $defaults;
61
+    /** @var ILogger */
62
+    private $logger;
63
+    /** @var IURLGenerator */
64
+    private $urlGenerator;
65 65
 
66
-	/**
67
-	 * @param IUser $user
68
-	 * @param IL10N $l10n
69
-	 * @param IMailer $mailer
70
-	 * @param ILogger $logger
71
-	 * @param Defaults $defaults
72
-	 * @param IURLGenerator $urlGenerator
73
-	 */
74
-	public function __construct(IUser $user,
75
-								IL10N $l10n,
76
-								IMailer $mailer,
77
-								ILogger $logger,
78
-								Defaults $defaults,
79
-								IURLGenerator $urlGenerator) {
80
-		$this->l = $l10n;
81
-		$this->user = $user;
82
-		$this->mailer = $mailer;
83
-		$this->logger = $logger;
84
-		$this->defaults = $defaults;
85
-		$this->urlGenerator = $urlGenerator;
66
+    /**
67
+     * @param IUser $user
68
+     * @param IL10N $l10n
69
+     * @param IMailer $mailer
70
+     * @param ILogger $logger
71
+     * @param Defaults $defaults
72
+     * @param IURLGenerator $urlGenerator
73
+     */
74
+    public function __construct(IUser $user,
75
+                                IL10N $l10n,
76
+                                IMailer $mailer,
77
+                                ILogger $logger,
78
+                                Defaults $defaults,
79
+                                IURLGenerator $urlGenerator) {
80
+        $this->l = $l10n;
81
+        $this->user = $user;
82
+        $this->mailer = $mailer;
83
+        $this->logger = $logger;
84
+        $this->defaults = $defaults;
85
+        $this->urlGenerator = $urlGenerator;
86 86
 
87
-		$this->replyTo = $this->user->getEMailAddress();
88
-		$this->senderDisplayName = $this->user->getDisplayName();
89
-	}
87
+        $this->replyTo = $this->user->getEMailAddress();
88
+        $this->senderDisplayName = $this->user->getDisplayName();
89
+    }
90 90
 
91
-	/**
92
-	 * inform recipient about public link share
93
-	 *
94
-	 * @param string $recipient recipient email address
95
-	 * @param string $filename the shared file
96
-	 * @param string $link the public link
97
-	 * @param int $expiration expiration date (timestamp)
98
-	 * @return string[] $result of failed recipients
99
-	 */
100
-	public function sendLinkShareMail($recipient, $filename, $link, $expiration) {
101
-		$subject = (string)$this->l->t('%s shared »%s« with you', [$this->senderDisplayName, $filename]);
102
-		list($htmlBody, $textBody) = $this->createMailBody($filename, $link, $expiration);
91
+    /**
92
+     * inform recipient about public link share
93
+     *
94
+     * @param string $recipient recipient email address
95
+     * @param string $filename the shared file
96
+     * @param string $link the public link
97
+     * @param int $expiration expiration date (timestamp)
98
+     * @return string[] $result of failed recipients
99
+     */
100
+    public function sendLinkShareMail($recipient, $filename, $link, $expiration) {
101
+        $subject = (string)$this->l->t('%s shared »%s« with you', [$this->senderDisplayName, $filename]);
102
+        list($htmlBody, $textBody) = $this->createMailBody($filename, $link, $expiration);
103 103
 
104
-		$recipient = str_replace([', ', '; ', ',', ';', ' '], ',', $recipient);
105
-		$recipients = explode(',', $recipient);
106
-		try {
107
-			$message = $this->mailer->createMessage();
108
-			$message->setSubject($subject);
109
-			$message->setTo($recipients);
110
-			$message->setHtmlBody($htmlBody);
111
-			$message->setPlainBody($textBody);
112
-			$message->setFrom([
113
-				Util::getDefaultEmailAddress('sharing-noreply') =>
114
-					(string)$this->l->t('%s via %s', [
115
-						$this->senderDisplayName,
116
-						$this->defaults->getName()
117
-					]),
118
-			]);
119
-			if(!is_null($this->replyTo)) {
120
-				$message->setReplyTo([$this->replyTo]);
121
-			}
104
+        $recipient = str_replace([', ', '; ', ',', ';', ' '], ',', $recipient);
105
+        $recipients = explode(',', $recipient);
106
+        try {
107
+            $message = $this->mailer->createMessage();
108
+            $message->setSubject($subject);
109
+            $message->setTo($recipients);
110
+            $message->setHtmlBody($htmlBody);
111
+            $message->setPlainBody($textBody);
112
+            $message->setFrom([
113
+                Util::getDefaultEmailAddress('sharing-noreply') =>
114
+                    (string)$this->l->t('%s via %s', [
115
+                        $this->senderDisplayName,
116
+                        $this->defaults->getName()
117
+                    ]),
118
+            ]);
119
+            if(!is_null($this->replyTo)) {
120
+                $message->setReplyTo([$this->replyTo]);
121
+            }
122 122
 
123
-			return $this->mailer->send($message);
124
-		} catch (\Exception $e) {
125
-			$this->logger->error("Can't send mail with public link to $recipient: ".$e->getMessage(), ['app' => 'sharing']);
126
-			return [$recipient];
127
-		}
128
-	}
123
+            return $this->mailer->send($message);
124
+        } catch (\Exception $e) {
125
+            $this->logger->error("Can't send mail with public link to $recipient: ".$e->getMessage(), ['app' => 'sharing']);
126
+            return [$recipient];
127
+        }
128
+    }
129 129
 
130
-	/**
131
-	 * create mail body for plain text and html mail
132
-	 *
133
-	 * @param string $filename the shared file
134
-	 * @param string $link link to the shared file
135
-	 * @param int $expiration expiration date (timestamp)
136
-	 * @param string $prefix prefix of mail template files
137
-	 * @return array an array of the html mail body and the plain text mail body
138
-	 */
139
-	private function createMailBody($filename, $link, $expiration, $prefix = '') {
140
-		$formattedDate = $expiration ? $this->l->l('date', $expiration) : null;
130
+    /**
131
+     * create mail body for plain text and html mail
132
+     *
133
+     * @param string $filename the shared file
134
+     * @param string $link link to the shared file
135
+     * @param int $expiration expiration date (timestamp)
136
+     * @param string $prefix prefix of mail template files
137
+     * @return array an array of the html mail body and the plain text mail body
138
+     */
139
+    private function createMailBody($filename, $link, $expiration, $prefix = '') {
140
+        $formattedDate = $expiration ? $this->l->l('date', $expiration) : null;
141 141
 
142
-		$html = new \OC_Template('core', $prefix . 'mail', '');
143
-		$html->assign ('link', $link);
144
-		$html->assign ('user_displayname', $this->senderDisplayName);
145
-		$html->assign ('filename', $filename);
146
-		$html->assign('expiration',  $formattedDate);
147
-		$htmlMail = $html->fetchPage();
142
+        $html = new \OC_Template('core', $prefix . 'mail', '');
143
+        $html->assign ('link', $link);
144
+        $html->assign ('user_displayname', $this->senderDisplayName);
145
+        $html->assign ('filename', $filename);
146
+        $html->assign('expiration',  $formattedDate);
147
+        $htmlMail = $html->fetchPage();
148 148
 
149
-		$plainText = new \OC_Template('core', $prefix . 'altmail', '');
150
-		$plainText->assign ('link', $link);
151
-		$plainText->assign ('user_displayname', $this->senderDisplayName);
152
-		$plainText->assign ('filename', $filename);
153
-		$plainText->assign('expiration', $formattedDate);
154
-		$plainTextMail = $plainText->fetchPage();
149
+        $plainText = new \OC_Template('core', $prefix . 'altmail', '');
150
+        $plainText->assign ('link', $link);
151
+        $plainText->assign ('user_displayname', $this->senderDisplayName);
152
+        $plainText->assign ('filename', $filename);
153
+        $plainText->assign('expiration', $formattedDate);
154
+        $plainTextMail = $plainText->fetchPage();
155 155
 
156
-		return [$htmlMail, $plainTextMail];
157
-	}
156
+        return [$htmlMail, $plainTextMail];
157
+    }
158 158
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -98,7 +98,7 @@  discard block
 block discarded – undo
98 98
 	 * @return string[] $result of failed recipients
99 99
 	 */
100 100
 	public function sendLinkShareMail($recipient, $filename, $link, $expiration) {
101
-		$subject = (string)$this->l->t('%s shared »%s« with you', [$this->senderDisplayName, $filename]);
101
+		$subject = (string) $this->l->t('%s shared »%s« with you', [$this->senderDisplayName, $filename]);
102 102
 		list($htmlBody, $textBody) = $this->createMailBody($filename, $link, $expiration);
103 103
 
104 104
 		$recipient = str_replace([', ', '; ', ',', ';', ' '], ',', $recipient);
@@ -111,12 +111,12 @@  discard block
 block discarded – undo
111 111
 			$message->setPlainBody($textBody);
112 112
 			$message->setFrom([
113 113
 				Util::getDefaultEmailAddress('sharing-noreply') =>
114
-					(string)$this->l->t('%s via %s', [
114
+					(string) $this->l->t('%s via %s', [
115 115
 						$this->senderDisplayName,
116 116
 						$this->defaults->getName()
117 117
 					]),
118 118
 			]);
119
-			if(!is_null($this->replyTo)) {
119
+			if (!is_null($this->replyTo)) {
120 120
 				$message->setReplyTo([$this->replyTo]);
121 121
 			}
122 122
 
@@ -139,17 +139,17 @@  discard block
 block discarded – undo
139 139
 	private function createMailBody($filename, $link, $expiration, $prefix = '') {
140 140
 		$formattedDate = $expiration ? $this->l->l('date', $expiration) : null;
141 141
 
142
-		$html = new \OC_Template('core', $prefix . 'mail', '');
143
-		$html->assign ('link', $link);
144
-		$html->assign ('user_displayname', $this->senderDisplayName);
145
-		$html->assign ('filename', $filename);
146
-		$html->assign('expiration',  $formattedDate);
142
+		$html = new \OC_Template('core', $prefix.'mail', '');
143
+		$html->assign('link', $link);
144
+		$html->assign('user_displayname', $this->senderDisplayName);
145
+		$html->assign('filename', $filename);
146
+		$html->assign('expiration', $formattedDate);
147 147
 		$htmlMail = $html->fetchPage();
148 148
 
149
-		$plainText = new \OC_Template('core', $prefix . 'altmail', '');
150
-		$plainText->assign ('link', $link);
151
-		$plainText->assign ('user_displayname', $this->senderDisplayName);
152
-		$plainText->assign ('filename', $filename);
149
+		$plainText = new \OC_Template('core', $prefix.'altmail', '');
150
+		$plainText->assign('link', $link);
151
+		$plainText->assign('user_displayname', $this->senderDisplayName);
152
+		$plainText->assign('filename', $filename);
153 153
 		$plainText->assign('expiration', $formattedDate);
154 154
 		$plainTextMail = $plainText->fetchPage();
155 155
 
Please login to merge, or discard this patch.
lib/private/legacy/app.php 4 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -51,7 +51,6 @@
 block discarded – undo
51 51
 use OC\App\InfoParser;
52 52
 use OC\App\Platform;
53 53
 use OC\Installer;
54
-use OC\OCSClient;
55 54
 use OC\Repair;
56 55
 use OCP\App\ManagerEvent;
57 56
 
Please login to merge, or discard this patch.
Doc Comments   +6 added lines, -1 removed lines patch added patch discarded remove patch
@@ -1063,7 +1063,7 @@  discard block
 block discarded – undo
1063 1063
 	 * @param string $app
1064 1064
 	 * @param \OCP\IConfig $config
1065 1065
 	 * @param \OCP\IL10N $l
1066
-	 * @return bool
1066
+	 * @return string
1067 1067
 	 *
1068 1068
 	 * @throws Exception if app is not compatible with this version of ownCloud
1069 1069
 	 * @throws Exception if no app-name was specified
@@ -1243,6 +1243,11 @@  discard block
 block discarded – undo
1243 1243
 		}
1244 1244
 	}
1245 1245
 
1246
+	/**
1247
+	 * @param string $lang
1248
+	 *
1249
+	 * @return string
1250
+	 */
1246 1251
 	protected static function findBestL10NOption($options, $lang) {
1247 1252
 		$fallback = $similarLangFallback = $englishFallback = false;
1248 1253
 
Please login to merge, or discard this patch.
Indentation   +1271 added lines, -1271 removed lines patch added patch discarded remove patch
@@ -61,1275 +61,1275 @@
 block discarded – undo
61 61
  * upgrading and removing apps.
62 62
  */
63 63
 class OC_App {
64
-	static private $appVersion = [];
65
-	static private $adminForms = array();
66
-	static private $personalForms = array();
67
-	static private $appInfo = array();
68
-	static private $appTypes = array();
69
-	static private $loadedApps = array();
70
-	static private $altLogin = array();
71
-	static private $alreadyRegistered = [];
72
-	const officialApp = 200;
73
-
74
-	/**
75
-	 * clean the appId
76
-	 *
77
-	 * @param string|boolean $app AppId that needs to be cleaned
78
-	 * @return string
79
-	 */
80
-	public static function cleanAppId($app) {
81
-		return str_replace(array('\0', '/', '\\', '..'), '', $app);
82
-	}
83
-
84
-	/**
85
-	 * Check if an app is loaded
86
-	 *
87
-	 * @param string $app
88
-	 * @return bool
89
-	 */
90
-	public static function isAppLoaded($app) {
91
-		return in_array($app, self::$loadedApps, true);
92
-	}
93
-
94
-	/**
95
-	 * loads all apps
96
-	 *
97
-	 * @param string[] | string | null $types
98
-	 * @return bool
99
-	 *
100
-	 * This function walks through the ownCloud directory and loads all apps
101
-	 * it can find. A directory contains an app if the file /appinfo/info.xml
102
-	 * exists.
103
-	 *
104
-	 * if $types is set, only apps of those types will be loaded
105
-	 */
106
-	public static function loadApps($types = null) {
107
-		if (\OC::$server->getSystemConfig()->getValue('maintenance', false)) {
108
-			return false;
109
-		}
110
-		// Load the enabled apps here
111
-		$apps = self::getEnabledApps();
112
-
113
-		// Add each apps' folder as allowed class path
114
-		foreach($apps as $app) {
115
-			$path = self::getAppPath($app);
116
-			if($path !== false) {
117
-				self::registerAutoloading($app, $path);
118
-			}
119
-		}
120
-
121
-		// prevent app.php from printing output
122
-		ob_start();
123
-		foreach ($apps as $app) {
124
-			if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
125
-				self::loadApp($app);
126
-			}
127
-		}
128
-		ob_end_clean();
129
-
130
-		return true;
131
-	}
132
-
133
-	/**
134
-	 * load a single app
135
-	 *
136
-	 * @param string $app
137
-	 * @param bool $checkUpgrade whether an upgrade check should be done
138
-	 * @throws \OC\NeedsUpdateException
139
-	 */
140
-	public static function loadApp($app, $checkUpgrade = true) {
141
-		self::$loadedApps[] = $app;
142
-		$appPath = self::getAppPath($app);
143
-		if($appPath === false) {
144
-			return;
145
-		}
146
-
147
-		// in case someone calls loadApp() directly
148
-		self::registerAutoloading($app, $appPath);
149
-
150
-		if (is_file($appPath . '/appinfo/app.php')) {
151
-			\OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
152
-			if ($checkUpgrade and self::shouldUpgrade($app)) {
153
-				throw new \OC\NeedsUpdateException();
154
-			}
155
-			self::requireAppFile($app);
156
-			if (self::isType($app, array('authentication'))) {
157
-				// since authentication apps affect the "is app enabled for group" check,
158
-				// the enabled apps cache needs to be cleared to make sure that the
159
-				// next time getEnableApps() is called it will also include apps that were
160
-				// enabled for groups
161
-				self::$enabledAppsCache = array();
162
-			}
163
-			\OC::$server->getEventLogger()->end('load_app_' . $app);
164
-		}
165
-
166
-		$info = self::getAppInfo($app);
167
-		if (!empty($info['activity']['filters'])) {
168
-			foreach ($info['activity']['filters'] as $filter) {
169
-				\OC::$server->getActivityManager()->registerFilter($filter);
170
-			}
171
-		}
172
-		if (!empty($info['activity']['settings'])) {
173
-			foreach ($info['activity']['settings'] as $setting) {
174
-				\OC::$server->getActivityManager()->registerSetting($setting);
175
-			}
176
-		}
177
-		if (!empty($info['activity']['providers'])) {
178
-			foreach ($info['activity']['providers'] as $provider) {
179
-				\OC::$server->getActivityManager()->registerProvider($provider);
180
-			}
181
-		}
182
-	}
183
-
184
-	/**
185
-	 * @internal
186
-	 * @param string $app
187
-	 * @param string $path
188
-	 */
189
-	public static function registerAutoloading($app, $path) {
190
-		$key = $app . '-' . $path;
191
-		if(isset(self::$alreadyRegistered[$key])) {
192
-			return;
193
-		}
194
-		self::$alreadyRegistered[$key] = true;
195
-		// Register on PSR-4 composer autoloader
196
-		$appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
197
-		\OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
198
-		if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
199
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
200
-		}
201
-
202
-		// Register on legacy autoloader
203
-		\OC::$loader->addValidRoot($path);
204
-	}
205
-
206
-	/**
207
-	 * Load app.php from the given app
208
-	 *
209
-	 * @param string $app app name
210
-	 */
211
-	private static function requireAppFile($app) {
212
-		try {
213
-			// encapsulated here to avoid variable scope conflicts
214
-			require_once $app . '/appinfo/app.php';
215
-		} catch (Error $ex) {
216
-			\OC::$server->getLogger()->logException($ex);
217
-			$blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps();
218
-			if (!in_array($app, $blacklist)) {
219
-				self::disable($app);
220
-			}
221
-		}
222
-	}
223
-
224
-	/**
225
-	 * check if an app is of a specific type
226
-	 *
227
-	 * @param string $app
228
-	 * @param string|array $types
229
-	 * @return bool
230
-	 */
231
-	public static function isType($app, $types) {
232
-		if (is_string($types)) {
233
-			$types = array($types);
234
-		}
235
-		$appTypes = self::getAppTypes($app);
236
-		foreach ($types as $type) {
237
-			if (array_search($type, $appTypes) !== false) {
238
-				return true;
239
-			}
240
-		}
241
-		return false;
242
-	}
243
-
244
-	/**
245
-	 * get the types of an app
246
-	 *
247
-	 * @param string $app
248
-	 * @return array
249
-	 */
250
-	private static function getAppTypes($app) {
251
-		//load the cache
252
-		if (count(self::$appTypes) == 0) {
253
-			self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
254
-		}
255
-
256
-		if (isset(self::$appTypes[$app])) {
257
-			return explode(',', self::$appTypes[$app]);
258
-		} else {
259
-			return array();
260
-		}
261
-	}
262
-
263
-	/**
264
-	 * read app types from info.xml and cache them in the database
265
-	 */
266
-	public static function setAppTypes($app) {
267
-		$appData = self::getAppInfo($app);
268
-		if(!is_array($appData)) {
269
-			return;
270
-		}
271
-
272
-		if (isset($appData['types'])) {
273
-			$appTypes = implode(',', $appData['types']);
274
-		} else {
275
-			$appTypes = '';
276
-			$appData['types'] = [];
277
-		}
278
-
279
-		\OC::$server->getAppConfig()->setValue($app, 'types', $appTypes);
280
-
281
-		if (\OC::$server->getAppManager()->hasProtectedAppType($appData['types'])) {
282
-			$enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'yes');
283
-			if ($enabled !== 'yes' && $enabled !== 'no') {
284
-				\OC::$server->getAppConfig()->setValue($app, 'enabled', 'yes');
285
-			}
286
-		}
287
-	}
288
-
289
-	/**
290
-	 * check if app is shipped
291
-	 *
292
-	 * @param string $appId the id of the app to check
293
-	 * @return bool
294
-	 *
295
-	 * Check if an app that is installed is a shipped app or installed from the appstore.
296
-	 */
297
-	public static function isShipped($appId) {
298
-		return \OC::$server->getAppManager()->isShipped($appId);
299
-	}
300
-
301
-	/**
302
-	 * get all enabled apps
303
-	 */
304
-	protected static $enabledAppsCache = array();
305
-
306
-	/**
307
-	 * Returns apps enabled for the current user.
308
-	 *
309
-	 * @param bool $forceRefresh whether to refresh the cache
310
-	 * @param bool $all whether to return apps for all users, not only the
311
-	 * currently logged in one
312
-	 * @return string[]
313
-	 */
314
-	public static function getEnabledApps($forceRefresh = false, $all = false) {
315
-		if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
316
-			return array();
317
-		}
318
-		// in incognito mode or when logged out, $user will be false,
319
-		// which is also the case during an upgrade
320
-		$appManager = \OC::$server->getAppManager();
321
-		if ($all) {
322
-			$user = null;
323
-		} else {
324
-			$user = \OC::$server->getUserSession()->getUser();
325
-		}
326
-
327
-		if (is_null($user)) {
328
-			$apps = $appManager->getInstalledApps();
329
-		} else {
330
-			$apps = $appManager->getEnabledAppsForUser($user);
331
-		}
332
-		$apps = array_filter($apps, function ($app) {
333
-			return $app !== 'files';//we add this manually
334
-		});
335
-		sort($apps);
336
-		array_unshift($apps, 'files');
337
-		return $apps;
338
-	}
339
-
340
-	/**
341
-	 * checks whether or not an app is enabled
342
-	 *
343
-	 * @param string $app app
344
-	 * @return bool
345
-	 *
346
-	 * This function checks whether or not an app is enabled.
347
-	 */
348
-	public static function isEnabled($app) {
349
-		return \OC::$server->getAppManager()->isEnabledForUser($app);
350
-	}
351
-
352
-	/**
353
-	 * enables an app
354
-	 *
355
-	 * @param string $appId
356
-	 * @param array $groups (optional) when set, only these groups will have access to the app
357
-	 * @throws \Exception
358
-	 * @return void
359
-	 *
360
-	 * This function set an app as enabled in appconfig.
361
-	 */
362
-	public function enable($appId,
363
-						   $groups = null) {
364
-		self::$enabledAppsCache = []; // flush
365
-		$l = \OC::$server->getL10N('core');
366
-		$config = \OC::$server->getConfig();
367
-
368
-		// Check if app is already downloaded
369
-		$installer = new Installer(
370
-			\OC::$server->getAppFetcher(),
371
-			\OC::$server->getHTTPClientService(),
372
-			\OC::$server->getTempManager(),
373
-			\OC::$server->getLogger()
374
-		);
375
-		$isDownloaded = $installer->isDownloaded($appId);
376
-
377
-		if(!$isDownloaded) {
378
-			$installer->downloadApp($appId);
379
-		}
380
-
381
-		if (!Installer::isInstalled($appId)) {
382
-			$appId = self::installApp(
383
-				$appId,
384
-				$config,
385
-				$l
386
-			);
387
-			$appPath = self::getAppPath($appId);
388
-			self::registerAutoloading($appId, $appPath);
389
-			$installer->installApp($appId);
390
-		} else {
391
-			// check for required dependencies
392
-			$info = self::getAppInfo($appId);
393
-			self::checkAppDependencies($config, $l, $info);
394
-			$appPath = self::getAppPath($appId);
395
-			self::registerAutoloading($appId, $appPath);
396
-			$installer->installApp($appId);
397
-		}
398
-
399
-		$appManager = \OC::$server->getAppManager();
400
-		if (!is_null($groups)) {
401
-			$groupManager = \OC::$server->getGroupManager();
402
-			$groupsList = [];
403
-			foreach ($groups as $group) {
404
-				$groupItem = $groupManager->get($group);
405
-				if ($groupItem instanceof \OCP\IGroup) {
406
-					$groupsList[] = $groupManager->get($group);
407
-				}
408
-			}
409
-			$appManager->enableAppForGroups($appId, $groupsList);
410
-		} else {
411
-			$appManager->enableApp($appId);
412
-		}
413
-
414
-		$info = self::getAppInfo($appId);
415
-		if(isset($info['settings']) && is_array($info['settings'])) {
416
-			$appPath = self::getAppPath($appId);
417
-			self::registerAutoloading($appId, $appPath);
418
-			\OC::$server->getSettingsManager()->setupSettings($info['settings']);
419
-		}
420
-	}
421
-
422
-	/**
423
-	 * @param string $app
424
-	 * @return bool
425
-	 */
426
-	public static function removeApp($app) {
427
-		if (self::isShipped($app)) {
428
-			return false;
429
-		}
430
-
431
-		$installer = new Installer(
432
-			\OC::$server->getAppFetcher(),
433
-			\OC::$server->getHTTPClientService(),
434
-			\OC::$server->getTempManager(),
435
-			\OC::$server->getLogger()
436
-		);
437
-		return $installer->removeApp($app);
438
-	}
439
-
440
-	/**
441
-	 * This function set an app as disabled in appconfig.
442
-	 *
443
-	 * @param string $app app
444
-	 * @throws Exception
445
-	 */
446
-	public static function disable($app) {
447
-		// flush
448
-		self::$enabledAppsCache = array();
449
-
450
-		// run uninstall steps
451
-		$appData = OC_App::getAppInfo($app);
452
-		if (!is_null($appData)) {
453
-			OC_App::executeRepairSteps($app, $appData['repair-steps']['uninstall']);
454
-		}
455
-
456
-		// emit disable hook - needed anymore ?
457
-		\OC_Hook::emit('OC_App', 'pre_disable', array('app' => $app));
458
-
459
-		// finally disable it
460
-		$appManager = \OC::$server->getAppManager();
461
-		$appManager->disableApp($app);
462
-	}
463
-
464
-	/**
465
-	 * Returns the Settings Navigation
466
-	 *
467
-	 * @return string[]
468
-	 *
469
-	 * This function returns an array containing all settings pages added. The
470
-	 * entries are sorted by the key 'order' ascending.
471
-	 */
472
-	public static function getSettingsNavigation() {
473
-		$l = \OC::$server->getL10N('lib');
474
-		$urlGenerator = \OC::$server->getURLGenerator();
475
-
476
-		$settings = array();
477
-		// by default, settings only contain the help menu
478
-		if (\OC::$server->getSystemConfig()->getValue('knowledgebaseenabled', true)) {
479
-			$settings = array(
480
-				array(
481
-					"id" => "help",
482
-					"order" => 4,
483
-					"href" => $urlGenerator->linkToRoute('settings_help'),
484
-					"name" => $l->t("Help"),
485
-					"icon" => $urlGenerator->imagePath("settings", "help.svg")
486
-				)
487
-			);
488
-		}
489
-
490
-		// if the user is logged-in
491
-		if (\OC::$server->getUserSession()->isLoggedIn()) {
492
-			// personal menu
493
-			$settings[] = array(
494
-				"id" => "personal",
495
-				"order" => 1,
496
-				"href" => $urlGenerator->linkToRoute('settings_personal'),
497
-				"name" => $l->t("Personal"),
498
-				"icon" => $urlGenerator->imagePath("settings", "personal.svg")
499
-			);
500
-
501
-			//SubAdmins are also allowed to access user management
502
-			$userObject = \OC::$server->getUserSession()->getUser();
503
-			$isSubAdmin = false;
504
-			if($userObject !== null) {
505
-				$isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
506
-			}
507
-			if ($isSubAdmin) {
508
-				// admin users menu
509
-				$settings[] = array(
510
-					"id" => "core_users",
511
-					"order" => 3,
512
-					"href" => $urlGenerator->linkToRoute('settings_users'),
513
-					"name" => $l->t("Users"),
514
-					"icon" => $urlGenerator->imagePath("settings", "users.svg")
515
-				);
516
-			}
517
-
518
-			// if the user is an admin
519
-			if (OC_User::isAdminUser(OC_User::getUser())) {
520
-				// admin settings
521
-				$settings[] = array(
522
-					"id" => "admin",
523
-					"order" => 2,
524
-					"href" => $urlGenerator->linkToRoute('settings.AdminSettings.index'),
525
-					"name" => $l->t("Admin"),
526
-					"icon" => $urlGenerator->imagePath("settings", "admin.svg")
527
-				);
528
-			}
529
-		}
530
-
531
-		$navigation = self::proceedNavigation($settings);
532
-		return $navigation;
533
-	}
534
-
535
-	// This is private as well. It simply works, so don't ask for more details
536
-	private static function proceedNavigation($list) {
537
-		$activeApp = OC::$server->getNavigationManager()->getActiveEntry();
538
-		foreach ($list as &$navEntry) {
539
-			if ($navEntry['id'] == $activeApp) {
540
-				$navEntry['active'] = true;
541
-			} else {
542
-				$navEntry['active'] = false;
543
-			}
544
-		}
545
-		unset($navEntry);
546
-
547
-		usort($list, function($a, $b) {
548
-			if (isset($a['order']) && isset($b['order'])) {
549
-				return ($a['order'] < $b['order']) ? -1 : 1;
550
-			} else if (isset($a['order']) || isset($b['order'])) {
551
-				return isset($a['order']) ? -1 : 1;
552
-			} else {
553
-				return ($a['name'] < $b['name']) ? -1 : 1;
554
-			}
555
-		});
556
-
557
-		return $list;
558
-	}
559
-
560
-	/**
561
-	 * Get the path where to install apps
562
-	 *
563
-	 * @return string|false
564
-	 */
565
-	public static function getInstallPath() {
566
-		if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) {
567
-			return false;
568
-		}
569
-
570
-		foreach (OC::$APPSROOTS as $dir) {
571
-			if (isset($dir['writable']) && $dir['writable'] === true) {
572
-				return $dir['path'];
573
-			}
574
-		}
575
-
576
-		\OCP\Util::writeLog('core', 'No application directories are marked as writable.', \OCP\Util::ERROR);
577
-		return null;
578
-	}
579
-
580
-
581
-	/**
582
-	 * search for an app in all app-directories
583
-	 *
584
-	 * @param string $appId
585
-	 * @return false|string
586
-	 */
587
-	public static function findAppInDirectories($appId) {
588
-		$sanitizedAppId = self::cleanAppId($appId);
589
-		if($sanitizedAppId !== $appId) {
590
-			return false;
591
-		}
592
-		static $app_dir = array();
593
-
594
-		if (isset($app_dir[$appId])) {
595
-			return $app_dir[$appId];
596
-		}
597
-
598
-		$possibleApps = array();
599
-		foreach (OC::$APPSROOTS as $dir) {
600
-			if (file_exists($dir['path'] . '/' . $appId)) {
601
-				$possibleApps[] = $dir;
602
-			}
603
-		}
604
-
605
-		if (empty($possibleApps)) {
606
-			return false;
607
-		} elseif (count($possibleApps) === 1) {
608
-			$dir = array_shift($possibleApps);
609
-			$app_dir[$appId] = $dir;
610
-			return $dir;
611
-		} else {
612
-			$versionToLoad = array();
613
-			foreach ($possibleApps as $possibleApp) {
614
-				$version = self::getAppVersionByPath($possibleApp['path']);
615
-				if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
616
-					$versionToLoad = array(
617
-						'dir' => $possibleApp,
618
-						'version' => $version,
619
-					);
620
-				}
621
-			}
622
-			$app_dir[$appId] = $versionToLoad['dir'];
623
-			return $versionToLoad['dir'];
624
-			//TODO - write test
625
-		}
626
-	}
627
-
628
-	/**
629
-	 * Get the directory for the given app.
630
-	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
631
-	 *
632
-	 * @param string $appId
633
-	 * @return string|false
634
-	 */
635
-	public static function getAppPath($appId) {
636
-		if ($appId === null || trim($appId) === '') {
637
-			return false;
638
-		}
639
-
640
-		if (($dir = self::findAppInDirectories($appId)) != false) {
641
-			return $dir['path'] . '/' . $appId;
642
-		}
643
-		return false;
644
-	}
645
-
646
-	/**
647
-	 * Get the path for the given app on the access
648
-	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
649
-	 *
650
-	 * @param string $appId
651
-	 * @return string|false
652
-	 */
653
-	public static function getAppWebPath($appId) {
654
-		if (($dir = self::findAppInDirectories($appId)) != false) {
655
-			return OC::$WEBROOT . $dir['url'] . '/' . $appId;
656
-		}
657
-		return false;
658
-	}
659
-
660
-	/**
661
-	 * get the last version of the app from appinfo/info.xml
662
-	 *
663
-	 * @param string $appId
664
-	 * @param bool $useCache
665
-	 * @return string
666
-	 */
667
-	public static function getAppVersion($appId, $useCache = true) {
668
-		if($useCache && isset(self::$appVersion[$appId])) {
669
-			return self::$appVersion[$appId];
670
-		}
671
-
672
-		$file = self::getAppPath($appId);
673
-		self::$appVersion[$appId] = ($file !== false) ? self::getAppVersionByPath($file) : '0';
674
-		return self::$appVersion[$appId];
675
-	}
676
-
677
-	/**
678
-	 * get app's version based on it's path
679
-	 *
680
-	 * @param string $path
681
-	 * @return string
682
-	 */
683
-	public static function getAppVersionByPath($path) {
684
-		$infoFile = $path . '/appinfo/info.xml';
685
-		$appData = self::getAppInfo($infoFile, true);
686
-		return isset($appData['version']) ? $appData['version'] : '';
687
-	}
688
-
689
-
690
-	/**
691
-	 * Read all app metadata from the info.xml file
692
-	 *
693
-	 * @param string $appId id of the app or the path of the info.xml file
694
-	 * @param bool $path
695
-	 * @param string $lang
696
-	 * @return array|null
697
-	 * @note all data is read from info.xml, not just pre-defined fields
698
-	 */
699
-	public static function getAppInfo($appId, $path = false, $lang = null) {
700
-		if ($path) {
701
-			$file = $appId;
702
-		} else {
703
-			if ($lang === null && isset(self::$appInfo[$appId])) {
704
-				return self::$appInfo[$appId];
705
-			}
706
-			$appPath = self::getAppPath($appId);
707
-			if($appPath === false) {
708
-				return null;
709
-			}
710
-			$file = $appPath . '/appinfo/info.xml';
711
-		}
712
-
713
-		$parser = new InfoParser(\OC::$server->getMemCacheFactory()->create('core.appinfo'));
714
-		$data = $parser->parse($file);
715
-
716
-		if (is_array($data)) {
717
-			$data = OC_App::parseAppInfo($data, $lang);
718
-		}
719
-		if(isset($data['ocsid'])) {
720
-			$storedId = \OC::$server->getConfig()->getAppValue($appId, 'ocsid');
721
-			if($storedId !== '' && $storedId !== $data['ocsid']) {
722
-				$data['ocsid'] = $storedId;
723
-			}
724
-		}
725
-
726
-		if ($lang === null) {
727
-			self::$appInfo[$appId] = $data;
728
-		}
729
-
730
-		return $data;
731
-	}
732
-
733
-	/**
734
-	 * Returns the navigation
735
-	 *
736
-	 * @return array
737
-	 *
738
-	 * This function returns an array containing all entries added. The
739
-	 * entries are sorted by the key 'order' ascending. Additional to the keys
740
-	 * given for each app the following keys exist:
741
-	 *   - active: boolean, signals if the user is on this navigation entry
742
-	 */
743
-	public static function getNavigation() {
744
-		$entries = OC::$server->getNavigationManager()->getAll();
745
-		$navigation = self::proceedNavigation($entries);
746
-		return $navigation;
747
-	}
748
-
749
-	/**
750
-	 * get the id of loaded app
751
-	 *
752
-	 * @return string
753
-	 */
754
-	public static function getCurrentApp() {
755
-		$request = \OC::$server->getRequest();
756
-		$script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
757
-		$topFolder = substr($script, 0, strpos($script, '/'));
758
-		if (empty($topFolder)) {
759
-			$path_info = $request->getPathInfo();
760
-			if ($path_info) {
761
-				$topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
762
-			}
763
-		}
764
-		if ($topFolder == 'apps') {
765
-			$length = strlen($topFolder);
766
-			return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1);
767
-		} else {
768
-			return $topFolder;
769
-		}
770
-	}
771
-
772
-	/**
773
-	 * @param string $type
774
-	 * @return array
775
-	 */
776
-	public static function getForms($type) {
777
-		$forms = array();
778
-		switch ($type) {
779
-			case 'admin':
780
-				$source = self::$adminForms;
781
-				break;
782
-			case 'personal':
783
-				$source = self::$personalForms;
784
-				break;
785
-			default:
786
-				return array();
787
-		}
788
-		foreach ($source as $form) {
789
-			$forms[] = include $form;
790
-		}
791
-		return $forms;
792
-	}
793
-
794
-	/**
795
-	 * register an admin form to be shown
796
-	 *
797
-	 * @param string $app
798
-	 * @param string $page
799
-	 */
800
-	public static function registerAdmin($app, $page) {
801
-		self::$adminForms[] = $app . '/' . $page . '.php';
802
-	}
803
-
804
-	/**
805
-	 * register a personal form to be shown
806
-	 * @param string $app
807
-	 * @param string $page
808
-	 */
809
-	public static function registerPersonal($app, $page) {
810
-		self::$personalForms[] = $app . '/' . $page . '.php';
811
-	}
812
-
813
-	/**
814
-	 * @param array $entry
815
-	 */
816
-	public static function registerLogIn(array $entry) {
817
-		self::$altLogin[] = $entry;
818
-	}
819
-
820
-	/**
821
-	 * @return array
822
-	 */
823
-	public static function getAlternativeLogIns() {
824
-		return self::$altLogin;
825
-	}
826
-
827
-	/**
828
-	 * get a list of all apps in the apps folder
829
-	 *
830
-	 * @return array an array of app names (string IDs)
831
-	 * @todo: change the name of this method to getInstalledApps, which is more accurate
832
-	 */
833
-	public static function getAllApps() {
834
-
835
-		$apps = array();
836
-
837
-		foreach (OC::$APPSROOTS as $apps_dir) {
838
-			if (!is_readable($apps_dir['path'])) {
839
-				\OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN);
840
-				continue;
841
-			}
842
-			$dh = opendir($apps_dir['path']);
843
-
844
-			if (is_resource($dh)) {
845
-				while (($file = readdir($dh)) !== false) {
846
-
847
-					if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
848
-
849
-						$apps[] = $file;
850
-					}
851
-				}
852
-			}
853
-		}
854
-
855
-		return $apps;
856
-	}
857
-
858
-	/**
859
-	 * List all apps, this is used in apps.php
860
-	 *
861
-	 * @return array
862
-	 */
863
-	public function listAllApps() {
864
-		$installedApps = OC_App::getAllApps();
865
-
866
-		//we don't want to show configuration for these
867
-		$blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps();
868
-		$appList = array();
869
-		$langCode = \OC::$server->getL10N('core')->getLanguageCode();
870
-		$urlGenerator = \OC::$server->getURLGenerator();
871
-
872
-		foreach ($installedApps as $app) {
873
-			if (array_search($app, $blacklist) === false) {
874
-
875
-				$info = OC_App::getAppInfo($app, false, $langCode);
876
-				if (!is_array($info)) {
877
-					\OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR);
878
-					continue;
879
-				}
880
-
881
-				if (!isset($info['name'])) {
882
-					\OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR);
883
-					continue;
884
-				}
885
-
886
-				$enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'no');
887
-				$info['groups'] = null;
888
-				if ($enabled === 'yes') {
889
-					$active = true;
890
-				} else if ($enabled === 'no') {
891
-					$active = false;
892
-				} else {
893
-					$active = true;
894
-					$info['groups'] = $enabled;
895
-				}
896
-
897
-				$info['active'] = $active;
898
-
899
-				if (self::isShipped($app)) {
900
-					$info['internal'] = true;
901
-					$info['level'] = self::officialApp;
902
-					$info['removable'] = false;
903
-				} else {
904
-					$info['internal'] = false;
905
-					$info['removable'] = true;
906
-				}
907
-
908
-				$appPath = self::getAppPath($app);
909
-				if($appPath !== false) {
910
-					$appIcon = $appPath . '/img/' . $app . '.svg';
911
-					if (file_exists($appIcon)) {
912
-						$info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, $app . '.svg');
913
-						$info['previewAsIcon'] = true;
914
-					} else {
915
-						$appIcon = $appPath . '/img/app.svg';
916
-						if (file_exists($appIcon)) {
917
-							$info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, 'app.svg');
918
-							$info['previewAsIcon'] = true;
919
-						}
920
-					}
921
-				}
922
-				// fix documentation
923
-				if (isset($info['documentation']) && is_array($info['documentation'])) {
924
-					foreach ($info['documentation'] as $key => $url) {
925
-						// If it is not an absolute URL we assume it is a key
926
-						// i.e. admin-ldap will get converted to go.php?to=admin-ldap
927
-						if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
928
-							$url = $urlGenerator->linkToDocs($url);
929
-						}
930
-
931
-						$info['documentation'][$key] = $url;
932
-					}
933
-				}
934
-
935
-				$info['version'] = OC_App::getAppVersion($app);
936
-				$appList[] = $info;
937
-			}
938
-		}
939
-
940
-		return $appList;
941
-	}
942
-
943
-	/**
944
-	 * Returns the internal app ID or false
945
-	 * @param string $ocsID
946
-	 * @return string|false
947
-	 */
948
-	public static function getInternalAppIdByOcs($ocsID) {
949
-		if(is_numeric($ocsID)) {
950
-			$idArray = \OC::$server->getAppConfig()->getValues(false, 'ocsid');
951
-			if(array_search($ocsID, $idArray)) {
952
-				return array_search($ocsID, $idArray);
953
-			}
954
-		}
955
-		return false;
956
-	}
957
-
958
-	public static function shouldUpgrade($app) {
959
-		$versions = self::getAppVersions();
960
-		$currentVersion = OC_App::getAppVersion($app);
961
-		if ($currentVersion && isset($versions[$app])) {
962
-			$installedVersion = $versions[$app];
963
-			if (!version_compare($currentVersion, $installedVersion, '=')) {
964
-				return true;
965
-			}
966
-		}
967
-		return false;
968
-	}
969
-
970
-	/**
971
-	 * Adjust the number of version parts of $version1 to match
972
-	 * the number of version parts of $version2.
973
-	 *
974
-	 * @param string $version1 version to adjust
975
-	 * @param string $version2 version to take the number of parts from
976
-	 * @return string shortened $version1
977
-	 */
978
-	private static function adjustVersionParts($version1, $version2) {
979
-		$version1 = explode('.', $version1);
980
-		$version2 = explode('.', $version2);
981
-		// reduce $version1 to match the number of parts in $version2
982
-		while (count($version1) > count($version2)) {
983
-			array_pop($version1);
984
-		}
985
-		// if $version1 does not have enough parts, add some
986
-		while (count($version1) < count($version2)) {
987
-			$version1[] = '0';
988
-		}
989
-		return implode('.', $version1);
990
-	}
991
-
992
-	/**
993
-	 * Check whether the current ownCloud version matches the given
994
-	 * application's version requirements.
995
-	 *
996
-	 * The comparison is made based on the number of parts that the
997
-	 * app info version has. For example for ownCloud 6.0.3 if the
998
-	 * app info version is expecting version 6.0, the comparison is
999
-	 * made on the first two parts of the ownCloud version.
1000
-	 * This means that it's possible to specify "requiremin" => 6
1001
-	 * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
1002
-	 *
1003
-	 * @param string $ocVersion ownCloud version to check against
1004
-	 * @param array $appInfo app info (from xml)
1005
-	 *
1006
-	 * @return boolean true if compatible, otherwise false
1007
-	 */
1008
-	public static function isAppCompatible($ocVersion, $appInfo) {
1009
-		$requireMin = '';
1010
-		$requireMax = '';
1011
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
1012
-			$requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
1013
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
1014
-			$requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
1015
-		} else if (isset($appInfo['requiremin'])) {
1016
-			$requireMin = $appInfo['requiremin'];
1017
-		} else if (isset($appInfo['require'])) {
1018
-			$requireMin = $appInfo['require'];
1019
-		}
1020
-
1021
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
1022
-			$requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
1023
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
1024
-			$requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
1025
-		} else if (isset($appInfo['requiremax'])) {
1026
-			$requireMax = $appInfo['requiremax'];
1027
-		}
1028
-
1029
-		if (is_array($ocVersion)) {
1030
-			$ocVersion = implode('.', $ocVersion);
1031
-		}
1032
-
1033
-		if (!empty($requireMin)
1034
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
1035
-		) {
1036
-
1037
-			return false;
1038
-		}
1039
-
1040
-		if (!empty($requireMax)
1041
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
1042
-		) {
1043
-			return false;
1044
-		}
1045
-
1046
-		return true;
1047
-	}
1048
-
1049
-	/**
1050
-	 * get the installed version of all apps
1051
-	 */
1052
-	public static function getAppVersions() {
1053
-		static $versions;
1054
-
1055
-		if(!$versions) {
1056
-			$appConfig = \OC::$server->getAppConfig();
1057
-			$versions = $appConfig->getValues(false, 'installed_version');
1058
-		}
1059
-		return $versions;
1060
-	}
1061
-
1062
-	/**
1063
-	 * @param string $app
1064
-	 * @param \OCP\IConfig $config
1065
-	 * @param \OCP\IL10N $l
1066
-	 * @return bool
1067
-	 *
1068
-	 * @throws Exception if app is not compatible with this version of ownCloud
1069
-	 * @throws Exception if no app-name was specified
1070
-	 */
1071
-	public function installApp($app,
1072
-							   \OCP\IConfig $config,
1073
-							   \OCP\IL10N $l) {
1074
-		if ($app !== false) {
1075
-			// check if the app is compatible with this version of ownCloud
1076
-			$info = self::getAppInfo($app);
1077
-			if(!is_array($info)) {
1078
-				throw new \Exception(
1079
-					$l->t('App "%s" cannot be installed because appinfo file cannot be read.',
1080
-						[$info['name']]
1081
-					)
1082
-				);
1083
-			}
1084
-
1085
-			$version = \OCP\Util::getVersion();
1086
-			if (!self::isAppCompatible($version, $info)) {
1087
-				throw new \Exception(
1088
-					$l->t('App "%s" cannot be installed because it is not compatible with this version of the server.',
1089
-						array($info['name'])
1090
-					)
1091
-				);
1092
-			}
1093
-
1094
-			// check for required dependencies
1095
-			self::checkAppDependencies($config, $l, $info);
1096
-
1097
-			$config->setAppValue($app, 'enabled', 'yes');
1098
-			if (isset($appData['id'])) {
1099
-				$config->setAppValue($app, 'ocsid', $appData['id']);
1100
-			}
1101
-
1102
-			if(isset($info['settings']) && is_array($info['settings'])) {
1103
-				$appPath = self::getAppPath($app);
1104
-				self::registerAutoloading($app, $appPath);
1105
-				\OC::$server->getSettingsManager()->setupSettings($info['settings']);
1106
-			}
1107
-
1108
-			\OC_Hook::emit('OC_App', 'post_enable', array('app' => $app));
1109
-		} else {
1110
-			if(empty($appName) ) {
1111
-				throw new \Exception($l->t("No app name specified"));
1112
-			} else {
1113
-				throw new \Exception($l->t("App '%s' could not be installed!", $appName));
1114
-			}
1115
-		}
1116
-
1117
-		return $app;
1118
-	}
1119
-
1120
-	/**
1121
-	 * update the database for the app and call the update script
1122
-	 *
1123
-	 * @param string $appId
1124
-	 * @return bool
1125
-	 */
1126
-	public static function updateApp($appId) {
1127
-		$appPath = self::getAppPath($appId);
1128
-		if($appPath === false) {
1129
-			return false;
1130
-		}
1131
-		$appData = self::getAppInfo($appId);
1132
-		self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
1133
-		if (file_exists($appPath . '/appinfo/database.xml')) {
1134
-			OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
1135
-		}
1136
-		self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
1137
-		self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
1138
-		unset(self::$appVersion[$appId]);
1139
-		// run upgrade code
1140
-		if (file_exists($appPath . '/appinfo/update.php')) {
1141
-			self::loadApp($appId, false);
1142
-			include $appPath . '/appinfo/update.php';
1143
-		}
1144
-		self::setupBackgroundJobs($appData['background-jobs']);
1145
-		if(isset($appData['settings']) && is_array($appData['settings'])) {
1146
-			$appPath = self::getAppPath($appId);
1147
-			self::registerAutoloading($appId, $appPath);
1148
-			\OC::$server->getSettingsManager()->setupSettings($appData['settings']);
1149
-		}
1150
-
1151
-		//set remote/public handlers
1152
-		if (array_key_exists('ocsid', $appData)) {
1153
-			\OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
1154
-		} elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
1155
-			\OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
1156
-		}
1157
-		foreach ($appData['remote'] as $name => $path) {
1158
-			\OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
1159
-		}
1160
-		foreach ($appData['public'] as $name => $path) {
1161
-			\OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
1162
-		}
1163
-
1164
-		self::setAppTypes($appId);
1165
-
1166
-		$version = \OC_App::getAppVersion($appId);
1167
-		\OC::$server->getAppConfig()->setValue($appId, 'installed_version', $version);
1168
-
1169
-		\OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
1170
-			ManagerEvent::EVENT_APP_UPDATE, $appId
1171
-		));
1172
-
1173
-		return true;
1174
-	}
1175
-
1176
-	/**
1177
-	 * @param string $appId
1178
-	 * @param string[] $steps
1179
-	 * @throws \OC\NeedsUpdateException
1180
-	 */
1181
-	public static function executeRepairSteps($appId, array $steps) {
1182
-		if (empty($steps)) {
1183
-			return;
1184
-		}
1185
-		// load the app
1186
-		self::loadApp($appId, false);
1187
-
1188
-		$dispatcher = OC::$server->getEventDispatcher();
1189
-
1190
-		// load the steps
1191
-		$r = new Repair([], $dispatcher);
1192
-		foreach ($steps as $step) {
1193
-			try {
1194
-				$r->addStep($step);
1195
-			} catch (Exception $ex) {
1196
-				$r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
1197
-				\OC::$server->getLogger()->logException($ex);
1198
-			}
1199
-		}
1200
-		// run the steps
1201
-		$r->run();
1202
-	}
1203
-
1204
-	public static function setupBackgroundJobs(array $jobs) {
1205
-		$queue = \OC::$server->getJobList();
1206
-		foreach ($jobs as $job) {
1207
-			$queue->add($job);
1208
-		}
1209
-	}
1210
-
1211
-	/**
1212
-	 * @param string $appId
1213
-	 * @param string[] $steps
1214
-	 */
1215
-	private static function setupLiveMigrations($appId, array $steps) {
1216
-		$queue = \OC::$server->getJobList();
1217
-		foreach ($steps as $step) {
1218
-			$queue->add('OC\Migration\BackgroundRepair', [
1219
-				'app' => $appId,
1220
-				'step' => $step]);
1221
-		}
1222
-	}
1223
-
1224
-	/**
1225
-	 * @param string $appId
1226
-	 * @return \OC\Files\View|false
1227
-	 */
1228
-	public static function getStorage($appId) {
1229
-		if (OC_App::isEnabled($appId)) { //sanity check
1230
-			if (\OC::$server->getUserSession()->isLoggedIn()) {
1231
-				$view = new \OC\Files\View('/' . OC_User::getUser());
1232
-				if (!$view->file_exists($appId)) {
1233
-					$view->mkdir($appId);
1234
-				}
1235
-				return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
1236
-			} else {
1237
-				\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR);
1238
-				return false;
1239
-			}
1240
-		} else {
1241
-			\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR);
1242
-			return false;
1243
-		}
1244
-	}
1245
-
1246
-	protected static function findBestL10NOption($options, $lang) {
1247
-		$fallback = $similarLangFallback = $englishFallback = false;
1248
-
1249
-		$lang = strtolower($lang);
1250
-		$similarLang = $lang;
1251
-		if (strpos($similarLang, '_')) {
1252
-			// For "de_DE" we want to find "de" and the other way around
1253
-			$similarLang = substr($lang, 0, strpos($lang, '_'));
1254
-		}
1255
-
1256
-		foreach ($options as $option) {
1257
-			if (is_array($option)) {
1258
-				if ($fallback === false) {
1259
-					$fallback = $option['@value'];
1260
-				}
1261
-
1262
-				if (!isset($option['@attributes']['lang'])) {
1263
-					continue;
1264
-				}
1265
-
1266
-				$attributeLang = strtolower($option['@attributes']['lang']);
1267
-				if ($attributeLang === $lang) {
1268
-					return $option['@value'];
1269
-				}
1270
-
1271
-				if ($attributeLang === $similarLang) {
1272
-					$similarLangFallback = $option['@value'];
1273
-				} else if (strpos($attributeLang, $similarLang . '_') === 0) {
1274
-					if ($similarLangFallback === false) {
1275
-						$similarLangFallback =  $option['@value'];
1276
-					}
1277
-				}
1278
-			} else {
1279
-				$englishFallback = $option;
1280
-			}
1281
-		}
1282
-
1283
-		if ($similarLangFallback !== false) {
1284
-			return $similarLangFallback;
1285
-		} else if ($englishFallback !== false) {
1286
-			return $englishFallback;
1287
-		}
1288
-		return (string) $fallback;
1289
-	}
1290
-
1291
-	/**
1292
-	 * parses the app data array and enhanced the 'description' value
1293
-	 *
1294
-	 * @param array $data the app data
1295
-	 * @param string $lang
1296
-	 * @return array improved app data
1297
-	 */
1298
-	public static function parseAppInfo(array $data, $lang = null) {
1299
-
1300
-		if ($lang && isset($data['name']) && is_array($data['name'])) {
1301
-			$data['name'] = self::findBestL10NOption($data['name'], $lang);
1302
-		}
1303
-		if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1304
-			$data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1305
-		}
1306
-		if ($lang && isset($data['description']) && is_array($data['description'])) {
1307
-			$data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1308
-		} else if (isset($data['description']) && is_string($data['description'])) {
1309
-			$data['description'] = trim($data['description']);
1310
-		} else  {
1311
-			$data['description'] = '';
1312
-		}
1313
-
1314
-		return $data;
1315
-	}
1316
-
1317
-	/**
1318
-	 * @param \OCP\IConfig $config
1319
-	 * @param \OCP\IL10N $l
1320
-	 * @param array $info
1321
-	 * @throws \Exception
1322
-	 */
1323
-	protected static function checkAppDependencies($config, $l, $info) {
1324
-		$dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1325
-		$missing = $dependencyAnalyzer->analyze($info);
1326
-		if (!empty($missing)) {
1327
-			$missingMsg = join(PHP_EOL, $missing);
1328
-			throw new \Exception(
1329
-				$l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s',
1330
-					[$info['name'], $missingMsg]
1331
-				)
1332
-			);
1333
-		}
1334
-	}
64
+    static private $appVersion = [];
65
+    static private $adminForms = array();
66
+    static private $personalForms = array();
67
+    static private $appInfo = array();
68
+    static private $appTypes = array();
69
+    static private $loadedApps = array();
70
+    static private $altLogin = array();
71
+    static private $alreadyRegistered = [];
72
+    const officialApp = 200;
73
+
74
+    /**
75
+     * clean the appId
76
+     *
77
+     * @param string|boolean $app AppId that needs to be cleaned
78
+     * @return string
79
+     */
80
+    public static function cleanAppId($app) {
81
+        return str_replace(array('\0', '/', '\\', '..'), '', $app);
82
+    }
83
+
84
+    /**
85
+     * Check if an app is loaded
86
+     *
87
+     * @param string $app
88
+     * @return bool
89
+     */
90
+    public static function isAppLoaded($app) {
91
+        return in_array($app, self::$loadedApps, true);
92
+    }
93
+
94
+    /**
95
+     * loads all apps
96
+     *
97
+     * @param string[] | string | null $types
98
+     * @return bool
99
+     *
100
+     * This function walks through the ownCloud directory and loads all apps
101
+     * it can find. A directory contains an app if the file /appinfo/info.xml
102
+     * exists.
103
+     *
104
+     * if $types is set, only apps of those types will be loaded
105
+     */
106
+    public static function loadApps($types = null) {
107
+        if (\OC::$server->getSystemConfig()->getValue('maintenance', false)) {
108
+            return false;
109
+        }
110
+        // Load the enabled apps here
111
+        $apps = self::getEnabledApps();
112
+
113
+        // Add each apps' folder as allowed class path
114
+        foreach($apps as $app) {
115
+            $path = self::getAppPath($app);
116
+            if($path !== false) {
117
+                self::registerAutoloading($app, $path);
118
+            }
119
+        }
120
+
121
+        // prevent app.php from printing output
122
+        ob_start();
123
+        foreach ($apps as $app) {
124
+            if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
125
+                self::loadApp($app);
126
+            }
127
+        }
128
+        ob_end_clean();
129
+
130
+        return true;
131
+    }
132
+
133
+    /**
134
+     * load a single app
135
+     *
136
+     * @param string $app
137
+     * @param bool $checkUpgrade whether an upgrade check should be done
138
+     * @throws \OC\NeedsUpdateException
139
+     */
140
+    public static function loadApp($app, $checkUpgrade = true) {
141
+        self::$loadedApps[] = $app;
142
+        $appPath = self::getAppPath($app);
143
+        if($appPath === false) {
144
+            return;
145
+        }
146
+
147
+        // in case someone calls loadApp() directly
148
+        self::registerAutoloading($app, $appPath);
149
+
150
+        if (is_file($appPath . '/appinfo/app.php')) {
151
+            \OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
152
+            if ($checkUpgrade and self::shouldUpgrade($app)) {
153
+                throw new \OC\NeedsUpdateException();
154
+            }
155
+            self::requireAppFile($app);
156
+            if (self::isType($app, array('authentication'))) {
157
+                // since authentication apps affect the "is app enabled for group" check,
158
+                // the enabled apps cache needs to be cleared to make sure that the
159
+                // next time getEnableApps() is called it will also include apps that were
160
+                // enabled for groups
161
+                self::$enabledAppsCache = array();
162
+            }
163
+            \OC::$server->getEventLogger()->end('load_app_' . $app);
164
+        }
165
+
166
+        $info = self::getAppInfo($app);
167
+        if (!empty($info['activity']['filters'])) {
168
+            foreach ($info['activity']['filters'] as $filter) {
169
+                \OC::$server->getActivityManager()->registerFilter($filter);
170
+            }
171
+        }
172
+        if (!empty($info['activity']['settings'])) {
173
+            foreach ($info['activity']['settings'] as $setting) {
174
+                \OC::$server->getActivityManager()->registerSetting($setting);
175
+            }
176
+        }
177
+        if (!empty($info['activity']['providers'])) {
178
+            foreach ($info['activity']['providers'] as $provider) {
179
+                \OC::$server->getActivityManager()->registerProvider($provider);
180
+            }
181
+        }
182
+    }
183
+
184
+    /**
185
+     * @internal
186
+     * @param string $app
187
+     * @param string $path
188
+     */
189
+    public static function registerAutoloading($app, $path) {
190
+        $key = $app . '-' . $path;
191
+        if(isset(self::$alreadyRegistered[$key])) {
192
+            return;
193
+        }
194
+        self::$alreadyRegistered[$key] = true;
195
+        // Register on PSR-4 composer autoloader
196
+        $appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
197
+        \OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
198
+        if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
199
+            \OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
200
+        }
201
+
202
+        // Register on legacy autoloader
203
+        \OC::$loader->addValidRoot($path);
204
+    }
205
+
206
+    /**
207
+     * Load app.php from the given app
208
+     *
209
+     * @param string $app app name
210
+     */
211
+    private static function requireAppFile($app) {
212
+        try {
213
+            // encapsulated here to avoid variable scope conflicts
214
+            require_once $app . '/appinfo/app.php';
215
+        } catch (Error $ex) {
216
+            \OC::$server->getLogger()->logException($ex);
217
+            $blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps();
218
+            if (!in_array($app, $blacklist)) {
219
+                self::disable($app);
220
+            }
221
+        }
222
+    }
223
+
224
+    /**
225
+     * check if an app is of a specific type
226
+     *
227
+     * @param string $app
228
+     * @param string|array $types
229
+     * @return bool
230
+     */
231
+    public static function isType($app, $types) {
232
+        if (is_string($types)) {
233
+            $types = array($types);
234
+        }
235
+        $appTypes = self::getAppTypes($app);
236
+        foreach ($types as $type) {
237
+            if (array_search($type, $appTypes) !== false) {
238
+                return true;
239
+            }
240
+        }
241
+        return false;
242
+    }
243
+
244
+    /**
245
+     * get the types of an app
246
+     *
247
+     * @param string $app
248
+     * @return array
249
+     */
250
+    private static function getAppTypes($app) {
251
+        //load the cache
252
+        if (count(self::$appTypes) == 0) {
253
+            self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
254
+        }
255
+
256
+        if (isset(self::$appTypes[$app])) {
257
+            return explode(',', self::$appTypes[$app]);
258
+        } else {
259
+            return array();
260
+        }
261
+    }
262
+
263
+    /**
264
+     * read app types from info.xml and cache them in the database
265
+     */
266
+    public static function setAppTypes($app) {
267
+        $appData = self::getAppInfo($app);
268
+        if(!is_array($appData)) {
269
+            return;
270
+        }
271
+
272
+        if (isset($appData['types'])) {
273
+            $appTypes = implode(',', $appData['types']);
274
+        } else {
275
+            $appTypes = '';
276
+            $appData['types'] = [];
277
+        }
278
+
279
+        \OC::$server->getAppConfig()->setValue($app, 'types', $appTypes);
280
+
281
+        if (\OC::$server->getAppManager()->hasProtectedAppType($appData['types'])) {
282
+            $enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'yes');
283
+            if ($enabled !== 'yes' && $enabled !== 'no') {
284
+                \OC::$server->getAppConfig()->setValue($app, 'enabled', 'yes');
285
+            }
286
+        }
287
+    }
288
+
289
+    /**
290
+     * check if app is shipped
291
+     *
292
+     * @param string $appId the id of the app to check
293
+     * @return bool
294
+     *
295
+     * Check if an app that is installed is a shipped app or installed from the appstore.
296
+     */
297
+    public static function isShipped($appId) {
298
+        return \OC::$server->getAppManager()->isShipped($appId);
299
+    }
300
+
301
+    /**
302
+     * get all enabled apps
303
+     */
304
+    protected static $enabledAppsCache = array();
305
+
306
+    /**
307
+     * Returns apps enabled for the current user.
308
+     *
309
+     * @param bool $forceRefresh whether to refresh the cache
310
+     * @param bool $all whether to return apps for all users, not only the
311
+     * currently logged in one
312
+     * @return string[]
313
+     */
314
+    public static function getEnabledApps($forceRefresh = false, $all = false) {
315
+        if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
316
+            return array();
317
+        }
318
+        // in incognito mode or when logged out, $user will be false,
319
+        // which is also the case during an upgrade
320
+        $appManager = \OC::$server->getAppManager();
321
+        if ($all) {
322
+            $user = null;
323
+        } else {
324
+            $user = \OC::$server->getUserSession()->getUser();
325
+        }
326
+
327
+        if (is_null($user)) {
328
+            $apps = $appManager->getInstalledApps();
329
+        } else {
330
+            $apps = $appManager->getEnabledAppsForUser($user);
331
+        }
332
+        $apps = array_filter($apps, function ($app) {
333
+            return $app !== 'files';//we add this manually
334
+        });
335
+        sort($apps);
336
+        array_unshift($apps, 'files');
337
+        return $apps;
338
+    }
339
+
340
+    /**
341
+     * checks whether or not an app is enabled
342
+     *
343
+     * @param string $app app
344
+     * @return bool
345
+     *
346
+     * This function checks whether or not an app is enabled.
347
+     */
348
+    public static function isEnabled($app) {
349
+        return \OC::$server->getAppManager()->isEnabledForUser($app);
350
+    }
351
+
352
+    /**
353
+     * enables an app
354
+     *
355
+     * @param string $appId
356
+     * @param array $groups (optional) when set, only these groups will have access to the app
357
+     * @throws \Exception
358
+     * @return void
359
+     *
360
+     * This function set an app as enabled in appconfig.
361
+     */
362
+    public function enable($appId,
363
+                            $groups = null) {
364
+        self::$enabledAppsCache = []; // flush
365
+        $l = \OC::$server->getL10N('core');
366
+        $config = \OC::$server->getConfig();
367
+
368
+        // Check if app is already downloaded
369
+        $installer = new Installer(
370
+            \OC::$server->getAppFetcher(),
371
+            \OC::$server->getHTTPClientService(),
372
+            \OC::$server->getTempManager(),
373
+            \OC::$server->getLogger()
374
+        );
375
+        $isDownloaded = $installer->isDownloaded($appId);
376
+
377
+        if(!$isDownloaded) {
378
+            $installer->downloadApp($appId);
379
+        }
380
+
381
+        if (!Installer::isInstalled($appId)) {
382
+            $appId = self::installApp(
383
+                $appId,
384
+                $config,
385
+                $l
386
+            );
387
+            $appPath = self::getAppPath($appId);
388
+            self::registerAutoloading($appId, $appPath);
389
+            $installer->installApp($appId);
390
+        } else {
391
+            // check for required dependencies
392
+            $info = self::getAppInfo($appId);
393
+            self::checkAppDependencies($config, $l, $info);
394
+            $appPath = self::getAppPath($appId);
395
+            self::registerAutoloading($appId, $appPath);
396
+            $installer->installApp($appId);
397
+        }
398
+
399
+        $appManager = \OC::$server->getAppManager();
400
+        if (!is_null($groups)) {
401
+            $groupManager = \OC::$server->getGroupManager();
402
+            $groupsList = [];
403
+            foreach ($groups as $group) {
404
+                $groupItem = $groupManager->get($group);
405
+                if ($groupItem instanceof \OCP\IGroup) {
406
+                    $groupsList[] = $groupManager->get($group);
407
+                }
408
+            }
409
+            $appManager->enableAppForGroups($appId, $groupsList);
410
+        } else {
411
+            $appManager->enableApp($appId);
412
+        }
413
+
414
+        $info = self::getAppInfo($appId);
415
+        if(isset($info['settings']) && is_array($info['settings'])) {
416
+            $appPath = self::getAppPath($appId);
417
+            self::registerAutoloading($appId, $appPath);
418
+            \OC::$server->getSettingsManager()->setupSettings($info['settings']);
419
+        }
420
+    }
421
+
422
+    /**
423
+     * @param string $app
424
+     * @return bool
425
+     */
426
+    public static function removeApp($app) {
427
+        if (self::isShipped($app)) {
428
+            return false;
429
+        }
430
+
431
+        $installer = new Installer(
432
+            \OC::$server->getAppFetcher(),
433
+            \OC::$server->getHTTPClientService(),
434
+            \OC::$server->getTempManager(),
435
+            \OC::$server->getLogger()
436
+        );
437
+        return $installer->removeApp($app);
438
+    }
439
+
440
+    /**
441
+     * This function set an app as disabled in appconfig.
442
+     *
443
+     * @param string $app app
444
+     * @throws Exception
445
+     */
446
+    public static function disable($app) {
447
+        // flush
448
+        self::$enabledAppsCache = array();
449
+
450
+        // run uninstall steps
451
+        $appData = OC_App::getAppInfo($app);
452
+        if (!is_null($appData)) {
453
+            OC_App::executeRepairSteps($app, $appData['repair-steps']['uninstall']);
454
+        }
455
+
456
+        // emit disable hook - needed anymore ?
457
+        \OC_Hook::emit('OC_App', 'pre_disable', array('app' => $app));
458
+
459
+        // finally disable it
460
+        $appManager = \OC::$server->getAppManager();
461
+        $appManager->disableApp($app);
462
+    }
463
+
464
+    /**
465
+     * Returns the Settings Navigation
466
+     *
467
+     * @return string[]
468
+     *
469
+     * This function returns an array containing all settings pages added. The
470
+     * entries are sorted by the key 'order' ascending.
471
+     */
472
+    public static function getSettingsNavigation() {
473
+        $l = \OC::$server->getL10N('lib');
474
+        $urlGenerator = \OC::$server->getURLGenerator();
475
+
476
+        $settings = array();
477
+        // by default, settings only contain the help menu
478
+        if (\OC::$server->getSystemConfig()->getValue('knowledgebaseenabled', true)) {
479
+            $settings = array(
480
+                array(
481
+                    "id" => "help",
482
+                    "order" => 4,
483
+                    "href" => $urlGenerator->linkToRoute('settings_help'),
484
+                    "name" => $l->t("Help"),
485
+                    "icon" => $urlGenerator->imagePath("settings", "help.svg")
486
+                )
487
+            );
488
+        }
489
+
490
+        // if the user is logged-in
491
+        if (\OC::$server->getUserSession()->isLoggedIn()) {
492
+            // personal menu
493
+            $settings[] = array(
494
+                "id" => "personal",
495
+                "order" => 1,
496
+                "href" => $urlGenerator->linkToRoute('settings_personal'),
497
+                "name" => $l->t("Personal"),
498
+                "icon" => $urlGenerator->imagePath("settings", "personal.svg")
499
+            );
500
+
501
+            //SubAdmins are also allowed to access user management
502
+            $userObject = \OC::$server->getUserSession()->getUser();
503
+            $isSubAdmin = false;
504
+            if($userObject !== null) {
505
+                $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
506
+            }
507
+            if ($isSubAdmin) {
508
+                // admin users menu
509
+                $settings[] = array(
510
+                    "id" => "core_users",
511
+                    "order" => 3,
512
+                    "href" => $urlGenerator->linkToRoute('settings_users'),
513
+                    "name" => $l->t("Users"),
514
+                    "icon" => $urlGenerator->imagePath("settings", "users.svg")
515
+                );
516
+            }
517
+
518
+            // if the user is an admin
519
+            if (OC_User::isAdminUser(OC_User::getUser())) {
520
+                // admin settings
521
+                $settings[] = array(
522
+                    "id" => "admin",
523
+                    "order" => 2,
524
+                    "href" => $urlGenerator->linkToRoute('settings.AdminSettings.index'),
525
+                    "name" => $l->t("Admin"),
526
+                    "icon" => $urlGenerator->imagePath("settings", "admin.svg")
527
+                );
528
+            }
529
+        }
530
+
531
+        $navigation = self::proceedNavigation($settings);
532
+        return $navigation;
533
+    }
534
+
535
+    // This is private as well. It simply works, so don't ask for more details
536
+    private static function proceedNavigation($list) {
537
+        $activeApp = OC::$server->getNavigationManager()->getActiveEntry();
538
+        foreach ($list as &$navEntry) {
539
+            if ($navEntry['id'] == $activeApp) {
540
+                $navEntry['active'] = true;
541
+            } else {
542
+                $navEntry['active'] = false;
543
+            }
544
+        }
545
+        unset($navEntry);
546
+
547
+        usort($list, function($a, $b) {
548
+            if (isset($a['order']) && isset($b['order'])) {
549
+                return ($a['order'] < $b['order']) ? -1 : 1;
550
+            } else if (isset($a['order']) || isset($b['order'])) {
551
+                return isset($a['order']) ? -1 : 1;
552
+            } else {
553
+                return ($a['name'] < $b['name']) ? -1 : 1;
554
+            }
555
+        });
556
+
557
+        return $list;
558
+    }
559
+
560
+    /**
561
+     * Get the path where to install apps
562
+     *
563
+     * @return string|false
564
+     */
565
+    public static function getInstallPath() {
566
+        if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) {
567
+            return false;
568
+        }
569
+
570
+        foreach (OC::$APPSROOTS as $dir) {
571
+            if (isset($dir['writable']) && $dir['writable'] === true) {
572
+                return $dir['path'];
573
+            }
574
+        }
575
+
576
+        \OCP\Util::writeLog('core', 'No application directories are marked as writable.', \OCP\Util::ERROR);
577
+        return null;
578
+    }
579
+
580
+
581
+    /**
582
+     * search for an app in all app-directories
583
+     *
584
+     * @param string $appId
585
+     * @return false|string
586
+     */
587
+    public static function findAppInDirectories($appId) {
588
+        $sanitizedAppId = self::cleanAppId($appId);
589
+        if($sanitizedAppId !== $appId) {
590
+            return false;
591
+        }
592
+        static $app_dir = array();
593
+
594
+        if (isset($app_dir[$appId])) {
595
+            return $app_dir[$appId];
596
+        }
597
+
598
+        $possibleApps = array();
599
+        foreach (OC::$APPSROOTS as $dir) {
600
+            if (file_exists($dir['path'] . '/' . $appId)) {
601
+                $possibleApps[] = $dir;
602
+            }
603
+        }
604
+
605
+        if (empty($possibleApps)) {
606
+            return false;
607
+        } elseif (count($possibleApps) === 1) {
608
+            $dir = array_shift($possibleApps);
609
+            $app_dir[$appId] = $dir;
610
+            return $dir;
611
+        } else {
612
+            $versionToLoad = array();
613
+            foreach ($possibleApps as $possibleApp) {
614
+                $version = self::getAppVersionByPath($possibleApp['path']);
615
+                if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
616
+                    $versionToLoad = array(
617
+                        'dir' => $possibleApp,
618
+                        'version' => $version,
619
+                    );
620
+                }
621
+            }
622
+            $app_dir[$appId] = $versionToLoad['dir'];
623
+            return $versionToLoad['dir'];
624
+            //TODO - write test
625
+        }
626
+    }
627
+
628
+    /**
629
+     * Get the directory for the given app.
630
+     * If the app is defined in multiple directories, the first one is taken. (false if not found)
631
+     *
632
+     * @param string $appId
633
+     * @return string|false
634
+     */
635
+    public static function getAppPath($appId) {
636
+        if ($appId === null || trim($appId) === '') {
637
+            return false;
638
+        }
639
+
640
+        if (($dir = self::findAppInDirectories($appId)) != false) {
641
+            return $dir['path'] . '/' . $appId;
642
+        }
643
+        return false;
644
+    }
645
+
646
+    /**
647
+     * Get the path for the given app on the access
648
+     * If the app is defined in multiple directories, the first one is taken. (false if not found)
649
+     *
650
+     * @param string $appId
651
+     * @return string|false
652
+     */
653
+    public static function getAppWebPath($appId) {
654
+        if (($dir = self::findAppInDirectories($appId)) != false) {
655
+            return OC::$WEBROOT . $dir['url'] . '/' . $appId;
656
+        }
657
+        return false;
658
+    }
659
+
660
+    /**
661
+     * get the last version of the app from appinfo/info.xml
662
+     *
663
+     * @param string $appId
664
+     * @param bool $useCache
665
+     * @return string
666
+     */
667
+    public static function getAppVersion($appId, $useCache = true) {
668
+        if($useCache && isset(self::$appVersion[$appId])) {
669
+            return self::$appVersion[$appId];
670
+        }
671
+
672
+        $file = self::getAppPath($appId);
673
+        self::$appVersion[$appId] = ($file !== false) ? self::getAppVersionByPath($file) : '0';
674
+        return self::$appVersion[$appId];
675
+    }
676
+
677
+    /**
678
+     * get app's version based on it's path
679
+     *
680
+     * @param string $path
681
+     * @return string
682
+     */
683
+    public static function getAppVersionByPath($path) {
684
+        $infoFile = $path . '/appinfo/info.xml';
685
+        $appData = self::getAppInfo($infoFile, true);
686
+        return isset($appData['version']) ? $appData['version'] : '';
687
+    }
688
+
689
+
690
+    /**
691
+     * Read all app metadata from the info.xml file
692
+     *
693
+     * @param string $appId id of the app or the path of the info.xml file
694
+     * @param bool $path
695
+     * @param string $lang
696
+     * @return array|null
697
+     * @note all data is read from info.xml, not just pre-defined fields
698
+     */
699
+    public static function getAppInfo($appId, $path = false, $lang = null) {
700
+        if ($path) {
701
+            $file = $appId;
702
+        } else {
703
+            if ($lang === null && isset(self::$appInfo[$appId])) {
704
+                return self::$appInfo[$appId];
705
+            }
706
+            $appPath = self::getAppPath($appId);
707
+            if($appPath === false) {
708
+                return null;
709
+            }
710
+            $file = $appPath . '/appinfo/info.xml';
711
+        }
712
+
713
+        $parser = new InfoParser(\OC::$server->getMemCacheFactory()->create('core.appinfo'));
714
+        $data = $parser->parse($file);
715
+
716
+        if (is_array($data)) {
717
+            $data = OC_App::parseAppInfo($data, $lang);
718
+        }
719
+        if(isset($data['ocsid'])) {
720
+            $storedId = \OC::$server->getConfig()->getAppValue($appId, 'ocsid');
721
+            if($storedId !== '' && $storedId !== $data['ocsid']) {
722
+                $data['ocsid'] = $storedId;
723
+            }
724
+        }
725
+
726
+        if ($lang === null) {
727
+            self::$appInfo[$appId] = $data;
728
+        }
729
+
730
+        return $data;
731
+    }
732
+
733
+    /**
734
+     * Returns the navigation
735
+     *
736
+     * @return array
737
+     *
738
+     * This function returns an array containing all entries added. The
739
+     * entries are sorted by the key 'order' ascending. Additional to the keys
740
+     * given for each app the following keys exist:
741
+     *   - active: boolean, signals if the user is on this navigation entry
742
+     */
743
+    public static function getNavigation() {
744
+        $entries = OC::$server->getNavigationManager()->getAll();
745
+        $navigation = self::proceedNavigation($entries);
746
+        return $navigation;
747
+    }
748
+
749
+    /**
750
+     * get the id of loaded app
751
+     *
752
+     * @return string
753
+     */
754
+    public static function getCurrentApp() {
755
+        $request = \OC::$server->getRequest();
756
+        $script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
757
+        $topFolder = substr($script, 0, strpos($script, '/'));
758
+        if (empty($topFolder)) {
759
+            $path_info = $request->getPathInfo();
760
+            if ($path_info) {
761
+                $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
762
+            }
763
+        }
764
+        if ($topFolder == 'apps') {
765
+            $length = strlen($topFolder);
766
+            return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1);
767
+        } else {
768
+            return $topFolder;
769
+        }
770
+    }
771
+
772
+    /**
773
+     * @param string $type
774
+     * @return array
775
+     */
776
+    public static function getForms($type) {
777
+        $forms = array();
778
+        switch ($type) {
779
+            case 'admin':
780
+                $source = self::$adminForms;
781
+                break;
782
+            case 'personal':
783
+                $source = self::$personalForms;
784
+                break;
785
+            default:
786
+                return array();
787
+        }
788
+        foreach ($source as $form) {
789
+            $forms[] = include $form;
790
+        }
791
+        return $forms;
792
+    }
793
+
794
+    /**
795
+     * register an admin form to be shown
796
+     *
797
+     * @param string $app
798
+     * @param string $page
799
+     */
800
+    public static function registerAdmin($app, $page) {
801
+        self::$adminForms[] = $app . '/' . $page . '.php';
802
+    }
803
+
804
+    /**
805
+     * register a personal form to be shown
806
+     * @param string $app
807
+     * @param string $page
808
+     */
809
+    public static function registerPersonal($app, $page) {
810
+        self::$personalForms[] = $app . '/' . $page . '.php';
811
+    }
812
+
813
+    /**
814
+     * @param array $entry
815
+     */
816
+    public static function registerLogIn(array $entry) {
817
+        self::$altLogin[] = $entry;
818
+    }
819
+
820
+    /**
821
+     * @return array
822
+     */
823
+    public static function getAlternativeLogIns() {
824
+        return self::$altLogin;
825
+    }
826
+
827
+    /**
828
+     * get a list of all apps in the apps folder
829
+     *
830
+     * @return array an array of app names (string IDs)
831
+     * @todo: change the name of this method to getInstalledApps, which is more accurate
832
+     */
833
+    public static function getAllApps() {
834
+
835
+        $apps = array();
836
+
837
+        foreach (OC::$APPSROOTS as $apps_dir) {
838
+            if (!is_readable($apps_dir['path'])) {
839
+                \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN);
840
+                continue;
841
+            }
842
+            $dh = opendir($apps_dir['path']);
843
+
844
+            if (is_resource($dh)) {
845
+                while (($file = readdir($dh)) !== false) {
846
+
847
+                    if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
848
+
849
+                        $apps[] = $file;
850
+                    }
851
+                }
852
+            }
853
+        }
854
+
855
+        return $apps;
856
+    }
857
+
858
+    /**
859
+     * List all apps, this is used in apps.php
860
+     *
861
+     * @return array
862
+     */
863
+    public function listAllApps() {
864
+        $installedApps = OC_App::getAllApps();
865
+
866
+        //we don't want to show configuration for these
867
+        $blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps();
868
+        $appList = array();
869
+        $langCode = \OC::$server->getL10N('core')->getLanguageCode();
870
+        $urlGenerator = \OC::$server->getURLGenerator();
871
+
872
+        foreach ($installedApps as $app) {
873
+            if (array_search($app, $blacklist) === false) {
874
+
875
+                $info = OC_App::getAppInfo($app, false, $langCode);
876
+                if (!is_array($info)) {
877
+                    \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR);
878
+                    continue;
879
+                }
880
+
881
+                if (!isset($info['name'])) {
882
+                    \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR);
883
+                    continue;
884
+                }
885
+
886
+                $enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'no');
887
+                $info['groups'] = null;
888
+                if ($enabled === 'yes') {
889
+                    $active = true;
890
+                } else if ($enabled === 'no') {
891
+                    $active = false;
892
+                } else {
893
+                    $active = true;
894
+                    $info['groups'] = $enabled;
895
+                }
896
+
897
+                $info['active'] = $active;
898
+
899
+                if (self::isShipped($app)) {
900
+                    $info['internal'] = true;
901
+                    $info['level'] = self::officialApp;
902
+                    $info['removable'] = false;
903
+                } else {
904
+                    $info['internal'] = false;
905
+                    $info['removable'] = true;
906
+                }
907
+
908
+                $appPath = self::getAppPath($app);
909
+                if($appPath !== false) {
910
+                    $appIcon = $appPath . '/img/' . $app . '.svg';
911
+                    if (file_exists($appIcon)) {
912
+                        $info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, $app . '.svg');
913
+                        $info['previewAsIcon'] = true;
914
+                    } else {
915
+                        $appIcon = $appPath . '/img/app.svg';
916
+                        if (file_exists($appIcon)) {
917
+                            $info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, 'app.svg');
918
+                            $info['previewAsIcon'] = true;
919
+                        }
920
+                    }
921
+                }
922
+                // fix documentation
923
+                if (isset($info['documentation']) && is_array($info['documentation'])) {
924
+                    foreach ($info['documentation'] as $key => $url) {
925
+                        // If it is not an absolute URL we assume it is a key
926
+                        // i.e. admin-ldap will get converted to go.php?to=admin-ldap
927
+                        if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
928
+                            $url = $urlGenerator->linkToDocs($url);
929
+                        }
930
+
931
+                        $info['documentation'][$key] = $url;
932
+                    }
933
+                }
934
+
935
+                $info['version'] = OC_App::getAppVersion($app);
936
+                $appList[] = $info;
937
+            }
938
+        }
939
+
940
+        return $appList;
941
+    }
942
+
943
+    /**
944
+     * Returns the internal app ID or false
945
+     * @param string $ocsID
946
+     * @return string|false
947
+     */
948
+    public static function getInternalAppIdByOcs($ocsID) {
949
+        if(is_numeric($ocsID)) {
950
+            $idArray = \OC::$server->getAppConfig()->getValues(false, 'ocsid');
951
+            if(array_search($ocsID, $idArray)) {
952
+                return array_search($ocsID, $idArray);
953
+            }
954
+        }
955
+        return false;
956
+    }
957
+
958
+    public static function shouldUpgrade($app) {
959
+        $versions = self::getAppVersions();
960
+        $currentVersion = OC_App::getAppVersion($app);
961
+        if ($currentVersion && isset($versions[$app])) {
962
+            $installedVersion = $versions[$app];
963
+            if (!version_compare($currentVersion, $installedVersion, '=')) {
964
+                return true;
965
+            }
966
+        }
967
+        return false;
968
+    }
969
+
970
+    /**
971
+     * Adjust the number of version parts of $version1 to match
972
+     * the number of version parts of $version2.
973
+     *
974
+     * @param string $version1 version to adjust
975
+     * @param string $version2 version to take the number of parts from
976
+     * @return string shortened $version1
977
+     */
978
+    private static function adjustVersionParts($version1, $version2) {
979
+        $version1 = explode('.', $version1);
980
+        $version2 = explode('.', $version2);
981
+        // reduce $version1 to match the number of parts in $version2
982
+        while (count($version1) > count($version2)) {
983
+            array_pop($version1);
984
+        }
985
+        // if $version1 does not have enough parts, add some
986
+        while (count($version1) < count($version2)) {
987
+            $version1[] = '0';
988
+        }
989
+        return implode('.', $version1);
990
+    }
991
+
992
+    /**
993
+     * Check whether the current ownCloud version matches the given
994
+     * application's version requirements.
995
+     *
996
+     * The comparison is made based on the number of parts that the
997
+     * app info version has. For example for ownCloud 6.0.3 if the
998
+     * app info version is expecting version 6.0, the comparison is
999
+     * made on the first two parts of the ownCloud version.
1000
+     * This means that it's possible to specify "requiremin" => 6
1001
+     * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
1002
+     *
1003
+     * @param string $ocVersion ownCloud version to check against
1004
+     * @param array $appInfo app info (from xml)
1005
+     *
1006
+     * @return boolean true if compatible, otherwise false
1007
+     */
1008
+    public static function isAppCompatible($ocVersion, $appInfo) {
1009
+        $requireMin = '';
1010
+        $requireMax = '';
1011
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
1012
+            $requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
1013
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
1014
+            $requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
1015
+        } else if (isset($appInfo['requiremin'])) {
1016
+            $requireMin = $appInfo['requiremin'];
1017
+        } else if (isset($appInfo['require'])) {
1018
+            $requireMin = $appInfo['require'];
1019
+        }
1020
+
1021
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
1022
+            $requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
1023
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
1024
+            $requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
1025
+        } else if (isset($appInfo['requiremax'])) {
1026
+            $requireMax = $appInfo['requiremax'];
1027
+        }
1028
+
1029
+        if (is_array($ocVersion)) {
1030
+            $ocVersion = implode('.', $ocVersion);
1031
+        }
1032
+
1033
+        if (!empty($requireMin)
1034
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
1035
+        ) {
1036
+
1037
+            return false;
1038
+        }
1039
+
1040
+        if (!empty($requireMax)
1041
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
1042
+        ) {
1043
+            return false;
1044
+        }
1045
+
1046
+        return true;
1047
+    }
1048
+
1049
+    /**
1050
+     * get the installed version of all apps
1051
+     */
1052
+    public static function getAppVersions() {
1053
+        static $versions;
1054
+
1055
+        if(!$versions) {
1056
+            $appConfig = \OC::$server->getAppConfig();
1057
+            $versions = $appConfig->getValues(false, 'installed_version');
1058
+        }
1059
+        return $versions;
1060
+    }
1061
+
1062
+    /**
1063
+     * @param string $app
1064
+     * @param \OCP\IConfig $config
1065
+     * @param \OCP\IL10N $l
1066
+     * @return bool
1067
+     *
1068
+     * @throws Exception if app is not compatible with this version of ownCloud
1069
+     * @throws Exception if no app-name was specified
1070
+     */
1071
+    public function installApp($app,
1072
+                                \OCP\IConfig $config,
1073
+                                \OCP\IL10N $l) {
1074
+        if ($app !== false) {
1075
+            // check if the app is compatible with this version of ownCloud
1076
+            $info = self::getAppInfo($app);
1077
+            if(!is_array($info)) {
1078
+                throw new \Exception(
1079
+                    $l->t('App "%s" cannot be installed because appinfo file cannot be read.',
1080
+                        [$info['name']]
1081
+                    )
1082
+                );
1083
+            }
1084
+
1085
+            $version = \OCP\Util::getVersion();
1086
+            if (!self::isAppCompatible($version, $info)) {
1087
+                throw new \Exception(
1088
+                    $l->t('App "%s" cannot be installed because it is not compatible with this version of the server.',
1089
+                        array($info['name'])
1090
+                    )
1091
+                );
1092
+            }
1093
+
1094
+            // check for required dependencies
1095
+            self::checkAppDependencies($config, $l, $info);
1096
+
1097
+            $config->setAppValue($app, 'enabled', 'yes');
1098
+            if (isset($appData['id'])) {
1099
+                $config->setAppValue($app, 'ocsid', $appData['id']);
1100
+            }
1101
+
1102
+            if(isset($info['settings']) && is_array($info['settings'])) {
1103
+                $appPath = self::getAppPath($app);
1104
+                self::registerAutoloading($app, $appPath);
1105
+                \OC::$server->getSettingsManager()->setupSettings($info['settings']);
1106
+            }
1107
+
1108
+            \OC_Hook::emit('OC_App', 'post_enable', array('app' => $app));
1109
+        } else {
1110
+            if(empty($appName) ) {
1111
+                throw new \Exception($l->t("No app name specified"));
1112
+            } else {
1113
+                throw new \Exception($l->t("App '%s' could not be installed!", $appName));
1114
+            }
1115
+        }
1116
+
1117
+        return $app;
1118
+    }
1119
+
1120
+    /**
1121
+     * update the database for the app and call the update script
1122
+     *
1123
+     * @param string $appId
1124
+     * @return bool
1125
+     */
1126
+    public static function updateApp($appId) {
1127
+        $appPath = self::getAppPath($appId);
1128
+        if($appPath === false) {
1129
+            return false;
1130
+        }
1131
+        $appData = self::getAppInfo($appId);
1132
+        self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
1133
+        if (file_exists($appPath . '/appinfo/database.xml')) {
1134
+            OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
1135
+        }
1136
+        self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
1137
+        self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
1138
+        unset(self::$appVersion[$appId]);
1139
+        // run upgrade code
1140
+        if (file_exists($appPath . '/appinfo/update.php')) {
1141
+            self::loadApp($appId, false);
1142
+            include $appPath . '/appinfo/update.php';
1143
+        }
1144
+        self::setupBackgroundJobs($appData['background-jobs']);
1145
+        if(isset($appData['settings']) && is_array($appData['settings'])) {
1146
+            $appPath = self::getAppPath($appId);
1147
+            self::registerAutoloading($appId, $appPath);
1148
+            \OC::$server->getSettingsManager()->setupSettings($appData['settings']);
1149
+        }
1150
+
1151
+        //set remote/public handlers
1152
+        if (array_key_exists('ocsid', $appData)) {
1153
+            \OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
1154
+        } elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
1155
+            \OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
1156
+        }
1157
+        foreach ($appData['remote'] as $name => $path) {
1158
+            \OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
1159
+        }
1160
+        foreach ($appData['public'] as $name => $path) {
1161
+            \OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
1162
+        }
1163
+
1164
+        self::setAppTypes($appId);
1165
+
1166
+        $version = \OC_App::getAppVersion($appId);
1167
+        \OC::$server->getAppConfig()->setValue($appId, 'installed_version', $version);
1168
+
1169
+        \OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
1170
+            ManagerEvent::EVENT_APP_UPDATE, $appId
1171
+        ));
1172
+
1173
+        return true;
1174
+    }
1175
+
1176
+    /**
1177
+     * @param string $appId
1178
+     * @param string[] $steps
1179
+     * @throws \OC\NeedsUpdateException
1180
+     */
1181
+    public static function executeRepairSteps($appId, array $steps) {
1182
+        if (empty($steps)) {
1183
+            return;
1184
+        }
1185
+        // load the app
1186
+        self::loadApp($appId, false);
1187
+
1188
+        $dispatcher = OC::$server->getEventDispatcher();
1189
+
1190
+        // load the steps
1191
+        $r = new Repair([], $dispatcher);
1192
+        foreach ($steps as $step) {
1193
+            try {
1194
+                $r->addStep($step);
1195
+            } catch (Exception $ex) {
1196
+                $r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
1197
+                \OC::$server->getLogger()->logException($ex);
1198
+            }
1199
+        }
1200
+        // run the steps
1201
+        $r->run();
1202
+    }
1203
+
1204
+    public static function setupBackgroundJobs(array $jobs) {
1205
+        $queue = \OC::$server->getJobList();
1206
+        foreach ($jobs as $job) {
1207
+            $queue->add($job);
1208
+        }
1209
+    }
1210
+
1211
+    /**
1212
+     * @param string $appId
1213
+     * @param string[] $steps
1214
+     */
1215
+    private static function setupLiveMigrations($appId, array $steps) {
1216
+        $queue = \OC::$server->getJobList();
1217
+        foreach ($steps as $step) {
1218
+            $queue->add('OC\Migration\BackgroundRepair', [
1219
+                'app' => $appId,
1220
+                'step' => $step]);
1221
+        }
1222
+    }
1223
+
1224
+    /**
1225
+     * @param string $appId
1226
+     * @return \OC\Files\View|false
1227
+     */
1228
+    public static function getStorage($appId) {
1229
+        if (OC_App::isEnabled($appId)) { //sanity check
1230
+            if (\OC::$server->getUserSession()->isLoggedIn()) {
1231
+                $view = new \OC\Files\View('/' . OC_User::getUser());
1232
+                if (!$view->file_exists($appId)) {
1233
+                    $view->mkdir($appId);
1234
+                }
1235
+                return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
1236
+            } else {
1237
+                \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR);
1238
+                return false;
1239
+            }
1240
+        } else {
1241
+            \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR);
1242
+            return false;
1243
+        }
1244
+    }
1245
+
1246
+    protected static function findBestL10NOption($options, $lang) {
1247
+        $fallback = $similarLangFallback = $englishFallback = false;
1248
+
1249
+        $lang = strtolower($lang);
1250
+        $similarLang = $lang;
1251
+        if (strpos($similarLang, '_')) {
1252
+            // For "de_DE" we want to find "de" and the other way around
1253
+            $similarLang = substr($lang, 0, strpos($lang, '_'));
1254
+        }
1255
+
1256
+        foreach ($options as $option) {
1257
+            if (is_array($option)) {
1258
+                if ($fallback === false) {
1259
+                    $fallback = $option['@value'];
1260
+                }
1261
+
1262
+                if (!isset($option['@attributes']['lang'])) {
1263
+                    continue;
1264
+                }
1265
+
1266
+                $attributeLang = strtolower($option['@attributes']['lang']);
1267
+                if ($attributeLang === $lang) {
1268
+                    return $option['@value'];
1269
+                }
1270
+
1271
+                if ($attributeLang === $similarLang) {
1272
+                    $similarLangFallback = $option['@value'];
1273
+                } else if (strpos($attributeLang, $similarLang . '_') === 0) {
1274
+                    if ($similarLangFallback === false) {
1275
+                        $similarLangFallback =  $option['@value'];
1276
+                    }
1277
+                }
1278
+            } else {
1279
+                $englishFallback = $option;
1280
+            }
1281
+        }
1282
+
1283
+        if ($similarLangFallback !== false) {
1284
+            return $similarLangFallback;
1285
+        } else if ($englishFallback !== false) {
1286
+            return $englishFallback;
1287
+        }
1288
+        return (string) $fallback;
1289
+    }
1290
+
1291
+    /**
1292
+     * parses the app data array and enhanced the 'description' value
1293
+     *
1294
+     * @param array $data the app data
1295
+     * @param string $lang
1296
+     * @return array improved app data
1297
+     */
1298
+    public static function parseAppInfo(array $data, $lang = null) {
1299
+
1300
+        if ($lang && isset($data['name']) && is_array($data['name'])) {
1301
+            $data['name'] = self::findBestL10NOption($data['name'], $lang);
1302
+        }
1303
+        if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1304
+            $data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1305
+        }
1306
+        if ($lang && isset($data['description']) && is_array($data['description'])) {
1307
+            $data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1308
+        } else if (isset($data['description']) && is_string($data['description'])) {
1309
+            $data['description'] = trim($data['description']);
1310
+        } else  {
1311
+            $data['description'] = '';
1312
+        }
1313
+
1314
+        return $data;
1315
+    }
1316
+
1317
+    /**
1318
+     * @param \OCP\IConfig $config
1319
+     * @param \OCP\IL10N $l
1320
+     * @param array $info
1321
+     * @throws \Exception
1322
+     */
1323
+    protected static function checkAppDependencies($config, $l, $info) {
1324
+        $dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1325
+        $missing = $dependencyAnalyzer->analyze($info);
1326
+        if (!empty($missing)) {
1327
+            $missingMsg = join(PHP_EOL, $missing);
1328
+            throw new \Exception(
1329
+                $l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s',
1330
+                    [$info['name'], $missingMsg]
1331
+                )
1332
+            );
1333
+        }
1334
+    }
1335 1335
 }
Please login to merge, or discard this patch.
Spacing   +59 added lines, -59 removed lines patch added patch discarded remove patch
@@ -111,9 +111,9 @@  discard block
 block discarded – undo
111 111
 		$apps = self::getEnabledApps();
112 112
 
113 113
 		// Add each apps' folder as allowed class path
114
-		foreach($apps as $app) {
114
+		foreach ($apps as $app) {
115 115
 			$path = self::getAppPath($app);
116
-			if($path !== false) {
116
+			if ($path !== false) {
117 117
 				self::registerAutoloading($app, $path);
118 118
 			}
119 119
 		}
@@ -140,15 +140,15 @@  discard block
 block discarded – undo
140 140
 	public static function loadApp($app, $checkUpgrade = true) {
141 141
 		self::$loadedApps[] = $app;
142 142
 		$appPath = self::getAppPath($app);
143
-		if($appPath === false) {
143
+		if ($appPath === false) {
144 144
 			return;
145 145
 		}
146 146
 
147 147
 		// in case someone calls loadApp() directly
148 148
 		self::registerAutoloading($app, $appPath);
149 149
 
150
-		if (is_file($appPath . '/appinfo/app.php')) {
151
-			\OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
150
+		if (is_file($appPath.'/appinfo/app.php')) {
151
+			\OC::$server->getEventLogger()->start('load_app_'.$app, 'Load app: '.$app);
152 152
 			if ($checkUpgrade and self::shouldUpgrade($app)) {
153 153
 				throw new \OC\NeedsUpdateException();
154 154
 			}
@@ -160,7 +160,7 @@  discard block
 block discarded – undo
160 160
 				// enabled for groups
161 161
 				self::$enabledAppsCache = array();
162 162
 			}
163
-			\OC::$server->getEventLogger()->end('load_app_' . $app);
163
+			\OC::$server->getEventLogger()->end('load_app_'.$app);
164 164
 		}
165 165
 
166 166
 		$info = self::getAppInfo($app);
@@ -187,16 +187,16 @@  discard block
 block discarded – undo
187 187
 	 * @param string $path
188 188
 	 */
189 189
 	public static function registerAutoloading($app, $path) {
190
-		$key = $app . '-' . $path;
191
-		if(isset(self::$alreadyRegistered[$key])) {
190
+		$key = $app.'-'.$path;
191
+		if (isset(self::$alreadyRegistered[$key])) {
192 192
 			return;
193 193
 		}
194 194
 		self::$alreadyRegistered[$key] = true;
195 195
 		// Register on PSR-4 composer autoloader
196 196
 		$appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
197
-		\OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
197
+		\OC::$composerAutoloader->addPsr4($appNamespace.'\\', $path.'/lib/', true);
198 198
 		if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
199
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
199
+			\OC::$composerAutoloader->addPsr4($appNamespace.'\\Tests\\', $path.'/tests/', true);
200 200
 		}
201 201
 
202 202
 		// Register on legacy autoloader
@@ -211,7 +211,7 @@  discard block
 block discarded – undo
211 211
 	private static function requireAppFile($app) {
212 212
 		try {
213 213
 			// encapsulated here to avoid variable scope conflicts
214
-			require_once $app . '/appinfo/app.php';
214
+			require_once $app.'/appinfo/app.php';
215 215
 		} catch (Error $ex) {
216 216
 			\OC::$server->getLogger()->logException($ex);
217 217
 			$blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps();
@@ -265,7 +265,7 @@  discard block
 block discarded – undo
265 265
 	 */
266 266
 	public static function setAppTypes($app) {
267 267
 		$appData = self::getAppInfo($app);
268
-		if(!is_array($appData)) {
268
+		if (!is_array($appData)) {
269 269
 			return;
270 270
 		}
271 271
 
@@ -329,8 +329,8 @@  discard block
 block discarded – undo
329 329
 		} else {
330 330
 			$apps = $appManager->getEnabledAppsForUser($user);
331 331
 		}
332
-		$apps = array_filter($apps, function ($app) {
333
-			return $app !== 'files';//we add this manually
332
+		$apps = array_filter($apps, function($app) {
333
+			return $app !== 'files'; //we add this manually
334 334
 		});
335 335
 		sort($apps);
336 336
 		array_unshift($apps, 'files');
@@ -374,7 +374,7 @@  discard block
 block discarded – undo
374 374
 		);
375 375
 		$isDownloaded = $installer->isDownloaded($appId);
376 376
 
377
-		if(!$isDownloaded) {
377
+		if (!$isDownloaded) {
378 378
 			$installer->downloadApp($appId);
379 379
 		}
380 380
 
@@ -412,7 +412,7 @@  discard block
 block discarded – undo
412 412
 		}
413 413
 
414 414
 		$info = self::getAppInfo($appId);
415
-		if(isset($info['settings']) && is_array($info['settings'])) {
415
+		if (isset($info['settings']) && is_array($info['settings'])) {
416 416
 			$appPath = self::getAppPath($appId);
417 417
 			self::registerAutoloading($appId, $appPath);
418 418
 			\OC::$server->getSettingsManager()->setupSettings($info['settings']);
@@ -501,7 +501,7 @@  discard block
 block discarded – undo
501 501
 			//SubAdmins are also allowed to access user management
502 502
 			$userObject = \OC::$server->getUserSession()->getUser();
503 503
 			$isSubAdmin = false;
504
-			if($userObject !== null) {
504
+			if ($userObject !== null) {
505 505
 				$isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
506 506
 			}
507 507
 			if ($isSubAdmin) {
@@ -586,7 +586,7 @@  discard block
 block discarded – undo
586 586
 	 */
587 587
 	public static function findAppInDirectories($appId) {
588 588
 		$sanitizedAppId = self::cleanAppId($appId);
589
-		if($sanitizedAppId !== $appId) {
589
+		if ($sanitizedAppId !== $appId) {
590 590
 			return false;
591 591
 		}
592 592
 		static $app_dir = array();
@@ -597,7 +597,7 @@  discard block
 block discarded – undo
597 597
 
598 598
 		$possibleApps = array();
599 599
 		foreach (OC::$APPSROOTS as $dir) {
600
-			if (file_exists($dir['path'] . '/' . $appId)) {
600
+			if (file_exists($dir['path'].'/'.$appId)) {
601 601
 				$possibleApps[] = $dir;
602 602
 			}
603 603
 		}
@@ -638,7 +638,7 @@  discard block
 block discarded – undo
638 638
 		}
639 639
 
640 640
 		if (($dir = self::findAppInDirectories($appId)) != false) {
641
-			return $dir['path'] . '/' . $appId;
641
+			return $dir['path'].'/'.$appId;
642 642
 		}
643 643
 		return false;
644 644
 	}
@@ -652,7 +652,7 @@  discard block
 block discarded – undo
652 652
 	 */
653 653
 	public static function getAppWebPath($appId) {
654 654
 		if (($dir = self::findAppInDirectories($appId)) != false) {
655
-			return OC::$WEBROOT . $dir['url'] . '/' . $appId;
655
+			return OC::$WEBROOT.$dir['url'].'/'.$appId;
656 656
 		}
657 657
 		return false;
658 658
 	}
@@ -665,7 +665,7 @@  discard block
 block discarded – undo
665 665
 	 * @return string
666 666
 	 */
667 667
 	public static function getAppVersion($appId, $useCache = true) {
668
-		if($useCache && isset(self::$appVersion[$appId])) {
668
+		if ($useCache && isset(self::$appVersion[$appId])) {
669 669
 			return self::$appVersion[$appId];
670 670
 		}
671 671
 
@@ -681,7 +681,7 @@  discard block
 block discarded – undo
681 681
 	 * @return string
682 682
 	 */
683 683
 	public static function getAppVersionByPath($path) {
684
-		$infoFile = $path . '/appinfo/info.xml';
684
+		$infoFile = $path.'/appinfo/info.xml';
685 685
 		$appData = self::getAppInfo($infoFile, true);
686 686
 		return isset($appData['version']) ? $appData['version'] : '';
687 687
 	}
@@ -704,10 +704,10 @@  discard block
 block discarded – undo
704 704
 				return self::$appInfo[$appId];
705 705
 			}
706 706
 			$appPath = self::getAppPath($appId);
707
-			if($appPath === false) {
707
+			if ($appPath === false) {
708 708
 				return null;
709 709
 			}
710
-			$file = $appPath . '/appinfo/info.xml';
710
+			$file = $appPath.'/appinfo/info.xml';
711 711
 		}
712 712
 
713 713
 		$parser = new InfoParser(\OC::$server->getMemCacheFactory()->create('core.appinfo'));
@@ -716,9 +716,9 @@  discard block
 block discarded – undo
716 716
 		if (is_array($data)) {
717 717
 			$data = OC_App::parseAppInfo($data, $lang);
718 718
 		}
719
-		if(isset($data['ocsid'])) {
719
+		if (isset($data['ocsid'])) {
720 720
 			$storedId = \OC::$server->getConfig()->getAppValue($appId, 'ocsid');
721
-			if($storedId !== '' && $storedId !== $data['ocsid']) {
721
+			if ($storedId !== '' && $storedId !== $data['ocsid']) {
722 722
 				$data['ocsid'] = $storedId;
723 723
 			}
724 724
 		}
@@ -798,7 +798,7 @@  discard block
 block discarded – undo
798 798
 	 * @param string $page
799 799
 	 */
800 800
 	public static function registerAdmin($app, $page) {
801
-		self::$adminForms[] = $app . '/' . $page . '.php';
801
+		self::$adminForms[] = $app.'/'.$page.'.php';
802 802
 	}
803 803
 
804 804
 	/**
@@ -807,7 +807,7 @@  discard block
 block discarded – undo
807 807
 	 * @param string $page
808 808
 	 */
809 809
 	public static function registerPersonal($app, $page) {
810
-		self::$personalForms[] = $app . '/' . $page . '.php';
810
+		self::$personalForms[] = $app.'/'.$page.'.php';
811 811
 	}
812 812
 
813 813
 	/**
@@ -836,7 +836,7 @@  discard block
 block discarded – undo
836 836
 
837 837
 		foreach (OC::$APPSROOTS as $apps_dir) {
838 838
 			if (!is_readable($apps_dir['path'])) {
839
-				\OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN);
839
+				\OCP\Util::writeLog('core', 'unable to read app folder : '.$apps_dir['path'], \OCP\Util::WARN);
840 840
 				continue;
841 841
 			}
842 842
 			$dh = opendir($apps_dir['path']);
@@ -844,7 +844,7 @@  discard block
 block discarded – undo
844 844
 			if (is_resource($dh)) {
845 845
 				while (($file = readdir($dh)) !== false) {
846 846
 
847
-					if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
847
+					if ($file[0] != '.' and is_dir($apps_dir['path'].'/'.$file) and is_file($apps_dir['path'].'/'.$file.'/appinfo/info.xml')) {
848 848
 
849 849
 						$apps[] = $file;
850 850
 					}
@@ -874,12 +874,12 @@  discard block
 block discarded – undo
874 874
 
875 875
 				$info = OC_App::getAppInfo($app, false, $langCode);
876 876
 				if (!is_array($info)) {
877
-					\OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR);
877
+					\OCP\Util::writeLog('core', 'Could not read app info file for app "'.$app.'"', \OCP\Util::ERROR);
878 878
 					continue;
879 879
 				}
880 880
 
881 881
 				if (!isset($info['name'])) {
882
-					\OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR);
882
+					\OCP\Util::writeLog('core', 'App id "'.$app.'" has no name in appinfo', \OCP\Util::ERROR);
883 883
 					continue;
884 884
 				}
885 885
 
@@ -906,13 +906,13 @@  discard block
 block discarded – undo
906 906
 				}
907 907
 
908 908
 				$appPath = self::getAppPath($app);
909
-				if($appPath !== false) {
910
-					$appIcon = $appPath . '/img/' . $app . '.svg';
909
+				if ($appPath !== false) {
910
+					$appIcon = $appPath.'/img/'.$app.'.svg';
911 911
 					if (file_exists($appIcon)) {
912
-						$info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, $app . '.svg');
912
+						$info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, $app.'.svg');
913 913
 						$info['previewAsIcon'] = true;
914 914
 					} else {
915
-						$appIcon = $appPath . '/img/app.svg';
915
+						$appIcon = $appPath.'/img/app.svg';
916 916
 						if (file_exists($appIcon)) {
917 917
 							$info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, 'app.svg');
918 918
 							$info['previewAsIcon'] = true;
@@ -946,9 +946,9 @@  discard block
 block discarded – undo
946 946
 	 * @return string|false
947 947
 	 */
948 948
 	public static function getInternalAppIdByOcs($ocsID) {
949
-		if(is_numeric($ocsID)) {
949
+		if (is_numeric($ocsID)) {
950 950
 			$idArray = \OC::$server->getAppConfig()->getValues(false, 'ocsid');
951
-			if(array_search($ocsID, $idArray)) {
951
+			if (array_search($ocsID, $idArray)) {
952 952
 				return array_search($ocsID, $idArray);
953 953
 			}
954 954
 		}
@@ -1052,7 +1052,7 @@  discard block
 block discarded – undo
1052 1052
 	public static function getAppVersions() {
1053 1053
 		static $versions;
1054 1054
 
1055
-		if(!$versions) {
1055
+		if (!$versions) {
1056 1056
 			$appConfig = \OC::$server->getAppConfig();
1057 1057
 			$versions = $appConfig->getValues(false, 'installed_version');
1058 1058
 		}
@@ -1074,7 +1074,7 @@  discard block
 block discarded – undo
1074 1074
 		if ($app !== false) {
1075 1075
 			// check if the app is compatible with this version of ownCloud
1076 1076
 			$info = self::getAppInfo($app);
1077
-			if(!is_array($info)) {
1077
+			if (!is_array($info)) {
1078 1078
 				throw new \Exception(
1079 1079
 					$l->t('App "%s" cannot be installed because appinfo file cannot be read.',
1080 1080
 						[$info['name']]
@@ -1099,7 +1099,7 @@  discard block
 block discarded – undo
1099 1099
 				$config->setAppValue($app, 'ocsid', $appData['id']);
1100 1100
 			}
1101 1101
 
1102
-			if(isset($info['settings']) && is_array($info['settings'])) {
1102
+			if (isset($info['settings']) && is_array($info['settings'])) {
1103 1103
 				$appPath = self::getAppPath($app);
1104 1104
 				self::registerAutoloading($app, $appPath);
1105 1105
 				\OC::$server->getSettingsManager()->setupSettings($info['settings']);
@@ -1107,7 +1107,7 @@  discard block
 block discarded – undo
1107 1107
 
1108 1108
 			\OC_Hook::emit('OC_App', 'post_enable', array('app' => $app));
1109 1109
 		} else {
1110
-			if(empty($appName) ) {
1110
+			if (empty($appName)) {
1111 1111
 				throw new \Exception($l->t("No app name specified"));
1112 1112
 			} else {
1113 1113
 				throw new \Exception($l->t("App '%s' could not be installed!", $appName));
@@ -1125,24 +1125,24 @@  discard block
 block discarded – undo
1125 1125
 	 */
1126 1126
 	public static function updateApp($appId) {
1127 1127
 		$appPath = self::getAppPath($appId);
1128
-		if($appPath === false) {
1128
+		if ($appPath === false) {
1129 1129
 			return false;
1130 1130
 		}
1131 1131
 		$appData = self::getAppInfo($appId);
1132 1132
 		self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
1133
-		if (file_exists($appPath . '/appinfo/database.xml')) {
1134
-			OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
1133
+		if (file_exists($appPath.'/appinfo/database.xml')) {
1134
+			OC_DB::updateDbFromStructure($appPath.'/appinfo/database.xml');
1135 1135
 		}
1136 1136
 		self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
1137 1137
 		self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
1138 1138
 		unset(self::$appVersion[$appId]);
1139 1139
 		// run upgrade code
1140
-		if (file_exists($appPath . '/appinfo/update.php')) {
1140
+		if (file_exists($appPath.'/appinfo/update.php')) {
1141 1141
 			self::loadApp($appId, false);
1142
-			include $appPath . '/appinfo/update.php';
1142
+			include $appPath.'/appinfo/update.php';
1143 1143
 		}
1144 1144
 		self::setupBackgroundJobs($appData['background-jobs']);
1145
-		if(isset($appData['settings']) && is_array($appData['settings'])) {
1145
+		if (isset($appData['settings']) && is_array($appData['settings'])) {
1146 1146
 			$appPath = self::getAppPath($appId);
1147 1147
 			self::registerAutoloading($appId, $appPath);
1148 1148
 			\OC::$server->getSettingsManager()->setupSettings($appData['settings']);
@@ -1151,14 +1151,14 @@  discard block
 block discarded – undo
1151 1151
 		//set remote/public handlers
1152 1152
 		if (array_key_exists('ocsid', $appData)) {
1153 1153
 			\OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
1154
-		} elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
1154
+		} elseif (\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
1155 1155
 			\OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
1156 1156
 		}
1157 1157
 		foreach ($appData['remote'] as $name => $path) {
1158
-			\OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
1158
+			\OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $appId.'/'.$path);
1159 1159
 		}
1160 1160
 		foreach ($appData['public'] as $name => $path) {
1161
-			\OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
1161
+			\OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $appId.'/'.$path);
1162 1162
 		}
1163 1163
 
1164 1164
 		self::setAppTypes($appId);
@@ -1228,17 +1228,17 @@  discard block
 block discarded – undo
1228 1228
 	public static function getStorage($appId) {
1229 1229
 		if (OC_App::isEnabled($appId)) { //sanity check
1230 1230
 			if (\OC::$server->getUserSession()->isLoggedIn()) {
1231
-				$view = new \OC\Files\View('/' . OC_User::getUser());
1231
+				$view = new \OC\Files\View('/'.OC_User::getUser());
1232 1232
 				if (!$view->file_exists($appId)) {
1233 1233
 					$view->mkdir($appId);
1234 1234
 				}
1235
-				return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
1235
+				return new \OC\Files\View('/'.OC_User::getUser().'/'.$appId);
1236 1236
 			} else {
1237
-				\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR);
1237
+				\OCP\Util::writeLog('core', 'Can\'t get app storage, app '.$appId.', user not logged in', \OCP\Util::ERROR);
1238 1238
 				return false;
1239 1239
 			}
1240 1240
 		} else {
1241
-			\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR);
1241
+			\OCP\Util::writeLog('core', 'Can\'t get app storage, app '.$appId.' not enabled', \OCP\Util::ERROR);
1242 1242
 			return false;
1243 1243
 		}
1244 1244
 	}
@@ -1270,9 +1270,9 @@  discard block
 block discarded – undo
1270 1270
 
1271 1271
 				if ($attributeLang === $similarLang) {
1272 1272
 					$similarLangFallback = $option['@value'];
1273
-				} else if (strpos($attributeLang, $similarLang . '_') === 0) {
1273
+				} else if (strpos($attributeLang, $similarLang.'_') === 0) {
1274 1274
 					if ($similarLangFallback === false) {
1275
-						$similarLangFallback =  $option['@value'];
1275
+						$similarLangFallback = $option['@value'];
1276 1276
 					}
1277 1277
 				}
1278 1278
 			} else {
@@ -1307,7 +1307,7 @@  discard block
 block discarded – undo
1307 1307
 			$data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1308 1308
 		} else if (isset($data['description']) && is_string($data['description'])) {
1309 1309
 			$data['description'] = trim($data['description']);
1310
-		} else  {
1310
+		} else {
1311 1311
 			$data['description'] = '';
1312 1312
 		}
1313 1313
 
Please login to merge, or discard this patch.
lib/private/L10N/Factory.php 3 patches
Doc Comments   +5 added lines patch added patch discarded remove patch
@@ -318,6 +318,11 @@
 block discarded – undo
318 318
 	 */
319 319
 	// FIXME This method is only public, until OC_L10N does not need it anymore,
320 320
 	// FIXME This is also the reason, why it is not in the public interface
321
+
322
+	/**
323
+	 * @param string $app
324
+	 * @param string $lang
325
+	 */
321 326
 	public function getL10nFilesForApp($app, $lang) {
322 327
 		$languageFiles = [];
323 328
 
Please login to merge, or discard this patch.
Indentation   +387 added lines, -387 removed lines patch added patch discarded remove patch
@@ -40,391 +40,391 @@
 block discarded – undo
40 40
  */
41 41
 class Factory implements IFactory {
42 42
 
43
-	/** @var string */
44
-	protected $requestLanguage = '';
45
-
46
-	/**
47
-	 * cached instances
48
-	 * @var array Structure: Lang => App => \OCP\IL10N
49
-	 */
50
-	protected $instances = [];
51
-
52
-	/**
53
-	 * @var array Structure: App => string[]
54
-	 */
55
-	protected $availableLanguages = [];
56
-
57
-	/**
58
-	 * @var array Structure: string => callable
59
-	 */
60
-	protected $pluralFunctions = [];
61
-
62
-	/** @var IConfig */
63
-	protected $config;
64
-
65
-	/** @var IRequest */
66
-	protected $request;
67
-
68
-	/** @var IUserSession */
69
-	protected $userSession;
70
-
71
-	/** @var string */
72
-	protected $serverRoot;
73
-
74
-	/**
75
-	 * @param IConfig $config
76
-	 * @param IRequest $request
77
-	 * @param IUserSession $userSession
78
-	 * @param string $serverRoot
79
-	 */
80
-	public function __construct(IConfig $config,
81
-								IRequest $request,
82
-								IUserSession $userSession,
83
-								$serverRoot) {
84
-		$this->config = $config;
85
-		$this->request = $request;
86
-		$this->userSession = $userSession;
87
-		$this->serverRoot = $serverRoot;
88
-	}
89
-
90
-	/**
91
-	 * Get a language instance
92
-	 *
93
-	 * @param string $app
94
-	 * @param string|null $lang
95
-	 * @return \OCP\IL10N
96
-	 */
97
-	public function get($app, $lang = null) {
98
-		$app = \OC_App::cleanAppId($app);
99
-		if ($lang !== null) {
100
-			$lang = str_replace(array('\0', '/', '\\', '..'), '', (string) $lang);
101
-		}
102
-		if ($lang === null || !$this->languageExists($app, $lang)) {
103
-			$lang = $this->findLanguage($app);
104
-		}
105
-
106
-		if (!isset($this->instances[$lang][$app])) {
107
-			$this->instances[$lang][$app] = new L10N(
108
-				$this, $app, $lang,
109
-				$this->getL10nFilesForApp($app, $lang)
110
-			);
111
-		}
112
-
113
-		return $this->instances[$lang][$app];
114
-	}
115
-
116
-	/**
117
-	 * Find the best language
118
-	 *
119
-	 * @param string|null $app App id or null for core
120
-	 * @return string language If nothing works it returns 'en'
121
-	 */
122
-	public function findLanguage($app = null) {
123
-		if ($this->requestLanguage !== '' && $this->languageExists($app, $this->requestLanguage)) {
124
-			return $this->requestLanguage;
125
-		}
126
-
127
-		/**
128
-		 * At this point ownCloud might not yet be installed and thus the lookup
129
-		 * in the preferences table might fail. For this reason we need to check
130
-		 * whether the instance has already been installed
131
-		 *
132
-		 * @link https://github.com/owncloud/core/issues/21955
133
-		 */
134
-		if($this->config->getSystemValue('installed', false)) {
135
-			$userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() :  null;
136
-			if(!is_null($userId)) {
137
-				$userLang = $this->config->getUserValue($userId, 'core', 'lang', null);
138
-			} else {
139
-				$userLang = null;
140
-			}
141
-		} else {
142
-			$userId = null;
143
-			$userLang = null;
144
-		}
145
-
146
-		if ($userLang) {
147
-			$this->requestLanguage = $userLang;
148
-			if ($this->languageExists($app, $userLang)) {
149
-				return $userLang;
150
-			}
151
-		}
152
-
153
-		try {
154
-			// Try to get the language from the Request
155
-			$lang = $this->getLanguageFromRequest($app);
156
-			if ($userId !== null && $app === null && !$userLang) {
157
-				$this->config->setUserValue($userId, 'core', 'lang', $lang);
158
-			}
159
-			return $lang;
160
-		} catch (LanguageNotFoundException $e) {
161
-			// Finding language from request failed fall back to default language
162
-			$defaultLanguage = $this->config->getSystemValue('default_language', false);
163
-			if ($defaultLanguage !== false && $this->languageExists($app, $defaultLanguage)) {
164
-				return $defaultLanguage;
165
-			}
166
-		}
167
-
168
-		// We could not find any language so fall back to english
169
-		return 'en';
170
-	}
171
-
172
-	/**
173
-	 * Find all available languages for an app
174
-	 *
175
-	 * @param string|null $app App id or null for core
176
-	 * @return array an array of available languages
177
-	 */
178
-	public function findAvailableLanguages($app = null) {
179
-		$key = $app;
180
-		if ($key === null) {
181
-			$key = 'null';
182
-		}
183
-
184
-		// also works with null as key
185
-		if (!empty($this->availableLanguages[$key])) {
186
-			return $this->availableLanguages[$key];
187
-		}
188
-
189
-		$available = ['en']; //english is always available
190
-		$dir = $this->findL10nDir($app);
191
-		if (is_dir($dir)) {
192
-			$files = scandir($dir);
193
-			if ($files !== false) {
194
-				foreach ($files as $file) {
195
-					if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
196
-						$available[] = substr($file, 0, -5);
197
-					}
198
-				}
199
-			}
200
-		}
201
-
202
-		// merge with translations from theme
203
-		$theme = $this->config->getSystemValue('theme');
204
-		if (!empty($theme)) {
205
-			$themeDir = $this->serverRoot . '/themes/' . $theme . substr($dir, strlen($this->serverRoot));
206
-
207
-			if (is_dir($themeDir)) {
208
-				$files = scandir($themeDir);
209
-				if ($files !== false) {
210
-					foreach ($files as $file) {
211
-						if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
212
-							$available[] = substr($file, 0, -5);
213
-						}
214
-					}
215
-				}
216
-			}
217
-		}
218
-
219
-		$this->availableLanguages[$key] = $available;
220
-		return $available;
221
-	}
222
-
223
-	/**
224
-	 * @param string|null $app App id or null for core
225
-	 * @param string $lang
226
-	 * @return bool
227
-	 */
228
-	public function languageExists($app, $lang) {
229
-		if ($lang === 'en') {//english is always available
230
-			return true;
231
-		}
232
-
233
-		$languages = $this->findAvailableLanguages($app);
234
-		return array_search($lang, $languages) !== false;
235
-	}
236
-
237
-	/**
238
-	 * @param string|null $app
239
-	 * @return string
240
-	 * @throws LanguageNotFoundException
241
-	 */
242
-	private function getLanguageFromRequest($app) {
243
-		$header = $this->request->getHeader('ACCEPT_LANGUAGE');
244
-		if ($header) {
245
-			$available = $this->findAvailableLanguages($app);
246
-
247
-			// E.g. make sure that 'de' is before 'de_DE'.
248
-			sort($available);
249
-
250
-			$preferences = preg_split('/,\s*/', strtolower($header));
251
-			foreach ($preferences as $preference) {
252
-				list($preferred_language) = explode(';', $preference);
253
-				$preferred_language = str_replace('-', '_', $preferred_language);
254
-
255
-				foreach ($available as $available_language) {
256
-					if ($preferred_language === strtolower($available_language)) {
257
-						return $available_language;
258
-					}
259
-				}
260
-
261
-				// Fallback from de_De to de
262
-				foreach ($available as $available_language) {
263
-					if (substr($preferred_language, 0, 2) === $available_language) {
264
-						return $available_language;
265
-					}
266
-				}
267
-			}
268
-		}
269
-
270
-		throw new LanguageNotFoundException();
271
-	}
272
-
273
-	/**
274
-	 * @param string|null $app App id or null for core
275
-	 * @return string
276
-	 */
277
-	public function setLanguageFromRequest($app = null) {
278
-
279
-		try {
280
-			$requestLanguage = $this->getLanguageFromRequest($app);
281
-		} catch (LanguageNotFoundException $e) {
282
-			$requestLanguage = 'en';
283
-		}
284
-
285
-		if ($app === null && !$this->requestLanguage) {
286
-			$this->requestLanguage = $requestLanguage;
287
-		}
288
-		return $requestLanguage;
289
-	}
290
-
291
-	/**
292
-	 * Checks if $sub is a subdirectory of $parent
293
-	 *
294
-	 * @param string $sub
295
-	 * @param string $parent
296
-	 * @return bool
297
-	 */
298
-	private function isSubDirectory($sub, $parent) {
299
-		// Check whether $sub contains no ".."
300
-		if(strpos($sub, '..') !== false) {
301
-			return false;
302
-		}
303
-
304
-		// Check whether $sub is a subdirectory of $parent
305
-		if (strpos($sub, $parent) === 0) {
306
-			return true;
307
-		}
308
-
309
-		return false;
310
-	}
311
-
312
-	/**
313
-	 * Get a list of language files that should be loaded
314
-	 *
315
-	 * @param string $app
316
-	 * @param string $lang
317
-	 * @return string[]
318
-	 */
319
-	// FIXME This method is only public, until OC_L10N does not need it anymore,
320
-	// FIXME This is also the reason, why it is not in the public interface
321
-	public function getL10nFilesForApp($app, $lang) {
322
-		$languageFiles = [];
323
-
324
-		$i18nDir = $this->findL10nDir($app);
325
-		$transFile = strip_tags($i18nDir) . strip_tags($lang) . '.json';
326
-
327
-		if (($this->isSubDirectory($transFile, $this->serverRoot . '/core/l10n/')
328
-				|| $this->isSubDirectory($transFile, $this->serverRoot . '/lib/l10n/')
329
-				|| $this->isSubDirectory($transFile, $this->serverRoot . '/settings/l10n/')
330
-				|| $this->isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/')
331
-			)
332
-			&& file_exists($transFile)) {
333
-			// load the translations file
334
-			$languageFiles[] = $transFile;
335
-		}
336
-
337
-		// merge with translations from theme
338
-		$theme = $this->config->getSystemValue('theme');
339
-		if (!empty($theme)) {
340
-			$transFile = $this->serverRoot . '/themes/' . $theme . substr($transFile, strlen($this->serverRoot));
341
-			if (file_exists($transFile)) {
342
-				$languageFiles[] = $transFile;
343
-			}
344
-		}
345
-
346
-		return $languageFiles;
347
-	}
348
-
349
-	/**
350
-	 * find the l10n directory
351
-	 *
352
-	 * @param string $app App id or empty string for core
353
-	 * @return string directory
354
-	 */
355
-	protected function findL10nDir($app = null) {
356
-		if (in_array($app, ['core', 'lib', 'settings'])) {
357
-			if (file_exists($this->serverRoot . '/' . $app . '/l10n/')) {
358
-				return $this->serverRoot . '/' . $app . '/l10n/';
359
-			}
360
-		} else if ($app && \OC_App::getAppPath($app) !== false) {
361
-			// Check if the app is in the app folder
362
-			return \OC_App::getAppPath($app) . '/l10n/';
363
-		}
364
-		return $this->serverRoot . '/core/l10n/';
365
-	}
366
-
367
-
368
-	/**
369
-	 * Creates a function from the plural string
370
-	 *
371
-	 * Parts of the code is copied from Habari:
372
-	 * https://github.com/habari/system/blob/master/classes/locale.php
373
-	 * @param string $string
374
-	 * @return string
375
-	 */
376
-	public function createPluralFunction($string) {
377
-		if (isset($this->pluralFunctions[$string])) {
378
-			return $this->pluralFunctions[$string];
379
-		}
380
-
381
-		if (preg_match( '/^\s*nplurals\s*=\s*(\d+)\s*;\s*plural=(.*)$/u', $string, $matches)) {
382
-			// sanitize
383
-			$nplurals = preg_replace( '/[^0-9]/', '', $matches[1] );
384
-			$plural = preg_replace( '#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2] );
385
-
386
-			$body = str_replace(
387
-				array( 'plural', 'n', '$n$plurals', ),
388
-				array( '$plural', '$n', '$nplurals', ),
389
-				'nplurals='. $nplurals . '; plural=' . $plural
390
-			);
391
-
392
-			// add parents
393
-			// important since PHP's ternary evaluates from left to right
394
-			$body .= ';';
395
-			$res = '';
396
-			$p = 0;
397
-			for($i = 0; $i < strlen($body); $i++) {
398
-				$ch = $body[$i];
399
-				switch ( $ch ) {
400
-					case '?':
401
-						$res .= ' ? (';
402
-						$p++;
403
-						break;
404
-					case ':':
405
-						$res .= ') : (';
406
-						break;
407
-					case ';':
408
-						$res .= str_repeat( ')', $p ) . ';';
409
-						$p = 0;
410
-						break;
411
-					default:
412
-						$res .= $ch;
413
-				}
414
-			}
415
-
416
-			$body = $res . 'return ($plural>=$nplurals?$nplurals-1:$plural);';
417
-			$function = create_function('$n', $body);
418
-			$this->pluralFunctions[$string] = $function;
419
-			return $function;
420
-		} else {
421
-			// default: one plural form for all cases but n==1 (english)
422
-			$function = create_function(
423
-				'$n',
424
-				'$nplurals=2;$plural=($n==1?0:1);return ($plural>=$nplurals?$nplurals-1:$plural);'
425
-			);
426
-			$this->pluralFunctions[$string] = $function;
427
-			return $function;
428
-		}
429
-	}
43
+    /** @var string */
44
+    protected $requestLanguage = '';
45
+
46
+    /**
47
+     * cached instances
48
+     * @var array Structure: Lang => App => \OCP\IL10N
49
+     */
50
+    protected $instances = [];
51
+
52
+    /**
53
+     * @var array Structure: App => string[]
54
+     */
55
+    protected $availableLanguages = [];
56
+
57
+    /**
58
+     * @var array Structure: string => callable
59
+     */
60
+    protected $pluralFunctions = [];
61
+
62
+    /** @var IConfig */
63
+    protected $config;
64
+
65
+    /** @var IRequest */
66
+    protected $request;
67
+
68
+    /** @var IUserSession */
69
+    protected $userSession;
70
+
71
+    /** @var string */
72
+    protected $serverRoot;
73
+
74
+    /**
75
+     * @param IConfig $config
76
+     * @param IRequest $request
77
+     * @param IUserSession $userSession
78
+     * @param string $serverRoot
79
+     */
80
+    public function __construct(IConfig $config,
81
+                                IRequest $request,
82
+                                IUserSession $userSession,
83
+                                $serverRoot) {
84
+        $this->config = $config;
85
+        $this->request = $request;
86
+        $this->userSession = $userSession;
87
+        $this->serverRoot = $serverRoot;
88
+    }
89
+
90
+    /**
91
+     * Get a language instance
92
+     *
93
+     * @param string $app
94
+     * @param string|null $lang
95
+     * @return \OCP\IL10N
96
+     */
97
+    public function get($app, $lang = null) {
98
+        $app = \OC_App::cleanAppId($app);
99
+        if ($lang !== null) {
100
+            $lang = str_replace(array('\0', '/', '\\', '..'), '', (string) $lang);
101
+        }
102
+        if ($lang === null || !$this->languageExists($app, $lang)) {
103
+            $lang = $this->findLanguage($app);
104
+        }
105
+
106
+        if (!isset($this->instances[$lang][$app])) {
107
+            $this->instances[$lang][$app] = new L10N(
108
+                $this, $app, $lang,
109
+                $this->getL10nFilesForApp($app, $lang)
110
+            );
111
+        }
112
+
113
+        return $this->instances[$lang][$app];
114
+    }
115
+
116
+    /**
117
+     * Find the best language
118
+     *
119
+     * @param string|null $app App id or null for core
120
+     * @return string language If nothing works it returns 'en'
121
+     */
122
+    public function findLanguage($app = null) {
123
+        if ($this->requestLanguage !== '' && $this->languageExists($app, $this->requestLanguage)) {
124
+            return $this->requestLanguage;
125
+        }
126
+
127
+        /**
128
+         * At this point ownCloud might not yet be installed and thus the lookup
129
+         * in the preferences table might fail. For this reason we need to check
130
+         * whether the instance has already been installed
131
+         *
132
+         * @link https://github.com/owncloud/core/issues/21955
133
+         */
134
+        if($this->config->getSystemValue('installed', false)) {
135
+            $userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() :  null;
136
+            if(!is_null($userId)) {
137
+                $userLang = $this->config->getUserValue($userId, 'core', 'lang', null);
138
+            } else {
139
+                $userLang = null;
140
+            }
141
+        } else {
142
+            $userId = null;
143
+            $userLang = null;
144
+        }
145
+
146
+        if ($userLang) {
147
+            $this->requestLanguage = $userLang;
148
+            if ($this->languageExists($app, $userLang)) {
149
+                return $userLang;
150
+            }
151
+        }
152
+
153
+        try {
154
+            // Try to get the language from the Request
155
+            $lang = $this->getLanguageFromRequest($app);
156
+            if ($userId !== null && $app === null && !$userLang) {
157
+                $this->config->setUserValue($userId, 'core', 'lang', $lang);
158
+            }
159
+            return $lang;
160
+        } catch (LanguageNotFoundException $e) {
161
+            // Finding language from request failed fall back to default language
162
+            $defaultLanguage = $this->config->getSystemValue('default_language', false);
163
+            if ($defaultLanguage !== false && $this->languageExists($app, $defaultLanguage)) {
164
+                return $defaultLanguage;
165
+            }
166
+        }
167
+
168
+        // We could not find any language so fall back to english
169
+        return 'en';
170
+    }
171
+
172
+    /**
173
+     * Find all available languages for an app
174
+     *
175
+     * @param string|null $app App id or null for core
176
+     * @return array an array of available languages
177
+     */
178
+    public function findAvailableLanguages($app = null) {
179
+        $key = $app;
180
+        if ($key === null) {
181
+            $key = 'null';
182
+        }
183
+
184
+        // also works with null as key
185
+        if (!empty($this->availableLanguages[$key])) {
186
+            return $this->availableLanguages[$key];
187
+        }
188
+
189
+        $available = ['en']; //english is always available
190
+        $dir = $this->findL10nDir($app);
191
+        if (is_dir($dir)) {
192
+            $files = scandir($dir);
193
+            if ($files !== false) {
194
+                foreach ($files as $file) {
195
+                    if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
196
+                        $available[] = substr($file, 0, -5);
197
+                    }
198
+                }
199
+            }
200
+        }
201
+
202
+        // merge with translations from theme
203
+        $theme = $this->config->getSystemValue('theme');
204
+        if (!empty($theme)) {
205
+            $themeDir = $this->serverRoot . '/themes/' . $theme . substr($dir, strlen($this->serverRoot));
206
+
207
+            if (is_dir($themeDir)) {
208
+                $files = scandir($themeDir);
209
+                if ($files !== false) {
210
+                    foreach ($files as $file) {
211
+                        if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
212
+                            $available[] = substr($file, 0, -5);
213
+                        }
214
+                    }
215
+                }
216
+            }
217
+        }
218
+
219
+        $this->availableLanguages[$key] = $available;
220
+        return $available;
221
+    }
222
+
223
+    /**
224
+     * @param string|null $app App id or null for core
225
+     * @param string $lang
226
+     * @return bool
227
+     */
228
+    public function languageExists($app, $lang) {
229
+        if ($lang === 'en') {//english is always available
230
+            return true;
231
+        }
232
+
233
+        $languages = $this->findAvailableLanguages($app);
234
+        return array_search($lang, $languages) !== false;
235
+    }
236
+
237
+    /**
238
+     * @param string|null $app
239
+     * @return string
240
+     * @throws LanguageNotFoundException
241
+     */
242
+    private function getLanguageFromRequest($app) {
243
+        $header = $this->request->getHeader('ACCEPT_LANGUAGE');
244
+        if ($header) {
245
+            $available = $this->findAvailableLanguages($app);
246
+
247
+            // E.g. make sure that 'de' is before 'de_DE'.
248
+            sort($available);
249
+
250
+            $preferences = preg_split('/,\s*/', strtolower($header));
251
+            foreach ($preferences as $preference) {
252
+                list($preferred_language) = explode(';', $preference);
253
+                $preferred_language = str_replace('-', '_', $preferred_language);
254
+
255
+                foreach ($available as $available_language) {
256
+                    if ($preferred_language === strtolower($available_language)) {
257
+                        return $available_language;
258
+                    }
259
+                }
260
+
261
+                // Fallback from de_De to de
262
+                foreach ($available as $available_language) {
263
+                    if (substr($preferred_language, 0, 2) === $available_language) {
264
+                        return $available_language;
265
+                    }
266
+                }
267
+            }
268
+        }
269
+
270
+        throw new LanguageNotFoundException();
271
+    }
272
+
273
+    /**
274
+     * @param string|null $app App id or null for core
275
+     * @return string
276
+     */
277
+    public function setLanguageFromRequest($app = null) {
278
+
279
+        try {
280
+            $requestLanguage = $this->getLanguageFromRequest($app);
281
+        } catch (LanguageNotFoundException $e) {
282
+            $requestLanguage = 'en';
283
+        }
284
+
285
+        if ($app === null && !$this->requestLanguage) {
286
+            $this->requestLanguage = $requestLanguage;
287
+        }
288
+        return $requestLanguage;
289
+    }
290
+
291
+    /**
292
+     * Checks if $sub is a subdirectory of $parent
293
+     *
294
+     * @param string $sub
295
+     * @param string $parent
296
+     * @return bool
297
+     */
298
+    private function isSubDirectory($sub, $parent) {
299
+        // Check whether $sub contains no ".."
300
+        if(strpos($sub, '..') !== false) {
301
+            return false;
302
+        }
303
+
304
+        // Check whether $sub is a subdirectory of $parent
305
+        if (strpos($sub, $parent) === 0) {
306
+            return true;
307
+        }
308
+
309
+        return false;
310
+    }
311
+
312
+    /**
313
+     * Get a list of language files that should be loaded
314
+     *
315
+     * @param string $app
316
+     * @param string $lang
317
+     * @return string[]
318
+     */
319
+    // FIXME This method is only public, until OC_L10N does not need it anymore,
320
+    // FIXME This is also the reason, why it is not in the public interface
321
+    public function getL10nFilesForApp($app, $lang) {
322
+        $languageFiles = [];
323
+
324
+        $i18nDir = $this->findL10nDir($app);
325
+        $transFile = strip_tags($i18nDir) . strip_tags($lang) . '.json';
326
+
327
+        if (($this->isSubDirectory($transFile, $this->serverRoot . '/core/l10n/')
328
+                || $this->isSubDirectory($transFile, $this->serverRoot . '/lib/l10n/')
329
+                || $this->isSubDirectory($transFile, $this->serverRoot . '/settings/l10n/')
330
+                || $this->isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/')
331
+            )
332
+            && file_exists($transFile)) {
333
+            // load the translations file
334
+            $languageFiles[] = $transFile;
335
+        }
336
+
337
+        // merge with translations from theme
338
+        $theme = $this->config->getSystemValue('theme');
339
+        if (!empty($theme)) {
340
+            $transFile = $this->serverRoot . '/themes/' . $theme . substr($transFile, strlen($this->serverRoot));
341
+            if (file_exists($transFile)) {
342
+                $languageFiles[] = $transFile;
343
+            }
344
+        }
345
+
346
+        return $languageFiles;
347
+    }
348
+
349
+    /**
350
+     * find the l10n directory
351
+     *
352
+     * @param string $app App id or empty string for core
353
+     * @return string directory
354
+     */
355
+    protected function findL10nDir($app = null) {
356
+        if (in_array($app, ['core', 'lib', 'settings'])) {
357
+            if (file_exists($this->serverRoot . '/' . $app . '/l10n/')) {
358
+                return $this->serverRoot . '/' . $app . '/l10n/';
359
+            }
360
+        } else if ($app && \OC_App::getAppPath($app) !== false) {
361
+            // Check if the app is in the app folder
362
+            return \OC_App::getAppPath($app) . '/l10n/';
363
+        }
364
+        return $this->serverRoot . '/core/l10n/';
365
+    }
366
+
367
+
368
+    /**
369
+     * Creates a function from the plural string
370
+     *
371
+     * Parts of the code is copied from Habari:
372
+     * https://github.com/habari/system/blob/master/classes/locale.php
373
+     * @param string $string
374
+     * @return string
375
+     */
376
+    public function createPluralFunction($string) {
377
+        if (isset($this->pluralFunctions[$string])) {
378
+            return $this->pluralFunctions[$string];
379
+        }
380
+
381
+        if (preg_match( '/^\s*nplurals\s*=\s*(\d+)\s*;\s*plural=(.*)$/u', $string, $matches)) {
382
+            // sanitize
383
+            $nplurals = preg_replace( '/[^0-9]/', '', $matches[1] );
384
+            $plural = preg_replace( '#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2] );
385
+
386
+            $body = str_replace(
387
+                array( 'plural', 'n', '$n$plurals', ),
388
+                array( '$plural', '$n', '$nplurals', ),
389
+                'nplurals='. $nplurals . '; plural=' . $plural
390
+            );
391
+
392
+            // add parents
393
+            // important since PHP's ternary evaluates from left to right
394
+            $body .= ';';
395
+            $res = '';
396
+            $p = 0;
397
+            for($i = 0; $i < strlen($body); $i++) {
398
+                $ch = $body[$i];
399
+                switch ( $ch ) {
400
+                    case '?':
401
+                        $res .= ' ? (';
402
+                        $p++;
403
+                        break;
404
+                    case ':':
405
+                        $res .= ') : (';
406
+                        break;
407
+                    case ';':
408
+                        $res .= str_repeat( ')', $p ) . ';';
409
+                        $p = 0;
410
+                        break;
411
+                    default:
412
+                        $res .= $ch;
413
+                }
414
+            }
415
+
416
+            $body = $res . 'return ($plural>=$nplurals?$nplurals-1:$plural);';
417
+            $function = create_function('$n', $body);
418
+            $this->pluralFunctions[$string] = $function;
419
+            return $function;
420
+        } else {
421
+            // default: one plural form for all cases but n==1 (english)
422
+            $function = create_function(
423
+                '$n',
424
+                '$nplurals=2;$plural=($n==1?0:1);return ($plural>=$nplurals?$nplurals-1:$plural);'
425
+            );
426
+            $this->pluralFunctions[$string] = $function;
427
+            return $function;
428
+        }
429
+    }
430 430
 }
Please login to merge, or discard this patch.
Spacing   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -131,9 +131,9 @@  discard block
 block discarded – undo
131 131
 		 *
132 132
 		 * @link https://github.com/owncloud/core/issues/21955
133 133
 		 */
134
-		if($this->config->getSystemValue('installed', false)) {
135
-			$userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() :  null;
136
-			if(!is_null($userId)) {
134
+		if ($this->config->getSystemValue('installed', false)) {
135
+			$userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() : null;
136
+			if (!is_null($userId)) {
137 137
 				$userLang = $this->config->getUserValue($userId, 'core', 'lang', null);
138 138
 			} else {
139 139
 				$userLang = null;
@@ -202,7 +202,7 @@  discard block
 block discarded – undo
202 202
 		// merge with translations from theme
203 203
 		$theme = $this->config->getSystemValue('theme');
204 204
 		if (!empty($theme)) {
205
-			$themeDir = $this->serverRoot . '/themes/' . $theme . substr($dir, strlen($this->serverRoot));
205
+			$themeDir = $this->serverRoot.'/themes/'.$theme.substr($dir, strlen($this->serverRoot));
206 206
 
207 207
 			if (is_dir($themeDir)) {
208 208
 				$files = scandir($themeDir);
@@ -297,7 +297,7 @@  discard block
 block discarded – undo
297 297
 	 */
298 298
 	private function isSubDirectory($sub, $parent) {
299 299
 		// Check whether $sub contains no ".."
300
-		if(strpos($sub, '..') !== false) {
300
+		if (strpos($sub, '..') !== false) {
301 301
 			return false;
302 302
 		}
303 303
 
@@ -322,12 +322,12 @@  discard block
 block discarded – undo
322 322
 		$languageFiles = [];
323 323
 
324 324
 		$i18nDir = $this->findL10nDir($app);
325
-		$transFile = strip_tags($i18nDir) . strip_tags($lang) . '.json';
325
+		$transFile = strip_tags($i18nDir).strip_tags($lang).'.json';
326 326
 
327
-		if (($this->isSubDirectory($transFile, $this->serverRoot . '/core/l10n/')
328
-				|| $this->isSubDirectory($transFile, $this->serverRoot . '/lib/l10n/')
329
-				|| $this->isSubDirectory($transFile, $this->serverRoot . '/settings/l10n/')
330
-				|| $this->isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/')
327
+		if (($this->isSubDirectory($transFile, $this->serverRoot.'/core/l10n/')
328
+				|| $this->isSubDirectory($transFile, $this->serverRoot.'/lib/l10n/')
329
+				|| $this->isSubDirectory($transFile, $this->serverRoot.'/settings/l10n/')
330
+				|| $this->isSubDirectory($transFile, \OC_App::getAppPath($app).'/l10n/')
331 331
 			)
332 332
 			&& file_exists($transFile)) {
333 333
 			// load the translations file
@@ -337,7 +337,7 @@  discard block
 block discarded – undo
337 337
 		// merge with translations from theme
338 338
 		$theme = $this->config->getSystemValue('theme');
339 339
 		if (!empty($theme)) {
340
-			$transFile = $this->serverRoot . '/themes/' . $theme . substr($transFile, strlen($this->serverRoot));
340
+			$transFile = $this->serverRoot.'/themes/'.$theme.substr($transFile, strlen($this->serverRoot));
341 341
 			if (file_exists($transFile)) {
342 342
 				$languageFiles[] = $transFile;
343 343
 			}
@@ -354,14 +354,14 @@  discard block
 block discarded – undo
354 354
 	 */
355 355
 	protected function findL10nDir($app = null) {
356 356
 		if (in_array($app, ['core', 'lib', 'settings'])) {
357
-			if (file_exists($this->serverRoot . '/' . $app . '/l10n/')) {
358
-				return $this->serverRoot . '/' . $app . '/l10n/';
357
+			if (file_exists($this->serverRoot.'/'.$app.'/l10n/')) {
358
+				return $this->serverRoot.'/'.$app.'/l10n/';
359 359
 			}
360 360
 		} else if ($app && \OC_App::getAppPath($app) !== false) {
361 361
 			// Check if the app is in the app folder
362
-			return \OC_App::getAppPath($app) . '/l10n/';
362
+			return \OC_App::getAppPath($app).'/l10n/';
363 363
 		}
364
-		return $this->serverRoot . '/core/l10n/';
364
+		return $this->serverRoot.'/core/l10n/';
365 365
 	}
366 366
 
367 367
 
@@ -378,15 +378,15 @@  discard block
 block discarded – undo
378 378
 			return $this->pluralFunctions[$string];
379 379
 		}
380 380
 
381
-		if (preg_match( '/^\s*nplurals\s*=\s*(\d+)\s*;\s*plural=(.*)$/u', $string, $matches)) {
381
+		if (preg_match('/^\s*nplurals\s*=\s*(\d+)\s*;\s*plural=(.*)$/u', $string, $matches)) {
382 382
 			// sanitize
383
-			$nplurals = preg_replace( '/[^0-9]/', '', $matches[1] );
384
-			$plural = preg_replace( '#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2] );
383
+			$nplurals = preg_replace('/[^0-9]/', '', $matches[1]);
384
+			$plural = preg_replace('#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2]);
385 385
 
386 386
 			$body = str_replace(
387
-				array( 'plural', 'n', '$n$plurals', ),
388
-				array( '$plural', '$n', '$nplurals', ),
389
-				'nplurals='. $nplurals . '; plural=' . $plural
387
+				array('plural', 'n', '$n$plurals',),
388
+				array('$plural', '$n', '$nplurals',),
389
+				'nplurals='.$nplurals.'; plural='.$plural
390 390
 			);
391 391
 
392 392
 			// add parents
@@ -394,9 +394,9 @@  discard block
 block discarded – undo
394 394
 			$body .= ';';
395 395
 			$res = '';
396 396
 			$p = 0;
397
-			for($i = 0; $i < strlen($body); $i++) {
397
+			for ($i = 0; $i < strlen($body); $i++) {
398 398
 				$ch = $body[$i];
399
-				switch ( $ch ) {
399
+				switch ($ch) {
400 400
 					case '?':
401 401
 						$res .= ' ? (';
402 402
 						$p++;
@@ -405,7 +405,7 @@  discard block
 block discarded – undo
405 405
 						$res .= ') : (';
406 406
 						break;
407 407
 					case ';':
408
-						$res .= str_repeat( ')', $p ) . ';';
408
+						$res .= str_repeat(')', $p).';';
409 409
 						$p = 0;
410 410
 						break;
411 411
 					default:
@@ -413,7 +413,7 @@  discard block
 block discarded – undo
413 413
 				}
414 414
 			}
415 415
 
416
-			$body = $res . 'return ($plural>=$nplurals?$nplurals-1:$plural);';
416
+			$body = $res.'return ($plural>=$nplurals?$nplurals-1:$plural);';
417 417
 			$function = create_function('$n', $body);
418 418
 			$this->pluralFunctions[$string] = $function;
419 419
 			return $function;
Please login to merge, or discard this patch.
lib/private/Installer.php 4 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -45,7 +45,6 @@
 block discarded – undo
45 45
 use OC\App\CodeChecker\CodeChecker;
46 46
 use OC\App\CodeChecker\EmptyCheck;
47 47
 use OC\App\CodeChecker\PrivateCheck;
48
-use OC\Archive\Archive;
49 48
 use OC\Archive\TAR;
50 49
 use OC_App;
51 50
 use OC_DB;
Please login to merge, or discard this patch.
Indentation   +489 added lines, -489 removed lines patch added patch discarded remove patch
@@ -60,493 +60,493 @@
 block discarded – undo
60 60
  * This class provides the functionality needed to install, update and remove apps
61 61
  */
62 62
 class Installer {
63
-	/** @var AppFetcher */
64
-	private $appFetcher;
65
-	/** @var IClientService */
66
-	private $clientService;
67
-	/** @var ITempManager */
68
-	private $tempManager;
69
-	/** @var ILogger */
70
-	private $logger;
71
-
72
-	/**
73
-	 * @param AppFetcher $appFetcher
74
-	 * @param IClientService $clientService
75
-	 * @param ITempManager $tempManager
76
-	 * @param ILogger $logger
77
-	 */
78
-	public function __construct(AppFetcher $appFetcher,
79
-								IClientService $clientService,
80
-								ITempManager $tempManager,
81
-								ILogger $logger) {
82
-		$this->appFetcher = $appFetcher;
83
-		$this->clientService = $clientService;
84
-		$this->tempManager = $tempManager;
85
-		$this->logger = $logger;
86
-	}
87
-
88
-	/**
89
-	 * Installs an app that is located in one of the app folders already
90
-	 *
91
-	 * @param string $appId App to install
92
-	 * @throws \Exception
93
-	 * @return integer
94
-	 */
95
-	public function installApp($appId) {
96
-		$app = \OC_App::findAppInDirectories($appId);
97
-		if($app === false) {
98
-			throw new \Exception('App not found in any app directory');
99
-		}
100
-
101
-		$basedir = $app['path'].'/'.$appId;
102
-		$info = OC_App::getAppInfo($basedir.'/appinfo/info.xml', true);
103
-
104
-		//install the database
105
-		if(is_file($basedir.'/appinfo/database.xml')) {
106
-			if (\OC::$server->getAppConfig()->getValue($info['id'], 'installed_version') === null) {
107
-				OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml');
108
-			} else {
109
-				OC_DB::updateDbFromStructure($basedir.'/appinfo/database.xml');
110
-			}
111
-		}
112
-
113
-		\OC_App::setupBackgroundJobs($info['background-jobs']);
114
-
115
-		//run appinfo/install.php
116
-		if((!isset($data['noinstall']) or $data['noinstall']==false)) {
117
-			self::includeAppScript($basedir . '/appinfo/install.php');
118
-		}
119
-
120
-		$appData = OC_App::getAppInfo($appId);
121
-		OC_App::executeRepairSteps($appId, $appData['repair-steps']['install']);
122
-
123
-		//set the installed version
124
-		\OC::$server->getConfig()->setAppValue($info['id'], 'installed_version', OC_App::getAppVersion($info['id'], false));
125
-		\OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no');
126
-
127
-		//set remote/public handlers
128
-		foreach($info['remote'] as $name=>$path) {
129
-			\OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path);
130
-		}
131
-		foreach($info['public'] as $name=>$path) {
132
-			\OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path);
133
-		}
134
-
135
-		OC_App::setAppTypes($info['id']);
136
-
137
-		return $info['id'];
138
-	}
139
-
140
-	/**
141
-	 * @brief checks whether or not an app is installed
142
-	 * @param string $app app
143
-	 * @returns bool
144
-	 *
145
-	 * Checks whether or not an app is installed, i.e. registered in apps table.
146
-	 */
147
-	public static function isInstalled( $app ) {
148
-		return (\OC::$server->getConfig()->getAppValue($app, "installed_version", null) !== null);
149
-	}
150
-
151
-	/**
152
-	 * Updates the specified app from the appstore
153
-	 *
154
-	 * @param string $appId
155
-	 * @return bool
156
-	 */
157
-	public function updateAppstoreApp($appId) {
158
-		if(self::isUpdateAvailable($appId, $this->appFetcher)) {
159
-			try {
160
-				$this->downloadApp($appId);
161
-			} catch (\Exception $e) {
162
-				$this->logger->error($e->getMessage(), ['app' => 'core']);
163
-				return false;
164
-			}
165
-			return OC_App::updateApp($appId);
166
-		}
167
-
168
-		return false;
169
-	}
170
-
171
-	/**
172
-	 * Downloads an app and puts it into the app directory
173
-	 *
174
-	 * @param string $appId
175
-	 *
176
-	 * @throws \Exception If the installation was not successful
177
-	 */
178
-	public function downloadApp($appId) {
179
-		$appId = strtolower($appId);
180
-
181
-		$apps = $this->appFetcher->get();
182
-		foreach($apps as $app) {
183
-			if($app['id'] === $appId) {
184
-				// Load the certificate
185
-				$certificate = new X509();
186
-				$certificate->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
187
-				$loadedCertificate = $certificate->loadX509($app['certificate']);
188
-
189
-				// Verify if the certificate has been revoked
190
-				$crl = new X509();
191
-				$crl->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
192
-				$crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl'));
193
-				if($crl->validateSignature() !== true) {
194
-					throw new \Exception('Could not validate CRL signature');
195
-				}
196
-				$csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString();
197
-				$revoked = $crl->getRevoked($csn);
198
-				if ($revoked !== false) {
199
-					throw new \Exception(
200
-						sprintf(
201
-							'Certificate "%s" has been revoked',
202
-							$csn
203
-						)
204
-					);
205
-				}
206
-
207
-				// Verify if the certificate has been issued by the Nextcloud Code Authority CA
208
-				if($certificate->validateSignature() !== true) {
209
-					throw new \Exception(
210
-						sprintf(
211
-							'App with id %s has a certificate not issued by a trusted Code Signing Authority',
212
-							$appId
213
-						)
214
-					);
215
-				}
216
-
217
-				// Verify if the certificate is issued for the requested app id
218
-				$certInfo = openssl_x509_parse($app['certificate']);
219
-				if(!isset($certInfo['subject']['CN'])) {
220
-					throw new \Exception(
221
-						sprintf(
222
-							'App with id %s has a cert with no CN',
223
-							$appId
224
-						)
225
-					);
226
-				}
227
-				if($certInfo['subject']['CN'] !== $appId) {
228
-					throw new \Exception(
229
-						sprintf(
230
-							'App with id %s has a cert issued to %s',
231
-							$appId,
232
-							$certInfo['subject']['CN']
233
-						)
234
-					);
235
-				}
236
-
237
-				// Download the release
238
-				$tempFile = $this->tempManager->getTemporaryFile('.tar.gz');
239
-				$client = $this->clientService->newClient();
240
-				$client->get($app['releases'][0]['download'], ['save_to' => $tempFile]);
241
-
242
-				// Check if the signature actually matches the downloaded content
243
-				$certificate = openssl_get_publickey($app['certificate']);
244
-				$verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512);
245
-				openssl_free_key($certificate);
246
-
247
-				if($verified === true) {
248
-					// Seems to match, let's proceed
249
-					$extractDir = $this->tempManager->getTemporaryFolder();
250
-					$archive = new TAR($tempFile);
251
-
252
-					if($archive) {
253
-						$archive->extract($extractDir);
254
-						$allFiles = scandir($extractDir);
255
-						$folders = array_diff($allFiles, ['.', '..']);
256
-						$folders = array_values($folders);
257
-
258
-						if(count($folders) > 1) {
259
-							throw new \Exception(
260
-								sprintf(
261
-									'Extracted app %s has more than 1 folder',
262
-									$appId
263
-								)
264
-							);
265
-						}
266
-
267
-						// Check if appinfo/info.xml has the same app ID as well
268
-						$loadEntities = libxml_disable_entity_loader(false);
269
-						$xml = simplexml_load_file($extractDir . '/' . $folders[0] . '/appinfo/info.xml');
270
-						libxml_disable_entity_loader($loadEntities);
271
-						if((string)$xml->id !== $appId) {
272
-							throw new \Exception(
273
-								sprintf(
274
-									'App for id %s has a wrong app ID in info.xml: %s',
275
-									$appId,
276
-									(string)$xml->id
277
-								)
278
-							);
279
-						}
280
-
281
-						// Check if the version is lower than before
282
-						$currentVersion = OC_App::getAppVersion($appId);
283
-						$newVersion = (string)$xml->version;
284
-						if(version_compare($currentVersion, $newVersion) === 1) {
285
-							throw new \Exception(
286
-								sprintf(
287
-									'App for id %s has version %s and tried to update to lower version %s',
288
-									$appId,
289
-									$currentVersion,
290
-									$newVersion
291
-								)
292
-							);
293
-						}
294
-
295
-						$baseDir = OC_App::getInstallPath() . '/' . $appId;
296
-						// Remove old app with the ID if existent
297
-						OC_Helper::rmdirr($baseDir);
298
-						// Move to app folder
299
-						if(@mkdir($baseDir)) {
300
-							$extractDir .= '/' . $folders[0];
301
-							OC_Helper::copyr($extractDir, $baseDir);
302
-						}
303
-						OC_Helper::copyr($extractDir, $baseDir);
304
-						OC_Helper::rmdirr($extractDir);
305
-						return;
306
-					} else {
307
-						throw new \Exception(
308
-							sprintf(
309
-								'Could not extract app with ID %s to %s',
310
-								$appId,
311
-								$extractDir
312
-							)
313
-						);
314
-					}
315
-				} else {
316
-					// Signature does not match
317
-					throw new \Exception(
318
-						sprintf(
319
-							'App with id %s has invalid signature',
320
-							$appId
321
-						)
322
-					);
323
-				}
324
-			}
325
-		}
326
-
327
-		throw new \Exception(
328
-			sprintf(
329
-				'Could not download app %s',
330
-				$appId
331
-			)
332
-		);
333
-	}
334
-
335
-	/**
336
-	 * Check if an update for the app is available
337
-	 *
338
-	 * @param string $appId
339
-	 * @param AppFetcher $appFetcher
340
-	 * @return string|false false or the version number of the update
341
-	 */
342
-	public static function isUpdateAvailable($appId,
343
-									  AppFetcher $appFetcher) {
344
-		static $isInstanceReadyForUpdates = null;
345
-
346
-		if ($isInstanceReadyForUpdates === null) {
347
-			$installPath = OC_App::getInstallPath();
348
-			if ($installPath === false || $installPath === null) {
349
-				$isInstanceReadyForUpdates = false;
350
-			} else {
351
-				$isInstanceReadyForUpdates = true;
352
-			}
353
-		}
354
-
355
-		if ($isInstanceReadyForUpdates === false) {
356
-			return false;
357
-		}
358
-
359
-		$apps = $appFetcher->get();
360
-		foreach($apps as $app) {
361
-			if($app['id'] === $appId) {
362
-				$currentVersion = OC_App::getAppVersion($appId);
363
-				$newestVersion = $app['releases'][0]['version'];
364
-				if (version_compare($newestVersion, $currentVersion, '>')) {
365
-					return $newestVersion;
366
-				} else {
367
-					return false;
368
-				}
369
-			}
370
-		}
371
-
372
-		return false;
373
-	}
374
-
375
-	/**
376
-	 * Check if app is already downloaded
377
-	 * @param string $name name of the application to remove
378
-	 * @return boolean
379
-	 *
380
-	 * The function will check if the app is already downloaded in the apps repository
381
-	 */
382
-	public function isDownloaded($name) {
383
-		foreach(\OC::$APPSROOTS as $dir) {
384
-			$dirToTest  = $dir['path'];
385
-			$dirToTest .= '/';
386
-			$dirToTest .= $name;
387
-			$dirToTest .= '/';
388
-
389
-			if (is_dir($dirToTest)) {
390
-				return true;
391
-			}
392
-		}
393
-
394
-		return false;
395
-	}
396
-
397
-	/**
398
-	 * Removes an app
399
-	 * @param string $appId ID of the application to remove
400
-	 * @return boolean
401
-	 *
402
-	 *
403
-	 * This function works as follows
404
-	 *   -# call uninstall repair steps
405
-	 *   -# removing the files
406
-	 *
407
-	 * The function will not delete preferences, tables and the configuration,
408
-	 * this has to be done by the function oc_app_uninstall().
409
-	 */
410
-	public function removeApp($appId) {
411
-		if($this->isDownloaded( $appId )) {
412
-			$appDir = OC_App::getInstallPath() . '/' . $appId;
413
-			OC_Helper::rmdirr($appDir);
414
-			return true;
415
-		}else{
416
-			\OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', \OCP\Util::ERROR);
417
-
418
-			return false;
419
-		}
420
-
421
-	}
422
-
423
-	/**
424
-	 * Installs shipped apps
425
-	 *
426
-	 * This function installs all apps found in the 'apps' directory that should be enabled by default;
427
-	 * @param bool $softErrors When updating we ignore errors and simply log them, better to have a
428
-	 *                         working ownCloud at the end instead of an aborted update.
429
-	 * @return array Array of error messages (appid => Exception)
430
-	 */
431
-	public static function installShippedApps($softErrors = false) {
432
-		$errors = [];
433
-		foreach(\OC::$APPSROOTS as $app_dir) {
434
-			if($dir = opendir( $app_dir['path'] )) {
435
-				while( false !== ( $filename = readdir( $dir ))) {
436
-					if( substr( $filename, 0, 1 ) != '.' and is_dir($app_dir['path']."/$filename") ) {
437
-						if( file_exists( $app_dir['path']."/$filename/appinfo/info.xml" )) {
438
-							if(!Installer::isInstalled($filename)) {
439
-								$info=OC_App::getAppInfo($filename);
440
-								$enabled = isset($info['default_enable']);
441
-								if (($enabled || in_array($filename, \OC::$server->getAppManager()->getAlwaysEnabledApps()))
442
-									  && \OC::$server->getConfig()->getAppValue($filename, 'enabled') !== 'no') {
443
-									if ($softErrors) {
444
-										try {
445
-											Installer::installShippedApp($filename);
446
-										} catch (HintException $e) {
447
-											if ($e->getPrevious() instanceof TableExistsException) {
448
-												$errors[$filename] = $e;
449
-												continue;
450
-											}
451
-											throw $e;
452
-										}
453
-									} else {
454
-										Installer::installShippedApp($filename);
455
-									}
456
-									\OC::$server->getConfig()->setAppValue($filename, 'enabled', 'yes');
457
-								}
458
-							}
459
-						}
460
-					}
461
-				}
462
-				closedir( $dir );
463
-			}
464
-		}
465
-
466
-		return $errors;
467
-	}
468
-
469
-	/**
470
-	 * install an app already placed in the app folder
471
-	 * @param string $app id of the app to install
472
-	 * @return integer
473
-	 */
474
-	public static function installShippedApp($app) {
475
-		//install the database
476
-		$appPath = OC_App::getAppPath($app);
477
-		if(is_file("$appPath/appinfo/database.xml")) {
478
-			try {
479
-				OC_DB::createDbFromStructure("$appPath/appinfo/database.xml");
480
-			} catch (TableExistsException $e) {
481
-				throw new HintException(
482
-					'Failed to enable app ' . $app,
483
-					'Please ask for help via one of our <a href="https://nextcloud.com/support/" target="_blank" rel="noreferrer">support channels</a>.',
484
-					0, $e
485
-				);
486
-			}
487
-		}
488
-
489
-		//run appinfo/install.php
490
-		\OC_App::registerAutoloading($app, $appPath);
491
-		self::includeAppScript("$appPath/appinfo/install.php");
492
-
493
-		$info = OC_App::getAppInfo($app);
494
-		if (is_null($info)) {
495
-			return false;
496
-		}
497
-		\OC_App::setupBackgroundJobs($info['background-jobs']);
498
-
499
-		OC_App::executeRepairSteps($app, $info['repair-steps']['install']);
500
-
501
-		$config = \OC::$server->getConfig();
502
-
503
-		$config->setAppValue($app, 'installed_version', OC_App::getAppVersion($app));
504
-		if (array_key_exists('ocsid', $info)) {
505
-			$config->setAppValue($app, 'ocsid', $info['ocsid']);
506
-		}
507
-
508
-		//set remote/public handlers
509
-		foreach($info['remote'] as $name=>$path) {
510
-			$config->setAppValue('core', 'remote_'.$name, $app.'/'.$path);
511
-		}
512
-		foreach($info['public'] as $name=>$path) {
513
-			$config->setAppValue('core', 'public_'.$name, $app.'/'.$path);
514
-		}
515
-
516
-		OC_App::setAppTypes($info['id']);
517
-
518
-		if(isset($info['settings']) && is_array($info['settings'])) {
519
-			// requires that autoloading was registered for the app,
520
-			// as happens before running the install.php some lines above
521
-			\OC::$server->getSettingsManager()->setupSettings($info['settings']);
522
-		}
523
-
524
-		return $info['id'];
525
-	}
526
-
527
-	/**
528
-	 * check the code of an app with some static code checks
529
-	 * @param string $folder the folder of the app to check
530
-	 * @return boolean true for app is o.k. and false for app is not o.k.
531
-	 */
532
-	public static function checkCode($folder) {
533
-		// is the code checker enabled?
534
-		if(!\OC::$server->getConfig()->getSystemValue('appcodechecker', false)) {
535
-			return true;
536
-		}
537
-
538
-		$codeChecker = new CodeChecker(new PrivateCheck(new EmptyCheck()));
539
-		$errors = $codeChecker->analyseFolder(basename($folder), $folder);
540
-
541
-		return empty($errors);
542
-	}
543
-
544
-	/**
545
-	 * @param string $script
546
-	 */
547
-	private static function includeAppScript($script) {
548
-		if ( file_exists($script) ){
549
-			include $script;
550
-		}
551
-	}
63
+    /** @var AppFetcher */
64
+    private $appFetcher;
65
+    /** @var IClientService */
66
+    private $clientService;
67
+    /** @var ITempManager */
68
+    private $tempManager;
69
+    /** @var ILogger */
70
+    private $logger;
71
+
72
+    /**
73
+     * @param AppFetcher $appFetcher
74
+     * @param IClientService $clientService
75
+     * @param ITempManager $tempManager
76
+     * @param ILogger $logger
77
+     */
78
+    public function __construct(AppFetcher $appFetcher,
79
+                                IClientService $clientService,
80
+                                ITempManager $tempManager,
81
+                                ILogger $logger) {
82
+        $this->appFetcher = $appFetcher;
83
+        $this->clientService = $clientService;
84
+        $this->tempManager = $tempManager;
85
+        $this->logger = $logger;
86
+    }
87
+
88
+    /**
89
+     * Installs an app that is located in one of the app folders already
90
+     *
91
+     * @param string $appId App to install
92
+     * @throws \Exception
93
+     * @return integer
94
+     */
95
+    public function installApp($appId) {
96
+        $app = \OC_App::findAppInDirectories($appId);
97
+        if($app === false) {
98
+            throw new \Exception('App not found in any app directory');
99
+        }
100
+
101
+        $basedir = $app['path'].'/'.$appId;
102
+        $info = OC_App::getAppInfo($basedir.'/appinfo/info.xml', true);
103
+
104
+        //install the database
105
+        if(is_file($basedir.'/appinfo/database.xml')) {
106
+            if (\OC::$server->getAppConfig()->getValue($info['id'], 'installed_version') === null) {
107
+                OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml');
108
+            } else {
109
+                OC_DB::updateDbFromStructure($basedir.'/appinfo/database.xml');
110
+            }
111
+        }
112
+
113
+        \OC_App::setupBackgroundJobs($info['background-jobs']);
114
+
115
+        //run appinfo/install.php
116
+        if((!isset($data['noinstall']) or $data['noinstall']==false)) {
117
+            self::includeAppScript($basedir . '/appinfo/install.php');
118
+        }
119
+
120
+        $appData = OC_App::getAppInfo($appId);
121
+        OC_App::executeRepairSteps($appId, $appData['repair-steps']['install']);
122
+
123
+        //set the installed version
124
+        \OC::$server->getConfig()->setAppValue($info['id'], 'installed_version', OC_App::getAppVersion($info['id'], false));
125
+        \OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no');
126
+
127
+        //set remote/public handlers
128
+        foreach($info['remote'] as $name=>$path) {
129
+            \OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path);
130
+        }
131
+        foreach($info['public'] as $name=>$path) {
132
+            \OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path);
133
+        }
134
+
135
+        OC_App::setAppTypes($info['id']);
136
+
137
+        return $info['id'];
138
+    }
139
+
140
+    /**
141
+     * @brief checks whether or not an app is installed
142
+     * @param string $app app
143
+     * @returns bool
144
+     *
145
+     * Checks whether or not an app is installed, i.e. registered in apps table.
146
+     */
147
+    public static function isInstalled( $app ) {
148
+        return (\OC::$server->getConfig()->getAppValue($app, "installed_version", null) !== null);
149
+    }
150
+
151
+    /**
152
+     * Updates the specified app from the appstore
153
+     *
154
+     * @param string $appId
155
+     * @return bool
156
+     */
157
+    public function updateAppstoreApp($appId) {
158
+        if(self::isUpdateAvailable($appId, $this->appFetcher)) {
159
+            try {
160
+                $this->downloadApp($appId);
161
+            } catch (\Exception $e) {
162
+                $this->logger->error($e->getMessage(), ['app' => 'core']);
163
+                return false;
164
+            }
165
+            return OC_App::updateApp($appId);
166
+        }
167
+
168
+        return false;
169
+    }
170
+
171
+    /**
172
+     * Downloads an app and puts it into the app directory
173
+     *
174
+     * @param string $appId
175
+     *
176
+     * @throws \Exception If the installation was not successful
177
+     */
178
+    public function downloadApp($appId) {
179
+        $appId = strtolower($appId);
180
+
181
+        $apps = $this->appFetcher->get();
182
+        foreach($apps as $app) {
183
+            if($app['id'] === $appId) {
184
+                // Load the certificate
185
+                $certificate = new X509();
186
+                $certificate->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
187
+                $loadedCertificate = $certificate->loadX509($app['certificate']);
188
+
189
+                // Verify if the certificate has been revoked
190
+                $crl = new X509();
191
+                $crl->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
192
+                $crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl'));
193
+                if($crl->validateSignature() !== true) {
194
+                    throw new \Exception('Could not validate CRL signature');
195
+                }
196
+                $csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString();
197
+                $revoked = $crl->getRevoked($csn);
198
+                if ($revoked !== false) {
199
+                    throw new \Exception(
200
+                        sprintf(
201
+                            'Certificate "%s" has been revoked',
202
+                            $csn
203
+                        )
204
+                    );
205
+                }
206
+
207
+                // Verify if the certificate has been issued by the Nextcloud Code Authority CA
208
+                if($certificate->validateSignature() !== true) {
209
+                    throw new \Exception(
210
+                        sprintf(
211
+                            'App with id %s has a certificate not issued by a trusted Code Signing Authority',
212
+                            $appId
213
+                        )
214
+                    );
215
+                }
216
+
217
+                // Verify if the certificate is issued for the requested app id
218
+                $certInfo = openssl_x509_parse($app['certificate']);
219
+                if(!isset($certInfo['subject']['CN'])) {
220
+                    throw new \Exception(
221
+                        sprintf(
222
+                            'App with id %s has a cert with no CN',
223
+                            $appId
224
+                        )
225
+                    );
226
+                }
227
+                if($certInfo['subject']['CN'] !== $appId) {
228
+                    throw new \Exception(
229
+                        sprintf(
230
+                            'App with id %s has a cert issued to %s',
231
+                            $appId,
232
+                            $certInfo['subject']['CN']
233
+                        )
234
+                    );
235
+                }
236
+
237
+                // Download the release
238
+                $tempFile = $this->tempManager->getTemporaryFile('.tar.gz');
239
+                $client = $this->clientService->newClient();
240
+                $client->get($app['releases'][0]['download'], ['save_to' => $tempFile]);
241
+
242
+                // Check if the signature actually matches the downloaded content
243
+                $certificate = openssl_get_publickey($app['certificate']);
244
+                $verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512);
245
+                openssl_free_key($certificate);
246
+
247
+                if($verified === true) {
248
+                    // Seems to match, let's proceed
249
+                    $extractDir = $this->tempManager->getTemporaryFolder();
250
+                    $archive = new TAR($tempFile);
251
+
252
+                    if($archive) {
253
+                        $archive->extract($extractDir);
254
+                        $allFiles = scandir($extractDir);
255
+                        $folders = array_diff($allFiles, ['.', '..']);
256
+                        $folders = array_values($folders);
257
+
258
+                        if(count($folders) > 1) {
259
+                            throw new \Exception(
260
+                                sprintf(
261
+                                    'Extracted app %s has more than 1 folder',
262
+                                    $appId
263
+                                )
264
+                            );
265
+                        }
266
+
267
+                        // Check if appinfo/info.xml has the same app ID as well
268
+                        $loadEntities = libxml_disable_entity_loader(false);
269
+                        $xml = simplexml_load_file($extractDir . '/' . $folders[0] . '/appinfo/info.xml');
270
+                        libxml_disable_entity_loader($loadEntities);
271
+                        if((string)$xml->id !== $appId) {
272
+                            throw new \Exception(
273
+                                sprintf(
274
+                                    'App for id %s has a wrong app ID in info.xml: %s',
275
+                                    $appId,
276
+                                    (string)$xml->id
277
+                                )
278
+                            );
279
+                        }
280
+
281
+                        // Check if the version is lower than before
282
+                        $currentVersion = OC_App::getAppVersion($appId);
283
+                        $newVersion = (string)$xml->version;
284
+                        if(version_compare($currentVersion, $newVersion) === 1) {
285
+                            throw new \Exception(
286
+                                sprintf(
287
+                                    'App for id %s has version %s and tried to update to lower version %s',
288
+                                    $appId,
289
+                                    $currentVersion,
290
+                                    $newVersion
291
+                                )
292
+                            );
293
+                        }
294
+
295
+                        $baseDir = OC_App::getInstallPath() . '/' . $appId;
296
+                        // Remove old app with the ID if existent
297
+                        OC_Helper::rmdirr($baseDir);
298
+                        // Move to app folder
299
+                        if(@mkdir($baseDir)) {
300
+                            $extractDir .= '/' . $folders[0];
301
+                            OC_Helper::copyr($extractDir, $baseDir);
302
+                        }
303
+                        OC_Helper::copyr($extractDir, $baseDir);
304
+                        OC_Helper::rmdirr($extractDir);
305
+                        return;
306
+                    } else {
307
+                        throw new \Exception(
308
+                            sprintf(
309
+                                'Could not extract app with ID %s to %s',
310
+                                $appId,
311
+                                $extractDir
312
+                            )
313
+                        );
314
+                    }
315
+                } else {
316
+                    // Signature does not match
317
+                    throw new \Exception(
318
+                        sprintf(
319
+                            'App with id %s has invalid signature',
320
+                            $appId
321
+                        )
322
+                    );
323
+                }
324
+            }
325
+        }
326
+
327
+        throw new \Exception(
328
+            sprintf(
329
+                'Could not download app %s',
330
+                $appId
331
+            )
332
+        );
333
+    }
334
+
335
+    /**
336
+     * Check if an update for the app is available
337
+     *
338
+     * @param string $appId
339
+     * @param AppFetcher $appFetcher
340
+     * @return string|false false or the version number of the update
341
+     */
342
+    public static function isUpdateAvailable($appId,
343
+                                        AppFetcher $appFetcher) {
344
+        static $isInstanceReadyForUpdates = null;
345
+
346
+        if ($isInstanceReadyForUpdates === null) {
347
+            $installPath = OC_App::getInstallPath();
348
+            if ($installPath === false || $installPath === null) {
349
+                $isInstanceReadyForUpdates = false;
350
+            } else {
351
+                $isInstanceReadyForUpdates = true;
352
+            }
353
+        }
354
+
355
+        if ($isInstanceReadyForUpdates === false) {
356
+            return false;
357
+        }
358
+
359
+        $apps = $appFetcher->get();
360
+        foreach($apps as $app) {
361
+            if($app['id'] === $appId) {
362
+                $currentVersion = OC_App::getAppVersion($appId);
363
+                $newestVersion = $app['releases'][0]['version'];
364
+                if (version_compare($newestVersion, $currentVersion, '>')) {
365
+                    return $newestVersion;
366
+                } else {
367
+                    return false;
368
+                }
369
+            }
370
+        }
371
+
372
+        return false;
373
+    }
374
+
375
+    /**
376
+     * Check if app is already downloaded
377
+     * @param string $name name of the application to remove
378
+     * @return boolean
379
+     *
380
+     * The function will check if the app is already downloaded in the apps repository
381
+     */
382
+    public function isDownloaded($name) {
383
+        foreach(\OC::$APPSROOTS as $dir) {
384
+            $dirToTest  = $dir['path'];
385
+            $dirToTest .= '/';
386
+            $dirToTest .= $name;
387
+            $dirToTest .= '/';
388
+
389
+            if (is_dir($dirToTest)) {
390
+                return true;
391
+            }
392
+        }
393
+
394
+        return false;
395
+    }
396
+
397
+    /**
398
+     * Removes an app
399
+     * @param string $appId ID of the application to remove
400
+     * @return boolean
401
+     *
402
+     *
403
+     * This function works as follows
404
+     *   -# call uninstall repair steps
405
+     *   -# removing the files
406
+     *
407
+     * The function will not delete preferences, tables and the configuration,
408
+     * this has to be done by the function oc_app_uninstall().
409
+     */
410
+    public function removeApp($appId) {
411
+        if($this->isDownloaded( $appId )) {
412
+            $appDir = OC_App::getInstallPath() . '/' . $appId;
413
+            OC_Helper::rmdirr($appDir);
414
+            return true;
415
+        }else{
416
+            \OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', \OCP\Util::ERROR);
417
+
418
+            return false;
419
+        }
420
+
421
+    }
422
+
423
+    /**
424
+     * Installs shipped apps
425
+     *
426
+     * This function installs all apps found in the 'apps' directory that should be enabled by default;
427
+     * @param bool $softErrors When updating we ignore errors and simply log them, better to have a
428
+     *                         working ownCloud at the end instead of an aborted update.
429
+     * @return array Array of error messages (appid => Exception)
430
+     */
431
+    public static function installShippedApps($softErrors = false) {
432
+        $errors = [];
433
+        foreach(\OC::$APPSROOTS as $app_dir) {
434
+            if($dir = opendir( $app_dir['path'] )) {
435
+                while( false !== ( $filename = readdir( $dir ))) {
436
+                    if( substr( $filename, 0, 1 ) != '.' and is_dir($app_dir['path']."/$filename") ) {
437
+                        if( file_exists( $app_dir['path']."/$filename/appinfo/info.xml" )) {
438
+                            if(!Installer::isInstalled($filename)) {
439
+                                $info=OC_App::getAppInfo($filename);
440
+                                $enabled = isset($info['default_enable']);
441
+                                if (($enabled || in_array($filename, \OC::$server->getAppManager()->getAlwaysEnabledApps()))
442
+                                      && \OC::$server->getConfig()->getAppValue($filename, 'enabled') !== 'no') {
443
+                                    if ($softErrors) {
444
+                                        try {
445
+                                            Installer::installShippedApp($filename);
446
+                                        } catch (HintException $e) {
447
+                                            if ($e->getPrevious() instanceof TableExistsException) {
448
+                                                $errors[$filename] = $e;
449
+                                                continue;
450
+                                            }
451
+                                            throw $e;
452
+                                        }
453
+                                    } else {
454
+                                        Installer::installShippedApp($filename);
455
+                                    }
456
+                                    \OC::$server->getConfig()->setAppValue($filename, 'enabled', 'yes');
457
+                                }
458
+                            }
459
+                        }
460
+                    }
461
+                }
462
+                closedir( $dir );
463
+            }
464
+        }
465
+
466
+        return $errors;
467
+    }
468
+
469
+    /**
470
+     * install an app already placed in the app folder
471
+     * @param string $app id of the app to install
472
+     * @return integer
473
+     */
474
+    public static function installShippedApp($app) {
475
+        //install the database
476
+        $appPath = OC_App::getAppPath($app);
477
+        if(is_file("$appPath/appinfo/database.xml")) {
478
+            try {
479
+                OC_DB::createDbFromStructure("$appPath/appinfo/database.xml");
480
+            } catch (TableExistsException $e) {
481
+                throw new HintException(
482
+                    'Failed to enable app ' . $app,
483
+                    'Please ask for help via one of our <a href="https://nextcloud.com/support/" target="_blank" rel="noreferrer">support channels</a>.',
484
+                    0, $e
485
+                );
486
+            }
487
+        }
488
+
489
+        //run appinfo/install.php
490
+        \OC_App::registerAutoloading($app, $appPath);
491
+        self::includeAppScript("$appPath/appinfo/install.php");
492
+
493
+        $info = OC_App::getAppInfo($app);
494
+        if (is_null($info)) {
495
+            return false;
496
+        }
497
+        \OC_App::setupBackgroundJobs($info['background-jobs']);
498
+
499
+        OC_App::executeRepairSteps($app, $info['repair-steps']['install']);
500
+
501
+        $config = \OC::$server->getConfig();
502
+
503
+        $config->setAppValue($app, 'installed_version', OC_App::getAppVersion($app));
504
+        if (array_key_exists('ocsid', $info)) {
505
+            $config->setAppValue($app, 'ocsid', $info['ocsid']);
506
+        }
507
+
508
+        //set remote/public handlers
509
+        foreach($info['remote'] as $name=>$path) {
510
+            $config->setAppValue('core', 'remote_'.$name, $app.'/'.$path);
511
+        }
512
+        foreach($info['public'] as $name=>$path) {
513
+            $config->setAppValue('core', 'public_'.$name, $app.'/'.$path);
514
+        }
515
+
516
+        OC_App::setAppTypes($info['id']);
517
+
518
+        if(isset($info['settings']) && is_array($info['settings'])) {
519
+            // requires that autoloading was registered for the app,
520
+            // as happens before running the install.php some lines above
521
+            \OC::$server->getSettingsManager()->setupSettings($info['settings']);
522
+        }
523
+
524
+        return $info['id'];
525
+    }
526
+
527
+    /**
528
+     * check the code of an app with some static code checks
529
+     * @param string $folder the folder of the app to check
530
+     * @return boolean true for app is o.k. and false for app is not o.k.
531
+     */
532
+    public static function checkCode($folder) {
533
+        // is the code checker enabled?
534
+        if(!\OC::$server->getConfig()->getSystemValue('appcodechecker', false)) {
535
+            return true;
536
+        }
537
+
538
+        $codeChecker = new CodeChecker(new PrivateCheck(new EmptyCheck()));
539
+        $errors = $codeChecker->analyseFolder(basename($folder), $folder);
540
+
541
+        return empty($errors);
542
+    }
543
+
544
+    /**
545
+     * @param string $script
546
+     */
547
+    private static function includeAppScript($script) {
548
+        if ( file_exists($script) ){
549
+            include $script;
550
+        }
551
+    }
552 552
 }
Please login to merge, or discard this patch.
Spacing   +50 added lines, -50 removed lines patch added patch discarded remove patch
@@ -94,7 +94,7 @@  discard block
 block discarded – undo
94 94
 	 */
95 95
 	public function installApp($appId) {
96 96
 		$app = \OC_App::findAppInDirectories($appId);
97
-		if($app === false) {
97
+		if ($app === false) {
98 98
 			throw new \Exception('App not found in any app directory');
99 99
 		}
100 100
 
@@ -102,7 +102,7 @@  discard block
 block discarded – undo
102 102
 		$info = OC_App::getAppInfo($basedir.'/appinfo/info.xml', true);
103 103
 
104 104
 		//install the database
105
-		if(is_file($basedir.'/appinfo/database.xml')) {
105
+		if (is_file($basedir.'/appinfo/database.xml')) {
106 106
 			if (\OC::$server->getAppConfig()->getValue($info['id'], 'installed_version') === null) {
107 107
 				OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml');
108 108
 			} else {
@@ -113,8 +113,8 @@  discard block
 block discarded – undo
113 113
 		\OC_App::setupBackgroundJobs($info['background-jobs']);
114 114
 
115 115
 		//run appinfo/install.php
116
-		if((!isset($data['noinstall']) or $data['noinstall']==false)) {
117
-			self::includeAppScript($basedir . '/appinfo/install.php');
116
+		if ((!isset($data['noinstall']) or $data['noinstall'] == false)) {
117
+			self::includeAppScript($basedir.'/appinfo/install.php');
118 118
 		}
119 119
 
120 120
 		$appData = OC_App::getAppInfo($appId);
@@ -125,10 +125,10 @@  discard block
 block discarded – undo
125 125
 		\OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no');
126 126
 
127 127
 		//set remote/public handlers
128
-		foreach($info['remote'] as $name=>$path) {
128
+		foreach ($info['remote'] as $name=>$path) {
129 129
 			\OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path);
130 130
 		}
131
-		foreach($info['public'] as $name=>$path) {
131
+		foreach ($info['public'] as $name=>$path) {
132 132
 			\OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path);
133 133
 		}
134 134
 
@@ -144,7 +144,7 @@  discard block
 block discarded – undo
144 144
 	 *
145 145
 	 * Checks whether or not an app is installed, i.e. registered in apps table.
146 146
 	 */
147
-	public static function isInstalled( $app ) {
147
+	public static function isInstalled($app) {
148 148
 		return (\OC::$server->getConfig()->getAppValue($app, "installed_version", null) !== null);
149 149
 	}
150 150
 
@@ -155,7 +155,7 @@  discard block
 block discarded – undo
155 155
 	 * @return bool
156 156
 	 */
157 157
 	public function updateAppstoreApp($appId) {
158
-		if(self::isUpdateAvailable($appId, $this->appFetcher)) {
158
+		if (self::isUpdateAvailable($appId, $this->appFetcher)) {
159 159
 			try {
160 160
 				$this->downloadApp($appId);
161 161
 			} catch (\Exception $e) {
@@ -179,18 +179,18 @@  discard block
 block discarded – undo
179 179
 		$appId = strtolower($appId);
180 180
 
181 181
 		$apps = $this->appFetcher->get();
182
-		foreach($apps as $app) {
183
-			if($app['id'] === $appId) {
182
+		foreach ($apps as $app) {
183
+			if ($app['id'] === $appId) {
184 184
 				// Load the certificate
185 185
 				$certificate = new X509();
186
-				$certificate->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
186
+				$certificate->loadCA(file_get_contents(__DIR__.'/../../resources/codesigning/root.crt'));
187 187
 				$loadedCertificate = $certificate->loadX509($app['certificate']);
188 188
 
189 189
 				// Verify if the certificate has been revoked
190 190
 				$crl = new X509();
191
-				$crl->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
192
-				$crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl'));
193
-				if($crl->validateSignature() !== true) {
191
+				$crl->loadCA(file_get_contents(__DIR__.'/../../resources/codesigning/root.crt'));
192
+				$crl->loadCRL(file_get_contents(__DIR__.'/../../resources/codesigning/root.crl'));
193
+				if ($crl->validateSignature() !== true) {
194 194
 					throw new \Exception('Could not validate CRL signature');
195 195
 				}
196 196
 				$csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString();
@@ -205,7 +205,7 @@  discard block
 block discarded – undo
205 205
 				}
206 206
 
207 207
 				// Verify if the certificate has been issued by the Nextcloud Code Authority CA
208
-				if($certificate->validateSignature() !== true) {
208
+				if ($certificate->validateSignature() !== true) {
209 209
 					throw new \Exception(
210 210
 						sprintf(
211 211
 							'App with id %s has a certificate not issued by a trusted Code Signing Authority',
@@ -216,7 +216,7 @@  discard block
 block discarded – undo
216 216
 
217 217
 				// Verify if the certificate is issued for the requested app id
218 218
 				$certInfo = openssl_x509_parse($app['certificate']);
219
-				if(!isset($certInfo['subject']['CN'])) {
219
+				if (!isset($certInfo['subject']['CN'])) {
220 220
 					throw new \Exception(
221 221
 						sprintf(
222 222
 							'App with id %s has a cert with no CN',
@@ -224,7 +224,7 @@  discard block
 block discarded – undo
224 224
 						)
225 225
 					);
226 226
 				}
227
-				if($certInfo['subject']['CN'] !== $appId) {
227
+				if ($certInfo['subject']['CN'] !== $appId) {
228 228
 					throw new \Exception(
229 229
 						sprintf(
230 230
 							'App with id %s has a cert issued to %s',
@@ -241,21 +241,21 @@  discard block
 block discarded – undo
241 241
 
242 242
 				// Check if the signature actually matches the downloaded content
243 243
 				$certificate = openssl_get_publickey($app['certificate']);
244
-				$verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512);
244
+				$verified = (bool) openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512);
245 245
 				openssl_free_key($certificate);
246 246
 
247
-				if($verified === true) {
247
+				if ($verified === true) {
248 248
 					// Seems to match, let's proceed
249 249
 					$extractDir = $this->tempManager->getTemporaryFolder();
250 250
 					$archive = new TAR($tempFile);
251 251
 
252
-					if($archive) {
252
+					if ($archive) {
253 253
 						$archive->extract($extractDir);
254 254
 						$allFiles = scandir($extractDir);
255 255
 						$folders = array_diff($allFiles, ['.', '..']);
256 256
 						$folders = array_values($folders);
257 257
 
258
-						if(count($folders) > 1) {
258
+						if (count($folders) > 1) {
259 259
 							throw new \Exception(
260 260
 								sprintf(
261 261
 									'Extracted app %s has more than 1 folder',
@@ -266,22 +266,22 @@  discard block
 block discarded – undo
266 266
 
267 267
 						// Check if appinfo/info.xml has the same app ID as well
268 268
 						$loadEntities = libxml_disable_entity_loader(false);
269
-						$xml = simplexml_load_file($extractDir . '/' . $folders[0] . '/appinfo/info.xml');
269
+						$xml = simplexml_load_file($extractDir.'/'.$folders[0].'/appinfo/info.xml');
270 270
 						libxml_disable_entity_loader($loadEntities);
271
-						if((string)$xml->id !== $appId) {
271
+						if ((string) $xml->id !== $appId) {
272 272
 							throw new \Exception(
273 273
 								sprintf(
274 274
 									'App for id %s has a wrong app ID in info.xml: %s',
275 275
 									$appId,
276
-									(string)$xml->id
276
+									(string) $xml->id
277 277
 								)
278 278
 							);
279 279
 						}
280 280
 
281 281
 						// Check if the version is lower than before
282 282
 						$currentVersion = OC_App::getAppVersion($appId);
283
-						$newVersion = (string)$xml->version;
284
-						if(version_compare($currentVersion, $newVersion) === 1) {
283
+						$newVersion = (string) $xml->version;
284
+						if (version_compare($currentVersion, $newVersion) === 1) {
285 285
 							throw new \Exception(
286 286
 								sprintf(
287 287
 									'App for id %s has version %s and tried to update to lower version %s',
@@ -292,12 +292,12 @@  discard block
 block discarded – undo
292 292
 							);
293 293
 						}
294 294
 
295
-						$baseDir = OC_App::getInstallPath() . '/' . $appId;
295
+						$baseDir = OC_App::getInstallPath().'/'.$appId;
296 296
 						// Remove old app with the ID if existent
297 297
 						OC_Helper::rmdirr($baseDir);
298 298
 						// Move to app folder
299
-						if(@mkdir($baseDir)) {
300
-							$extractDir .= '/' . $folders[0];
299
+						if (@mkdir($baseDir)) {
300
+							$extractDir .= '/'.$folders[0];
301 301
 							OC_Helper::copyr($extractDir, $baseDir);
302 302
 						}
303 303
 						OC_Helper::copyr($extractDir, $baseDir);
@@ -357,8 +357,8 @@  discard block
 block discarded – undo
357 357
 		}
358 358
 
359 359
 		$apps = $appFetcher->get();
360
-		foreach($apps as $app) {
361
-			if($app['id'] === $appId) {
360
+		foreach ($apps as $app) {
361
+			if ($app['id'] === $appId) {
362 362
 				$currentVersion = OC_App::getAppVersion($appId);
363 363
 				$newestVersion = $app['releases'][0]['version'];
364 364
 				if (version_compare($newestVersion, $currentVersion, '>')) {
@@ -380,7 +380,7 @@  discard block
 block discarded – undo
380 380
 	 * The function will check if the app is already downloaded in the apps repository
381 381
 	 */
382 382
 	public function isDownloaded($name) {
383
-		foreach(\OC::$APPSROOTS as $dir) {
383
+		foreach (\OC::$APPSROOTS as $dir) {
384 384
 			$dirToTest  = $dir['path'];
385 385
 			$dirToTest .= '/';
386 386
 			$dirToTest .= $name;
@@ -408,11 +408,11 @@  discard block
 block discarded – undo
408 408
 	 * this has to be done by the function oc_app_uninstall().
409 409
 	 */
410 410
 	public function removeApp($appId) {
411
-		if($this->isDownloaded( $appId )) {
412
-			$appDir = OC_App::getInstallPath() . '/' . $appId;
411
+		if ($this->isDownloaded($appId)) {
412
+			$appDir = OC_App::getInstallPath().'/'.$appId;
413 413
 			OC_Helper::rmdirr($appDir);
414 414
 			return true;
415
-		}else{
415
+		} else {
416 416
 			\OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', \OCP\Util::ERROR);
417 417
 
418 418
 			return false;
@@ -430,13 +430,13 @@  discard block
 block discarded – undo
430 430
 	 */
431 431
 	public static function installShippedApps($softErrors = false) {
432 432
 		$errors = [];
433
-		foreach(\OC::$APPSROOTS as $app_dir) {
434
-			if($dir = opendir( $app_dir['path'] )) {
435
-				while( false !== ( $filename = readdir( $dir ))) {
436
-					if( substr( $filename, 0, 1 ) != '.' and is_dir($app_dir['path']."/$filename") ) {
437
-						if( file_exists( $app_dir['path']."/$filename/appinfo/info.xml" )) {
438
-							if(!Installer::isInstalled($filename)) {
439
-								$info=OC_App::getAppInfo($filename);
433
+		foreach (\OC::$APPSROOTS as $app_dir) {
434
+			if ($dir = opendir($app_dir['path'])) {
435
+				while (false !== ($filename = readdir($dir))) {
436
+					if (substr($filename, 0, 1) != '.' and is_dir($app_dir['path']."/$filename")) {
437
+						if (file_exists($app_dir['path']."/$filename/appinfo/info.xml")) {
438
+							if (!Installer::isInstalled($filename)) {
439
+								$info = OC_App::getAppInfo($filename);
440 440
 								$enabled = isset($info['default_enable']);
441 441
 								if (($enabled || in_array($filename, \OC::$server->getAppManager()->getAlwaysEnabledApps()))
442 442
 									  && \OC::$server->getConfig()->getAppValue($filename, 'enabled') !== 'no') {
@@ -459,7 +459,7 @@  discard block
 block discarded – undo
459 459
 						}
460 460
 					}
461 461
 				}
462
-				closedir( $dir );
462
+				closedir($dir);
463 463
 			}
464 464
 		}
465 465
 
@@ -474,12 +474,12 @@  discard block
 block discarded – undo
474 474
 	public static function installShippedApp($app) {
475 475
 		//install the database
476 476
 		$appPath = OC_App::getAppPath($app);
477
-		if(is_file("$appPath/appinfo/database.xml")) {
477
+		if (is_file("$appPath/appinfo/database.xml")) {
478 478
 			try {
479 479
 				OC_DB::createDbFromStructure("$appPath/appinfo/database.xml");
480 480
 			} catch (TableExistsException $e) {
481 481
 				throw new HintException(
482
-					'Failed to enable app ' . $app,
482
+					'Failed to enable app '.$app,
483 483
 					'Please ask for help via one of our <a href="https://nextcloud.com/support/" target="_blank" rel="noreferrer">support channels</a>.',
484 484
 					0, $e
485 485
 				);
@@ -506,16 +506,16 @@  discard block
 block discarded – undo
506 506
 		}
507 507
 
508 508
 		//set remote/public handlers
509
-		foreach($info['remote'] as $name=>$path) {
509
+		foreach ($info['remote'] as $name=>$path) {
510 510
 			$config->setAppValue('core', 'remote_'.$name, $app.'/'.$path);
511 511
 		}
512
-		foreach($info['public'] as $name=>$path) {
512
+		foreach ($info['public'] as $name=>$path) {
513 513
 			$config->setAppValue('core', 'public_'.$name, $app.'/'.$path);
514 514
 		}
515 515
 
516 516
 		OC_App::setAppTypes($info['id']);
517 517
 
518
-		if(isset($info['settings']) && is_array($info['settings'])) {
518
+		if (isset($info['settings']) && is_array($info['settings'])) {
519 519
 			// requires that autoloading was registered for the app,
520 520
 			// as happens before running the install.php some lines above
521 521
 			\OC::$server->getSettingsManager()->setupSettings($info['settings']);
@@ -531,7 +531,7 @@  discard block
 block discarded – undo
531 531
 	 */
532 532
 	public static function checkCode($folder) {
533 533
 		// is the code checker enabled?
534
-		if(!\OC::$server->getConfig()->getSystemValue('appcodechecker', false)) {
534
+		if (!\OC::$server->getConfig()->getSystemValue('appcodechecker', false)) {
535 535
 			return true;
536 536
 		}
537 537
 
@@ -545,7 +545,7 @@  discard block
 block discarded – undo
545 545
 	 * @param string $script
546 546
 	 */
547 547
 	private static function includeAppScript($script) {
548
-		if ( file_exists($script) ){
548
+		if (file_exists($script)) {
549 549
 			include $script;
550 550
 		}
551 551
 	}
Please login to merge, or discard this patch.
Braces   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -412,7 +412,7 @@
 block discarded – undo
412 412
 			$appDir = OC_App::getInstallPath() . '/' . $appId;
413 413
 			OC_Helper::rmdirr($appDir);
414 414
 			return true;
415
-		}else{
415
+		} else{
416 416
 			\OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', \OCP\Util::ERROR);
417 417
 
418 418
 			return false;
Please login to merge, or discard this patch.