diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b25c15b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/dicos/25_schedule.xml b/dicos/25_schedule.xml index fc07576..f37a6e1 100644 --- a/dicos/25_schedule.xml +++ b/dicos/25_schedule.xml @@ -6,7 +6,9 @@ - schedule-apps + schedule @@ -32,6 +34,7 @@ + diff --git a/src/schedule-2.0/.env b/src/schedule-2.0/.env index 32372c0..b4a5ce3 100644 --- a/src/schedule-2.0/.env +++ b/src/schedule-2.0/.env @@ -32,6 +32,16 @@ APP_NAME=Schedule APP_ENV=PROD APP_CRON=false +# Office hours +OFFICE_HOUR_START=09:00 +OFFICE_HOUR_END=17:30 + +# MAIL sendmail / smtp +MAILER_METHOD=sendmail +MAILER_URL= +MAILER_NOREPLY=noreply@noreply.fr +MAILER_DEFAULT_NOTIF= + # BDD DATABASE_NAME= DATABASE_USER= @@ -46,3 +56,9 @@ CAS_USERNAME=username CAS_EMAIL=email CAS_LASTNAME=lastname CAS_FIRSTNAME=firstname + +###> symfony/swiftmailer-bundle ### +# For Gmail as a transport, use: "gmail://username:password@localhost" +# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode=" +# Delivery is disabled by default via "null://localhost" +MAILER_URL= \ No newline at end of file diff --git a/src/schedule-2.0/.gitignore b/src/schedule-2.0/.gitignore index ba27aaf..19b388e 100644 --- a/src/schedule-2.0/.gitignore +++ b/src/schedule-2.0/.gitignore @@ -17,7 +17,7 @@ ###> symfony/webpack-encore-bundle ### /node_modules/ -/public/build/ npm-debug.log yarn-error.log ###< symfony/webpack-encore-bundle ### +*~ \ No newline at end of file diff --git a/src/schedule-2.0/assets/js/fullcalendar.js b/src/schedule-2.0/assets/js/fullcalendar.js index dcdf8a1..ee44799 100644 --- a/src/schedule-2.0/assets/js/fullcalendar.js +++ b/src/schedule-2.0/assets/js/fullcalendar.js @@ -17,7 +17,7 @@ document.addEventListener('DOMContentLoaded', function() { weekNumbers: true, selectable: true, events: 'event/load', - eventLimit:4, + eventLimit:8, eventDrop: function(info) { info.revert(); }, diff --git a/src/schedule-2.0/assets/js/timer.js b/src/schedule-2.0/assets/js/timer.js new file mode 100644 index 0000000..95638e4 --- /dev/null +++ b/src/schedule-2.0/assets/js/timer.js @@ -0,0 +1,609 @@ + + +/* Creates a new Task object. */ +function Task(id ,name, description) { + this._name = name; + this._taskid = id; + this._description = description; + this._state = Task.State.STOPPED; + this._timeSpentInPreviousIterations = 0; + this._startDate = 0; + this._endDate = 0; + this._currentIterationStartTime = 0; + this._customTimeSpent = 0; + this._onChange = null; +} + +/* Possible task states. */ +Task.State = { + STOPPED: "stopped", + RUNNING: "running" +} + +Task.prototype = { + /* Returns the task name. */ + getName: function() { + return this._name; + }, + + /* Returns the task description. */ + getDescription: function() { + if (this._description == "NaN") { + this._description = "" + } + return this._description; + }, + + /* Returns the task state. */ + getState: function() { + return this._state; + }, + + /* Is the task stopped? */ + isStopped: function() { + return this._state == Task.State.STOPPED; + }, + + /* Is the task running? */ + isRunning: function() { + return this._state == Task.State.RUNNING; + }, + + /* + * Sets the "onChange" event handler. The "onChange" event is fired when the + * task is started, stopped, or reset. + */ + setOnChange: function(onChange) { + this._onChange = onChange; + }, + + /* + * Returns the time spent on the task in the current work iteration. Works + * correctly only when the task is running. + */ + _getCurrentIterationTime: function() { + return (new Date).getTime() - this._currentIterationStartTime; + }, + + /* + * Returns the total time spent on the task. This includes time spent in + * the current work iteration if the task is running. + */ + getTimeSpent: function() { + var result = this._timeSpentInPreviousIterations; + if (this._state == Task.State.RUNNING) { + result += this._getCurrentIterationTime(); + } + return result; + }, + + /* Calls the "onChange" event handler if set. */ + _callOnChange: function() { + if (typeof this._onChange == "function") { + this._onChange(); + } + }, + + /* Starts a new task work iteration. */ + start: function() { + if (this._state == Task.State.RUNNING) { return }; + if (this._startDate == 0) {this._startDate = new Date()} + this._state = Task.State.RUNNING; + this._currentIterationStartTime = (new Date).getTime(); + this._callOnChange(); + }, + + /* Stops the current task work iteration. */ + stop: function() { + if (this._state == Task.State.STOPPED) { return }; + + this._state = Task.State.STOPPED; + this._timeSpentInPreviousIterations += this._getCurrentIterationTime(); + this._currentIterationStartTime = 0; + this._endDate = new Date(); + this._callOnChange(); + }, + + /* Stops the current task work iteration and resets the time data. */ + reset: function() { + this.stop(); + this._timeSpentInPreviousIterations = 0; + this._callOnChange(); + }, + save: function(task){ + console.log(task) + + $.ajax({ + type: "POST", + data: { + taskname: task._name, + taskid: task._taskid, + description: task._description, + start: task._startDate, + end: task._endDate, + duration: task._timeSpentInPreviousIterations, + }, + url: "{{ path('app_timer_create') }}", + success: function (response) { + response=JSON.parse(response); + if(response.return=="KO") { + $("#dataTable_wrapper").append("
"+response.error+"
"); + }else{ + location.reload(); + } + } + }); + }, + /* Serializes the task into a string. */ + serialize: function() { + /* + * Originally, I wanted to use "toSource" and "eval" for serialization and + * deserialization, but "toSource" is not supported by WebKit, so I resorted + * to ugly hackery... + */ + return [ + encodeURIComponent(this._name), + this._state, + this._timeSpentInPreviousIterations, + this._currentIterationStartTime, + this._startDate, + this._endDate, + this._taskid, + this._description, + ].join("&"); + }, + + /* Deserializes the task from a string. */ + deserialize: function(serialized) { + var parts = serialized.split("&"); + this._name = decodeURIComponent(parts[0]); + this._state = parts[1]; + this._timeSpentInPreviousIterations = parseInt(parts[2]); + this._currentIterationStartTime = parseInt(parts[3]); + this._startDate = parseInt(parts[4]); + this._endDate = parseInt(parts[5]); + this._taskid = parseInt(parts[6]); + this._description = parseInt(parts[7]); + + } +} + +/* ===== Tasks ===== */ + +/* The Tasks class represents a list of tasks. */ + +/* Creates a new Tasks object. */ +function Tasks() { + this._tasks = []; + + this._onAdd = null; + this._onRemove = null; +} + +Tasks.prototype = { + /* + * Sets the "onAdd" event handler. The "onAdd" event is fired when a task + * is added to the list. + */ + setOnAdd: function(onAdd) { + this._onAdd = onAdd; + }, + + /* + * Sets the "onRemove" event handler. The "onRemove" event is fired when a + * task is removed from the list. + */ + setOnRemove: function(onRemove) { + this._onRemove = onRemove; + }, + + /* Returns the length of the task list. */ + length: function() { + return this._tasks.length + }, + + /* + * Returns index-th task in the list, or "undefined" if the index is out of + * bounds. + */ + get: function(index) { + return this._tasks[index]; + }, + + /* + * Calls the callback function for each task in the list. The function is + * called with three parameters - the task, its index and the task list + * object. This is modeled after "Array.forEach" in JavaScript 1.6. + */ + forEach: function(callback) { + for (var i = 0; i < this._tasks.length; i++) { + callback(this._tasks[i], i, this); + } + }, + + /* Calls the "onAdd" event handler if set. */ + _callOnAdd: function(task) { + if (typeof this._onAdd == "function") { + this._onAdd(task); + } + }, + + /* Adds a new task to the end of the list. */ + add: function(task) { + this._tasks.push(task); + this._callOnAdd(task); + }, + + /* Calls the "onRemove" event handler if set. */ + _callOnRemove: function(index) { + if (typeof this._onRemove == "function") { + this._onRemove(index); + } + }, + + /* + * Removes index-th task from the list. Does not do anything if the index + * is out of bounds. + */ + remove: function(index) { + this._callOnRemove(index); + this._tasks.splice(index, 1); + }, + + + + /* Serializes the list of tasks into a string. */ + serialize: function() { + var serializedTasks = []; + this.forEach(function(task) { + serializedTasks.push(task.serialize()); + }); + return serializedTasks.join("|"); + }, + + /* Deserializes the list of tasks from a string. */ + deserialize: function(serialized) { + /* + * Repeatedly use "remove" so the "onRemove" event is triggered. Do the same + * with the "add" method below. + */ + while (this._tasks.length > 0) { + this.remove(0); + } + + var serializedTasks = serialized.split("|"); + for (var i = 0; i < serializedTasks.length; i++) { + var task = new Task(0 ,"", ""); + task.deserialize(serializedTasks[i]); + this.add(task); + } + } +} + +/* ===== Extensions ===== */ + +/* + * Pads this string with another string on the left until the resulting string + * has specified length. If the padding string has more than one character, the + * resulting string may be longer than desired (the padding string is not + * truncated and it is only prepended as a whole). Bad API, I know, but it's + * good enough for me. + */ +String.prototype.pad = function(length, padding) { + var result = this; + while (result.length < length) { + result = padding + result; + } + return result; +} + +/* ===== Task List + DOM Storage ===== */ + +/* The list of tasks. */ +var tasks = new Tasks(); + +/* The last value of the serialized task list string. */ +var lastSerializedTasksString; + +/* + * The key under which the serialized task list string is stored in the DOM + * Storage. + */ +var TASKS_DOM_STORAGE_KEY = "timerTasks"; + +/* + * Returns DOM Storage used by the application, or "null" if the browser does + * not support DOM Storage. + */ +function getStorage() { + /* + * HTML 5 says that the persistent storage is available in the + * "window.localStorage" attribute, however Firefox implements older version + * of the proposal, which uses "window.globalStorage" indexed by the domain + * name. We deal with this situation here as gracefully as possible (i.e. + * without concrete browser detection and with forward compatibility). + * + * For more information, see: + * + * http://www.whatwg.org/specs/web-apps/current-work/#storage + * https://developer.mozilla.org/En/DOM/Storage + */ + if (window.localStorage !== undefined) { + return window.localStorage; + } else if (window.globalStorage !== undefined) { + return window.globalStorage[location.hostname]; + } else { + return null; + } +} + +/* + * Saves the task list into a DOM Storage. Also updates the value of the + * "lastSerializedTasksString" variable. + */ +function saveTasks() { + var serializedTasksString = tasks.serialize(); + getStorage()[TASKS_DOM_STORAGE_KEY] = serializedTasksString; + lastSerializedTasksString = serializedTasksString; +} + +/* + * Loads the serialized task list string from the DOM Storage. Returns + * "undefined" if the storage does not contain the string (this happens when + * running the application for the first time). + */ +function loadSerializedTasksString() { + var storedValue = getStorage()[TASKS_DOM_STORAGE_KEY]; + /* + * The spec says "null" should be returned when the key is not found, but some + * browsers return "undefined" instead. Maybe it was in some earlier version + * of the spec (I didn't bother to check). + */ + if (storedValue !== null && storedValue !== undefined && storedValue.length > 0) { + /* + * The values retrieved from "globalStorage" use one more level of + * indirection. + */ + return (window.localStorage === undefined) ? storedValue.value : storedValue; + } else { + return undefined; + } +} + +/* + * Loads the task list from the DOM Storage. Also updates the value of the + * "lastSerializedTasksString" variable. + */ +function loadTasks() { + var serializedTasksString = loadSerializedTasksString(); + if (serializedTasksString !== undefined) { + tasks.deserialize(serializedTasksString); + lastSerializedTasksString = serializedTasksString; + } +} + +/* + * Was the task list changed outside of the application? Detects the change + * by comparing the current serialized task list string in the DOM Storage + * with a kept old value. + */ +function tasksHaveChangedOutsideApplication() { + var serializedTasksString = loadSerializedTasksString(); + if (serializedTasksString != lastSerializedTasksString) { + return true + } + return false; +} + +/* ===== View ===== */ + +/* Some time constants. */ +var MILISECONDS_IN_SECOND = 1000; +var MILISECONDS_IN_MINUTE = 60 * MILISECONDS_IN_SECOND; +var MINUTES_IN_HOUR = 60; + +/* Formats the time in the H:MM format. */ +function formatTime(time) { + var timeInMinutes = time / MILISECONDS_IN_MINUTE; + var hours = Math.floor(timeInMinutes / MINUTES_IN_HOUR); + var minutes = Math.floor(timeInMinutes - hours * MINUTES_IN_HOUR); + return hours + ":" + String(minutes).pad(2, "0"); +} + +/* + * Computes the URL of the image in the start/stop link according to the task + * state. + */ +function computeStartStopLinkImageUrl(state) { + switch (state) { + case Task.State.STOPPED: + return "fa-play"; + case Task.State.RUNNING: + return "fa-stop"; + default: + throw "Invalid task state." + } +} + +/* + * Builds the HTML element of the row in the task table corresponding to the + * specified task and index. + */ +function buildTaskRow(task, index) { + var result = $(""); + + var startStopLink = $( + "" + + "" + + ); + startStopLink.click(function() { + switch (task.getState()) { + case Task.State.STOPPED: + task.start(); + break; + case Task.State.RUNNING: + task.stop(); + break; + default: + throw "Invalid task state." + } + saveTasks(); + return false; + }); + + var resetLink = $( + "" + + "" + ); + resetLink.click(function() { + task.reset(); + saveTasks(); + return false; + }); + + var deleteLink = $( + "" + + "" + + ); + deleteLink.click(function() { + if (confirm("Do you really want to delete task \"" + task.getName() + "\"?")) { + tasks.remove(index); + saveTasks(); + } + return false; + }); + + var saveLink = $( + "" + + "" + ); + saveLink.click(function() { + tasks.remove(index); + saveTasks(); + task.save(task); + return false; + }); + result + .addClass("state-" + task.getState()) + .append($("").text(task.getName())) + .append($("").text(task.getDescription())) + .append($("").text(formatTime(task.getTimeSpent()))) + .append($("") + .append(startStopLink) + .append("  ") + .append(resetLink) + .append("  ") + .append(saveLink) + .append("    ") + .append(deleteLink) + ); + + return result; +} + +/* Finds row with the specified index in the task table. */ +function findRowWithIndex(index) { + return $("#task-table").find("tr").slice(1).eq(index); +} + +/* + * Updates the row in the task table corresponding to a task according to + * its state. + */ +function updateTaskRow(row, task) { + if (task.isStopped()) { + row.removeClass("state-running"); + row.addClass("state-stopped"); + } else if (task.isRunning()) { + row.removeClass("state-stopped"); + row.addClass("state-running"); + } + + row.find(".task-time").text(formatTime(task.getTimeSpent())) + + row.find(".start-stop-link i").removeClass(); + row.find(".start-stop-link i").addClass("fa "+computeStartStopLinkImageUrl(task.getState())); +} + +/* ===== Initialization ===== */ + +/* Initializes event handlers on the task list. */ +function initializeTasksEventHandlers() { + tasks.setOnAdd(function(task) { + var row = buildTaskRow(task, tasks.length() - 1); + $("#task-table").append(row); + task.setOnChange(function() { + updateTaskRow(row, task); + }); + }); + + tasks.setOnRemove(function(index) { + findRowWithIndex(index).remove(); + if (tasks.length() == 1 ) { + $( "#task-table" ).css({"display":"none"}); + } + }); +} + +/* Initializes GUI event handlers. */ +function initializeGuiEventHandlers() { + $( "#addtimer" ).click(function() { + displayTaskAdd(); + }); + + $( "#timer-desc" ).keypress(function( event ) { + if ( event.which == 13 ) { + event.preventDefault(); + displayTaskAdd(); + } + }); + +} +function displayTaskAdd() { + $( "#task-table" ).css({"display":"inline"}); + var taskName = $("#timer-task option:selected").text(); + var taskId = $("#timer-task option:selected").val(); + var taskDesc = $("#timer-desc").val(); + + var task = new Task(taskId, taskName, taskDesc); + tasks.add(task); + saveTasks(); + $("#timer-desc").val(""); +} +/* + * Initializes a timer used to update task times and detect changes in the + * data stored in the DOM Storage. + */ +function initializeTimer() { + setInterval(function() { + tasks.forEach(function(task, index) { + updateTaskRow(findRowWithIndex(index), task); + }); + + if (tasksHaveChangedOutsideApplication()) { + loadTasks(); + } + }, 10 * MILISECONDS_IN_SECOND); +} + +/* Initializes the application. */ +$(document).ready(function(){ + try { + if (!getStorage()) { + alert("Timer requires a browser with DOM Storage support, such as Firefox 3+ or Safari 4+."); + return; + } + } catch (e) { + alert("Timer does not work with file: URLs in Firefox."); + return; + } + + initializeTasksEventHandlers(); + loadTasks(); + initializeGuiEventHandlers(); + initializeTimer(); +}); + + diff --git a/src/schedule-2.0/composer.json b/src/schedule-2.0/composer.json index 6d855bb..81bd9ec 100644 --- a/src/schedule-2.0/composer.json +++ b/src/schedule-2.0/composer.json @@ -33,6 +33,7 @@ "symfony/profiler-pack": "^1.0", "symfony/security-bundle": "4.4.*", "symfony/serializer-pack": "*", + "symfony/swiftmailer-bundle": "^3.4", "symfony/translation": "4.4.*", "symfony/twig-pack": "*", "symfony/validator": "4.4.*", diff --git a/src/schedule-2.0/composer.lock b/src/schedule-2.0/composer.lock index 13cf066..4f8643b 100644 --- a/src/schedule-2.0/composer.lock +++ b/src/schedule-2.0/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "24b27b5f4efd6efcabe5c89e1e1ac51b", + "content-hash": "1be2a2e3a398eb23cd3c3e26bc75c090", "packages": [ { "name": "doctrine/annotations", @@ -2557,6 +2557,68 @@ ], "time": "2019-12-27T08:57:19+00:00" }, + { + "name": "swiftmailer/swiftmailer", + "version": "v6.2.3", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/149cfdf118b169f7840bbe3ef0d4bc795d1780c9", + "reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9", + "shasum": "" + }, + "require": { + "egulias/email-validator": "~2.0", + "php": ">=7.0.0", + "symfony/polyfill-iconv": "^1.0", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "require-dev": { + "mockery/mockery": "~0.9.1", + "symfony/phpunit-bridge": "^3.4.19|^4.1.8" + }, + "suggest": { + "ext-intl": "Needed to support internationalized email addresses", + "true/punycode": "Needed to support internationalized email addresses, if ext-intl is not installed" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.2-dev" + } + }, + "autoload": { + "files": [ + "lib/swift_required.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "https://swiftmailer.symfony.com", + "keywords": [ + "email", + "mail", + "mailer" + ], + "time": "2019-11-12T09:31:26+00:00" + }, { "name": "symfony/apache-pack", "version": "v1.0.1", @@ -5811,6 +5873,71 @@ "homepage": "https://symfony.com", "time": "2020-01-04T13:00:46+00:00" }, + { + "name": "symfony/swiftmailer-bundle", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/swiftmailer-bundle.git", + "reference": "553d2474288349faed873da8ab7c1551a00d26ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/swiftmailer-bundle/zipball/553d2474288349faed873da8ab7c1551a00d26ae", + "reference": "553d2474288349faed873da8ab7c1551a00d26ae", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "swiftmailer/swiftmailer": "^6.1.3", + "symfony/config": "^4.3.8|^5.0", + "symfony/dependency-injection": "^4.3.8|^5.0", + "symfony/http-kernel": "^4.3.8|^5.0" + }, + "conflict": { + "twig/twig": "<1.41|<2.10" + }, + "require-dev": { + "symfony/console": "^4.3.8|^5.0", + "symfony/framework-bundle": "^4.3.8|^5.0", + "symfony/phpunit-bridge": "^4.3.8|^5.0", + "symfony/yaml": "^4.3.8|^5.0" + }, + "suggest": { + "psr/log": "Allows logging" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\SwiftmailerBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony SwiftmailerBundle", + "homepage": "http://symfony.com", + "time": "2019-11-14T16:18:31+00:00" + }, { "name": "symfony/templating", "version": "v4.4.5", diff --git a/src/schedule-2.0/config/bundles.php b/src/schedule-2.0/config/bundles.php index 36f3dcf..40aec7b 100644 --- a/src/schedule-2.0/config/bundles.php +++ b/src/schedule-2.0/config/bundles.php @@ -19,4 +19,5 @@ return [ Tetranz\Select2EntityBundle\TetranzSelect2EntityBundle::class => ['all' => true], Oneup\UploaderBundle\OneupUploaderBundle::class => ['all' => true], Knp\Bundle\SnappyBundle\KnpSnappyBundle::class => ['all' => true], + Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true], ]; diff --git a/src/schedule-2.0/config/packages/dev/swiftmailer.yaml b/src/schedule-2.0/config/packages/dev/swiftmailer.yaml new file mode 100644 index 0000000..b98158e --- /dev/null +++ b/src/schedule-2.0/config/packages/dev/swiftmailer.yaml @@ -0,0 +1,4 @@ +# See https://symfony.com/doc/current/email/dev_environment.html +swiftmailer: + # send all emails to a specific address + #delivery_addresses: ['me@example.com'] diff --git a/src/schedule-2.0/config/packages/swiftmailer.yaml b/src/schedule-2.0/config/packages/swiftmailer.yaml new file mode 100644 index 0000000..db8d136 --- /dev/null +++ b/src/schedule-2.0/config/packages/swiftmailer.yaml @@ -0,0 +1,5 @@ +swiftmailer: + url: '%env(MAILER_URL)%' + spool: + type: file + path: '%kernel.project_dir%/var/spoolmail' \ No newline at end of file diff --git a/src/schedule-2.0/config/packages/test/swiftmailer.yaml b/src/schedule-2.0/config/packages/test/swiftmailer.yaml new file mode 100644 index 0000000..db8d136 --- /dev/null +++ b/src/schedule-2.0/config/packages/test/swiftmailer.yaml @@ -0,0 +1,5 @@ +swiftmailer: + url: '%env(MAILER_URL)%' + spool: + type: file + path: '%kernel.project_dir%/var/spoolmail' \ No newline at end of file diff --git a/src/schedule-2.0/config/routes.yaml b/src/schedule-2.0/config/routes.yaml index 114088d..da7d898 100644 --- a/src/schedule-2.0/config/routes.yaml +++ b/src/schedule-2.0/config/routes.yaml @@ -343,6 +343,52 @@ app_validationholiday_activeholiday: path: /validator/validateholiday/activeholiday defaults: { _controller: App\Controller\ValidationController:activeholiday } +#== Validationtimer ==================================================================================================== +app_validationtimer: + path: /validator/validationtimer + defaults: { _controller: App\Controller\ValidationController:validationtimer } + +app_validationtimer_validate: + path: /validator/validatetimer + defaults: { _controller: App\Controller\ValidationController:validatetimer } + +app_validationtimer_devalidate: + path: /validator/devalidatetimer + defaults: { _controller: App\Controller\ValidationController:devalidatetimer } + +app_validationtimer_activetimer: + path: /validator/validateholiday/activetimer + defaults: { _controller: App\Controller\ValidationController:activetimer } + +#== Timer ==================================================================================================== +app_timer: + path: /timer + defaults: { _controller: App\Controller\TimerController:list } + +app_timer_view: + path: /timer/event + defaults: { _controller: App\Controller\TimerController:view } + +app_timer_load: + path: /timer/event/load + defaults: { _controller: App\Controller\TimerController:load } + +app_timer_submit: + path: /timer/submit + defaults: { _controller: App\Controller\TimerController:submit } + +app_timer_create: + path: /timer/create + defaults: { _controller: App\Controller\TimerController:create } + +app_timer_update: + path: /timer/update/{id} + defaults: { _controller: App\Controller\TimerController:update } + +app_timer_delete: + path: /timer/delete/{id} + defaults: { _controller: App\Controller\TimerController:delete } + #== Customer ====================================================================================================== app_customer_report: @@ -351,7 +397,25 @@ app_customer_report: app_customer_planning: path: /customer/planning/{key} - defaults: { _controller: App\Controller\ReportController:planning, access: 'customer' } + defaults: { _controller: App\Controller\ReportController:planning, access: 'customer' } + +#== Export ====================================================================================================== +app_export_view: + path: /export + defaults: { _controller: App\Controller\ExportController:view } + +app_export_penalty_additional: + path: /export/export_penalty_additional + defaults: { _controller: App\Controller\ExportController:export_penalty_additional } + +export_project_weekly: + path: /export/export_project_weekly + defaults: { _controller: App\Controller\ExportController:export_project_weekly } + +export_full_worked_days: + path: /export/export_full_worked_days + defaults: { _controller: App\Controller\ExportController:export_full_worked_days } + #== API =========================================================================================================== app_api: diff --git a/src/schedule-2.0/config/services.yaml b/src/schedule-2.0/config/services.yaml index 1ac6453..c9ebaa1 100644 --- a/src/schedule-2.0/config/services.yaml +++ b/src/schedule-2.0/config/services.yaml @@ -9,6 +9,9 @@ parameters: appName: '%env(resolve:APP_NAME)%' appEnv: '%env(resolve:APP_ENV)%' appCron: '%env(resolve:APP_CRON)%' + appMailmethod: '%env(resolve:MAILER_METHOD)%' + appMailnoreply: '%env(resolve:MAILER_NOREPLY)%' + appMailnotif: '%env(resolve:MAILER_DEFAULT_NOTIF)%' casHost: '%env(resolve:CAS_HOST)%' casPort: '%env(resolve:CAS_PORT)%' casPath: '%env(resolve:CAS_PATH)%' @@ -16,6 +19,8 @@ parameters: casEmail: '%env(resolve:CAS_EMAIL)%' casLastname: '%env(resolve:CAS_LASTNAME)%' casFirstname: '%env(resolve:CAS_FIRSTNAME)%' + officeHourStart: '%env(resolve:OFFICE_HOUR_START)%' + officeHourEnd: '%env(resolve:OFFICE_HOUR_END)%' services: # default configuration for services in *this* file @@ -59,3 +64,12 @@ services: tags: - { name: kernel.event_listener, event: kernel.request, method: onDomainParse } + app.sendmail.transport: + class: Swift_SendmailTransport + public: true + arguments: ['/usr/sbin/sendmail -t'] + + app.mail.service: + public: true + class: App\Service\mailService + arguments: ["@mailer", "@twig"] diff --git a/src/schedule-2.0/scripts/migration/migration.php b/src/schedule-2.0/scripts/migration/migration.php index a9a1fba..17ae0d2 100644 --- a/src/schedule-2.0/scripts/migration/migration.php +++ b/src/schedule-2.0/scripts/migration/migration.php @@ -15,7 +15,7 @@ function purge($table) { writeligne("$table"); $q="DELETE FROM $table"; $query=$bddnew->prepare($q); - $query->execute(); + $query->execute(); } // Test de la connexion à la base @@ -48,6 +48,8 @@ writeligne("MIGRATION"); writeligne(""); writeligne("Purge des Tables Scheudle"); +Purge("userproject"); +Purge("timer"); Purge("event"); Purge("penalty"); Purge("task"); @@ -66,46 +68,46 @@ writeligne(""); writeligne("== Récupération Customer"); $q="SELECT * FROM schedule_customer"; $queryold=$bddold->prepare($q); -$queryold->execute(); +$queryold->execute(); while($row=$queryold->fetch()) { writeligne($row["customer_name"]); $q="INSERT IGNORE INTO customer (id, name, keypass) VALUES (?,?,?)"; $query=$bddnew->prepare($q); - $query->execute([$row["customer_id"],$row["customer_name"],$row["customer_key"]]); + $query->execute([$row["customer_id"],$row["customer_name"],$row["customer_key"]]); } writeligne(""); writeligne("== Récupération Nature"); $q="SELECT * FROM schedule_nature"; $queryold=$bddold->prepare($q); -$queryold->execute(); +$queryold->execute(); while($row=$queryold->fetch()) { writeligne($row["nature_name"]); $q="INSERT IGNORE INTO nature (id, name, isvacation) VALUES (?,?,?)"; $query=$bddnew->prepare($q); - $query->execute([$row["nature_id"],$row["nature_name"],false]); + $query->execute([$row["nature_id"],$row["nature_name"],false]); } writeligne("Congés"); $q="INSERT IGNORE INTO nature (id, name, isvacation) VALUES (?,?,?)"; $query=$bddnew->prepare($q); -$query->execute([-200,"Congés",true]); +$query->execute([-200,"Congés",true]); writeligne("Temps Partiel"); $q="INSERT IGNORE INTO nature (id, name, isvacation) VALUES (?,?,?)"; $query=$bddnew->prepare($q); -$query->execute([-190,"Temps Partiel",true]); +$query->execute([-190,"Temps Partiel",true]); writeligne(""); writeligne("== Récupération Service"); $q="SELECT * FROM schedule_service"; $queryold=$bddold->prepare($q); -$queryold->execute(); +$queryold->execute(); while($row=$queryold->fetch()) { writeligne($row["service_name"]); $q="INSERT IGNORE INTO service (id, name) VALUES (?,?)"; $query=$bddnew->prepare($q); - $query->execute([$row["service_id"],$row["service_name"]]); + $query->execute([$row["service_id"],$row["service_name"]]); } @@ -113,16 +115,16 @@ writeligne(""); writeligne("== Récupération User"); $q="SELECT * FROM schedule_user WHERE user_login!='system'"; $queryold=$bddold->prepare($q); -$queryold->execute(); +$queryold->execute(); while($row=$queryold->fetch()) { writeligne($row["user_login"]); $q="INSERT IGNORE INTO user (id, username, firstname, lastname, email, service_id, apikey, password) VALUES (?,?,?,?,?,?,?,?)"; $query=$bddnew->prepare($q); - $query->execute([$row["user_id"],$row["user_login"],$row["user_firstname"],$row["user_lastname"],$row["user_email"],$row["user_service"],$row["user_login"],"nopassword" ]); - + $query->execute([$row["user_id"],$row["user_login"],$row["user_firstname"],$row["user_lastname"],$row["user_email"],$row["user_service"],$row["user_login"],"nopassword" ]); + $output = shell_exec('/var/www/html/schedule/bin/console app:setPassword '.$row["user_login"].' '.$row["user_login"]); - + if($row["user_login"]=="afornerot") $roles='ROLE_ADMIN,ROLE_USER'; else { switch($row["user_profil"]) { @@ -131,7 +133,7 @@ while($row=$queryold->fetch()) { case 50: $roles='ROLE_USER'; break; case 99: $roles='ROLE_VISITOR'; break; default: $roles='ROLE_VISITOR'; break; - } + } } $output = shell_exec('/var/www/html/schedule/bin/console app:setRoles '.$row["user_login"].' '.$roles); } @@ -141,12 +143,12 @@ writeligne(""); writeligne("== Récupération Project"); $q="SELECT * FROM schedule_project"; $queryold=$bddold->prepare($q); -$queryold->execute(); +$queryold->execute(); while($row=$queryold->fetch()) { writeligne($row["project_name"]); $q="INSERT IGNORE INTO project (id, name, active, service_id, customer_id ) VALUES (?,?,?,?,?)"; $query=$bddnew->prepare($q); - $query->execute([$row["project_id"],$row["project_name"],$row["project_actif"],$row["project_service"],$row["project_customer"] ]); + $query->execute([$row["project_id"],$row["project_name"],$row["project_actif"],$row["project_service"],$row["project_customer"] ]); } @@ -154,19 +156,19 @@ writeligne(""); writeligne("== Récupération Offer"); $q="SELECT * FROM schedule_order"; $queryold=$bddold->prepare($q); -$queryold->execute(); +$queryold->execute(); while($row=$queryold->fetch()) { writeligne($row["order_name"]); $q="INSERT IGNORE INTO offer (id, name, ref, quantity, pu, validate, active, project_id ) VALUES (?,?,?,?,?,?,?,?)"; $query=$bddnew->prepare($q); - $query->execute([$row["order_id"],$row["order_name"],$row["order_ref"],$row["order_quantity"],$row["order_pu"],$row["order_validate"],$row["order_actif"],$row["order_project"] ]); + $query->execute([$row["order_id"],$row["order_name"],$row["order_ref"],$row["order_quantity"],$row["order_pu"],$row["order_validate"],$row["order_actif"],$row["order_project"] ]); } writeligne(""); writeligne("== Récupération Task"); $q="SELECT * FROM schedule_task"; $queryold=$bddold->prepare($q); -$queryold->execute(); +$queryold->execute(); while($row=$queryold->fetch()) { writeligne($row["task_name"]); $nature=$row["task_nature"]; @@ -175,67 +177,78 @@ while($row=$queryold->fetch()) { $q="INSERT IGNORE INTO task (id, name, color, quantity, validate, project_id, nature_id ) VALUES (?,?,?,?,?,?,?)"; $quantity=($row["task_quantity"]==0?null:$row["task_quantity"]); $query=$bddnew->prepare($q); - $query->execute([$row["task_id"],$row["task_name"],"#".$row["task_color"],$quantity,$row["task_validate"],$row["task_project"],$nature ]); + $query->execute([$row["task_id"],$row["task_name"],"#".$row["task_color"],$quantity,$row["task_validate"],$row["task_project"],$nature ]); } writeligne(""); writeligne("== Récupération Event"); -$q="SELECT * FROM schedule_event"; +$q="SELECT * FROM schedule_event, schedule_task WHERE event_task=task_id"; $queryold=$bddold->prepare($q); -$queryold->execute(); +$queryold->execute(); while($row=$queryold->fetch()) { + // Event vacation ? + $isvacation=false; + if($row["task_id"]<=-70) $isvacation=true; + if($row["task_id"]==-85 || $row["task_id"]==-70) $isvacation=true; + + // Validation + $validate=$row["event_validate"]; + $validateholiday=false; + if($isvacation) $validateholiday=$row["event_validate"]; + + // Génération de l'event writeligne($row["event_id"]); $q="INSERT IGNORE INTO event (id, description, start, end, allday, duration, validate, validateholiday, task_id, user_id ) VALUES (?,?,?,?,?,?,?,?,?,?)"; $query=$bddnew->prepare($q); - $query->execute([$row["event_id"],$row["event_description"],$row["event_start"],$row["event_end"],$row["event_allday"],$row["event_duration"],$row["event_validate"],$row["event_validate"],$row["event_task"],$row["event_user"] ]); + $query->execute([$row["event_id"],$row["event_description"],$row["event_start"],$row["event_end"],$row["event_allday"],$row["event_duration"],$validate,$validateholiday,$row["event_task"],$row["event_user"] ]); } writeligne(""); writeligne("== Récupération Penalty"); $q="SELECT * FROM schedule_penalty"; $queryold=$bddold->prepare($q); -$queryold->execute(); +$queryold->execute(); while($row=$queryold->fetch()) { writeligne($row["penalty_id"]); $q="INSERT IGNORE INTO penalty (id, description, start, end, allday, duration, validate, task_id, user_id ) VALUES (?,?,?,?,?,?,?,?,?)"; $query=$bddnew->prepare($q); - $query->execute([$row["penalty_id"],$row["penalty_description"],$row["penalty_start"],$row["penalty_end"],$row["penalty_allday"],$row["penalty_duration"],$row["penalty_validate"],$row["penalty_task"],$row["penalty_user"] ]); + $query->execute([$row["penalty_id"],$row["penalty_description"],$row["penalty_start"],$row["penalty_end"],$row["penalty_allday"],$row["penalty_duration"],$row["penalty_validate"],$row["penalty_task"],$row["penalty_user"] ]); } writeligne(""); writeligne("== Récupération Breakday"); $q="SELECT * FROM schedule_breakday"; $queryold=$bddold->prepare($q); -$queryold->execute(); +$queryold->execute(); while($row=$queryold->fetch()) { writeligne($row["breakday_id"]); $q="INSERT IGNORE INTO breakday (id, start, end ) VALUES (?,?,?)"; $query=$bddnew->prepare($q); - $query->execute([$row["breakday_id"],$row["breakday_start"],$row["breakday_end"] ]); + $query->execute([$row["breakday_id"],$row["breakday_start"],$row["breakday_end"] ]); } writeligne(""); writeligne("== Récupération Job"); $q="SELECT * FROM schedule_job"; $queryold=$bddold->prepare($q); -$queryold->execute(); +$queryold->execute(); while($row=$queryold->fetch()) { writeligne($row["job_id"]); $q="INSERT IGNORE INTO job (id, name, type ) VALUES (?,?,?)"; $query=$bddnew->prepare($q); - $query->execute([$row["job_id"],$row["job_name"],$row["type"] ]); + $query->execute([$row["job_id"],$row["job_name"],$row["type"] ]); } writeligne(""); writeligne("== Récupération UserJob"); $q="SELECT * FROM schedule_user_jobs WHERE project_id=-100"; $queryold=$bddold->prepare($q); -$queryold->execute(); +$queryold->execute(); while($row=$queryold->fetch()) { writeligne($row["user_job_id"]); $q="INSERT IGNORE INTO userjob (user, job ) VALUES (?,?)"; $query=$bddnew->prepare($q); - $query->execute([$row["user_id"],$row["job_id"] ]); + $query->execute([$row["user_id"],$row["job_id"] ]); } echo "\n\n"; diff --git a/src/schedule-2.0/scripts/reconfigure.sh b/src/schedule-2.0/scripts/reconfigure.sh index 872c076..a9c3dbc 100755 --- a/src/schedule-2.0/scripts/reconfigure.sh +++ b/src/schedule-2.0/scripts/reconfigure.sh @@ -3,7 +3,7 @@ cd /var/www/html/schedule # Déclaration d'un proxy -. proxy.sh +. scripts/proxy.sh # Mise en place du fichier d'environnement model if [ ! -f /var/www/html/schedule/.env ]; then diff --git a/src/schedule-2.0/src/Command/SendMailCommand.php b/src/schedule-2.0/src/Command/SendMailCommand.php new file mode 100644 index 0000000..c3b2833 --- /dev/null +++ b/src/schedule-2.0/src/Command/SendMailCommand.php @@ -0,0 +1,87 @@ +container = $container; + $this->em = $em; + } + + protected function configure() + { + $this + ->setName('app:sendMail') + ->setDescription('Envoi des mails') + ->setHelp('Envoi des mails') + ->addArgument('message-limit', InputArgument::OPTIONAL, 'limit message Mail') + ->addArgument('env', InputArgument::OPTIONAL, 'env Mail') + ->addArgument('cronid', InputArgument::OPTIONAL, 'ID Cron Job') + ->addArgument('lastchance', InputArgument::OPTIONAL, 'Lastchance to run the cron') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->output = $output; + $this->filesystem = new Filesystem(); + $this->rootlog = $this->container->get('kernel')->getRootDir()."/../var/logs/"; + + $this->writelnred(''); + $this->writelnred('== app:sendMail'); + $this->writelnred('=========================================================================================================='); + + $appMailmethod=$this->container->getParameter("appMailmethod"); + + $command = $this->getApplication()->find("swiftmailer:spool:send"); + $tbparameter["--message-limit"]=100; + $tbparameter["--env"]="prod"; + $tbparameter["--time-limit"]=5; + if($appMailmethod=="sendmail") $tbparameter["--transport"]="app.sendmail.transport"; + + + $parameter = new ArrayInput($tbparameter); + try{ + $returnCode = $command->run($parameter, $output); + } + catch(\Exception $e) { + $this->writeln(""); + $this->writelnred("Impossible d'envoyer des mails"); + $this->writeln(""); + return 0; + } + $this->writeln(""); + + return 1; + + } + + private function writelnred($string) { + $this->output->writeln(''.$string.''); + $this->filesystem->appendToFile($this->rootlog.'cron.log', $string."\n"); + } + private function writeln($string) { + $this->output->writeln($string); + $this->filesystem->appendToFile($this->rootlog.'cron.log', $string."\n"); + } + +} diff --git a/src/schedule-2.0/src/Controller/BreakdayController.php b/src/schedule-2.0/src/Controller/BreakdayController.php index 5880b9c..f019473 100755 --- a/src/schedule-2.0/src/Controller/BreakdayController.php +++ b/src/schedule-2.0/src/Controller/BreakdayController.php @@ -136,8 +136,15 @@ class BreakdayController extends AbstractController if($error) return $this->redirectToRoute($this->route."_update",["id"=>$id]); else { - $em->remove($data); - $em->flush(); + try { + $em->remove($data); + $em->flush(); + } + catch(\Doctrine\DBAL\DBALException $e) { + // Création du formulaire + $this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement'); + return $this->redirectToRoute($this->route."_update",["id"=>$id]); + } // Retour à la liste return $this->redirectToRoute($this->route); diff --git a/src/schedule-2.0/src/Controller/CropController.php b/src/schedule-2.0/src/Controller/CropController.php index d1e2def..bda0101 100644 --- a/src/schedule-2.0/src/Controller/CropController.php +++ b/src/schedule-2.0/src/Controller/CropController.php @@ -77,7 +77,6 @@ class CropController extends AbstractController // Cacul de la largeur protected function getWidth($image) { - dump($image); $size = getimagesize($image); $width = $size[0]; return $width; diff --git a/src/schedule-2.0/src/Controller/CustomerController.php b/src/schedule-2.0/src/Controller/CustomerController.php index b08bea5..74f990e 100755 --- a/src/schedule-2.0/src/Controller/CustomerController.php +++ b/src/schedule-2.0/src/Controller/CustomerController.php @@ -146,8 +146,15 @@ class CustomerController extends AbstractController if($error) return $this->redirectToRoute($this->route."_update",["id"=>$id]); else { - $em->remove($data); - $em->flush(); + try { + $em->remove($data); + $em->flush(); + } + catch(\Doctrine\DBAL\DBALException $e) { + // Création du formulaire + $this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement'); + return $this->redirectToRoute($this->route."_update",["id"=>$id]); + } // Retour à la liste return $this->redirectToRoute($this->route); diff --git a/src/schedule-2.0/src/Controller/EventController.php b/src/schedule-2.0/src/Controller/EventController.php index 1e7e4cd..2708c8a 100755 --- a/src/schedule-2.0/src/Controller/EventController.php +++ b/src/schedule-2.0/src/Controller/EventController.php @@ -17,11 +17,15 @@ class EventController extends AbstractController private $route = "app_event"; private $render = "Event/"; private $entity = "App:Event"; + private $notificator; + + public function __construct(\App\Service\notificationService $notificator) { + $this->notificator = $notificator; + } public function list(Request $request) { $em = $this->getDoctrine()->getManager(); - $users = $em->getRepository("App:User")->findAll(); $tasks = $em->getRepository("App:Task")->findAll(); @@ -37,24 +41,56 @@ class EventController extends AbstractController { $em = $this->getDoctrine()->getManager(); $tbevents=[]; - - // Evenements + $start = $request->query->get('start'); + $end = $request->query->get('end'); $iduser=$this->get("session")->get("iduser"); - if($iduser=="all") + // Evenements + if($iduser=="all") { $events=$em->getRepository("App:Event")->findAll(); + $events = $em + ->createQueryBuilder('event') + ->select('event') + ->from('App:Event','event') + ->Where('event.start>=:start AND event.end <:end') + ->setParameter('start',$start) + ->setParameter('end',$end) + ->getQuery()->getResult(); + } else { - $iduser=$this->get("session")->get("iduser"); $user=$em->getRepository("App:User")->find($iduser); $events=$em->getRepository("App:Event")->findBy(["user"=>$user]); + $events = $em + ->createQueryBuilder('event') + ->select('event') + ->from('App:Event','event') + ->Where('event.user=:user AND event.start>=:start AND event.end <:end') + ->setParameter('user',$user->getId()) + ->setParameter('start',$start) + ->setParameter('end',$end) + ->getQuery()->getResult(); } - foreach($events as $event) { + //Filtre par service + + if($this->get('session')->get('idservice')!="all") { + + $idservice=$event->getUser()->getService()->getId(); + if ($idservice!=$this->get('session')->get('idservice')){ + continue; + } + } + // Filtre par project + + if($this->get('session')->get('idproject')!="all") { + $idproject=$event->getTask()->getProject()->getId(); + if($idproject!=$this->get('session')->get('idproject')) + continue; + } $tmp=$this->formatEvent($event); array_push($tbevents,$tmp); } // Astreintes - $iduser=$this->get("session")->get("iduser"); if($iduser=="all") $penaltys=$em->getRepository("App:Penalty")->findAll(); else { @@ -62,11 +98,9 @@ class EventController extends AbstractController $user=$em->getRepository("App:User")->find($iduser); $penaltys=$em->getRepository("App:Penalty")->findBy(["user"=>$user]); } - foreach($penaltys as $penalty) { $tmp=$this->formatEvent($penalty); array_push($tbevents,$tmp); - } // Breakday @@ -77,7 +111,6 @@ class EventController extends AbstractController } - // Retour return new Response(json_encode($tbevents)); } @@ -94,6 +127,7 @@ class EventController extends AbstractController $am = ($request->request->get('am')=="true"); $ap = ($request->request->get('ap')=="true"); $astreinte = ($request->request->get('astreinte')=="true"); + $externaltrip = ($request->request->get('externaltrip')=="true"); $description = $request->request->get('description'); $user = $em->getRepository("App:User")->find($iduser); @@ -237,6 +271,7 @@ class EventController extends AbstractController $event->setEnd($dateend); $event->setDuration($duration); $event->setAllday($allday); + $event->setExternalTrip($externaltrip); $event->setDescription($description); $event->setUser($user); $event->setTask($task); @@ -245,6 +280,12 @@ class EventController extends AbstractController $em->persist($event); $em->flush(); + if($task->getNature()->getIsvacation()){ + $idevent=$event->getId(); + $valid_url = $this->generateUrl('app_validationholiday'); + $this->notificator->sendNotifAttenteValid("Congés en attente de validation", $iduser, $idevent, $valid_url); + + } $output=$this->formatEvent($event); } @@ -260,7 +301,10 @@ class EventController extends AbstractController $idevent = str_replace("A","",$request->request->get('idevent')); $iduser = $request->request->get('iduser'); $idtask = $request->request->get('idtask'); + $am = ($request->request->get('am')=="true"); + $ap = ($request->request->get('ap')=="true"); $fgastreinte = ($request->request->get('fgastreinte')=="true"); + $externaltrip = ($request->request->get('externaltrip')=="true"); $description = $request->request->get('description'); $user = $em->getRepository("App:User")->find($iduser); @@ -316,17 +360,82 @@ class EventController extends AbstractController return new Response(json_encode($output)); } + $datestart=$event->getStart(); + $dateend =$event->getEnd(); + $duration=$dateend->diff($datestart)->d; + if($am&&$ap) { + if ($duration >= 1) { + $dateend->SetTime(0,0,-1); + } + $datestart->SetTime(0,0,0); + $dateend->add(new \DateInterval('P1D')); + $dateend->SetTime(0,0,0); + $duration=$dateend->diff($datestart)->d; + $allday=true; + } + else { + $duration=$dateend->diff($datestart)->d; + if ($duration == 1) { + $dateend->SetTime(0,0,-1); + } + $duration=0.5; + $allday=false; + if($am) { + $datestart->SetTime(9,0,0); + $dateend->SetTime(12,0,0); + } + else { + $datestart->SetTime(13,0,0); + $dateend->SetTime(17,0,0); + } + } + // On regarde si une tache ne commence pas pendant une autre intervention ou qui se termine pendant une autre intervention ou qui a une intervention compris dans ses dates + $events = $em->createQueryBuilder('event') + ->select('event') + ->from('App:Event','event') + ->Where('event.user=:user AND event.start<=:start AND event.end >:start') + ->orWhere('event.user=:user AND event.start<:end AND event.end >=:end') + ->orWhere('event.user=:user AND event.start>:start AND event.end <:end') + ->setParameter('user',$iduser) + ->setParameter('start',$datestart) + ->setParameter('end',$dateend) + ->getQuery()->getResult(); + if($events) { + $tbevent=[]; + foreach($events as $ev) { + if ($event->getId() != $ev->getId()) { + $tmp=[ + "id" => $ev->getId(), + "task" => $ev->getTask()->getName(), + "start" => $ev->getStart(), + "end" => $ev->getEnd(), + ]; + array_push($tbevent,$tmp); + } + } + if (sizeof($tbevent)>0) { + $output=["return"=>"KO","error"=>"Cet intervant a déjà une tache à cette date","start"=>$datestart,"end"=>$dateend,"events"=>$tbevent]; + return new Response(json_encode($output)); + } + + } + + // Modification de l'évenement + $event->setStart($datestart); + $event->setEnd($dateend); $event->setDescription($description); + $event->setDuration($duration); + $event->setAllday($allday); + $event->setExternalTrip($externaltrip); $event->setUser($user); $event->setTask($task); - + $em->persist($event); $em->flush(); $output=$this->formatEvent($event); } - return new Response(json_encode($output)); } @@ -400,12 +509,15 @@ class EventController extends AbstractController "borderColor" => $event->getTask()->getColor(), "textColor" => "#ffffff", "allDay" => $event->getAllday(), + "holiday" => $event->getTask()->getNature()->getIsvacation(), + "externaltrip" => ($event instanceof Penalty?false:$event->getExternalTrip()), "editable" => $editable, "durationEditable" => false, "extendedProps" => [ "fulldescription" => ($event instanceof Penalty?"ASTREINTE\n":"").strtoupper($event->getTask()->getDisplayname())."\n\n".$event->getDescription(), "description" => $event->getDescription(), "userid" => $event->getUser()->getId(), + "username" => $event->getUser()->getUsername(), "taskid" => $event->getTask()->getId(), "avatar" => "/".$this->getParameter("appAlias")."/uploads/avatar/".$event->getUser()->getAvatar(), "estimate" => $event->getTask()->getDuration($event->getEnd())." / ".$event->getTask()->getQuantity(), @@ -429,12 +541,15 @@ class EventController extends AbstractController "borderColor" => "#cdcdcd", "textColor" => "#ffffff", "allDay" => true, + "externaltrip" => false, + "holiday" => false, "editable" => false, "durationEditable" => false, "extendedProps" => [ "fulldescription" => "Jour Férié", "description" => "Jour Férié", "userid" => null, + "username" => "", "taskid" => null, "avatar" => "/".$this->getParameter("appAlias")."/uploads/avatar/".$this->getUser()->getAvatar(), "estimate" => "", diff --git a/src/schedule-2.0/src/Controller/ExportController.php b/src/schedule-2.0/src/Controller/ExportController.php new file mode 100644 index 0000000..46f2fd7 --- /dev/null +++ b/src/schedule-2.0/src/Controller/ExportController.php @@ -0,0 +1,437 @@ +getDoctrine()->getManager(); + + $iduser = $this->get("session")->get("iduser"); + $user = $em->getRepository("App:User")->find($iduser); + + return $this->render($this->render.'list.html.twig',[ + "useheader" => true, + "usesidebar" => true, + "user" => $user, + + ]); + } + + public function export_full_worked_days(Request $request,$access=null): Response + { + $em = $this->getDoctrine()->getManager(); + $nbmonth=$this->get("session")->get("nbmonth"); + $iduser=$this->get("session")->get("iduser"); + + if($iduser=="all") { + $users=$em->getRepository("App:User")->findAll(); + } + else { + $users=$em->getRepository("App:User")->findBy(["id"=>$iduser]); + } + + $tbevents=[]; + foreach($users as $user) { + if(in_array("ROLE_USER",$user->getRoles())) { + // Filtre par Service + if($this->get('session')->get('idservice')!="all") { + if($user->getService()->getId()!=$this->get('session')->get('idservice')) + continue; + } + + $tmp=[ + "id" => $user->getId(), + "user" => $user, + "events" => [], + ]; + + // On formate le tableau de jour + $start=new \Datetime('first day of this month'); + $start->sub(new \DateInterval('P'.$nbmonth.'M')); + $start->modify('previous monday'); + $start->setTime(0,0,0); + $end = new \Datetime('first day of this month'); + $end->add(new \DateInterval('P1M')); + $end->modify('next monday'); + $end->setTime(23,59,0); + while($start<$end) { + $idday=$start->format("Ymd"); + $idmonth=$start->format("Ym"); + + $tmp["events"][$idday] = [ + "date"=>clone $start, + "allday"=>false, + "descriptionday"=>"", + "am"=>false, + "descriptionam"=>"", + "ap"=>false, + "descriptionap"=>"", + "astreinte"=>false, + "descriptionastreinte"=>"", + ]; + + $start->add(new \DateInterval('P1D')); + } + + // On formate le tableau des event + $start=new \Datetime('first day of this month'); + $start->sub(new \DateInterval('P'.$nbmonth.'M')); + $start->modify('previous monday'); + $start->setTime(0,0,0); + $end = new \Datetime('first day of this month'); + $end->add(new \DateInterval('P1M')); + $end->modify('next monday'); + $end->setTime(23,59,0); + + $events = $em + ->createQueryBuilder('event') + ->select('event') + ->from('App:Event','event') + ->Where('event.user=:user AND event.start>=:start AND event.end <:end') + ->setParameter('user',$user->getId()) + ->setParameter('start',$start) + ->setParameter('end',$end) + ->getQuery()->getResult(); + foreach($events as $event) { + $idproject=$event->getTask()->getProject()->getId(); + + // Filtre par project + if($this->get('session')->get('idproject')!="all") { + if($idproject!=$this->get('session')->get('idproject')) + continue; + } + $isvac= $event->getTask()->getNature()->getIsvacation(); + if($isvac) {continue;} + $st=clone $event->getStart(); + while($st<$event->getEnd()) { + + $idday=$st->format("Ymd"); + if($event->getAllday()) { + $tmp["events"][$idday]["allday"]=true; + $tmp["events"][$idday]["descriptionday"]=strtoupper($event->getTask()->getDisplayname()); + } + else { + // Matin ou après-midi ? + $time=$event->getStart()->format("H"); + if($time==9) { + $tmp["events"][$idday]["am"]=true; + if(isset($tmp["events"][$idday]["ap"]) && $tmp["events"][$idday]["ap"]==true){$tmp["events"][$idday]["allday"]=true;} + $tmp["events"][$idday]["descriptionam"]=strtoupper($event->getTask()->getDisplayname()); + } + else { + $tmp["events"][$idday]["ap"]=true; + if(isset($tmp["events"][$idday]["am"]) && $tmp["events"][$idday]["am"] ==true){$tmp["events"][$idday]["allday"]=true;} + $tmp["events"][$idday]["descriptionap"]=strtoupper($event->getTask()->getDisplayname()); + } + } + + $st->add(new \DateInterval('P1D')); + } + } + + + // On formate le tableau des astreintes + $start=new \Datetime('first day of this month'); + $start->sub(new \DateInterval('P'.$nbmonth.'M')); + $start->modify('previous monday'); + $start->setTime(0,0,0); + $end = new \Datetime('first day of this month'); + $end->add(new \DateInterval('P1M')); + $end->modify('next monday'); + $end->setTime(23,59,0); + + $penaltys = $em + ->createQueryBuilder('penalty') + ->select('penalty') + ->from('App:Penalty','penalty') + ->Where('penalty.user=:user AND penalty.start>=:start AND penalty.end <:end') + ->setParameter('user',$user->getId()) + ->setParameter('start',$start) + ->setParameter('end',$end) + ->getQuery()->getResult(); + foreach($penaltys as $penalty) { + $idproject=$penalty->getTask()->getProject()->getId(); + + // Filtre par project + if($this->get('session')->get('idproject')!="all") { + if($idproject!=$this->get('session')->get('idproject')) + continue; + } + + $st=clone $penalty->getStart(); + while($st<$penalty->getEnd()) { + $idday=$st->format("Ymd"); + if($penalty->getAllday()) { + $tmp["events"][$idday]["astreinte"]=true; + $tmp["events"][$idday]["descriptionastreinte"]=strtoupper($penalty->getTask()->getDisplayname()); + } + + $st->add(new \DateInterval('P1D')); + } + } + + array_push($tbevents,$tmp); + } + } + $csv = $this->renderView('Export/export_full_worked_days.csv.twig', ["users" => $tbevents]); + $response = new Response($csv); + $response->headers->set('Content-Type', 'text/csv; charset=utf-8'); + $response->headers->set('Content-Disposition', 'attachment; filename="export_full_worked_days.csv"'); + + return $response; + } + + public function export_penalty_additional(Request $request,$access=null): Response + { + $em = $this->getDoctrine()->getManager(); + + $iduser = $this->get("session")->get("iduser"); + $timers = $em->getRepository("App:Timer")->findBy(["user"=>$iduser]); + $tbtimers = []; + foreach ($timers as $timer) { + $isactive = $timer->getActivePenalty(); + $isadditional = $timer->getAdditionalHour(); + if ($isactive || $isadditional) { + $tbtimer["id"] = $timer->getId(); + $tbtimer["taskname"] = $timer->getTask()->getDisplayname(); + $tbtimer["user"] = $timer->getUser()->getUsername(); + $tbtimer["start"] = $timer->getStart()->format("Y-m-d H:i"); + $tbtimer["end"] = $timer->getEnd()->format("Y-m-d H:i"); + $tbtimer["duration"] = $timer->getDuration(); + $tbtimer["activepenalty"] = $timer->getActivePenalty(); + $tbtimer["additionalhour"] = $timer->getAdditionalHour(); + $tbtimer["description"] = $timer->getDescription(); + array_push($tbtimers, $tbtimer); + } + + } + + $csv = $this->renderView('Export/export_penalty_additional.csv.twig', ["timers" => $tbtimers]); + $response = new Response($csv); + $response->headers->set('Content-Type', 'text/csv; charset=utf-8'); + $response->headers->set('Content-Disposition', 'attachment; filename="export_penalty_additional.csv"'); + + return $response; + } + + public function export_project_weekly(Request $request,$access=null): Response { + $nbmonth=$this->get("session")->get("nbmonth"); + $em = $this->getDoctrine()->getManager(); + $projects=$em->getRepository("App:Project")->findAll(); + + //Construction du tableau des projets + $tbprojects=[]; + foreach($projects as $project) { + // Filtre par Customer + if($access=="customer") { + if($project->getCustomer()->getKeypass()!=$key) + continue; + } + + // Filtre par Service + if($this->get('session')->get('idservice')!="all") { + if($project->getService()->getId()!=$this->get('session')->get('idservice')) + continue; + } + + // Filtre par project + if($this->get('session')->get('idproject')!="all") { + if($project->getId()!=$this->get('session')->get('idproject')) + continue; + } + + // Ne prendre que les projets actif/inactif + if($this->get('session')->get('activeproject')!=$project->getActive()) + continue; + + $tbproject= []; + $tbproject["projectname"] = $project->getDisplayname(); + $tbproject["name"] = $project->getname(); + $tbproject["customer"] = $project->getCustomer()->getName(); + // Somme event validé par semaine + $start=new \Datetime('first day of this month'); + $start->sub(new \DateInterval('P'.$nbmonth.'M')); + $start->modify('previous monday'); + $start->setTime(0,0,0); + $endmonth = new \Datetime('first day of this month'); + $endmonth->add(new \DateInterval('P1M')); + $endmonth->modify('next monday'); + $endmonth->setTime(23,59,0); + + $eventsbyweek = $em + ->createQueryBuilder('event') + ->select('event') + ->from('App:Task','task') + ->from('App:Event','event') + ->Where('task.project=:project') + ->andWhere('event.task=task') + ->andWhere('event.end >=:start') + ->andWhere('event.end <:end') + ->andWhere('event.validate=:validate') + ->setParameter('project',$project) + ->setParameter('validate',true) + ->setParameter('start',$start) + ->setParameter('end',$endmonth) + ->orderBy('event.start') + ->getQuery()->getResult(); + + // foreach($eventsbyweek as $event) { + // if(!isset($tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")])){ + // $tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")] = [ + // "weeknumber" => $event->getStart()->format("W"), + // "cumul" => 0, + // ]; + // } + // $tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["cumul"] = $tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["cumul"]+$event->getDuration(); + + // } + // foreach($eventsbyweek as $event) { + // if(!isset($tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"])){ + // $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")] = [ + // "weeknumber" => $event->getStart()->format("W"), + // "users" => [], + // ]; + // } + // if(!isset($tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()])){ + // $tbuser= [ + // "id"=>$event->getUser()->getId(), + // "displayname"=>$event->getUser()->getDisplayname(), + // "cumul"=>0 + // ]; + // $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()] = $tbuser; + // } + + // $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()]["cumul"] = $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()]["cumul"]+$event->getDuration(); + // } + foreach($eventsbyweek as $event) { + if(!isset($tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"])){ + $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")] = [ + "weeknumber" => $event->getStart()->format("W"), + "tasks" => [], + ]; + } + if(!isset($tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()])){ + $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()] = [ + "taskname" => $event->getTask()->getName(), + "nature" => $event->getTask()->getNature()->getName(), + "users" => [], + ]; + } + if(!isset($tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()])){ + $tbuser= [ + "id"=>$event->getUser()->getId(), + "displayname"=>$event->getUser()->getDisplayname(), + "cumul"=>0 + ]; + $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()] = $tbuser; + } + + $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()]["cumul"] = $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()]["cumul"]+$event->getDuration(); + } + // Somme astreintes validé par semaine + $start=new \Datetime('first day of this month'); + $start->sub(new \DateInterval('P'.$nbmonth.'M')); + $start->modify('previous monday'); + $start->setTime(0,0,0); + $endmonth = new \Datetime('first day of this month'); + $endmonth->add(new \DateInterval('P1M')); + $endmonth->modify('next monday'); + $endmonth->setTime(23,59,0); + + $penaltybyweek = $em + + ->createQueryBuilder('penalty') + ->select('penalty') + ->from('App:Task','task') + ->from('App:Penalty','penalty') + ->Where('task.project=:project') + ->andWhere('penalty.task=task') + ->andWhere('penalty.end >=:start') + ->andWhere('penalty.end <:end') + ->andWhere('penalty.validate=:validate') + ->setParameter('project',$project) + ->setParameter('validate',true) + ->setParameter('start',$start) + ->setParameter('end',$endmonth) + ->orderBy('penalty.start') + ->getQuery()->getResult(); + // foreach($penaltybyweek as $penalty) { + // if(!isset($tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")])){ + // $tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")] = [ + // "weeknumber" => $penalty->getStart()->format("W"), + // "cumul" => 0, + // ]; + // } + // $tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["cumul"] = $tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["cumul"]+$penalty->getDuration(); + // } + // foreach($penaltybyweek as $penalty) { + // if(!isset($tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"])){ + // $tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")] = [ + // "weeknumber" => $penalty->getStart()->format("W"), + // "users" => [], + // ]; + // } + // if(!isset($tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()])){ + // $tbuser= [ + // "id"=>$penalty->getUser()->getId(), + // "displayname"=>$penalty->getUser()->getDisplayname(), + // "cumul"=>0 + // ]; + // $tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()] = $tbuser; + // } + + // $tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()]["cumul"] = $tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()]["cumul"]+$penalty->getDuration(); + // } + foreach($penaltybyweek as $penalty) { + if(!isset($tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"])){ + $tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")] = [ + "weeknumber" => $penalty->getStart()->format("W"), + "tasks" => [], + ]; + } + if(!isset($tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"][$penalty->getTask()->getId()])){ + $tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"][$penalty->getTask()->getId()] = [ + "taskname" => $penalty->getTask()->getName(), + "nature" => $event->getTask()->getNature()->getName(), + "users" => [], + ]; + } + if(!isset($tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"][$penalty->getTask()->getId()]["users"][$penalty->getUser()->getId()])){ + $tbuser= [ + "id"=>$penalty->getUser()->getId(), + "displayname"=>$penalty->getUser()->getDisplayname(), + "cumul"=>0 + ]; + $tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"][$penalty->getTask()->getId()]["users"][$penalty->getUser()->getId()] = $tbuser; + } + + $tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"][$penalty->getTask()->getId()]["users"][$penalty->getUser()->getId()]["cumul"] = $tbproject["weeks_by_task_by_user"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["tasks"][$penalty->getTask()->getId()]["users"][$penalty->getUser()->getId()]["cumul"]+$penalty->getDuration(); + } + + $tbprojects[$project->getId()]=$tbproject; + } + + $csv = $this->renderView('Export/export_project_weekly.csv.twig', ["projects" => $tbprojects]); + $response = new Response($csv); + $response->headers->set('Content-Type', 'text/csv; charset=utf-8'); + $response->headers->set('Content-Disposition', 'attachment; filename="export_project_weekly.csv"'); + + return $response; + } + + +} diff --git a/src/schedule-2.0/src/Controller/JobController.php b/src/schedule-2.0/src/Controller/JobController.php index ba5f7c0..da1eca3 100755 --- a/src/schedule-2.0/src/Controller/JobController.php +++ b/src/schedule-2.0/src/Controller/JobController.php @@ -146,8 +146,15 @@ class JobController extends AbstractController if($error) return $this->redirectToRoute($this->route."_update",["id"=>$id]); else { - $em->remove($data); - $em->flush(); + try { + $em->remove($data); + $em->flush(); + } + catch(\Doctrine\DBAL\DBALException $e) { + // Création du formulaire + $this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement'); + return $this->redirectToRoute($this->route."_update",["id"=>$id]); + } // Retour à la liste return $this->redirectToRoute($this->route); diff --git a/src/schedule-2.0/src/Controller/NatureController.php b/src/schedule-2.0/src/Controller/NatureController.php index f4fa49b..f0f35eb 100755 --- a/src/schedule-2.0/src/Controller/NatureController.php +++ b/src/schedule-2.0/src/Controller/NatureController.php @@ -129,8 +129,15 @@ class NatureController extends AbstractController if($error) return $this->redirectToRoute($this->route."_update",["id"=>$id]); else { - $em->remove($data); - $em->flush(); + try { + $em->remove($data); + $em->flush(); + } + catch(\Doctrine\DBAL\DBALException $e) { + // Création du formulaire + $this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement'); + return $this->redirectToRoute($this->route."_update",["id"=>$id]); + } // Retour à la liste return $this->redirectToRoute($this->route); diff --git a/src/schedule-2.0/src/Controller/OfferController.php b/src/schedule-2.0/src/Controller/OfferController.php index 48d64a4..7ffc0b9 100755 --- a/src/schedule-2.0/src/Controller/OfferController.php +++ b/src/schedule-2.0/src/Controller/OfferController.php @@ -146,8 +146,15 @@ class OfferController extends AbstractController if($error) return $this->redirectToRoute($this->route."_update",["id"=>$id]); else { - $em->remove($data); - $em->flush(); + try { + $em->remove($data); + $em->flush(); + } + catch(\Doctrine\DBAL\DBALException $e) { + // Création du formulaire + $this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement'); + return $this->redirectToRoute($this->route."_update",["id"=>$id]); + } // Retour à la liste return $this->redirectToRoute($this->route); diff --git a/src/schedule-2.0/src/Controller/ProjectController.php b/src/schedule-2.0/src/Controller/ProjectController.php index 317161a..8912b98 100755 --- a/src/schedule-2.0/src/Controller/ProjectController.php +++ b/src/schedule-2.0/src/Controller/ProjectController.php @@ -150,8 +150,16 @@ class ProjectController extends AbstractController if($error) return $this->redirectToRoute($this->route."_update",["id"=>$id]); else { - $em->remove($data); - $em->flush(); + try { + $em->remove($data); + $em->flush(); + } + catch(\Doctrine\DBAL\DBALException $e) { + // Création du formulaire + $this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement'); + return $this->redirectToRoute($this->route."_update",["id"=>$id]); + } + $this->refreshsession(); // Retour à la liste diff --git a/src/schedule-2.0/src/Controller/ReportController.php b/src/schedule-2.0/src/Controller/ReportController.php index d4b736b..74e59e8 100755 --- a/src/schedule-2.0/src/Controller/ReportController.php +++ b/src/schedule-2.0/src/Controller/ReportController.php @@ -6,6 +6,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Knp\Bundle\SnappyBundle\Snappy\Response\PdfResponse; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; class ReportController extends AbstractController { @@ -15,7 +16,6 @@ class ReportController extends AbstractController public function synthese(Request $request) { $em = $this->getDoctrine()->getManager(); - $nbmonth=$this->get("session")->get("nbmonth"); $iduser=$this->get("session")->get("iduser"); @@ -102,7 +102,7 @@ class ReportController extends AbstractController $tmp["events"][$idday]["allday"]=true; $tmp["events"][$idday]["colorday"]=$event->getTask()->getColor(); $tmp["events"][$idday]["descriptionday"]=strtoupper($event->getTask()->getDisplayname())."\n\n".$event->getDescription(); - } + } else { // Matin ou après-midi ? $time=$event->getStart()->format("H"); @@ -119,8 +119,9 @@ class ReportController extends AbstractController } $st->add(new \DateInterval('P1D')); - } + } } + // On formate le tableau des astreintes $start=new \Datetime('first day of this month'); @@ -157,7 +158,7 @@ class ReportController extends AbstractController } $st->add(new \DateInterval('P1D')); - } + } } // On formate le tableau des jours fériés @@ -184,7 +185,7 @@ class ReportController extends AbstractController $tmp["events"][$idday]["descriptionday"]="Jour Férié"; $st->add(new \DateInterval('P1D')); - } + } } array_push($tbevents,$tmp); @@ -202,9 +203,10 @@ class ReportController extends AbstractController return new PdfResponse( $this->knpSnappy->getOutputFromHtml($render), 'synthese.pdf' - ); + ); } - else { + else { + return $this->render('Report/synthese.html.twig',[ "useheader" => true, "usesidebar" => ($this->getUser()), @@ -246,7 +248,7 @@ class ReportController extends AbstractController if($project->getId()!=$this->get('session')->get('idproject')) continue; } - + // Ne prendre que les projets actif/inactif if($this->get('session')->get('activeproject')!=$project->getActive()) continue; @@ -368,7 +370,7 @@ class ReportController extends AbstractController } } } - + // Cumule foreach($tbprojects as $project) { foreach($project["months"] as $month) { @@ -387,7 +389,7 @@ class ReportController extends AbstractController $totmonth+=$day["duration"]; $totmontha+=$day["astreinte"]; } - + if($totuser==0&&$totusera==0) { unset($tbprojects[$project["id"]]["months"][$month["monthid"]]["users"][$user["id"]]); } else { @@ -398,7 +400,7 @@ class ReportController extends AbstractController if(!$haveuser) unset($tbprojects[$project["id"]]["months"][$month["monthid"]]); - else { + else { $tbprojects[$project["id"]]["months"][$month["monthid"]]["total"]=$totmonth; $tbprojects[$project["id"]]["months"][$month["monthid"]]["totala"]=$totmontha; } @@ -418,9 +420,9 @@ class ReportController extends AbstractController return new PdfResponse( $this->knpSnappy->getOutputFromHtml($render,["orientation"=>"Landscape"]), 'planning.pdf' - ); + ); } - else { + else { return $this->render('Report/planning.html.twig',[ "useheader" => true, "usesidebar" => ($this->getUser()), @@ -428,7 +430,7 @@ class ReportController extends AbstractController "access" => $access, "key" => $key ]); - } + } } public function report($key=null,$access=null,Request $request) { @@ -447,7 +449,7 @@ class ReportController extends AbstractController } $projects=$em->getRepository("App:Project")->findAll(); - + //Construction du tableau des projets $tbprojects=[]; foreach($projects as $project) { @@ -468,7 +470,7 @@ class ReportController extends AbstractController if($project->getId()!=$this->get('session')->get('idproject')) continue; } - + // Ne prendre que les projets actif/inactif if($this->get('session')->get('activeproject')!=$project->getActive()) continue; @@ -513,11 +515,13 @@ class ReportController extends AbstractController "beforeastreinte"=>[], "months"=>[], "offers"=>[], + "weeks"=>[], ]; // Somme event validé avant la date $end=new \Datetime('first day of this month'); $end->sub(new \DateInterval('P'.$nbmonth.'M')); + $end->setTime(23,59,0); $events = $em ->createQueryBuilder('event') ->select('event') @@ -533,19 +537,20 @@ class ReportController extends AbstractController ->orderBy('event.start') ->getQuery()->getResult(); foreach($events as $event) { - if(!isset($tbproject["before"][$event->getStart()->format("Ym")])) { - $tbproject["before"][$event->getStart()->format("Ym")] = [ + if(!isset($tbproject["before"][$event->getStart()->format("Y")][$event->getStart()->format("Ym")])) { + $tbproject["before"][$event->getStart()->format("Y")][$event->getStart()->format("Ym")] = [ "idmonth" => $event->getStart()->format("Ym"), "monthlabel"=>$event->getStart()->format("m/Y"), "duration" => 0, ]; } - $tbproject["before"][$event->getStart()->format("Ym")]["duration"]=$tbproject["before"][$event->getStart()->format("Ym")]["duration"]+$event->getDuration(); + $tbproject["before"][$event->getStart()->format("Y")][$event->getStart()->format("Ym")]["duration"]=$tbproject["before"][$event->getStart()->format("Y")][$event->getStart()->format("Ym")]["duration"]+$event->getDuration(); } // Somme astreinte validé avant la date $end=new \Datetime('first day of this month'); $end->sub(new \DateInterval('P'.$nbmonth.'M')); + $end->setTime(23,59,0); $penaltys = $em ->createQueryBuilder('penalty') ->select('penalty') @@ -561,17 +566,150 @@ class ReportController extends AbstractController ->orderBy('penalty.start') ->getQuery()->getResult(); foreach($penaltys as $penalty) { - if(!isset($tbproject["beforeastreinte"][$penalty->getStart()->format("Ym")])) { - $tbproject["beforeastreinte"][$penalty->getStart()->format("Ym")] = [ + if(!isset($tbproject["beforeastreinte"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("Ym")])) { + $tbproject["beforeastreinte"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("Ym")] = [ "idmonth" => $penalty->getStart()->format("Ym"), "monthlabel"=>$penalty->getStart()->format("m/Y"), "duration" => 0, ]; } - $tbproject["beforeastreinte"][$penalty->getStart()->format("Ym")]["duration"]=$tbproject["beforeastreinte"][$penalty->getStart()->format("Ym")]["duration"]+$penalty->getDuration(); + $tbproject["beforeastreinte"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("Ym")]["duration"]=$tbproject["beforeastreinte"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("Ym")]["duration"]+$penalty->getDuration(); } + + // Somme event validé par semaine + $start=new \Datetime('first day of this month'); + $start->sub(new \DateInterval('P'.$nbmonth.'M')); + $start->modify('previous monday'); + $start->setTime(0,0,0); + $endmonth = new \Datetime('first day of this month'); + $endmonth->add(new \DateInterval('P1M')); + $endmonth->modify('next monday'); + $end->setTime(23,59,0); + + $eventsbyweek = $em + ->createQueryBuilder('event') + ->select('event') + ->from('App:Task','task') + ->from('App:Event','event') + ->Where('task.project=:project') + ->andWhere('event.task=task') + ->andWhere('event.end >=:start') + ->andWhere('event.end <:end') + ->andWhere('event.validate=:validate') + ->setParameter('project',$project) + ->setParameter('validate',true) + ->setParameter('start',$start) + ->setParameter('end',$endmonth) + ->orderBy('event.start') + ->getQuery()->getResult(); + foreach($eventsbyweek as $event) { + if(!isset($tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")])){ + $tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")] = [ + "weeknumber" => $event->getStart()->format("W"), + "cumul" => 0, + ]; + } + $tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["cumul"] = $tbproject["weeks"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["cumul"]+$event->getDuration(); + } + + // foreach($eventsbyweek as $event) { + // if(!isset($tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"])){ + // $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")] = [ + // "weeknumber" => $event->getStart()->format("W"), + // "users" => [], + // ]; + // } + // if(!isset($tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()])){ + // $tbuser= [ + // "id"=>$event->getUser()->getId(), + // "displayname"=>$event->getUser()->getDisplayname(), + // "cumul"=>0 + // ]; + // $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()] = $tbuser; + // } - // Recap des propositions + // $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()]["cumul"] = $tbproject["weeks_by_name"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["users"][$event->getUser()->getId()]["cumul"]+$event->getDuration(); + // } + // foreach($eventsbyweek as $event) { + // if(!isset($tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"])){ + // $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")] = [ + // "weeknumber" => $event->getStart()->format("W"), + // "tasks" => [], + // ]; + // } + // if(!isset($tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()])){ + // $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()] = [ + // "taskname" => $event->getTask()->getName(), + // "users" => [], + // ]; + // } + // if(!isset($tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()])){ + // $tbuser= [ + // "id"=>$event->getUser()->getId(), + // "displayname"=>$event->getUser()->getDisplayname(), + // "cumul"=>0 + // ]; + // $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()] = $tbuser; + // } + + // $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()]["cumul"] = $tbproject["weeks_by_task_by_user"][$event->getStart()->format("Y")][$event->getStart()->format("W")]["tasks"][$event->getTask()->getId()]["users"][$event->getUser()->getId()]["cumul"]+$event->getDuration(); + // } + // Somme astreintes validé par semaine + $start=new \Datetime('first day of this month'); + $start->sub(new \DateInterval('P'.$nbmonth.'M')); + $start->modify('previous monday'); + $start->setTime(0,0,0); + $endmonth = new \Datetime('first day of this month'); + $endmonth->add(new \DateInterval('P1M')); + $endmonth->modify('next monday'); + $end->setTime(23,59,0); + + $penaltybyweek = $em + + ->createQueryBuilder('penalty') + ->select('penalty') + ->from('App:Task','task') + ->from('App:Penalty','penalty') + ->Where('task.project=:project') + ->andWhere('penalty.task=task') + ->andWhere('penalty.end >=:start') + ->andWhere('penalty.end <:end') + ->andWhere('penalty.validate=:validate') + ->setParameter('project',$project) + ->setParameter('validate',true) + ->setParameter('start',$start) + ->setParameter('end',$endmonth) + ->orderBy('penalty.start') + ->getQuery()->getResult(); + foreach($penaltybyweek as $penalty) { + if(!isset($tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")])){ + $tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")] = [ + "weeknumber" => $penalty->getStart()->format("W"), + "cumul" => 0, + ]; + } + $tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["cumul"] = $tbproject["weeks"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["cumul"]+$penalty->getDuration(); + } + foreach($penaltybyweek as $penaltybyweek) { + if(!isset($tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"])){ + $tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")] = [ + "weeknumber" => $penalty->getStart()->format("W"), + "users" => [], + ]; + } + if(!isset($tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()])){ + $tbuser= [ + "id"=>$penalty->getUser()->getId(), + "displayname"=>$penalty->getUser()->getDisplayname(), + "cumul"=>0 + ]; + $tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()] = $tbuser; + } + + $tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()]["cumul"] = $tbproject["weeks_by_name"][$penalty->getStart()->format("Y")][$penalty->getStart()->format("W")]["users"][$penalty->getUser()->getId()]["cumul"]+$penalty->getDuration(); + } + + // Recap des Commandes $offers=$em->getRepository("App:Offer")->findBy(["project"=>$project->getId()]); foreach($offers as $offer) { $tbproject["offers"][$offer->getId()] = [ @@ -579,14 +717,16 @@ class ReportController extends AbstractController "ref"=>$offer->getRef(), "quantity"=>$offer->getQuantity(), ]; - } + } // Formater les mois $start=new \Datetime('first day of this month'); $start->sub(new \DateInterval('P'.$nbmonth.'M')); + $start->setTime(0,0,0); $end=new \Datetime('first day of this month'); $end->add(new \DateInterval('P'.$nbmonth.'M')); $end->sub(new \DateInterval('P1D')); + $end->setTime(23,59,0); while($start<$end) { $tbproject["months"][$start->format("Ym")]=[ "monthid"=> $start->format("Ym"), @@ -634,7 +774,7 @@ class ReportController extends AbstractController ]; $tbproject["months"][$start->format("Ym")]["tasks"][$event["id"]]=$tbtask; } - + $start->add(new \DateInterval('P1M')); } @@ -670,14 +810,15 @@ class ReportController extends AbstractController } $tbprojects[$project->getId()]=$tbproject; } - // Formater les utilisateurs $start=new \Datetime('first day of this month'); $start->sub(new \DateInterval('P'.$nbmonth.'M')); + $start->setTime(0,0,0); $end=new \Datetime('first day of this month'); $end->add(new \DateInterval('P'.$nbmonth.'M')); $end->sub(new \DateInterval('P1D')); - + $end->setTime(23,59,0); + foreach($users as $user) { $tbevents = $this->getEventuser($user,$start,$end,true); @@ -695,7 +836,6 @@ class ReportController extends AbstractController } } } - // Cumule foreach($tbprojects as $project) { foreach($project["months"] as $month) { @@ -714,7 +854,7 @@ class ReportController extends AbstractController $totmonth+=$day["duration"]; $totmontha+=$day["astreinte"]; } - + if($totuser==0&&$totusera==0) { unset($tbprojects[$project["id"]]["months"][$month["monthid"]]["users"][$user["id"]]); } else { @@ -725,7 +865,7 @@ class ReportController extends AbstractController if(!$haveuser) unset($tbprojects[$project["id"]]["months"][$month["monthid"]]); - else { + else { $tbprojects[$project["id"]]["months"][$month["monthid"]]["total"]=$totmonth; $tbprojects[$project["id"]]["months"][$month["monthid"]]["totala"]=$totmontha; } @@ -745,9 +885,9 @@ class ReportController extends AbstractController return new PdfResponse( $this->knpSnappy->getOutputFromHtml($render,["orientation"=>"Landscape"]), 'report.pdf' - ); + ); } - else { + else { return $this->render('Report/report.html.twig',[ "useheader" => true, "usesidebar" => ($this->getUser()), @@ -755,13 +895,12 @@ class ReportController extends AbstractController "access" => $access, "key" => $key ]); - } + } } private function getEventuser($user,$start,$end,$onlyvalidate) { $em = $this->getDoctrine()->getManager(); $tbevents=[]; - // Récupération de event $qb = $em ->createQueryBuilder('event') @@ -773,7 +912,7 @@ class ReportController extends AbstractController ->setParameter('end',$end); if($onlyvalidate) $qb->andWhere('event.validate=:validate')->setParameter('validate',true); - + $events=$qb->getQuery()->getResult(); foreach($events as $event) { $project=$event->getTask()->getProject(); @@ -792,14 +931,14 @@ class ReportController extends AbstractController if($idproject!=$this->get('session')->get('idproject')) continue; } - + // Ne prendre que les projets actif/inactif if($this->get('session')->get('activeproject')!=$activeproject) continue; if(!isset($tbevents[$idproject])) $tbevents[$idproject] = []; - + $st=clone $event->getStart(); while($st<$event->getEnd()) { $idday=$st->format("Ymd"); @@ -816,11 +955,11 @@ class ReportController extends AbstractController ]; } - $tbevents[$idproject][$idday]["duration"]=($event->getAllday()?1:0.5); + $tbevents[$idproject][$idday]["duration"]+=($event->getAllday()?1:0.5); $st->add(new \DateInterval('P1D')); - } + } } @@ -835,7 +974,7 @@ class ReportController extends AbstractController ->setParameter('end',$end); if($onlyvalidate) $qb->andWhere('penalty.validate=:validate')->setParameter('validate',true); - + $penaltys=$qb->getQuery()->getResult(); foreach($penaltys as $penalty) { $project=$penalty->getTask()->getProject(); @@ -854,14 +993,14 @@ class ReportController extends AbstractController if($idproject!=$this->get('session')->get('idproject')) continue; } - + // Ne prendre que les projets actif/inactif if($this->get('session')->get('activeproject')!=$activeproject) continue; if(!isset($tbevents[$idproject])) $tbevents[$idproject] = []; - + $st=clone $penalty->getStart(); while($st<$penalty->getEnd()) { $idday=$st->format("Ymd"); @@ -882,7 +1021,7 @@ class ReportController extends AbstractController $st->add(new \DateInterval('P1D')); - } + } } return $tbevents; @@ -891,7 +1030,7 @@ class ReportController extends AbstractController public function holiday(Request $request) { $em = $this->getDoctrine()->getManager(); - + $iduser=$this->getUser(); $users=$em->getRepository("App:User")->findBy(["id"=>$iduser]); @@ -940,13 +1079,13 @@ class ReportController extends AbstractController "useheader" => true, "usesidebar" => ($this->getUser()), "users" => $tbevents, - "fgprint" => $request->query->get('fgprint'), + "fgprint" => $request->query->get('fgprint'), ]); return new PdfResponse( $this->knpSnappy->getOutputFromHtml($render), 'conges.pdf' - ); + ); } else { return $this->render('Report/holiday.html.twig',[ @@ -954,15 +1093,15 @@ class ReportController extends AbstractController "usesidebar" => ($this->getUser()), "users" => $tbevents ]); - } } + public function activeholiday() { $this->get('session')->set('activeholiday',!$this->get('session')->get('activeholiday')); - + return $this->redirectToRoute("app_holiday"); - } + } private function frmDay($daynumber) { switch($daynumber) { @@ -988,3 +1127,4 @@ class ReportController extends AbstractController } } } + diff --git a/src/schedule-2.0/src/Controller/SecurityController.php b/src/schedule-2.0/src/Controller/SecurityController.php index 0b98a22..f1282d5 100755 --- a/src/schedule-2.0/src/Controller/SecurityController.php +++ b/src/schedule-2.0/src/Controller/SecurityController.php @@ -97,7 +97,7 @@ class SecurityController extends AbstractController $user->setPassword("CASPWD-".$username); $user->setSalt("CASPWD-".$username); - $user->setRoles(["ROLE_USER"]); + $user->setRole("ROLE_USER"); $em->persist($user); $em->flush(); @@ -132,26 +132,14 @@ class SecurityController extends AbstractController public function logout() { - $auth_mode=$this->getParameter("appAuth"); - switch($auth_mode) { - case "MYSQL": - return $this->logoutMYSQL(); - break; - - case "CAS": - return $this->logoutCAS(); - break; - } - - } - - public function logoutMYSQL() { $this->get('security.token_storage')->setToken(null); $this->get('session')->invalidate(); - return $this->redirect($this->generateUrl("app_home")); + return $this->redirect($this->generateUrl("cnous_portal_homepage")); } + + public function logoutcas() { // Init Client CAS \phpCAS::setDebug('/var/www/html/schedule/var/log/cas.log'); @@ -162,7 +150,5 @@ class SecurityController extends AbstractController // Logout $url=$this->generateUrl('app_home', array(), UrlGeneratorInterface::ABSOLUTE_URL); \phpCAS::logout(array("service"=>$url)); - - return true; } } diff --git a/src/schedule-2.0/src/Controller/ServiceController.php b/src/schedule-2.0/src/Controller/ServiceController.php index 80c9229..dc61e53 100755 --- a/src/schedule-2.0/src/Controller/ServiceController.php +++ b/src/schedule-2.0/src/Controller/ServiceController.php @@ -146,8 +146,15 @@ class ServiceController extends AbstractController if($error) return $this->redirectToRoute($this->route."_update",["id"=>$id]); else { - $em->remove($data); - $em->flush(); + try { + $em->remove($data); + $em->flush(); + } + catch(\Doctrine\DBAL\DBALException $e) { + // Création du formulaire + $this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement'); + return $this->redirectToRoute($this->route."_update",["id"=>$id]); + } // Retour à la liste return $this->redirectToRoute($this->route); diff --git a/src/schedule-2.0/src/Controller/TaskController.php b/src/schedule-2.0/src/Controller/TaskController.php index 869d5c5..260bffa 100755 --- a/src/schedule-2.0/src/Controller/TaskController.php +++ b/src/schedule-2.0/src/Controller/TaskController.php @@ -146,8 +146,15 @@ class TaskController extends AbstractController if($error) return $this->redirectToRoute($this->route."_update",["id"=>$id]); else { - $em->remove($data); - $em->flush(); + try { + $em->remove($data); + $em->flush(); + } + catch(\Doctrine\DBAL\DBALException $e) { + // Création du formulaire + $this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement'); + return $this->redirectToRoute($this->route."_update",["id"=>$id]); + } // Retour à la liste return $this->redirectToRoute($this->route); diff --git a/src/schedule-2.0/src/Controller/TimerController.php b/src/schedule-2.0/src/Controller/TimerController.php new file mode 100644 index 0000000..bf6ed94 --- /dev/null +++ b/src/schedule-2.0/src/Controller/TimerController.php @@ -0,0 +1,283 @@ +getDoctrine()->getManager(); + + $iduser = $this->get("session")->get("iduser"); + $user = $em->getRepository("App:User")->find($iduser); + $tasks = $em->getRepository("App:Task")->findAll(); + $timers = $em->getRepository("App:Timer")->findBy(["user"=>$iduser]); + + return $this->render($this->render.'list.html.twig',[ + "useheader" => true, + "usesidebar" => true, + "user" => $user, + "tasks" => $tasks, + "timers" => $timers, + ]); + } + + public function view(Request $request) + { + $em = $this->getDoctrine()->getManager(); + + $iduser = $this->get("session")->get("iduser"); + $user = $em->getRepository("App:User")->find($iduser); + $tasks = $em->getRepository("App:Task")->findAll(); + $timers = $em->getRepository("App:Timer")->findBy(["user"=>$iduser]); + return $this->render($this->render.'list.cal.html.twig',[ + "useheader" => true, + "usesidebar" => true, + "user" => $user, + "tasks" => $tasks, + "timers" => $timers, + ]); + } + + public function load(Request $request) + { + $em = $this->getDoctrine()->getManager(); + $tbtimers=[]; + + // Evenements + $iduser=$this->get("session")->get("iduser"); + if($iduser=="all") + $timers=$em->getRepository("App:Timer")->findAll(); + else { + $iduser=$this->get("session")->get("iduser"); + $user=$em->getRepository("App:User")->find($iduser); + $timers=$em->getRepository("App:Timer")->findBy(["user"=>$user]); + } + + foreach($timers as $timer) { + $tmp=$this->formatTimer($timer); + array_push($tbtimers,$tmp); + } + + // Retour + return new Response(json_encode($tbtimers)); + } + + public function create(Request $request) + { + // Initialisation de l'enregistrement + $em = $this->getDoctrine()->getManager(); + $iduser = $this->get("session")->get("iduser"); + $user = $em->getRepository("App:User")->find($iduser); + $taskid = $request->request->get('taskid'); + $description = $request->request->get('description'); + $task = $em->getRepository("App:Task")->find($taskid); + $start = \DateTime::createFromFormat('D M d Y H:i:s e+',$request->request->get('start')); + $end = \DateTime::createFromFormat('D M d Y H:i:s e+',$request->request->get('end')); + $duration = new \DateTime(date("H:i:s", ($request->request->get('duration')/1000))); + $duration->sub(new \DateInterval('PT1H')); + $activepenalty = $request->request->get('activepenalty'); + $additionalhour= $request->request->get('additionalhour'); + + $officeworkstart = clone $start; + $officeworkend = clone $officeworkstart; + $officeworkstart->SetTime(9,0,0); + $officeworkend->SetTime(17,30,0); + + $timer = new Entity(); + $timer->setUser($user); + $timer->setTask($task); + $timer->setStart($start); + $timer->setEnd($end); + $timer->setDuration($duration); + $timer->setDescription($description); + $timer->setActivePenalty($activepenalty ? $activepenalty : false); + if ($start < $officeworkstart || $end > $officeworkend) { + $timer->setAdditionalHour(true); + }else{ + $timer->setAdditionalHour($additionalhour ? $additionalhour : false); + } + $em->persist($timer); + $em->flush(); + + $output=["return"=>"OK"]; + return new Response(json_encode($output)); + } + + public function submit(Request $request) + { + // Initialisation de l'enregistrement + $em = $this->getDoctrine()->getManager(); + $data = new Entity(); + + $iduser = $this->get("session")->get("iduser"); + $user = $em->getRepository("App:User")->find($iduser); + + // Création du formulaire + $form = $this->createForm(Form::class,$data,array("mode"=>"submit")); + + // Récupération des data du formulaire + $form->handleRequest($request); + + // Sur erreur + $this->getErrorForm(null,$form,$request,$data,"submit"); + + // Sur validation + if ($form->get('submit')->isClicked() && $form->isValid()) { + $data = $form->getData(); + $data->setUser($user); + + $start = $data->getStart(); + $end = $data->getEnd(); + $additionalhour = $data->getAdditionalHour(); + + $officeworkstart = clone $start; + $officeworkend = clone $officeworkstart; + $uStart = explode(":",$this->getParameter('officeHourStart')); + $uEnd = explode(":",$this->getParameter('officeHourEnd')); + $officeworkstart->SetTime(intval($uStart[0]),intval($uStart[1]),0); + $officeworkend->SetTime(intval($uEnd[0]),intval($uEnd[1]),0); + if ($start < $officeworkstart || $end > $officeworkend) { + $data->setAdditionalHour(true); + }else{ + $data->setAdditionalHour($additionalhour); + } + $em->persist($data); + $em->flush(); + + // Retour à la liste + return $this->redirectToRoute($this->route); + } + + // Affichage du formulaire + return $this->render($this->render.'edit.html.twig', [ + 'useheader' => true, + 'usesidebar' => true, + $this->data => $data, + 'mode' => 'submit', + 'form' => $form->createView() + ]); + } + + public function update($id,Request $request) + { + // Initialisation de l'enregistrement + $em = $this->getDoctrine()->getManager(); + $data=$em->getRepository($this->entity)->find($id); + + // Création du formulaire + $form = $this->createForm(Form::class,$data,array("mode"=>"update")); + + // Récupération des data du formulaire + $form->handleRequest($request); + + // Sur erreur + $this->getErrorForm(null,$form,$request,$data,"update"); + + // Sur validation + if ($form->get('submit')->isClicked() && $form->isValid()) { + $data = $form->getData(); + $em->persist($data); + $em->flush(); + + // Retour à la liste + return $this->redirectToRoute($this->route); + } + + + return $this->render($this->render.'edit.html.twig', [ + 'useheader' => true, + 'usesidebar' => true, + $this->data => $data, + 'mode' => 'update', + 'form' => $form->createView() + ]); + + } + public function delete($id, Request $request) + { + // Initialisation de l'enregistrement + $em = $this->getDoctrine()->getManager(); + $data=$em->getRepository($this->entity)->find($id); + + // Controle avant suppression + $error=false; + if($error) + return $this->redirectToRoute($this->route."_update",["id"=>$id]); + else { + try { + $em->remove($data); + $em->flush(); + } + catch(\Doctrine\DBAL\DBALException $e) { + // Création du formulaire + $this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement'); + return $this->redirectToRoute($this->route."_update",["id"=>$id]); + } + + // Retour à la liste + return $this->redirectToRoute($this->route); + } + } + + + + protected function getErrorForm($id,$form,$request,$data,$mode) { + if ($form->get('submit')->isClicked()&&$mode=="delete") { + } + + if ($form->get('submit')->isClicked() && $mode=="submit") { + } + + if ($form->get('submit')->isClicked() && !$form->isValid()) { + $this->get('session')->getFlashBag()->clear(); + + $errors = $form->getErrors(); + foreach( $errors as $error ) { + $request->getSession()->getFlashBag()->add("error", $error->getMessage()); + } + } + } + public function formatTimer($timer) { + + $tmp= [ + "id"=> $timer->getId(), + "title" => $timer->getTask()->getDisplayname(), + "start" => $timer->getStart()->format("Y-m-d H:i"), + "end" => $timer->getEnd()->format("Y-m-d H:i"), + "backgroundColor" => $timer->getTask()->getColor(), + "borderColor" => $timer->getTask()->getColor(), + "textColor" => "#ffffff", + "allDay" => false, + "editable" => true, + "durationEditable" => false, + "extendedProps" => [ + "fulldescription" => $timer->getTask()->getDisplayname()."\n\n".$timer->getDescription(), + "description" => $timer->getDescription(), + "userid" => $timer->getUser()->getId(), + "username" => $timer->getUser()->getUsername(), + "taskid" => $timer->getTask()->getId(), + "avatar" => "/".$this->getParameter("appAlias")."/uploads/avatar/".$timer->getUser()->getAvatar(), + "estimate" => $timer->getDuration()->format("H:i"), + "locked" => false, + "editable" => true, + "astreinte" => false + ] + ]; + return $tmp; + } + +} diff --git a/src/schedule-2.0/src/Controller/UserController.php b/src/schedule-2.0/src/Controller/UserController.php index cbbcb4d..3071fbd 100755 --- a/src/schedule-2.0/src/Controller/UserController.php +++ b/src/schedule-2.0/src/Controller/UserController.php @@ -202,8 +202,16 @@ class UserController extends AbstractController if($error) return $this->redirectToRoute($this->route."_update",["id"=>$id]); else { - $em->remove($data); - $em->flush(); + try { + $em->remove($data); + $em->flush(); + } + catch(\Doctrine\DBAL\DBALException $e) { + // Création du formulaire + $this->get('session')->getFlashBag()->add('error', 'Impossible de supprimer cet enregistrement'); + return $this->redirectToRoute($this->route."_update",["id"=>$id]); + } + $this->refreshsession(); // Retour à la liste diff --git a/src/schedule-2.0/src/Controller/ValidationController.php b/src/schedule-2.0/src/Controller/ValidationController.php index 66bc702..6134e8d 100755 --- a/src/schedule-2.0/src/Controller/ValidationController.php +++ b/src/schedule-2.0/src/Controller/ValidationController.php @@ -10,7 +10,11 @@ use Knp\Bundle\SnappyBundle\Snappy\Response\PdfResponse; class ValidationController extends AbstractController { private $knpSnappy; - public function __construct(\Knp\Snappy\Pdf $knpSnappy) { $this->knpSnappy = $knpSnappy; } + private $notificator; + public function __construct(\Knp\Snappy\Pdf $knpSnappy, \App\Service\notificationService $notificator) { + $this->knpSnappy = $knpSnappy; + $this->notificator = $notificator; + } public function validation(Request $request) { @@ -510,6 +514,9 @@ class ValidationController extends AbstractController $event->setValidateholiday(true); $em->persist($event); $em->flush(); + $iduser=$this->get("session")->get("iduser"); + $idevent=$event->getId(); + $this->notificator->sendNotifValid("Congé validé", $iduser, $event); } $output=[]; @@ -541,6 +548,128 @@ class ValidationController extends AbstractController $this->get('session')->set('activeholiday',!$this->get('session')->get('activeholiday')); return $this->redirectToRoute("app_validationholiday"); - } + } + + public function validationtimer(Request $request) + { + $em = $this->getDoctrine()->getManager(); + + $iduser=$this->get("session")->get("iduser"); + if($iduser=="all") { + $users=$em->getRepository("App:User")->findAll(); + } + else { + $users=$em->getRepository("App:User")->findBy(["id"=>$iduser]); + } + + $tbtimers=[]; + foreach($users as $user) { + if(in_array("ROLE_USER",$user->getRoles())) { + // Filtre par Service + if($this->get('session')->get('idservice')!="all") { + if($user->getService()->getId()!=$this->get('session')->get('idservice')) + continue; + } + + $tmp=[ + "id" => $user->getId(), + "user" => $user, + "timers" => [], + "timerstodevalidate" => [], + ]; + + // Timers à valider ou à dévalider + $timers = $em + ->createQueryBuilder('timer') + ->select('timer') + ->from('App:Timer','timer') + ->from('App:Task','task') + ->Where('timer.user=:user') + ->andWhere('timer.validate=:validate') + ->andWhere('task=timer.task') + ->setParameter('user',$user->getId()) + ->setParameter('validate',!($this->get("session")->get("activetimer"))) + ->getQuery()->getResult(); + foreach($timers as $timer) { + + $tbtimer = [ + "id"=>$timer->getId(), + "taskname"=>$timer->getTask()->getDisplayname(), + "user"=>$timer->getUser()->getUsername(), + "start"=>$timer->getStart()->format("Y-m-d H:i"), + "end"=>$timer->getEnd()->format("Y-m-d H:i"), + "duration"=>$timer->getDuration(), + "activepenalty"=>$timer->getActivePenalty(), + "additionalhour"=>$timer->getAdditionalHour(), + "description"=>$timer->getDescription(), + "validate"=>$timer->getValidate(), + ]; + + array_push($tmp["timers"],$tbtimer); + } + array_push($tbtimers,$tmp); + } + } + if($request->query->get('fgprint')) { + $render = $this->renderView('Validation/validationtimer.html.twig',[ + "useheader" => true, + "usesidebar" => ($this->getUser()), + "users" => $tbtimers, + "fgprint" => $request->query->get('fgprint'), + ]); + + return new PdfResponse( + $this->knpSnappy->getOutputFromHtml($render), + 'validationhoraires.pdf' + ); + } + else { + return $this->render('Validation/validationtimer.html.twig',[ + "useheader" => true, + "usesidebar" => ($this->getUser()), + "users" => $tbtimers + ]); + + } + } + + public function validatetimer(Request $request){ + $em = $this->getDoctrine()->getManager(); + + // Récupération des variables envoyées en post + $id = $request->request->get('id'); + $timer=$em->getRepository("App:Timer")->find($id); + if($timer) { + $timer->setValidate(true); + $em->persist($timer); + $em->flush(); + $iduser=$this->get("session")->get("iduser"); + $idtimer=$timer->getId(); + } + + $output=[]; + return new Response(json_encode($output)); + } + + public function devalidatetimer(Request $request){ + $em = $this->getDoctrine()->getManager(); + + // Récupération des variables envoyées en post + $id = $request->request->get('id'); + $timer=$em->getRepository("App:Timer")->find($id); + if($timer) { + $timer->setValidate(false); + $em->persist($timer); + $em->flush(); + } + + $output=[]; + return new Response(json_encode($output)); + } + public function activetimer() { + $this->get('session')->set('activetimer',!$this->get('session')->get('activetimer')); + + return $this->redirectToRoute("app_validationtimer"); + } } diff --git a/src/schedule-2.0/src/Entity/Event.php b/src/schedule-2.0/src/Entity/Event.php index eb026c2..491a1ae 100644 --- a/src/schedule-2.0/src/Entity/Event.php +++ b/src/schedule-2.0/src/Entity/Event.php @@ -50,6 +50,12 @@ class Event */ private $allday; + /** + * @ORM\Column(name="externaltrip", type="boolean") + * + */ + private $externaltrip; + /** * @ORM\Column(name="validate", type="boolean") * @@ -96,24 +102,24 @@ class Event public function getStart(): ?\DateTimeInterface { - return $this->start; + return clone $this->start; } public function setStart(\DateTimeInterface $start): self { - $this->start = $start; + $this->start = clone $start; return $this; } public function getEnd(): ?\DateTimeInterface { - return $this->end; + return clone $this->end; } public function setEnd(\DateTimeInterface $end): self { - $this->end = $end; + $this->end = clone $end; return $this; } @@ -129,6 +135,17 @@ class Event return $this; } + public function getExternalTrip(): ?bool + { + return $this->externaltrip; + } + + public function setExternalTrip(bool $externaltrip): self + { + $this->externaltrip = $externaltrip; + + return $this; + } public function getValidate(): ?bool { diff --git a/src/schedule-2.0/src/Entity/Task.php b/src/schedule-2.0/src/Entity/Task.php index ff59675..3a9f470 100644 --- a/src/schedule-2.0/src/Entity/Task.php +++ b/src/schedule-2.0/src/Entity/Task.php @@ -61,12 +61,17 @@ class Task */ private $events; + /** + * @ORM\OneToMany(targetEntity="Timer", mappedBy="task", cascade={"persist"}, orphanRemoval=false) + */ + private $timers; + /** * @ORM\OneToMany(targetEntity="Penalty", mappedBy="task", cascade={"persist"}, orphanRemoval=false) */ private $penaltys; - /** + /** * Calculate Displayname */ public function getDisplayname(): ?string @@ -74,7 +79,7 @@ class Task return $this->project->getCustomer()->getName()."-".$this->project->getName()."-".$this->name; } - /** + /** * Calculate Duration */ public function getDuration($end): ?string @@ -90,6 +95,7 @@ class Task public function __construct() { $this->events = new ArrayCollection(); + $this->timers = new ArrayCollection(); $this->penaltys = new ArrayCollection(); } @@ -191,6 +197,24 @@ class Task return $this; } + /** + * @return Collection|Timer[] + */ + public function getTimers(): Collection + { + return $this->tasks; + } + + public function addTimer(Timer $timer): self + { + if (!$this->timers->contains($timer)) { + $this->timers[] = $timer; + $timer->setTask($this); + } + + return $this; + } + public function removeEvent(Event $event): self { if ($this->events->contains($event)) { @@ -235,4 +259,17 @@ class Task return $this; } + public function removeTimer(Timer $timer): self + { + if ($this->timers->contains($timer)) { + $this->timers->removeElement($timer); + // set the owning side to null (unless already changed) + if ($timer->getTask() === $this) { + $timer->setTask(null); + } + } + + return $this; + } + } diff --git a/src/schedule-2.0/src/Entity/Timer.php b/src/schedule-2.0/src/Entity/Timer.php new file mode 100644 index 0000000..4b49086 --- /dev/null +++ b/src/schedule-2.0/src/Entity/Timer.php @@ -0,0 +1,197 @@ +start = new \DateTime(); + $this->end = new \DateTime(); + $this->duration = new \DateTime(); + $this->validate = false; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getStart(): ?\DateTimeInterface + { + return $this->start; + } + + public function setStart(\DateTimeInterface $start): self + { + $this->start = $start; + + return $this; + } + + public function getEnd(): ?\DateTimeInterface + { + return $this->end; + } + + public function setEnd(\DateTimeInterface $end): self + { + $this->end = $end; + + return $this; + } + + public function getDuration(): ?\DateTimeInterface + { + return $this->duration; + } + + public function setDuration(\DateTimeInterface $duration): self + { + $this->duration = $duration; + + return $this; + } + + public function getDescription(): ?string + { + return $this->description; + } + + public function setDescription(?string $description): self + { + $this->description = $description; + + return $this; + } + + public function getUser(): ?User + { + return $this->user; + } + + public function setUser(?User $user): self + { + $this->user = $user; + + return $this; + } + + public function getTask(): ?Task + { + return $this->task; + } + + public function setTask(?Task $task): self + { + $this->task = $task; + + return $this; + } + + public function getActivePenalty(): ?bool + { + return $this->activepenalty; + } + + public function setActivePenalty(bool $activepenalty): self + { + $this->activepenalty = $activepenalty; + + return $this; + } + + public function getAdditionalHour(): ?bool + { + return $this->additionalhour; + } + + public function setAdditionalHour(bool $additionalhour): self + { + $this->additionalhour = $additionalhour; + + return $this; + } + + public function getValidate(): ?bool + { + return $this->validate; + } + + public function setValidate(bool $validate): self + { + $this->validate = $validate; + + return $this; + } + +} diff --git a/src/schedule-2.0/src/Form/TimerType.php b/src/schedule-2.0/src/Form/TimerType.php new file mode 100644 index 0000000..c66a809 --- /dev/null +++ b/src/schedule-2.0/src/Form/TimerType.php @@ -0,0 +1,110 @@ +add('submit', + SubmitType::class, [ + "label" => "Valider", + "attr" => ["class" => "btn btn-success no-print"], + ] + ); + $builder->add('task', + EntityType::class, [ + "label" => "Tâche", + "class" => "App:Task", + "choice_label" => function ($task) { + return $task->getDisplayname();}, + + "disabled" => false, + "required" => true, + "multiple" => false, + "placeholder" => "Selectionner une Tâche", + ] + ); + + $builder->add('description', + TextType::class, [ + "label" => "Description", + "required" => false + ] + ); + + $builder->add("activepenalty", + ChoiceType::class,[ + "label" => "Astreinte active", + "choices" => ["Non"=>false, "Oui"=>true] + ] + ); + $builder->add("additionalhour", + ChoiceType::class,[ + "label" => "Heures supplémentaires", + "choices" => ["Non"=>false, "Oui"=>true] + ] + ); + + $builder->add('start', + DateTimeType::class, [ + "label" =>"Début", + "date_widget" => "single_text", + "time_widget" => "single_text", + "format" => "yyyy-MM-dd HH:mm", + ] + ); + + $builder->add('end', + DateTimeType::class, [ + "label" =>"Fin", + "date_widget" => "single_text", + "time_widget" => "single_text", + "format" => "yyyy-MM-dd HH:mm", + ] + ); + + $builder->add('duration', + TimeType::class, [ + "label" =>"Durée", + "widget" => "single_text", + ] + ); + + + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults(array( + 'data_class' => 'App\Entity\Timer', + 'mode' => 'string', + )); + } +} diff --git a/src/schedule-2.0/src/Repository/ProjectRepository.php b/src/schedule-2.0/src/Repository/ProjectRepository.php index b4a435d..fe3ddd5 100644 --- a/src/schedule-2.0/src/Repository/ProjectRepository.php +++ b/src/schedule-2.0/src/Repository/ProjectRepository.php @@ -81,14 +81,14 @@ class ProjectRepository extends ServiceEntityRepository public function sumEstimate($id) { $qb = $this->createQueryBuilder('project') - ->select('SUM(task.quantity) as somme1, SUM(task.validate) as somme2 ') + ->select('SUM(task.quantity) as somme') ->from('App:Task','task') ->Where('project.id=:id') ->andWhere('task.project=project') ->setParameter('id',$id); $result=$qb->getQuery()->getOneOrNullResult(); - $estimate=($result["somme1"]?$result["somme1"]:0)+($result["somme2"]?$result["somme2"]:0); + $estimate=($result["somme"]?$result["somme"]:0); return $estimate; } } diff --git a/src/schedule-2.0/src/Service/notificationService.php b/src/schedule-2.0/src/Service/notificationService.php new file mode 100644 index 0000000..1c754af --- /dev/null +++ b/src/schedule-2.0/src/Service/notificationService.php @@ -0,0 +1,98 @@ +mailer = $mailer; + $this->twig = $twig; + $this->em = $em; + } + + public function sendNotifAttenteValid($subject, $iduser, $idevent, $valid_url) + { + $template = $this->twig->load('Notif/attentevalidation.html.twig'); + $user=$this->em->getRepository("App:User")->find($iduser); + $event=$this->em->getRepository("App:Event")->find($idevent); + $users=$this->em->getRepository("App:User")->findAll(); + $tbemails=[]; + foreach($users as $usr) { + if(in_array("ROLE_VALIDATOR",$usr->getRoles())) { + array_push($tbemails,$usr->getEmail()); + } + // else{ + // if(in_array("ROLE_MASTER",$usr->getRoles())) { + // if ($usr->getService() == $user->getService() ){ + // $tmp=[ + // "id"=>$usr->getId(), + // "email"=>$usr->getEmail() + // ]; + // } + // array_push($tbemails,$tmp); + // } + // } + } + + $parameters=[ + 'date' => new \DateTime(), + 'username' => $user->getUsername(), + 'start' => $event->getStart(), + 'end' => $event->getEnd(), + 'duration' => $event->getDuration(), + 'valid_link' => $valid_url, + ]; + $bodyHtml = $template->renderBlock('body', $parameters); + + $message = (new \Swift_Message()) + ->setFrom('schedule@cadoles.com') + ->setSubject($user->getUsername() . " = " . $subject) + //TODO envoyer à tt les users master associé au service de l'utilisateur et l'ensemble des user validator + ->setTo($tbemails) + ->setBody($bodyHtml, 'text/html'); + + $response = $this->mailer->send($message); + + return $response; + } + public function sendNotifValid($subject, $iduser, $idevent) + { + $template = $this->twig->load('Notif/validation.html.twig'); + $user=$this->em->getRepository("App:User")->find($iduser); + $event=$this->em->getRepository("App:Event")->find($idevent); + $users=$this->em->getRepository("App:User")->findAll(); + $tbemails=[]; + foreach($users as $usr) { + if(in_array("ROLE_VALIDATOR",$usr->getRoles())) { + array_push($tbemails,$usr->getEmail()); + } + + } + $parameters=[ + 'date' => new \DateTime(), + 'username' => $user->getUsername(), + 'start' => $event->getStart(), + 'end' => $event->getEnd(), + 'duration' => $event->getDuration(), + ]; + $bodyHtml = $template->renderBlock('body', $parameters); + + $message = (new \Swift_Message()) + ->setFrom('schedule@cadoles.com') + ->setSubject($user->getUsername() . " = " . $subject) + ->setTo($this->container->getParameter('appMailnotif')) + ->setCc($tbemails) + ->setBody($bodyHtml, 'text/html'); + + $response = $this->mailer->send($message); + + + return $response; + } +} diff --git a/src/schedule-2.0/src/Service/sessionListener.php b/src/schedule-2.0/src/Service/sessionListener.php index 760ce9c..e99f79d 100644 --- a/src/schedule-2.0/src/Service/sessionListener.php +++ b/src/schedule-2.0/src/Service/sessionListener.php @@ -69,6 +69,7 @@ class sessionListener { $session->set('activeproject',true); $session->set('activeoffer',true); $session->set('activeholiday',true); + $session->set('activetimer',true); $session->set('nbmonth',3); if($curentuser!="anon.") { @@ -119,7 +120,8 @@ class sessionListener { ]; array_push($tbservices,$tmp); } - $session->set('services',$tbservices); + $session->set('services',$tbservices); + } } } diff --git a/src/schedule-2.0/templates/Event/list.html.twig b/src/schedule-2.0/templates/Event/list.html.twig index 2599c58..896d2f9 100644 --- a/src/schedule-2.0/templates/Event/list.html.twig +++ b/src/schedule-2.0/templates/Event/list.html.twig @@ -21,17 +21,21 @@ .fc-title { font-weight: bolder; - font-size: 14px; + font-size: 12px; } .eventAvatar { - width: 40px; + width: 20px; margin: 0px 5px 0px 0px; float: left; } .eventInfo{ - margin: -18px 5px 0px 0px; + margin: -5px 5px 0px 0px; + clear: both; + } + + .eventUser{ clear: both; } @@ -105,6 +109,12 @@ +
+
+ + +
+
-
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+ +
+

Exporter le cumul des points éffectués sur un/des projets, par tâche par utilisateur.

+

Filtres utiles : Nombres de mois, Projet, Service

+
+
+

+
+ +
+

Exporter la liste des astreintes actives réalisés, ainsi que les saisies en heure supplémentaires.

+

Filtres utiles : Intervenant (hors "Tout le monde")

+
+
+

+
+ +
+

Exporter la liste des jours travaillés pleinement éligibles aux tickets restaurants

+

Filtres utiles : Nombre de mois, Intervenant

+
+
+

+{% endblock %} \ No newline at end of file diff --git a/src/schedule-2.0/templates/Notif/attentevalidation.html.twig b/src/schedule-2.0/templates/Notif/attentevalidation.html.twig new file mode 100644 index 0000000..a224421 --- /dev/null +++ b/src/schedule-2.0/templates/Notif/attentevalidation.html.twig @@ -0,0 +1,25 @@ +{% block body %} +{% autoescape %} +

Date de la demande : {{ date|date("d/m/Y H:i") }}

+

+ Utilisateur = {{ username }} +

+

+ Début = {{ start|date("d/m/Y H:i") }} +

+

+ Fin = {{ end|date("d/m/Y H:i") }} +

+

+ Durée = {{ duration }} +

+

+ Type = Congé +

+

+ Lien pour valider = {{ valid_link }} +

+ +{% endautoescape %} +{% endblock %} + diff --git a/src/schedule-2.0/templates/Notif/validation.html.twig b/src/schedule-2.0/templates/Notif/validation.html.twig new file mode 100644 index 0000000..4a35676 --- /dev/null +++ b/src/schedule-2.0/templates/Notif/validation.html.twig @@ -0,0 +1,23 @@ +{% block body %} +{% autoescape %} +

VALIDATION

+

+ Utilisateur = {{ username }} +

+

+ Début = {{ start|date("d/m/Y H:i") }} +

+

+ Fin = {{ end|date("d/m/Y H:i") }} +

+

+ Durée = {{ duration }} +

+

+ Type = Congé +

+ + +{% endautoescape %} +{% endblock %} + diff --git a/src/schedule-2.0/templates/Offer/list.html.twig b/src/schedule-2.0/templates/Offer/list.html.twig index b4a65f2..99f9f02 100644 --- a/src/schedule-2.0/templates/Offer/list.html.twig +++ b/src/schedule-2.0/templates/Offer/list.html.twig @@ -18,7 +18,7 @@ {% block body %}

- PROPOSITIONS + COMMANDES

Ajouter diff --git a/src/schedule-2.0/templates/Project/list.html.twig b/src/schedule-2.0/templates/Project/list.html.twig index 7c8d114..80b4b93 100644 --- a/src/schedule-2.0/templates/Project/list.html.twig +++ b/src/schedule-2.0/templates/Project/list.html.twig @@ -12,7 +12,7 @@ thead { display: table-header-group; } - tr { page-break-inside: avoid; } + tr { page-break-inside: avoid; } {%endif%} {% endblock %} @@ -69,7 +69,12 @@ - + {{project.customer.name}} @@ -89,7 +94,7 @@ {% for task in project.tasks %} {% set tottask=tottask+task.quantity %} - {% set totvalidate=totvalidate+task.validate %} + {% set totplanified=totplanified+task.validate %} {% for event in task.events %} {% set totplanified=totplanified+event.duration %} @@ -130,6 +135,9 @@ {% endblock %} {% block localjavascript %} + $(function () { + $('[data-toggle="tooltip"]').tooltip() + }) $(document).ready(function() { {% if not fgprint is defined or not fgprint %} $('.table').DataTable({ diff --git a/src/schedule-2.0/templates/Report/planning.html.twig b/src/schedule-2.0/templates/Report/planning.html.twig index 9186e25..50dfb35 100644 --- a/src/schedule-2.0/templates/Report/planning.html.twig +++ b/src/schedule-2.0/templates/Report/planning.html.twig @@ -99,9 +99,10 @@

{{ project.displayname }}

Estimé = {{ project.estimate }}
+ Commandé = {{ project.proposed }}
Validé = {{ project.validate }}
Planifié = {{ project.planified }}
- RESTE = {{ (project.estimate - project.validate - project.planified) }} + RESTE = {{ ( project.proposed - project.validate - project.planified) }}
diff --git a/src/schedule-2.0/templates/Report/report.html.twig b/src/schedule-2.0/templates/Report/report.html.twig index d9a9cd3..d3ef41d 100644 --- a/src/schedule-2.0/templates/Report/report.html.twig +++ b/src/schedule-2.0/templates/Report/report.html.twig @@ -93,7 +93,6 @@ {% block body %} {% if fgprint is defined and fgprint %}

Planning

{%endif%} - {% if access=="customer" and not app.user %}
@@ -125,40 +124,9 @@
-

RAPPORT

+

RAPPORT

- {% if project.hors!=0 %} - consommé avant = {{ project.hors }}
- {% endif %} - - {% set bycolonne = max(10,((project.before|length)/3)|round) %} - - {% set compteur = 0 %} -
-
- {% for month in project.before %} - {% set compteur = compteur + 1 %} - {% if compteur > bycolonne %} -
-
- {% set compteur = 1 %} - {% endif %} - consommé le {{ month.monthlabel }} = {{ month.duration }}
- {% endfor %} -
-
-
- - {% if not project.beforeastreinte is empty %} -

ASTREINTE

- {% set compteur = 0 %} -
- {% for month in project.beforeastreinte %} - consommé le {{ month.monthlabel }} = {{ month.duration }}
- {% endfor %} -
- {% endif %}
 
{% for month in project.months %} @@ -221,10 +189,37 @@ {% endif %} -
 
+ {% endfor %} + +

CUMUL HEBDOMADAIRE

+ + {% for year, weeks in project.weeks %} + + + {% for week in weeks %} + + {% endfor %} + + + + {% for week in weeks %} + + {% endfor %} + + {% endfor %} +
+ {{ year}} + + S{{ week.weeknumber}} +
+ + + {{ week.cumul}} +
+
 
{% if not project.offers is empty %}

COMMANDES

{% set count=(project.offers|length)-8 %} @@ -334,7 +329,64 @@
 
{% endif %} +

CONSOMMATION PASSEE

+ {% if project.hors!=0 %} + Consommation précédente totale = {{ project.hors }}
+ {% endif %} + + + {% for year in project.before %} + + + {% for month in year %} + + {% endfor %} + + + + {% for month in year %} + + {% endfor %} + + {% endfor %} +
+ Date + + {{ month.monthlabel }} +
+ Consommé + + {{ month.duration }} +
+ + {% if not project.beforeastreinte is empty %} +

ASTREINTES PASSEES

+ + {% for year in project.beforeastreinte %} + + + {% for month in year %} + + {% endfor %} + + + + {% for month in year %} + + {% endfor %} + + {% endfor %} +
+ Date + + {{ month.monthlabel }} +
+ Consommé + + {{ month.duration }} +
+ {% endif %}
{% endif %} diff --git a/src/schedule-2.0/templates/Report/synthese.html.twig b/src/schedule-2.0/templates/Report/synthese.html.twig index ab428a4..f758afe 100644 --- a/src/schedule-2.0/templates/Report/synthese.html.twig +++ b/src/schedule-2.0/templates/Report/synthese.html.twig @@ -66,6 +66,7 @@
+ @@ -78,8 +79,8 @@ {% for event in user.events %} {% if nbday==1 %} + {% endif %} - "); + + var startStopLink = $( + "" + + "" + + ); + startStopLink.click(function() { + switch (task.getState()) { + case Task.State.STOPPED: + task.start(); + break; + case Task.State.RUNNING: + task.stop(); + break; + default: + throw "Invalid task state." + } + saveTasks(); + return false; + }); + + var resetLink = $( + "" + + "" + ); + resetLink.click(function() { + task.reset(); + saveTasks(); + return false; + }); + + var deleteLink = $( + "" + + "" + + ); + deleteLink.click(function() { + if (confirm("Do you really want to delete task \"" + task.getName() + "\"?")) { + tasks.remove(index); + saveTasks(); + } + return false; + }); + + var saveLink = $( + "" + + "" + ); + saveLink.click(function() { + tasks.remove(index); + saveTasks(); + task.save(task); + return false; + }); + result + .addClass("state-" + task.getState()) + .append($("
S L M M
{{event.date|date("W")}}
{{ event.date | date("d/m") }}
{% if event.allday %} diff --git a/src/schedule-2.0/templates/Timer/edit.html.twig b/src/schedule-2.0/templates/Timer/edit.html.twig new file mode 100644 index 0000000..e4d44be --- /dev/null +++ b/src/schedule-2.0/templates/Timer/edit.html.twig @@ -0,0 +1,112 @@ +{% extends "base.html.twig" %} + +{% block localstyle %} + td { + padding:5px !important; + } +{% endblock %} + +{% block body %} +{{ form_start(form) }} +

+ {% if mode=="update" %} + Modification TIMER + {% elseif mode=="submit" %} + Création TIMER + {% endif %} +

+ + + {{ form_widget(form.submit) }} + + Annuler + + {% if mode=="update" %} + + + Supprimer + + {% endif %} + +

+ + {% if app.session.flashbag.has('error') %} +
+ Erreur
+ {% for flashMessage in app.session.flashbag.get('error') %} + {{ flashMessage }}
+ {% endfor %} +
+ {% endif %} + + {% if app.session.flashbag.has('notice') %} +
+ Information
+ {% for flashMessage in app.session.flashbag.get('notice') %} + {{ flashMessage }}
+ {% endfor %} +
+ {% endif %} +
+
+ Informations +
+ +
+ {{ form_row(form.task) }} + {{ form_row(form.description) }} + {{ form_row(form.activepenalty) }} + {{ form_row(form.additionalhour) }} + {{ form_row(form.start) }} + {{ form_row(form.end) }} + {{ form_row(form.duration) }} +
+
+{{ form_end(form) }} + + +{% endblock %} + +{% block localjavascript %} +$("#timer_task").addClass("select2entity"); +/* + * Pads this string with another string on the left until the resulting string + * has specified length. If the padding string has more than one character, the + * resulting string may be longer than desired (the padding string is not + * truncated and it is only prepended as a whole). Bad API, I know, but it's + * good enough for me. + */ +String.prototype.pad = function(length, padding) { + var result = this; + while (result.length < length) { + result = padding + result; + } + return result; +} +/* Some time constants. */ +var MILISECONDS_IN_SECOND = 1000; +var MILISECONDS_IN_MINUTE = 60 * MILISECONDS_IN_SECOND; +var MINUTES_IN_HOUR = 60; + +/* Formats the time in the H:MM format. */ +function formatTime(time) { + var timeInMinutes = time / MILISECONDS_IN_MINUTE; + var hours = Math.floor(timeInMinutes / MINUTES_IN_HOUR); + var minutes = Math.floor(timeInMinutes - hours * MINUTES_IN_HOUR); + return String(hours).pad(2, "0") + ":" + String(minutes).pad(2, "0"); +} + +$("#timer_start_time,#timer_end_time").on('input',function(){ + console.log($("#timer_start_date").val() +"T"+$("#timer_start_time").val()+":00Z") + console.log($("#timer_end_date").val()+"T"+$("#timer_end_time").val()+":00Z") + var start = Date.parse($("#timer_start_date").val() +"T"+$("#timer_start_time").val()+":00Z"); + var end = Date.parse($("#timer_end_date").val()+"T"+$("#timer_end_time").val()+":00Z"); + var diff = end - start; + $("#timer_duration").val(formatTime(diff)) +}) + +{% endblock %} + diff --git a/src/schedule-2.0/templates/Timer/list.cal.html.twig b/src/schedule-2.0/templates/Timer/list.cal.html.twig new file mode 100644 index 0000000..ea7b27f --- /dev/null +++ b/src/schedule-2.0/templates/Timer/list.cal.html.twig @@ -0,0 +1,186 @@ +{% extends "base.html.twig" %} + +{% block head_style %} + {{ encore_entry_link_tags('app') }} + {{ encore_entry_link_tags('fullcalendar') }} +{% endblock head_style %} + +{% block localstyle %} + .fc-header-toolbar h2 { + text-transform: uppercase; + } + + .fc-day-grid-event { + padding:0px; + border-radius:0px; + border: none; + } + .fc-content { + height: 40px; + } + + .fc-title { + font-weight: bolder; + font-size: 12px; + } + + .eventAvatar { + width: 20px; + margin: 0px 5px 0px 0px; + float: left; + } + + .eventInfo{ + margin: -5px 5px 0px 0px; + clear: both; + } + + .eventUser{ + clear: both; + } + + .eventEstimate { + margin: -3px 10px; + } +{% endblock %} + +{% block body %} +
+ + + {{ encore_entry_script_tags('fullcalendar') }} +{% endblock %} + + + + +{% block localjavascript %} + +$(document).ready(function() { + $("#modalsubmit #user").select2({ + theme: 'bootstrap4', + language: "fr" + }); + + $("#modalsubmit #task").select2({ + placeholder: "Selectionnez un projet", + allowClear: true, + theme: 'bootstrap4', + language: "fr" + }); + + $("#modalupdate #user").select2({ + theme: 'bootstrap4', + language: "fr" + }); + + $("#modalupdate #task").select2({ + placeholder: "Selectionnez un projet", + theme: 'bootstrap4', + language: "fr" + }); +}); + +// Rendu d'un évenement +function eventRender(info) { + console.log(info.event.extendedProps); + // Récupération des divers élements du rendu event + var content=$(info.el).children('.fc-content'); + var title=$(content).children('.fc-title'); + + // Ajouter l'avatar + content.prepend(""); + content.append(""+info.event.extendedProps.username+""); + var eventInfo=$(content).children('.eventUser'); + + // Ajout container + content.append(""); + var eventInfo=$(content).children('.eventInfo'); + + // Ajouter le verrou si event non editable + if(info.event.extendedProps.locked) { + eventInfo.append(""); + } + + // Ajout estimation + eventInfo.append(""+info.event.extendedProps.estimate+""); + + // Description + content.attr("title",info.event.extendedProps.fulldescription); +} + +// Formulaire Création d'un évelement +var allDay; +function eventSelect(selectionInfo) { + var start=moment(selectionInfo.start); + var end=moment(selectionInfo.end); + var end=end.subtract(1, 'd'); + allDay=(start.format("DD/MM/YYYY") != end.format("DD/MM/YYYY")); + + // Controle + if(start.month()!=end.month()) { + alert("Une tâche ne peut être sur deux mois différents"); + return false; + } + + if(start.week()!=end.week()) { + alert("Une tâche ne peut être sur deux semaines différentes"); + return false; + } + + // Valeur par défaut + {% if (is_granted('ROLE_ADMIN') or is_granted('ROLE_VALIDATOR') or is_granted('ROLE_MASTER')) and app.session.get('iduser')!="all" %} + $('#usersubmit').val({{app.session.get('iduser')}}).trigger("change"); + {% else %} + $('#usersubmit').val({{app.user.id}}).trigger("change"); + {% endif %} + + // Si jour de fin un samedi ou un dimanche : on est forcement en astreinte + if(moment(end).day()==0||moment(end).day()==6) { + $("#modalsubmit #astreinte").prop("checked",true); + $("#modalsubmit #astreinte").attr("disabled",true); + allDay=true; + } + else { + $("#modalsubmit #astreinte").prop('checked', false); + $("#modalsubmit #astreinte").attr('disabled', false); + } + + $('#modalsubmit #amsubmit').prop("checked",true); + $('#modalsubmit #apsubmit').prop("checked",true); + $('#modalsubmit #amsubmit').attr("disabled",allDay); + $('#modalsubmit #apsubmit').attr("disabled",allDay); + + $('#modalsubmit #start').val(start.format("YYYY-MM-DD")); + $('#modalsubmit #end').val(end.format("YYYY-MM-DD")); + + $('#modalsubmit #description').val(""); + + $("#modalsubmit .alert").remove(); + + // Formulaire de création d'un évènement + $('#modalsubmit').modal(); +} + + + +// On change astreinte +$("#astreinte").change(function() { + console.log(allDay) + if(this.checked) { + $("#amsubmit").prop("disabled",true); + $("#apsubmit").prop("disabled",true); + + $('#modalsubmit #amsubmit').prop("checked",true); + $('#modalsubmit #apsubmit').prop("checked",true); + } + else { + $("#amsubmit").prop("disabled",allDay); + $("#apsubmit").prop("disabled",allDay); + + $('#modalsubmit #amsubmit').prop("checked",true); + $('#modalsubmit #apsubmit').prop("checked",true); + } +}); + +{% endblock %} \ No newline at end of file diff --git a/src/schedule-2.0/templates/Timer/list.html.twig b/src/schedule-2.0/templates/Timer/list.html.twig new file mode 100644 index 0000000..f829542 --- /dev/null +++ b/src/schedule-2.0/templates/Timer/list.html.twig @@ -0,0 +1,710 @@ +{% extends "base.html.twig" %} + +{% block localstyle %} +#timer-task { + width:300px; + } +#timer-desc { + width:300px; +} +{% endblock %} + +{% block body %} +

+SUIVI HORAIRE +

+
+ +
+ + Créer un Timer + {% if user %} + Lancer un Timer : + + + + + +{% else %} + Veuillez choisir un Intervenant pour lancer un Timer. +{% endif %} +
+ + +
+ +
+ +
+
+ +Voir le calendrier +

+
+ + + + + + + + + + + + + {%for timer in timers %} + + + + + + + + + {% endfor %} + +
TâcheDescriptionDébutFinDuréeActions
{{ timer.task.displayname }} + + + {{ timer.activepenalty ? " Astreinte active" : "" }} + + + {{ timer.additionalhour ? " Heures supplémentaires" : "" }} + +

{{ timer.description }}

+
{{ timer.start|date("d/m/Y H:i") }}{{ timer.end|date("d/m/Y H:i") }}{{ timer.duration|date("H:i") }} + + + +    + + + +
+
+{% endblock %} + +{% block localjavascript %} + + +/* Creates a new Task object. */ +function Task(id ,name, description) { + this._name = name; + this._taskid = id; + this._description = description; + this._state = Task.State.STOPPED; + this._timeSpentInPreviousIterations = 0; + this._startDate = 0; + this._endDate = 0; + this._currentIterationStartTime = 0; + this._customTimeSpent = 0; + this._onChange = null; +} + +/* Possible task states. */ +Task.State = { + STOPPED: "stopped", + RUNNING: "running" +} + +Task.prototype = { + /* Returns the task name. */ + getName: function() { + return this._name; + }, + + /* Returns the task description. */ + getDescription: function() { + if (this._description == "NaN") { + this._description = "" + } + return this._description; + }, + + /* Returns the task state. */ + getState: function() { + return this._state; + }, + + /* Is the task stopped? */ + isStopped: function() { + return this._state == Task.State.STOPPED; + }, + + /* Is the task running? */ + isRunning: function() { + return this._state == Task.State.RUNNING; + }, + + /* + * Sets the "onChange" event handler. The "onChange" event is fired when the + * task is started, stopped, or reset. + */ + setOnChange: function(onChange) { + this._onChange = onChange; + }, + + /* + * Returns the time spent on the task in the current work iteration. Works + * correctly only when the task is running. + */ + _getCurrentIterationTime: function() { + return (new Date).getTime() - this._currentIterationStartTime; + }, + + /* + * Returns the total time spent on the task. This includes time spent in + * the current work iteration if the task is running. + */ + getTimeSpent: function() { + var result = this._timeSpentInPreviousIterations; + if (this._state == Task.State.RUNNING) { + result += this._getCurrentIterationTime(); + } + return result; + }, + + /* Calls the "onChange" event handler if set. */ + _callOnChange: function() { + if (typeof this._onChange == "function") { + this._onChange(); + } + }, + + /* Starts a new task work iteration. */ + start: function() { + if (this._state == Task.State.RUNNING) { return }; + if (this._startDate == 0) {this._startDate = new Date()} + this._state = Task.State.RUNNING; + this._currentIterationStartTime = (new Date).getTime(); + this._callOnChange(); + }, + + /* Stops the current task work iteration. */ + stop: function() { + if (this._state == Task.State.STOPPED) { return }; + + this._state = Task.State.STOPPED; + this._timeSpentInPreviousIterations += this._getCurrentIterationTime(); + this._currentIterationStartTime = 0; + this._endDate = new Date(); + this._callOnChange(); + }, + + /* Stops the current task work iteration and resets the time data. */ + reset: function() { + this.stop(); + this._timeSpentInPreviousIterations = 0; + this._callOnChange(); + }, + save: function(task){ + console.log(task) + + $.ajax({ + type: "POST", + data: { + taskname: task._name, + taskid: task._taskid, + description: task._description, + start: task._startDate, + end: task._endDate, + duration: task._timeSpentInPreviousIterations, + }, + url: "{{ path('app_timer_create') }}", + success: function (response) { + response=JSON.parse(response); + if(response.return=="KO") { + $("#dataTable_wrapper").append("
"+response.error+"
"); + }else{ + location.reload(); + } + } + }); + }, + /* Serializes the task into a string. */ + serialize: function() { + /* + * Originally, I wanted to use "toSource" and "eval" for serialization and + * deserialization, but "toSource" is not supported by WebKit, so I resorted + * to ugly hackery... + */ + return [ + encodeURIComponent(this._name), + this._state, + this._timeSpentInPreviousIterations, + this._currentIterationStartTime, + this._startDate, + this._endDate, + this._taskid, + this._description, + ].join("&"); + }, + + /* Deserializes the task from a string. */ + deserialize: function(serialized) { + var parts = serialized.split("&"); + this._name = decodeURIComponent(parts[0]); + this._state = parts[1]; + this._timeSpentInPreviousIterations = parseInt(parts[2]); + this._currentIterationStartTime = parseInt(parts[3]); + this._startDate = parseInt(parts[4]); + this._endDate = parseInt(parts[5]); + this._taskid = parseInt(parts[6]); + this._description = parseInt(parts[7]); + + } +} + +/* ===== Tasks ===== */ + +/* The Tasks class represents a list of tasks. */ + +/* Creates a new Tasks object. */ +function Tasks() { + this._tasks = []; + + this._onAdd = null; + this._onRemove = null; +} + +Tasks.prototype = { + /* + * Sets the "onAdd" event handler. The "onAdd" event is fired when a task + * is added to the list. + */ + setOnAdd: function(onAdd) { + this._onAdd = onAdd; + }, + + /* + * Sets the "onRemove" event handler. The "onRemove" event is fired when a + * task is removed from the list. + */ + setOnRemove: function(onRemove) { + this._onRemove = onRemove; + }, + + /* Returns the length of the task list. */ + length: function() { + return this._tasks.length + }, + + /* + * Returns index-th task in the list, or "undefined" if the index is out of + * bounds. + */ + get: function(index) { + return this._tasks[index]; + }, + + /* + * Calls the callback function for each task in the list. The function is + * called with three parameters - the task, its index and the task list + * object. This is modeled after "Array.forEach" in JavaScript 1.6. + */ + forEach: function(callback) { + for (var i = 0; i < this._tasks.length; i++) { + callback(this._tasks[i], i, this); + } + }, + + /* Calls the "onAdd" event handler if set. */ + _callOnAdd: function(task) { + if (typeof this._onAdd == "function") { + this._onAdd(task); + } + }, + + /* Adds a new task to the end of the list. */ + add: function(task) { + this._tasks.push(task); + this._callOnAdd(task); + }, + + /* Calls the "onRemove" event handler if set. */ + _callOnRemove: function(index) { + if (typeof this._onRemove == "function") { + this._onRemove(index); + } + }, + + /* + * Removes index-th task from the list. Does not do anything if the index + * is out of bounds. + */ + remove: function(index) { + this._callOnRemove(index); + this._tasks.splice(index, 1); + }, + + + + /* Serializes the list of tasks into a string. */ + serialize: function() { + var serializedTasks = []; + this.forEach(function(task) { + serializedTasks.push(task.serialize()); + }); + return serializedTasks.join("|"); + }, + + /* Deserializes the list of tasks from a string. */ + deserialize: function(serialized) { + /* + * Repeatedly use "remove" so the "onRemove" event is triggered. Do the same + * with the "add" method below. + */ + while (this._tasks.length > 0) { + this.remove(0); + } + + var serializedTasks = serialized.split("|"); + for (var i = 0; i < serializedTasks.length; i++) { + var task = new Task(0 ,"", ""); + task.deserialize(serializedTasks[i]); + this.add(task); + } + } +} + +/* ===== Extensions ===== */ + +/* + * Pads this string with another string on the left until the resulting string + * has specified length. If the padding string has more than one character, the + * resulting string may be longer than desired (the padding string is not + * truncated and it is only prepended as a whole). Bad API, I know, but it's + * good enough for me. + */ +String.prototype.pad = function(length, padding) { + var result = this; + while (result.length < length) { + result = padding + result; + } + return result; +} + +/* ===== Task List + DOM Storage ===== */ + +/* The list of tasks. */ +var tasks = new Tasks(); + +/* The last value of the serialized task list string. */ +var lastSerializedTasksString; + +/* + * The key under which the serialized task list string is stored in the DOM + * Storage. + */ +var TASKS_DOM_STORAGE_KEY = "timerTasks"; + +/* + * Returns DOM Storage used by the application, or "null" if the browser does + * not support DOM Storage. + */ +function getStorage() { + /* + * HTML 5 says that the persistent storage is available in the + * "window.localStorage" attribute, however Firefox implements older version + * of the proposal, which uses "window.globalStorage" indexed by the domain + * name. We deal with this situation here as gracefully as possible (i.e. + * without concrete browser detection and with forward compatibility). + * + * For more information, see: + * + * http://www.whatwg.org/specs/web-apps/current-work/#storage + * https://developer.mozilla.org/En/DOM/Storage + */ + if (window.localStorage !== undefined) { + return window.localStorage; + } else if (window.globalStorage !== undefined) { + return window.globalStorage[location.hostname]; + } else { + return null; + } +} + +/* + * Saves the task list into a DOM Storage. Also updates the value of the + * "lastSerializedTasksString" variable. + */ +function saveTasks() { + var serializedTasksString = tasks.serialize(); + getStorage()[TASKS_DOM_STORAGE_KEY] = serializedTasksString; + lastSerializedTasksString = serializedTasksString; +} + +/* + * Loads the serialized task list string from the DOM Storage. Returns + * "undefined" if the storage does not contain the string (this happens when + * running the application for the first time). + */ +function loadSerializedTasksString() { + var storedValue = getStorage()[TASKS_DOM_STORAGE_KEY]; + /* + * The spec says "null" should be returned when the key is not found, but some + * browsers return "undefined" instead. Maybe it was in some earlier version + * of the spec (I didn't bother to check). + */ + if (storedValue !== null && storedValue !== undefined && storedValue.length > 0) { + /* + * The values retrieved from "globalStorage" use one more level of + * indirection. + */ + return (window.localStorage === undefined) ? storedValue.value : storedValue; + } else { + return undefined; + } +} + +/* + * Loads the task list from the DOM Storage. Also updates the value of the + * "lastSerializedTasksString" variable. + */ +function loadTasks() { + var serializedTasksString = loadSerializedTasksString(); + if (serializedTasksString !== undefined) { + tasks.deserialize(serializedTasksString); + lastSerializedTasksString = serializedTasksString; + } +} + +/* + * Was the task list changed outside of the application? Detects the change + * by comparing the current serialized task list string in the DOM Storage + * with a kept old value. + */ +function tasksHaveChangedOutsideApplication() { + var serializedTasksString = loadSerializedTasksString(); + if (serializedTasksString != lastSerializedTasksString) { + return true + } + return false; +} + +/* ===== View ===== */ + +/* Some time constants. */ +var MILISECONDS_IN_SECOND = 1000; +var MILISECONDS_IN_MINUTE = 60 * MILISECONDS_IN_SECOND; +var MINUTES_IN_HOUR = 60; + +/* Formats the time in the H:MM format. */ +function formatTime(time) { + var timeInMinutes = time / MILISECONDS_IN_MINUTE; + var hours = Math.floor(timeInMinutes / MINUTES_IN_HOUR); + var minutes = Math.floor(timeInMinutes - hours * MINUTES_IN_HOUR); + return hours + ":" + String(minutes).pad(2, "0"); +} + +/* + * Computes the URL of the image in the start/stop link according to the task + * state. + */ +function computeStartStopLinkImageUrl(state) { + switch (state) { + case Task.State.STOPPED: + return "fa-play"; + case Task.State.RUNNING: + return "fa-stop"; + default: + throw "Invalid task state." + } +} + +/* + * Builds the HTML element of the row in the task table corresponding to the + * specified task and index. + */ +function buildTaskRow(task, index) { + var result = $("
").text(task.getName())) + .append($("").text(task.getDescription())) + .append($("").text(formatTime(task.getTimeSpent()))) + .append($("") + .append(startStopLink) + .append("  ") + .append(resetLink) + .append("  ") + .append(saveLink) + .append("    ") + .append(deleteLink) + ); + + return result; +} + +/* Finds row with the specified index in the task table. */ +function findRowWithIndex(index) { + return $("#task-table").find("tr").slice(1).eq(index); +} + +/* + * Updates the row in the task table corresponding to a task according to + * its state. + */ +function updateTaskRow(row, task) { + if (task.isStopped()) { + row.removeClass("state-running"); + row.addClass("state-stopped"); + } else if (task.isRunning()) { + row.removeClass("state-stopped"); + row.addClass("state-running"); + } + + row.find(".task-time").text(formatTime(task.getTimeSpent())) + + row.find(".start-stop-link i").removeClass(); + row.find(".start-stop-link i").addClass("fa "+computeStartStopLinkImageUrl(task.getState())); +} + +/* ===== Initialization ===== */ + +/* Initializes event handlers on the task list. */ +function initializeTasksEventHandlers() { + tasks.setOnAdd(function(task) { + var row = buildTaskRow(task, tasks.length() - 1); + $("#task-table").append(row); + task.setOnChange(function() { + updateTaskRow(row, task); + }); + }); + + tasks.setOnRemove(function(index) { + findRowWithIndex(index).remove(); + if (tasks.length() == 1 ) { + $( "#task-table" ).css({"display":"none"}); + } + }); +} + +/* Initializes GUI event handlers. */ +function initializeGuiEventHandlers() { + $( "#addtimer" ).click(function() { + displayTaskAdd(); + }); + + $( "#timer-desc" ).keypress(function( event ) { + if ( event.which == 13 ) { + event.preventDefault(); + displayTaskAdd(); + } + }); + +} +function displayTaskAdd() { + $( "#task-table" ).css({"display":"inline"}); + var taskName = $("#timer-task option:selected").text(); + var taskId = $("#timer-task option:selected").val(); + var taskDesc = $("#timer-desc").val(); + + var task = new Task(taskId, taskName, taskDesc); + tasks.add(task); + saveTasks(); + $("#timer-desc").val(""); +} +/* + * Initializes a timer used to update task times and detect changes in the + * data stored in the DOM Storage. + */ +function initializeTimer() { + setInterval(function() { + tasks.forEach(function(task, index) { + updateTaskRow(findRowWithIndex(index), task); + }); + + if (tasksHaveChangedOutsideApplication()) { + loadTasks(); + } + }, 10 * MILISECONDS_IN_SECOND); +} + +/* Initializes the application. */ +$(document).ready(function(){ + try { + if (!getStorage()) { + alert("Timer requires a browser with DOM Storage support, such as Firefox 3+ or Safari 4+."); + return; + } + } catch (e) { + alert("Timer does not work with file: URLs in Firefox."); + return; + } + + initializeTasksEventHandlers(); + loadTasks(); + initializeGuiEventHandlers(); + initializeTimer(); +}); + + + + + +{% endblock %} + + diff --git a/src/schedule-2.0/templates/User/edit.html.twig b/src/schedule-2.0/templates/User/edit.html.twig index 3bbfbdd..07ae6ad 100755 --- a/src/schedule-2.0/templates/User/edit.html.twig +++ b/src/schedule-2.0/templates/User/edit.html.twig @@ -94,7 +94,33 @@ {{ form_row(form.roles) }} {%endif%} - + +

+
+
+ Affectations +
+ +
+ + + + + + + + + {% for userproject in user.userprojects %} + + + + + {% endfor %} + +
ProjetRôle
{{userproject.project.displayname}}{{userproject.job.name}}
+ +
+
{{ form_end(form) }} diff --git a/src/schedule-2.0/templates/Validation/validation.html.twig b/src/schedule-2.0/templates/Validation/validation.html.twig index ff71c90..f22f5d3 100644 --- a/src/schedule-2.0/templates/Validation/validation.html.twig +++ b/src/schedule-2.0/templates/Validation/validation.html.twig @@ -68,6 +68,7 @@ + @@ -89,6 +90,7 @@ {% set btnvalidate=true %} {%endif%} + {% endif %}
S L M M{{event.date|date("W")}} diff --git a/src/schedule-2.0/templates/Validation/validationtimer.html.twig b/src/schedule-2.0/templates/Validation/validationtimer.html.twig new file mode 100644 index 0000000..b70af14 --- /dev/null +++ b/src/schedule-2.0/templates/Validation/validationtimer.html.twig @@ -0,0 +1,163 @@ +{% extends "base.html.twig" %} + +{% block localstyle %} + {% if fgprint is defined and fgprint %} + table { font-size:10px;} + th,td { + border: 1px solid #37474F; + } + thead { + display: table-header-group; + } + tr { page-break-inside: avoid; } + .homecard {width: 100% } + {%endif%} + +{% endblock %} + +{% block body %} +

+ VALIDATION HORAIRES +

+ +
+ + +
+
+ +
+
+
+ {% if not app.session.get('activetimer') %} + Horaires à Dévalider + {% else %} + Horaires à Valider + {% endif %} +
+ +
+
+ + + + + + + + + + + + {% for user in users %} + {% for timer in user.timers %} + + + + + + + + + + + + + + + + {% endfor %} + {% endfor %} +
UtilisateurTâcheDescriptionDébutFinDurée
+ {% if timer.validate %} + + {% else %} + + {% endif %} + + {{ user.user.displayname }} + + {{ timer.taskname }} + + + {{ timer.activepenalty ? " Astreinte active" : "" }} + + + {{ timer.additionalhour ? " Heures supplémentaires" : "" }} + +

{{ timer.description }}

+
+ {{ timer.start|date("d/m/Y H:i") }} + + {{ timer.end|date("d/m/Y H:i") }} + + {{ timer.duration|date("H:i") }} +
+
+
+
+ +{% endblock %} + +{% block localjavascript %} + $(document).ready(function() { + {% if not fgprint is defined or not fgprint %} + $('.table ').DataTable({ + columnDefs: [ { "targets": "no-sort", "orderable": false }, { "targets": "no-string", "type" : "num" } ], + responsive: true, + iDisplayLength: 100, + order: [[ 1, "asc" ]] + }); + {%else%} + $('#dataTables').removeClass("table table-striped table-bordered table-hover small dataTable no-footer"); + {% endif %} + }); + + function myprint() { + href=document.location.href; + document.location.href=href+"?fgprint=true"; + } + + function validate(id) { + $.ajax({ + type: "POST", + data: { + id: id, + }, + url: "{{ path('app_validationtimer_validate') }}", + success: function (response) { + response=JSON.parse(response); + if(response.return=="KO") { + alert(response.error); + } + else { + $("#row-"+id).remove(); + } + } + }); + } + + function devalidate(id) { + $.ajax({ + type: "POST", + data: { + id: id, + }, + url: "{{ path('app_validationtimer_devalidate') }}", + success: function (response) { + response=JSON.parse(response); + if(response.return=="KO") { + alert(response.error); + } + else { + $("#row-"+id).remove(); + } + } + }); + } + + $('#switchactive').change(function() { + window.location="{{ path('app_validationtimer_activetimer' )}}"; + }); + +{% endblock %} diff --git a/src/schedule-2.0/templates/base.html.twig b/src/schedule-2.0/templates/base.html.twig index 0ba44a0..c6c61e5 100644 --- a/src/schedule-2.0/templates/base.html.twig +++ b/src/schedule-2.0/templates/base.html.twig @@ -256,7 +256,7 @@ {% set contentsidebar="contentsidebar" %} - + + + +
+ +
- + {% for user in app.session.get('users') %} {% set selected="" %} {%if user.id==app.session.get('iduser') %} - {% set selected="selected" %} + {% set selected="selected" %} {% endif %} {% endfor %} @@ -313,23 +330,7 @@ {% endfor %}
- - - -
- -
+
  • @@ -359,9 +360,21 @@ -
  • +
  • + + Suivi Horaire + +
  • + +
  • - Mes Congés + Mes Congés + +
  • + +
  • + + Exports
  • @@ -372,15 +385,21 @@
  • - Validation + Validation Calendrier + +
  • + +
  • + + Validation Congés
  • - - Validation Congés + + Validation Horaires -
  • + {% endif %} @@ -401,7 +420,7 @@
  • - Propositions + Commandes
  • diff --git a/tmpl/schedule-env.local b/tmpl/schedule-env.local index 31f3ab6..49afc0f 100644 --- a/tmpl/schedule-env.local +++ b/tmpl/schedule-env.local @@ -4,6 +4,12 @@ APP_SECRET=%%pwdreader("","/var/www/html/schedule/.key") APP_AUTH=CAS +# MAIL sendmail / smtp +MAILER_METHOD=sendmail +MAILER_URL= +MAILER_NOREPLY=noreply@noreply.fr +MAILER_DEFAULT_NOTIF=%%getVar('schedule_email_global_notif', '') + # Bdd = Redefine local DATABASE_NAME=schedule DATABASE_USER=schedule @@ -29,4 +35,7 @@ DATABASE_HOST=%%adresse_ip_mysql # CAS = Redefine local CAS_HOST=%%eolesso_adresse CAS_PORT=%%eolesso_port -CAS_PATH=%%eolesso_cas_folder \ No newline at end of file +CAS_PATH=%%eolesso_cas_folder + +OFFICE_HOUR_START=09:00 +OFFICE_HOUR_END=17:30 \ No newline at end of file