1<?php 2/** 3 * InfoLog - Datasource for ProjektManager 4 * 5 * @link http://www.egroupware.org 6 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de> 7 * @package infolog 8 * @subpackage projectmanager 9 * @copyright (c) 2005-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de> 10 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License 11 * @version $Id$ 12 */ 13 14use EGroupware\Api; 15use EGroupware\Api\Link; 16use EGroupware\Api\Acl; 17 18include_once(EGW_INCLUDE_ROOT.'/projectmanager/inc/class.datasource.inc.php'); 19 20/** 21 * DataSource for InfoLog 22 * 23 * The InfoLog datasource set's only real start- and endtimes, plus planned and used time and 24 * the responsible user as resources (not always the owner too!). 25 * The read method of the extended datasource class sets the planned start- and endtime: 26 * - planned start from the end of a start constrain 27 * - planned end from the planned time and a start-time 28 * - planned start and end from the "real" values 29 */ 30class infolog_datasource extends datasource 31{ 32 /** 33 * Reference to infolog_bo 34 * 35 * @var infolog_bo 36 */ 37 var $infolog_bo; 38 39 /** 40 * Constructor 41 */ 42 function __construct() 43 { 44 parent::__construct('infolog'); 45 46 $this->valid = PM_COMPLETION|PM_PLANNED_START|PM_PLANNED_END|PM_REAL_END|PM_PLANNED_TIME|PM_REPLANNED_TIME|PM_USED_TIME|PM_RESOURCES|PM_CAT_ID; 47 48 // we use $GLOBALS['infolog_bo'] as an already running instance might be availible there 49 if (!is_object($GLOBALS['infolog_bo'])) 50 { 51 $GLOBALS['infolog_bo'] = new infolog_bo(); 52 } 53 $this->infolog_bo =& $GLOBALS['infolog_bo']; 54 } 55 56 /** 57 * get an entry from the underlaying app (if not given) and convert it into a datasource array 58 * 59 * @param mixed $data_id id as used in the link-class for that app, or complete entry as array 60 * @return array/boolean array with the data supported by that source or false on error (eg. not found, not availible) 61 */ 62 function get($data_id) 63 { 64 if (!is_array($data_id)) 65 { 66 $data =& $this->infolog_bo->read((int) $data_id); 67 68 if (!is_array($data)) return false; 69 } 70 else 71 { 72 $data =& $data_id; 73 } 74 75 return array( 76 'pe_title' => $this->infolog_bo->link_title($data), 77 'pe_completion' => $data['info_percent'], 78 'pe_planned_start'=> $data['info_startdate'] ? $data['info_startdate'] : null, 79 'pe_planned_end' => $data['info_enddate'] ? $data['info_enddate'] : null, 80 'pe_real_end' => $data['info_datecompleted'] ? $data['info_datecompleted'] : null, 81 'pe_planned_time' => $data['info_planned_time'], 82 'pe_replanned_time' => $data['info_replanned_time'], 83 'pe_used_time' => $data['info_used_time'], 84 'pe_resources' => count($data['info_responsible']) ? $data['info_responsible'] : array($data['info_owner']), 85 'pe_details' => $data['info_des'] ? nl2br($data['info_des']) : '', 86 'pl_id' => $data['pl_id'], 87 'pe_unitprice' => $data['info_price'], 88 'pe_planned_quantity' => $data['info_planned_time'] / 60, 89 'pe_planned_budget' => $data['info_planned_time'] / 60 * $data['info_price'], 90 'pe_used_quantity' => $data['info_used_time'] / 60, 91 'pe_used_budget' => $data['info_used_time'] / 60 * $data['info_price'], 92 'cat_id' => $data['info_cat'], 93 ); 94 } 95 96 /** 97 * Copy the datasource of a projectelement (InfoLog entry) and re-link it with project $target 98 * 99 * @param array $element source project element representing an InfoLog entry, $element['pe_app_id'] = info_id 100 * @param int $target target project id 101 * @param array $extra =null data of target-project, atm not used by the infolog datasource 102 * @param DateInterval[] $date_offsets = Array() - When copying, a list of date fields 103 * and the amount to offset them from the original while copying 104 * @return array/boolean array(info_id,link_id) on success, false otherwise 105 */ 106 function copy($element,$target,$extra=null,$date_offsets = Array()) 107 { 108 unset($extra); // not used, but required by function signature 109 110 $info =& $this->infolog_bo->read((int) $element['pe_app_id']); 111 112 if (!is_array($info)) return false; 113 114 $info_contact = $info['info_contact']; 115 $info_from = $info['info_from']; 116 117 // unsetting info_link_id and evtl. info_from 118 if ($info['info_link_id']) 119 { 120 $this->infolog_bo->link_id2from($info); // unsets info_from and sets info_link_target 121 unset($info['info_link_id']); 122 unset($info['info_contact']); 123 } 124 125 // we need to unset a few fields to get a new entry 126 foreach(array('info_id','info_owner','info_modified','info_modifierer') as $key) 127 { 128 unset($info[$key]); 129 } 130 131 // Apply date offsets, if any 132 $map = array( 133 'planned_start' => 'info_startdate', 134 'planned_end' => 'info_enddate', 135 'real_start' => 'info_startdate', 136 'real_end' => 'info_datecompleted' 137 ); 138 139 $startdate_original = $info['info_startdate']; 140 foreach($map as $offset_field => $info_field) 141 { 142 if($date_offsets[$offset_field] && $info[$info_field]) 143 { 144 // Don't move startdate twice, but prefer later value 145 if($startdate_original && $info_field == 'info_startdate') 146 { 147 $info[$info_field] = $startdate_original; 148 } 149 //error_log($offset_field . ' ' . Api\DateTime::to($info[$info_field]) . ' ' . $date_offsets[$offset_field]->format('%R%a days') . ' ' . date_add(new Api\DateTime($info[$info_field]), $date_offsets[$offset_field]) ); 150 151 $info[$info_field] = date_add(new Api\DateTime($info[$info_field]), $date_offsets[$offset_field])->format('ts'); 152 } 153 } 154 // Sanity check - not due or ended before it starts 155 if($info['info_startdate'] && $info['info_enddate'] && $info['info_startdate'] > $info['info_enddate']) 156 { 157 unset($info['info_enddate']); 158 } 159 if($info['info_startdate'] && $info['info_datecompleted'] && $info['info_startdate'] > $info['info_datecompleted']) 160 { 161 unset($info['info_datecompleted']); 162 } 163 164 // If info_from missing or matches project title, update it 165 if (!$info['info_from'] || $info['info_from'] == Link::title('projectmanager', $info['pm_id'])) 166 { 167 $info['info_from'] = Link::title('projectmanager',$target); 168 unset($info['info_custom_from']); 169 } 170 if ($info['info_status'] == 'template') 171 { 172 $info['info_status'] = $this->infolog_bo->activate($info); 173 } 174 175 $info['pm_id'] = $target; 176 if(!($info['info_id'] = $this->infolog_bo->write($info))) return false; 177 $this->infolog_bo->link_id2from($info); 178 179 // creating again all links, beside the one to the source-project 180 foreach(Link::get_links('infolog',$element['pe_app_id']) as $link) 181 { 182 if ($link['app'] == 'projectmanager' && $link['id'] == $element['pm_id'] || // ignoring the source project 183 $link['app'] == Link::VFS_APPNAME) // ignoring files attachments for now 184 { 185 continue; 186 } 187 Link::link('infolog',$info['info_id'],$link['app'],$link['id'],$link['remark']); 188 } 189 $this->infolog_bo->write($info); 190 $ret = array($info['info_id'],$info['info_link_id']); 191 192 // if we have a parent set, return our callback to modify the parent id, 193 // or we have a contact or custom info_from and need to re-set it after 194 // all entries are copied 195 if ($info['info_id_parent'] || $info_contact) 196 { 197 $ret[] = array($this,'copy_callback'); // callback 198 $ret[] = array($info['info_id'],$info['info_id_parent'], $info_contact, $info_from); // $param 199 } 200 return $ret; 201 } 202 203 /** 204 * Callback called after copying of all datasource, used to: 205 * - fix parent id's 206 * - reset contact if it was a link to another entry (not the project) 207 * - fix info_from 208 * 209 * @param array $param array($info_id,$info_id_parent) 210 * @param array $apps_copied array('infolog' => array($old_info_id => $new_info_id)) 211 */ 212 public function copy_callback(array $param, array $apps_copied) 213 { 214 //error_log(__METHOD__."(".array2string($param).', '.array2string($apps_copied).')'); 215 list($info_id,$parent_id, $contact, $from) = $param; 216 if ($parent_id && isset($apps_copied['infolog'][$parent_id]) && ($info = $this->infolog_bo->read($info_id))) 217 { 218 $info['info_id_parent'] = $apps_copied['infolog'][$parent_id]; 219 $this->infolog_bo->write($info,false,true,true,true); // no default and no notification 220 } 221 if($contact && $contact['app'] != 'projectmanager' && ($info = $this->infolog_bo->read($info_id))) 222 { 223 $info['info_contact'] = $contact; 224 $this->infolog_bo->write($info,false,true,true,true); // no default and no notification 225 } 226 if($from && ($info = $this->infolog_bo->read($info_id))) 227 { 228 $info['info_from'] = $from; 229 $this->infolog_bo->write($info,false,true,true,true); // no default and no notification 230 } 231 } 232 233 /** 234 * Delete the datasource of a project element 235 * 236 * @param int $id 237 * @return boolean true on success, false on error 238 */ 239 function delete($id) 240 { 241 if (!is_object($GLOBALS['infolog_bo'])) 242 { 243 include_once(EGW_INCLUDE_ROOT.'/infolog/inc/class.infolog_bo.inc.php'); 244 $GLOBALS['infolog_bo'] = new infolog_bo(); 245 } 246 // dont delete infolog, which are linked to other elements, but their project 247 if (count(Link::get_links('infolog',$id)) > 1) 248 { 249 return false; 250 } 251 return $this->infolog_bo->delete($id); 252 } 253 254 /** 255 * Change the status of an infolog entry according to the project status 256 * 257 * @param int $id 258 * @param string $status 259 * @return boolean true if status changed, false otherwise 260 */ 261 function change_status($id,$status) 262 { 263 //error_log("datasource_infolog::change_status($id,$status)"); 264 if (($info = $this->infolog_bo->read($id)) && ( 265 $this->infolog_bo->check_access($info,Acl::EDIT) || 266 $info['info_status'] == 'deleted' && $this->infolog_bo->check_access($info, infolog_bo::ACL_UNDELETE) 267 )) 268 { 269 if ($status == 'active' && in_array($info['info_status'],array('template','nonactive','archive','deleted'))) 270 { 271 $status = $this->infolog_bo->activate($info); 272 } 273 if($info['info_status'] != $status && isset($this->infolog_bo->status[$info['info_type']][$status])) 274 { 275 //error_log("datasource_infolog::change_status($id,$status) setting status from ".$info['info_status']); 276 $info['info_status'] = $status; 277 return $this->infolog_bo->write($info) !== false; 278 } 279 } 280 return false; 281 } 282}