1 <?php
2 3 4 5 6 7
8
9 namespace Hawk;
10
11 12 13 14 15
16 class ItemList {
17 use Utils;
18
19
20 const DEFAULT_MODEL = '\StdClass';
21 const ALL_LINES = 'all';
22 const DEFAULT_LINE_CHOICE = 20;
23
24 25 26
27 public static $lineChoice = array(10, 20, 50, 100);
28
29 30 31 32 33
34 public $id,
35
36 37 38 39 40
41 $controls = array(),
42
43 44 45 46 47
48 $fields = array(),
49
50 51 52 53 54
55 $searches = array(),
56
57 58 59 60 61
62 $sorts = array(),
63
64 65 66 67 68
69 $binds = array(),
70
71 72 73 74 75
76 $lines = self::DEFAULT_LINE_CHOICE,
77
78 79 80 81 82
83 $page = 1,
84
85 86 87 88 89
90 $action,
91
92 93 94 95 96
97 $model = self::DEFAULT_MODEL,
98
99 100 101 102 103
104 $table,
105
106 107 108 109 110
111 $reference,
112
113 114 115 116 117
118 $filter,
119
120 121 122 123 124
125 $dbname = MAINDB,
126
127 128 129 130 131
132 $data = null,
133
134 135 136 137 138
139 $group = array(),
140
141 142 143 144 145
146 $selected = null,
147
148 149 150 151 152
153 $lineClass = null,
154
155 156 157 158 159
160 $navigation = true,
161
162 163 164 165 166
167 $noHeader = false,
168
169 170 171 172 173
174 $target = '',
175
176 177 178 179 180
181 $emptyMessage,
182
183
184 185 186 187 188
189 $tpl,
190
191 192 193 194 195
196 $navigationBarTpl,
197
198 199 200 201 202
203 $listTpl,
204
205 206 207 208 209
210 $resultTpl;
211
212
213 214 215 216 217
218 private $dbo,
219
220 221 222 223 224
225 $refresh = false;
226
227
228 229 230 231 232
233 public function __construct($params) {
234
235 $this->emptyMessage = Lang::get('main.list-no-result');
236 $this->action = getenv('REQUEST_URI');
237 $this->refresh = !!App::request()->getParams('refresh');
238
239
240 $this->map($params);
241
242 if($this->data === null) {
243 if(!class_exists($this->model)) {
244 $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
245 $reflection = new \ReflectionClass($trace[1]['class']);
246 $this->model = $reflection->getNamespaceName().'\\'.$this->model;
247 }
248
249 if($this->model == self::DEFAULT_MODEL) {
250 if(!isset($this->table)) {
251 throw new \Exception('ItemList contructor expects property "table" or "model" to be set');
252 }
253
254 if(!isset($this->reference)) {
255 $this->reference = 'id';
256 }
257 }
258 else {
259 $model = $this->model;
260
261 if(!isset($this->reference)) {
262 $this->reference = $model::getPrimaryColumn();
263 }
264
265 if(!isset($this->table)) {
266 $this->table = $model::getTable();
267 }
268
269 $this->dbname = $model::getDbName();
270 }
271
272 $this->refAlias = is_array($this->reference) ? reset($this->reference) : $this->reference;
273 $this->refField = is_array($this->reference) ? reset(array_keys($this->reference)) : $this->reference;
274
275 $this->dbo = DB::get($this->dbname);
276 }
277
278
279 foreach($this->controls as &$button) {
280 if(!empty($button['template'])) {
281 switch($button['template']) {
282 case 'refresh' :
283 $button = array(
284 'icon' => 'refresh',
285 'type' => 'button',
286 'onclick' => 'app.lists["'.$this->id.'"].refresh();',
287 );
288 break;
289
290 case 'print' :
291 $button = array(
292 'icon' => 'print',
293 'type' => 'button',
294 'onclick' => 'app.lists["'.$this->id.'"].print();',
295 );
296 break;
297 }
298 }
299 }
300
301
302 $parameters = array(
303 'searches',
304 'sorts',
305 'lines',
306 'page',
307 );
308
309 if(App::request()->getHeaders('X-List-Filter-'.$this->id)) {
310 App::session()->getUser()->setOption('main.list-'.$this->id, App::request()->getHeaders('X-List-Filter-'.$this->id));
311 }
312
313 $this->userParam = json_decode(App::session()->getUser()->getOptions('main.list-'.$this->id), true);
314
315 foreach($parameters as $name) {
316 if(isset($this->userParam[$name])) {
317 $this->$name = $this->userParam[$name];
318 }
319 }
320
321
322 foreach($this->fields as $name => &$field) {
323 $field = new ItemListField($name, $field, $this);
324 if(isset($this->searches[$name])) {
325 $field->searchValue = $this->searches[$name];
326 }
327
328 if(!empty($this->sorts[$name])) {
329 $field->sortValue = $this->sorts[$name];
330 }
331 }
332
333 $event = new Event('list.'.$this->id.'.instanciated', array('list' => $this));
334 $event->trigger();
335
336 }
337
338
339 340 341 342 343
344 public function get() {
345 if(isset($this->data) && is_array($this->data)) {
346 return $this->getFromArray($this->data);
347 }
348 else if($this->model && $this->table) {
349 return $this->getFromDatabase();
350 }
351
352 }
353
354
355 356 357 358 359
360 private function getFromDatabase() {
361 $fields = array();
362
363 $where = array();
364 if(!empty($this->filter)) {
365 if($this->filter instanceof DBExample) {
366 $where[] = $this->filter->parse($this->binds);
367 }
368 else if(is_array($this->filter)) {
369 $where[] = $this->filter[0];
370 $this->binds = $this->filter[1];
371 }
372 else{
373 $where[] = $this->filter;
374 }
375 }
376
377
378 if(!isset($this->fields[$this->refAlias])) {
379 $this->fields[$this->refAlias] = new ItemListField(
380 $this->refAlias,
381 array(
382 'field' => $this->refField,
383 'hidden' => true,
384 ),
385 $this
386 );
387 }
388
389
390 $searches = array();
391 foreach($this->fields as $name => &$field){
392 if(!$field->independant) {
393 $fields[$this->dbo->formatField($field->field)] = $this->dbo->formatField($name);
394
395
396 $sql = $field->getSearchCondition($this->binds);
397 if($sql) {
398 $where[] = $sql;
399 }
400 }
401 }
402
403 try{
404 $where = implode(' AND ', $where);
405
406 $model = $this->model;
407 $this->recordNumber = $this->dbo->count($this->table, $where, $this->binds, $this->refField, $this->group);
408
409
410 if($this->lines == self::ALL_LINES) {
411 $this->lines = $this->recordNumber;
412 }
413
414 if($this->page > 1 && $this->page > ceil($this->recordNumber / $this->lines)) {
415 $this->page = (ceil($this->recordNumber / $this->lines) > 0) ? ceil($this->recordNumber / $this->lines) : 1;
416 }
417
418 $this->start = (($this->page - 1) * $this->lines);
419
420
421 $request = array(
422 'fields' => $fields,
423 'from' => $this->table,
424 'where' => $where,
425 'binds' => $this->binds,
426 'orderby' => $this->sorts,
427 'group' => $this->group,
428 'start' => $this->start,
429 'limit' => $this->lines,
430 'index' => $this->refAlias,
431 'return' => $this->model,
432 );
433
434 $this->results = $this->dbo->select($request);
435
436 return $this->results;
437 }
438 catch(DBException $e){
439 exit(DEBUG_MODE ? $e->getMessage() : Lang::get('main.list-error'));
440 }
441
442 }
443
444
445 446 447 448 449
450 private function getFromArray($data)
451 {
452 foreach($this->fields as $name => &$field){
453 $pattern = isset($this->searches[$name]) ? $this->searches[$name] : null;
454 if($pattern) {
455 $data = array_filter(
456 $data,
457 function ($line) use ($pattern, $name) {
458 return stripos($line[$name], $pattern) !== false;
459 }
460 );
461 }
462
463 $sort = isset($this->sorts[$name]) ? $this->sorts[$name] : null;
464 if($sort) {
465 usort(
466 $data,
467 function ($a, $b) use ($sort, $name) {
468 if($sort > 0) {
469 return $a[$name] < $b[$name];
470 }
471 else{
472 return $b[$name] < $a[$name];
473 }
474 }
475 );
476 }
477 }
478
479 $this->recordNumber = count($data);
480
481 if($this->page > ceil($this->recordNumber / $this->lines) && $this->page > 1) {
482 $this->page = (ceil($this->recordNumber / $this->lines) > 0) ? ceil($this->recordNumber / $this->lines) : 1;
483 }
484
485 $this->start = (($this->page - 1) * $this->lines);
486 $this->results = array_slice(
487 array_map(
488 function ($line) {
489 return (object) $line;
490 },
491 $data
492 ),
493 $this->start,
494 $this->lines
495 );
496
497 return $this->results;
498
499 }
500
501
502 503 504 505 506
507 public function set($data) {
508 $this->getFromArray($data);
509
510 }
511
512
513 514 515
516 private function getViews() {
517 if(empty($this->tpl)) {
518 $this->tpl = Theme::getSelected()->getView('item-list/container.tpl');
519 }
520
521 if(empty($this->navigationBarTpl)) {
522 $this->navigationBarTpl = Theme::getSelected()->getView('item-list/navigation-bar.tpl');
523 }
524
525 if(empty($this->listTpl)) {
526 $this->listTpl = Theme::getSelected()->getView('item-list/list.tpl');
527 }
528
529 if(empty($this->resultTpl)) {
530 $this->resultTpl = Theme::getSelected()->getView('item-list/result.tpl');
531 }
532
533 }
534
535
536 537 538 539 540
541 public function __toString() {
542 return $this->display();
543
544 }
545
546
547 548 549 550 551
552 public function display() {
553 try{
554
555 $this->get();
556
557
558 $pages = (ceil($this->recordNumber / $this->lines) > 0) ? ceil($this->recordNumber / $this->lines) : 1;
559
560
561 $data = array();
562 $param = array();
563 if(is_array($this->results)) {
564 foreach($this->results as $id => $line){
565 $data[$id] = array();
566 $param[$id] = array('class' => '');
567
568 if($this->selected === $id) {
569 $param[$id]['class'] .= 'selected ';
570 }
571
572 if($this->lineClass) {
573 $function = $this->lineClass;
574 $param[$id]['class'] .= $function($line);
575 }
576
577 foreach($this->fields as $name => $field){
578 $data[$id][$name] = $field->displayCell($id);
579 }
580 }
581 }
582
583
584 $this->getViews();
585
586 return
587 View::make($this->refresh ? $this->resultTpl : $this->tpl, array(
588 'list' => $this,
589 'data' => $data,
590 'linesParameters' => $param,
591 'pages' => $pages,
592 )) .
593 View::make(Plugin::get('main')->getView('list.js.tpl'), array(
594 'list' => $this,
595 'pages' => $pages
596 ));
597 }
598 catch(\Exception $e){
599 App::errorHandler()->exception($e);
600 }
601 }
602
603
604 605 606
607 public function isRefreshing() {
608 return $this->refresh;
609 }
610 }
611