[ Index ] |
PHP Cross Reference of vtigercrm-6.1.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /********************************************************************************* 3 ** The contents of this file are subject to the vtiger CRM Public License Version 1.0 4 * ("License"); You may not use this file except in compliance with the License 5 * The Original Code is: vtiger CRM Open Source 6 * The Initial Developer of the Original Code is vtiger. 7 * Portions created by vtiger are Copyright (C) vtiger. 8 * All Rights Reserved. 9 * 10 ********************************************************************************/ 11 12 13 require_once ("modules/Emails/class.phpmailer.php"); 14 require_once 'include/utils/CommonUtils.php'; 15 require_once 'include/utils/VTCacheUtils.php'; 16 17 /** Function used to send email 18 * $module -- current module 19 * $to_email -- to email address 20 * $from_name -- currently loggedin user name 21 * $from_email -- currently loggedin vtiger_users's email id. you can give as '' if you are not in HelpDesk module 22 * $subject -- subject of the email you want to send 23 * $contents -- body of the email you want to send 24 * $cc -- add email ids with comma seperated. - optional 25 * $bcc -- add email ids with comma seperated. - optional. 26 * $attachment -- whether we want to attach the currently selected file or all vtiger_files.[values = current,all] - optional 27 * $emailid -- id of the email object which will be used to get the vtiger_attachments 28 */ 29 function send_mail($module,$to_email,$from_name,$from_email,$subject,$contents,$cc='',$bcc='',$attachment='',$emailid='',$logo='', $useGivenFromEmailAddress=false) 30 { 31 32 global $adb, $log; 33 global $root_directory; 34 global $HELPDESK_SUPPORT_EMAIL_ID, $HELPDESK_SUPPORT_NAME; 35 36 $uploaddir = $root_directory ."/test/upload/"; 37 38 $adb->println("To id => '".$to_email."'\nSubject ==>'".$subject."'\nContents ==> '".$contents."'"); 39 40 //Get the email id of assigned_to user -- pass the value and name, name must be "user_name" or "id"(field names of vtiger_users vtiger_table) 41 //$to_email = getUserEmailId('id',$assigned_user_id); 42 43 //if module is HelpDesk then from_email will come based on support email id 44 if($from_email == '') { 45 //if from email is not defined, then use the useremailid as the from address 46 $from_email = getUserEmailId('user_name',$from_name); 47 } 48 49 //if the newly defined from email field is set, then use this email address as the from address 50 //and use the username as the reply-to address 51 $cachedFromEmail = VTCacheUtils::getOutgoingMailFromEmailAddress(); 52 if($cachedFromEmail === null) { 53 $query = "select from_email_field from vtiger_systems where server_type=?"; 54 $params = array('email'); 55 $result = $adb->pquery($query,$params); 56 $from_email_field = $adb->query_result($result,0,'from_email_field'); 57 VTCacheUtils::setOutgoingMailFromEmailAddress($from_email_field); 58 } 59 60 if(isUserInitiated()) { 61 $replyToEmail = $from_email; 62 } else { 63 $replyToEmail = $from_email_field; 64 } 65 if(isset($from_email_field) && $from_email_field!='' && !$useGivenFromEmailAddress){ 66 //setting from _email to the defined email address in the outgoing server configuration 67 $from_email = $from_email_field; 68 } 69 70 if($module != "Calendar") 71 $contents = addSignature($contents,$from_name); 72 73 $mail = new PHPMailer(); 74 75 setMailerProperties($mail,$subject,$contents,$from_email,$from_name,trim($to_email,","),$attachment,$emailid,$module,$logo); 76 setCCAddress($mail,'cc',$cc); 77 setCCAddress($mail,'bcc',$bcc); 78 if(!empty($replyToEmail)) { 79 $mail->AddReplyTo($replyToEmail); 80 } 81 82 // vtmailscanner customization: If Support Reply to is defined use it. 83 global $HELPDESK_SUPPORT_EMAIL_REPLY_ID; 84 if($HELPDESK_SUPPORT_EMAIL_REPLY_ID && $HELPDESK_SUPPORT_EMAIL_ID != $HELPDESK_SUPPORT_EMAIL_REPLY_ID) { 85 $mail->AddReplyTo($HELPDESK_SUPPORT_EMAIL_REPLY_ID); 86 } 87 // END 88 89 // Fix: Return immediately if Outgoing server not configured 90 if(empty($mail->Host)) { 91 return 0; 92 } 93 // END 94 95 $mail_status = MailSend($mail); 96 97 if($mail_status != 1) 98 { 99 $mail_error = getMailError($mail,$mail_status,$mailto); 100 } 101 else 102 { 103 $mail_error = $mail_status; 104 } 105 106 return $mail_error; 107 } 108 109 /** Function to get the user Email id based on column name and column value 110 * $name -- column name of the vtiger_users vtiger_table 111 * $val -- column value 112 */ 113 function getUserEmailId($name,$val) 114 { 115 global $adb; 116 $adb->println("Inside the function getUserEmailId. --- ".$name." = '".$val."'"); 117 if($val != '') 118 { 119 //done to resolve the PHP5 specific behaviour 120 $sql = "SELECT email1, email2, secondaryemail from vtiger_users WHERE status='Active' AND ". $adb->sql_escape_string($name)." = ?"; 121 $res = $adb->pquery($sql, array($val)); 122 $email = $adb->query_result($res,0,'email1'); 123 if($email == '') 124 { 125 $email = $adb->query_result($res,0,'email2'); 126 if($email == '') 127 { 128 $email = $adb->query_result($res,0,'secondaryemail '); 129 } 130 } 131 $adb->println("Email id is selected => '".$email."'"); 132 return $email; 133 } 134 else 135 { 136 $adb->println("User id is empty. so return value is ''"); 137 return ''; 138 } 139 } 140 141 /** Funtion to add the user's signature with the content passed 142 * $contents -- where we want to add the signature 143 * $fromname -- which user's signature will be added to the contents 144 */ 145 function addSignature($contents, $fromname) 146 { 147 global $adb; 148 $adb->println("Inside the function addSignature"); 149 150 $sign = VTCacheUtils::getUserSignature($fromname); 151 if($sign === null) { 152 $result = $adb->pquery("select signature, first_name, last_name from vtiger_users where user_name=?", array($fromname)); 153 $sign = $adb->query_result($result,0,"signature"); 154 VTCacheUtils::setUserSignature($fromname, $sign); 155 VTCacheUtils::setUserFullName($fromname, $adb->query_result($result,0,"first_name").' '.$adb->query_result($result,0,"last_name")); 156 } 157 158 $sign = nl2br($sign); 159 160 if($sign != '') 161 { 162 $contents .= '<br><br>'.$sign; 163 $adb->println("Signature is added with the body => '.".$sign."'"); 164 } 165 else 166 { 167 $adb->println("Signature is empty for the user => '".$fromname."'"); 168 } 169 return $contents; 170 } 171 172 /** Function to set all the Mailer properties 173 * $mail -- reference of the mail object 174 * $subject -- subject of the email you want to send 175 * $contents -- body of the email you want to send 176 * $from_email -- from email id which will be displayed in the mail 177 * $from_name -- from name which will be displayed in the mail 178 * $to_email -- to email address -- This can be an email in a single string, a comma separated 179 * list of emails or an array of email addresses 180 * $attachment -- whether we want to attach the currently selected file or all vtiger_files. 181 [values = current,all] - optional 182 * $emailid -- id of the email object which will be used to get the vtiger_attachments - optional 183 */ 184 function setMailerProperties($mail,$subject,$contents,$from_email,$from_name,$to_email,$attachment='',$emailid='',$module='',$logo='') 185 { 186 global $adb; 187 $adb->println("Inside the function setMailerProperties"); 188 if($module == "Support" || $logo ==1) 189 $mail->AddEmbeddedImage('layouts/vlayout/skins/images/logo_mail.jpg', 'logo', 'logo.jpg',"base64","image/jpg"); 190 191 $mail->Subject = $subject; 192 //Added back as we have changed php mailer library, older library was using html_entity_decode before sending mail 193 $mail->Body = decode_html($contents); 194 //$mail->Body = html_entity_decode(nl2br($contents)); //if we get html tags in mail then we will use this line 195 $mail->AltBody = strip_tags(preg_replace(array("/<p>/i","/<br>/i","/<br \/>/i"),array("\n","\n","\n"),$contents)); 196 197 $mail->IsSMTP(); //set mailer to use SMTP 198 //$mail->Host = "smtp1.example.com;smtp2.example.com"; // specify main and backup server 199 200 setMailServerProperties($mail); 201 202 //Handle the from name and email for HelpDesk 203 $mail->From = $from_email; 204 $userFullName = trim(VTCacheUtils::getUserFullName($from_name)); 205 if(empty($userFullName)) { 206 $rs = $adb->pquery("select first_name,last_name from vtiger_users where user_name=?", array($from_name)); 207 $num_rows = $adb->num_rows($rs); 208 if($num_rows > 0) { 209 $fullName = getFullNameFromQResult($rs, 0, 'Users'); 210 VTCacheUtils::setUserFullName($from_name, $fullName); 211 } 212 } else { 213 $from_name = $userFullName; 214 } 215 $mail->FromName = decode_html($from_name); 216 217 218 if($to_email != '') 219 { 220 if(is_array($to_email)) { 221 for($j=0,$num=count($to_email);$j<$num;$j++) { 222 $mail->addAddress($to_email[$j]); 223 } 224 } else { 225 $_tmp = explode(",",$to_email); 226 for($j=0,$num=count($_tmp);$j<$num;$j++) { 227 $mail->addAddress($_tmp[$j]); 228 } 229 } 230 } 231 232 //commented so that it does not add from_email in reply to 233 //$mail->AddReplyTo($from_email); 234 $mail->WordWrap = 50; 235 236 //If we want to add the currently selected file only then we will use the following function 237 if($attachment == 'current' && $emailid != '') 238 { 239 if (isset($_REQUEST['filename_hidden'])) { 240 $file_name = $_REQUEST['filename_hidden']; 241 } else { 242 $file_name = $_FILES['filename']['name']; 243 } 244 addAttachment($mail,$file_name,$emailid); 245 } 246 247 //This will add all the vtiger_files which are related to this record or email 248 if($attachment == 'all' && $emailid != '') 249 { 250 addAllAttachments($mail,$emailid); 251 } 252 253 $mail->IsHTML(true); // set email format to HTML 254 255 return; 256 } 257 258 /** Function to set the Mail Server Properties in the object passed 259 * $mail -- reference of the mailobject 260 */ 261 function setMailServerProperties($mail) 262 { 263 global $adb; 264 $adb->println("Inside the function setMailServerProperties"); 265 266 $res = $adb->pquery("select * from vtiger_systems where server_type=?", array('email')); 267 if(isset($_REQUEST['server'])) 268 $server = $_REQUEST['server']; 269 else 270 $server = $adb->query_result($res,0,'server'); 271 if(isset($_REQUEST['server_username'])) 272 $username = $_REQUEST['server_username']; 273 else 274 $username = $adb->query_result($res,0,'server_username'); 275 if(isset($_REQUEST['server_password'])) 276 $password = $_REQUEST['server_password']; 277 else 278 $password = $adb->query_result($res,0,'server_password'); 279 // Prasad: First time read smtp_auth from the request 280 if(isset($_REQUEST['smtp_auth'])) 281 { 282 $smtp_auth = $_REQUEST['smtp_auth']; 283 if($smtp_auth == 'on') 284 $smtp_auth = true; 285 } 286 else if (isset($_REQUEST['module']) && $_REQUEST['module'] == 'Settings' && (!isset($_REQUEST['smtp_auth']))) 287 { 288 //added to avoid issue while editing the values in the outgoing mail server. 289 $smtp_auth = false; 290 } 291 else{ 292 $smtp_auth = $adb->query_result($res,0,'smtp_auth'); 293 if($smtp_auth == "1" || $smtp_auth == "true"){ 294 $smtp_auth = true; 295 } 296 } 297 298 $adb->println("Mail server name,username & password => '".$server."','".$username."','".$password."'"); 299 if($smtp_auth){ 300 $mail->SMTPAuth = true; // turn on SMTP authentication 301 } 302 $mail->Host = $server; // specify main and backup server 303 $mail->Username = $username ; // SMTP username 304 $mail->Password = $password ; // SMTP password 305 306 // To Support TLS 307 $serverinfo = explode("://", $server); 308 $smtpsecure = $serverinfo[0]; 309 if($smtpsecure == 'tls'){ 310 $mail->SMTPSecure = $smtpsecure; 311 $mail->Host = $serverinfo[1]; 312 } 313 // End 314 315 return; 316 } 317 318 /** Function to add the file as attachment with the mail object 319 * $mail -- reference of the mail object 320 * $filename -- filename which is going to added with the mail 321 * $record -- id of the record - optional 322 */ 323 function addAttachment($mail,$filename,$record) 324 { 325 global $adb, $root_directory; 326 $adb->println("Inside the function addAttachment"); 327 $adb->println("The file name is => '".$filename."'"); 328 329 //This is the file which has been selected in Email EditView 330 if(is_file($filename) && $filename != '') 331 { 332 $mail->AddAttachment($root_directory."test/upload/".$filename); 333 } 334 } 335 336 /** Function to add all the vtiger_files as attachment with the mail object 337 * $mail -- reference of the mail object 338 * $record -- email id ie., record id which is used to get the all vtiger_attachments from database 339 */ 340 function addAllAttachments($mail,$record) 341 { 342 global $adb,$log, $root_directory; 343 $adb->println("Inside the function addAllAttachments"); 344 345 //Retrieve the vtiger_files from database where avoid the file which has been currently selected 346 $sql = "select vtiger_attachments.* from vtiger_attachments inner join vtiger_seattachmentsrel on vtiger_attachments.attachmentsid = vtiger_seattachmentsrel.attachmentsid inner join vtiger_crmentity on vtiger_crmentity.crmid = vtiger_attachments.attachmentsid where vtiger_crmentity.deleted=0 and vtiger_seattachmentsrel.crmid=?"; 347 $res = $adb->pquery($sql, array($record)); 348 $count = $adb->num_rows($res); 349 350 for($i=0;$i<$count;$i++) 351 { 352 $fileid = $adb->query_result($res,$i,'attachmentsid'); 353 $filename = decode_html($adb->query_result($res,$i,'name')); 354 $filepath = $adb->query_result($res,$i,'path'); 355 $filewithpath = $root_directory.$filepath.$fileid."_".$filename; 356 357 //if the file is exist in test/upload directory then we will add directly 358 //else get the contents of the file and write it as a file and then attach (this will occur when we unlink the file) 359 if(is_file($filewithpath)) 360 { 361 $mail->AddAttachment($filewithpath,$filename); 362 } 363 } 364 } 365 366 /** Function to set the CC or BCC addresses in the mail 367 * $mail -- reference of the mail object 368 * $cc_mod -- mode to set the address ie., cc or bcc 369 * $cc_val -- addresss with comma seperated to set as CC or BCC in the mail 370 */ 371 function setCCAddress($mail,$cc_mod,$cc_val) 372 { 373 global $adb; 374 $adb->println("Inside the functin setCCAddress"); 375 376 if($cc_mod == 'cc') 377 $method = 'AddCC'; 378 if($cc_mod == 'bcc') 379 $method = 'AddBCC'; 380 if($cc_val != '') 381 { 382 $ccmail = explode(",",trim($cc_val,",")); 383 for($i=0;$i<count($ccmail);$i++) 384 { 385 $addr = $ccmail[$i]; 386 $cc_name = preg_replace('/([^@]+)@(.*)/', '$1', $addr); // First Part Of Email 387 if(stripos($addr, '<')) { 388 $name_addr_pair = explode("<",$ccmail[$i]); 389 $cc_name = $name_addr_pair[0]; 390 $addr = trim($name_addr_pair[1],">"); 391 } 392 if($ccmail[$i] != '') 393 $mail->$method($addr,$cc_name); 394 } 395 } 396 } 397 398 /** Function to send the mail which will be called after set all the mail object values 399 * $mail -- reference of the mail object 400 */ 401 function MailSend($mail) 402 { 403 global $log; 404 $log->info("Inside of Send Mail function."); 405 if(!$mail->Send()) 406 { 407 $log->debug("Error in Mail Sending : Error log = '".$mail->ErrorInfo."'"); 408 return $mail->ErrorInfo; 409 } 410 else 411 { 412 $log->info("Mail has been sent from the vtigerCRM system : Status : '".$mail->ErrorInfo."'"); 413 return 1; 414 } 415 } 416 417 /** Function to get the Parent email id from HelpDesk to send the details about the ticket via email 418 * $returnmodule -- Parent module value. Contact or Account for send email about the ticket details 419 * $parentid -- id of the parent ie., contact or vtiger_account 420 */ 421 function getParentMailId($parentmodule,$parentid) 422 { 423 global $adb; 424 $adb->println("Inside the function getParentMailId. \n parent module and id => ".$parentmodule."&".$parentid); 425 426 if($parentmodule == 'Contacts') 427 { 428 $tablename = 'vtiger_contactdetails'; 429 $idname = 'contactid'; 430 $first_email = 'email'; 431 $second_email = 'secondaryemail'; 432 } 433 if($parentmodule == 'Accounts') 434 { 435 $tablename = 'vtiger_account'; 436 $idname = 'accountid'; 437 $first_email = 'email1'; 438 $second_email = 'email2'; 439 } 440 if($parentid != '') 441 { 442 //$query = 'select * from '.$tablename.' where '.$idname.' = '.$parentid; 443 $query = 'select * from '.$tablename.' where '. $idname.' = ?'; 444 $res = $adb->pquery($query, array($parentid)); 445 $mailid = $adb->query_result($res,0,$first_email); 446 $mailid2 = $adb->query_result($res,0,$second_email); 447 } 448 if($mailid == '' && $mailid2 != '') 449 $mailid = $mailid2; 450 451 return $mailid; 452 } 453 454 /** Function to parse and get the mail error 455 * $mail -- reference of the mail object 456 * $mail_status -- status of the mail which is sent or not 457 * $to -- the email address to whom we sent the mail and failes 458 * return -- Mail error occured during the mail sending process 459 */ 460 function getMailError($mail,$mail_status,$to) 461 { 462 //Error types in class.phpmailer.php 463 /* 464 provide_address, mailer_not_supported, execute, instantiate, file_access, file_open, encoding, data_not_accepted, authenticate, 465 connect_host, recipients_failed, from_failed 466 */ 467 468 global $adb; 469 $adb->println("Inside the function getMailError"); 470 471 $msg = array_search($mail_status,$mail->language); 472 $adb->println("Error message ==> ".$msg); 473 474 if($msg == 'connect_host') 475 { 476 $error_msg = $msg; 477 } 478 elseif(strstr($msg,'from_failed')) 479 { 480 $error_msg = $msg; 481 } 482 elseif(strstr($msg,'recipients_failed')) 483 { 484 $error_msg = $msg; 485 } 486 else 487 { 488 $adb->println("Mail error is not as connect_host or from_failed or recipients_failed"); 489 //$error_msg = $msg; 490 } 491 492 $adb->println("return error => ".$error_msg); 493 return $error_msg; 494 } 495 496 /** Function to get the mail status string (string of sent mail status) 497 * $mail_status_str -- concatenated string with all the error messages with &&& seperation 498 * return - the error status as a encoded string 499 */ 500 function getMailErrorString($mail_status_str) 501 { 502 global $adb; 503 $adb->println("Inside getMailErrorString function.\nMail status string ==> ".$mail_status_str); 504 505 $mail_status_str = trim($mail_status_str,"&&&"); 506 $mail_status_array = explode("&&&",$mail_status_str); 507 $adb->println("All Mail status ==>\n".$mail_status_str."\n"); 508 509 foreach($mail_status_array as $key => $val) 510 { 511 $list = explode("=",$val); 512 $adb->println("Mail id & status ==> ".$list[0]." = ".$list[1]); 513 if($list[1] == 0) 514 { 515 $mail_error_str .= $list[0]."=".$list[1]."&&&"; 516 } 517 } 518 $adb->println("Mail error string => '".$mail_error_str."'"); 519 if($mail_error_str != '') 520 { 521 $mail_error_str = 'mail_error='.base64_encode($mail_error_str); 522 } 523 return $mail_error_str; 524 } 525 526 /** Function to parse the error string 527 * $mail_error_str -- base64 encoded string which contains the mail sending errors as concatenated with &&& 528 * return - Error message to display 529 */ 530 function parseEmailErrorString($mail_error_str) 531 { 532 //TODO -- we can modify this function for better email error handling in future 533 global $adb, $mod_strings; 534 $adb->println("Inside the parseEmailErrorString function.\n encoded mail error string ==> ".$mail_error_str); 535 536 $mail_error = base64_decode($mail_error_str); 537 $adb->println("Original error string => ".$mail_error); 538 $mail_status = explode("&&&",trim($mail_error,"&&&")); 539 foreach($mail_status as $key => $val) 540 { 541 $status_str = explode("=",$val); 542 $adb->println('Mail id => "'.$status_str[0].'".........status => "'.$status_str[1].'"'); 543 if($status_str[1] != 1 && $status_str[1] != '') 544 { 545 $adb->println("Error in mail sending"); 546 if($status_str[1] == 'connect_host') 547 { 548 $adb->println("if part - Mail sever is not configured"); 549 $errorstr .= '<br><b><font color=red>'.$mod_strings['MESSAGE_CHECK_MAIL_SERVER_NAME'].'</font></b>'; 550 break; 551 } 552 elseif($status_str[1] == '0') 553 { 554 $adb->println("first elseif part - status will be 0 which is the case of assigned to vtiger_users's email is empty."); 555 $errorstr .= '<br><b><font color=red> '.$mod_strings['MESSAGE_MAIL_COULD_NOT_BE_SEND'].' '.$mod_strings['MESSAGE_PLEASE_CHECK_FROM_THE_MAILID'].'</font></b>'; 556 //Added to display the message about the CC && BCC mail sending status 557 if($status_str[0] == 'cc_success') 558 { 559 $cc_msg = 'But the mail has been sent to CC & BCC addresses.'; 560 $errorstr .= '<br><b><font color=purple>'.$cc_msg.'</font></b>'; 561 } 562 } 563 elseif(strstr($status_str[1],'from_failed')) 564 { 565 $adb->println("second elseif part - from email id is failed."); 566 $from = explode('from_failed',$status_str[1]); 567 $errorstr .= "<br><b><font color=red>".$mod_strings['MESSAGE_PLEASE_CHECK_THE_FROM_MAILID']." '".$from[1]."'</font></b>"; 568 } 569 else 570 { 571 $adb->println("else part - mail send process failed due to the following reason."); 572 $errorstr .= "<br><b><font color=red> ".$mod_strings['MESSAGE_MAIL_COULD_NOT_BE_SEND_TO_THIS_EMAILID']." '".$status_str[0]."'. ".$mod_strings['PLEASE_CHECK_THIS_EMAILID']."</font></b>"; 573 } 574 } 575 } 576 $adb->println("Return Error string => ".$errorstr); 577 return $errorstr; 578 } 579 580 function isUserInitiated() { 581 return (($_REQUEST['module'] == 'Emails' || $_REQUEST['module'] == 'Webmails') && 582 ($_REQUEST['action'] == 'mailsend' || $_REQUEST['action'] == 'webmailsend' || $_REQUEST['action'] == 'Save')); 583 } 584 585 /** 586 * Function to get the group users Email ids 587 */ 588 function getDefaultAssigneeEmailIds($groupId) { 589 global $adb; 590 $emails = Array(); 591 if($groupId != '') { 592 require_once 'include/utils/GetGroupUsers.php'; 593 $userGroups = new GetGroupUsers(); 594 $userGroups->getAllUsersInGroup($groupId); 595 596 if(count($userGroups->group_users) == 0) return array(); 597 598 $result = $adb->pquery('SELECT email1,email2,secondaryemail FROM vtiger_users WHERE vtiger_users.id IN 599 ('. generateQuestionMarks($userGroups->group_users).') AND vtiger_users.status= ?', 600 array($userGroups->group_users, 'Active')); 601 $rows = $adb->num_rows($result); 602 for($i = 0;$i < $rows; $i++) { 603 $email = $adb->query_result($result,$i,'email1'); 604 if($email == '') { 605 $email = $adb->query_result($result,$i,'email2'); 606 if($email == '') { 607 $email = $adb->query_result($result,$i,'secondaryemail'); 608 } else { 609 $email = ''; 610 } 611 } 612 array_push($emails,$email); 613 } 614 $adb->println("Email ids are selected => '".$emails."'"); 615 return $emails; 616 } else { 617 $adb->println("User id is empty. so return value is ''"); 618 return array(); 619 } 620 } 621 622 623 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:08:37 2014 | Cross-referenced by PHPXref 0.7.1 |