From: Taylor Hurt Date: Fri, 26 Aug 2016 16:28:42 +0000 (-0400) Subject: expands mobile page access (#9) X-Git-Tag: timetracker_1.19-1~1657 X-Git-Url: http://wagnertech.de/git?a=commitdiff_plain;h=ee49ee74b2a7d3c6daaa2d4bc6397b0c89e3099b;p=timetracker.git expands mobile page access (#9) * expands mobile page access add mobile .tpl files; modify mobile/header.tpl to accomodate; add mobile php files; fix template paths to mobile/ and path to initialize.php; add mobile-table CSS class; change main mobile/ pages to use mobile-table class; fix path to subm_bg.gif; remove Top Menu (the one with black backgroud); remove Charts, Reports, Invoices, Export from submenu; remove fixed width for many input elements; remove nowrap attribute in header.tpl submenu; add inline style for background-repeat in header.tpl submenu; prevent textarea's from being resizable horizontally, allow them to be resizable vertically; remove fixed width from textarea cols attribute; add mobile-textarea CSS class; fix bug in project_edit.php -- onload event referring to wrong property name; change expenses date input to be like that of time date input; * remove fixed width attribute in header table * add mobile-table-details CSS for main pages * remove Delete column from pages * add redundant word-wrap rule for browser compatibility * for expense_edit: fix template paths to mobile/ and path to initialize.php --- diff --git a/WEB-INF/templates/mobile/client_add.tpl b/WEB-INF/templates/mobile/client_add.tpl new file mode 100644 index 00000000..6814d1e2 --- /dev/null +++ b/WEB-INF/templates/mobile/client_add.tpl @@ -0,0 +1,37 @@ +{$forms.clientForm.open} + + + + +
+ + + + + + + + + + + + + + + + + + +{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + + + + + +{/if} + + + +
{$i18n.label.client_name} (*):{$forms.clientForm.name.control}
{$i18n.label.client_address}:{$forms.clientForm.address.control}
{$i18n.label.tax}, %:{$forms.clientForm.tax.control} (0{$user->decimal_mark}00)
{$i18n.label.required_fields}
 
{$i18n.label.projects}:{$forms.clientForm.projects.control}
 
{$forms.clientForm.btn_submit.control}
+
+{$forms.clientForm.close} \ No newline at end of file diff --git a/WEB-INF/templates/mobile/client_delete.tpl b/WEB-INF/templates/mobile/client_delete.tpl new file mode 100644 index 00000000..8a0b8770 --- /dev/null +++ b/WEB-INF/templates/mobile/client_delete.tpl @@ -0,0 +1,25 @@ +{$forms.clientDeleteForm.open} + + + + +
+ + + + + + + + + + + + + + + + +
{$i18n.form.client.client_to_delete}:{$client_to_delete|escape:'html'}
{$i18n.form.client.client_entries}:{$forms.clientDeleteForm.delete_client_entries.control}
 
{$forms.clientDeleteForm.btn_delete.control}  {$forms.clientDeleteForm.btn_cancel.control}
+
+{$forms.clientDeleteForm.close} \ No newline at end of file diff --git a/WEB-INF/templates/mobile/client_edit.tpl b/WEB-INF/templates/mobile/client_edit.tpl new file mode 100644 index 00000000..578aa64e --- /dev/null +++ b/WEB-INF/templates/mobile/client_edit.tpl @@ -0,0 +1,41 @@ +{$forms.clientForm.open} + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + + + + +{/if} + + + + +
{$i18n.label.client_name} (*):{$forms.clientForm.name.control}
{$i18n.label.client_address}:{$forms.clientForm.address.control}
{$i18n.label.tax}, %:{$forms.clientForm.tax.control} (0{$user->decimal_mark}00)
{$i18n.label.status}:{$forms.clientForm.status.control}
{$i18n.label.required_fields}
 
{$i18n.label.projects}:{$forms.clientForm.projects.control}
 
{$forms.clientForm.btn_save.control} {$forms.clientForm.btn_copy.control}
+
+{$forms.clientForm.close} \ No newline at end of file diff --git a/WEB-INF/templates/mobile/clients.tpl b/WEB-INF/templates/mobile/clients.tpl new file mode 100644 index 00000000..ded17ba9 --- /dev/null +++ b/WEB-INF/templates/mobile/clients.tpl @@ -0,0 +1,55 @@ + + + + + + +
+{if ($user->canManageTeam())} + + {if $inactive_clients} + + {/if} + + + + + + {foreach $active_clients as $client} + + + + + + {/foreach} +
{$i18n.form.clients.active_clients}
{$i18n.label.person_name}{$i18n.label.address}{$i18n.label.edit}
{$client.name|escape:'html'}{$client.address|escape:'html'}{$i18n.label.edit}
+ + + +

+ + {if $inactive_clients} + + + + + + + + {foreach $inactive_clients as $client} + + + + + + {/foreach} +
{$i18n.form.clients.inactive_clients}
{$i18n.label.person_name}{$i18n.label.address}{$i18n.label.edit}
{$client.name|escape:'html'}{$client.address|escape:'html'}{$i18n.label.edit}
+ + + +

+ {/if} +{/if} +
diff --git a/WEB-INF/templates/mobile/expense_delete.tpl b/WEB-INF/templates/mobile/expense_delete.tpl new file mode 100644 index 00000000..54cd89cb --- /dev/null +++ b/WEB-INF/templates/mobile/expense_delete.tpl @@ -0,0 +1,39 @@ +{$forms.expenseItemForm.open} + + + + +
+ + +{if $user->isPluginEnabled('cl')} + +{/if} + +{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + +{/if} + + + + +{if $user->isPluginEnabled('cl')} + +{/if} +{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + +{/if} + + + +
{$i18n.label.client}{$i18n.label.project}{$i18n.label.item}{$i18n.label.cost}
{$expense_item.client_name|escape:'html'}{$expense_item.project_name|escape:'html'}{$expense_item.name|escape:'html'}{$expense_item.cost}
+ + + + + + + +
 
{$forms.expenseItemForm.delete_button.control}  {$forms.expenseItemForm.cancel_button.control}
+
+{$forms.expenseItemForm.close} \ No newline at end of file diff --git a/WEB-INF/templates/mobile/expense_edit.tpl b/WEB-INF/templates/mobile/expense_edit.tpl new file mode 100644 index 00000000..41e3c2e2 --- /dev/null +++ b/WEB-INF/templates/mobile/expense_edit.tpl @@ -0,0 +1,117 @@ + + +{$forms.expenseItemForm.open} + + + + +
+ + + + +
+ +{if $user->isPluginEnabled('cl')} + + + + +{/if} +{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + + + + +{/if} + + + + + + + + + + + + + + + + + + + +
{$i18n.label.client} {if $user->isPluginEnabled('cm')}(*){/if}:{$forms.expenseItemForm.client.control}
{$i18n.label.project} (*):{$forms.expenseItemForm.project.control}
{$i18n.label.item}:{$forms.expenseItemForm.item_name.control}
{$i18n.label.cost}:{$forms.expenseItemForm.cost.control} {$user->currency|escape:'html'}
{$i18n.label.date}:{$forms.expenseItemForm.date.control}
 
{$forms.expenseItemForm.btn_save.control} {$forms.expenseItemForm.btn_copy.control} {$forms.expenseItemForm.btn_delete.control}
+
+
+{$forms.expenseItemForm.close} \ No newline at end of file diff --git a/WEB-INF/templates/mobile/expenses.tpl b/WEB-INF/templates/mobile/expenses.tpl new file mode 100644 index 00000000..bf99d801 --- /dev/null +++ b/WEB-INF/templates/mobile/expenses.tpl @@ -0,0 +1,169 @@ + + + + + + + + + +
<<{$timestring}>>
+ +{$forms.expensesForm.open} + + + + + +
+ +{if $on_behalf_control} + + + + +{/if} +{if $user->isPluginEnabled('cl')} + + + + +{/if} +{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + + + + +{/if} + + + + + + + + +
{$i18n.label.user}:{$forms.expensesForm.onBehalfUser.control}
{$i18n.label.client}{if $user->isPluginEnabled('cm')} (*){/if}:{$forms.expensesForm.client.control}
{$i18n.label.project} (*):{$forms.expensesForm.project.control}
{$i18n.label.item} (*):{$forms.expensesForm.item_name.control}
{$i18n.label.cost} (*):{$forms.expensesForm.cost.control} {$user->currency|escape:'html'}
+
+ + + + + +
{$forms.expensesForm.btn_submit.control}
+ + + + + +
+{if $expense_items} + + + {if $user->isPluginEnabled('cl')} + + {/if} + {if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + + {/if} + + + + + {foreach $expense_items as $item} + + {if $user->isPluginEnabled('cl')} + + {/if} + {if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + + {/if} + + + + + {/foreach} +
{$i18n.label.client}{$i18n.label.project}{$i18n.label.item}{$i18n.label.cost}{$i18n.label.edit}
{$item.client|escape:'html'}{$item.project|escape:'html'}{$item.item|escape:'html'}{$item.cost}{if $item.invoice_id} {else}{$i18n.label.edit}{/if}
+ + + + +
{$i18n.label.day_total}: {$user->currency|escape:'html'} {$day_total}
+{/if} +
+ +{$forms.expensesForm.close} diff --git a/WEB-INF/templates/mobile/header.tpl b/WEB-INF/templates/mobile/header.tpl index 03bfa18f..cd266aeb 100644 --- a/WEB-INF/templates/mobile/header.tpl +++ b/WEB-INF/templates/mobile/header.tpl @@ -21,7 +21,7 @@ {assign var="tab_width" value="300"} - +
@@ -53,7 +53,51 @@
+ +{if $authenticated} + {if $user->isAdmin()} + + + + + + +
  + {$i18n.menu.teams} · + {$i18n.menu.options} +
+ + {else} + + + + + +
  + {if !$user->isClient()} + {$i18n.menu.time} + {/if} + {if $user->isPluginEnabled('ex') && !$user->isClient()} + · {$i18n.menu.expenses} + {/if} + {if !$user->isClient() && ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + · {$i18n.menu.projects} + {/if} + {if $user->canManageTeam() && ($smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + · {$i18n.menu.tasks} + {/if} + {if !$user->isClient()} + · {$i18n.menu.users} + {/if} + {if $user->canManageTeam() && $user->isPluginEnabled('cl')} + · {$i18n.menu.clients} + {/if} +
+ + {/if} +{/if} + {if $err->yes()} diff --git a/WEB-INF/templates/mobile/project_add.tpl b/WEB-INF/templates/mobile/project_add.tpl new file mode 100644 index 00000000..2bd7f6fc --- /dev/null +++ b/WEB-INF/templates/mobile/project_add.tpl @@ -0,0 +1,42 @@ +{$forms.projectForm.open} +
+ + + +
+ + + + + + + + + + + + + + + +{if ($smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + + + + + +{/if} + + + + + + + + + + + +
{$i18n.label.thing_name} (*):{$forms.projectForm.project_name.control}
{$i18n.label.description}:{$forms.projectForm.description.control}
 
{$i18n.label.users}:{$forms.projectForm.users.control}
 
{$i18n.label.tasks}:{$forms.projectForm.tasks.control}
{$i18n.label.required_fields}
 
{$forms.projectForm.btn_add.control}
+
+{$forms.projectForm.close} \ No newline at end of file diff --git a/WEB-INF/templates/mobile/project_delete.tpl b/WEB-INF/templates/mobile/project_delete.tpl new file mode 100644 index 00000000..bf1fc95f --- /dev/null +++ b/WEB-INF/templates/mobile/project_delete.tpl @@ -0,0 +1,18 @@ +{$forms.projectDeleteForm.open} + + + + +
+ + + + + + + + + +
{$project_to_delete|escape:'html'}
 
{$forms.projectDeleteForm.btn_delete.control}  {$forms.projectDeleteForm.btn_cancel.control}
+
+{$forms.projectDeleteForm.close} \ No newline at end of file diff --git a/WEB-INF/templates/mobile/project_edit.tpl b/WEB-INF/templates/mobile/project_edit.tpl new file mode 100644 index 00000000..c94e4754 --- /dev/null +++ b/WEB-INF/templates/mobile/project_edit.tpl @@ -0,0 +1,45 @@ +{$forms.projectForm.open} + + + + +
+ + + + + + + + + + + + + + + + + + +{if ($smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + + + + + +{/if} + + + + + + + + + + + +
{$i18n.label.thing_name} (*):{$forms.projectForm.project_name.control}
{$i18n.label.description}:{$forms.projectForm.description.control}
{$i18n.label.status}:{$forms.projectForm.status.control}
 
{$i18n.label.users}:{$forms.projectForm.users.control}
 
{$i18n.label.tasks}:{$forms.projectForm.tasks.control}
{$i18n.label.required_fields}
 
{$forms.projectForm.btn_save.control} {$forms.projectForm.btn_copy.control}
+
+{$forms.projectForm.close} \ No newline at end of file diff --git a/WEB-INF/templates/mobile/projects.tpl b/WEB-INF/templates/mobile/projects.tpl new file mode 100644 index 00000000..45cd184a --- /dev/null +++ b/WEB-INF/templates/mobile/projects.tpl @@ -0,0 +1,80 @@ + + + + + + +
+{if $user->canManageTeam()} + + {if $inactive_projects} + + {/if} + + + + + + {if $active_projects} + {foreach $active_projects as $project} + + + + + + {/foreach} + {/if} +
{$i18n.form.projects.active_projects}
{$i18n.label.thing_name}{$i18n.label.description}{$i18n.label.edit}
{$project.name|escape:'html'}{$project.description|escape:'html'}{$i18n.label.edit}
+ + + + + +

+
+
+ + {if $inactive_projects} + + + + + + + + {foreach $inactive_projects as $project} + + + + + + {/foreach} +
{$i18n.form.projects.inactive_projects}
{$i18n.label.thing_name}{$i18n.label.description}{$i18n.label.edit}
{$project.name|escape:'html'}{$project.description|escape:'html'}{$i18n.label.edit}
+ + + + + +

+
+
+ {/if} +{else} + + + + + + {if $active_projects} + {foreach $active_projects as $project} + + + + + {/foreach} + {/if} +
{$i18n.label.thing_name}{$i18n.label.description}
{$project.name|escape:'html'}{$project.description|escape:'html'}
+{/if} +
diff --git a/WEB-INF/templates/mobile/task_add.tpl b/WEB-INF/templates/mobile/task_add.tpl new file mode 100644 index 00000000..53ce483d --- /dev/null +++ b/WEB-INF/templates/mobile/task_add.tpl @@ -0,0 +1,33 @@ +{$forms.taskForm.open} + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
{$i18n.label.thing_name} (*):{$forms.taskForm.name.control}
{$i18n.label.description}:{$forms.taskForm.description.control}
{$i18n.label.projects}:{$forms.taskForm.projects.control}
{$i18n.label.required_fields}
 
{$forms.taskForm.btn_submit.control}
+
+{$forms.taskForm.close} diff --git a/WEB-INF/templates/mobile/task_delete.tpl b/WEB-INF/templates/mobile/task_delete.tpl new file mode 100644 index 00000000..f25cc951 --- /dev/null +++ b/WEB-INF/templates/mobile/task_delete.tpl @@ -0,0 +1,20 @@ +{$forms.taskDeleteForm.open} + + + + +
+ + + + + + + + + + + +
{$task_to_delete|escape:'html'}
 
{$forms.taskDeleteForm.btn_delete.control}  {$forms.taskDeleteForm.btn_cancel.control}
+
+{$forms.taskDeleteForm.close} diff --git a/WEB-INF/templates/mobile/task_edit.tpl b/WEB-INF/templates/mobile/task_edit.tpl new file mode 100644 index 00000000..5af8d935 --- /dev/null +++ b/WEB-INF/templates/mobile/task_edit.tpl @@ -0,0 +1,37 @@ +{$forms.taskForm.open} + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{$i18n.label.thing_name} (*):{$forms.taskForm.name.control}
{$i18n.label.description}:{$forms.taskForm.description.control}
{$i18n.label.status}:{$forms.taskForm.status.control}
{$i18n.label.projects}:{$forms.taskForm.projects.control}
{$i18n.label.required_fields}
 
{$forms.taskForm.btn_save.control} {$forms.taskForm.btn_copy.control}
+
+{$forms.taskForm.close} diff --git a/WEB-INF/templates/mobile/tasks.tpl b/WEB-INF/templates/mobile/tasks.tpl new file mode 100644 index 00000000..0a521073 --- /dev/null +++ b/WEB-INF/templates/mobile/tasks.tpl @@ -0,0 +1,80 @@ + + + + + + +
+{if $user->canManageTeam()} + + {if $inactive_tasks} + + {/if} + + + + + + {if $active_tasks} + {foreach $active_tasks as $task} + + + + + + {/foreach} + {/if} +
{$i18n.form.tasks.active_tasks}
{$i18n.label.thing_name}{$i18n.label.description}{$i18n.label.edit}
{$task.name|escape:'html'}{$task.description|escape:'html'}{$i18n.label.edit}
+ + + + + +

+
+
+ + {if $inactive_tasks} + + + + + + + + {foreach $inactive_tasks as $task} + + + + + + {/foreach} +
{$i18n.form.tasks.inactive_tasks}
{$i18n.label.thing_name}{$i18n.label.description}{$i18n.label.edit}
{$task.name|escape:'html'}{$task.description|escape:'html'}{$i18n.label.edit}
+ + + + + +

+
+
+ {/if} +{else} + + + + + + {if $active_tasks} + {foreach $active_tasks as $task} + + + + + {/foreach} + {/if} +
{$i18n.label.thing_name}{$i18n.label.description}
{$task.name|escape:'html'}{$task.description|escape:'html'}
+ {/if} +
diff --git a/WEB-INF/templates/mobile/time.tpl b/WEB-INF/templates/mobile/time.tpl index ed83c9cb..c3e9b59b 100644 --- a/WEB-INF/templates/mobile/time.tpl +++ b/WEB-INF/templates/mobile/time.tpl @@ -220,7 +220,7 @@ function get_date() { {if $time_records} - +
{foreach $time_records as $record} {if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} diff --git a/WEB-INF/templates/mobile/user_add.tpl b/WEB-INF/templates/mobile/user_add.tpl new file mode 100644 index 00000000..29714044 --- /dev/null +++ b/WEB-INF/templates/mobile/user_add.tpl @@ -0,0 +1,83 @@ + + +{$forms.userForm.open} +
+
+ + + + + + + + +{if !$auth_external} + + + + + + + + +{/if} + + + + +{if $user->isManager()} + + + + +{/if} + + + + +{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + + + + + + + +{/if} + + + +
{$i18n.label.person_name} (*):{$forms.userForm.name.control}
{$i18n.label.login} (*):{$forms.userForm.login.control}
{$i18n.label.password} (*):{$forms.userForm.pas1.control}
{$i18n.label.confirm_password} (*):{$forms.userForm.pas2.control}
{$i18n.label.email}:{$forms.userForm.email.control}
{$i18n.form.users.role}:{$forms.userForm.role.control} {$forms.userForm.client.control}
{$i18n.form.users.default_rate} (0{$user->decimal_mark}00):{$forms.userForm.rate.control}
{$i18n.label.projects}:{$forms.userForm.projects.control}
{$i18n.label.required_fields}
{$forms.userForm.btn_submit.control}
+ +{$forms.userForm.close} diff --git a/WEB-INF/templates/mobile/user_delete.tpl b/WEB-INF/templates/mobile/user_delete.tpl new file mode 100644 index 00000000..1f5b442e --- /dev/null +++ b/WEB-INF/templates/mobile/user_delete.tpl @@ -0,0 +1,20 @@ +{$forms.userDeleteForm.open} + + + + +
+ + + + + + + + + + + +
{$user_to_delete|escape:'html'}
 
{$forms.userDeleteForm.btn_delete.control}  {$forms.userDeleteForm.btn_cancel.control}
+
+{$forms.userDeleteForm.close} diff --git a/WEB-INF/templates/mobile/user_edit.tpl b/WEB-INF/templates/mobile/user_edit.tpl new file mode 100644 index 00000000..7f81ca03 --- /dev/null +++ b/WEB-INF/templates/mobile/user_edit.tpl @@ -0,0 +1,112 @@ + + +{$forms.userForm.open} + +
+ + + + + + + + +{if !$auth_external} + + + + + + + + +{/if} + + + + +{if $user->isManager() && ($user->id != $user_id)} + + + + +{/if} +{* Prohibit deactivating team manager. Deactivating others is ok. *} +{if $user->canManageTeam() && !($user->isManager() && $user->id == $user_id)} + + + + +{/if} + + + + +{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} + + + + +{/if} + + + + + + +
{$i18n.label.person_name} (*):{$forms.userForm.name.control}
{$i18n.label.login} (*):{$forms.userForm.login.control}
{$i18n.label.password} (*):{$forms.userForm.pas1.control}
{$i18n.label.confirm_password} (*):{$forms.userForm.pas2.control}
{$i18n.label.email}:{$forms.userForm.email.control}
{$i18n.form.users.role}:{$forms.userForm.role.control} {$forms.userForm.client.control}
{$i18n.label.status}:{$forms.userForm.status.control}
{$i18n.form.users.default_rate} (0{$user->decimal_mark}00):{$forms.userForm.rate.control}
{$i18n.label.projects}:{$forms.userForm.projects.control}
{$i18n.label.required_fields}
{$forms.userForm.btn_submit.control}
+ +{$forms.userForm.close} diff --git a/WEB-INF/templates/mobile/users.tpl b/WEB-INF/templates/mobile/users.tpl new file mode 100644 index 00000000..aab86d82 --- /dev/null +++ b/WEB-INF/templates/mobile/users.tpl @@ -0,0 +1,124 @@ + + + + + + +
+{if $user->canManageTeam()} + + {if $inactive_users} + + {/if} + + + + + + + {if $active_users} + {foreach $active_users as $u} + + + + {if $smarty.const.ROLE_MANAGER == $u.role} + + {elseif $smarty.const.ROLE_COMANAGER == $u.role} + + {elseif $smarty.const.ROLE_CLIENT == $u.role} + + {elseif $smarty.const.ROLE_USER == $u.role} + + {/if} + {if $user->isManager()} + + + {else} + + + {/if} + + {/foreach} + {/if} +
{$i18n.form.users.active_users}
{$i18n.label.person_name}{$i18n.label.login}{$i18n.form.users.role}{$i18n.label.edit}
{$u.name|escape:'html'}{$u.login|escape:'html'}{$i18n.form.users.manager}{$i18n.form.users.comanager}{$i18n.label.client}{$i18n.label.user}{$i18n.label.edit}{if ($user->id == $u.id) || ($smarty.const.ROLE_CLIENT == $u.role) || ($smarty.const.ROLE_USER == $u.role)}{$i18n.label.edit}{/if}
+ + + + + +

+
+
+ + {if $inactive_users} + + + + + + + + + + {foreach $inactive_users as $u} + + + + {if $smarty.const.ROLE_MANAGER == $u.role} + + {elseif $smarty.const.ROLE_COMANAGER == $u.role} + + {elseif $smarty.const.ROLE_CLIENT == $u.role} + + {elseif $smarty.const.ROLE_USER == $u.role} + + {/if} + {if $user->isManager()} + + + + {else} + + + + {/if} + + {/foreach} + +
{$i18n.form.users.inactive_users}
{$i18n.label.person_name}{$i18n.label.login}{$i18n.form.users.role}{$i18n.label.edit}{$i18n.label.delete}
{$u.name|escape:'html'}{$u.login|escape:'html'}{$i18n.form.users.manager}{$i18n.form.users.comanager}{$i18n.label.client}{$i18n.label.user}{$i18n.label.edit}{if $smarty.const.ROLE_MANAGER != $u.role || $can_delete_manager}{$i18n.label.delete}{/if}{if ($user->id == $u.id) || ($smarty.const.ROLE_CLIENT == $u.role) || ($smarty.const.ROLE_USER == $u.role)}{$i18n.label.edit}{/if}{if ($user->id == $u.id) || ($smarty.const.ROLE_CLIENT == $u.role) || ($smarty.const.ROLE_USER == $u.role)}{$i18n.label.delete}{/if}
+ + + + + +
+
+
+ {/if} +{else} + + + + + + + {foreach $active_users as $u} + + + + {if $smarty.const.ROLE_MANAGER == $u.role} + + {elseif $smarty.const.ROLE_COMANAGER == $u.role} + + {elseif $smarty.const.ROLE_CLIENT == $u.role} + + {elseif $smarty.const.ROLE_USER == $u.role} + + {/if} + + {/foreach} +
{$i18n.label.person_name}{$i18n.label.login}{$i18n.form.users.role}
{$u.name|escape:'html'}{$u.login|escape:'html'}{$i18n.form.users.manager}{$i18n.form.users.comanager}{$i18n.label.client}{$i18n.label.user}
+{/if} +
diff --git a/default.css b/default.css index 62032fca..161f61f1 100644 --- a/default.css +++ b/default.css @@ -143,3 +143,34 @@ table.divider { } div#LoginAboutText { width:400px; } + +/* Mobile styles */ +.mobile-table { + border: 0; + width: 100%; + border-spacing: 0; +} + +.mobile-textarea { + width: 100%; + resize: vertical; + height: 5em; +} + +.mobile-input { + width: 100%; +} + +.mobile-table-details { + width: 100%; + table-layout: fixed; + overflow-wrap: break-word; + word-wrap: break-word; + border-spacing: 1px; + border: 0; +} + +.mobile-table-details td { + padding: 3px; +} + diff --git a/mobile/client_add.php b/mobile/client_add.php new file mode 100644 index 00000000..56f5d08a --- /dev/null +++ b/mobile/client_add.php @@ -0,0 +1,89 @@ +team_id); + +if ($request->isPost()) { + $cl_name = trim($request->getParameter('name')); + $cl_address = trim($request->getParameter('address')); + $cl_tax = $request->getParameter('tax'); + $cl_projects = $request->getParameter('projects'); +} else { + // Do not assign all projects to a new client by default. This should help to reduce clutter. + // foreach ($projects as $project_item) + // $cl_projects[] = $project_item['id']; +} + +$form = new Form('clientForm'); +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'name','value'=>$cl_name)); +$form->addInput(array('type'=>'textarea','name'=>'address','maxlength'=>'255','class'=>'mobile-textarea','value'=>$cl_address)); +$form->addInput(array('type'=>'floatfield','name'=>'tax','size'=>'10','format'=>'.2','value'=>$cl_tax)); +if (MODE_PROJECTS == $user->tracking_mode || MODE_PROJECTS_AND_TASKS == $user->tracking_mode) + $form->addInput(array('type'=>'checkboxgroup','name'=>'projects','data'=>$projects,'layout'=>'H','datakeys'=>array('id','name'),'value'=>$cl_projects)); +$form->addInput(array('type'=>'submit','name'=>'btn_submit','value'=>$i18n->getKey('button.add'))); + +if ($request->isPost()) { + // Validate user input. + if (!ttValidString($cl_name)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.client_name')); + if (!ttValidString($cl_address, true)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.client_address')); + if (!ttValidFloat($cl_tax, true)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.tax')); + + if ($err->no()) { + if (!ttClientHelper::getClientByName($cl_name)) { + if (ttClientHelper::insert(array( + 'team_id' => $user->team_id, + 'name' => $cl_name, + 'address' => $cl_address, + 'tax' => $cl_tax, + 'projects' => $cl_projects, + 'status' => ACTIVE))) { + header('Location: clients.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } else + $err->add($i18n->getKey('error.client_exists')); + } +} // isPost + +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('onload', 'onLoad="document.clientForm.name.focus()"'); +$smarty->assign('title', $i18n->getKey('title.add_client')); +$smarty->assign('content_page_name', 'mobile/client_add.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/client_delete.php b/mobile/client_delete.php new file mode 100644 index 00000000..a0caf4fe --- /dev/null +++ b/mobile/client_delete.php @@ -0,0 +1,73 @@ +getParameter('id'); +$client = ttClientHelper::getClient($id); + +$client_to_delete = $client['name']; + +$form = new Form('clientDeleteForm'); +$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$id)); +$form->addInput(array('type'=>'combobox','name'=>'delete_client_entries', + 'data'=>array('0'=>$i18n->getKey('dropdown.do_not_delete'),'1'=>$i18n->getKey('dropdown.delete')))); +$form->addInput(array('type'=>'submit','name'=>'btn_delete','value'=>$i18n->getKey('label.delete'))); +$form->addInput(array('type'=>'submit','name'=>'btn_cancel','value'=>$i18n->getKey('button.cancel'))); + +if ($request->isPost()) { + if(ttClientHelper::getClient($id)) { + if ($request->getParameter('btn_delete')) { + if (ttClientHelper::delete($id, $request->getParameter('delete_client_entries'))) { + header('Location: clients.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } + } else + $err->add($i18n->getKey('error.db')); + + if ($request->getParameter('btn_cancel')) { + header('Location: clients.php'); + exit(); + } +} // isPost + +$smarty->assign('client_to_delete', $client_to_delete); +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('title', $i18n->getKey('title.delete_client')); +$smarty->assign('content_page_name', 'mobile/client_delete.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/client_edit.php b/mobile/client_edit.php new file mode 100644 index 00000000..2c553daf --- /dev/null +++ b/mobile/client_edit.php @@ -0,0 +1,121 @@ +getParameter('id'); + +$projects = ttTeamHelper::getActiveProjects($user->team_id); + +if ($request->isPost()) { + $cl_name = trim($request->getParameter('name')); + $cl_address = trim($request->getParameter('address')); + $cl_tax = trim($request->getParameter('tax')); + $cl_status = $request->getParameter('status'); + $cl_projects = $request->getParameter('projects'); +} else { + $client = ttClientHelper::getClient($cl_id, true); + $cl_name = $client['name']; + $cl_address = $client['address']; + $cl_tax = $client['tax']; + $cl_status = $client['status']; + $assigned_projects = ttClientHelper::getAssignedProjects($cl_id); + foreach($assigned_projects as $project_item) { + $cl_projects[] = $project_item['id']; + } +} + +$form = new Form('clientForm'); +$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_id)); +$form->addInput(array('type'=>'text','name'=>'name','maxlength'=>'100','value'=>$cl_name)); +$form->addInput(array('type'=>'textarea','name'=>'address','maxlength'=>'255','class'=>'mobile-textarea','value'=>$cl_address)); +$form->addInput(array('type'=>'floatfield','name'=>'tax','size'=>'10','format'=>'.2','value'=>$cl_tax)); +$form->addInput(array('type'=>'combobox','name'=>'status','value'=>$cl_status, + 'data'=>array(ACTIVE=>$i18n->getKey('dropdown.status_active'),INACTIVE=>$i18n->getKey('dropdown.status_inactive')))); +if (MODE_PROJECTS == $user->tracking_mode || MODE_PROJECTS_AND_TASKS == $user->tracking_mode) + $form->addInput(array('type'=>'checkboxgroup','name'=>'projects','data'=>$projects,'datakeys'=>array('id','name'),'layout'=>'H','value'=>$cl_projects)); +$form->addInput(array('type'=>'submit','name'=>'btn_save','value'=>$i18n->getKey('button.save'))); +$form->addInput(array('type'=>'submit','name'=>'btn_copy','value'=>$i18n->getKey('button.copy'))); + +if ($request->isPost()) { + // Validate user input. + if (!ttValidString($cl_name)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.client_name')); + if (!ttValidString($cl_address, true)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.client_address')); + if (!ttValidFloat($cl_tax, true)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.tax')); + + if ($err->no()) { + if ($request->getParameter('btn_save')) { + $client = ttClientHelper::getClientByName($cl_name); + if (($client && ($cl_id == $client['id'])) || !$client) { + if (ttClientHelper::update(array( + 'id' => $cl_id, + 'name' => $cl_name, + 'address' => $cl_address, + 'tax' => $cl_tax, + 'status' => $cl_status, + 'projects' => $cl_projects))) { + header('Location: clients.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } else + $err->add($i18n->getKey('error.client_exists')); + } + + if ($request->getParameter('btn_copy')) { + if (!ttClientHelper::getClientByName($cl_name)) { + if (ttClientHelper::insert(array( + 'team_id' => $user->team_id, + 'name' => $cl_name, + 'address' => $cl_address, + 'tax' => $cl_tax, + 'status' => $cl_status, + 'projects' => $cl_projects))) { + header('Location: clients.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } else + $err->add($i18n->getKey('error.client_exists')); + } + } +} // isPost + +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('title', $i18n->getKey('title.edit_client')); +$smarty->assign('content_page_name', 'mobile/client_edit.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/clients.php b/mobile/clients.php new file mode 100644 index 00000000..d6ebbe40 --- /dev/null +++ b/mobile/clients.php @@ -0,0 +1,43 @@ +assign('active_clients', ttTeamHelper::getActiveClients($user->team_id, true)); +$smarty->assign('inactive_clients', ttTeamHelper::getInactiveClients($user->team_id, true)); +$smarty->assign('title', $i18n->getKey('title.clients')); +$smarty->assign('content_page_name', 'mobile/clients.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/expense_delete.php b/mobile/expense_delete.php new file mode 100644 index 00000000..38800b62 --- /dev/null +++ b/mobile/expense_delete.php @@ -0,0 +1,78 @@ +getParameter('id'); +$expense_item = ttExpenseHelper::getItem($cl_id, $user->getActiveUser()); + +// Prohibit deleting invoiced records. +if ($expense_item['invoice_id']) die($i18n->getKey('error.sys')); + +if ($request->isPost()) { + if ($request->getParameter('delete_button')) { // Delete button pressed. + + // Determine if it is okay to delete the record. + $item_date = new DateAndTime(DB_DATEFORMAT, $expense_item['date']); + if ($user->isDateLocked($item_date)) + $err->add($i18n->getKey('error.range_locked')); + + if ($err->no()) { + // Mark the record as deleted. + if (ttExpenseHelper::markDeleted($cl_id, $user->getActiveUser())) { + header('Location: expenses.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } + } + if ($request->getParameter('cancel_button')) { // Cancel button pressed. + header('Location: expenses.php'); + exit(); + } +} // isPost + +$form = new Form('expenseItemForm'); +$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_id)); +$form->addInput(array('type'=>'submit','name'=>'delete_button','value'=>$i18n->getKey('label.delete'))); +$form->addInput(array('type'=>'submit','name'=>'cancel_button','value'=>$i18n->getKey('button.cancel'))); + +$smarty->assign('expense_item', $expense_item); +$smarty->assign('forms', array($form->getName() => $form->toArray())); +$smarty->assign('title', $i18n->getKey('title.delete_expense')); +$smarty->assign('content_page_name', 'expense_delete.tpl'); +$smarty->display('index.tpl'); diff --git a/mobile/expense_edit.php b/mobile/expense_edit.php new file mode 100644 index 00000000..e14372ea --- /dev/null +++ b/mobile/expense_edit.php @@ -0,0 +1,207 @@ +getParameter('id'); + +// Get the expense item we are editing. +$expense_item = ttExpenseHelper::getItem($cl_id, $user->getActiveUser()); + +// Prohibit editing invoiced items. +if ($expense_item['invoice_id']) die($i18n->getKey('error.sys')); + +$item_date = new DateAndTime(DB_DATEFORMAT, $expense_item['date']); + +// Initialize variables. +$cl_date = $cl_client = $cl_project = $cl_item_name = $cl_cost = null; +if ($request->isPost()) { + $cl_date = trim($request->getParameter('date')); + $cl_client = $request->getParameter('client'); + $cl_project = $request->getParameter('project'); + $cl_item_name = trim($request->getParameter('item_name')); + $cl_cost = trim($request->getParameter('cost')); +} else { + $cl_date = $item_date->toString($user->date_format); + $cl_client = $expense_item['client_id']; + $cl_project = $expense_item['project_id']; + $cl_item_name = $expense_item['name']; + $cl_cost = $expense_item['cost']; +} + +// Initialize elements of 'expenseItemForm'. +$form = new Form('expenseItemForm'); + +// Dropdown for clients in MODE_TIME. Use all active clients. +if (MODE_TIME == $user->tracking_mode && $user->isPluginEnabled('cl')) { + $active_clients = ttTeamHelper::getActiveClients($user->team_id, true); + $form->addInput(array('type'=>'combobox', + 'onchange'=>'fillProjectDropdown(this.value);', + 'name'=>'client', + 'style'=>'width: 250px;', + 'value'=>$cl_client, + 'data'=>$active_clients, + 'datakeys'=>array('id', 'name'), + 'empty'=>array(''=>$i18n->getKey('dropdown.select')))); + // Note: in other modes the client list is filtered to relevant clients only. See below. +} + +if (MODE_PROJECTS == $user->tracking_mode || MODE_PROJECTS_AND_TASKS == $user->tracking_mode) { + // Dropdown for projects assigned to user. + $project_list = $user->getAssignedProjects(); + $form->addInput(array('type'=>'combobox', + 'name'=>'project', + 'style'=>'width: 250px;', + 'value'=>$cl_project, + 'data'=>$project_list, + 'datakeys'=>array('id','name'), + 'empty'=>array(''=>$i18n->getKey('dropdown.select')))); + + // Dropdown for clients if the clients plugin is enabled. + if ($user->isPluginEnabled('cl')) { + $active_clients = ttTeamHelper::getActiveClients($user->team_id, true); + // We need an array of assigned project ids to do some trimming. + foreach($project_list as $project) + $projects_assigned_to_user[] = $project['id']; + + // Build a client list out of active clients. Use only clients that are relevant to user. + // Also trim their associated project list to only assigned projects (to user). + foreach($active_clients as $client) { + $projects_assigned_to_client = explode(',', $client['projects']); + $intersection = array_intersect($projects_assigned_to_client, $projects_assigned_to_user); + if ($intersection) { + $client['projects'] = implode(',', $intersection); + $client_list[] = $client; + } + } + $form->addInput(array('type'=>'combobox', + 'onchange'=>'fillProjectDropdown(this.value);', + 'name'=>'client', + 'style'=>'width: 250px;', + 'value'=>$cl_client, + 'data'=>$client_list, + 'datakeys'=>array('id', 'name'), + 'empty'=>array(''=>$i18n->getKey('dropdown.select')))); + } +} +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'item_name','style'=>'width: 250px;','value'=>$cl_item_name)); +$form->addInput(array('type'=>'text','maxlength'=>'40','name'=>'cost','style'=>'width: 100px;','value'=>$cl_cost)); +$form->addInput(array('type'=>'datefield','name'=>'date','maxlength'=>'20','value'=>$cl_date)); +// Hidden control for record id. +$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_id)); +$form->addInput(array('type'=>'hidden','name'=>'browser_today','value'=>'')); // User current date, which gets filled in on btn_save or btn_copy click. +$form->addInput(array('type'=>'submit','name'=>'btn_save','onclick'=>'browser_today.value=get_date()','value'=>$i18n->getKey('button.save'))); +$form->addInput(array('type'=>'submit','name'=>'btn_copy','onclick'=>'browser_today.value=get_date()','value'=>$i18n->getKey('button.copy'))); +$form->addInput(array('type'=>'submit','name'=>'btn_delete','value'=>$i18n->getKey('label.delete'))); + +if ($request->isPost()) { + // Validate user input. + if ($user->isPluginEnabled('cl') && $user->isPluginEnabled('cm') && !$cl_client) + $err->add($i18n->getKey('error.client')); + if (MODE_PROJECTS == $user->tracking_mode || MODE_PROJECTS_AND_TASKS == $user->tracking_mode) { + if (!$cl_project) $err->add($i18n->getKey('error.project')); + } + if (!ttValidString($cl_item_name)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.item')); + if (!ttValidFloat($cl_cost)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.cost')); + if (!ttValidDate($cl_date)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.date')); + + // This is a new date for the expense item. + $new_date = new DateAndTime($user->date_format, $cl_date); + + // Prohibit creating entries in future. + if (defined('FUTURE_ENTRIES') && !isTrue(FUTURE_ENTRIES)) { + $browser_today = new DateAndTime(DB_DATEFORMAT, $request->getParameter('browser_today', null)); + if ($new_date->after($browser_today)) + $err->add($i18n->getKey('error.future_date')); + } + + // Save record. + if ($request->getParameter('btn_save')) { + // We need to: + // 1) Prohibit updating locked entries (that are in locked range). + // 2) Prohibit saving unlocked entries into locked range. + + // Now, step by step. + // 1) Prohibit saving locked entries in any form. + if ($user->isDateLocked($item_date)) + $err->add($i18n->getKey('error.range_locked')); + + // 2) Prohibit saving unlocked entries into locked range. + if ($err->no() && $user->isDateLocked($new_date)) + $err->add($i18n->getKey('error.range_locked')); + + // Now, an update. + if ($err->no()) { + if (ttExpenseHelper::update(array('id'=>$cl_id,'date'=>$new_date->toString(DB_DATEFORMAT),'user_id'=>$user->getActiveUser(), + 'client_id'=>$cl_client,'project_id'=>$cl_project,'name'=>$cl_item_name,'cost'=>$cl_cost))) { + header('Location: expenses.php?date='.$new_date->toString(DB_DATEFORMAT)); + exit(); + } + } + } + + // Save as new record. + if ($request->getParameter('btn_copy')) { + // We need to prohibit saving into locked interval. + if ($user->isDateLocked($new_date)) + $err->add($i18n->getKey('error.range_locked')); + + // Now, a new insert. + if ($err->no()) { + if (ttExpenseHelper::insert(array('date'=>$new_date->toString(DB_DATEFORMAT),'user_id'=>$user->getActiveUser(), + 'client_id'=>$cl_client,'project_id'=>$cl_project,'name'=>$cl_item_name,'cost'=>$cl_cost,'status'=>1))) { + header('Location: expenses.php?date='.$new_date->toString(DB_DATEFORMAT)); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } + } + + if ($request->getParameter('btn_delete')) { + header("Location: expense_delete.php?id=$cl_id"); + exit(); + } +} // isPost + +$smarty->assign('client_list', $client_list); +$smarty->assign('project_list', $project_list); +$smarty->assign('task_list', $task_list); +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('title', $i18n->getKey('title.edit_expense')); +$smarty->assign('content_page_name', 'mobile/expense_edit.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/expenses.php b/mobile/expenses.php new file mode 100644 index 00000000..4246f589 --- /dev/null +++ b/mobile/expenses.php @@ -0,0 +1,198 @@ +getParameter('date', @$_SESSION['date']); +$selected_date = new DateAndTime(DB_DATEFORMAT, $cl_date); +if($selected_date->isError()) + $selected_date = new DateAndTime(DB_DATEFORMAT); +if(!$cl_date) + $cl_date = $selected_date->toString(DB_DATEFORMAT); +$_SESSION['date'] = $cl_date; + +// Determine previous and next dates for simple navigation. +$prev_date = date('Y-m-d', strtotime('-1 day', strtotime($cl_date))); +$next_date = date('Y-m-d', strtotime('+1 day', strtotime($cl_date))); + +// Initialize variables. +$on_behalf_id = $request->getParameter('onBehalfUser', (isset($_SESSION['behalf_id']) ? $_SESSION['behalf_id'] : $user->id)); +$cl_client = $request->getParameter('client', ($request->getMethod()=='POST' ? null : @$_SESSION['client'])); +$_SESSION['client'] = $cl_client; +$cl_project = $request->getParameter('project', ($request->getMethod()=='POST' ? null : @$_SESSION['project'])); +$_SESSION['project'] = $cl_project; +$cl_item_name = $request->getParameter('item_name'); +$cl_cost = $request->getParameter('cost'); + +// Elements of expensesForm. +$form = new Form('expensesForm'); + +if ($user->canManageTeam()) { + $user_list = ttTeamHelper::getActiveUsers(array('putSelfFirst'=>true)); + if (count($user_list) > 1) { + $form->addInput(array('type'=>'combobox', + 'onchange'=>'this.form.submit();', + 'name'=>'onBehalfUser', + 'style'=>'width: 250px;', + 'value'=>$on_behalf_id, + 'data'=>$user_list, + 'datakeys'=>array('id','name'))); + $smarty->assign('on_behalf_control', 1); + } +} + +// Dropdown for clients in MODE_TIME. Use all active clients. +if (MODE_TIME == $user->tracking_mode && $user->isPluginEnabled('cl')) { + $active_clients = ttTeamHelper::getActiveClients($user->team_id, true); + $form->addInput(array('type'=>'combobox', + 'onchange'=>'fillProjectDropdown(this.value);', + 'name'=>'client', + 'style'=>'width: 250px;', + 'value'=>$cl_client, + 'data'=>$active_clients, + 'datakeys'=>array('id', 'name'), + 'empty'=>array(''=>$i18n->getKey('dropdown.select')))); + // Note: in other modes the client list is filtered to relevant clients only. See below. +} + +if (MODE_PROJECTS == $user->tracking_mode || MODE_PROJECTS_AND_TASKS == $user->tracking_mode) { + // Dropdown for projects assigned to user. + $project_list = $user->getAssignedProjects(); + $form->addInput(array('type'=>'combobox', + // 'onchange'=>'fillTaskDropdown(this.value);', + 'name'=>'project', + 'style'=>'width: 250px;', + 'value'=>$cl_project, + 'data'=>$project_list, + 'datakeys'=>array('id','name'), + 'empty'=>array(''=>$i18n->getKey('dropdown.select')))); + + // Dropdown for clients if the clients plugin is enabled. + if ($user->isPluginEnabled('cl')) { + $active_clients = ttTeamHelper::getActiveClients($user->team_id, true); + // We need an array of assigned project ids to do some trimming. + foreach($project_list as $project) + $projects_assigned_to_user[] = $project['id']; + + // Build a client list out of active clients. Use only clients that are relevant to user. + // Also trim their associated project list to only assigned projects (to user). + foreach($active_clients as $client) { + $projects_assigned_to_client = explode(',', $client['projects']); + $intersection = array_intersect($projects_assigned_to_client, $projects_assigned_to_user); + if ($intersection) { + $client['projects'] = implode(',', $intersection); + $client_list[] = $client; + } + } + $form->addInput(array('type'=>'combobox', + 'onchange'=>'fillProjectDropdown(this.value);', + 'name'=>'client', + 'style'=>'width: 250px;', + 'value'=>$cl_client, + 'data'=>$client_list, + 'datakeys'=>array('id', 'name'), + 'empty'=>array(''=>$i18n->getKey('dropdown.select')))); + } +} +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'item_name','style'=>'width: 250px;','value'=>$cl_item_name)); +$form->addInput(array('type'=>'text','maxlength'=>'40','name'=>'cost','style'=>'width: 100px;','value'=>$cl_cost)); +$form->addInput(array('type'=>'calendar','name'=>'date','highlight'=>'expenses','value'=>$cl_date)); // calendar +$form->addInput(array('type'=>'hidden','name'=>'browser_today','value'=>'')); // User current date, which gets filled in on btn_submit click. +$form->addInput(array('type'=>'submit','name'=>'btn_submit','onclick'=>'browser_today.value=get_date()','value'=>$i18n->getKey('button.submit'))); + +// Submit. +if ($request->isPost()) { + if ($request->getParameter('btn_submit')) { + // Validate user input. + if ($user->isPluginEnabled('cl') && $user->isPluginEnabled('cm') && !$cl_client) + $err->add($i18n->getKey('error.client')); + if (MODE_PROJECTS == $user->tracking_mode || MODE_PROJECTS_AND_TASKS == $user->tracking_mode) { + if (!$cl_project) $err->add($i18n->getKey('error.project')); + } + if (!ttValidString($cl_item_name)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.item')); + if (!ttValidFloat($cl_cost)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.cost')); + + // Prohibit creating entries in future. + if (defined('FUTURE_ENTRIES') && !isTrue(FUTURE_ENTRIES)) { + $browser_today = new DateAndTime(DB_DATEFORMAT, $request->getParameter('browser_today', null)); + if ($selected_date->after($browser_today)) + $err->add($i18n->getKey('error.future_date')); + } + // Finished validating input data. + + // Prohibit creating entries in locked range. + if ($user->isDateLocked($selected_date)) + $err->add($i18n->getKey('error.range_locked')); + + // Insert record. + if ($err->no()) { + if (ttExpenseHelper::insert(array('date'=>$cl_date,'user_id'=>$user->getActiveUser(), + 'client_id'=>$cl_client,'project_id'=>$cl_project,'name'=>$cl_item_name,'cost'=>$cl_cost,'status'=>1))) { + header('Location: expenses.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } + } elseif ($request->getParameter('onBehalfUser')) { + if($user->canManageTeam()) { + unset($_SESSION['behalf_id']); + unset($_SESSION['behalf_name']); + + if($on_behalf_id != $user->id) { + $_SESSION['behalf_id'] = $on_behalf_id; + $_SESSION['behalf_name'] = ttUserHelper::getUserName($on_behalf_id); + } + header('Location: expenses.php'); + exit(); + } + } +} + +$smarty->assign('next_date', $next_date); +$smarty->assign('prev_date', $prev_date); +$smarty->assign('day_total', ttExpenseHelper::getTotalForDay($user->getActiveUser(), $cl_date)); +$smarty->assign('expense_items', ttExpenseHelper::getItems($user->getActiveUser(), $cl_date)); +$smarty->assign('client_list', $client_list); +$smarty->assign('project_list', $project_list); +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('timestring', $selected_date->toString($user->date_format)); +$smarty->assign('title', $i18n->getKey('title.expenses')); +$smarty->assign('content_page_name', 'mobile/expenses.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/project_add.php b/mobile/project_add.php new file mode 100644 index 00000000..4c9ad185 --- /dev/null +++ b/mobile/project_add.php @@ -0,0 +1,95 @@ +team_id); +foreach ($tasks as $task_item) + $all_tasks[$task_item['id']] = $task_item['name']; + +if ($request->isPost()) { + $cl_name = trim($request->getParameter('project_name')); + $cl_description = trim($request->getParameter('description')); + $cl_users = $request->getParameter('users', array()); + $cl_tasks = $request->getParameter('tasks', array()); +} else { + foreach ($users as $user_item) + $cl_users[] = $user_item['id']; + foreach ($tasks as $task_item) + $cl_tasks[] = $task_item['id']; +} + +$form = new Form('projectForm'); +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'project_name','value'=>$cl_name)); +$form->addInput(array('type'=>'textarea','name'=>'description','class'=>'mobile-textarea','value'=>$cl_description)); +$form->addInput(array('type'=>'checkboxgroup','name'=>'users','data'=>$all_users,'layout'=>'H','value'=>$cl_users)); +if (MODE_PROJECTS_AND_TASKS == $user->tracking_mode) + $form->addInput(array('type'=>'checkboxgroup','name'=>'tasks','data'=>$all_tasks,'layout'=>'H','value'=>$cl_tasks)); +$form->addInput(array('type'=>'submit','name'=>'btn_add','value'=>$i18n->getKey('button.add'))); + +if ($request->isPost()) { + // Validate user input. + if (!ttValidString($cl_name)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.thing_name')); + if (!ttValidString($cl_description, true)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.description')); + + if ($err->no()) { + if (!ttProjectHelper::getProjectByName($cl_name)) { + if (ttProjectHelper::insert(array( + 'team_id' => $user->team_id, + 'name' => $cl_name, + 'description' => $cl_description, + 'users' => $cl_users, + 'tasks' => $cl_tasks, + 'status' => ACTIVE))) { + header('Location: projects.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } else + $err->add($i18n->getKey('error.project_exists')); + } +} // isPost + +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('onload', 'onLoad="document.projectForm.project_name.focus()"'); +$smarty->assign('title', $i18n->getKey('title.add_project')); +$smarty->assign('content_page_name', 'mobile/project_add.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/project_delete.php b/mobile/project_delete.php new file mode 100644 index 00000000..79ed438e --- /dev/null +++ b/mobile/project_delete.php @@ -0,0 +1,69 @@ +getParameter('id'); +$project = ttProjectHelper::get($cl_project_id); +$project_to_delete = $project['name']; + +$form = new Form('projectDeleteForm'); +$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_project_id)); +$form->addInput(array('type'=>'submit','name'=>'btn_delete','value'=>$i18n->getKey('label.delete'))); +$form->addInput(array('type'=>'submit','name'=>'btn_cancel','value'=>$i18n->getKey('button.cancel'))); + +if ($request->isPost()) { + if ($request->getParameter('btn_delete')) { + if(ttProjectHelper::get($cl_project_id)) { + if (ttProjectHelper::delete($cl_project_id)) { + header('Location: projects.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } else + $err->add($i18n->getKey('error.db')); + } elseif ($request->getParameter('btn_cancel')) { + header('Location: projects.php'); + exit(); + } +} // isPost + +$smarty->assign('project_to_delete', $project_to_delete); +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('onload', 'onLoad="document.projectDeleteForm.btn_cancel.focus()"'); +$smarty->assign('title', $i18n->getKey('title.delete_project')); +$smarty->assign('content_page_name', 'mobile/project_delete.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/project_edit.php b/mobile/project_edit.php new file mode 100644 index 00000000..c1eb1804 --- /dev/null +++ b/mobile/project_edit.php @@ -0,0 +1,133 @@ +getParameter('id'); + +$users = ttTeamHelper::getActiveUsers(); +foreach ($users as $user_item) + $all_users[$user_item['id']] = $user_item['name']; + +$tasks = ttTeamHelper::getActiveTasks($user->team_id); +foreach ($tasks as $task_item) + $all_tasks[$task_item['id']] = $task_item['name']; + +if ($request->isPost()) { + $cl_name = trim($request->getParameter('project_name')); + $cl_description = trim($request->getParameter('description')); + $cl_status = $request->getParameter('status'); + $cl_users = $request->getParameter('users', array()); + $cl_tasks = $request->getParameter('tasks', array()); +} else { + $project = ttProjectHelper::get($cl_project_id); + $cl_name = $project['name']; + $cl_description = $project['description']; + $cl_status = $project['status']; + + $mdb2 = getConnection(); + $sql = "select user_id from tt_user_project_binds where status = 1 and project_id = $cl_project_id"; + $res = $mdb2->query($sql); + if (is_a($res, 'PEAR_Error')) + die($res->getMessage()); + while ($row = $res->fetchRow()) + $cl_users[] = $row['user_id']; + + $cl_tasks = explode(',', $project['tasks']); +} + +$form = new Form('projectForm'); +$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_project_id)); +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'project_name','value'=>$cl_name)); +$form->addInput(array('type'=>'textarea','name'=>'description','class'=>'mobile-textarea','value'=>$cl_description)); +$form->addInput(array('type'=>'combobox','name'=>'status','value'=>$cl_status, + 'data'=>array(ACTIVE=>$i18n->getKey('dropdown.status_active'),INACTIVE=>$i18n->getKey('dropdown.status_inactive')))); +$form->addInput(array('type'=>'checkboxgroup','name'=>'users','data'=>$all_users,'layout'=>'H','value'=>$cl_users)); +if (MODE_PROJECTS_AND_TASKS == $user->tracking_mode) + $form->addInput(array('type'=>'checkboxgroup','name'=>'tasks','data'=>$all_tasks,'layout'=>'H','value'=>$cl_tasks)); +$form->addInput(array('type'=>'submit','name'=>'btn_save','value'=>$i18n->getKey('button.save'))); +$form->addInput(array('type'=>'submit','name'=>'btn_copy','value'=>$i18n->getKey('button.copy'))); + +if ($request->isPost()) { + // Validate user input. + if (!ttValidString($cl_name)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.thing_name')); + if (!ttValidString($cl_description, true)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.description')); + + if ($err->no()) { + if ($request->getParameter('btn_save')) { + $existing_project = ttProjectHelper::getProjectByName($cl_name); + if (!$existing_project || ($cl_project_id == $existing_project['id'])) { + // Update project information. + if (ttProjectHelper::update(array( + 'id' => $cl_project_id, + 'name' => $cl_name, + 'description' => $cl_description, + 'status' => $cl_status, + 'users' => $cl_users, + 'tasks' => $cl_tasks))) { + header('Location: projects.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } else + $err->add($i18n->getKey('error.project_exists')); + } + + if ($request->getParameter('btn_copy')) { + if (!ttProjectHelper::getProjectByName($cl_name)) { + if (ttProjectHelper::insert(array( + 'team_id' => $user->team_id, + 'name' => $cl_name, + 'description' => $cl_description, + 'users' => $cl_users, + 'tasks' => $cl_tasks, + 'status' => ACTIVE))) { + header('Location: projects.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } else + $err->add($i18n->getKey('error.project_exists')); + } + } +} // isPost + +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('onload', 'onLoad="document.projectForm.project_name.focus()"'); +$smarty->assign('title', $i18n->getKey('title.edit_project')); +$smarty->assign('content_page_name', 'mobile/project_edit.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/projects.php b/mobile/projects.php new file mode 100644 index 00000000..33d1d51b --- /dev/null +++ b/mobile/projects.php @@ -0,0 +1,49 @@ +canManageTeam()) { + $active_projects = ttTeamHelper::getActiveProjects($user->team_id); + $inactive_projects = ttTeamHelper::getInactiveProjects($user->team_id); +} else + $active_projects = $user->getAssignedProjects(); + +$smarty->assign('active_projects', $active_projects); +$smarty->assign('inactive_projects', $inactive_projects); +$smarty->assign('title', $i18n->getKey('title.projects')); +$smarty->assign('content_page_name', 'mobile/projects.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/task_add.php b/mobile/task_add.php new file mode 100644 index 00000000..9319e2a9 --- /dev/null +++ b/mobile/task_add.php @@ -0,0 +1,84 @@ +team_id); + +if ($request->isPost()) { + $cl_name = trim($request->getParameter('name')); + $cl_description = trim($request->getParameter('description')); + $cl_projects = $request->getParameter('projects'); +} else { + foreach ($projects as $project_item) + $cl_projects[] = $project_item['id']; +} + +$form = new Form('taskForm'); +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'name','value'=>$cl_name)); +$form->addInput(array('type'=>'textarea','name'=>'description','class'=>'mobile-textarea','value'=>$cl_description)); +$form->addInput(array('type'=>'checkboxgroup','name'=>'projects','layout'=>'H','data'=>$projects,'datakeys'=>array('id','name'),'value'=>$cl_projects)); +$form->addInput(array('type'=>'submit','name'=>'btn_submit','value'=>$i18n->getKey('button.add'))); + +if ($request->isPost()) { + // Validate user input. + if (!ttValidString($cl_name)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.thing_name')); + if (!ttValidString($cl_description, true)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.description')); + + if ($err->no()) { + if (!ttTaskHelper::getTaskByName($cl_name)) { + if (ttTaskHelper::insert(array( + 'team_id' => $user->team_id, + 'name' => $cl_name, + 'description' => $cl_description, + 'status' => ACTIVE, + 'projects' => $cl_projects))) { + header('Location: tasks.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } else + $err->add($i18n->getKey('error.task_exists')); + } +} // isPost + +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('onload', 'onLoad="document.taskForm.name.focus()"'); +$smarty->assign('title', $i18n->getKey('title.add_task')); +$smarty->assign('content_page_name', 'mobile/task_add.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/task_delete.php b/mobile/task_delete.php new file mode 100644 index 00000000..aa74be1f --- /dev/null +++ b/mobile/task_delete.php @@ -0,0 +1,69 @@ +getParameter('id'); +$task = ttTaskHelper::getTask($cl_task_id); +$task_to_delete = $task['name']; + +$form = new Form('taskDeleteForm'); +$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_task_id)); +$form->addInput(array('type'=>'submit','name'=>'btn_delete','value'=>$i18n->getKey('label.delete'))); +$form->addInput(array('type'=>'submit','name'=>'btn_cancel','value'=>$i18n->getKey('button.cancel'))); + +if ($request->isPost()) { + if ($request->getParameter('btn_delete')) { + if(ttTaskHelper::getTask($cl_task_id)) { + if (ttTaskHelper::delete($cl_task_id)) { + header('Location: tasks.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } else + $err->add($i18n->getKey('error.db')); + } elseif ($request->getParameter('btn_cancel')) { + header('Location: tasks.php'); + exit(); + } +} // isPost + +$smarty->assign('task_to_delete', $task_to_delete); +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('onload', 'onLoad="document.taskDeleteForm.btn_cancel.focus()"'); +$smarty->assign('title', $i18n->getKey('title.delete_task')); +$smarty->assign('content_page_name', 'mobile/task_delete.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/task_edit.php b/mobile/task_edit.php new file mode 100644 index 00000000..fd5b35bb --- /dev/null +++ b/mobile/task_edit.php @@ -0,0 +1,114 @@ +getParameter('id'); +$projects = ttTeamHelper::getActiveProjects($user->team_id); + +if ($request->isPost()) { + $cl_name = trim($request->getParameter('name')); + $cl_description = trim($request->getParameter('description')); + $cl_status = $request->getParameter('status'); + $cl_projects = $request->getParameter('projects'); +} else { + $task = ttTaskHelper::getTask($cl_task_id); + $cl_name = $task['name']; + $cl_description = $task['description']; + $cl_status = $task['status']; + + $assigned_projects = ttTaskHelper::getAssignedProjects($cl_task_id); + foreach ($assigned_projects as $project_item) + $cl_projects[] = $project_item['id']; +} + +$form = new Form('taskForm'); +$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_task_id)); +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'name','value'=>$cl_name)); +$form->addInput(array('type'=>'textarea','name'=>'description','class'=>'mobile-textarea','value'=>$cl_description)); +$form->addInput(array('type'=>'combobox','name'=>'status','value'=>$cl_status, + 'data'=>array(ACTIVE=>$i18n->getKey('dropdown.status_active'),INACTIVE=>$i18n->getKey('dropdown.status_inactive')))); +$form->addInput(array('type'=>'checkboxgroup','name'=>'projects','layout'=>'H','data'=>$projects,'datakeys'=>array('id','name'),'value'=>$cl_projects)); +$form->addInput(array('type'=>'submit','name'=>'btn_save','value'=>$i18n->getKey('button.save'))); +$form->addInput(array('type'=>'submit','name'=>'btn_copy','value'=>$i18n->getKey('button.copy'))); + +if ($request->isPost()) { + // Validate user input. + if (!ttValidString($cl_name)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.thing_name')); + if (!ttValidString($cl_description, true)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.description')); + + if ($err->no()) { + if ($request->getParameter('btn_save')) { + $existing_task = ttTaskHelper::getTaskByName($cl_name); + if (!$existing_task || ($cl_task_id == $existing_task['id'])) { + // Update task information. + if (ttTaskHelper::update(array( + 'task_id' => $cl_task_id, + 'name' => $cl_name, + 'description' => $cl_description, + 'status' => $cl_status, + 'projects' => $cl_projects))) { + header('Location: tasks.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } else + $err->add($i18n->getKey('error.task_exists')); + } + + if ($request->getParameter('btn_copy')) { + if (!ttTaskHelper::getTaskByName($cl_name)) { + if (ttTaskHelper::insert(array( + 'team_id' => $user->team_id, + 'name' => $cl_name, + 'description' => $cl_description, + 'status' => $cl_status, + 'projects' => $cl_projects))) { + header('Location: tasks.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } else + $err->add($i18n->getKey('error.task_exists')); + } + } +} // isPost + +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('title', $i18n->getKey('title.edit_task')); +$smarty->assign('content_page_name', 'mobile/task_edit.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/tasks.php b/mobile/tasks.php new file mode 100644 index 00000000..9b778bbf --- /dev/null +++ b/mobile/tasks.php @@ -0,0 +1,43 @@ +assign('active_tasks', ttTeamHelper::getActiveTasks($user->team_id)); +$smarty->assign('inactive_tasks', ttTeamHelper::getInactiveTasks($user->team_id)); +$smarty->assign('title', $i18n->getKey('title.tasks')); +$smarty->assign('content_page_name', 'mobile/tasks.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/time_edit.php b/mobile/time_edit.php index d0d3f448..a99835af 100644 --- a/mobile/time_edit.php +++ b/mobile/time_edit.php @@ -184,7 +184,7 @@ if (!$user->canManageTeam() && defined('READONLY_START_FINISH') && isTrue(READON if ((TYPE_DURATION == $user->record_type) || (TYPE_ALL == $user->record_type)) $form->addInput(array('type'=>'text','name'=>'duration','value'=>$cl_duration,'onchange'=>"formDisable('duration');")); $form->addInput(array('type'=>'datefield','name'=>'date','maxlength'=>'20','value'=>$cl_date)); -$form->addInput(array('type'=>'textarea','name'=>'note','style'=>'width: 250px; height: 60px;','value'=>$cl_note)); +$form->addInput(array('type'=>'textarea','name'=>'note','class'=>'mobile-textarea','value'=>$cl_note)); // If we have custom fields - add controls for them. if ($custom_fields && $custom_fields->fields[0]) { // Only one custom field is supported at this time. diff --git a/mobile/user_add.php b/mobile/user_add.php new file mode 100644 index 00000000..f42dd4dd --- /dev/null +++ b/mobile/user_add.php @@ -0,0 +1,174 @@ +isPluginEnabled('cl')) + $clients = ttTeamHelper::getActiveClients($user->team_id); + +$assigned_projects = array(); +if ($request->isPost()) { + $cl_name = trim($request->getParameter('name')); + $cl_login = trim($request->getParameter('login')); + if (!$auth->isPasswordExternal()) { + $cl_password1 = $request->getParameter('pas1'); + $cl_password2 = $request->getParameter('pas2'); + } + $cl_email = trim($request->getParameter('email')); + $cl_role = $request->getParameter('role'); + if (!$cl_role) $cl_role = ROLE_USER; + $cl_client_id = $request->getParameter('client'); + $cl_rate = $request->getParameter('rate'); + $cl_projects = $request->getParameter('projects'); + if (is_array($cl_projects)) { + foreach ($cl_projects as $p) { + if (ttValidFloat($request->getParameter('rate_'.$p), true)) { + $project_with_rate = array(); + $project_with_rate['id'] = $p; + $project_with_rate['rate'] = $request->getParameter('rate_'.$p); + $assigned_projects[] = $project_with_rate; + } else + $err->add($i18n->getKey('error.field'), 'rate_'.$p); + } + } +} + +$form = new Form('userForm'); +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'name','value'=>$cl_name)); +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'login','value'=>$cl_login)); +if (!$auth->isPasswordExternal()) { + $form->addInput(array('type'=>'text','maxlength'=>'30','name'=>'pas1','aspassword'=>true,'value'=>$cl_password1)); + $form->addInput(array('type'=>'text','maxlength'=>'30','name'=>'pas2','aspassword'=>true,'value'=>$cl_password2)); +} +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'email','value'=>$cl_email)); + +$roles[ROLE_USER] = $i18n->getKey('label.user'); +$roles[ROLE_COMANAGER] = $i18n->getKey('form.users.comanager'); +if ($user->isPluginEnabled('cl')) + $roles[ROLE_CLIENT] = $i18n->getKey('label.client'); +$form->addInput(array('type'=>'combobox','onchange'=>'handleClientControl()','name'=>'role','value'=>$cl_role,'data'=>$roles)); +if ($user->isPluginEnabled('cl')) + $form->addInput(array('type'=>'combobox','name'=>'client','value'=>$cl_client_id,'data'=>$clients,'datakeys'=>array('id', 'name'),'empty'=>array(''=>$i18n->getKey('dropdown.select')))); + +$form->addInput(array('type'=>'floatfield','maxlength'=>'10','name'=>'rate','format'=>'.2','value'=>$cl_rate)); + +$projects = ttTeamHelper::getActiveProjects($user->team_id); + +// Define classes for the projects table. +class NameCellRenderer extends DefaultCellRenderer { + function render(&$table, $value, $row, $column, $selected = false) { + $this->setOptions(array('width'=>200,'valign'=>'top')); + $this->setValue(''); + return $this->toString(); + } +} +class RateCellRenderer extends DefaultCellRenderer { + function render(&$table, $value, $row, $column, $selected = false) { + global $assigned_projects; + $field = new FloatField('rate_'.$table->getValueAtName($row, 'id'), $table->getValueAtName($row, 'p_rate')); + $field->setFormName($table->getFormName()); + $field->setLocalization($GLOBALS['I18N']); + $field->setSize(5); + $field->setFormat('.2'); + foreach ($assigned_projects as $p) { + if ($p['id'] == $table->getValueAtName($row,'id')) $field->setValue($p['rate']); + } + $this->setValue($field->toStringControl()); + return $this->toString(); + } +} +// Create projects table. +$table = new Table('projects'); +$table->setIAScript('setDefaultRate'); +$table->setTableOptions(array('width'=>'100%','cellspacing'=>'1','cellpadding'=>'3','border'=>'0')); +$table->setRowOptions(array('valign'=>'top','class'=>'tableHeader')); +$table->setData($projects); +$table->setKeyField('id'); +$table->setValue($cl_projects); +$table->addColumn(new TableColumn('name', $i18n->getKey('label.project'), new NameCellRenderer())); +$table->addColumn(new TableColumn('p_rate', $i18n->getKey('form.users.rate'), new RateCellRenderer())); +$form->addInputElement($table); + +$form->addInput(array('type'=>'submit','name'=>'btn_submit','value'=>$i18n->getKey('button.submit'))); + +if ($request->isPost()) { + // Validate user input. + if (!ttValidString($cl_name)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.person_name')); + if (!ttValidString($cl_login)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.login')); + if (!$auth->isPasswordExternal()) { + if (!ttValidString($cl_password1)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.password')); + if (!ttValidString($cl_password2)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.confirm_password')); + if ($cl_password1 !== $cl_password2) + $err->add($i18n->getKey('error.not_equal'), $i18n->getKey('label.password'), $i18n->getKey('label.confirm_password')); + } + if (!ttValidEmail($cl_email, true)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.email')); + if (!ttValidFloat($cl_rate, true)) $err->add($i18n->getKey('error.field'), $i18n->getKey('form.users.default_rate')); + + if ($err->no()) { + if (!ttUserHelper::getUserByLogin($cl_login)) { + $fields = array( + 'name' => $cl_name, + 'login' => $cl_login, + 'password' => $cl_password1, + 'rate' => $cl_rate, + 'team_id' => $user->team_id, + 'role' => $cl_role, + 'client_id' => $cl_client_id, + 'projects' => $assigned_projects, + 'email' => $cl_email); + if (ttUserHelper::insert($fields)) { + header('Location: users.php'); + exit(); + } else + $err->add($i18n->getKey('error.db')); + } else + $err->add($i18n->getKey('error.user_exists')); + } +} // isPost + +$smarty->assign('auth_external', $auth->isPasswordExternal()); +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('onload', 'onLoad="document.userForm.name.focus();handleClientControl();"'); +$smarty->assign('title', $i18n->getKey('title.add_user')); +$smarty->assign('content_page_name', 'mobile/user_add.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/user_delete.php b/mobile/user_delete.php new file mode 100644 index 00000000..aa25a5b3 --- /dev/null +++ b/mobile/user_delete.php @@ -0,0 +1,99 @@ +getParameter('id'); + +// We need user name and login to display. +$user_details = ttUserHelper::getUserDetails($user_id); + +// Security checks. +$ok_to_go = $user->canManageTeam(); // Are we authorized for user deletes? +if ($ok_to_go) $ok_to_go = $ok_to_go && $user_details; // Are we deleting a real user? +if ($ok_to_go) $ok_to_go = $ok_to_go && ($user->team_id == $user_details['team_id']); // User belongs to our team? +if ($ok_to_go && $user->isCoManager() && (ROLE_COMANAGER == $user_details['role'])) + $ok_to_go = ($user->id == $user_details['id']); // Comanager is not allowed to delete other comanagers. +if ($ok_to_go && $user->isCoManager() && (ROLE_MANAGER == $user_details['role'])) + $ok_to_go = false; // Comanager is not allowed to delete a manager. + +if (!$ok_to_go) + die ($i18n->getKey('error.sys')); +else + $smarty->assign('user_to_delete', $user_details['name']." (".$user_details['login'].")"); + +// Create confirmation form. +$form = new Form('userDeleteForm'); +$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$user_id)); +$form->addInput(array('type'=>'submit','name'=>'btn_delete','value'=>$i18n->getKey('label.delete'))); +$form->addInput(array('type'=>'submit','name'=>'btn_cancel','value'=>$i18n->getKey('button.cancel'))); + +if ($request->isPost()) { + if ($request->getParameter('btn_delete')) { + if (ttUserHelper::markDeleted($user_id)) { + // If we deleted the "on behalf" user reset its info in session. + if ($user_id == $user->behalf_id) { + unset($_SESSION['behalf_id']); + unset($_SESSION['behalf_name']); + } + // If we deleted our own account, do housekeeping and logout. + if ($user->id == $user_id) { + // Remove tt_login cookie that stores login name. + unset($_COOKIE['tt_login']); + setcookie('tt_login', NULL, -1); + + $auth->doLogout(); + header('Location: login.php'); + } else { + header('Location: users.php'); + } + exit(); + } else { + $err->add($i18n->getKey('error.db')); + } + } + if ($request->getParameter('btn_cancel')) { + header('Location: users.php'); + exit(); + } +} // isPost + +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('title', $i18n->getKey('title.delete_user')); +$smarty->assign('content_page_name', 'mobile/user_delete.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/user_edit.php b/mobile/user_edit.php new file mode 100644 index 00000000..156e320a --- /dev/null +++ b/mobile/user_edit.php @@ -0,0 +1,237 @@ +getParameter('id'); + +// Get user details. +$user_details = ttUserHelper::getUserDetails($user_id); + +// Security checks. +$ok_to_go = $user->canManageTeam(); // Are we authorized for user management? +if ($ok_to_go) $ok_to_go = $ok_to_go && $user_details; // Are we editing a real user? +if ($ok_to_go) $ok_to_go = $ok_to_go && ($user->team_id == $user_details['team_id']); // User belongs to our team? +if ($ok_to_go && $user->isCoManager() && (ROLE_COMANAGER == $user_details['role'])) + $ok_to_go = ($user->id == $user_details['id']); // Comanager is not allowed to edit other comanagers. +if ($ok_to_go && $user->isCoManager() && (ROLE_MANAGER == $user_details['role'])) + $ok_to_go = false; // Comanager is not allowed to edit a manager. +if (!$ok_to_go) { + die ($i18n->getKey('error.sys')); +} + +if ($user->isPluginEnabled('cl')) + $clients = ttTeamHelper::getActiveClients($user->team_id); + +$projects = ttTeamHelper::getActiveProjects($user->team_id); +$assigned_projects = array(); + +if ($request->isPost()) { + $cl_name = trim($request->getParameter('name')); + $cl_login = trim($request->getParameter('login')); + if (!$auth->isPasswordExternal()) { + $cl_password1 = $request->getParameter('pas1'); + $cl_password2 = $request->getParameter('pas2'); + } + $cl_email = trim($request->getParameter('email')); + $cl_role = $request->getParameter('role'); + $cl_client_id = $request->getParameter('client'); + $cl_status = $request->getParameter('status'); + $cl_rate = $request->getParameter('rate'); + $cl_projects = $request->getParameter('projects'); + if (is_array($cl_projects)) { + foreach ($cl_projects as $p) { + if (ttValidFloat($request->getParameter('rate_'.$p), true)) { + $project_with_rate = array(); + $project_with_rate['id'] = $p; + $project_with_rate['rate'] = $request->getParameter('rate_'.$p); + $assigned_projects[] = $project_with_rate; + } else + $err->add($i18n->getKey('error.field'), 'rate_'.$p); + } + } +} else { + $cl_name = $user_details['name']; + $cl_login = $user_details['login']; + $cl_email = $user_details['email']; + $cl_rate = str_replace('.', $user->decimal_mark, $user_details['rate']); + $cl_role = $user_details['role']; + $cl_client_id = $user_details['client_id']; + $cl_status = $user_details['status']; + $cl_projects = array(); + $assigned_projects = ttProjectHelper::getAssignedProjects($user_id); + foreach($assigned_projects as $p) { + $cl_projects[] = $p['id']; + } +} + +$form = new Form('userForm'); +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'name','value'=>$cl_name)); +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'login','value'=>$cl_login)); +if (!$auth->isPasswordExternal()) { + $form->addInput(array('type'=>'text','maxlength'=>'30','name'=>'pas1','aspassword'=>true,'value'=>$cl_password1)); + $form->addInput(array('type'=>'text','maxlength'=>'30','name'=>'pas2','aspassword'=>true,'value'=>$cl_password2)); +} +$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'email','value'=>$cl_email)); + +$roles[ROLE_USER] = $i18n->getKey('label.user'); +$roles[ROLE_COMANAGER] = $i18n->getKey('form.users.comanager'); +if ($user->isPluginEnabled('cl')) + $roles[ROLE_CLIENT] = $i18n->getKey('label.client'); +$form->addInput(array('type'=>'combobox','onchange'=>'handleClientControl()','name'=>'role','value'=>$cl_role,'data'=>$roles)); +if ($user->isPluginEnabled('cl')) + $form->addInput(array('type'=>'combobox','name'=>'client','value'=>$cl_client_id,'data'=>$clients,'datakeys'=>array('id', 'name'),'empty'=>array(''=>$i18n->getKey('dropdown.select')))); + +$form->addInput(array('type'=>'combobox','name'=>'status','value'=>$cl_status, + 'data'=>array(ACTIVE=>$i18n->getKey('dropdown.status_active'),INACTIVE=>$i18n->getKey('dropdown.status_inactive')))); +$form->addInput(array('type'=>'floatfield','maxlength'=>'10','name'=>'rate','format'=>'.2','value'=>$cl_rate)); + +// Define classes for the projects table. +class NameCellRenderer extends DefaultCellRenderer { + function render(&$table, $value, $row, $column, $selected = false) { + $this->setOptions(array('width'=>200,'valign'=>'top')); + $this->setValue(''); + return $this->toString(); + } +} +class RateCellRenderer extends DefaultCellRenderer { + function render(&$table, $value, $row, $column, $selected = false) { + global $assigned_projects; + $field = new FloatField('rate_'.$table->getValueAtName($row,'id'), $table->getValueAtName($row, 'p_rate')); + $field->setFormName($table->getFormName()); + $field->setLocalization($GLOBALS['I18N']); + $field->setSize(5); + $field->setFormat('.2'); + foreach ($assigned_projects as $p) { + if ($p['id'] == $table->getValueAtName($row,'id')) $field->setValue($p['rate']); + } + $this->setValue($field->toStringControl()); + return $this->toString(); + } +} +// Create projects table. +$table = new Table('projects'); +$table->setIAScript('setRate'); +$table->setTableOptions(array('width'=>'100%','cellspacing'=>'1','cellpadding'=>'3','border'=>'0')); +$table->setRowOptions(array('valign'=>'top','class'=>'tableHeader')); +$table->setData($projects); +$table->setKeyField('id'); +$table->setValue($cl_projects); +$table->addColumn(new TableColumn('name', $i18n->getKey('label.project'), new NameCellRenderer())); +$table->addColumn(new TableColumn('p_rate', $i18n->getKey('form.users.rate'), new RateCellRenderer())); +$form->addInputElement($table); + +$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$user_id)); +$form->addInput(array('type'=>'submit','name'=>'btn_submit','value'=>$i18n->getKey('button.save'))); + +if ($request->isPost()) { + // Validate user input. + if (!ttValidString($cl_name)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.person_name')); + if (!ttValidString($cl_login)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.login')); + if (!$auth->isPasswordExternal() && ($cl_password1 || $cl_password2)) { + if (!ttValidString($cl_password1)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.password')); + if (!ttValidString($cl_password2)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.confirm_password')); + if ($cl_password1 !== $cl_password2) + $err->add($i18n->getKey('error.not_equal'), $i18n->getKey('label.password'), $i18n->getKey('label.confirm_password')); + } + if (!ttValidEmail($cl_email, true)) $err->add($i18n->getKey('error.field'), $i18n->getKey('label.email')); + if (!ttValidFloat($cl_rate, true)) $err->add($i18n->getKey('error.field'), $i18n->getKey('form.users.default_rate')); + + if ($err->no()) { + $existing_user = ttUserHelper::getUserByLogin($cl_login); + if (!$existing_user || ($user_id == $existing_user['id'])) { + + $fields = array( + 'name' => $cl_name, + 'login' => $cl_login, + 'password' => $cl_password1, + 'email' => $cl_email, + 'status' => $cl_status, + 'rate' => $cl_rate, + 'projects' => $assigned_projects); + if (right_assign_roles & $user->rights) { + $fields['role'] = $cl_role; + $fields['client_id'] = $cl_client_id; + } + + if (ttUserHelper::update($user_id, $fields)) { + + // If our own login changed, set new one in cookie to remember it. + if (($user_id == $user->id) && ($user->login != $cl_login)) { + setcookie('tt_login', $cl_login, time() + COOKIE_EXPIRE, '/'); + } + + // In case the name of the "on behalf" user has changed - set it in session. + if (($user->behalf_id == $user_id) && ($user->behalf_name != $cl_name)) { + $_SESSION['behalf_name'] = $cl_name; + } + + // If we deactivated our own account, do housekeeping and logout. + if ($user->id == $user_id && !is_null($cl_status) && $cl_status == INACTIVE) { + // Remove tt_login cookie that stores login name. + unset($_COOKIE['tt_login']); + setcookie('tt_login', NULL, -1); + + $auth->doLogout(); + header('Location: login.php'); + exit(); + } + + header('Location: users.php'); + exit(); + + } else + $err->add($i18n->getKey('error.db')); + } else + $err->add($i18n->getKey('error.user_exists')); + } +} // isPost + +$rates = ttProjectHelper::getRates($user_id); +$smarty->assign('rates', $rates); + +$smarty->assign('auth_external', $auth->isPasswordExternal()); +$smarty->assign('forms', array($form->getName()=>$form->toArray())); +$smarty->assign('onload', 'onLoad="document.userForm.name.focus();handleClientControl();"'); +$smarty->assign('user_id', $user_id); +$smarty->assign('title', $i18n->getKey('title.edit_user')); +$smarty->assign('content_page_name', 'mobile/user_edit.tpl'); +$smarty->display('mobile/index.tpl'); diff --git a/mobile/users.php b/mobile/users.php new file mode 100644 index 00000000..71caa6ea --- /dev/null +++ b/mobile/users.php @@ -0,0 +1,51 @@ +true)); +if($user->canManageTeam()) { + $can_delete_manager = (1 == count($active_users)); + $inactive_users = ttTeamHelper::getInactiveUsers($user->team_id, true); +} + +$smarty->assign('active_users', $active_users); +$smarty->assign('inactive_users', $inactive_users); +$smarty->assign('can_delete_manager', $can_delete_manager); +$smarty->assign('title', $i18n->getKey('title.users')); +$smarty->assign('content_page_name', 'mobile/users.tpl'); +$smarty->display('mobile/index.tpl');