Projet

Général

Profil

« Précédent | Suivant » 

Révision b77557ca

Ajouté par dlaniel il y a presque 18 ans

  • ID b77557ca44ce9cfe58c805ea6c82667fb60a0af5

example vhost configurations + admin web interface which writes vhost files

git-svn-id: svn+ssh://labs.libre-entreprise.org/svnroot/larpe@1 3ed937ae-f919-0410-9a43-8e6f19e4ba6e

Voir les différences:

TODO
1
- interface d'admin, permettant de d?finir les points ci-dessus, (?criture d'un fichier inclus dans la conf apache puis reload)
2
- options possibles par serveur vhost/r?pertoire
3
  - [x] acc?s n?cessitant authentification (?a, ?a ferait un ?quivalent LemonLDAP)
4
- possibilit? interception URL /login
5
  - pour en faire libertysation:
6
    - redirect, ...
7
    - page proposant d'?tablir la f?d?ration, o? l'utilisateur entre identifiant / mot de passe du SP
8

  
9

  
10
Fait
11
====
12

  
13
- rp par vhost (appli1.example.com, rp de appli1.interne)
14
- rp par repertoire (www.example.com/appli1, rp de appl1.interne)
admin/__init__.py
1
from quixote.server.scgi_server import run
2
from quixote.publish import Publisher
3
from quixote import enable_ptl
4
import quixote
5
enable_ptl()
6

  
7
from root import RootDirectory
8

  
9
def create_publisher():
10
	return Publisher(RootDirectory())
11

  
12
#quixote.DEFAULT_CHARSET = 'utf-8'
13
run(create_publisher, port=3003, script_name = '')
admin/apache.ptl
1
from quixote.directory import Directory
2

  
3
from template import generate_html
4
from apache_edit import ApacheEdit
5
from apache_form import ApacheForm
6
from apache_vhost import ApacheVhost
7

  
8
class Apache(Directory):
9
	"""
10
	Apache administration directory
11
	"""
12
	_q_exports = ['', 'new', 'edit']
13

  
14
	edit = ApacheEdit()
15

  
16
	def _q_index [html] (self):
17
		#form = ApacheForm()
18
		body = '<a href="new">Cr?er un nouvel h?te virtuel</a>'
19
		generate_html('apache', body)
20
		#generate_html('apache', self.generate_body())
21

  
22
#	def _q_lookup [html] (self, component):
23
#		vhost = ApacheVhost(component)
24
#		vhost._q_lookup(component)
25
#		return component
26

  
27
	def new [html] (self):
28
		form = ApacheForm()
29
		generate_html('apache', form.handle())
admin/apache_edit.ptl
1
from quixote.directory import Directory
2

  
3
from template import generate_html
4
from apache_form import ApacheForm
5

  
6
class ApacheEdit(Directory):
7
	"""
8
	Apache administration directory
9
	"""
10
	_q_exports = ['']
11

  
12
	def _q_index [html] (self):
13
		"""Error"""
14
		return
15

  
16
	def _q_lookup [html] (self, server_name):
17
		form = ApacheForm(server_name)
18
		generate_html('apache', form.handle())
admin/apache_form.ptl
1
import os
2

  
3
from quixote.publish import get_response
4
from quixote.form import Form, StringWidget, PasswordWidget, \
5
     RadiobuttonsWidget, SingleSelectWidget, MultipleSelectWidget, \
6
     CheckboxWidget, FileWidget
7

  
8
class ApacheForm(Form):
9
	def __init__(self, server_name = None):
10
		Form.__init__(self)
11
		#get_response().set_charset('utf-8')
12
		self.server_name = server_name
13

  
14
	def handle(self):
15
		if (self.server_name is None):
16
			self.add_string(name = 'proxy_name', title = 'Nom du serveur proxy', required = True)
17
		else:
18
			self.add_string(name = 'proxy_name', title = 'Nom du serveur proxy', value = self.server_name, required = True)
19
		self.add_string(name = 'proxy_ip', title = 'Adresse IP du serveur proxy', required = True)
20
		self.add_string(name = 'proxy_port', title = 'Port du serveur proxy', value = '80', required = True)
21
		#self.add_string(name = 'proxy_admin', title = 'Administrateur du serveur proxy', required = False)
22
		self.add_string(name = 'site_address', title = 'Adresse du site', value = 'http://', required = True)
23
		self.add_string(name = 'proxy_directory', title = 'R?pertoire du proxy', value = '/', required = True)
24
		self.add_submit('submit')
25
		if not self.is_submitted() or self.has_errors():
26
			return self.render()
27
		return self.success()
28
		
29
	def success [html] (self):
30
		values = {}
31
		#get_response().set_charset('utf-8')
32
		for widget in self.get_all_widgets():
33
			values[widget.get_name()] = widget.parse()
34
			
35
			#"""<p>name : """
36
			#name
37
			#"""</p><p>value : """
38
			#repr(value)
39
			#"""</p>"""
40

  
41
		try:
42
			self.write_apache_conf(values)
43
			#self.write_apache_conf(values['proxy_name'],
44
			#						values['proxy_ip'],
45
			#						values['proxy_port'],
46
			#						values['site_address'],
47
			#						values['proxy_directory'])
48
		except IOError:
49
			"""Failed writing apache configuration"""
50
		else:
51
			"""Apache configuration written"""
52

  
53
	#def write_apache_conf(self, proxy_name, proxy_ip, proxy_port, site_address, proxy_directory):
54
	def write_apache_conf(self, values):
55
		"""Caller must catch IOError exceptions"""
56
		#print ip + " - " + name + " - " + directory
57
		vhost_dir = '/var/lib/larpe'
58
		conf_dir = '%s/%s' % (vhost_dir, values['proxy_name'])
59
		if not os.path.exists(conf_dir):
60
			os.makedirs(conf_dir, mode = 0755)
61
		if not os.path.isdir(conf_dir):
62
			raise IOError()
63
		conf_file = open('%s/%s' % (conf_dir, 'apache2-vhost.conf'), 'w')
64
		
65
		try:
66
			conf_file.write("""NameVirtualHost %(proxy_ip)s:%(proxy_port)s
67
<VirtualHost %(proxy_ip)s:%(proxy_port)s>
68

  
69
	ServerName %(proxy_name)s
70
	# ServerAdmin admin@test.org
71

  
72
	# LoadFile		/usr/lib/libxml2.so.2
73
	SetOutputFilter	proxy-html
74

  
75
	<Location %(proxy_directory)s>
76
		ProxyPass			%(site_address)s
77
		ProxyPassReverse	%(site_address)s
78
		ProxyHTMLURLMap		/		%(proxy_directory)s
79
		ProxyHTMLURLMap		%(site_address)s		%(proxy_directory)s
80
	</Location>
81
  
82
</VirtualHost>
83
""" % values)
84

  
85
		finally:
86
			conf_file.close()
87
			
88
			
admin/apache_new.ptl
1
from quixote.directory import Directory
2

  
3
from template import generate_html
4
from apache_form import ApacheForm
5

  
6
class ApacheNew(Directory):
7
	"""
8
	Apache administration directory
9
	"""
10
	_q_exports = ['']
11

  
12
	def _q_index [html] (self):
13
		"""Error"""
14
		return
15

  
16
	def _q_lookup [html] (self, server_name):
17
		form = ApacheForm()
18
		generate_html('apache', form.handle())
admin/apache_vhost.ptl
1
from quixote.directory import Directory
2

  
3
from template import generate_html
4
from apache_form import ApacheForm
5

  
6
class ApacheVhost(Directory):
7
	"""
8
	Apache vhost management
9
	"""
10
	_q_exports = ['', 'edit']
11

  
12
	def __init__(self, server_name):
13
		self.server_name = server_name
14

  
15
	def _q_index [html] (self):
16
		"""ok"""
17
	
18
	def _q_lookup [html] (self, component):
19
		component + " " + self.server_name
20

  
21
	def edit [html] (self):
22
		'test'
admin/css/larpe-admin.css
1
@import url(../wcs-common.css);
2

  
3
html, body {
4
	margin: 0;
5
	background: white url(page-bg.png) repeat-y;
6
}
7

  
8
div#main-content {
9
	margin-left: 160px;
10
	margin-top: -10px;
11
	margin-right: 20px;
12
}
13

  
14
div#main-content h1 {
15
	color: #006699;
16
	font-size: 120%;
17
}
18

  
19
div#main-content h2 {
20
	color: #006699;
21
	font-size: 115%;
22
}
23

  
24
div#main-content h3 {
25
	color: #006699;
26
	font-size: 108%
27
}
28

  
29

  
30

  
31
div#header {
32
	margin: 0;
33
	background: white url(head-bg.png) repeat-x;
34
	height: 58px;
35
}
36

  
37
ul#menu {
38
	background: transparent url(head-logo-larpe.png) no-repeat;
39
	width: 177px;
40
	margin: 0;
41
	padding: 80px 0 0 5px;
42
}
43

  
44
a {
45
	color: #0066cc;
46
	text-decoration: none;
47
	border-bottom: 1px dotted #ff9900;
48
}
49

  
50
p.commands a {
51
	border: 0;
52
}
53

  
54
ul#menu a {
55
	font-weight: bold;
56
}
57

  
58
ul#menu li.active a {
59
	border-bottom: 1px solid #ff9900;
60
}
61

  
62
ul#menu li {
63
	font-size: 90%;
64
	margin-bottom: 1em;
65
	max-width: 130px;
66
}
67

  
68
div#footer {
69
	display: none;
70
}
71

  
72
ul.user-info {
73
	position: absolute;
74
	margin: 0;
75
	padding: 0;
76
	right: 25px;
77
	top: 13px;
78
	font-size: 70%;
79
	font-weight: bold;
80
}
81

  
82
ul.user-info li {
83
	display: inline;
84
	padding-left: 10px;
85
}
86

  
87
/** end of dc2 changes **/
88

  
89

  
90

  
91
ul.biglist {
92
	margin: 0;
93
	padding: 0;
94
}
95

  
96
ul.biglist li {
97
	list-style-type: none;
98
	margin: 4px 0;
99
	padding: 0 2px;
100
	border: 1px solid #888;
101
	background: #ffe;
102
	clear: both;
103
}
104

  
105
ul.biglist li p.details {
106
	display: block;
107
	margin: 0;
108
	color: #555;
109
	font-size: 80%;
110
}
111

  
112

  
113
ul.biglist li p.commands {
114
	float: right;
115
	margin-top: -17px;
116
}
117

  
118
ul.biglist li p.commands img {
119
	padding-right: 5px;
120
}
121

  
122
a img {
123
	border: 0;
124
}
125

  
126
td.time {
127
	text-align: right;
128
}
129

  
130
ul.biglist li.disabled, ul.biglist li.disabled p.details {
131
	color: #999;
132
	background: #ddd;
133
}
134

  
135

  
136
dl dt {
137
	margin : 0;
138
	padding : 0 0 0 0;
139
}
140

  
141
dl dd {
142
        margin : 0.3em 0 1.5em 10px;
143
}
144

  
145

  
146
img.theme-icon {
147
	float: right;
148
	margin: -16px 4px 0px 3px;
149
	border: 1px solid #999;
150
}
151

  
152
div#new-field table {
153
	margin: 0;
154
	padding: 0;
155
}
156

  
157
div#new-field div.widget {
158
	margin: 0;
159
	padding: 0;
160
}
161

  
162
div#new-field div.buttons {
163
	margin: 0;
164
	padding: 0;
165
}
166

  
167
div#new-field div.buttons input {
168
	margin: 0;
169
	padding: 0;
170
}
171

  
172
div#new-field {
173
	border: 1px solid #888;
174
	background: #ffe;
175
	margin: 2em 0 4px 0;
176
	padding: 0 2px;
177
}
178

  
179
div#new-field div.widget {
180
}
181

  
182
div#new-field h3 {
183
	margin: 0;
184
	font-size: 100%;
185
}
186

  
187
div#new-field br {
188
	display: none;
189
}
190

  
191
div#new-field p.commands {
192
	float: right;
193
	margin-top: -17px;
194
	margin-right: 3px;
195
}
196

  
197
div.WorkflowStatusWidget {
198
	border-left: 1px solid black;
199
}
200

  
201
p#breadcrumb {
202
	background: #e6e6e6;
203
	-moz-border-radius: 6px;
204
	padding: 3px 8px;
205
	font-size: 80%;
206
	border: 1px solid #bfbfbf;
207
}
208

  
209
/** steps **/
210
#steps {
211
	height: 32px;
212
	margin-bottom: 1em;
213
	background: #f0f0f0;
214
	color: #aaa;
215
}
216

  
217
#steps ol {
218
	list-style: none;
219
	padding: 0 20px;
220
}
221

  
222
#steps li {
223
	display: inline;
224
	padding-right: 1em;
225
	display: block;
226
	float: left;
227
	width: 30%;
228
	list-style: none;
229
}
230

  
231
#steps ol ul {
232
	display: none;
233
}
234

  
235
#steps span.marker {
236
	font-size: 26px;
237
	padding: 2px 9px;
238
	font-weight: bold;
239
	color: white;
240
	text-align: center;
241
	background: #ddd;
242
	border: 1px solid #ddd;
243
	-moz-border-radius: 0.7ex;
244
}
245

  
246
#steps li.current span.marker {
247
	background: #ffa500;
248
	border: 1px solid #ffc400;
249
}
250

  
251
#steps span.label {
252
	font-size: 90%;
253
}
254

  
255
#steps li.current span.label {
256
	color: black;
257
}
258

  
259
#steps ol ul {
260
	display: none;
261
}
262

  
263

  
264
/** logs **/
265
form#other-log-select {
266
	margin-top: 2em;
267
	padding-top: 1em;
268
	border-top: 1px solid #999;
269
}
270

  
271
form#other-log-select select {
272
	margin: 0 1em;
273
}
274

  
275
tr.level-error td {
276
	border: 1px solid #800;
277
	background: red;
278
}
279

  
280
tr.level-error td.message {
281
	font-weight: bold;
282
}
283

  
admin/ezt.py
1
#!/usr/bin/env python
2
"""ezt.py -- easy templating
3

  
4
ezt templates are simply text files in whatever format you so desire
5
(such as XML, HTML, etc.) which contain directives sprinkled
6
throughout.  With these directives it is possible to generate the
7
dynamic content from the ezt templates.
8

  
9
These directives are enclosed in square brackets.  If you are a
10
C-programmer, you might be familar with the #ifdef directives of the C
11
preprocessor 'cpp'.  ezt provides a similar concept.  Additionally EZT
12
has a 'for' directive, which allows it to iterate (repeat) certain
13
subsections of the template according to sequence of data items
14
provided by the application.
15

  
16
The final rendering is performed by the method generate() of the Template
17
class.  Building template instances can either be done using external
18
EZT files (convention: use the suffix .ezt for such files):
19

  
20
    >>> template = Template("../templates/log.ezt")
21

  
22
or by calling the parse() method of a template instance directly with 
23
a EZT template string:
24

  
25
    >>> template = Template()
26
    >>> template.parse('''<html><head>
27
    ... <title>[title_string]</title></head>
28
    ... <body><h1>[title_string]</h1>
29
    ...    [for a_sequence] <p>[a_sequence]</p>
30
    ...    [end] <hr>
31
    ...    The [person] is [if-any state]in[else]out[end].
32
    ... </body>
33
    ... </html>
34
    ... ''')
35

  
36
The application should build a dictionary 'data' and pass it together
37
with the output fileobject to the templates generate method:
38

  
39
    >>> data = {'title_string' : "A Dummy Page",
40
    ...         'a_sequence' : ['list item 1', 'list item 2', 'another element'],
41
    ...         'person': "doctor",
42
    ...         'state' : None }
43
    >>> import sys
44
    >>> template.generate(sys.stdout, data)
45
    <html><head>
46
    <title>A Dummy Page</title></head>
47
    <body><h1>A Dummy Page</h1>
48
     <p>list item 1</p>
49
     <p>list item 2</p>
50
     <p>another element</p>
51
     <hr>
52
    The doctor is out.
53
    </body>
54
    </html>
55

  
56
Template syntax error reporting should be improved.  Currently it is 
57
very sparse (template line numbers would be nice):
58

  
59
    >>> Template().parse("[if-any where] foo [else] bar [end unexpected args]")
60
    Traceback (innermost last):
61
      File "<stdin>", line 1, in ?
62
      File "ezt.py", line 220, in parse
63
        self.program = self._parse(text)
64
      File "ezt.py", line 275, in _parse
65
        raise ArgCountSyntaxError(str(args[1:]))
66
    ArgCountSyntaxError: ['unexpected', 'args']
67
    >>> Template().parse("[if unmatched_end]foo[end]")
68
    Traceback (innermost last):
69
      File "<stdin>", line 1, in ?
70
      File "ezt.py", line 206, in parse
71
        self.program = self._parse(text)
72
      File "ezt.py", line 266, in _parse
73
        raise UnmatchedEndError()
74
    UnmatchedEndError
75

  
76

  
77
Directives
78
==========
79

  
80
 Several directives allow the use of dotted qualified names refering to objects
81
 or attributes of objects contained in the data dictionary given to the 
82
 .generate() method.
83

  
84
 Qualified names
85
 ---------------
86

  
87
   Qualified names have two basic forms: a variable reference, or a string
88
   constant. References are a name from the data dictionary with optional
89
   dotted attributes (where each intermediary is an object with attributes,
90
   of course).
91

  
92
   Examples:
93

  
94
     [varname]
95

  
96
     [ob.attr]
97

  
98
     ["string"]
99

  
100
 Simple directives
101
 -----------------
102

  
103
   [QUAL_NAME]
104

  
105
   This directive is simply replaced by the value of the qualified name.
106
   If the value is a number it's converted to a string before being 
107
   outputted. If it is None, nothing is outputted. If it is a python file
108
   object (i.e. any object with a "read" method), it's contents are
109
   outputted. If it is a callback function (any callable python object
110
   is assumed to be a callback function), it is invoked and passed an EZT
111
   printer function as an argument.
112

  
113
   [QUAL_NAME QUAL_NAME ...]
114

  
115
   If the first value is a callback function, it is invoked with the
116
   output file pointer as a first argument, and the rest of the values as
117
   additional arguments.
118

  
119
   Otherwise, the first value defines a substitution format, specifying
120
   constant text and indices of the additional arguments. The arguments
121
   are substituted and the result is inserted into the output stream.
122

  
123
   Example:
124
     ["abc %0 def %1 ghi %0" foo bar.baz]
125

  
126
   Note that the first value can be any type of qualified name -- a string
127
   constant or a variable reference. Use %% to substitute a percent sign.
128
   Argument indices are 0-based.
129

  
130
   [include "filename"]  or [include QUAL_NAME]
131

  
132
   This directive is replaced by content of the named include file. Note
133
   that a string constant is more efficient -- the target file is compiled
134
   inline. In the variable form, the target file is compiled and executed
135
   at runtime.
136

  
137
 Block directives
138
 ----------------
139

  
140
   [for QUAL_NAME] ... [end]
141
   
142
   The text within the [for ...] directive and the corresponding [end]
143
   is repeated for each element in the sequence referred to by the
144
   qualified name in the for directive.  Within the for block this
145
   identifiers now refers to the actual item indexed by this loop
146
   iteration.
147

  
148
   [if-any QUAL_NAME [QUAL_NAME2 ...]] ... [else] ... [end]
149

  
150
   Test if any QUAL_NAME value is not None or an empty string or list.
151
   The [else] clause is optional.  CAUTION: Numeric values are
152
   converted to string, so if QUAL_NAME refers to a numeric value 0,
153
   the then-clause is substituted!
154

  
155
   [if-index INDEX_FROM_FOR odd] ... [else] ... [end]
156
   [if-index INDEX_FROM_FOR even] ... [else] ... [end]
157
   [if-index INDEX_FROM_FOR first] ... [else] ... [end]
158
   [if-index INDEX_FROM_FOR last] ... [else] ... [end]
159
   [if-index INDEX_FROM_FOR NUMBER] ... [else] ... [end]
160

  
161
   These five directives work similar to [if-any], but are only useful
162
   within a [for ...]-block (see above).  The odd/even directives are
163
   for example useful to choose different background colors for
164
   adjacent rows in a table.  Similar the first/last directives might
165
   be used to remove certain parts (for example "Diff to previous"
166
   doesn't make sense, if there is no previous).
167

  
168
   [is QUAL_NAME STRING] ... [else] ... [end]
169
   [is QUAL_NAME QUAL_NAME] ... [else] ... [end]
170

  
171
   The [is ...] directive is similar to the other conditional
172
   directives above.  But it allows to compare two value references or
173
   a value reference with some constant string.
174

  
175
   [define VARIABLE] ... [end]
176

  
177
   The [define ...] directive allows you to create and modify template
178
   variables from within the template itself.  Essentially, any data
179
   between inside the [define ...] and its matching [end] will be
180
   expanded using the other template parsing and output generation
181
   rules, and then stored as a string value assigned to the variable
182
   VARIABLE.  The new (or changed) variable is then available for use
183
   with other mechanisms such as [is ...] or [if-any ...], as long as
184
   they appear later in the template.
185

  
186
   [format STRING] ... [end]
187

  
188
   The format directive controls how the values substituted into
189
   templates are escaped before they are put into the output stream. It
190
   has no effect on the literal text of the templates, only the output
191
   from [QUAL_NAME ...] directives. STRING can be one of "raw" "html" 
192
   or "xml". The "raw" mode leaves the output unaltered. The "html" and
193
   "xml" modes escape special characters using entity escapes (like
194
   &quot; and &gt;)
195
"""
196
#
197
# Copyright (C) 2001-2005 Greg Stein. All Rights Reserved.
198
#
199
# Redistribution and use in source and binary forms, with or without 
200
# modification, are permitted provided that the following conditions are 
201
# met:
202
#
203
# * Redistributions of source code must retain the above copyright 
204
#   notice, this list of conditions and the following disclaimer. 
205
#
206
# * Redistributions in binary form must reproduce the above copyright 
207
#   notice, this list of conditions and the following disclaimer in the 
208
#   documentation and/or other materials provided with the distribution. 
209
#
210
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
211
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
212
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
213
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 
214
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
215
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
216
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
217
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
218
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
219
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
220
# POSSIBILITY OF SUCH DAMAGE.
221
#
222
#
223
# This software is maintained by Greg and is available at:
224
#    http://svn.webdav.org/repos/projects/ezt/trunk/
225
#
226

  
227
import string
228
import re
229
from types import StringType, IntType, FloatType, LongType
230
import os
231
import cgi
232
try:
233
  import cStringIO
234
except ImportError:
235
  import StringIO
236
  cStringIO = StringIO
237

  
238
#
239
# Formatting types
240
#
241
FORMAT_RAW = 'raw'
242
FORMAT_HTML = 'html'
243
FORMAT_XML = 'xml'
244

  
245
#
246
# This regular expression matches three alternatives:
247
#   expr: DIRECTIVE | BRACKET | COMMENT
248
#   DIRECTIVE: '[' ITEM (whitespace ITEM)* ']
249
#   ITEM: STRING | NAME
250
#   STRING: '"' (not-slash-or-dquote | '\' anychar)* '"'
251
#   NAME: (alphanum | '_' | '-' | '.')+
252
#   BRACKET: '[[]'
253
#   COMMENT: '[#' not-rbracket* ']'
254
#
255
# When used with the split() method, the return value will be composed of
256
# non-matching text and the two paren groups (DIRECTIVE and BRACKET). Since
257
# the COMMENT matches are not placed into a group, they are considered a
258
# "splitting" value and simply dropped.
259
#
260
_item = r'(?:"(?:[^\\"]|\\.)*"|[-\w.]+)'
261
_re_parse = re.compile(r'\[(%s(?: +%s)*)\]|(\[\[\])|\[#[^\]]*\]' % (_item, _item))
262

  
263
_re_args = re.compile(r'"(?:[^\\"]|\\.)*"|[-\w.]+')
264

  
265
# block commands and their argument counts
266
_block_cmd_specs = { 'if-index':2, 'for':1, 'is':2, 'define':1, 'format':1 }
267
_block_cmds = _block_cmd_specs.keys()
268

  
269
# two regular expresssions for compressing whitespace. the first is used to
270
# compress any whitespace including a newline into a single newline. the
271
# second regex is used to compress runs of whitespace into a single space.
272
_re_newline = re.compile('[ \t\r\f\v]*\n\\s*')
273
_re_whitespace = re.compile(r'\s\s+')
274

  
275
# this regex is used to substitute arguments into a value. we split the value,
276
# replace the relevant pieces, and then put it all back together. splitting
277
# will produce a list of: TEXT ( splitter TEXT )*. splitter will be '%' or
278
# an integer.
279
_re_subst = re.compile('%(%|[0-9]+)')
280

  
281
class Template:
282

  
283
  _printers = {
284
    FORMAT_RAW  : '_cmd_print',
285
    FORMAT_HTML : '_cmd_print_html',
286
    FORMAT_XML  : '_cmd_print_xml',
287
    }
288

  
289
  def __init__(self, fname=None, compress_whitespace=1,
290
               base_format=FORMAT_RAW):
291
    self.compress_whitespace = compress_whitespace
292
    if fname:
293
      self.parse_file(fname, base_format)
294

  
295
  def parse_file(self, fname, base_format=FORMAT_RAW):
296
    "fname -> a string object with pathname of file containg an EZT template."
297

  
298
    self.parse(_FileReader(fname), base_format)
299

  
300
  def parse(self, text_or_reader, base_format=FORMAT_RAW):
301
    """Parse the template specified by text_or_reader.
302

  
303
    The argument should be a string containing the template, or it should
304
    specify a subclass of ezt.Reader which can read templates. The base
305
    format for printing values is given by base_format.
306
    """
307
    if not isinstance(text_or_reader, Reader):
308
      # assume the argument is a plain text string
309
      text_or_reader = _TextReader(text_or_reader)
310

  
311
    printer = getattr(self, self._printers[base_format])
312
    self.program = self._parse(text_or_reader, base_printer=printer)
313

  
314
  def generate(self, fp, data):
315
    if hasattr(data, '__getitem__') or callable(getattr(data, 'keys', None)):
316
      # a dictionary-like object was passed. convert it to an
317
      # attribute-based object.
318
      class _data_ob:
319
        def __init__(self, d):
320
          vars(self).update(d)
321
      data = _data_ob(data)
322

  
323
    ctx = _context()
324
    ctx.data = data
325
    ctx.for_index = { }
326
    ctx.defines = { }
327
    self._execute(self.program, fp, ctx)
328

  
329
  def _parse(self, reader, for_names=None, file_args=(), base_printer=None):
330
    """text -> string object containing the template.
331

  
332
    This is a private helper function doing the real work for method parse.
333
    It returns the parsed template as a 'program'.  This program is a sequence
334
    made out of strings or (function, argument) 2-tuples.
335

  
336
    Note: comment directives [# ...] are automatically dropped by _re_parse.
337
    """
338

  
339
    # parse the template program into: (TEXT DIRECTIVE BRACKET)* TEXT
340
    parts = _re_parse.split(reader.text)
341

  
342
    program = [ ]
343
    stack = [ ]
344
    if not for_names:
345
      for_names = [ ]
346

  
347
    if base_printer:
348
      printers = [ base_printer ]
349
    else:
350
      printers = [ self._cmd_print ]
351

  
352
    for i in range(len(parts)):
353
      piece = parts[i]
354
      which = i % 3  # discriminate between: TEXT DIRECTIVE BRACKET
355
      if which == 0:
356
        # TEXT. append if non-empty.
357
        if piece:
358
          if self.compress_whitespace:
359
            piece = _re_whitespace.sub(' ', _re_newline.sub('\n', piece))
360
          program.append(piece)
361
      elif which == 2:
362
        # BRACKET directive. append '[' if present.
363
        if piece:
364
          program.append('[')
365
      elif piece:
366
        # DIRECTIVE is present.
367
        args = _re_args.findall(piece)
368
        cmd = args[0]
369
        if cmd == 'else':
370
          if len(args) > 1:
371
            raise ArgCountSyntaxError(str(args[1:]))
372
          ### check: don't allow for 'for' cmd
373
          idx = stack[-1][1]
374
          true_section = program[idx:]
375
          del program[idx:]
376
          stack[-1][3] = true_section
377
        elif cmd == 'end':
378
          if len(args) > 1:
379
            raise ArgCountSyntaxError(str(args[1:]))
380
          # note: true-section may be None
381
          try:
382
            cmd, idx, args, true_section = stack.pop()
383
          except IndexError:
384
            raise UnmatchedEndError()
385
          else_section = program[idx:]
386
          if cmd == 'format':
387
            printers.pop()
388
          else:
389
            func = getattr(self, '_cmd_' + re.sub('-', '_', cmd))
390
            program[idx:] = [ (func, (args, true_section, else_section)) ]
391
            if cmd == 'for':
392
              for_names.pop()
393
        elif cmd in _block_cmds:
394
          if len(args) > _block_cmd_specs[cmd] + 1:
395
            raise ArgCountSyntaxError(str(args[1:]))
396
          ### this assumes arg1 is always a ref unless cmd is 'define'
397
          if cmd != 'define':
398
            args[1] = _prepare_ref(args[1], for_names, file_args)
399

  
400
          # handle arg2 for the 'is' command
401
          if cmd == 'is':
402
            args[2] = _prepare_ref(args[2], for_names, file_args)
403
          elif cmd == 'for':
404
            for_names.append(args[1][0])  # append the refname
405
          elif cmd == 'format':
406
            if args[1][0]:
407
              raise BadFormatConstantError(str(args[1:]))
408
            funcname = self._printers.get(args[1][1])
409
            if not funcname:
410
              raise UnknownFormatConstantError(str(args[1:]))
411
            printers.append(getattr(self, funcname))
412

  
413
          # remember the cmd, current pos, args, and a section placeholder
414
          stack.append([cmd, len(program), args[1:], None])
415
        elif cmd == 'include':
416
          if args[1][0] == '"':
417
            include_filename = args[1][1:-1]
418
            f_args = [ ]
419
            for arg in args[2:]:
420
              f_args.append(_prepare_ref(arg, for_names, file_args))
421
            program.extend(self._parse(reader.read_other(include_filename),
422
                                       for_names, f_args, printers[-1]))
423
          else:
424
            if len(args) != 2:
425
              raise ArgCountSyntaxError(str(args))
426
            program.append((self._cmd_include,
427
                            (_prepare_ref(args[1], for_names, file_args),
428
                             reader)))
429
        elif cmd == 'if-any':
430
          f_args = [ ]
431
          for arg in args[1:]:
432
            f_args.append(_prepare_ref(arg, for_names, file_args))
433
          stack.append(['if-any', len(program), f_args, None])
434
        else:
435
          # implied PRINT command
436
          f_args = [ ]
437
          for arg in args:
438
            f_args.append(_prepare_ref(arg, for_names, file_args))
439
          program.append((printers[-1], f_args))
440

  
441
    if stack:
442
      ### would be nice to say which blocks...
443
      raise UnclosedBlocksError()
444
    return program
445

  
446
  def _execute(self, program, fp, ctx):
447
    """This private helper function takes a 'program' sequence as created
448
    by the method '_parse' and executes it step by step.  strings are written
449
    to the file object 'fp' and functions are called.
450
    """
451
    for step in program:
452
      if isinstance(step, StringType):
453
        fp.write(step)
454
      else:
455
        step[0](step[1], fp, ctx)
456

  
457
  def _cmd_print(self, valref, fp, ctx):
458
    _write_value(valref, fp, ctx)
459

  
460
  def _cmd_print_html(self, valref, fp, ctx):
461
    _write_value(valref, fp, ctx, cgi.escape)
462

  
463
  def _cmd_print_xml(self, valref, fp, ctx):
464
    ### use the same quoting as HTML for now
465
    self._cmd_print_html(valref, fp, ctx)
466

  
467
  def _cmd_include(self, (valref, reader), fp, ctx):
468
    fname = _get_value(valref, ctx)
469
    ### note: we don't have the set of for_names to pass into this parse.
470
    ### I don't think there is anything to do but document it. we also
471
    ### don't have a current format (since that is a compile-time concept).
472
    self._execute(self._parse(reader.read_other(fname)), fp, ctx)
473

  
474
  def _cmd_if_any(self, args, fp, ctx):
475
    "If any value is a non-empty string or non-empty list, then T else F."
476
    (valrefs, t_section, f_section) = args
477
    value = 0
478
    for valref in valrefs:
479
      if _get_value(valref, ctx):
480
        value = 1
481
        break
482
    self._do_if(value, t_section, f_section, fp, ctx)
483

  
484
  def _cmd_if_index(self, args, fp, ctx):
485
    ((valref, value), t_section, f_section) = args
486
    list, idx = ctx.for_index[valref[0]]
487
    if value == 'even':
488
      value = idx % 2 == 0
489
    elif value == 'odd':
490
      value = idx % 2 == 1
491
    elif value == 'first':
492
      value = idx == 0
493
    elif value == 'last':
494
      value = idx == len(list)-1
495
    else:
496
      value = idx == int(value)
497
    self._do_if(value, t_section, f_section, fp, ctx)
498

  
499
  def _cmd_is(self, args, fp, ctx):
500
    ((left_ref, right_ref), t_section, f_section) = args
501
    value = _get_value(right_ref, ctx)
502
    value = string.lower(_get_value(left_ref, ctx)) == string.lower(value)
503
    self._do_if(value, t_section, f_section, fp, ctx)
504

  
505
  def _do_if(self, value, t_section, f_section, fp, ctx):
506
    if t_section is None:
507
      t_section = f_section
508
      f_section = None
509
    if value:
510
      section = t_section
511
    else:
512
      section = f_section
513
    if section is not None:
514
      self._execute(section, fp, ctx)
515

  
516
  def _cmd_for(self, args, fp, ctx):
517
    ((valref,), unused, section) = args
518
    list = _get_value(valref, ctx)
519
    if isinstance(list, StringType):
520
      raise NeedSequenceError()
521
    refname = valref[0]
522
    ctx.for_index[refname] = idx = [ list, 0 ]
523
    for item in list:
524
      self._execute(section, fp, ctx)
525
      idx[1] = idx[1] + 1
526
    del ctx.for_index[refname]
527

  
528
  def _cmd_define(self, args, fp, ctx):
529
    ((name,), unused, section) = args
530
    valfp = cStringIO.StringIO()
531
    if section is not None:
532
      self._execute(section, valfp, ctx)
533
    ctx.defines[name] = valfp.getvalue()
534

  
535
def boolean(value):
536
  "Return a value suitable for [if-any bool_var] usage in a template."
537
  if value:
538
    return 'yes'
539
  return None
540

  
541

  
542
def _prepare_ref(refname, for_names, file_args):
543
  """refname -> a string containing a dotted identifier. example:"foo.bar.bang"
544
  for_names -> a list of active for sequences.
545

  
546
  Returns a `value reference', a 3-tuple made out of (refname, start, rest), 
547
  for fast access later.
548
  """
549
  # is the reference a string constant?
550
  if refname[0] == '"':
551
    return None, refname[1:-1], None
552

  
553
  parts = string.split(refname, '.')
554
  start = parts[0]
555
  rest = parts[1:]
556

  
557
  # if this is an include-argument, then just return the prepared ref
558
  if start[:3] == 'arg':
559
    try:
560
      idx = int(start[3:])
561
    except ValueError:
562
      pass
563
    else:
564
      if idx < len(file_args):
565
        orig_refname, start, more_rest = file_args[idx]
566
        if more_rest is None:
567
          # the include-argument was a string constant
568
          return None, start, None
569

  
570
        # prepend the argument's "rest" for our further processing
571
        rest[:0] = more_rest
572

  
573
        # rewrite the refname to ensure that any potential 'for' processing
574
        # has the correct name
575
        ### this can make it hard for debugging include files since we lose
576
        ### the 'argNNN' names
577
        if not rest:
578
          return start, start, [ ]
579
        refname = start + '.' + string.join(rest, '.')
580

  
581
  if for_names:
582
    # From last to first part, check if this reference is part of a for loop
583
    for i in range(len(parts), 0, -1):
584
      name = string.join(parts[:i], '.')
585
      if name in for_names:
586
        return refname, name, parts[i:]
587

  
588
  return refname, start, rest
589

  
590
def _get_value((refname, start, rest), ctx):
591
  """(refname, start, rest) -> a prepared `value reference' (see above).
592
  ctx -> an execution context instance.
593

  
594
  Does a name space lookup within the template name space.  Active 
595
  for blocks take precedence over data dictionary members with the 
596
  same name.
597
  """
598
  if rest is None:
599
    # it was a string constant
600
    return start
601

  
602
  # get the starting object
603
  if ctx.for_index.has_key(start):
604
    list, idx = ctx.for_index[start]
605
    ob = list[idx]
606
  elif ctx.defines.has_key(start):
607
    ob = ctx.defines[start]
608
  elif hasattr(ctx.data, start):
609
    ob = getattr(ctx.data, start)
610
  else:
611
    raise UnknownReference(refname)
612

  
613
  # walk the rest of the dotted reference
614
  for attr in rest:
615
    try:
616
      ob = getattr(ob, attr)
617
    except AttributeError:
618
      raise UnknownReference(refname)
619

  
620
  # make sure we return a string instead of some various Python types
621
  if isinstance(ob, IntType) \
622
         or isinstance(ob, LongType) \
623
         or isinstance(ob, FloatType):
624
    return str(ob)
625
  if ob is None:
626
    return ''
627

  
628
  # string or a sequence
629
  return ob
630

  
631
def _write_value(valrefs, fp, ctx, format=lambda s: s):
632
  value = _get_value(valrefs[0], ctx)
633
  args = map(lambda valref, ctx=ctx: _get_value(valref, ctx), valrefs[1:])
634

  
635
  # if the value has a 'read' attribute, then it is a stream: copy it
636
  if hasattr(value, 'read'):
637
    while 1:
638
      chunk = value.read(16384)
639
      if not chunk:
640
        break
641
      fp.write(format(chunk))
642

  
643
  # value is a callback function: call with file pointer and extra args
644
  elif callable(value):
645
    apply(value, [fp] + args)
646

  
647
  # value is a substitution pattern
648
  elif args:
649
    parts = _re_subst.split(value)
650
    for i in range(len(parts)):
651
      piece = parts[i]
652
      if i%2 == 1 and piece != '%':
653
        idx = int(piece)
654
        if idx < len(args):
655
          piece = args[idx]
656
        else:
657
          piece = '<undef>'
658
      if format:
659
        fp.write(format(piece))
660

  
661
  # plain old value, write to output
662
  else:
663
    fp.write(format(value))
664

  
665

  
666
class _context:
667
  """A container for the execution context"""
668

  
669

  
670
class Reader:
671
  "Abstract class which allows EZT to detect Reader objects."
672

  
673
class _FileReader(Reader):
674
  """Reads templates from the filesystem."""
675
  def __init__(self, fname):
676
    self.text = open(fname, 'rb').read()
677
    self._dir = os.path.dirname(fname)
678
  def read_other(self, relative):
679
    return _FileReader(os.path.join(self._dir, relative))
680

  
681
class _TextReader(Reader):
682
  """'Reads' a template from provided text."""
683
  def __init__(self, text):
684
    self.text = text
685
  def read_other(self, relative):
686
    raise BaseUnavailableError()
687

  
688

  
689
class EZTException(Exception):
690
  """Parent class of all EZT exceptions."""
691

  
692
class ArgCountSyntaxError(EZTException):
693
  """A bracket directive got the wrong number of arguments."""
694

  
695
class UnknownReference(EZTException):
696
  """The template references an object not contained in the data dictionary."""
697

  
698
class NeedSequenceError(EZTException):
699
  """The object dereferenced by the template is no sequence (tuple or list)."""
700

  
701
class UnclosedBlocksError(EZTException):
702
  """This error may be simply a missing [end]."""
703

  
704
class UnmatchedEndError(EZTException):
705
  """This error may be caused by a misspelled if directive."""
706

  
707
class BaseUnavailableError(EZTException):
708
  """Base location is unavailable, which disables includes."""
709

  
710
class BadFormatConstantError(EZTException):
711
  """Format specifiers must be string constants."""
712

  
713
class UnknownFormatConstantError(EZTException):
714
  """The format specifier is an unknown value."""
715

  
716

  
717
# --- standard test environment ---
718
def test_parse():
719
  assert _re_parse.split('[a]') == ['', '[a]', None, '']
720
  assert _re_parse.split('[a] [b]') == \
721
         ['', '[a]', None, ' ', '[b]', None, '']
722
  assert _re_parse.split('[a c] [b]') == \
723
         ['', '[a c]', None, ' ', '[b]', None, '']
724
  assert _re_parse.split('x [a] y [b] z') == \
725
         ['x ', '[a]', None, ' y ', '[b]', None, ' z']
726
  assert _re_parse.split('[a "b" c "d"]') == \
727
         ['', '[a "b" c "d"]', None, '']
728
  assert _re_parse.split(r'["a \"b[foo]" c.d f]') == \
729
         ['', '["a \\"b[foo]" c.d f]', None, '']
730

  
731
def _test(argv):
732
  import doctest, ezt           
733
  verbose = "-v" in argv
734
  return doctest.testmod(ezt, verbose=verbose)
735

  
736
if __name__ == "__main__":
737
  # invoke unit test for this module:
738
  import sys
739
  sys.exit(_test(sys.argv)[0])
admin/menu.ptl
1
import os
2
import quixote
3
from quixote import get_response
4
#from wcs import storage
5
#from wcs import misc
6
#from wcs.users import User
7

  
8
items = [
9
    ('apache', 'Apache')]
10

  
11
#    ('forms', N_('Forms')),
12
    #('consultations', N_('Consultations')),
13
#    ('workflows', N_('Workflows')),
14
#    ('users', N_('Users')),
15
#    ('roles', N_('Roles')),
16
#    ('categories', N_('Categories')),
17
#    ('logger', N_('Logs')),
18
#    ('settings', N_('Settings')),
19
#    ('/', N_('WCS Form Server'))]
20

  
21
def generate_header_menu [html] (selected = None):
22
    s = ["""<ul id="menu">\n"""]
23
    base_url = quixote.get_request().environ['SCRIPT_NAME'] + '/admin'
24
#    features = misc.cfg.get('misc', {}).get('features', 'both')
25
#    show_logger = misc.cfg.get('debug', {}).get('logger', False)
26
    for k, v in items:
27
        if k == '/':
28
            continue # skip root
29
#        if k == 'logger' and not show_logger:
30
#            continue
31
#        if features == 'forms' and k == 'consultations':
32
#            continue
33
#        if features == 'consultations' and k == 'forms':
34
#            continue
35
        if k == selected:
36
            s.append('<li class="active">')
37
        else:
38
            s.append('<li>')
39
        s.append('<a href="%s/%s/">%s</a></li>\n' % (base_url, k, v))
40
    s.append('</ul>\n')
41
    return ''.join(s)
42

  
43
#def generate_user_info [html] ():
44
#    session = quixote.get_session()
45
#    if not session or not session.user:
46
#        return ''
47
#    try:
48
#        user = User.get(session.user)
49
#        username = user.name
50
#    except KeyError:
51
#        username = _('Unknown')
52
#    logout_url = quixote.get_request().environ['SCRIPT_NAME'] + '/logout'
53
#    """<ul class="user-info">
54
#  <li class="ui-name">%s</li>
55
#  <li class="ui-logout"><a href="%s">%s</a></li>
56
#</ul>""" % (username, logout_url, _('logout'))
57

  
58

  
59
def html_top [html] (section, title = None, scripts = None):
60
    header_menu = generate_header_menu(section)
61
#    user_info = generate_user_info()
62
    subtitle = ''
63
    for s in items:
64
        if s[0] == section:
65
            subtitle = _(s[1])
66
    if not title:
67
        title = ''
68
    else:
69
        title = ' - ' + title
70
    if not scripts:
71
        scripts = ''
72
    else:
73
        scripts = '\n'.join(['<script src="%s" type="text/javascript"></script>' % x for x in scripts])
74

  
75
    sitetitle = 'Larpe Administration'
76
    if title:
77
        sitetitle += ' - '
78

  
79
    admin_ezt = True
80
    
81

  
82

  
83
    """
84
<?xml version="1.0" encoding="utf-8"?>
85
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
86
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
87
<html xmlns="http://www.w3.org/1999/xhtml">
88
<head>
89
<title>Administration de Larpe</title>
90
<link rel="stylesheet" type="text/css" href="/css/larpe-admin.css"/>
91
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
92
</head>
93
<body>
94
<div id="header">%s
95
</div>
96
<div id="main-content">
97
<p id="breadcrumb"><a href="/">Administration</a></p>
98

  
99
<h1>Relai inverse Liberty Alliance</h1>
100
 
101
</div>
102
<div id="footer">
103
<p id="lasso">Powered by Lasso</p>
104
</div>
105
</body>
106
</html>
107
""" % header_menu
108

  
109
#<ul id="menu">
110
#<li><a href="/admin/apache/">Apache</a></li>
111
#</ul>
112
#</div>
113
#<div id="main-content">
114

  
115

  
116
	#    get_response().
117
#    get_response().filter.update(locals())
118

  
119
def error_page [html] (section, error):
120
    html_top(section, title = _('Error'))
121
    '<div id="error-page">'
122
    '<h2>%s</h2>' % _('Error')
123
    '<p>%s</p>' % error
124
    '</div>'
125

  
126
#def command_icon [html] (url, type, label = None, icon = None):
127
#    icons = {
128
#        'edit': 'stock_edit_16.png',
129
#        'add': 'stock_add_16.png',
130
#        'remove': 'stock_remove_16.png',
131
#        'duplicate': 'stock_copy_16.png',
132
#        'view': 'view_16.png',
133
#    }
134
#    labels = {
135
#        'add': N_('Add'),
136
#        'edit': N_('Edit'),
137
#        'remove': N_('Remove'),
138
#        'duplicate': N_('Duplicate'),
139
#        'view': N_('View'),
140
#        }
141
#    if not label:
142
#        label = _(labels[str(type)])
143
#    if not icon:
144
#        icon = icons[str(type)]
145
#    if url:
146
#        '''<span class="%(type)s">
147
#  <a href="%(url)s"><img src="/images/%(icon)s" alt="%(label)s" title="%(label)s" /></a>
148
#</span>''' % locals()
149
#    else:
150
#        # no url -> image button
151
#        '''<span class="%(type)s">
152
#  <input type="image" src="/images/%(icon)s" alt="%(label)s" title="%(label)s" />
153
#</span>''' % locals()
admin/root.ptl
1
from quixote.directory import Directory
2

  
3
#from menu import html_top
4
from template import generate_html
5
from apache import Apache
6

  
7
class RootDirectory(Directory):
8
	_q_exports = ['', 'apache']
9

  
10
	apache = Apache()
11

  
12
	def _q_index [html] (self):
13
		text = """<p>Choisissez dans le menu ce que vous souhaitez administrer</p>"""
14
		generate_html(None, text)
15
		#html_top('/')
admin/template.ptl
1
from cStringIO import StringIO
2

  
3
from quixote.html import htmltext
4

  
5
import ezt
6

  
7
#<?xml version="1.0" encoding="utf-8"?>
8
#    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
9

  
10

  
11
ADMIN_TEMPLATE_EZT = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
12
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
13
<html xmlns="http://www.w3.org/1999/xhtml">
14
  <head>
15
    <title>Administration de Larpe - [page_title]</title>
16
    <link rel="stylesheet" type="text/css" href="/css/larpe-admin.css"/>
17
  </head>
18
  <body>
19
  <div id="header">[main_menu]</div>
20
  <div id="main-content">
21
    <p id="breadcrumb">[breadcrumb]</p>
22
    <h1>[subtitle]</h1>
23

  
24
    [body]
25

  
26
  </div>
27
  <div id="footer">
28
   <p id="lasso">Powered by Lasso</p>
29
  </div>
30
 </body>
31
</html>"""
32

  
33
admin_template = ezt.Template()
34
admin_template.parse(ADMIN_TEMPLATE_EZT)
35

  
36
menu_items = [
37
    ('apache', 'Apache')]
38

  
39
def generate_main_menu(selected = None):
40
    s = '<ul id="menu">\n'
41
    for k, v in menu_items:
42
        if k == selected:
43
            s += '<li class="active">'
44
        else:
45
            s += '<li>'
46
        s += '<a href="/%s/">%s</a></li>\n' % (k, v)
47
    s + '</ul>\n'
48
    return s
49

  
50
def generate_breadcrumb(section = None):
51
	s = '<a href="/">Administration</a>'
52
	if section is not None:
53
	    for k, v in menu_items:
54
	    	if k == section:
55
				s += ' > <a href="/%s">%s</a>' % (k, v)
56
				break
57
	return s
58

  
59
def generate_html(section, body):
60
	if section is not None:
61
		section = str(section)
62
	body = str(body)
63
	main_menu = generate_main_menu(section)
64
	breadcrumb = generate_breadcrumb(section)
65
	page_title = ''
66
	if section is not None:
67
	    for k, v in menu_items:
68
	    	if k == section:
69
				page_title = v				
70
				break
71
	subtitle = page_title
72
	
73
	fd = StringIO()
74
	admin_template.generate(fd, locals())
75
	return htmltext(fd.getvalue())
doc/vhost-eo
1
<VirtualHost 127.0.0.6:80>
2
  ServerName entrouvert.test.org
3
  ServerAdmin dlaniel@entrouvert.com
4

  
5
  SetOutputFilter	proxy-html
6

  
7
  <Location />
8
    ProxyPass		http://www.entrouvert.com/
9
    ProxyPassReverse	http://www.entrouvert.com/
10
#    ProxyPassReverse	/
11
#    ProxyHTMLURLMap	/	/eo/
12
  </Location>
13

  
14
</VirtualHost>
doc/vhost-rp
1
NameVirtualHost 127.0.0.6:80
2
<VirtualHost 127.0.0.6:80>
3
  ServerName test.org
4
#  ServerAdmin dlaniel@entrouvert.com
5

  
6
#  LoadFile		/usr/lib/libxml2.so.2
7
  SetOutputFilter	proxy-html
8

  
9
#  ProxyPass /eo/ http://www.entrouvert.com/
10
#  ProxyPassReverse /eo/ http://www.entrouvert.com/
11

  
12
  <Location /eo/>
13
    ProxyPass		http://www.entrouvert.com/
14
    ProxyPassReverse	http://www.entrouvert.com/
15
#    ProxyPassReverse	/
16
    ProxyHTMLURLMap	/	/eo/
17
  </Location>
18

  
19
  <Location /linuxfr/>
20
    ProxyPass		http://linuxfr.org/
21
    ProxyPassReverse	http://linuxfr.org/
22
    ProxyHTMLURLMap	/	/linuxfr/
23
    ProxyHTMLURLMap	http://linuxfr.org/	/linuxfr/
24
  </Location>
25

  
26
  <Location /libe/>
27
    ProxyPass		http://www.liberation.fr/
28
#    ProxyPassReverse	/
29
    ProxyHTMLURLMap	/	  /libe/
30
    ProxyHTMLURLMap	http://www.liberation.fr/	/libe/
31
  </Location>
32

  
33
#  <Location *>
34
#    ProxyPass		!
35
#  </Location>
36

  
37
</VirtualHost>

Formats disponibles : Unified diff