public function search() { $this->view = null; $user = User::find(Session::uid()); $isRunner = $user->is_runner; $publicOnly = !$user->isInternal(); $query = isset($_REQUEST['query']) ? $_REQUEST['query'] : null; if ($query != null) { if (preg_match("/^\\#?\\d+\$/", $query)) { $id = ltrim($query, '#'); if (Project::isJobId($id)) { echo json_encode(array('redirect' => $id)); return; } } } $conds = array(); $invertedStatusConds = array(); $subConds = array(); $projectFilter = isset($_REQUEST['project_id']) ? $_REQUEST['project_id'] : ''; if (!empty($projectFilter) && $projectFilter != 'All') { $projectId = (int) $projectFilter; $conds[] = "`w`.`project_id` = '{$projectId}'"; $invertedStatusConds[] = "`w`.`project_id` = '{$projectId}'"; } $statusFilter = isset($_REQUEST['status']) && !empty($_REQUEST['status']) ? preg_split('/,/', $_REQUEST['status']) : (empty($query) ? array('Active') : array()); if ($statusFilter) { $statusCond = ''; foreach ($statusFilter as $status) { if (empty($status)) { continue; } switch ($status) { case 'Draft': $statusCond .= (empty($statusCond) ? '' : ' OR ') . "`w`.`status` = '{$status}' AND `w`.`creator_id` = '{$user->getId()}'"; break; case 'Suggestion': case 'Bidding': case 'In Progress': case 'QA Ready': case 'Review': case 'Merged': case 'Done': case 'Pass': $statusCond .= (empty($statusCond) ? '' : ' OR ') . "`w`.`status` = '{$status}'"; break; // Pseudo status filters // Pseudo status filters case 'Code Review': $statusCond .= (empty($statusCond) ? '' : ' OR ') . "`w`.`status` = 'Review'"; break; case 'Needs-Review': $statusCond .= (empty($statusCond) ? '' : ' OR ') . "\n `w`.`status` = 'Review'\n AND `w`.`code_review_started` = 0"; break; case 'Active': $statusCond .= (empty($statusCond) ? '' : ' OR ') . "\n `w`.`status` = 'Suggestion'\n OR `w`.`status` = 'Bidding'\n OR `w`.`status` = 'In Progress'\n OR `w`.`status` = 'QA Ready'\n OR `w`.`status` = 'Review'\n OR `w`.`status` = 'Merged'"; break; } } if (!empty($statusCond)) { $conds[] = "({$statusCond})"; $invertedStatusConds[] = "(\n !({$statusCond})\n AND\n CASE\n WHEN(`w`.`status` = 'Draft')\n THEN `w`.`creator_id` = '{$user->getId()}'\n ELSE true\n END\n )"; } else { $invertedStatusConds[] = "false"; } } else { $conds[] = "(\n `w`.`status` != 'Draft'\n OR (\n `w`.`status` = 'Draft'\n AND `w`.`creator_id` = '{$user->getId()}'\n )\n )"; $invertedStatusConds[] = "false"; } $participants = array(); if (isset($_REQUEST['participated']) && !empty($_REQUEST['participated'])) { $participants = preg_split('/,/', $_REQUEST['participated']); } $mentions = array(); if (preg_match_all('/@([a-zA-Z][a-zA-Z0-9-]+)/', $query, $mentions, PREG_SET_ORDER)) { $mentionUsersFound = array(); foreach ($mentions as $mention) { $mentionUser = User::find($mention[1]); if ($mentionId = $mentionUser->getId()) { $mentionUsersFound[] = $mentionUser->getNickname(); $participants[] = $mentionId; $query = trim(preg_replace('/@' . $mentionUser->getNickname() . '/', '', $query)); } } } if ($publicOnly) { $conds[] = "`w`.is_internal = 0"; $invertedStatusConds[] = "`w`.is_internal = 0"; } if (isset($_REQUEST['following']) && !empty($_REQUEST["following"])) { $followingCond = "(\n SELECT COUNT(*)\n FROM `" . TASK_FOLLOWERS . "` `fol`\n WHERE `fol`.`workitem_id` = `w`.`id`\n AND `fol`.`user_id` = {$user->getId()}\n ) > 0"; $conds[] = $followingCond; $invertedStatusConds[] = $followingCond; } /* text search */ if ($query != null) { preg_match_all('/"(?:\\\\.|[^\\\\"])*"|\\S+/', $query, $matches); foreach ($matches[0] as $match) { $safeQuery = str_replace('\'', '\\\'', rawurldecode($match)); $subConds[] = "(\n MATCH(`sub_w`.`summary`, `sub_w`.`notes`) AGAINST ('{$safeQuery}')\n OR MATCH(`sub_f`.`notes`) AGAINST ('{$safeQuery}')\n OR MATCH(`sub_com`.`comment`) AGAINST ('{$safeQuery}')\n )"; } } if (count($participants) > 0) { foreach ($participants as $participant) { $participantUser = User::find($participant); if (!($participantId = $participantUser->getId())) { continue; } $participantNickname = $participantUser->getNickname(); $participantsCond = "(\n `w`.`mechanic_id` = '{$participantId}'\n OR `w`.`runner_id` = '{$participantId}'\n OR `w`.`creator_id` = '{$participantId}'\n OR `w`.`notes` REGEXP '([[:space:]]|^)@?{$participantNickname}([[:space:]]|\$)'\n OR EXISTS (\n SELECT 1\n FROM `" . FEES . "` AS `involved_f`\n WHERE `involved_f`.`worklist_id` = `w`.`id`\n AND `involved_f`.`withdrawn` = 0\n AND (\n `involved_f`.`notes` REGEXP '([[:space:]]|^)@?{$participantNickname}([[:space:]]|\$)'\n OR `involved_f`.`user_id` = {$participantId}\n )\n LIMIT 1\n )\n OR EXISTS (\n SELECT 1\n FROM `" . COMMENTS . "` AS `involved_com`\n WHERE `involved_com`.`worklist_id` = `w`.`id`\n AND (\n `involved_com`.`user_id` = {$participantId}\n OR `involved_com`.`comment` REGEXP '([[:space:]]|^)@?{$participantNickname}([[:space:]]|\$)'\n )\n LIMIT 1\n )" . ($isRunner || $user->getId() == $participantId ? "\n OR EXISTS (\n SELECT 1\n FROM `" . BIDS . "` AS `involved_b`\n WHERE `involved_b`.`worklist_id` = `w`.`id`\n AND `involved_b`.`bidder_id` = {$participantId}\n LIMIT 1\n )" : '') . "\n )"; $conds[] = $participantsCond; $invertedStatusConds[] = $participantsCond; } } /* labels filter */ if (isset($_REQUEST["labels"]) && !empty($_REQUEST["labels"])) { $labels = preg_split('/,/', $_REQUEST["labels"]); if ($labels) { $labelsCond = ''; foreach ($labels as $label) { if (!strlen(trim($label))) { continue; } $label = mysql_real_escape_string($label); $labelsCond .= (strlen($labelsCond) ? ' AND ' : '') . "labels LIKE '%:{$label}:%'"; } if ($labelsCond) { $conds[] = "({$labelsCond})"; $invertedStatusConds[] = "({$labelsCond})"; } } } $ret = WorkItem::search($query, $conds, $subConds, $_REQUEST['offset'], $_REQUEST['limit']); $invertedStatusRet = WorkItem::searchStats($query, $invertedStatusConds, $subConds); echo json_encode(array_merge($ret, $invertedStatusRet)); }