From 5823ec15e2da1066670be175dd7df59df7ca0131 Mon Sep 17 00:00:00 2001 From: Skylar Ittner Date: Tue, 11 Dec 2018 21:29:10 -0700 Subject: [PATCH] Add member list and expiring member spreadsheets (close #9, close #14) --- composer.json | 4 +- composer.lock | 112 ++++++++++++++++++++++++++++++++++- langs/en/labels.json | 10 +++- lib/Report.lib.php | 137 +++++++++++++++++++++++++++++++++++++++++++ lib/reports.php | 120 +++++++++++++++++++++++++++++++++++++ pages/reports.php | 57 ++++++++++++++---- 6 files changed, 426 insertions(+), 14 deletions(-) create mode 100644 lib/Report.lib.php create mode 100644 lib/reports.php diff --git a/composer.json b/composer.json index 5622da4..d223e90 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,9 @@ "stripe/stripe-php": "^6.24", "dompdf/dompdf": "^0.8.2", "phpoffice/phpword": "^0.15.0", - "phpmailer/phpmailer": "^6.0" + "phpmailer/phpmailer": "^6.0", + "lapinator/ods-php-generator": "^0.0.3", + "league/csv": "^9.1" }, "license": "MPL-2.0", "authors": [ diff --git a/composer.lock b/composer.lock index 213b3dc..873beb1 100644 --- a/composer.lock +++ b/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": "fc511c5441078b0d99375ed15cbe16a0", + "content-hash": "a0ad7932ffc1d635a64f0cba686125ba", "packages": [ { "name": "catfan/medoo", @@ -308,6 +308,116 @@ ], "time": "2017-03-20T17:10:46+00:00" }, + { + "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-14T21:51:27+00:00" + }, + { + "name": "league/csv", + "version": "9.1.4", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/csv.git", + "reference": "9c8ad06fb5d747c149875beb6133566c00eaa481" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/9c8ad06fb5d747c149875beb6133566c00eaa481", + "reference": "9c8ad06fb5d747c149875beb6133566c00eaa481", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=7.0.10" + }, + "require-dev": { + "ext-curl": "*", + "friendsofphp/php-cs-fixer": "^2.0", + "phpstan/phpstan": "^0.9.2", + "phpstan/phpstan-phpunit": "^0.9.4", + "phpstan/phpstan-strict-rules": "^0.9.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": "2018-05-01T18:32:48+00:00" + }, { "name": "pclzip/pclzip", "version": "2.8.2", diff --git a/langs/en/labels.json b/langs/en/labels.json index 6c2aefe..5c8a93b 100644 --- a/langs/en/labels.json +++ b/langs/en/labels.json @@ -16,6 +16,7 @@ "Newsletter": "Newsletter", "Print": "Print", "Email and Print": "Email and Print", + "Email+Print": "Email+Print", "Children": "Children", "Born": "Born", "Graduated": "Graduated", @@ -29,9 +30,13 @@ "Event": "Event", "Download Reports": "Download Reports", "Member Directory": "Member Directory", + "Expiring Memberships": "Expiring Memberships", + "Family List": "Family List", "Office (ODT)": "Office (ODT)", + "Spreadsheet (ODS)": "Spreadsheet (ODS)", "HTML": "HTML", "DOCX": "DOCX", + "CSV": "CSV", "Amount": "Amount", "Date": "Date", "Status": "Status", @@ -46,5 +51,8 @@ "Expires": "Expires", "Choose...": "Choose...", "Mark as paid": "Mark as paid", - "This payment is a membership renewal (automatically add one year to the family's membership)": "This payment is a membership renewal (automatically add one year to the family's membership)" + "This payment is a membership renewal (automatically add one year to the family's membership)": "This payment is a membership renewal (automatically add one year to the family's membership)", + "All members expired or expiring within a month.": "All members expired or expiring within a month.", + "All the data from the member directory in a spreadsheet.": "All the data from the member directory in a spreadsheet.", + "A formatted and up-to-date HACHE member directory.": "A formatted and up-to-date HACHE member directory." } diff --git a/lib/Report.lib.php b/lib/Report.lib.php new file mode 100644 index 0000000..73f1305 --- /dev/null +++ b/lib/Report.lib.php @@ -0,0 +1,137 @@ +title = $title; + $this->header = $header; + $this->data = $data; + } + + public function setHeader(array $header) { + $this->header = $header; + } + + public function addDataRow(array $columns) { + $this->data[] = $columns; + } + + public function getHeader(): array { + return $this->header; + } + + public function getData(): array { + return $this->data; + } + + public function output(string $format) { + switch ($format) { + case "ods": + $this->toODS(); + break; + case "html": + $this->toHTML(); + break; + case "csv": + default: + $this->toCSV(); + break; + } + } + + private function toODS() { + $ods = new ods(); + $styleColumn = new odsStyleTableColumn(); + $styleColumn->setUseOptimalColumnWidth(true); + $headerstyle = new odsStyleTableCell(); + $headerstyle->setFontWeight("bold"); + $table = new odsTable($this->title); + + for ($i = 0; $i < count($this->header); $i++) { + $table->addTableColumn(new odsTableColumn($styleColumn)); + } + + $row = new odsTableRow(); + foreach ($this->header as $cell) { + $row->addCell(new odsTableCellString($cell, $headerstyle)); + } + $table->addRow($row); + + foreach ($this->data as $cols) { + $row = new odsTableRow(); + foreach ($cols as $cell) { + $row->addCell(new odsTableCellString($cell)); + } + $table->addRow($row); + } + $ods->addTable($table); + // The @ is a workaround to silence the tempnam notice, + // which breaks the file. This is apparently the intended behavior: + // https://bugs.php.net/bug.php?id=69489 + @$ods->downloadOdsFile($this->title . "_" . date("Y-m-d_Hi") . ".ods"); + } + + private function toHTML() { + global $SECURE_NONCE; + $data = array_merge([$this->header], $this->data); + // 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] = ''; + } + } + } + header('Content-type: text/html'); + $converter = new HTMLConverter(); + $out = "\n" + . "\n" + . "\n" + . "" . $this->title . "_" . date("Y-m-d_Hi") . "\n" + . << +STYLE + . $converter->convert($data); + echo $out; + } + + private function toCSV() { + $csv = Writer::createFromString(''); + $data = array_merge([$this->header], $this->data); + $csv->insertAll($data); + header('Content-type: text/csv'); + header('Content-Disposition: attachment; filename="' . $this->title . "_" . date("Y-m-d_Hi") . ".csv" . '"'); + echo $csv; + } + +} diff --git a/lib/reports.php b/lib/reports.php new file mode 100644 index 0000000..77e3b8f --- /dev/null +++ b/lib/reports.php @@ -0,0 +1,120 @@ +get("invalid parameters"); + die(); + } +} + +/** + * Get a 2d array of the families in the database. + * @global type $database + * @param array $filter Medoo WHERE clause. + * @return string + */ +function getMemberReport($filter = []): Report { + global $database, $Strings; + + if (empty($filter)) { + $report = new Report($Strings->get("Families", false)); + $filter = ["ORDER" => ["familyname" => "ASC"]]; + } else { + $report = new Report($Strings->get("Expiring Memberships", false)); + } + + $familyids = $database->select( + "families", "familyid", $filter + ); + + $report->setHeader([ + $Strings->get("Name", false), + $Strings->get("Father", false), + $Strings->get("Mother", false), + $Strings->get("Phone", false), + $Strings->get("Email", false), + $Strings->get("Address", false), + $Strings->get("City", false), + $Strings->get("State", false), + $Strings->get("ZIP", false), + $Strings->get("Photo Permission", false), + $Strings->get("Newsletter", false), + $Strings->get("Expires", false), + $Strings->get("Children", false) + ]); + $families = []; + foreach ($familyids as $id) { + $families[] = (new Family())->load($id); + } + foreach ($families as $f) { + $newsletter = ""; + switch ($f->getNewsletter()) { + case 1: + $newsletter = $Strings->get("Email", false); + break; + case 2: + $newsletter = $Strings->get("Print", false); + break; + case 3: + $newsletter = $Strings->get("Email+Print", false); + break; + } + $children = []; + foreach ($f->getChildren() as $c) { + $children[] = $c->getName() . " (" . date("n/d", $c->getBirthday()) . ")"; + } + $report->addDataRow([ + $f->getName(), + $f->getFather(), + $f->getMother(), + $f->getPhone() . "", + $f->getEmail(), + $f->getAddress(), + $f->getCity(), + $f->getState(), + $f->getZip() . "", + $f->getPhotoPermission() ? $Strings->get("Yes", false) : $Strings->get("No", false), + $newsletter, + date("Y-m-d", $f->getExpires()), + implode(", ", $children) + ]); + } + return $report; +} + +function getReport($type): Report { + switch ($type) { + case "members": + return getMemberReport(); + break; + case "expiring": + return getMemberReport(["expires[<]" => date("Y-m-d", strtotime("+1 month")), "ORDER" => ["expires" => "ASC"]]); + break; + default: + return new Report("error", ["ERROR"], ["Invalid report type."]); + } +} + +function generateReport($type, $format) { + $report = getReport($type); + $report->output($format); +} diff --git a/pages/reports.php b/pages/reports.php index 322707f..96904ba 100644 --- a/pages/reports.php +++ b/pages/reports.php @@ -19,16 +19,51 @@ if (!$user->hasPermission("HACHEPORTAL_VIEW")) { get("Download Reports"); ?> -

get("Member Directory"); ?>

- - get("Office (ODT)"); ?> - -
- - get("HTML"); ?> - - - get("DOCX"); ?> - +
+
+

get("Member Directory"); ?>

+

get("A formatted and up-to-date HACHE member directory."); ?>

+ + get("Office (ODT)"); ?> + +
+ + get("HTML"); ?> + + + get("DOCX"); ?> + +
+ +
+

get("Family List"); ?>

+

get("All the data from the member directory in a spreadsheet."); ?>

+ + get("Spreadsheet (ODS)"); ?> + +
+ + get("HTML"); ?> + + + get("CSV"); ?> + +
+ +
+

get("Expiring Memberships"); ?>

+

get("All members expired or expiring within a month."); ?>

+ + get("Spreadsheet (ODS)"); ?> + +
+ + get("HTML"); ?> + + + get("CSV"); ?> + +
+
\ No newline at end of file