diff --git a/README.md b/README.md
index 7f37ce4..b6babc2 100755
--- a/README.md
+++ b/README.md
@@ -1,5 +1,203 @@
[](https://github.com/Adacis/whereami/actions/workflows/release.yml)
-# whereami
-Is a simple application to locate everybody in your company
+# Where Am I ?
+
+
+
+
+
+
+
Where Am I ? (WAI)
+
+
+ Where am I is a management application allowing users to record and see who and what everyone is working on in a calendar and table. The application is hosted on Nextcloud.
+
+
+
+
+
+
+## Tech Stack
+
+**Client:** JS
+
+**Server:** PHP Symphony
+
+**Database:** MySQL
+
+## Installation
+
+### Prerequisites
+
+The Git command must be installed on the machine, or if you're on Windows or MacOS, you can use the Github Desktop application: https://desktop.github.com/
+
+Once git is installed on your machine, clone the WhereAmI project (https://github.com/Adacis/whereami.git) to a directory on your machine.
+
+Install Docker (Windows, MacOS or Linux): https://www.docker.com/products/docker-desktop
+or from the command line (e.g. Ubuntu):
+https://docs.docker.com/engine/install/ubuntu/
+
+After installing Docker, create the “nextcloud” network:
+```sh
+ docker network create nextcloud
+ ```
+
+
+
+### Installing the Nextcloud database
+
+This part of the installation will enable us to install a MySQL 8.3.0 database, which will be the database for our Nextcloud application.
+
+**Step 1**:
+Go to the directory where the project was cloned in the prerequisites and then to the “whereami-docker” folder:
+
+```sh
+cd whereami-docker/
+```
+
+**Step 2**:
+Run the docker command to create the MySQL database container:
+```sh
+docker compose up -d
+```
+
+**Step 3**:
+Check that the container has launched with the command :
+```sh
+docker ps
+```
+
+A container named “nextcloud-db” should be present in the list. If not, you can retrieve the container's logs with the command
+docker logs nextcloud-db
+
+**Step 4**:
+Check that the database is accessible with a tool like Dbeaver (https://dbeaver.io/).
+The identifiers are as follows:
+```
+Server Host: localhost
+Port: 3306
+Username: nextcloud
+Password: nextcloud
+```
+
+### Installing the Nextcloud application
+
+This part requires you to have installed the database beforehand.
+
+**Step 1**:
+Create a “nextcloud_data” folder on your machine:
+```sh
+mkdir /Users/miage/Documents/nextcloud_data
+```
+
+**Step 2**:
+Launch the nextcloud container, which loads the nextcloud:28.0.5 image.
+(Remember to change the data folder /Users/miage/Documents/nextcloud_data to the one you created in step 1)
+```sh
+docker run -d --name nextcloud -p 8001:80 -v /Users/miage/Documents/nextcloud_data:/var/www/html --network nextcloud nextcloud:28.0.5
+```
+
+Note that the container will be on port 8001 of the machine, so it's possible to modify it if necessary.
+
+**Step 3**:
+Check that the container has launched with the command :
+```sh
+docker ps
+```
+
+A container named “nextcloud” should be present in the list. If not, you can retrieve the container's logs with the command :
+```sh
+docker logs nextcloud
+```
+
+
+**Step 4**:
+Go to Nextcloud at http://localhost:8001
+Enter the credentials for the Nextcloud administrator account you want, e.g. :
+```
+Username : adacis
+Password : admin
+```
+
+In the “Configure database” section, select the “MySQL/MariaDB” box and enter the following information:
+```
+Database user: nextcloud
+Database password: nextcloud
+Database name: nextcloud-db
+Database host: nextcloud-db:3306
+```
+
+
+
+Then click on Install.
+You now have an instance of Nextcloud 28.0.5.
+
+### Installing the WhereAmI application
+
+**Step 1** :
+Take the project already cloned in a directory, and copy it into the nextcloud_data/apps/ folder created during Nextcloud installation with the command
+```sh
+cp -r [Directory containing cloned project]/whereami [Path to nextcloud_data folder]/nextcloud_data/apps/
+```
+
+**Step 2** :
+- Log on to Nextcloud with an administrator account
+- Click on your profile photo in the top right-hand corner of the screen
+- Select “Applications
+- Search for the “Where am I?” application and click on Activate
+
+
+You now have the Where Am I application on Nextcloud. To use this application, please refer to the user manual.
+
+
+## Deployment (only in dev mode)
+
+### First time deployment
+To deploy this project run the following commands in the order :
+- Start the Docker Bash Terminal for Nextcloud
+```bash
+docker exec -it nextcloud bash
+```
+- Navigate to the correct folder
+```bash
+cd apps/whereami/
+```
+- Install and update NPM if you don't have it
+```bash
+apt-get update && apt-get install -y npm
+```
+- Initialise the project
+```bash
+make npm-init
+```
+- Build the project
+```bash
+make build-js
+```
+
+### Build
+- Start the Docker Bash Terminal
+```bash
+docker exec -it nextcloud bash
+```
+- Place yourself in the correct folder
+```bash
+cd apps/whereami/
+```
+- Build the project
+```bash
+make build-js
+```
+## Features
+- **Event Assignment using the Calendar feature**: Users can register themselves, record their whereabouts and time slots for specific contracts as well as their work status, which are then converted into full or half work days.
+- **Employees Tab**: A summary table of all users, their locations, and their status for the selected date range.
+- **Location Tab**: A summary table showing the number of users by location for the selected date range.
+- **Last Seen Tab**: A summary table of all the instances where users have crossed paths with other users within the selected date range.
+- **Contract Tab**: A summary table listing all users with their contracts and the total hours worked, rounded to the nearest half day, for the selected date range.
+- **Summary Tab**: A summary table showing the locations and the time spent at each location by the user for the selected month.
+- **Hover Summary Functionality**: Provide brief information when hovering over specific dates.
+
+## Authors
+- [@Benjamin Aimard](https://github.com/baimard)
diff --git a/appinfo/info.xml b/appinfo/info.xml
index 414fc8e..88a3165 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -4,7 +4,7 @@
Where am I ?
- 0.0.36
+ 0.0.37
agpl
ADACIS
Whereami
@@ -12,7 +12,7 @@
organization
https://github.com/Adacis/whereami/issues
-
+
OCA\Whereami\Settings\WhereamiAdmin
diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php
index ceb873d..dc8b0ed 100644
--- a/lib/Controller/PageController.php
+++ b/lib/Controller/PageController.php
@@ -86,7 +86,7 @@ public function getNavigationLink()
public function getContracts(String $dtStart, String $dtEnd)
{
// Fetch data from the database
- $result = $this->myDb->getContracts($dtStart, $dtEnd);
+ $result = $this->myDb->getContracts($dtStart, $dtEnd, $this->userId);
// Initialize arrays to store contracts and users per contract
$contracts = [];
diff --git a/lib/Db/Bdd.php b/lib/Db/Bdd.php
index 2c5f87e..c5a2811 100755
--- a/lib/Db/Bdd.php
+++ b/lib/Db/Bdd.php
@@ -12,11 +12,29 @@ class Bdd
private String $tableprefix;
private $logger;
+ private $grpWhereAmI = 'whereami_global';
+
+
public function __construct(IDbConnection $db, LoggerInterface $log)
{
$this->pdo = $db;
$this->tableprefix = '*PREFIX*' . "whereami_";
$this->logger = $log;
+ $this->initializeGroupWhereAmI();
+ }
+
+ /**
+ * Insert the group whereami_global in the database if it does not exist
+ */
+ public function initializeGroupWhereAmI()
+ {
+
+ $sql = "SELECT * FROM `*PREFIX*groups` WHERE `gId` = ?";
+
+ if (empty($this->execSQLNoJsonReturn($sql, array($this->grpWhereAmI)))) {
+ $sql = "INSERT INTO `*PREFIX*groups` (`gId`,`displayname`) VALUES (?,?)";
+ $this->execSQLNoData($sql, array($this->grpWhereAmI, $this->grpWhereAmI));
+ }
}
public function listUID()
@@ -180,14 +198,21 @@ public function getIconsInPrefixList($person, $label)
* Retrieve activity reports for contracts within a specified date range.
*
* This method fetches activity reports including the number of contracts (`nb_contract`),
- * associated usernames (`username`), aggregated activity values (`activity_report_value`),
+ * associated usernames (`uid`), aggregated activity values (`activity_report_value`),
* and corresponding dates (`activity_report_date`) for a given time period.
+ * The results are filtered based on the user's group membership. If the user belongs
+ * to the "WhereAmI" global group, they will see all available data for every user. Otherwise,
+ * they will only see their own data.
*
+ * @see Bdd::$grpWhereAmI Constant that holds the value for the "WhereAmI" global group.
* @param string $dtStart The start date of the date range (YYYY-MM-DD format).
* @param string $dtEnd The end date of the date range (YYYY-MM-DD format).
+ * @param string $uid The ID of the current user.
* @return array Fetched database results containing activity report details.
*/
- public function getContracts($dtStart, $dtEnd){
+ public function getContracts($dtStart, $dtEnd, $uid){
+ $isGlobalUser = $this->isGrpWanted($uid, $this->grpWhereAmI);
+
$sql = "SELECT
REGEXP_SUBSTR(value, '\\\\b[Dd]\\\\d{5}\\\\b') as nb_contract,
uid,
@@ -221,9 +246,11 @@ public function getContracts($dtStart, $dtEnd){
AND FROM_UNIXTIME(oc.firstoccurence) BETWEEN ? AND ?
AND oc.deleted_at IS NULL
) sr
+ WHERE
+ (? = TRUE) OR ? = uid
GROUP BY
value, uid, first_occurence, last_occurence";
- return $this->execSQLNoJsonReturn($sql, array($dtStart, $dtEnd));
+ return $this->execSQLNoJsonReturn($sql, array($dtStart, $dtEnd, $isGlobalUser, $uid));
}
@@ -249,6 +276,16 @@ private function getQuadri($name)
return strtoupper($quadri);
}
+ /**
+ * Return true if the user is in the group wanted
+ * @userId : current user id
+ */
+ public function isGrpWanted($userId,$grp)
+ {
+ $sql = "SELECT * FROM `*PREFIX*group_user` WHERE `gid` = ? AND `uid` = ?";
+ return !empty($this->execSQLNoJsonReturn($sql, array($grp, $userId)));
+ }
+
/**
* @sql
* @array() //prepare statement
diff --git a/src/js/class/ListEvents.js b/src/js/class/ListEvents.js
index 5afb664..24be636 100644
--- a/src/js/class/ListEvents.js
+++ b/src/js/class/ListEvents.js
@@ -102,6 +102,7 @@ export class ListEvents {
e.place2 = e.place
}
+
if (placeIsExcluded) {
isSomeoneThere = true
isSomeoneThere2 = true
@@ -118,16 +119,10 @@ export class ListEvents {
}
}
- if (e.place === this.id) {
title += e.nextcloud_users + '\n'
res += 1
found = true
- }
- if (e.place2 === this.id) {
- title2 += e.nextcloud_users + '\n'
- res2 += 1
- found2 = true
- }
+
}
})
@@ -140,10 +135,8 @@ export class ListEvents {
let div1 = this.createDiv(res, title, found, isSomeoneThere, from)
div.appendChild(div1)
if (title !== title2) {
- let div2 = this.createDiv(res2, title2, found2, isSomeoneThere2, from)
div1.classList.add('cell-base-two')
- div2.classList.add('cell-base-two')
- div.appendChild(div2)
+ div.appendChild(div1)
}
else
div1.classList.add('cell-base-alone')
diff --git a/src/js/module/datatables.js b/src/js/module/datatables.js
index 3fa1aad..688447f 100644
--- a/src/js/module/datatables.js
+++ b/src/js/module/datatables.js
@@ -98,84 +98,71 @@ function getAllDatesFromContract (informations) {
* }
*/
export function newTableContracts(response) {
- const res = JSON.parse(response);
-
- const table = document.createElement('table');
- table.setAttribute('id', 'contracts');
- table.setAttribute('class', 'table table-striped');
-
- let thead = document.createElement('thead');
- let tbody = document.createElement('tbody');
-
- // Create a headline with contracts names
- const headLine = document.createElement('tr');
- headLine.appendChild(newCell('th', 'Contracts')); // Header for contracts column
-
-
- // Add each contracts name to the headline
- Object.keys(res.userByContract).forEach(contractKey => {
- headLine.appendChild(newCell('th', contractKey));
- });
-
- thead.appendChild(headLine);
-
- // Create an array to hold all contracts for each employee
- const employeeContracts = {};
-
- // Iterate over each contract
- Object.keys(res.contracts).forEach(contractKey => {
- const contractData = res.contracts[contractKey];
- const userName = Object.keys(contractData)[0];
- // Iterate over each employee
- Object.keys(res.userByContract).forEach(contractKey => {
- const contractCount = res.userByContract[contractKey][userName] || 0;
- // Add contract value to employee's contract array
- if (!employeeContracts[contractKey]) {
- employeeContracts[contractKey] = [];
- }
- //If employeeContractcs[contractKey] not contains userName, add it
- if (!employeeContracts[contractKey].some(e => e.user === userName)){
- employeeContracts[contractKey].push({
- user: userName,
- count: contractCount
- });
- }
- });
- });
-
- let userPresent = []
-
- // Create rows for each contract
- Object.keys(employeeContracts).forEach(contractKey => {
- const contractData = res.contracts[contractKey];
- const userName = Object.keys(contractData)[0];
- const contractRow = document.createElement('tr');
-
- if (!userPresent.includes(userName)) {
- contractRow.appendChild(newCell('td', userName)); // Add employee in the first row
- // Add each time the employee was present for each contract
- for (const key in employeeContracts) {
- for(let i = 0; i < employeeContracts[key].length; i++){
- if(employeeContracts[key][i].user === userName) {
- contractRow.appendChild(newCell('td', employeeContracts[key][i].count, '', getAllDatesFromContract(res.contracts[key][userName])));
- }
- }
-
- }
-
- tbody.appendChild(contractRow);
- userPresent.push(userName);
- }
+ const res = JSON.parse(response);
+
+ const table = document.createElement('table');
+ table.setAttribute('id', 'contracts');
+ table.setAttribute('class', 'table table-striped');
+
+ let thead = document.createElement('thead');
+ let tbody = document.createElement('tbody');
+
+ // Create a headline with contracts names
+ const headLine = document.createElement('tr');
+ headLine.appendChild(newCell('th', 'Contracts')); // Header for contracts column
+
+ // Add each contracts name to the headline
+ Object.keys(res.userByContract).forEach(contractKey => {
+ headLine.appendChild(newCell('th', contractKey));
+ });
+
+ thead.appendChild(headLine);
+
+ // Create an array to hold all contracts for each employee
+ const employeeContracts = {};
+
+ // Iterate over each contract
+ Object.keys(res.contracts).forEach(contractKey => {
+ const contractData = res.contracts[contractKey];
+ // Iterate over each user within the contract
+ Object.keys(contractData).forEach(userName => {
+ const contractCount = res.userByContract[contractKey][userName] || 0;
+ // Initialize user in employeeContracts if not already present
+ if (!employeeContracts[userName]) {
+ employeeContracts[userName] = {};
+ }
+ // Add contract value to employee's contract array
+ employeeContracts[userName][contractKey] = contractCount;
});
+ });
+
+ // Track which users have been added to the table
+ let userPresent = [];
+
+ // Create rows for each user
+ Object.keys(employeeContracts).forEach(userName => {
+ if (!userPresent.includes(userName)) {
+ const userContracts = employeeContracts[userName];
+ const contractRow = document.createElement('tr');
+
+ contractRow.appendChild(newCell('td', userName)); // Add employee name in the first column
+ // Add each contract count for the user
+ Object.keys(res.userByContract).forEach(contractKey => {
+ const contractCount = userContracts[contractKey] || 0;
+ contractRow.appendChild(newCell('td', contractCount, '', getAllDatesFromContract(res.contracts[contractKey][userName])));
+ });
+
+ tbody.appendChild(contractRow);
+ userPresent.push(userName);
+ }
+ });
- table.appendChild(thead);
- table.appendChild(tbody);
- document.getElementById('myapp').innerHTML = '';
- document.getElementById('myapp').appendChild(table);
+ table.appendChild(thead);
+ table.appendChild(tbody);
+ document.getElementById('myapp').innerHTML = '';
+ document.getElementById('myapp').appendChild(table);
}
-
-
export function newTableSeen(response, dtStart, dtEnd) {
const res = JSON.parse(response)
var totalPeople = 1
diff --git a/templates/navigation/index.php b/templates/navigation/index.php
index 27ac4cf..474317d 100644
--- a/templates/navigation/index.php
+++ b/templates/navigation/index.php
@@ -1,5 +1,5 @@
- - Where am I V 0.0.36
+ - Where am I V 0.0.37
-