Completed
Push — stable8.2 ( cd78b9...7e92ef )
by Morris
47:01
created

Shared_Updater   C

Complexity

Total Complexity 36

Size/Duplication

Total Lines 190
Duplicated Lines 9.47 %

Coupling/Cohesion

Components 2
Dependencies 19

Test Coverage

Coverage 73%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 36
c 1
b 0
f 0
lcom 2
cbo 19
dl 18
loc 190
ccs 73
cts 100
cp 0.73
rs 6.05

9 Methods

Rating   Name   Duplication   Size   Complexity  
A renameHook() 0 4 1
A correctUsersFolder() 0 17 3
B moveShareToShare() 0 35 6
A deleteHook() 0 3 1
B postShareHook() 0 16 6
B postUnshareHook() 9 20 9
B postUnshareFromSelfHook() 9 13 6
A fixBrokenSharesOnAppUpdate() 0 8 1
A renameChildren() 0 15 3

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 Björn Schießle <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Michael Gapczynski <[email protected]>
6
 * @author Morris Jobke <[email protected]>
7
 * @author Robin Appelman <[email protected]>
8
 * @author Roeland Jago Douma <[email protected]>
9
 * @author Vincent Petry <[email protected]>
10
 *
11
 * @copyright Copyright (c) 2015, ownCloud, Inc.
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OC\Files\Cache;
29
30
class Shared_Updater {
31
32
	/**
33
	 * Walk up the users file tree and update the etags.
34
	 *
35
	 * @param string $user user id
36
	 * @param string $path share mount point path, relative to the user's "files" folder
37
	 */
38 30
	static private function correctUsersFolder($user, $path) {
39
		// $path points to the mount point which is a virtual folder, so we start with
40
		// the parent
41 30
		$path = '/' . ltrim($path, '/');
42 30
		$path = '/files' . dirname($path);
43 30
		\OC\Files\Filesystem::initMountPoints($user);
44 30
		$view = new \OC\Files\View('/' . $user);
45 30
		if ($view->file_exists($path)) {
46 27
			while ($path !== dirname($path)) {
47 27
				$etag = $view->getETag($path);
48 27
				$view->putFileInfo($path, array('etag' => $etag));
49 27
				$path = dirname($path);
50 27
			}
51 27
		} else {
52 3
			\OCP\Util::writeLog('files_sharing', 'can not update etags on ' . $path . ' for user ' . $user . '. Path does not exists', \OCP\Util::DEBUG);
53
		}
54 30
	}
55
56
	/**
57
	 * @param array $params
58
	 */
59 3
	static public function renameHook($params) {
60 3
		self::renameChildren($params['oldpath'], $params['newpath']);
61 3
		self::moveShareToShare($params['newpath']);
62 3
	}
63
64
	/**
65
	 * Fix for https://github.com/owncloud/core/issues/20769
66
	 *
67
	 * The owner is allowed to move their files (if they are shared) into a receiving folder
68
	 * In this case we need to update the parent of the moved share. Since they are
69
	 * effectively handing over ownership of the file the rest of the code needs to know
70
	 * they need to build up the reshare tree.
71
	 *
72
	 * @param string $path
73
	 */
74 3
	static private function moveShareToShare($path) {
75 3
		$userFolder = \OC::$server->getUserFolder();
76 3
77
		// If the user folder can't be constructed (e.g. link share) just return.
78 3
		if ($userFolder === null) {
79 3
			return;
80
		}
81
82 3
		$src = $userFolder->get($path);
83 3
84
		$type = $src instanceof \OCP\Files\File ? 'file' : 'folder';
85
		$shares = \OCP\Share::getItemShared($type, $src->getId());
86
87
		// If the path we move is not a share we don't care
88
		if (empty($shares)) {
89
			return;
90
		}
91
92
		// Check if the destination is inside a share
93
		$mountManager = \OC::$server->getMountManager();
94
		$dstMount = $mountManager->find($src->getPath());
95
		if (!($dstMount instanceof \OCA\Files_Sharing\SharedMount)) {
96
			return;
97
		}
98
99
		$parenShare = $dstMount->getShare();
100
101
		foreach ($shares as $share) {
102
			$qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
103
			$qb->update('share')
104
					->set('parent', $qb->createNamedParameter($parenShare['id']))
105
					->where($qb->expr()->eq('id', $qb->createNamedParameter($share['id'])))
106
					->execute();
107 9
		}
108 9
	}
109 9
110
	/**
111
	 * @param array $params
112
	 */
113
	static public function deleteHook($params) {
114
		$path = $params['path'];
0 ignored issues
show
Unused Code introduced by
$path is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
115 30
	}
116
117 30
	/**
118
	 * update etags if a file was shared
119 30
	 * @param array $params
120 30
	 */
121
	static public function postShareHook($params) {
122 30
123 28
		if ($params['itemType'] === 'folder' || $params['itemType'] === 'file') {
124 30
125 2
			$shareWith = $params['shareWith'];
126 2
			$shareType = $params['shareType'];
127 2
128 2
			if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
129 30
				self::correctUsersFolder($shareWith, $params['fileTarget']);
130 30
			} elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
131
				foreach (\OC_Group::usersInGroup($shareWith) as $user) {
132
					self::correctUsersFolder($user, $params['fileTarget']);
133
				}
134
			}
135
		}
136
	}
137 4
138
	/**
139
	 * update etags if a file was unshared
140 4
	 *
141 4
	 * @param array $params
142 4
	 */
143
	static public function postUnshareHook($params) {
144 4
145
		// only update etags for file/folders shared to local users/groups
146 4
		if (($params['itemType'] === 'file' || $params['itemType'] === 'folder') &&
147 4
				$params['shareType'] !== \OCP\Share::SHARE_TYPE_LINK &&
148
				$params['shareType'] !== \OCP\Share::SHARE_TYPE_REMOTE) {
149
150
			$deletedShares = isset($params['deletedShares']) ? $params['deletedShares'] : array();
151
152 4 View Code Duplication
			foreach ($deletedShares as $share) {
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...
153
				if ($share['shareType'] === \OCP\Share::SHARE_TYPE_GROUP) {
154 4
					foreach (\OC_Group::usersInGroup($share['shareWith']) as $user) {
155 4
						self::correctUsersFolder($user, $share['fileTarget']);
156 4
					}
157
				} else {
158
					self::correctUsersFolder($share['shareWith'], $share['fileTarget']);
159
				}
160
			}
161
		}
162 2
	}
163 2
164 2
	/**
165 2
	 * update etags if file was unshared from self
166
	 * @param array $params
167
	 */
168
	static public function postUnshareFromSelfHook($params) {
169
		if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
170 2 View Code Duplication
			foreach ($params['unsharedItems'] as $item) {
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...
171
				if ($item['shareType'] === \OCP\Share::SHARE_TYPE_GROUP) {
172 2
					foreach (\OC_Group::usersInGroup($item['shareWith']) as $user) {
173 2
						self::correctUsersFolder($user, $item['fileTarget']);
174 2
					}
175
				} else {
176
					self::correctUsersFolder($item['shareWith'], $item['fileTarget']);
177
				}
178
			}
179
		}
180
	}
181
182
	/**
183
	 * clean up oc_share table from files which are no longer exists
184
	 *
185
	 * This fixes issues from updates from files_sharing < 0.3.5.6 (ownCloud 4.5)
186
	 * It will just be called during the update of the app
187
	 */
188
	static public function fixBrokenSharesOnAppUpdate() {
189
		// delete all shares where the original file no longer exists
190
		$findAndRemoveShares = \OCP\DB::prepare('DELETE FROM `*PREFIX*share` ' .
191
			'WHERE `item_type` IN (\'file\', \'folder\') ' .
192
			'AND `file_source` NOT IN (SELECT `fileid` FROM `*PREFIX*filecache`)'
193
		);
194
		$findAndRemoveShares->execute(array());
195
	}
196
197 3
	/**
198
	 * rename mount point from the children if the parent was renamed
199 3
	 *
200 3
	 * @param string $oldPath old path relative to data/user/files
201
	 * @param string $newPath new path relative to data/user/files
202 3
	 */
203 3
	static private function renameChildren($oldPath, $newPath) {
204 3
205 1
		$absNewPath =  \OC\Files\Filesystem::normalizePath('/' . \OCP\User::getUser() . '/files/' . $newPath);
206 1
		$absOldPath =  \OC\Files\Filesystem::normalizePath('/' . \OCP\User::getUser() . '/files/' . $oldPath);
207 1
208 1
		$mountManager = \OC\Files\Filesystem::getMountManager();
209 1
		$mountedShares = $mountManager->findIn('/' . \OCP\User::getUser() . '/files/' . $oldPath);
210 3
		foreach ($mountedShares as $mount) {
211 3
			if ($mount->getStorage()->instanceOfStorage('OCA\Files_Sharing\ISharedStorage')) {
212
				$mountPoint = $mount->getMountPoint();
213
				$target = str_replace($absOldPath, $absNewPath, $mountPoint);
214
				$mount->moveMount($target);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class OC\Files\Mount\MountPoint as the method moveMount() does only exist in the following sub-classes of OC\Files\Mount\MountPoint: DummyTestClassSharedMount, OCA\Files_External\Lib\PersonalMount, OCA\Files_Sharing\External\Mount, OCA\Files_Sharing\SharedMount, Test\TestMoveableMountPoint. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
215
			}
216
		}
217
	}
218
219
}
220