1 <?php
3 class printerPPDDialog extends plugin
4 {
5 /* attribute list for save action */
6 var $ignore_account = TRUE;
7 var $attributes = array("cn");
8 var $objectclasses = array("whatever");
10 /* PPD Handling */
11 var $selectedPPD = false; // e.g. /vendor/device.ppd
12 var $ppdManager = false; // new ppdManager;
13 var $ppdConfig = false; // $this->ppdManager->loadProperties($this->selectedPPD['link']);
14 var $ppdList = array(); // Contains all Printer models
15 var $ppdListHeader = array(); // Contains all printer vendors
17 /* Paths */
18 var $pathToPPD = ""; // Base path, defined in gosa.conf e.g. "/var/spool/ppd/"
19 var $pathToModified = "modified/"; // used to store the modified ppds
21 /* Object Info */
22 var $cn = "" ; // Used to tag the ppds modified by the printer object,
24 /* If there is already a ppd file for the same type of printer,
25 * remember the path to ppd file and display a dialog which allows
26 * to overwrite the current ppd file.
27 */
28 var $add_ppd_later = "";
29 var $add_later_msg_dialog = NULL;
31 function printerPPDDialog (&$config, $dn= NULL, $ppdfile=NULL )
32 {
33 plugin::plugin ($config, $dn);
34 $this->depselect = $this->config->current['BASE'];
36 /* Get PPD path and remove double //, and add trailing / */
37 $config = session::get('config');
38 if($config->get_cfg_value("ppdPath") != ""){
39 $this->pathToPPD = $config->get_cfg_value("ppdPath");
40 $this->pathToPPD= preg_replace("/\/\//", "/", $this->pathToPPD);
41 if(!preg_match("/\/$/",$this->pathToPPD)){
42 $this->pathToPPD = $this->pathToPPD."/";
43 }
44 }else{
45 $this->pathToPPD = "";
46 }
48 /* Check if ppdPath is set in gosa.conf.
49 */
50 if(empty($this->pathToPPD)){
51 msg_dialog::display(_("Configuration error"), msgPool::invalidConfigurationAttribute("ppdPath"), ERROR_DIALOG);
52 }
54 /* It seams that we have an existing PPD path, so go on */
55 if(!((!is_dir($this->pathToPPD))||(empty($this->pathToPPD)))){
57 /* Load all available PPD files and sort them into an array */
58 $this->ppdManager= new ppdManager($this->pathToPPD);
59 $this->getPrinterReload ();
61 /* The user has already a valid PPD assigned
62 * Get some informations about this PPD
63 * and set it as selected.
64 * The ppdpath ['link'] should be relative from .../ppd/modified/
65 * e.g. "/Compaq/Compaq-J1200.ppd" */
66 if(($ppdfile!== NULL)&&(strlen($ppdfile)>0)){
67 $ppdfile = preg_replace("#".$this->pathToModified."#","",$ppdfile);
68 if(!file_exists($this->pathToPPD.$this->pathToModified.$ppdfile)){
69 msg_dialog::display(_("PPD error"), sprintf(_("Cannot open PPD '%s'!"), $ppdfile), ERROR_DIALOG);
70 }else{
71 $ppdDesc = $this->ppdManager->loadDescription($this->pathToPPD.$this->pathToModified.$ppdfile);
72 if($ppdDesc){
73 $selectedPPD = array();
74 $selectedPPD['name'] = $ppdDesc['name'];
75 $selectedPPD['link'] = $ppdfile;
76 $selectedPPD['ppd'] = $ppdDesc;
77 }
78 $this->selectedPPD = $selectedPPD;
79 }
80 }
81 }
82 }
85 function execute()
86 {
87 /* Call parent execute */
88 plugin::execute();
90 /* Fill templating stuff */
91 $display= "";
92 $smarty= get_smarty();
93 $smarty->assign("ppdString", _("Can't get ppd informations."));
94 $smarty->assign("showOptions", "");
95 $smarty->assign("path_valid", TRUE);
97 if(empty($this->pathToPPD)){
99 /* Print out template */
100 $smarty->assign("path_valid", FALSE);
101 $display.= $smarty->fetch(get_template_path('printerPPDDialog.tpl', TRUE,dirname(__FILE__)));
102 return($display);
103 }
105 /* Check these paths */
106 $paths = array($this->pathToPPD, $this->pathToPPD.$this->pathToModified);
108 /* If one of our required paths is not available, stop here and display some info */
109 foreach($paths as $path){
111 /* Check if path is write/readable*/
112 $is_r = @is_readable($path);
113 if(((!is_dir($path))||(empty($path)) || (!$is_r)) && (!@mkdir($path))){
114 msg_dialog::display(_("Configuration error"), sprintf(_("Cannot open PPD path '%s' for reading and writing!"), $path), ERROR_DIALOG);
116 /* Print out template */
117 $display.= $smarty->fetch(get_template_path('printerPPDDialog.tpl', TRUE,dirname(__FILE__)));
118 return($display);
119 }
120 }
122 // PPD selection / upload / dialog handling
124 /* Is there a new PPD file uploaded ? */
125 if((isset($_FILES['NewPPDFile']))&&(isset($_POST['SubmitNewPPDFile']))){
126 $file = ($_FILES['NewPPDFile']);
127 if($file['size'] != 0 ){
128 if($name = $this->AddPPD($file['tmp_name'])){
129 $this->SelectPPD($name);
130 }
131 }else{
132 msg_dialog::display(_("PPD error"), msgPool::incorrectUpload(_("file is empty")), ERROR_DIALOG);
133 }
134 }
136 /* Overwrite existing PPD file and select it as currently used for this object */
137 if(is_object($this->add_later_msg_dialog) && ($this->add_later_msg_dialog->is_confirmed()) && $this->add_ppd_later != ""){
138 if($name = $this->AddPPD($this->add_ppd_later,TRUE)){
139 $this->SelectPPD($name);
140 }
141 $this->add_ppd_later = "";
142 $this->add_later_msg_dialog = NULL;
143 }
145 /* Open a dialog that allow us to select different PPDs */
146 if(isset($_POST['SelectPPD'])){
147 $this->dialog= new printerPPDSelectionDialog($this->config,$this->dn,$this->ppdList,$this->ppdListHeader,$this->selectedPPD);
148 }
150 /* The selection dialog fpr PPDs is canceled */
151 if(isset($_POST['ClosePPDSelection'])){
152 unset($this->dialog);
153 $this->dialog=FALSE;
154 }
156 /* Div Selection */
157 if((isset($_GET['act']))&&($_GET['act']=="use")){
158 $this->SelectPPD(base64_decode($_GET['id']));
159 unset($this->dialog);
160 $this->dialog=FALSE;
162 }
164 /* if a dialog is open, print the dialog instead of this class */
165 if(is_object($this->dialog)){
166 $display = $this->dialog->execute();
167 return($display);
168 }
170 // ENDE PPD selection / upload / dialog handling
172 /* Give smarty the information it needs */
173 $smarty->assign("ppdString" ,$this->getPPDInformation());
174 $tmp= $this->generateProperties();
175 if ($tmp == ""){
176 $smarty->assign("showOptions", 0);
177 } else {
178 $smarty->assign("showOptions", 1);
179 $smarty->assign("properties",$this->generateProperties());
180 }
182 /* Print out template */
183 $display.= $smarty->fetch(get_template_path('printerPPDDialog.tpl', TRUE,dirname(__FILE__)));
184 return($display);
185 }
188 /* Select PPD */
189 function SelectPPD($name)
190 {
191 /* Replace base path we don't need it here
192 The path we need looks like this : "/Vendor/ModellName.ppd";
193 thats all */
194 $name = preg_replace("#".$this->pathToPPD."#","",$name);
196 /* Intialise some base vars */
197 $AbsoluteSourceName = $this->pathToPPD.$name;
198 $AbsoluteDestinationPath = $this->pathToPPD.$this->pathToModified;
199 $Vendor = ""; // Vendor
200 $Name = ""; // Name
201 $Modell = ""; // Modell
202 $PrinterName = ""; // The new name of the printer
203 $PPDName = "";
205 /* Force reload of config dialog */
206 $this->ppdConfig = false;
207 $this->selectedPPD['link'] = false;
209 /* Get PPD informations and set vendor / modell / name */
210 if((!file_exists($AbsoluteSourceName)) || (!is_readable($AbsoluteSourceName))){
211 msg_dialog::display(_("PPD error"), msgPool::cannotReadFile($AbsoluteSourceName), ERROR_DIALOG);
212 return;
213 }
214 $ppdDesc = $this->ppdManager->loadDescription($AbsoluteSourceName);
215 if($ppdDesc){
216 $Name = $ppdDesc['name'];
217 $Vendor = $ppdDesc['manufacturer'];
218 $Model = $ppdDesc['model'];
219 }
221 $PrinterName = $this->cn."-".preg_replace("/[^a-z0-9-_\.]/i","_",$Name);
222 $PPDName = $Vendor."/".$PrinterName.".ppd";
224 /* Create the vendors path, if it doesn't exists already */
225 if(!is_dir($AbsoluteDestinationPath.$Vendor)){
226 if(!(@mkdir($AbsoluteDestinationPath.$Vendor))){
227 msg_dialog::display(_("PPD error"), msgPool::cannotCreateFolder($AbsoluteDestinationPath.$Vendor), ERROR_DIALOG);
228 return(false);
229 }
230 }
232 /* Create destination file handle */
233 $fp = @fopen($AbsoluteDestinationPath.$PPDName,"w+");
234 if(!$fp){
235 msg_dialog::display(_("PPD error"), msgPool::cannotWriteFile($AbsoluteDestinationPath.$PPDName), ERROR_DIALOG);
236 return(false);
237 }
239 $str = file_get_contents($AbsoluteSourceName);
240 fputs($fp,$str);
241 @fclose($fp);
243 //$this->ppdManager->updateAttribute($filename,"NO_SECTION","ModelName",$printerName);
245 $tmp3['link'] =$PPDName;
246 $this->selectedPPD = $tmp3;
247 $this->getPrinterReload();
248 return($PPDName);
249 }
252 /* This function adds a new ppd file to the list of available ppds.
253 All required paths and files will be created
254 $_PathOnHdd e.g. = /tmp/PHP_tmpfile213452 */
255 function AddPPD($_PathOnHdd,$overwrite = FALSE)
256 {
257 /* Check if file exists && is readable */
258 if((!is_file($_PathOnHdd)) || (!is_readable($_PathOnHdd))){
259 msg_dialog::display(_("PPD error"), msgPool::cannotReadFile($_PathOnHdd), ERROR_DIALOG);
260 return(false);
261 }
263 /* Reload list to detect changes e.g. a file has been deleted */
264 $this->getPrinterReload();
266 /* Get Description from ppd, & parse out some informations */
267 $ppdDesc = @$this->ppdManager->loadDescription($_PathOnHdd);
268 if($ppdDesc){
269 $name = $ppdDesc['name'];
270 $vendor = $ppdDesc['manufacturer'];
271 $model = $ppdDesc['model'];
272 }
274 /* Check if parse was successfull */
275 if(empty($name) || empty($vendor)){
276 msg_dialog::display(_("PPD error"), sprintf(_("Cannot parse PPD '%s'!"), $_PathOnHdd), ERROR_DIALOG);
277 return(false);
278 }
280 /* Prepare list of ppds */
281 if(!isset($this->ppdList[$vendor])){
282 $this->ppdList[$vendor] = array();
283 }
285 /* Create ppd file and fill in the source contents */
286 $ppdname = $vendor."/".$name.".ppd";
287 $filename = $this->pathToPPD.preg_replace("/[^a-z0-9-_\.\/]/i","_",$ppdname);
288 $filename = $this->pathToPPD.$ppdname;
289 $contents = file_get_contents($_PathOnHdd);
292 /* Check if this ppd already exists */
293 $found = false;
294 foreach($this->ppdList[$vendor] as $key => $val){
295 if(preg_match("/".$model.".*/i",$key)){
296 $found = true;
297 if(!$overwrite){
298 if(!copy($_PathOnHdd,$_PathOnHdd."_back")){
299 msg_dialog::display(_("PPD error"), msgPool::cannotReadFile($_PathOnHdd), ERROR_DIALOG);
300 }else{
301 $this->add_ppd_later = $_PathOnHdd."_back";
302 $this->add_later_msg_dialog = new msg_dialog(_("Overwrite existing PPD"),
303 _("There is already a ppd file for this kind of printer. Do you want to overwrite it?"),CONFIRM_DIALOG);
304 }
305 return;
306 }
307 }
308 }
310 /* Create the vendors path, if it doesn't exists already */
311 if(!is_dir($this->pathToPPD.$vendor)){
312 if(!(@mkdir($this->pathToPPD.$vendor))){
313 msg_dialog::display(_("PPD error"), msgPool::cannotCreateFolder($this->pathToPPD.$vendor), ERROR_DIALOG);
314 return(false);
315 }
316 }
318 /* Open file handle */
319 $fp = fopen($filename,"w+");
321 /* Check file handle & contents */
322 if(!$fp){
323 msg_dialog::display(_("PPD error"), msgPool::cannotWriteFile($filename), ERROR_DIALOG);
324 return;
325 }
326 if(empty($contents)){
327 msg_dialog::display(_("PPD error"), msgPool::incorrectUpload(_("file is empty")), ERROR_DIALOG);
328 return;
329 }
331 /* Fille file with data */
332 fputs($fp,$contents);
333 @fclose($fp);
335 /* Our job is done here */
336 return($ppdname);
337 }
340 /* This function reloads the list of available printers/vendors
341 $this->ppdListHeader
342 Compaq => 1
343 $this->ppdList
344 Compaq => Compaq IJ1200 => name => Compaq IJ1200
345 link => /var/spool/ppd/Compaq/Compaq-J1200.ppd
346 ppd => Compaq - Co
347 */
348 function getPrinterReload()
349 {
350 if(is_readable($this->pathToPPD)){
351 $tmp = @$this->ppdManager->getPrinterList(true);
353 $this->ppdListHeader = $this->ppdList = array();
355 /* Sort all available files, and create header (Vendor index) */
356 foreach($tmp as $file=>$ppd){
358 if(preg_match("#".$this->pathToModified."#",$file)) continue;
360 if(!isset($this->ppdListHeader[$ppd['manufacturer']])){
361 $this->ppdListHeader[$ppd['manufacturer']]=0;
362 }
364 $tmp3['name'] =$ppd['name'];
365 $tmp3['link'] =$file;
366 $tmp3['ppd'] =$ppd;
367 $this->ppdListHeader[$ppd['manufacturer']]++;
368 if (isset($this->ppdList[$ppd['manufacturer']][$ppd['name']])) {
370 # Printer exists already, add directory/nickname to identify it
372 $tmp4 = $this->ppdList[$ppd['manufacturer']][$ppd['name']]['link'];
374 # Remove the default path (e.g. /srv/www/ppd) plus a possible debian/ directory
375 $extension = preg_replace("#".$this->pathToPPD."/(debian/)?#", "", $tmp3['link']);
377 # Remove all further directories, just keep the first level (e.g. foomatic-rip, hpijs)
378 $extension = preg_replace("#/.*#", "", $extension);
380 # Add the nickname as well to distinguish between two drivers for the same model in one directory
381 $extension = $extension." / ".$ppd['nickname'];
383 # Generate the updated extension from the original path and nickname, same way as above
384 $updated_nickname = $this->ppdList[$ppd['manufacturer']][$ppd['name']]['ppd']['nickname'];
385 $updated_extension = preg_replace("#".$this->pathToPPD."/(debian/)?#", "", $tmp4);
386 $updated_extension = preg_replace("#/.*#", "", $updated_extension);
387 $updated_extension = $updated_extension." / ".$updated_nickname;
388 $updated_name = $ppd['name']." ($updated_extension)";
390 if (!isset($this->ppdList[$ppd['manufacturer']][$updated_name]['name'])) {
391 $old_ppd = array_keys($this->ppdList[$ppd['manufacturer']][$ppd['name']]);
392 # Copy old printer attributes to new name
393 foreach($old_ppd as $entry){
394 $this->ppdList[$ppd['manufacturer']][$updated_name][$entry] = $this->ppdList[$ppd['manufacturer']][$ppd['name']][$entry];
395 }
397 # Add old printer under updated name
398 $this->ppdList[$ppd['manufacturer']][$updated_name]['name'] = $updated_name;
399 $this->ppdList[$ppd['manufacturer']][$updated_name]['ppd']['name'] = $ppd['name']." ($updated_extension)";
400 $this->ppdList[$ppd['manufacturer']][$updated_name]['ppd']['model'] = $ppd['model']." ($updated_extension)";
401 }
403 # Now generate name/model including extension for new printer
404 $tmp3['ppd']['name'] = $tmp3['name']." ($extension)";
405 $tmp3['ppd']['model'] = $tmp3['ppd']['model']." ($extension)";
406 $tmp3['name'] = $tmp3['name']." ($extension)";
407 $myname = $tmp3['name'];
408 }
409 $this->ppdList[$ppd['manufacturer']][$tmp3['name']]=$tmp3;
410 }
412 # Remove all non-nicknamed/directorized printers from list who have a
413 # nicknamed counterpart already, those are dupes.
414 foreach($this->ppdList as $manufacturer => $printers) {
415 foreach($printers as $ppd) {
416 $to_remove = preg_replace("/ \(.*\)$/", "", $ppd['name']);
417 if ($to_remove != $ppd['name'] && isset($this->ppdList[$manufacturer][$to_remove])) {
418 unset($this->ppdList[$manufacturer][$to_remove]);
419 }
420 }
421 }
422 }
423 }
426 /* Save all options posted from ppd dialog */
427 function save_object()
428 {
429 if(!((isset($_POST['PPDDisSubmitted'])) && (is_array($this->ppdConfig)))){
430 return;
431 }
433 foreach($this->ppdConfig as $cat => $obj){
434 foreach($obj as $attr => $attributes){
435 if(isset($_POST[base64_encode($attributes['_name'])])){
436 $this->ppdConfig[$cat][$attr]['_default'] = $_POST[base64_encode($attributes['_name'])];
437 }
438 }
439 }
440 }
443 /* Save modified ppd */
444 function save_ppd()
445 {
446 if($this->ppdManager){
447 $this->ppdManager->saveProperties($this->pathToPPD.$this->pathToModified.$this->selectedPPD['link'],$this->ppdConfig);
448 }
449 }
452 /* Return selected ppd path, if none is selected then false */
453 function save()
454 {
455 /* return the selected PPD, and in future the selected options too */
456 return($this->pathToModified.$this->selectedPPD['link']);
457 }
460 /* Get Information for a single PPD entry
461 * This will be shown on top of template
462 */
463 function getPPDInformation()
464 {
465 $str = "none";
466 if(!empty($this->selectedPPD)){
467 $ppdDesc = $this->ppdManager->loadDescription($this->pathToPPD.$this->pathToModified.$this->selectedPPD['link']);
468 $str = $ppdDesc['name'];
469 }
470 return($str) ;
471 }
473 /* Display all options from the selected ppd file */
474 function generateProperties()
475 {
476 /* Set Headline */
477 $str = "";
478 $feed= "";
480 $s_ppd = $this->pathToPPD.$this->pathToModified.$this->selectedPPD['link'];
482 /* If ppd exists and is readable */
483 if((!empty($this->selectedPPD['link']))&&(file_exists($s_ppd))){
485 /* If there is no initial Configuration, load it */
486 if($this->ppdConfig == false){
487 $this->ppdConfig = $this->ppdManager->loadProperties($s_ppd);
488 }
490 /* Create a table */
491 $str .= "<div style='padding-left:30px;'><table summary=''>";
493 /* Input all data to the table */
494 foreach($this->ppdConfig as $cat => $obj){
496 /* Add new category */
497 $str .= "<tr><td colspan='2'>$feed";
498 if ($feed == ""){
499 $feed= "<br>";
500 }
501 $str .= "<b>"._("Section")." '".$cat."' </b><br>";
502 $str .= "</td></tr>";
504 /* Add attributes of the current category */
505 foreach($obj as $attr => $settings){
507 /* Skip all entries beginning with _ */
508 if($attr[0] == "_") continue;
510 /* Prepare data */
511 $values = array();
512 $name = $settings['_name'];
514 if (!isset($settings['_default'])){
515 $default = "";
516 } else {
517 $default = $settings['_default'];
518 }
520 $type = $settings['_type'];
522 /* Add name to table */
523 $str .= "<tr><td style='padding-left:40px;'>\n";
524 $str .= $name."<br>\n";
525 $str .= "</td><td>\n";
527 /* Get all values */
528 foreach( $settings as $vname => $value){
529 if($vname[0] != "_"){
530 $values[$vname]= $value;
531 }
532 }
534 /* preparing Html output
535 * Supported types are PickOne/Boolean
536 */
538 /* If type is PickOne, create a select box */
539 if(($type == "PickOne")||(($type=="Boolean")&&(count($values)>1))){
541 $str .= "<select name='".base64_encode($name)."'>\n";
542 foreach($values as $optionKey => $value){
543 $selected = "";
544 if($optionKey == $default){
545 $selected = " selected ";
546 }
547 $str .= "<option value='".$optionKey."' ".$selected.">".$value."</option>\n";
548 }
549 $str .= "</select>\n";
551 }elseif($type == "Boolean"){
553 /* If type is Boolean & no values are given */
554 $str .= "<select name='".base64_encode($name)."'>\n";
555 if($default == "False"){
556 $str .= "<option value='True' >"._("True")."</option>\n";
557 $str .= "<option value='False' selected>"._("False")."</option>\n";
558 }else{
559 $str .= "<option value='True' selected>"._("True")."</option>\n";
560 $str .= "<option value='False' >"._("False")."</option>\n";
561 }
562 $str .= "</select>\n";
564 }else{
565 msg_dialog::display(_("PPD error"), sprintf(_("PPD type '%s' is not supported!"), $type), ERROR_DIALOG);
566 }
567 $str .= "</td></tr>\n";
568 }
569 }
570 $str .= "</table></div>\n";
571 }
572 return($str);
573 }
575 function removeModifiedPPD()
576 {
577 $path = $this->pathToPPD.$this->pathToModified.$this->selectedPPD['link'];
579 if(file_exists($path)){
580 if(is_writeable($path)){
581 if(!@unlink($path)){
582 msg_dialog::display(_("PPD error"), msgPool::cannotDeleteFile($path), ERROR_DIALOG);
583 }
584 }else{
585 msg_dialog::display(_("PPD error"), msgPool::cannotDeleteFile($path), ERROR_DIALOG);
586 }
587 }else{
588 msg_dialog::display(_("PPD error"), msgPool::cannotDeleteFile($path), ERROR_DIALOG);
589 }
590 }
592 function update_ppd_url()
593 {
594 $this->SelectPPD("modified/".$this->selectedPPD['link']);
595 return("modified/".$this->selectedPPD['link']);
596 }
598 function check()
599 {
600 $message = plugin::check();
601 if(empty($this->selectedPPD['link'])){
602 $message[] = _("Please select a valid ppd file or use 'Cancel' to go back to printer configuration.");
603 }
604 return($message);
605 }
606 }
607 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
608 ?>