From a762e81c72c648d59f8b1b8995e4e15aa893e1d5 Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Wed, 7 Dec 2016 19:05:04 +0000 Subject: [PATCH 01/18] Added new functionality to allow LaTeX generation of invoices. Example template is included. --- extensions/ki_adminpanel/processor.php | 1 + .../templates/scripts/advanced.php | 3 +++ extensions/ki_invoice/init.php | 20 +++++++++++++++++-- extensions/ki_invoice/print.php | 3 ++- language/en.php | 1 + libraries/Kimai/Database/Mysql.php | 1 + 6 files changed, 26 insertions(+), 3 deletions(-) diff --git a/extensions/ki_adminpanel/processor.php b/extensions/ki_adminpanel/processor.php index f184e0dd2..aa0b129ee 100755 --- a/extensions/ki_adminpanel/processor.php +++ b/extensions/ki_adminpanel/processor.php @@ -463,6 +463,7 @@ $config_data['decimalSeparator'] = $_REQUEST['decimalSeparator']; $config_data['durationWithSeconds'] = getRequestBool('durationWithSeconds'); $config_data['exactSums'] = getRequestBool('exactSums'); + $config_data['LaTeXExec'] = $_REQUEST['LaTeX_exec']; $editLimit = false; if (getRequestBool('editLimitEnabled')) { $hours = (int)$_REQUEST['editLimitHours']; diff --git a/extensions/ki_adminpanel/templates/scripts/advanced.php b/extensions/ki_adminpanel/templates/scripts/advanced.php index ce14998e1..6a9e88d54 100755 --- a/extensions/ki_adminpanel/templates/scripts/advanced.php +++ b/extensions/ki_adminpanel/templates/scripts/advanced.php @@ -96,6 +96,9 @@ roundTimesheetEntries): ?> disabled="disabled" > kga['lang']['minutes']?> kga['lang']['and']?> roundTimesheetEntries): ?> disabled="disabled" > kga['lang']['seconds']?> +
+ kga['lang']['LaTeXExecutable']?> +
diff --git a/extensions/ki_invoice/init.php b/extensions/ki_invoice/init.php index f33d14fe0..0e4ff4e4c 100644 --- a/extensions/ki_invoice/init.php +++ b/extensions/ki_invoice/init.php @@ -55,13 +55,29 @@ $allInvoices = glob('invoices/*'); foreach($allInvoices as $tplFile) { - $extension = 'HTML'; + $extension = ''; $tplInfo = pathinfo($tplFile); if (!is_dir($tplFile)) { $extension = strtoupper($tplInfo['extension']); + } else { + //Check if index.hmtl or invoice.tex is there! + if (file_exists($tplFile.'/index.html')) { + $extension = 'HTML'; + } elseif (file_exists($tplFile.'/invoice.tex')) { + $extension = 'LaTeX'; + //Test if we can execute pdflatex + $output = exec($kga['LaTeXExec']); + if(strlen($output) == 0) { + Kimai_Logger::logfile("Could not execute pdflatex. Check your installation!"); + $extension = ''; + } + } } $filename = str_replace('_', ' ', $tplInfo['filename']); - $invoice_template_files[$extension][$tplInfo['basename']] = ucfirst($filename); + //Only add if the extension was detected. + if ($extension != "") { + $invoice_template_files[$extension][$tplInfo['basename']] = ucfirst($filename); + } } $view->assign('invoice_templates', $invoice_template_files); diff --git a/extensions/ki_invoice/print.php b/extensions/ki_invoice/print.php index 02f43d368..3e0fecf94 100644 --- a/extensions/ki_invoice/print.php +++ b/extensions/ki_invoice/print.php @@ -168,7 +168,8 @@ $renderers = array( 'odt' => new Kimai_Invoice_OdtRenderer(), 'html' => new Kimai_Invoice_HtmlRenderer(), - 'pdf' => new Kimai_Invoice_HtmlToPdfRenderer() + 'pdf' => new Kimai_Invoice_HtmlToPdfRenderer(), + 'LaTeX' => new Kimai_Invoice_LaTeXRenderer() ); /* @var $renderer Kimai_Invoice_AbstractRenderer */ diff --git a/language/en.php b/language/en.php index 7b358a502..98ede69de 100644 --- a/language/en.php +++ b/language/en.php @@ -257,6 +257,7 @@ "roundTimesheetEntries" => "Round new timesheet entries to", "minutes" => "Minutes", "seconds" => "Seconds", + "LaTeXExecutable" => "Full path to LaTeX executable, i.e., 'pdflatex'.", "and" => "and", "customerlogin" => "customer login", "expense" => "Expense", diff --git a/libraries/Kimai/Database/Mysql.php b/libraries/Kimai/Database/Mysql.php index 6d9834b57..b85da5e94 100644 --- a/libraries/Kimai/Database/Mysql.php +++ b/libraries/Kimai/Database/Mysql.php @@ -2932,6 +2932,7 @@ public function initializeConfig(Kimai_Config $config) // break is not here on purpose! case 'adminmail': + case 'LaTeXExec': case 'loginTries': case 'loginBanTime': case 'currency_name': From bb903f6013020d01c988c98713e6feff404068b6 Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Wed, 7 Dec 2016 19:10:57 +0000 Subject: [PATCH 02/18] Added example template for LaTeX invoice. --- .../invoices/my_company_LaTeX/invoice.cls | 182 ++++++++++++++++++ .../invoices/my_company_LaTeX/invoice.ini | 60 ++++++ .../invoices/my_company_LaTeX/invoice.tex | 84 ++++++++ .../invoices/my_company_LaTeX/logo.png | Bin 0 -> 11158 bytes 4 files changed, 326 insertions(+) create mode 100644 extensions/ki_invoice/invoices/my_company_LaTeX/invoice.cls create mode 100644 extensions/ki_invoice/invoices/my_company_LaTeX/invoice.ini create mode 100644 extensions/ki_invoice/invoices/my_company_LaTeX/invoice.tex create mode 100644 extensions/ki_invoice/invoices/my_company_LaTeX/logo.png diff --git a/extensions/ki_invoice/invoices/my_company_LaTeX/invoice.cls b/extensions/ki_invoice/invoices/my_company_LaTeX/invoice.cls new file mode 100644 index 000000000..7b23ffe09 --- /dev/null +++ b/extensions/ki_invoice/invoices/my_company_LaTeX/invoice.cls @@ -0,0 +1,182 @@ +\NeedsTeXFormat{LaTeX2e} +\ProvidesClass{invoice}[2016/12/07 v1.0 Invoice for kimai] + +%% +% This file is part of +% Kimai - Open Source Time Tracking // http://www.kimai.org +% (c) Kimai-Development-Team since 2006 +% +% Kimai is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; Version 3, 29 June 2007 +% +% Kimai is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Kimai; If not, see . +%% + +%% +% An example LaTeX class template for printing invoices. +% +% @author Gustav Johansson +%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Load standard document type % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\LoadClass[10pt,a4paper]{article} +\RequirePackage{fancyhdr} +\RequirePackage{graphicx} +\RequirePackage{longtable} +\RequirePackage{lastpage} +\RequirePackage{hhline} +\RequirePackage{etoolbox} +\RequirePackage{multirow} +\RequirePackage{numprint} +\RequirePackage[table]{xcolor} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Define template controls % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%These can be redefined in the template for different languages, etc. +\def\textTitle{\textbf{\textsf{Invoice}}} +\def\textPage{Page} +\def\textDate{Date:} +\def\textDueDate{Due Date:} +\def\textTotal{Total:} +\def\textID{Invoice ID:} +\def\textPhone{Phone: } +\def\textEmail{Email: } +\def\textTotalExVAT{\textbf{Total (excl. VAT)}} +\def\textVAT{\textbf{VAT \vatRate{}\%}} +\def\textGTotal{\textbf{Total (incl. VAT)}} +\def\rulerWidth{0.4pt} +\def\headerOne{Product} +\def\headerTwo{Price} +\def\headerThree{Quantity} +\def\headerFour{Total} +\def\headerColor{gray!50} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Define commands % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%Define product command +\newcommand*{\@products}{} +\def\rowCol{gray!25} +\newcounter{colCount} +\setcounter{colCount}{0} +\newcommand*{\productColor}[4]{% + \protected@edef\@products{\@products + \cellcolor{\rowCol}#1\unitOne & \cellcolor{\rowCol}#2\unitTwo &% + \cellcolor{\rowCol}% + \ifstrempty{#3}{}{\numprint{#3}\unitThree}% + & \cellcolor{\rowCol}\numprint{#4}\unitFour\\ + }% +} +\newcommand*{\productNoColor}[4]{% + \protected@edef\@products{\@products + #1\unitOne & #2\unitTwo &% + \ifstrempty{#3}{}{\numprint{#3}\unitThree}% + & \numprint{#4}\unitFour\\ + }% +} +\newcommand*{\product}[4]{% + \ifnumequal{\thecolCount}{1}{% + \productColor{#1}{#2}{#3}{#4} + \addtocounter{colCount}{-1}% + }{% + \productNoColor{#1}{#2}{#3}{#4} + \addtocounter{colCount}{1}% + }% +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Define printing of commands % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%Print company info +\newcommand{\makecompany}{% + \noindent + \companyName\\ + \companyAddress\\ + \textPhone\companyPhone\\ + \textEmail\companyEmail\\ + \par\noindent + \textID\space\invoiceID\\ + \par\noindent +} + +%Print products into a table +\newcommand{\makeproducts}{% + \nprounddigits{2} + \begin{longtable}{@{}p{0.5\textwidth}rrr@{}} + \rowcolor{\headerColor} + \headerOne&\headerTwo&\headerThree&\headerFour\\ + \@products% + \hhline{~~--} + &&\multicolumn{1}{l}{\textTotalExVAT}&\numprint{\total}\space\currency\\ + &&\multicolumn{1}{l}{\textVAT}&\numprint{\vat}\space\currency\\ + &&\multicolumn{1}{l}{\textGTotal}&\numprint{\gtotal}\space\currency + \end{longtable} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Setup the headers and footers % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\pagestyle{fancy} +\fancyhf{}%Clear all settings +%Set the rulers +\renewcommand{\headrulewidth}{\rulerWidth} +\renewcommand{\footrulewidth}{\rulerWidth} +%Setup header +\fancyhead[L]{% + \hspace*{0.2cm}\includegraphics[width=4.0cm]{logo.png}% +} +\fancyhead[C]{% + \raisebox{1.7cm}{% + \large{\textTitle}% + } +} +\fancyhead[R]{% + \raisebox{0.5cm}{% + \textsf{% + \begin{tabular}{p{2.0cm}p{3.5cm}}% + \rowcolor{white}% + {\small\textDate} & {\small\today}\tabularnewline% + \rowcolor{white}% + {\small\textID} & {\small\invoiceID}\tabularnewline% + \rowcolor{white}% + \textbf{\small\textDueDate} & \textbf{\duedate}\tabularnewline% + \rowcolor{white}% + {\small\textTotal} & {\numprint{\gtotal}\space\currency}% + \end{tabular}% + }% + }% +} +%Setup footer +\fancyfoot[L]{% + \textsf{% + \leftOne\\% + \leftTwo\\% + \leftThree% + }% +} +\fancyfoot[C]{% + \textsf{% + \centerOne\\% + \centerTwo\\% + \centerThree\\% + }% +} +\fancyfoot[R]{% + \textsf{% + \rightOne\\% + \rightTwo\\% + \rightThree\\% + \textPage\space\thepage(\pageref{LastPage})% + }% +} +\endinput diff --git a/extensions/ki_invoice/invoices/my_company_LaTeX/invoice.ini b/extensions/ki_invoice/invoices/my_company_LaTeX/invoice.ini new file mode 100644 index 000000000..6c11a9950 --- /dev/null +++ b/extensions/ki_invoice/invoices/my_company_LaTeX/invoice.ini @@ -0,0 +1,60 @@ +;; +; This file is part of +; Kimai - Open Source Time Tracking // http://www.kimai.org +; (c) Kimai-Development-Team since 2006 +; +; Kimai is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; Version 3, 29 June 2007 +; +; Kimai is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with Kimai; If not, see . +;; + +;; +; An example LaTeX setting file for printing invoices. +; +; @author Gustav Johansson +;; + +; This configuration file is used to define some settings +; that defines how the template is rendered. + +; List all files that are needed for rendering except the +; invoice.tex and the automatically generated data.tex and +; info.tex. Typically classes and images. +files[] = "invoice.cls" +files[] = "logo.png" + +; The followin keys can be used in the table generation, i.e., for each row: +; 'type' +; 'desc' +; 'start' +; 'end' +; 'hour' +; 'fDuration' +; 'duration' +; 'timestamp' +; 'amount' +; 'description' +; 'rate' +; 'comment' +; 'username' +; 'useralias' +; 'location' +; 'trackingNr' +; 'projectID' +; 'projectName' +; 'projectComment' +; 'date' + +; List all fields that shall be used as columns in the generated table. +table[] = "desc" +table[] = "rate" +table[] = "hour" +table[] = "amount" diff --git a/extensions/ki_invoice/invoices/my_company_LaTeX/invoice.tex b/extensions/ki_invoice/invoices/my_company_LaTeX/invoice.tex new file mode 100644 index 000000000..259bcfdc0 --- /dev/null +++ b/extensions/ki_invoice/invoices/my_company_LaTeX/invoice.tex @@ -0,0 +1,84 @@ +\documentclass{invoice} + +%% +% This file is part of +% Kimai - Open Source Time Tracking // http://www.kimai.org +% (c) Kimai-Development-Team since 2006 +% +% Kimai is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; Version 3, 29 June 2007 +% +% Kimai is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Kimai; If not, see . +%% + +%% +% An example LaTeX template for printing invoices. +% +% @author Gustav Johansson +%% + +%%%%%%%%%%%%%%%%%%%%%% +% Setup other stuff % +%%%%%%%%%%%%%%%%%%%%%% +%Language +\usepackage[utf8]{inputenc} %Needed since the data from PHP is in utf8. Don't use any other encoding! + +%%%%%%%%%%%%%%%%%%%%%% +% Setup page % +%%%%%%%%%%%%%%%%%%%%%% +\setlength{\topmargin}{-2cm} +\setlength{\textwidth}{16cm} +\setlength{\textheight}{22cm} +\setlength{\oddsidemargin}{5.5mm} +\setlength{\intextsep}{24pt} +\setlength{\headheight}{3cm} +\setlength{\headwidth}{16cm} + +%%%%%%%%%%%%%%%%%%%%%% +% Setup footer % +%%%%%%%%%%%%%%%%%%%%%% +\def\leftOne{Test company AB} +\def\leftTwo{Fake address 33} +\def\leftThree{SE-123 45 Gotham City} +\def\centerOne{Telephone: +99 99 999 9999} +\def\centerTwo{E-mail: contact@batman.robin} +\def\centerThree{Web: www.batman.robin} +\def\rightOne{Org. nr: 123456-7890} +\def\rightTwo{Bankgiro: 123-1234} +\def\rightThree{Approved for tax.} + +%%%%%%%%%%%%%%%%%%%%%% +% Setup table units % +%%%%%%%%%%%%%%%%%%%%%% +\def\unitOne{} +\def\unitTwo{} +\def\unitThree{\space h} +\def\unitFour{\space\currency} + +%%%%%%%%%%%%%%%%%%%%%% +% Start document % +%%%%%%%%%%%%%%%%%%%%%% +\input{info.tex} +\pagestyle{fancy}% +\thispagestyle{fancy}% +\begin{document} + \sffamily% + %Process company info + \makecompany + %Write any text you like before the table + The following invoice is for services performed from \startDate{} through +\endDate{} under agreement 11111 between \leftOne{} and \companyName. + %Load data + \input{data.tex} + %Alternating colors in table +% \rowcolors{2}{gray!25}{white}% + %Create the table + \makeproducts +\end{document} diff --git a/extensions/ki_invoice/invoices/my_company_LaTeX/logo.png b/extensions/ki_invoice/invoices/my_company_LaTeX/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ed241acc2dcaf478a29602b0149502edb698c4ea GIT binary patch literal 11158 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 literal 0 HcmV?d00001 From 3e5b42a6d1fb4e216d188fed3b474ae1d2ede141 Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Thu, 8 Dec 2016 15:48:47 +0000 Subject: [PATCH 03/18] Added check if exec is allowed in the system. Changed the way execution of the pdflatex is tested. --- extensions/ki_adminpanel/init.php | 8 ++++++++ extensions/ki_adminpanel/templates/scripts/advanced.php | 6 +++++- extensions/ki_invoice/init.php | 3 +-- language/en.php | 1 + 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/extensions/ki_adminpanel/init.php b/extensions/ki_adminpanel/init.php index fb991a756..c79e33e35 100755 --- a/extensions/ki_adminpanel/init.php +++ b/extensions/ki_adminpanel/init.php @@ -121,6 +121,14 @@ $view->assign('roundSeconds', $kga['conf']['roundSeconds']); } + if(!function_exists('exec')) { + // deactivate latex functionality + Kimai_Logger::logfile("Cannot execute external files. LaTeX invoices will be disabled."); + $view->assign('canExecute', false); + } else { + $view->assign('canExecute', true); + } + $view->assign('tab_advanced', $view->render("advanced.php")); $view->assign('tab_database', $view->render("database.php")); } diff --git a/extensions/ki_adminpanel/templates/scripts/advanced.php b/extensions/ki_adminpanel/templates/scripts/advanced.php index 6a9e88d54..0490cac20 100755 --- a/extensions/ki_adminpanel/templates/scripts/advanced.php +++ b/extensions/ki_adminpanel/templates/scripts/advanced.php @@ -97,7 +97,11 @@ roundTimesheetEntries): ?> disabled="disabled" > kga['lang']['seconds']?>
- kga['lang']['LaTeXExecutable']?> + canExecute): ?> + kga['lang']['LaTeXExecutable']?> + + kga['lang']['cannotExecute']?> +
diff --git a/extensions/ki_invoice/init.php b/extensions/ki_invoice/init.php index 0e4ff4e4c..7c4555ac4 100644 --- a/extensions/ki_invoice/init.php +++ b/extensions/ki_invoice/init.php @@ -66,8 +66,7 @@ } elseif (file_exists($tplFile.'/invoice.tex')) { $extension = 'LaTeX'; //Test if we can execute pdflatex - $output = exec($kga['LaTeXExec']); - if(strlen($output) == 0) { + if(!function_exists('exec') or !is_executable($kga['LaTeXExec'])) { Kimai_Logger::logfile("Could not execute pdflatex. Check your installation!"); $extension = ''; } diff --git a/language/en.php b/language/en.php index 98ede69de..d72493091 100644 --- a/language/en.php +++ b/language/en.php @@ -258,6 +258,7 @@ "minutes" => "Minutes", "seconds" => "Seconds", "LaTeXExecutable" => "Full path to LaTeX executable, i.e., 'pdflatex'.", + "cannotExecute" => "Your system does not allow execution of external files. LaTeX invoice generation will be disabled.", "and" => "and", "customerlogin" => "customer login", "expense" => "Expense", From da943126e054cb2f6864c48fcd49066d364a9166 Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Thu, 8 Dec 2016 23:21:02 +0000 Subject: [PATCH 04/18] Change 'canExecute' to 'execAvaiable'. Less misleading. --- extensions/ki_adminpanel/init.php | 4 ++-- extensions/ki_adminpanel/templates/scripts/advanced.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/ki_adminpanel/init.php b/extensions/ki_adminpanel/init.php index c79e33e35..7b13a0d10 100755 --- a/extensions/ki_adminpanel/init.php +++ b/extensions/ki_adminpanel/init.php @@ -124,9 +124,9 @@ if(!function_exists('exec')) { // deactivate latex functionality Kimai_Logger::logfile("Cannot execute external files. LaTeX invoices will be disabled."); - $view->assign('canExecute', false); + $view->assign('execAvailable', false); } else { - $view->assign('canExecute', true); + $view->assign('execAvailable', true); } $view->assign('tab_advanced', $view->render("advanced.php")); diff --git a/extensions/ki_adminpanel/templates/scripts/advanced.php b/extensions/ki_adminpanel/templates/scripts/advanced.php index 0490cac20..0a753eb58 100755 --- a/extensions/ki_adminpanel/templates/scripts/advanced.php +++ b/extensions/ki_adminpanel/templates/scripts/advanced.php @@ -97,7 +97,7 @@ roundTimesheetEntries): ?> disabled="disabled" > kga['lang']['seconds']?>
- canExecute): ?> + execAvailable): ?> kga['lang']['LaTeXExecutable']?> kga['lang']['cannotExecute']?> From cea423a280c88089751685d53b1e663fccda7a51 Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Fri, 9 Dec 2016 00:31:27 +0000 Subject: [PATCH 05/18] Added LaTeXRenderer.php and Checksum.php --- libraries/Kimai/Invoice/Checksum.php | 87 +++++++++++++ libraries/Kimai/Invoice/LaTeXRenderer.php | 150 ++++++++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 libraries/Kimai/Invoice/Checksum.php create mode 100644 libraries/Kimai/Invoice/LaTeXRenderer.php diff --git a/libraries/Kimai/Invoice/Checksum.php b/libraries/Kimai/Invoice/Checksum.php new file mode 100644 index 000000000..fbfff1a29 --- /dev/null +++ b/libraries/Kimai/Invoice/Checksum.php @@ -0,0 +1,87 @@ +. + */ + +/** + * This file calculates a checksum for the invoice number. + * Feel free to add more checksum functions as needed in different countries. + * + * @author Gustav Johansson + */ + +function checksum($type, $id, $args) { + switch ($type) { + case 'OCR': + return OCR($id, $args); + break; + } +} + + +function OCR($id, $addLength = True) { +/** + * Calculates the checksum with length number according to the swedish OCR + * system. I.e., 123456 will have a length number added to it (including the + * length number itself and a checksum digit. The return invoice id will be + * a valid OCR-number: 12345682 where the next to last digit is the total + * length and the last digit is the checksum. + */ + //Check length. Max is 25 including checksum and length no. + if ($addLength) { + $max = 23; + } else { + $max = 24; + } + if (strlen($id) > $max){ + return -1; + } + + //Calculate the length number (only last digit) + $len = (strlen($id) + 2) % 10; + if ($addLength) { + $invoice = $id.$len; + } else { + $invoice = $id; + } + + //Calculate checksum + $inReverse = array_reverse(str_split($invoice)); + $sum = 0; + $even = True; + foreach ($inReverse as $num){ + if ($even) { + $even = False; + $tmp = $num * 2; + if ($tmp > 9) { + $tmp = $tmp - 9; + } + $sum = $sum + $tmp; + } else { + $even = True; + $sum = $sum + $num; + } + } + $check = 10 - ($sum % 10); + //Make sure we use 0 and not 10 + if ($check == 10) { + $check = 0; + } + $checksum = $invoice.$check; + return $checksum; +} +?> \ No newline at end of file diff --git a/libraries/Kimai/Invoice/LaTeXRenderer.php b/libraries/Kimai/Invoice/LaTeXRenderer.php new file mode 100644 index 000000000..801bec191 --- /dev/null +++ b/libraries/Kimai/Invoice/LaTeXRenderer.php @@ -0,0 +1,150 @@ +. + */ + +/** + * Class for rendering LaTeX invoices templates as PDF. + * + * @author Gustav Johansson + */ +class Kimai_Invoice_LaTeXRenderer extends Kimai_Invoice_AbstractRenderer +{ + const FILE_TEX = 'invoice.tex'; + + /** + * Render the invoice. + * + * @return mixed + */ + public function render() + { + //Setup + Kimai_Logger::logfile("Rendering LaTeX invoice!"); + $vault = $this->getTemporaryDirectory(); + $dateFormat = "%B %e, %Y"; + $templateDir = $this->getTemplateDir().$this->getTemplateFile(); + //Load the ini file + $ini_file = $templateDir."/invoice.ini"; + $ini_array = parse_ini_file($ini_file, true); + // fetch variables from model to get values + $customer = $this->getModel()->getCustomer(); + $projects = $this->getModel()->getProjects(); + $entries = $this->getModel()->getEntries(); + $data = $this->getModel()->toArray(); + //Get global settings + global $kga; + //Create invoiceId + $in = time(); + $invoiceID = date("y",$in).$customer['customerID'].date("m", $in).date("d", $in); + include "Checksum.php"; + $invoiceID = checksum('OCR', $invoiceID, True); + Kimai_Logger::logfile("invoiceID = $invoiceID"); + $this->getModel()->setInvoiceId($invoiceID); + //Get data model + $data = $this->getModel()->toArray(); + Kimai_Logger::logfile("tempdir = ".$vault); + + //Write the header/footer data + $File = $vault."/info.tex"; + $Handle = fopen($File, 'w'); + fwrite($Handle, "\\def\\duedate{".strftime($dateFormat, $data['dueDate'])."}%\n"); + fwrite($Handle, "\\def\\total{".$data['amount']."}%\n"); + fwrite($Handle, "\\def\\invoiceID{".$data['invoiceId']."}%\n"); + fwrite($Handle, "\\def\\currency{".$data['currencySign']."}%\n"); + fwrite($Handle, "\\def\\companyName{".$customer['company']."}%\n"); + fwrite($Handle, "\\def\\companyAddress{".$customer['street']."\\\\".$customer['zipcode']." ".$customer['city']."}%\n"); + fwrite($Handle, "\\def\\companyPhone{".$customer['phone']."}%\n"); + fwrite($Handle, "\\def\\companyEmail{".$customer['mail']."}%\n"); + fwrite($Handle, "\\def\\comment{".$customer['comment']."}%\n"); + fwrite($Handle, "\\def\\startDate{".strftime($dateFormat, $data['beginDate'])."}%\n"); + fwrite($Handle, "\\def\\endDate{".strftime($dateFormat, $data['endDate'])."}%\n"); + fwrite($Handle, "\\def\\vatRate{".$data['vatRate']."}%\n"); + fwrite($Handle, "\\def\\vat{".$data['vat']."}%\n"); + fwrite($Handle, "\\def\\gtotal{".$data['total']."}%\n"); + fwrite($Handle, "\\endinput"); + fclose($Handle); + + //Write the table + $File = $vault."/data.tex"; + $Handle = fopen($File, "w"); + foreach($entries as $row){ + $table_row = "\product"; + foreach($ini_array['table'] as $index) { + $table_row = $table_row."{".$row[$index]."}"; + } + $table_row = $table_row."%\n"; + fwrite($Handle, $table_row); + } + fwrite($Handle, "\\endinput"); + fclose($Handle); + + //Copy all the neccessary files to the rendering directory + copy($templateDir."/invoice.tex", $vault."/".$data['invoiceId'].".tex"); + foreach($ini_array['files'] as $file) { + copy($templateDir."/".$file, $vault."/".$file); + } + + //Run pdflatex, throw error if not! + $output = exec("cd ".$vault." && ".$kga['LaTeXExec']." ".$data['invoiceId'].".tex"); + if(strlen($output) == 0) { + Kimai_Logger::logfile("Could not execute pdflatex. Check your installation!"); + return; + } + //Run pdflatex again, throw error if not! + $output = exec("cd ".$vault." && ".$kga['LaTeXExec']." ".$data['invoiceId'].".tex"); + + //Return the rendered file + $this->sendResponse($vault."/".$data['invoiceId'].".pdf"); + } + + /** + * @return pdf + */ + public function sendResponse($data) + { + header('Content-Type: pdf'); + header('Content-Disposition: attachment; filename="'.basename($data).'"'); + header('Content-Length: '.filesize($data)); + Kimai_Logger::logfile("File to send:".$data); + readfile($data); + } + + /** + * @return string + */ + protected function getTemplateFilename() + { + return self::FILE_TEX; + } + + /** + * Returns if the file can be rendered. + * + * @return bool + */ + public function canRender() + { + if (!is_dir($this->getTemplateDir() . $this->getTemplateFile())) { + return false; + } + return ( + is_file($this->getTemplateDir() . $this->getTemplateFile() . DIRECTORY_SEPARATOR . self::FILE_TEX) + ); + } + +} From 8233a193cbb4b2209369c373c313dfc0d78fc196 Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Fri, 9 Dec 2016 19:17:19 +0000 Subject: [PATCH 06/18] Fixed style of files to comply with PSR2. --- libraries/Kimai/Invoice/Checksum.php | 45 ++++++++++++----------- libraries/Kimai/Invoice/LaTeXRenderer.php | 41 ++++++++++----------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/libraries/Kimai/Invoice/Checksum.php b/libraries/Kimai/Invoice/Checksum.php index fbfff1a29..ea49b91af 100644 --- a/libraries/Kimai/Invoice/Checksum.php +++ b/libraries/Kimai/Invoice/Checksum.php @@ -24,7 +24,8 @@ * @author Gustav Johansson */ -function checksum($type, $id, $args) { +function checksum($type, $id, $args) +{ switch ($type) { case 'OCR': return OCR($id, $args); @@ -33,8 +34,9 @@ function checksum($type, $id, $args) { } -function OCR($id, $addLength = True) { -/** +function OCR($id, $addLength = true) +{ + /** * Calculates the checksum with length number according to the swedish OCR * system. I.e., 123456 will have a length number added to it (including the * length number itself and a checksum digit. The return invoice id will be @@ -43,45 +45,44 @@ function OCR($id, $addLength = True) { */ //Check length. Max is 25 including checksum and length no. if ($addLength) { - $max = 23; + $max = 23; } else { - $max = 24; + $max = 24; } - if (strlen($id) > $max){ + if (strlen($id) > $max) { return -1; } //Calculate the length number (only last digit) - $len = (strlen($id) + 2) % 10; + $len = (strlen($id) + 2) % 10; if ($addLength) { - $invoice = $id.$len; + $invoice = $id.$len; } else { - $invoice = $id; + $invoice = $id; } //Calculate checksum - $inReverse = array_reverse(str_split($invoice)); - $sum = 0; - $even = True; - foreach ($inReverse as $num){ + $inReverse = array_reverse(str_split($invoice)); + $sum = 0; + $even = true; + foreach ($inReverse as $num) { if ($even) { - $even = False; - $tmp = $num * 2; + $even = false; + $tmp = $num * 2; if ($tmp > 9) { - $tmp = $tmp - 9; + $tmp = $tmp - 9; } - $sum = $sum + $tmp; + $sum = $sum + $tmp; } else { - $even = True; - $sum = $sum + $num; + $even = true; + $sum = $sum + $num; } } - $check = 10 - ($sum % 10); + $check = 10 - ($sum % 10); //Make sure we use 0 and not 10 if ($check == 10) { $check = 0; } - $checksum = $invoice.$check; + $checksum = $invoice.$check; return $checksum; } -?> \ No newline at end of file diff --git a/libraries/Kimai/Invoice/LaTeXRenderer.php b/libraries/Kimai/Invoice/LaTeXRenderer.php index 801bec191..008874864 100644 --- a/libraries/Kimai/Invoice/LaTeXRenderer.php +++ b/libraries/Kimai/Invoice/LaTeXRenderer.php @@ -50,9 +50,9 @@ public function render() global $kga; //Create invoiceId $in = time(); - $invoiceID = date("y",$in).$customer['customerID'].date("m", $in).date("d", $in); + $invoiceID = date("y", $in).$customer['customerID'].date("m", $in).date("d", $in); include "Checksum.php"; - $invoiceID = checksum('OCR', $invoiceID, True); + $invoiceID = checksum('OCR', $invoiceID, true); Kimai_Logger::logfile("invoiceID = $invoiceID"); $this->getModel()->setInvoiceId($invoiceID); //Get data model @@ -60,48 +60,48 @@ public function render() Kimai_Logger::logfile("tempdir = ".$vault); //Write the header/footer data - $File = $vault."/info.tex"; - $Handle = fopen($File, 'w'); - fwrite($Handle, "\\def\\duedate{".strftime($dateFormat, $data['dueDate'])."}%\n"); + $File = $vault."/info.tex"; + $Handle = fopen($File, 'w'); + fwrite($Handle, "\\def\\duedate{".strftime($dateFormat, $data['dueDate'])."}%\n"); fwrite($Handle, "\\def\\total{".$data['amount']."}%\n"); - fwrite($Handle, "\\def\\invoiceID{".$data['invoiceId']."}%\n"); + fwrite($Handle, "\\def\\invoiceID{".$data['invoiceId']."}%\n"); fwrite($Handle, "\\def\\currency{".$data['currencySign']."}%\n"); fwrite($Handle, "\\def\\companyName{".$customer['company']."}%\n"); - fwrite($Handle, "\\def\\companyAddress{".$customer['street']."\\\\".$customer['zipcode']." ".$customer['city']."}%\n"); + fwrite($Handle, "\\def\\companyAddress{".$customer['street']."\\\\".$customer['zipcode']." ".$customer['city']."}%\n"); fwrite($Handle, "\\def\\companyPhone{".$customer['phone']."}%\n"); - fwrite($Handle, "\\def\\companyEmail{".$customer['mail']."}%\n"); + fwrite($Handle, "\\def\\companyEmail{".$customer['mail']."}%\n"); fwrite($Handle, "\\def\\comment{".$customer['comment']."}%\n"); - fwrite($Handle, "\\def\\startDate{".strftime($dateFormat, $data['beginDate'])."}%\n"); + fwrite($Handle, "\\def\\startDate{".strftime($dateFormat, $data['beginDate'])."}%\n"); fwrite($Handle, "\\def\\endDate{".strftime($dateFormat, $data['endDate'])."}%\n"); - fwrite($Handle, "\\def\\vatRate{".$data['vatRate']."}%\n"); + fwrite($Handle, "\\def\\vatRate{".$data['vatRate']."}%\n"); fwrite($Handle, "\\def\\vat{".$data['vat']."}%\n"); - fwrite($Handle, "\\def\\gtotal{".$data['total']."}%\n"); + fwrite($Handle, "\\def\\gtotal{".$data['total']."}%\n"); fwrite($Handle, "\\endinput"); - fclose($Handle); + fclose($Handle); //Write the table - $File = $vault."/data.tex"; - $Handle = fopen($File, "w"); - foreach($entries as $row){ - $table_row = "\product"; - foreach($ini_array['table'] as $index) { + $File = $vault."/data.tex"; + $Handle = fopen($File, "w"); + foreach ($entries as $row) { + $table_row = "\product"; + foreach ($ini_array['table'] as $index) { $table_row = $table_row."{".$row[$index]."}"; } $table_row = $table_row."%\n"; fwrite($Handle, $table_row); - } + } fwrite($Handle, "\\endinput"); fclose($Handle); //Copy all the neccessary files to the rendering directory copy($templateDir."/invoice.tex", $vault."/".$data['invoiceId'].".tex"); - foreach($ini_array['files'] as $file) { + foreach ($ini_array['files'] as $file) { copy($templateDir."/".$file, $vault."/".$file); } //Run pdflatex, throw error if not! $output = exec("cd ".$vault." && ".$kga['LaTeXExec']." ".$data['invoiceId'].".tex"); - if(strlen($output) == 0) { + if (strlen($output) == 0) { Kimai_Logger::logfile("Could not execute pdflatex. Check your installation!"); return; } @@ -146,5 +146,4 @@ public function canRender() is_file($this->getTemplateDir() . $this->getTemplateFile() . DIRECTORY_SEPARATOR . self::FILE_TEX) ); } - } From 7d322f461884e4c7149aea0cc890dd1eec290d75 Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Fri, 9 Dec 2016 19:23:20 +0000 Subject: [PATCH 07/18] Added download link to help text in admin panel. --- language/en.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language/en.php b/language/en.php index d72493091..10ef9e37c 100644 --- a/language/en.php +++ b/language/en.php @@ -257,7 +257,7 @@ "roundTimesheetEntries" => "Round new timesheet entries to", "minutes" => "Minutes", "seconds" => "Seconds", - "LaTeXExecutable" => "Full path to LaTeX executable, i.e., 'pdflatex'.", + "LaTeXExecutable" => "Full path to LaTeX executable, i.e., 'pdflatex'. For more information, see https://www.ctan.org/", "cannotExecute" => "Your system does not allow execution of external files. LaTeX invoice generation will be disabled.", "and" => "and", "customerlogin" => "customer login", From 7e70e43dcae0f6bebc62790d58a327ff91c37a2d Mon Sep 17 00:00:00 2001 From: Simon Schaufelberger Date: Fri, 9 Dec 2016 22:59:55 +0100 Subject: [PATCH 08/18] Adjust spaces --- extensions/ki_invoice/init.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/extensions/ki_invoice/init.php b/extensions/ki_invoice/init.php index 7c4555ac4..f48e6c0d4 100644 --- a/extensions/ki_invoice/init.php +++ b/extensions/ki_invoice/init.php @@ -53,20 +53,19 @@ // Extract all Invoice Templates in groups $invoice_template_files = array(); $allInvoices = glob('invoices/*'); -foreach($allInvoices as $tplFile) -{ +foreach($allInvoices as $tplFile) { $extension = ''; $tplInfo = pathinfo($tplFile); if (!is_dir($tplFile)) { $extension = strtoupper($tplInfo['extension']); } else { //Check if index.hmtl or invoice.tex is there! - if (file_exists($tplFile.'/index.html')) { + if (file_exists($tplFile . '/index.html')) { $extension = 'HTML'; - } elseif (file_exists($tplFile.'/invoice.tex')) { + } elseif (file_exists($tplFile . '/invoice.tex')) { $extension = 'LaTeX'; //Test if we can execute pdflatex - if(!function_exists('exec') or !is_executable($kga['LaTeXExec'])) { + if (!function_exists('exec') or !is_executable($kga['LaTeXExec'])) { Kimai_Logger::logfile("Could not execute pdflatex. Check your installation!"); $extension = ''; } @@ -74,7 +73,7 @@ } $filename = str_replace('_', ' ', $tplInfo['filename']); //Only add if the extension was detected. - if ($extension != "") { + if ($extension != '') { $invoice_template_files[$extension][$tplInfo['basename']] = ucfirst($filename); } } From a3fd2ff59be69d47b4f8b04c654b56bc2d51248d Mon Sep 17 00:00:00 2001 From: Simon Schaufelberger Date: Sat, 10 Dec 2016 01:07:08 +0100 Subject: [PATCH 09/18] Further code improvements --- libraries/Kimai/Invoice/Checksum.php | 59 ++++++----- libraries/Kimai/Invoice/LaTeXRenderer.php | 121 ++++++++++++---------- 2 files changed, 99 insertions(+), 81 deletions(-) diff --git a/libraries/Kimai/Invoice/Checksum.php b/libraries/Kimai/Invoice/Checksum.php index ea49b91af..71a68d080 100644 --- a/libraries/Kimai/Invoice/Checksum.php +++ b/libraries/Kimai/Invoice/Checksum.php @@ -24,65 +24,76 @@ * @author Gustav Johansson */ +/** + * @param $type + * @param $id + * @param $args + * @return string + */ function checksum($type, $id, $args) { switch ($type) { case 'OCR': return OCR($id, $args); - break; + break; } } - +/** + * @param $id + * @param bool $addLength + * @return int|string + */ function OCR($id, $addLength = true) { /** - * Calculates the checksum with length number according to the swedish OCR - * system. I.e., 123456 will have a length number added to it (including the - * length number itself and a checksum digit. The return invoice id will be - * a valid OCR-number: 12345682 where the next to last digit is the total - * length and the last digit is the checksum. - */ + * Calculates the checksum with length number according to the swedish OCR + * system. I.e., 123456 will have a length number added to it (including the + * length number itself and a checksum digit. The return invoice id will be + * a valid OCR-number: 12345682 where the next to last digit is the total + * length and the last digit is the checksum. + */ //Check length. Max is 25 including checksum and length no. if ($addLength) { - $max = 23; + $max = 23; } else { - $max = 24; + $max = 24; } if (strlen($id) > $max) { return -1; } //Calculate the length number (only last digit) - $len = (strlen($id) + 2) % 10; + $len = (strlen($id) + 2) % 10; if ($addLength) { - $invoice = $id.$len; + $invoice = $id . $len; } else { - $invoice = $id; + $invoice = $id; } //Calculate checksum - $inReverse = array_reverse(str_split($invoice)); - $sum = 0; - $even = true; + $inReverse = array_reverse(str_split($invoice)); + $sum = 0; + $even = true; foreach ($inReverse as $num) { if ($even) { - $even = false; - $tmp = $num * 2; + $even = false; + $tmp = $num * 2; if ($tmp > 9) { - $tmp = $tmp - 9; + $tmp = $tmp - 9; } - $sum = $sum + $tmp; + $sum = $sum + $tmp; } else { - $even = true; - $sum = $sum + $num; + $even = true; + $sum = $sum + $num; } } - $check = 10 - ($sum % 10); + $check = 10 - ($sum % 10); //Make sure we use 0 and not 10 if ($check == 10) { $check = 0; } - $checksum = $invoice.$check; + $checksum = $invoice . $check; + return $checksum; } diff --git a/libraries/Kimai/Invoice/LaTeXRenderer.php b/libraries/Kimai/Invoice/LaTeXRenderer.php index 008874864..5cd7a7fa7 100644 --- a/libraries/Kimai/Invoice/LaTeXRenderer.php +++ b/libraries/Kimai/Invoice/LaTeXRenderer.php @@ -33,95 +33,103 @@ class Kimai_Invoice_LaTeXRenderer extends Kimai_Invoice_AbstractRenderer */ public function render() { - //Setup - Kimai_Logger::logfile("Rendering LaTeX invoice!"); - $vault = $this->getTemporaryDirectory(); - $dateFormat = "%B %e, %Y"; - $templateDir = $this->getTemplateDir().$this->getTemplateFile(); + global $kga; + + Kimai_Logger::logfile('Rendering LaTeX invoice!'); + $tempDir = $this->getTemporaryDirectory(); + $dateFormat = '%B %e, %Y'; + $templateDir = $this->getTemplateDir() . $this->getTemplateFile(); + //Load the ini file - $ini_file = $templateDir."/invoice.ini"; - $ini_array = parse_ini_file($ini_file, true); + $iniFile = $templateDir . '/invoice.ini'; + $iniArray = parse_ini_file($iniFile, true); + // fetch variables from model to get values $customer = $this->getModel()->getCustomer(); $projects = $this->getModel()->getProjects(); $entries = $this->getModel()->getEntries(); $data = $this->getModel()->toArray(); - //Get global settings - global $kga; + //Create invoiceId $in = time(); - $invoiceID = date("y", $in).$customer['customerID'].date("m", $in).date("d", $in); - include "Checksum.php"; - $invoiceID = checksum('OCR', $invoiceID, true); - Kimai_Logger::logfile("invoiceID = $invoiceID"); + $invoiceID = date('y', $in) . $customer['customerID'] . date('m', $in) . date('d', $in); + require_once 'Checksum.php'; + $invoiceID = checksum('OCR', $invoiceID, true); + Kimai_Logger::logfile('invoiceID: ' . $invoiceID); $this->getModel()->setInvoiceId($invoiceID); - //Get data model + $data = $this->getModel()->toArray(); - Kimai_Logger::logfile("tempdir = ".$vault); //Write the header/footer data - $File = $vault."/info.tex"; - $Handle = fopen($File, 'w'); - fwrite($Handle, "\\def\\duedate{".strftime($dateFormat, $data['dueDate'])."}%\n"); - fwrite($Handle, "\\def\\total{".$data['amount']."}%\n"); - fwrite($Handle, "\\def\\invoiceID{".$data['invoiceId']."}%\n"); - fwrite($Handle, "\\def\\currency{".$data['currencySign']."}%\n"); - fwrite($Handle, "\\def\\companyName{".$customer['company']."}%\n"); - fwrite($Handle, "\\def\\companyAddress{".$customer['street']."\\\\".$customer['zipcode']." ".$customer['city']."}%\n"); - fwrite($Handle, "\\def\\companyPhone{".$customer['phone']."}%\n"); - fwrite($Handle, "\\def\\companyEmail{".$customer['mail']."}%\n"); - fwrite($Handle, "\\def\\comment{".$customer['comment']."}%\n"); - fwrite($Handle, "\\def\\startDate{".strftime($dateFormat, $data['beginDate'])."}%\n"); - fwrite($Handle, "\\def\\endDate{".strftime($dateFormat, $data['endDate'])."}%\n"); - fwrite($Handle, "\\def\\vatRate{".$data['vatRate']."}%\n"); - fwrite($Handle, "\\def\\vat{".$data['vat']."}%\n"); - fwrite($Handle, "\\def\\gtotal{".$data['total']."}%\n"); - fwrite($Handle, "\\endinput"); - fclose($Handle); + $file = $tempDir . '/info.tex'; + $handle = fopen($file, 'w'); + $content = ''; + $content .= "\\def\\duedate{" . strftime($dateFormat, $data['dueDate']) . "}%\n"; + $content .= "\\def\\total{" . $data['amount'] . "}%\n"; + $content .= "\\def\\invoiceID{" . $data['invoiceId'] . "}%\n"; + $content .= "\\def\\currency{" . $data['currencySign'] . "}%\n"; + $content .= "\\def\\companyName{" . $customer['company'] . "}%\n"; + $content .= "\\def\\companyAddress{" . $customer['street'] . "\\\\" . $customer['zipcode'] . " " . $customer['city'] . "}%\n"; + $content .= "\\def\\companyPhone{" . $customer['phone'] . "}%\n"; + $content .= "\\def\\companyEmail{" . $customer['mail'] . "}%\n"; + $content .= "\\def\\comment{" . $customer['comment'] . "}%\n"; + $content .= "\\def\\startDate{" . strftime($dateFormat, $data['beginDate']) . "}%\n"; + $content .= "\\def\\endDate{" . strftime($dateFormat, $data['endDate']) . "}%\n"; + $content .= "\\def\\vatRate{" . $data['vatRate'] . "}%\n"; + $content .= "\\def\\vat{" . $data['vat'] . "}%\n"; + $content .= "\\def\\gtotal{" . $data['total'] . "}%\n"; + $content .= "\\endinput"; + fwrite($handle, $content); + fclose($handle); //Write the table - $File = $vault."/data.tex"; - $Handle = fopen($File, "w"); + $file = $tempDir . '/data.tex'; + $handle = fopen($file, 'w'); + $content = ''; foreach ($entries as $row) { - $table_row = "\product"; - foreach ($ini_array['table'] as $index) { - $table_row = $table_row."{".$row[$index]."}"; + $table_row = "\\product"; + foreach ($iniArray['table'] as $index) { + $table_row = $table_row . '{' . $row[$index] . '}'; } - $table_row = $table_row."%\n"; - fwrite($Handle, $table_row); + $table_row = $table_row . "%\n"; + $content .= $table_row; } - fwrite($Handle, "\\endinput"); - fclose($Handle); - + $content .= "\\endinput"; + fwrite($handle, $content); + fclose($handle); + //Copy all the neccessary files to the rendering directory - copy($templateDir."/invoice.tex", $vault."/".$data['invoiceId'].".tex"); - foreach ($ini_array['files'] as $file) { - copy($templateDir."/".$file, $vault."/".$file); + copy($templateDir . '/invoice.tex', $tempDir . '/' . $data['invoiceId'] . '.tex'); + foreach ($iniArray['files'] as $file) { + copy($templateDir . '/' . $file, $tempDir . '/' . $file); } //Run pdflatex, throw error if not! - $output = exec("cd ".$vault." && ".$kga['LaTeXExec']." ".$data['invoiceId'].".tex"); + $output = exec('cd ' . $tempDir . ' && ' . $kga['LaTeXExec'] . ' ' . $data['invoiceId'] . '.tex'); if (strlen($output) == 0) { - Kimai_Logger::logfile("Could not execute pdflatex. Check your installation!"); + Kimai_Logger::logfile('Could not execute pdflatex. Check your installation!'); return; } //Run pdflatex again, throw error if not! - $output = exec("cd ".$vault." && ".$kga['LaTeXExec']." ".$data['invoiceId'].".tex"); + $output = exec('cd ' . $tempDir . ' && ' . $kga['LaTeXExec'] . ' ' . $data['invoiceId'] . '.tex'); //Return the rendered file - $this->sendResponse($vault."/".$data['invoiceId'].".pdf"); + $this->sendResponse($tempDir . '/' . $data['invoiceId'] . '.pdf'); } - + /** * @return pdf */ public function sendResponse($data) { + Kimai_Logger::logfile('File to send: ' . $data); header('Content-Type: pdf'); - header('Content-Disposition: attachment; filename="'.basename($data).'"'); - header('Content-Length: '.filesize($data)); - Kimai_Logger::logfile("File to send:".$data); + header('Content-Disposition: attachment; filename="' . basename($data) . '"'); + header('Content-Length: ' . filesize($data)); + ob_clean(); + flush(); readfile($data); + exit; } /** @@ -142,8 +150,7 @@ public function canRender() if (!is_dir($this->getTemplateDir() . $this->getTemplateFile())) { return false; } - return ( - is_file($this->getTemplateDir() . $this->getTemplateFile() . DIRECTORY_SEPARATOR . self::FILE_TEX) - ); + + return (is_file($this->getTemplateDir() . $this->getTemplateFile() . DIRECTORY_SEPARATOR . self::FILE_TEX)); } } From 924a9338c017ea6f753fbf71f32d69e414ee35e5 Mon Sep 17 00:00:00 2001 From: Simon Schaufelberger Date: Sat, 10 Dec 2016 01:14:39 +0100 Subject: [PATCH 10/18] Further code improvements and a little bit less logging --- extensions/ki_adminpanel/init.php | 3 +-- extensions/ki_invoice/init.php | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/extensions/ki_adminpanel/init.php b/extensions/ki_adminpanel/init.php index 7b13a0d10..d57df80ec 100755 --- a/extensions/ki_adminpanel/init.php +++ b/extensions/ki_adminpanel/init.php @@ -121,9 +121,8 @@ $view->assign('roundSeconds', $kga['conf']['roundSeconds']); } - if(!function_exists('exec')) { + if (!function_exists('exec')) { // deactivate latex functionality - Kimai_Logger::logfile("Cannot execute external files. LaTeX invoices will be disabled."); $view->assign('execAvailable', false); } else { $view->assign('execAvailable', true); diff --git a/extensions/ki_invoice/init.php b/extensions/ki_invoice/init.php index f48e6c0d4..a6a52fe95 100644 --- a/extensions/ki_invoice/init.php +++ b/extensions/ki_invoice/init.php @@ -53,20 +53,20 @@ // Extract all Invoice Templates in groups $invoice_template_files = array(); $allInvoices = glob('invoices/*'); -foreach($allInvoices as $tplFile) { +foreach ($allInvoices as $tplFile) { $extension = ''; $tplInfo = pathinfo($tplFile); if (!is_dir($tplFile)) { $extension = strtoupper($tplInfo['extension']); } else { - //Check if index.hmtl or invoice.tex is there! + //Check if index.html or invoice.tex is there if (file_exists($tplFile . '/index.html')) { $extension = 'HTML'; } elseif (file_exists($tplFile . '/invoice.tex')) { $extension = 'LaTeX'; //Test if we can execute pdflatex if (!function_exists('exec') or !is_executable($kga['LaTeXExec'])) { - Kimai_Logger::logfile("Could not execute pdflatex. Check your installation!"); + Kimai_Logger::logfile('Could not execute pdflatex. Check your installation!'); $extension = ''; } } From e867de352d733c0f62c81d5b04a83e74e74a22fd Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Sat, 10 Dec 2016 16:42:19 +0000 Subject: [PATCH 11/18] Changed Checksum to be a class. --- libraries/Kimai/Invoice/Checksum.php | 133 +++++++++++----------- libraries/Kimai/Invoice/LaTeXRenderer.php | 3 +- 2 files changed, 71 insertions(+), 65 deletions(-) diff --git a/libraries/Kimai/Invoice/Checksum.php b/libraries/Kimai/Invoice/Checksum.php index 71a68d080..ede376e72 100644 --- a/libraries/Kimai/Invoice/Checksum.php +++ b/libraries/Kimai/Invoice/Checksum.php @@ -18,82 +18,87 @@ */ /** + * Class for generating checksums for invoices. * This file calculates a checksum for the invoice number. * Feel free to add more checksum functions as needed in different countries. * * @author Gustav Johansson */ -/** - * @param $type - * @param $id - * @param $args - * @return string - */ -function checksum($type, $id, $args) +class Kimai_Invoice_Checksum { - switch ($type) { - case 'OCR': - return OCR($id, $args); - break; - } -} -/** - * @param $id - * @param bool $addLength - * @return int|string - */ -function OCR($id, $addLength = true) -{ /** - * Calculates the checksum with length number according to the swedish OCR - * system. I.e., 123456 will have a length number added to it (including the - * length number itself and a checksum digit. The return invoice id will be - * a valid OCR-number: 12345682 where the next to last digit is the total - * length and the last digit is the checksum. + * @param $type + * @param $id + * @param $args + * @return string */ - //Check length. Max is 25 including checksum and length no. - if ($addLength) { - $max = 23; - } else { - $max = 24; - } - if (strlen($id) > $max) { - return -1; - } - - //Calculate the length number (only last digit) - $len = (strlen($id) + 2) % 10; - if ($addLength) { - $invoice = $id . $len; - } else { - $invoice = $id; + public function generateChecksum($type, $id, $args) + { + switch ($type) { + case 'OCR': + return $this->OCR($id, $args); + break; + } } - //Calculate checksum - $inReverse = array_reverse(str_split($invoice)); - $sum = 0; - $even = true; - foreach ($inReverse as $num) { - if ($even) { - $even = false; - $tmp = $num * 2; - if ($tmp > 9) { - $tmp = $tmp - 9; - } - $sum = $sum + $tmp; + /** + * @param $id + * @param bool $addLength + * @return int|string + */ + private function OCR($id, $addLength = true) + { + /** + * Calculates the checksum with length number according to the swedish OCR + * system. I.e., 123456 will have a length number added to it (including the + * length number itself and a checksum digit. The return invoice id will be + * a valid OCR-number: 12345682 where the next to last digit is the total + * length and the last digit is the checksum. + */ + //Check length. Max is 25 including checksum and length no. + if ($addLength) { + $max = 23; } else { - $even = true; - $sum = $sum + $num; + $max = 24; } + if (strlen($id) > $max) { + return -1; + } + + //Calculate the length number (only last digit) + $len = (strlen($id) + 2) % 10; + if ($addLength) { + $invoice = $id . $len; + } else { + $invoice = $id; + } + + //Calculate checksum + $inReverse = array_reverse(str_split($invoice)); + $sum = 0; + $even = true; + foreach ($inReverse as $num) { + if ($even) { + $even = false; + $tmp = $num * 2; + if ($tmp > 9) { + $tmp = $tmp - 9; + } + $sum = $sum + $tmp; + } else { + $even = true; + $sum = $sum + $num; + } + } + $check = 10 - ($sum % 10); + //Make sure we use 0 and not 10 + if ($check == 10) { + $check = 0; + } + $checksum = $invoice . $check; + + return $checksum; } - $check = 10 - ($sum % 10); - //Make sure we use 0 and not 10 - if ($check == 10) { - $check = 0; - } - $checksum = $invoice . $check; - - return $checksum; -} +} \ No newline at end of file diff --git a/libraries/Kimai/Invoice/LaTeXRenderer.php b/libraries/Kimai/Invoice/LaTeXRenderer.php index 5cd7a7fa7..1a6ec5694 100644 --- a/libraries/Kimai/Invoice/LaTeXRenderer.php +++ b/libraries/Kimai/Invoice/LaTeXRenderer.php @@ -54,7 +54,8 @@ public function render() $in = time(); $invoiceID = date('y', $in) . $customer['customerID'] . date('m', $in) . date('d', $in); require_once 'Checksum.php'; - $invoiceID = checksum('OCR', $invoiceID, true); + $checksum = new Kimai_Invoice_Checksum(); + $invoiceID = $checksum->generateChecksum('OCR', $invoiceID, true); Kimai_Logger::logfile('invoiceID: ' . $invoiceID); $this->getModel()->setInvoiceId($invoiceID); From e3219633e7125827be99f8221a56192ad043cd75 Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Sat, 10 Dec 2016 16:46:23 +0000 Subject: [PATCH 12/18] Fixed spaces. --- libraries/Kimai/Invoice/Checksum.php | 62 ++++++++++++++-------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/libraries/Kimai/Invoice/Checksum.php b/libraries/Kimai/Invoice/Checksum.php index ede376e72..5e71e4321 100644 --- a/libraries/Kimai/Invoice/Checksum.php +++ b/libraries/Kimai/Invoice/Checksum.php @@ -36,12 +36,12 @@ class Kimai_Invoice_Checksum */ public function generateChecksum($type, $id, $args) { - switch ($type) { + switch ($type) { case 'OCR': return $this->OCR($id, $args); break; } - } + } /** * @param $id @@ -53,7 +53,7 @@ private function OCR($id, $addLength = true) /** * Calculates the checksum with length number according to the swedish OCR * system. I.e., 123456 will have a length number added to it (including the - * length number itself and a checksum digit. The return invoice id will be + * length number itself and a checksum digit. The return invoice id will be * a valid OCR-number: 12345682 where the next to last digit is the total * length and the last digit is the checksum. */ @@ -63,42 +63,42 @@ private function OCR($id, $addLength = true) } else { $max = 24; } - if (strlen($id) > $max) { - return -1; - } + if (strlen($id) > $max) { + return -1; + } //Calculate the length number (only last digit) $len = (strlen($id) + 2) % 10; - if ($addLength) { - $invoice = $id . $len; - } else { - $invoice = $id; - } + if ($addLength) { + $invoice = $id . $len; + } else { + $invoice = $id; + } //Calculate checksum $inReverse = array_reverse(str_split($invoice)); - $sum = 0; - $even = true; - foreach ($inReverse as $num) { - if ($even) { - $even = false; - $tmp = $num * 2; - if ($tmp > 9) { - $tmp = $tmp - 9; - } - $sum = $sum + $tmp; - } else { - $even = true; - $sum = $sum + $num; - } - } - $check = 10 - ($sum % 10); + $sum = 0; + $even = true; + foreach ($inReverse as $num) { + if ($even) { + $even = false; + $tmp = $num * 2; + if ($tmp > 9) { + $tmp = $tmp - 9; + } + $sum = $sum + $tmp; + } else { + $even = true; + $sum = $sum + $num; + } + } + $check = 10 - ($sum % 10); //Make sure we use 0 and not 10 if ($check == 10) { $check = 0; } - $checksum = $invoice . $check; + $checksum = $invoice . $check; - return $checksum; - } -} \ No newline at end of file + return $checksum; + } +} From b7f87c066793cd904e5be26310ac313b18b0c2e0 Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Sun, 11 Dec 2016 11:18:27 +0000 Subject: [PATCH 13/18] Fixed indentation. --- libraries/Kimai/Invoice/Checksum.php | 99 ++++++++++++++-------------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/libraries/Kimai/Invoice/Checksum.php b/libraries/Kimai/Invoice/Checksum.php index 5e71e4321..50cf86558 100644 --- a/libraries/Kimai/Invoice/Checksum.php +++ b/libraries/Kimai/Invoice/Checksum.php @@ -27,78 +27,77 @@ class Kimai_Invoice_Checksum { - /** - * @param $type - * @param $id - * @param $args - * @return string - */ - public function generateChecksum($type, $id, $args) - { - switch ($type) { + * @param $type + * @param $id + * @param $args + * @return string + */ + public function generateChecksum($type, $id, $args) + { + switch ($type) { case 'OCR': return $this->OCR($id, $args); break; } - } + } /** - * @param $id - * @param bool $addLength - * @return int|string - */ - private function OCR($id, $addLength = true) - { - /** - * Calculates the checksum with length number according to the swedish OCR - * system. I.e., 123456 will have a length number added to it (including the - * length number itself and a checksum digit. The return invoice id will be - * a valid OCR-number: 12345682 where the next to last digit is the total - * length and the last digit is the checksum. - */ + * @param $id + * @param bool $addLength + * @return int|string + */ + private function OCR($id, $addLength = true) + { + /** + * Calculates the checksum with length number according to the swedish OCR + * system. I.e., 123456 will have a length number added to it (including the + * length number itself and a checksum digit. The return invoice id will be + * a valid OCR-number: 12345682 where the next to last digit is the total + * length and the last digit is the checksum. + */ //Check length. Max is 25 including checksum and length no. if ($addLength) { $max = 23; } else { $max = 24; } - if (strlen($id) > $max) { - return -1; - } + if (strlen($id) > $max) { + return -1; + } //Calculate the length number (only last digit) $len = (strlen($id) + 2) % 10; - if ($addLength) { - $invoice = $id . $len; - } else { - $invoice = $id; - } + if ($addLength) { + $invoice = $id . $len; + } else { + $invoice = $id; + } //Calculate checksum $inReverse = array_reverse(str_split($invoice)); - $sum = 0; - $even = true; - foreach ($inReverse as $num) { - if ($even) { - $even = false; - $tmp = $num * 2; - if ($tmp > 9) { - $tmp = $tmp - 9; - } - $sum = $sum + $tmp; - } else { - $even = true; - $sum = $sum + $num; - } - } - $check = 10 - ($sum % 10); + $sum = 0; + $even = true; + foreach ($inReverse as $num) { + if ($even) { + $even = false; + $tmp = $num * 2; + if ($tmp > 9) { + $tmp = $tmp - 9; + } + $sum = $sum + $tmp; + } else { + $even = true; + $sum = $sum + $num; + } + } + $check = 10 - ($sum % 10); //Make sure we use 0 and not 10 if ($check == 10) { $check = 0; } - $checksum = $invoice . $check; + $checksum = $invoice . $check; - return $checksum; - } + return $checksum; + } } From 25c575d883fdb233181fe071b55d1dc371bb3d7c Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Sun, 11 Dec 2016 11:29:36 +0000 Subject: [PATCH 14/18] Removed explicit load of file and corrected return value. --- libraries/Kimai/Invoice/LaTeXRenderer.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/Kimai/Invoice/LaTeXRenderer.php b/libraries/Kimai/Invoice/LaTeXRenderer.php index 1a6ec5694..5edb9fc46 100644 --- a/libraries/Kimai/Invoice/LaTeXRenderer.php +++ b/libraries/Kimai/Invoice/LaTeXRenderer.php @@ -53,7 +53,6 @@ public function render() //Create invoiceId $in = time(); $invoiceID = date('y', $in) . $customer['customerID'] . date('m', $in) . date('d', $in); - require_once 'Checksum.php'; $checksum = new Kimai_Invoice_Checksum(); $invoiceID = $checksum->generateChecksum('OCR', $invoiceID, true); Kimai_Logger::logfile('invoiceID: ' . $invoiceID); @@ -119,7 +118,7 @@ public function render() } /** - * @return pdf + * @return null */ public function sendResponse($data) { From 3221e3e2a7c29eb50c0bd700f11d957c8cd60c32 Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Sun, 11 Dec 2016 11:43:11 +0000 Subject: [PATCH 15/18] Added necessary updates to the database. --- installer/install.php | 1 + updater/updater.php | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/installer/install.php b/installer/install.php index 7a27b54a5..e047274ce 100644 --- a/installer/install.php +++ b/installer/install.php @@ -345,6 +345,7 @@ function quoteForSql($input) { ('roundSeconds', '0'), ('allowRoundDown', '0'), ('defaultStatusID', '1') +('LaTeXExec', '') "); // CROSS TABLES diff --git a/updater/updater.php b/updater/updater.php index 91b41215b..57ce39722 100644 --- a/updater/updater.php +++ b/updater/updater.php @@ -1174,6 +1174,11 @@ exec_query("ALTER TABLE `${p}timeSheet` CHANGE `fixedRate` `fixedRate` DECIMAL(10,2) NULL"); } +if ((int)$revisionDB < 1394) { + Kimai_Logger::logfile("-- update to r1394"); + exec_query("INSERT INTO `${p}configuration` (`option`,`value`) VALUES('LaTeXExec', '')"); +} + // ================================================================================ // FINALIZATION: update DB version number // ================================================================================ From d426792ce98db89c667904e42af658ba1f372ded Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Sun, 11 Dec 2016 18:02:53 +0000 Subject: [PATCH 16/18] Fixed typo and increased database version number. --- includes/version.php | 2 +- installer/install.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/version.php b/includes/version.php index 41567d524..3c40ff11f 100644 --- a/includes/version.php +++ b/includes/version.php @@ -1,4 +1,4 @@ Date: Thu, 24 Aug 2017 22:15:07 +0200 Subject: [PATCH 17/18] Update LaTeXRenderer.php --- libraries/Kimai/Invoice/LaTeXRenderer.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/Kimai/Invoice/LaTeXRenderer.php b/libraries/Kimai/Invoice/LaTeXRenderer.php index 5edb9fc46..c71ae6c63 100644 --- a/libraries/Kimai/Invoice/LaTeXRenderer.php +++ b/libraries/Kimai/Invoice/LaTeXRenderer.php @@ -118,17 +118,17 @@ public function render() } /** - * @return null + * @param string $fileName */ - public function sendResponse($data) + public function sendResponse($fileName) { - Kimai_Logger::logfile('File to send: ' . $data); + Kimai_Logger::logfile('File to send: ' . $fileName); header('Content-Type: pdf'); - header('Content-Disposition: attachment; filename="' . basename($data) . '"'); - header('Content-Length: ' . filesize($data)); + header('Content-Disposition: attachment; filename="' . basename($fileName) . '"'); + header('Content-Length: ' . filesize($fileName)); ob_clean(); flush(); - readfile($data); + readfile($fileName); exit; } From c96af4eea173a744d143a6a12c0dda9279dee567 Mon Sep 17 00:00:00 2001 From: Simon Schaufelberger Date: Thu, 24 Aug 2017 22:19:10 +0200 Subject: [PATCH 18/18] Update LaTeXRenderer.php --- libraries/Kimai/Invoice/LaTeXRenderer.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/Kimai/Invoice/LaTeXRenderer.php b/libraries/Kimai/Invoice/LaTeXRenderer.php index c71ae6c63..344252503 100644 --- a/libraries/Kimai/Invoice/LaTeXRenderer.php +++ b/libraries/Kimai/Invoice/LaTeXRenderer.php @@ -55,7 +55,7 @@ public function render() $invoiceID = date('y', $in) . $customer['customerID'] . date('m', $in) . date('d', $in); $checksum = new Kimai_Invoice_Checksum(); $invoiceID = $checksum->generateChecksum('OCR', $invoiceID, true); - Kimai_Logger::logfile('invoiceID: ' . $invoiceID); + Kimai_Logger::logfile('InvoiceID: ' . $invoiceID); $this->getModel()->setInvoiceId($invoiceID); $data = $this->getModel()->toArray(); @@ -123,7 +123,7 @@ public function render() public function sendResponse($fileName) { Kimai_Logger::logfile('File to send: ' . $fileName); - header('Content-Type: pdf'); + header('Content-Type: application/pdf'); header('Content-Disposition: attachment; filename="' . basename($fileName) . '"'); header('Content-Length: ' . filesize($fileName)); ob_clean(); @@ -141,7 +141,7 @@ protected function getTemplateFilename() } /** - * Returns if the file can be rendered. + * Check if the file can be rendered. * * @return bool */