index.php 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126
  1. <?php
  2. // Copyright (C) 2010 Combodo SARL
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation; version 3 of the License.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. /**
  17. * iTop User Portal main page
  18. *
  19. * @author Erwan Taloc <erwan.taloc@combodo.com>
  20. * @author Romain Quetiez <romain.quetiez@combodo.com>
  21. * @author Denis Flaven <denis.flaven@combodo.com>
  22. * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
  23. */
  24. require_once('../approot.inc.php');
  25. require_once(APPROOT.'/application/application.inc.php');
  26. require_once(APPROOT.'/application/nicewebpage.class.inc.php');
  27. require_once(APPROOT.'/application/wizardhelper.class.inc.php');
  28. /**
  29. * Get the list of parameters (i.e. attribute codes) to be handled while creating a new UserRequest object
  30. * @return Array The list of attribute codes
  31. */
  32. function GetParamsList()
  33. {
  34. return array('org_id', 'caller_id', 'service_id', 'servicesubcategory_id', 'request_type', 'title', 'description', 'impact', 'urgency', 'workgroup_id');
  35. }
  36. /**
  37. * Outputs a list of parameters as hidden field into the current page
  38. * (must be called when inside a form)
  39. * @param WebPage $oP The current web page
  40. * @param Array $aInteractive The list of parameters that are handled intractively and thus should not be output as hidden fields
  41. * @param Hash $aParameters Array name => value for the parameters
  42. * @return void
  43. */
  44. function DumpHiddenParams($oP, $aInteractive, $aParameters)
  45. {
  46. foreach($aParameters as $sAttCode => $value)
  47. {
  48. if (!in_array($sAttCode, $aInteractive))
  49. {
  50. $oP->Add("<input type=\"hidden\" name=\"attr_$sAttCode\" value=\"$value\">");
  51. }
  52. }
  53. }
  54. /**
  55. * Read all the parameters of the page for building a UserRequest
  56. * Parameters that were absent from the page's parameters are not set in the resulting hash array
  57. * @input string $sMethod Either get or post
  58. * @return Hash Array of name => value corresponding to the parameters that were passed to the page
  59. */
  60. function ReadAllParams()
  61. {
  62. $aParams = GetParamsList();
  63. $aValues = array();
  64. foreach($aParams as $sName)
  65. {
  66. $value = utils::ReadParam('attr_'.$sName, null, false, 'raw_data');
  67. if (!is_null($value))
  68. {
  69. $aValues[$sName] = $value;
  70. }
  71. }
  72. return $aValues;
  73. }
  74. /**
  75. * Displays the portal main menu
  76. * @param WebPage $oP The current web page
  77. * @return void
  78. */
  79. function DisplayMainMenu(WebPage $oP)
  80. {
  81. $oP->AddMenuButton('refresh', 'Portal:Refresh', './index.php?operation=welcome');
  82. $oP->AddMenuButton('create', 'Portal:CreateNewRequest', './index.php?operation=create_request');
  83. $oP->AddMenuButton('change_pwd', 'Portal:ChangeMyPassword', './index.php?loginop=change_pwd');
  84. $oP->add("<div id=\"#div_resolved_requests\">\n");
  85. $oP->add("<h1 id=\"#open_requests\">".Dict::S('Portal:OpenRequests')."</h1>\n");
  86. ListOpenRequests($oP);
  87. $oP->add("</div>\n");
  88. $oP->add("<div id=\"#div_resolved_requests\">\n");
  89. $oP->add("<h1 id=\"#resolved_requests\">".Dict::S('Portal:ResolvedRequests')."</h1>\n");
  90. ListResolvedRequests($oP);
  91. $oP->add("</div>\n");
  92. }
  93. /**
  94. * Displays the form to select a Service Id (among the valid ones for the specified user Organization)
  95. * @param WebPage $oP Web page for the form output
  96. * @param Organization $oUserOrg The organization of the current user
  97. * @return void
  98. */
  99. function SelectService($oP, $oUserOrg)
  100. {
  101. // Init forms parameters
  102. $aParameters = ReadAllParams();
  103. $oSearch = DBObjectSearch::FromOQL('SELECT Service AS s JOIN SLA AS sla ON sla.service_id=s.id JOIN lnkContractToSLA AS ln ON ln.sla_id=sla.id JOIN CustomerContract AS cc ON ln.contract_id=cc.id WHERE cc.org_id = :org_id');
  104. $oSet = new CMDBObjectSet($oSearch, array(), array('org_id' => $oUserOrg->GetKey()));
  105. $oP->add("<div class=\"wizContainer\" id=\"form_select_service\">\n");
  106. $oP->add("<h1 id=\"select_subcategory\">".Dict::S('Portal:SelectService')."</h1>\n");
  107. $oP->add("<form action=\"../portal/index.php\" id=\"request_form\" method=\"get\">\n");
  108. $oP->add("<table>\n");
  109. while($oService = $oSet->Fetch())
  110. {
  111. $id = $oService->GetKey();
  112. $sChecked = "";
  113. if (isset($aParameters['service_id']) && ($id == $aParameters['service_id']))
  114. {
  115. $sChecked = "checked";
  116. }
  117. $oP->p("<tr><td style=\"vertical-align:top\"><p><input name=\"attr_service_id\" $sChecked type=\"radio\" id=\"svc_$id\" value=\"$id\"></p></td><td style=\"vertical-align:top\"><p><b><label for=\"svc_$id\">".htmlentities($oService->GetName(), ENT_QUOTES, 'UTF-8')."</label></b></p>");
  118. $oP->p("<p>".htmlentities($oService->Get('description'), ENT_QUOTES, 'UTF-8')."</p></td></tr>");
  119. }
  120. $oP->add("</table>\n");
  121. DumpHiddenParams($oP, array('service_id'), $aParameters);
  122. $oP->add("<input type=\"hidden\" name=\"step\" value=\"1\">");
  123. $oP->add("<input type=\"hidden\" name=\"operation\" value=\"create_request\">");
  124. $oP->p("<input type=\"submit\" value=\"".Dict::S('UI:Button:Next')."\">");
  125. $oP->add("</form>");
  126. $oP->add("</div class=\"wizContainer\">\n");
  127. $sMessage = Dict::S('Portal:PleaseSelectOneService');
  128. $oP->add_ready_script(
  129. <<<EOF
  130. $('#request_form').submit(function() {
  131. return CheckSelection('$sMessage');
  132. });
  133. EOF
  134. );
  135. }
  136. /**
  137. * Displays the form to select a Service Subcategory Id (among the valid ones for the specified user Organization)
  138. * and based on the page's parameter 'service_id'
  139. * @param WebPage $oP Web page for the form output
  140. * @param Organization $oUserOrg The organization of the current user
  141. * @return void
  142. */
  143. function SelectSubService($oP, $oUserOrg)
  144. {
  145. // Init forms parameters
  146. $aParameters = ReadAllParams();
  147. $iSvcId = $aParameters['service_id'];
  148. $iDefaultSubSvcId = isset($aParameters['servicesubcategory_id']) ? $aParameters['servicesubcategory_id'] : 0;
  149. $oSearch = DBObjectSearch::FromOQL('SELECT ServiceSubcategory AS ss WHERE ss.service_id = :svc_id');
  150. $oSet = new CMDBObjectSet($oSearch, array(), array('svc_id' => $iSvcId));
  151. $oService = MetaModel::GetObject('Service', $iSvcId, false);
  152. if (is_object($oService))
  153. {
  154. $oP->add("<div class=\"wizContainer\" id=\"form_select_servicesubcategory\">\n");
  155. $oP->add("<h1 id=\"select_subcategory\">".Dict::Format('Portal:SelectSubcategoryFrom_Service', htmlentities($oService->GetName(), ENT_QUOTES, 'UTF-8'))."</h1>\n");
  156. $oP->add("<form id=\"request_form\" method=\"get\">\n");
  157. $oP->add("<table>\n");
  158. while($oSubService = $oSet->Fetch())
  159. {
  160. $id = $oSubService->GetKey();
  161. $sChecked = "";
  162. if ($id == $iDefaultSubSvcId)
  163. {
  164. $sChecked = "checked";
  165. }
  166. $oP->p("<tr><td style=\"vertical-align:top\"><p><input name=\"attr_servicesubcategory_id\" $sChecked type=\"radio\" id=\"subsvc_$id\" value=\"$id\"></p></td><td style=\"vertical-align:top\"><p><b><label for=\"subsvc_$id\">".htmlentities($oSubService->GetName(), ENT_QUOTES, 'UTF-8')."</label></b></p>");
  167. $oP->p("<p>".htmlentities($oSubService->Get('description'), ENT_QUOTES, 'UTF-8')."</p></td></tr>");
  168. }
  169. $sMessage = Dict::S('Portal:PleaseSelectAServiceSubCategory');
  170. $oP->add_ready_script(
  171. <<<EOF
  172. $('#request_form').submit(function() {
  173. return CheckSelection('$sMessage');
  174. });
  175. EOF
  176. );
  177. $oP->add("</table>\n");
  178. DumpHiddenParams($oP, array('servicesubcategory_id'), $aParameters);
  179. $oP->add("<input type=\"hidden\" name=\"step\" value=\"2\">");
  180. $oP->add("<input type=\"hidden\" name=\"operation\" value=\"create_request\">");
  181. $oP->p("<input type=\"submit\" value=\"".Dict::S('UI:Button:Back')."\" onClick=\"$('#request_form input[name=step]').val(0); $('#request_form').unbind('submit');\"\">&nbsp;<input type=\"submit\" value=\"".Dict::S('UI:Button:Next')."\">");
  182. $oP->add("</form>");
  183. $oP->add("</div>\n");
  184. }
  185. else
  186. {
  187. $oP->p("Error: Invalid Service: id = $iSvcId");
  188. }
  189. }
  190. /**
  191. * Displays the form for the final step of the UserRequest creation
  192. * @param WebPage $oP The current web page for the form output
  193. * @param Organization $oUserOrg The organization of the current user
  194. * @return void
  195. */
  196. function RequestCreationForm($oP, $oUserOrg)
  197. {
  198. $aList = array('request_type', 'title', 'description', 'impact', 'urgency', 'workgroup_id');
  199. $aParameters = ReadAllParams();
  200. $oService = MetaModel::GetObject('Service', $aParameters['service_id'], false);
  201. $oSubService = MetaModel::GetObject('ServiceSubcategory', $aParameters['servicesubcategory_id'], false);
  202. if (is_object($oService) && is_object($oSubService))
  203. {
  204. $oRequest = new UserRequest();
  205. $oRequest->Set('org_id', $oUserOrg->GetKey());
  206. $oRequest->Set('caller_id', UserRights::GetContactId());
  207. $oRequest->Set('service_id', $aParameters['service_id']);
  208. $oRequest->Set('servicesubcategory_id', $aParameters['servicesubcategory_id']);
  209. $oAttDef = MetaModel::GetAttributeDef('UserRequest', 'service_id');
  210. $aDetails[] = array('label' => '<span>'.$oAttDef->GetLabel().'</span>', 'value' => htmlentities($oService->GetName(), ENT_QUOTES, 'UTF-8'));
  211. $oAttDef = MetaModel::GetAttributeDef('UserRequest', 'servicesubcategory_id');
  212. $aDetails[] = array('label' => '<span>'.$oAttDef->GetLabel().'</span>', 'value' => htmlentities($oSubService->GetName(), ENT_QUOTES, 'UTF-8'));
  213. $iFlags = 0;
  214. foreach($aList as $sAttCode)
  215. {
  216. $value = '';
  217. if (isset($aParameters[$sAttCode]))
  218. {
  219. $value = $aParameters[$sAttCode];
  220. $oRequest->Set($sAttCode, $value);
  221. }
  222. }
  223. $aFieldsMap = array();
  224. foreach($aList as $sAttCode)
  225. {
  226. $value = '';
  227. $oAttDef = MetaModel::GetAttributeDef(get_class($oRequest), $sAttCode);
  228. $iFlags = $oRequest->GetAttributeFlags($sAttCode);
  229. if (isset($aParameters[$sAttCode]))
  230. {
  231. $value = $aParameters[$sAttCode];
  232. }
  233. $aArgs = array('this' => $oRequest);
  234. $aFieldsMap[$sAttCode] = 'attr_'.$sAttCode;
  235. $sValue = $oRequest->GetFormElementForField($oP, get_class($oRequest), $sAttCode, $oAttDef, $value, '', 'attr_'.$sAttCode, '', $iFlags, $aArgs);
  236. $aDetails[] = array('label' => '<span>'.$oAttDef->GetLabel().'</span>', 'value' => $sValue);
  237. }
  238. if (!class_exists('AttachmentPlugIn'))
  239. {
  240. // the Attachement plug-ins is not installed, do it the old way
  241. $aDetails[] = array('label' => '<span>'.Dict::S('Portal:Attachments').'</span>', 'value' => '&nbsp;');
  242. $aDetails[] = array('label' => '&nbsp;', 'value' => '<div id="attachments"></div><p><button type="button" onClick="AddAttachment();">'.Dict::S('Portal:AddAttachment').'</button/></p>');
  243. }
  244. $oP->add_linked_script("../js/json.js");
  245. $oP->add_linked_script("../js/forms-json-utils.js");
  246. $oP->add_linked_script("../js/wizardhelper.js");
  247. $oP->add_linked_script("../js/wizard.utils.js");
  248. $oP->add_linked_script("../js/linkswidget.js");
  249. $oP->add_linked_script("../js/extkeywidget.js");
  250. $oP->add_linked_script("../js/jquery.blockUI.js");
  251. $oP->add("<div class=\"wizContainer\" id=\"form_request_description\">\n");
  252. $oP->add("<h1 id=\"title_request_form\">".Dict::S('Portal:DescriptionOfTheRequest')."</h1>\n");
  253. $oP->add("<form action=\"../portal/index.php\" enctype=\"multipart/form-data\" id=\"request_form\" method=\"post\">\n");
  254. $oP->add("<table><tr><td>\n");
  255. $oP->details($aDetails);
  256. $sTransactionId = utils::GetNewTransactionId();
  257. $oP->SetTransactionId($sTransactionId); // Must be set before calling the plug-in
  258. if (class_exists('AttachmentPlugIn'))
  259. {
  260. $oAttPlugin = new AttachmentPlugIn();
  261. // depending on the plug-in's configuration, the attachments are displayed either in the 'properties' or in the 'relations'
  262. // in the portal, both are handled the same way... it does not matter
  263. $oAttPlugin->OnDisplayProperties($oRequest, $oP, true /* edit */);
  264. $oAttPlugin->OnDisplayRelations($oRequest, $oP, true /* edit */);
  265. }
  266. $oP->add("</td></tr></table>\n");
  267. DumpHiddenParams($oP, $aList, $aParameters);
  268. $oP->add("<input type=\"hidden\" name=\"step\" value=\"3\">");
  269. $oP->add("<input type=\"hidden\" name=\"operation\" value=\"create_request\">");
  270. $oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"$sTransactionId\">\n");
  271. $oP->p("<input type=\"submit\" value=\"".Dict::S('UI:Button:Back')."\" onClick=\"$('#request_form input[name=step]').val(1); $('#request_form').unbind('submit');\">&nbsp;<input type=\"submit\" value=\"".Dict::S('UI:Button:Finish')."\">");
  272. $oP->add("</form>");
  273. $oP->add("</div>\n");
  274. $iFieldsCount = count($aFieldsMap);
  275. $sJsonFieldsMap = json_encode($aFieldsMap);
  276. $oP->add_ready_script(
  277. <<<EOF
  278. // Create the object once at the beginning of the page... no state specified => new
  279. var oWizardHelper = new WizardHelper('UserRequest', '');
  280. oWizardHelper.SetFieldsMap($sJsonFieldsMap);
  281. oWizardHelper.SetFieldsCount($iFieldsCount);
  282. // Starts the validation when the page is ready
  283. CheckFields('request_form', false);
  284. $('#request_form').submit( function() {
  285. return OnSubmit('request_form');
  286. });
  287. $(window).unload(function() { OnUnload('$sTransactionId') } );
  288. EOF
  289. );
  290. $sBtnLabel = Dict::S('Portal:RemoveAttachment');
  291. $oP->add_script(
  292. <<<EOF
  293. var index = 0;
  294. function AddAttachment()
  295. {
  296. $('#attachments').append('<p id="attachment_'+index+'"><input type="file" name="attachement_'+index+'"/>&nbsp;<button type="button" onClick="RemoveAttachment('+index+')">{$sBtnLabel}</button/></p>');
  297. index++;
  298. }
  299. function RemoveAttachment(id_attachment)
  300. {
  301. $('#attachment_'+id_attachment).remove();
  302. }
  303. EOF
  304. );
  305. }
  306. else
  307. {
  308. // User not authorized to use this service ?
  309. DisplayMainMenu($oP);
  310. }
  311. }
  312. /**
  313. * Validate the parameters and create the UserRequest object (based on the page's POSTed parameters)
  314. * @param WebPage $oP The current web page for the output
  315. * @param Organization $oUserOrg The organization of the current user
  316. * @return void
  317. */
  318. function DoCreateRequest($oP, $oUserOrg)
  319. {
  320. $aParameters = ReadAllParams();
  321. $sTransactionId = utils::ReadPostedParam('transaction_id', '');
  322. if (!utils::IsTransactionValid($sTransactionId))
  323. {
  324. $oP->add("<h1>".Dict::S('UI:Error:ObjectAlreadyCreated')."</h1>\n");
  325. DisplayMainMenu($oP);
  326. return;
  327. }
  328. // Validate the parameters
  329. // 1) Service
  330. $oSearch = DBObjectSearch::FromOQL('SELECT Service AS s JOIN SLA AS sla ON sla.service_id=s.id JOIN lnkContractToSLA AS ln ON ln.sla_id=sla.id JOIN CustomerContract AS cc ON ln.contract_id=cc.id WHERE cc.org_id = :org_id AND s.id = :svc_id');
  331. $oSet = new CMDBObjectSet($oSearch, array(), array('org_id' => $oUserOrg->GetKey(), 'svc_id' => $aParameters['service_id']));
  332. if ($oSet->Count() != 1)
  333. {
  334. // Invalid service for the current user !
  335. throw new Exception("Invalid Service: id={$aParameters['servicesubcategory_id']} for the current user (org_id=".$oUserOrg->GetKey().").");
  336. }
  337. $oService = $oSet->Fetch();
  338. // 2) Service Subcategory
  339. $oSearch = DBObjectSearch::FromOQL('SELECT ServiceSubcategory AS sc WHERE sc.id = :subcategory_id AND sc.service_id = :svc_id');
  340. $oSet = new CMDBObjectSet($oSearch, array(), array('svc_id' => $aParameters['service_id'], 'subcategory_id' =>$aParameters['servicesubcategory_id'] ));
  341. if ($oSet->Count() != 1)
  342. {
  343. // Invalid subcategory
  344. throw new Exception("Invalid ServiceSubcategory: id={$aParameters['servicesubcategory_id']} for service ".$oService->GetName()."({$aParameters['service_id']})");
  345. }
  346. $oRequest = new UserRequest();
  347. $oRequest->Set('org_id', $oUserOrg->GetKey());
  348. $oRequest->Set('caller_id', UserRights::GetContactId());
  349. $aList = array('service_id', 'servicesubcategory_id', 'request_type', 'title', 'description', 'impact', 'urgency', 'workgroup_id');
  350. foreach($aList as $sAttCode)
  351. {
  352. $oRequest->Set($sAttCode, $aParameters[$sAttCode]);
  353. }
  354. list($bRes, $aIssues) = $oRequest->CheckToWrite();
  355. if ($bRes)
  356. {
  357. $oMyChange = MetaModel::NewObject("CMDBChange");
  358. $oMyChange->Set("date", time());
  359. $sUserString = CMDBChange::GetCurrentUserName();
  360. $oMyChange->Set("userinfo", $sUserString);
  361. $iChangeId = $oMyChange->DBInsert();
  362. $oRequest->DBInsertTracked($oMyChange);
  363. $oP->add("<h1>".Dict::Format('UI:Title:Object_Of_Class_Created', $oRequest->GetName(), MetaModel::GetName(get_class($oRequest)))."</h1>\n");
  364. if (class_exists('AttachmentPlugIn'))
  365. {
  366. // New way: use the plug-in
  367. $oAttPlugin = new AttachmentPlugIn();
  368. $oAttPlugin->OnFormSubmit($oRequest);
  369. }
  370. else
  371. {
  372. // Old way: create linked documents
  373. $index = 0;
  374. foreach($_FILES as $sName => $void)
  375. {
  376. $oAttachment = utils::ReadPostedDocument($sName);
  377. if (!$oAttachment->IsEmpty())
  378. {
  379. $index++;
  380. // Create a document and attach it to the created ticket
  381. $oDoc = new FileDoc();
  382. $oDoc->Set('name', Dict::Format('Portal:Attachment_No_To_Ticket_Name', $index, $oRequest->GetName(), $oAttachment->GetFileName()));
  383. $oDoc->Set('org_id', $oUserOrg->GetKey());
  384. $oDoc->Set('description', $oAttachment->GetFileName());
  385. $oDoc->Set('contents', $oAttachment);
  386. $oDoc->DBInsertTracked($oMyChange);
  387. // Link the document to the ticket
  388. $oLink = new lnkTicketToDoc();
  389. $oLink->Set('ticket_id', $oRequest->GetKey());
  390. $oLink->Set('document_id', $oDoc->GetKey());
  391. $oLink->DBInsertTracked($oMyChange);
  392. }
  393. }
  394. }
  395. DisplayMainMenu($oP);
  396. }
  397. else
  398. {
  399. RequestCreationForm($oP, $oUserOrg);
  400. $sIssueDesc = Dict::Format('UI:ObjectCouldNotBeWritten', implode(', ', $aIssues));
  401. $oP->add_ready_script("alert('".addslashes($sIssueDesc)."');");
  402. }
  403. }
  404. /**
  405. * Prompts the user for creating a new request
  406. * @param WebPage $oP The current web page
  407. * @return void
  408. */
  409. function CreateRequest(WebPage $oP, Organization $oUserOrg)
  410. {
  411. $iStep = utils::ReadParam('step', 0);
  412. switch($iStep)
  413. {
  414. case 0:
  415. default:
  416. $oP->AddMenuButton('cancel', 'UI:Button:Cancel', './index.php?operation=welcome');
  417. SelectService($oP, $oUserOrg);
  418. break;
  419. case 1:
  420. $oP->AddMenuButton('cancel', 'UI:Button:Cancel', './index.php?operation=welcome');
  421. SelectSubService($oP, $oUserOrg);
  422. break;
  423. case 2:
  424. $oP->AddMenuButton('cancel', 'UI:Button:Cancel', './index.php?operation=welcome');
  425. RequestCreationForm($oP, $oUserOrg);
  426. break;
  427. case 3:
  428. DoCreateRequest($oP, $oUserOrg);
  429. break;
  430. }
  431. }
  432. /**
  433. * Displays the value of the given field, in HTML, without any hyperlink to other objects
  434. * @param DBObject $oObj The object to use
  435. * @param string $sAttCode Code of the attribute to display
  436. * @return string HTML text representing the value of this field
  437. */
  438. function GetFieldAsHtml($oObj, $sAttCode)
  439. {
  440. $sValue = '';
  441. $oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
  442. if ($oAttDef->IsExternalKey())
  443. {
  444. // Special processing for external keys: don't display any hyperlink
  445. $oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $oObj->Get($sAttCode), false);
  446. if (is_object($oTargetObj))
  447. {
  448. $sValue = $oTargetObj->GetName();
  449. }
  450. else
  451. {
  452. $sValue = Dict::S('UI:UndefinedObject');
  453. }
  454. }
  455. else
  456. {
  457. $sValue = $oObj->GetAsHTML($sAttCode);
  458. }
  459. return $sValue;
  460. }
  461. /**
  462. * Displays a list of objects, without any hyperlink (except for the object's details)
  463. * @param WebPage $oP The web page for the output
  464. * @param DBObjectSet $oSet The set of objects to display
  465. * @param Array $aZList The ZList (list of field codes) to use for the tabular display
  466. * @return string The HTML text representing the list
  467. */
  468. function DisplaySet($oP, $oSet, $aZList)
  469. {
  470. if ($oSet->Count() > 0)
  471. {
  472. $aAttribs = array();
  473. $aValues = array();
  474. $oAttDef = MetaModel::GetAttributeDef('UserRequest', 'ref');
  475. $aAttribs['key'] = array('label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
  476. foreach($aZList as $sAttCode)
  477. {
  478. $oAttDef = MetaModel::GetAttributeDef('UserRequest', $sAttCode);
  479. $aAttribs[$sAttCode] = array('label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
  480. }
  481. while($oRequest = $oSet->Fetch())
  482. {
  483. $aRow = array();
  484. $aRow['key'] = '<a href="'.utils::GetAbsoluteUrlAppRoot().'portal/index.php?operation=details&id='.$oRequest->GetKey().'">'.$oRequest->Get('ref').'</a>';
  485. $sHilightClass = $oRequest->GetHilightClass();
  486. if ($sHilightClass != '')
  487. {
  488. $aRow['@class'] = $sHilightClass;
  489. }
  490. foreach($aZList as $sAttCode)
  491. {
  492. $aRow[$sAttCode] = GetFieldAsHtml($oRequest, $sAttCode);
  493. }
  494. $aValues[$oRequest->GetKey()] = $aRow;
  495. }
  496. $oP->Table($aAttribs, $aValues);
  497. // Temprory until we merge re-use the paginated tables:
  498. $oP->add_ready_script(
  499. <<<EOF
  500. $('table.listResults').tableHover().tablesorter( { widgets: ['myZebra', 'truncatedList']} );
  501. EOF
  502. );
  503. }
  504. else
  505. {
  506. $oP->add(Dict::S('Portal:NoOpenRequest'));
  507. }
  508. }
  509. /**
  510. * Lists all the currently opened User Requests for the current user
  511. * @param WebPage $oP The current web page
  512. * @return void
  513. */
  514. function ListOpenRequests(WebPage $oP)
  515. {
  516. $iContactId = UserRights::GetContactId();
  517. $oContact = MetaModel::GetObject('Contact', $iContactId, false); // false => Can fail
  518. if (is_object($oContact))
  519. {
  520. $sOQL = 'SELECT UserRequest WHERE caller_id = :contact_id AND status NOT IN ("resolved", "closed")';
  521. $oSearch = DBObjectSearch::FromOQL($sOQL);
  522. $oSet = new CMDBObjectSet($oSearch, array(), array('contact_id' => $iContactId));
  523. $aZList = array('title', 'start_date', 'status', 'service_id', 'priority', 'workgroup_id', 'agent_id');
  524. DisplaySet($oP, $oSet, $aZList);
  525. }
  526. }
  527. /**
  528. * Lists all the currently Resolved (not "Closed")User Requests for the current user
  529. * @param WebPage $oP The current web page
  530. * @return void
  531. */
  532. function ListResolvedRequests(WebPage $oP)
  533. {
  534. $iContactId = UserRights::GetContactId();
  535. $oContact = MetaModel::GetObject('Contact', $iContactId, false); // false => Can fail
  536. if (is_object($oContact))
  537. {
  538. $sOQL = 'SELECT UserRequest WHERE caller_id = :contact_id AND status ="resolved"';
  539. $oSearch = DBObjectSearch::FromOQL($sOQL);
  540. $oSet = new CMDBObjectSet($oSearch, array(), array('contact_id' => $iContactId));
  541. $aZList = array('title', 'start_date', 'status', 'service_id', 'priority', 'workgroup_id', 'agent_id');
  542. DisplaySet($oP, $oSet, $aZList);
  543. }
  544. }
  545. /**
  546. * Displays the details of the specified UserRequest object
  547. * @param WebPage $oP The current web page for the output
  548. * @param UserRequest $oRequest The object to display
  549. * @return void
  550. */
  551. function DisplayRequestDetails($oP, UserRequest $oRequest, $bEditMode = true)
  552. {
  553. // Identical to the standard 'details' ZList of UserRequest, except that the field 'org_id' has been removed
  554. $aList = array(
  555. 'col:col1' => array(
  556. 'fieldset:Ticket:baseinfo' => array('ref','title','request_type','status','priority','service_id','servicesubcategory_id','product' ),
  557. 'fieldset:Ticket:moreinfo' => array('impact','urgency','description','resolution_code', 'solution', 'user_satisfaction', 'user_commment','freeze_reason'),
  558. ),
  559. 'col:col2' => array(
  560. 'fieldset:Ticket:date' => array('start_date','last_update','assignment_date','tto_escalation_deadline', 'ttr_escalation_deadline', 'close_date', 'closure_deadline',),
  561. 'fieldset:Ticket:contact' => array('caller_id','workgroup_id','agent_id',),
  562. 'fieldset:Ticket:relation' => array('related_problem_id', 'related_change_id'),
  563. )
  564. );
  565. // Similar to CMDBAbstractObject::GetBareProperties except that: multiple tabs are not supported and GetFieldAsHtml is customized
  566. // in order to NOT display any hyperlink
  567. $aDetails = array();
  568. $oP->add('<div id="request_details" class="ui-widget-content">');
  569. $aDetailsStruct = CMDBAbstractObject::ProcessZlist($aList, array('UI:PropertiesTab' => array()), 'UI:PropertiesTab', 'col1', '');
  570. // Compute the list of properties to display, first the attributes in the 'details' list, then
  571. // all the remaining attributes that are not external fields
  572. $aDetails = array();
  573. $iInputId = 0;
  574. foreach($aDetailsStruct as $sTab => $aCols )
  575. {
  576. $aDetails[$sTab] = array();
  577. ksort($aCols);
  578. $oP->add('<table style="vertical-align:top"><tr>');
  579. foreach($aCols as $sColIndex => $aFieldsets)
  580. {
  581. $oP->add('<td style="vertical-align:top">');
  582. //$aDetails[$sTab][$sColIndex] = array();
  583. $sLabel = '';
  584. $sPreviousLabel = '';
  585. $aDetails[$sTab][$sColIndex] = array();
  586. foreach($aFieldsets as $sFieldsetName => $aFields)
  587. {
  588. if (!empty($sFieldsetName) && ($sFieldsetName[0] != '_'))
  589. {
  590. $sLabel = $sFieldsetName;
  591. }
  592. else
  593. {
  594. $sLabel = '';
  595. }
  596. if ($sLabel != $sPreviousLabel)
  597. {
  598. if (!empty($sPreviousLabel))
  599. {
  600. $oP->add('<fieldset>');
  601. $oP->add('<legend>'.Dict::S($sPreviousLabel).'</legend>');
  602. }
  603. $oP->Details($aDetails[$sTab][$sColIndex]);
  604. if (!empty($sPreviousLabel))
  605. {
  606. $oP->add('</fieldset>');
  607. }
  608. $aDetails[$sTab][$sColIndex] = array();
  609. $sPreviousLabel = $sLabel;
  610. }
  611. foreach($aFields as $sAttCode)
  612. {
  613. if (MetaModel::IsValidAttCode(get_class($oRequest), $sAttCode))
  614. {
  615. $iFlags = $oRequest->GetAttributeFlags($sAttCode);
  616. if ( ($iFlags & OPT_ATT_HIDDEN) == 0)
  617. {
  618. // The field is visible, add it to the current column
  619. $val = GetFieldAsHtml($oRequest, $sAttCode);
  620. $aDetails[$sTab][$sColIndex][] = array( 'label' => '<span title="'.MetaModel::GetDescription('UserRequest', $sAttCode).'">'.MetaModel::GetLabel('UserRequest', $sAttCode).'</span>', 'value' => $val);
  621. $iInputId++;
  622. }
  623. }
  624. }
  625. }
  626. if (!empty($sPreviousLabel))
  627. {
  628. $oP->add('<fieldset>');
  629. $oP->add('<legend>'.Dict::S($sFieldsetName).'</legend>');
  630. }
  631. $oP->Details($aDetails[$sTab][$sColIndex]);
  632. if (!empty($sPreviousLabel))
  633. {
  634. $oP->add('</fieldset>');
  635. }
  636. $oP->add('</td>');
  637. }
  638. $oP->add('</tr></table>');
  639. }
  640. if (!class_exists('AttachmentPlugIn'))
  641. {
  642. // Attachments, the old way
  643. $sOQL = 'SELECT FileDoc AS Doc JOIN lnkTicketToDoc AS L ON L.document_id = Doc.id WHERE L.ticket_id = :request_id';
  644. $oSearch = DBObjectSearch::FromOQL($sOQL);
  645. $oSet = new CMDBObjectSet($oSearch, array(), array('request_id' => $oRequest->GetKey()));
  646. $aDetails = array();
  647. if ($oSet->Count() > 0)
  648. {
  649. $sAttachements = '<table>';
  650. while($oDoc = $oSet->Fetch())
  651. {
  652. $sAttachements .= '<tr><td>'.$oDoc->GetAsHtml('contents').'</td></tr>';
  653. }
  654. $sAttachements .= '</table>';
  655. $aDetails[] = array('label' => Dict::S('Portal:Attachments'), 'value' => $sAttachements);
  656. }
  657. $oP->Details($aDetails);
  658. }
  659. // Case log... editable so that users can post comments
  660. if ($bEditMode)
  661. {
  662. $oP->add("<form action=\"../portal/index.php\" id=\"request_form\" method=\"post\">\n");
  663. $oP->add("<input type=\"hidden\" name=\"id\" value=\"".$oRequest->GetKey()."\">");
  664. $oP->add("<input type=\"hidden\" name=\"step\" value=\"3\">");
  665. $sTransactionId = utils::GetNewTransactionId();
  666. $oP->SetTransactionId($sTransactionId);
  667. $oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"$sTransactionId\">\n");
  668. $oP->add("<input type=\"hidden\" name=\"operation\" value=\"details\">");
  669. $oP->add('<fieldset id="request_details_log"><legend>'.MetaModel::GetLabel('UserRequest', 'ticket_log').'</legend>');
  670. $oAttDef = MetaModel::GetAttributeDef(get_class($oRequest), 'ticket_log');
  671. $oValue = $oRequest->Get('ticket_log');
  672. $oP->add($oRequest->GetFormElementForField($oP, get_class($oRequest), 'ticket_log', $oAttDef, $oValue, $sDisplayValue = '', $iId = 'att_ticket_log'));
  673. //$oP->add(GetFieldAsHtml($oRequest, 'ticket_log'));
  674. $oP->add('</fieldset>');
  675. if (class_exists('AttachmentPlugIn'))
  676. {
  677. $oAttPlugin = new AttachmentPlugIn();
  678. // depending on the plug-in's configuration, the attachments are displayed either in the 'properties' or in the 'relations'
  679. // in the portal, both are handled the same way... it does not matter
  680. $oAttPlugin->OnDisplayProperties($oRequest, $oP, true /* edit */);
  681. $oAttPlugin->OnDisplayRelations($oRequest, $oP, true /* edit */);
  682. }
  683. $oP->p('<input type="submit" value="'.Dict::S('UI:Button:Ok').'">');
  684. $oP->add('</form>');
  685. $oP->add_ready_script(
  686. <<<EOF
  687. $('#request_form').submit( function() { return OnSubmit('request_form'); });
  688. $(window).unload(function() { OnUnload('$sTransactionId') } );
  689. EOF
  690. );
  691. }
  692. else
  693. {
  694. $oP->add('<fieldset id="request_details_log"><legend>'.MetaModel::GetLabel('UserRequest', 'ticket_log').'</legend>');
  695. $oP->add(GetFieldAsHtml($oRequest, 'ticket_log'));
  696. $oP->add('</fieldset>');
  697. if (class_exists('AttachmentPlugIn'))
  698. {
  699. $oAttPlugin = new AttachmentPlugIn();
  700. // depending on the plug-in's configuration, the attachments are displayed either in the 'properties' or in the 'relations'
  701. // in the portal, both are handled the same way... it does not matter
  702. $oAttPlugin->OnDisplayProperties($oRequest, $oP, false /* edit */);
  703. $oAttPlugin->OnDisplayRelations($oRequest, $oP, false /* edit */);
  704. }
  705. }
  706. $oP->add('</div>');
  707. }
  708. /**
  709. * Displays a form for the user to provide feedback about a 'resolved' UserRequest and then close the request
  710. * @param WebPage $oP The current web page
  711. * @param UserRequest $oRequest The object to display
  712. * @return void
  713. */
  714. function DisplayResolvedRequestForm($oP, UserRequest $oRequest)
  715. {
  716. $oP->add("<div class=\"wizContainer\" id=\"form_close_request\">\n");
  717. $oP->add("<form action=\"../portal/index.php\" id=\"request_form\" method=\"post\">\n");
  718. $aArgs = array('this' => $oRequest);
  719. $sClass = get_class($oRequest);
  720. $aDetails = array();
  721. $aTargetStates = MetaModel::EnumStates($sClass);
  722. $aTargetState = $aTargetStates['closed'];
  723. $aExpectedAttributes = $aTargetState['attribute_list'];
  724. $iFieldIndex = 0;
  725. foreach($aExpectedAttributes as $sAttCode => $iExpectCode)
  726. {
  727. // Prompt for an attribute if
  728. // - the attribute must be changed or must be displayed to the user for confirmation
  729. // - or the field is mandatory and currently empty
  730. if ( ($iExpectCode & (OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) ||
  731. (($iExpectCode & OPT_ATT_MANDATORY) && ($oRequest->Get($sAttCode) == '')) )
  732. {
  733. $aAttributesDef = MetaModel::ListAttributeDefs($sClass);
  734. $oAttDef = $aAttributesDef[$sAttCode];
  735. $aArgs = array('this' => $oRequest);
  736. $sHTMLValue = cmdbAbstractObject::GetFormElementForField($oP, $sClass, $sAttCode, $oAttDef, $oRequest->Get($sAttCode), $oRequest->GetEditValue($sAttCode), 'att_'.$iFieldIndex, '', $iExpectCode, $aArgs);
  737. $aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => "<span id=\"field_att_$iFieldIndex\">$sHTMLValue</span>");
  738. $aFieldsMap[$sAttCode] = 'att_'.$iFieldIndex;
  739. $iFieldIndex++;
  740. }
  741. }
  742. $aStimuli = MetaModel::EnumStimuli($sClass);
  743. $oP->add("<h1>".Dict::S('Portal:EnterYourCommentsOnTicket')."</h1>");
  744. $oP->details($aDetails);
  745. $oP->add("<input type=\"hidden\" name=\"id\" value=\"".$oRequest->GetKey()."\">");
  746. $oP->add("<input type=\"hidden\" name=\"step\" value=\"2\">");
  747. $oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\">\n");
  748. $oP->add("<input type=\"hidden\" name=\"operation\" value=\"details\">");
  749. $oP->p("<input type=\"submit\" value=\"".Dict::S('Portal:Button:CloseTicket')."\">");
  750. $oP->add("</form>");
  751. $oP->add("</div>\n");
  752. $oP->add("<h1 id=\"title_request_details\">".Dict::Format('Portal:TitleRequestDetailsFor_Request', $oRequest->GetName())."</h1>\n");
  753. DisplayRequestDetails($oP, $oRequest, false /* bEditMode */);
  754. $oP->add_ready_script(
  755. <<<EOF
  756. // Starts the validation when the page is ready
  757. CheckFields('request_form', false);
  758. $('#request_form').submit( function() {
  759. return OnSubmit('request_form');
  760. });
  761. EOF
  762. );
  763. }
  764. /**
  765. * Actually close the request and saves the user's feedback
  766. * @param WebPage $oP The current web page
  767. * @param UserRequest $oRequest The object to close
  768. * @return void
  769. */
  770. function DoCloseRequest($oP, UserRequest $oRequest)
  771. {
  772. $sTransactionId = utils::ReadPostedParam('transaction_id', '');
  773. if (!utils::IsTransactionValid($sTransactionId))
  774. {
  775. $oP->add("<h1>".Dict::S('UI:Error:ObjectAlreadyCreated')."</h1>\n");
  776. DisplayMainMenu($oP);
  777. return;
  778. }
  779. $sClass = get_class($oRequest);
  780. $aDetails = array();
  781. $aTargetStates = MetaModel::EnumStates($sClass);
  782. $aTargetState = $aTargetStates['closed'];
  783. $aExpectedAttributes = $aTargetState['attribute_list'];
  784. $iFieldIndex = 0;
  785. foreach($aExpectedAttributes as $sAttCode => $iExpectCode)
  786. {
  787. // Prompt for an attribute if
  788. // - the attribute must be changed or must be displayed to the user for confirmation
  789. // - or the field is mandatory and currently empty
  790. if ( ($iExpectCode & (OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) ||
  791. (($iExpectCode & OPT_ATT_MANDATORY) && ($oRequest->Get($sAttCode) == '')) )
  792. {
  793. $value = utils::ReadPostedParam('attr_'.$sAttCode, null, 'raw_data');
  794. if (!is_null($value))
  795. {
  796. $oRequest->Set($sAttCode, $value);
  797. }
  798. }
  799. }
  800. if ($oRequest->ApplyStimulus('ev_close'))
  801. {
  802. $oMyChange = MetaModel::NewObject("CMDBChange");
  803. $oMyChange->Set("date", time());
  804. $sUserString = CMDBChange::GetCurrentUserName();
  805. $oMyChange->Set("userinfo", $sUserString);
  806. $iChangeId = $oMyChange->DBInsert();
  807. $oRequest->DBUpdateTracked($oMyChange);
  808. $oP->p("<h1>".Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oRequest)), $oRequest->GetName())."</h1>\n");
  809. DisplayMainMenu($oP);
  810. }
  811. else
  812. {
  813. $oP->AddMenuButton('back', 'Portal:Back', './index.php?operation=welcome');
  814. $oP->add('Error: cannot close the request - '.$oRequest->GetName());
  815. }
  816. }
  817. /**
  818. * Find the UserRequest object of the specified ID. Make sure that it the caller is the current user
  819. * @param integer $id The ID of the request to find
  820. * @return UserRequert The found object, or null in case of failure (object does not exist, user has no rights to see it...)
  821. */
  822. function FindRequest($id)
  823. {
  824. $oRequest = null;
  825. $iContactId = UserRights::GetContactId();
  826. $oContact = MetaModel::GetObject('Contact', $iContactId, false); // false => Can fail
  827. if (is_object($oContact))
  828. {
  829. $sOQL = "SELECT UserRequest WHERE caller_id = :contact_id AND id = :request_id";
  830. $oSearch = DBObjectSearch::FromOQL($sOQL);
  831. $oSet = new CMDBObjectSet($oSearch, array(), array('contact_id' => $iContactId, 'request_id' => $id));
  832. if ($oSet->Count() > 0)
  833. {
  834. $oRequest = $oSet->Fetch();
  835. }
  836. }
  837. else
  838. {
  839. $oP->AddMenuButton('back', 'Portal:Back', './index.php?operation=welcome');
  840. $oP->add("<p class=\"error\">".Dict::S('Portal:ErrorNoContactForThisUser')."</p>");
  841. }
  842. return $oRequest;
  843. }
  844. /**
  845. * Displays the details of a request
  846. * @param WebPage $oP The current web page
  847. * @return void
  848. */
  849. function RequestDetails(WebPage $oP, $id)
  850. {
  851. $oRequest = FindRequest($id);
  852. if (!is_object($oRequest))
  853. {
  854. DisplayMainMenu($oP);
  855. return;
  856. }
  857. $iDefaultStep = 0;
  858. if ($oRequest->GetState() == 'resolved')
  859. {
  860. // The current ticket is in 'resolved' state, prompt to close it
  861. $iDefaultStep = 1;
  862. }
  863. $iStep = utils::ReadParam('step', $iDefaultStep);
  864. switch($iStep)
  865. {
  866. case 0:
  867. $oP->AddMenuButton('back', 'Portal:Back', './index.php?operation=welcome');
  868. $oP->add("<h1 id=\"title_request_details\">".$oRequest->GetIcon()."&nbsp;".Dict::Format('Portal:TitleRequestDetailsFor_Request', $oRequest->GetName())."</h1>\n");
  869. DisplayRequestDetails($oP, $oRequest);
  870. break;
  871. case 1:
  872. $oP->AddMenuButton('cancel', 'UI:Button:Cancel', './index.php?operation=welcome');
  873. DisplayResolvedRequestForm($oP, $oRequest);
  874. break;
  875. case 2:
  876. DoCloseRequest($oP, $oRequest);
  877. break;
  878. case 3:
  879. AddComment($oP, $oRequest);
  880. break;
  881. default:
  882. // Should never happen
  883. DisplayMainMenu($oP);
  884. }
  885. }
  886. /**
  887. * Adds a comment to the specified UserRequest and displays the main menu
  888. * @param WebPage $oP The current web page for the output
  889. * @param $id ID of the object to update
  890. * @return void
  891. */
  892. function AddComment($oP, $id)
  893. {
  894. $oRequest = FindRequest($id);
  895. if (!is_object($oRequest))
  896. {
  897. DisplayMainMenu($oP);
  898. return;
  899. }
  900. $sTransactionId = utils::ReadPostedParam('transaction_id', '');
  901. if (!utils::IsTransactionValid($sTransactionId))
  902. {
  903. $oP->add("<h1>".Dict::S('UI:Error:ObjectAlreadyUpdated')."</h1>\n");
  904. DisplayMainMenu($oP);
  905. return;
  906. }
  907. $sComment = trim(utils::ReadPostedParam('attr_ticket_log', '', 'raw_data'));
  908. if (!empty($sComment))
  909. {
  910. $oRequest->Set('ticket_log', $sComment);
  911. }
  912. if (class_exists('AttachmentPlugIn'))
  913. {
  914. $oAttPlugin = new AttachmentPlugIn();
  915. $oAttPlugin->OnFormSubmit($oRequest, '');
  916. }
  917. if ($oRequest->IsModified())
  918. {
  919. $oMyChange = MetaModel::NewObject("CMDBChange");
  920. $oMyChange->Set("date", time());
  921. $sUserString = CMDBChange::GetCurrentUserName();
  922. $oMyChange->Set("userinfo", $sUserString);
  923. $iChangeId = $oMyChange->DBInsert();
  924. $oRequest->DBUpdateTracked($oMyChange);
  925. $oP->p("<h1>".Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oRequest)), $oRequest->GetName())."</h1>\n");
  926. // If there is any trigger for the Portal Update, then activate them
  927. $sOQL = "SELECT TriggerOnPortalUpdate WHERE target_class ='UserRequest'";
  928. $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL));
  929. while($oTrigger = $oSet->Fetch())
  930. {
  931. $oTrigger->DoActivate($oRequest->ToArgs('this'));
  932. }
  933. }
  934. else
  935. {
  936. $oP->p("<h1>".Dict::Format('UI:Class_Object_NotUpdated', MetaModel::GetName(get_class($oRequest)), $oRequest->GetName())."</h1>\n");
  937. }
  938. DisplayMainMenu($oP);
  939. }
  940. /**
  941. * Get The organization of the current user (i.e. the organization of its contact)
  942. * @param WebPage $oP The current page, for errors output
  943. * @return Organization The user's org or null in case of problem...
  944. */
  945. function GetUserOrg()
  946. {
  947. $oOrg = null;
  948. $iContactId = UserRights::GetContactId();
  949. $oContact = MetaModel::GetObject('Contact', $iContactId, false); // false => Can fail
  950. if (is_object($oContact))
  951. {
  952. $oOrg = MetaModel::GetObject('Organization', $oContact->Get('org_id'), false); // false => can fail
  953. }
  954. else
  955. {
  956. throw new Exception(Dict::S('Portal:ErrorNoContactForThisUser'));
  957. }
  958. return $oOrg;
  959. }
  960. try
  961. {
  962. require_once(APPROOT.'/application/startup.inc.php');
  963. require_once(APPROOT.'/application/portalwebpage.class.inc.php');
  964. $oAppContext = new ApplicationContext();
  965. $sOperation = utils::ReadParam('operation', '');
  966. require_once(APPROOT.'/application/loginwebpage.class.inc.php');
  967. LoginWebPage::DoLogin(false /* bMustBeAdmin */, true /* IsAllowedToPortalUsers */); // Check user rights and prompt if needed
  968. $oUserOrg = GetUserOrg();
  969. $sCode = $oUserOrg->Get('code');
  970. $sAlternateStylesheet = '';
  971. if (@file_exists("./$sCode/portal.css"))
  972. {
  973. $sAlternateStylesheet = "$sCode";
  974. }
  975. $oP = new PortalWebPage(Dict::S('Portal:Title'), $sAlternateStylesheet);
  976. $oP->add($sAlternateStylesheet);
  977. if (is_object($oUserOrg))
  978. {
  979. switch($sOperation)
  980. {
  981. case 'create_request':
  982. CreateRequest($oP, $oUserOrg);
  983. break;
  984. case 'details':
  985. $iRequestId = utils::ReadParam('id', 0);
  986. RequestDetails($oP, $iRequestId);
  987. break;
  988. case 'welcome':
  989. default:
  990. DisplayMainMenu($oP);
  991. }
  992. }
  993. $oP->output();
  994. }
  995. catch(CoreException $e)
  996. {
  997. require_once(APPROOT.'/setup/setuppage.class.inc.php');
  998. $oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
  999. $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");
  1000. $oP->error(Dict::Format('UI:Error_Details', $e->getHtmlDesc()));
  1001. $oP->output();
  1002. if (MetaModel::IsLogEnabledIssue())
  1003. {
  1004. if (MetaModel::IsValidClass('EventIssue'))
  1005. {
  1006. try
  1007. {
  1008. $oLog = new EventIssue();
  1009. $oLog->Set('message', $e->getMessage());
  1010. $oLog->Set('userinfo', '');
  1011. $oLog->Set('issue', $e->GetIssue());
  1012. $oLog->Set('impact', 'Page could not be displayed');
  1013. $oLog->Set('callstack', $e->getTrace());
  1014. $oLog->Set('data', $e->getContextData());
  1015. $oLog->DBInsertNoReload();
  1016. }
  1017. catch(Exception $e)
  1018. {
  1019. IssueLog::Error("Failed to log issue into the DB");
  1020. }
  1021. }
  1022. IssueLog::Error($e->getMessage());
  1023. }
  1024. // For debugging only
  1025. //throw $e;
  1026. }
  1027. catch(Exception $e)
  1028. {
  1029. require_once(APPROOT.'/setup/setuppage.class.inc.php');
  1030. $oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
  1031. $oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");
  1032. $oP->error(Dict::Format('UI:Error_Details', $e->getMessage()));
  1033. $oP->output();
  1034. if (MetaModel::IsLogEnabledIssue())
  1035. {
  1036. if (MetaModel::IsValidClass('EventIssue'))
  1037. {
  1038. try
  1039. {
  1040. $oLog = new EventIssue();
  1041. $oLog->Set('message', $e->getMessage());
  1042. $oLog->Set('userinfo', '');
  1043. $oLog->Set('issue', 'PHP Exception');
  1044. $oLog->Set('impact', 'Page could not be displayed');
  1045. $oLog->Set('callstack', $e->getTrace());
  1046. $oLog->Set('data', array());
  1047. $oLog->DBInsertNoReload();
  1048. }
  1049. catch(Exception $e)
  1050. {
  1051. IssueLog::Error("Failed to log issue into the DB");
  1052. }
  1053. }
  1054. IssueLog::Error($e->getMessage());
  1055. }
  1056. }
  1057. ?>