Index: application/controllers/CommentController.class.php
===================================================================
--- application/controllers/CommentController.class.php (revision 0)
+++ application/controllers/CommentController.class.php (revision 0)
@@ -78,8 +78,8 @@
ApplicationLogs::createLog($comment, active_project(), ApplicationLogs::ACTION_ADD);
- // Subscribe user to message (if $object is message)
- if($object instanceof ProjectMessage) {
+ // Subscribe user to object (if $object is subscribible)
+ if($object->isSubscribible()) {
if(!$object->isSubscriber(logged_user())) {
$object->subscribeUser(logged_user());
} // if
Index: application/controllers/DashboardController.class.php proyectos/application/controllers/DashboardController.class.php
===================================================================
--- application/controllers/DashboardController.class.php (revision 0)
+++ application/controllers/DashboardController.class.php (revision 0)
@@ -75,6 +75,17 @@
tpl_assign('active_projects', logged_user()->getActiveProjects());
$this->setSidebar(get_template_path('my_tasks_sidebar', 'dashboard'));
} // my_tasks
+
+ /**
+ * Return all tickets assigned to this user
+ *
+ * @param void
+ * @return array
+ */
+ function my_tickets() {
+ tpl_assign('active_projects', logged_user()->getActiveProjects());
+ $this->setSidebar(get_template_path('index_sidebar', 'trac'));
+ } // my_trac
} // DashboardController
Index: application/controllers/FilesController.class.php proyectos/application/controllers/FilesController.class.php
===================================================================
--- application/controllers/FilesController.class.php (revision 0)
+++ application/controllers/FilesController.class.php (revision 0)
@@ -675,6 +675,7 @@
} // foreach
DB::commit();
+ $object->onAttachFiles($attach_files);
flash_success(lang('success attach files', $counter));
$this->redirectToUrl($object->getObjectUrl());
Index: application/controllers/MessageController.class.php proyectos/application/controllers/MessageController.class.php
===================================================================
--- application/controllers/MessageController.class.php (revision 0)
+++ application/controllers/MessageController.class.php (revision 0)
@@ -356,9 +356,9 @@
} // if
if($message->subscribeUser(logged_user())) {
- flash_success('success subscribe to message');
+ flash_success(lang('success subscribe to message'));
} else {
- flash_error('error subscribe to message');
+ flash_error(lang('error subscribe to message'));
} // if
$this->redirectToUrl($message->getViewUrl());
} // subscribe
@@ -382,9 +382,9 @@
} // if
if($message->unsubscribeUser(logged_user())) {
- flash_success('success unsubscribe to message');
+ flash_success(lang('success unsubscribe to message'));
} else {
- flash_error('error unsubscribe to message');
+ flash_error(lang('error unsubscribe to message'));
} // if
$this->redirectToUrl($message->getViewUrl());
} // unsubscribe
Index: application/controllers/TracController.class.php proyectos/application/controllers/TracController.class.php
===================================================================
--- application/controllers/TracController.class.php (revision 0)
+++ application/controllers/TracController.class.php (revision 0)
@@ -0,0 +1,686 @@
+getId());
+
+ list($categories, $pagination) = Categories::paginate(
+ array(
+ 'conditions' => $conditions,
+ 'order' => '`name`'
+ ),
+ config_option('categories_per_page', 25),
+ $page
+ ); // paginate
+
+ tpl_assign('categories', $categories);
+ tpl_assign('categories_pagination', $pagination);
+
+ $this->setSidebar(get_template_path('trac_sidebar', 'trac'));
+ } // categories
+
+ /**
+ * Return project tickets
+ *
+ * @access public
+ * @param void
+ * @return array
+ */
+ function index() {
+ $page = (integer) array_var($_GET, 'page', 1);
+ if($page < 0) $page = 1;
+
+ $closed = (boolean) array_var($_GET, 'closed', false);
+ $conditions = DB::prepareString('`closed_on` '.($closed ? '>' : '=').' ? and `project_id` = ?', array(EMPTY_DATETIME, active_project()->getId()));
+ if(!logged_user()->isMemberOfOwnerCompany()) {
+ $conditions .= DB::prepareString(' AND `is_private` = ?', array(0));
+ } // if
+
+ if ($closed) {
+ $order = '`closed_on` DESC';
+ } else {
+ $order = '`created_on` DESC';
+ } // if
+
+ list($tickets, $pagination) = ProjectTickets::paginate(
+ array(
+ 'conditions' => $conditions,
+ 'order' => $order
+ ),
+ config_option('tickets_per_page', 25),
+ $page
+ ); // paginate
+
+ tpl_assign('closed', $closed);
+ tpl_assign('tickets', $tickets);
+ tpl_assign('tickets_pagination', $pagination);
+
+ $this->setSidebar(get_template_path('index_sidebar', 'trac'));
+ } // index
+
+ /**
+ * Add ticket
+ *
+ * @access public
+ * @param void
+ * @return null
+ */
+ function add() {
+ $this->addHelper('ticket');
+
+ if(!ProjectTicket::canAdd(logged_user(), active_project())) {
+ flash_error(lang('no access permissions'));
+ $this->redirectToReferer(get_url('trac'));
+ } // if
+
+ $ticket = new ProjectTicket();
+ $ticket->setProjectId(active_project()->getId());
+ $ticket_data = array_var($_POST, 'ticket');
+
+ tpl_assign('ticket', $ticket);
+ tpl_assign('ticket_data', $ticket_data);
+
+ if(is_array(array_var($_POST, 'ticket'))) {
+ try {
+ $uploaded_files = ProjectFiles::handleHelperUploads(active_project());
+ } catch(Exception $e) {
+ $uploaded_files = null;
+ } // try
+
+ try {
+ $ticket->setFromAttributes($ticket_data);
+
+ $assigned_to = explode(':', array_var($ticket_data, 'assigned_to', ''));
+ $ticket->setAssignedToCompanyId(array_var($assigned_to, 0, 0));
+ $ticket->setAssignedToUserId(array_var($assigned_to, 1, 0));
+
+ // Options are reserved only for members of owner company
+ if(!logged_user()->isMemberOfOwnerCompany()) {
+ $ticket->setIsPrivate(false);
+ } // if
+
+ DB::beginWork();
+ $ticket->save();
+
+ if(is_array($uploaded_files)) {
+ foreach($uploaded_files as $uploaded_file) {
+ $ticket->attachFile($uploaded_file);
+ $uploaded_file->setIsPrivate($ticket->isPrivate());
+ $uploaded_file->setIsVisible(true);
+ $uploaded_file->setExpirationTime(EMPTY_DATETIME);
+ $uploaded_file->save();
+ } // if
+ } // if
+
+ ApplicationLogs::createLog($ticket, active_project(), ApplicationLogs::ACTION_ADD);
+ DB::commit();
+
+ // Try to send notifications but don't break submission in case of an error
+ try {
+ if ($ticket->getAssignedToUserId()) {
+ $ticket_data['notify_user_' . $ticket->getAssignedToUserId()] = 'checked';
+ }
+
+ $notify_people = array();
+ $project_companies = active_project()->getCompanies();
+ foreach($project_companies as $project_company) {
+ $company_users = $project_company->getUsersOnProject(active_project());
+ if(is_array($company_users)) {
+ foreach($company_users as $company_user) {
+ if((array_var($ticket_data, 'notify_company_' . $project_company->getId()) == 'checked') || (array_var($ticket_data, 'notify_user_' . $company_user->getId()))) {
+ $ticket->subscribeUser($company_user); // subscribe
+ $notify_people[] = $company_user;
+ } // if
+ } // if
+ } // if
+ } // if
+
+ Notifier::ticket($ticket, $notify_people, 'new_ticket', $ticket->getCreatedBy());
+ } catch(Exception $e) {
+
+ } // try
+
+ flash_success(lang('success add ticket', $ticket->getSummary()));
+ $this->redirectTo('trac');
+
+ // Error...
+ } catch(Exception $e) {
+ DB::rollback();
+
+ if(is_array($uploaded_files)) {
+ foreach($uploaded_files as $uploaded_file) {
+ $uploaded_file->delete();
+ } // foreach
+ } // if
+
+ $ticket->setNew(true);
+ tpl_assign('error', $e);
+ } // try
+
+ } // if
+ } // add
+
+ /**
+ * Edit specific ticket
+ *
+ * @access public
+ * @param void
+ * @return null
+ */
+ function edit() {
+ $this->addHelper('textile');
+ $this->addHelper('ticket');
+
+ $ticket = ProjectTickets::findById(get_id());
+ if(!($ticket instanceof ProjectTicket)) {
+ flash_error(lang('ticket dnx'));
+ $this->redirectTo('trac');
+ } // if
+
+ if(!$ticket->canView(logged_user())) {
+ flash_error(lang('no access permissions'));
+ $this->redirectToReferer(get_url('trac'));
+ } // if
+
+ $ticket_data = array_var($_POST, 'ticket');
+ if(!is_array($ticket_data)) {
+ $ticket_data = array(
+ 'is_private' => $ticket->isPrivate(),
+ 'summary' => $ticket->getSummary(),
+ 'priority' => $ticket->getPriority(),
+ 'type' => $ticket->getType(),
+ 'category_id' => $ticket->getCategoryId(),
+ 'assigned_to' => $ticket->getAssignedToCompanyId() . ':' . $ticket->getAssignedToUserId()
+ ); // array
+ } // if
+
+ tpl_assign('ticket', $ticket);
+ tpl_assign('ticket_data', $ticket_data);
+ tpl_assign('subscribers', $ticket->getSubscribers());
+ tpl_assign('changes', $ticket->getChanges());
+
+ $this->setSidebar(get_template_path('edit_sidebar', 'trac'));
+
+ if(is_array(array_var($_POST, 'ticket'))) {
+ if(!$ticket->canEdit(logged_user())) {
+ flash_error(lang('no access permissions'));
+ $this->redirectTo('trac');
+ } else {
+ $old_fields = array(
+ 'summary' => $ticket->getSummary(),
+ 'priority' => $ticket->getPriority(),
+ 'type' => $ticket->getType(),
+ 'category' => $ticket->getCategory(),
+ 'assigned to' => $ticket->getAssignedTo()
+ );
+ $old_private = $ticket->isPrivate();
+
+ try {
+ $ticket->setFromAttributes($ticket_data);
+ $ticket->setUpdated('settings');
+
+ // Options are reserved only for members of owner company
+ if(!logged_user()->isMemberOfOwnerCompany()) {
+ $ticket->setIsPrivate($old_private);
+ } // if
+
+ $old_assigned_user_id = $ticket->getAssignedToUserId();
+ $assigned_to = explode(':', array_var($ticket_data, 'assigned_to', ''));
+ $ticket->setAssignedToCompanyId(array_var($assigned_to, 0, 0));
+ $ticket->setAssignedToUserId(array_var($assigned_to, 1, 0));
+
+ DB::beginWork();
+ $ticket->save();
+
+ ApplicationLogs::createLog($ticket, $ticket->getProject(), ApplicationLogs::ACTION_EDIT);
+ DB::commit();
+
+ $user = $ticket->getAssignedToUser();
+ if ($user instanceof User && $user->getId() != $old_assigned_user_id) {
+ if(!$ticket->isSubscriber($user)) {
+ $ticket->subscribeUser($user);
+ } // if
+ } // if
+
+ $new_fields = array(
+ 'summary' => $ticket->getSummary(),
+ 'priority' => $ticket->getPriority(),
+ 'type' => $ticket->getType(),
+ 'category' => $ticket->getCategory(),
+ 'assigned to' => $ticket->getAssignedTo()
+ );
+
+ foreach ($old_fields as $type => $old_field) {
+ $new_field = $new_fields[$type];
+ if ($old_field === $new_field) {
+ continue;
+ }
+ $from_data = ($old_field instanceof ApplicationDataObject) ? $old_field->getObjectName() : $old_field;
+ $to_data = ($new_field instanceof ApplicationDataObject) ? $new_field->getObjectName() : $new_field;
+
+ $change = new TicketChange();
+ $change->setTicketId($ticket->getId());
+ $change->setType($type);
+ $change->setFromData($from_data);
+ $change->setToData($to_data);
+ $change->save();
+ } // foreach
+
+ try {
+ Notifier::ticket($ticket, $ticket->getSubscribers(), 'edit_ticket', $ticket->getUpdatedBy());
+ } catch(Exception $e) {
+ // nothing here, just suppress error...
+ } // try
+
+ flash_success(lang('success edit ticket', $ticket->getSummary()));
+ $this->redirectToUrl($ticket->getViewUrl());
+
+ } catch(Exception $e) {
+ DB::rollback();
+ tpl_assign('error', $e);
+ } // try
+ } // if
+ } // if
+ } // edit
+
+ /**
+ * Update message options. This is execute only function and if we don't have
+ * options in post it will redirect back to the message
+ *
+ * @param void
+ * @return null
+ */
+ function update_options() {
+ $ticket = ProjectTickets::findById(get_id());
+ if(!($ticket instanceof ProjectTicket)) {
+ flash_error(lang('ticket dnx'));
+ $this->redirectTo('trac');
+ } // if
+
+ if(!$ticket->canUpdateOptions(logged_user())) {
+ flash_error(lang('no access permissions'));
+ $this->redirectToReferer(get_url('trac'));
+ } // if
+
+ $ticket_data = array_var($_POST, 'ticket');
+ if(is_array(array_var($_POST, 'ticket'))) {
+ try {
+ $old_private = $ticket->isPrivate();
+ $ticket->setIsPrivate((boolean) array_var($ticket_data, 'is_private', $ticket->isPrivate()));
+
+ DB::beginWork();
+ $ticket->save();
+ ApplicationLogs::createLog($ticket, $ticket->getProject(), ApplicationLogs::ACTION_EDIT);
+ DB::commit();
+
+ if ($old_private != $ticket->isPrivate()) {
+ $change = new TicketChange();
+ $change->setTicketId($ticket->getId());
+ $change->setType('private');
+ $change->setFromData($old_private ? 'yes' : 'no');
+ $change->setToData($ticket->isPrivate() ? 'yes' : 'no');
+ $change->save();
+ }
+
+ flash_success(lang('success edit ticket', $ticket->getSummary()));
+ } catch(Exception $e) {
+ flash_error(lang('error update ticket options'), $ticket->getSummary());
+ } // try
+ } // if
+ $this->redirectToUrl($ticket->getViewUrl());
+ } // update_options
+
+ /**
+ * Close specific ticket
+ *
+ * @access public
+ * @param void
+ * @return null
+ */
+ function close() {
+ $ticket = ProjectTickets::findById(get_id());
+ if(!($ticket instanceof ProjectTicket)) {
+ flash_error(lang('ticket dnx'));
+ $this->redirectTo('trac');
+ } // if
+
+ if(!$ticket->canChangeStatus(logged_user())) {
+ flash_error(lang('no access permissions'));
+ $this->redirectToReferer(get_url('trac'));
+ } // if
+
+ $status = $ticket->isClosed() ? 'closed' : 'open';
+
+ try {
+ DB::beginWork();
+ $ticket->closeTicket();
+ ApplicationLogs::createLog($ticket, active_project(), ApplicationLogs::ACTION_CLOSE);
+ DB::commit();
+
+ if ($status != 'closed') {
+ $change = new TicketChange();
+ $change->setTicketId($ticket->getId());
+ $change->setType('status');
+ $change->setFromData($status);
+ $change->setToData('closed');
+ $change->save();
+ }
+
+ try {
+ Notifier::ticket($ticket, $ticket->getSubscribers(), 'close_ticket', $ticket->getClosedBy());
+ } catch(Exception $e) {
+ // nothing here, just suppress error...
+ } // try
+
+ flash_success(lang('success close ticket'));
+ } catch(Exception $e) {
+ flash_error(lang('error close ticket'));
+ DB::rollback();
+ } // try
+
+ $this->redirectToUrl(ProjectTickets::getIndexUrl(true));
+ } // close
+
+ /**
+ * Open specific ticket
+ *
+ * @access public
+ * @param void
+ * @return null
+ */
+ function open() {
+ $ticket = ProjectTickets::findById(get_id());
+ if(!($ticket instanceof ProjectTicket)) {
+ flash_error(lang('ticket dnx'));
+ $this->redirectTo('trac');
+ } // if
+
+ if(!$ticket->canChangeStatus(logged_user())) {
+ flash_error(lang('no access permissions'));
+ $this->redirectToReferer(get_url('trac'));
+ } // if
+
+ $status = $ticket->isClosed() ? 'closed' : 'open';
+
+ try {
+ DB::beginWork();
+ $ticket->openTicket();
+ ApplicationLogs::createLog($ticket, active_project(), ApplicationLogs::ACTION_OPEN);
+ DB::commit();
+
+ if ($status != 'open') {
+ $change = new TicketChange();
+ $change->setTicketId($ticket->getId());
+ $change->setType('status');
+ $change->setFromData($status);
+ $change->setToData('open');
+ $change->save();
+ }
+
+ try {
+ Notifier::ticket($ticket, $ticket->getSubscribers(), 'open_ticket', logged_user());
+ } catch(Exception $e) {
+ // nothing here, just suppress error...
+ } // try
+
+ flash_success(lang('success open ticket'));
+ } catch(Exception $e) {
+ flash_error(lang('error open ticket'));
+ DB::rollback();
+ } // try
+
+ $this->redirectToUrl(ProjectTickets::getIndexUrl());
+ } // open
+
+ /**
+ * Delete specific ticket
+ *
+ * @access public
+ * @param void
+ * @return null
+ */
+ function delete() {
+ $ticket = ProjectTickets::findById(get_id());
+ if(!($ticket instanceof ProjectTicket)) {
+ flash_error(lang('ticket dnx'));
+ $this->redirectTo('trac');
+ } // if
+
+ if(!$ticket->canDelete(logged_user())) {
+ flash_error(lang('no access permissions'));
+ $this->redirectTo('trac');
+ } // if
+
+ try {
+
+ DB::beginWork();
+ $ticket->delete();
+ ApplicationLogs::createLog($ticket, $ticket->getProject(), ApplicationLogs::ACTION_DELETE);
+ DB::commit();
+
+ flash_success(lang('success deleted ticket', $ticket->getSummary()));
+ } catch(Exception $e) {
+ DB::rollback();
+ flash_error(lang('error delete ticket'));
+ } // try
+
+ $this->redirectTo('trac');
+ } // delete
+
+ /**
+ * Add a new category
+ *
+ * @access public
+ * @param void
+ * @return null
+ */
+ function add_category() {
+ if(!Category::canAdd(logged_user(), active_project())) {
+ flash_error(lang('no access permissions'));
+ $this->redirectToReferer(get_url('trac', 'categories'));
+ } // if
+
+ $category = new Category();
+ $category_data = array_var($_POST, 'category');
+
+ tpl_assign('category', $category);
+ tpl_assign('category_data', $category_data);
+
+ if(is_array(array_var($_POST, 'category'))) {
+ try {
+ $category->setFromAttributes($category_data);
+ $category->setProjectId(active_project()->getId());
+
+ DB::beginWork();
+ $category->save();
+
+ ApplicationLogs::createLog($category, active_project(), ApplicationLogs::ACTION_ADD);
+ DB::commit();
+
+ flash_success(lang('success add category', $category->getName()));
+ $this->redirectTo('trac', 'categories');
+
+ // Error...
+ } catch(Exception $e) {
+ DB::rollback();
+
+ $category->setNew(true);
+ tpl_assign('error', $e);
+ } // try
+
+ } // if
+ } // add_category
+
+ /**
+ * Edit specific category
+ *
+ * @access public
+ * @param void
+ * @return null
+ */
+ function edit_category() {
+ $this->setTemplate('add_category');
+
+ $category = Categories::findById(get_id());
+ if(!($category instanceof Category)) {
+ flash_error(lang('category dnx'));
+ $this->redirectTo('trac', 'categories');
+ } // if
+
+ if(!$category->canView(logged_user())) {
+ flash_error(lang('no access permissions'));
+ $this->redirectToReferer(get_url('trac', 'categories'));
+ } // if
+
+ $category_data = array_var($_POST, 'category');
+ if(!is_array($category_data)) {
+ $category_data = array(
+ 'name' => $category->getName(),
+ 'description' => $category->getDescription()
+ ); // array
+ } // if
+
+ tpl_assign('category', $category);
+ tpl_assign('category_data', $category_data);
+
+ if(is_array(array_var($_POST, 'category'))) {
+ if(!$category->canEdit(logged_user())) {
+ flash_error(lang('no access permissions'));
+ $this->redirectTo('trac', 'categories');
+ } else {
+ try {
+ $category->setFromAttributes($category_data);
+
+ DB::beginWork();
+ $category->save();
+
+ ApplicationLogs::createLog($category, $category->getProject(), ApplicationLogs::ACTION_EDIT);
+ DB::commit();
+
+ flash_success(lang('success edit category', $category->getName()));
+ $this->redirectToUrl($category->getViewUrl());
+
+ } catch(Exception $e) {
+ DB::rollback();
+ tpl_assign('error', $e);
+ } // try
+ } // if
+ } // if
+ } // edit_category
+
+ /**
+ * Delete specific category
+ *
+ * @access public
+ * @param void
+ * @return null
+ */
+ function delete_category() {
+ $category = Categories::findById(get_id());
+ if(!($category instanceof Category)) {
+ flash_error(lang('category dnx'));
+ $this->redirectTo('trac', 'categories');
+ } // if
+
+ if(!$category->canDelete(logged_user())) {
+ flash_error(lang('no access permissions'));
+ $this->redirectToReferer(get_url('trac', 'categories'));
+ } // if
+
+ try {
+
+ DB::beginWork();
+ $category->delete();
+ ApplicationLogs::createLog($category, $category->getProject(), ApplicationLogs::ACTION_DELETE);
+ DB::commit();
+
+ flash_success(lang('success deleted category', $category->getName()));
+ } catch(Exception $e) {
+ DB::rollback();
+ flash_error(lang('error delete category'));
+ } // try
+
+ $this->redirectTo('trac', 'categories');
+ } // delete
+
+ // ---------------------------------------------------
+ // Subscriptions
+ // ---------------------------------------------------
+
+ /**
+ * Subscribe to ticket
+ *
+ * @param void
+ * @return null
+ */
+ function subscribe() {
+ $ticket = ProjectTickets::findById(get_id());
+ if(!($ticket instanceof ProjectTicket)) {
+ flash_error(lang('ticket dnx'));
+ $this->redirectTo('trac');
+ } // if
+
+ if(!$ticket->canView(logged_user())) {
+ flash_error(lang('no access permissions'));
+ $this->redirectTo('trac');
+ } // if
+
+ if($ticket->subscribeUser(logged_user())) {
+ flash_success(lang('success subscribe to ticket'));
+ } else {
+ flash_error(lang('error subscribe to ticket'));
+ } // if
+ $this->redirectToUrl($ticket->getViewUrl());
+ } // subscribe
+
+ /**
+ * Unsubscribe from message
+ *
+ * @param void
+ * @return null
+ */
+ function unsubscribe() {
+ $ticket = ProjectTickets::findById(get_id());
+ if(!($ticket instanceof ProjectTicket)) {
+ flash_error(lang('ticket dnx'));
+ $this->redirectTo('trac');
+ } // if
+
+ if(!$ticket->canView(logged_user())) {
+ flash_error(lang('no access permissions'));
+ $this->redirectTo('trac');
+ } // if
+
+ if($ticket->unsubscribeUser(logged_user())) {
+ flash_success(lang('success unsubscribe to ticket'));
+ } else {
+ flash_error(lang('error unsubscribe to ticket'));
+ } // if
+ $this->redirectToUrl($ticket->getViewUrl());
+ } // unsubscribe
+
+ } // TracController
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/helpers/company_website.php proyectos/application/helpers/company_website.php
===================================================================
--- application/helpers/company_website.php (revision 0)
+++ application/helpers/company_website.php (revision 0)
@@ -24,6 +24,7 @@
define('DASHBOARD_TAB_OVERVIEW', 'overview');
define('DASHBOARD_TAB_MY_PROJECTS', 'my_projects');
define('DASHBOARD_TAB_MY_TASKS', 'my_task');
+ define('DASHBOARD_TAB_MY_TICKETS', 'my_tickets');
/**
* Prepare dashboard tabbed navigation
@@ -48,6 +49,11 @@
lang('my tasks'),
get_url('dashboard', 'my_tasks')
));
+ add_tabbed_navigation_item(new TabbedNavigationItem(
+ DASHBOARD_TAB_MY_TICKETS,
+ lang('my tickets'),
+ get_url('dashboard', 'my_tickets')
+ ));
tabbed_navigation_set_selected($selected);
} // dashboard_tabbed_navigation
Index: application/helpers/page.php proyectos/application/helpers/page.php
===================================================================
--- application/helpers/page.php (revision 0)
+++ application/helpers/page.php (revision 0)
@@ -513,11 +513,10 @@
function add_stylesheet_to_page($href, $title = null, $media = null) {
if(!is_valid_url($href)) $href = get_stylesheet_url($href);
$page = PageDescription::instance();
- $page->addRelLink($href, 'Stylesheet', array(
- 'type' => 'text/css',
- 'title' => $title,
- 'media' => $media
- )); // addRelLink
+ $attributes = array('type' => 'text/css');
+ if ($title) $attributes['title'] = $title;
+ if ($media) $attributes['media'] = $media;
+ $page->addRelLink($href, 'Stylesheet', $attributes); // addRelLink
} // add_stylesheet_to_page
/**
Index: application/helpers/project_website.php proyectos/application/helpers/project_website.php
===================================================================
--- application/helpers/project_website.php (revision 0)
+++ application/helpers/project_website.php (revision 0)
@@ -15,12 +15,13 @@
if(!count($args)) return;
BreadCrumbs::instance()->addByFunctionArguments($args);
- } // dashboard_crumbs
+ } // project_crumbs
// Tab IDs
define('PROJECT_TAB_OVERVIEW', 'overview');
define('PROJECT_TAB_MESSAGES', 'messages');
define('PROJECT_TAB_TASKS', 'tasks');
+ define('PROJECT_TAB_TICKETS', 'tickets');
define('PROJECT_TAB_MILESTONES', 'milestones');
define('PROJECT_TAB_FILES', 'files');
define('PROJECT_TAB_TAGS', 'tags');
@@ -50,6 +51,11 @@
get_url('task')
));
add_tabbed_navigation_item(new TabbedNavigationItem(
+ PROJECT_TAB_TICKETS,
+ lang('tickets'),
+ get_url('trac')
+ ));
+ add_tabbed_navigation_item(new TabbedNavigationItem(
PROJECT_TAB_MILESTONES,
lang('milestones'),
get_url('milestone')
@@ -77,6 +83,6 @@
get_url('project', 'people')
));
tabbed_navigation_set_selected($selected);
- } // dashboard_tabbed_navigation
+ } // project_tabbed_navigation
?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/helpers/ticket.php
===================================================================
--- application/helpers/ticket.php (revision 0)
+++ application/helpers/ticket.php (revision 0)
@@ -0,0 +1,67 @@
+ 'selected') : null;
+ $options[] = option_tag(lang($type), $type, $option_attributes);
+ } // foreach
+ return select_box($name, $options, $attributes);
+ } // select_ticket_type
+
+ /**
+ * Render select ticket priority
+ *
+ * @param string $selected priority of ticket
+ * @param array $attributes Additional attributes
+ * @return string
+ */
+ function select_ticket_priority($name, $selected = null, $attributes = null) {
+ if ($selected == null) $selected = 'minor';
+ $types = array('critical', 'major', 'minor', 'trivial');
+ $options = array();
+ foreach($types as $type) {
+ $option_attributes = $type == $selected ? array('selected' => 'selected') : null;
+ $options[] = option_tag(lang($type), $type, $option_attributes);
+ } // foreach
+ return select_box($name, $options, $attributes);
+ } // select_ticket_priority
+
+ /**
+ * Render select ticket priority
+ *
+ * @param Project $project ticket's project to get the categories
+ * @param int $selected category id of ticket
+ * @param array $attributes Additional attributes
+ * @return string
+ */
+ function select_ticket_category($name, $project, $selected = null, $attributes = null) {
+ $categories = $project->getCategories();
+ $option_attributes = $selected ? null: array('selected' => 'selected');
+ $options = array(option_tag(lang('none'), 0, $option_attributes));
+ if ($categories && count($categories)) {
+ foreach($categories as $category) {
+ $option_attributes = $category->getId() == $selected ? array('selected' => 'selected') : null;
+ $options[] = option_tag($category->getName(), $category->getId(), $option_attributes);
+ } // foreach
+ } // if
+ return select_box($name, $options, $attributes);
+ } // select_ticket_priority
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/notifier/Notifier.class.php
===================================================================
--- application/models/notifier/Notifier.class.php (revision 0)
+++ application/models/notifier/Notifier.class.php (revision 0)
@@ -95,6 +95,82 @@
tpl_fetch(get_template_path('new_message', 'notifier'))
); // send
} // newMessage
+
+ /**
+ * Send ticket notification to the list of users ($people)
+ *
+ * @param ProjectTicket $ticket New ticket
+ * @param array $people
+ * @param string $template template to send notification
+ * @param User $user user who send the notification
+ * @return boolean
+ * @throws NotifierConnectionError
+ */
+ static function ticket(ProjectTicket $ticket, $people, $template, $user) {
+ if(!is_array($people) || !count($people)) {
+ return; // nothing here...
+ } // if
+
+ $recepients = array();
+ foreach($people as $subscriber) {
+ if($subscriber->getId() == $user->getId()) {
+ continue; // skip comment author
+ } // if
+
+ $recepients[] = self::prepareEmailAddress($subscriber->getEmail(), $subscriber->getDisplayName());
+ } // foreach
+
+ if(!count($recepients)) {
+ return true; // no recepients
+ } // if
+
+ tpl_assign('ticket', $ticket);
+
+ return self::sendEmail(
+ $recepients,
+ self::prepareEmailAddress($user->getEmail(), $user->getDisplayName()),
+ $ticket->getProject()->getName() . ' - ' . $ticket->getSummary(),
+ tpl_fetch(get_template_path($template, 'notifier'))
+ ); // send
+ } // ticket
+
+ /**
+ * Send some files attached to ticket notification to ticket subscribers
+ *
+ * @param ProjectTicket $ticket
+ * @param array $attached_files Files attached to ticket
+ * @return boolean
+ * @throws NotifierConnectionError
+ */
+ static function attachFilesToTicket(ProjectTicket $ticket, $attached_files) {
+ $all_subscribers = $ticket->getSubscribers();
+ if(!is_array($all_subscribers)) {
+ return true; // no subscribers
+ } // if
+
+ $recepients = array();
+ foreach($all_subscribers as $subscriber) {
+ if($subscriber->getId() == $ticket->getUpdatedById()) {
+ continue; // skip comment author
+ } // if
+
+ $recepients[] = self::prepareEmailAddress($subscriber->getEmail(), $subscriber->getDisplayName());
+ } // foreach
+
+ if(!count($recepients)) {
+ return true; // no recepients
+ } // if
+
+ tpl_assign('ticket', $ticket);
+ tpl_assign('attached_files', $attached_files);
+
+ return self::sendEmail(
+ $recepients,
+ self::prepareEmailAddress($ticket->getUpdatedBy()->getEmail(), $ticket->getUpdatedBy()->getDisplayName()),
+ $ticket->getProject()->getName() . ' - ' . $ticket->getSummary(),
+ tpl_fetch(get_template_path('attach_files_ticket', 'notifier'))
+ ); // send
+ } // attachFilesToTicket
/**
* Send new comment notification to message subscriber
@@ -109,7 +185,36 @@
throw new Error('Invalid comment object');
} // if
- $all_subscribers = $message->getSubscribers();
+ return self::newComment($comment, $message->getSubscribers());
+ } // newMessageComment
+
+ /**
+ * Send new comment notification to ticket subscriber
+ *
+ * @param TicketComment $comment
+ * @return boolean
+ * @throws NotifierConnectionError
+ */
+ static function newTicketComment(Comment $comment) {
+ $ticket = $comment->getObject();
+ if(!($ticket instanceof ProjectTicket)) {
+ throw new Error('Invalid comment object');
+ } // if
+
+ return self::newComment($comment, $ticket->getSubscribers());
+ } // newTicketComment
+
+ /**
+ * Send new comment notification to subscribers
+ *
+ * @access private
+ * @param Comment $comment
+ * @param string $title title of object for subject
+ * @param array $all_subscribers subscribers
+ * @return boolean
+ * @throws NotifierConnectionError
+ */
+ static function newComment(Comment $comment, $all_subscribers) {
if(!is_array($all_subscribers)) {
return true; // no subscribers
} // if
@@ -138,10 +243,10 @@
return self::sendEmail(
$recepients,
self::prepareEmailAddress($comment->getCreatedBy()->getEmail(), $comment->getCreatedByDisplayName()),
- $comment->getProject()->getName() . ' - ' . $message->getTitle(),
+ $comment->getProject()->getName() . ' - ' . $comment->getObject()->getTitle(),
tpl_fetch(get_template_path('new_comment', 'notifier'))
); // send
- } // newMessageComment
+ } // newComment
// ---------------------------------------------------
// Milestone
Index: application/models/project_categories/base/BaseCategories.class.php
===================================================================
--- application/models/project_categories/base/BaseCategories.class.php (revision 0)
+++ application/models/project_categories/base/BaseCategories.class.php (revision 0)
@@ -0,0 +1,220 @@
+ Column type map
+ *
+ * @var array
+ * @static
+ */
+ static private $columns = array('id' => DATA_TYPE_INTEGER, 'project_id' => DATA_TYPE_INTEGER, 'name' => DATA_TYPE_STRING, 'description' => DATA_TYPE_STRING);
+
+ /**
+ * Construct
+ *
+ * @return BaseCategories
+ */
+ function __construct() {
+ parent::__construct('Category', 'project_categories', true);
+ } // __construct
+
+ // -------------------------------------------------------
+ // Description methods
+ // -------------------------------------------------------
+
+ /**
+ * Return array of object columns
+ *
+ * @access public
+ * @param void
+ * @return array
+ */
+ function getColumns() {
+ return array_keys(self::$columns);
+ } // getColumns
+
+ /**
+ * Return column type
+ *
+ * @access public
+ * @param string $column_name
+ * @return string
+ */
+ function getColumnType($column_name) {
+ if(isset(self::$columns[$column_name])) {
+ return self::$columns[$column_name];
+ } else {
+ return DATA_TYPE_STRING;
+ } // if
+ } // getColumnType
+
+ /**
+ * Return array of PK columns. If only one column is PK returns its name as string
+ *
+ * @access public
+ * @param void
+ * @return array or string
+ */
+ function getPkColumns() {
+ return 'id';
+ } // getPkColumns
+
+ /**
+ * Return name of first auto_incremenent column if it exists
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getAutoIncrementColumn() {
+ return 'id';
+ } // getAutoIncrementColumn
+
+ // -------------------------------------------------------
+ // Finders
+ // -------------------------------------------------------
+
+ /**
+ * Do a SELECT query over database with specified arguments
+ *
+ * @access public
+ * @param array $arguments Array of query arguments. Fields:
+ *
+ * - one - select first row
+ * - conditions - additional conditions
+ * - order - order by string
+ * - offset - limit offset, valid only if limit is present
+ * - limit
+ *
+ * @return one or Categories objects
+ * @throws DBQueryError
+ */
+ function find($arguments = null) {
+ if(isset($this) && instance_of($this, 'Categories')) {
+ return parent::find($arguments);
+ } else {
+ return Categories::instance()->find($arguments);
+ } // if
+ } // find
+
+ /**
+ * Find all records
+ *
+ * @access public
+ * @param array $arguments
+ * @return one or Categories objects
+ */
+ function findAll($arguments = null) {
+ if(isset($this) && instance_of($this, 'Categories')) {
+ return parent::findAll($arguments);
+ } else {
+ return Categories::instance()->findAll($arguments);
+ } // if
+ } // findAll
+
+ /**
+ * Find one specific record
+ *
+ * @access public
+ * @param array $arguments
+ * @return Category
+ */
+ function findOne($arguments = null) {
+ if(isset($this) && instance_of($this, 'Categories')) {
+ return parent::findOne($arguments);
+ } else {
+ return Categories::instance()->findOne($arguments);
+ } // if
+ } // findOne
+
+ /**
+ * Return object by its PK value
+ *
+ * @access public
+ * @param mixed $id
+ * @param boolean $force_reload If true cache will be skipped and data will be loaded from database
+ * @return Category
+ */
+ function findById($id, $force_reload = false) {
+ if(isset($this) && instance_of($this, 'Categories')) {
+ return parent::findById($id, $force_reload);
+ } else {
+ return Categories::instance()->findById($id, $force_reload);
+ } // if
+ } // findById
+
+ /**
+ * Return number of rows in this table
+ *
+ * @access public
+ * @param string $conditions Query conditions
+ * @return integer
+ */
+ function count($condition = null) {
+ if(isset($this) && instance_of($this, 'Categories')) {
+ return parent::count($condition);
+ } else {
+ return Categories::instance()->count($condition);
+ } // if
+ } // count
+
+ /**
+ * Delete rows that match specific conditions. If $conditions is NULL all rows from table will be deleted
+ *
+ * @access public
+ * @param string $conditions Query conditions
+ * @return boolean
+ */
+ function delete($condition = null) {
+ if(isset($this) && instance_of($this, 'Categories')) {
+ return parent::delete($condition);
+ } else {
+ return Categories::instance()->delete($condition);
+ } // if
+ } // delete
+
+ /**
+ * This function will return paginated result. Result is an array where first element is
+ * array of returned object and second populated pagination object that can be used for
+ * obtaining and rendering pagination data using various helpers.
+ *
+ * Items and pagination array vars are indexed with 0 for items and 1 for pagination
+ * because you can't use associative indexing with list() construct
+ *
+ * @access public
+ * @param array $arguments Query argumens (@see find()) Limit and offset are ignored!
+ * @param integer $items_per_page Number of items per page
+ * @param integer $current_page Current page number
+ * @return array
+ */
+ function paginate($arguments = null, $items_per_page = 10, $current_page = 1) {
+ if(isset($this) && instance_of($this, 'Categories')) {
+ return parent::paginate($arguments, $items_per_page, $current_page);
+ } else {
+ return Categories::instance()->paginate($arguments, $items_per_page, $current_page);
+ } // if
+ } // paginate
+
+ /**
+ * Return manager instance
+ *
+ * @return Categories
+ */
+ function instance() {
+ static $instance;
+ if(!instance_of($instance, 'Categories')) {
+ $instance = new Categories();
+ } // if
+ return $instance;
+ } // instance
+
+ } // Categories
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/project_categories/base/BaseCategory.class.php
===================================================================
--- application/models/project_categories/base/BaseCategory.class.php (revision 0)
+++ application/models/project_categories/base/BaseCategory.class.php (revision 0)
@@ -0,0 +1,117 @@
+getColumnValue('id');
+ } // getId()
+
+ /**
+ * Set value of 'id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setId($value) {
+ return $this->setColumnValue('id', $value);
+ } // setId()
+
+ /**
+ * Return value of 'project_id' field
+ *
+ * @access public
+ * @param void
+ * @return integer
+ */
+ function getProjectId() {
+ return $this->getColumnValue('project_id');
+ } // getProjectId()
+
+ /**
+ * Set value of 'project_id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setProjectId($value) {
+ return $this->setColumnValue('project_id', $value);
+ } // setProjectId()
+
+ /**
+ * Return value of 'name' field
+ *
+ * @access public
+ * @param void
+ * @return integer
+ */
+ function getName() {
+ return $this->getColumnValue('name');
+ } // getName()
+
+ /**
+ * Set value of 'name' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setName($value) {
+ return $this->setColumnValue('name', $value);
+ } // setName()
+
+ /**
+ * Return value of 'description' field
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getDescription() {
+ return $this->getColumnValue('description');
+ } // getDescription()
+
+ /**
+ * Set value of 'description' field
+ *
+ * @access public
+ * @param string $value
+ * @return boolean
+ */
+ function setDescription($value) {
+ return $this->setColumnValue('description', $value);
+ } // setDescription()
+
+
+ /**
+ * Return manager instance
+ *
+ * @access protected
+ * @param void
+ * @return Comments
+ */
+ function manager() {
+ if(!($this->manager instanceof Categories)) $this->manager = Categories::instance();
+ return $this->manager;
+ } // manager
+
+ } // BaseComment
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/project_categories/Categories.class.php
===================================================================
--- application/models/project_categories/Categories.class.php (revision 0)
+++ application/models/project_categories/Categories.class.php (revision 0)
@@ -0,0 +1,27 @@
+ array('`project_id` = ?', $project->getId()),
+ 'order' => '`name`',
+ )); // findAll
+ } // getProjectCategories
+
+ } // Categories
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/project_categories/Category.class.php
===================================================================
--- application/models/project_categories/Category.class.php (revision 0)
+++ application/models/project_categories/Category.class.php (revision 0)
@@ -0,0 +1,186 @@
+getProjectId());
+ } // getProject
+
+ /**
+ * Return shortened description
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getShortDescription() {
+ $return = substr_utf($this->getDescription(), 0, 50);
+ return strlen_utf($this->getDescription()) > 50 ? $return . '...' : $return;
+ } // getShortDescription
+
+ // ---------------------------------------------------
+ // Permissions
+ // ---------------------------------------------------
+
+ /**
+ * Return true if $user can view this category
+ *
+ * @param User $user
+ * @return boolean
+ */
+ function canView(User $user) {
+ if(!$user->isProjectUser($this->getProject())) {
+ return false; // user have access to project
+ } // if
+ return true;
+ } // canView
+
+ /**
+ * Check if user can add categories in specific project
+ *
+ * @param User $user
+ * @param Project $project
+ * @return boolean
+ */
+ function canAdd(User $user, Project $project) {
+ if(!$user->isProjectUser($project)) {
+ return false; // user is on project
+ } // if
+ if($user->isAdministrator()) {
+ return true; // user is administrator or root
+ } // if
+ return $user->getProjectPermission($project, ProjectUsers::CAN_MANAGE_TICKETS);
+ } // canAdd
+
+ /**
+ * Check if specific user can update this category
+ *
+ * @param User $user
+ * @return boolean
+ */
+ function canEdit(User $user) {
+ if(!$user->isProjectUser($this->getProject())) {
+ return false; // user is on project
+ } // if
+ if($user->isAdministrator()) {
+ return true; // user is administrator or root
+ } // if
+ return $user->getProjectPermission($this->getProject(), ProjectUsers::CAN_MANAGE_TICKETS);
+ } // canEdit
+
+ /**
+ * Check if specific user can delete this
+ *
+ * @param User $user
+ * @return boolean
+ */
+ function canDelete(User $user) {
+ if(!$user->isProjectUser($this->getProject())) {
+ return false; // user is on project
+ } // if
+ if($user->isAdministrator()) {
+ return true; // user is administrator or root
+ } // if
+ return $user->getProjectPermission($this->getProject(), ProjectUsers::CAN_MANAGE_TICKETS);
+ } // canDelete
+
+ // ---------------------------------------------------
+ // URLs
+ // ---------------------------------------------------
+
+ /**
+ * Return tag URL
+ *
+ * @param void
+ * @return string
+ */
+ function getViewUrl() {
+ return $this->getEditUrl();
+ } // getViewUrl
+
+ /**
+ * Return edit URL
+ *
+ * @param void
+ * @return string
+ */
+ function getEditUrl() {
+ return get_url('trac', 'edit_category', array('id' => $this->getId(), 'active_project' => $this->getProjectId()));
+ } // getEditUrl
+
+ /**
+ * Return delete URL
+ *
+ * @param void
+ * @return string
+ */
+ function getDeleteUrl() {
+ return get_url('trac', 'delete_category', array('id' => $this->getId(), 'active_project' => $this->getProjectId()));
+ } // getDeleteUrl
+
+ // ---------------------------------------------------
+ // System
+ // ---------------------------------------------------
+
+ /**
+ * Validate before save
+ *
+ * @param array $error
+ * @return null
+ */
+ function validate(&$errors) {
+ if(!$this->validatePresenceOf('name')) {
+ $errors[] = lang('category name required');
+ } // if
+ } // validate
+
+ // ---------------------------------------------------
+ // ApplicationDataObject implementation
+ // ---------------------------------------------------
+
+ /**
+ * Return object name
+ *
+ * @param void
+ * @return string
+ */
+ function getObjectName() {
+ return $this->getName();
+ } // getObjectName
+
+ /**
+ * Return object type name
+ *
+ * @param void
+ * @return string
+ */
+ function getObjectTypeName() {
+ return lang('category');
+ } // getObjectTypeName
+
+ /**
+ * Return view tag URL
+ *
+ * @param void
+ * @return string
+ */
+ function getObjectUrl() {
+ return $this->getViewUrl();
+ } // getObjectUrl
+
+ } // Comment
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/ProjectDataObject.class.php
===================================================================
--- application/models/ProjectDataObject.class.php (revision 0)
+++ application/models/ProjectDataObject.class.php (revision 0)
@@ -114,6 +114,17 @@
*/
protected $attached_files;
+ // ---------------------------------------------------
+ // Subscribers
+ // ---------------------------------------------------
+
+ /**
+ * Mark this object as subscribible
+ *
+ * @var boolean
+ */
+ protected $is_subscribible = false;
+
/**
* Return owner project. If project_id field does not exists NULL is returned
*
@@ -671,6 +682,30 @@
)); // get_url
} // getDetachFileUrl
+ /**
+ * This event is triggered when we attach new files
+ *
+ * @param array $files
+ * @return boolean
+ */
+ function onAttachFiles($files) {
+ return true;
+ } // onAttachFiles
+
+ // ---------------------------------------------------
+ // Subscribible
+ // ---------------------------------------------------
+
+ /**
+ * Returns true if users can subscribe to this object
+ *
+ * @param void
+ * @return boolean
+ */
+ function isSubscribible() {
+ return (boolean) $this->is_subscribible;
+ } // isSubscribible
+
// ---------------------------------------------------
// System
// ---------------------------------------------------
Index: application/models/project_messages/ProjectMessage.class.php
===================================================================
--- application/models/project_messages/ProjectMessage.class.php (revision 0)
+++ application/models/project_messages/ProjectMessage.class.php (revision 0)
@@ -42,6 +42,13 @@
* @var boolean
*/
protected $is_file_container = true;
+
+ /**
+ * Message is subscribible
+ *
+ * @var boolean
+ */
+ protected $is_subscribible = true;
/**
* Cached array of subscribers
Index: application/models/projects/Project.class.php
===================================================================
--- application/models/projects/Project.class.php (revision 0)
+++ application/models/projects/Project.class.php (revision 0)
@@ -165,6 +165,62 @@
private $completed_task_lists;
// ---------------------------------------------------
+ // Trac
+ // ---------------------------------------------------
+
+ /**
+ * All categories in this project
+ *
+ * @var array
+ */
+ private $categories;
+
+ /**
+ * All tickets in this project
+ *
+ * @var array
+ */
+ private $all_tickets;
+
+ /**
+ * Array of all tickets. If user is not member of owner company private tickets
+ * will be excluded from the list
+ *
+ * @var array
+ */
+ private $tickets;
+
+ /**
+ * All open tickets in this project
+ *
+ * @var array
+ */
+ private $all_open_tickets;
+
+ /**
+ * Array of open tickets. If user is not member of owner company private tickets
+ * will be excluded from the list
+ *
+ * @var array
+ */
+ private $open_tickets;
+
+ /**
+ * All closed tickets in this project
+ *
+ * @var array
+ */
+ private $all_closed_tickets;
+
+ /**
+ * Array of closed tickets. If user is not member of owner company private tickets
+ * will be excluded from the list
+ *
+ * @var array
+ */
+ private $closed_tickets;
+
+ // ---------------------------------------------------
// Tags
// ---------------------------------------------------
@@ -585,6 +641,119 @@
} // getCompletedTaskLists
// ---------------------------------------------------
+ // Trac
+ // ---------------------------------------------------
+
+ /**
+ * Return all categories
+ *
+ * @param void
+ * @return array
+ */
+ function getCategories() {
+ if(is_null($this->categories)) {
+ $this->categories = Categories::getProjectCategories($this);
+ } // if
+ return $this->categories;
+ } // getCategories
+
+ /**
+ * This function will return all tickets in project and it will not exclude private
+ * tickets if logged user is not member of owner company
+ *
+ * @param void
+ * @return array
+ */
+ function getAllTickets() {
+ if(is_null($this->all_tickets)) {
+ $this->all_tickets = ProjectTickets::getProjectTickets($this, true);
+ } // if
+ return $this->all_tickets;
+ } // getAllTickets
+
+ /**
+ * Return only the tickets that current user can see (if not member of owner company private
+ * tickets will be excluded)
+ *
+ * @param void
+ * @return array
+ */
+ function getTickets() {
+ if(logged_user()->isMemberOfOwnerCompany()) {
+ return $this->getAllTickets(); // members of owner company can view all tickets
+ } // if
+
+ if(is_null($this->tickets)) {
+ $this->tickets = ProjectTickets::getProjectTickets($this, false);
+ } // if
+ return $this->tickets;
+ } // getTickets
+
+ /**
+ * This function will return all open tickets in project and it will not exclude private
+ * tickets if logged user is not member of owner company
+ *
+ * @param void
+ * @return array
+ */
+ function getAllOpenTickets() {
+ if(is_null($this->all_open_tickets)) {
+ $this->all_open_tickets = ProjectTickets::getOpenProjectTickets($this, true);
+ } // if
+ return $this->all_open_tickets;
+ } // getAllOpenTickets
+
+ /**
+ * Return only the open tickets that current user can see (if not member of owner company private
+ * tickets will be excluded)
+ *
+ * @param void
+ * @return array
+ */
+ function getOpenTickets() {
+ if(logged_user()->isMemberOfOwnerCompany()) {
+ return $this->getAllOpenTickets(); // members of owner company can view all tickets
+ } // if
+
+ if(is_null($this->open_tickets)) {
+ $this->open_tickets = ProjectTickets::getOpenProjectTickets($this, false);
+ } // if
+ return $this->open_tickets;
+ } // getOpenTickets
+
+ /**
+ * This function will return all closed tickets in project and it will not exclude private
+ * tickets if logged user is not member of owner company
+ *
+ * @param void
+ * @return array
+ */
+ function getAllClosedTickets() {
+ if(is_null($this->all_closed_tickets)) {
+ $this->all_closed_tickets = ProjectTickets::getClosedProjectTickets($this, true);
+ } // if
+ return $this->all_closed_tickets;
+ } // getAllClosedTickets
+
+ /**
+ * Return only the closed tickets that current user can see (if not member of owner company private
+ * tickets will be excluded)
+ *
+ * @param void
+ * @return array
+ */
+ function getClosedTickets() {
+ if(logged_user()->isMemberOfOwnerCompany()) {
+ return $this->getAllClosedTickets(); // members of owner company can view all tickets
+ } // if
+
+ if(is_null($this->closed_tickets)) {
+ $this->closed_tickets = ProjectTickets::getClosedProjectTickets($this, false);
+ } // if
+ return $this->closed_tickets;
+ } // getClosedTickets
+
+ // ---------------------------------------------------
// Tags
// ---------------------------------------------------
@@ -877,6 +1046,27 @@
} // getUsersTasks
// ---------------------------------------------------
+ // User tickets
+ // ---------------------------------------------------
+
+ /**
+ * Return array of task that are assigned to specific user or his company
+ *
+ * @param User $user
+ * @return array
+ */
+ function getUsersTickets(User $user) {
+ $conditions = DB::prepareString('`project_id` = ? AND ((`assigned_to_user_id` = ? AND `assigned_to_company_id` = ?) OR (`assigned_to_user_id` = ? AND `assigned_to_company_id` = ?) OR (`assigned_to_user_id` = ? AND `assigned_to_company_id` = ?) OR `created_by_id`= ?) AND `closed_on` = ?', array($this->getId(), $user->getId(), $user->getCompanyId(), 0, $user->getCompanyId(), 0, 0, $user->getId(), EMPTY_DATETIME));
+ if(!$user->isMemberOfOwnerCompany()) {
+ $conditions .= DB::prepareString(' AND `is_private` = ?', array(0));
+ } // if
+ return ProjectTickets::findAll(array(
+ 'conditions' => $conditions,
+ 'order' => '`created_on`'
+ )); // findAll
+ } // getUsersTickets
+
+ // ---------------------------------------------------
// Files
// ---------------------------------------------------
@@ -1100,6 +1290,16 @@
} // getMilestonesUrl
/**
+ * Return project trac index page URL
+ *
+ * @param void
+ * @return string
+ */
+ function getTracUrl() {
+ return get_url('trac', 'index', array('active_project' => $this->getId()));
+ } // getTracUrl
+
+ /**
* Return project forms index page URL
*
* @param void
Index: application/models/project_tickets/base/BaseProjectTicket.class.php
===================================================================
--- application/models/project_tickets/base/BaseProjectTicket.class.php (revision 0)
+++ application/models/project_tickets/base/BaseProjectTicket.class.php (revision 0)
@@ -0,0 +1,403 @@
+getColumnValue('id');
+ } // getId()
+
+ /**
+ * Set value of 'id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setId($value) {
+ return $this->setColumnValue('id', $value);
+ } // setId()
+
+ /**
+ * Return value of 'project_id' field
+ *
+ * @access public
+ * @param void
+ * @return integer
+ */
+ function getProjectId() {
+ return $this->getColumnValue('project_id');
+ } // getProjectId()
+
+ /**
+ * Set value of 'project_id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setProjectId($value) {
+ return $this->setColumnValue('project_id', $value);
+ } // setProjectId()
+
+ /**
+ * Return value of 'category_id' field
+ *
+ * @access public
+ * @param void
+ * @return integer
+ */
+ function getCategoryId() {
+ return $this->getColumnValue('category_id');
+ } // getCategoryId()
+
+ /**
+ * Set value of 'category_id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setCategoryId($value) {
+ return $this->setColumnValue('category_id', $value);
+ } // setCategoryId()
+
+ /**
+ * Return value of 'created_by_id' field
+ *
+ * @access public
+ * @param void
+ * @return integer
+ */
+ function getCreatedById() {
+ return $this->getColumnValue('created_by_id');
+ } // getCreatedById()
+
+ /**
+ * Set value of 'created_by_id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setCreatedById($value) {
+ return $this->setColumnValue('created_by_id', $value);
+ } // setCreatedById()
+
+ /**
+ * Return value of 'closed_by_id' field
+ *
+ * @access public
+ * @param void
+ * @return integer
+ */
+ function getClosedById() {
+ return $this->getColumnValue('closed_by_id');
+ } // getClosedById()
+
+ /**
+ * Set value of 'closed_by_id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setClosedById($value) {
+ return $this->setColumnValue('closed_by_id', $value);
+ } // getClosedById()
+
+ /**
+ * Return value of 'assigned_to_company_id' field
+ *
+ * @access public
+ * @param void
+ * @return integer
+ */
+ function getAssignedToCompanyId() {
+ return $this->getColumnValue('assigned_to_company_id');
+ } // getAssignedToCompanyId()
+
+ /**
+ * Set value of 'assigned_to_company_id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setAssignedToCompanyId($value) {
+ return $this->setColumnValue('assigned_to_company_id', $value);
+ } // setAssignedToCompanyId()
+
+ /**
+ * Return value of 'assigned_to_user_id' field
+ *
+ * @access public
+ * @param void
+ * @return integer
+ */
+ function getAssignedToUserId() {
+ return $this->getColumnValue('assigned_to_user_id');
+ } // getAssignedToUserId()
+
+ /**
+ * Set value of 'assigned_to_user_id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setAssignedToUserId($value) {
+ return $this->setColumnValue('assigned_to_user_id', $value);
+ } // setAssignedToUserId()
+
+ /**
+ * Return value of 'summary' field
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getSummary() {
+ return $this->getColumnValue('summary');
+ } // getSummary()
+
+ /**
+ * Set value of 'summary' field
+ *
+ * @access public
+ * @param string $value
+ * @return boolean
+ */
+ function setSummary($value) {
+ return $this->setColumnValue('summary', $value);
+ } // setSummary()
+
+ /**
+ * Return value of 'type' field
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getType() {
+ return $this->getColumnValue('type');
+ } // getType()
+
+ /**
+ * Set value of 'type' field
+ *
+ * @access public
+ * @param string $value
+ * @return boolean
+ */
+ function setType($value) {
+ return $this->setColumnValue('type', $value);
+ } // setType()
+
+ /**
+ * Return value of 'description' field
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getDescription() {
+ return $this->getColumnValue('description');
+ } // getDescription()
+
+ /**
+ * Set value of 'description' field
+ *
+ * @access public
+ * @param string $value
+ * @return boolean
+ */
+ function setDescription($value) {
+ return $this->setColumnValue('description', $value);
+ } // setDescription()
+
+ /**
+ * Return value of 'priority' field
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getPriority() {
+ return $this->getColumnValue('priority');
+ } // getPriority()
+
+ /**
+ * Set value of 'priority' field
+ *
+ * @access public
+ * @param string $value
+ * @return boolean
+ */
+ function setPriority($value) {
+ return $this->setColumnValue('priority', $value);
+ } // setPriority()
+
+ /**
+ * Return value of 'created_on' field
+ *
+ * @access public
+ * @param void
+ * @return DateTimeValue
+ */
+ function getCreatedOn() {
+ return $this->getColumnValue('created_on');
+ } // getCreatedOn()
+
+ /**
+ * Set value of 'created_on' field
+ *
+ * @access public
+ * @param DateTimeValue $value
+ * @return boolean
+ */
+ function setCreatedOn($value) {
+ return $this->setColumnValue('created_on', $value);
+ } // setCreatedOn()
+
+ /**
+ * Return value of 'closed_on' field
+ *
+ * @access public
+ * @param void
+ * @return DateTimeValue
+ */
+ function getClosedOn() {
+ return $this->getColumnValue('closed_on');
+ } // getClosedOn()
+
+ /**
+ * Set value of 'closed_on' field
+ *
+ * @access public
+ * @param DateTimeValue $value
+ * @return boolean
+ */
+ function setClosedOn($value) {
+ return $this->setColumnValue('closed_on', $value);
+ } // setClosedOn()
+
+ /**
+ * Return value of 'updated_on' field
+ *
+ * @access public
+ * @param void
+ * @return DateTimeValue
+ */
+ function getUpdatedOn() {
+ return $this->getColumnValue('updated_on');
+ } // getUpdatedOn()
+
+ /**
+ * Set value of 'updated_on' field
+ *
+ * @access public
+ * @param DateTimeValue $value
+ * @return boolean
+ */
+ function setUpdatedOn($value) {
+ return $this->setColumnValue('updated_on', $value);
+ } // setUpdatedOn()
+
+ /**
+ * Return value of 'updated_by_id' field
+ *
+ * @access public
+ * @param void
+ * @return integer
+ */
+ function getUpdatedById() {
+ return $this->getColumnValue('updated_by_id');
+ } // getUpdatedById()
+
+ /**
+ * Set value of 'updated_by_id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setUpdatedById($value) {
+ return $this->setColumnValue('updated_by_id', $value);
+ } // setUpdatedById()
+
+ /**
+ * Return value of 'updated' field
+ *
+ * @access public
+ * @param void
+ * @return integer
+ */
+ function getUpdated() {
+ return $this->getColumnValue('updated');
+ } // getUpdated()
+
+ /**
+ * Set value of 'updated' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setUpdated($value) {
+ return $this->setColumnValue('updated', $value);
+ } // setUpdated()
+
+ /**
+ * Return value of 'is_private' field
+ *
+ * @access public
+ * @param void
+ * @return boolean
+ */
+ function getIsPrivate() {
+ return $this->getColumnValue('is_private');
+ } // getIsPrivate()
+
+ /**
+ * Set value of 'is_private' field
+ *
+ * @access public
+ * @param boolean $value
+ * @return boolean
+ */
+ function setIsPrivate($value) {
+ return $this->setColumnValue('is_private', $value);
+ } // setIsPrivate()
+
+
+ /**
+ * Return manager instance
+ *
+ * @access protected
+ * @param void
+ * @return ProjectTickets
+ */
+ function manager() {
+ if(!($this->manager instanceof ProjectTickets)) $this->manager = ProjectTickets::instance();
+ return $this->manager;
+ } // manager
+
+ } // BaseProjectTicket
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/project_tickets/base/BaseProjectTickets.class.php
===================================================================
--- application/models/project_tickets/base/BaseProjectTickets.class.php (revision 0)
+++ application/models/project_tickets/base/BaseProjectTickets.class.php (revision 0)
@@ -0,0 +1,220 @@
+ Column type map
+ *
+ * @var array
+ * @static
+ */
+ static private $columns = array('id' => DATA_TYPE_INTEGER, 'project_id' => DATA_TYPE_INTEGER, 'category_id' => DATA_TYPE_INTEGER, 'created_by_id' => DATA_TYPE_INTEGER, 'closed_by_id' => DATA_TYPE_INTEGER, 'assigned_to_user_id' => DATA_TYPE_INTEGER, 'assigned_to_company_id' => DATA_TYPE_INTEGER, 'summary' => DATA_TYPE_STRING, 'type' => DATA_TYPE_STRING, 'description' => DATA_TYPE_STRING, 'priority' => DATA_TYPE_STRING, 'created_on' => DATA_TYPE_DATETIME, 'closed_on' => DATA_TYPE_DATETIME, 'updated_on' => DATA_TYPE_DATETIME, 'updated_by_id' => DATA_TYPE_INTEGER, 'updated' => DATA_TYPE_STRING, 'is_private' => DATA_TYPE_BOOLEAN);
+
+ /**
+ * Construct
+ *
+ * @return BaseProjectTickets
+ */
+ function __construct() {
+ parent::__construct('ProjectTicket', 'project_tickets', true);
+ } // __construct
+
+ // -------------------------------------------------------
+ // Description methods
+ // -------------------------------------------------------
+
+ /**
+ * Return array of object columns
+ *
+ * @access public
+ * @param void
+ * @return array
+ */
+ function getColumns() {
+ return array_keys(self::$columns);
+ } // getColumns
+
+ /**
+ * Return column type
+ *
+ * @access public
+ * @param string $column_name
+ * @return string
+ */
+ function getColumnType($column_name) {
+ if(isset(self::$columns[$column_name])) {
+ return self::$columns[$column_name];
+ } else {
+ return DATA_TYPE_STRING;
+ } // if
+ } // getColumnType
+
+ /**
+ * Return array of PK columns. If only one column is PK returns its name as string
+ *
+ * @access public
+ * @param void
+ * @return array or string
+ */
+ function getPkColumns() {
+ return 'id';
+ } // getPkColumns
+
+ /**
+ * Return name of first auto_incremenent column if it exists
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getAutoIncrementColumn() {
+ return 'id';
+ } // getAutoIncrementColumn
+
+ // -------------------------------------------------------
+ // Finders
+ // -------------------------------------------------------
+
+ /**
+ * Do a SELECT query over database with specified arguments
+ *
+ * @access public
+ * @param array $arguments Array of query arguments. Fields:
+ *
+ * - one - select first row
+ * - conditions - additional conditions
+ * - order - order by string
+ * - offset - limit offset, valid only if limit is present
+ * - limit
+ *
+ * @return one or ProjectTickets objects
+ * @throws DBQueryError
+ */
+ function find($arguments = null) {
+ if(isset($this) && instance_of($this, 'ProjectTickets')) {
+ return parent::find($arguments);
+ } else {
+ return ProjectTickets::instance()->find($arguments);
+ } // if
+ } // find
+
+ /**
+ * Find all records
+ *
+ * @access public
+ * @param array $arguments
+ * @return one or ProjectTickets objects
+ */
+ function findAll($arguments = null) {
+ if(isset($this) && instance_of($this, 'ProjectTickets')) {
+ return parent::findAll($arguments);
+ } else {
+ return ProjectTickets::instance()->findAll($arguments);
+ } // if
+ } // findAll
+
+ /**
+ * Find one specific record
+ *
+ * @access public
+ * @param array $arguments
+ * @return ProjectTicket
+ */
+ function findOne($arguments = null) {
+ if(isset($this) && instance_of($this, 'ProjectTickets')) {
+ return parent::findOne($arguments);
+ } else {
+ return ProjectTickets::instance()->findOne($arguments);
+ } // if
+ } // findOne
+
+ /**
+ * Return object by its PK value
+ *
+ * @access public
+ * @param mixed $id
+ * @param boolean $force_reload If true cache will be skipped and data will be loaded from database
+ * @return ProjectTicket
+ */
+ function findById($id, $force_reload = false) {
+ if(isset($this) && instance_of($this, 'ProjectTickets')) {
+ return parent::findById($id, $force_reload);
+ } else {
+ return ProjectTickets::instance()->findById($id, $force_reload);
+ } // if
+ } // findById
+
+ /**
+ * Return number of rows in this table
+ *
+ * @access public
+ * @param string $conditions Query conditions
+ * @return integer
+ */
+ function count($condition = null) {
+ if(isset($this) && instance_of($this, 'ProjectTickets')) {
+ return parent::count($condition);
+ } else {
+ return ProjectTickets::instance()->count($condition);
+ } // if
+ } // count
+
+ /**
+ * Delete rows that match specific conditions. If $conditions is NULL all rows from table will be deleted
+ *
+ * @access public
+ * @param string $conditions Query conditions
+ * @return boolean
+ */
+ function delete($condition = null) {
+ if(isset($this) && instance_of($this, 'ProjectTickets')) {
+ return parent::delete($condition);
+ } else {
+ return ProjectTickets::instance()->delete($condition);
+ } // if
+ } // delete
+
+ /**
+ * This function will return paginated result. Result is an array where first element is
+ * array of returned object and second populated pagination object that can be used for
+ * obtaining and rendering pagination data using various helpers.
+ *
+ * Items and pagination array vars are indexed with 0 for items and 1 for pagination
+ * because you can't use associative indexing with list() construct
+ *
+ * @access public
+ * @param array $arguments Query argumens (@see find()) Limit and offset are ignored!
+ * @param integer $items_per_page Number of items per page
+ * @param integer $current_page Current page number
+ * @return array
+ */
+ function paginate($arguments = null, $items_per_page = 10, $current_page = 1) {
+ if(isset($this) && instance_of($this, 'ProjectTickets')) {
+ return parent::paginate($arguments, $items_per_page, $current_page);
+ } else {
+ return ProjectTickets::instance()->paginate($arguments, $items_per_page, $current_page);
+ } // if
+ } // paginate
+
+ /**
+ * Return manager instance
+ *
+ * @return ProjectTickets
+ */
+ function instance() {
+ static $instance;
+ if(!instance_of($instance, 'ProjectTickets')) {
+ $instance = new ProjectTickets();
+ } // if
+ return $instance;
+ } // instance
+
+ } // BaseProjectTickets
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/project_tickets/ProjectTicket.class.php
===================================================================
--- application/models/project_tickets/ProjectTicket.class.php (revision 0)
+++ application/models/project_tickets/ProjectTicket.class.php (revision 0)
@@ -0,0 +1,654 @@
+setUpdated('comment');
+ $this->save();
+
+ $change = new TicketChange();
+ $change->setTicketId($this->getId());
+ $change->setType('comment');
+ $change->setToData('#'.$this->countAllComments());
+ $change->save();
+
+ Notifier::newTicketComment($comment);
+ } catch(Exception $e) {
+ // nothing here, just suppress error...
+ } // try
+ } // onAddComment
+
+ // ---------------------------------------------------
+ // Files
+ // ---------------------------------------------------
+
+ /**
+ * Handle on add comment event
+ *
+ * @param array $files Attached files
+ * @return null
+ */
+ function onAttachFiles($files) {
+ try {
+ $this->setUpdated('attachment');
+ $this->save();
+
+ foreach ($files as $file) {
+ $change = new TicketChange();
+ $change->setTicketId($this->getId());
+ $change->setType('attachment');
+ $change->setToData($file->getFilename());
+ $change->save();
+ } // foreach
+
+ Notifier::attachFilesToTicket($this, $files);
+ } catch(Exception $e) {
+ // nothing here, just suppress error...
+ } // try
+ } // onAttachFiles
+
+ // ---------------------------------------------------
+ // Changes
+ // ---------------------------------------------------
+
+ /**
+ * Return array of changes
+ *
+ * @param void
+ * @return array
+ */
+ function getChanges() {
+ if(is_null($this->changes)) $this->changes = TicketChanges::getChangesByTicket($this);
+ return $this->changes;
+ } // getChanges
+
+ // ---------------------------------------------------
+ // Subscriptions
+ // ---------------------------------------------------
+
+ /**
+ * Return array of subscribers
+ *
+ * @param void
+ * @return array
+ */
+ function getSubscribers() {
+ if(is_null($this->subscribers)) $this->subscribers = TicketSubscriptions::getUsersByTicket($this);
+ return $this->subscribers;
+ } // getSubscribers
+
+ /**
+ * Check if specific user is subscriber
+ *
+ * @param User $user
+ * @return boolean
+ */
+ function isSubscriber(User $user) {
+ $subscription = TicketSubscriptions::findById(array(
+ 'ticket_id' => $this->getId(),
+ 'user_id' => $user->getId()
+ )); // findById
+ return $subscription instanceof TicketSubscription;
+ } // isSubscriber
+
+ /**
+ * Subscribe specific user to this ticket
+ *
+ * @param User $user
+ * @return boolean
+ */
+ function subscribeUser(User $user) {
+ if($this->isNew()) {
+ throw new Error('Can\'t subscribe user to ticket that is not saved');
+ } // if
+ if($this->isSubscriber($user)) {
+ return true;
+ } // if
+
+ // New subscription
+ $subscription = new TicketSubscription();
+ $subscription->setTicketId($this->getId());
+ $subscription->setUserId($user->getId());
+ return $subscription->save();
+ } // subscribeUser
+
+ /**
+ * Unsubscribe user
+ *
+ * @param User $user
+ * @return boolean
+ */
+ function unsubscribeUser(User $user) {
+ $subscription = TicketSubscriptions::findById(array(
+ 'ticket_id' => $this->getId(),
+ 'user_id' => $user->getId()
+ )); // findById
+ if($subscription instanceof TicketSubscription) {
+ return $subscription->delete();
+ } else {
+ return true;
+ } // if
+ } // unsubscribeUser
+
+ /**
+ * Clear all ticket subscriptions
+ *
+ * @param void
+ * @return boolean
+ */
+ function clearSubscriptions() {
+ return TicketSubscriptions::clearByTicket($this);
+ } // clearSubscriptions
+
+ // ---------------------------------------------------
+ // Operations
+ // ---------------------------------------------------
+
+ /**
+ * Return object name
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getTitle() {
+ return $this->getSummary();
+ } // getObjectName
+
+ /**
+ * Return owner project obj
+ *
+ * @access public
+ * @param void
+ * @return Project
+ */
+ function getProject() {
+ return Projects::findById($this->getProjectId());
+ } // getProject
+
+ /**
+ * Return user object of person who created this ticket
+ *
+ * @access public
+ * @param void
+ * @return User
+ */
+ function getClosedBy() {
+ return Users::findById($this->getClosedById());
+ } // getCreatedBy
+
+ /**
+ * Return owner user or company
+ *
+ * @access public
+ * @param void
+ * @return ApplicationDataObject
+ */
+ function getAssignedTo() {
+ if($this->getAssignedToUserId() > 0) {
+ return $this->getAssignedToUser();
+ } elseif($this->getAssignedToCompanyId() > 0) {
+ return $this->getAssignedToCompany();
+ } else {
+ return null;
+ } // if
+ } // getAssignedTo
+
+ /**
+ * Return owner comapny
+ *
+ * @access public
+ * @param void
+ * @return Company
+ */
+ function getAssignedToCompany() {
+ return Companies::findById($this->getAssignedToCompanyId());
+ } // getAssignedToCompany
+
+ /**
+ * Return owner user
+ *
+ * @access public
+ * @param void
+ * @return User
+ */
+ function getAssignedToUser() {
+ return Users::findById($this->getAssignedToUserId());
+ } // getAssignedToUser
+
+ /**
+ * Return owner user or company
+ *
+ * @access public
+ * @param void
+ * @return ApplicationDataObject
+ */
+ function getCategory() {
+ if($this->getCategoryId() > 0) {
+ return Categories::findById($this->getCategoryId());
+ } else {
+ return null;
+ } // if
+ } // getAssignedTo
+
+ /**
+ * Return status of ticket
+ *
+ * @access public
+ * @param void
+ * @return boolean
+ */
+ function getStatus() {
+ return $this->isClosed() ? 'closed' : 'open';
+ } // getStatus
+
+ /**
+ * Returns true if this ticket was not closed
+ *
+ * @access public
+ * @param void
+ * @return boolean
+ */
+ function isOpen() {
+ return !$this->isClosed();
+ } // isOpen
+
+ /**
+ * Returns true if this ticket is closed
+ *
+ * @access public
+ * @param void
+ * @return boolean
+ */
+ function isClosed() {
+ return $this->getClosedOn() instanceof DateTimeValue;
+ } // isClosed
+
+ // ---------------------------------------------------
+ // Permissions
+ // ---------------------------------------------------
+
+ /**
+ * Returns true if $user can access this ticket
+ *
+ * @param User $user
+ * @return boolean
+ */
+ function canView(User $user) {
+ if(!$user->isProjectUser($this->getProject())) {
+ return false; // user have access to project
+ } // if
+ if($this->isPrivate() && !$user->isMemberOfOwnerCompany()) {
+ return false; // user that is not member of owner company can't access private objects
+ } // if
+ return true;
+ } // canView
+
+ /**
+ * Check if specific user can add tickets to specific project
+ *
+ * @access public
+ * @param User $user
+ * @param Project $project
+ * @return booelean
+ */
+ function canAdd(User $user, Project $project) {
+ if(!$user->isProjectUser($project)) {
+ return false; // user is on project
+ } // if
+ return true;
+ } // canAdd
+
+ /**
+ * Check if specific user can update this ticket
+ *
+ * @access public
+ * @param User $user
+ * @return boolean
+ */
+ function canChangeStatus(User $user) {
+ if(!$user->isProjectUser($this->getProject())) {
+ return false;
+ } // if
+ if($this->canEdit($user)) {
+ return true;
+ } // if
+
+ return $user->getId() == $this->getCreatedById();
+ } // canEdit
+
+ /**
+ * Check if specific user can update this ticket
+ *
+ * @access public
+ * @param User $user
+ * @return boolean
+ */
+ function canEdit(User $user) {
+ if(!$user->isProjectUser($this->getProject())) {
+ return false;
+ } // if
+ if($user->isAdministrator()) {
+ return true;
+ } // if
+ if($this->isPrivate() && !$user->isMemberOfOwnerCompany()) {
+ return false; // user that is not member of owner company can't access private objects
+ } // if
+
+ $assigned_to = $this->getAssignedTo();
+ if($assigned_to instanceof User) {
+ if($user->getId() == $assigned_to->getId()) {
+ return true;
+ } // if
+ } elseif($assigned_to instanceof Company) {
+ if($user->getCompanyId() == $assigned_to->getId()) {
+ return true;
+ } // if
+ } // if
+
+ return $user->getProjectPermission($this->getProject(), ProjectUsers::CAN_MANAGE_TICKETS);
+ } // canEdit
+
+ /**
+ * Check if $user can update message options
+ *
+ * @param User $user
+ * @return boolean
+ */
+ function canUpdateOptions(User $user) {
+ return $user->isMemberOfOwnerCompany() && $this->canEdit($user);
+ } // canUpdateOptions
+
+ /**
+ * Check if specific user can delete this task
+ *
+ * @access public
+ * @param User $user
+ * @return boolean
+ */
+ function canDelete(User $user) {
+ if(!$user->isProjectUser($this->getProject())) {
+ return false;
+ } // if
+ if($user->isAdministrator()) {
+ return true;
+ } // if
+
+ return false; // no no
+ } // canDelete
+
+ // ---------------------------------------------------
+ // Operations
+ // ---------------------------------------------------
+
+ /**
+ * Complete this task and check if we need to complete the list
+ *
+ * @access public
+ * @param void
+ * @return null
+ */
+ function closeTicket() {
+ $this->setClosedOn(DateTimeValueLib::now());
+ $this->setClosedById(logged_user()->getId());
+ $this->setUpdated('closed');
+ $this->save();
+ } // completeTask
+
+ /**
+ * Open this task and check if we need to reopen list again
+ *
+ * @access public
+ * @param void
+ * @return null
+ */
+ function openTicket() {
+ $this->setClosedOn(null);
+ $this->setClosedById(0);
+ $this->setUpdated('open');
+ $this->save();
+ } // openTask
+
+ // ---------------------------------------------------
+ // URLs
+ // ---------------------------------------------------
+
+ /**
+ * Return view ticket URL
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getViewUrl() {
+ return $this->getEditUrl();
+ } // getViewUrl
+
+ /**
+ * Return edit task URL
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getEditUrl() {
+ return get_url('trac', 'edit', array('id' => $this->getId(), 'active_project' => $this->getProjectId()));
+ } // getEditUrl
+
+ /**
+ * Return delete task URL
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getDeleteUrl() {
+ return get_url('trac', 'delete', array('id' => $this->getId(), 'active_project' => $this->getProjectId()));
+ } // getDeleteUrl
+
+ /**
+ * Return comete task URL
+ *
+ * @access public
+ * @param string $redirect_to Redirect to this URL (referer will be used if this URL is not provided)
+ * @return string
+ */
+ function getCloseUrl($redirect_to = null) {
+ $params = array(
+ 'id' => $this->getId(),
+ 'active_project' => $this->getProjectId()
+ ); // array
+
+ if(trim($redirect_to)) {
+ $params['redirect_to'] = $redirect_to;
+ } // if
+
+ return get_url('trac', 'close', $params);
+ } // getCompleteUrl
+
+ /**
+ * Return open task URL
+ *
+ * @access public
+ * @param string $redirect_to Redirect to this URL (referer will be used if this URL is not provided)
+ * @return string
+ */
+ function getOpenUrl($redirect_to = null) {
+ $params = array(
+ 'id' => $this->getId(),
+ 'active_project' => $this->getProjectId()
+ ); // array
+
+ if(trim($redirect_to)) {
+ $params['redirect_to'] = $redirect_to;
+ } // if
+
+ return get_url('trac', 'open', $params);
+ } // getOpenUrl
+
+ /**
+ * Return update options URL
+ *
+ * @param void
+ * @return string
+ */
+ function getUpdateOptionsUrl() {
+ return get_url('trac', 'update_options', array('id' => $this->getId(), 'active_project' => $this->getProjectId()));
+ } // getUpdateOptionsUrl
+
+ /**
+ * Return subscribe URL
+ *
+ * @param void
+ * @return boolean
+ */
+ function getSubscribeUrl() {
+ return get_url('trac', 'subscribe', array('id' => $this->getId(), 'active_project' => $this->getProjectId()));
+ } // getSubscribeUrl
+
+ /**
+ * Return unsubscribe URL
+ *
+ * @param void
+ * @return boolean
+ */
+ function getUnsubscribeUrl() {
+ return get_url('trac', 'unsubscribe', array('id' => $this->getId(), 'active_project' => $this->getProjectId()));
+ } // getUnsubscribeUrl
+
+ // ---------------------------------------------------
+ // System
+ // ---------------------------------------------------
+
+ /**
+ * Validate before save
+ *
+ * @access public
+ * @param array $errors
+ * @return null
+ */
+ function validate(&$errors) {
+ if(!$this->validatePresenceOf('summary')) $errors[] = lang('ticket summary required');
+ if(!$this->validatePresenceOf('description')) $errors[] = lang('ticket description required');
+ } // validate
+
+ /**
+ * Delete this task
+ *
+ * @access public
+ * @param void
+ * @return boolean
+ */
+ function delete() {
+ $comments = $this->getComments();
+ if(is_array($comments)) foreach($comments as $comment) $comment->delete();
+
+ $this->clearSubscriptions();
+ return parent::delete();
+ } // delete
+
+ // ---------------------------------------------------
+ // ApplicationDataObject implementation
+ // ---------------------------------------------------
+
+ /**
+ * Return object name
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getObjectName() {
+ return $this->getSummary();
+ } // getObjectName
+
+ /**
+ * Return object type name
+ *
+ * @param void
+ * @return string
+ */
+ function getObjectTypeName() {
+ return lang('ticket');
+ } // getObjectTypeName
+
+ /**
+ * Return object URl
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getObjectUrl() {
+ return $this->getViewUrl();
+ } // getObjectUrl
+
+ } // ProjectTicket
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/project_tickets/ProjectTickets.class.php
===================================================================
--- application/models/project_tickets/ProjectTickets.class.php (revision 0)
+++ application/models/project_tickets/ProjectTickets.class.php (revision 0)
@@ -0,0 +1,89 @@
+getId());
+ } else {
+ $conditions = array('`project_id` = ? AND `is_private` = ?', $project->getId(), false);
+ } // if
+
+ return self::findAll(array(
+ 'conditions' => $conditions,
+ 'order' => '`created_on` DESC',
+ )); // findAll
+ } // getProjectTickets
+
+ /**
+ * Return open tickets for specific project
+ *
+ * @param Project $project
+ * @param boolean $include_private Include private tickets
+ * @return array
+ */
+ static function getOpenProjectTickets(Project $project, $include_private = false) {
+ if($include_private) {
+ $conditions = array('`project_id` = ? AND `closed_on` = ?', $project->getId(), EMPTY_DATETIME);
+ } else {
+ $conditions = array('`project_id` = ? AND `closed_on` = ? AND `is_private` = ?', $project->getId(), EMPTY_DATETIME, false);
+ } // if
+
+ return self::findAll(array(
+ 'conditions' => $conditions,
+ 'order' => '`created_on` DESC',
+ )); // findAll
+ } // getOpenProjectTickets
+
+ /**
+ * Return closed tickets for specific project
+ *
+ * @param Project $project
+ * @param boolean $include_private Include private tickets
+ * @return array
+ */
+ static function getClosedProjectTickets(Project $project, $include_private = false) {
+ if($include_private) {
+ $conditions = array('`project_id` = ? AND `closed_on` > ?', $project->getId(), EMPTY_DATETIME);
+ } else {
+ $conditions = array('`project_id` = ? AND `closed_on` > ? AND `is_private` = ?', $project->getId(), EMPTY_DATETIME, false);
+ } // if
+
+ return self::findAll(array(
+ 'conditions' => $conditions,
+ 'order' => '`created_on` DESC',
+ )); // findAll
+ } // getClosedProjectTickets
+
+ /**
+ * Return trac index page
+ *
+ * @param string $order_by
+ * @param integer $page
+ * @return string
+ */
+ static function getIndexUrl($closed = false) {
+ if ($closed) {
+ $options = array('closed' => true);
+ } else {
+ $options = array();
+ } // if
+ return get_url('trac', 'index', $options);
+ } // getIndexUrl
+
+ } // ProjectTickets
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/project_users/base/BaseProjectUser.class.php
===================================================================
--- application/models/project_users/base/BaseProjectUser.class.php (revision 0)
+++ application/models/project_users/base/BaseProjectUser.class.php (revision 0)
@@ -210,6 +210,28 @@
} // setCanManageFiles()
/**
+ * Return value of 'can_manage_tickets' field
+ *
+ * @access public
+ * @param void
+ * @return boolean
+ */
+ function getCanManageTickets() {
+ return $this->getColumnValue('can_manage_tickets');
+ } // getCanManageFiles()
+
+ /**
+ * Set value of 'can_manage_tickets' field
+ *
+ * @access public
+ * @param boolean $value
+ * @return boolean
+ */
+ function setCanManageTickets($value) {
+ return $this->setColumnValue('can_manage_tickets', $value);
+ } // setCanManageFiles()
+
+ /**
* Return value of 'can_assign_to_owners' field
*
* @access public
Index: application/models/project_users/base/BaseProjectUsers.class.php
===================================================================
--- application/models/project_users/base/BaseProjectUsers.class.php (revision 0)
+++ application/models/project_users/base/BaseProjectUsers.class.php (revision 0)
@@ -14,7 +14,7 @@
* @var array
* @static
*/
- static private $columns = array('project_id' => DATA_TYPE_INTEGER, 'user_id' => DATA_TYPE_INTEGER, 'created_on' => DATA_TYPE_DATETIME, 'created_by_id' => DATA_TYPE_INTEGER, 'can_manage_messages' => DATA_TYPE_BOOLEAN, 'can_manage_tasks' => DATA_TYPE_BOOLEAN, 'can_manage_milestones' => DATA_TYPE_BOOLEAN, 'can_upload_files' => DATA_TYPE_BOOLEAN, 'can_manage_files' => DATA_TYPE_BOOLEAN, 'can_assign_to_owners' => DATA_TYPE_BOOLEAN, 'can_assign_to_other' => DATA_TYPE_BOOLEAN);
+ static private $columns = array('project_id' => DATA_TYPE_INTEGER, 'user_id' => DATA_TYPE_INTEGER, 'created_on' => DATA_TYPE_DATETIME, 'created_by_id' => DATA_TYPE_INTEGER, 'can_manage_messages' => DATA_TYPE_BOOLEAN, 'can_manage_tasks' => DATA_TYPE_BOOLEAN, 'can_manage_milestones' => DATA_TYPE_BOOLEAN, 'can_upload_files' => DATA_TYPE_BOOLEAN, 'can_manage_files' => DATA_TYPE_BOOLEAN, 'can_manage_tickets' => DATA_TYPE_BOOLEAN, 'can_assign_to_owners' => DATA_TYPE_BOOLEAN, 'can_assign_to_other' => DATA_TYPE_BOOLEAN);
/**
* Construct
@@ -63,9 +63,9 @@
* @return array or string
*/
function getPkColumns() {
- return array (
- 0 => 'project_id',
- 1 => 'user_id',
+ return array (
+ 0 => 'project_id',
+ 1 => 'user_id',
);
} // getPkColumns
Index: application/models/project_users/ProjectUsers.class.php
===================================================================
--- application/models/project_users/ProjectUsers.class.php (revision 0)
+++ application/models/project_users/ProjectUsers.class.php (revision 0)
@@ -14,6 +14,7 @@
const CAN_MANAGE_MILESTONES = 'can_manage_milestones';
const CAN_UPLOAD_FILES = 'can_upload_files';
const CAN_MANAGE_FILES = 'can_manage_files';
+ const CAN_MANAGE_TICKETS = 'can_manage_tickets';
const CAN_ASSIGN_TO_OWNERS = 'can_assign_to_owners';
const CAN_ASSIGN_TO_OTHER = 'can_assign_to_other';
@@ -123,6 +124,7 @@
self::CAN_MANAGE_MILESTONES,
self::CAN_UPLOAD_FILES,
self::CAN_MANAGE_FILES,
+ self::CAN_MANAGE_TICKETS,
self::CAN_ASSIGN_TO_OWNERS,
self::CAN_ASSIGN_TO_OTHER,
); // array
@@ -141,6 +143,7 @@
ProjectUsers::CAN_MANAGE_MILESTONES => lang('can manage milestones'),
ProjectUsers::CAN_UPLOAD_FILES => lang('can upload files'),
ProjectUsers::CAN_MANAGE_FILES => lang('can manage files'),
+ ProjectUsers::CAN_MANAGE_TICKETS => lang('can manage tickets'),
ProjectUsers::CAN_ASSIGN_TO_OWNERS => lang('can assign to owners'),
ProjectUsers::CAN_ASSIGN_TO_OTHER => lang('can assign to other'),
); // array
Index: application/models/ticket_changes/base/BaseTicketChange.class.php
===================================================================
--- application/models/ticket_changes/base/BaseTicketChange.class.php (revision 0)
+++ application/models/ticket_changes/base/BaseTicketChange.class.php (revision 0)
@@ -0,0 +1,161 @@
+getColumnValue('ticket_id');
+ } // getTicketId()
+
+ /**
+ * Set value of 'ticket_id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setTicketId($value) {
+ return $this->setColumnValue('ticket_id', $value);
+ } // setTicketId()
+
+ /**
+ * Return value of 'type' field
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getType() {
+ return $this->getColumnValue('type');
+ } // getType()
+
+ /**
+ * Set value of 'type' field
+ *
+ * @access public
+ * @param string $value
+ * @return boolean
+ */
+ function setType($value) {
+ return $this->setColumnValue('type', $value);
+ } // setType()
+
+ /**
+ * Return value of 'from_data' field
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getFromData() {
+ return $this->getColumnValue('from_data');
+ } // getFromData()
+
+ /**
+ * Set value of 'from_data' field
+ *
+ * @access public
+ * @param string $value
+ * @return boolean
+ */
+ function setFromData($value) {
+ return $this->setColumnValue('from_data', $value);
+ } // setFromData()
+
+ /**
+ * Return value of 'to_data' field
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getToData() {
+ return $this->getColumnValue('to_data');
+ } // getToData()
+
+ /**
+ * Set value of 'to_data' field
+ *
+ * @access public
+ * @param string $value
+ * @return boolean
+ */
+ function setToData($value) {
+ return $this->setColumnValue('to_data', $value);
+ } // setToData()
+
+ /**
+ * Return value of 'created_on' field
+ *
+ * @access public
+ * @param void
+ * @return DateTimeValue
+ */
+ function getCreatedOn() {
+ return $this->getColumnValue('created_on');
+ } // getCreatedOn()
+
+ /**
+ * Set value of 'created_on' field
+ *
+ * @access public
+ * @param DateTimeValue $value
+ * @return boolean
+ */
+ function setCreatedOn($value) {
+ return $this->setColumnValue('created_on', $value);
+ } // setCreatedOn()
+
+ /**
+ * Return value of 'created_by_id' field
+ *
+ * @access public
+ * @param void
+ * @return integer
+ */
+ function getCreatedById() {
+ return $this->getColumnValue('created_by_id');
+ } // getCreatedById()
+
+ /**
+ * Set value of 'created_by_id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setCreatedById($value) {
+ return $this->setColumnValue('created_by_id', $value);
+ } // setCreatedById()
+
+
+ /**
+ * Return manager instance
+ *
+ * @access protected
+ * @param void
+ * @return TicketChanges
+ */
+ function manager() {
+ if(!($this->manager instanceof TicketChanges)) $this->manager = TicketChanges::instance();
+ return $this->manager;
+ } // manager
+
+ } // BaseTicketChange
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/ticket_changes/base/BaseTicketChanges.class.php
===================================================================
--- application/models/ticket_changes/base/BaseTicketChanges.class.php (revision 0)
+++ application/models/ticket_changes/base/BaseTicketChanges.class.php (revision 0)
@@ -0,0 +1,220 @@
+ Column type map
+ *
+ * @var array
+ * @static
+ */
+ static private $columns = array('id' => DATA_TYPE_INTEGER, 'ticket_id' => DATA_TYPE_INTEGER, 'type' => DATA_TYPE_STRING, 'from_data' => DATA_TYPE_STRING, 'to_data' => DATA_TYPE_STRING, 'created_on' => DATA_TYPE_DATETIME, 'created_by_id' => DATA_TYPE_INTEGER);
+
+ /**
+ * Construct
+ *
+ * @return BaseTicketChanges
+ */
+ function __construct() {
+ parent::__construct('TicketChange', 'ticket_changes', true);
+ } // __construct
+
+ // -------------------------------------------------------
+ // Description methods
+ // -------------------------------------------------------
+
+ /**
+ * Return array of object columns
+ *
+ * @access public
+ * @param void
+ * @return array
+ */
+ function getColumns() {
+ return array_keys(self::$columns);
+ } // getColumns
+
+ /**
+ * Return column type
+ *
+ * @access public
+ * @param string $column_name
+ * @return string
+ */
+ function getColumnType($column_name) {
+ if(isset(self::$columns[$column_name])) {
+ return self::$columns[$column_name];
+ } else {
+ return DATA_TYPE_STRING;
+ } // if
+ } // getColumnType
+
+ /**
+ * Return array of PK columns. If only one column is PK returns its name as string
+ *
+ * @access public
+ * @param void
+ * @return array or string
+ */
+ function getPkColumns() {
+ return 'id';
+ } // getPkColumns
+
+ /**
+ * Return name of first auto_incremenent column if it exists
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getAutoIncrementColumn() {
+ return 'id';
+ } // getAutoIncrementColumn
+
+ // -------------------------------------------------------
+ // Finders
+ // -------------------------------------------------------
+
+ /**
+ * Do a SELECT query over database with specified arguments
+ *
+ * @access public
+ * @param array $arguments Array of query arguments. Fields:
+ *
+ * - one - select first row
+ * - conditions - additional conditions
+ * - order - order by string
+ * - offset - limit offset, valid only if limit is present
+ * - limit
+ *
+ * @return one or TicketChanges objects
+ * @throws DBQueryError
+ */
+ function find($arguments = null) {
+ if(isset($this) && instance_of($this, 'TicketChanges')) {
+ return parent::find($arguments);
+ } else {
+ return TicketChanges::instance()->find($arguments);
+ } // if
+ } // find
+
+ /**
+ * Find all records
+ *
+ * @access public
+ * @param array $arguments
+ * @return one or TicketChanges objects
+ */
+ function findAll($arguments = null) {
+ if(isset($this) && instance_of($this, 'TicketChanges')) {
+ return parent::findAll($arguments);
+ } else {
+ return TicketChanges::instance()->findAll($arguments);
+ } // if
+ } // findAll
+
+ /**
+ * Find one specific record
+ *
+ * @access public
+ * @param array $arguments
+ * @return TicketChange
+ */
+ function findOne($arguments = null) {
+ if(isset($this) && instance_of($this, 'TicketChanges')) {
+ return parent::findOne($arguments);
+ } else {
+ return TicketChanges::instance()->findOne($arguments);
+ } // if
+ } // findOne
+
+ /**
+ * Return object by its PK value
+ *
+ * @access public
+ * @param mixed $id
+ * @param boolean $force_reload If true cache will be skipped and data will be loaded from database
+ * @return TicketChange
+ */
+ function findById($id, $force_reload = false) {
+ if(isset($this) && instance_of($this, 'TicketChanges')) {
+ return parent::findById($id, $force_reload);
+ } else {
+ return TicketChanges::instance()->findById($id, $force_reload);
+ } // if
+ } // findById
+
+ /**
+ * Return number of rows in this table
+ *
+ * @access public
+ * @param string $conditions Query conditions
+ * @return integer
+ */
+ function count($condition = null) {
+ if(isset($this) && instance_of($this, 'TicketChanges')) {
+ return parent::count($condition);
+ } else {
+ return TicketChanges::instance()->count($condition);
+ } // if
+ } // count
+
+ /**
+ * Delete rows that match specific conditions. If $conditions is NULL all rows from table will be deleted
+ *
+ * @access public
+ * @param string $conditions Query conditions
+ * @return boolean
+ */
+ function delete($condition = null) {
+ if(isset($this) && instance_of($this, 'TicketChanges')) {
+ return parent::delete($condition);
+ } else {
+ return TicketChanges::instance()->delete($condition);
+ } // if
+ } // delete
+
+ /**
+ * This function will return paginated result. Result is an array where first element is
+ * array of returned object and second populated pagination object that can be used for
+ * obtaining and rendering pagination data using various helpers.
+ *
+ * Items and pagination array vars are indexed with 0 for items and 1 for pagination
+ * because you can't use associative indexing with list() construct
+ *
+ * @access public
+ * @param array $arguments Query argumens (@see find()) Limit and offset are ignored!
+ * @param integer $items_per_page Number of items per page
+ * @param integer $current_page Current page number
+ * @return array
+ */
+ function paginate($arguments = null, $items_per_page = 10, $current_page = 1) {
+ if(isset($this) && instance_of($this, 'TicketChanges')) {
+ return parent::paginate($arguments, $items_per_page, $current_page);
+ } else {
+ return TicketChanges::instance()->paginate($arguments, $items_per_page, $current_page);
+ } // if
+ } // paginate
+
+ /**
+ * Return manager instance
+ *
+ * @return TicketChanges
+ */
+ function instance() {
+ static $instance;
+ if(!instance_of($instance, 'TicketChanges')) {
+ $instance = new TicketChanges();
+ } // if
+ return $instance;
+ } // instance
+
+ } // TicketChanges
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/ticket_changes/TicketChange.class.php
===================================================================
--- application/models/ticket_changes/TicketChange.class.php (revision 0)
+++ application/models/ticket_changes/TicketChange.class.php (revision 0)
@@ -0,0 +1,41 @@
+ticket)) $this->ticket = ProjectTickets::findById($this->getTicketId());
+ return $this->ticket;
+ } // getTicket
+
+ /**
+ * Return if data needs translation
+ *
+ * @param void
+ * @return ProjectTicket
+ */
+ function dataNeedsTranslation() {
+ return ($this->getType() == 'priority') || ($this->getType() == 'type') || ($this->getType() == 'status') || ($this->getType() == 'private');
+ } // dataNeedsTranslation
+
+ } // TicketChanges
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/ticket_changes/TicketChanges.class.php
===================================================================
--- application/models/ticket_changes/TicketChanges.class.php (revision 0)
+++ application/models/ticket_changes/TicketChanges.class.php (revision 0)
@@ -0,0 +1,26 @@
+ array('`ticket_id` = ?', $ticket->getId()),
+ 'order' => '`created_on`'
+ )); // array
+ } // getChangesByTicket
+
+ } // TicketChanges
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/ticket_subscriptions/base/BaseTicketSubscription.class.php
===================================================================
--- application/models/ticket_subscriptions/base/BaseTicketSubscription.class.php (revision 0)
+++ application/models/ticket_subscriptions/base/BaseTicketSubscription.class.php (revision 0)
@@ -0,0 +1,73 @@
+getColumnValue('ticket_id');
+ } // getTicketId()
+
+ /**
+ * Set value of 'ticket_id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setTicketId($value) {
+ return $this->setColumnValue('ticket_id', $value);
+ } // setTicketId()
+
+ /**
+ * Return value of 'user_id' field
+ *
+ * @access public
+ * @param void
+ * @return integer
+ */
+ function getUserId() {
+ return $this->getColumnValue('user_id');
+ } // getUserId()
+
+ /**
+ * Set value of 'user_id' field
+ *
+ * @access public
+ * @param integer $value
+ * @return boolean
+ */
+ function setUserId($value) {
+ return $this->setColumnValue('user_id', $value);
+ } // setUserId()
+
+
+ /**
+ * Return manager instance
+ *
+ * @access protected
+ * @param void
+ * @return TicketSubscriptions
+ */
+ function manager() {
+ if(!($this->manager instanceof TicketSubscriptions)) $this->manager = TicketSubscriptions::instance();
+ return $this->manager;
+ } // manager
+
+ } // BaseTicketSubscription
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/ticket_subscriptions/base/BaseTicketSubscriptions.class.php
===================================================================
--- application/models/ticket_subscriptions/base/BaseTicketSubscriptions.class.php (revision 0)
+++ application/models/ticket_subscriptions/base/BaseTicketSubscriptions.class.php (revision 0)
@@ -0,0 +1,223 @@
+ Column type map
+ *
+ * @var array
+ * @static
+ */
+ static private $columns = array('ticket_id' => DATA_TYPE_INTEGER, 'user_id' => DATA_TYPE_INTEGER);
+
+ /**
+ * Construct
+ *
+ * @return BaseTicketSubscriptions
+ */
+ function __construct() {
+ parent::__construct('TicketSubscription', 'ticket_subscriptions', true);
+ } // __construct
+
+ // -------------------------------------------------------
+ // Description methods
+ // -------------------------------------------------------
+
+ /**
+ * Return array of object columns
+ *
+ * @access public
+ * @param void
+ * @return array
+ */
+ function getColumns() {
+ return array_keys(self::$columns);
+ } // getColumns
+
+ /**
+ * Return column type
+ *
+ * @access public
+ * @param string $column_name
+ * @return string
+ */
+ function getColumnType($column_name) {
+ if(isset(self::$columns[$column_name])) {
+ return self::$columns[$column_name];
+ } else {
+ return DATA_TYPE_STRING;
+ } // if
+ } // getColumnType
+
+ /**
+ * Return array of PK columns. If only one column is PK returns its name as string
+ *
+ * @access public
+ * @param void
+ * @return array or string
+ */
+ function getPkColumns() {
+ return array (
+ 0 => 'ticket_id',
+ 1 => 'user_id',
+);
+ } // getPkColumns
+
+ /**
+ * Return name of first auto_incremenent column if it exists
+ *
+ * @access public
+ * @param void
+ * @return string
+ */
+ function getAutoIncrementColumn() {
+ return NULL;
+ } // getAutoIncrementColumn
+
+ // -------------------------------------------------------
+ // Finders
+ // -------------------------------------------------------
+
+ /**
+ * Do a SELECT query over database with specified arguments
+ *
+ * @access public
+ * @param array $arguments Array of query arguments. Fields:
+ *
+ * - one - select first row
+ * - conditions - additional conditions
+ * - order - order by string
+ * - offset - limit offset, valid only if limit is present
+ * - limit
+ *
+ * @return one or TicketSubscriptions objects
+ * @throws DBQueryError
+ */
+ function find($arguments = null) {
+ if(isset($this) && instance_of($this, 'TicketSubscriptions')) {
+ return parent::find($arguments);
+ } else {
+ return TicketSubscriptions::instance()->find($arguments);
+ } // if
+ } // find
+
+ /**
+ * Find all records
+ *
+ * @access public
+ * @param array $arguments
+ * @return one or TicketSubscriptions objects
+ */
+ function findAll($arguments = null) {
+ if(isset($this) && instance_of($this, 'TicketSubscriptions')) {
+ return parent::findAll($arguments);
+ } else {
+ return TicketSubscriptions::instance()->findAll($arguments);
+ } // if
+ } // findAll
+
+ /**
+ * Find one specific record
+ *
+ * @access public
+ * @param array $arguments
+ * @return TicketSubscription
+ */
+ function findOne($arguments = null) {
+ if(isset($this) && instance_of($this, 'TicketSubscriptions')) {
+ return parent::findOne($arguments);
+ } else {
+ return TicketSubscriptions::instance()->findOne($arguments);
+ } // if
+ } // findOne
+
+ /**
+ * Return object by its PK value
+ *
+ * @access public
+ * @param mixed $id
+ * @param boolean $force_reload If true cache will be skipped and data will be loaded from database
+ * @return TicketSubscription
+ */
+ function findById($id, $force_reload = false) {
+ if(isset($this) && instance_of($this, 'TicketSubscriptions')) {
+ return parent::findById($id, $force_reload);
+ } else {
+ return TicketSubscriptions::instance()->findById($id, $force_reload);
+ } // if
+ } // findById
+
+ /**
+ * Return number of rows in this table
+ *
+ * @access public
+ * @param string $conditions Query conditions
+ * @return integer
+ */
+ function count($condition = null) {
+ if(isset($this) && instance_of($this, 'TicketSubscriptions')) {
+ return parent::count($condition);
+ } else {
+ return TicketSubscriptions::instance()->count($condition);
+ } // if
+ } // count
+
+ /**
+ * Delete rows that match specific conditions. If $conditions is NULL all rows from table will be deleted
+ *
+ * @access public
+ * @param string $conditions Query conditions
+ * @return boolean
+ */
+ function delete($condition = null) {
+ if(isset($this) && instance_of($this, 'TicketSubscriptions')) {
+ return parent::delete($condition);
+ } else {
+ return TicketSubscriptions::instance()->delete($condition);
+ } // if
+ } // delete
+
+ /**
+ * This function will return paginated result. Result is an array where first element is
+ * array of returned object and second populated pagination object that can be used for
+ * obtaining and rendering pagination data using various helpers.
+ *
+ * Items and pagination array vars are indexed with 0 for items and 1 for pagination
+ * because you can't use associative indexing with list() construct
+ *
+ * @access public
+ * @param array $arguments Query argumens (@see find()) Limit and offset are ignored!
+ * @param integer $items_per_page Number of items per page
+ * @param integer $current_page Current page number
+ * @return array
+ */
+ function paginate($arguments = null, $items_per_page = 10, $current_page = 1) {
+ if(isset($this) && instance_of($this, 'TicketSubscriptions')) {
+ return parent::paginate($arguments, $items_per_page, $current_page);
+ } else {
+ return TicketSubscriptions::instance()->paginate($arguments, $items_per_page, $current_page);
+ } // if
+ } // paginate
+
+ /**
+ * Return manager instance
+ *
+ * @return TicketSubscriptions
+ */
+ function instance() {
+ static $instance;
+ if(!instance_of($instance, 'TicketSubscriptions')) {
+ $instance = new TicketSubscriptions();
+ } // if
+ return $instance;
+ } // instance
+
+ } // TicketSubscriptions
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/ticket_subscriptions/TicketSubscription.class.php
===================================================================
--- application/models/ticket_subscriptions/TicketSubscription.class.php (revision 0)
+++ application/models/ticket_subscriptions/TicketSubscription.class.php (revision 0)
@@ -0,0 +1,49 @@
+user)) $this->user = Users::findById($this->getUserId());
+ return $this->user;
+ } // getUser
+
+ /**
+ * Return ticket object
+ *
+ * @param void
+ * @return ProjectTicket
+ */
+ function getTicket() {
+ if(is_null($this->ticket)) $this->ticket = ProjectTickets::findById($this->getTicketId());
+ return $this->ticket;
+ } // getTicket
+
+ } // TicketSubscription
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/models/ticket_subscriptions/TicketSubscriptions.class.php
===================================================================
--- application/models/ticket_subscriptions/TicketSubscriptions.class.php (revision 0)
+++ application/models/ticket_subscriptions/TicketSubscriptions.class.php (revision 0)
@@ -0,0 +1,73 @@
+ '`ticket_id` = ' . DB::escape($ticket->getId())
+ )); // findAll
+ if(is_array($subscriptions)) {
+ foreach($subscriptions as $subscription) {
+ $user = $subscription->getUser();
+ if($user instanceof User) $users[] = $user;
+ } // foreach
+ } // if
+ return count($users) ? $users : null;
+ } // getUsersByTicket
+
+ /**
+ * Return array of tickets that $user is subscribed to
+ *
+ * @param User $user
+ * @return array
+ */
+ static function getTicketsByUser(User $user) {
+ $tickets = array();
+ $subscriptions = TicketSubscriptions::findAll(array(
+ 'conditions' => '`user_id` = ' . DB::escape($user->getId())
+ )); // findAll
+ if(is_array($subscriptions)) {
+ foreach($subscriptions as $subscription) {
+ $ticket = $subscription->getTicket();
+ if($tickets instanceof ProjectTicket) $tickets[] = $ticket;
+ } // foreach
+ } // if
+ return count($tickets) ? $tickets : null;
+ } // getTicketsByUser
+
+ /**
+ * Clear subscriptions by ticket
+ *
+ * @param ProjectTicket $ticket
+ * @return boolean
+ */
+ static function clearByTicket(ProjectTicket $ticket) {
+ return TicketSubscriptions::delete('`ticket_id` = ' . DB::escape($ticket->getId()));
+ } // clearByTicket
+
+ /**
+ * Clear subscriptions by user
+ *
+ * @param User $user
+ * @return boolean
+ */
+ static function clearByUser(User $user) {
+ return TicketSubscriptions::delete('`user_id` = ' . DB::escape($user->getId()));
+ } // clearByUser
+
+ } // TicketSubscriptions
+
+?>
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/views/application/user_box.php
===================================================================
--- application/views/application/user_box.php (revision 0)
+++ application/views/application/user_box.php (revision 0)
@@ -21,6 +21,7 @@
+
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/views/trac/categories.php
===================================================================
--- application/views/trac/categories.php (revision 0)
+++ application/views/trac/categories.php (revision 0)
@@ -0,0 +1,32 @@
+
+
+
+
+
+ | Category |
+ Description |
+
+
+
+ | getName() ?> |
+ getShortDescription() ?> |
+
+
+
+
+
+
+
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/views/trac/edit.php
===================================================================
--- application/views/trac/edit.php (revision 0)
+++ application/views/trac/edit.php (revision 0)
@@ -0,0 +1,148 @@
+canEdit(logged_user());
+
+ // Set page title and set crumbs to index
+ $title = $canEdit ? 'edit tracTicket' : 'view tracTicket';
+ set_page_title(lang($title));
+ project_tabbed_navigation(PROJECT_TAB_TICKETS);
+ $crumbs = array(array(lang('tickets'), get_url('trac')));
+ if ($ticket->isClosed()) {
+ $crumbs[] = array(lang('closed tracTickets'), ProjectTickets::getIndexUrl(true));
+ }
+ $crumbs[] = array(lang($title));
+ project_crumbs($crumbs);
+
+ if ($ticket->canChangeStatus(logged_user())) {
+ if ($ticket->isClosed()) {
+ add_page_action(lang('open tracTicket'), $ticket->getOpenUrl());
+ } else {
+ add_page_action(lang('close tracTicket'), $ticket->getCloseUrl());
+ }
+ }
+ add_stylesheet_to_page('project/tickets.css');
+?>
+isPrivate()) { ?>
+
+
+getId()); ?>
+: getStatus()); ?>
+
+
+
+
+ canEdit(logged_user())) ?>
+
+
+
+
+
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+ | getType()) ?> |
+dataNeedsTranslation()) { ?>
+ getFromData()) ?> |
+ getToData()) ?> |
+
+ getFromData() ?> |
+ getToData() ?> |
+
+ getCreatedByDisplayName() ?> |
+ getCreatedOn()) ?> |
+
+
+
+
+
+
+
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/views/trac/edit_sidebar.php
===================================================================
--- application/views/trac/edit_sidebar.php (revision 0)
+++ application/views/trac/edit_sidebar.php (revision 0)
@@ -0,0 +1,44 @@
+
+
+canUpdateOptions(logged_user())) { ?>
+
+
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/views/trac/index.php
===================================================================
--- application/views/trac/index.php (revision 0)
+++ application/views/trac/index.php (revision 0)
@@ -0,0 +1,30 @@
+ '#PAGE#');
+ if ($closed) $options_pagination['closed'] = true;
+?>
+
+
+
+assign('tickets', $tickets);
+ $this->includeTemplate(get_template_path('view_tickets', 'trac'));
+?>
+
+
+
+
+
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/views/trac/index_sidebar.php
===================================================================
--- application/views/trac/index_sidebar.php (revision 0)
+++ application/views/trac/index_sidebar.php (revision 0)
@@ -0,0 +1,10 @@
+includeTemplate(get_template_path('trac_sidebar', 'trac')) ?>
+
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/views/trac/trac_sidebar.php
===================================================================
--- application/views/trac/trac_sidebar.php (revision 0)
+++ application/views/trac/trac_sidebar.php (revision 0)
@@ -0,0 +1,10 @@
+
\ No hay ningún carácter de nueva línea al final del fichero
Index: application/views/trac/view_tickets.php
===================================================================
--- application/views/trac/view_tickets.php (revision 0)
+++ application/views/trac/view_tickets.php (revision 0)
@@ -0,0 +1,28 @@
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ | getId() ?> |
+ getSummary() ?> |
+ getType()) ?> |
+
+getCategory()) { ?>
+ getCategory()->getName()) ?>
+
+ |
+ getCreatedBy()->getDisplayName() ?> |
+
+getAssignedTo()) { ?>
+ getAssignedTo()->getObjectName()) ?>
+
+ |
+
+
+
\ No hay ningún carácter de nueva línea al final del fichero
Index: language/en_us/actions.php
===================================================================
--- language/en_us/actions.php (revision 0)
+++ language/en_us/actions.php (revision 0)
@@ -59,6 +59,20 @@
'mark task as completed' => 'Mark task as completed',
'mark task as open' => 'Mark task as open',
+ // Bug Trac
+ 'open tracTickets' => 'Open tickets',
+ 'closed tracTickets' => 'Closed tickets',
+ 'add tracTicket' => 'Add ticket',
+ 'edit tracTicket' => 'Edit ticket',
+ 'view tracTicket' => 'View ticket',
+ 'open tracTicket' => 'Open ticket',
+ 'close tracTicket' => 'Close ticket',
+ 'delete tracTicket' => 'Delete ticket',
+ 'add tracCategory' => 'Add category',
+ 'edit tracCategory' => 'Edit category',
+ 'tracCategories' => 'Trac categories',
+ 'update ticket options' => 'Update options',
+
// Milestone
'add milestone' => 'Add milestone',
'edit milestone' => 'Edit milestone',
Index: language/en_us/emails.php
===================================================================
--- language/en_us/emails.php (revision 0)
+++ language/en_us/emails.php (revision 0)
@@ -5,6 +5,7 @@
// Titles
'new message' => 'New message',
'new comment' => 'New comment',
+ 'new ticket' => 'New ticket',
'your account created' => 'Your account has been created',
'your password' => 'Your password',
'milestone assigned to you' => 'Milestone has been assigned to you',
@@ -21,6 +22,13 @@
'new comment posted' => 'New comment on "%s" has been posted',
'view new comment' => 'View that comment',
+ 'new ticket posted' => 'New ticket "%s" has been posted in "%s" project',
+ 'ticket edited' => 'Ticket "%s" has been edited in "%s" project',
+ 'ticket closed' => 'Ticket "%s" has been closed in "%s" project',
+ 'ticket opened' => 'Ticket "%s" has been opened in "%s" project',
+ 'attached files to ticket' => 'Some files have been attached to ticket "%s" in "%s" project',
+ 'view new ticket' => 'View that ticket',
+
'user created your account' => '%s created new account for you',
'visit and login' => 'Visit %s and login with',
Index: language/en_us/errors.php
===================================================================
--- language/en_us/errors.php (revision 0)
+++ language/en_us/errors.php (revision 0)
@@ -69,6 +69,13 @@
// Add task
'task text required' => 'Task text is required',
+ // Add category
+ 'category name required' => 'Category name value is required',
+
+ // Add ticket
+ 'ticket summary required' => 'Summary value is required',
+ 'ticket description required' => 'Description value is required',
+
// Add project form
'form name required' => 'Form name is required',
'form name unique' => 'Form name must be unique',
Index: language/en_us/general.php
===================================================================
--- language/en_us/general.php (revision 0)
+++ language/en_us/general.php (revision 0)
@@ -101,6 +101,7 @@
'changelog' => 'Changelog',
'hint' => 'Hint',
'order' => 'Order',
+ 'private' => 'Private',
'project calendar' => '%s calendar',
'user calendar' => '%s\'s calendar',
Index: language/en_us/messages.php
===================================================================
--- language/en_us/messages.php (revision 0)
+++ language/en_us/messages.php (revision 0)
@@ -16,6 +16,7 @@
'no comments associated with object' => 'There are no comments posted for this object',
'no messages in project' => 'There are no messages in this project',
'no subscribers' => 'There are no users subscribed to this message',
+ 'no ticket subscribers' => 'There are no users subscribed to this ticket',
'no activities in project' => 'There are no activities logged for this project',
'comment dnx' => 'Requested comment does not exist',
'milestone dnx' => 'Requested milestone does not exist',
@@ -67,6 +68,12 @@
'no files to attach' => 'Please select files that need to be attached',
'no administration tools' => 'There are no registered administration tools in the database',
'administration tool dnx' => 'Administration tool "%s" does not exists',
+ 'ticket dnx' => 'Requested ticket does not exist',
+ 'no tickets in project' => 'There are no tickets in this project',
+ 'no my tickets' => 'There are no tickets assigned to you',
+ 'no changes in ticket' => 'There are no changes in this ticket',
+ 'category dnx' => 'Requested category does not exist',
+ 'no categories in project' => 'There are no categories in this project',
// Success
'success add project' => 'Project %s has been added successfully',
@@ -98,6 +105,15 @@
'success open task' => 'Selected task has been reopened',
'success n tasks updated' => '%s tasks updated',
+ 'success add ticket' => 'Ticket \'%s\' has been added successfully',
+ 'success edit ticket' => 'Ticket \'%s\' has been updated successfully',
+ 'success deleted ticket' => 'Ticket \'%s\' and all of its comments has been deleted successfully',
+ 'success close ticket' => 'Selected ticket has been closed',
+ 'success open ticket' => 'Selected ticket has been reopened',
+ 'success add category' => 'Category \'%s\' has been added successfully',
+ 'success edit category' => 'Category \'%s\' has been updated successfully',
+ 'success deleted category' => 'Category \'%s\' and all of its comments has been deleted successfully',
+
'success add client' => 'Client company %s has been added',
'success edit client' => 'Client company %s has been updated',
'success delete client' => 'Client company %s has been deleted',
@@ -126,6 +142,9 @@
'success subscribe to message' => 'You have been successfully subscribed to this message',
'success unsubscribe to message' => 'You have been successfully unsubscribed from this message',
+ 'success subscribe to ticket' => 'You have been successfully subscribed to this ticket',
+ 'success unsubscribe to ticket' => 'You have been successfully unsubscribed from this ticket',
+
'success add project form' => 'Form \'%s\' has been added',
'success edit project form' => 'Form \'%s\' has been updated',
'success delete project form' => 'Form \'%s\' has been deleted',
@@ -158,10 +177,13 @@
'error delete owner company' => 'Owner company can\'t be deleted',
'error delete message' => 'Failed to delete selected message',
'error update message options' => 'Failed to update message options',
+ 'error update ticket options' => 'Failed to update ticket options',
'error delete comment' => 'Failed to delete selected comment',
'error delete milestone' => 'Failed to delete selected milestone',
'error complete task' => 'Failed to complete selected task',
'error open task' => 'Failed to reopen selected task',
+ 'error close ticket' => 'Failed to close selected ticket',
+ 'error open ticket' => 'Failed to reopen selected ticket',
'error upload file' => 'Failed to upload file',
'error delete project' => 'Failed to delete selected project',
'error complete project' => 'Failed to complete selected project',
@@ -182,6 +204,8 @@
'error delete company logo' => 'Failed to delete company logo',
'error subscribe to message' => 'Failed to subscribe to selected message',
'error unsubscribe to message' => 'Failed to unsubscribe from selected message',
+ 'error subscribe to ticket' => 'Failed to subscribe to selected ticket',
+ 'error unsubscribe to ticket' => 'Failed to unsubscribe from selected ticket',
'error add project form' => 'Failed to add project form',
'error submit project form' => 'Failed to submit project form',
'error delete folder' => 'Failed to delete selected folder',
@@ -189,6 +213,8 @@
'error delete file revision' => 'Failed to delete file revision',
'error delete task list' => 'Failed to delete selected task list',
'error delete task' => 'Failed to delete selected task',
+ 'error delete ticket' => 'Failed to delete selected ticket',
+ 'error delete category' => 'Failed to delete selected category',
'error check for upgrade' => 'Failed to check for a new version',
'error attach file' => 'Failed to attach file(s)',
'error detach file' => 'Failed to detach file(s)',
@@ -207,6 +233,8 @@
'confirm delete task list' => 'Are you sure that you want to delete this task lists and all of its tasks?',
'confirm delete task' => 'Are you sure that you want to delete this task?',
'confirm delete comment' => 'Are you sure that you want to delete this comment?',
+ 'confirm delete ticket' => 'Are you sure that you want to delete this ticket?',
+ 'confirm delete category' => 'Are you sure that you want to delete this category?',
'confirm delete project' => 'Are you sure that you want to delete this project and all related data (messages, tasks, milestones, files...)?',
'confirm complete project' => 'Are you sure that you want to mark this project as finished? All project actions will be locked',
'confirm open project' => 'Are you sure that you want to mark this project as open? This will unlock all project actions',
@@ -221,6 +249,7 @@
'confirm delete company logo' => 'Are you sure that you want to delete this logo?',
'confirm subscribe' => 'Are you sure that you want to subscribe to this message? You will receive an email everytime someone (except you) posts a comment on this message?',
'confirm unsubscribe' => 'Are you sure that you want to unsubscribe?',
+ 'confirm subscribe ticket' => 'Are you sure that you want to subscribe to this ticket? You will receive an email everytime someone (except you) makes a change or posts a comment on this ticket',
'confirm delete project form' => 'Are you sure that you want to delete this form?',
'confirm delete folder' => 'Are you sure that you want to delete this folder?',
'confirm delete file' => 'Are you sure that you want to delete this file?',
@@ -258,6 +287,15 @@
'log close projecttasks' => '\'%s\' closed',
'log open projecttasks' => '\'%s\' opened',
+ 'log add categories' => '\'%s\' added',
+ 'log edit categories' => '\'%s\' updated',
+ 'log delete categories' => '\'%s\' deleted',
+ 'log add projecttickets' => '\'%s\' added',
+ 'log edit projecttickets' => '\'%s\' updated',
+ 'log delete projecttickets' => '\'%s\' deleted',
+ 'log close projecttickets' => '\'%s\' closed',
+ 'log open projecttickets' => '\'%s\' opened',
+
'log add projectforms' => '\'%s\' added',
'log edit projectforms' => '\'%s\' updated',
'log delete projectforms' => '\'%s\' deleted',
Index: language/en_us/objects.php
===================================================================
--- language/en_us/objects.php (revision 0)
+++ language/en_us/objects.php (revision 0)
@@ -24,6 +24,8 @@
'tasks' => 'Tasks',
'task list' => 'Task list',
'task lists' => 'Task lists',
+ 'ticket' => 'Ticket',
+ 'tickets' => 'Tickets',
'tag' => 'Tag',
'tags' => 'Tags',
'attachment' => 'Attachment',
@@ -73,6 +75,7 @@
'private task list' => 'Private task lists',
'private comment' => 'Private comment',
'private file' => 'Private file',
+ 'private ticket' => 'Private ticket',
); // array
Index: language/en_us/project_interface.php
===================================================================
--- language/en_us/project_interface.php (revision 0)
+++ language/en_us/project_interface.php (revision 0)
@@ -58,6 +58,7 @@
'collapse additional text' => 'Collapse',
'email notification' => 'Email notification',
'email notification desc' => 'Notify selected people about this message via email',
+ 'email notification ticket desc' => 'Notify selected people about this ticket via email',
'attach existing file' => 'Attach existing file (from Files section)',
'upload and attach' => 'Upload new file and attach it to message',
@@ -65,12 +66,15 @@
'subscribers desc' => 'Subscribers will receive an email notification whenever someone (except themselves) posts a comment on this message',
'admins can post comments on locked objects desc' => 'Comments are locked, but you as administrator still have the permissions to post comments. Note that if you expect replies from your clients and non-admin users you need to unlock comments for this object (set "Enable comments" option to "Yes").',
+ 'subscribers ticket desc' => 'Subscribers will receive an email notification whenever someone (except themselves) makes a change or posts a comment on this ticket',
+
'all permissions' => 'All',
'can manage messages' => 'Manage messages',
'can manage tasks' => 'Manage tasks',
'can manage milestones' => 'Manage milestones',
'can upload files' => 'Upload files',
'can manage files' => 'Manage files',
+ 'can manage tickets' => 'Manage tickets',
'can assign to owners' => 'Assign tasks to members of owner company',
'can assign to other' => 'Assign tasks to members of other clients',
@@ -117,6 +121,30 @@
'admin notice comments disabled' => 'Comments are disabled for this object, but you as administrator still can comment. If you expect replies from other, non-admin users you should set value of Enable comments option to Yes.',
+ // Tickets
+ 'summary' => 'Summary',
+ 'category' => 'Category',
+ 'priority' => 'Priority',
+ 'assigned to' => 'Assigned to',
+ 'reported by' => 'Reported by',
+ 'closed' => 'Closed',
+ 'open' => 'Open',
+ 'critical' => 'Critical',
+ 'major' => 'Major',
+ 'minor' => 'Minor',
+ 'trivial' => 'Trivial',
+ 'defect' => 'Defect',
+ 'enhancement' => 'Enhancement',
+ 'feature request' => 'Feature',
+ 'legend' => 'Legend',
+ 'ticket #' => 'Ticket #%s',
+ 'updated on by' => '%s | %s | %s',
+ 'history' => 'Change history',
+ 'field' => 'Field',
+ 'old value' => 'Old value',
+ 'new value' => 'New value',
+ 'change date' => 'Change date',
+
// iCal
'icalendar' => 'iCalendar',
'icalendar subscribe' => 'iCalendar',
@@ -141,6 +169,7 @@
'private task list desc' => 'Private task lists are visible only to owner company members. Members of client companies will not be able to see them.',
'private comment desc' => 'Private comments are visible only to owner company members. Members of client companies will not be able to see them.',
'private file desc' => 'Private files are visible only to the members of the owner company. Members of client companies will not be able to see them',
+ 'private ticket desc' => 'Private tickets are visible only to owner company members. Members of client companies will not be able to see them.',
); // array
Index: language/en_us/site_interface.php
===================================================================
--- language/en_us/site_interface.php (revision 0)
+++ language/en_us/site_interface.php (revision 0)
@@ -14,6 +14,9 @@
'my tasks' => 'My tasks',
'welcome back' => 'Welcome back %s',
+ // Bug Trac
+ 'my tickets' => 'My tickets',
+
'online users' => 'Online users',
'online users desc' => 'Users who were active in last 15 minutes:',
Index: language/es_ar/actions.php
===================================================================
--- language/es_ar/actions.php (revision 0)
+++ language/es_ar/actions.php (revision 0)
@@ -59,16 +59,25 @@
'mark task as completed' => 'Marcar tarea como completada',
'mark task as open' => 'Marcar tarea como abierta',
+ // Bug Trac
+ 'open tracTickets' => 'Tickets abiertos',
+ 'closed tracTickets' => 'Tickets cerrados',
+ 'add tracTicket' => 'Agregar ticket',
+ 'edit tracTicket' => 'Editar ticket',
+ 'view tracTicket' => 'Ver ticket',
+ 'open tracTicket' => 'Abrir ticket',
+ 'close tracTicket' => 'Cerrar ticket',
+ 'delete tracTicket' => 'Borrar ticket',
+ 'add tracCategory' => 'Añadir categoría',
+ 'edit tracCategory' => 'Editar categoría',
+ 'tracCategories' => 'Categorías',
+ 'update ticket options' => 'Actualizar opciones',
+
// Milestone
'add milestone' => 'Agregar evento',
'edit milestone' => 'Editar evento',
'delete milestone' => 'Borrar evento',
- // Documents ¿¿??
- 'add document' => 'Agregar documento',
- 'edit document' => 'Editar documento',
- 'delete document' => 'Borrar documento',
-
// People
'update people' => 'Actualizar',
'remove user from project' => 'Remover del proyecto',
Index: language/es_ar/emails.php
===================================================================
--- language/es_ar/emails.php (revision 0)
+++ language/es_ar/emails.php (revision 0)
@@ -5,6 +5,7 @@
// Titles
'new message' => 'Nuevo mensaje',
'new comment' => 'Nuevo comentario',
+ 'new ticket' => 'Nuevo ticket',
'your account created' => 'Su cuenta ha sido creada',
'your password' => 'Su password',
'milestone assigned to you' => 'Evento asignado a ud.',
@@ -21,6 +22,13 @@
'new comment posted' => 'Nuevo comentario sobre "%s" ha sido publicado',
'view new comment' => 'Ver el comentario',
+ 'new ticket posted' => 'Nuevo ticket "%s" ha sido publicado en "%s" proyecto',
+ 'ticket edited' => 'El ticket "%s" ha sido editado en "%s" proyecto',
+ 'ticket closed' => 'El ticket "%s" ha sido cerrado en "%s" proyecto',
+ 'ticket opened' => 'El ticket "%s" ha sido reabierto en "%s" proyecto',
+ 'attached files to ticket' => 'Se han adjuntado archivos al ticket "%s" en "%s" proyecto',
+ 'view new ticket' => 'Ver el ticket',
+
'user created your account' => '%s creada nueva cuenta para ud.',
'visit and login' => 'Visitar %s e ingresar (login) con',
@@ -29,4 +37,4 @@
); // array
-?>
\ No hay ningún carácter de nueva línea al final del fichero
+?>
Index: language/es_ar/errors.php
===================================================================
--- language/es_ar/errors.php (revision 0)
+++ language/es_ar/errors.php (revision 0)
@@ -56,12 +56,19 @@
'milestone due date required' => 'Fecha de vencimiento de evento es requerida',
// Add task list
- 'task list name required' => 'Nombre de lista de tarea es requerida',
- 'task list name unique' => 'El nombre de lista de tarea debe ser única en el proyecto',
+ 'task list name required' => 'Nombre de lista de tareas es requerido',
+ 'task list name unique' => 'El nombre de lista de tareas debe ser única en el proyecto',
// Add task
'task text required' => 'Un texto de tarea es requerido',
+ // Add category
+ 'category name required' => 'Nombre de la categoría es requerido',
+
+ // Add ticket
+ 'ticket summary required' => 'Un resumen es requerido',
+ 'ticket description required' => 'Una descripción es requerida',
+
// Add project form
'form name required' => 'Un nombre de formulario es requerido',
'form name unique' => 'El nombre de formulario debe ser único',
Index: language/es_ar/general.php
===================================================================
--- language/es_ar/general.php (revision 0)
+++ language/es_ar/general.php (revision 0)
@@ -102,6 +102,7 @@
'changelog' => 'Registro de cambios',
'hint' => 'Consejo',
'order' => 'Orden',
+ 'private' => 'Privado',
'project calendar' => '%s calendario',
'user calendar' => 'calendario de %s',
Index: language/es_ar/messages.php
===================================================================
--- language/es_ar/messages.php (revision 0)
+++ language/es_ar/messages.php (revision 0)
@@ -9,6 +9,7 @@
'no comments associated with object' => 'No hay comentarios publicados para este objeto',
'no messages in project' => 'No hay mensajes en este proyecto',
'no subscribers' => 'No hay usuarios subscritos a este mensaje',
+ 'no ticket subscribers' => 'No hay usuarios subscritos a este ticket',
'no activities in project' => 'No hay actividades registradas para este proyecto',
'comment dnx' => 'El comentario solicitado no existe',
'milestone dnx' => 'El evento solicitado no existe',
@@ -63,6 +64,12 @@
'no files to attach' => 'Por favor, seleccione los archivos que necesita adjuntar',
'no administration tools' => 'No hay herramientas de administración registradas en la base de datos',
'administration tool dnx' => 'La herramienta de administración "%s" no existe',
+ 'ticket dnx' => 'El ticket solicitado no existe',
+ 'no tickets in project' => 'No hay tickets en este proyecto',
+ 'no my tickets' => 'No hay tickets asignados a usted',
+ 'no changes in ticket' => 'No hay cambios en este ticket',
+ 'category dnx' => 'La categoría solicitada no existe',
+ 'no categories in project' => 'No hay categorías en este proyecto',
// Success
'success add project' => 'El proyecto %s ha sido agregado exitosamente',
@@ -75,9 +82,9 @@
'success edit milestone' => 'El evento \'%s\' ha sido actualizado exitosamente',
'success deleted milestone' => 'El evento \'%s\' ha sido borrado exitosamente',
- 'success add message' => 'Message %s has been added successfully',
- 'success edit message' => 'Message %s has been updated successfully',
- 'success deleted message' => 'Message \'%s\' and all of its comments has been deleted successfully',
+ 'success add message' => 'El mensaje %s ha sido agregado exitosamente',
+ 'success edit message' => 'El mensaje %s ha sido actualizado exitosamente',
+ 'success deleted message' => 'El mensaje \'%s\' y todos sus comentarios han sido borrado exitosamente',
'success add comment' => 'El comentario ha sido publicado exitosamente',
'success edit comment' => 'El comentario ha sido actualizado exitosamente',
@@ -94,9 +101,14 @@
'success open task' => 'La tarea seleccionada ha sido reabierta',
'success n tasks updated' => '%s tareas actualizadas',
- 'success add document' => 'El documento \'%s\' se ha adjuntado satisfactoriamente',//??
- 'success edit document' => 'El documento \'%s\' se ha editado satisfactoriamente',//??
- 'success delete document' => 'El documento \'%s\' ha sido eliminado correctamente', //??
+ 'success add ticket' => 'El ticket \'%s\' ha sido agregado exitosamente',
+ 'success edit ticket' => 'El ticket \'%s\' ha sido actualizado exitosamente',
+ 'success deleted ticket' => 'El ticket \'%s\' y todos sus comentarios han sido borrados exitosamente',
+ 'success close ticket' => 'El ticket seleccionado ha sido cerrado',
+ 'success open ticket' => 'El ticket seleccionado ha sido reabierto',
+ 'success add category' => 'La categoría \'%s\' ha sido agregada exitosamente',
+ 'success edit category' => 'La categoría \'%s\' ha sido actualizada exitosamente',
+ 'success deleted category' => 'La categoría \'%s\' ha sido borrada exitosamente',
'success add client' => 'La compañía del cliente %s ha sido agregada',
'success edit client' => 'La compañía del cliente %s ha sido actualizada',
@@ -126,8 +138,11 @@
'success detach document' => 'El archivo \'%s\' ha sido des-adjuntado correctamente desde \'%s\'',//??
'success attach file' => 'El archivo \'%s\' ha sido adjuntado correctamente',//??
- 'success subscribe to message' => 'Ud. ha sido suscripto exitosamente a este mensaje',
- 'success unsubscribe to message' => 'Ud. ha sido desuscripto exitosamente a este mensaje',
+ 'success subscribe to message' => 'Ud. ha sido suscrito exitosamente a este mensaje',
+ 'success unsubscribe to message' => 'Ud. ha sido desuscrito exitosamente a este mensaje',
+
+ 'success subscribe to ticket' => 'Ud. ha sido suscrito exitosamente a este ticket',
+ 'success unsubscribe to ticket' => 'Ud. ha sido desuscrito exitosamente a este ticket',
'success add project form' => 'El formulario \'%s\' ha sido agregado',
'success edit project form' => 'El formulario \'%s\' ha sido actualizado',
@@ -150,21 +165,24 @@
'success update config category' => '%s valor de configuración ha sido actualizado',
'success forgot password' => 'Su password le ha sido enviado por mail',
- 'success test mail settings' => 'El mail de prueba ha sido enviado exitosamente',
- 'success massmail' => 'El mail ha sido enviado',
+ 'success test mail settings' => 'El correo de prueba ha sido enviado exitosamente',
+ 'success massmail' => 'El correo ha sido enviado',
- 'success update company permissions' => 'Los permisos de compañía han sido actualizados exitosamente. %s registro(s) actualizado(s)',
- 'success user permissions updated' => 'User permissions have been updated',
+ 'success update company permissions' => 'Los permisos de la compañía han sido actualizados exitosamente. %s registro(s) actualizado(s)',
+ 'success user permissions updated' => 'Se han actualizado los permisos del usuario',
// Failures
'error form validation' => 'Falló la grabación del objeto porque alguna(s) de las propiedades no es/son válida(s)',
'error delete owner company' => 'El propietario de la compañía no puede ser borrado',
'error delete message' => 'Falló el borrado del mensaje seleccionado',
'error update message options' => 'Falló al actualizar las opciones de mensaje',
+ 'error update ticket options' => 'Falló al actualizar las opciones de ticket',
'error delete comment' => 'Falló al borrar el comentario seleccionado',
'error delete milestone' => 'Falló al borrar el evento seleccionado',
'error complete task' => 'Falló al completar la tarea seleccionada',
'error open task' => 'Falló al reabrir la tarea seleccionada',
+ 'error close ticket' => 'Falló al cerrar el ticket seleccionado',
+ 'error open ticket' => 'Falló al reabrir el ticket seleccionado',
'error upload file' => 'Falló al subir el archivo',
'error delete document' => 'Fallo al borrar el documento',//??
'error delete project' => 'Falló al borrar el proyecto seleccionado',
@@ -187,6 +205,8 @@
'error delete company logo' => 'Falló al borrar el logo de la compañía',
'error subscribe to message' => 'Falló al suscribir al mensaje seleccionado',
'error unsubscribe to message' => 'Falló al desuscribir del mensaje seleccionado',
+ 'error subscribe to ticket' => 'Falló al suscribir al ticket seleccionado',
+ 'error unsubscribe to ticket' => 'Falló al desuscribir del ticket seleccionado',
'error add project form' => 'Falló al agregar el formulario de proyecto',
'error submit project form' => 'Falló al enviar el formulario de proyecto',
'error delete folder' => 'Falló al borrar la carpeta seleccionada',
@@ -194,6 +214,8 @@
'error delete file revision' => 'Falló al borrar la revisión del archivo',
'error delete task list' => 'Falló al borrar la lista de tarea seleccionada',
'error delete task' => 'Falló al borrar la tarea seleccionada',
+ 'error delete ticket' => 'Falló al borrar el ticket seleccionado',
+ 'error delete category' => 'Falló al borrar la categoría seleccionada',
'error check for upgrade' => 'Falló al chequear la existencia de nueva versión',
'error attach file' => 'Falló al adjuntar archivo(s)',
'error detach file' => 'Falló al des-adjuntar archivo(s)',
@@ -212,7 +234,8 @@
'confirm delete task list' => '¿Está seguro/a que desea borrar estas listas de tarea y todas sus tareas?',
'confirm delete task' => '¿Está seguro/a que desea borra esta tarea?',
'confirm delete comment' => '¿Está seguro/a que desea borrar este comentario?',
- 'confirm delete document' => 'Estas seguro de borrar este documento?', //??
+ 'confirm delete ticket' => '¿Está seguro/a que desea borrar este ticket?',
+ 'confirm delete category' => '¿Está seguro/a que desea borrar esta categoría?',
'confirm delete project' => '¿Está seguro/a que desea borrar este proyecto y todos los datos relacionados (mensajes, tareas, eventos, archivos...)?',
'confirm complete project' => '¿Está seguro/a que desea marcar este proyecto como finalizado? Todas las acciones de proyecto serán bloqueadas',
'confirm open project' => '¿Está seguro/a que desea marcar este proyecto como abierto? Esto desbloqueará todas las acciones de proyecto',
@@ -228,6 +251,7 @@
'confirm delete company logo' => '¿Está seguro/a que desea borrar este logo?',
'confirm subscribe' => '¿Está seguro/a que desea suscribirse a este mensaje? Recibirá un mail cada vez que alguien (excepto Ud.) publique un comentario en este mensaje',
'confirm unsubscribe' => '¿Está seguro/a que desea desuscribirse?',
+ 'confirm subscribe ticket' => '¿Está seguro/a que desea suscribirse a este ticket? Recibirá un mail cada vez que alguien (excepto Ud.) haga un cambio o publique un comentario en este ticket',
'confirm delete project form' => '¿Está seguro/a que desea borrar este formulario?',
'confirm delete folder' => '¿Está seguro/a que desea borrar esta carpeta?',
'confirm delete file' => '¿Está seguro/a que desea borrar este archivo?',
@@ -269,6 +293,15 @@
'log close projecttasks' => '\'%s\' cerrada',
'log open projecttasks' => '\'%s\' abierta',
+ 'log add categories' => '\'%s\' agregada',
+ 'log edit categories' => '\'%s\' editada',
+ 'log delete categories' => '\'%s\' borrada',
+ 'log add projecttickets' => '\'%s\' agregado',
+ 'log edit projecttickets' => '\'%s\' editado',
+ 'log delete projecttickets' => '\'%s\' borrado',
+ 'log close projecttickets' => '\'%s\' cerrado',
+ 'log open projecttickets' => '\'%s\' abierto',
+
'log add projectforms' => '\'%s\' agregado',
'log edit projectforms' => '\'%s\' actualizado',
'log delete projectforms' => '\'%s\' borrado',
Index: language/es_ar/objects.php
===================================================================
--- language/es_ar/objects.php (revision 0)
+++ language/es_ar/objects.php (revision 0)
@@ -22,12 +22,12 @@
'milestones' => 'Eventos',
'task' => 'Tarea',
'tasks' => 'Tareas',
- 'task list' => 'Lista de tarea',
- 'task lists' => 'Lista de tareas',
+ 'task list' => 'Lista de tareas',
+ 'task lists' => 'Listas de tareas',
+ 'ticket' => 'Ticket',
+ 'tickets' => 'Tickets',
'tag' => 'Tag',
'tags' => 'Tags',
- 'document' => 'Documento',//??
- 'documents' => 'Documentos',//??
'attachment' => 'Adjunto',
'attachments' => 'Adjuntos',
'message subscribers' => 'Subscriptores',
@@ -75,7 +75,7 @@
'private task list' => 'Lista de tareas privada',
'private comment' => 'Comentario privado',
'private file' => 'Archivo privado',
- 'private document' => 'Documento privado', //??
+ 'private ticket' => 'Ticket privado',
); // array
Index: language/es_ar/project_interface.php
===================================================================
--- language/es_ar/project_interface.php (revision 0)
+++ language/es_ar/project_interface.php (revision 0)
@@ -60,6 +60,7 @@
'collapse additional text' => 'Colapsar',
'email notification' => 'Notificación por mail',
'email notification desc' => 'Notificar a las personas seleccionadas sobre este mensaje por email',
+ 'email notification ticket desc' => 'Notificar a las personas seleccionadas sobre este ticket por email',
'attach to message' => 'Agregar archivo',//??
'attach to message desc' => 'El documento subido será accesible por la página del mensaje y de las del proyecto',//??
'detach message document' => 'desagregar archivo',//??
@@ -71,12 +72,15 @@
'subscribers desc' => 'Los suscriptores recibirán ena notificación por mail cada vez que alguien (excepto ellos mismos) publique un comentario en este mensaje',
'admins can post comments on locked objects desc' => 'Los comentarios están bloqueados, pero ud. como administrador aún tiene los permisos para publicar comentarios. Observe que si espera respuestas de sus clientes y usuarios no administradores necesitará desbloquear los comentarios para este objeto (poner la opción "Habilitar comentarios" en "Sí").',
+ 'subscribers ticket desc' => 'Los suscriptores recibirán ena notificación por mail cada vez que alguien (excepto ellos mismos) haga un cambio o publique un comentario en este ticket',
+
'all permissions' => 'Todos',
'can manage messages' => 'Manejar mensajes',
'can manage tasks' => 'Manejar tareas',
'can manage milestones' => 'Manejar eventos',
'can upload files' => 'Subir archivos',
'can manage files' => 'Manejar archivos',
+ 'can manage tickets' => 'Manejar tickets',
'can assign to owners' => 'Asignar tareas a propietarios',
'can assign to other' => 'Asignar tareas a otros clientes',
@@ -123,6 +127,30 @@
'admin notice comments disabled' => 'Los comentarios están deshabilitados para este objeto, pero Ud. como administrador aún puede comentar. Si espera respuestas de otros usuarios no administradores, debería setear el valor de Habilitar comentarios en Sí.',
+ // Tickets
+ 'summary' => 'Resumen',
+ 'category' => 'Categoría',
+ 'priority' => 'Prioridad',
+ 'assigned to' => 'Asignado a',
+ 'reported by' => 'Informado por',
+ 'closed' => 'Cerrado',
+ 'open' => 'Abierto',
+ 'critical' => 'Crítico',
+ 'major' => 'Importante',
+ 'minor' => 'Menor',
+ 'trivial' => 'Trivial',
+ 'defect' => 'Fallo',
+ 'enhancement' => 'Mejora',
+ 'feature request' => 'Característica',
+ 'legend' => 'Leyenda',
+ 'ticket #' => 'Ticket #%s',
+ 'updated on by' => '%s | %s | %s',
+ 'history' => 'Historial de cambios',
+ 'field' => 'Campo',
+ 'old value' => 'Valor antiguo',
+ 'new value' => 'Valor nuevo',
+ 'change date' => 'Fecha del cambio',
+
// iCal
'icalendar' => 'iCalendar',
'icalendar subscribe' => 'iCalendar',
@@ -147,6 +175,7 @@
'private task list desc' => 'Las listas de tareas privadas son visibles sólo para los miembros propietarios de compañía. Los miembros de compañías cliente no podrán verlas.',
'private comment desc' => 'Los comentarios privados son visibles sólo para los miembros propietarios de compañía. Los miembros de compañías cliente no podrán verlos.',
'private file desc' => 'Los archivos privados son visibles sólo para los miembros propietarios de compañía. Los miembros de compañías cliente no podrán verlos.',
+ 'private ticket desc' => 'Los tickets privados son visibles sólo para los miembros propietarios de compañía. Los miembros de compañías cliente no podrán verlos.',
'private document desc' => 'Los documentos privados son visibles sólo para los miembros propietarios de compañía. Los miembros de compañías cliente no podrán verlos.',//??
Index: language/es_ar/site_interface.php
===================================================================
--- language/es_ar/site_interface.php (revision 0)
+++ language/es_ar/site_interface.php (revision 0)
@@ -8,6 +8,9 @@
'my tasks' => 'Mis tareas',
'welcome back' => 'Bienvenid@ una vez más %s',
+ // Bug Trac
+ 'my tickets' => 'Mis tickets',
+
'online users' => 'Usuarios en línea',
'online users desc' => 'Usuarios activos en los últimos 15 mins.:',
Index: public/assets/themes/acClassic/stylesheets/project/tickets.css
===================================================================
--- public/assets/themes/acClassic/stylesheets/project/tickets.css (revision 0)
+++ public/assets/themes/acClassic/stylesheets/project/tickets.css (revision 0)
@@ -0,0 +1,40 @@
+/** Tickets display **/
+
+#tickets .critical, #tickets_legend .critical {
+ background-color: #ffbaba;
+}
+
+#tickets .major, #tickets_legend .major {
+ background-color: #fcffb1;
+}
+
+#tickets .minor, #tickets_legend .minor {
+ background-color: #d6e7ff;
+}
+
+#tickets .trivial, #tickets_legend .trivial {
+ background-color: #ffffff;
+}
+
+#tickets_legend {
+ width: 120px;
+}
+
+#tickets_legend .legend {
+ border: 1px solid #000000;
+ padding: 2px;
+ margin-bottom: 2px;
+}
+
+/** Ticket display */
+#ticket {
+ background: #ffd;
+ border: 1px outset #996;
+ margin-top: 1em;
+ padding: .5em 1em;
+ position: relative;
+}
+
+#ticket table.properties {
+ width: 100%;
+}
Index: public/assets/themes/acSimple/stylesheets/project/tickets.css
===================================================================
--- public/assets/themes/acSimple/stylesheets/project/tickets.css (revision 0)
+++ public/assets/themes/acSimple/stylesheets/project/tickets.css (revision 0)
@@ -0,0 +1,40 @@
+/** Tickets display **/
+
+#tickets .critical, #tickets_legend .critical {
+ background-color: #ffbaba;
+}
+
+#tickets .major, #tickets_legend .major {
+ background-color: #fcffb1;
+}
+
+#tickets .minor, #tickets_legend .minor {
+ background-color: #d6e7ff;
+}
+
+#tickets .trivial, #tickets_legend .trivial {
+ background-color: #ffffff;
+}
+
+#tickets_legend {
+ width: 120px;
+}
+
+#tickets_legend .legend {
+ border: 1px solid #000000;
+ padding: 2px;
+ margin-bottom: 2px;
+}
+
+/** Ticket display */
+#ticket {
+ background: #ffd;
+ border: 1px outset #996;
+ margin-top: 1em;
+ padding: .5em 1em;
+ position: relative;
+}
+
+#ticket table.properties {
+ width: 100%;
+}
Index: public/assets/themes/azul/stylesheets/project/tickets.css
===================================================================
--- public/assets/themes/azul/stylesheets/project/tickets.css (revision 0)
+++ public/assets/themes/azul/stylesheets/project/tickets.css (revision 0)
@@ -0,0 +1,40 @@
+/** Tickets display **/
+
+#tickets .critical, #tickets_legend .critical {
+ background-color: #ffbaba;
+}
+
+#tickets .major, #tickets_legend .major {
+ background-color: #fcffb1;
+}
+
+#tickets .minor, #tickets_legend .minor {
+ background-color: #d6e7ff;
+}
+
+#tickets .trivial, #tickets_legend .trivial {
+ background-color: #ffffff;
+}
+
+#tickets_legend {
+ width: 120px;
+}
+
+#tickets_legend .legend {
+ border: 1px solid #000000;
+ padding: 2px;
+ margin-bottom: 2px;
+}
+
+/** Ticket display */
+#ticket {
+ background: #ffd;
+ border: 1px outset #996;
+ margin-top: 1em;
+ padding: .5em 1em;
+ position: relative;
+}
+
+#ticket table.properties {
+ width: 100%;
+}
Index: public/assets/themes/basic-aqua/stylesheets/project/tickets.css
===================================================================
--- public/assets/themes/basic-aqua/stylesheets/project/tickets.css (revision 0)
+++ public/assets/themes/basic-aqua/stylesheets/project/tickets.css (revision 0)
@@ -0,0 +1,40 @@
+/** Tickets display **/
+
+#tickets .critical, #tickets_legend .critical {
+ background-color: #ffbaba;
+}
+
+#tickets .major, #tickets_legend .major {
+ background-color: #fcffb1;
+}
+
+#tickets .minor, #tickets_legend .minor {
+ background-color: #d6e7ff;
+}
+
+#tickets .trivial, #tickets_legend .trivial {
+ background-color: #ffffff;
+}
+
+#tickets_legend {
+ width: 120px;
+}
+
+#tickets_legend .legend {
+ border: 1px solid #000000;
+ padding: 2px;
+ margin-bottom: 2px;
+}
+
+/** Ticket display */
+#ticket {
+ background: #ffd;
+ border: 1px outset #996;
+ margin-top: 1em;
+ padding: .5em 1em;
+ position: relative;
+}
+
+#ticket table.properties {
+ width: 100%;
+}
Index: public/assets/themes/basic-orange/stylesheets/project/tickets.css
===================================================================
--- public/assets/themes/basic-orange/stylesheets/project/tickets.css (revision 0)
+++ public/assets/themes/basic-orange/stylesheets/project/tickets.css (revision 0)
@@ -0,0 +1,40 @@
+/** Tickets display **/
+
+#tickets .critical, #tickets_legend .critical {
+ background-color: #ffbaba;
+}
+
+#tickets .major, #tickets_legend .major {
+ background-color: #fcffb1;
+}
+
+#tickets .minor, #tickets_legend .minor {
+ background-color: #d6e7ff;
+}
+
+#tickets .trivial, #tickets_legend .trivial {
+ background-color: #ffffff;
+}
+
+#tickets_legend {
+ width: 120px;
+}
+
+#tickets_legend .legend {
+ border: 1px solid #000000;
+ padding: 2px;
+ margin-bottom: 2px;
+}
+
+/** Ticket display */
+#ticket {
+ background: #ffd;
+ border: 1px outset #996;
+ margin-top: 1em;
+ padding: .5em 1em;
+ position: relative;
+}
+
+#ticket table.properties {
+ width: 100%;
+}
Index: public/assets/themes/default/stylesheets/project/tickets.css
===================================================================
--- public/assets/themes/default/stylesheets/project/tickets.css (revision 0)
+++ public/assets/themes/default/stylesheets/project/tickets.css (revision 0)
@@ -0,0 +1,40 @@
+/** Tickets display **/
+
+#tickets .critical, #tickets_legend .critical {
+ background-color: #ffbaba;
+}
+
+#tickets .major, #tickets_legend .major {
+ background-color: #fcffb1;
+}
+
+#tickets .minor, #tickets_legend .minor {
+ background-color: #d6e7ff;
+}
+
+#tickets .trivial, #tickets_legend .trivial {
+ background-color: #ffffff;
+}
+
+#tickets_legend {
+ width: 120px;
+}
+
+#tickets_legend .legend {
+ border: 1px solid #000000;
+ padding: 2px;
+ margin-bottom: 2px;
+}
+
+/** Ticket display */
+#ticket {
+ background: #ffd;
+ border: 1px outset #996;
+ margin-top: 1em;
+ padding: .5em 1em;
+ position: relative;
+}
+
+#ticket table.properties {
+ width: 100%;
+}
Index: public/assets/themes/ebrentnelson/stylesheets/project/tickets.css
===================================================================
--- public/assets/themes/ebrentnelson/stylesheets/project/tickets.css (revision 0)
+++ public/assets/themes/ebrentnelson/stylesheets/project/tickets.css (revision 0)
@@ -0,0 +1,40 @@
+/** Tickets display **/
+
+#tickets .critical, #tickets_legend .critical {
+ background-color: #ffbaba;
+}
+
+#tickets .major, #tickets_legend .major {
+ background-color: #fcffb1;
+}
+
+#tickets .minor, #tickets_legend .minor {
+ background-color: #d6e7ff;
+}
+
+#tickets .trivial, #tickets_legend .trivial {
+ background-color: #ffffff;
+}
+
+#tickets_legend {
+ width: 120px;
+}
+
+#tickets_legend .legend {
+ border: 1px solid #000000;
+ padding: 2px;
+ margin-bottom: 2px;
+}
+
+/** Ticket display */
+#ticket {
+ background: #ffd;
+ border: 1px outset #996;
+ margin-top: 1em;
+ padding: .5em 1em;
+ position: relative;
+}
+
+#ticket table.properties {
+ width: 100%;
+}
Index: public/assets/themes/goCollab/stylesheets/project/tickets.css
===================================================================
--- public/assets/themes/goCollab/stylesheets/project/tickets.css (revision 0)
+++ public/assets/themes/goCollab/stylesheets/project/tickets.css (revision 0)
@@ -0,0 +1,40 @@
+/** Tickets display **/
+
+#tickets .critical, #tickets_legend .critical {
+ background-color: #ffbaba;
+}
+
+#tickets .major, #tickets_legend .major {
+ background-color: #fcffb1;
+}
+
+#tickets .minor, #tickets_legend .minor {
+ background-color: #d6e7ff;
+}
+
+#tickets .trivial, #tickets_legend .trivial {
+ background-color: #ffffff;
+}
+
+#tickets_legend {
+ width: 120px;
+}
+
+#tickets_legend .legend {
+ border: 1px solid #000000;
+ padding: 2px;
+ margin-bottom: 2px;
+}
+
+/** Ticket display */
+#ticket {
+ background: #ffd;
+ border: 1px outset #996;
+ margin-top: 1em;
+ padding: .5em 1em;
+ position: relative;
+}
+
+#ticket table.properties {
+ width: 100%;
+}
Index: public/assets/themes/goCollab_Monochrome/stylesheets/project/tickets.css
===================================================================
--- public/assets/themes/goCollab_Monochrome/stylesheets/project/tickets.css (revision 0)
+++ public/assets/themes/goCollab_Monochrome/stylesheets/project/tickets.css (revision 0)
@@ -0,0 +1,40 @@
+/** Tickets display **/
+
+#tickets .critical, #tickets_legend .critical {
+ background-color: #ffbaba;
+}
+
+#tickets .major, #tickets_legend .major {
+ background-color: #fcffb1;
+}
+
+#tickets .minor, #tickets_legend .minor {
+ background-color: #d6e7ff;
+}
+
+#tickets .trivial, #tickets_legend .trivial {
+ background-color: #ffffff;
+}
+
+#tickets_legend {
+ width: 120px;
+}
+
+#tickets_legend .legend {
+ border: 1px solid #000000;
+ padding: 2px;
+ margin-bottom: 2px;
+}
+
+/** Ticket display */
+#ticket {
+ background: #ffd;
+ border: 1px outset #996;
+ margin-top: 1em;
+ padding: .5em 1em;
+ position: relative;
+}
+
+#ticket table.properties {
+ width: 100%;
+}
Index: public/install/installation/templates/sql/mysql_schema.php
===================================================================
--- public/install/installation/templates/sql/mysql_schema.php (revision 0)
+++ public/install/installation/templates/sql/mysql_schema.php (revision 0)
@@ -142,6 +142,14 @@
PRIMARY KEY (`message_id`,`user_id`)
) ENGINE=InnoDB ;
+CREATE TABLE `project_categories` (
+ `id` int(10) unsigned NOT NULL auto_increment,
+ `project_id` int(10) unsigned NOT NULL default '0',
+ `name` varchar(50) NOT NULL default '',
+ `description` varchar(255) default NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB ;
+
CREATE TABLE `project_companies` (
`project_id` int(10) unsigned NOT NULL default '0',
`company_id` smallint(5) unsigned NOT NULL default '0',
@@ -299,6 +307,30 @@
KEY `order` (`order`)
) ENGINE=InnoDB ;
+CREATE TABLE `project_tickets` (
+ `id` int(10) unsigned NOT NULL auto_increment,
+ `project_id` int(10) unsigned NOT NULL default '0',
+ `category_id` int(10) unsigned default NULL,
+ `assigned_to_company_id` smallint(5) unsigned default NULL,
+ `assigned_to_user_id` int(10) unsigned default NULL,
+ `summary` varchar(200) NOT NULL default '',
+ `type` enum('defect','enhancement','feature request') NOT NULL default 'defect',
+ `description` text ,
+ `priority` enum('critical','major','minor','trivial') NOT NULL default 'major',
+ `is_private` tinyint(1) NOT NULL default '0',
+ `closed_on` datetime NOT NULL default '0000-00-00 00:00:00',
+ `closed_by_id` int(10) default NULL,
+ `created_on` datetime NOT NULL default '0000-00-00 00:00:00',
+ `created_by_id` int(10) unsigned default NULL,
+ `updated_on` datetime NOT NULL default '0000-00-00 00:00:00',
+ `updated_by_id` int(10) default NULL,
+ `updated` enum('settings','comment','attachment','open','closed') default NULL,
+ PRIMARY KEY (`id`),
+ KEY `created_on` (`created_on`),
+ KEY `closed_on` (`closed_on`),
+ KEY `project_id` (`project_id`)
+) ENGINE=InnoDB ;
+
CREATE TABLE `project_users` (
`project_id` int(10) unsigned NOT NULL default '0',
`user_id` int(10) unsigned NOT NULL default '0',
@@ -309,6 +341,7 @@
`can_manage_milestones` tinyint(1) unsigned default '0',
`can_upload_files` tinyint(1) unsigned default '0',
`can_manage_files` tinyint(1) unsigned default '0',
+ `can_manage_tickets` tinyint(1) unsigned default '0',
`can_assign_to_owners` tinyint(1) unsigned NOT NULL default '0',
`can_assign_to_other` tinyint(1) unsigned NOT NULL default '0',
PRIMARY KEY (`project_id`,`user_id`)
@@ -356,6 +389,25 @@
KEY `object_id` (`rel_object_id`,`rel_object_manager`)
) ENGINE=InnoDB ;
+CREATE TABLE `ticket_changes` (
+ `id` int(11) unsigned NOT NULL auto_increment,
+ `ticket_id` int(11) unsigned NOT NULL default '0',
+ `type` enum('status','priority','assigned to','summary','category','type','private','comment','attachment') NOT NULL,
+ `from_data` varchar(255) NOT NULL default '',
+ `to_data` varchar(255) NOT NULL default '',
+ `created_on` datetime NOT NULL default '0000-00-00 00:00:00',
+ `created_by_id` int(10) default NULL,
+ PRIMARY KEY (`id`),
+ KEY `created_on` (`created_on`),
+ KEY `ticket_id` (`ticket_id`)
+) ENGINE=InnoDB ;
+
+CREATE TABLE `ticket_subscriptions` (
+ `ticket_id` int(10) unsigned NOT NULL default '0',
+ `user_id` int(10) unsigned NOT NULL default '0',
+ PRIMARY KEY (`ticket_id`,`user_id`)
+) ENGINE=InnoDB ;
+
CREATE TABLE `user_im_values` (
`user_id` int(10) unsigned NOT NULL default '0',
`im_type_id` tinyint(3) unsigned NOT NULL default '0',