[Checkins] SVN: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/ added
Andreas Jung
andreas at andreas-jung.com
Sat May 16 01:27:03 EDT 2009
Log message for revision 99998:
added
Changed:
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/__init__.py
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app.py
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app.txt
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app_templates/
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app_templates/index.pt
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app_templates/master.pt
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app_templates/showjobs.pt
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/configure.zcml
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/ftesting.zcml
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/static/
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/static/README.txt
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/static/smartprintng.css
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/static/zopyx_logo.gif
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/test.html
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/test.jpg
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/test.zip
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/zip_client.py
A zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/tests.py
-=-
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/__init__.py
===================================================================
--- zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/__init__.py (rev 0)
+++ zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/__init__.py 2009-05-16 05:27:02 UTC (rev 99998)
@@ -0,0 +1 @@
+# this directory is a package
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app.py
===================================================================
--- zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app.py (rev 0)
+++ zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app.py 2009-05-16 05:27:02 UTC (rev 99998)
@@ -0,0 +1,239 @@
+
+import re
+import os
+from datetime import datetime
+import grok
+import time
+import uuid
+import tempfile
+import base64
+import zipfile
+import glob
+import shutil
+from zope import interface, schema
+from zopyx.convert2.convert import Converter
+
+default_tempdir = unicode(tempfile.tempdir)
+
+class ManageServer(grok.Permission):
+ grok.name('zopyx.smartprintng.server.manage')
+
+class IServer(interface.Interface):
+ title = schema.TextLine(title=u'Title of this instance',
+ required=True)
+ spool_directory = schema.TextLine(title=u'Spool directory',
+ default=default_tempdir,
+ required=True)
+
+
+class Zopyx_smartprintng_server(grok.Application, grok.Container):
+ """ This is the base of all evil """
+
+ interface.implements(IServer)
+
+ title = u''
+ spool_directory = default_tempdir
+
+ def addJob(self, *args, **kw):
+ if not 'accounting' in self:
+ self['accounting'] = Accounting()
+ self['accounting'].addJob(*args, **kw)
+ self._p_changed = 1
+
+
+class EditForm(grok.EditForm):
+ """ Eggserver's edit form """
+
+ grok.name('edit')
+ grok.require('zopyx.smartprintng.server.manage')
+ grok.context(IServer)
+ form_fields = grok.AutoFields(IServer)
+
+ @grok.action('Apply changes')
+ def applyChanges(self, **data):
+ self.applyData(self.context, **data)
+ self.redirect(self.url(self.context))
+
+ @grok.action('Cancel')
+ def returnToIndex(self, **data):
+ self.redirect(self.url(self.context))
+
+
+class Accounting(grok.Container):
+ """ A folder for Job objects """
+
+ num_items = 0
+
+ def addJob(self, *args, **kw):
+ self.num_items += 1
+ self[str(self.num_items)] = Job(*args, **kw)
+ self._p_changed = 1
+
+
+class Job(grok.Model):
+ """ Holds informations about each conversion job """
+
+ def __init__(self, *args, **kw):
+ super(Job, self).__init__()
+ self.created = datetime.now()
+ for k,v in kw.items():
+ setattr(self, k, v)
+
+class Index(grok.View):
+ grok.context(Zopyx_smartprintng_server)
+
+class ShowJobs(grok.View):
+ grok.context(Zopyx_smartprintng_server)
+
+ def getJobs(self):
+ lst = list(self.context['accounting'].values())
+ lst.sort(lambda x,y: -cmp(x.created, y.created))
+ return lst
+
+class Master(grok.View):
+ grok.context(Zopyx_smartprintng_server)
+
+class Download(grok.View):
+ """ Provides download functionality for generated files over HTTP"""
+
+ grok.context(Zopyx_smartprintng_server)
+
+ def render(self, id, extension='pdf'):
+ """ Return generated PDF file """
+
+ filename = os.path.join(self.context.spool_directory,
+ '%s.%s' % (id, extension))
+ r = self.response
+ if not os.path.exists(filename):
+ r.setStatus(404)
+ return
+
+ r.setHeader('content-type', 'application/pdf')
+ r.setHeader('content-disposition', 'attachment; filename="%s"' %
+ toAscii(os.path.basename(filename)))
+ return file(filename)
+
+
+#######################################
+# The XML-RPC API
+#######################################
+
+class XMLRPC(grok.XMLRPC):
+
+ grok.context(Zopyx_smartprintng_server)
+
+ rxcountpages = re.compile(r"$\s*/Type\s*/Page[/\s]", re.MULTILINE|re.DOTALL)
+
+ def _countPages(self, filename):
+ data = file(filename,"rb").read()
+ return len(self.rxcountpages.findall(data))
+
+ def convertZIP(self, zip_archive, spool=0, converter_name='pdf-prince'):
+ """ Process html-file + images within a ZIP archive """
+
+ # store zip archive first
+ tempdir = tempfile.mkdtemp()
+ zip_temp = os.path.join(tempdir, 'input.zip')
+ file(zip_temp, 'wb').write(base64.decodestring(zip_archive))
+ ZF = zipfile.ZipFile(zip_temp, 'r')
+ for name in ZF.namelist():
+ destfile = os.path.join(tempdir, name)
+ if not os.path.exists(os.path.dirname(destfile)):
+ os.makedirs(os.path.dirname(destfile))
+ file(destfile, 'wb').write(ZF.read(name))
+ ZF.close()
+
+ # find HTML file
+ html_files = glob.glob(os.path.join(tempdir, '*.htm*'))
+ if not html_files:
+ raise IOError('No HTML files found in %s' % tempdir)
+ html_filename = html_files[0]
+
+ result = self.convert(html_filename,
+ spool=spool,
+ converter_name=converter_name)
+
+ # Generate result ZIP archive with base64-encoded result
+ zip_out = os.path.join(tempdir, 'output.zip')
+ ZF = zipfile.ZipFile(zip_out, 'w')
+ ZF.writestr('output.pdf', file(result, 'rb').read())
+ ZF.close()
+ encoded_result = base64.encodestring(file(zip_out, 'rb').read())
+ shutil.rmtree(tempdir)
+ return encoded_result
+
+ def convert(self, html_filename, spool=0, converter_name='pdf-prince'):
+ """ Process a single HTML file """
+
+ start_time = time.time()
+ c = Converter(html_filename)
+ output_filename = c(converter_name)
+ file_size = os.stat(output_filename)[6]
+ duration = time.time() - start_time
+ self.context.addJob(input_filename=html_filename,
+ output_filename=output_filename,
+ output_size=file_size,
+ duration=duration,
+ pages=self._countPages(output_filename),
+ converter_name=converter_name)
+ if spool:
+ id = uuid.uuid1()
+ if not os.path.exists(self.context.spool_directory):
+ os.makedirs(self.context.spool_directory)
+ new_filename = os.path.join(self.context.spool_directory,
+ '%s.pdf' % id)
+ os.rename(output_filename, new_filename)
+ return grok.url(self.request, self.context) + \
+ '/download?id=%s' % id
+ else:
+ return output_filename
+
+def _c(s):
+ if isinstance(s, unicode):
+ return s
+ try:
+ return unicode(s, 'utf-8')
+ except UnicodeError:
+ return unicode(s, 'iso-8859-15')
+
+def toAscii(s):
+ return _c(s).encode('ascii', 'ignore')
+
+
+class BaseForm(grok.AddForm):
+ grok.context(Zopyx_smartprintng_server)
+
+ def _deliver(self, filename):
+ """ Return generated PDF file """
+ r = self.response
+ r.setHeader('content-type', 'application/pdf')
+ r.setHeader('content-length', os.stat(filename)[6])
+ r.setHeader('content-disposition', 'attachment; filename="%s"' %
+ toAscii(os.path.basename(filename)))
+ return file(filename)
+
+
+#######################################
+# Businesscard demo
+#######################################
+
+class IBusinessCard(interface.Interface):
+ fullname = schema.TextLine(title=u"Fullname (firstname + lastname)")
+ position = schema.Choice(('CEO', 'Developer', 'Facility manager'),
+ title=u"Position")
+ phone = schema.TextLine(title=u"Phone", default=u'+49-7071-793257')
+ orientation= schema.Choice(('horizontal', 'vertical'),
+ title=u"Businesscard layout")
+
+
+class generateBusinessCard(BaseForm):
+ grok.name('generateBusinessCard')
+ grok.context(Zopyx_smartprintng_server)
+ form_fields = grok.AutoFields(IBusinessCard)
+
+ @grok.action('Generate businesscard')
+ def add(self, **data):
+ from zopyx.smartprintng.core.demo2 import demo_app as demo2
+ filename = demo2.demo_convert(**data)
+ return self._deliver(filename)
+
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app.txt
===================================================================
--- zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app.txt (rev 0)
+++ zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app.txt 2009-05-16 05:27:02 UTC (rev 99998)
@@ -0,0 +1,32 @@
+Do a functional doctest test on the app.
+========================================
+
+:Test-Layer: functional
+
+Let's first create an instance of Zopyx_smartprintng_server at the top level:
+
+ >>> from zopyx_smartprintng_server.app import Zopyx_smartprintng_server
+ >>> root = getRootFolder()
+ >>> root['app'] = Zopyx_smartprintng_server()
+
+
+Run tests in the testbrowser
+----------------------------
+
+The zope.testbrowser.browser module exposes a Browser class that
+simulates a web browser similar to Mozilla Firefox or IE. We use that
+to test how our application behaves in a browser. For more
+information, see http://pypi.python.org/pypi/zope.testbrowser.
+
+Create a browser and visit the instance you just created:
+
+ >>> from zope.testbrowser.testing import Browser
+ >>> browser = Browser()
+ >>> browser.open('http://localhost/app')
+
+Check some basic information about the page you visit:
+
+ >>> browser.url
+ 'http://localhost/app'
+ >>> browser.headers.get('Status').upper()
+ '200 OK'
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app_templates/index.pt
===================================================================
--- zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app_templates/index.pt (rev 0)
+++ zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app_templates/index.pt 2009-05-16 05:27:02 UTC (rev 99998)
@@ -0,0 +1,9 @@
+<html metal:use-macro="context/@@master/macros/master">
+ <metal:slot fill-slot="main">
+ <ul>
+ <li>
+ <a href="generateBusinessCard">Businesscard generator demo</a>
+ </li>
+ </ul>
+ </metal:slot>
+</html>
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app_templates/master.pt
===================================================================
--- zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app_templates/master.pt (rev 0)
+++ zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app_templates/master.pt 2009-05-16 05:27:02 UTC (rev 99998)
@@ -0,0 +1,26 @@
+<metal:page define-macro="master">
+ <html>
+ <head>
+ <title tal:content="context/title" />
+ <link rel="stylesheet" type="text/css"
+ tal:attributes="href static/smartprintng.css"
+ />
+ <metal:slot define-slot="css_slot" />
+ <metal:slot define-slot="js_slot" />
+ </head>
+ <body>
+ <a href="http://www.zopyx.com" border="0">
+ <img tal:attributes="src static/zopyx_logo.gif" />
+ </a>
+ <h1 i18n:translate="" tal:content="context/title" />
+
+ <metal:slot define-slot="main" />
+
+ <hr/>
+ <div class="footer">
+ The SmartPrintNG server is (C) 2008, ZOPYX Ltd. & Co. KG, D-72070 Tübingen,
+ Germany, www.zopyx.com. All rights reserved!
+ </div>
+ </body>
+ </html>
+</metal:page>
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app_templates/showjobs.pt
===================================================================
--- zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app_templates/showjobs.pt (rev 0)
+++ zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/app_templates/showjobs.pt 2009-05-16 05:27:02 UTC (rev 99998)
@@ -0,0 +1,16 @@
+<html metal:use-macro="context/@@master/macros/master">
+ <metal:slot fill-slot="main">
+ <h1>Accounting</h1>
+ <table border="1">
+
+ <tal:loop repeat="job view/getJobs">
+ <tr>
+ <td tal:content="python: job.created.strftime('%d.%m.%Y %H:%M:%S')" />
+ <td tal:content="job/duration" />
+ <td tal:content="job/pages" />
+ <td tal:content="job/converter_name" />
+ <tr>
+ </tal:loop>
+ </table>
+ </metal:slot>
+</html>
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/configure.zcml
===================================================================
--- zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/configure.zcml (rev 0)
+++ zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/configure.zcml 2009-05-16 05:27:02 UTC (rev 99998)
@@ -0,0 +1,6 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:grok="http://namespaces.zope.org/grok">
+ <include package="grok" />
+ <includeDependencies package="." />
+ <grok:grok package="." />
+</configure>
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/ftesting.zcml
===================================================================
--- zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/ftesting.zcml (rev 0)
+++ zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/ftesting.zcml 2009-05-16 05:27:02 UTC (rev 99998)
@@ -0,0 +1,34 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="zopyx_smartprintng_server"
+ package="zopyx_smartprintng_server"
+ >
+
+ <include package="zopyx_smartprintng_server" />
+
+ <!-- Typical functional testing security setup -->
+ <securityPolicy
+ component="zope.securitypolicy.zopepolicy.ZopeSecurityPolicy"
+ />
+
+ <unauthenticatedPrincipal
+ id="zope.anybody"
+ title="Unauthenticated User"
+ />
+ <grant
+ permission="zope.View"
+ principal="zope.anybody"
+ />
+
+ <principal
+ id="zope.mgr"
+ title="Manager"
+ login="mgr"
+ password="mgrpw"
+ />
+
+ <role id="zope.Manager" title="Site Manager" />
+ <grantAll role="zope.Manager" />
+ <grant role="zope.Manager" principal="zope.mgr" />
+
+</configure>
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/static/README.txt
===================================================================
--- zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/static/README.txt (rev 0)
+++ zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/static/README.txt 2009-05-16 05:27:02 UTC (rev 99998)
@@ -0,0 +1,2 @@
+Put static files here, like javascript and css. They will be
+available as static/<filename> in views.
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/static/smartprintng.css
===================================================================
--- zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/static/smartprintng.css (rev 0)
+++ zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/static/smartprintng.css 2009-05-16 05:27:02 UTC (rev 99998)
@@ -0,0 +1,17 @@
+* {
+ font-family: Georgia,"Bitstream Vera Serif","New York",Palatino,serif;
+ font-weight:normal;
+}
+
+.footer {
+ font-size: 11px;
+ font-style:italic;
+}
+
+img {
+ border: 0;
+}
+
+a {
+ text-decoration: none;
+}
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/static/zopyx_logo.gif
===================================================================
(Binary files differ)
Property changes on: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/static/zopyx_logo.gif
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/test.html
===================================================================
--- zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/test.html (rev 0)
+++ zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/test.html 2009-05-16 05:27:02 UTC (rev 99998)
@@ -0,0 +1,54 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta http-equiv="Content-Style-Type" content="text/css">
+ <title></title>
+ <meta name="Generator" content="Cocoa HTML Writer">
+ <meta name="CocoaVersion" content="949.35">
+ <style type="text/css">
+ p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica}
+ p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px}
+ </style>
+</head>
+<body>
+<img src="test.jpg" />
+<p class="p1"><span class="Apple-converted-space">Â </span>Further technologies</p>
+<p class="p2"><br></p>
+<p class="p1">Programming languages:</p>
+<p class="p2"><br></p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* Python, C, C++, Shell, Javascript,</p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* SQL, PHP</p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* SGML, XML, DOM, SAX, XSL, XSLT, XSL-FO, XPATH etc.</p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* Java, JDBC, Servlets</p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* C, C++</p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* Middleware (ILU, CORBA)</p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* HTML, CSS</p>
+<p class="p2"><br></p>
+<p class="p1">Databases:</p>
+<p class="p2"><br></p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* Oracle, MySQL, Postgres, ZODB</p>
+<p class="p2"><br></p>
+<p class="p1">Tools:</p>
+<p class="p2"><br></p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* Apache, Squid, Pound</p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* Zope, CMF, Plone</p>
+<p class="p2"><br></p>
+<p class="p1">Operating system:</p>
+<p class="p2"><br></p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* Unix, Linux, Solaris</p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* Windows</p>
+<p class="p2"><br></p>
+<p class="p1">Applications server:</p>
+<p class="p2"><br></p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* Zope 2, PHP</p>
+<p class="p2"><br></p>
+<p class="p1">Content-Management-Systems:</p>
+<p class="p2"><br></p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* Typo 3</p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* Zope</p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* Plone</p>
+<p class="p1"><span class="Apple-converted-space">Â Â </span>* CPS</p>
+<p class="p2"><br></p>
+</body>
+</html>
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/test.jpg
===================================================================
(Binary files differ)
Property changes on: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/test.jpg
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/test.zip
===================================================================
(Binary files differ)
Property changes on: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/test.zip
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/zip_client.py
===================================================================
--- zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/zip_client.py (rev 0)
+++ zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/test_data/zip_client.py 2009-05-16 05:27:02 UTC (rev 99998)
@@ -0,0 +1,19 @@
+import base64
+import xmlrpclib
+import zipfile
+import tempfile
+
+# XMLRPC server instance
+server = xmlrpclib.Server('http://localhost:11080/demo')
+
+# send the ZIP archive base64 encoded
+zip_data = server.convertZIP(base64.encodestring(file('test.zip', 'rb').read()))
+
+# and receive the result PDF as base64 encoded ZIP archive
+zip_temp = tempfile.mktemp()
+file(zip_temp, 'wb').write(base64.decodestring(zip_data))
+ZF = zipfile.ZipFile(zip_temp, 'r')
+file('output.pdf', 'wb').write(ZF.read('output.pdf'))
+ZF.close()
+
+print 'output.pdf'
Added: zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/tests.py
===================================================================
--- zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/tests.py (rev 0)
+++ zopyx.smartprintng.server/trunk/zopyx/smartprintng/server/tests.py 2009-05-16 05:27:02 UTC (rev 99998)
@@ -0,0 +1,12 @@
+import os.path
+import z3c.testsetup
+import zopyx_smartprintng_server
+from zope.app.testing.functional import ZCMLLayer
+
+
+ftesting_zcml = os.path.join(
+ os.path.dirname(zopyx_smartprintng_server.__file__), 'ftesting.zcml')
+FunctionalLayer = ZCMLLayer(ftesting_zcml, __name__, 'FunctionalLayer',
+ allow_teardown=True)
+
+test_suite = z3c.testsetup.register_all_tests('zopyx_smartprintng_server')
More information about the Checkins
mailing list