Passed
Push — master ( 725fec...3c5692 )
by Morris
11:25 queued 12s
created
apps/files/lib/Activity/Settings/FileFavoriteChanged.php 1 patch
Indentation   +54 added lines, -54 removed lines patch added patch discarded remove patch
@@ -24,65 +24,65 @@
 block discarded – undo
24 24
 namespace OCA\Files\Activity\Settings;
25 25
 
26 26
 class FileFavoriteChanged extends FileActivitySettings {
27
-	/**
28
-	 * @return string Lowercase a-z and underscore only identifier
29
-	 * @since 11.0.0
30
-	 */
31
-	public function getIdentifier() {
32
-		return 'file_favorite_changed';
33
-	}
27
+    /**
28
+     * @return string Lowercase a-z and underscore only identifier
29
+     * @since 11.0.0
30
+     */
31
+    public function getIdentifier() {
32
+        return 'file_favorite_changed';
33
+    }
34 34
 
35
-	/**
36
-	 * @return string A translated string
37
-	 * @since 11.0.0
38
-	 */
39
-	public function getName() {
40
-		return $this->l->t('A favorite file or folder has been <strong>changed</strong>');
41
-	}
35
+    /**
36
+     * @return string A translated string
37
+     * @since 11.0.0
38
+     */
39
+    public function getName() {
40
+        return $this->l->t('A favorite file or folder has been <strong>changed</strong>');
41
+    }
42 42
 
43
-	/**
44
-	 * @return int whether the filter should be rather on the top or bottom of
45
-	 * the admin section. The filters are arranged in ascending order of the
46
-	 * priority values. It is required to return a value between 0 and 100.
47
-	 * @since 11.0.0
48
-	 */
49
-	public function getPriority() {
50
-		return 1;
51
-	}
43
+    /**
44
+     * @return int whether the filter should be rather on the top or bottom of
45
+     * the admin section. The filters are arranged in ascending order of the
46
+     * priority values. It is required to return a value between 0 and 100.
47
+     * @since 11.0.0
48
+     */
49
+    public function getPriority() {
50
+        return 1;
51
+    }
52 52
 
53
-	/**
54
-	 * @return bool True when the option can be changed for the stream
55
-	 * @since 11.0.0
56
-	 */
57
-	public function canChangeStream() {
58
-		return true;
59
-	}
53
+    /**
54
+     * @return bool True when the option can be changed for the stream
55
+     * @since 11.0.0
56
+     */
57
+    public function canChangeStream() {
58
+        return true;
59
+    }
60 60
 
61
-	/**
62
-	 * @return bool True when the option can be changed for the stream
63
-	 * @since 11.0.0
64
-	 */
65
-	public function isDefaultEnabledStream() {
66
-		return true;
67
-	}
61
+    /**
62
+     * @return bool True when the option can be changed for the stream
63
+     * @since 11.0.0
64
+     */
65
+    public function isDefaultEnabledStream() {
66
+        return true;
67
+    }
68 68
 
69
-	/**
70
-	 * @return bool True when the option can be changed for the mail
71
-	 * @since 11.0.0
72
-	 */
73
-	public function canChangeMail() {
74
-		return true;
75
-	}
69
+    /**
70
+     * @return bool True when the option can be changed for the mail
71
+     * @since 11.0.0
72
+     */
73
+    public function canChangeMail() {
74
+        return true;
75
+    }
76 76
 
77
-	/**
78
-	 * @return bool True when the option can be changed for the stream
79
-	 * @since 11.0.0
80
-	 */
81
-	public function isDefaultEnabledMail() {
82
-		return false;
83
-	}
77
+    /**
78
+     * @return bool True when the option can be changed for the stream
79
+     * @since 11.0.0
80
+     */
81
+    public function isDefaultEnabledMail() {
82
+        return false;
83
+    }
84 84
 
85
-	public function isDefaultEnabledNotification() {
86
-		return true;
87
-	}
85
+    public function isDefaultEnabledNotification() {
86
+        return true;
87
+    }
88 88
 }
Please login to merge, or discard this patch.
apps/files/lib/Activity/Settings/FileChanged.php 1 patch
Indentation   +35 added lines, -35 removed lines patch added patch discarded remove patch
@@ -24,45 +24,45 @@
 block discarded – undo
24 24
 namespace OCA\Files\Activity\Settings;
25 25
 
26 26
 class FileChanged extends FileActivitySettings {
27
-	/**
28
-	 * @return string Lowercase a-z and underscore only identifier
29
-	 * @since 11.0.0
30
-	 */
31
-	public function getIdentifier() {
32
-		return 'file_changed';
33
-	}
27
+    /**
28
+     * @return string Lowercase a-z and underscore only identifier
29
+     * @since 11.0.0
30
+     */
31
+    public function getIdentifier() {
32
+        return 'file_changed';
33
+    }
34 34
 
35
-	/**
36
-	 * @return string A translated string
37
-	 * @since 11.0.0
38
-	 */
39
-	public function getName() {
40
-		return $this->l->t('A file or folder has been <strong>changed</strong>');
41
-	}
35
+    /**
36
+     * @return string A translated string
37
+     * @since 11.0.0
38
+     */
39
+    public function getName() {
40
+        return $this->l->t('A file or folder has been <strong>changed</strong>');
41
+    }
42 42
 
43
-	/**
44
-	 * @return int whether the filter should be rather on the top or bottom of
45
-	 * the admin section. The filters are arranged in ascending order of the
46
-	 * priority values. It is required to return a value between 0 and 100.
47
-	 * @since 11.0.0
48
-	 */
49
-	public function getPriority() {
50
-		return 1;
51
-	}
43
+    /**
44
+     * @return int whether the filter should be rather on the top or bottom of
45
+     * the admin section. The filters are arranged in ascending order of the
46
+     * priority values. It is required to return a value between 0 and 100.
47
+     * @since 11.0.0
48
+     */
49
+    public function getPriority() {
50
+        return 1;
51
+    }
52 52
 
53
-	public function canChangeMail() {
54
-		return false;
55
-	}
53
+    public function canChangeMail() {
54
+        return false;
55
+    }
56 56
 
57
-	public function isDefaultEnabledMail() {
58
-		return false;
59
-	}
57
+    public function isDefaultEnabledMail() {
58
+        return false;
59
+    }
60 60
 
61
-	public function canChangeNotification() {
62
-		return false;
63
-	}
61
+    public function canChangeNotification() {
62
+        return false;
63
+    }
64 64
 
65
-	public function isDefaultEnabledNotification() {
66
-		return false;
67
-	}
65
+    public function isDefaultEnabledNotification() {
66
+        return false;
67
+    }
68 68
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Activity/Settings/PublicLinks.php 1 patch
Indentation   +51 added lines, -51 removed lines patch added patch discarded remove patch
@@ -24,61 +24,61 @@
 block discarded – undo
24 24
 namespace OCA\Files_Sharing\Activity\Settings;
25 25
 
26 26
 class PublicLinks extends ShareActivitySettings {
27
-	/**
28
-	 * @return string Lowercase a-z and underscore only identifier
29
-	 * @since 11.0.0
30
-	 */
31
-	public function getIdentifier() {
32
-		return 'public_links';
33
-	}
27
+    /**
28
+     * @return string Lowercase a-z and underscore only identifier
29
+     * @since 11.0.0
30
+     */
31
+    public function getIdentifier() {
32
+        return 'public_links';
33
+    }
34 34
 
35
-	/**
36
-	 * @return string A translated string
37
-	 * @since 11.0.0
38
-	 */
39
-	public function getName() {
40
-		return $this->l->t('A file or folder shared by mail or by public link was <strong>downloaded</strong>');
41
-	}
35
+    /**
36
+     * @return string A translated string
37
+     * @since 11.0.0
38
+     */
39
+    public function getName() {
40
+        return $this->l->t('A file or folder shared by mail or by public link was <strong>downloaded</strong>');
41
+    }
42 42
 
43
-	/**
44
-	 * @return int whether the filter should be rather on the top or bottom of
45
-	 * the admin section. The filters are arranged in ascending order of the
46
-	 * priority values. It is required to return a value between 0 and 100.
47
-	 * @since 11.0.0
48
-	 */
49
-	public function getPriority() {
50
-		return 20;
51
-	}
43
+    /**
44
+     * @return int whether the filter should be rather on the top or bottom of
45
+     * the admin section. The filters are arranged in ascending order of the
46
+     * priority values. It is required to return a value between 0 and 100.
47
+     * @since 11.0.0
48
+     */
49
+    public function getPriority() {
50
+        return 20;
51
+    }
52 52
 
53
-	/**
54
-	 * @return bool True when the option can be changed for the stream
55
-	 * @since 11.0.0
56
-	 */
57
-	public function canChangeStream() {
58
-		return true;
59
-	}
53
+    /**
54
+     * @return bool True when the option can be changed for the stream
55
+     * @since 11.0.0
56
+     */
57
+    public function canChangeStream() {
58
+        return true;
59
+    }
60 60
 
61
-	/**
62
-	 * @return bool True when the option can be changed for the stream
63
-	 * @since 11.0.0
64
-	 */
65
-	public function isDefaultEnabledStream() {
66
-		return true;
67
-	}
61
+    /**
62
+     * @return bool True when the option can be changed for the stream
63
+     * @since 11.0.0
64
+     */
65
+    public function isDefaultEnabledStream() {
66
+        return true;
67
+    }
68 68
 
69
-	/**
70
-	 * @return bool True when the option can be changed for the mail
71
-	 * @since 11.0.0
72
-	 */
73
-	public function canChangeMail() {
74
-		return true;
75
-	}
69
+    /**
70
+     * @return bool True when the option can be changed for the mail
71
+     * @since 11.0.0
72
+     */
73
+    public function canChangeMail() {
74
+        return true;
75
+    }
76 76
 
77
-	/**
78
-	 * @return bool True when the option can be changed for the stream
79
-	 * @since 11.0.0
80
-	 */
81
-	public function isDefaultEnabledMail() {
82
-		return false;
83
-	}
77
+    /**
78
+     * @return bool True when the option can be changed for the stream
79
+     * @since 11.0.0
80
+     */
81
+    public function isDefaultEnabledMail() {
82
+        return false;
83
+    }
84 84
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Activity/Settings/Shared.php 1 patch
Indentation   +51 added lines, -51 removed lines patch added patch discarded remove patch
@@ -25,61 +25,61 @@
 block discarded – undo
25 25
 namespace OCA\Files_Sharing\Activity\Settings;
26 26
 
27 27
 class Shared extends ShareActivitySettings {
28
-	/**
29
-	 * @return string Lowercase a-z and underscore only identifier
30
-	 * @since 11.0.0
31
-	 */
32
-	public function getIdentifier() {
33
-		return 'shared';
34
-	}
28
+    /**
29
+     * @return string Lowercase a-z and underscore only identifier
30
+     * @since 11.0.0
31
+     */
32
+    public function getIdentifier() {
33
+        return 'shared';
34
+    }
35 35
 
36
-	/**
37
-	 * @return string A translated string
38
-	 * @since 11.0.0
39
-	 */
40
-	public function getName() {
41
-		return $this->l->t('A file or folder has been <strong>shared</strong>');
42
-	}
36
+    /**
37
+     * @return string A translated string
38
+     * @since 11.0.0
39
+     */
40
+    public function getName() {
41
+        return $this->l->t('A file or folder has been <strong>shared</strong>');
42
+    }
43 43
 
44
-	/**
45
-	 * @return int whether the filter should be rather on the top or bottom of
46
-	 * the admin section. The filters are arranged in ascending order of the
47
-	 * priority values. It is required to return a value between 0 and 100.
48
-	 * @since 11.0.0
49
-	 */
50
-	public function getPriority() {
51
-		return 10;
52
-	}
44
+    /**
45
+     * @return int whether the filter should be rather on the top or bottom of
46
+     * the admin section. The filters are arranged in ascending order of the
47
+     * priority values. It is required to return a value between 0 and 100.
48
+     * @since 11.0.0
49
+     */
50
+    public function getPriority() {
51
+        return 10;
52
+    }
53 53
 
54
-	/**
55
-	 * @return bool True when the option can be changed for the stream
56
-	 * @since 11.0.0
57
-	 */
58
-	public function canChangeStream() {
59
-		return true;
60
-	}
54
+    /**
55
+     * @return bool True when the option can be changed for the stream
56
+     * @since 11.0.0
57
+     */
58
+    public function canChangeStream() {
59
+        return true;
60
+    }
61 61
 
62
-	/**
63
-	 * @return bool True when the option can be changed for the stream
64
-	 * @since 11.0.0
65
-	 */
66
-	public function isDefaultEnabledStream() {
67
-		return true;
68
-	}
62
+    /**
63
+     * @return bool True when the option can be changed for the stream
64
+     * @since 11.0.0
65
+     */
66
+    public function isDefaultEnabledStream() {
67
+        return true;
68
+    }
69 69
 
70
-	/**
71
-	 * @return bool True when the option can be changed for the mail
72
-	 * @since 11.0.0
73
-	 */
74
-	public function canChangeMail() {
75
-		return true;
76
-	}
70
+    /**
71
+     * @return bool True when the option can be changed for the mail
72
+     * @since 11.0.0
73
+     */
74
+    public function canChangeMail() {
75
+        return true;
76
+    }
77 77
 
78
-	/**
79
-	 * @return bool True when the option can be changed for the stream
80
-	 * @since 11.0.0
81
-	 */
82
-	public function isDefaultEnabledMail() {
83
-		return false;
84
-	}
78
+    /**
79
+     * @return bool True when the option can be changed for the stream
80
+     * @since 11.0.0
81
+     */
82
+    public function isDefaultEnabledMail() {
83
+        return false;
84
+    }
85 85
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Activity/Settings/RemoteShare.php 1 patch
Indentation   +51 added lines, -51 removed lines patch added patch discarded remove patch
@@ -25,61 +25,61 @@
 block discarded – undo
25 25
 namespace OCA\Files_Sharing\Activity\Settings;
26 26
 
27 27
 class RemoteShare extends ShareActivitySettings {
28
-	/**
29
-	 * @return string Lowercase a-z and underscore only identifier
30
-	 * @since 11.0.0
31
-	 */
32
-	public function getIdentifier() {
33
-		return 'remote_share';
34
-	}
28
+    /**
29
+     * @return string Lowercase a-z and underscore only identifier
30
+     * @since 11.0.0
31
+     */
32
+    public function getIdentifier() {
33
+        return 'remote_share';
34
+    }
35 35
 
36
-	/**
37
-	 * @return string A translated string
38
-	 * @since 11.0.0
39
-	 */
40
-	public function getName() {
41
-		return $this->l->t('A file or folder was shared from <strong>another server</strong>');
42
-	}
36
+    /**
37
+     * @return string A translated string
38
+     * @since 11.0.0
39
+     */
40
+    public function getName() {
41
+        return $this->l->t('A file or folder was shared from <strong>another server</strong>');
42
+    }
43 43
 
44
-	/**
45
-	 * @return int whether the filter should be rather on the top or bottom of
46
-	 * the admin section. The filters are arranged in ascending order of the
47
-	 * priority values. It is required to return a value between 0 and 100.
48
-	 * @since 11.0.0
49
-	 */
50
-	public function getPriority() {
51
-		return 11;
52
-	}
44
+    /**
45
+     * @return int whether the filter should be rather on the top or bottom of
46
+     * the admin section. The filters are arranged in ascending order of the
47
+     * priority values. It is required to return a value between 0 and 100.
48
+     * @since 11.0.0
49
+     */
50
+    public function getPriority() {
51
+        return 11;
52
+    }
53 53
 
54
-	/**
55
-	 * @return bool True when the option can be changed for the stream
56
-	 * @since 11.0.0
57
-	 */
58
-	public function canChangeStream() {
59
-		return true;
60
-	}
54
+    /**
55
+     * @return bool True when the option can be changed for the stream
56
+     * @since 11.0.0
57
+     */
58
+    public function canChangeStream() {
59
+        return true;
60
+    }
61 61
 
62
-	/**
63
-	 * @return bool True when the option can be changed for the stream
64
-	 * @since 11.0.0
65
-	 */
66
-	public function isDefaultEnabledStream() {
67
-		return true;
68
-	}
62
+    /**
63
+     * @return bool True when the option can be changed for the stream
64
+     * @since 11.0.0
65
+     */
66
+    public function isDefaultEnabledStream() {
67
+        return true;
68
+    }
69 69
 
70
-	/**
71
-	 * @return bool True when the option can be changed for the mail
72
-	 * @since 11.0.0
73
-	 */
74
-	public function canChangeMail() {
75
-		return true;
76
-	}
70
+    /**
71
+     * @return bool True when the option can be changed for the mail
72
+     * @since 11.0.0
73
+     */
74
+    public function canChangeMail() {
75
+        return true;
76
+    }
77 77
 
78
-	/**
79
-	 * @return bool True when the option can be changed for the stream
80
-	 * @since 11.0.0
81
-	 */
82
-	public function isDefaultEnabledMail() {
83
-		return false;
84
-	}
78
+    /**
79
+     * @return bool True when the option can be changed for the stream
80
+     * @since 11.0.0
81
+     */
82
+    public function isDefaultEnabledMail() {
83
+        return false;
84
+    }
85 85
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Activity/Settings/ShareActivitySettings.php 1 patch
Indentation   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -27,21 +27,21 @@
 block discarded – undo
27 27
 use OCP\IL10N;
28 28
 
29 29
 abstract class ShareActivitySettings extends ActivitySettings {
30
-	/** @var IL10N */
31
-	protected $l;
30
+    /** @var IL10N */
31
+    protected $l;
32 32
 
33
-	/**
34
-	 * @param IL10N $l
35
-	 */
36
-	public function __construct(IL10N $l) {
37
-		$this->l = $l;
38
-	}
33
+    /**
34
+     * @param IL10N $l
35
+     */
36
+    public function __construct(IL10N $l) {
37
+        $this->l = $l;
38
+    }
39 39
 
40
-	public function getGroupIdentifier() {
41
-		return 'files';
42
-	}
40
+    public function getGroupIdentifier() {
41
+        return 'files';
42
+    }
43 43
 
44
-	public function getGroupName() {
45
-		return $this->l->t('Files');
46
-	}
44
+    public function getGroupName() {
45
+        return $this->l->t('Files');
46
+    }
47 47
 }
Please login to merge, or discard this patch.
lib/private/Tags.php 1 patch
Indentation   +802 added lines, -802 removed lines patch added patch discarded remove patch
@@ -51,806 +51,806 @@
 block discarded – undo
51 51
 
52 52
 class Tags implements ITags {
53 53
 
54
-	/**
55
-	 * Tags
56
-	 *
57
-	 * @var array
58
-	 */
59
-	private $tags = [];
60
-
61
-	/**
62
-	 * Used for storing objectid/categoryname pairs while rescanning.
63
-	 *
64
-	 * @var array
65
-	 */
66
-	private static $relations = [];
67
-
68
-	/**
69
-	 * Type
70
-	 *
71
-	 * @var string
72
-	 */
73
-	private $type;
74
-
75
-	/**
76
-	 * User
77
-	 *
78
-	 * @var string
79
-	 */
80
-	private $user;
81
-
82
-	/**
83
-	 * Are we including tags for shared items?
84
-	 *
85
-	 * @var bool
86
-	 */
87
-	private $includeShared = false;
88
-
89
-	/**
90
-	 * The current user, plus any owners of the items shared with the current
91
-	 * user, if $this->includeShared === true.
92
-	 *
93
-	 * @var array
94
-	 */
95
-	private $owners = [];
96
-
97
-	/**
98
-	 * The Mapper we're using to communicate our Tag objects to the database.
99
-	 *
100
-	 * @var TagMapper
101
-	 */
102
-	private $mapper;
103
-
104
-	/**
105
-	 * The sharing backend for objects of $this->type. Required if
106
-	 * $this->includeShared === true to determine ownership of items.
107
-	 *
108
-	 * @var \OCP\Share_Backend
109
-	 */
110
-	private $backend;
111
-
112
-	public const TAG_TABLE = '*PREFIX*vcategory';
113
-	public const RELATION_TABLE = '*PREFIX*vcategory_to_object';
114
-
115
-	/**
116
-	 * Constructor.
117
-	 *
118
-	 * @param TagMapper $mapper Instance of the TagMapper abstraction layer.
119
-	 * @param string $user The user whose data the object will operate on.
120
-	 * @param string $type The type of items for which tags will be loaded.
121
-	 * @param array $defaultTags Tags that should be created at construction.
122
-	 *
123
-	 * since 20.0.0 $includeShared isn't used anymore
124
-	 */
125
-	public function __construct(TagMapper $mapper, $user, $type, $defaultTags = []) {
126
-		$this->mapper = $mapper;
127
-		$this->user = $user;
128
-		$this->type = $type;
129
-		$this->owners = [$this->user];
130
-		$this->tags = $this->mapper->loadTags($this->owners, $this->type);
131
-
132
-		if (count($defaultTags) > 0 && count($this->tags) === 0) {
133
-			$this->addMultiple($defaultTags, true);
134
-		}
135
-	}
136
-
137
-	/**
138
-	 * Check if any tags are saved for this type and user.
139
-	 *
140
-	 * @return boolean
141
-	 */
142
-	public function isEmpty() {
143
-		return count($this->tags) === 0;
144
-	}
145
-
146
-	/**
147
-	 * Returns an array mapping a given tag's properties to its values:
148
-	 * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
149
-	 *
150
-	 * @param string $id The ID of the tag that is going to be mapped
151
-	 * @return array|false
152
-	 */
153
-	public function getTag($id) {
154
-		$key = $this->getTagById($id);
155
-		if ($key !== false) {
156
-			return $this->tagMap($this->tags[$key]);
157
-		}
158
-		return false;
159
-	}
160
-
161
-	/**
162
-	 * Get the tags for a specific user.
163
-	 *
164
-	 * This returns an array with maps containing each tag's properties:
165
-	 * [
166
-	 * 	['id' => 0, 'name' = 'First tag', 'owner' = 'User', 'type' => 'tagtype'],
167
-	 * 	['id' => 1, 'name' = 'Shared tag', 'owner' = 'Other user', 'type' => 'tagtype'],
168
-	 * ]
169
-	 *
170
-	 * @return array
171
-	 */
172
-	public function getTags() {
173
-		if (!count($this->tags)) {
174
-			return [];
175
-		}
176
-
177
-		usort($this->tags, function ($a, $b) {
178
-			return strnatcasecmp($a->getName(), $b->getName());
179
-		});
180
-		$tagMap = [];
181
-
182
-		foreach ($this->tags as $tag) {
183
-			if ($tag->getName() !== ITags::TAG_FAVORITE) {
184
-				$tagMap[] = $this->tagMap($tag);
185
-			}
186
-		}
187
-		return $tagMap;
188
-	}
189
-
190
-	/**
191
-	 * Return only the tags owned by the given user, omitting any tags shared
192
-	 * by other users.
193
-	 *
194
-	 * @param string $user The user whose tags are to be checked.
195
-	 * @return array An array of Tag objects.
196
-	 */
197
-	public function getTagsForUser($user) {
198
-		return array_filter($this->tags,
199
-			function ($tag) use ($user) {
200
-				return $tag->getOwner() === $user;
201
-			}
202
-		);
203
-	}
204
-
205
-	/**
206
-	 * Get the list of tags for the given ids.
207
-	 *
208
-	 * @param array $objIds array of object ids
209
-	 * @return array|boolean of tags id as key to array of tag names
210
-	 * or false if an error occurred
211
-	 */
212
-	public function getTagsForObjects(array $objIds) {
213
-		$entries = [];
214
-
215
-		try {
216
-			$conn = \OC::$server->getDatabaseConnection();
217
-			$chunks = array_chunk($objIds, 900, false);
218
-			foreach ($chunks as $chunk) {
219
-				$result = $conn->executeQuery(
220
-					'SELECT `category`, `categoryid`, `objid` ' .
221
-					'FROM `' . self::RELATION_TABLE . '` r, `' . self::TAG_TABLE . '` ' .
222
-					'WHERE `categoryid` = `id` AND `uid` = ? AND r.`type` = ? AND `objid` IN (?)',
223
-					[$this->user, $this->type, $chunk],
224
-					[null, null, IQueryBuilder::PARAM_INT_ARRAY]
225
-				);
226
-				while ($row = $result->fetch()) {
227
-					$objId = (int)$row['objid'];
228
-					if (!isset($entries[$objId])) {
229
-						$entries[$objId] = [];
230
-					}
231
-					$entries[$objId][] = $row['category'];
232
-				}
233
-				if ($result === null) {
234
-					\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
235
-					return false;
236
-				}
237
-			}
238
-		} catch (\Exception $e) {
239
-			\OC::$server->getLogger()->logException($e, [
240
-				'message' => __METHOD__,
241
-				'level' => ILogger::ERROR,
242
-				'app' => 'core',
243
-			]);
244
-			return false;
245
-		}
246
-
247
-		return $entries;
248
-	}
249
-
250
-	/**
251
-	 * Get the a list if items tagged with $tag.
252
-	 *
253
-	 * Throws an exception if the tag could not be found.
254
-	 *
255
-	 * @param string $tag Tag id or name.
256
-	 * @return array|false An array of object ids or false on error.
257
-	 * @throws \Exception
258
-	 */
259
-	public function getIdsForTag($tag) {
260
-		$result = null;
261
-		$tagId = false;
262
-		if (is_numeric($tag)) {
263
-			$tagId = $tag;
264
-		} elseif (is_string($tag)) {
265
-			$tag = trim($tag);
266
-			if ($tag === '') {
267
-				\OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
268
-				return false;
269
-			}
270
-			$tagId = $this->getTagId($tag);
271
-		}
272
-
273
-		if ($tagId === false) {
274
-			$l10n = \OC::$server->getL10N('core');
275
-			throw new \Exception(
276
-				$l10n->t('Could not find category "%s"', [$tag])
277
-			);
278
-		}
279
-
280
-		$ids = [];
281
-		$sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE
282
-			. '` WHERE `categoryid` = ?';
283
-
284
-		try {
285
-			$stmt = \OC_DB::prepare($sql);
286
-			$result = $stmt->execute([$tagId]);
287
-			if ($result === null) {
288
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
289
-				return false;
290
-			}
291
-		} catch (\Exception $e) {
292
-			\OC::$server->getLogger()->logException($e, [
293
-				'message' => __METHOD__,
294
-				'level' => ILogger::ERROR,
295
-				'app' => 'core',
296
-			]);
297
-			return false;
298
-		}
299
-
300
-		if (!is_null($result)) {
301
-			while ($row = $result->fetchRow()) {
302
-				$ids[] = (int)$row['objid'];
303
-			}
304
-		}
305
-
306
-		return $ids;
307
-	}
308
-
309
-	/**
310
-	 * Checks whether a tag is saved for the given user,
311
-	 * disregarding the ones shared with him or her.
312
-	 *
313
-	 * @param string $name The tag name to check for.
314
-	 * @param string $user The user whose tags are to be checked.
315
-	 * @return bool
316
-	 */
317
-	public function userHasTag($name, $user) {
318
-		$key = $this->array_searchi($name, $this->getTagsForUser($user));
319
-		return ($key !== false) ? $this->tags[$key]->getId() : false;
320
-	}
321
-
322
-	/**
323
-	 * Checks whether a tag is saved for or shared with the current user.
324
-	 *
325
-	 * @param string $name The tag name to check for.
326
-	 * @return bool
327
-	 */
328
-	public function hasTag($name) {
329
-		return $this->getTagId($name) !== false;
330
-	}
331
-
332
-	/**
333
-	 * Add a new tag.
334
-	 *
335
-	 * @param string $name A string with a name of the tag
336
-	 * @return false|int the id of the added tag or false on error.
337
-	 */
338
-	public function add($name) {
339
-		$name = trim($name);
340
-
341
-		if ($name === '') {
342
-			\OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
343
-			return false;
344
-		}
345
-		if ($this->userHasTag($name, $this->user)) {
346
-			\OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', ILogger::DEBUG);
347
-			return false;
348
-		}
349
-		try {
350
-			$tag = new Tag($this->user, $this->type, $name);
351
-			$tag = $this->mapper->insert($tag);
352
-			$this->tags[] = $tag;
353
-		} catch (\Exception $e) {
354
-			\OC::$server->getLogger()->logException($e, [
355
-				'message' => __METHOD__,
356
-				'level' => ILogger::ERROR,
357
-				'app' => 'core',
358
-			]);
359
-			return false;
360
-		}
361
-		\OCP\Util::writeLog('core', __METHOD__.', id: ' . $tag->getId(), ILogger::DEBUG);
362
-		return $tag->getId();
363
-	}
364
-
365
-	/**
366
-	 * Rename tag.
367
-	 *
368
-	 * @param string|integer $from The name or ID of the existing tag
369
-	 * @param string $to The new name of the tag.
370
-	 * @return bool
371
-	 */
372
-	public function rename($from, $to) {
373
-		$from = trim($from);
374
-		$to = trim($to);
375
-
376
-		if ($to === '' || $from === '') {
377
-			\OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
378
-			return false;
379
-		}
380
-
381
-		if (is_numeric($from)) {
382
-			$key = $this->getTagById($from);
383
-		} else {
384
-			$key = $this->getTagByName($from);
385
-		}
386
-		if ($key === false) {
387
-			\OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', ILogger::DEBUG);
388
-			return false;
389
-		}
390
-		$tag = $this->tags[$key];
391
-
392
-		if ($this->userHasTag($to, $tag->getOwner())) {
393
-			\OCP\Util::writeLog('core', __METHOD__.', A tag named ' . $to. ' already exists for user ' . $tag->getOwner() . '.', ILogger::DEBUG);
394
-			return false;
395
-		}
396
-
397
-		try {
398
-			$tag->setName($to);
399
-			$this->tags[$key] = $this->mapper->update($tag);
400
-		} catch (\Exception $e) {
401
-			\OC::$server->getLogger()->logException($e, [
402
-				'message' => __METHOD__,
403
-				'level' => ILogger::ERROR,
404
-				'app' => 'core',
405
-			]);
406
-			return false;
407
-		}
408
-		return true;
409
-	}
410
-
411
-	/**
412
-	 * Add a list of new tags.
413
-	 *
414
-	 * @param string[] $names A string with a name or an array of strings containing
415
-	 * the name(s) of the tag(s) to add.
416
-	 * @param bool $sync When true, save the tags
417
-	 * @param int|null $id int Optional object id to add to this|these tag(s)
418
-	 * @return bool Returns false on error.
419
-	 */
420
-	public function addMultiple($names, $sync=false, $id = null) {
421
-		if (!is_array($names)) {
422
-			$names = [$names];
423
-		}
424
-		$names = array_map('trim', $names);
425
-		array_filter($names);
426
-
427
-		$newones = [];
428
-		foreach ($names as $name) {
429
-			if (!$this->hasTag($name) && $name !== '') {
430
-				$newones[] = new Tag($this->user, $this->type, $name);
431
-			}
432
-			if (!is_null($id)) {
433
-				// Insert $objectid, $categoryid  pairs if not exist.
434
-				self::$relations[] = ['objid' => $id, 'tag' => $name];
435
-			}
436
-		}
437
-		$this->tags = array_merge($this->tags, $newones);
438
-		if ($sync === true) {
439
-			$this->save();
440
-		}
441
-
442
-		return true;
443
-	}
444
-
445
-	/**
446
-	 * Save the list of tags and their object relations
447
-	 */
448
-	protected function save() {
449
-		if (is_array($this->tags)) {
450
-			foreach ($this->tags as $tag) {
451
-				try {
452
-					if (!$this->mapper->tagExists($tag)) {
453
-						$this->mapper->insert($tag);
454
-					}
455
-				} catch (\Exception $e) {
456
-					\OC::$server->getLogger()->logException($e, [
457
-						'message' => __METHOD__,
458
-						'level' => ILogger::ERROR,
459
-						'app' => 'core',
460
-					]);
461
-				}
462
-			}
463
-
464
-			// reload tags to get the proper ids.
465
-			$this->tags = $this->mapper->loadTags($this->owners, $this->type);
466
-			\OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true),
467
-				ILogger::DEBUG);
468
-			// Loop through temporarily cached objectid/tagname pairs
469
-			// and save relations.
470
-			$tags = $this->tags;
471
-			// For some reason this is needed or array_search(i) will return 0..?
472
-			ksort($tags);
473
-			$dbConnection = \OC::$server->getDatabaseConnection();
474
-			foreach (self::$relations as $relation) {
475
-				$tagId = $this->getTagId($relation['tag']);
476
-				\OCP\Util::writeLog('core', __METHOD__ . 'catid, ' . $relation['tag'] . ' ' . $tagId, ILogger::DEBUG);
477
-				if ($tagId) {
478
-					try {
479
-						$dbConnection->insertIfNotExist(self::RELATION_TABLE,
480
-							[
481
-								'objid' => $relation['objid'],
482
-								'categoryid' => $tagId,
483
-								'type' => $this->type,
484
-							]);
485
-					} catch (\Exception $e) {
486
-						\OC::$server->getLogger()->logException($e, [
487
-							'message' => __METHOD__,
488
-							'level' => ILogger::ERROR,
489
-							'app' => 'core',
490
-						]);
491
-					}
492
-				}
493
-			}
494
-			self::$relations = []; // reset
495
-		} else {
496
-			\OCP\Util::writeLog('core', __METHOD__.', $this->tags is not an array! '
497
-				. print_r($this->tags, true), ILogger::ERROR);
498
-		}
499
-	}
500
-
501
-	/**
502
-	 * Delete tags and tag/object relations for a user.
503
-	 *
504
-	 * For hooking up on post_deleteUser
505
-	 *
506
-	 * @param array $arguments
507
-	 */
508
-	public static function post_deleteUser($arguments) {
509
-		// Find all objectid/tagId pairs.
510
-		$result = null;
511
-		try {
512
-			$stmt = \OC_DB::prepare('SELECT `id` FROM `' . self::TAG_TABLE . '` '
513
-				. 'WHERE `uid` = ?');
514
-			$result = $stmt->execute([$arguments['uid']]);
515
-			if ($result === null) {
516
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
517
-			}
518
-		} catch (\Exception $e) {
519
-			\OC::$server->getLogger()->logException($e, [
520
-				'message' => __METHOD__,
521
-				'level' => ILogger::ERROR,
522
-				'app' => 'core',
523
-			]);
524
-		}
525
-
526
-		if (!is_null($result)) {
527
-			try {
528
-				$stmt = \OC_DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` '
529
-					. 'WHERE `categoryid` = ?');
530
-				while ($row = $result->fetchRow()) {
531
-					try {
532
-						$stmt->execute([$row['id']]);
533
-					} catch (\Exception $e) {
534
-						\OC::$server->getLogger()->logException($e, [
535
-							'message' => __METHOD__,
536
-							'level' => ILogger::ERROR,
537
-							'app' => 'core',
538
-						]);
539
-					}
540
-				}
541
-			} catch (\Exception $e) {
542
-				\OC::$server->getLogger()->logException($e, [
543
-					'message' => __METHOD__,
544
-					'level' => ILogger::ERROR,
545
-					'app' => 'core',
546
-				]);
547
-			}
548
-		}
549
-		try {
550
-			$stmt = \OC_DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` '
551
-				. 'WHERE `uid` = ?');
552
-			$result = $stmt->execute([$arguments['uid']]);
553
-			if ($result === null) {
554
-				\OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
555
-			}
556
-		} catch (\Exception $e) {
557
-			\OC::$server->getLogger()->logException($e, [
558
-				'message' => __METHOD__,
559
-				'level' => ILogger::ERROR,
560
-				'app' => 'core',
561
-			]);
562
-		}
563
-	}
564
-
565
-	/**
566
-	 * Delete tag/object relations from the db
567
-	 *
568
-	 * @param array $ids The ids of the objects
569
-	 * @return boolean Returns false on error.
570
-	 */
571
-	public function purgeObjects(array $ids) {
572
-		if (count($ids) === 0) {
573
-			// job done ;)
574
-			return true;
575
-		}
576
-		$updates = $ids;
577
-		try {
578
-			$query = 'DELETE FROM `' . self::RELATION_TABLE . '` ';
579
-			$query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids)-1) . '?) ';
580
-			$query .= 'AND `type`= ?';
581
-			$updates[] = $this->type;
582
-			$stmt = \OC_DB::prepare($query);
583
-			$result = $stmt->execute($updates);
584
-			if ($result === null) {
585
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
586
-				return false;
587
-			}
588
-		} catch (\Exception $e) {
589
-			\OC::$server->getLogger()->logException($e, [
590
-				'message' => __METHOD__,
591
-				'level' => ILogger::ERROR,
592
-				'app' => 'core',
593
-			]);
594
-			return false;
595
-		}
596
-		return true;
597
-	}
598
-
599
-	/**
600
-	 * Get favorites for an object type
601
-	 *
602
-	 * @return array|false An array of object ids.
603
-	 */
604
-	public function getFavorites() {
605
-		if (!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
606
-			return [];
607
-		}
608
-
609
-		try {
610
-			return $this->getIdsForTag(ITags::TAG_FAVORITE);
611
-		} catch (\Exception $e) {
612
-			\OC::$server->getLogger()->logException($e, [
613
-				'message' => __METHOD__,
614
-				'level' => ILogger::ERROR,
615
-				'app' => 'core',
616
-			]);
617
-			return [];
618
-		}
619
-	}
620
-
621
-	/**
622
-	 * Add an object to favorites
623
-	 *
624
-	 * @param int $objid The id of the object
625
-	 * @return boolean
626
-	 */
627
-	public function addToFavorites($objid) {
628
-		if (!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
629
-			$this->add(ITags::TAG_FAVORITE);
630
-		}
631
-		return $this->tagAs($objid, ITags::TAG_FAVORITE);
632
-	}
633
-
634
-	/**
635
-	 * Remove an object from favorites
636
-	 *
637
-	 * @param int $objid The id of the object
638
-	 * @return boolean
639
-	 */
640
-	public function removeFromFavorites($objid) {
641
-		return $this->unTag($objid, ITags::TAG_FAVORITE);
642
-	}
643
-
644
-	/**
645
-	 * Get all users who favorited an object
646
-	 */
647
-	public function getUsersFavoritingObject($objId) {
648
-		$entries = [];
649
-
650
-		$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
651
-		$query->select('uid')
652
-			->from('vcategory_to_object', 'o')
653
-			->innerJoin('o', 'vcategory', 'c', $query->expr()->eq('o.categoryid', 'c.id'))
654
-			->where($query->expr()->eq('objid', $query->createNamedParameter($objId, IQueryBuilder::PARAM_INT)))
655
-			->andWhere($query->expr()->eq('c.type', $query->createNamedParameter($this->type)))
656
-			->andWhere($query->expr()->eq('c.category', $query->createNamedParameter(ITags::TAG_FAVORITE)));
657
-
658
-		return $query->execute()->fetchAll(\PDO::FETCH_COLUMN);
659
-	}
660
-
661
-	/**
662
-	 * Creates a tag/object relation.
663
-	 *
664
-	 * @param int $objid The id of the object
665
-	 * @param string $tag The id or name of the tag
666
-	 * @return boolean Returns false on error.
667
-	 */
668
-	public function tagAs($objid, $tag) {
669
-		if (is_string($tag) && !is_numeric($tag)) {
670
-			$tag = trim($tag);
671
-			if ($tag === '') {
672
-				\OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
673
-				return false;
674
-			}
675
-			if (!$this->hasTag($tag)) {
676
-				$this->add($tag);
677
-			}
678
-			$tagId =  $this->getTagId($tag);
679
-		} else {
680
-			$tagId = $tag;
681
-		}
682
-		try {
683
-			\OC::$server->getDatabaseConnection()->insertIfNotExist(self::RELATION_TABLE,
684
-				[
685
-					'objid' => $objid,
686
-					'categoryid' => $tagId,
687
-					'type' => $this->type,
688
-				]);
689
-		} catch (\Exception $e) {
690
-			\OC::$server->getLogger()->logException($e, [
691
-				'message' => __METHOD__,
692
-				'level' => ILogger::ERROR,
693
-				'app' => 'core',
694
-			]);
695
-			return false;
696
-		}
697
-		return true;
698
-	}
699
-
700
-	/**
701
-	 * Delete single tag/object relation from the db
702
-	 *
703
-	 * @param int $objid The id of the object
704
-	 * @param string $tag The id or name of the tag
705
-	 * @return boolean
706
-	 */
707
-	public function unTag($objid, $tag) {
708
-		if (is_string($tag) && !is_numeric($tag)) {
709
-			$tag = trim($tag);
710
-			if ($tag === '') {
711
-				\OCP\Util::writeLog('core', __METHOD__.', Tag name is empty', ILogger::DEBUG);
712
-				return false;
713
-			}
714
-			$tagId =  $this->getTagId($tag);
715
-		} else {
716
-			$tagId = $tag;
717
-		}
718
-
719
-		try {
720
-			$sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
721
-					. 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?';
722
-			$stmt = \OC_DB::prepare($sql);
723
-			$stmt->execute([$objid, $tagId, $this->type]);
724
-		} catch (\Exception $e) {
725
-			\OC::$server->getLogger()->logException($e, [
726
-				'message' => __METHOD__,
727
-				'level' => ILogger::ERROR,
728
-				'app' => 'core',
729
-			]);
730
-			return false;
731
-		}
732
-		return true;
733
-	}
734
-
735
-	/**
736
-	 * Delete tags from the database.
737
-	 *
738
-	 * @param string[]|integer[] $names An array of tags (names or IDs) to delete
739
-	 * @return bool Returns false on error
740
-	 */
741
-	public function delete($names) {
742
-		if (!is_array($names)) {
743
-			$names = [$names];
744
-		}
745
-
746
-		$names = array_map('trim', $names);
747
-		array_filter($names);
748
-
749
-		\OCP\Util::writeLog('core', __METHOD__ . ', before: '
750
-			. print_r($this->tags, true), ILogger::DEBUG);
751
-		foreach ($names as $name) {
752
-			$id = null;
753
-
754
-			if (is_numeric($name)) {
755
-				$key = $this->getTagById($name);
756
-			} else {
757
-				$key = $this->getTagByName($name);
758
-			}
759
-			if ($key !== false) {
760
-				$tag = $this->tags[$key];
761
-				$id = $tag->getId();
762
-				unset($this->tags[$key]);
763
-				$this->mapper->delete($tag);
764
-			} else {
765
-				\OCP\Util::writeLog('core', __METHOD__ . 'Cannot delete tag ' . $name
766
-					. ': not found.', ILogger::ERROR);
767
-			}
768
-			if (!is_null($id) && $id !== false) {
769
-				try {
770
-					$sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
771
-							. 'WHERE `categoryid` = ?';
772
-					$stmt = \OC_DB::prepare($sql);
773
-					$result = $stmt->execute([$id]);
774
-					if ($result === null) {
775
-						\OCP\Util::writeLog('core',
776
-							__METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(),
777
-							ILogger::ERROR);
778
-						return false;
779
-					}
780
-				} catch (\Exception $e) {
781
-					\OC::$server->getLogger()->logException($e, [
782
-						'message' => __METHOD__,
783
-						'level' => ILogger::ERROR,
784
-						'app' => 'core',
785
-					]);
786
-					return false;
787
-				}
788
-			}
789
-		}
790
-		return true;
791
-	}
792
-
793
-	// case-insensitive array_search
794
-	protected function array_searchi($needle, $haystack, $mem='getName') {
795
-		if (!is_array($haystack)) {
796
-			return false;
797
-		}
798
-		return array_search(strtolower($needle), array_map(
799
-			function ($tag) use ($mem) {
800
-				return strtolower(call_user_func([$tag, $mem]));
801
-			}, $haystack)
802
-		);
803
-	}
804
-
805
-	/**
806
-	 * Get a tag's ID.
807
-	 *
808
-	 * @param string $name The tag name to look for.
809
-	 * @return string|bool The tag's id or false if no matching tag is found.
810
-	 */
811
-	private function getTagId($name) {
812
-		$key = $this->array_searchi($name, $this->tags);
813
-		if ($key !== false) {
814
-			return $this->tags[$key]->getId();
815
-		}
816
-		return false;
817
-	}
818
-
819
-	/**
820
-	 * Get a tag by its name.
821
-	 *
822
-	 * @param string $name The tag name.
823
-	 * @return integer|bool The tag object's offset within the $this->tags
824
-	 *                      array or false if it doesn't exist.
825
-	 */
826
-	private function getTagByName($name) {
827
-		return $this->array_searchi($name, $this->tags, 'getName');
828
-	}
829
-
830
-	/**
831
-	 * Get a tag by its ID.
832
-	 *
833
-	 * @param string $id The tag ID to look for.
834
-	 * @return integer|bool The tag object's offset within the $this->tags
835
-	 *                      array or false if it doesn't exist.
836
-	 */
837
-	private function getTagById($id) {
838
-		return $this->array_searchi($id, $this->tags, 'getId');
839
-	}
840
-
841
-	/**
842
-	 * Returns an array mapping a given tag's properties to its values:
843
-	 * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
844
-	 *
845
-	 * @param Tag $tag The tag that is going to be mapped
846
-	 * @return array
847
-	 */
848
-	private function tagMap(Tag $tag) {
849
-		return [
850
-			'id'    => $tag->getId(),
851
-			'name'  => $tag->getName(),
852
-			'owner' => $tag->getOwner(),
853
-			'type'  => $tag->getType()
854
-		];
855
-	}
54
+    /**
55
+     * Tags
56
+     *
57
+     * @var array
58
+     */
59
+    private $tags = [];
60
+
61
+    /**
62
+     * Used for storing objectid/categoryname pairs while rescanning.
63
+     *
64
+     * @var array
65
+     */
66
+    private static $relations = [];
67
+
68
+    /**
69
+     * Type
70
+     *
71
+     * @var string
72
+     */
73
+    private $type;
74
+
75
+    /**
76
+     * User
77
+     *
78
+     * @var string
79
+     */
80
+    private $user;
81
+
82
+    /**
83
+     * Are we including tags for shared items?
84
+     *
85
+     * @var bool
86
+     */
87
+    private $includeShared = false;
88
+
89
+    /**
90
+     * The current user, plus any owners of the items shared with the current
91
+     * user, if $this->includeShared === true.
92
+     *
93
+     * @var array
94
+     */
95
+    private $owners = [];
96
+
97
+    /**
98
+     * The Mapper we're using to communicate our Tag objects to the database.
99
+     *
100
+     * @var TagMapper
101
+     */
102
+    private $mapper;
103
+
104
+    /**
105
+     * The sharing backend for objects of $this->type. Required if
106
+     * $this->includeShared === true to determine ownership of items.
107
+     *
108
+     * @var \OCP\Share_Backend
109
+     */
110
+    private $backend;
111
+
112
+    public const TAG_TABLE = '*PREFIX*vcategory';
113
+    public const RELATION_TABLE = '*PREFIX*vcategory_to_object';
114
+
115
+    /**
116
+     * Constructor.
117
+     *
118
+     * @param TagMapper $mapper Instance of the TagMapper abstraction layer.
119
+     * @param string $user The user whose data the object will operate on.
120
+     * @param string $type The type of items for which tags will be loaded.
121
+     * @param array $defaultTags Tags that should be created at construction.
122
+     *
123
+     * since 20.0.0 $includeShared isn't used anymore
124
+     */
125
+    public function __construct(TagMapper $mapper, $user, $type, $defaultTags = []) {
126
+        $this->mapper = $mapper;
127
+        $this->user = $user;
128
+        $this->type = $type;
129
+        $this->owners = [$this->user];
130
+        $this->tags = $this->mapper->loadTags($this->owners, $this->type);
131
+
132
+        if (count($defaultTags) > 0 && count($this->tags) === 0) {
133
+            $this->addMultiple($defaultTags, true);
134
+        }
135
+    }
136
+
137
+    /**
138
+     * Check if any tags are saved for this type and user.
139
+     *
140
+     * @return boolean
141
+     */
142
+    public function isEmpty() {
143
+        return count($this->tags) === 0;
144
+    }
145
+
146
+    /**
147
+     * Returns an array mapping a given tag's properties to its values:
148
+     * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
149
+     *
150
+     * @param string $id The ID of the tag that is going to be mapped
151
+     * @return array|false
152
+     */
153
+    public function getTag($id) {
154
+        $key = $this->getTagById($id);
155
+        if ($key !== false) {
156
+            return $this->tagMap($this->tags[$key]);
157
+        }
158
+        return false;
159
+    }
160
+
161
+    /**
162
+     * Get the tags for a specific user.
163
+     *
164
+     * This returns an array with maps containing each tag's properties:
165
+     * [
166
+     * 	['id' => 0, 'name' = 'First tag', 'owner' = 'User', 'type' => 'tagtype'],
167
+     * 	['id' => 1, 'name' = 'Shared tag', 'owner' = 'Other user', 'type' => 'tagtype'],
168
+     * ]
169
+     *
170
+     * @return array
171
+     */
172
+    public function getTags() {
173
+        if (!count($this->tags)) {
174
+            return [];
175
+        }
176
+
177
+        usort($this->tags, function ($a, $b) {
178
+            return strnatcasecmp($a->getName(), $b->getName());
179
+        });
180
+        $tagMap = [];
181
+
182
+        foreach ($this->tags as $tag) {
183
+            if ($tag->getName() !== ITags::TAG_FAVORITE) {
184
+                $tagMap[] = $this->tagMap($tag);
185
+            }
186
+        }
187
+        return $tagMap;
188
+    }
189
+
190
+    /**
191
+     * Return only the tags owned by the given user, omitting any tags shared
192
+     * by other users.
193
+     *
194
+     * @param string $user The user whose tags are to be checked.
195
+     * @return array An array of Tag objects.
196
+     */
197
+    public function getTagsForUser($user) {
198
+        return array_filter($this->tags,
199
+            function ($tag) use ($user) {
200
+                return $tag->getOwner() === $user;
201
+            }
202
+        );
203
+    }
204
+
205
+    /**
206
+     * Get the list of tags for the given ids.
207
+     *
208
+     * @param array $objIds array of object ids
209
+     * @return array|boolean of tags id as key to array of tag names
210
+     * or false if an error occurred
211
+     */
212
+    public function getTagsForObjects(array $objIds) {
213
+        $entries = [];
214
+
215
+        try {
216
+            $conn = \OC::$server->getDatabaseConnection();
217
+            $chunks = array_chunk($objIds, 900, false);
218
+            foreach ($chunks as $chunk) {
219
+                $result = $conn->executeQuery(
220
+                    'SELECT `category`, `categoryid`, `objid` ' .
221
+                    'FROM `' . self::RELATION_TABLE . '` r, `' . self::TAG_TABLE . '` ' .
222
+                    'WHERE `categoryid` = `id` AND `uid` = ? AND r.`type` = ? AND `objid` IN (?)',
223
+                    [$this->user, $this->type, $chunk],
224
+                    [null, null, IQueryBuilder::PARAM_INT_ARRAY]
225
+                );
226
+                while ($row = $result->fetch()) {
227
+                    $objId = (int)$row['objid'];
228
+                    if (!isset($entries[$objId])) {
229
+                        $entries[$objId] = [];
230
+                    }
231
+                    $entries[$objId][] = $row['category'];
232
+                }
233
+                if ($result === null) {
234
+                    \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
235
+                    return false;
236
+                }
237
+            }
238
+        } catch (\Exception $e) {
239
+            \OC::$server->getLogger()->logException($e, [
240
+                'message' => __METHOD__,
241
+                'level' => ILogger::ERROR,
242
+                'app' => 'core',
243
+            ]);
244
+            return false;
245
+        }
246
+
247
+        return $entries;
248
+    }
249
+
250
+    /**
251
+     * Get the a list if items tagged with $tag.
252
+     *
253
+     * Throws an exception if the tag could not be found.
254
+     *
255
+     * @param string $tag Tag id or name.
256
+     * @return array|false An array of object ids or false on error.
257
+     * @throws \Exception
258
+     */
259
+    public function getIdsForTag($tag) {
260
+        $result = null;
261
+        $tagId = false;
262
+        if (is_numeric($tag)) {
263
+            $tagId = $tag;
264
+        } elseif (is_string($tag)) {
265
+            $tag = trim($tag);
266
+            if ($tag === '') {
267
+                \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
268
+                return false;
269
+            }
270
+            $tagId = $this->getTagId($tag);
271
+        }
272
+
273
+        if ($tagId === false) {
274
+            $l10n = \OC::$server->getL10N('core');
275
+            throw new \Exception(
276
+                $l10n->t('Could not find category "%s"', [$tag])
277
+            );
278
+        }
279
+
280
+        $ids = [];
281
+        $sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE
282
+            . '` WHERE `categoryid` = ?';
283
+
284
+        try {
285
+            $stmt = \OC_DB::prepare($sql);
286
+            $result = $stmt->execute([$tagId]);
287
+            if ($result === null) {
288
+                \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
289
+                return false;
290
+            }
291
+        } catch (\Exception $e) {
292
+            \OC::$server->getLogger()->logException($e, [
293
+                'message' => __METHOD__,
294
+                'level' => ILogger::ERROR,
295
+                'app' => 'core',
296
+            ]);
297
+            return false;
298
+        }
299
+
300
+        if (!is_null($result)) {
301
+            while ($row = $result->fetchRow()) {
302
+                $ids[] = (int)$row['objid'];
303
+            }
304
+        }
305
+
306
+        return $ids;
307
+    }
308
+
309
+    /**
310
+     * Checks whether a tag is saved for the given user,
311
+     * disregarding the ones shared with him or her.
312
+     *
313
+     * @param string $name The tag name to check for.
314
+     * @param string $user The user whose tags are to be checked.
315
+     * @return bool
316
+     */
317
+    public function userHasTag($name, $user) {
318
+        $key = $this->array_searchi($name, $this->getTagsForUser($user));
319
+        return ($key !== false) ? $this->tags[$key]->getId() : false;
320
+    }
321
+
322
+    /**
323
+     * Checks whether a tag is saved for or shared with the current user.
324
+     *
325
+     * @param string $name The tag name to check for.
326
+     * @return bool
327
+     */
328
+    public function hasTag($name) {
329
+        return $this->getTagId($name) !== false;
330
+    }
331
+
332
+    /**
333
+     * Add a new tag.
334
+     *
335
+     * @param string $name A string with a name of the tag
336
+     * @return false|int the id of the added tag or false on error.
337
+     */
338
+    public function add($name) {
339
+        $name = trim($name);
340
+
341
+        if ($name === '') {
342
+            \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
343
+            return false;
344
+        }
345
+        if ($this->userHasTag($name, $this->user)) {
346
+            \OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', ILogger::DEBUG);
347
+            return false;
348
+        }
349
+        try {
350
+            $tag = new Tag($this->user, $this->type, $name);
351
+            $tag = $this->mapper->insert($tag);
352
+            $this->tags[] = $tag;
353
+        } catch (\Exception $e) {
354
+            \OC::$server->getLogger()->logException($e, [
355
+                'message' => __METHOD__,
356
+                'level' => ILogger::ERROR,
357
+                'app' => 'core',
358
+            ]);
359
+            return false;
360
+        }
361
+        \OCP\Util::writeLog('core', __METHOD__.', id: ' . $tag->getId(), ILogger::DEBUG);
362
+        return $tag->getId();
363
+    }
364
+
365
+    /**
366
+     * Rename tag.
367
+     *
368
+     * @param string|integer $from The name or ID of the existing tag
369
+     * @param string $to The new name of the tag.
370
+     * @return bool
371
+     */
372
+    public function rename($from, $to) {
373
+        $from = trim($from);
374
+        $to = trim($to);
375
+
376
+        if ($to === '' || $from === '') {
377
+            \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
378
+            return false;
379
+        }
380
+
381
+        if (is_numeric($from)) {
382
+            $key = $this->getTagById($from);
383
+        } else {
384
+            $key = $this->getTagByName($from);
385
+        }
386
+        if ($key === false) {
387
+            \OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', ILogger::DEBUG);
388
+            return false;
389
+        }
390
+        $tag = $this->tags[$key];
391
+
392
+        if ($this->userHasTag($to, $tag->getOwner())) {
393
+            \OCP\Util::writeLog('core', __METHOD__.', A tag named ' . $to. ' already exists for user ' . $tag->getOwner() . '.', ILogger::DEBUG);
394
+            return false;
395
+        }
396
+
397
+        try {
398
+            $tag->setName($to);
399
+            $this->tags[$key] = $this->mapper->update($tag);
400
+        } catch (\Exception $e) {
401
+            \OC::$server->getLogger()->logException($e, [
402
+                'message' => __METHOD__,
403
+                'level' => ILogger::ERROR,
404
+                'app' => 'core',
405
+            ]);
406
+            return false;
407
+        }
408
+        return true;
409
+    }
410
+
411
+    /**
412
+     * Add a list of new tags.
413
+     *
414
+     * @param string[] $names A string with a name or an array of strings containing
415
+     * the name(s) of the tag(s) to add.
416
+     * @param bool $sync When true, save the tags
417
+     * @param int|null $id int Optional object id to add to this|these tag(s)
418
+     * @return bool Returns false on error.
419
+     */
420
+    public function addMultiple($names, $sync=false, $id = null) {
421
+        if (!is_array($names)) {
422
+            $names = [$names];
423
+        }
424
+        $names = array_map('trim', $names);
425
+        array_filter($names);
426
+
427
+        $newones = [];
428
+        foreach ($names as $name) {
429
+            if (!$this->hasTag($name) && $name !== '') {
430
+                $newones[] = new Tag($this->user, $this->type, $name);
431
+            }
432
+            if (!is_null($id)) {
433
+                // Insert $objectid, $categoryid  pairs if not exist.
434
+                self::$relations[] = ['objid' => $id, 'tag' => $name];
435
+            }
436
+        }
437
+        $this->tags = array_merge($this->tags, $newones);
438
+        if ($sync === true) {
439
+            $this->save();
440
+        }
441
+
442
+        return true;
443
+    }
444
+
445
+    /**
446
+     * Save the list of tags and their object relations
447
+     */
448
+    protected function save() {
449
+        if (is_array($this->tags)) {
450
+            foreach ($this->tags as $tag) {
451
+                try {
452
+                    if (!$this->mapper->tagExists($tag)) {
453
+                        $this->mapper->insert($tag);
454
+                    }
455
+                } catch (\Exception $e) {
456
+                    \OC::$server->getLogger()->logException($e, [
457
+                        'message' => __METHOD__,
458
+                        'level' => ILogger::ERROR,
459
+                        'app' => 'core',
460
+                    ]);
461
+                }
462
+            }
463
+
464
+            // reload tags to get the proper ids.
465
+            $this->tags = $this->mapper->loadTags($this->owners, $this->type);
466
+            \OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true),
467
+                ILogger::DEBUG);
468
+            // Loop through temporarily cached objectid/tagname pairs
469
+            // and save relations.
470
+            $tags = $this->tags;
471
+            // For some reason this is needed or array_search(i) will return 0..?
472
+            ksort($tags);
473
+            $dbConnection = \OC::$server->getDatabaseConnection();
474
+            foreach (self::$relations as $relation) {
475
+                $tagId = $this->getTagId($relation['tag']);
476
+                \OCP\Util::writeLog('core', __METHOD__ . 'catid, ' . $relation['tag'] . ' ' . $tagId, ILogger::DEBUG);
477
+                if ($tagId) {
478
+                    try {
479
+                        $dbConnection->insertIfNotExist(self::RELATION_TABLE,
480
+                            [
481
+                                'objid' => $relation['objid'],
482
+                                'categoryid' => $tagId,
483
+                                'type' => $this->type,
484
+                            ]);
485
+                    } catch (\Exception $e) {
486
+                        \OC::$server->getLogger()->logException($e, [
487
+                            'message' => __METHOD__,
488
+                            'level' => ILogger::ERROR,
489
+                            'app' => 'core',
490
+                        ]);
491
+                    }
492
+                }
493
+            }
494
+            self::$relations = []; // reset
495
+        } else {
496
+            \OCP\Util::writeLog('core', __METHOD__.', $this->tags is not an array! '
497
+                . print_r($this->tags, true), ILogger::ERROR);
498
+        }
499
+    }
500
+
501
+    /**
502
+     * Delete tags and tag/object relations for a user.
503
+     *
504
+     * For hooking up on post_deleteUser
505
+     *
506
+     * @param array $arguments
507
+     */
508
+    public static function post_deleteUser($arguments) {
509
+        // Find all objectid/tagId pairs.
510
+        $result = null;
511
+        try {
512
+            $stmt = \OC_DB::prepare('SELECT `id` FROM `' . self::TAG_TABLE . '` '
513
+                . 'WHERE `uid` = ?');
514
+            $result = $stmt->execute([$arguments['uid']]);
515
+            if ($result === null) {
516
+                \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
517
+            }
518
+        } catch (\Exception $e) {
519
+            \OC::$server->getLogger()->logException($e, [
520
+                'message' => __METHOD__,
521
+                'level' => ILogger::ERROR,
522
+                'app' => 'core',
523
+            ]);
524
+        }
525
+
526
+        if (!is_null($result)) {
527
+            try {
528
+                $stmt = \OC_DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` '
529
+                    . 'WHERE `categoryid` = ?');
530
+                while ($row = $result->fetchRow()) {
531
+                    try {
532
+                        $stmt->execute([$row['id']]);
533
+                    } catch (\Exception $e) {
534
+                        \OC::$server->getLogger()->logException($e, [
535
+                            'message' => __METHOD__,
536
+                            'level' => ILogger::ERROR,
537
+                            'app' => 'core',
538
+                        ]);
539
+                    }
540
+                }
541
+            } catch (\Exception $e) {
542
+                \OC::$server->getLogger()->logException($e, [
543
+                    'message' => __METHOD__,
544
+                    'level' => ILogger::ERROR,
545
+                    'app' => 'core',
546
+                ]);
547
+            }
548
+        }
549
+        try {
550
+            $stmt = \OC_DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` '
551
+                . 'WHERE `uid` = ?');
552
+            $result = $stmt->execute([$arguments['uid']]);
553
+            if ($result === null) {
554
+                \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
555
+            }
556
+        } catch (\Exception $e) {
557
+            \OC::$server->getLogger()->logException($e, [
558
+                'message' => __METHOD__,
559
+                'level' => ILogger::ERROR,
560
+                'app' => 'core',
561
+            ]);
562
+        }
563
+    }
564
+
565
+    /**
566
+     * Delete tag/object relations from the db
567
+     *
568
+     * @param array $ids The ids of the objects
569
+     * @return boolean Returns false on error.
570
+     */
571
+    public function purgeObjects(array $ids) {
572
+        if (count($ids) === 0) {
573
+            // job done ;)
574
+            return true;
575
+        }
576
+        $updates = $ids;
577
+        try {
578
+            $query = 'DELETE FROM `' . self::RELATION_TABLE . '` ';
579
+            $query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids)-1) . '?) ';
580
+            $query .= 'AND `type`= ?';
581
+            $updates[] = $this->type;
582
+            $stmt = \OC_DB::prepare($query);
583
+            $result = $stmt->execute($updates);
584
+            if ($result === null) {
585
+                \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
586
+                return false;
587
+            }
588
+        } catch (\Exception $e) {
589
+            \OC::$server->getLogger()->logException($e, [
590
+                'message' => __METHOD__,
591
+                'level' => ILogger::ERROR,
592
+                'app' => 'core',
593
+            ]);
594
+            return false;
595
+        }
596
+        return true;
597
+    }
598
+
599
+    /**
600
+     * Get favorites for an object type
601
+     *
602
+     * @return array|false An array of object ids.
603
+     */
604
+    public function getFavorites() {
605
+        if (!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
606
+            return [];
607
+        }
608
+
609
+        try {
610
+            return $this->getIdsForTag(ITags::TAG_FAVORITE);
611
+        } catch (\Exception $e) {
612
+            \OC::$server->getLogger()->logException($e, [
613
+                'message' => __METHOD__,
614
+                'level' => ILogger::ERROR,
615
+                'app' => 'core',
616
+            ]);
617
+            return [];
618
+        }
619
+    }
620
+
621
+    /**
622
+     * Add an object to favorites
623
+     *
624
+     * @param int $objid The id of the object
625
+     * @return boolean
626
+     */
627
+    public function addToFavorites($objid) {
628
+        if (!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
629
+            $this->add(ITags::TAG_FAVORITE);
630
+        }
631
+        return $this->tagAs($objid, ITags::TAG_FAVORITE);
632
+    }
633
+
634
+    /**
635
+     * Remove an object from favorites
636
+     *
637
+     * @param int $objid The id of the object
638
+     * @return boolean
639
+     */
640
+    public function removeFromFavorites($objid) {
641
+        return $this->unTag($objid, ITags::TAG_FAVORITE);
642
+    }
643
+
644
+    /**
645
+     * Get all users who favorited an object
646
+     */
647
+    public function getUsersFavoritingObject($objId) {
648
+        $entries = [];
649
+
650
+        $query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
651
+        $query->select('uid')
652
+            ->from('vcategory_to_object', 'o')
653
+            ->innerJoin('o', 'vcategory', 'c', $query->expr()->eq('o.categoryid', 'c.id'))
654
+            ->where($query->expr()->eq('objid', $query->createNamedParameter($objId, IQueryBuilder::PARAM_INT)))
655
+            ->andWhere($query->expr()->eq('c.type', $query->createNamedParameter($this->type)))
656
+            ->andWhere($query->expr()->eq('c.category', $query->createNamedParameter(ITags::TAG_FAVORITE)));
657
+
658
+        return $query->execute()->fetchAll(\PDO::FETCH_COLUMN);
659
+    }
660
+
661
+    /**
662
+     * Creates a tag/object relation.
663
+     *
664
+     * @param int $objid The id of the object
665
+     * @param string $tag The id or name of the tag
666
+     * @return boolean Returns false on error.
667
+     */
668
+    public function tagAs($objid, $tag) {
669
+        if (is_string($tag) && !is_numeric($tag)) {
670
+            $tag = trim($tag);
671
+            if ($tag === '') {
672
+                \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
673
+                return false;
674
+            }
675
+            if (!$this->hasTag($tag)) {
676
+                $this->add($tag);
677
+            }
678
+            $tagId =  $this->getTagId($tag);
679
+        } else {
680
+            $tagId = $tag;
681
+        }
682
+        try {
683
+            \OC::$server->getDatabaseConnection()->insertIfNotExist(self::RELATION_TABLE,
684
+                [
685
+                    'objid' => $objid,
686
+                    'categoryid' => $tagId,
687
+                    'type' => $this->type,
688
+                ]);
689
+        } catch (\Exception $e) {
690
+            \OC::$server->getLogger()->logException($e, [
691
+                'message' => __METHOD__,
692
+                'level' => ILogger::ERROR,
693
+                'app' => 'core',
694
+            ]);
695
+            return false;
696
+        }
697
+        return true;
698
+    }
699
+
700
+    /**
701
+     * Delete single tag/object relation from the db
702
+     *
703
+     * @param int $objid The id of the object
704
+     * @param string $tag The id or name of the tag
705
+     * @return boolean
706
+     */
707
+    public function unTag($objid, $tag) {
708
+        if (is_string($tag) && !is_numeric($tag)) {
709
+            $tag = trim($tag);
710
+            if ($tag === '') {
711
+                \OCP\Util::writeLog('core', __METHOD__.', Tag name is empty', ILogger::DEBUG);
712
+                return false;
713
+            }
714
+            $tagId =  $this->getTagId($tag);
715
+        } else {
716
+            $tagId = $tag;
717
+        }
718
+
719
+        try {
720
+            $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
721
+                    . 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?';
722
+            $stmt = \OC_DB::prepare($sql);
723
+            $stmt->execute([$objid, $tagId, $this->type]);
724
+        } catch (\Exception $e) {
725
+            \OC::$server->getLogger()->logException($e, [
726
+                'message' => __METHOD__,
727
+                'level' => ILogger::ERROR,
728
+                'app' => 'core',
729
+            ]);
730
+            return false;
731
+        }
732
+        return true;
733
+    }
734
+
735
+    /**
736
+     * Delete tags from the database.
737
+     *
738
+     * @param string[]|integer[] $names An array of tags (names or IDs) to delete
739
+     * @return bool Returns false on error
740
+     */
741
+    public function delete($names) {
742
+        if (!is_array($names)) {
743
+            $names = [$names];
744
+        }
745
+
746
+        $names = array_map('trim', $names);
747
+        array_filter($names);
748
+
749
+        \OCP\Util::writeLog('core', __METHOD__ . ', before: '
750
+            . print_r($this->tags, true), ILogger::DEBUG);
751
+        foreach ($names as $name) {
752
+            $id = null;
753
+
754
+            if (is_numeric($name)) {
755
+                $key = $this->getTagById($name);
756
+            } else {
757
+                $key = $this->getTagByName($name);
758
+            }
759
+            if ($key !== false) {
760
+                $tag = $this->tags[$key];
761
+                $id = $tag->getId();
762
+                unset($this->tags[$key]);
763
+                $this->mapper->delete($tag);
764
+            } else {
765
+                \OCP\Util::writeLog('core', __METHOD__ . 'Cannot delete tag ' . $name
766
+                    . ': not found.', ILogger::ERROR);
767
+            }
768
+            if (!is_null($id) && $id !== false) {
769
+                try {
770
+                    $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
771
+                            . 'WHERE `categoryid` = ?';
772
+                    $stmt = \OC_DB::prepare($sql);
773
+                    $result = $stmt->execute([$id]);
774
+                    if ($result === null) {
775
+                        \OCP\Util::writeLog('core',
776
+                            __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(),
777
+                            ILogger::ERROR);
778
+                        return false;
779
+                    }
780
+                } catch (\Exception $e) {
781
+                    \OC::$server->getLogger()->logException($e, [
782
+                        'message' => __METHOD__,
783
+                        'level' => ILogger::ERROR,
784
+                        'app' => 'core',
785
+                    ]);
786
+                    return false;
787
+                }
788
+            }
789
+        }
790
+        return true;
791
+    }
792
+
793
+    // case-insensitive array_search
794
+    protected function array_searchi($needle, $haystack, $mem='getName') {
795
+        if (!is_array($haystack)) {
796
+            return false;
797
+        }
798
+        return array_search(strtolower($needle), array_map(
799
+            function ($tag) use ($mem) {
800
+                return strtolower(call_user_func([$tag, $mem]));
801
+            }, $haystack)
802
+        );
803
+    }
804
+
805
+    /**
806
+     * Get a tag's ID.
807
+     *
808
+     * @param string $name The tag name to look for.
809
+     * @return string|bool The tag's id or false if no matching tag is found.
810
+     */
811
+    private function getTagId($name) {
812
+        $key = $this->array_searchi($name, $this->tags);
813
+        if ($key !== false) {
814
+            return $this->tags[$key]->getId();
815
+        }
816
+        return false;
817
+    }
818
+
819
+    /**
820
+     * Get a tag by its name.
821
+     *
822
+     * @param string $name The tag name.
823
+     * @return integer|bool The tag object's offset within the $this->tags
824
+     *                      array or false if it doesn't exist.
825
+     */
826
+    private function getTagByName($name) {
827
+        return $this->array_searchi($name, $this->tags, 'getName');
828
+    }
829
+
830
+    /**
831
+     * Get a tag by its ID.
832
+     *
833
+     * @param string $id The tag ID to look for.
834
+     * @return integer|bool The tag object's offset within the $this->tags
835
+     *                      array or false if it doesn't exist.
836
+     */
837
+    private function getTagById($id) {
838
+        return $this->array_searchi($id, $this->tags, 'getId');
839
+    }
840
+
841
+    /**
842
+     * Returns an array mapping a given tag's properties to its values:
843
+     * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
844
+     *
845
+     * @param Tag $tag The tag that is going to be mapped
846
+     * @return array
847
+     */
848
+    private function tagMap(Tag $tag) {
849
+        return [
850
+            'id'    => $tag->getId(),
851
+            'name'  => $tag->getName(),
852
+            'owner' => $tag->getOwner(),
853
+            'type'  => $tag->getType()
854
+        ];
855
+    }
856 856
 }
Please login to merge, or discard this patch.