Completed
Pull Request — master (#12)
by Piotr
02:39
created

PaperHiveController   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 296
Duplicated Lines 17.91 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 92.06%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 39
lcom 1
cbo 0
dl 53
loc 296
ccs 116
cts 126
cp 0.9206
rs 8.2857
c 1
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A adjustPaperHiveExtensions() 0 8 2
B loadPaperHiveIdFromFile() 9 18 6
C loadMetadata() 10 51 12
A fetchDiscussions() 9 9 2
A fetchDocument() 9 9 2
C generatePaperHiveDocument() 16 61 13
A getPaperHiveDetails() 0 9 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * @author Piotr Mrowczynski <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2017, Piotr Mrowczynski.
6
 * @license AGPL-3.0
7
 *
8
 * This code is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License, version 3,
10
 * as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License, version 3,
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19
 *
20
 */
21
22
namespace OCA\Files_PaperHive\Controller;
23
24
use OCP\Http\Client\IClient;
25
use OC\Files\View;
26
use OC\HintException;
27
use OCP\AppFramework\Controller;
28
use OCP\AppFramework\Http;
29
use OCP\AppFramework\Http\DataResponse;
30
use OCP\Files\ForbiddenException;
31
use OCP\IL10N;
32
use OCP\ILogger;
33
use OCP\IRequest;
34
use OCP\Lock\LockedException;
35
36
class PaperHiveController extends Controller{
37
38
	/** @var IL10N */
39
	private $l;
40
41
	/** @var View */
42
	private $view;
43
44
	/** @var ILogger */
45
	private $logger;
46
47
	/** @var \OCP\Http\Client\IClient */
48
	private $client;
49
50
	/**
51
	 * Paperhive base URL
52
	 */
53
	private $paperhive_base_url = 'https://paperhive.org';
54
55
	/**
56
	 * Paperhive url for document API
57
	 */
58
	private $paperhive_api_url = '/api/documents/';
59
60
	/**
61
	 * Paperhive url for text API
62
	 */
63
	private $paperhive_document_url = '/documents/';
64
65
	/**
66
	 * Paperhive url for discussions API
67
	 */
68
	private $paperhive_discussion_api_endpoint = '/discussions';
69
70
	/**
71
	 * Paperhive file extension
72
	 */
73
	private $paperhive_file_extension = '.paperhive';
74
75
	/**
76
	 * Paperhive revision extension
77
	 */
78
	private $paperhive_rev_extension = '.rev';
79
	
80
	/**
81
	 * @NoAdminRequired
82
	 *
83
	 * @param string $appName
84
	 * @param IRequest $request
85
	 * @param IL10N $l10n
86
	 * @param View $view
87
	 * @param ILogger $logger
88
	 */
89 17
	public function __construct($appName,
90
								IRequest $request,
91
								IL10N $l10n,
92
								View $view,
93
								ILogger $logger,
94
								IClient $client) {
95 17
		parent::__construct($appName, $request);
96 17
		$this->l = $l10n;
97 17
		$this->view = $view;
98 17
		$this->logger = $logger;
99 17
		$this->client = $client;
100 17
	}
101
102
	/**
103
	 * Adjust paperhive extension for correct one
104
	 *
105
	 * @param string $path
106
	 * @param string $revision
107
	 * @return boolean - returns true if success or false in case of error
108
	 */
109 2
	private function adjustPaperHiveExtensions($path, $revision) {
110 2
		$pathWithoutPHExtension = str_replace($this->paperhive_file_extension, '', $path);
111 2
		$newPath = $pathWithoutPHExtension . $this->paperhive_rev_extension . $revision . $this->paperhive_file_extension;
112 2
		if ($this->view->rename($path, $newPath) !== false) {
113 1
			return true;
114
		}
115 1
		return false;
116
	}
117
118
	/**
119
	 * Load paperhive metadata from file, 
120
	 * adjust filename if wrong and return PaperHive ID
121
	 *	 *
122
	 * @param string $dir
123
	 * @param string $filename
124
	 * @return string/boolean - returns PaperHive ID or false in case of error
0 ignored issues
show
Documentation introduced by
The doc-type string/boolean could not be parsed: Unknown type name "string/boolean" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
125
	 */
126 7
	private function loadPaperHiveIdFromFile($dir, $filename) {
127 7
		$path = $dir . '/' . $filename;
128 7
		if (!$this->view->file_exists($path)){
129 1
			return false;
130
		}
131
		
132 6
		$fileContents = $this->view->file_get_contents($path);
133 3 View Code Duplication
		if ($fileContents !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
134 3
			$paperHiveObject = json_decode($fileContents, true);
135 3
			if (json_last_error() === JSON_ERROR_NONE && isset($paperHiveObject['id'])) {
136 2
				$paperHiveId = $paperHiveObject['id'];
137 2
				if($this->adjustPaperHiveExtensions($path, $paperHiveId)){
138 1
					return $paperHiveId;
139
				}
140 1
			}
141 2
		}
142 2
		return false;
143
	}
144
145
	/**
146
	 * load paperhive metadata and if needed, discussion count
147
	 *
148
	 * @NoAdminRequired
149
	 *
150
	 * @param string $dir
151
	 * @param string $filename
152
	 * @param boolean $fetchDiscussions
153
	 * @return DataResponse
154
	 */
155 10
	public function loadMetadata($dir, $filename, $fetchDiscussions) {
156
		try {
157 10
			$filenameParts = explode('.', $filename);
158 10
			if (sizeof($filenameParts) > 1) {
159
				// Correct, file needs filename and extension
160 9
				if (sizeof($filenameParts) === 2 || (sizeof($filenameParts) > 2 &&
161 9
						strpos('.'.$filenameParts[sizeof($filenameParts)-2],$this->paperhive_rev_extension) === false)){
162
					// File needs correction, since been renamed or is obsolete
163 7
					$revision = $this->loadPaperHiveIdFromFile($dir, $filename);
164 4 View Code Duplication
					if ($revision === false){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
165 3
						return new DataResponse(['message' => (string)$this->l->t('File is obsolete, incorrectly renamed or cannot be read.')], Http::STATUS_BAD_REQUEST);
166
					}
167 1
				} else {
168
					// File has correct format, and revision is the second extension
169
					// Add extension dot since explode removed it and replace rev extension with empty string
170 2
					$revisionString = '.'.$filenameParts[sizeof($filenameParts)-2];
171 2
					$revision = str_replace($this->paperhive_rev_extension, '', $revisionString);
172
				}
173
174 3
				$disscussionCount = -1;
175 3 View Code Duplication
				if ($fetchDiscussions == "true") {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176 2
					$paperHiveString = $this->fetchDiscussions($revision);
177 2
					$paperHiveDiscussions = json_decode($paperHiveString, true);
178 2
					if (json_last_error() === JSON_ERROR_NONE && isset($paperHiveDiscussions['discussions'])) {
179 2
						$disscussionCount = count($paperHiveDiscussions['discussions']);
180 2
					}
181 2
				}
182
183 3
				return new DataResponse([
184 3
					'paperhive_base_url' => $this->paperhive_base_url,
185 3
					'paperhive_api_url' => $this->paperhive_api_url,
186 3
					'paperhive_document_url' => $this->paperhive_document_url,
187 3
					'paperhive_document_id' => $revision,
188 3
					'paperhive_discussion_api_endpoint' => $this->paperhive_discussion_api_endpoint,
189 3
					'paperhive_extension' => $this->paperhive_file_extension,
190
					'paperhive_discussion_count' => $disscussionCount
191 3
				], Http::STATUS_OK);
192
			} else {
193 1
				return new DataResponse(['message' => (string)$this->l->t('Invalid file path supplied.')], Http::STATUS_BAD_REQUEST);
194
			}
195
196 3
		} catch (LockedException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Lock\LockedException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
197 1
			$message = (string) $this->l->t('The file is locked.');
198 1
			return new DataResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
199 2
		} catch (ForbiddenException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\ForbiddenException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
200 1
			return new DataResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
201 1
		} catch (\Exception $e) {
202 1
			$message = (string)$this->l->t('An internal server error occurred.');
203 1
			return new DataResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
204
		}
205
	}
206
207
	/**
208
	 * Does the call to PaperHive Discussions API and returns disussions for specific it for specific BookID
209
	 *
210
	 * @NoAdminRequired
211
	 *
212
	 * @param string $bookID
213
	 * @return string
214
	 */
215 8 View Code Duplication
	private function fetchDiscussions($bookID) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
216 8
		$urlDiscussions = $this->paperhive_base_url . $this->paperhive_api_url . $bookID . $this->paperhive_discussion_api_endpoint;
217
		try {
218 8
			$response = $this->client->get($urlDiscussions, []);
219 8
		} catch (\Exception $e) {
220
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by OCA\Files_PaperHive\Cont...oller::fetchDiscussions of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
221
		}
222 8
		return $response->getBody();
223
	}
224
225
	/**
226
	 * Does the call to PaperHive Documents API and returns the JSON for a book for specific BookID
227
	 *
228
	 * @NoAdminRequired
229
	 *
230
	 * @param string $bookID
231
	 * @return string/boolean
0 ignored issues
show
Documentation introduced by
The doc-type string/boolean could not be parsed: Unknown type name "string/boolean" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
232
	 */
233 6 View Code Duplication
	private function fetchDocument($bookID) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
234 6
		$urlDocument = $this->paperhive_base_url . $this->paperhive_api_url . $bookID;
235
		try {
236 6
			$response = $this->client->get($urlDocument, []);
237 6
		} catch (\Exception $e) {
238
			return false;
239
		}
240 6
		return $response->getBody();
241
	}
242
243
	/**
244
	 * Gets the information about the book for specific BookID and saves as a file in requested directory
245
	 *
246
	 * @NoAdminRequired
247
	 *
248
	 * @param string $dir
249
	 * @param string $bookID
250
	 * @return DataResponse
251
	 */
252 6
	public function generatePaperHiveDocument($dir, $bookID) {
253
		// Send request to PaperHive
254 6
		$paperHiveString = $this->fetchDiscussions($bookID);
255 6 View Code Duplication
		if ($paperHiveString === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
256
			$message = (string)$this->l->t('Problem connecting to PaperHive.');
257
			return new DataResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
258
		}
259 6
		$paperHiveDiscussions = json_decode($paperHiveString, true);
260 6
		$discussionCount = -1;
261 6
		if (json_last_error() === JSON_ERROR_NONE && isset($paperHiveDiscussions['discussions'])) {
262 6
			$discussionCount = count($paperHiveDiscussions['discussions']);
263 6
		}
264
		
265 6
		$paperHiveString = $this->fetchDocument($bookID);
266 6 View Code Duplication
		if ($paperHiveString === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
267
			$message = (string)$this->l->t('Problem connecting to PaperHive.');
268
			return new DataResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
269
		}
270 6
		$paperHiveObject = json_decode($paperHiveString, true);
271
		
272
273 6
		if (json_last_error() === JSON_ERROR_NONE && isset($paperHiveObject['title'])) {
274 6
			$extension = $this->paperhive_rev_extension . $bookID . $this->paperhive_file_extension;
275 6
			$filename = $paperHiveObject['title'] . $extension;
276
277 6
			if($dir == '/') {
278 3
				$path = $dir . $filename;
279 3
			} else {
280 3
				$path = $dir . '/' . $filename;
281
			}
282
			
283
			try {
284 6
				$exists = $this->view->file_exists($path);
285 6 View Code Duplication
				if ($exists) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
286 1
					$message = (string) $this->l->t('The file already exists.');
287 1
					return new DataResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
288
				}
289 5
				$filecontents = iconv(mb_detect_encoding($paperHiveString), "UTF-8", $paperHiveString);
290
				try {
291 5
					$this->view->file_put_contents($path, $filecontents);
292 5
				} catch (LockedException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Lock\LockedException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
293 1
					$message = (string) $this->l->t('The file is locked.');
294 1
					return new DataResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
295 2
				} catch (ForbiddenException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\ForbiddenException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
296 1
					return new DataResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
297
				}
298
				// Clear statcache
299 2
				clearstatcache();
300 2
				return new DataResponse(['path' => $path, 'filename' => $paperHiveObject['title'], 'extension' => $extension, 'discussionCount' => $discussionCount], Http::STATUS_OK);
301 1
			} catch (HintException $e) {
0 ignored issues
show
Bug introduced by
The class OC\HintException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
302
				$message = (string)$e->getHint();
303
				return new DataResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
304 1
			} catch (\Exception $e) {
305 1
				$message = (string)$this->l->t('An internal server error occurred.');
306 1
				return new DataResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
307
			}
308 View Code Duplication
		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
309
			$message = (string)$this->l->t('Received wrong response from PaperHive.');
310
			return new DataResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
311
		}
312
	}
313
	
314
	/**
315
	 * Returns all required PaperHive setting
316
	 *
317
	 * @NoAdminRequired
318
	 *
319
	 * @param string $bookID
0 ignored issues
show
Bug introduced by
There is no parameter named $bookID. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
320
	 * @return DataResponse
321
	 */
322 1
	public function getPaperHiveDetails() {
323 1
		return new DataResponse([
324 1
			'paperhive_base_url' => $this->paperhive_base_url,
325 1
			'paperhive_api_url' => $this->paperhive_api_url,
326 1
			'paperhive_document_url' => $this->paperhive_document_url,
327 1
			'paperhive_discussion_api_endpoint' => $this->paperhive_discussion_api_endpoint,
328 1
			'paperhive_extension' => $this->paperhive_file_extension
329 1
		], Http::STATUS_OK);
330
	}
331
}
332