1
0
mirror of https://github.com/kakwa/ldapcherry synced 2024-11-22 09:24: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:
kakwa 2019-02-09 18:31:37 +01:00
parent 02357d886a
commit a56c491ee1
6 changed files with 19 additions and 260 deletions

View File

@ -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}

View File

@ -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>

View File

@ -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>

View File

@ -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',

View File

@ -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()

View File

@ -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