diff --git a/composer.json b/composer.json index 09111ba..afc0a60 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,9 @@ "type": "project", "require": { "catfan/medoo": "^1.2", - "guzzlehttp/guzzle": "^6.2" + "guzzlehttp/guzzle": "^6.2", + "league/csv": "^9.1", + "lapinator/ods-php-generator": "^0.0.3" }, "license": "OTHER", "authors": [ diff --git a/composer.lock b/composer.lock index 1896297..f55f26e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,8 @@ "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": "1c8b61c5d506ae016285b99b20040cf0", + "hash": "809fda99e7cc89e7857f8a5a7bb0bc78", + "content-hash": "b8ba52c94c62ef0abbe09903ec2cdca8", "packages": [ { "name": "catfan/medoo", @@ -63,7 +64,7 @@ "sql", "sqlite" ], - "time": "2017-05-22T04:39:48+00:00" + "time": "2017-05-22 04:39:48" }, { "name": "guzzlehttp/guzzle", @@ -125,7 +126,7 @@ "rest", "web service" ], - "time": "2017-02-28T22:50:30+00:00" + "time": "2017-02-28 22:50:30" }, { "name": "guzzlehttp/promises", @@ -176,7 +177,7 @@ "keywords": [ "promise" ], - "time": "2016-12-20T10:07:11+00:00" + "time": "2016-12-20 10:07:11" }, { "name": "guzzlehttp/psr7", @@ -241,7 +242,114 @@ "uri", "url" ], - "time": "2017-03-20T17:10:46+00:00" + "time": "2017-03-20 17:10:46" + }, + { + "name": "lapinator/ods-php-generator", + "version": "v0.0.3", + "source": { + "type": "git", + "url": "https://github.com/Lapinator/odsPhpGenerator.git", + "reference": "575314c003c2ec3032813bedcc1d27032b7b7ab2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Lapinator/odsPhpGenerator/zipball/575314c003c2ec3032813bedcc1d27032b7b7ab2", + "reference": "575314c003c2ec3032813bedcc1d27032b7b7ab2", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Laurent VUIBERT", + "email": "lapinator@gmx.fr", + "homepage": "http://lapinator.net", + "role": "Developer" + } + ], + "description": "Open Document Spreadsheet (.ods) generator ", + "homepage": "https://odsphpgenerator.lapinator.net/", + "keywords": [ + "LibreOffice", + "ods" + ], + "time": "2016-04-14 21:51:27" + }, + { + "name": "league/csv", + "version": "9.1.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/csv.git", + "reference": "bfa3aa8e755377cd6cc2242cfd074e48c7c300ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/bfa3aa8e755377cd6cc2242cfd074e48c7c300ba", + "reference": "bfa3aa8e755377cd6cc2242cfd074e48c7c300ba", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=7.0.10" + }, + "require-dev": { + "ext-curl": "*", + "friendsofphp/php-cs-fixer": "^2.0", + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Csv\\": "src" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://github.com/nyamsprod/", + "role": "Developer" + } + ], + "description": "Csv data manipulation made easy in PHP", + "homepage": "http://csv.thephpleague.com", + "keywords": [ + "csv", + "export", + "filter", + "import", + "read", + "write" + ], + "time": "2017-10-20 08:03:26" }, { "name": "psr/http-message", @@ -291,7 +399,7 @@ "request", "response" ], - "time": "2016-08-06T14:39:51+00:00" + "time": "2016-08-06 14:39:51" } ], "packages-dev": [], diff --git a/database.mwb b/database.mwb index 323e7fb..2d9040b 100644 Binary files a/database.mwb and b/database.mwb differ diff --git a/lang/en_us.php b/lang/en_us.php index 6d29d23..47a583a 100644 --- a/lang/en_us.php +++ b/lang/en_us.php @@ -77,4 +77,17 @@ define("STRINGS", [ "add" => "Add", "choose a shift" => "Choose a shift", "shift assigned" => "Shift assigned.", + "report export" => "Reports/Export", + "report type" => "Report type", + "format" => "Format", + "generate report" => "Generate report", + "choose an option" => "Choose an option", + "csv file" => "CSV text file", + "ods file" => "ODS spreadsheet", + "html file" => "HTML web page", + "report filtered to" => "Report filtered to {name} ({username})", + "all users" => "All users", + "one user" => "One user", + "choose user" => "Type to choose user", + "filter" => "Filter" ]); \ No newline at end of file diff --git a/lib/reports.php b/lib/reports.php new file mode 100644 index 0000000..d70e2b8 --- /dev/null +++ b/lib/reports.php @@ -0,0 +1,212 @@ +has('report_access_codes', ["AND" => ['code' => $VARS['code'], 'expires[>]' => $date]])) { + dieifnotloggedin(); + } +} else { + dieifnotloggedin(); +} + +// Delete old DB entries +$database->delete('report_access_codes', ['expires[<=]' => $date]); + +if (LOADED) { + $user = null; + require_once __DIR__ . "/userinfo.php"; + require_once __DIR__ . "/login.php"; + if ($VARS['users'] != "all" && !is_empty($VARS['user']) && user_exists($VARS['user'])) { + $user = getUserByUsername($VARS['user']); + } + if (isset($VARS['type']) && isset($VARS['format'])) { + generateReport($VARS['type'], $VARS['format'], $user); + die(); + } else { + lang("invalid parameters"); + die(); + } +} + +function getShiftReport($user = null) { + global $database; + if ($user != null && array_key_exists('uid', $user)) { + $shifts = $database->select( + "shifts", [ + "[>]assigned_shifts" => ["shiftid" => "shiftid"] + ], [ + "shifts.shiftid", "shiftname", "start", "end", "days" + ], [ + "uid" => $user['uid'] + ] + ); + } else { + $shifts = $database->select( + "shifts", [ + "shiftid", "shiftname", "start", "end", "days" + ] + ); + } + $header = [lang("shiftid", false), lang("shift name", false), lang("start", false), lang("end", false), lang("workers", false), lang("sunday", false), lang("monday", false), lang("tuesday", false), lang("wednesday", false), lang("thursday", false), lang("friday", false), lang("saturday", false)]; + $out = [$header]; + for ($i = 0; $i < count($shifts); $i++) { + $daycodes = str_split($shifts[$i]['days'], 2); + $assigned = $database->count("assigned_shifts", ['shiftid' => $shifts[$i]["shiftid"]]); + $out[] = [ + $shifts[$i]["shiftid"], + $shifts[$i]["shiftname"], + date(TIME_FORMAT, strtotime($shifts[$i]['start'])), + date(TIME_FORMAT, strtotime($shifts[$i]['end'])), + $assigned . "", + (in_array("Su", $daycodes) == true ? "Y" : "N"), + (in_array("Mo", $daycodes) == true ? "Y" : "N"), + (in_array("Tu", $daycodes) == true ? "Y" : "N"), + (in_array("We", $daycodes) == true ? "Y" : "N"), + (in_array("Th", $daycodes) == true ? "Y" : "N"), + (in_array("Fr", $daycodes) == true ? "Y" : "N"), + (in_array("Sa", $daycodes) == true ? "Y" : "N") + ]; + } + return $out; +} + +function getReportData($type, $user = null) { + switch ($type) { + case "shifts": + return getShiftReport($user); + break; + default: + return [["error"]]; + } +} + +function dataToCSV($data, $name = "report", $user = null) { + $csv = Writer::createFromString(''); + $usernotice = ""; + $usertitle = ""; + if ($user != null && array_key_exists('username', $user) && array_key_exists('name', $user)) { + $usernotice = lang2("report filtered to", ["name" => $user['name'], "username" => $user['username']], false); + $usertitle = "_" . $user['username']; + $csv->insertOne([$usernotice]); + } + $csv->insertAll($data); + header('Content-type: text/csv'); + header('Content-Disposition: attachment; filename="' . $name . $usertitle . "_" . date("Y-m-d_Hi") . ".csv" . '"'); + echo $csv; + die(); +} + +function dataToODS($data, $name = "report", $user = null) { + $ods = new ods(); + $styleColumn = new odsStyleTableColumn(); + $styleColumn->setUseOptimalColumnWidth(true); + $headerstyle = new odsStyleTableCell(); + $headerstyle->setFontWeight("bold"); + $table = new odsTable($name); + + for ($i = 0; $i < count($data[0]); $i++) { + $table->addTableColumn(new odsTableColumn($styleColumn)); + } + + $usernotice = ""; + $usertitle = ""; + if ($user != null && array_key_exists('username', $user) && array_key_exists('name', $user)) { + $usernotice = lang2("report filtered to", ["name" => $user['name'], "username" => $user['username']], false); + $usertitle = "_" . $user['username']; + $row = new odsTableRow(); + $row->addCell(new odsTableCellString($usernotice)); + $table->addRow($row); + } + + $rowid = 0; + foreach ($data as $datarow) { + $row = new odsTableRow(); + foreach ($datarow as $cell) { + if ($rowid == 0) { + $row->addCell(new odsTableCellString($cell, $headerstyle)); + } else { + $row->addCell(new odsTableCellString($cell)); + } + } + $table->addRow($row); + $rowid++; + } + $ods->addTable($table); + $ods->downloadOdsFile($name . $usertitle . "_" . date("Y-m-d_Hi") . ".ods"); +} + +function dataToHTML($data, $name = "report", $user = null) { + global $SECURE_NONCE; + // HTML exporter doesn't like null values + for ($i = 0; $i < count($data); $i++) { + for ($j = 0; $j < count($data[$i]); $j++) { + if (is_null($data[$i][$j])) { + $data[$i][$j] = ''; + } + } + } + $usernotice = ""; + $usertitle = ""; + if ($user != null && array_key_exists('username', $user) && array_key_exists('name', $user)) { + $usernotice = "" . lang2("report filtered to", ["name" => $user['name'], "username" => $user['username']], false) . "
"; + $usertitle = "_" . $user['username']; + } + header('Content-type: text/html'); + $converter = new HTMLConverter(); + $out = "\n" + . "\n" + . "\n" + . "" . $name . $usertitle . "_" . date("Y-m-d_Hi") . "\n" + . << +STYLE + . $usernotice + . $converter->convert($data); + echo $out; +} + +function generateReport($type, $format, $user = null) { + $data = getReportData($type, $user); + switch ($format) { + case "ods": + dataToODS($data, $type, $user); + break; + case "html": + dataToHTML($data, $type, $user); + break; + case "csv": + default: + echo dataToCSV($data, $type, $user); + break; + } +} diff --git a/pages.php b/pages.php index 416643d..7e1dca4 100644 --- a/pages.php +++ b/pages.php @@ -51,6 +51,18 @@ define("PAGES", [ "static/js/addshift.js" ] ], + "export" => [ + "title" => "report export", + "navbar" => true, + "icon" => "download", + "styles" => [ + "static/css/easy-autocomplete.min.css" + ], + "scripts" => [ + "static/js/jquery.easy-autocomplete.min.js", + "static/js/export.js" + ] + ], "assignshift" => [ "title" => "assign shift", "navbar" => false, diff --git a/pages/export.php b/pages/export.php new file mode 100644 index 0000000..744bc78 --- /dev/null +++ b/pages/export.php @@ -0,0 +1,48 @@ + + +
+
+
+ + +
+
+ +
+ +
+
+ +
+
+
+ + +
+
+
+ insert('report_access_codes', ['code' => $code, 'expires' => date("Y-m-d H:i:s", strtotime("+5 minutes"))]); + ?> + + + +
\ No newline at end of file diff --git a/static/css/app.css b/static/css/app.css index 7049ef4..ba45b88 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -78,6 +78,10 @@ margin-left: 10px; } +#user-box { + display: inline-block; +} + /* ============================== THEMING diff --git a/static/js/export.js b/static/js/export.js new file mode 100644 index 0000000..07d3329 --- /dev/null +++ b/static/js/export.js @@ -0,0 +1,31 @@ +$("#genrptbtn").click(function () { + setTimeout(function () { + window.location.reload(); + }, 1000) +}); + +var options = { + url: "action.php", + ajaxSettings: { + dataType: "json", + method: "GET", + data: { + action: "autocomplete_user" + } + }, + preparePostData: function (data) { + data.q = $("#user-box").val(); + return data; + }, + getValue: function (element) { + return element.username; + }, + template: { + type: "custom", + method: function (value, item) { + return item.name + " " + item.username + ""; + } + } +}; + +$("#user-box").easyAutocomplete(options); \ No newline at end of file