Example #1
0
 public function entries()
 {
     $worklist_entries = array_reverse($this->read('entries'));
     $gh_events = $this->read('gh_events');
     $entries = array_merge($worklist_entries, $gh_events);
     usort($entries, array('StatusView', 'sortEntries'));
     $ret = '';
     $now = 0;
     foreach ($entries as $entry) {
         if (!$now) {
             $now = strtotime(Model::now());
         }
         if (get_class($entry) == 'EntryModel') {
             $id = $entry->id;
             $type = 'worklist';
             $date = strtotime($entry->date);
             $content = self::formatWorklistEntry($entry);
         } else {
             // github event
             if (!preg_match('/^(Fork|PullRequest(ReviewComment)?|IssueComment)Event$/', $entry['type'])) {
                 continue;
             }
             $id = $entry['id'];
             $type = 'github' . preg_replace('/Event$/', '', $entry['type']);
             $date = strtotime($entry['created_at']);
             $content = self::formatGithubEntry($entry);
         }
         $ret .= '<li entryid="' . $id . '" date="' . $date . '" type="' . $type . '">' . '<h4>' . Utils::relativeTime($date - $now) . '</h4>' . $content . '</li>';
     }
     return $ret;
 }
Example #2
0
 /**
  * Worklist entries longpoll, retrieves new entries by simulating server pushes
  * to the status page. Refer to client side code at js/status.js to clarify.
  */
 public function longpoll()
 {
     $this->view = new JsonView();
     $ret = array();
     try {
         $since = $_POST['since'];
         $entry = new EntryModel();
         $ret = array();
         // this is a 30 seconds timeout long poll, so let's loop up to 25 times
         // with 1 sec delays at the end of each iteration
         $fromTime = (int) $since;
         for ($i = 0; $i < 25; $i++) {
             $toTime = strtotime(Model::now());
             $seconds_ago = abs($toTime - $fromTime);
             // we are searching for new worklist entries
             $entries = $entry->latest($seconds_ago, 90);
             if ($entries) {
                 $now = 0;
                 foreach ($entries as $entry) {
                     if (!$now) {
                         $now = strtotime(Model::now());
                     }
                     $date = strtotime($entry->date);
                     $relativeDate = Utils::relativeTime($date - $now);
                     $mention_regex = '/(^|\\s)@([a-zA-Z0-9][a-zA-Z0-9-]+)/';
                     $task_regex = '/(^|\\s)\\*\\*#(\\d+)\\*\\*/';
                     $content = preg_replace($mention_regex, '\\1[\\2](./user/\\2)', $entry->entry);
                     $content = preg_replace($task_regex, '\\1[\\\\#\\2](./\\2)', $content);
                     // proccesed entries are returned as markdown-processed html
                     $content = Markdown::defaultTransform($content);
                     $ret[] = array('id' => $entry->id, 'date' => $date, 'relativeDate' => $relativeDate, 'content' => $content);
                 }
                 // if we found new entries, no need to keep looping so we can return data inmediatly
                 break;
             }
             sleep(1);
         }
         $ret = array('success' => true, 'data' => $ret);
     } catch (Exception $e) {
         $ret = array('success' => false, 'message' => $e->getMessage());
     }
     $this->write('output', $ret);
 }
Example #3
0
 public function __construct()
 {
     $this->currentYear = date('Y', strtotime(Model::now()));
     parent::__construct();
 }
Example #4
0
 public function getRelativeDate()
 {
     return Utils::relativeTime(strtotime($this->date) - strtotime(Model::now()));
 }
Example #5
0
 public function view($job_id)
 {
     $this->write('statusListRunner', array("Draft", "Suggestion", "Bidding", "In Progress", "QA Ready", "Code Review", "Merged", "Done", "Pass"));
     $statusListMechanic = array("In Progress", "QA Ready", "Code Review", "Merged", "Pass");
     $this->write('statusListMechanic', $statusListMechanic);
     $this->write('statusListCreator', array("Suggestion", "Pass"));
     if (!defined("WORKITEM_URL")) {
         define("WORKITEM_URL", SERVER_URL);
     }
     if (!defined("WORKLIST_REDIRECT_URL")) {
         define("WORKLIST_REDIRECT_URL", SERVER_URL);
     }
     $worklist_id = intval($job_id);
     $is_runner = isset($_SESSION['is_runner']) ? $_SESSION['is_runner'] : 0;
     $currentUsername = isset($_SESSION['username']) ? $_SESSION['username'] : '';
     //initialize user accessing the page
     $userId = Session::uid();
     $user = new User();
     if ($userId > 0) {
         $user->findUserById($userId);
     } else {
         $user->setId(0);
     }
     $this->write('user', $user);
     // TODO: Would be good to take out all the checks for isset($_SESSION['userid'] etc. and have them use $user instead, check $user->getId() > 0.
     if (empty($worklist_id)) {
         $this->view = null;
         return;
     }
     //Set an empty variable for $journal_message to avoid errors/warnings with .=
     $journal_message = null;
     //initialize the workitem class
     $workitem = new WorkItem();
     try {
         $workitem->loadById($worklist_id);
     } catch (Exception $e) {
         $error = $e->getMessage();
         $this->view = null;
         die($error);
     }
     if ($workitem->isInternal() && !$user->isInternal()) {
         $this->write('msg', 'You don\'t have permissions to view this job.');
         $this->write('link', WORKLIST_URL);
         $this->view = new ErrorView();
         parent::run();
         exit;
     }
     if ($workitem->getStatus() == 'Draft' && $workitem->getCreatorId() != $_SESSION['userid']) {
         $this->write('msg', 'You don\'t have permissions to view this job.');
         $this->write('link', WORKLIST_URL);
         $this->view = new ErrorView();
         parent::run();
         exit;
     }
     $this->write('workitem', $workitem);
     // we need to be able to grant runner rights to a project founder for all jobs for their project
     $workitem_project = Project::getById($workitem->getProjectId());
     $is_project_founder = false;
     if ($workitem_project->getOwnerId() == $_SESSION['userid']) {
         $is_project_founder = true;
     }
     $this->write('workitem_project', $workitem_project);
     $this->write('is_project_founder', $is_project_founder);
     $this->write('isGitHubConnected', $user->isGithub_connected($workitem_project->getGithubId()));
     //used for is_project_runner rights
     $is_project_runner = false;
     if ($workitem->getIsRelRunner() == 1) {
         $is_project_runner = true;
     }
     $this->write('is_project_runner', $is_project_runner);
     $redirectToDefaultView = false;
     $promptForReviewUrl = true;
     $runner_budget = $user->getBudget();
     $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'view';
     if ($workitem->getStatus() == 'Done' && $action == 'edit') {
         $action = 'view';
     }
     $view_bid_id = 0;
     if (isset($_REQUEST['withdraw_bid'])) {
         $action = "withdraw_bid";
     } else {
         if (isset($_REQUEST['decline_bid'])) {
             $action = "decline_bid";
         } else {
             if (isset($_REQUEST['save_workitem'])) {
                 $action = "save_workitem";
             } else {
                 if (isset($_REQUEST['place_bid'])) {
                     $action = "place_bid";
                 } else {
                     if (isset($_REQUEST['swb'])) {
                         $action = "swb";
                     } else {
                         if (isset($_REQUEST['edit_bid'])) {
                             $action = "edit_bid";
                         } else {
                             if (isset($_REQUEST['add_fee'])) {
                                 $action = "add_fee";
                             } else {
                                 if (isset($_REQUEST['add_tip'])) {
                                     $action = "add_tip";
                                 } else {
                                     if (isset($_REQUEST['accept_bid'])) {
                                         $action = "accept_bid";
                                     } else {
                                         if (isset($_REQUEST['accept_multiple_bid'])) {
                                             $action = "accept_multiple_bid";
                                         } else {
                                             if (isset($_REQUEST['status-switch'])) {
                                                 $action = "status-switch";
                                             } else {
                                                 if (isset($_REQUEST['newcomment'])) {
                                                     $action = 'new-comment';
                                                 }
                                             }
                                         }
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
     if ($action == 'view_bid') {
         $action = "view";
         $this->write('view_bid_id', isset($_REQUEST['bid_id']) ? $_REQUEST['bid_id'] : 0);
     }
     // for any other action user has to be logged in
     if ($action != 'view') {
         Utils::checkLogin();
         $action_error = '';
         $action = $workitem->validateAction($action, $action_error);
     }
     $this->write('action', $action);
     // Save WorkItem was requested. We only support Update here
     $notifyEmpty = true;
     $job_changes = array();
     $status_change = '';
     if ($action == 'save_workitem') {
         $this->edit($worklist_id);
     }
     if ($action == 'new-comment') {
         if (isset($_REQUEST['worklist_id']) && !empty($_REQUEST['worklist_id']) && (isset($_REQUEST['user_id']) && !empty($_REQUEST['user_id'])) && (isset($_REQUEST['comment']) && !empty($_REQUEST['comment']))) {
             if (isset($_REQUEST['comment_id']) && !empty($_REQUEST['comment_id'])) {
                 $parent_comment = (int) $_REQUEST['comment_id'];
             } else {
                 $parent_comment = NULL;
             }
             $worklist_id = (int) $_REQUEST['worklist_id'];
             $user_id = (int) $_REQUEST['user_id'];
             $comment = $_REQUEST['comment'];
             $rt = $this->addComment($worklist_id, $user_id, $comment, $parent_comment);
             // Send journal notification
             if ($workitem->getStatus() != 'Draft') {
                 $related = $this->getRelated($comment);
                 $journal_message .= '@' . $_SESSION['nickname'] . ' posted a comment on #' . $worklist_id . $related;
                 $options = array('type' => 'comment', 'workitem' => $workitem, 'recipients' => array('creator', 'runner', 'mechanic', 'followers'), 'emails' => $rt['correspondent']);
                 $data = array('who' => $_SESSION['nickname'], 'comment' => $comment, 'related' => $related, 'comment-id' => $rt['id']);
                 Notification::workitemNotify($options, $data, false);
                 Notification::workitemNotifyHipchat($options, $data);
                 // workitem mentions
                 $matches = array();
                 if (preg_match_all('/@(\\w+)/', $comment, $matches, PREG_SET_ORDER)) {
                     foreach ($matches as $mention) {
                         // validate the username actually exists
                         if ($recipient = User::find($mention[1])) {
                             // exclude creator, designer, developer and followers
                             if ($recipient->getId() != $workitem->getRunnerId() && $recipient->getId() != $workitem->getMechanicId() && $recipient->getId() != $workitem->getCreatorId() && !$workitem->isUserFollowing($recipient->getId())) {
                                 $emailTemplate = 'workitem-mention';
                                 $comment_url = WORKLIST_URL . $workitem->getId() . '#comment-' . $rt['id'];
                                 $data = array('job_id' => $workitem->getId(), 'summary' => $workitem->getSummary(), 'author' => $_SESSION['nickname'], 'text' => $comment, 'link' => '<a href="' . $comment_url . '">See the comment</a>');
                                 $senderEmail = 'Worklist - ' . $_SESSION['nickname'] . ' <*****@*****.**> ';
                                 Utils::sendTemplateEmail($recipient->getUsername(), $emailTemplate, $data, $senderEmail);
                             }
                         }
                     }
                 }
             }
             Utils::systemNotification($journal_message);
             $comment = new Comment();
             $comment->findCommentById((int) $rt['id']);
             $result = array('success' => true, 'id' => $rt['id'], 'comment' => str_replace(array('\\n\\r', '\\r\\n', '\\n', '\\r'), '<br/>', Utils::linkify($comment->getComment())), 'avatar' => $comment->getUser()->getAvatar(), 'nickname' => $comment->getUser()->getNickname(), 'userid' => $comment->getUser()->getId(), 'date' => Utils::relativeTime(strtotime($comment->getDate()) - strtotime(Model::now())));
             ob_start();
             $json = json_encode($result);
         } else {
             $json = json_encode(array('success' => false));
         }
         $this->view = null;
         echo $json;
         ob_end_flush();
         exit;
     }
     if ($action == 'status-switch') {
         $status = $_REQUEST['quick-status'];
         $status_error = '';
         if ($status == 'Done' && $workitem->getProjectId() == 0) {
             $status_error = "No project associated with workitem. Could not set to DONE.";
         } else {
             if ($this->changeStatus($workitem, $status, $user)) {
                 if ($workitem->save() == false) {
                     $status_error = "Error in save workitem process. Could not change the status.";
                 } else {
                     if ($status == 'Merged') {
                         $workitem->addFeesToCompletedJob();
                     }
                     if ($status != 'Draft') {
                         $new_update_message = "Status set to *{$status}*. ";
                         $notifyEmpty = false;
                         $status_change = '-' . ucfirst(strtolower($status));
                         if ($status == 'QA Ready') {
                             Notification::workitemNotify(array('type' => 'new_qa', 'workitem' => $workitem, 'status_change' => $status_change, 'job_changes' => $job_changes, 'recipients' => array($workitem->getRunnerId(), 'creator', 'mechanic', 'followers')), array('changes' => $new_update_message));
                             $notifyEmpty = true;
                         }
                         if ($status == 'Code Review') {
                             Notification::workitemNotify(array('type' => 'new_review', 'workitem' => $workitem, 'status_change' => $status_change, 'job_changes' => $job_changes, 'recipients' => array($workitem->getRunnerId(), 'creator', 'mechanic', 'followers', 'reviewNotifs')), array('changes' => $new_update_message));
                             $notifyEmpty = true;
                         }
                         $journal_message = '\\#' . $worklist_id . ' updated by @' . $_SESSION['nickname'] . ' ' . $new_update_message;
                     }
                 }
             } else {
                 $message = '';
                 if ($status & 4) {
                     //sandbox not updated
                     $message .= " - Sandbox is not up-to-date\n";
                 }
                 if ($status & 8) {
                     //sandbox has conflicts
                     $message .= " - Sandbox contains conflicted files\n";
                 }
                 if ($status & 16) {
                     //sandbox has not-included files
                     $message .= " - Sandbox contains 'not-included' files\n";
                 }
                 $status_error = "Sandbox verification failed. " . $message;
             }
         }
     }
     if (!$notifyEmpty) {
         $options = array('type' => 'modified', 'workitem' => $workitem, 'status_change' => $status_change, 'job_changes' => $job_changes, 'recipients' => array('runner', 'creator', 'mechanic', 'followers'));
         $data = array('changes' => $new_update_message);
         Notification::workitemNotify($options, $data);
     }
     if ($action == "place_bid") {
         //Escaping $notes with mysql_real_escape_string is generating \n\r instead of <br>
         //a new variable is used to send the unenscaped notes in email alert.
         //so it can parse the new line as <BR>   12-Mar-2011 <webdev>
         $args = array('bid_amount', 'done_in', 'bid_expires', 'notes', 'mechanic_id');
         foreach ($args as $arg) {
             ${$arg} = mysql_real_escape_string($_REQUEST[$arg]);
         }
         $bid_amount = (double) $bid_amount;
         $mechanic_id = (int) $mechanic_id;
         if ($_SESSION['timezone'] == '0000') {
             $_SESSION['timezone'] = '+0000';
         }
         $summary = $workitem->getSummary();
         if ($mechanic_id != Session::uid()) {
             $row = $workitem->getUserDetails($mechanic_id);
             if (!empty($row)) {
                 $nickname = $row['nickname'];
                 $username = $row['username'];
             } else {
                 $username = "******";
                 $nickname = "unknown-{$mechanic_id}";
             }
         } else {
             $mechanic_id = $_SESSION['userid'];
             $username = $_SESSION['username'];
             $nickname = $_SESSION['nickname'];
         }
         if ($user->isEligible()) {
             $bid_id = $workitem->placeBid($mechanic_id, $username, $worklist_id, $bid_amount, $done_in, $bid_expires, $notes);
             //sending email to the runner of worklist item or all runners if not assigned
             $row = $workitem->getRunnerSummary($worklist_id);
             if (!empty($row)) {
                 $id = $row['id'];
                 $summary = $row['summary'];
                 $username = $row['username'];
             }
             $options = array('type' => 'bid_placed', 'workitem' => $workitem, 'recipients' => array($workitem->getRunnerId() == '' ? 'projectRunners' : 'runner'), 'jobsInfo' => $user->jobsForProject('Done', $workitem->getProjectId(), 1, 3), 'totalJobs' => $user->jobsCount(array('In Progress', 'QA Ready', 'Review', 'Merged', 'Done')), 'activeJobs' => $user->jobsCount(array('In Progress', 'QA Ready', 'Review')));
             $journal_message = 'A bid was placed on #' . $worklist_id;
             $data = array('done_in' => $done_in, 'bid_expires' => $bid_expires, 'bid_amount' => $bid_amount, 'notes' => str_replace(array('\\n\\r', '\\r\\n', '\\n', '\\r'), '<br/>', $notes), 'bid_id' => $bid_id);
             // notify runner of new bid
             Notification::workitemNotify($options, $data);
             $status = $workitem->loadStatusByBidId($bid_id);
             $data['new_update_message'] = $new_update_message;
             Notification::workitemNotifyHipchat($options, $data);
         } else {
             error_log("Input forgery detected for user {$userId}: attempting to {$action}.");
         }
         $redirectToDefaultView = true;
     }
     // Edit Bid
     if ($action == "edit_bid") {
         if (!$user->isEligible()) {
             error_log("Input forgery detected for user {$userId}: attempting to {$action} (isEligible in job)");
         } else {
             //Escaping $notes with mysql_real_escape_string is generating \n\r instead of <br>
             //a new variable is used to send the unenscaped notes in email alert.
             //so it can parse the new line as <BR>   12-Mar-2011 <webdev>
             $args = array('bid_id', 'bid_amount', 'done_in', 'bid_expires', 'notes');
             foreach ($args as $arg) {
                 ${$arg} = mysql_real_escape_string($_REQUEST[$arg]);
             }
             $bid_amount = (double) $bid_amount;
             $mechanic_id = (int) $mechanic_id;
             if ($_SESSION['timezone'] == '0000') {
                 $_SESSION['timezone'] = '+0000';
             }
             $summary = $workitem->getSummary();
             $bid_id = $workitem->updateBid($bid_id, $bid_amount, $done_in, $bid_expires, $_SESSION['timezone'], $notes);
             // Journal notification
             $journal_message = 'Bid updated on #' . $worklist_id;
             //sending email to the runner of worklist item
             $row = $workitem->getRunnerSummary($worklist_id);
             if (!empty($row)) {
                 $id = $row['id'];
                 $summary = $row['summary'];
                 $username = $row['username'];
             }
             $options = array('type' => 'bid_updated', 'workitem' => $workitem, 'recipients' => array('runner'), 'jobsInfo' => $user->jobsForProject('Done', $workitem->getProjectId(), 1, 3), 'totalJobs' => $user->jobsCount(array('In Progress', 'QA Ready', 'Review', 'Merged', 'Done')), 'activeJobs' => $user->jobsCount(array('In Progress', 'QA Ready', 'Review')));
             $data = array('done_in' => $done_in, 'bid_expires' => $bid_expires, 'bid_amount' => $bid_amount, 'notes' => str_replace(array('\\n\\r', '\\r\\n', '\\n', '\\r'), '<br/>', $notes), 'bid_id' => $bid_id);
             // notify runner of new bid
             Notification::workitemNotify($options, $data);
             Notification::workitemNotifyHipchat($options, $data);
         }
         $redirectToDefaultView = true;
     }
     // Request submitted from Add Fee popup
     if ($action == "add_fee") {
         if (!$user->isEligible()) {
             error_log("Input forgery detected for user {$userId}: attempting to {$action}.");
         } else {
             $args = array('itemid', 'fee_amount', 'fee_desc', 'mechanic_id', 'is_expense', 'is_rewarder');
             foreach ($args as $arg) {
                 if (isset($_REQUEST[$arg])) {
                     ${$arg} = mysql_real_escape_string($_REQUEST[$arg]);
                 } else {
                     ${$arg} = '';
                 }
             }
             $itemid = (int) $itemid;
             $fee_amount = (double) $fee_amount;
             $mechanic_id = (int) $mechanic_id;
             $journal_message = Fee::add($itemid, $fee_amount, '', $fee_desc, $mechanic_id, '', '');
             if ($workitem->getStatus() != 'Draft') {
                 $options = array('type' => 'fee_added', 'workitem' => $workitem, 'recipients' => array('runner'));
                 $data = array('fee_adder' => $user->getNickname(), 'fee_amount' => $fee_amount, 'fee_desc' => $fee_desc, 'mechanic_id' => $mechanic_id);
                 Notification::workitemNotify($options, $data);
                 $data['nick'] = $_SESSION['nickname'];
                 Notification::workitemNotifyHipchat($options, $data);
                 // update budget
                 $runner = new User();
                 $runner->findUserById($workitem->getRunnerId());
                 $runner->updateBudget(-$fee_amount, $workitem->getBudget_id());
             }
             $redirectToDefaultView = true;
         }
     }
     // Accept a bid
     if ($action == 'accept_bid') {
         if (!isset($_REQUEST['bid_id']) || !isset($_REQUEST['budget_id'])) {
             $_SESSION['workitem_error'] = "Missing parameter to accept a bid!";
         } else {
             $bid_id = intval($_REQUEST['bid_id']);
             $budget_id = intval($_REQUEST['budget_id']);
             $budget = new Budget();
             if (!$budget->loadById($budget_id)) {
                 $_SESSION['workitem_error'] = "Invalid budget!";
             }
             $is_job_runner = $workitem->getRunnerId() == Session::uid();
             $is_assigned = $workitem->getAssigned_id() == Session::uid();
             // only runners can accept bids
             if ($is_project_runner || $is_job_runner || $is_assigned || $user->getIs_admin() == 1 && $is_runner && !$workitem->hasAcceptedBids() && $workitem->getStatus() == "Bidding") {
                 // query to get a list of bids (to use the current class rather than breaking uniformity)
                 // I could have done this quite easier with just 1 query and an if statement..
                 $bids = (array) $workitem->getBids($workitem->getId());
                 $exists = false;
                 foreach ($bids as $array) {
                     if ($array['id'] == $bid_id) {
                         $exists = true;
                         $bid_amount = $array["bid_amount"];
                         break;
                     }
                 }
                 if ($exists) {
                     $remainingFunds = $budget->getRemainingFunds();
                     if ($bid_amount <= $remainingFunds) {
                         $bid_info = $workitem->acceptBid($bid_id, $budget_id);
                         $budget->recalculateBudgetRemaining();
                         // Journal notification
                         $journal_message .= '@' . $_SESSION['nickname'] . " accepted {$bid_info['bid_amount']} from " . $bid_info['nickname'] . " on #" . $bid_info['worklist_id'] . " Status set to *In Progress*.";
                         $options = array('type' => 'bid_accepted', 'workitem' => $workitem, 'recipients' => array('mechanic', 'followers'));
                         // mail notification - including any data returned from acceptBid
                         Notification::workitemNotify($options, $bid_info);
                         $data = $bid_info;
                         $data['nick'] = $_SESSION['nickname'];
                         Notification::workitemNotifyHipchat($options, $data);
                         $bidder = new User();
                         $bidder->findUserById($bid_info['bidder_id']);
                         // Update Budget
                         $runner = new User();
                         $runner->findUserById($workitem->getRunnerId());
                         $runner->updateBudget(-$bid_amount, $workitem->getBudget_id());
                         // Send email to not accepted bidders
                         $this->sendMailToDiscardedBids($worklist_id);
                     } else {
                         $overBudget = money_format('%i', $bid_amount - $remainingFunds);
                         $_SESSION['workitem_error'] = "Failed to accept bid. Accepting this bid would make you " . $overBudget . " over your budget!";
                     }
                 } else {
                     $_SESSION['workitem_error'] = "Failed to accept bid, bid has been deleted!";
                 }
             } else {
                 if ($workitem->getIsRelRunner() || $workitem->getRunnerId() == $_SESSION['userid']) {
                     if ($workitem->hasAcceptedBids()) {
                         $_SESSION['workitem_error'] = "Failed to accept bid on task with an accepted bid!";
                     } else {
                         $_SESSION['workitem_error'] = "Accept Bid Failed, unknown task state!";
                     }
                 }
             }
         }
         $redirectToDefaultView = true;
     }
     // Accept Multiple  bid
     if ($action == 'accept_multiple_bid') {
         if (!isset($_REQUEST['budget_id'])) {
             $_SESSION['workitem_error'] = "Missing budget to accept a bid!";
         } else {
             $bid_id = $_REQUEST['chkMultipleBid'];
             $mechanic_id = $_REQUEST['mechanic'];
             $budget_id = intval($_REQUEST['budget_id']);
             $budget = new Budget();
             if (!$budget->loadById($budget_id)) {
                 $_SESSION['workitem_error'] = "Invalid budget!";
             }
             if (count($bid_id) > 0) {
                 //only runners can accept bids
                 if (($is_project_runner || $workitem->getRunnerId() == Session::uid() || $user->getIs_admin() == 1 && $is_runner) && !$workitem->hasAcceptedBids() && $workitem->getStatus() == "Bidding") {
                     $total = 0;
                     foreach ($bid_id as $bid) {
                         $currentBid = new Bid();
                         $currentBid->findBidById($bid);
                         $total = $total + $currentBid->getBid_amount();
                     }
                     $remainingFunds = $budget->getRemainingFunds();
                     if ($total <= $remainingFunds) {
                         foreach ($bid_id as $bid) {
                             $bids = (array) $workitem->getBids($workitem->getId());
                             $exists = false;
                             foreach ($bids as $array) {
                                 if ($array['id'] == $bid) {
                                     if ($array['bidder_id'] == $mechanic_id) {
                                         $is_mechanic = true;
                                     } else {
                                         $is_mechanic = false;
                                     }
                                     $exists = true;
                                     break;
                                 }
                             }
                             if ($exists) {
                                 $bid_info = $workitem->acceptBid($bid, $budget_id, $is_mechanic);
                                 // Journal notification
                                 $journal_message .= '@' . $_SESSION['nickname'] . " accepted {$bid_info['bid_amount']} from " . $bid_info['nickname'] . " " . ($is_mechanic ? ' as Developer ' : '') . "on #" . $bid_info['worklist_id'] . " Status set to *In Progress*.";
                                 // mail notification
                                 Notification::workitemNotify(array('type' => 'bid_accepted', 'workitem' => $workitem, 'recipients' => array('mechanic', 'followers')));
                             } else {
                                 $_SESSION['workitem_error'] = "Failed to accept bid, bid has been deleted!";
                             }
                         }
                         // Send email to not accepted bidders
                         $this->sendMailToDiscardedBids($worklist_id);
                         $runner = new User();
                         $runner->findUserById($workitem->getRunnerId());
                         $runner->updateBudget(-$total, $workitem->getBudget_id());
                     } else {
                         $overBudget = money_format('%i', $total - $remainingFunds);
                         $_SESSION['workitem_error'] = "Failed to accept bids. Accepting this bids would make you " . $overBudget . " over your budget!";
                     }
                 }
             }
         }
         $redirectToDefaultView = true;
     }
     //Withdraw a bid
     if ($action == "withdraw_bid") {
         if (isset($_REQUEST['bid_id'])) {
             $this->withdrawBid(intval($_REQUEST['bid_id']), $_REQUEST['withdraw_bid_reason']);
         } else {
             $fee_id = intval($_REQUEST['fee_id']);
             $res = mysql_query('SELECT f.bid_id, f.amount, w.runner_id FROM `' . FEES . '` AS f, ' . WORKLIST . ' AS w WHERE f.`id`=' . $fee_id . ' AND f.worklist_id = w.id');
             $fee = mysql_fetch_object($res);
             if ((int) $fee->bid_id !== 0) {
                 $this->withdrawBid($fee->bid_id, $_REQUEST['withdraw_bid_reason']);
             } else {
                 $this->deleteFee($fee_id);
             }
             // Update Runner's Budget
             $runner = new User();
             $runner->findUserById($fee->runner_id);
             $runner->updateBudget($fee->amount, $workitem->getBudget_id());
         }
         $redirectToDefaultView = true;
     }
     //Decline a bid
     if ($action == "decline_bid") {
         if (isset($_REQUEST['bid_id'])) {
             $this->withdrawBid(intval($_REQUEST['bid_id']), $_REQUEST['decline_bid_reason']);
         } else {
             $fee_id = intval($_REQUEST['fee_id']);
             $res = mysql_query('SELECT f.bid_id, f.amount, w.runner_id FROM `' . FEES . '` AS f, ' . WORKLIST . ' AS w WHERE f.`id`=' . $fee_id . ' AND f.worklist_id = w.id');
             $fee = mysql_fetch_object($res);
             if ((int) $fee->bid_id !== 0) {
                 $this->withdrawBid($fee->bid_id, $_REQUEST['decline_bid_reason']);
             } else {
                 $this->deleteFee($fee_id);
             }
             // Update Runner's Budget
             $runner = new User();
             $runner->findUserById($fee->runner_id);
             $runner->updateBudget($fee->amount, $workitem->getBudget_id());
         }
         $redirectToDefaultView = true;
     }
     // we have a Journal message, send it to Journal - except for DRAFTS
     if (isset($journal_message) && $workitem->getStatus() != 'Draft') {
         Utils::systemNotification($journal_message);
         //$postProcessUrl = WORKITEM_URL . $worklist_id . "?msg=" . $journal_message;
     }
     if ($redirectToDefaultView) {
         $this->redirect('./' . $worklist_id);
     }
     // handle the makeshift error I made..
     $erroneous = false;
     if (isset($_SESSION['workitem_error'])) {
         $erroneous = true;
         $the_errors = $_SESSION['workitem_error'];
         unset($_SESSION['workitem_error']);
         $this->write('erroneous', $erroneous);
         $this->write('the_errors', $the_errors);
     }
     // Process the request normally and display the page.
     //get workitem from db
     $worklist = $workitem->getWorkItem($worklist_id);
     $this->write('worklist', $worklist);
     //get bids
     $bids = $workitem->getBids($worklist_id);
     // get only those bids that have not expired, used to determine whether
     // runner can edit the job notes
     $this->write('activeBids', (array) $workitem->getBids($workitem->getId(), false));
     //Findout if the current user already has any bids.
     // Yes, it's a String instead of boolean to make it easy to use in JS.
     // Suppress names if not is_runner, or creator of Item. Still show if it's user's bid.
     $currentUserHasBid = "false";
     if (!empty($bids) && is_array($bids)) {
         foreach ($bids as &$bid) {
             if ($bid['email'] == $currentUsername) {
                 $currentUserHasBid = "true";
                 //break;
             }
             if (!($user->getId() == $bid['bidder_id'] || $user->isRunnerOfWorkitem($workitem) || $workitem->getIsRelRunner() && !$worklist['runner_id'])) {
                 if ($user->getIs_admin() == 0) {
                     $bid['nickname'] = '*name hidden*';
                     $bid['bid_amount'] = '***';
                     $bid['email'] = '********';
                     $bid['notes'] = '********';
                 }
             }
             $bid['bid_created'] = $this->convertTimezone($bid['unix_bid_created']);
             if ($bid['unix_bid_accepted'] > 0) {
                 $bid['bid_accepted'] = $this->convertTimezone($bid['unix_bid_accepted']);
             } else {
                 $bid['bid_accepted'] = '';
             }
             if ($bid['unix_done_full'] > 0 && !empty($bid['unix_done_full'])) {
                 $bid['unix_done_full'] = $this->convertTimezone($bid['unix_done_full']);
             } else {
                 $bid['unix_done_full'] = '';
             }
             // calculate Total Time to Complete
             if (isset($bid['unix_done_by']) && $bid['unix_done_by'] != 0) {
                 $timeToComplete = (int) $bid['unix_done_by'] - (int) $bid['unix_bid_created'];
                 if ($bid['unix_bid_accepted'] > 0) {
                     $timeElapsed = (int) $bid['unix_now'] - (int) $bid['unix_bid_accepted'];
                     $timeToComplete -= $timeElapsed;
                 }
                 $fullDays = floor($timeToComplete / (60 * 60 * 24));
                 $fullHours = floor(($timeToComplete - $fullDays * 60 * 60 * 24) / (60 * 60));
                 $fullMinutes = floor(($timeToComplete - $fullDays * 60 * 60 * 24 - $fullHours * 60 * 60) / 60);
                 $bid['time_to_complete'] = $fullDays . ($fullDays == 1 ? " day, " : " days, ") . $fullHours . ($fullHours == 1 ? " hour and " : " hours and ") . $fullMinutes . ($fullMinutes == 1 ? " minute." : " minutes.");
             } else {
                 $bid['time_to_complete'] = null;
             }
         }
     }
     // break reference to $bid
     unset($bid);
     //get fees
     $fees = $workitem->getFees($worklist_id);
     $this->write('fees', $fees);
     $user_id = isset($_SESSION['userid']) ? $_SESSION['userid'] : "";
     $is_runner = isset($_SESSION['is_runner']) ? $_SESSION['is_runner'] : 0;
     $is_admin = isset($_SESSION['is_admin']) ? $_SESSION['is_admin'] : 0;
     $is_payer = isset($_SESSION['is_payer']) ? $_SESSION['is_payer'] : 0;
     $creator_id = isset($worklist['creator_id']) ? $worklist['creator_id'] : 0;
     $mechanic_id = isset($worklist['mechanic_id']) ? $worklist['mechanic_id'] : 0;
     $runner_id = isset($worklist['runner_id']) ? $worklist['runner_id'] : 0;
     $status_error = '';
     $has_budget = 0;
     if (!empty($user_id)) {
         $user = new User();
         $user->findUserById($user_id);
         if ($user->getBudget() > 0) {
             $has_budget = 1;
         }
         // fee defaults to 0 for internal users
         $crFee = 0;
         if (!$user->isInternal()) {
             // otherwise, lookup reviewer fee on the Project
             $crFee = $this->getCRFee($workitem);
         }
         $this->write('crFee', $crFee);
     }
     $workitem = WorkItem::getById($worklist['id']);
     if ($worklist['project_id']) {
         $workitem_project = new Project($worklist['project_id']);
     }
     $projects = Project::getProjects();
     $allowEdit = false;
     $classEditable = "";
     if ($workitem->getIsRelRunner() && is_null($worklist['runner_id']) || $user->getIs_admin() == 1 && $is_runner || $creator_id == $user_id && $worklist['status'] == 'Suggestion' && is_null($worklist['runner_id']) || $runner_id == $user_id) {
         $allowEdit = true;
         if ($action != "edit") {
             $classEditable = " editable";
         }
     }
     $this->write('classEditable', $classEditable);
     $this->write('allowEdit', $allowEdit);
     $hideFees = false;
     if ($worklist['status'] == 'Bidding' || $worklist['status'] == 'Suggestion') {
         $hideFees = true;
     }
     $this->write('hideFees', $hideFees);
     $this->write('bids', $bids);
     $this->write('userHasCodeReviewRights', $this->hasCodeReviewRights($user_id, $workitem));
     $this->write('mechanic', $workitem->getUserDetails($worklist['mechanic_id']));
     $reviewer = new User();
     $reviewer->findUserById($workitem->getCReviewerId());
     $this->write('reviewer', $reviewer);
     $this->write('action_error', isset($action_error) ? $action_error : '');
     $this->write('comments', Comment::findCommentsForWorkitem($worklist['id']));
     $this->write('entries', $this->getTaskPosts($worklist['id']));
     $this->write('message', isset($message) ? $message : '');
     $this->write('currentUserHasBid', $currentUserHasBid);
     $this->write('has_budget', $has_budget);
     $this->write('promptForReviewUrl', $promptForReviewUrl);
     $this->write('status_error', $status_error);
     $this->write('{{userinfotoshow}}', isset($_REQUEST['userinfotoshow']) && isset($_SESSION['userid']) ? $_REQUEST['userinfotoshow'] : 0);
     $job_analytics = VisitQueryTools::visitQuery($worklist_id);
     $this->write('viewCount', $job_analytics['views']);
     $job_views = $job_analytics['views'] > 1 ? " views" : " view";
     $this->write('views', $job_views);
     parent::run();
 }
Example #6
0
 public function taskEntries()
 {
     // we don't need comments from status entries cause
     // we are mixing them with real comments
     $worklist_entries = self::removeCommentsEntries($this->read('entries'));
     // let's group reply comments so they get rendered toghether, no matter their
     // date according to the rest of the entries of other groups (only the first
     // level comment date is taken for orfering)
     $comments = self::groupComments($this->read('comments'));
     $entries = array_merge($worklist_entries, $comments);
     usort($entries, array('JobView', 'sortEntries'));
     $ret = '';
     $now = 0;
     foreach ($entries as $entry) {
         if (!$now) {
             $now = strtotime(Model::now());
         }
         if (is_object($entry) && get_class($entry) == 'EntryModel') {
             $id = $entry->id;
             $type = 'worklist';
             $date = strtotime($entry->date);
             $content = self::formatEntry($entry);
             $ret .= '<li entryid="' . $id . '" date="' . $date . '" type="' . $type . '">' . '<h4>' . Utils::relativeTime($date - $now) . '</h4>' . $content . '</li>';
         } else {
             foreach ($entry['content'] as $comment) {
                 $commentObj = $comment['comment'];
                 $ret .= '<li id="comment-' . $comment['id'] . '" class="depth-' . $comment['depth'] . '">' . '    <div class="comment">' . '        <a class="commenter-avatar" href="./user/' . $commentObj->getUser()->getId() . '">' . '            <img class="picture profile-link" src="' . $commentObj->getUser()->getAvatar() . '" title="Profile Picture - ' . $commentObj->getUser()->getNickname() . '" />' . '        </a>' . '        <div class="comment-container">' . '            <div class="comment-info">' . '                <a class="author profile-link" href="./user/' . $commentObj->getUser()->getId() . '">' . '                    ' . $commentObj->getUser()->getNickname() . '                </a>' . '                <a class="date" href="./' . $this->worklist['id'] . '#comment-' . $comment['id'] . '">' . '                ' . $commentObj->getRelativeDate() . '                </a>' . '            </div>' . '            <div class="comment-text">' . '              ' . $commentObj->getCommentWithLinks() . '            </div>' . '        </div>' . '    </div>' . '</li>';
             }
         }
     }
     return $ret;
 }