PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/includes/class.flyspray.php

https://bitbucket.org/djl/flyspray-mirror
PHP | 1164 lines | 656 code | 124 blank | 384 comment | 124 complexity | 0e2c015b2c45bde79f0e4cb7a86d3d4e MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, BSD-3-Clause, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * Flyspray
  4. *
  5. * Flyspray class
  6. *
  7. * This script contains all the functions we use often in
  8. * Flyspray to do miscellaneous things.
  9. *
  10. * @license http://opensource.org/licenses/lgpl-license.php Lesser GNU Public License
  11. * @package flyspray
  12. * @author Tony Collins
  13. * @author Florian Schmitz
  14. * @author Cristian Rodriguez
  15. */
  16. class Flyspray
  17. {
  18. /**
  19. * Current Flyspray version. Change this for each release. Don't forget!
  20. * @access public
  21. * @var string
  22. */
  23. var $version = '1.0.0 dev';
  24. /**
  25. * Flyspray preferences
  26. * @access public
  27. * @var array
  28. */
  29. var $prefs = array();
  30. /**
  31. * Max. file size for file uploads. 0 = no uploads allowed
  32. * @access public
  33. * @var integer
  34. */
  35. var $max_file_size = 0;
  36. /**
  37. * List of projects the user is allowed to view
  38. * @access public
  39. * @var array
  40. */
  41. var $projects = array();
  42. /**
  43. * List of severities. Loaded in i18n.inc.php
  44. * @access public
  45. * @var array
  46. */
  47. var $severities = array();
  48. /**
  49. * List of all permissions to be used at various places
  50. * @access public
  51. * @var array
  52. */
  53. var $perms = array('manage_project', 'view_private', 'edit_private',
  54. 'view_tasks', 'open_new_tasks', 'modify_own_tasks',
  55. 'modify_all_tasks', 'view_history', 'close_own_tasks', 'close_other_tasks',
  56. 'edit_assignments', 'add_to_assignees', 'assign_to_self', 'assign_others_to_self', 'show_as_assignees',
  57. 'view_comments', 'add_comments', 'edit_own_comments', 'edit_comments', 'delete_comments',
  58. 'create_attachments', 'delete_attachments', 'view_userlist', 'view_reports',
  59. 'add_votes', 'view_svn');
  60. /**
  61. * Sort permissions into "groups". Only for the visuals.
  62. * @access public
  63. * @var array
  64. */
  65. var $permgroups = array( array(0, 2, 'specialperms'), array(3, 9, 'taskperms'),
  66. array(10, 14, 'assignmentperms'), array(15, 19, 'commentperms'),
  67. array(20, 21, 'attachmentperms'), array(22, 25, 'variousperms'));
  68. // Application-wide preferences {{{
  69. /**
  70. * Constructor, starts session, loads settings
  71. * @access private
  72. * @return void
  73. * @version 1.0
  74. */
  75. function Flyspray()
  76. {
  77. global $db;
  78. $this->startSession();
  79. $res = $db->query('SELECT pref_name, pref_value FROM {prefs}');
  80. while ($row = $res->fetchRow()) {
  81. $this->prefs[$row['pref_name']] = $row['pref_value'];
  82. }
  83. $sizes = array();
  84. foreach (array(ini_get('memory_limit'), ini_get('post_max_size'), ini_get('upload_max_filesize')) as $val) {
  85. if (!$val) {
  86. continue;
  87. }
  88. $val = trim($val);
  89. $last = strtolower($val{strlen($val)-1});
  90. switch ($last) {
  91. // The 'G' modifier is available since PHP 5.1.0
  92. case 'g':
  93. $val *= 1024;
  94. case 'm':
  95. $val *= 1024;
  96. case 'k':
  97. $val *= 1024;
  98. }
  99. $sizes[] = $val;
  100. }
  101. clearstatcache();
  102. $func = create_function('$x', 'return @is_file($x . "/index.html") && is_writable($x);');
  103. $this->max_file_size = ((bool) ini_get('file_uploads') && $func(BASEDIR . '/attachments')) ? round((min($sizes)/1024/1024), 1) : 0;
  104. } // }}}
  105. function base_version($version)
  106. {
  107. if (strpos($version, ' ') === false) {
  108. return $version;
  109. }
  110. return substr($version, 0, strpos($version, ' '));
  111. }
  112. function get_config_path($basedir = BASEDIR)
  113. {
  114. $cfile = $basedir . '/flyspray.conf.php';
  115. if (is_readable($hostconfig = sprintf('%s/%s.conf.php', $basedir, $_SERVER['SERVER_NAME']))) {
  116. $cfile = $hostconfig;
  117. }
  118. return $cfile;
  119. }
  120. // {{{ Redirect to $url
  121. /**
  122. * Redirects the browser to the page in $url
  123. * This function is based on PEAR HTTP class
  124. * @param string $url
  125. * @param bool $exit
  126. * @param bool $rfc2616
  127. * @license BSD
  128. * @access public static
  129. * @return bool
  130. * @version 1.0
  131. */
  132. function Redirect($url, $exit = true, $rfc2616 = true)
  133. {
  134. @ob_clean();
  135. if (isset($_SESSION) && count($_SESSION)) {
  136. session_write_close();
  137. }
  138. if (headers_sent()) {
  139. die('Headers are already sent, this should not have happened. Please inform Flyspray developers.');
  140. }
  141. $url = FlySpray::absoluteURI($url);
  142. header('Location: '. $url);
  143. if ($rfc2616 && isset($_SERVER['REQUEST_METHOD']) &&
  144. $_SERVER['REQUEST_METHOD'] != 'HEAD') {
  145. $url = Filters::noXSS($url);
  146. printf('%s to: <a href="%s">%s</a>.', eL('Redirect'), $url, $url);
  147. }
  148. if ($exit) {
  149. exit;
  150. }
  151. return true;
  152. } // }}}
  153. /**
  154. * Absolute URI (This function is part of PEAR::HTTP licensed under the BSD) {{{
  155. *
  156. * This function returns the absolute URI for the partial URL passed.
  157. * The current scheme (HTTP/HTTPS), host server, port, current script
  158. * location are used if necessary to resolve any relative URLs.
  159. *
  160. * Offsets potentially created by PATH_INFO are taken care of to resolve
  161. * relative URLs to the current script.
  162. *
  163. * You can choose a new protocol while resolving the URI. This is
  164. * particularly useful when redirecting a web browser using relative URIs
  165. * and to switch from HTTP to HTTPS, or vice-versa, at the same time.
  166. *
  167. * @author Philippe Jausions <Philippe.Jausions@11abacus.com>
  168. * @static
  169. * @access public
  170. * @return string The absolute URI.
  171. * @param string $url Absolute or relative URI the redirect should go to.
  172. * @param string $protocol Protocol to use when redirecting URIs.
  173. * @param integer $port A new port number.
  174. */
  175. function absoluteURI($url = null, $protocol = null, $port = null)
  176. {
  177. // filter CR/LF
  178. $url = str_replace(array("\r", "\n"), ' ', $url);
  179. // Mess around with already absolute URIs
  180. if (preg_match('!^([a-z0-9]+)://!i', $url)) {
  181. if (empty($protocol) && empty($port)) {
  182. return $url;
  183. }
  184. if (!empty($protocol)) {
  185. $url = $protocol .':'. end($array = explode(':', $url, 2));
  186. }
  187. if (!empty($port)) {
  188. $url = preg_replace('!^(([a-z0-9]+)://[^/:]+)(:[\d]+)?!i',
  189. '\1:'. $port, $url);
  190. }
  191. return $url;
  192. }
  193. $host = 'localhost';
  194. if (!empty($_SERVER['HTTP_HOST'])) {
  195. list($host) = explode(':', $_SERVER['HTTP_HOST']);
  196. } elseif (!empty($_SERVER['SERVER_NAME'])) {
  197. list($host) = explode(':', $_SERVER['SERVER_NAME']);
  198. }
  199. if (empty($protocol)) {
  200. if (isset($_SERVER['HTTPS']) && !strcasecmp($_SERVER['HTTPS'], 'on')) {
  201. $protocol = 'https';
  202. } else {
  203. $protocol = 'http';
  204. }
  205. if (!isset($port) || $port != intval($port)) {
  206. $port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
  207. }
  208. }
  209. if ($protocol == 'http' && $port == 80) {
  210. unset($port);
  211. }
  212. if ($protocol == 'https' && $port == 443) {
  213. unset($port);
  214. }
  215. $server = $protocol .'://'. $host . (isset($port) ? ':'. $port : '');
  216. if (!strlen($url) || $url{0} == '?' || $url{0} == '#') {
  217. $uri = isset($_SERVER['REQUEST_URI']) ?
  218. $_SERVER['REQUEST_URI'] : $_SERVER['PHP_SELF'];
  219. if ($url && $url{0} == '?' && false !== ($q = strpos($uri, '?'))) {
  220. $url = substr($uri, 0, $q) . $url;
  221. } else {
  222. $url = $uri . $url;
  223. }
  224. }
  225. if ($url{0} == '/') {
  226. return $server . $url;
  227. }
  228. // Check for PATH_INFO
  229. if (isset($_SERVER['PATH_INFO']) && strlen($_SERVER['PATH_INFO']) &&
  230. $_SERVER['PHP_SELF'] != $_SERVER['PATH_INFO']) {
  231. $path = dirname(substr($_SERVER['PHP_SELF'], 0, -strlen($_SERVER['PATH_INFO'])));
  232. } else {
  233. $path = dirname($_SERVER['PHP_SELF']);
  234. }
  235. if (substr($path = strtr($path, '\\', '/'), -1) != '/') {
  236. $path .= '/';
  237. }
  238. return $server . $path . $url;
  239. } // }}}
  240. // Duplicate submission check {{{
  241. /**
  242. * Test to see if user resubmitted a form.
  243. * Checks only newtask and addcomment actions.
  244. * @return bool true if user has submitted the same action within less than 6 hours, false otherwise
  245. * @access public static
  246. * @version 1.0
  247. */
  248. function requestDuplicated()
  249. {
  250. // garbage collection -- clean entries older than 6 hrs
  251. $now = isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time();
  252. if (!empty($_SESSION['requests_hash'])) {
  253. foreach ($_SESSION['requests_hash'] as $key => $val) {
  254. if ($val < $now-6*60*60) {
  255. unset($_SESSION['requests_hash'][$key]);
  256. }
  257. }
  258. }
  259. if (count($_POST)) {
  260. if (preg_match('/^newtask|addcomment$/', Post::val('action', '')))
  261. {
  262. $currentrequest = md5(serialize($_POST));
  263. if (!empty($_SESSION['requests_hash'][$currentrequest])) {
  264. return true;
  265. }
  266. $_SESSION['requests_hash'][$currentrequest] = time();
  267. }
  268. }
  269. return false;
  270. } // }}}
  271. /**
  272. * Gets the global ID of a task
  273. * @param string $task_id eg. FS#123, PR#12, 543 = FS#123, ...
  274. * @access public static
  275. * @return integer 0 on failure
  276. */
  277. function GetTaskId($task_id)
  278. {
  279. global $db;
  280. if (is_numeric($task_id)) {
  281. // can only be global
  282. return $task_id;
  283. }
  284. @list($prefix, $task) = explode( (strpos($task_id, '#') !== false) ? '#' : ' ', $task_id);
  285. if (!$task) {
  286. // certainly no existing task
  287. return 0;
  288. }
  289. if ($prefix == 'FS' || trim($prefix) == 'bug') {
  290. // global as well
  291. return $task;
  292. }
  293. // now try to get the global ID based on project level id
  294. return (int) $db->x->GetOne('SELECT task_id
  295. FROM {tasks} t
  296. LEFT JOIN {projects} p ON t.project_id = p.project_id
  297. WHERE prefix_id = ? AND project_prefix = ?',
  298. null, array($task, $prefix));
  299. }
  300. // Retrieve task details {{{
  301. /**
  302. * Gets all information about a task (and caches information if wanted)
  303. * @param integer $task_id
  304. * @param bool $cache_enabled
  305. * @access public static
  306. * @return mixed an array with all taskdetails or false on failure
  307. */
  308. function GetTaskDetails($task_id, $cache_enabled = false, $prefix = null)
  309. {
  310. global $db, $fs, $proj;
  311. static $cache = array();
  312. $task_id = intval($task_id);
  313. if (isset($cache[$task_id . (string) $prefix]) && $cache_enabled) {
  314. return $cache[$task_id . (string) $prefix];
  315. }
  316. if (!is_null($prefix) && $prefix != 'FS' && trim($prefix) != 'bug') {
  317. $where = 't.prefix_id = ? AND project_prefix = ?';
  318. $params = array($task_id, trim($prefix));
  319. } else {
  320. $where = 't.task_id = ?';
  321. $params = array($task_id);
  322. }
  323. $task = $db->x->getRow('SELECT t.*, p.project_prefix, p.project_title,
  324. r.item_name AS resolution_name,
  325. uo.real_name AS opened_by_name,
  326. ue.real_name AS last_edited_by_name,
  327. uc.real_name AS closed_by_name
  328. FROM {tasks} t
  329. LEFT JOIN {list_items} r ON t.resolution_reason = r.list_item_id
  330. LEFT JOIN {users} uo ON t.opened_by = uo.user_id
  331. LEFT JOIN {users} ue ON t.last_edited_by = ue.user_id
  332. LEFT JOIN {users} uc ON t.closed_by = uc.user_id
  333. LEFT JOIN {projects} p ON t.project_id = p.project_id
  334. WHERE ' . $where, null, $params);
  335. if (!$task) {
  336. return false;
  337. }
  338. // Now add custom fields
  339. $sql = $db->x->getAll('SELECT field_value, field_name, f.field_id, li.item_name, lc.category_name, user_name
  340. FROM {field_values} fv
  341. LEFT JOIN {fields} f ON f.field_id = fv.field_id
  342. LEFT JOIN {list_items} li ON (f.list_id = li.list_id AND field_value = li.list_item_id)
  343. LEFT JOIN {list_category} lc ON (f.list_id = lc.list_id AND field_value = lc.category_id)
  344. LEFT JOIN {users} u ON (field_type = ? AND field_value = u.user_id)
  345. WHERE task_id = ?', null, array(FIELD_USER, $task['task_id']));
  346. foreach ($sql as $row) {
  347. $task['field' . $row['field_id']] = $row['field_value'];
  348. $task['field' . $row['field_id'] . '_name'] = ($row['user_name'] ? $row['user_name'] : ($row['item_name'] ? $row['item_name'] : $row['category_name']));
  349. }
  350. $task['assigned_to'] = $task['assigned_to_name'] = $task['assigned_to_uname'] = array();
  351. if ($assignees = Flyspray::GetAssignees($task_id, true)) {
  352. $task['assigned_to'] = $assignees[0];
  353. $task['assigned_to_name'] = $assignees[1];
  354. $task['assigned_to_uname'] = $assignees[2];
  355. }
  356. $cache[$task_id . (string) $prefix] = $task;
  357. return $task;
  358. } // }}}
  359. // List projects {{{
  360. /**
  361. * Returns a list of all projects
  362. * @access public static
  363. * @return array
  364. * @version 1.0
  365. */
  366. function listProjects()
  367. {
  368. global $db;
  369. return $db->x->getAll('SELECT project_id, project_title FROM {projects}');
  370. } // }}}
  371. // List themes {{{
  372. /**
  373. * Returns a list of all themes
  374. * @access public static
  375. * @return array
  376. * @version 1.0
  377. */
  378. function listThemes()
  379. {
  380. if ($handle = opendir(BASEDIR . '/themes/')) {
  381. $theme_array = array();
  382. while (false !== ($file = readdir($handle))) {
  383. if ($file != '.' && $file != '..' && is_file(BASEDIR . "/themes/$file/theme.css")) {
  384. $theme_array[] = $file;
  385. }
  386. }
  387. closedir($handle);
  388. }
  389. sort($theme_array);
  390. return $theme_array;
  391. } // }}}
  392. // List a project's group {{{
  393. /**
  394. * Returns a list of a project's groups
  395. * @param integer $proj_id
  396. * @access public static
  397. * @return array
  398. * @version 1.0
  399. */
  400. function listGroups($proj_id = 0)
  401. {
  402. global $db;
  403. return $db->x->getAll('SELECT *
  404. FROM {groups}
  405. WHERE project_id = ?
  406. ORDER BY group_id ASC', null, array($proj_id));
  407. }
  408. /**
  409. * Returns a list of all groups, sorted by project
  410. * @param integer $user_id restrict to groups the user is member of
  411. * @access public static
  412. * @return array
  413. * @version 1.0
  414. */
  415. function listallGroups($user_id = null)
  416. {
  417. global $db, $fs;
  418. $group_list = array(L('global') => null);
  419. $params = array();
  420. $query = 'SELECT g.group_id, group_name, group_desc, g.project_id, project_title
  421. FROM {groups} g
  422. LEFT JOIN {projects} p ON p.project_id = g.project_id';
  423. // Limit to groups a specific user is in
  424. if (!is_null($user_id)) {
  425. $query .= ' LEFT JOIN {users_in_groups} uig ON uig.group_id = g.group_id
  426. WHERE uig.user_id = ? ';
  427. $params[] = $user_id;
  428. }
  429. $sql = $db->getAll($query, null, $params);
  430. foreach ($sql as $row) {
  431. // make sure that the user only sees projects he is allowed to
  432. if ($row['project_id'] != '0' && Flyspray::array_find('project_id', $row['project_id'], $fs->projects) === false) {
  433. continue;
  434. }
  435. $group_list[$row['project_title']][] = $row;
  436. }
  437. $group_list[L('global')] = $group_list[''];
  438. unset($group_list['']);
  439. return $group_list;
  440. }
  441. // }}}
  442. // List languages {{{
  443. /**
  444. * Returns a list of installed languages
  445. * @access public static
  446. * @return array
  447. * @version 1.0
  448. */
  449. function listLangs()
  450. {
  451. return str_replace('.php', '', array_map('basename', glob_compat(BASEDIR ."/lang/[a-zA-Z]*.php")));
  452. } // }}}
  453. // Log events to the history table {{{
  454. /**
  455. * Saves an event to the database
  456. * @param integer $task_id
  457. * @param integer $type
  458. * @param string $newvalue
  459. * @param string $oldvalue
  460. * @param string $field
  461. * @param integer $time for synchronisation with other functions
  462. * @access public static
  463. * @return void
  464. * @version 1.0
  465. */
  466. function logEvent($task_id, $type, $newvalue = '', $oldvalue = '', $field = '', $time = null)
  467. {
  468. global $db, $user;
  469. // This function creates entries in the history table. These are the event types:
  470. // 0: Fields changed in a task
  471. // 1: New task created
  472. // 2: Task closed
  473. // 3: Task edited (for backwards compatibility with events prior to the history system)
  474. // 4: Comment added
  475. // 5: Comment edited
  476. // 6: Comment deleted
  477. // 7: Attachment added
  478. // 8: Attachment deleted
  479. // 9: User added to notification list
  480. // 10: User removed from notification list
  481. // 11: Related task added to this task
  482. // 12: Related task removed from this task
  483. // 13: Task re-opened
  484. // 14: Task assigned to user / re-assigned to different user / Unassigned
  485. // 15: This task was added to another task's related list
  486. // 16: This task was removed from another task's related list
  487. // 17: Reminder added
  488. // 18: Reminder deleted
  489. // 19: User took ownership
  490. // 20: Closure request made
  491. // 21: Re-opening request made
  492. // 22: Adding a new dependency
  493. // 23: This task added as a dependency of another task
  494. // 24: Removing a dependency
  495. // 25: This task removed from another task's dependency list
  496. // 26: Task was made private
  497. // 27: Task was made public
  498. // 28: PM request denied
  499. // 29: User added to the list of assignees
  500. // 30: New user registration
  501. // 31: User deletion
  502. $query_params = array('task_id'=> intval($task_id),
  503. 'user_id'=> intval($user->id),
  504. 'event_date'=> ((!is_numeric($time)) ? time() : $time),
  505. 'event_type'=> $type,
  506. 'field_changed'=> $field,
  507. 'old_value'=> (string) $oldvalue,
  508. 'new_value'=> $newvalue);
  509. if (!Pear::isError($db->x->autoExecute('{history}', $query_params))) {
  510. return true;
  511. }
  512. return false;
  513. } // }}}
  514. // Log a request for an admin/project manager to do something {{{
  515. /**
  516. * Adds an admin request to the database
  517. * @param integer $type 1: Task close, 2: Task re-open
  518. * @param integer $project_id
  519. * @param integer $task_id
  520. * @param integer $submitter
  521. * @param string $reason
  522. * @access public static
  523. * @return void
  524. * @version 1.0
  525. */
  526. function AdminRequest($type, $project_id, $task_id, $submitter, $reason)
  527. {
  528. global $db;
  529. $db->x->autoExecute('{admin_requests}', array('project_id'=> $project_id,
  530. 'task_id'=> $task_id,
  531. 'submitted_by'=> $submitter,
  532. 'request_type'=> $type,
  533. 'reason_given'=> $reason,
  534. 'time_submitted'=> time()));
  535. } // }}}
  536. // Check for an existing admin request for a task and event type {{{;
  537. /**
  538. * Checks whether or not there is an admin request for a task
  539. * @param integer $type 1: Task close, 2: Task re-open
  540. * @param integer $task_id
  541. * @access public static
  542. * @return bool
  543. * @version 1.0
  544. */
  545. function AdminRequestCheck($type, $task_id)
  546. {
  547. global $db;
  548. $check = $db->x->getOne('SELECT request_id
  549. FROM {admin_requests}
  550. WHERE request_type = ? AND task_id = ? AND resolved_by = 0',
  551. null, array($type, $task_id));
  552. return (bool) $check;
  553. } // }}}
  554. // Get the current user's details {{{
  555. /**
  556. * Gets all user details of a user
  557. * @param integer $user_id
  558. * @access public static
  559. * @return array
  560. * @version 1.0
  561. */
  562. function getUserDetails($user_id)
  563. {
  564. global $db;
  565. return $db->x->getRow('SELECT * FROM {users} WHERE user_id = ?', null, intval($user_id));
  566. } // }}}
  567. // Get group details {{{
  568. /**
  569. * Gets all information about a group
  570. * @param integer $group_id
  571. * @access public static
  572. * @return array
  573. * @version 1.0
  574. */
  575. function getGroupDetails($group_id)
  576. {
  577. global $db;
  578. return $db->x->getRow('SELECT *, count(uig.user_id) AS num_users
  579. FROM {groups} g
  580. LEFT JOIN {users_in_groups} uig ON uig.group_id = g.group_id
  581. WHERE g.group_id = ?
  582. GROUP BY g.group_id',
  583. null, $group_id);
  584. } // }}}
  585. // {{{
  586. /**
  587. * Crypt a password with md5
  588. * @param string $password
  589. * @access public static
  590. * @return string
  591. * @version 1.0
  592. */
  593. function cryptPassword($password, $salt = null)
  594. {
  595. return is_null($salt) ? md5($password) : hash_hmac('md5', $password, $salt);
  596. } // }}}
  597. // Set cookie {{{
  598. /**
  599. * Sets a cookie, automatically setting the URL
  600. * @param string $name
  601. * @param string $val
  602. * @param integer $time
  603. * @access public static
  604. * @return bool
  605. * @version 1.0
  606. */
  607. function setCookie($name, $val, $time = null)
  608. {
  609. $url = parse_url($GLOBALS['baseurl']);
  610. if (!is_int($time)) {
  611. $time = time()+60*60*24*30;
  612. }
  613. if ((strlen($name) + strlen($val)) > 4096) {
  614. //violation of the protocol
  615. trigger_error("Flyspray sent a too big cookie, browsers will not handle it");
  616. return false;
  617. }
  618. return setcookie($name, $val, $time, $url['path']);
  619. } // }}}
  620. // Reminder daemon {{{
  621. /**
  622. * Starts the reminder daemon
  623. * @access public static
  624. * @return void
  625. * @version 1.0
  626. */
  627. function startReminderDaemon()
  628. {
  629. global $baseurl;
  630. $runfile = Flyspray::get_tmp_dir() . '/flysprayreminders.run';
  631. $timeout = 600;
  632. if (!is_file($runfile) or filemtime($runfile) < time() - ($timeout * 2)) {
  633. $include = 'schedule.php';
  634. /* "localhost" is on **purpose** not a mistake Â?Â?
  635. * almost any server accepts requests to itself in localhost ;)
  636. * firewalls will not block it.
  637. * the "Host" http header will tell the webserver where flyspray is running.
  638. */
  639. Flyspray::remote_request($baseurl . $include, !GET_CONTENTS, $_SERVER['SERVER_PORT'], 'localhost', $_SERVER['HTTP_HOST']);
  640. }
  641. }
  642. // Start the session {{{
  643. /**
  644. * Starts the session
  645. * @access public static
  646. * @return void
  647. * @version 1.0
  648. * @notes smile intented
  649. */
  650. function startSession()
  651. {
  652. if (isset($_SESSION['SESSNAME'])) {
  653. return;
  654. }
  655. $names = array( 'GetFirefox',
  656. 'UseLinux',
  657. 'NoMicrosoft',
  658. 'ThinkB4Replying',
  659. 'FreeSoftware',
  660. 'ReadTheFAQ',
  661. 'RTFM',
  662. 'VisitAU',
  663. 'SubliminalAdvertising',
  664. );
  665. foreach ($names as $val)
  666. {
  667. session_name($val);
  668. session_start();
  669. if (isset($_SESSION['SESSNAME']))
  670. {
  671. $sessname = $_SESSION['SESSNAME'];
  672. break;
  673. }
  674. $_SESSION = array();
  675. session_destroy();
  676. setcookie(session_name(), '', time()-60, '/');
  677. }
  678. if (empty($sessname))
  679. {
  680. $rand_key = array_rand($names);
  681. $sessname = $names[$rand_key];
  682. session_name($sessname);
  683. session_start();
  684. $_SESSION['SESSNAME'] = $sessname;
  685. }
  686. } // }}}
  687. // Compare tasks {{{
  688. /**
  689. * Compares two tasks and returns an array of differences
  690. * @param array $old
  691. * @param array $new
  692. * @access public static
  693. * @return array array('field', 'old', 'new')
  694. * @version 1.0
  695. */
  696. function compare_tasks($old, $new)
  697. {
  698. global $proj;
  699. $comp = array('assigned_to_name', 'percent_complete',
  700. 'item_summary', 'detailed_desc', 'mark_private');
  701. $translation = array('assigned_to_name' => L('assignedto'),
  702. 'percent_complete' => L('percentcomplete'),
  703. 'mark_private' => L('visibility'),
  704. 'item_summary' => L('summary'),
  705. 'detailed_desc' => L('taskedited'));
  706. $changes = array();
  707. foreach ($comp as $db => $key)
  708. {
  709. $replace_new = $new[$key];
  710. $replace_old = $old[$key];
  711. if ($old[$key] != $new[$key]) {
  712. switch ($key)
  713. {
  714. case 'percent_complete':
  715. $replace_new = $new[$key] . '%';
  716. $replace_old = $old[$key] . '%';
  717. break;
  718. case 'mark_private':
  719. $replace_new = $new[$key] ? L('private') : L('public');
  720. $replace_old = $old[$key] ? L('private') : L('public');
  721. break;
  722. }
  723. $raw = (is_numeric($db)) ? $key : $db;
  724. $changes[] = array($key, $replace_old, $replace_new, $translation[$key], $raw, $old[$raw], $new[$raw]);
  725. }
  726. }
  727. foreach ($proj->fields as $field) {
  728. $key = 'field'. $field->id;
  729. $old[$key] = isset($old[$key]) ? $old[$key] : '';
  730. $new[$key] = isset($new[$key]) ? $new[$key] : '';
  731. if ($old[$key] != $new[$key]) {
  732. if ($field->prefs['field_type'] == FIELD_DATE) {
  733. $changes[] = array($key, formatDate($old[$key]), formatDate($new[$key]), $field->prefs['field_name'], $field->id, $old[$key], $new[$key]);
  734. } else {
  735. $old[$key . '_name'] = isset($old[$key . '_name']) ? $old[$key . '_name'] : $old[$key];
  736. $new[$key . '_name'] = isset($new[$key . '_name']) ? $new[$key . '_name'] : $new[$key];
  737. $changes[] = array($key, $old[$key . '_name'], $new[$key . '_name'], $field->prefs['field_name'], $field->id, $old[$key], $new[$key]);
  738. }
  739. }
  740. }
  741. return $changes;
  742. } // }}}
  743. // {{{
  744. /**
  745. * Get a list of assignees for a task
  746. * @param integer $task_id
  747. * @param bool $name whether or not names of the assignees should be returned as well
  748. * @access public static
  749. * @return array
  750. * @version 1.0
  751. */
  752. function GetAssignees($task_id, $names = false)
  753. {
  754. global $db;
  755. $sql = $db->x->getAll('SELECT u.real_name, u.user_id, u.user_name
  756. FROM {users} u, {assigned} a
  757. WHERE task_id = ? AND u.user_id = a.user_id',
  758. null, $task_id);
  759. $assignees = array();
  760. foreach ($sql as $row) {
  761. if ($names) {
  762. $assignees[0][] = $row['user_id'];
  763. $assignees[1][] = $row['real_name'];
  764. $assignees[2][] = $row['user_name'];
  765. } else {
  766. $assignees[] = $row['user_id'];
  767. }
  768. }
  769. return $assignees;
  770. } /// }}}
  771. /**
  772. * Checks if a function is disabled
  773. * @param string $func_name
  774. * @access public static
  775. * @return bool
  776. * @version 1.0
  777. */
  778. function function_disabled($func_name)
  779. {
  780. $disabled_functions = explode(',', ini_get('disable_functions'));
  781. return in_array($func_name, $disabled_functions);
  782. }
  783. /**
  784. * Returns the key number of an array which contains an array like array($key => $value)
  785. * For use with SQL result arrays
  786. * @param string $key
  787. * @param string $value
  788. * @param array $array
  789. * @access public static
  790. * @return mixed false if not found
  791. * @version 1.0
  792. */
  793. function array_find($key, $value, $array)
  794. {
  795. foreach ($array as $num => $part) {
  796. if (isset($part[$key]) && $part[$key] == $value) {
  797. return $num;
  798. }
  799. }
  800. return false;
  801. }
  802. /**
  803. * Returns the user ID if valid, 0 otherwise
  804. * @param int $id
  805. * @access public static
  806. * @return integer 0 if the user does not exist
  807. * @version 1.0
  808. */
  809. function ValidUserId($id)
  810. {
  811. global $db;
  812. $val = $db->x->GetOne('SELECT user_id FROM {users} WHERE user_id = ?', null, intval($id));
  813. return intval($val);
  814. }
  815. /**
  816. * Tries to determine a user ID from a user name. If the
  817. * user name does not exist, it assumes an user ID as input.
  818. * @param mixed $user (string or int)
  819. * @access public static
  820. * @return integer 0 if the user does not exist
  821. * @version 1.0
  822. */
  823. function UserNameOrId($user)
  824. {
  825. $val = Flyspray::UserNameToId($user);
  826. return ($val) ? $val : Flyspray::ValidUserId($user);
  827. }
  828. /**
  829. * Returns the ID of a user with $name
  830. * @param string $name
  831. * @access public static
  832. * @return integer 0 if the user does not exist
  833. * @version 1.0
  834. */
  835. function UserNameToId($name)
  836. {
  837. global $db;
  838. $val = $db->x->GetOne('SELECT user_id FROM {users} WHERE user_name = ?', null, trim($name));
  839. return intval($val);
  840. }
  841. /**
  842. * check_email
  843. * checks if an email is valid
  844. * @param string $email
  845. * @access public
  846. * @return bool
  847. */
  848. function check_email($email)
  849. {
  850. include_once 'Validate.php';
  851. return Validate::email($email);
  852. }
  853. /**
  854. * get_tmp_dir
  855. * Based on PEAR System::tmpdir() by Tomas V.V.Cox.
  856. * @access public
  857. * @return void
  858. */
  859. function get_tmp_dir()
  860. {
  861. $return = '';
  862. if (function_exists('sys_get_temp_dir')) {
  863. $return = sys_get_temp_dir();
  864. } elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
  865. if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
  866. $return = $var;
  867. } else
  868. if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
  869. $return = $var;
  870. } else
  871. if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
  872. $return = $var;
  873. } else {
  874. $return = getenv('SystemRoot') . '\temp';
  875. }
  876. } elseif ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
  877. $return = $var;
  878. } else {
  879. $return = '/tmp';
  880. }
  881. // Now, the final check
  882. if (@is_dir($return) && is_writable($return)) {
  883. return rtrim($return, DIRECTORY_SEPARATOR);
  884. // we have a problem at this stage.
  885. } elseif(is_writable(ini_get('upload_tmp_dir'))) {
  886. $return = ini_get('upload_tmp_dir');
  887. } elseif(is_writable(ini_get('session.save_path'))) {
  888. $return = ini_get('session.save_path');
  889. }
  890. return rtrim($return, DIRECTORY_SEPARATOR);
  891. }
  892. /**
  893. * check_mime_type
  894. *
  895. * @param string $fname path to filename
  896. * @access public
  897. * @return string the mime type of the offended file.
  898. * @notes DO NOT use this function for any security related
  899. * task (i.e limiting file uploads by type)
  900. * it wasn't designed for that purpose but to UI related tasks.
  901. */
  902. function check_mime_type($fname) {
  903. $type = '';
  904. if (extension_loaded('fileinfo') && class_exists('finfo')) {
  905. $info = new finfo(FILEINFO_MIME);
  906. $type = $info->file($fname);
  907. } elseif(function_exists('mime_content_type')) {
  908. $type = @mime_content_type($fname);
  909. // I hope we don't have to...
  910. } elseif(!FlySpray::function_disabled('exec') && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN'
  911. && php_uname('s') !== 'SunOS') {
  912. include_once 'class.commandexecution.php';
  913. $file =& new CommandExecution();
  914. $file->setCmd('file');
  915. $file->bi = $fname;
  916. $type = $file->getCmdResult();
  917. }
  918. // if wasn't possible to determine , return empty string so
  919. // we can use the browser reported mime-type (probably fake)
  920. return trim($type);
  921. }
  922. /**
  923. * Works like strtotime, but it considers the user's timezone
  924. * @access public
  925. * @param string $time
  926. * @return integer
  927. */
  928. function strtotime($time)
  929. {
  930. global $user;
  931. $time = strtotime($time);
  932. if (!$user->isAnon()) {
  933. $st = date('Z'); // server timezone offset
  934. // Example: User is UTC+3, Server UTC-2.
  935. // User enters 7:00. For the server it must be converted to 2:00 (done below)
  936. $time += ($st - $user->infos['time_zone']);
  937. // later it adds 5 hours to 2:00 for the user when the date is displayed.
  938. }
  939. //strtotime() may return false, making this method to return bool instead of int.
  940. return $time ? $time : 0;
  941. }
  942. /**
  943. * file_get_contents replacement for remote files
  944. * @access public
  945. * @param string $url
  946. * @param bool $get_contents whether or not to return file contents, use GET_CONTENTS for true
  947. * @param integer $port
  948. * @param string $connect manually choose server for connection
  949. * @return string an empty string is not necessarily a failure
  950. */
  951. function remote_request($url, $get_contents = false, $port = 80, $connect = '', $host = null)
  952. {
  953. $url = parse_url($url);
  954. if (!$connect) {
  955. $connect = $url['host'];
  956. }
  957. if ($host) {
  958. $url['host'] = $host;
  959. }
  960. $data = '';
  961. if ($conn = @fsockopen($connect, $port, $errno, $errstr, 10)) {
  962. $out = "GET {$url['path']} HTTP/1.0\r\n";
  963. $out .= "Host: {$url['host']}\r\n\r\n";
  964. $out .= "Connection: Close\r\n\r\n";
  965. stream_set_timeout($conn, 5);
  966. fwrite($conn, $out);
  967. if ($get_contents) {
  968. while (!feof($conn)) {
  969. $data .= fgets($conn, 128);
  970. }
  971. $pos = strpos($data, "\r\n\r\n");
  972. if ($pos !== false) {
  973. //strip the http headers.
  974. $data = substr($data, $pos + 2 * strlen("\r\n"));
  975. }
  976. }
  977. fclose($conn);
  978. }
  979. return $data;
  980. }
  981. /**
  982. * Returns an array containing all notification options the user is
  983. * allowed to use.
  984. * @access public
  985. * @return array
  986. */
  987. function GetNotificationOptions($noneAllowed = true)
  988. {
  989. switch ($this->prefs['user_notify'])
  990. {
  991. case 0:
  992. return array(0 => L('none'));
  993. case 2:
  994. return array(NOTIFY_EMAIL => L('email'));
  995. case 3:
  996. return array(NOTIFY_JABBER => L('jabber'));
  997. }
  998. $return = array(0 => L('none'),
  999. NOTIFY_EMAIL => L('email'),
  1000. NOTIFY_JABBER => L('jabber'),
  1001. NOTIFY_BOTH => L('both'));
  1002. if (!$noneAllowed) {
  1003. unset($return[0]);
  1004. }
  1005. return $return;
  1006. }
  1007. /**
  1008. * getSvnRev
  1009. * For internal use
  1010. * @access public
  1011. * @return string
  1012. */
  1013. function getSvnRev()
  1014. {
  1015. if(is_file(BASEDIR. '/REVISION') && is_dir(BASEDIR . '/.svn')) {
  1016. return sprintf('r%d',file_get_contents(BASEDIR .'/REVISION'));
  1017. }
  1018. return '';
  1019. }
  1020. function GetColorCssClass($task, $alternative = null)
  1021. {
  1022. $field = 'field' . $this->prefs['color_field'];
  1023. if (!isset($task[$field])) {
  1024. if (is_array($alternative) && isset($alternative[$field])) {
  1025. $task = $alternative;
  1026. } else {
  1027. return '';
  1028. }
  1029. }
  1030. return 'colorfield' . $task[$field];
  1031. }
  1032. }
  1033. ?>