root / pykota / trunk / cgi-bin / printquota.cgi @ 2103

Revision 2056, 15.1 kB (checked in by jalet, 20 years ago)

Added missing fields

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#! /usr/bin/python
2# -*- coding: ISO-8859-15 -*-
3
4# PyKota Print Quota Reports generator
5#
6# PyKota - Print Quotas for CUPS and LPRng
7#
8# (c) 2003, 2004, 2005 Jerome Alet <alet@librelogiciel.com>
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22#
23# $Id$
24#
25# $Log$
26# Revision 1.45  2005/02/13 22:22:03  jalet
27# Added missing fields
28#
29# Revision 1.44  2005/01/19 08:49:41  jalet
30# Now dumpykota.cgi behaves like printquota.cgi wrt the REMOTE_USER environment
31# variables if the script is username+password protected.
32# Small fix in printquota.cgi wrt ldap auth with Apache : the workaround was
33# not used everywhere.
34#
35# Revision 1.43  2005/01/17 08:44:24  jalet
36# Modified copyright years
37#
38# Revision 1.42  2005/01/08 17:19:02  jalet
39# Fixed incorrect import statement
40#
41# Revision 1.41  2005/01/08 17:13:08  jalet
42# Move some functions outside of the CGI to allow their use in other CGI scripts
43#
44# Revision 1.40  2004/10/23 19:28:18  jalet
45# Small fix to the cgi script to please Freshmeat's fmII URL validator/1.1
46#
47# Revision 1.39  2004/10/13 09:38:25  jalet
48# Added a translatable message from the CGI script
49#
50# Revision 1.38  2004/10/02 13:47:46  jalet
51# The CGI script doesn't depend anymore on what is in the submit button
52# to display the print quota report.
53#
54# Revision 1.37  2004/10/02 05:48:56  jalet
55# Should now correctly deal with charsets both when storing into databases and when
56# retrieving datas. Works with both PostgreSQL and LDAP.
57#
58# Revision 1.36  2004/09/02 10:34:09  jalet
59# Fixed problem with mod_auth_ldap Apache module
60#
61# Revision 1.35  2004/07/24 20:10:10  jalet
62# Incorrect number of parameters in error method
63#
64# Revision 1.34  2004/07/24 20:07:13  jalet
65# Added special Python comment to remove warning at runtime
66#
67# Revision 1.33  2004/07/21 19:24:48  jalet
68# Inversion
69#
70# Revision 1.32  2004/07/21 19:20:05  jalet
71# Missing translation in CGI script
72#
73# Revision 1.31  2004/07/21 13:32:02  jalet
74# All messages should be translatable now.
75#
76# Revision 1.30  2004/07/01 17:45:48  jalet
77# Added code to handle the description field for printers
78#
79# Revision 1.29  2004/06/06 21:07:55  jalet
80# Improved CGI script to allow history on printers and hostnames.
81# Better (?) colors in stylesheet.
82#
83# Revision 1.28  2004/06/05 22:42:41  jalet
84# Improved web history reports
85#
86# Revision 1.27  2004/05/26 14:49:40  jalet
87# First try at saving the job-originating-hostname in the database
88#
89# Revision 1.26  2004/03/24 19:37:04  jalet
90# Doesn't retrieve users or printers objects to display the history,
91# this is not needed, and saves a lot of time (and database queries
92# if storagecahing is disabled)
93#
94# Revision 1.25  2004/01/12 15:28:45  jalet
95# Now can output the user's history on several printers at the same time.
96#
97# Revision 1.24  2004/01/12 14:52:03  jalet
98# Cuts the date string
99#
100# Revision 1.23  2004/01/12 14:35:01  jalet
101# Printing history added to CGI script.
102#
103# Revision 1.22  2004/01/09 07:58:53  jalet
104# Changed URL to PyKota's logo
105#
106# Revision 1.21  2004/01/08 14:10:32  jalet
107# Copyright year changed.
108#
109# Revision 1.20  2004/01/07 16:07:17  jalet
110# The stylesheet is again expected to be local, it was a bad idea to use
111# the one on my server.
112#
113# Revision 1.19  2004/01/06 16:05:45  jalet
114# Will now search the stylesheet on my own website.
115#
116# Revision 1.18  2003/12/27 16:49:25  uid67467
117# Should be ok now.
118#
119# Revision 1.16  2003/12/02 14:40:20  jalet
120# Some code refactoring.
121# New HTML reporter added, which is now used in the CGI script for web based
122# print quota reports. It will need some de-uglyfication though...
123#
124# Revision 1.15  2003/10/24 22:06:42  jalet
125# Initial support for browser's language preference added.
126#
127# Revision 1.14  2003/10/10 19:48:07  jalet
128# Now displays version number
129#
130# Revision 1.13  2003/08/25 11:23:05  jalet
131# More powerful CGI script for quota reports
132#
133# Revision 1.12  2003/07/29 20:55:17  jalet
134# 1.14 is out !
135#
136# Revision 1.11  2003/07/01 12:37:31  jalet
137# Nicer UI
138#
139# Revision 1.10  2003/07/01 07:30:32  jalet
140# Message changed.
141#
142# Revision 1.9  2003/06/30 13:47:26  jalet
143# Allows multiple user / group names masks in the input field
144#
145# Revision 1.8  2003/06/30 13:32:01  jalet
146# Much more powerful CGI script for quota reports
147#
148# Revision 1.7  2003/06/30 12:46:15  jalet
149# Extracted reporting code.
150#
151# Revision 1.6  2003/04/23 22:13:56  jalet
152# Preliminary support for LPRng added BUT STILL UNTESTED.
153#
154# Revision 1.5  2003/04/17 21:30:09  jalet
155# Now includes the logo
156#
157# Revision 1.4  2003/04/08 21:20:25  jalet
158# CGI Script now displays a link to PyKota's website.
159#
160# Revision 1.3  2003/03/29 13:45:27  jalet
161# GPL paragraphs were incorrectly (from memory) copied into the sources.
162# Two README files were added.
163# Upgrade script for PostgreSQL pre 1.01 schema was added.
164#
165# Revision 1.2  2003/02/12 11:31:51  jalet
166# doesn't use the jaxml module anymore
167#
168# Revision 1.1  2003/02/10 13:41:38  jalet
169# repykota cgi script added.
170# cleaner doc.
171#
172
173import sys
174import os
175import cgi
176import urllib
177
178from pykota import version
179from pykota.tool import PyKotaTool, PyKotaToolError
180from pykota.reporter import PyKotaReporterError, openReporter
181from pykota.cgifuncs import getLanguagePreference, getCharsetPreference
182
183header = """Content-type: text/html
184
185<?xml version="1.0" encoding="%s"?>
186<html>
187  <head>
188    <title>%s</title>
189    <link rel="stylesheet" type="text/css" href="/pykota.css" />
190  </head>
191  <body>
192    <form action="printquota.cgi" method="POST">
193      <table>
194        <tr>
195          <td>
196            <p>
197              <a href="http://www.librelogiciel.com/software/"><img src="http://www.librelogiciel.com/software/PyKota/pykota.png" alt="PyKota's Logo" /></a>
198              <br />
199              <a href="http://www.librelogiciel.com/software/">PyKota v%s</a>
200            </p>
201          </td>
202          <td colspan="2">
203            <h1>%s</h1>
204          </td>
205        </tr>
206        <tr>
207          <td colspan="3" align="center">
208            <input type="submit" name="report" value="%s" />
209          </td>
210        </tr>
211      </table>"""
212   
213footer = """
214      <table>
215        <tr>
216          <td colspan="3" align="center">
217            <input type="submit" name="report" value="%s" />
218          </td>
219        </tr>
220      </table> 
221    </form>
222  </body>
223</html>""" 
224
225class PyKotaReportGUI(PyKotaTool) :
226    """PyKota Administrative GUI"""
227    def guiDisplay(self) :
228        """Displays the administrative interface."""
229        global header, footer
230        print header % (self.getCharset(), _("PyKota Reports"), version.__version__, _("PyKota Reports"), _("Report"))
231        print self.body
232        print footer % _("Report")
233       
234    def error(self, message) :
235        """Adds an error message to the GUI's body."""
236        if message :
237            self.body = '<p><font color="red">%s</font></p>\n%s' % (message, self.body)
238       
239    def htmlListPrinters(self, selected=[], mask="*") :   
240        """Displays the printers multiple selection list."""
241        printers = self.storage.getMatchingPrinters(mask)
242        selectednames = [p.Name for p in selected]
243        message = '<table><tr><td valign="top">%s :</td><td valign="top"><select name="printers" multiple="multiple">' % _("Printer")
244        for printer in printers :
245            if printer.Name in selectednames :
246                message += '<option value="%s" selected="selected">%s (%s)</option>' % (printer.Name, printer.Name, printer.Description)
247            else :
248                message += '<option value="%s">%s (%s)</option>' % (printer.Name, printer.Name, printer.Description)
249        message += '</select></td></tr></table>'
250        return message
251       
252    def htmlUGNamesInput(self, value="*") :   
253        """Input field for user/group names wildcard."""
254        return _("User / Group names mask") + (' : <input type="text" name="ugmask" size="20" value="%s" /> <em>e.g. <strong>jo*</strong></em>' % (value or "*"))
255       
256    def htmlGroupsCheckbox(self, isgroup=0) :
257        """Groups checkbox."""
258        if isgroup :
259            return _("Groups report") + ' : <input type="checkbox" checked="checked" name="isgroup" />'
260        else :   
261            return _("Groups report") + ' : <input type="checkbox" name="isgroup" />'
262           
263    def guiAction(self) :
264        """Main function"""
265        printers = ugmask = isgroup = None
266        remuser = os.environ.get("REMOTE_USER", "root")   
267        # special hack to accomodate mod_auth_ldap Apache module
268        try :
269            remuser = remuser.split("=")[1].split(",")[0]
270        except IndexError :   
271            pass
272        self.body = "<p>%s</p>\n" % _("Please click on the above button")
273        if self.form.has_key("report") :
274            if self.form.has_key("printers") :
275                printersfield = self.form["printers"]
276                if type(printersfield) != type([]) :
277                    printersfield = [ printersfield ]
278                printers = [self.storage.getPrinter(p.value) for p in printersfield]
279            else :   
280                printers = self.storage.getMatchingPrinters("*")
281            if remuser == "root" :
282                if self.form.has_key("ugmask") :     
283                    ugmask = self.form["ugmask"].value
284                else :     
285                    ugmask = "*"
286            else :       
287                if self.form.has_key("isgroup") :   
288                    user = self.storage.getUser(remuser)
289                    if user.Exists :
290                        ugmask = " ".join([ g.Name for g in self.storage.getUserGroups(user) ])
291                    else :   
292                        ugmask = remuser # result will probably be empty, we don't care
293                else :   
294                    ugmask = remuser
295            if self.form.has_key("isgroup") :   
296                isgroup = 1
297            else :   
298                isgroup = 0
299        self.body += self.htmlListPrinters(printers or [])           
300        self.body += "<br />"
301        self.body += self.htmlUGNamesInput(ugmask)
302        self.body += "<br />"
303        self.body += self.htmlGroupsCheckbox(isgroup)
304        if not self.form.has_key("history") :
305            if printers and ugmask :
306                self.reportingtool = openReporter(admin, "html", printers, ugmask.split(), isgroup)
307                self.body += "%s" % self.reportingtool.generateReport()
308        else :       
309            if remuser != "root" :
310                username = remuser
311            elif self.form.has_key("username") :   
312                username = self.form["username"].value
313            else :   
314                username = None
315            if username is not None :   
316                user = self.storage.getUser(username)
317            else :   
318                user =None
319            if self.form.has_key("printername") :
320                printer = self.storage.getPrinter(self.form["printername"].value)
321            else :   
322                printer = None
323            if self.form.has_key("datelimit") :   
324                datelimit = self.form["datelimit"].value
325            else :   
326                datelimit = None
327            if self.form.has_key("hostname") :   
328                hostname = self.form["hostname"].value
329            else :   
330                hostname = None
331            self.report = ["<h2>%s</h2>" % _("History")]   
332            history = self.storage.retrieveHistory(user, printer, datelimit, hostname)
333            if not history :
334                self.report.append("<h3>%s</h3>" % _("Empty"))
335            else :
336                self.report.append('<table class="pykotatable" border="1">')
337                headers = [_("Date"), _("Action"), _("User"), _("Printer"), \
338                           _("Hostname"), _("JobId"), _("JobSize"), \
339                           _("JobPrice"), _("Copies"), _("JobBytes"), \
340                           _("PageCounter"), _("Title"), _("Filename"), \
341                           _("Options"), _("MD5Sum"), _("BillingCode"), \
342                           _("Pages")]
343                self.report.append('<tr class="pykotacolsheader">%s</tr>' % "".join(["<th>%s</th>" % h for h in headers]))
344                oddeven = 0
345                for job in history :
346                    oddeven += 1
347                    if oddeven % 2 :
348                        oddevenclass = "odd"
349                    else :   
350                        oddevenclass = "even"
351                    if job.JobAction == "DENY" :
352                        oddevenclass = "deny"
353                    elif job.JobAction == "WARN" :   
354                        oddevenclass = "warn"
355                    username_url = '<a href="%s?%s">%s</a>' % (os.environ.get("SCRIPT_NAME", ""), urllib.urlencode({"history" : 1, "username" : job.UserName}), job.UserName)
356                    printername_url = '<a href="%s?%s">%s</a>' % (os.environ.get("SCRIPT_NAME", ""), urllib.urlencode({"history" : 1, "printername" : job.PrinterName}), job.PrinterName)
357                    if job.JobHostName :
358                        hostname_url = '<a href="%s?%s">%s</a>' % (os.environ.get("SCRIPT_NAME", ""), urllib.urlencode({"history" : 1, "hostname" : job.JobHostName}), job.JobHostName)
359                    else :   
360                        hostname_url = None
361                    self.report.append('<tr class="%s">%s</tr>' % (oddevenclass, "".join(["<td>%s</td>" % (h or "&nbsp;") for h in (job.JobDate[:19], job.JobAction, username_url, printername_url, hostname_url, job.JobId, job.JobSize, job.JobPrice, job.JobCopies, job.JobSizeBytes, job.PrinterPageCounter, job.JobTitle, job.JobFileName, job.JobOptions, job.JobMD5Sum, job.JobBillingCode, job.JobPages)])))
362                self.report.append('</table>')
363                dico = { "history" : 1,
364                         "datelimit" : job.JobDate,
365                       }
366                if user and user.Exists :
367                    dico.update({ "username" : user.Name })
368                if printer and printer.Exists :
369                    dico.update({ "printername" : printer.Name })
370                if hostname :   
371                    dico.update({ "hostname" : hostname })
372                prevurl = "%s?%s" % (os.environ.get("SCRIPT_NAME", ""), urllib.urlencode(dico))
373                self.report.append('<a href="%s">%s</a>' % (prevurl, _("Previous page")))
374            self.body = "\n".join(self.report)   
375           
376if __name__ == "__main__" :
377    os.environ["LC_ALL"] = getLanguagePreference()
378    admin = PyKotaReportGUI(lang=os.environ["LC_ALL"], charset=getCharsetPreference())
379    admin.form = cgi.FieldStorage()
380    admin.guiAction()
381    admin.guiDisplay()
382    try :
383        admin.storage.close()
384    except (TypeError, NameError, AttributeError) :   
385        pass
386       
387    sys.exit(0)
Note: See TracBrowser for help on using the browser.