Mercurial > hg > isophonics-drupal-site
comparison core/modules/node/src/NodeGrantDatabaseStorage.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\node; | |
4 | |
5 use Drupal\Core\Access\AccessResult; | |
6 use Drupal\Core\Database\Connection; | |
7 use Drupal\Core\Database\Query\SelectInterface; | |
8 use Drupal\Core\Database\Query\Condition; | |
9 use Drupal\Core\Extension\ModuleHandlerInterface; | |
10 use Drupal\Core\Language\LanguageManagerInterface; | |
11 use Drupal\Core\Session\AccountInterface; | |
12 | |
13 /** | |
14 * Defines a storage handler class that handles the node grants system. | |
15 * | |
16 * This is used to build node query access. | |
17 * | |
18 * @ingroup node_access | |
19 */ | |
20 class NodeGrantDatabaseStorage implements NodeGrantDatabaseStorageInterface { | |
21 | |
22 /** | |
23 * The database connection. | |
24 * | |
25 * @var \Drupal\Core\Database\Connection | |
26 */ | |
27 protected $database; | |
28 | |
29 /** | |
30 * The module handler. | |
31 * | |
32 * @var \Drupal\Core\Extension\ModuleHandlerInterface | |
33 */ | |
34 protected $moduleHandler; | |
35 | |
36 /** | |
37 * The language manager. | |
38 * | |
39 * @var \Drupal\Core\Language\LanguageManagerInterface | |
40 */ | |
41 protected $languageManager; | |
42 | |
43 /** | |
44 * Constructs a NodeGrantDatabaseStorage object. | |
45 * | |
46 * @param \Drupal\Core\Database\Connection $database | |
47 * The database connection. | |
48 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | |
49 * The module handler. | |
50 * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager | |
51 * The language manager. | |
52 */ | |
53 public function __construct(Connection $database, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) { | |
54 $this->database = $database; | |
55 $this->moduleHandler = $module_handler; | |
56 $this->languageManager = $language_manager; | |
57 } | |
58 | |
59 /** | |
60 * {@inheritdoc} | |
61 */ | |
62 public function access(NodeInterface $node, $operation, AccountInterface $account) { | |
63 // Grants only support these operations. | |
64 if (!in_array($operation, ['view', 'update', 'delete'])) { | |
65 return AccessResult::neutral(); | |
66 } | |
67 | |
68 // If no module implements the hook or the node does not have an id there is | |
69 // no point in querying the database for access grants. | |
70 if (!$this->moduleHandler->getImplementations('node_grants') || !$node->id()) { | |
71 // Return the equivalent of the default grant, defined by | |
72 // self::writeDefault(). | |
73 if ($operation === 'view') { | |
74 return AccessResult::allowedIf($node->isPublished())->addCacheableDependency($node); | |
75 } | |
76 else { | |
77 return AccessResult::neutral(); | |
78 } | |
79 } | |
80 | |
81 // Check the database for potential access grants. | |
82 $query = $this->database->select('node_access'); | |
83 $query->addExpression('1'); | |
84 // Only interested for granting in the current operation. | |
85 $query->condition('grant_' . $operation, 1, '>='); | |
86 // Check for grants for this node and the correct langcode. | |
87 $nids = $query->andConditionGroup() | |
88 ->condition('nid', $node->id()) | |
89 ->condition('langcode', $node->language()->getId()); | |
90 // If the node is published, also take the default grant into account. The | |
91 // default is saved with a node ID of 0. | |
92 $status = $node->isPublished(); | |
93 if ($status) { | |
94 $nids = $query->orConditionGroup() | |
95 ->condition($nids) | |
96 ->condition('nid', 0); | |
97 } | |
98 $query->condition($nids); | |
99 $query->range(0, 1); | |
100 | |
101 $grants = static::buildGrantsQueryCondition(node_access_grants($operation, $account)); | |
102 | |
103 if (count($grants) > 0) { | |
104 $query->condition($grants); | |
105 } | |
106 | |
107 // Only the 'view' node grant can currently be cached; the others currently | |
108 // don't have any cacheability metadata. Hopefully, we can add that in the | |
109 // future, which would allow this access check result to be cacheable in all | |
110 // cases. For now, this must remain marked as uncacheable, even when it is | |
111 // theoretically cacheable, because we don't have the necessary metadata to | |
112 // know it for a fact. | |
113 $set_cacheability = function (AccessResult $access_result) use ($operation) { | |
114 $access_result->addCacheContexts(['user.node_grants:' . $operation]); | |
115 if ($operation !== 'view') { | |
116 $access_result->setCacheMaxAge(0); | |
117 } | |
118 return $access_result; | |
119 }; | |
120 | |
121 if ($query->execute()->fetchField()) { | |
122 return $set_cacheability(AccessResult::allowed()); | |
123 } | |
124 else { | |
125 return $set_cacheability(AccessResult::neutral()); | |
126 } | |
127 } | |
128 | |
129 /** | |
130 * {@inheritdoc} | |
131 */ | |
132 public function checkAll(AccountInterface $account) { | |
133 $query = $this->database->select('node_access'); | |
134 $query->addExpression('COUNT(*)'); | |
135 $query | |
136 ->condition('nid', 0) | |
137 ->condition('grant_view', 1, '>='); | |
138 | |
139 $grants = static::buildGrantsQueryCondition(node_access_grants('view', $account)); | |
140 | |
141 if (count($grants) > 0) { | |
142 $query->condition($grants); | |
143 } | |
144 return $query->execute()->fetchField(); | |
145 } | |
146 | |
147 /** | |
148 * {@inheritdoc} | |
149 */ | |
150 public function alterQuery($query, array $tables, $op, AccountInterface $account, $base_table) { | |
151 if (!$langcode = $query->getMetaData('langcode')) { | |
152 $langcode = FALSE; | |
153 } | |
154 | |
155 // Find all instances of the base table being joined -- could appear | |
156 // more than once in the query, and could be aliased. Join each one to | |
157 // the node_access table. | |
158 $grants = node_access_grants($op, $account); | |
159 foreach ($tables as $nalias => $tableinfo) { | |
160 $table = $tableinfo['table']; | |
161 if (!($table instanceof SelectInterface) && $table == $base_table) { | |
162 // Set the subquery. | |
163 $subquery = $this->database->select('node_access', 'na') | |
164 ->fields('na', ['nid']); | |
165 | |
166 // If any grant exists for the specified user, then user has access to the | |
167 // node for the specified operation. | |
168 $grant_conditions = static::buildGrantsQueryCondition($grants); | |
169 | |
170 // Attach conditions to the subquery for nodes. | |
171 if (count($grant_conditions->conditions())) { | |
172 $subquery->condition($grant_conditions); | |
173 } | |
174 $subquery->condition('na.grant_' . $op, 1, '>='); | |
175 | |
176 // Add langcode-based filtering if this is a multilingual site. | |
177 if (\Drupal::languageManager()->isMultilingual()) { | |
178 // If no specific langcode to check for is given, use the grant entry | |
179 // which is set as a fallback. | |
180 // If a specific langcode is given, use the grant entry for it. | |
181 if ($langcode === FALSE) { | |
182 $subquery->condition('na.fallback', 1, '='); | |
183 } | |
184 else { | |
185 $subquery->condition('na.langcode', $langcode, '='); | |
186 } | |
187 } | |
188 | |
189 $field = 'nid'; | |
190 // Now handle entities. | |
191 $subquery->where("$nalias.$field = na.nid"); | |
192 | |
193 $query->exists($subquery); | |
194 } | |
195 } | |
196 } | |
197 | |
198 /** | |
199 * {@inheritdoc} | |
200 */ | |
201 public function write(NodeInterface $node, array $grants, $realm = NULL, $delete = TRUE) { | |
202 if ($delete) { | |
203 $query = $this->database->delete('node_access')->condition('nid', $node->id()); | |
204 if ($realm) { | |
205 $query->condition('realm', [$realm, 'all'], 'IN'); | |
206 } | |
207 $query->execute(); | |
208 } | |
209 // Only perform work when node_access modules are active. | |
210 if (!empty($grants) && count($this->moduleHandler->getImplementations('node_grants'))) { | |
211 $query = $this->database->insert('node_access')->fields(['nid', 'langcode', 'fallback', 'realm', 'gid', 'grant_view', 'grant_update', 'grant_delete']); | |
212 // If we have defined a granted langcode, use it. But if not, add a grant | |
213 // for every language this node is translated to. | |
214 foreach ($grants as $grant) { | |
215 if ($realm && $realm != $grant['realm']) { | |
216 continue; | |
217 } | |
218 if (isset($grant['langcode'])) { | |
219 $grant_languages = [$grant['langcode'] => $this->languageManager->getLanguage($grant['langcode'])]; | |
220 } | |
221 else { | |
222 $grant_languages = $node->getTranslationLanguages(TRUE); | |
223 } | |
224 foreach ($grant_languages as $grant_langcode => $grant_language) { | |
225 // Only write grants; denies are implicit. | |
226 if ($grant['grant_view'] || $grant['grant_update'] || $grant['grant_delete']) { | |
227 $grant['nid'] = $node->id(); | |
228 $grant['langcode'] = $grant_langcode; | |
229 // The record with the original langcode is used as the fallback. | |
230 if ($grant['langcode'] == $node->language()->getId()) { | |
231 $grant['fallback'] = 1; | |
232 } | |
233 else { | |
234 $grant['fallback'] = 0; | |
235 } | |
236 $query->values($grant); | |
237 } | |
238 } | |
239 } | |
240 $query->execute(); | |
241 } | |
242 } | |
243 | |
244 /** | |
245 * {@inheritdoc} | |
246 */ | |
247 public function delete() { | |
248 $this->database->truncate('node_access')->execute(); | |
249 } | |
250 | |
251 /** | |
252 * {@inheritdoc} | |
253 */ | |
254 public function writeDefault() { | |
255 $this->database->insert('node_access') | |
256 ->fields([ | |
257 'nid' => 0, | |
258 'realm' => 'all', | |
259 'gid' => 0, | |
260 'grant_view' => 1, | |
261 'grant_update' => 0, | |
262 'grant_delete' => 0, | |
263 ]) | |
264 ->execute(); | |
265 } | |
266 | |
267 /** | |
268 * {@inheritdoc} | |
269 */ | |
270 public function count() { | |
271 return $this->database->query('SELECT COUNT(*) FROM {node_access}')->fetchField(); | |
272 } | |
273 | |
274 /** | |
275 * {@inheritdoc} | |
276 */ | |
277 public function deleteNodeRecords(array $nids) { | |
278 $this->database->delete('node_access') | |
279 ->condition('nid', $nids, 'IN') | |
280 ->execute(); | |
281 } | |
282 | |
283 /** | |
284 * Creates a query condition from an array of node access grants. | |
285 * | |
286 * @param array $node_access_grants | |
287 * An array of grants, as returned by node_access_grants(). | |
288 * @return \Drupal\Core\Database\Query\Condition | |
289 * A condition object to be passed to $query->condition(). | |
290 * | |
291 * @see node_access_grants() | |
292 */ | |
293 protected static function buildGrantsQueryCondition(array $node_access_grants) { | |
294 $grants = new Condition("OR"); | |
295 foreach ($node_access_grants as $realm => $gids) { | |
296 if (!empty($gids)) { | |
297 $and = new Condition('AND'); | |
298 $grants->condition($and | |
299 ->condition('gid', $gids, 'IN') | |
300 ->condition('realm', $realm) | |
301 ); | |
302 } | |
303 } | |
304 | |
305 return $grants; | |
306 } | |
307 | |
308 } |