e91f52ea8b2576b018c4cfca010d45041b9a2e7e
[yaffs-website] / web / core / modules / book / src / Form / BookAdminEditForm.php
1 <?php
2
3 namespace Drupal\book\Form;
4
5 use Drupal\book\BookManager;
6 use Drupal\book\BookManagerInterface;
7 use Drupal\Component\Utility\Crypt;
8 use Drupal\Core\Entity\EntityStorageInterface;
9 use Drupal\Core\Form\FormBase;
10 use Drupal\Core\Form\FormStateInterface;
11 use Drupal\Core\Render\Element;
12 use Drupal\Core\Url;
13 use Drupal\node\NodeInterface;
14 use Symfony\Component\DependencyInjection\ContainerInterface;
15
16 /**
17  * Provides a form for administering a single book's hierarchy.
18  */
19 class BookAdminEditForm extends FormBase {
20
21   /**
22    * The node storage.
23    *
24    * @var \Drupal\Core\Entity\EntityStorageInterface
25    */
26   protected $nodeStorage;
27
28   /**
29    * The book manager.
30    *
31    * @var \Drupal\book\BookManagerInterface
32    */
33   protected $bookManager;
34
35   /**
36    * Constructs a new BookAdminEditForm.
37    *
38    * @param \Drupal\Core\Entity\EntityStorageInterface $node_storage
39    *   The custom block storage.
40    * @param \Drupal\book\BookManagerInterface $book_manager
41    *   The book manager.
42    */
43   public function __construct(EntityStorageInterface $node_storage, BookManagerInterface $book_manager) {
44     $this->nodeStorage = $node_storage;
45     $this->bookManager = $book_manager;
46   }
47
48   /**
49    * {@inheritdoc}
50    */
51   public static function create(ContainerInterface $container) {
52     $entity_manager = $container->get('entity.manager');
53     return new static(
54       $entity_manager->getStorage('node'),
55       $container->get('book.manager')
56     );
57   }
58
59   /**
60    * {@inheritdoc}
61    */
62   public function getFormId() {
63     return 'book_admin_edit';
64   }
65
66   /**
67    * {@inheritdoc}
68    */
69   public function buildForm(array $form, FormStateInterface $form_state, NodeInterface $node = NULL) {
70     $form['#title'] = $node->label();
71     $form['#node'] = $node;
72     $this->bookAdminTable($node, $form);
73     $form['save'] = [
74       '#type' => 'submit',
75       '#value' => $this->t('Save book pages'),
76     ];
77
78     return $form;
79   }
80
81   /**
82    * {@inheritdoc}
83    */
84   public function validateForm(array &$form, FormStateInterface $form_state) {
85     if ($form_state->getValue('tree_hash') != $form_state->getValue('tree_current_hash')) {
86       $form_state->setErrorByName('', $this->t('This book has been modified by another user, the changes could not be saved.'));
87     }
88   }
89
90   /**
91    * {@inheritdoc}
92    */
93   public function submitForm(array &$form, FormStateInterface $form_state) {
94     // Save elements in the same order as defined in post rather than the form.
95     // This ensures parents are updated before their children, preventing orphans.
96     $user_input = $form_state->getUserInput();
97     if (isset($user_input['table'])) {
98       $order = array_flip(array_keys($user_input['table']));
99       $form['table'] = array_merge($order, $form['table']);
100
101       foreach (Element::children($form['table']) as $key) {
102         if ($form['table'][$key]['#item']) {
103           $row = $form['table'][$key];
104           $values = $form_state->getValue(['table', $key]);
105
106           // Update menu item if moved.
107           if ($row['parent']['pid']['#default_value'] != $values['pid'] || $row['weight']['#default_value'] != $values['weight']) {
108             $link = $this->bookManager->loadBookLink($values['nid'], FALSE);
109             $link['weight'] = $values['weight'];
110             $link['pid'] = $values['pid'];
111             $this->bookManager->saveBookLink($link, FALSE);
112           }
113
114           // Update the title if changed.
115           if ($row['title']['#default_value'] != $values['title']) {
116             $node = $this->nodeStorage->load($values['nid']);
117             $node->revision_log = $this->t('Title changed from %original to %current.', ['%original' => $node->label(), '%current' => $values['title']]);
118             $node->title = $values['title'];
119             $node->book['link_title'] = $values['title'];
120             $node->setNewRevision();
121             $node->save();
122             $this->logger('content')->notice('book: updated %title.', ['%title' => $node->label(), 'link' => $node->link($this->t('View'))]);
123           }
124         }
125       }
126     }
127
128     drupal_set_message($this->t('Updated book %title.', ['%title' => $form['#node']->label()]));
129   }
130
131   /**
132    * Builds the table portion of the form for the book administration page.
133    *
134    * @param \Drupal\node\NodeInterface $node
135    *   The node of the top-level page in the book.
136    * @param array $form
137    *   The form that is being modified, passed by reference.
138    *
139    * @see self::buildForm()
140    */
141   protected function bookAdminTable(NodeInterface $node, array &$form) {
142     $form['table'] = [
143       '#type' => 'table',
144       '#header' => [
145         $this->t('Title'),
146         $this->t('Weight'),
147         $this->t('Parent'),
148         $this->t('Operations'),
149       ],
150       '#empty' => $this->t('No book content available.'),
151       '#tabledrag' => [
152         [
153           'action' => 'match',
154           'relationship' => 'parent',
155           'group' => 'book-pid',
156           'subgroup' => 'book-pid',
157           'source' => 'book-nid',
158           'hidden' => TRUE,
159           'limit' => BookManager::BOOK_MAX_DEPTH - 2,
160         ],
161         [
162           'action' => 'order',
163           'relationship' => 'sibling',
164           'group' => 'book-weight',
165         ],
166       ],
167     ];
168
169     $tree = $this->bookManager->bookSubtreeData($node->book);
170     // Do not include the book item itself.
171     $tree = array_shift($tree);
172     if ($tree['below']) {
173       $hash = Crypt::hashBase64(serialize($tree['below']));
174       // Store the hash value as a hidden form element so that we can detect
175       // if another user changed the book hierarchy.
176       $form['tree_hash'] = [
177         '#type' => 'hidden',
178         '#default_value' => $hash,
179       ];
180       $form['tree_current_hash'] = [
181         '#type' => 'value',
182         '#value' => $hash,
183       ];
184       $this->bookAdminTableTree($tree['below'], $form['table']);
185     }
186   }
187
188   /**
189    * Helps build the main table in the book administration page form.
190    *
191    * @param array $tree
192    *   A subtree of the book menu hierarchy.
193    * @param array $form
194    *   The form that is being modified, passed by reference.
195    *
196    * @see self::buildForm()
197    */
198   protected function bookAdminTableTree(array $tree, array &$form) {
199     // The delta must be big enough to give each node a distinct value.
200     $count = count($tree);
201     $delta = ($count < 30) ? 15 : intval($count / 2) + 1;
202
203     $access = \Drupal::currentUser()->hasPermission('administer nodes');
204     $destination = $this->getDestinationArray();
205
206     foreach ($tree as $data) {
207       $nid = $data['link']['nid'];
208       $id = 'book-admin-' . $nid;
209
210       $form[$id]['#item'] = $data['link'];
211       $form[$id]['#nid'] = $nid;
212       $form[$id]['#attributes']['class'][] = 'draggable';
213       $form[$id]['#weight'] = $data['link']['weight'];
214
215       if (isset($data['link']['depth']) && $data['link']['depth'] > 2) {
216         $indentation = [
217           '#theme' => 'indentation',
218           '#size' => $data['link']['depth'] - 2,
219         ];
220       }
221
222       $form[$id]['title'] = [
223         '#prefix' => !empty($indentation) ? drupal_render($indentation) : '',
224         '#type' => 'textfield',
225         '#default_value' => $data['link']['title'],
226         '#maxlength' => 255,
227         '#size' => 40,
228       ];
229
230       $form[$id]['weight'] = [
231         '#type' => 'weight',
232         '#default_value' => $data['link']['weight'],
233         '#delta' => max($delta, abs($data['link']['weight'])),
234         '#title' => $this->t('Weight for @title', ['@title' => $data['link']['title']]),
235         '#title_display' => 'invisible',
236         '#attributes' => [
237           'class' => ['book-weight'],
238         ],
239       ];
240
241       $form[$id]['parent']['nid'] = [
242         '#parents' => ['table', $id, 'nid'],
243         '#type' => 'hidden',
244         '#value' => $nid,
245         '#attributes' => [
246           'class' => ['book-nid'],
247         ],
248       ];
249
250       $form[$id]['parent']['pid'] = [
251         '#parents' => ['table', $id, 'pid'],
252         '#type' => 'hidden',
253         '#default_value' => $data['link']['pid'],
254         '#attributes' => [
255           'class' => ['book-pid'],
256         ],
257       ];
258
259       $form[$id]['parent']['bid'] = [
260         '#parents' => ['table', $id, 'bid'],
261         '#type' => 'hidden',
262         '#default_value' => $data['link']['bid'],
263         '#attributes' => [
264           'class' => ['book-bid'],
265         ],
266       ];
267
268       $form[$id]['operations'] = [
269         '#type' => 'operations',
270       ];
271       $form[$id]['operations']['#links']['view'] = [
272         'title' => $this->t('View'),
273         'url' => new Url('entity.node.canonical', ['node' => $nid]),
274       ];
275
276       if ($access) {
277         $form[$id]['operations']['#links']['edit'] = [
278           'title' => $this->t('Edit'),
279           'url' => new Url('entity.node.edit_form', ['node' => $nid]),
280           'query' => $destination,
281         ];
282         $form[$id]['operations']['#links']['delete'] = [
283           'title' => $this->t('Delete'),
284           'url' => new Url('entity.node.delete_form', ['node' => $nid]),
285           'query' => $destination,
286         ];
287       }
288
289       if ($data['below']) {
290         $this->bookAdminTableTree($data['below'], $form);
291       }
292     }
293   }
294
295 }