PageRenderTime 59ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/WorkItem.class.php

https://github.com/BrianPrz/worklist
PHP | 1420 lines | 1092 code | 186 blank | 142 comment | 216 complexity | 5c5a9c34286c3b49903c3491484162e8 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Workitem
  4. *
  5. * @package Workitem
  6. */
  7. class WorkItem {
  8. protected $id;
  9. protected $summary;
  10. protected $creatorId;
  11. protected $creator;
  12. protected $runnerId;
  13. protected $runner;
  14. protected $mechanicId;
  15. protected $mechanic;
  16. protected $status;
  17. protected $notes;
  18. protected $sandbox;
  19. protected $project_id;
  20. protected $project_name;
  21. protected $bug_job_id;
  22. protected $is_bug;
  23. protected $code_reviewer_id;
  24. protected $code_review_started;
  25. protected $code_review_completed;
  26. protected $budget_id;
  27. protected $is_internal;
  28. var $status_changed;
  29. var $skills = array();
  30. protected $origStatus = null;
  31. public function __construct($id = null)
  32. {
  33. if (!mysql_connect(DB_SERVER, DB_USER, DB_PASSWORD)) {
  34. throw new Exception('Error: ' . mysql_error());
  35. }
  36. if (!mysql_select_db(DB_NAME)) {
  37. throw new Exception('Error: ' . mysql_error());
  38. }
  39. if ($id !== null) {
  40. $this->load($id);
  41. }
  42. }
  43. static public function getById($id)
  44. {
  45. $workitem = new WorkItem();
  46. $workitem->loadById($id);
  47. return $workitem;
  48. }
  49. public function loadById($id)
  50. {
  51. return $this->load($id);
  52. }
  53. protected function load($id = null)
  54. {
  55. if ($id === null && !$this->id) {
  56. throw new Exception('Missing workitem id.');
  57. } elseif ($id === null) {
  58. $id = $this->id;
  59. }
  60. $query = "
  61. SELECT
  62. w.id,
  63. w.summary,
  64. w.creator_id,
  65. w.runner_id,
  66. w.mechanic_id,
  67. w.status,
  68. w.project_id,
  69. w.notes,
  70. w.sandbox,
  71. w.bug_job_id,
  72. w.is_bug,
  73. w.is_internal,
  74. w.code_reviewer_id,
  75. w.code_review_started,
  76. w.code_review_completed,
  77. w.status_changed,
  78. w.budget_id,
  79. p.name AS project_name
  80. FROM ".WORKLIST. " as w
  81. LEFT JOIN ".PROJECTS." AS p ON w.project_id = p.project_id
  82. WHERE w.id = '" . (int)$id . "'";
  83. $res = mysql_query($query);
  84. if (!$res) {
  85. throw new Exception('MySQL error.');
  86. }
  87. $row = mysql_fetch_assoc($res);
  88. if (!$row) {
  89. throw new Exception('Invalid workitem id.');
  90. }
  91. $this->setId($row['id'])
  92. ->setSummary($row['summary'])
  93. ->setCreatorId($row['creator_id'])
  94. ->setRunnerId($row['runner_id'])
  95. ->setMechanicId($row['mechanic_id'])
  96. ->setStatus($row['status'])
  97. ->setProjectId($row['project_id'])
  98. ->setNotes($row['notes'])
  99. ->setSandbox($row['sandbox'])
  100. ->setBugJobId($row['bug_job_id'])
  101. ->setIs_internal($row['is_internal'])
  102. ->setIs_bug($row['is_bug'])
  103. ->setBudget_id($row['budget_id'])
  104. ->setCReviewerId($row['code_reviewer_id'] == "" ? 0 : $row['code_reviewer_id'])
  105. ->setCRStarted($row['code_review_started'])
  106. ->setCRCompleted($row['code_review_completed'])
  107. ->setWorkitemSkills();
  108. $this->status_changed = $row['status_changed'];
  109. $this->project_name = $row['project_name'];
  110. return true;
  111. }
  112. public function idExists($id)
  113. {
  114. $query = '
  115. SELECT COUNT(*)
  116. FROM `' . WORKLIST . '`
  117. WHERE `id` = ' . (int) $id;
  118. $res = mysql_query($query);
  119. if (!$res) {
  120. throw new Exception('MySQL error.');
  121. }
  122. $row = mysql_fetch_row($res);
  123. return (boolean) $row[0];
  124. }
  125. public function setId($id)
  126. {
  127. $this->id = (int)$id;
  128. return $this;
  129. }
  130. public function getId()
  131. {
  132. return $this->id;
  133. }
  134. public function setSummary($summary)
  135. {
  136. $this->summary = $summary;
  137. return $this;
  138. }
  139. public function getSummary()
  140. {
  141. return $this->summary;
  142. }
  143. public function setBugJobId($id) {
  144. $this->bugJobId = intval($id);
  145. return $this;
  146. }
  147. public function getBugJobId() {
  148. return $this->bugJobId;
  149. }
  150. public function setCreatorId($creatorId)
  151. {
  152. $this->creatorId = (int)$creatorId;
  153. $this->setCreator();
  154. return $this;
  155. }
  156. public function getCreatorId()
  157. {
  158. return $this->creatorId;
  159. }
  160. public function setRunnerId($runnerId)
  161. {
  162. $this->runnerId = (int)$runnerId;
  163. $this->setRunner();
  164. return $this;
  165. }
  166. /**
  167. *
  168. * Get users with fees in work item
  169. *
  170. * @return ARRAY list of users id
  171. */
  172. public function getUsersWithFeesId() {
  173. $query = " SELECT f.`user_id`
  174. FROM `" . FEES . "` f INNER JOIN `" . USERS . "` u ON u.`id` = f.`user_id`
  175. WHERE f.`worklist_id` = " . $this->id . " AND u.`is_active` = 1";
  176. $result_query = mysql_query($query);
  177. if($result_query) {
  178. $temp_array = array();
  179. while($row = mysql_fetch_assoc($result_query)) {
  180. $temp_array[] = $row['user_id'];
  181. }
  182. return $temp_array;
  183. } else {
  184. return null;
  185. }
  186. }
  187. /**
  188. *
  189. * Get users with bids in work item
  190. *
  191. * @return ARRAY list of users id
  192. */
  193. public function getUsersWithBidsId() {
  194. $query = "SELECT b.`bidder_id`
  195. FROM `" . BIDS . "` f INNER JOIN `" . USERS . "` u ON u.`id` = b.`bidder_id`
  196. WHERE b.`worklist_id` = " . $this->id . " AND u.`is_active` = 1";
  197. $result_query = mysql_query($query);
  198. if($result_query) {
  199. $temp_array = array();
  200. while($row = mysql_fetch_assoc($result_query)) {
  201. $temp_array[] = $row['bidder_id'];
  202. }
  203. return $temp_array;
  204. } else {
  205. return null;
  206. }
  207. }
  208. public function getRunnerId()
  209. {
  210. return $this->runnerId;
  211. }
  212. public function setMechanicId($mechanicId)
  213. {
  214. $this->mechanicId = (int)$mechanicId;
  215. $this->setMechanic();
  216. return $this;
  217. }
  218. public function getMechanicId()
  219. {
  220. return $this->mechanicId;
  221. }
  222. protected function setCreator()
  223. {
  224. $user = new User();
  225. $this->creator = $user->findUserById($this->getCreatorId());
  226. return $this;
  227. }
  228. protected function setRunner()
  229. {
  230. $user = new User();
  231. $this->runner = $user->findUserById($this->getRunnerId());
  232. return $this;
  233. }
  234. protected function setMechanic()
  235. {
  236. $user = new User();
  237. $this->mechanic = $user->findUserById($this->getMechanicId());
  238. return $this;
  239. }
  240. public function getCreator()
  241. {
  242. return $this->creator;
  243. }
  244. public function getRunner()
  245. {
  246. return $this->runner;
  247. }
  248. public function getMechanic()
  249. {
  250. return $this->mechanic;
  251. }
  252. public function setStatus($status)
  253. {
  254. $this->status = $status;
  255. return $this;
  256. }
  257. public function resetCRFlags() {
  258. $this->code_reviewer_id = 0;
  259. $this->code_review_started = 0;
  260. $this->code_review_completed = 0;
  261. return $this;
  262. }
  263. public function getStatus()
  264. {
  265. return $this->status;
  266. }
  267. public function setProjectId($project_id)
  268. {
  269. $this->project_id = $project_id;
  270. return $this;
  271. }
  272. public function getProjectId()
  273. {
  274. return $this->project_id;
  275. }
  276. public function setNotes($notes)
  277. {
  278. $this->notes = $notes;
  279. return $this;
  280. }
  281. public function getNotes()
  282. {
  283. return $this->notes;
  284. }
  285. public function setIs_bug($is_bug)
  286. {
  287. $this->is_bug = $is_bug;
  288. return $this;
  289. }
  290. public function getIs_bug()
  291. {
  292. return $this->is_bug;
  293. }
  294. public function setBudget_id($budget_id)
  295. {
  296. $this->budget_id = $budget_id;
  297. return $this;
  298. }
  299. public function getBudget_id()
  300. {
  301. if (!isset($this->budget_id)) {
  302. $this->budget_id = 0;
  303. }
  304. return $this->budget_id;
  305. }
  306. public function setSandbox($sandbox)
  307. {
  308. $this->sandbox = $sandbox;
  309. return $this;
  310. }
  311. public function getSandbox()
  312. {
  313. return $this->sandbox;
  314. }
  315. public function setCReviewerId($code_reviewer_id) {
  316. $this->code_reviewer_id = $code_reviewer_id;
  317. return $this;
  318. }
  319. public function getCReviewerId() {
  320. return $this->code_reviewer_id;
  321. }
  322. public function setCRStarted($cr_status) {
  323. $this->code_review_started = $cr_status;
  324. return $this;
  325. }
  326. public function getCRStarted() {
  327. return $this->code_review_started;
  328. }
  329. public function setCRCompleted($cr_status) {
  330. $this->code_review_completed = $cr_status;
  331. return $this;
  332. }
  333. public function getCRcompleted() {
  334. return $this->code_review_completed;
  335. }
  336. public function setWorkitemSkills($skills = false) {
  337. // if no array provided, get skill from db
  338. if (! $skills) {
  339. $query = "SELECT s.skill
  340. FROM ".SKILLS." AS s, ".WORKITEM_SKILLS." AS ws
  341. WHERE s.id = ws.skill_id AND ws.workitem_id = " . $this->getId();
  342. $result = mysql_query($query);
  343. if (mysql_num_rows($result)) {
  344. while ($row = mysql_fetch_assoc($result)) {
  345. $this->skills[] = $row['skill'];
  346. }
  347. }
  348. } else {
  349. $this->skills = $skills;
  350. }
  351. }
  352. public function saveSkills() {
  353. // clear current skills
  354. if ($this->getId()) {
  355. $query = "DELETE FROM ".WORKITEM_SKILLS." WHERE workitem_id=" . $this->getId();
  356. $result = mysql_query($query);
  357. foreach ($this->skills as $skill) {
  358. $query = "INSERT INTO ".WORKITEM_SKILLS." (workitem_id, skill_id)
  359. SELECT ".$this->getId().", id FROM ".SKILLS." WHERE skill='". trim($skill) ."'";
  360. mysql_query($query) || die('There was an error ' . mysql_error() . ' QUERY: ' . $query);
  361. }
  362. return true;
  363. } else {
  364. return false;
  365. }
  366. }
  367. public function getSkills() {
  368. return $this->skills;
  369. }
  370. /**
  371. * A method to check if this job is internal / for the hifi team.
  372. *
  373. * @return (boolean)
  374. */
  375. public function isInternal()
  376. {
  377. if ((int) $this->getIs_internal() == 1) {
  378. return true;
  379. }
  380. return false;
  381. }
  382. /**
  383. * @return bool $is_internal
  384. */
  385. public function getIs_internal() {
  386. return $this->is_internal;
  387. }
  388. /**
  389. * @param bool $is_internal the $is_internal to set
  390. */
  391. public function setIs_internal($is_internal) {
  392. $this->is_internal = $is_internal;
  393. return $this;
  394. }
  395. /**
  396. * @return bool Whether the operation was successful
  397. */
  398. public function toggleInternal($user_id) {
  399. $user = new User();
  400. $user->findUserById($user_id);
  401. if ($user->isInternal()){
  402. if ($this->isInternal()) {
  403. $this->setIs_internal(false);
  404. } else {
  405. $this->setIs_internal(true);
  406. }
  407. } else {
  408. return false;
  409. }
  410. $this->save();
  411. return true;
  412. }
  413. public static function getStates()
  414. {
  415. $states = array();
  416. $query = 'SELECT DISTINCT `status` FROM `' . WORKLIST . '` WHERE `status` != "Draft" LIMIT 0 , 30';
  417. $result = mysql_query($query);
  418. if ($result) {
  419. while ($row = mysql_fetch_assoc($result)) {
  420. $states[] = $row['status'];
  421. }
  422. }
  423. return $states;
  424. }
  425. public function getRepository() {
  426. $query = "SELECT `repository` FROM `projects` WHERE `project_id` = " . $this->getProjectId();
  427. $rt = mysql_query($query);
  428. if (mysql_num_rows($rt)) {
  429. $row = mysql_fetch_array($rt);
  430. $repository = $row['repository'];
  431. return $repository;
  432. } else {
  433. return false;
  434. }
  435. }
  436. protected function insert()
  437. {
  438. $query = "INSERT INTO ".WORKLIST." (summary, creator_id, runner_id, status,".
  439. "project_id, notes, bug_job_id, created, is_bug, status_changed, is_internal) ".
  440. " VALUES (".
  441. "'".mysql_real_escape_string($this->getSummary())."', ".
  442. "'".mysql_real_escape_string($this->getCreatorId())."', ".
  443. "'".mysql_real_escape_string($this->getRunnerId())."', ".
  444. "'".mysql_real_escape_string($this->getStatus())."', ".
  445. "'".mysql_real_escape_string($this->getProjectId())."', ".
  446. "'".mysql_real_escape_string($this->getNotes())."', ".
  447. "'".intval($this->getBugJobId())."', ".
  448. "NOW(), ".
  449. "'".$this->getIs_bug()."', ".
  450. "NOW(), ".
  451. mysql_real_escape_string($this->getIs_internal()) . ")";
  452. $rt = mysql_query($query);
  453. $this->id = mysql_insert_id();
  454. /* Keep track of status changes including the initial one */
  455. $status = mysql_real_escape_string($this->status);
  456. $query = "INSERT INTO ".STATUS_LOG." (worklist_id, status, user_id, change_date) VALUES (".$this->getId().", '$status', ".$_SESSION['userid'].", NOW())";
  457. mysql_unbuffered_query($query);
  458. if($this->status == 'Bidding') {
  459. $this->tweetNewJob();
  460. }
  461. return $rt ? 1 : 0;
  462. }
  463. protected function update()
  464. {
  465. /* Keep track of status changes */
  466. if ($this->origStatus != $this->status) {
  467. if ($this->status == 'Bidding') {
  468. $this->tweetNewJob();
  469. }
  470. if ($this->status == 'Code Review') {
  471. $this->status = 'Review';
  472. } else {
  473. $status = mysql_real_escape_string($this->status);
  474. }
  475. if(isset($_SESSION['userid']) && !empty($_SESSION['userid'])) {
  476. $user_id = $_SESSION['userid'];
  477. } else {
  478. $user_id = 0 ; // this means auto pass script has changed the status to PASS
  479. }
  480. $query = "INSERT INTO ".STATUS_LOG." (worklist_id, status, user_id, change_date) VALUES (".$this->getId().", '$status', ".$user_id.", NOW())";
  481. mysql_unbuffered_query($query);
  482. }
  483. $query = 'UPDATE '.WORKLIST.' SET
  484. summary= "'. mysql_real_escape_string($this->getSummary()).'",
  485. notes="'.mysql_real_escape_string($this->getNotes()).'",
  486. project_id="'.mysql_real_escape_string($this->getProjectId()).'",
  487. status="' .mysql_real_escape_string($this->getStatus()).'",
  488. status_changed=NOW(),
  489. runner_id="' .intval($this->getRunnerId()). '",
  490. bug_job_id="' .intval($this->getBugJobId()).'",
  491. is_internal=' . (int) $this->getIs_internal() . ',
  492. is_bug='.($this->getIs_bug() ? 1 : 0).',
  493. budget_id='.$this->getBudget_id().',
  494. code_reviewer_id=' . $this->getCReviewerId() . ',
  495. code_review_started='.$this->getCRStarted().',
  496. code_review_completed='.$this->getCRCompleted().',
  497. sandbox ="' .mysql_real_escape_string($this->getSandbox()).'"';
  498. $query .= ' WHERE id='.$this->getId();
  499. $result_query = mysql_query($query);
  500. if($result_query) {
  501. return 1;
  502. }
  503. error_log($query);
  504. return 0;
  505. }
  506. protected function tweetNewJob()
  507. {
  508. /*
  509. if (!defined('TWITTER_OAUTH_SECRET') || TWITTER_OAUTH_SECRET=='' ) {
  510. return false;
  511. }
  512. if (empty($_SERVER['HTTPS']))
  513. {
  514. $prefix = 'http://';
  515. $port = ((int)$_SERVER['SERVER_PORT'] == 80) ? '' : ":{$_SERVER['SERVER_PORT']}";
  516. }
  517. else
  518. {
  519. $prefix = 'https://';
  520. $port = ((int)$_SERVER['SERVER_PORT'] == 443) ? '' : ":{$_SERVER['SERVER_PORT']}";
  521. }
  522. $link = $prefix . $_SERVER['HTTP_HOST'] . $port . '/rw/?' . $this->id;
  523. $summary_max_length = 140-strlen('New job: ')-strlen($link)-1;
  524. $summary = substr(html_entity_decode($this->summary, ENT_QUOTES), 0, $summary_max_length);
  525. $connection = new TwitterOAuth(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET, TWITTER_OAUTH_TOKEN, TWITTER_OAUTH_SECRET);
  526. $content = $connection->get('account/verify_credentials');
  527. $message='New job: ' . $summary . ' ' . $link;
  528. $connection->post('statuses/update', array('status' => $message));
  529. */
  530. }
  531. public function save() {
  532. if(isset($this->id)){
  533. if ($this->idExists($this->getId())) {
  534. if ($this->update()) {
  535. $this->saveSkills($this->skills);
  536. return true;
  537. } else {
  538. error_log("error1 in update, save function");
  539. }
  540. } else {
  541. if ($this->insert()) {
  542. $this->saveSkills($this->skills);
  543. return true;
  544. } else {
  545. error_log("error2 in insert, save function");
  546. }
  547. }
  548. } else {
  549. if ($this->insert()) {
  550. $this->saveSkills($this->skills);
  551. } else {
  552. error_log("error3 in insert, save function");
  553. }
  554. }
  555. return false;
  556. }
  557. /**
  558. * @param int $worklist_id
  559. * @return array|null
  560. */
  561. public function getWorkItem($worklist_id)
  562. {
  563. $query = "SELECT w.id, w.summary,w.creator_id,w.runner_id, w.mechanic_id, ".
  564. " u.nickname AS runner_nickname, u.id AS runner_id,".
  565. " uc.nickname AS creator_nickname, w.status, w.notes, ".
  566. " w.project_id, p.name AS project_name, p.repository AS repository, p.website AS p_website,
  567. w.sandbox, w.bug_job_id, w.is_bug, w.budget_id, b.reason AS budget_reason, b.giver_id AS budget_giver_id
  568. FROM " . WORKLIST . " as w
  569. LEFT JOIN " . USERS . " as uc ON w.creator_id = uc.id
  570. LEFT JOIN " . USERS . " as u ON w.runner_id = u.id
  571. LEFT JOIN " . PROJECTS . " AS p ON w.project_id = p.project_id
  572. LEFT JOIN " . BUDGETS . " AS b ON w.budget_id = b.id
  573. WHERE w.id = '$worklist_id'";
  574. $result_query = mysql_query($query);
  575. $row = $result_query ? mysql_fetch_assoc($result_query) : null;
  576. return !empty($row) ? $row : null;
  577. }
  578. /**
  579. * @param int $worklist_id
  580. * @param bool $expired If true, return expired bids
  581. * @return array|null
  582. */
  583. public function getBids($worklist_id, $expired = true) {
  584. $having = '';
  585. // code is here in case we want to start including expired bids
  586. // default behaviour is to ignore expired bids
  587. // Demenza 22 of August 2011 - Something needs to be done here, to change the behavior of true/false to the if(true/false) show espired bids.
  588. $query = "
  589. SELECT bids.`id`, bids.`bidder_id`, bids.`email`, u.`nickname`, bids.`bid_amount`, bids.`accepted`,
  590. UNIX_TIMESTAMP(bids.`bid_created`) AS `unix_bid_created`,
  591. TIMESTAMPDIFF(SECOND, NOW(), bids.`bid_expires`) AS `expires`,
  592. TIMESTAMPDIFF(SECOND, NOW(), bids.`bid_done`) AS `future_delta`,
  593. bids.`bid_done_in` AS done_in,
  594. DATE_FORMAT(bids.`bid_done`, '%m/%d/%Y') AS `bid_done`,
  595. UNIX_TIMESTAMP(`bid_done`) AS `unix_done_by`,
  596. UNIX_TIMESTAMP(bids.`bid_done`) AS `unix_done_full`,
  597. bids.`notes`,
  598. UNIX_TIMESTAMP(f.`date`) AS `unix_bid_accepted`,
  599. UNIX_TIMESTAMP(NOW()) AS `unix_now`,
  600. bids.`bid_created` AS `bid_created_full`
  601. FROM `".BIDS. "` AS bids
  602. INNER JOIN `".USERS."` AS u ON (u.id = bids.bidder_id)
  603. LEFT JOIN ".FEES." AS f ON (f.bid_id=bids.id)
  604. WHERE bids.worklist_id={$worklist_id}
  605. AND bids.withdrawn = 0
  606. $having
  607. ORDER BY bids.`id` DESC";
  608. $result_query = mysql_query($query);
  609. if ($result_query) {
  610. $temp_array = array();
  611. while ($row = mysql_fetch_assoc($result_query)) {
  612. // skip expired bids if they have not been accepted
  613. // Doesn't skip expired bids anymore - Demenza 22 of August 2011
  614. if (! empty($row['unix_bid_accepted'])) {
  615. $row['expires'] = null;
  616. $temp_array[] = $row;
  617. } else if (empty($row['expires'])) {
  618. // take any bid with bid_expires written as 0000-00-00 00:00:00 -mika - Mar 31 2013
  619. $temp_array[] = $row;
  620. } else if (! empty($row['expires']) && empty($row['unix_bid_accepted'])) {
  621. // skip expired bids that are not accepted;
  622. // Had to change this, because of oddness of this if() statement
  623. // The true/false in the top doesn't work at all, see note at the top.
  624. if ($row['expires'] < 0) {
  625. // the bid has expired, include it only if $expired is true
  626. if ($expired) {
  627. $temp_array[] = $row;
  628. }
  629. } else {
  630. $temp_array[] = $row;
  631. }
  632. } else if (! $expired && ($row['future_delta'] < 0 || $row['expires'] < 0)) {
  633. // do not return this bid, it has expired
  634. } else {
  635. $temp_array[] = $row;
  636. }
  637. }
  638. return $temp_array;
  639. } else {
  640. return null;
  641. }
  642. }
  643. public function getProjectRunnersId() {
  644. // Fix for #15353: Only select active runners 20-SEP-2011 <danS>
  645. $query = " SELECT DISTINCT(w.`runner_id`) as runner
  646. FROM `" . WORKLIST . "` AS w INNER JOIN `" . USERS . "` u ON u.`id` = w.`runner_id`
  647. WHERE w.`project_id` = " . $this->getProjectId() . "
  648. AND u.`is_runner` = 1
  649. AND u.`is_active` = 1";
  650. $result_query = mysql_query($query);
  651. if($result_query) {
  652. $temp_array = array();
  653. while($row = mysql_fetch_assoc($result_query)) {
  654. $temp_array[] = $row['runner'];
  655. }
  656. return $temp_array;
  657. } else {
  658. return null;
  659. }
  660. }
  661. public function getIsRelRunner() {
  662. $query = "SELECT r.`runner_id` as relrunner
  663. FROM `" . PROJECT_RUNNERS . "` as r
  664. WHERE r.`project_id` = " . $this->getProjectId() . "
  665. AND r.`runner_id` = " . $_SESSION['userid'] . " ";
  666. $result = mysql_query($query);
  667. if ($result && mysql_num_rows($result) > 0) {
  668. return true;
  669. }
  670. return false;
  671. }
  672. public function getFees($worklist_id)
  673. {
  674. $query = "SELECT fees.`id`, fees.`amount`, u.`nickname`, fees.`desc`,fees.`user_id`, DATE_FORMAT(fees.`date`, '%m/%d/%Y') as date, fees.`paid`, fees.`bid_notes`
  675. FROM `".FEES. "` as fees LEFT OUTER JOIN `".USERS."` u ON u.`id` = fees.`user_id`
  676. WHERE worklist_id = ".$worklist_id."
  677. AND fees.withdrawn = 0 ";
  678. $result_query = mysql_query($query);
  679. if($result_query){
  680. $temp_array = array();
  681. while($row = mysql_fetch_assoc($result_query)) {
  682. // this is to make sure to remove extra slashes 11-MAR-2011 <webdev>
  683. $row['desc'] = stripslashes($row['desc']);
  684. $temp_array[] = $row;
  685. }
  686. return $temp_array;
  687. } else {
  688. return null;
  689. }
  690. }
  691. public function placeBid($mechanic_id, $username, $itemid, $bid_amount, $done_in, $expires, $notes) {
  692. if($this->status == 'Bidding') {
  693. $bid_expires = strtotime($expires);
  694. $query = "INSERT INTO `".BIDS."` (`id`, `bidder_id`, `email`, `worklist_id`, `bid_amount`, `bid_created`, `bid_expires`, `bid_done_in`, `notes`)
  695. VALUES (NULL, '$mechanic_id', '$username', '$itemid', '$bid_amount', NOW(), FROM_UNIXTIME('$bid_expires'), '$done_in', '$notes')";
  696. }
  697. else if($this->status == 'SuggestedWithBid' || $this->status == 'Suggested') {
  698. $query = "INSERT INTO `".BIDS."` (`id`, `bidder_id`, `email`, `worklist_id`, `bid_amount`, `bid_created`, `bid_expires`, `bid_done_in`, `notes`)
  699. VALUES (NULL, '$mechanic_id', '$username', '$itemid', '$bid_amount', NOW(), '1 years', '$done_in', '$notes')";
  700. }
  701. if($this->status == 'Suggested') {
  702. mysql_unbuffered_query("UPDATE `" . WORKLIST . "` SET `" . WORKLIST . "` . `status` = 'SuggestedWithBid',
  703. `status_changed`=NOW() WHERE `" . WORKLIST . "` . `id` = '$itemid'");
  704. }
  705. return mysql_query($query) ? mysql_insert_id() : null;
  706. }
  707. public function updateBid($bid_id, $bid_amount, $done_in, $bid_expires, $timezone, $notes) {
  708. $bid_expires = strtotime($bid_expires);
  709. if ($bid_id > 0 && $this->status != 'SuggestedWithBid') {
  710. $query = "UPDATE `".BIDS."` SET `bid_amount` = '".$bid_amount."' ,`bid_done_in` = '$done_in', `bid_expires` = FROM_UNIXTIME({$bid_expires}), `notes` = '".$notes."' WHERE id = '".$bid_id."'";
  711. mysql_query($query);
  712. }
  713. if ($bid_id > 0 && $this->status == 'SuggestedWithBid') {
  714. $query = "UPDATE `".BIDS."` SET `bid_amount` = '".$bid_amount."' ,`bid_done_in` = '$done_in', `bid_expires` = '1 years', `notes` = '".$notes."' WHERE id = '".$bid_id."'";
  715. mysql_query($query);
  716. }
  717. return $bid_id;
  718. }
  719. public function getUserDetails($mechanic_id)
  720. {
  721. $query = "SELECT nickname, username FROM ".USERS." WHERE id='{$mechanic_id}'";
  722. $result_query = mysql_query($query);
  723. return $result_query ? mysql_fetch_assoc($result_query) : null;
  724. }
  725. // look for getOwnerSummary !!!
  726. public function getRunnerSummary($worklist_id) {
  727. $query = "SELECT `" . USERS . "`.`id` as id, `username`, `summary`"
  728. . " FROM `" . USERS . "`, `" . WORKLIST . "`"
  729. . " WHERE `" . WORKLIST . "`.`runner_id` = `" . USERS . "`.`id` AND `" . WORKLIST . "`.`id` = ".$worklist_id;
  730. $result_query = mysql_query($query);
  731. return $result_query ? mysql_fetch_assoc($result_query) : null ;
  732. }
  733. public function getSumOfFee($worklist_id)
  734. {
  735. $query = "SELECT SUM(`amount`) FROM `".FEES."` WHERE worklist_id = ".$worklist_id . " and withdrawn = 0 ";
  736. $result_query = mysql_query($query);
  737. $row = $result_query ? mysql_fetch_row($result_query) : null;
  738. return !empty($row) ? $row[0] : 0;
  739. }
  740. /**
  741. * Given a bid_id, get the corresponding worklist_id. If this is loaded compare the two ids
  742. * and throw an error if the don't match. If not loaded, load the item.
  743. *
  744. * @param int $bidId
  745. * @return int
  746. */
  747. public function conditionalLoadByBidId($bid_id)
  748. {
  749. $query = "SELECT `worklist_id` FROM `".BIDS."` WHERE `id` = ".(int)$bid_id;
  750. $res = mysql_query($query);
  751. if (!$res || !($row = mysql_fetch_row($res))) {
  752. throw new Exception('Bid not found.');
  753. }
  754. if ($this->id && $this->id != $row[0]) {
  755. throw new Exception('Bid belongs to another work item.');
  756. } else if (!$this->id) {
  757. $this->load($row[0]);
  758. }
  759. }
  760. public function loadStatusByBidId($bid_id)
  761. {
  762. $query = "SELECT `worklist_id`," . WORKLIST . ".status FROM `".BIDS."` LEFT JOIN " . WORKLIST . " ON " . BIDS. ".worklist_id = " . WORKLIST . ".id WHERE " . BIDS . ".`id` = ".(int)$bid_id;
  763. $res = mysql_query($query);
  764. if (!$res || !($row = mysql_fetch_row($res))) {
  765. throw new Exception('Bid not found.');
  766. }
  767. return $row[1];
  768. }
  769. /**
  770. * Checks if a workitem has any accepted bids
  771. *
  772. * @param int $worklistId
  773. * @return boolean
  774. */
  775. public function hasAcceptedBids()
  776. {
  777. $query = "SELECT COUNT(*) FROM `".BIDS."` ".
  778. "WHERE `worklist_id`=".(int)$this->id." AND `accepted` = 1 AND `withdrawn` = 0";
  779. $res = mysql_query($query);
  780. if (!$res) {
  781. throw new Exception('Could not retrieve result.');
  782. }
  783. $row = mysql_fetch_row($res);
  784. return ($row[0] > 0);
  785. }
  786. /**
  787. * If a given bid is accepted, the method returns TRUE.
  788. *
  789. * @param int $bidId
  790. * @return boolean
  791. */
  792. public function bidAccepted($bidId)
  793. {
  794. $query = 'SELECT COUNT(*) FROM `' . BIDS . '` WHERE `id` = ' . (int)$bidId . ' AND `accepted` = 1';
  795. $res = mysql_query($query);
  796. if (!$res) {
  797. throw new Exception('Could not retrieve result.');
  798. }
  799. $row = mysql_fetch_row($res);
  800. return ($row[0] == 1);
  801. }
  802. // Accept a bid given it's Bid id
  803. public function acceptBid($bid_id, $budget_id = 0, $is_mechanic = true) {
  804. $this->conditionalLoadByBidId($bid_id);
  805. /*if ($this->hasAcceptedBids()) {
  806. throw new Exception('Can not accept an already accepted bid.');
  807. }*/
  808. $user_id = isset($_SESSION['userid']) ? (int)$_SESSION['userid'] : 0;
  809. $is_runner = isset($_SESSION['is_runner']) ? (int)$_SESSION['is_runner'] : 0;
  810. // If a bid is being accepted, and the runner for the workitem does not exist (incase a bid went from suggested straight
  811. // to working), then we should set the person accepting the bid as the runner;
  812. if (!$this->getRunnerId()) {
  813. $this->setRunnerId($user_id);
  814. }
  815. $res = mysql_query('SELECT * FROM `'.BIDS.'` WHERE `id`=' . $bid_id);
  816. $bid_info = mysql_fetch_assoc($res);
  817. $workitem_info = $this->getWorkItem($bid_info['worklist_id']);
  818. // Get bidder information
  819. $bidder = new User;
  820. if (! $bidder->findUserById($bid_info['bidder_id'])) {
  821. // If bidder doesn't exist, return false. Don't want to throw an
  822. // exception because it would kill multiple bid acceptances
  823. return false;
  824. }
  825. $bid_info['nickname'] = $bidder->getNickname();
  826. $project = new Project($this->getProjectId());
  827. // Get the repo for this project
  828. $repository = $this->getRepository();
  829. $job_id = $this->getId();
  830. // Verify whether the user already has this repo forked on his account
  831. // If not create the fork
  832. $GitHubUser = new User($bid_info['bidder_id']);
  833. if (!$GitHubUser->verifyForkExists($project)) {
  834. $forkStatus = $GitHubUser->createForkForUser($project);
  835. $bidderEmail = $bidder->getUsername();
  836. $emailTemplate = 'forked-repo';
  837. $data = array(
  838. 'project_name' => $forkStatus['data']['full_name'],
  839. 'nickname' => $bidder->getNickname(),
  840. 'users_fork' => $forkStatus['data']['git_url'],
  841. 'master_repo' => str_replace('https://', 'git://', $project->getRepository())
  842. );
  843. $senderEmail = 'Worklist <contact@worklist.net>';
  844. sendTemplateEmail($bidderEmail, $emailTemplate, $data, $senderEmail);
  845. sleep(10);
  846. }
  847. // Create a branch for the user
  848. if (!$forkStatus['error']) {
  849. $branchStatus = $GitHubUser->createBranchForUser($job_id, $project);
  850. $bidderEmail = $bidder->getUsername();
  851. $emailTemplate = 'branch-created';
  852. $data = array(
  853. 'branch_name' => $job_id,
  854. 'nickname' => $bidder->getNickname(),
  855. 'users_fork' => $forkStatus['data']['git_url'],
  856. 'master_repo' => str_replace('https://', 'git://', $project->getRepository())
  857. );
  858. $bid_info = array_merge($data, $bid_info);
  859. }
  860. if (!$branchStatus['error']) {
  861. $bid_info['sandbox'] = $branchStatus['branch_url'];
  862. }
  863. $bid_info['bid_done'] = strtotime('+' . $bid_info['bid_done_in'], time());
  864. // Adding transaction wrapper around steps
  865. if (mysql_query('BEGIN')) {
  866. // changing mechanic of the job
  867. $sql = "UPDATE `" . WORKLIST ."` SET " .
  868. ($is_mechanic ? "`mechanic_id` = '" . $bid_info['bidder_id'] . "', " : '') .
  869. ($is_runner && $user_id > 0 && $workitem_info['runner_id'] == 0 ? "`runner_id` = '".$user_id."', " : '') .
  870. " `status` = 'Working',`status_changed`=NOW(),`sandbox` = '" . $bid_info['sandbox'] . "',`budget_id` = " . $budget_id .
  871. " WHERE `" . WORKLIST . "`.`id` = " . $bid_info['worklist_id'];
  872. if (! $myresult = mysql_query($sql)) {
  873. error_log("AcceptBid:UpdateMechanic failed: ".mysql_error());
  874. mysql_query("ROLLBACK");
  875. return false;
  876. }
  877. // marking bid as "accepted"
  878. if (! $result = mysql_query("UPDATE `".BIDS."` SET `accepted` = 1, `bid_done` = FROM_UNIXTIME('".$bid_info['bid_done']."') WHERE `id` = ".$bid_id)) {
  879. error_log("AcceptBid:MarkBid failed: ".mysql_error());
  880. mysql_query("ROLLBACK");
  881. return false;
  882. }
  883. // adding bid amount to list of fees
  884. if (! $result = mysql_query("INSERT INTO `".FEES."` (`id`, `worklist_id`, `amount`, `user_id`, `desc`, `bid_notes`, `date`, `bid_id`) VALUES (NULL, ".$bid_info['worklist_id'].", '".$bid_info['bid_amount']."', '".$bid_info['bidder_id']."', 'Accepted Bid', '".mysql_real_escape_string($bid_info['notes'])."', NOW(), '$bid_id')")) {
  885. error_log("AcceptBid:Insert Fee failed: ".mysql_error());
  886. mysql_query("ROLLBACK");
  887. return false;
  888. }
  889. $creator_fee = 0;
  890. $creator_fee_desc = 'Creator';
  891. $creator_fee_added = false;
  892. $runner_fee = 0;
  893. $runner_fee_desc = 'Runner';
  894. $runner_fee_added = false;
  895. $accepted_bid_amount = $bid_info['bid_amount'];
  896. $fee_category = '';
  897. $is_expense = '';
  898. $is_rewarder = '';
  899. $fees = $this->getFees($this->getId());
  900. foreach ($fees as $fee) {
  901. // find the accepted bid amount
  902. if ($fee['desc'] == 'Accepted Bid') {
  903. $accepted_bid_amount = $fee['amount'];
  904. }
  905. if (preg_match($reviewer_fee_desc, $fee['desc'])) {
  906. $reviewer_fee_added = true;
  907. }
  908. if ($fee['desc'] == $creator_fee_desc) {
  909. $creator_fee_added = true;
  910. }
  911. if ($fee['desc'] == $runner_fee_desc) {
  912. $runner_fee_added = true;
  913. }
  914. }
  915. // get project creator role settings, if not available, no fee is added
  916. // and will need to be added manually if applicable
  917. $project = new Project();
  918. $project_roles = $project->getRoles($this->getProjectId(), "role_title = 'Creator'");
  919. if (count($project_roles) != 0 && ! $creator_fee_added) {
  920. // fees are not automatically created for internal users
  921. if (! $this->getCreator()->isInternal()) {
  922. $creator_role = $project_roles[0];
  923. if ($creator_role['percentage'] !== null && $creator_role['min_amount'] !== null) {
  924. $creator_fee = ($creator_role['percentage'] / 100) * $accepted_bid_amount;
  925. if ((float) $creator_fee < $creator_role['min_amount']) {
  926. $creator_fee = $creator_role['min_amount'];
  927. }
  928. // add the fee
  929. /**
  930. * @TODO - We call addfees and then deduct from budget
  931. * seems we should add the deduction process to the AddFee
  932. * function
  933. *
  934. */
  935. AddFee($this->getId(), $creator_fee, $fee_category, $creator_fee_desc, $this->getCreatorId(), $is_expense, $is_rewarder);
  936. // and reduce the runners budget
  937. $myRunner = new User();
  938. $myRunner->findUserById($this->getRunnerId());
  939. $myRunner->updateBudget(-$creator_fee, $this->getBudget_id());
  940. }
  941. }
  942. }
  943. $project_roles = $project->getRoles($this->getProjectId(), "role_title = 'Runner'");
  944. if (count($project_roles) != 0 && ! $runner_fee_added) {
  945. error_log("[FEES] we have a role for runner");
  946. $runner_role = $project_roles[0];
  947. // fees are not automatically created for internal users
  948. if (! $this->getRunner()->isInternal()) {
  949. if ($runner_role['percentage'] !== null && $runner_role['min_amount'] !== null) {
  950. $runner_fee = ($runner_role['percentage'] / 100) * $accepted_bid_amount;
  951. if ((float) $runner_fee < $runner_role['min_amount']) {
  952. $runner_fee = $runner_role['min_amount'];
  953. }
  954. // add the fee
  955. AddFee($this->getId(), $runner_fee, $fee_category, $runner_fee_desc, $this->getRunnerId(), $is_expense, $is_rewarder);
  956. // and reduce the runners budget
  957. $myRunner = new User();
  958. $myRunner->findUserById($this->getRunnerId());
  959. $myRunner->updateBudget(-$runner_fee, $this->getBudget_id());
  960. }
  961. }
  962. }
  963. // add an entry to the status log
  964. $status_sql = "
  965. INSERT INTO " . STATUS_LOG . " (worklist_id, status, user_id, change_date)
  966. VALUES({$bid_info['worklist_id']}, 'Working', {$_SESSION['userid']}, NOW())";
  967. if (! $result = mysql_query($status_sql)) {
  968. error_log("AcceptedBid:Insert status log failed: " . mysql_error());
  969. mysql_query("ROLLBACK");
  970. return false;
  971. }
  972. // When we get this far, commit and return bid_info
  973. if (mysql_query('COMMIT')) {
  974. $bid_info['summary'] = getWorkItemSummary($bid_info['worklist_id']);
  975. $this -> setMechanicId($bid_info['bidder_id']);
  976. return $bid_info;
  977. } else {
  978. return false;
  979. }
  980. } else {
  981. return false;
  982. }
  983. }
  984. public function validateAction($action, $action_error) {
  985. switch ($action) {
  986. case 'withdraw_bid':
  987. if ($this->getStatus() == 'Done') {
  988. $action_error = 'Cannot withdraw bid when status is Done';
  989. return false;
  990. }
  991. return $action;
  992. break;
  993. case 'decline_bid':
  994. if ($this->getStatus() == 'Done') {
  995. $action_error = 'Cannot decline bid when status is Done';
  996. return false;
  997. }
  998. return $action;
  999. break;
  1000. case 'save_workitem':
  1001. if ($this->getStatus() == 'Done') {
  1002. return false;
  1003. }
  1004. return $action;
  1005. break;
  1006. case 'place_bid':
  1007. if ($this->getStatus() != 'Bidding') {
  1008. if ($this->getStatus() != 'SuggestedWithBid') {
  1009. if ($this->getStatus() != 'Suggested') {
  1010. $action_error = 'Cannot place bid when workitem is in this status';
  1011. return false;
  1012. }
  1013. }
  1014. }
  1015. return $action;
  1016. break;
  1017. case 'edit_bid':
  1018. if ($this->getStatus() != 'Bidding') {
  1019. if ($this->getStatus() != 'SuggestedWithBid') {
  1020. if ($this->getStatus() != 'Suggested') {
  1021. $action_error = 'Cannot edit bid for this workitem status';
  1022. return false;
  1023. }
  1024. }
  1025. }
  1026. return $action;
  1027. break;
  1028. case 'add_fee':
  1029. if ($this->getStatus() == 'Done') {
  1030. $action_error = 'Cannot add fee when status is Done';
  1031. return false;
  1032. }
  1033. return $action;
  1034. break;
  1035. case 'add_tip':
  1036. if ($this->getStatus()== 'Done' || $this->getStatus() == 'Pass' || $this->getStatus() == 'Suggested') {
  1037. $action_error = 'Cannot add tip when status is Done, Pass or Suggested';
  1038. return false;
  1039. }
  1040. return $action;
  1041. break;
  1042. case 'view_bid':
  1043. if ($this->getStatus() != 'Bidding') {
  1044. if ($this->getStatus() != 'SuggestedWithBid') {
  1045. if ($this->getStatus() != 'Suggested') {
  1046. $action_error = 'Cannot accept bid when status is not Bidding';
  1047. return false;
  1048. }
  1049. }
  1050. }
  1051. return $action;
  1052. break;
  1053. case 'accept_bid':
  1054. if ($this->getStatus() != 'Bidding') {
  1055. if ($this->getStatus() != 'SuggestedWithBid') {
  1056. if ($this->getStatus() != 'Suggested') {
  1057. $action_error = 'Cannot accept bid when status is not Bidding';
  1058. return false;
  1059. }
  1060. }
  1061. }
  1062. return $action;
  1063. break;
  1064. case 'accept_multiple_bid':
  1065. if ($this->getStatus() != 'Bidding') {
  1066. if ($this->getStatus() != 'SuggestedWithBid') {
  1067. if ($this->getStatus() != 'Suggested') {
  1068. $action_error = 'Cannot accept bid when status is not Bidding';
  1069. return false;
  1070. }
  1071. }
  1072. }
  1073. return $action;
  1074. break;
  1075. case 'status-switch':
  1076. return $action;
  1077. break;
  1078. case 'save-review-url':
  1079. if ($this->getStatus() == 'Done') {
  1080. $action_error = 'Cannot change review URL when status is Done';
  1081. return false;
  1082. }
  1083. return $action;
  1084. break;
  1085. case 'new-comment':
  1086. if ($this->getStatus() == 'Done') {
  1087. $action_error = 'Cannot add comment when status is Done';
  1088. return false;
  1089. }
  1090. return $action;
  1091. break;
  1092. case 'edit':
  1093. return $action;
  1094. break;
  1095. case 'cancel_codereview':
  1096. return $action;
  1097. break;
  1098. case 'finish_codereview':
  1099. return $action;
  1100. break;
  1101. default:
  1102. $action_error = 'Invalid action';
  1103. return false;
  1104. }
  1105. }
  1106. function isUserFollowing($id) {
  1107. $query = 'SELECT COUNT(*) FROM ' . TASK_FOLLOWERS . ' WHERE user_id = ' . (int)$id . ' AND workitem_id=' . $this->id;
  1108. $res = mysql_query($query);
  1109. if (!$res) {
  1110. throw new Exception('Could not retrieve result.');
  1111. }
  1112. $row = mysql_fetch_row($res);
  1113. return (boolean)$row[0];
  1114. }
  1115. function toggleUserFollowing($user_id) {
  1116. $isFollowing = $this->isUserFollowing($user_id);
  1117. if($isFollowing == true) {
  1118. $query = 'DELETE FROM ' . TASK_FOLLOWERS . ' WHERE user_id=' . $user_id . ' AND workitem_id = ' . $this->id;
  1119. } else {
  1120. $query = 'INSERT INTO ' . TASK_FOLLOWERS . ' (user_id, workitem_id) VALUES (' . $user_id . ' , ' . $this->id . ')';
  1121. }
  1122. // return $query;
  1123. $res = mysql_query($query);
  1124. if (!$res) {
  1125. throw new Exception('Could not retrieve result.');
  1126. }
  1127. return !$isFollowing;
  1128. }
  1129. function getFollowersId() {
  1130. $followers = array();
  1131. $query = ' SELECT f.`user_id`
  1132. FROM `' . TASK_FOLLOWERS . '` f INNER JOIN `' . USERS . '` u ON u.`id` = f.`user_id`
  1133. WHERE f.`workitem_id` = ' . $this->id . '
  1134. AND u.`is_active` = 1';
  1135. $res = mysql_query($query);
  1136. while($row = mysql_fetch_row($res)) {
  1137. $followers[]= $row[0];
  1138. }
  1139. return $followers;
  1140. }
  1141. function getReviewNotifsId() {
  1142. $user_id = $_SESSION['userid'];
  1143. $reviewNotifs = array();
  1144. $query = 'SELECT u.`id`
  1145. FROM `' . USERS . '` u
  1146. WHERE ((`review_notif` = 1
  1147. AND `id` != ' . $user_id . ')
  1148. OR (self_notif = 1 and `id` = ' . $user_id . ')
  1149. AND `is_active` = 1)';
  1150. $res = mysql_query($query);
  1151. while($row = mysql_fetch_row($res)) {
  1152. $reviewNotifs[]= $row[0];
  1153. }
  1154. return $reviewNotifs;
  1155. }
  1156. public function getSandboxPath() {
  1157. $url_array = parse_url($this->sandbox);
  1158. if ($url_array['path']) {
  1159. $path_array = explode('/', $url_array['path']);
  1160. if (count($path_array) > 2 && strpos($path_array[1], '~') == 0) {
  1161. $path = substr($path_array[1], 1, strlen($path_array[1]) - 1);
  1162. $path .= DIRECTORY_SEPARATOR . 'public_html' . DIRECTORY_SEPARATOR . $path_array[2];
  1163. return $path;
  1164. }
  1165. }
  1166. return '';
  1167. }
  1168. public function startCodeReview($reviewer_id) {
  1169. if ($this->status != 'Review' || $this->code_review_started != 0) {
  1170. return null; // CR is only allowed for REVIEW items without the CR started
  1171. }
  1172. $this->setCRStarted(1);
  1173. $this->setCReviewerId($reviewer_id);
  1174. $this->save();
  1175. return true;
  1176. }
  1177. public function addFeesToCompletedJob($include_review = false) {
  1178. // workitem is DONE, calculate the creator fee based on project roles
  1179. // and accepted bid
  1180. if ($this->hasAcceptedBids()) {
  1181. $reviewer_fee = 0;
  1182. $reviewer_fee_desc = '/^Code Review - comment/';
  1183. $reviewer_fee_added = false;
  1184. $fees = $this->getFees($this->getId());
  1185. foreach ($fees as $fee) {
  1186. // find the accepted bid amount
  1187. if ($fee['desc'] == 'Accepted Bid') {
  1188. $accepted_bid_amount = $fee['amount'];
  1189. }
  1190. if (preg_match($reviewer_fee_desc, $fee['desc'])) {
  1191. $reviewer_fee_added = true;
  1192. }
  1193. }
  1194. if (!$reviewer_fee_added && $include_review) {
  1195. $project = new Project();
  1196. $project_roles = $project->getRoles($this->getProjectId(), "role_title = 'Reviewer'");
  1197. if (count($project_roles) != 0) {
  1198. error_log("[FEES] we have a role for reviewer");
  1199. $reviewer_role = $project_roles[0];
  1200. if ($reviewer_role['percentage'] !== null && $reviewer_role['min_amount'] !== null) {
  1201. $reviewer_fee = ($reviewer_role['percentage'] / 100) * $accepted_bid_amount;
  1202. if ((float) $reviewer_fee < $reviewer_role['min_amount']) {
  1203. $reviewer_fee = $reviewer_role['min_amount'];
  1204. }
  1205. // add the fee
  1206. $reviewer_fee_detail = 'Code Review - comment';
  1207. AddFee($this->getId(),
  1208. $reviewer_fee,
  1209. $fee_category,
  1210. $reviewer_fee_detail,
  1211. $this->getCReviewerId(),

Large files files are truncated, but you can click here to view the full file