mirror of
https://github.com/kakwa/ldapcherry
synced 2024-11-22 01:14:21 +01:00
cleanup in html template + tidylib
* few small cleanup in html template (avoid empty tbody, put id between quotes) * switch to tidylib to validate the html instead of the previous hack calling an external service (https://html5.validator.nu/) * remove the previous validator script * add exception for tidylib on empty <span> (these are required by bootstrap)
This commit is contained in:
parent
02357d886a
commit
a56c491ee1
@ -6,7 +6,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-12 column">
|
<div class="col-md-12 column">
|
||||||
<div class="well well-sm">
|
<div class="well well-sm">
|
||||||
<form method='POST' autocomplete="off" action='/adduser' role="form" class="form-signin" id=form>
|
<form method='POST' autocomplete="off" action='/adduser' role="form" class="form-signin" id="form">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Fill new user's attributes:</legend>
|
<legend>Fill new user's attributes:</legend>
|
||||||
${form | n}
|
${form | n}
|
||||||
|
@ -70,6 +70,6 @@
|
|||||||
<p class="muted credit"><a href="http://ldapcherry.readthedocs.org" target="_blank">LdapCherry</a> • © 2016 • Pierre-François Carpentier • Released under the MIT License</p>
|
<p class="muted credit"><a href="http://ldapcherry.readthedocs.org" target="_blank">LdapCherry</a> • © 2016 • Pierre-François Carpentier • Released under the MIT License</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
<script type="text/javascript" src="/static/js/alignforms.js"></script>
|
<script type="text/javascript" src="/static/js/alignforms.js"></script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<table id="RecordTable" class="table table-hover table-condensed">
|
<table id="RecordTable" class="table table-hover table-condensed">
|
||||||
<tbody>
|
|
||||||
% if not searchresult is None:
|
% if not searchresult is None:
|
||||||
|
<tbody>
|
||||||
%for attr in sorted(attrs_list.keys(), key=lambda attr: attrs_list[attr]['weight']):
|
%for attr in sorted(attrs_list.keys(), key=lambda attr: attrs_list[attr]['weight']):
|
||||||
<tr>
|
<tr>
|
||||||
% if attr in searchresult:
|
% if attr in searchresult:
|
||||||
@ -26,8 +26,8 @@
|
|||||||
% endif
|
% endif
|
||||||
</tr>
|
</tr>
|
||||||
% endfor
|
% endfor
|
||||||
%endif
|
|
||||||
</tbody>
|
</tbody>
|
||||||
|
%endif
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
2
setup.py
2
setup.py
@ -132,7 +132,7 @@ setup(
|
|||||||
description=small_description,
|
description=small_description,
|
||||||
long_description=description,
|
long_description=description,
|
||||||
install_requires=install_requires,
|
install_requires=install_requires,
|
||||||
tests_require=['pytest', 'pep8'],
|
tests_require=['pytest', 'pep8', 'pytidylib'],
|
||||||
cmdclass={'test': PyTest},
|
cmdclass={'test': PyTest},
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 3 - Alpha',
|
'Development Status :: 3 - Alpha',
|
||||||
|
@ -1,243 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# Copyright (c) 2007-2008 Mozilla Foundation
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
# copy of this software and associated documentation files (the "Software"),
|
|
||||||
# to deal in the Software without restriction, including without limitation
|
|
||||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
# and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
# Software is furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
# DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
from __future__ import print_function, with_statement
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
import string
|
|
||||||
import gzip
|
|
||||||
|
|
||||||
# Several "try" blocks for python2/3 differences (@secretrobotron)
|
|
||||||
try:
|
|
||||||
import httplib
|
|
||||||
except ImportError:
|
|
||||||
import http.client as httplib
|
|
||||||
|
|
||||||
try:
|
|
||||||
import urlparse
|
|
||||||
except ImportError:
|
|
||||||
import urllib.parse as urlparse
|
|
||||||
|
|
||||||
try:
|
|
||||||
from BytesIO import BytesIO
|
|
||||||
except ImportError:
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
try:
|
|
||||||
maketrans = str.maketrans
|
|
||||||
except AttributeError:
|
|
||||||
maketrans = string.maketrans
|
|
||||||
|
|
||||||
#
|
|
||||||
# Begin
|
|
||||||
#
|
|
||||||
extPat = re.compile(r'^.*\.([A-Za-z]+)$')
|
|
||||||
extDict = {
|
|
||||||
'html' : 'text/html',
|
|
||||||
'htm' : 'text/html',
|
|
||||||
'xhtml' : 'application/xhtml+xml',
|
|
||||||
'xht' : 'application/xhtml+xml',
|
|
||||||
'xml' : 'application/xml',
|
|
||||||
}
|
|
||||||
|
|
||||||
forceXml = False
|
|
||||||
forceHtml = False
|
|
||||||
gnu = False
|
|
||||||
errorsOnly = False
|
|
||||||
encoding = None
|
|
||||||
fileName = None
|
|
||||||
contentType = None
|
|
||||||
inputHandle = None
|
|
||||||
service = 'https://html5.validator.nu/'
|
|
||||||
|
|
||||||
argv = sys.argv[1:]
|
|
||||||
|
|
||||||
#
|
|
||||||
# Parse command line input
|
|
||||||
#
|
|
||||||
for arg in argv:
|
|
||||||
if '--help' == arg:
|
|
||||||
print('-h : force text/html')
|
|
||||||
print('-x : force application/xhtml+xml')
|
|
||||||
print('-g : GNU output')
|
|
||||||
print('-e : errors only (no info or warnings)')
|
|
||||||
print('--encoding=foo : declare encoding foo')
|
|
||||||
print('--service=url : the address of the HTML5 validator')
|
|
||||||
print('One file argument allowed. Leave out to read from stdin.')
|
|
||||||
sys.exit(0)
|
|
||||||
elif arg.startswith('--encoding='):
|
|
||||||
encoding = arg[11:]
|
|
||||||
elif arg.startswith('--service='):
|
|
||||||
service = arg[10:]
|
|
||||||
elif arg.startswith('--'):
|
|
||||||
sys.stderr.write('Unknown argument %s.\n' % arg)
|
|
||||||
sys.exit(2)
|
|
||||||
elif arg.startswith('-'):
|
|
||||||
for c in arg[1:]:
|
|
||||||
if 'x' == c:
|
|
||||||
forceXml = True
|
|
||||||
elif 'h' == c:
|
|
||||||
forceHtml = True
|
|
||||||
elif 'g' == c:
|
|
||||||
gnu = True
|
|
||||||
elif 'e' == c:
|
|
||||||
errorsOnly = True
|
|
||||||
else:
|
|
||||||
sys.stderr.write('Unknown argument %s.\n' % arg)
|
|
||||||
sys.exit(3)
|
|
||||||
else:
|
|
||||||
if fileName:
|
|
||||||
sys.stderr.write('Cannot have more than one input file.\n')
|
|
||||||
sys.exit(1)
|
|
||||||
fileName = arg
|
|
||||||
|
|
||||||
#
|
|
||||||
# Ensure a maximum of one forced output type
|
|
||||||
#
|
|
||||||
if forceXml and forceHtml:
|
|
||||||
sys.stderr.write('Cannot force HTML and XHTML at the same time.\n')
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Set contentType
|
|
||||||
#
|
|
||||||
if forceXml:
|
|
||||||
contentType = 'application/xhtml+xml'
|
|
||||||
elif forceHtml:
|
|
||||||
contentType = 'text/html'
|
|
||||||
elif fileName:
|
|
||||||
m = extPat.match(fileName)
|
|
||||||
if m:
|
|
||||||
ext = m.group(1)
|
|
||||||
ext = ext.translate(maketrans(string.ascii_uppercase, string.ascii_lowercase))
|
|
||||||
if ext in extDict:
|
|
||||||
contentType = extDict[ext]
|
|
||||||
else:
|
|
||||||
sys.stderr.write('Unable to guess Content-Type from file name. Please force the type.\n')
|
|
||||||
sys.exit(3)
|
|
||||||
else:
|
|
||||||
sys.stderr.write('Could not extract a filename extension. Please force the type.\n')
|
|
||||||
sys.exit(6)
|
|
||||||
else:
|
|
||||||
sys.stderr.write('Need to force HTML or XHTML when reading from stdin.\n')
|
|
||||||
sys.exit(4)
|
|
||||||
|
|
||||||
if encoding:
|
|
||||||
contentType = '%s; charset=%s' % (contentType, encoding)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Read the file argument (or STDIN)
|
|
||||||
#
|
|
||||||
if fileName:
|
|
||||||
inputHandle = fileName
|
|
||||||
else:
|
|
||||||
inputHandle = sys.stdin
|
|
||||||
|
|
||||||
with open(inputHandle, mode='rb') as inFile:
|
|
||||||
data = inFile.read()
|
|
||||||
with BytesIO() as buf:
|
|
||||||
# we could use another with block here, but it requires Python 2.7+
|
|
||||||
zipFile = gzip.GzipFile(fileobj=buf, mode='wb')
|
|
||||||
zipFile.write(data)
|
|
||||||
zipFile.close()
|
|
||||||
gzippeddata = buf.getvalue()
|
|
||||||
|
|
||||||
#
|
|
||||||
# Prepare the request
|
|
||||||
#
|
|
||||||
url = service
|
|
||||||
|
|
||||||
if gnu:
|
|
||||||
url = url + '?out=gnu'
|
|
||||||
else:
|
|
||||||
url = url + '?out=text'
|
|
||||||
|
|
||||||
if errorsOnly:
|
|
||||||
url = url + '&level=error'
|
|
||||||
|
|
||||||
connection = None
|
|
||||||
response = None
|
|
||||||
status = 302
|
|
||||||
redirectCount = 0
|
|
||||||
|
|
||||||
#
|
|
||||||
# Make the request
|
|
||||||
#
|
|
||||||
while status in (302,301,307) and redirectCount < 10:
|
|
||||||
if redirectCount > 0:
|
|
||||||
url = response.getheader('Location')
|
|
||||||
parsed = urlparse.urlsplit(url)
|
|
||||||
|
|
||||||
if redirectCount > 0:
|
|
||||||
connection.close() # previous connection
|
|
||||||
print('Redirecting to %s' % url)
|
|
||||||
print('Please press enter to continue or type \'stop\' followed by enter to stop.')
|
|
||||||
if raw_input() != '':
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if parsed.scheme == 'https':
|
|
||||||
connection = httplib.HTTPSConnection(parsed[1])
|
|
||||||
else:
|
|
||||||
connection = httplib.HTTPConnection(parsed[1])
|
|
||||||
|
|
||||||
headers = {
|
|
||||||
'Accept-Encoding': 'gzip',
|
|
||||||
'Content-Type': contentType,
|
|
||||||
'Content-Encoding': 'gzip',
|
|
||||||
'Content-Length': len(gzippeddata),
|
|
||||||
}
|
|
||||||
urlSuffix = '%s?%s' % (parsed[2], parsed[3])
|
|
||||||
|
|
||||||
connection.connect()
|
|
||||||
connection.request('POST', urlSuffix, body=gzippeddata, headers=headers)
|
|
||||||
|
|
||||||
response = connection.getresponse()
|
|
||||||
status = response.status
|
|
||||||
|
|
||||||
redirectCount += 1
|
|
||||||
|
|
||||||
#
|
|
||||||
# Handle the response
|
|
||||||
#
|
|
||||||
if status != 200:
|
|
||||||
sys.stderr.write('%s %s\n' % (status, response.reason))
|
|
||||||
sys.exit(5)
|
|
||||||
|
|
||||||
if response.getheader('Content-Encoding', 'identity').lower() == 'gzip':
|
|
||||||
response = gzip.GzipFile(fileobj=BytesIO(response.read()))
|
|
||||||
|
|
||||||
if fileName and gnu:
|
|
||||||
quotedName = '"%s"' % fileName.replace("'", '\\042')
|
|
||||||
for line in response.read().split('\n'):
|
|
||||||
if line:
|
|
||||||
sys.stdout.write(quotedName)
|
|
||||||
sys.stdout.write(line + '\n')
|
|
||||||
else:
|
|
||||||
output = response.read()
|
|
||||||
# python2/3 difference in output's type
|
|
||||||
if not isinstance(output, str):
|
|
||||||
output = output.decode('utf-8')
|
|
||||||
sys.stdout.write(output)
|
|
||||||
|
|
||||||
connection.close()
|
|
@ -20,6 +20,7 @@ import logging
|
|||||||
from ldapcherry.lclogging import *
|
from ldapcherry.lclogging import *
|
||||||
from disable import *
|
from disable import *
|
||||||
import json
|
import json
|
||||||
|
from tidylib import tidy_document
|
||||||
if sys.version < '3':
|
if sys.version < '3':
|
||||||
from sets import Set as set
|
from sets import Set as set
|
||||||
|
|
||||||
@ -79,19 +80,21 @@ class HtmlValidationFailed(Exception):
|
|||||||
def __init__(self, out):
|
def __init__(self, out):
|
||||||
self.errors = out
|
self.errors = out
|
||||||
|
|
||||||
|
def _is_html_error(line):
|
||||||
|
ret = True
|
||||||
|
for p in [r'.*Warning: trimming empty <span>.*']:
|
||||||
|
if re.match(p, line):
|
||||||
|
ret = False
|
||||||
|
return ret
|
||||||
|
|
||||||
def htmlvalidator(page):
|
def htmlvalidator(page):
|
||||||
|
document, errors = tidy_document(page,
|
||||||
|
options={'numeric-entities':1})
|
||||||
f = tempfile()
|
f = tempfile()
|
||||||
stdout = tempfile()
|
for line in errors.splitlines():
|
||||||
f.write(page.encode("utf-8"))
|
if _is_html_error(line):
|
||||||
f.seek(0)
|
print(line)
|
||||||
ret = subprocess.call(['./tests/html_validator.py', '-h', f.name], stdout=stdout)
|
raise HtmlValidationFailed(line)
|
||||||
stdout.seek(0)
|
|
||||||
out = stdout.read()
|
|
||||||
f.close()
|
|
||||||
stdout.close()
|
|
||||||
print(out)
|
|
||||||
if not re.search(r'Error:.*', str(out)) is None:
|
|
||||||
raise HtmlValidationFailed(out)
|
|
||||||
|
|
||||||
class BadModule():
|
class BadModule():
|
||||||
pass
|
pass
|
||||||
@ -361,4 +364,3 @@ class TestError(object):
|
|||||||
get_loglevel('alert') is logging.CRITICAL and \
|
get_loglevel('alert') is logging.CRITICAL and \
|
||||||
get_loglevel('emergency') is logging.CRITICAL and \
|
get_loglevel('emergency') is logging.CRITICAL and \
|
||||||
get_loglevel('notalevel') is logging.INFO
|
get_loglevel('notalevel') is logging.INFO
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user