View difference between Paste ID: 5gVnameJ and qS7aT5Na
SHOW: | | - or go back to the newest paste.
1
<?php
2
// $Id$
3
/*
4
 * @file allocated_seating.module
5
 * Provides form elements to enable other modules to define allocated seating
6
 * and collect user input
7
 * @copyright Copyright(c) 2010 Lee Rowlands
8
 * @license GPL v2 http://www.fsf.org/licensing/licenses/gpl.html 
9
 * @author Lee Rowlands leerowlands at rowlands-bcs dot com
10
 * 
11
 */
12
13
/**
14
 * Implementation of hook_menu().
15
 */
16
function allocated_seating_menu() {
17
  //generic Call handler, append args to return specific form elements
18
19
  $items['allocated_seating/ajax'] = array(
20
    'title'            => 'Generic Call handler',
21
    'delivery callback' => 'drupal_json_output',
22
    'page callback'    => 'allocated_seating_ajax',
23
    'access arguments' =>  array('create seating plans'),
24
    'type'             => MENU_CALLBACK
25
  );
26
  return $items;
27
} 
28
29
30
/**
31
 * Implements hook_permission
32
*/
33
34
function  allocated_seating_permission() {
35
  return array(
36
    'create seating plans' => array(
37
      'title' => t('Create seating plans'),
38
    )
39
  );
40
}
41
42
/**
43
 * Implementation of hook_elements_info().
44
 */
45
function allocated_seating_element_info() {
46
  $type['allocated_seating_seat_design'] = array(
47
    '#input' => TRUE,
48
    '#tree' => TRUE,
49
    '#default_value' => array(
50
      'seats' => 40,
51
      'row_count' => 2,
52
      'floor_count' => 1,
53
      'floors' => array(),
54
    ),
55
    '#process' => array('allocated_seating_seat_design_process'),
56
    '#theme' => array('allocated_seating_seat_design'),
57
  );
58
  
59
  $type['allocated_seating_seat_select'] = array(
60
    '#input' => TRUE,
61
    '#tree' => TRUE,
62
    '#process' => array('allocated_seating_seat_select_process'),
63
    '#element_validate' => array('allocated_seating_seat_select_validate'),    
64
    '#required_seats' => NULL,
65
  );
66
  return $type;
67
}
68
69
70
/**
71
 * Implementation of hook_theme().
72
 */
73
function allocated_seating_theme($existing, $type, $theme, $path) {
74
  return array(
75
    'allocated_seating_seat_design' => array(
76
      'arguments' => array('element' => NULL),
77
    ),
78
    'allocated_seating_seat_select' => array(
79
      'arguments' => array('element' => NULL),
80
    ),
81
    'allocated_seating_seat_available' => array(
82
      'arguments' => array('plan' => array(),
83
                           'not_available' => array()
84
                          ),
85
    ),
86
  );
87
}
88
89
/**
90
 * Provides processing to expand the form element
91
*/
92
function allocated_seating_seat_select_process($element, $form_state, $complete_form) {
93
  if (empty($element['#plan'])) {
94
    //no floor plan!
95
    return;
96
  }
97
  $plan = $element['#plan'];
98
  if (!$element['#not_available']) {
99
    $element['#not_available'] = array();
100
  }
101
  $blank = 1;
102
  $parents = $element['#parents'];
103
  $element['floors'] = array(
104
    '#tree' => FALSE, //we structure the data in a nest for theming but only want seat# => 1/0
105
    '#parents' => array_merge($parents, array('floors'))
106
  );
107
  foreach ($plan['floors'] as $f => $floor) {
108
    $element['floors'][$f] = array(
109
      '#tree' => FALSE,
110
      '#col_count' => $floor['col_count'],
111
      'title' => array(
112
        '#value' => $floor['title']
113
      ),
114
      'rows' => array(
115
        '#tree' => FALSE,
116
      )  
117
    );
118
    foreach ($floor['rows'] as $r => $row) {
119
      $element['floors'][$f]['rows'][$r] = array(
120
        '#tree' => FALSE,
121
        'columns' => array(
122
          '#tree' => FALSE,
123
          '#parents' => array_merge($parents, array('floors', $f, 'rows', $r, 'columns')),
124
        )
125
      );
126
      foreach ($row['columns'] as $c => $column) {
127
        if ($column['seat'] &&
128
            is_numeric($column['seat']) &&
129
            !in_array($column['seat'], $element['#not_available'])) {
130
          //add the seat checkbox
131
          $element['floors'][$f]['rows'][$r]['columns'][$column['seat']] = array(
132
            '#attributes' => array('class' => 'allocated-seating-seat'),
133
            '#type' => 'checkbox',
134
            '#parents' => array('seats', $column['seat']),
135
            '#title' => $column['seat']
136
          );
137
        }
138
        else {
139
          //add the blank
140
          $element['floors'][$f]['rows'][$r]['columns']['b_'. $blank] = array(
141
            '#value' => '&nbsp;'
142
          );
143
          $blank++;
144
        }
145
      }
146
    }
147
  }
148
  return $element;
149
}
150
151
/**
152
 * Provides processing to expand the form element
153
*/
154
function allocated_seating_seat_design_process($element, $form_state, $complete_form) {
155
  static $plan,$post_static;
156
  // Cache the submitted $_POST data.
157
  if (!empty($element['#post']) || $post_static === NULL) {
158
    if (count($element['#array_parents']) == 1) {
159
      $post = &$element['#post'][$element['#array_parents'][0]];
160
      $post_static = $element['#post'][$element['#array_parents'][0]];
161
    }
162
    elseif (count($element['#array_parents']) == 2) {
163
      $post = &$element['#post'][$element['#array_parents'][0]][$element['#array_parents'][1]];
164
      $post_static = $element['#post'][$element['#array_parents'][0]][$element['#array_parents'][1]];
165
    }
166
    elseif (count($element['#array_parents']) == 3) {
167
      $post = &$element['#post'][$element['#array_parents'][0]][$element['#array_parents'][1]][$element['#array_parents'][2]];
168
      $post_static = $element['#post'][$element['#array_parents'][0]][$element['#array_parents'][1]][$element['#array_parents'][2]];
169
    }
170
    else {
171
      $post = array();
172
      $post_static = array();
173
    }
174
  }
175
  $all_defaults = array();
176
  $all_defaults['allocated_seating_seat_design'] = element_info('allocated_seating_seat_design');
177
  $defaults = $all_defaults['allocated_seating_seat_design']['#default_value'];
178
  // Ensure there are correct defaults.
179
  foreach ($all_defaults['allocated_seating_seat_design']['#default_value'] as $var => $default) {
180
    if (empty($element['#value'][$var])) {
181
      $element['#value'][$var] = $default;
182
    }
183
  }
184
  if (empty($element['#seats'])) {
185
    //must know the # of seats!
186
    return array(
187
      '#value' => t('Did not receive number of seats for the design, cannot proceed. Please set the #seats element attribute.')
188
    );
189
  }
190-
  if (empty($element['#value'])) {
190+
 if (empty($plan)) {
191
    if (empty($element['#value']['floors'])) {
192
    $default = array(
193
      'floors' => array(
194
        array(
195
          'title' => t('Floor 1'),
196
          'rows' => array()
197
        )
198
      )
199
    );//the default
200
    $cols = ceil($element['#seats'] / 4); //default 4 rows
201
    $row = 0;
202
    $s = 1;
203
    while ($row < 4) {
204
      $col = 0;
205
      $default['floors'][0]['rows'][$row] = array('columns' => array()); //add the rows
206
      while ($col < $cols) {
207
        if ($s <= $element['#seats']) {
208
          $seat = $s;
209
          $s++;
210
        }
211
        else {
212
          $seat = FALSE;
213
        }
214
        $default['floors'][0]['rows'][$row]['columns'][] = array('seat' => $seat); //add the seat
215
        $col++;
216
      }
217
      $row++;
218
    }
219
    $element['#value'] = $default;
220
  }
221
  else {
222
      // Check if a new floor needs to be added.
223
      if (!empty($form_state['post']['_triggering_element_name']) && $form_state['post']['_triggering_element_value'] == t('Add floor')) {
224
        $element['#value'] =  $_POST['ticket_plan'];
225
        allocated_seating_plan_add_floor($element, $post);
226
      }
227
    //make sure our seats matches the number in the plan
228
    $real_seats = allocated_seating_real_seats($element['#value']);
229
    $needed_seats = $element['#seats'];
230
    //do we have enough?
231
    $total_seats = allocated_seating_plan_seats($element['#value']);
232
    while ($total_seats < $needed_seats) {
233
      allocated_seating_plan_floor_add_row($element['#value'], 0);
234
      $total_seats = allocated_seating_plan_seats($element['#value']);
235
    }
236
    if ($needed_seats != $real_seats) {
237
      //we have an issue here
238
      $seat_map = allocated_seating_get_map($element['#value']);
239
      $blanks = $seat_map['blanks'];
240
      if ($needed_seats < $real_seats) {
241
        //we need to remove some seats
242
        while ($needed_seats < $real_seats) {
243
          $remove = $seat_map['seats'][$real_seats];
244
          if ($remove) {
245
            $element['#value']['floors'][$remove['floor']]['rows'][$remove['row']]['columns'][$remove['column']]['seat'] = FALSE;
246
          }
247
          $real_seats--;
248
        }
249
      }
250
      
251
      else {
252
        //we need to add some
253
        while ($needed_seats > $real_seats) {
254
          $real_seats++;
255
          $blank = array_shift($blanks);
256
          $element['#value']['floors'][$blank['floor']]['rows'][$blank['row']]['columns'][$blank['column']]['seat'] = $real_seats;
257
        }
258
      }
259
    }
260
  }
261
262
  $plan = $element['#value'];
263
}
264
  $parents = $element['#parents'];
265
  $element['floors'] = array(
266
    '#prefix' => '<div id="as-wrap-'. $element['#id'] .'">',
267
    '#suffix' => '</div>'
268
  );
269
  if ($form_state['storage']['type']) {
270
    $total_seats = allocated_seating_total_seats($plan);
271
    if ($total_seats < $element['#seats']) {
272
      form_set_error(implode('][', $element['#parents']), t('The entered row and column combination is not sufficient to meet the required number of seats, please try again'));
273
    }
274
    switch ($form_state['storage']['type']) {
275
      case 'row':
276
        $floor = $form_state['storage']['floor'];
277
        $current_rows = $original_rows = count($plan['floors'][$floor]['rows']);
278
        $row_count = $plan['floors'][$floor]['row_count'];
279
        while ($current_rows < $row_count) {
280
          allocated_seating_plan_floor_add_row($plan, $floor);
281
          $current_rows++;
282
        }
283
        while ($current_rows > $row_count) {
284
          allocated_seating_plan_remove_row($plan, $floor);
285
          $current_rows--;
286
        }
287
        break;
288
      
289
      case 'column':
290
        $floor = $form_state['storage']['floor'];
291
        $current_cols = $original_cols = count($plan['floors'][$floor]['rows'][0]['columns']);
292
        $col_count = $plan['floors'][$floor]['col_count'];
293
        if ($col_count > 23) {
294
          form_set_error('', t('The seating designer does not support more than 23 columns.'));
295
          $col_count = 23;
296
        }
297
        while ($current_cols < $col_count) {
298
          allocated_seating_plan_floor_add_column($plan, $floor);
299
          $current_cols++;
300
        }
301
        while ($current_cols > $col_count) {
302
          allocated_seating_plan_remove_column($plan, $floor);
303
          $current_cols--;
304
        }
305
        break;
306
    }
307
    unset($form_state['storage']['type'], $form_state['storage']['floor']);
308
  }
309
  foreach ($plan['floors'] as $f => $floor) {
310
    $element['floors'][$f] = array(
311
      '#tree' => TRUE,
312
      '#parents' => array_merge($parents, array('floors', $f)),
313
      'title' => array(
314
        '#type'          => 'textfield',
315
        '#title'         => t('Floor title'),
316
        '#parents' => array_merge($parents, array('floors', $f, 'title')),
317
        '#default_value' => t('Floor !floor', array('!floor' => $f + 1)),
318
        '#size'          => 10,
319
        '#maxlength'     => 50,
320
        '#required'      => TRUE,
321
      ),
322
      'row_count' => array(
323
        '#type'          => 'textfield',
324
        '#title'         => t('Rows'),
325
        '#parents' => array_merge($parents, array('floors', $f, 'row_count')),
326
        '#default_value' => count($floor['rows']),
327
        '#size'          => 10,
328
        '#maxlength'     => 10,
329
        '#required'      => TRUE,
330
        '#ajax' => array(
331
          'path' => 'allocated_seating/ajax/row/'. $f + 1 .'/'. $element['#ajax_extra'] . implode('/', $parents),
332
          'wrapper' => 'as-wrap-'. $element['#id'],
333
          'event' => 'change',
334
          'method' => 'replace',
335
          'effect' => 'fade',
336
        ),
337
      ),
338
      'col_count' => array(
339
        '#type'          => 'textfield',
340
        '#title'         => t('Columns'),
341
        '#parents'       => array_merge($parents, array('floors', $f, 'col_count')),
342
        '#default_value' => count($floor['rows'][0]['columns']),
343
        '#size'          => 10,
344
        '#maxlength'     => 10,
345
        '#required'      => TRUE,
346
        '#ajax' => array(
347
          'event' => 'change',
348
          'path' => 'allocated_seating/ajax/column/'. $f + 1 .'/'. $element['#ajax_extra'] . implode('/', $parents),
349
          'wrapper' => 'as-wrap-'. $element['#id'],
350
          'method' => 'replace',
351
          'effect' => 'fade',
352
        ),
353
      ),
354
      'remove_floor' => array(
355
        '#type' => 'submit',
356
        '#value' => t('Remove floor '). ($f + 1),
357
        '#submit' => array('allocated_seating_design_remove_floor'),
358
        '#ajax' => array(
359
          'path' => 'allocated_seating/ajax/floor/'. $element['#ajax_extra'] . implode('/', $parents),
360
          'wrapper' => 'as-wrap-'. $element['#id'],
361
          'method' => 'replace',
362
          'effect' => 'fade',
363
        )
364
      ),
365
      'rows' => array(
366
        '#tree' => TRUE,
367
        '#parents' => array_merge($parents, array('floors', $f, 'rows')),
368
      )  
369
    );
370
    foreach ($floor['rows'] as $r => $row) {
371
      $element['floors'][$f]['rows'][$r] = array(
372
        '#tree' => TRUE,
373
        '#parents' => array_merge($parents, array('floors', $f, 'rows', $r)),
374
        'columns' => array(
375
          '#tree' => TRUE,
376
          '#parents' => array_merge($parents, array('floors', $f, 'rows', $r, 'columns')),
377
        )
378
      );
379
      foreach ($row['columns'] as $c => $column) {
380
        //add the seat string and value
381
        $element['floors'][$f]['rows'][$r]['columns'][$c]['drag'] = array(
382
          '#parents' => array_merge($parents, array('floors', $f, 'rows', $r, 'columns', $c, 'drag')),
383
          '#attributes' => array('class' => 'allocated-seating-seat'),
384
          '#type' => '#markup',
385
          '#value' => $column['seat'] ? $column['seat'] : '&nbsp;'
386
        );
387
        $element['floors'][$f]['rows'][$r]['columns'][$c]['seat'] = array(
388
          '#parents' => array_merge($parents, array('floors', $f, 'rows', $r, 'columns', $c, 'seat')),
389
          '#type' => 'hidden',
390
          '#value' => $column['seat']
391
        );
392
      }
393
    }
394
  }
395
  
396
  $element['add_floor_row_count'] = array(
397
    '#type'          => 'textfield',
398
    '#title'         => t('Rows'),
399
    '#default_value' => isset($form_state['post']['add_floor_row_count']) ? $form_state['post']['add_floor_row_count'] : 4,
400
    '#size'          => 10,
401
    '#maxlength'     => 10
402
  );
403
  
404
  $element['add_floor_col_count'] = array(
405
    '#type'          => 'textfield',
406
    '#title'         => t('Cols'),
407
    '#default_value' => isset($form_state['post']['add_floor_col_count']) ? $form_state['post']['add_floor_col_count'] : 10,
408
    '#size'          => 10,
409
    '#maxlength'     => 10
410
  );
411
  
412
  $element['add_floor_ticket_count'] = array(
413
    '#type'          => 'textfield',
414
    '#title'         => t('Tickets'),
415
    '#default_value' => 0,
416
    '#size'          => 10,
417
    '#maxlength'     => 10,
418
    '#description'   => t('Start with this many tickets on the floor')
419
  );
420
  
421
  $element['add_floor'] = array(
422
    '#type' => 'submit',
423
    '#value' => t('Add floor'),
424
    '#submit' => array('allocated_seating_design_add_floor'),
425
    '#executes_submit_callback' => true,
426
    '#ajax' => array(
427
      'path' => 'allocated_seating/ajax/floor/'. $element['#ajax_extra'] . implode('/', $parents),
428
      'callback' => 'allocated_seating_design_add_floor',
429
      'wrapper' => 'as-wrap-'. $element['#id'],
430
      'method' => 'replace',
431
      'effect' => 'fade',
432
    )
433
  );
434
  return $element;  
435
}
436
437
/**
438
 * Removes a floor from the design
439
*/
440
function allocated_seating_design_remove_floor($form, &$form_state) {
441
  $button = $form_state['clicked_button'];
442
  $field_parents = $button['#parents'];
443
  $floor = $field_parents[count($field_parents) - 2];
444
  $depth = count($field_parents) - 4;
445
  $field_name = $field_parents[$depth];
446
  if ($depth > 0) {
447
    //we have a tree arrangement here
448
    switch ($depth) {
449
      case 1:
450
        allocated_seating_plan_remove_floor($form_state['values'][$field_parents[0]][$field_name], $floor);
451
        break;
452
      case 2:
453
        allocated_seating_plan_remove_floor($form_state['values'][$field_parents[0]][$field_parents[1]][$field_name], $floor);
454
        break;
455
      case 3:
456
        allocated_seating_plan_remove_floor($form_state['values'][$field_parents[0]][$field_parents[1]][$field_parents[2]][$field_name], $floor);
457
        break;
458
      //that ought to be enough
459
    }
460
  }
461
  else {
462
    allocated_seating_plan_remove_floor($form_state['values'][$field_name], $floor);
463
  }
464
  unset($form_state['submit_handlers']);
465
  form_execute_handlers('submit', $form, $form_state);
466
  $form_state['rebuild'] = TRUE;
467
}
468
469
/**
470
 * Adds a floor to a design
471
*/
472
function allocated_seating_design_add_floor($form, &$form_state) {
473
474
  $button = $form_state['clicked_button'];
475
  $parents = $button['#array_parents'];
476
  $button_name = array_pop($parents);
477
  $field_name = array_pop($parents);
478
  $field_parents = $button['#parents'];
479
  $new_rows = $form_state['values']['add_floor_row_count'];
480
  $new_cols = $form_state['values']['add_floor_col_count'];
481
  $new_tickets = $form_state['values']['add_floor_ticket_count'];
482
  echo 'asds';
483
  exit();
484
  if (count($field_parents) > 1) {
485
    //we have a tree arrangement here
486
    switch (count($field_parents)) {
487
      case 2:
488
        //allocated_seating_plan_add_floor($form_state['values'][$field_parents[0]][$field_name], $new_rows, $new_cols, $new_tickets);
489
        break;
490
      case 3:
491
       // allocated_seating_plan_add_floor($form_state['values'][$field_parents[0]][$field_parents[1]][$field_name], $new_rows, $new_cols, $new_tickets);
492
        break;
493
      case 4:
494
       // allocated_seating_plan_add_floor($form_state['values'][$field_parents[0]][$field_parents[1]][$field_parents[2]][$field_name], $new_rows, $new_cols, $new_tickets);
495
        break;
496
      //that ought to be enough
497
    }
498
  }
499
  else {
500
    //allocated_seating_plan_add_floor($form_state['values'][$field_name], $new_rows, $new_cols, $new_tickets);
501
  }
502
  unset($form_state['submit_handlers']);
503
  form_execute_handlers('submit', $form, $form_state);
504
  $form_state['rebuild'] = TRUE;
505
}
506
507
/**
508
 * Default implementation of theme_allocated_seating_seat_select
509
*/
510
function theme_allocated_seating_seat_select($element) {
511
  jquery_ui_add(array('ui.sortable', 'ui.droppable', 'ui.tabs'));
512
  drupal_add_js(drupal_get_path('module', 'allocated_seating') .'/allocated_seating.js');
513
  drupal_add_css(drupal_get_path('module', 'allocated_seating') .'/allocated_seating.css');
514
  drupal_add_css(drupal_get_path('module', 'jquery_ui') . '/jquery.ui/themes/default/ui.all.css');
515
  $output = '<div id="allocated-seating-tabs" class="allocated-seating-seat-select"><ul>';
516
  $tabs = '';
517
  foreach (element_children($element['floors']) as $key) {
518
    $title = $element['floors'][$key]['title']['#value'];
519
    $output .= '<li><a href="#allocated-seating-tabs-'. $key .'">'. check_plain($title) .'</a></li>';
520
    $tabs .= '<div id="allocated-seating-tabs-'. $key .'">'.
521
      $element['floors'][$key]['title']['#value']
522
      . '<div class="floor-front cols-'. $element['floors'][$key]['#col_count'] .'">'. t('Front') .'</div>'
523
      .'<ul class="allocated-seating-seats no-drag cols-'. $element['floors'][$key]['#col_count'] .'">';
524
    foreach (element_children($element['floors'][$key]['rows']) as $row) {
525
      foreach (element_children($element['floors'][$key]['rows'][$row]['columns']) as $column) {
526
        $tabs .= '<li class="ui-state-highlight';
527
        if ($element['floors'][$key]['rows'][$row]['columns'][$column]['#type'] == 'checkbox') {
528
          $tabs .= '">'. theme('checkbox', $element['floors'][$key]['rows'][$row]['columns'][$column]);
529
        }
530
        else {
531
          $tabs .= ' not-available">&nbsp';
532
        }
533
        $tabs .=  '</li>';
534
      }
535
    }
536
    $tabs .= '</ul>'. $values .'<div class="clear"></div></div>';
537
  }
538
  $output .= '</ul>';
539
  $output .= $tabs;
540
  $output .= '</div>';
541
  return $output;
542
}
543
544
/**
545
 * Default implementation of theme_allocated_seating_seat_available
546
*/
547
function theme_allocated_seating_seat_available($plan, $not_available) {
548
  $blank = 1;
549
  $element['floors'] = array(
550
    '#tree' => FALSE, //we structure the data in a nest for theming but only want seat# => 1/0
551
    '#parents' => array('floors')
552
  );
553
  foreach ($plan['floors'] as $f => $floor) {
554
    $element['floors'][$f] = array(
555
      '#tree' => FALSE,
556
      '#col_count' => $floor['col_count'],
557
      'title' => array(
558
        '#value' => $floor['title']
559
      ),
560
      'rows' => array(
561
        '#tree' => FALSE,
562
      )  
563
    );
564
    foreach ($floor['rows'] as $r => $row) {
565
      $element['floors'][$f]['rows'][$r] = array(
566
        '#tree' => FALSE,
567
        'columns' => array(
568
          '#tree' => FALSE,
569
          '#parents' => array('floors', $f, 'rows', $r, 'columns'),
570
        )
571
      );
572
      foreach ($row['columns'] as $c => $column) {
573
        $state = 'available';
574
        if ($column['seat'] &&
575
            is_numeric($column['seat']) &&
576
            in_array($column['seat'], $not_available)) {
577
          $state = 'unavailable';
578
        }
579
        //add the seat field
580
        $element['floors'][$f]['rows'][$r]['columns'][$column['seat']] = array(
581
          '#value' => '<div class="allocated-seating-seat '. $state .'">'. $column['seat'] .'</div>',
582
          '#parents' => array('seats', $column['seat']),
583
        );
584
      }
585
    }
586
  }
587
  drupal_add_library('system', 'ui.sortable');
588
  drupal_add_library('system', 'ui.tabs');
589
  drupal_add_library('system', 'ui.droppable');
590
  //jquery_ui_add(array('ui.sortable', 'ui.droppable', 'ui.tabs'));
591
  drupal_add_js(drupal_get_path('module', 'allocated_seating') .'/allocated_seating.js');
592
  drupal_add_css(drupal_get_path('module', 'allocated_seating') .'/allocated_seating.css');
593
  drupal_add_css(drupal_get_path('module', 'jquery_ui') . '/jquery.ui/themes/default/ui.all.css');
594
  $output = '<div id="allocated-seating-tabs" class="allocated-seating-seat-select"><ul>';
595
  $tabs = '';
596
  foreach (element_children($element['floors']) as $key) {
597
    $title = $element['floors'][$key]['title']['#value'];
598
    $output .= '<li><a href="#allocated-seating-tabs-'. $key .'">'. check_plain($title) .'</a></li>';
599
    $tabs .= '<div id="allocated-seating-tabs-'. $key .'">'.
600
      $element['floors'][$key]['title']['#value']
601
      . '<div class="floor-front cols-'. $element['floors'][$key]['#col_count'] .'">'. t('Front') .'</div>'
602
      .'<ul class="allocated-seating-seats no-drag cols-'. $element['floors'][$key]['#col_count'] .'">';
603
    foreach (element_children($element['floors'][$key]['rows']) as $row) {
604
      foreach (element_children($element['floors'][$key]['rows'][$row]['columns']) as $column) {
605
        $tabs .= '<li class="ui-state-highlight">'.
606
          drupal_render($element['floors'][$key]['rows'][$row]['columns'][$column])
607
        .'</li>';
608
      }
609
    }
610
    $tabs .= '</ul>'. $values .'<div class="clear"></div></div>';
611
  }
612
  $output .= '</ul>';
613
  $output .= $tabs;
614
  $output .= '</div>';
615
  return $output;
616
}
617
618
/**
619
 * Default implementation of theme_allocated_seating_seat_design
620
*/
621
function theme_allocated_seating_seat_design($variable) {
622
  drupal_add_library('system', 'ui.sortable');
623
  drupal_add_library('system', 'ui.tabs');
624
  drupal_add_library('system', 'ui.droppable');
625
  $element = array_shift($variable);
626
627
  //jquery_ui_add(array('ui.sortable', 'ui.tabs', 'ui.droppable'));
628
  drupal_add_js(drupal_get_path('module', 'allocated_seating') .'/allocated_seating.js');
629
  drupal_add_css(drupal_get_path('module', 'allocated_seating') .'/allocated_seating.css');
630
  drupal_add_css(drupal_get_path('module', 'jquery_ui') . '/jquery.ui/themes/default/ui.all.css');
631
  $output = '<div id="as-wrap-'. $element['#id'] .'" class="as-wrap"><div id="allocated-seating-tabs" class="allocated-seating-seat-design"><ul>';
632
  $tabs = '';
633
  foreach (element_children($element['floors']) as $key) {
634
    $inputs = '';
635
    $title = $element['floors'][$key]['title']['#value'];
636
    $output .= '<li><a href="#allocated-seating-tabs-'. $key .'">'. check_plain($title) .'</a></li>';
637
    $tabs .= '<div id="allocated-seating-tabs-'. $key .'" class="floor-details">'.
638
      '<div class="floor-item">'.
639
      theme('form_element_label', $element['floors'][$key]['title']).
640
      theme('textfield', $element['floors'][$key]['title']) .
641
      '</div><div class="floor-item">'.
642
      theme('form_element_label', $element['floors'][$key]['row_count']).
643
      theme('textfield', $element['floors'][$key]['row_count']) .
644
      '</div><div class="floor-item">'.
645
      theme('form_element_label', $element['floors'][$key]['col_count']).
646
      theme('textfield', $element['floors'][$key]['col_count']).
647
      '</div>'
648
      . '<div class="floor-front cols-'. $element['floors'][$key]['col_count']['#value'] .'">'. t('Front') .'</div>'
649
      .'<ul class="allocated-seating-seats connectedSortable ui-helper-reset cols-'. $element['floors'][$key]['col_count']['#value'] .'">';
650
    foreach (element_children($element['floors'][$key]['rows']) as $row) {
651
      foreach (element_children($element['floors'][$key]['rows'][$row]['columns']) as $column) {
652
        $tabs .= '<li class="ui-state-highlight" rel="'. $element['floors'][$key]['rows'][$row]['columns'][$column]['seat']['#value'] .'">'.
653
                    $element['floors'][$key]['rows'][$row]['columns'][$column]['drag']['#value']
654
                .'</li>';
655
        $inputs .= theme('hidden', $element['floors'][$key]['rows'][$row]['columns'][$column]['seat']);
656
      }
657
    }
658
    $tabs .= $inputs .'</ul>'. $values .'<div class="clear"></div>'.
659
      drupal_render($element['floors'][$key]['remove_floor'])
660
      .'</div>';
661
  }
662
  $output .= '</ul>';
663
  $output .= $tabs;
664
  $output .= '</div><div class="allocated-seating-design-add-floor"><fieldset><legend>'. t('Add a floor') .'</legend>'.
665
            t('Add a new floor with this many rows/columns').
666
            drupal_render($element['add_floor_row_count']).
667
            drupal_render($element['add_floor_col_count']).
668
            drupal_render($element['add_floor_ticket_count']).
669
            drupal_render($element['add_floor'])
670
          .'</fieldset></div>';
671
  if (!empty($element['#description'])) {
672
    $output .= ' <div class="description as-description">'. $element['#description'] ."</div>\n";
673
  }       
674
  $output .= '</div><div class="clear"></div>';
675
  return $output;
676
}
677
678
/**
679
 * Validation handler for seat selection
680
*/
681
function allocated_seating_seat_select_validate($element, &$form_state) {
682
  $seats = array_sum($form_state['values']['seats']);
683
  if ($element['#required_seats'] &&
684
      $element['#required_seats'] != $seats) {
685
    form_error($element, format_plural($element['#required_seats'],
686
                                     'Please choose 1 seat',
687
                                     'Please choose @count seats'));
688
  }
689
}
690
691
/**
692
 * Call callback handler.
693
 * 
694
 */
695
function allocated_seating_ajax() {
696
  module_load_include('inc', 'node', 'node.pages');
697
  //get args
698
699
  $elements = func_get_args();
700
  $type = array_shift($elements);
701
  if ($type == 'row' || $type == 'column') {
702
    $floor = array_shift($elements);
703
  }
704
705
  
706
  //we set submitted as TRUE so prevent _form_builder_ie_cleanup() from triggering
707
  //submit on ajax that does not involve a button
708
  $form_state = array('storage' => NULL, 'submitted' => TRUE);
709
  $form_build_id = $_POST['form_build_id'];
710
  $form = form_get_cache($form_build_id, $form_state);
711
  if (isset($_POST['_triggering_element_name'])) {
712
713
	if($_POST['_triggering_element_name'] == 'op' && $_POST['_triggering_element_value'] == 'Add floor') {
714
		$form_state['submit_handlers'] = array('allocated_seating_design_add_floor');
715
	}
716
  } 
717
718
  unset($form_state['storage']['type']); //don't redo the cached action
719
  //remove validation (thanks ajax_helper!)
720
  if (!isset($_POST['op'])) {
721
    // For the defaults 
722
    $form['#validate'] = NULL;
723
    $form['#submit'] = NULL;
724
    // For customly set #validate and #submit handlers.
725
    $form_state['submit_handlers'] = array();
726
    $form_state['validate_handlers'] = array();
727
  }
728
  $form_id = $form['#form_id'];
729
  $form['#post'] = $_POST;
730
  $form['#redirect'] = FALSE;
731
  $form['#programmed'] = FALSE;
732
  $form_state['post'] = $_POST;
733
734
  drupal_process_form($form_id, $form, $form_state);
735
736
  $form_state['storage']['type'] = $type;
737
  $form_state['storage']['floor'] = $floor;
738
  if ($type == 'floor') {
739
    $form_state['storage']['add_floor_row_count'] = $form_state['values']['add_floor_row_count'];
740
    $form_state['storage']['add_floor_col_count'] = $form_state['values']['add_floor_col_count'];
741
  }
742
743
  $form = drupal_rebuild_form($form_id, $form_state,  $form );
744
  foreach ($elements as $element) {
745
    $form = $form[$element];
746
  }
747
  if (!$form) {
748
    watchdog('allocated seating', 'Call method was called with elements not found in resultant form', array(), WATCHDOG_ERROR);
749
    $form = array(
750
      'error' => array(
751
        '#value' => t('An error occured while processing your request, please try again. If the problem persists, please contact the site administrator')
752
      )
753
    );
754
  }
755
  unset($form['#prefix'], $form['#suffix']); // Prevent duplicate wrappers.
756
  $javascript = drupal_add_js(NULL, NULL, 'header');
757
  // Loop through the JS settings and find the settings needed for our buttons.
758
759
760
  $new_ajax_settings = array();
761
  if (isset($javascript['settings'])) {
762
    foreach ($javascript['settings'] as $settings) {
763
      if (isset($settings['ajax'])) {
764
        foreach ($settings['ajax'] as $id => $ajax_settings) {
765
            $new_ajax_settings[$id] = $ajax_settings;
766
        }
767
      }
768
    }
769
  }
770
771
  // Add the Call settings needed for our new elements.
772
  if (!empty($new_ajax_settings)) {
773
    $output = '<script type="text/javascript">jQuery.extend(Drupal.settings.Ajax, '. drupal_to_js($new_ajax_settings) .');</script>';
774
  }
775
776
  return array(array(
777
    'status'   => TRUE,
778
    'data'     => theme('status_messages') . $output . drupal_render($form),
779
    'settings' => call_user_func_array('array_merge_recursive', $javascript['setting']),
780
    'command' => 'syncSeating'
781
  ));
782
}
783
784
/**
785
 * Utility to remove a floor from the plan
786
 * @param $plan array the plan
787
*/
788
function allocated_seating_plan_remove_floor(&$plan, $floor) {
789
  if (!isset($plan['floors'][$floor])) {
790
    return;
791
  }
792
  foreach ($plan['floors'][$floor]['rows'] as $row) {
793
    foreach ($row['columns'] as $col) {
794
      if ((int)$col['seat'] > 0) {
795
        form_set_error('', t('You cannot remove this floor, it still contains seats, please move them to another floor first.'));
796
        return;
797
      }
798
    }
799
  }
800
  unset($plan['floors'][$floor]);
801
}
802
803
804
/**
805
 * Utility to remove a col from the plan
806
 * @param $plan array the plan
807
*/
808
function allocated_seating_plan_remove_column(&$plan, $floor) {
809
  if (!isset($plan['floors'][$floor])) {
810
    return;
811
  }
812
  $remove_col = count($plan['floors'][$floor]['rows'][0]['columns']) - 1;
813
  foreach ($plan['floors'][$floor]['rows'] as $row) {
814
    $col = $row['columns'][$remove_col];
815
    if ((int)$col['seat'] > 0) {
816
      form_set_error('', t('You cannot remove the last column from this floor, it still contains seats, please move them to another floor or column first.'));
817
      return;
818
    }
819
  }
820
  foreach ($plan['floors'][$floor]['rows'] as $key => $row) {
821
    unset($plan['floors'][$floor]['rows'][$key]['columns'][$remove_col]);
822
  }
823
}
824
825
/**
826
 * Utility to remove a row from the plan
827
 * @param $plan array the plan
828
*/
829
function allocated_seating_plan_remove_row(&$plan, $floor) {
830
  if (!isset($plan['floors'][$floor])) {
831
    return;
832
  }
833
  $remove_row = count($plan['floors'][$floor]['rows']) - 1;
834
  foreach ($plan['floors'][$floor]['rows'][$remove_row]['columns'] as $col) {
835
    if ((int)$col['seat'] > 0) {
836
      form_set_error('', t('You cannot remove the last row from this floor, it still contains seats, please move them to another floor or row first.'));
837
      return;
838
    }
839
  }
840
  unset($plan['floors'][$floor]['rows'][$remove_row]);
841
}
842
843
/**
844
 * Utility to add a floor to a plan
845
 * @param $plan array the plan
846
*/
847
function allocated_seating_plan_add_floor(&$element, &$post) {
848
849
  $plan = &$element['#value'];
850
  $rows = $plan['add_floor_row_count'];
851
  $cols = $plan['add_floor_col_count'];
852
  $tickets = $plan['add_floor_ticket_count'];
853
  $available_tickets = array();
854
  if ($tickets) {
855
    if ($tickets > ($rows * $cols)) {
856
      $tickets = $rows * $cols;
857
      drupal_set_message(t('You cannot move more than !tix seats to the new floor', array('!tix' => ($rows * $cols))));
858
    }
859
    $map = allocated_seating_get_map($plan);
860
    $seat = count($available_tickets);
861
    $seats = $map['seats'];
862
    $seat_numbers = array_keys($seats);
863
    asort($seat_numbers);
864
    while ($seat < $tickets) {
865
      $seat++;
866
      $new_seat = array_pop($seat_numbers);
867
      $remove = $seats[$new_seat];
868
      $available_tickets[] = $new_seat;
869
      $plan['floors'][$remove['floor']]['rows'][$remove['row']]['columns'][$remove['column']]['seat'] = FALSE;
870
    }
871
  }
872
  $floor = array(
873
    'title' => t('Floor !floor', array('!floor' => count($plan['floors']) + 1)),
874
    'rows' => array()
875
  );
876
  $row = 0;
877
  $s = 1;
878
  while ($row < $rows) {
879
    $col = 0;
880
    $floor['rows'][$row] = array('columns' => array()); //add the rows
881
    while ($col < $cols) {
882
      if (count($available_tickets) > 0) {
883
        $seat = array_pop($available_tickets);
884
      }
885
      else {
886
        $seat = FALSE;
887
      }
888
      $floor['rows'][$row]['columns'][] = array('seat' => $seat); //add the seat as a blank or a number
889
      $col++;
890
    }
891
    $row++;
892
  }
893
  $plan['floors'][] = $floor;
894
}
895
896
/**
897
 * Utility to add row to a floor of a plan
898
 * @param $plan array the plan
899
 * @param $floor int the floor delta
900
*/
901
function allocated_seating_plan_floor_add_row(&$plan, $floor) {
902
  $cols = count($plan['floors'][$floor]['rows'][0]['columns']);
903
  $rows = count($plan['floors'][$floor]['rows']);
904
  $col = 0;
905
  $row = array('columns' => array()); //create the row
906
  while ($col < $cols) {
907
    $row['columns'][] = array('seat' => FALSE); //add the seat as a blank
908
    $col++;
909
  }
910
  $plan['floors'][$floor]['rows'][] = $row;
911
}
912
913
/**
914
 * Utility to add column to a floor of a plan
915
 * @param $plan array the plan
916
 * @param $floor int the floor delta
917
*/
918
function allocated_seating_plan_floor_add_column(&$plan, $floor) {
919
  $rows = count($plan['floors'][$floor]['rows']);
920
  $row = 0;
921
  while ($row < $rows) {
922
    $plan['floors'][$floor]['rows'][$row]['columns'][] = array('seat' => FALSE);
923
    $row++;
924
  }
925
}
926
927
/**
928
 * Utility to setup required js files etc
929
*/
930
function allocated_seating_setup() {
931
  drupal_add_library('system', 'ui.sortable');
932
  drupal_add_library('system', 'ui.tabs');
933
  drupal_add_library('system', 'ui.droppable');
934
  //jquery_ui_add(array('ui.sortable', 'ui.tabs', 'ui.droppable'));
935
  drupal_add_js(drupal_get_path('module', 'allocated_seating') .'/allocated_seating.js');
936
  drupal_add_css(drupal_get_path('module', 'allocated_seating') .'/allocated_seating.css');
937
}
938
939
/**
940
 * Util to fetch number of rows and columns (seats) submitted in the plan
941
 * @param $plan array seating plan as submitted in $element['#value']
942
*/
943
function allocated_seating_total_seats($plan) {
944
  $seats = 0;
945
  foreach ($plan['floors'] as $floor) {
946
    $rows = $floor['row_count'];
947
    $cols = $floor['col_count'];
948
    $seats += ($rows * $cols);
949
  }
950
  return $seats;
951
}
952
953
/**
954
 * Util to fetch number of rows and columns (seats) submitted in the plan
955
 * @param $plan array seating plan as submitted in $element['#value']
956
*/
957
function allocated_seating_plan_seats($plan) {
958
  $seats = 0;
959
  foreach ($plan['floors'] as $floor) {
960
    $rows = count($floor['rows']);
961
    $cols = count($floor['rows'][0]['columns']);
962
    $seats += ($rows * $cols);
963
  }
964
  return $seats;
965
}
966
967
/**
968
 * Util to fetch number of real seats (non blank) in the plan
969
 * @param $plan array seating plan as submitted in $element['#value']
970
*/
971
function allocated_seating_real_seats($plan) {
972
  $seats = 0;
973
  foreach ($plan['floors'] as $floor) {
974
    foreach ($floor['rows'] as $row) {
975
      foreach ($row['columns'] as $column) {
976
        if ((int)$column['seat'] > 0) {
977
          $seats++;
978
        }
979
      }
980
    }
981
  }
982
  return $seats;
983
}
984
985
/**
986
 * Util to fetch reveresed map keyed by seat
987
 * @param $plan array seating plan
988
 * @return array with two members as follows
989
 *  seats => array keyed by seat # with array or floor, row and column members
990
 *  blanks => same but for blanks
991
*/
992
function allocated_seating_get_map($plan) {
993
  $map = $blanks = array();
994
  foreach ($plan['floors'] as $fid => $floor) {
995
    foreach ($floor['rows'] as $rid => $row) {
996
      foreach ($row['columns'] as $cid => $column) {
997
        if ((int)$column['seat'] > 0) {
998
          $map[$column['seat']] = array('row' => $rid, 'column' => $cid, 'floor' => $fid);
999
        }
1000
        else {
1001
          $blanks[] = array('row' => $rid, 'column' => $cid, 'floor' => $fid);
1002
        }
1003
      }
1004
    }
1005
  }
1006
  return array('seats' => $map, 'blanks' => $blanks);
1007
}