/[drupal]/contributions/modules/book_access/book_access.module
ViewVC logotype

Contents of /contributions/modules/book_access/book_access.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.15 - (show annotations) (download) (as text)
Mon Jan 26 05:56:15 2009 UTC (9 months, 4 weeks ago) by hlslaughter
Branch: MAIN
CVS Tags: DRUPAL-6--1-0-RC2, HEAD
Branch point for: DRUPAL-6--1
Changes since 1.14: +22 -11 lines
File MIME type: text/x-php
fixed an undiscovered bug in which the 'book outline' options were not generating properly.
1 <?php
2 // $Id: book_access.module,v 1.14 2009/01/25 22:50:24 hlslaughter Exp $
3
4 /**
5 * @file
6 *
7 * Allow access control for book nodes on a per book basis.
8 *
9 * Based on forum_access.module and tac_lite.module
10 *
11 *
12 */
13
14 // defines may become admin settings in the future
15 define(BOOK_ACCESS_GRANT_PRIORITY, 0);
16
17 /**
18 * Implementation of hook_help()
19 */
20 function book_access_help($path, $arg) {
21 switch ($path) {
22 case 'admin/content/book/access':
23 return t('
24 <p>Configure access control per book based on user roles. Settings
25 affect all pages within the given book. If a page is moved into a
26 different book, it will assume that book\'s access control settings.</p>
27 <p><em>Important:</em> If you are going to manage access control here,
28 please disable the "edit book pages" and "edit own book pages"
29 permissions in the <a href="@permissions">Permissions</a> page or else
30 you may see unexpected behavior.</p>
31 <p>These settings will have no effect for roles with "administer nodes"
32 permission.</p>
33 <p>For more information, see the <a href="@book_access_help">Book Access
34 Help</a>.</p>
35 ', array(
36 '@permissions' => url('admin/user/permissions'),
37 '@book_access_help' => url('admin/help/book_access'),
38 )
39 );
40 break;
41 case 'admin/help#book_access':
42 return t('
43 <p>Allow access control for book nodes on a per book basis.</p>
44 <p><a href="@permissions">Permissions enabled</a> will override <a
45 href="@book_access_settings">Book Access Settings</a>. So, for example,
46 if you would like a role to be able to edit all book pages, regardless,
47 enable "edit pages" in <a href="@permissions">Permissions</a>. However,
48 if you would like to control edit permission on a per book basis,
49 disable that permission in <a href="@permissions">Permissions</a> and
50 configure <a href="@book_access_settings">Book Access Settings</a>
51 accordingly.
52 <p>Certain access control modules can impact functionality of this
53 module. Broad reaching modules such as "taxonomy access" and "content
54 access" can override the values set in the <a
55 href="@book_access_settings">Book Access Settings</a> page. You must
56 turn off all enabled access controls in such modules.
57 <p>To be clear, if you are using additional access control modules, be
58 certain that none of them are allowing access to book nodes. The simplest
59 way to do this is to limit the types of pages that a book may contain to
60 a single node type (such as "book page") and unset any access grants
61 provided by other modules on that node type\'s configuration page.
62 ', array(
63 '@book_access_settings' => url('admin/content/book/access'),
64 '@permissions' => url('admin/user/permissions'),
65 )
66
67 );
68 break;
69 }
70 }
71
72 /**
73 * Implementation of hook_perm().
74 *
75 */
76 function book_access_perm() {
77 return array('administer book access');
78 }
79
80 /**
81 * Implementation of hook_menu().
82 *
83 */
84 function book_access_menu() {
85
86 // we create an additional tab in the book admin page
87 $items['admin/content/book/access'] = array(
88 'title' => t('Access control'),
89 'page callback' => 'drupal_get_form',
90 'page arguments' => array('book_access_admin_form'),
91 'type' => MENU_LOCAL_TASK,
92 'weight' => 7,
93 'access arguments' => array('administer book access'),
94 );
95
96 return $items;
97 }
98
99 /**
100 * This function supplies the book access grants. book_access simply uses
101 * roles as grant IDs.
102 */
103 function book_access_node_grants($user, $op) {
104 $grants['book_access'] = array_keys($user->roles);
105 return $grants;
106 }
107
108 /**
109 * Implementation of hook_node_access_records().
110 *
111 * Returns a list of grant records for the passed in book node object. If we
112 * have a book child page, we return the access settings of the top level parent.
113 */
114 function book_access_node_access_records($node) {
115
116 if (isset($node->book)) {
117 $book_nid = $node->book['bid'];
118
119 $result = db_query('SELECT * FROM {book_access} WHERE nid = %d', $book_nid);
120 while ($grant = db_fetch_object($result)) {
121 $grants[] = array(
122 'realm' => 'book_access',
123 'gid' => $grant->rid,
124 'grant_view' => $grant->grant_view,
125 'grant_update' => $grant->grant_update,
126 'grant_delete' => $grant->grant_delete,
127 'priority' => BOOK_ACCESS_GRANT_PRIORITY,
128 );
129 }
130
131 return $grants;
132 }
133 }
134
135 /**
136 * Implementation of hook_form_alter().
137 *
138 */
139 function book_access_form_alter(&$form, &$form_state, $form_id) {
140
141 if ((isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id)
142 || ($form_id == 'book_outline_form')) {
143 _book_access_restrict_options($form['#node']->nid, $form['book']['bid']['#options']);
144 }
145
146 // when an outline is modified, taxonomy is changed, but the node is not
147 // saved, so node grants can become broken if a book page is moved into
148 // another book. so we fix that by adding an additional #submit callback
149 // that rebuilds the grants when the book outline is modified.
150 if ($form_id == 'book_outline_form') {
151 // TODO: diff settings before/after and only rebuild if changes were made
152 $form['#submit'][] = '_book_access_build_node_grants';
153 }
154 }
155
156 /**
157 * Book access configuration page.
158 *
159 */
160 function book_access_admin_form() {
161 $form = array();
162 $rids = array();
163 $books = array();
164
165 drupal_add_css(drupal_get_path('module', 'book_access') .'/book_access.css');
166
167 // Get a list of roles (which act as grant IDs)
168 $rids = user_roles();
169
170 // Get listing of books, each of which will have it's own access settings
171 $sql = "
172 SELECT n.nid, n.title
173 FROM {node} n
174 LEFT JOIN {book} b ON n.nid = b.bid
175 WHERE b.nid = b.bid
176 ";
177
178 $book_results = db_query($sql);
179
180 while ($book = db_fetch_object($book_results)) {
181 $books[$book->nid] = $book->title;
182 }
183
184 // Each book has its own access control settings
185 foreach ($books as $book_nid => $book_name) {
186
187 // used to store existing grants for this book
188 $view = array();
189 $update = array();
190 $delete = array();
191
192 $result = db_query("SELECT * FROM {book_access} where nid = %d", $book_nid);
193 // if no existing grants, use some safe defaults, only make sense for the
194 // first time when new book created
195 if (db_affected_rows($result) == 0) {
196 $view = array(1);
197 $update = array();
198 $delete = array();
199 }
200 else {
201 while ($book_access = db_fetch_object($result)) {
202 if ($book_access->grant_view) {
203 $view[] = $book_access->rid;
204 }
205 if ($book_access->grant_update) {
206 $update[] = $book_access->rid;
207 }
208 if ($book_access->grant_delete) {
209 $delete[] = $book_access->rid;
210 }
211 }
212 }
213
214 $form['#tree'] = TRUE;
215
216 $form['access'][$book_nid] = array(
217 '#type' => 'fieldset',
218 '#title' => $book_name,
219 '#collapsible' => TRUE,
220 );
221
222 $form['access'][$book_nid]['view'] = array(
223 '#type' => 'checkboxes',
224 '#prefix' => '<div class="book-access-div">',
225 '#suffix' => '</div>',
226 '#options' => $rids,
227 '#title' => t('View this book'),
228 '#default_value' => $view
229 );
230 $form['access'][$book_nid]['update'] = array('#type' => 'checkboxes',
231 '#prefix' => '<div class="book-access-div">',
232 '#suffix' => '</div>',
233 '#options' => $rids,
234 '#title' => t('Edit pages in this book'),
235 '#default_value' => $update
236 );
237 $form['access'][$book_nid]['delete'] = array('#type' => 'checkboxes',
238 '#prefix' => '<div class="book-access-div">',
239 '#suffix' => '</div>',
240 '#options' => $rids,
241 '#title' => t('Delete pages in this book'),
242 '#default_value' => $delete
243 );
244 }
245
246 $form['clearer'] = array(
247 '#value' => '<div class="book-access-clearer"></div>',
248 );
249 $form['submit'] = array(
250 '#type' => 'submit',
251 '#value' => t('Save configuration'),
252 );
253 $form['notice'] = array(
254 '#type' => 'markup',
255 '#value' => '<p>'. t('Node access tables must be rebuilt when these changes are
256 submitted. This may take a few moments.') .'</p>',
257 );
258
259 return $form;
260 }
261
262 function book_access_admin_form_submit($form, &$form_state) {
263
264 foreach ($form_state['values']['access'] as $book_nid => $access) {
265 db_query("DELETE FROM {book_access} WHERE nid = %d", $book_nid);
266
267 foreach ($access['view'] as $rid => $checked) {
268 $grant_view = (bool) $checked;
269 $grant_update = ($access['update'][$rid] > 0);
270 $grant_delete = ($access['delete'][$rid] > 0);
271
272 $sql = "
273 INSERT INTO {book_access} (nid, rid, grant_view, grant_update, grant_delete)
274 VALUES (%d, %d, %d, %d, %d)
275 ";
276
277 db_query($sql, $book_nid, $rid, $grant_view, $grant_update, $grant_delete);
278 }
279 }
280 node_access_rebuild();
281 }
282
283 /**
284 * Implementation of hook_enable().
285 */
286 function book_access_enable() {
287 node_access_needs_rebuild();
288 }
289
290 /**
291 * Implementation of hook_disable().
292 */
293 function book_access_disable() {
294 node_access_needs_rebuild();
295 }
296
297 /**
298 * Somewhat redundant node grants function to allow us to set a node's grants
299 * when a book outline is modified
300 */
301 function _book_access_build_node_grants($form) {
302 $node = $form['#node'];
303 $grants = book_access_node_access_records($node);
304 node_access_write_grants($node, $grants, 'book_access');
305 }
306
307 /**
308 * We don't want users to be able to add child pages to pages they do not
309 * have 'update' grants for, so we remove select options which point to book
310 * pages user does not have that grant for.
311 */
312 function _book_access_restrict_options($bid, &$options) {
313 global $user;
314 $permitted_bids = NULL;
315
316 if ($user->uid == 0 || user_access('administer nodes')) {
317 return;
318 }
319
320 $sql = "
321 SELECT nid
322 FROM {node_access}
323 WHERE realm = 'book_access'
324 AND gid IN (%s)
325 AND grant_update > 0
326 ";
327
328 $results = db_query($sql, implode(',', array_keys($user->roles)));
329
330 while ($result = db_fetch_object($results)) {
331 $permitted_bids[$result->nid] = $result->nid;
332 }
333
334 if (isset($options)) {
335 foreach ($options as $bid => $value) {
336 // <create new book> option uses current nid as the key, skip it
337 if ($bid == $nid) {
338 continue;
339 }
340 if ($bid > 0 && !isset($permitted_bids[$bid])) {
341 unset($options[$nid]);
342 }
343 }
344 }
345 }
346
347 /**
348 * Implementation of hook_node_access_explain() [devel.module].
349 *
350 */
351 function book_access_node_access_explain($row) {
352 static $roles = NULL;
353
354 if ($row->realm == 'book_access') {
355 if (!isset($roles)) {
356 $roles = user_roles();
357 }
358 if (isset($roles[$row->gid])) {
359 //return array($roles[$row->gid]);
360 return t('grants for users of role') . ': ' . $roles[$row->gid];
361 }
362 return array('(unknown gid!)');
363 }
364 }

  ViewVC Help
Powered by ViewVC 1.1.2