Completed
Push — master ( bedfb5...151b1b )
by Tom
19:02 queued 08:28
created

AccountMapper::findUserIds()   B

Complexity

Conditions 6
Paths 24

Size

Total Lines 26
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 19
nc 24
nop 4
dl 0
loc 26
rs 8.439
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Jörn Friedrich Dreyer <[email protected]>
4
 * @author Thomas Müller <[email protected]>
5
 * @author Tom Needham <[email protected]>
6
 *
7
 * @copyright Copyright (c) 2018, ownCloud GmbH
8
 * @license AGPL-3.0
9
 *
10
 * This code is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License, version 3,
12
 * as published by the Free Software Foundation.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License, version 3,
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21
 *
22
 */
23
24
namespace OC\User;
25
26
27
use OC\DB\QueryBuilder\Literal;
28
use OCP\AppFramework\Db\DoesNotExistException;
29
use OCP\AppFramework\Db\Entity;
30
use OCP\AppFramework\Db\Mapper;
31
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
32
use OCP\IConfig;
33
use OCP\IDBConnection;
34
35
class AccountMapper extends Mapper {
36
37
	/* @var IConfig */
38
	protected $config;
39
40
	/* @var AccountTermMapper */
41
	protected $termMapper;
42
43
	public function __construct(IConfig $config, IDBConnection $db, AccountTermMapper $termMapper) {
44
		parent::__construct($db, 'accounts', Account::class);
45
		$this->config = $config;
46
		$this->termMapper = $termMapper;
47
	}
48
49
	/**
50
	 * Delegate to term mapper to avoid needing to inject term mapper
51
	 * @param $account_id
52
	 * @param array $terms
53
	 */
54
	public function setTermsForAccount($account_id, array $terms) {
55
		$this->termMapper->setTermsForAccount($account_id, $terms);
56
	}
57
58
	/**
59
	 * Delegate to term mapper to avoid needing to inject term mapper
60
	 * @param $account_id
61
	 * @return AccountTerm[] $terms
62
	 */
63
	public function findByAccountId($account_id) {
64
		return $this->termMapper->findByAccountId($account_id);
65
	}
66
67
	/**
68
	 * @param Account $entity
69
	 * @return Entity the saved entity with the set id
70
	 */
71
	public function insert(Entity $entity) {
72
		// run the normal entity insert operation to get an id
73
		$entity = parent::insert($entity);
74
75
		/** @var Account $entity */
76
		if ($entity->haveTermsChanged()) {
77
			$this->termMapper->setTermsForAccount($entity->getId(), $entity->getSearchTerms());
78
		}
79
		return $entity;
80
	}
81
82
	/**
83
	 * @param Account $entity
84
	 * @return Entity the deleted entity
85
	 */
86
	public function delete(Entity $entity) {
87
		// First delete the search terms for this account
88
		$this->termMapper->deleteTermsForAccount($entity->getId());
89
		return parent::delete($entity);
90
	}
91
92
	/**
93
	 * @param Account $entity
94
	 * @return Entity the updated entity
95
	 */
96
	public function update(Entity $entity) {
97
		if ($entity->haveTermsChanged()) {
0 ignored issues
show
Documentation Bug introduced by
The method haveTermsChanged does not exist on object<OCP\AppFramework\Db\Entity>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
98
			$this->termMapper->setTermsForAccount($entity->getId(), $entity->getSearchTerms());
0 ignored issues
show
Documentation Bug introduced by
The method getSearchTerms does not exist on object<OCP\AppFramework\Db\Entity>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
99
		}
100
		// Then run the normal entity insert operation
101
		return parent::update($entity);
102
	}
103
104
	/**
105
	 * @param string $email
106
	 * @return Account[]
107
	 */
108
	public function getByEmail($email) {
109
		if ($email === null || trim($email) === '') {
110
			throw new \InvalidArgumentException('$email must be defined');
111
		}
112
		$qb = $this->db->getQueryBuilder();
113
		// RFC 5321 says that only domain name is case insensitive, but in practice
114
		// it's the whole email
115
		$qb->select('*')
116
			->from($this->getTableName())
117
			->where($qb->expr()->eq($qb->createFunction('LOWER(`email`)'), $qb->createFunction('LOWER(' . $qb->createNamedParameter($email) . ')')));
118
119
		return $this->findEntities($qb->getSQL(), $qb->getParameters());
120
	}
121
122
	/**
123
	 * @param string $uid
124
	 * @throws DoesNotExistException if the account does not exist
125
	 * @throws MultipleObjectsReturnedException if more than one account exists
126
	 * @return Account
127
	 */
128 View Code Duplication
	public function getByUid($uid) {
129
		$qb = $this->db->getQueryBuilder();
130
		$qb->select('*')
131
			->from($this->getTableName())
132
			->where($qb->expr()->eq('lower_user_id', $qb->createNamedParameter(strtolower($uid))));
133
134
		return $this->findEntity($qb->getSQL(), $qb->getParameters());
135
	}
136
137
	/**
138
	 * @param string $fieldName
139
	 * @param string $pattern
140
	 * @param integer $limit
141
	 * @param integer $offset
142
	 * @return Account[]
143
	 */
144
	public function search($fieldName, $pattern, $limit, $offset) {
145
		$qb = $this->db->getQueryBuilder();
146
		$qb->select('*')
147
			->from($this->getTableName())
148
			->where($qb->expr()->iLike($fieldName, $qb->createNamedParameter('%' . $this->db->escapeLikeParameter($pattern) . '%')))
149
			->orderBy($fieldName);
150
151
		return $this->findEntities($qb->getSQL(), $qb->getParameters(), $limit, $offset);
152
	}
153
154
	/**
155
	 * @param string $pattern
156
	 * @param integer $limit
157
	 * @param integer $offset
158
	 * @return Account[]
159
	 */
160
	public function find($pattern, $limit = null, $offset = null) {
161
162
		$allowMedialSearches = $this->config->getSystemValue('accounts.enable_medial_search', true);
163
		if ($allowMedialSearches) {
164
			$parameter = '%' . $this->db->escapeLikeParameter($pattern) . '%';
165
			$loweredParameter = '%' . $this->db->escapeLikeParameter(strtolower($pattern)) . '%';
166
		} else {
167
			$parameter = $this->db->escapeLikeParameter($pattern) . '%';
168
			$loweredParameter = $this->db->escapeLikeParameter(strtolower($pattern)) . '%';
169
		}
170
171
		$qb = $this->db->getQueryBuilder();
172
		$qb->selectAlias('DISTINCT a.id', 'id')
173
			->addSelect(['user_id', 'lower_user_id', 'display_name', 'email', 'last_login', 'backend', 'state', 'quota', 'home'])
174
			->from($this->getTableName(), 'a')
175
			->leftJoin('a', 'account_terms', 't', $qb->expr()->eq('a.id', 't.account_id'))
176
			->orderBy('display_name')
177
			->where($qb->expr()->like('lower_user_id', $qb->createNamedParameter($loweredParameter)))
178
			->orWhere($qb->expr()->iLike('display_name', $qb->createNamedParameter($parameter)))
179
			->orWhere($qb->expr()->iLike('email', $qb->createNamedParameter($parameter)))
180
			->orWhere($qb->expr()->like('t.term', $qb->createNamedParameter($loweredParameter)));
181
182
		return $this->findEntities($qb->getSQL(), $qb->getParameters(), $limit, $offset);
183
	}
184
185
	public function getUserCountPerBackend($hasLoggedIn) {
186
		$qb = $this->db->getQueryBuilder();
187
		$qb->select(['backend', $qb->createFunction('count(*) as `count`')])
188
			->from($this->getTableName())
189
			->groupBy('backend');
190
191
		if ($hasLoggedIn) {
192
			$qb->where($qb->expr()->gt('last_login', new Literal(0)));
193
		}
194
195
		$result = $qb->execute();
196
		$data = $result->fetchAll();
197
		$result->closeCursor();
198
199
		$return = [];
200
		foreach ($data as $d) {
201
			$return[$d['backend']] = $d['count'];
202
		}
203
204
		return $return;
205
	}
206
207
	public function getUserCount($hasLoggedIn) {
208
		$qb = $this->db->getQueryBuilder();
209
		$qb->select([$qb->createFunction('count(*) as `count`')])
210
			->from($this->getTableName());
211
212
		if ($hasLoggedIn) {
213
			$qb->where($qb->expr()->gt('last_login', new Literal(0)));
214
		}
215
216
		$result = $qb->execute();
217
		$data = $result->fetch();
218
		$result->closeCursor();
219
220
		return (int) $data['count'];
221
	}
222
223
	public function callForAllUsers($callback, $search, $onlySeen) {
224
		$qb = $this->db->getQueryBuilder();
225
		$qb->select(['*'])
226
			->from($this->getTableName());
227
228
		if ($search) {
229
			$qb->where($qb->expr()->iLike('user_id',
230
				$qb->createNamedParameter('%' . $this->db->escapeLikeParameter($search) . '%')));
231
		}
232
		if ($onlySeen) {
233
			$qb->where($qb->expr()->gt('last_login', new Literal(0)));
234
		}
235
		$stmt = $qb->execute();
236
		while ($row = $stmt->fetch()) {
237
			$return = $callback($this->mapRowToEntity($row));
238
			if ($return === false) {
239
				break;
240
			}
241
		}
242
243
		$stmt->closeCursor();
244
	}
245
246
	/**
247
	 * @param string $backend
248
	 * @param bool $hasLoggedIn
249
	 * @param integer $limit
250
	 * @param integer $offset
251
	 * @return string[]
252
	 */
253
	public function findUserIds($backend = null, $hasLoggedIn = null, $limit = null, $offset = null) {
254
		$qb = $this->db->getQueryBuilder();
255
		$qb->select('user_id')
256
			->from($this->getTableName())
257
			->orderBy('user_id'); // needed for predictable limit & offset
258
259
		if ($backend !== null) {
260
			$qb->andWhere($qb->expr()->eq('backend', $qb->createNamedParameter($backend)));
261
		}
262
		if ($hasLoggedIn === true) {
263
			$qb->andWhere($qb->expr()->gt('last_login', new Literal(0)));
264
		} else if ($hasLoggedIn === false) {
265
			$qb->andWhere($qb->expr()->eq('last_login', new Literal(0)));
266
		}
267
		if ($limit !== null) {
268
			$qb->setMaxResults($limit);
269
		}
270
		if ($offset !== null) {
271
			$qb->setFirstResult($offset);
272
		}
273
274
		$stmt = $qb->execute();
275
		$rows = $stmt->fetchAll(\PDO::FETCH_COLUMN);
276
		$stmt->closeCursor();
277
		return $rows;
278
	}
279
280
}
281