Code

2dc8237a9b8b3a10ec99e04ce7b72e216e62cf1b
[gosa.git] / gosa-core / include / class_sortableListing.inc
1 <?php
2 /*
3  * This code is part of GOsa (http://www.gosa-project.org)
4  * Copyright (C) 2003-2008 GONICUS GmbH
5  *
6  * ID: $$Id: class_listing.inc 15087 2010-01-06 13:45:49Z hickert $$
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
23 class sortableListing {
24   private $header= null;
25   private $colspecs= null;
26   private $reorderable= true;
27   private $width= "400px";
28   private $height= "100px";
29   private $cssclass= "";
30   private $id;
32   private $data= array();
33   private $displayData= array();
34   private $columns= 0;
35   private $deleteable= false;
36   private $editable= false;
37   private $instantDelete= true;
38   private $action;
39   private $targets;
40   private $mapping;
41   private $current_mapping;
42   private $active_index;
43   private $scrollPosition= 0;
45   private $acl= "";
46   private $modified= false;
48   function sortableListing($data= array(), $displayData= null)
49   {
50     global $config;
52     // Save data to display
53     $this->setData($data);
54     if (!$displayData) {
55       $displayData= array();
56       foreach ($data as $value) {
57         $displayData[]= array($value);
58       }
59     } else {
60       $this->setDisplayData($displayData);
61     }
63     // Generate instance wide unique ID
64     $tmp= gettimeofday();
65     $this->id= 'l'.md5($tmp['sec']);
66   }
69   function setData($data)
70   {
71     $this->data= $data;
72   }
75   function setDisplayData($data)
76   {
77     if (!is_array($data)) {
78       die ("sortableList needs an array as data!");
79     }
81     // Transfer information
82     $this->displayData= $data;
84     // Create initial mapping
85     $this->mapping= array_keys($data);
86     $this->current_mapping= $this->mapping;
88     // Find the number of coluns
89     reset($this->displayData);
90     $first= current($this->displayData);
91     if (is_array($first)) {
92       $this->columns= count($first);
93     } else {
94       $this->columns= 1;
95     }
96   }
99   function setWidth($width)
100   {
101     $this->width= $width;
102   }
105   function setReorderable($flag)
106   {
107     $this->reorderable= $flag;
108   }
111   function setInstantDelete($flag)
112   {
113     $this->instantDelete= $flag;
114   }
117   function setEditable($flag)
118   {
119     $this->editable= $flag;
120   }
123   function setDeleteable($flag)
124   {
125     $this->deleteable= $flag;
126   }
129   function setHeight($height)
130   {
131     $this->height= $height;
132   }
135   function setCssClass($css)
136   {
137     $this->cssclass= $css;
138   }
141   function setHeader($header)
142   {
143     $this->header= $header;
144   }
147   function setColspecs($specs)
148   {
149     $this->colspecs= $specs;
150   }
153   function render()
154   {
155     $result= "<div class='sortableListContainer' id='scroll_".$this->id."' style='width: ".$this->width."; height: ".$this->height."'>\n";
156     $result.= "<table border='0' cellpadding='0' cellspacing='0' width='100%' height='100%' style='position: relative;'".(!empty($this->cssclass)?" class='".$this->cssclass."'":"").">\n";
157     $action_width= 0;
158     if (strpos($this->acl, 'w') === false) {
159       $edit_image= $this->editable?"<img class='center' src='images/lists/edit-grey.png' alt='"._("Edit")."'>":"";
160     } else {
161       $edit_image= $this->editable?"<input class='center' type='image' src='images/lists/edit.png' alt='"._("Edit")."' name='%ID' id='%ID' title='"._("Edit this entry")."'>":"";
162     }
163     if (strpos($this->acl, 'd') === false) {
164       $delete_image= $this->deleteable?"<img class='center' src='images/lists/trash-grey.png' alt='"._("Delete")."'>":"";
165     } else {
166       $delete_image= $this->deleteable?"<input class='center' type='image' src='images/lists/trash.png' alt='"._("Delete")."' name='%ID' title='"._("Delete this entry")."'>":"";
167     }
169     // Do we need colspecs?
170     $action_width= ($this->editable?20:0) + ($this->deleteable?20:0);
171     if ($this->colspecs) {
172       $result.= " <colgroup>\n";
173       for ($i= 0; $i<$this->columns; $i++) {
174         $result.= "  <col width='".(isset($this->colspecs[$i])?$this->colspecs[$i]:"*")."'/>\n";
175       }
177       // Extend by another column if we've actions specified
178       if ($action_width) {
179         $result.= "  <col width='$action_width'/>\n";
180       }
181       $result.= " </colgroup>\n";
182     }
184     // Do we need a header?
185     if ($this->header) {
186       $result.= " <thead>\n  <tr>\n";
187       for ($i= 0; $i<$this->columns; $i++) {
188         $result.= "   <th>".(isset($this->header[$i])?$this->header[$i]:"")."</th>";
189       }
190       if ($action_width) {
191         $result.= "<th>&nbsp;</th>";
192       }
193       $result.= "\n  </tr>\n </thead>\n";
194     }
196     // Render table body if we've read permission
197     $result.= " <tbody id='".$this->id."'>\n";
198     $reorderable= $this->reorderable?"":" style='cursor:default'";
199     if (strpos($this->acl, 'r') !== false) {
200       foreach ($this->mapping as $nr => $row) {
201         $editable= $this->editable?" onclick='$(\"edit_".$this->id."_$nr\").click()'":"";
202         $result.= "  <tr class='sortableListItem".($nr&1?'Odd':'')."' id='item_".$this->id."_$nr'$reorderable$editable>\n";
203         foreach ($this->displayData[$row] as $column) {
204           $result.= "   <td>".htmlentities($column)."</td>\n";
205         }
206         if ($action_width) {
207           $result.= "<td>".str_replace('%ID', "edit_".$this->id."_$nr", $edit_image).
208                            str_replace('%ID', "del_".$this->id."_$nr", $delete_image)."</td>";
209         }
210         $result.= "  </tr>\n";
211       }
212     } else {
213       $result.= "  <tr class='sortableListItem' style='height:100%'></tr>\n";
214     }
216     $result.= " </tbody>\n</table>\n</div>\n";
217     $result.= " <input type='hidden' name='position_".$this->id."' id='position_".$this->id."'>\n";
218     $result.= " <input type='hidden' name='reorder_".$this->id."' id='reorder_".$this->id."'>\n";
220     // Append script stuff if needed
221     $result.= '<script type="text/javascript" language="javascript">';
222     if ($this->reorderable) {
223       $result.= ' function updateOrder(){';
224       $result.= '    var ampcharcode= \'%26\';';
225       $result.= '    var serializeOpts = Sortable.serialize(\''.$this->id.'\')+"='.$this->id.'";';
226       $result.= '    $("reorder_'.$this->id.'").value= serializeOpts;';
227       $result.= '    document.mainform.submit();';
228       $result.= ' }';
229       $result.= 'Position.includeScrollOffsets = true;';
230       $result.= ' Sortable.create(\''.$this->id.'\',{tag:\'tr\', ghosting:false, constraint:\'vertical\', scroll:\'scroll_'.$this->id.'\',onUpdate : updateOrder});';
231     }
232     $result.= '$("scroll_'.$this->id.'").scrollTop= '.$this->scrollPosition.';';
233     $result.= 'var box = $("scroll_'.$this->id.'").onscroll= function() {$("position_'.$this->id.'").value= this.scrollTop;}';
234     $result.= '</script>';
236     return $result;
237   }
240   function update()
241   {
242     // Do not do anything if we're not posted - or have no permission
243     if (strpos($this->acl, 'w') !== false && isset($_POST['reorder_'.$this->id])){
245       if (isset($_POST['position_'.$this->id]) && is_numeric($_POST['position_'.$this->id])) {
246         $this->scrollPosition= $_POST['position_'.$this->id];
247       }
249       // Move requested?
250       $move= $_POST['reorder_'.$this->id];
251       if ($move != "") {
252         preg_match_all('/=([0-9]+)[&=]/', $move, $matches);
253         $this->action= "reorder";
254         $tmp= array();
255         foreach ($matches[1] as $id => $row) {
256           $tmp[$id]= $this->mapping[$row];
257         }
258         $this->mapping= $tmp;
259         $this->current_mapping= $matches[1];
260         $this->modified= true;
261         return;
262       }
263     }
265     // Delete requested?
266     if (strpos($this->acl, 'd') !== false){
267       foreach ($_POST as $key => $value) {
268         if (preg_match('/^del_'.$this->id.'_([0-9]+)_x.*$/', $key, $matches)) {
269           $this->active_index= $this->mapping[$matches[1]];
270           $this->action= "delete";
271           if ($this->instantDelete) {
272             $this->deleteEntry($this->active_index);
273           }
274         }
275       }
276     }
278     // Edit requested?
279     if (strpos($this->acl, 'w') !== false){
280       foreach ($_POST as $key => $value) {
281         if (preg_match('/^edit_'.$this->id.'_([0-9]+)_x.*$/', $key, $matches)) {
282           $this->active_index= $this->mapping[$matches[1]];
283           $this->action= "edit";
284         }
285       }
286     }
287   }
290   function getAction()
291   {
292     // Do not do anything if we're not posted
293     if(!isset($_POST['reorder_'.$this->id])) {
294       return;
295     }
297     // For reordering, return current mapping
298     if ($this->action == 'reorder') {
299       return array("targets" => $this->current_mapping, "mapping" => $this->mapping, "action" => $this->action);
300     }
302     // Edit and delete
303     $result= array("targets" => array($this->active_index), "action" => $this->action);
305     return $result;
306   }
309   function deleteEntry($id)
310   {
311     // Remove mapping
312     $index= array_search($id, $this->mapping);
313     if ($index !== false) {
314       unset($this->mapping[$index]);
315       $this->mapping= array_values($this->mapping);
316       $this->modified= true;
317     }
318   }
321   function getMaintainedData()
322   {
323     $tmp= array();
325     foreach ($this->mapping as $src => $dst) {
326       $tmp[$src]= $this->data[$dst];
327     }
329     return $tmp;
330   }
333   function isModified()
334   {
335     return $this->modified;
336   }
339   function setAcl($acl)
340   {
341     $this->acl= $acl;
342   }