--- /dev/null
+{$forms.clientForm.open}
+<table cellspacing="4" cellpadding="7" border="0">
+ <tr>
+ <td>
+ <table cellspacing="1" cellpadding="2" border="0">
+ <tr>
+ <td align="right">{$i18n.label.client_name} (*):</td>
+ <td>{$forms.clientForm.name.control}</td>
+ </tr>
+ <tr>
+ <td align="right">{$i18n.label.client_address}:</td>
+ <td>{$forms.clientForm.address.control}</td>
+ </tr>
+ <tr>
+ <td align="right">{$i18n.label.tax}, %:</td>
+ <td>{$forms.clientForm.tax.control} (0{$user->decimal_mark}00)</td>
+ </tr>
+ <tr>
+ <td height="40"></td>
+ <td>{$i18n.label.required_fields}</td>
+ </tr>
+ <tr><td> </td></tr>
+{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ <tr>
+ <td align="right">{$i18n.label.projects}:</td>
+ <td>{$forms.clientForm.projects.control}</td>
+ </tr>
+ <tr><td> </td></tr>
+{/if}
+ <tr>
+ <td colspan="2" align="center" height="50">{$forms.clientForm.btn_submit.control}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+{$forms.clientForm.close}
\ No newline at end of file
--- /dev/null
+{$forms.clientDeleteForm.open}
+<table cellspacing="4" cellpadding="7" border="0">
+ <tr>
+ <td>
+ <table cellspacing="0" cellpadding="2" border="0">
+ <tr>
+ <td>{$i18n.form.client.client_to_delete}:</td>
+ <th>{$client_to_delete|escape:'html'}</th>
+ </tr>
+ <tr>
+ <td>{$i18n.form.client.client_entries}:</td>
+ <td>{$forms.clientDeleteForm.delete_client_entries.control}</td>
+ </tr>
+ <tr>
+ <td colspan="2" align="center"> </td>
+ </tr>
+ <tr>
+ <td align="right">{$forms.clientDeleteForm.btn_delete.control} </td>
+ <td align="left"> {$forms.clientDeleteForm.btn_cancel.control}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+{$forms.clientDeleteForm.close}
\ No newline at end of file
--- /dev/null
+{$forms.clientForm.open}
+<table cellspacing="4" cellpadding="7" border="0">
+ <tr>
+ <td>
+ <table cellspacing="1" cellpadding="2" border="0">
+ <tr>
+ <td align="right">{$i18n.label.client_name} (*):</td>
+ <td>{$forms.clientForm.name.control}</td>
+ </tr>
+ <tr>
+ <td align="right">{$i18n.label.client_address}:</td>
+ <td>{$forms.clientForm.address.control}</td>
+ </tr>
+ <tr>
+ <td align="right">{$i18n.label.tax}, %:</td>
+ <td>{$forms.clientForm.tax.control} (0{$user->decimal_mark}00)</td>
+ </tr>
+ <tr>
+ <td align = "right">{$i18n.label.status}:</td>
+ <td>{$forms.clientForm.status.control}</td>
+ </tr>
+ <tr>
+ <td height="40"></td>
+ <td>{$i18n.label.required_fields}</td>
+ </tr>
+ <tr><td> </td></tr>
+{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ <tr>
+ <td align="right">{$i18n.label.projects}:</td>
+ <td>{$forms.clientForm.projects.control}</td>
+ </tr>
+{/if}
+ <tr><td> </td></tr>
+ <tr>
+ <td colspan="2" align="center" height="50">{$forms.clientForm.btn_save.control} {$forms.clientForm.btn_copy.control}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+{$forms.clientForm.close}
\ No newline at end of file
--- /dev/null
+<script>
+ function chLocation(newLocation) { document.location = newLocation; }
+</script>
+
+<table class="mobile-table">
+ <tr>
+ <td valign="top">
+{if ($user->canManageTeam())}
+ <table class="mobile-table-details">
+ {if $inactive_clients}
+ <tr><td class="sectionHeaderNoBorder">{$i18n.form.clients.active_clients}</td></tr>
+ {/if}
+ <tr>
+ <td width="40%" class="tableHeader">{$i18n.label.person_name}</td>
+ <td width="40%" class="tableHeader">{$i18n.label.address}</td>
+ <td class="tableHeader">{$i18n.label.edit}</td>
+ </tr>
+ {foreach $active_clients as $client}
+ <tr valign="top" bgcolor="{cycle values="#f5f5f5,#dedee5"}">
+ <td>{$client.name|escape:'html'}</td>
+ <td>{$client.address|escape:'html'}</td>
+ <td><a href="client_edit.php?id={$client.id}">{$i18n.label.edit}</a></td>
+ </tr>
+ {/foreach}
+ </table>
+
+ <table width="100%">
+ <tr><td align="center"><br><form><input type="button" onclick="chLocation('client_add.php');" value="{$i18n.button.add_client}"></form></td></tr>
+ </table>
+
+ {if $inactive_clients}
+ <table cellspacing="1" cellpadding="3" border="0" width="100%">
+ <tr><td class="sectionHeaderNoBorder">{$i18n.form.clients.inactive_clients}</td></tr>
+ <tr>
+ <td width="40%" class="tableHeader">{$i18n.label.person_name}</td>
+ <td width="40%" class="tableHeader">{$i18n.label.address}</td>
+ <td class="tableHeader">{$i18n.label.edit}</td>
+ </tr>
+ {foreach $inactive_clients as $client}
+ <tr valign="top" bgcolor="{cycle values="#f5f5f5,#dedee5"}">
+ <td>{$client.name|escape:'html'}</td>
+ <td>{$client.address|escape:'html'}</td>
+ <td><a href="client_edit.php?id={$client.id}">{$i18n.label.edit}</a></td>
+ </tr>
+ {/foreach}
+ </table>
+
+ <table width="100%">
+ <tr><td align="center"><br><form><input type="button" onclick="chLocation('client_add.php');" value="{$i18n.button.add_client}"></form></td></tr>
+ </table>
+ {/if}
+{/if}
+ </td>
+ </tr>
+</table>
--- /dev/null
+{$forms.expenseItemForm.open}
+<table class="mobile-table">
+<tr>
+ <td>
+ <table border='0' cellpadding='3' cellspacing='1' width="100%">
+ <tr>
+{if $user->isPluginEnabled('cl')}
+ <td class="tableHeader" align="center">{$i18n.label.client}</td>
+{/if}
+
+{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ <td class="tableHeader" align="center">{$i18n.label.project}</td>
+{/if}
+ <td class="tableHeader" align="center">{$i18n.label.item}</td>
+ <td class="tableHeader" align="center">{$i18n.label.cost}</td>
+ </tr>
+ <tr bgcolor="{cycle values="#f5f5f5,#ccccce"}">
+{if $user->isPluginEnabled('cl')}
+ <td>{$expense_item.client_name|escape:'html'}</td>
+{/if}
+{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ <td>{$expense_item.project_name|escape:'html'}</td>
+{/if}
+ <td>{$expense_item.name|escape:'html'}</td>
+ <td align="right">{$expense_item.cost}</td>
+ </tr>
+ </table>
+ <table width="100%">
+ <tr>
+ <td align="center"> </td>
+ </tr>
+ <tr>
+ <td align="center">{$forms.expenseItemForm.delete_button.control} {$forms.expenseItemForm.cancel_button.control}</td>
+ </tr>
+ </table>
+ </td>
+</tr>
+</table>
+{$forms.expenseItemForm.close}
\ No newline at end of file
--- /dev/null
+<script>
+// We need a few arrays to populate project dropdown.
+// When client selection changes, the project dropdown must be re-populated with only relevant projects.
+// Format:
+// project_ids[143] = "325,370,390,400"; // Comma-separated list of project ids for client.
+// project_names[325] = "Time Tracker"; // Project name.
+
+// Prepare an array of project ids for clients.
+project_ids = new Array();
+{foreach $client_list as $client}
+ project_ids[{$client.id}] = "{$client.projects}";
+{/foreach}
+// Prepare an array of project names.
+project_names = new Array();
+{foreach $project_list as $project}
+ project_names[{$project.id}] = "{$project.name|escape:'javascript'}";
+{/foreach}
+// We'll use this array to populate project dropdown when client is not selected.
+var idx = 0;
+projects = new Array();
+{foreach $project_list as $project}
+ projects[idx] = new Array("{$project.id}", "{$project.name|escape:'javascript'}");
+ idx++;
+{/foreach}
+
+// Mandatory top option for project dropdown.
+empty_label_project = '{$i18n.dropdown.select|escape:'javascript'}';
+
+// The fillProjectDropdown function populates the project combo box with
+// projects associated with a selected client (client id is passed here as id).
+function fillProjectDropdown(id) {
+ var str_ids = project_ids[id];
+ var dropdown = document.getElementById("project");
+ // Determine previously selected item.
+ var selected_item = dropdown.options[dropdown.selectedIndex].value;
+
+ // Remove existing content.
+ dropdown.length = 0;
+ // Add mandatory top option.
+ dropdown.options[0] = new Option(empty_label_project, '', true);
+
+ // Populate project dropdown.
+ if (!id) {
+ // If we are here, client is not selected.
+ var len = projects.length;
+ for (var i = 0; i < len; i++) {
+ dropdown.options[i+1] = new Option(projects[i][1], projects[i][0]);
+ if (dropdown.options[i+1].value == selected_item)
+ dropdown.options[i+1].selected = true;
+ }
+ } else if (str_ids) {
+ var ids = new Array();
+ ids = str_ids.split(",");
+ var len = ids.length;
+
+ for (var i = 0; i < len; i++) {
+ var p_id = ids[i];
+ dropdown.options[i+1] = new Option(project_names[p_id], p_id);
+ if (dropdown.options[i+1].value == selected_item)
+ dropdown.options[i+1].selected = true;
+ }
+ }
+}
+
+function get_date() {
+ var date = new Date();
+ return date.strftime("%Y-%m-%d");
+}
+</script>
+
+{$forms.expenseItemForm.open}
+<table cellspacing="4" cellpadding="7" border="0">
+<tr>
+ <td>
+ <table width = "100%">
+ <tr>
+ <td valign="top">
+ <table border="0">
+{if $user->isPluginEnabled('cl')}
+ <tr>
+ <td align="right">{$i18n.label.client} {if $user->isPluginEnabled('cm')}(*){/if}:</td>
+ <td>{$forms.expenseItemForm.client.control}</td>
+ </tr>
+{/if}
+{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ <tr>
+ <td align="right">{$i18n.label.project} (*):</td>
+ <td>{$forms.expenseItemForm.project.control}</td>
+ </tr>
+{/if}
+ <tr>
+ <td align="right">{$i18n.label.item}:</td>
+ <td>{$forms.expenseItemForm.item_name.control}</td>
+ </tr>
+ <tr>
+ <td align="right">{$i18n.label.cost}:</td>
+ <td>{$forms.expenseItemForm.cost.control} {$user->currency|escape:'html'}</td>
+ </tr>
+ <tr>
+ <td align="right">{$i18n.label.date}:</td>
+ <td>{$forms.expenseItemForm.date.control}</td>
+ </tr>
+ <tr>
+ <td colspan="2"> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td align="left">{$forms.expenseItemForm.btn_save.control} {$forms.expenseItemForm.btn_copy.control} {$forms.expenseItemForm.btn_delete.control}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+{$forms.expenseItemForm.close}
\ No newline at end of file
--- /dev/null
+<script>
+// We need a few arrays to populate project dropdown.
+// When client selection changes, the project dropdown must be re-populated with only relevant projects.
+// Format:
+// project_ids[143] = "325,370,390,400"; // Comma-separated list of project ids for client.
+// project_names[325] = "Time Tracker"; // Project name.
+
+// Prepare an array of project ids for clients.
+project_ids = new Array();
+{foreach $client_list as $client}
+ project_ids[{$client.id}] = "{$client.projects}";
+{/foreach}
+// Prepare an array of project names.
+project_names = new Array();
+{foreach $project_list as $project}
+ project_names[{$project.id}] = "{$project.name|escape:'javascript'}";
+{/foreach}
+// We'll use this array to populate project dropdown when client is not selected.
+var idx = 0;
+projects = new Array();
+{foreach $project_list as $project}
+ projects[idx] = new Array("{$project.id}", "{$project.name|escape:'javascript'}");
+ idx++;
+{/foreach}
+
+// Mandatory top option for project dropdown.
+empty_label_project = '{$i18n.dropdown.select|escape:'javascript'}';
+
+// The fillProjectDropdown function populates the project combo box with
+// projects associated with a selected client (client id is passed here as id).
+function fillProjectDropdown(id) {
+ var str_ids = project_ids[id];
+ var dropdown = document.getElementById("project");
+ // Determine previously selected item.
+ var selected_item = dropdown.options[dropdown.selectedIndex].value;
+
+ // Remove existing content.
+ dropdown.length = 0;
+ // Add mandatory top option.
+ dropdown.options[0] = new Option(empty_label_project, '', true);
+
+ // Populate project dropdown.
+ if (!id) {
+ // If we are here, client is not selected.
+ var len = projects.length;
+ for (var i = 0; i < len; i++) {
+ dropdown.options[i+1] = new Option(projects[i][1], projects[i][0]);
+ if (dropdown.options[i+1].value == selected_item)
+ dropdown.options[i+1].selected = true;
+ }
+ } else if (str_ids) {
+ var ids = new Array();
+ ids = str_ids.split(",");
+ var len = ids.length;
+
+ for (var i = 0; i < len; i++) {
+ var p_id = ids[i];
+ dropdown.options[i+1] = new Option(project_names[p_id], p_id);
+ if (dropdown.options[i+1].value == selected_item)
+ dropdown.options[i+1].selected = true;
+ }
+ }
+}
+
+function get_date() {
+ var date = new Date();
+ return date.strftime("%Y-%m-%d");
+}
+</script>
+
+<!-- Inserted from time.tpl -->
+<table cellspacing="3" cellpadding="0" border="0" width="100%">
+ <tr>
+ <td class="sectionHeaderNoBorder" align="right"><a href="expenses.php?date={$prev_date}"><<</a></td>
+ <td class="sectionHeaderNoBorder" align="center">{$timestring}</td>
+ <td class="sectionHeaderNoBorder" align="left"><a href="expenses.php?date={$next_date}">>></a></td>
+ </tr>
+</table>
+
+{$forms.expensesForm.open}
+<table cellspacing="4" cellpadding="0" border="0">
+ <tr>
+ <td valign="top">
+ <table>
+{if $on_behalf_control}
+ <tr>
+ <td align="right">{$i18n.label.user}:</td>
+ <td>{$forms.expensesForm.onBehalfUser.control}</td>
+ </tr>
+{/if}
+{if $user->isPluginEnabled('cl')}
+ <tr>
+ <td align="right">{$i18n.label.client}{if $user->isPluginEnabled('cm')} (*){/if}:</td>
+ <td>{$forms.expensesForm.client.control}</td>
+ </tr>
+{/if}
+{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ <tr>
+ <td align="right">{$i18n.label.project} (*):</td>
+ <td>{$forms.expensesForm.project.control}</td>
+ </tr>
+{/if}
+ <tr>
+ <td align="right">{$i18n.label.item} (*):</td>
+ <td>{$forms.expensesForm.item_name.control}</td>
+ </tr>
+ <tr>
+ <td align="right">{$i18n.label.cost} (*):</td>
+ <td>{$forms.expensesForm.cost.control} {$user->currency|escape:'html'}</td>
+ </tr>
+ </table>
+ </td>
+ <!--
+ <td valign="top">
+ <table>
+ <tr><td>{$forms.expensesForm.date.control}</td></tr>
+ </table>
+ </td>
+ -->
+ </tr>
+</table>
+
+<table>
+ <tr>
+ <td align="center" colspan="2">{$forms.expensesForm.btn_submit.control}</td>
+ </tr>
+</table>
+
+<table class="mobile-table">
+<tr>
+ <td valign="top">
+{if $expense_items}
+ <table class="mobile-table-details">
+ <tr>
+ {if $user->isPluginEnabled('cl')}
+ <td width="20%" class="tableHeader">{$i18n.label.client}</td>
+ {/if}
+ {if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ <td class="tableHeader">{$i18n.label.project}</td>
+ {/if}
+ <td class="tableHeader">{$i18n.label.item}</td>
+ <td width="5%" class="tableHeaderCentered">{$i18n.label.cost}</td>
+ <td width="5%" class="tableHeader">{$i18n.label.edit}</td>
+ </tr>
+ {foreach $expense_items as $item}
+ <tr bgcolor="{cycle values="#f5f5f5,#ccccce"}">
+ {if $user->isPluginEnabled('cl')}
+ <td valign='top'>{$item.client|escape:'html'}</td>
+ {/if}
+ {if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ <td valign='top'>{$item.project|escape:'html'}</td>
+ {/if}
+ <td valign='top'>{$item.item|escape:'html'}</td>
+ <td valign='top' align='right'>{$item.cost}</td>
+ <td valign='top' align='center'>{if $item.invoice_id} {else}<a href='expense_edit.php?id={$item.id}'>{$i18n.label.edit}</a>{/if}</td>
+ </tr>
+ {/foreach}
+ </table>
+ <table border="0" cellpadding="3" cellspacing="1" width="100%">
+ <tr>
+ <td nowrap align="right">{$i18n.label.day_total}: {$user->currency|escape:'html'} {$day_total}</td>
+ </tr>
+ </table>
+{/if}
+ </td>
+</tr>
+</table>
+
+{$forms.expensesForm.close}
{assign var="tab_width" value="300"}
-<table height="100%" cellspacing="0" cellpadding="0" width="320" border="0">
+<table height="100%" cellspacing="0" cellpadding="0" border="0">
<tr>
<td valign="top" align="center"> <!-- This is to centrally align all our content. -->
</tr>
</table>
<!-- End of top image -->
+
+{if $authenticated}
+ {if $user->isAdmin()}
+
+ <!-- Sub menu for admin -->
+ <table cellspacing="0" cellpadding="3" width="100%" border="0">
+ <tr>
+ <td align="center" bgcolor="#d9d9d9" height="17" style="background-repeat: repeat-x;" background="../images/subm_bg.gif">
+ <a class="mainMenu" href="admin_teams.php">{$i18n.menu.teams}</a> ·
+ <a class="mainMenu" href="admin_options.php">{$i18n.menu.options}</a>
+ </td>
+ </tr>
+ </table>
+ <!-- End of sub menu for admin -->
+ {else}
+ <!-- Sub menu for authorized user -->
+ <table cellspacing="0" cellpadding="3" width="100%" border="0">
+ <tr>
+ <td align="center" bgcolor="#d9d9d9" height="17" style="background-repeat: repeat-x;" background="../images/subm_bg.gif">
+ {if !$user->isClient()}
+ <a class="mainMenu" href="time.php">{$i18n.menu.time}</a>
+ {/if}
+ {if $user->isPluginEnabled('ex') && !$user->isClient()}
+ · <a class="mainMenu" href="expenses.php">{$i18n.menu.expenses}</a>
+ {/if}
+ {if !$user->isClient() && ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ · <a class="mainMenu" href="projects.php">{$i18n.menu.projects}</a>
+ {/if}
+ {if $user->canManageTeam() && ($smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ · <a class="mainMenu" href="tasks.php">{$i18n.menu.tasks}</a>
+ {/if}
+ {if !$user->isClient()}
+ · <a class="mainMenu" href="users.php">{$i18n.menu.users}</a>
+ {/if}
+ {if $user->canManageTeam() && $user->isPluginEnabled('cl')}
+ · <a class="mainMenu" href="clients.php">{$i18n.menu.clients}</a>
+ {/if}
+ </td>
+ </tr>
+ </table>
+ <!-- End of sub menu for authorized user -->
+ {/if}
+{/if}
+
<!-- Output errors -->
{if $err->yes()}
<table cellspacing="4" cellpadding="7" width="{$tab_width}" border="0">
--- /dev/null
+{$forms.projectForm.open}
+<table cellspacing="4" cellpadding="7" border="0">
+ <tr>
+ <td>
+ <table cellspacing="1" cellpadding="2" border="0">
+ <tr>
+ <td align="right">{$i18n.label.thing_name} (*):</td>
+ <td>{$forms.projectForm.project_name.control}</td>
+ </tr>
+ <tr>
+ <td align = "right">{$i18n.label.description}:</td>
+ <td>{$forms.projectForm.description.control}</td>
+ </tr>
+ <tr><td> </td></tr>
+
+ <tr>
+ <td align="right">{$i18n.label.users}:</td>
+ <td>{$forms.projectForm.users.control}</td>
+ </tr>
+{if ($smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ <tr><td> </td></tr>
+ <tr>
+ <td align="right">{$i18n.label.tasks}:</td>
+ <td>{$forms.projectForm.tasks.control}</td>
+ </tr>
+{/if}
+ <tr>
+ <td></td>
+ <td>{$i18n.label.required_fields}</td>
+ </tr>
+ <tr>
+ <td></td>
+ <td> </td>
+ </tr>
+ <tr>
+ <td colspan="2" align="center" height="50">{$forms.projectForm.btn_add.control}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+{$forms.projectForm.close}
\ No newline at end of file
--- /dev/null
+{$forms.projectDeleteForm.open}
+<table cellspacing="4" cellpadding="7" border="0">
+ <tr>
+ <td>
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td colspan="2" align="center"><b>{$project_to_delete|escape:'html'}</b></td>
+ </tr>
+ <tr><td colspan="2" align="center"> </td></tr>
+ <tr>
+ <td align="right">{$forms.projectDeleteForm.btn_delete.control} </td>
+ <td align="left"> {$forms.projectDeleteForm.btn_cancel.control}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+{$forms.projectDeleteForm.close}
\ No newline at end of file
--- /dev/null
+{$forms.projectForm.open}
+<table cellspacing="4" cellpadding="7" border="0">
+ <tr>
+ <td>
+ <table cellspacing="1" cellpadding="2" border="0">
+ <tr>
+ <td align = "right">{$i18n.label.thing_name} (*):</td>
+ <td>{$forms.projectForm.project_name.control}</td>
+ </tr>
+ <tr>
+ <td align = "right">{$i18n.label.description}:</td>
+ <td>{$forms.projectForm.description.control}</td>
+ </tr>
+ <tr>
+ <td align="right">{$i18n.label.status}:</td>
+ <td>{$forms.projectForm.status.control}</td>
+ </tr>
+ <tr><td> </td></tr>
+ <tr>
+ <td align="right">{$i18n.label.users}:</td>
+ <td>{$forms.projectForm.users.control}</td>
+ </tr>
+{if ($smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ <tr><td> </td></tr>
+ <tr>
+ <td align="right">{$i18n.label.tasks}:</td>
+ <td>{$forms.projectForm.tasks.control}</td>
+ </tr>
+{/if}
+ <tr>
+ <td></td>
+ <td>{$i18n.label.required_fields}</td>
+ </tr>
+ <tr>
+ <td></td>
+ <td> </td>
+ </tr>
+ <tr>
+ <td colspan="2" align="center" height="50">{$forms.projectForm.btn_save.control} {$forms.projectForm.btn_copy.control}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+{$forms.projectForm.close}
\ No newline at end of file
--- /dev/null
+<script>
+ function chLocation(newLocation) { document.location = newLocation; }
+</script>
+
+<table class="mobile-table">
+ <tr>
+ <td valign="top">
+{if $user->canManageTeam()}
+ <table class="mobile-table-details">
+ {if $inactive_projects}
+ <tr><td class="sectionHeaderNoBorder">{$i18n.form.projects.active_projects}</td></tr>
+ {/if}
+ <tr>
+ <td width="35%" class="tableHeader">{$i18n.label.thing_name}</td>
+ <td width="35%" class="tableHeader">{$i18n.label.description}</td>
+ <td class="tableHeader">{$i18n.label.edit}</td>
+ </tr>
+ {if $active_projects}
+ {foreach $active_projects as $project}
+ <tr bgcolor="{cycle values="#f5f5f5,#dedee5"}">
+ <td>{$project.name|escape:'html'}</td>
+ <td>{$project.description|escape:'html'}</td>
+ <td><a href="project_edit.php?id={$project.id}">{$i18n.label.edit}</a></td>
+ </tr>
+ {/foreach}
+ {/if}
+ </table>
+
+ <table width="100%">
+ <tr>
+ <td align="center"><br>
+ <form><input type="button" onclick="chLocation('project_add.php');" value="{$i18n.button.add_project}"></form>
+ </td>
+ </tr>
+ </table>
+
+ {if $inactive_projects}
+ <table cellspacing="1" cellpadding="3" border="0" width="100%">
+ <tr><td class="sectionHeaderNoBorder">{$i18n.form.projects.inactive_projects}</td></tr>
+ <tr>
+ <td width="35%" class="tableHeader">{$i18n.label.thing_name}</td>
+ <td width="35%" class="tableHeader">{$i18n.label.description}</td>
+ <td class="tableHeader">{$i18n.label.edit}</td>
+ </tr>
+ {foreach $inactive_projects as $project}
+ <tr bgcolor="{cycle values="#f5f5f5,#dedee5"}">
+ <td>{$project.name|escape:'html'}</td>
+ <td>{$project.description|escape:'html'}</td>
+ <td><a href="project_edit.php?id={$project.id}">{$i18n.label.edit}</a></td>
+ </tr>
+ {/foreach}
+ </table>
+
+ <table width="100%">
+ <tr>
+ <td align="center"><br>
+ <form><input type="button" onclick="chLocation('project_add.php');" value="{$i18n.button.add_project}"></form>
+ </td>
+ </tr>
+ </table>
+ {/if}
+{else}
+ <table cellspacing="1" cellpadding="3" border="0" width="100%">
+ <tr>
+ <td class="tableHeader">{$i18n.label.thing_name}</td>
+ <td class="tableHeader">{$i18n.label.description}</td>
+ </tr>
+ {if $active_projects}
+ {foreach $active_projects as $project}
+ <tr bgcolor="{cycle values="#f5f5f5,#dedee5"}">
+ <td>{$project.name|escape:'html'}</td>
+ <td>{$project.description|escape:'html'}</td>
+ </tr>
+ {/foreach}
+ {/if}
+ </table>
+{/if}
+ </td>
+ </tr>
+</table>
--- /dev/null
+{$forms.taskForm.open}
+<table cellspacing="4" cellpadding="7" border="0">
+ <tr>
+ <td>
+ <table cellspacing="1" cellpadding="2" border="0">
+ <tr>
+ <td align="right">{$i18n.label.thing_name} (*):</td>
+ <td>{$forms.taskForm.name.control}</td>
+ </tr>
+ <tr>
+ <td align = "right">{$i18n.label.description}:</td>
+ <td>{$forms.taskForm.description.control}</td>
+ </tr>
+ <tr valign="top">
+ <td align="right">{$i18n.label.projects}:</td>
+ <td>{$forms.taskForm.projects.control}</td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>{$i18n.label.required_fields}</td>
+ </tr>
+ <tr>
+ <td></td>
+ <td> </td>
+ </tr>
+ <tr>
+ <td colspan="2" align="center" height="50">{$forms.taskForm.btn_submit.control}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+{$forms.taskForm.close}
--- /dev/null
+{$forms.taskDeleteForm.open}
+<table cellspacing="4" cellpadding="7" border="0">
+ <tr>
+ <td>
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td colspan="2" align="center"><b>{$task_to_delete|escape:'html'}</b></td>
+ </tr>
+ <tr>
+ <td colspan="2" align="center"> </td>
+ </tr>
+ <tr>
+ <td align="right">{$forms.taskDeleteForm.btn_delete.control} </td>
+ <td align="left"> {$forms.taskDeleteForm.btn_cancel.control}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+{$forms.taskDeleteForm.close}
--- /dev/null
+{$forms.taskForm.open}
+<table cellspacing="4" cellpadding="7" border="0">
+ <tr>
+ <td>
+ <table cellspacing="1" cellpadding="2" border="0">
+ <tr>
+ <td align="right">{$i18n.label.thing_name} (*):</td>
+ <td>{$forms.taskForm.name.control}</td>
+ </tr>
+ <tr>
+ <td align = "right">{$i18n.label.description}:</td>
+ <td>{$forms.taskForm.description.control}</td>
+ </tr>
+ <tr>
+ <td align = "right">{$i18n.label.status}:</td>
+ <td>{$forms.taskForm.status.control}</td>
+ </tr>
+ <tr valign="top">
+ <td align="right">{$i18n.label.projects}:</td>
+ <td>{$forms.taskForm.projects.control}</td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>{$i18n.label.required_fields}</td>
+ </tr>
+ <tr>
+ <td></td>
+ <td> </td>
+ </tr>
+ <tr>
+ <td colspan="2" align="center" height="50">{$forms.taskForm.btn_save.control} {$forms.taskForm.btn_copy.control}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+{$forms.taskForm.close}
--- /dev/null
+<script>
+ function chLocation(newLocation) { document.location = newLocation; }
+</script>
+
+<table class="mobile-table">
+ <tr>
+ <td valign="top">
+{if $user->canManageTeam()}
+ <table class="mobile-table-details">
+ {if $inactive_tasks}
+ <tr><td class="sectionHeaderNoBorder">{$i18n.form.tasks.active_tasks}</td></tr>
+ {/if}
+ <tr>
+ <td width="35%" class="tableHeader">{$i18n.label.thing_name}</td>
+ <td width="35%" class="tableHeader">{$i18n.label.description}</td>
+ <td class="tableHeader">{$i18n.label.edit}</td>
+ </tr>
+ {if $active_tasks}
+ {foreach $active_tasks as $task}
+ <tr bgcolor="{cycle values="#f5f5f5,#dedee5"}">
+ <td>{$task.name|escape:'html'}</td>
+ <td>{$task.description|escape:'html'}</td>
+ <td><a href="task_edit.php?id={$task.id}">{$i18n.label.edit}</a></td>
+ </tr>
+ {/foreach}
+ {/if}
+ </table>
+
+ <table width="100%">
+ <tr>
+ <td align="center"><br>
+ <form><input type="button" onclick="chLocation('task_add.php');" value="{$i18n.button.add_task}"></form>
+ </td>
+ </tr>
+ </table>
+
+ {if $inactive_tasks}
+ <table cellspacing="1" cellpadding="3" border="0" width="100%">
+ <tr><td class="sectionHeaderNoBorder">{$i18n.form.tasks.inactive_tasks}</td></tr>
+ <tr>
+ <td width="35%" class="tableHeader">{$i18n.label.thing_name}</td>
+ <td width="35%" class="tableHeader">{$i18n.label.description}</td>
+ <td class="tableHeader">{$i18n.label.edit}</td>
+ </tr>
+ {foreach $inactive_tasks as $task}
+ <tr bgcolor="{cycle values="#f5f5f5,#dedee5"}">
+ <td>{$task.name|escape:'html'}</td>
+ <td>{$task.description|escape:'html'}</td>
+ <td><a href="task_edit.php?id={$task.id}">{$i18n.label.edit}</a></td>
+ </tr>
+ {/foreach}
+ </table>
+
+ <table width="100%">
+ <tr>
+ <td align="center"><br>
+ <form><input type="button" onclick="chLocation('task_add.php');" value="{$i18n.button.add_task}"></form>
+ </td>
+ </tr>
+ </table>
+ {/if}
+{else}
+ <table cellspacing="1" cellpadding="3" border="0" width="100%">
+ <tr>
+ <td class="tableHeader">{$i18n.label.thing_name}</td>
+ <td class="tableHeader">{$i18n.label.description}</td>
+ </tr>
+ {if $active_tasks}
+ {foreach $active_tasks as $task}
+ <tr bgcolor="{cycle values="#f5f5f5,#dedee5"}">
+ <td>{$task.name|escape:'html'}</td>
+ <td>{$task.description|escape:'html'}</td>
+ </tr>
+ {/foreach}
+ {/if}
+ </table>
+ {/if}
+ </td>
+ </tr>
+</table>
<tr>
<td align="center">
{if $time_records}
- <table border='0' cellpadding='4' cellspacing='1' width="100%">
+ <table class="mobile-table-details">
{foreach $time_records as $record}
<tr bgcolor="{cycle values="#ccccce,#f5f5f5"}" {if !$record.billable} class="not_billable" {/if}>
{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
--- /dev/null
+<script>
+// The setDefaultRate function sets / unsets default rate for a project
+// when a corresponding checkbox is ticked.
+function setDefaultRate(element) {
+ var default_rate = document.userForm.rate.value;
+ if (default_rate == '') {
+ // No default rate, nothing to do!
+ return;
+ }
+ // Iterate through elements of the form to find and set the project rate.
+ for (var i = 0; i < userForm.elements.length; i++) {
+ if ((userForm.elements[i].type == 'text') && (userForm.elements[i].name == ('rate_'+element.value))) {
+ if (element.checked) {
+ userForm.elements[i].value = default_rate;
+ } else {
+ userForm.elements[i].value = '';
+ }
+ break; // Element is found and set, nothing more to do, break out of the loop.
+ }
+ }
+}
+
+// handleClientControl - controls visibility of the client dropdown depending on the selected user role.
+// We need to show it only when the "Client" user role is selected.
+function handleClientControl() {
+ var clientControl = document.getElementById("client");
+ if ("16" == document.getElementById("role").value)
+ clientControl.style.visibility = "visible";
+ else
+ clientControl.style.visibility = "hidden";
+}
+</script>
+
+{$forms.userForm.open}
+<table cellspacing="4" cellpadding="7" border="0">
+ <table cellspacing="1" cellpadding="2" border="0">
+ <tr>
+ <td align="right">{$i18n.label.person_name} (*):</td>
+ <td>{$forms.userForm.name.control}</td>
+ </tr>
+ <tr>
+ <td align="right">{$i18n.label.login} (*):</td>
+ <td>{$forms.userForm.login.control}</td>
+ </tr>
+{if !$auth_external}
+ <tr>
+ <td align="right">{$i18n.label.password} (*):</td>
+ <td>{$forms.userForm.pas1.control}</td>
+ </tr>
+ <tr>
+ <td align="right">{$i18n.label.confirm_password} (*):</td>
+ <td>{$forms.userForm.pas2.control}</td>
+ </tr>
+{/if}
+ <tr>
+ <td align="right" nowrap>{$i18n.label.email}:</td>
+ <td>{$forms.userForm.email.control}</td>
+ </tr>
+{if $user->isManager()}
+ <tr>
+ <td align="right">{$i18n.form.users.role}:</td>
+ <td>{$forms.userForm.role.control} {$forms.userForm.client.control}</td>
+ </tr>
+{/if}
+ <tr>
+ <td align="right">{$i18n.form.users.default_rate} (0{$user->decimal_mark}00):</td>
+ <td>{$forms.userForm.rate.control}</td>
+ </tr>
+{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ <tr valign="top">
+ <td align="right">{$i18n.label.projects}:</td>
+ <td>{$forms.userForm.projects.control}</td>
+ </tr>
+ <tr>
+ <td colspan="2" align="center">{$i18n.label.required_fields}</td>
+ </tr>
+{/if}
+ <tr>
+ <td colspan="2" align="center" height="50">{$forms.userForm.btn_submit.control}</td>
+ </tr>
+ </table>
+</table>
+{$forms.userForm.close}
--- /dev/null
+{$forms.userDeleteForm.open}
+<table cellspacing="4" cellpadding="7" border="0">
+ <tr>
+ <td>
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td colspan="2" align="center"><b>{$user_to_delete|escape:'html'}</b></td>
+ </tr>
+ <tr>
+ <td colspan="2" align="center"> </td>
+ </tr>
+ <tr>
+ <td align="right">{$forms.userDeleteForm.btn_delete.control} </td>
+ <td align="left"> {$forms.userDeleteForm.btn_cancel.control}</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+{$forms.userDeleteForm.close}
--- /dev/null
+<script>
+// Prepare an array of rates.
+// Format: project_rates[0] = Array(100, '25.00'), project_rates[1] = Array(120, '30.00'), etc...
+// First element = project_id, second element = rate for project. Quotes needed for string representation of rates.
+project_rates = new Array();
+var idx = 0;
+{foreach $rates as $rate}
+project_rates[idx] = new Array({$rate.id}, '{$rate.rate}');
+idx++;
+{/foreach}
+
+// getRate - returns a rate for the project. If rate was set for user previously we'll get this old rate
+// if project time entries for user exists. Otherwise return user default rate.
+function getRate(project_id) {
+ var length = project_rates.length;
+ for(var i = 0; i < length; i++) {
+ if(project_rates[i][0] == project_id) {
+ return project_rates[i][1];
+ }
+ }
+ var default_rate = document.userForm.rate.value;
+ return default_rate;
+}
+
+// The setRate function sets / unsets user rate for a project when a corresponding checkbox is ticked.
+function setRate(element) {
+ var default_rate = document.userForm.rate.value;
+ if (default_rate == '') {
+ // No default rate, nothing to do!
+ return;
+ }
+ // Iterate through elements of the form to find and set the project rate.
+ for (var i = 0; i < userForm.elements.length; i++) {
+ if ((userForm.elements[i].type == 'text') && (userForm.elements[i].name == ('rate_'+element.value))) {
+ if (element.checked) {
+ userForm.elements[i].value = getRate(element.value);
+ } else {
+ userForm.elements[i].value = '';
+ }
+ break; // Element is found and set, nothing more to do, break out of the loop.
+ }
+ }
+}
+
+// handleClientControl - controls visibility of the client dropdown depending on the selected user role.
+// We need to show it only when the "Client" user role is selected.
+function handleClientControl() {
+ var clientControl = document.getElementById("client");
+ if ("16" == document.getElementById("role").value)
+ clientControl.style.visibility = "visible";
+ else
+ clientControl.style.visibility = "hidden";
+}
+</script>
+
+{$forms.userForm.open}
+<table cellspacing="4" cellpadding="7" border="0">
+ <table cellspacing="1" cellpadding="2" border="0">
+ <tr>
+ <td align="right">{$i18n.label.person_name} (*):</td>
+ <td>{$forms.userForm.name.control}</td>
+ </tr>
+ <tr>
+ <td align="right">{$i18n.label.login} (*):</td>
+ <td>{$forms.userForm.login.control}</td>
+ </tr>
+{if !$auth_external}
+ <tr>
+ <td align="right">{$i18n.label.password} (*):</td>
+ <td>{$forms.userForm.pas1.control}</td>
+ </tr>
+ <tr>
+ <td align="right">{$i18n.label.confirm_password} (*):</td>
+ <td>{$forms.userForm.pas2.control}</td>
+ </tr>
+{/if}
+ <tr>
+ <td align="right" nowrap>{$i18n.label.email}:</td>
+ <td>{$forms.userForm.email.control}</td>
+ </tr>
+{if $user->isManager() && ($user->id != $user_id)}
+ <tr>
+ <td align="right">{$i18n.form.users.role}:</td>
+ <td>{$forms.userForm.role.control} {$forms.userForm.client.control}</td>
+ </tr>
+{/if}
+{* Prohibit deactivating team manager. Deactivating others is ok. *}
+{if $user->canManageTeam() && !($user->isManager() && $user->id == $user_id)}
+ <tr>
+ <td align="right">{$i18n.label.status}:</td>
+ <td>{$forms.userForm.status.control}</td>
+ </tr>
+{/if}
+ <tr>
+ <td align="right">{$i18n.form.users.default_rate} (0{$user->decimal_mark}00):</td>
+ <td>{$forms.userForm.rate.control}</td>
+ </tr>
+{if ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)}
+ <tr valign="top">
+ <td align="right">{$i18n.label.projects}:</td>
+ <td>{$forms.userForm.projects.control}</td>
+ </tr>
+{/if}
+ <tr>
+ <td colspan="2" align="center">{$i18n.label.required_fields}</td>
+ </tr>
+ <tr>
+ <td colspan="2" align="center" height="50">{$forms.userForm.btn_submit.control}</td>
+ </tr>
+ </table>
+</table>
+{$forms.userForm.close}
--- /dev/null
+<script>
+ function chLocation(newLocation) { document.location = newLocation; }
+</script>
+
+<table class="mobile-table">
+ <tr>
+ <td valign="top">
+{if $user->canManageTeam()}
+ <table class="mobile-table-details">
+ {if $inactive_users}
+ <tr><td class="sectionHeaderNoBorder">{$i18n.form.users.active_users}</td></tr>
+ {/if}
+ <tr>
+ <td width="35%" class="tableHeader">{$i18n.label.person_name}</td>
+ <td width="35%" class="tableHeader">{$i18n.label.login}</td>
+ <td width="10%" class="tableHeader">{$i18n.form.users.role}</td>
+ <td width="10%" class="tableHeader">{$i18n.label.edit}</td>
+ </tr>
+ {if $active_users}
+ {foreach $active_users as $u}
+ <tr bgcolor="{cycle values="#f5f5f5,#dedee5"}">
+ <td>{$u.name|escape:'html'}</td>
+ <td>{$u.login|escape:'html'}</td>
+ {if $smarty.const.ROLE_MANAGER == $u.role}
+ <td>{$i18n.form.users.manager}</td>
+ {elseif $smarty.const.ROLE_COMANAGER == $u.role}
+ <td>{$i18n.form.users.comanager}</td>
+ {elseif $smarty.const.ROLE_CLIENT == $u.role}
+ <td>{$i18n.label.client}</td>
+ {elseif $smarty.const.ROLE_USER == $u.role}
+ <td>{$i18n.label.user}</td>
+ {/if}
+ {if $user->isManager()}
+ <!-- Manager can edit everybody. -->
+ <td><a href="user_edit.php?id={$u.id}">{$i18n.label.edit}</a></td>
+ {else}
+ <!-- Comanager can edit self and clients or users but not manager and other comanagers. -->
+ <td>{if ($user->id == $u.id) || ($smarty.const.ROLE_CLIENT == $u.role) || ($smarty.const.ROLE_USER == $u.role)}<a href="user_edit.php?id={$u.id}">{$i18n.label.edit}</a>{/if}</td>
+ {/if}
+ </tr>
+ {/foreach}
+ {/if}
+ </table>
+
+ <table width="100%">
+ <tr>
+ <td align="center"><br>
+ <form><input type="button" onclick="chLocation('user_add.php');" value="{$i18n.button.add_user}"></form>
+ </td>
+ </tr>
+ </table>
+
+ {if $inactive_users}
+ <table cellspacing="1" cellpadding="3" border="0" width="100%">
+ <tr><td class="sectionHeaderNoBorder">{$i18n.form.users.inactive_users}</td></tr>
+ <tr>
+ <td width="35%" class="tableHeader">{$i18n.label.person_name}</td>
+ <td width="35%" class="tableHeader">{$i18n.label.login}</td>
+ <td width="10%" class="tableHeader">{$i18n.form.users.role}</td>
+ <td width="10%" class="tableHeader">{$i18n.label.edit}</td>
+ <td width="10%" class="tableHeader">{$i18n.label.delete}</td>
+ </tr>
+ {foreach $inactive_users as $u}
+ <tr bgcolor="{cycle values="#f5f5f5,#dedee5"}">
+ <td>{$u.name|escape:'html'}</td>
+ <td>{$u.login|escape:'html'}</td>
+ {if $smarty.const.ROLE_MANAGER == $u.role}
+ <td>{$i18n.form.users.manager}</td>
+ {elseif $smarty.const.ROLE_COMANAGER == $u.role}
+ <td>{$i18n.form.users.comanager}</td>
+ {elseif $smarty.const.ROLE_CLIENT == $u.role}
+ <td>{$i18n.label.client}</td>
+ {elseif $smarty.const.ROLE_USER == $u.role}
+ <td>{$i18n.label.user}</td>
+ {/if}
+ {if $user->isManager()}
+ <!-- Manager can edit everybody. -->
+ <td><a href="user_edit.php?id={$u.id}">{$i18n.label.edit}</a></td>
+ <td>{if $smarty.const.ROLE_MANAGER != $u.role || $can_delete_manager}<a href="user_delete.php?id={$u.id}">{$i18n.label.delete}</a>{/if}</td>
+ {else}
+ <!-- Comanager can edit self and clients or users but not manager and other comanagers. -->
+ <td>{if ($user->id == $u.id) || ($smarty.const.ROLE_CLIENT == $u.role) || ($smarty.const.ROLE_USER == $u.role)}<a href="user_edit.php?id={$u.id}">{$i18n.label.edit}</a>{/if}</td>
+ <td>{if ($user->id == $u.id) || ($smarty.const.ROLE_CLIENT == $u.role) || ($smarty.const.ROLE_USER == $u.role)}<a href="user_delete.php?id={$u.id}">{$i18n.label.delete}</a>{/if}</td>
+ {/if}
+ </tr>
+ {/foreach}
+
+ </table>
+
+ <table width="100%">
+ <tr>
+ <td align="center" height="50">
+ <form><input type="button" onclick="chLocation('user_add.php');" value="{$i18n.button.add_user}"></form>
+ </td>
+ </tr>
+ </table>
+ {/if}
+{else}
+ <table cellspacing="1" cellpadding="3" border="0" width="100%">
+ <tr>
+ <td width="35%" class="tableHeader">{$i18n.label.person_name}</td>
+ <td width="35%" class="tableHeader">{$i18n.label.login}</td>
+ <td class="tableHeader">{$i18n.form.users.role}</td>
+ </tr>
+ {foreach $active_users as $u}
+ <tr bgcolor="{cycle values="#f5f5f5,#dedee5"}">
+ <td>{$u.name|escape:'html'}</td>
+ <td>{$u.login|escape:'html'}</td>
+ {if $smarty.const.ROLE_MANAGER == $u.role}
+ <td>{$i18n.form.users.manager}</td>
+ {elseif $smarty.const.ROLE_COMANAGER == $u.role}
+ <td>{$i18n.form.users.comanager}</td>
+ {elseif $smarty.const.ROLE_CLIENT == $u.role}
+ <td>{$i18n.label.client}</td>
+ {elseif $smarty.const.ROLE_USER == $u.role}
+ <td>{$i18n.label.user}</td>
+ {/if}
+ </tr>
+ {/foreach}
+ </table>
+{/if}
+ </td>
+ </tr>
+</table>
}
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;
+}
+
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttClientHelper');
+import('ttTeamHelper');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+$projects = ttTeamHelper::getActiveProjects($user->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttClientHelper');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+$id = (int)$request->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttClientHelper');
+import('ttTeamHelper');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+$cl_id = (int) $request->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttTeamHelper');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+$smarty->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('initialize.php');
+import('form.Form');
+import('DateAndTime');
+import('ttExpenseHelper');
+
+// Access check.
+if (!ttAccessCheck(right_data_entry)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+$cl_id = $request->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttTeamHelper');
+import('DateAndTime');
+import('ttExpenseHelper');
+
+// Access check.
+if (!ttAccessCheck(right_data_entry)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+$cl_id = $request->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttUserHelper');
+import('ttTeamHelper');
+import('DateAndTime');
+import('ttExpenseHelper');
+
+// Access check.
+if (!ttAccessCheck(right_data_entry)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+// Initialize and store date in session.
+$cl_date = $request->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttProjectHelper');
+import('ttTeamHelper');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+$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_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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttProjectHelper');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+$cl_project_id = (int)$request->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttProjectHelper');
+import('ttTeamHelper');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+$cl_project_id = (int)$request->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttTeamHelper');
+
+// Access check.
+if (!ttAccessCheck(right_data_entry)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+if($user->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('form.ActionForm');
+import('ttTeamHelper');
+import('ttTaskHelper');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+$projects = ttTeamHelper::getActiveProjects($user->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('ttTaskHelper');
+import('form.Form');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+$cl_task_id = (int)$request->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttTeamHelper');
+import('ttTaskHelper');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+$cl_task_id = (int)$request->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttTeamHelper');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+$smarty->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');
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.
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttTeamHelper');
+import('ttUserHelper');
+import('form.Table');
+import('form.TableColumn');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+// Use the "limit" plugin if we have one. Ignore include errors.
+// The "limit" plugin is not required for normal operation of the Time Tracker.
+@include('plugins/limit/user_add.php');
+
+if ($user->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('<label for = "'.$table->getName().'_'.$row.'">'.htmlspecialchars($value).'</label>');
+ 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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttUserHelper');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+// Get user id we are deleting from the request.
+// A cast to int is for safety against manipulation of request parameter (sql injection).
+$user_id = (int) $request->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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttProjectHelper');
+import('ttTeamHelper');
+import('ttUserHelper');
+import('form.Table');
+import('form.TableColumn');
+
+// Access check.
+if (!ttAccessCheck(right_manage_team)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+// Get user id we are editing from the request.
+$user_id = (int) $request->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('<label for = "'.$table->getName().'_'.$row.'">'.htmlspecialchars($value).'</label>');
+ 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');
--- /dev/null
+<?php
+// +----------------------------------------------------------------------+
+// | Anuko Time Tracker
+// +----------------------------------------------------------------------+
+// | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
+// +----------------------------------------------------------------------+
+// | LIBERAL FREEWARE LICENSE: This source code document may be used
+// | by anyone for any purpose, and freely redistributed alone or in
+// | combination with other software, provided that the license is obeyed.
+// |
+// | There are only two ways to violate the license:
+// |
+// | 1. To redistribute this code in source form, with the copyright
+// | notice or license removed or altered. (Distributing in compiled
+// | forms without embedded copyright notices is permitted).
+// |
+// | 2. To redistribute modified versions of this code in *any* form
+// | that bears insufficient indications that the modifications are
+// | not the work of the original author(s).
+// |
+// | This license applies to this document only, not any other software
+// | that it may be combined with.
+// |
+// +----------------------------------------------------------------------+
+// | Contributors:
+// | https://www.anuko.com/time_tracker/credits.htm
+// +----------------------------------------------------------------------+
+
+require_once('../initialize.php');
+import('form.Form');
+import('ttTeamHelper');
+
+// Access check.
+if (!ttAccessCheck(right_data_entry)) {
+ header('Location: access_denied.php');
+ exit();
+}
+
+// Get users.
+$active_users = ttTeamHelper::getActiveUsers(array('getAllFields'=>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');