Completed
Pull Request — master (#4106)
by Blizzz
12:50
created
apps/files_external/lib/AppInfo/Application.php 1 patch
Indentation   +103 added lines, -103 removed lines patch added patch discarded remove patch
@@ -52,108 +52,108 @@
 block discarded – undo
52 52
  */
53 53
 class Application extends App implements IBackendProvider, IAuthMechanismProvider {
54 54
 
55
-	public function __construct(array $urlParams = array()) {
56
-		parent::__construct('files_external', $urlParams);
57
-
58
-		$container = $this->getContainer();
59
-
60
-		$container->registerService('OCP\Files\Config\IUserMountCache', function (IAppContainer $c) {
61
-			return $c->getServer()->query('UserMountCache');
62
-		});
63
-
64
-		$backendService = $container->query('OCA\\Files_External\\Service\\BackendService');
65
-		$backendService->registerBackendProvider($this);
66
-		$backendService->registerAuthMechanismProvider($this);
67
-
68
-		// force-load auth mechanisms since some will register hooks
69
-		// TODO: obsolete these and use the TokenProvider to get the user's password from the session
70
-		$this->getAuthMechanisms();
71
-
72
-		// app developers: do NOT depend on this! it will disappear with oC 9.0!
73
-		\OC::$server->getEventDispatcher()->dispatch(
74
-			'OCA\\Files_External::loadAdditionalBackends'
75
-		);
76
-	}
77
-
78
-	/**
79
-	 * Register settings templates
80
-	 */
81
-	public function registerSettings() {
82
-		$container = $this->getContainer();
83
-		$userSession = $container->getServer()->getUserSession();
84
-		if (!$userSession->isLoggedIn()) {
85
-			return;
86
-		}
87
-		$backendService = $container->query('OCA\\Files_External\\Service\\BackendService');
88
-
89
-		/** @var \OCA\Files_External\Service\UserGlobalStoragesService $userGlobalStoragesService */
90
-		$userGlobalStoragesService = $container->query('OCA\Files_External\Service\UserGlobalStoragesService');
91
-		if (count($userGlobalStoragesService->getStorages()) > 0 || $backendService->isUserMountingAllowed()) {
92
-			\OCP\App::registerPersonal('files_external', 'personal');
93
-		}
94
-	}
95
-
96
-	/**
97
-	 * @{inheritdoc}
98
-	 */
99
-	public function getBackends() {
100
-		$container = $this->getContainer();
101
-
102
-		$backends = [
103
-			$container->query(Local::class),
104
-			$container->query(FTP::class),
105
-			$container->query(DAV::class),
106
-			$container->query(OwnCloud::class),
107
-			$container->query(SFTP::class),
108
-			$container->query(AmazonS3::class),
109
-			$container->query(Dropbox::class),
110
-			$container->query(Google::class),
111
-			$container->query(Swift::class),
112
-			$container->query(SFTP_Key::class),
113
-			$container->query(SMB::class),
114
-			$container->query(SMB_OC::class),
115
-			$container->query(SharePoint::class),
116
-		];
117
-
118
-		return $backends;
119
-	}
120
-
121
-	/**
122
-	 * @{inheritdoc}
123
-	 */
124
-	public function getAuthMechanisms() {
125
-		$container = $this->getContainer();
126
-
127
-		return [
128
-			// AuthMechanism::SCHEME_NULL mechanism
129
-			$container->query('OCA\Files_External\Lib\Auth\NullMechanism'),
130
-
131
-			// AuthMechanism::SCHEME_BUILTIN mechanism
132
-			$container->query('OCA\Files_External\Lib\Auth\Builtin'),
133
-
134
-			// AuthMechanism::SCHEME_PASSWORD mechanisms
135
-			$container->query('OCA\Files_External\Lib\Auth\Password\Password'),
136
-			$container->query('OCA\Files_External\Lib\Auth\Password\SessionCredentials'),
137
-			$container->query('OCA\Files_External\Lib\Auth\Password\LoginCredentials'),
138
-			$container->query('OCA\Files_External\Lib\Auth\Password\UserProvided'),
139
-			$container->query('OCA\Files_External\Lib\Auth\Password\GlobalAuth'),
140
-
141
-			// AuthMechanism::SCHEME_OAUTH1 mechanisms
142
-			$container->query('OCA\Files_External\Lib\Auth\OAuth1\OAuth1'),
143
-
144
-			// AuthMechanism::SCHEME_OAUTH2 mechanisms
145
-			$container->query('OCA\Files_External\Lib\Auth\OAuth2\OAuth2'),
146
-
147
-			// AuthMechanism::SCHEME_PUBLICKEY mechanisms
148
-			$container->query('OCA\Files_External\Lib\Auth\PublicKey\RSA'),
149
-
150
-			// AuthMechanism::SCHEME_OPENSTACK mechanisms
151
-			$container->query('OCA\Files_External\Lib\Auth\OpenStack\OpenStack'),
152
-			$container->query('OCA\Files_External\Lib\Auth\OpenStack\Rackspace'),
153
-
154
-			// Specialized mechanisms
155
-			$container->query('OCA\Files_External\Lib\Auth\AmazonS3\AccessKey'),
156
-		];
157
-	}
55
+    public function __construct(array $urlParams = array()) {
56
+        parent::__construct('files_external', $urlParams);
57
+
58
+        $container = $this->getContainer();
59
+
60
+        $container->registerService('OCP\Files\Config\IUserMountCache', function (IAppContainer $c) {
61
+            return $c->getServer()->query('UserMountCache');
62
+        });
63
+
64
+        $backendService = $container->query('OCA\\Files_External\\Service\\BackendService');
65
+        $backendService->registerBackendProvider($this);
66
+        $backendService->registerAuthMechanismProvider($this);
67
+
68
+        // force-load auth mechanisms since some will register hooks
69
+        // TODO: obsolete these and use the TokenProvider to get the user's password from the session
70
+        $this->getAuthMechanisms();
71
+
72
+        // app developers: do NOT depend on this! it will disappear with oC 9.0!
73
+        \OC::$server->getEventDispatcher()->dispatch(
74
+            'OCA\\Files_External::loadAdditionalBackends'
75
+        );
76
+    }
77
+
78
+    /**
79
+     * Register settings templates
80
+     */
81
+    public function registerSettings() {
82
+        $container = $this->getContainer();
83
+        $userSession = $container->getServer()->getUserSession();
84
+        if (!$userSession->isLoggedIn()) {
85
+            return;
86
+        }
87
+        $backendService = $container->query('OCA\\Files_External\\Service\\BackendService');
88
+
89
+        /** @var \OCA\Files_External\Service\UserGlobalStoragesService $userGlobalStoragesService */
90
+        $userGlobalStoragesService = $container->query('OCA\Files_External\Service\UserGlobalStoragesService');
91
+        if (count($userGlobalStoragesService->getStorages()) > 0 || $backendService->isUserMountingAllowed()) {
92
+            \OCP\App::registerPersonal('files_external', 'personal');
93
+        }
94
+    }
95
+
96
+    /**
97
+     * @{inheritdoc}
98
+     */
99
+    public function getBackends() {
100
+        $container = $this->getContainer();
101
+
102
+        $backends = [
103
+            $container->query(Local::class),
104
+            $container->query(FTP::class),
105
+            $container->query(DAV::class),
106
+            $container->query(OwnCloud::class),
107
+            $container->query(SFTP::class),
108
+            $container->query(AmazonS3::class),
109
+            $container->query(Dropbox::class),
110
+            $container->query(Google::class),
111
+            $container->query(Swift::class),
112
+            $container->query(SFTP_Key::class),
113
+            $container->query(SMB::class),
114
+            $container->query(SMB_OC::class),
115
+            $container->query(SharePoint::class),
116
+        ];
117
+
118
+        return $backends;
119
+    }
120
+
121
+    /**
122
+     * @{inheritdoc}
123
+     */
124
+    public function getAuthMechanisms() {
125
+        $container = $this->getContainer();
126
+
127
+        return [
128
+            // AuthMechanism::SCHEME_NULL mechanism
129
+            $container->query('OCA\Files_External\Lib\Auth\NullMechanism'),
130
+
131
+            // AuthMechanism::SCHEME_BUILTIN mechanism
132
+            $container->query('OCA\Files_External\Lib\Auth\Builtin'),
133
+
134
+            // AuthMechanism::SCHEME_PASSWORD mechanisms
135
+            $container->query('OCA\Files_External\Lib\Auth\Password\Password'),
136
+            $container->query('OCA\Files_External\Lib\Auth\Password\SessionCredentials'),
137
+            $container->query('OCA\Files_External\Lib\Auth\Password\LoginCredentials'),
138
+            $container->query('OCA\Files_External\Lib\Auth\Password\UserProvided'),
139
+            $container->query('OCA\Files_External\Lib\Auth\Password\GlobalAuth'),
140
+
141
+            // AuthMechanism::SCHEME_OAUTH1 mechanisms
142
+            $container->query('OCA\Files_External\Lib\Auth\OAuth1\OAuth1'),
143
+
144
+            // AuthMechanism::SCHEME_OAUTH2 mechanisms
145
+            $container->query('OCA\Files_External\Lib\Auth\OAuth2\OAuth2'),
146
+
147
+            // AuthMechanism::SCHEME_PUBLICKEY mechanisms
148
+            $container->query('OCA\Files_External\Lib\Auth\PublicKey\RSA'),
149
+
150
+            // AuthMechanism::SCHEME_OPENSTACK mechanisms
151
+            $container->query('OCA\Files_External\Lib\Auth\OpenStack\OpenStack'),
152
+            $container->query('OCA\Files_External\Lib\Auth\OpenStack\Rackspace'),
153
+
154
+            // Specialized mechanisms
155
+            $container->query('OCA\Files_External\Lib\Auth\AmazonS3\AccessKey'),
156
+        ];
157
+    }
158 158
 
159 159
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Backend/SharePoint.php 1 patch
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -32,18 +32,18 @@
 block discarded – undo
32 32
 
33 33
 class SharePoint extends Backend {
34 34
 
35
-	public function __construct(IL10N $l, Password $legacyAuth) {
36
-		$this
37
-			->setIdentifier('sharepoint')
38
-			->setStorageClass(SharePointStorage::class)
39
-			->setText($l->t('SharePoint'))
40
-			->addParameters([
41
-				(new DefinitionParameter('host', $l->t('Host'))),
42
-				(new DefinitionParameter('documentLibrary', $l->t('Document Library'))),
43
-			])
44
-			->addAuthScheme(AuthMechanism::SCHEME_PASSWORD)
45
-			->setLegacyAuthMechanism($legacyAuth)
46
-		;
47
-	}
35
+    public function __construct(IL10N $l, Password $legacyAuth) {
36
+        $this
37
+            ->setIdentifier('sharepoint')
38
+            ->setStorageClass(SharePointStorage::class)
39
+            ->setText($l->t('SharePoint'))
40
+            ->addParameters([
41
+                (new DefinitionParameter('host', $l->t('Host'))),
42
+                (new DefinitionParameter('documentLibrary', $l->t('Document Library'))),
43
+            ])
44
+            ->addAuthScheme(AuthMechanism::SCHEME_PASSWORD)
45
+            ->setLegacyAuthMechanism($legacyAuth)
46
+        ;
47
+    }
48 48
 
49 49
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/SharePoint/ContextsFactory.php 1 patch
Indentation   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -30,12 +30,12 @@
 block discarded – undo
30 30
 
31 31
 class ContextsFactory {
32 32
 
33
-	public function getAuthContext($user, $password) {
34
-		return new NetworkCredentialContext($user, $password);
35
-	}
33
+    public function getAuthContext($user, $password) {
34
+        return new NetworkCredentialContext($user, $password);
35
+    }
36 36
 
37
-	public function getClientContext($server, IAuthenticationContext $authContext) {
38
-		return new ClientContext($server, $authContext);
39
-	}
37
+    public function getClientContext($server, IAuthenticationContext $authContext) {
38
+        return new ClientContext($server, $authContext);
39
+    }
40 40
 
41 41
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/SharePoint/SharePointClientFactory.php 1 patch
Indentation   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -24,19 +24,19 @@
 block discarded – undo
24 24
 namespace OCA\Files_External\Lib\SharePoint;
25 25
 
26 26
 class SharePointClientFactory {
27
-	/**
28
-	 * @param ContextsFactory $contextsFactory
29
-	 * @param string $sharePointUrl
30
-	 * @param array $credentials
31
-	 * @param string $documentLibraryTitle
32
-	 * @return SharePointClient
33
-	 */
34
-	public function getClient(
35
-		ContextsFactory $contextsFactory,
36
-		$sharePointUrl,
37
-		array $credentials,
38
-		$documentLibraryTitle)
39
-	{
40
-		return new SharePointClient($contextsFactory, $sharePointUrl, $credentials, $documentLibraryTitle);
41
-	}
27
+    /**
28
+     * @param ContextsFactory $contextsFactory
29
+     * @param string $sharePointUrl
30
+     * @param array $credentials
31
+     * @param string $documentLibraryTitle
32
+     * @return SharePointClient
33
+     */
34
+    public function getClient(
35
+        ContextsFactory $contextsFactory,
36
+        $sharePointUrl,
37
+        array $credentials,
38
+        $documentLibraryTitle)
39
+    {
40
+        return new SharePointClient($contextsFactory, $sharePointUrl, $credentials, $documentLibraryTitle);
41
+    }
42 42
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Storage/SharePoint.php 1 patch
Indentation   +387 added lines, -387 removed lines patch added patch discarded remove patch
@@ -37,392 +37,392 @@
 block discarded – undo
37 37
 use Office365\PHP\Client\SharePoint\ListItem;
38 38
 
39 39
 class SharePoint extends Common {
40
-	const SP_PROPERTY_SIZE = 'Length';
41
-	const SP_PROPERTY_MTIME = 'TimeLastModified';
42
-	const SP_PROPERTY_URL = 'ServerRelativeUrl';
43
-
44
-	/** @var  string */
45
-	protected $server;
46
-
47
-	/** @var  string */
48
-	protected $documentLibrary;
49
-
50
-	/** @var  string */
51
-	protected $authUser;
52
-
53
-	/** @var  string */
54
-	protected $authPwd;
55
-
56
-	/** @var  SharePointClient */
57
-	protected $spClient;
58
-
59
-	/** @var  CappedMemoryCache */
60
-	protected $fileCache;
61
-
62
-	/** @var ContextsFactory */
63
-	private $contextsFactory;
64
-
65
-	public function __construct($parameters) {
66
-		$this->server = $parameters['host'];
67
-		$this->documentLibrary = $parameters['documentLibrary'];
68
-
69
-		if(strpos($this->documentLibrary, '"') !== false) {
70
-			// they are, amongst others, not allowed and we use it in the filter
71
-			// cf. https://support.microsoft.com/en-us/kb/2933738
72
-			// TODO: verify, it talks about files and folders mostly
73
-			throw new \InvalidArgumentException('Illegal character in Document Library Name');
74
-		}
75
-
76
-		if(!isset($parameters['user']) || !isset($parameters['password'])) {
77
-			throw new \UnexpectedValueException('No user or password given');
78
-		}
79
-		$this->authUser = $parameters['user'];
80
-		$this->authPwd  = $parameters['password'];
81
-
82
-		$this->fixDI($parameters);
83
-	}
84
-
85
-	/**
86
-	 * Get the identifier for the storage,
87
-	 * the returned id should be the same for every storage object that is created with the same parameters
88
-	 * and two storage objects with the same id should refer to two storages that display the same files.
89
-	 *
90
-	 * @return string
91
-	 * @since 6.0.0
92
-	 */
93
-	public function getId() {
94
-		return 'SharePoint::' . $this->server . '::' . $this->documentLibrary . '::' . $this->authUser;
95
-	}
96
-
97
-	/**
98
-	 * see http://php.net/manual/en/function.mkdir.php
99
-	 * implementations need to implement a recursive mkdir
100
-	 *
101
-	 * @param string $path
102
-	 * @return bool
103
-	 * @since 6.0.0
104
-	 */
105
-	public function mkdir($path) {
106
-		$serverUrl = $this->formatPath($path);
107
-		try {
108
-			$folder = $this->spClient->createFolder($serverUrl);
109
-			$this->fileCache->set($serverUrl, [
110
-				'instance' => $folder,
111
-				'children' => [
112
-					'folders' => $folder->getFolders(),
113
-					'files' => $folder->getFiles()
114
-				]
115
-			]);
116
-			return true;
117
-		} catch (\Exception $e) {
118
-			$this->fileCache->remove($serverUrl);
119
-			return false;
120
-		}
121
-	}
122
-
123
-	/**
124
-	 * see http://php.net/manual/en/function.rmdir.php
125
-	 *
126
-	 * @param string $path
127
-	 * @return bool
128
-	 * @since 6.0.0
129
-	 */
130
-	public function rmdir($path) {
131
-		$serverUrl = $this->formatPath($path);
132
-		try {
133
-			$cacheEntry = $this->fileCache->get($serverUrl);
134
-			$folder = null;
135
-			if(is_array($cacheEntry) && isset($cacheEntry['instance'])) {
136
-				$folder = $cacheEntry['instance'];
137
-			}
138
-			$this->spClient->deleteFolder($serverUrl, $folder);
139
-			$this->fileCache->set($serverUrl, false);
140
-			return true;
141
-		} catch (\Exception $e) {
142
-			$this->fileCache->remove($serverUrl);
143
-			return false;
144
-		}
145
-	}
146
-
147
-	/**
148
-	 * see http://php.net/manual/en/function.opendir.php
149
-	 *
150
-	 * @param string $path
151
-	 * @return resource|false
152
-	 * @since 6.0.0
153
-	 */
154
-	public function opendir($path) {
155
-		try {
156
-			$serverUrl = $this->formatPath($path);
157
-			//$collections = $this->spClient->fetchFolderContents($serverUrl, ['Name', 'ListItemAllFields']);	// does not work for some reason :(
158
-			$collections = $this->getFolderContents($serverUrl);
159
-			$files = [];
160
-
161
-			foreach ($collections as $collection) {
162
-				/** @var File[]|Folder[] $items */
163
-				$items = $collection->getData();
164
-				foreach ($items as $item) {
165
-					/** @var ListItem $fields */
166
-					if(!$this->spClient->isHidden($item)) {
167
-						$files[] = $item->getProperty('Name');
168
-					}
169
-				}
170
-			}
171
-
172
-			return IteratorDirectory::wrap($files);
173
-		} catch (NotFoundException $e) {
174
-			return false;
175
-		}
176
-	}
177
-
178
-	/**
179
-	 * see http://php.net/manual/en/function.stat.php
180
-	 * only the following keys are required in the result: size and mtime
181
-	 *
182
-	 * @param string $path
183
-	 * @return array|false
184
-	 * @since 6.0.0
185
-	 */
186
-	public function stat($path) {
187
-		$serverUrl = $this->formatPath($path);
188
-		try {
189
-			$file = $this->getFileOrFolder($serverUrl);
190
-		} catch (\Exception $e) {
191
-			return false;
192
-		}
193
-
194
-		$size = $file->getProperty(self::SP_PROPERTY_SIZE) ?: FileInfo::SPACE_UNKNOWN;
195
-		$mtimeValue = $file->getProperty(self::SP_PROPERTY_MTIME);
196
-		$mtime = $mtimeValue ? new \DateTime($mtimeValue) : null;
197
-
198
-		$stat = [
199
-			// int64, size in bytes, excluding the size of any Web Parts that are used in the file.
200
-			'size'  => $size,
201
-			'mtime' => $mtime->getTimestamp(),
202
-			// no property in SP 2013 & 2016, other storages do the same  :speak_no_evil:
203
-			'atime' => time(),
204
-		];
205
-
206
-		if(!is_null($stat['mtime'])) {
207
-			return $stat;
208
-		}
209
-
210
-		// If we do not get a mtime from SP, we treat it as an error
211
-		// thus returning false, according to PHP documentation on stat()
212
-		return false;
213
-	}
214
-
215
-	/**
216
-	 * see http://php.net/manual/en/function.filetype.php
217
-	 *
218
-	 * @param string $path
219
-	 * @return false|string
220
-	 * @throws \Exception
221
-	 * @since 6.0.0
222
-	 */
223
-	public function filetype($path) {
224
-		try {
225
-			$serverUrl = $this->formatPath($path);
226
-			$object = $this->getFileOrFolder($serverUrl);
227
-		} catch (NotFoundException $e) {
228
-			return false;
229
-		}
230
-		if($object instanceof File) {
231
-			return 'file';
232
-		} else if($object instanceof Folder) {
233
-			return 'dir';
234
-		} else {
235
-			return false;
236
-		}
237
-	}
238
-
239
-	/**
240
-	 * see http://php.net/manual/en/function.file_exists.php
241
-	 *
242
-	 * @param string $path
243
-	 * @return bool
244
-	 * @since 6.0.0
245
-	 */
246
-	public function file_exists($path) {
247
-		try {
248
-			$serverUrl = $this->formatPath($path);
249
-			// alternative approach is to use a CAML query instead of querying
250
-			// for file and folder. It is not necessarily faster, though.
251
-			// Would need evaluation of typical use cases (I assume most often
252
-			// existing files are checked) and measurements.
253
-			$this->getFileOrFolder($serverUrl);
254
-			return true;
255
-		} catch (NotFoundException $e) {
256
-			return false;
257
-		}
258
-	}
259
-
260
-	/**
261
-	 * see http://php.net/manual/en/function.unlink.php
262
-	 *
263
-	 * @param string $path
264
-	 * @return bool
265
-	 * @since 6.0.0
266
-	 */
267
-	public function unlink($path) {
268
-		//  FIXME:: all is totally wrong
269
-		$path = trim($path);
270
-		if($path === '/' || $path === '') {
271
-			return false;
272
-		}
273
-		foreach([true, false] as $asFile) {
274
-			try {
275
-				$fsObject = $this->spClient->fetchFileOrFolder($path, $asFile);
276
-				$fsObject->deleteObject();
277
-				return true;
278
-			} catch(\Exception $e) {
279
-				// NOOP
280
-			}
281
-		}
282
-		return false;
283
-	}
284
-
285
-	/**
286
-	 * see http://php.net/manual/en/function.fopen.php
287
-	 *
288
-	 * @param string $path
289
-	 * @param string $mode
290
-	 * @return resource|false
291
-	 * @since 6.0.0
292
-	 */
293
-	public function fopen($path, $mode) {
294
-		// TODO: Implement fopen() method.
295
-		return false;
296
-	}
297
-
298
-	/**
299
-	 * see http://php.net/manual/en/function.touch.php
300
-	 * If the backend does not support the operation, false should be returned
301
-	 *
302
-	 * @param string $path
303
-	 * @param int $mtime
304
-	 * @return bool
305
-	 * @since 6.0.0
306
-	 */
307
-	public function touch($path, $mtime = null) {
308
-		// TODO: Implement touch() method.
309
-		return true;
310
-	}
311
-
312
-
313
-	/**
314
-	 * work around dependency injection issues so we can test this class properly
315
-	 *
316
-	 * @param array $parameters
317
-	 */
318
-	private function fixDI(array $parameters) {
319
-		if(isset($parameters['contextFactory'])
320
-			&& $parameters['contextFactory'] instanceof ContextsFactory)
321
-		{
322
-			$this->contextsFactory = $parameters['contextFactory'];
323
-		} else {
324
-			$this->contextsFactory = new ContextsFactory();
325
-		}
326
-
327
-		if(isset($parameters['sharePointClientFactory'])
328
-			&& $parameters['sharePointClientFactory'] instanceof SharePointClientFactory)
329
-		{
330
-			$spcFactory = $parameters['sharePointClientFactory'];
331
-		} else {
332
-			$spcFactory = new SharePointClientFactory();
333
-		}
334
-		$this->spClient = $spcFactory->getClient(
335
-			$this->contextsFactory,
336
-			$this->server,
337
-			[ 'user' => $this->authUser, 'password' => $this->authPwd],
338
-			$this->documentLibrary
339
-		);
340
-
341
-		if(isset($parameters['cappedMemoryCache'])) {
342
-			$this->fileCache = $parameters['cappedMemoryCache'];
343
-		} else {
344
-			$this->fileCache = new CappedMemoryCache('256');
345
-		}
346
-	}
347
-
348
-	/**
349
-	 * @param $serverUrl
350
-	 * @return ClientObjectCollection[]
351
-	 */
352
-	private function getFolderContents($serverUrl) {
353
-		$entry = $this->fileCache->get($serverUrl);
354
-		if($entry === null || !isset($entry['children'])) {
355
-			$folder = isset($entry['instance']) ? $entry['instance'] : null;
356
-			$contents = $this->spClient->fetchFolderContents($serverUrl, null, $folder);
357
-			$cacheItem = $entry ?: [];
358
-			$cacheItem['children'] = $contents;
359
-			$this->fileCache->set($serverUrl, $cacheItem);
360
-
361
-			// cache children instances
362
-			foreach ($contents as $collection) {
363
-				foreach ($collection->getData() as $item) {
364
-					/** @var  File|Folder $item */
365
-					$url = $item->getProperty(self::SP_PROPERTY_URL);
366
-					$itemEntry = $this->fileCache->get($url);
367
-					$itemEntry = $itemEntry ?: [];
368
-					if(!isset($itemEntry['instance'])) {
369
-						$itemEntry['instance'] = $item;
370
-						$this->fileCache->set($url, $itemEntry);
371
-					}
372
-				}
373
-			}
374
-		} else {
375
-			$contents = $entry['children'];
376
-		}
377
-		return $contents;
378
-	}
379
-
380
-	/**
381
-	 * @param $serverUrl
382
-	 * @return File|Folder
383
-	 * @throws NotFoundException
384
-	 */
385
-	private function getFileOrFolder($serverUrl) {
386
-		$entry = $this->fileCache->get($serverUrl);
387
-		if($entry === false) {
388
-			throw new NotFoundException('File or Folder not found');
389
-		} else if($entry === null || !isset($entry['instance'])) {
390
-			try {
391
-				$file = $this->spClient->fetchFileOrFolder($serverUrl, [self::SP_PROPERTY_SIZE, self::SP_PROPERTY_MTIME]);
392
-			} catch (NotFoundException $e) {
393
-				$this->fileCache->set($serverUrl, false);
394
-				throw $e;
395
-			}
396
-			$cacheItem = $entry ?: [];
397
-			$cacheItem['instance'] = $file;
398
-			$this->fileCache->set($serverUrl, $cacheItem);
399
-		} else {
400
-			$file = $entry['instance'];
401
-		}
402
-		return $file;
403
-	}
404
-
405
-	/**
406
-	 * creates the relative server "url" out of the provided path
407
-	 *
408
-	 * @param $path
409
-	 * @return string
410
-	 */
411
-	private function formatPath($path) {
412
-		$path = trim($path, '/');
413
-		$serverUrl = '/' . $this->documentLibrary;
414
-		if($path !== '') {
415
-			$serverUrl .= '/' . $path;
416
-		}
417
-
418
-		$pathParts = explode('/', $serverUrl);
419
-		$filename = array_pop($pathParts);
420
-		if($filename === '.') {
421
-			// remove /. from the end of the path
422
-			$serverUrl = mb_substr($serverUrl, 0, mb_strlen($serverUrl) - 2);
423
-		}
424
-
425
-		return $serverUrl;
426
-	}
40
+    const SP_PROPERTY_SIZE = 'Length';
41
+    const SP_PROPERTY_MTIME = 'TimeLastModified';
42
+    const SP_PROPERTY_URL = 'ServerRelativeUrl';
43
+
44
+    /** @var  string */
45
+    protected $server;
46
+
47
+    /** @var  string */
48
+    protected $documentLibrary;
49
+
50
+    /** @var  string */
51
+    protected $authUser;
52
+
53
+    /** @var  string */
54
+    protected $authPwd;
55
+
56
+    /** @var  SharePointClient */
57
+    protected $spClient;
58
+
59
+    /** @var  CappedMemoryCache */
60
+    protected $fileCache;
61
+
62
+    /** @var ContextsFactory */
63
+    private $contextsFactory;
64
+
65
+    public function __construct($parameters) {
66
+        $this->server = $parameters['host'];
67
+        $this->documentLibrary = $parameters['documentLibrary'];
68
+
69
+        if(strpos($this->documentLibrary, '"') !== false) {
70
+            // they are, amongst others, not allowed and we use it in the filter
71
+            // cf. https://support.microsoft.com/en-us/kb/2933738
72
+            // TODO: verify, it talks about files and folders mostly
73
+            throw new \InvalidArgumentException('Illegal character in Document Library Name');
74
+        }
75
+
76
+        if(!isset($parameters['user']) || !isset($parameters['password'])) {
77
+            throw new \UnexpectedValueException('No user or password given');
78
+        }
79
+        $this->authUser = $parameters['user'];
80
+        $this->authPwd  = $parameters['password'];
81
+
82
+        $this->fixDI($parameters);
83
+    }
84
+
85
+    /**
86
+     * Get the identifier for the storage,
87
+     * the returned id should be the same for every storage object that is created with the same parameters
88
+     * and two storage objects with the same id should refer to two storages that display the same files.
89
+     *
90
+     * @return string
91
+     * @since 6.0.0
92
+     */
93
+    public function getId() {
94
+        return 'SharePoint::' . $this->server . '::' . $this->documentLibrary . '::' . $this->authUser;
95
+    }
96
+
97
+    /**
98
+     * see http://php.net/manual/en/function.mkdir.php
99
+     * implementations need to implement a recursive mkdir
100
+     *
101
+     * @param string $path
102
+     * @return bool
103
+     * @since 6.0.0
104
+     */
105
+    public function mkdir($path) {
106
+        $serverUrl = $this->formatPath($path);
107
+        try {
108
+            $folder = $this->spClient->createFolder($serverUrl);
109
+            $this->fileCache->set($serverUrl, [
110
+                'instance' => $folder,
111
+                'children' => [
112
+                    'folders' => $folder->getFolders(),
113
+                    'files' => $folder->getFiles()
114
+                ]
115
+            ]);
116
+            return true;
117
+        } catch (\Exception $e) {
118
+            $this->fileCache->remove($serverUrl);
119
+            return false;
120
+        }
121
+    }
122
+
123
+    /**
124
+     * see http://php.net/manual/en/function.rmdir.php
125
+     *
126
+     * @param string $path
127
+     * @return bool
128
+     * @since 6.0.0
129
+     */
130
+    public function rmdir($path) {
131
+        $serverUrl = $this->formatPath($path);
132
+        try {
133
+            $cacheEntry = $this->fileCache->get($serverUrl);
134
+            $folder = null;
135
+            if(is_array($cacheEntry) && isset($cacheEntry['instance'])) {
136
+                $folder = $cacheEntry['instance'];
137
+            }
138
+            $this->spClient->deleteFolder($serverUrl, $folder);
139
+            $this->fileCache->set($serverUrl, false);
140
+            return true;
141
+        } catch (\Exception $e) {
142
+            $this->fileCache->remove($serverUrl);
143
+            return false;
144
+        }
145
+    }
146
+
147
+    /**
148
+     * see http://php.net/manual/en/function.opendir.php
149
+     *
150
+     * @param string $path
151
+     * @return resource|false
152
+     * @since 6.0.0
153
+     */
154
+    public function opendir($path) {
155
+        try {
156
+            $serverUrl = $this->formatPath($path);
157
+            //$collections = $this->spClient->fetchFolderContents($serverUrl, ['Name', 'ListItemAllFields']);	// does not work for some reason :(
158
+            $collections = $this->getFolderContents($serverUrl);
159
+            $files = [];
160
+
161
+            foreach ($collections as $collection) {
162
+                /** @var File[]|Folder[] $items */
163
+                $items = $collection->getData();
164
+                foreach ($items as $item) {
165
+                    /** @var ListItem $fields */
166
+                    if(!$this->spClient->isHidden($item)) {
167
+                        $files[] = $item->getProperty('Name');
168
+                    }
169
+                }
170
+            }
171
+
172
+            return IteratorDirectory::wrap($files);
173
+        } catch (NotFoundException $e) {
174
+            return false;
175
+        }
176
+    }
177
+
178
+    /**
179
+     * see http://php.net/manual/en/function.stat.php
180
+     * only the following keys are required in the result: size and mtime
181
+     *
182
+     * @param string $path
183
+     * @return array|false
184
+     * @since 6.0.0
185
+     */
186
+    public function stat($path) {
187
+        $serverUrl = $this->formatPath($path);
188
+        try {
189
+            $file = $this->getFileOrFolder($serverUrl);
190
+        } catch (\Exception $e) {
191
+            return false;
192
+        }
193
+
194
+        $size = $file->getProperty(self::SP_PROPERTY_SIZE) ?: FileInfo::SPACE_UNKNOWN;
195
+        $mtimeValue = $file->getProperty(self::SP_PROPERTY_MTIME);
196
+        $mtime = $mtimeValue ? new \DateTime($mtimeValue) : null;
197
+
198
+        $stat = [
199
+            // int64, size in bytes, excluding the size of any Web Parts that are used in the file.
200
+            'size'  => $size,
201
+            'mtime' => $mtime->getTimestamp(),
202
+            // no property in SP 2013 & 2016, other storages do the same  :speak_no_evil:
203
+            'atime' => time(),
204
+        ];
205
+
206
+        if(!is_null($stat['mtime'])) {
207
+            return $stat;
208
+        }
209
+
210
+        // If we do not get a mtime from SP, we treat it as an error
211
+        // thus returning false, according to PHP documentation on stat()
212
+        return false;
213
+    }
214
+
215
+    /**
216
+     * see http://php.net/manual/en/function.filetype.php
217
+     *
218
+     * @param string $path
219
+     * @return false|string
220
+     * @throws \Exception
221
+     * @since 6.0.0
222
+     */
223
+    public function filetype($path) {
224
+        try {
225
+            $serverUrl = $this->formatPath($path);
226
+            $object = $this->getFileOrFolder($serverUrl);
227
+        } catch (NotFoundException $e) {
228
+            return false;
229
+        }
230
+        if($object instanceof File) {
231
+            return 'file';
232
+        } else if($object instanceof Folder) {
233
+            return 'dir';
234
+        } else {
235
+            return false;
236
+        }
237
+    }
238
+
239
+    /**
240
+     * see http://php.net/manual/en/function.file_exists.php
241
+     *
242
+     * @param string $path
243
+     * @return bool
244
+     * @since 6.0.0
245
+     */
246
+    public function file_exists($path) {
247
+        try {
248
+            $serverUrl = $this->formatPath($path);
249
+            // alternative approach is to use a CAML query instead of querying
250
+            // for file and folder. It is not necessarily faster, though.
251
+            // Would need evaluation of typical use cases (I assume most often
252
+            // existing files are checked) and measurements.
253
+            $this->getFileOrFolder($serverUrl);
254
+            return true;
255
+        } catch (NotFoundException $e) {
256
+            return false;
257
+        }
258
+    }
259
+
260
+    /**
261
+     * see http://php.net/manual/en/function.unlink.php
262
+     *
263
+     * @param string $path
264
+     * @return bool
265
+     * @since 6.0.0
266
+     */
267
+    public function unlink($path) {
268
+        //  FIXME:: all is totally wrong
269
+        $path = trim($path);
270
+        if($path === '/' || $path === '') {
271
+            return false;
272
+        }
273
+        foreach([true, false] as $asFile) {
274
+            try {
275
+                $fsObject = $this->spClient->fetchFileOrFolder($path, $asFile);
276
+                $fsObject->deleteObject();
277
+                return true;
278
+            } catch(\Exception $e) {
279
+                // NOOP
280
+            }
281
+        }
282
+        return false;
283
+    }
284
+
285
+    /**
286
+     * see http://php.net/manual/en/function.fopen.php
287
+     *
288
+     * @param string $path
289
+     * @param string $mode
290
+     * @return resource|false
291
+     * @since 6.0.0
292
+     */
293
+    public function fopen($path, $mode) {
294
+        // TODO: Implement fopen() method.
295
+        return false;
296
+    }
297
+
298
+    /**
299
+     * see http://php.net/manual/en/function.touch.php
300
+     * If the backend does not support the operation, false should be returned
301
+     *
302
+     * @param string $path
303
+     * @param int $mtime
304
+     * @return bool
305
+     * @since 6.0.0
306
+     */
307
+    public function touch($path, $mtime = null) {
308
+        // TODO: Implement touch() method.
309
+        return true;
310
+    }
311
+
312
+
313
+    /**
314
+     * work around dependency injection issues so we can test this class properly
315
+     *
316
+     * @param array $parameters
317
+     */
318
+    private function fixDI(array $parameters) {
319
+        if(isset($parameters['contextFactory'])
320
+            && $parameters['contextFactory'] instanceof ContextsFactory)
321
+        {
322
+            $this->contextsFactory = $parameters['contextFactory'];
323
+        } else {
324
+            $this->contextsFactory = new ContextsFactory();
325
+        }
326
+
327
+        if(isset($parameters['sharePointClientFactory'])
328
+            && $parameters['sharePointClientFactory'] instanceof SharePointClientFactory)
329
+        {
330
+            $spcFactory = $parameters['sharePointClientFactory'];
331
+        } else {
332
+            $spcFactory = new SharePointClientFactory();
333
+        }
334
+        $this->spClient = $spcFactory->getClient(
335
+            $this->contextsFactory,
336
+            $this->server,
337
+            [ 'user' => $this->authUser, 'password' => $this->authPwd],
338
+            $this->documentLibrary
339
+        );
340
+
341
+        if(isset($parameters['cappedMemoryCache'])) {
342
+            $this->fileCache = $parameters['cappedMemoryCache'];
343
+        } else {
344
+            $this->fileCache = new CappedMemoryCache('256');
345
+        }
346
+    }
347
+
348
+    /**
349
+     * @param $serverUrl
350
+     * @return ClientObjectCollection[]
351
+     */
352
+    private function getFolderContents($serverUrl) {
353
+        $entry = $this->fileCache->get($serverUrl);
354
+        if($entry === null || !isset($entry['children'])) {
355
+            $folder = isset($entry['instance']) ? $entry['instance'] : null;
356
+            $contents = $this->spClient->fetchFolderContents($serverUrl, null, $folder);
357
+            $cacheItem = $entry ?: [];
358
+            $cacheItem['children'] = $contents;
359
+            $this->fileCache->set($serverUrl, $cacheItem);
360
+
361
+            // cache children instances
362
+            foreach ($contents as $collection) {
363
+                foreach ($collection->getData() as $item) {
364
+                    /** @var  File|Folder $item */
365
+                    $url = $item->getProperty(self::SP_PROPERTY_URL);
366
+                    $itemEntry = $this->fileCache->get($url);
367
+                    $itemEntry = $itemEntry ?: [];
368
+                    if(!isset($itemEntry['instance'])) {
369
+                        $itemEntry['instance'] = $item;
370
+                        $this->fileCache->set($url, $itemEntry);
371
+                    }
372
+                }
373
+            }
374
+        } else {
375
+            $contents = $entry['children'];
376
+        }
377
+        return $contents;
378
+    }
379
+
380
+    /**
381
+     * @param $serverUrl
382
+     * @return File|Folder
383
+     * @throws NotFoundException
384
+     */
385
+    private function getFileOrFolder($serverUrl) {
386
+        $entry = $this->fileCache->get($serverUrl);
387
+        if($entry === false) {
388
+            throw new NotFoundException('File or Folder not found');
389
+        } else if($entry === null || !isset($entry['instance'])) {
390
+            try {
391
+                $file = $this->spClient->fetchFileOrFolder($serverUrl, [self::SP_PROPERTY_SIZE, self::SP_PROPERTY_MTIME]);
392
+            } catch (NotFoundException $e) {
393
+                $this->fileCache->set($serverUrl, false);
394
+                throw $e;
395
+            }
396
+            $cacheItem = $entry ?: [];
397
+            $cacheItem['instance'] = $file;
398
+            $this->fileCache->set($serverUrl, $cacheItem);
399
+        } else {
400
+            $file = $entry['instance'];
401
+        }
402
+        return $file;
403
+    }
404
+
405
+    /**
406
+     * creates the relative server "url" out of the provided path
407
+     *
408
+     * @param $path
409
+     * @return string
410
+     */
411
+    private function formatPath($path) {
412
+        $path = trim($path, '/');
413
+        $serverUrl = '/' . $this->documentLibrary;
414
+        if($path !== '') {
415
+            $serverUrl .= '/' . $path;
416
+        }
417
+
418
+        $pathParts = explode('/', $serverUrl);
419
+        $filename = array_pop($pathParts);
420
+        if($filename === '.') {
421
+            // remove /. from the end of the path
422
+            $serverUrl = mb_substr($serverUrl, 0, mb_strlen($serverUrl) - 2);
423
+        }
424
+
425
+        return $serverUrl;
426
+    }
427 427
 
428 428
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/SharePoint/SharePointClient.php 1 patch
Indentation   +219 added lines, -219 removed lines patch added patch discarded remove patch
@@ -32,223 +32,223 @@
 block discarded – undo
32 32
 use Office365\PHP\Client\SharePoint\SPList;
33 33
 
34 34
 class SharePointClient {
35
-	/** @var  ClientContext */
36
-	protected $context;
37
-
38
-	/** @var  AuthenticationContext */
39
-	protected $authContext;
40
-
41
-	/** @var  SPList */
42
-	protected $documentLibrary;
43
-
44
-	/** @var ContextsFactory */
45
-	private $contextsFactory;
46
-
47
-	/** @var  string */
48
-	private $sharePointUrl;
49
-
50
-	/** @var string[] */
51
-	private $credentials;
52
-
53
-	/** @var  string */
54
-	private $documentLibraryTitle;
55
-
56
-	public function __construct(ContextsFactory $contextsFactory, $sharePointUrl, array $credentials, $documentLibraryTitle) {
57
-		$this->contextsFactory = $contextsFactory;
58
-		$this->sharePointUrl = $sharePointUrl;
59
-		$this->credentials = $credentials;
60
-		$this->documentLibraryTitle = $documentLibraryTitle;
61
-	}
62
-
63
-	/**
64
-	 * @param string $path
65
-	 * @param array $properties
66
-	 * @return File|Folder
67
-	 * @throws \Exception
68
-	 */
69
-	public function fetchFileOrFolder($path, array $properties = null) {
70
-		$fetchFileFunc = function ($path, $props) { return $this->fetchFile($path, $props);};
71
-		$fetchFolderFunc = function ($path, $props) { return $this->fetchFolder($path, $props);};
72
-		$fetchers = [ $fetchFileFunc, $fetchFolderFunc ];
73
-		if(strpos($path, '.') === false) {
74
-			$fetchers = array_reverse($fetchers);
75
-		}
76
-
77
-		foreach ($fetchers as $fetchFunction) {
78
-			try {
79
-				$instance = call_user_func_array($fetchFunction, [$path, $properties]);
80
-				return $instance;
81
-			} catch (\Exception $e) {
82
-				if(preg_match('/^The file \/.* does not exist\.$/', $e->getMessage()) !== 1
83
-					&& $e->getMessage() !== 'Unknown Error'
84
-					&& $e->getMessage() !== 'File Not Found.'
85
-				) {
86
-					$this->createClientContext();
87
-					# Unexpected Exception, pass it on
88
-					throw $e;
89
-				}
90
-			}
91
-			$this->createClientContext();
92
-		}
93
-
94
-		# Nothing succeeded, quit with not found
95
-		throw new NotFoundException('File or Folder not found');
96
-	}
97
-
98
-	public function fetchFile($relativeServerPath, array $properties = null) {
99
-		$this->ensureConnection();
100
-		$file = $this->context->getWeb()->getFileByServerRelativeUrl($relativeServerPath);
101
-		$this->loadAndExecute($file, $properties);
102
-		return $file;
103
-	}
104
-
105
-	public function fetchFolder($relativeServerPath, array $properties = null) {
106
-		$this->ensureConnection();
107
-		$folder = $this->context->getWeb()->getFolderByServerRelativeUrl($relativeServerPath);
108
-		$this->loadAndExecute($folder, $properties);
109
-		return $folder;
110
-	}
111
-
112
-	/**
113
-	 * adds a folder on the given server path
114
-	 *
115
-	 * @param string $relativeServerPath
116
-	 * @throws \Exception
117
-	 */
118
-	public function createFolder($relativeServerPath) {
119
-		$this->ensureConnection();
120
-
121
-		$serverUrlParts = explode('/', $relativeServerPath);
122
-		$newFolderName = array_pop($serverUrlParts);
123
-		$parentUrl =  implode('/', $serverUrlParts);
124
-
125
-		$parentFolder = $this->context->getWeb()->getFolderByServerRelativeUrl($parentUrl);
126
-		$folder = $parentFolder->getFolders()->add($newFolderName);
127
-
128
-		try {
129
-			$this->context->executeQuery();
130
-			return $folder;
131
-		} catch (\Exception $e) {
132
-			$this->createClientContext();
133
-			throw $e;
134
-		}
135
-	}
136
-
137
-	public function deleteFolder($relativeServerPath, Folder $folder = null) {
138
-		$this->ensureConnection();
139
-
140
-		try {
141
-			if($folder === null) {
142
-				$folder = $this->context->getWeb()->getFolderByServerRelativeUrl($relativeServerPath);
143
-			}
144
-			$folder->deleteObject();
145
-			$this->context->executeQuery();
146
-		} catch (\Exception $e) {
147
-			$this->createClientContext();
148
-			throw $e;
149
-		}
150
-	}
151
-
152
-	/**
153
-	 * @param $relativeServerPath
154
-	 * @param null $properties
155
-	 * @param Folder $folder
156
-	 * @return ClientObjectCollection[]
157
-	 */
158
-	public function fetchFolderContents($relativeServerPath, $properties = null, Folder $folder = null) {
159
-		$this->ensureConnection();
160
-		if($folder === null) {
161
-			$folder = $this->context->getWeb()->getFolderByServerRelativeUrl($relativeServerPath);
162
-		}
163
-
164
-		$folderCollection = $folder->getFolders();
165
-		$fileCollection = $folder->getFiles();
166
-		$this->context->load($folderCollection, $properties);
167
-		$this->context->load($fileCollection, $properties);
168
-		$this->context->executeQuery();
169
-
170
-		$collections = ['folders' => $folderCollection, 'files' => $fileCollection];
171
-
172
-		return $collections;
173
-	}
174
-
175
-	public function isHidden(ClientObject $file) {
176
-		// ClientObject itself does not have getListItemAllFields but is
177
-		// the common denominator of File and Folder
178
-		if(!$file instanceof File && !$file instanceof Folder) {
179
-			throw new \InvalidArgumentException('File or Folder expected');
180
-		}
181
-		if($file instanceof File) {
182
-			// it's expensive, we only check folders
183
-			return false;
184
-		}
185
-		$fields = $file->getListItemAllFields();
186
-		if($fields->getProperties() === []) {
187
-			$this->loadAndExecute($fields, ['Id', 'Hidden']);
188
-		}
189
-		$id = $fields->getProperty('Id');
190
-		$hidden = $fields->getProperty('Hidden'); // TODO: get someone to test this in SP 2013
191
-		if($hidden === false || $id !== null) {
192
-			// avoids listing hidden "Forms" folder (and its contents).
193
-			// Have not found a different mechanism to detect whether
194
-			// a file or folder is hidden. There used to be a Hidden
195
-			// field, but seems to have gone (since SP 2016?).
196
-			return false;
197
-		}
198
-		return true;
199
-	}
200
-
201
-	public function loadAndExecute(ClientObject $object, array $properties = null) {
202
-		$this->context->load($object, $properties);
203
-		$this->context->executeQuery();
204
-	}
205
-
206
-	/**
207
-	 * @return SPList
208
-	 * @throws NotFoundException
209
-	 */
210
-	private function getDocumentLibrary() {
211
-		if(!is_null($this->documentLibrary)) {
212
-			return $this->documentLibrary;
213
-		}
214
-
215
-		$lists = $this->context->getWeb()->getLists()->filter('Title eq \'' . $this->documentLibraryTitle . '\'')->top(1);
216
-		$this->context->load($lists)->executeQuery();
217
-		if ($lists->getCount() === 1 && $lists->getData()[0] instanceof SPList) {
218
-			$this->documentLibrary = $lists->getData()[0];
219
-			return $this->documentLibrary;
220
-		}
221
-
222
-		throw new NotFoundException('List not found');
223
-	}
224
-
225
-	/**
226
-	 * Set up necessary contexts for authentication and access to SharePoint
227
-	 *
228
-	 * @throws \InvalidArgumentException
229
-	 */
230
-	private function ensureConnection() {
231
-		if($this->context instanceof ClientContext) {
232
-			return;
233
-		}
234
-
235
-		if(!is_string($this->credentials['user']) || empty($this->credentials['user'])) {
236
-			throw new \InvalidArgumentException('No user given');
237
-		}
238
-		if(!is_string($this->credentials['password']) || empty($this->credentials['password'])) {
239
-			throw new \InvalidArgumentException('No password given');
240
-		}
241
-		$this->authContext = $this->contextsFactory->getAuthContext($this->credentials['user'], $this->credentials['password']);
242
-		$this->authContext->AuthType = CURLAUTH_NTLM;		# Basic auth does not work somehow…
243
-		$this->createClientContext();
244
-		# Auth is not triggered yet. This will happen when something is requested from SharePoint (on demand), e.g.:
245
-	}
246
-
247
-	/**
248
-	 * (re)creates the sharepoint client context
249
-	 */
250
-	private function createClientContext() {
251
-		$this->context = null;
252
-		$this->context = $this->contextsFactory->getClientContext($this->sharePointUrl, $this->authContext);
253
-	}
35
+    /** @var  ClientContext */
36
+    protected $context;
37
+
38
+    /** @var  AuthenticationContext */
39
+    protected $authContext;
40
+
41
+    /** @var  SPList */
42
+    protected $documentLibrary;
43
+
44
+    /** @var ContextsFactory */
45
+    private $contextsFactory;
46
+
47
+    /** @var  string */
48
+    private $sharePointUrl;
49
+
50
+    /** @var string[] */
51
+    private $credentials;
52
+
53
+    /** @var  string */
54
+    private $documentLibraryTitle;
55
+
56
+    public function __construct(ContextsFactory $contextsFactory, $sharePointUrl, array $credentials, $documentLibraryTitle) {
57
+        $this->contextsFactory = $contextsFactory;
58
+        $this->sharePointUrl = $sharePointUrl;
59
+        $this->credentials = $credentials;
60
+        $this->documentLibraryTitle = $documentLibraryTitle;
61
+    }
62
+
63
+    /**
64
+     * @param string $path
65
+     * @param array $properties
66
+     * @return File|Folder
67
+     * @throws \Exception
68
+     */
69
+    public function fetchFileOrFolder($path, array $properties = null) {
70
+        $fetchFileFunc = function ($path, $props) { return $this->fetchFile($path, $props);};
71
+        $fetchFolderFunc = function ($path, $props) { return $this->fetchFolder($path, $props);};
72
+        $fetchers = [ $fetchFileFunc, $fetchFolderFunc ];
73
+        if(strpos($path, '.') === false) {
74
+            $fetchers = array_reverse($fetchers);
75
+        }
76
+
77
+        foreach ($fetchers as $fetchFunction) {
78
+            try {
79
+                $instance = call_user_func_array($fetchFunction, [$path, $properties]);
80
+                return $instance;
81
+            } catch (\Exception $e) {
82
+                if(preg_match('/^The file \/.* does not exist\.$/', $e->getMessage()) !== 1
83
+                    && $e->getMessage() !== 'Unknown Error'
84
+                    && $e->getMessage() !== 'File Not Found.'
85
+                ) {
86
+                    $this->createClientContext();
87
+                    # Unexpected Exception, pass it on
88
+                    throw $e;
89
+                }
90
+            }
91
+            $this->createClientContext();
92
+        }
93
+
94
+        # Nothing succeeded, quit with not found
95
+        throw new NotFoundException('File or Folder not found');
96
+    }
97
+
98
+    public function fetchFile($relativeServerPath, array $properties = null) {
99
+        $this->ensureConnection();
100
+        $file = $this->context->getWeb()->getFileByServerRelativeUrl($relativeServerPath);
101
+        $this->loadAndExecute($file, $properties);
102
+        return $file;
103
+    }
104
+
105
+    public function fetchFolder($relativeServerPath, array $properties = null) {
106
+        $this->ensureConnection();
107
+        $folder = $this->context->getWeb()->getFolderByServerRelativeUrl($relativeServerPath);
108
+        $this->loadAndExecute($folder, $properties);
109
+        return $folder;
110
+    }
111
+
112
+    /**
113
+     * adds a folder on the given server path
114
+     *
115
+     * @param string $relativeServerPath
116
+     * @throws \Exception
117
+     */
118
+    public function createFolder($relativeServerPath) {
119
+        $this->ensureConnection();
120
+
121
+        $serverUrlParts = explode('/', $relativeServerPath);
122
+        $newFolderName = array_pop($serverUrlParts);
123
+        $parentUrl =  implode('/', $serverUrlParts);
124
+
125
+        $parentFolder = $this->context->getWeb()->getFolderByServerRelativeUrl($parentUrl);
126
+        $folder = $parentFolder->getFolders()->add($newFolderName);
127
+
128
+        try {
129
+            $this->context->executeQuery();
130
+            return $folder;
131
+        } catch (\Exception $e) {
132
+            $this->createClientContext();
133
+            throw $e;
134
+        }
135
+    }
136
+
137
+    public function deleteFolder($relativeServerPath, Folder $folder = null) {
138
+        $this->ensureConnection();
139
+
140
+        try {
141
+            if($folder === null) {
142
+                $folder = $this->context->getWeb()->getFolderByServerRelativeUrl($relativeServerPath);
143
+            }
144
+            $folder->deleteObject();
145
+            $this->context->executeQuery();
146
+        } catch (\Exception $e) {
147
+            $this->createClientContext();
148
+            throw $e;
149
+        }
150
+    }
151
+
152
+    /**
153
+     * @param $relativeServerPath
154
+     * @param null $properties
155
+     * @param Folder $folder
156
+     * @return ClientObjectCollection[]
157
+     */
158
+    public function fetchFolderContents($relativeServerPath, $properties = null, Folder $folder = null) {
159
+        $this->ensureConnection();
160
+        if($folder === null) {
161
+            $folder = $this->context->getWeb()->getFolderByServerRelativeUrl($relativeServerPath);
162
+        }
163
+
164
+        $folderCollection = $folder->getFolders();
165
+        $fileCollection = $folder->getFiles();
166
+        $this->context->load($folderCollection, $properties);
167
+        $this->context->load($fileCollection, $properties);
168
+        $this->context->executeQuery();
169
+
170
+        $collections = ['folders' => $folderCollection, 'files' => $fileCollection];
171
+
172
+        return $collections;
173
+    }
174
+
175
+    public function isHidden(ClientObject $file) {
176
+        // ClientObject itself does not have getListItemAllFields but is
177
+        // the common denominator of File and Folder
178
+        if(!$file instanceof File && !$file instanceof Folder) {
179
+            throw new \InvalidArgumentException('File or Folder expected');
180
+        }
181
+        if($file instanceof File) {
182
+            // it's expensive, we only check folders
183
+            return false;
184
+        }
185
+        $fields = $file->getListItemAllFields();
186
+        if($fields->getProperties() === []) {
187
+            $this->loadAndExecute($fields, ['Id', 'Hidden']);
188
+        }
189
+        $id = $fields->getProperty('Id');
190
+        $hidden = $fields->getProperty('Hidden'); // TODO: get someone to test this in SP 2013
191
+        if($hidden === false || $id !== null) {
192
+            // avoids listing hidden "Forms" folder (and its contents).
193
+            // Have not found a different mechanism to detect whether
194
+            // a file or folder is hidden. There used to be a Hidden
195
+            // field, but seems to have gone (since SP 2016?).
196
+            return false;
197
+        }
198
+        return true;
199
+    }
200
+
201
+    public function loadAndExecute(ClientObject $object, array $properties = null) {
202
+        $this->context->load($object, $properties);
203
+        $this->context->executeQuery();
204
+    }
205
+
206
+    /**
207
+     * @return SPList
208
+     * @throws NotFoundException
209
+     */
210
+    private function getDocumentLibrary() {
211
+        if(!is_null($this->documentLibrary)) {
212
+            return $this->documentLibrary;
213
+        }
214
+
215
+        $lists = $this->context->getWeb()->getLists()->filter('Title eq \'' . $this->documentLibraryTitle . '\'')->top(1);
216
+        $this->context->load($lists)->executeQuery();
217
+        if ($lists->getCount() === 1 && $lists->getData()[0] instanceof SPList) {
218
+            $this->documentLibrary = $lists->getData()[0];
219
+            return $this->documentLibrary;
220
+        }
221
+
222
+        throw new NotFoundException('List not found');
223
+    }
224
+
225
+    /**
226
+     * Set up necessary contexts for authentication and access to SharePoint
227
+     *
228
+     * @throws \InvalidArgumentException
229
+     */
230
+    private function ensureConnection() {
231
+        if($this->context instanceof ClientContext) {
232
+            return;
233
+        }
234
+
235
+        if(!is_string($this->credentials['user']) || empty($this->credentials['user'])) {
236
+            throw new \InvalidArgumentException('No user given');
237
+        }
238
+        if(!is_string($this->credentials['password']) || empty($this->credentials['password'])) {
239
+            throw new \InvalidArgumentException('No password given');
240
+        }
241
+        $this->authContext = $this->contextsFactory->getAuthContext($this->credentials['user'], $this->credentials['password']);
242
+        $this->authContext->AuthType = CURLAUTH_NTLM;		# Basic auth does not work somehow…
243
+        $this->createClientContext();
244
+        # Auth is not triggered yet. This will happen when something is requested from SharePoint (on demand), e.g.:
245
+    }
246
+
247
+    /**
248
+     * (re)creates the sharepoint client context
249
+     */
250
+    private function createClientContext() {
251
+        $this->context = null;
252
+        $this->context = $this->contextsFactory->getClientContext($this->sharePointUrl, $this->authContext);
253
+    }
254 254
 }
Please login to merge, or discard this patch.