diff --git a/app/auth/forms.py b/app/auth/forms.py index a418bc51a02b3b718699883ce0c5566793248637..e8825aa21b56d41d08d3f3976624f6b2e8cdfca8 100644 --- a/app/auth/forms.py +++ b/app/auth/forms.py @@ -7,7 +7,7 @@ from wtforms import ( SubmitField, ValidationError ) -from wtforms.validators import DataRequired, Email, EqualTo, Length, Regexp +from wtforms.validators import DataRequired, InputRequired, Email, EqualTo, Length, Regexp from . import USERNAME_REGEX @@ -20,31 +20,18 @@ class LoginForm(FlaskForm): class RegistrationForm(FlaskForm): email = StringField('Email', validators=[DataRequired(), Email()]) - username = StringField( - 'Username', + username = StringField('Username', validators=[ - DataRequired(), - Length(1, 64), + InputRequired(), + Length(min=1, max=64), Regexp( USERNAME_REGEX, message='Usernames must have only letters, numbers, dots or underscores' # noqa ) ] ) - password = PasswordField( - 'Password', - validators=[ - DataRequired(), - EqualTo('password_confirmation', message='Passwords must match') - ] - ) - password_confirmation = PasswordField( - 'Password confirmation', - validators=[ - DataRequired(), - EqualTo('password', message='Passwords must match') - ] - ) + password = PasswordField('Password', validators=[DataRequired(), EqualTo('password_confirmation', message='Passwords must match')]) + password_confirmation = PasswordField('Password confirmation', validators=[DataRequired(), EqualTo('password', message='Passwords must match')]) submit = SubmitField('Register') def validate_email(self, field): @@ -57,20 +44,8 @@ class RegistrationForm(FlaskForm): class ResetPasswordForm(FlaskForm): - password = PasswordField( - 'New password', - validators=[ - DataRequired(), - EqualTo('password_confirmation', message='Passwords must match') - ] - ) - password_confirmation = PasswordField( - 'Password confirmation', - validators=[ - DataRequired(), - EqualTo('password', message='Passwords must match') - ] - ) + password = PasswordField('New password', validators=[DataRequired(), EqualTo('password_confirmation', message='Passwords must match')]) + password_confirmation = PasswordField('Password confirmation', validators=[DataRequired(), EqualTo('password', message='Passwords must match')]) submit = SubmitField('Reset Password') diff --git a/app/corpora/forms.py b/app/corpora/forms.py index 9af4a5ce518c80e964c2c7a7eabce5367af2073e..7e1e98d8f626f37c1de9a09ef9b5fda559ed7bc8 100644 --- a/app/corpora/forms.py +++ b/app/corpora/forms.py @@ -1,13 +1,13 @@ from flask_wtf import FlaskForm +from flask_wtf.file import FileField, FileRequired from werkzeug.utils import secure_filename from wtforms import ( - FileField, StringField, SubmitField, ValidationError, IntegerField ) -from wtforms.validators import DataRequired, Length +from wtforms.validators import DataRequired, InputRequired, Length class AddCorpusFileForm(FlaskForm): @@ -15,36 +15,25 @@ class AddCorpusFileForm(FlaskForm): Form to add a .vrt corpus file to the current corpus. ''' # Required fields - author = StringField('Author', validators=[DataRequired(), Length(1, 255)]) - file = FileField('File', validators=[DataRequired()]) - publishing_year = IntegerField('Publishing year', - validators=[DataRequired()]) - title = StringField('Title', validators=[DataRequired(), Length(1, 255)]) + author = StringField('Author', validators=[InputRequired(), Length(min=1, max=255)]) + publishing_year = IntegerField('Publishing year', validators=[InputRequired(), Length(min=1, max=255)]) + title = StringField('Title', validators=[InputRequired(), Length(min=1, max=255)]) + vrt = FileField('File', validators=[FileRequired()]) # Optional fields - address = StringField('Adress', validators=[Length(0, 255)]) - booktitle = StringField('Booktitle', validators=[Length(0, 255)]) - chapter = StringField('Chapter', validators=[Length(0, 255)]) - editor = StringField('Editor', validators=[Length(0, 255)]) - institution = StringField('Institution', validators=[Length(0, 255)]) - journal = StringField('Journal', validators=[Length(0, 255)]) - pages = StringField('Pages', validators=[Length(0, 255)]) - publisher = StringField('Publisher', validators=[Length(0, 255)]) - school = StringField('School', validators=[Length(0, 255)]) + address = StringField('Adress', validators=[Length(max=255)]) + booktitle = StringField('Booktitle', validators=[Length(max=255)]) + chapter = StringField('Chapter', validators=[Length(max=255)]) + editor = StringField('Editor', validators=[Length(max=255)]) + institution = StringField('Institution', validators=[Length(max=255)]) + journal = StringField('Journal', validators=[Length(max=255)]) + pages = StringField('Pages', validators=[Length(max=255)]) + publisher = StringField('Publisher', validators=[Length(max=255)]) + school = StringField('School', validators=[Length(max=255)]) submit = SubmitField() - def __init__(self, corpus, *args, **kwargs): - super().__init__(*args, **kwargs) - self.corpus = corpus - - def validate_file(self, field): + def validate_vrt(self, field): if not field.data.filename.lower().endswith('.vrt'): - raise ValidationError('File does not have an approved extension: ' - '.vrt') - field.data.filename = secure_filename(field.data.filename) - for corpus_file in self.corpus.files: - if field.data.filename == corpus_file.filename: - raise ValidationError('File already registered to corpus.') - + raise ValidationError('VRT files only!') class EditCorpusFileForm(FlaskForm): ''' @@ -52,8 +41,7 @@ class EditCorpusFileForm(FlaskForm): ''' # Required fields author = StringField('Author', validators=[DataRequired(), Length(1, 255)]) - publishing_year = IntegerField('Publishing year', - validators=[DataRequired()]) + publishing_year = IntegerField('Publishing year', validators=[DataRequired()]) title = StringField('Title', validators=[DataRequired(), Length(1, 255)]) # Optional fields address = StringField('Adress', validators=[Length(0, 255)]) @@ -72,27 +60,23 @@ class AddCorpusForm(FlaskForm): ''' Form to add a a new corpus. ''' - description = StringField('Description', - validators=[DataRequired(), Length(1, 255)]) - submit = SubmitField() + description = StringField('Description', validators=[DataRequired(), Length(1, 255)]) title = StringField('Title', validators=[DataRequired(), Length(1, 32)]) + submit = SubmitField() class ImportCorpusForm(FlaskForm): ''' Form to import a corpus. ''' - description = StringField('Description', - validators=[DataRequired(), Length(1, 255)]) - file = FileField('File', validators=[DataRequired()]) - submit = SubmitField() + description = StringField('Description', validators=[DataRequired(), Length(1, 255)]) + archive = FileField('File', validators=[DataRequired()]) title = StringField('Title', validators=[DataRequired(), Length(1, 32)]) + submit = SubmitField() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - def validate_file(self, field): - if not field.data.filename.lower().endswith('.zip'): - raise ValidationError('File does not have an approved extension: ' - '.zip') - field.data.filename = secure_filename(field.data.filename) + def validate_archive(self, field): + if field.data.mimetype != 'application/zip': + raise ValidationError('ZIP files only!') diff --git a/app/corpora/routes.py b/app/corpora/routes.py index 3b334d83b0e7b67ef1fc57e00a8b75e066cf8d07..681789d9f3e05b711b7731a12ecd951d098fa007 100644 --- a/app/corpora/routes.py +++ b/app/corpora/routes.py @@ -190,7 +190,7 @@ def add_corpus_file(corpus_id): corpus = Corpus.query.get_or_404(corpus_id) if not (corpus.user == current_user or current_user.is_administrator()): abort(403) - form = AddCorpusFileForm(corpus, prefix='add-corpus-file-form') + form = AddCorpusFileForm(prefix='add-corpus-file-form') if form.is_submitted(): if not form.validate(): return make_response(form.errors, 400) @@ -269,8 +269,9 @@ def download_corpus_file(corpus_id, corpus_file_id): abort(403) return send_from_directory( as_attachment=True, + attachment_filename=corpus_file.filename, directory=os.path.dirname(corpus_file.path), - filename=corpus_file.filename + filename=os.path.basename(corpus_file.path) ) diff --git a/app/services/forms.py b/app/services/forms.py index 21db025e5de83b65d651a41a5c4c3f887b35c754..5e184e351106475ddac7697522b4ed85df7ead62 100644 --- a/app/services/forms.py +++ b/app/services/forms.py @@ -1,7 +1,7 @@ -from app.models import Job, TesseractOCRModel +from app.models import TesseractOCRModel from flask_login import current_user from flask_wtf import FlaskForm -from flask_wtf.file import FileField, FileAllowed, FileRequired +from flask_wtf.file import FileField, FileRequired from wtforms import ( BooleanField, MultipleFileField, @@ -15,24 +15,10 @@ from . import SERVICES class AddJobForm(FlaskForm): - description = StringField('Description', validators=[InputRequired()]) # noqa - submit = SubmitField() - title = StringField('Title', validators=[InputRequired()]) + description = StringField('Description', validators=[InputRequired(), Length(min=1, max=255)]) + title = StringField('Title', validators=[InputRequired(), Length(min=1, max=32)]) version = SelectField('Version', validators=[DataRequired()]) - - def validate_description(self, field): - max_length = Job.description.property.columns[0].type.length - if len(field.data) > max_length: - raise ValidationError( - f'Description must be less than {max_length} characters' - ) - - def validate_title(self, field): - max_length = Job.title.property.columns[0].type.length - if len(field.data) > max_length: - raise ValidationError( - f'Title must be less than {max_length} characters' - ) + submit = SubmitField() class AddFileSetupPipelineJobForm(AddJobForm): diff --git a/app/settings/forms.py b/app/settings/forms.py index f17c5f892f175d66f2a1dbc1cda36b6a374fef42..cfc6918c3192aa13389a274b6da0c3c309447b0f 100644 --- a/app/settings/forms.py +++ b/app/settings/forms.py @@ -9,28 +9,13 @@ from wtforms import ( SubmitField, ValidationError ) -from wtforms.validators import DataRequired, Email, EqualTo, Length, Regexp +from wtforms.validators import DataRequired, InputRequired, Email, EqualTo, Length, Regexp class ChangePasswordForm(FlaskForm): password = PasswordField('Old password', validators=[DataRequired()]) - new_password = PasswordField( - 'New password', - validators=[ - DataRequired(), - EqualTo( - 'new_password_confirmation', - message='Passwords must match' - ) - ] - ) - new_password_confirmation = PasswordField( - 'Confirm new password', - validators=[ - DataRequired(), - EqualTo('new_password', message='Passwords must match') - ] - ) + new_password = PasswordField('New password', validators=[DataRequired(), EqualTo('new_password_confirmation', message='Passwords must match')]) + new_password_confirmation = PasswordField('Confirm new password', validators=[DataRequired(), EqualTo('new_password', message='Passwords must match')]) submit = SubmitField('Submit') def __init__(self, user, *args, **kwargs): @@ -43,14 +28,11 @@ class ChangePasswordForm(FlaskForm): class EditGeneralSettingsForm(FlaskForm): - email = StringField( - 'E-Mail', - validators=[DataRequired(), Length(1, 254), Email()] - ) + email = StringField('E-Mail', validators=[DataRequired(), Length(1, 254), Email()]) username = StringField( 'Username', validators=[ - DataRequired(), + InputRequired(), Length(1, 64), Regexp( USERNAME_REGEX, diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2 index ccfea83905ab0856a59ade495740d540118cabea..f7668ab743fab962979c29c05849fd3e595ef4d6 100644 --- a/app/templates/_scripts.html.j2 +++ b/app/templates/_scripts.html.j2 @@ -45,9 +45,14 @@ optionElementWithoutValue.disabled = true; } + // Set the data-length attribute on inputs with the maxlength attribute + for (let inputElement of document.querySelectorAll('input[maxlength]')) { + inputElement.dataset.length = inputElement.getAttribute('maxlength'); + } + // Initialize components M.AutoInit(); - M.CharacterCounter.init(document.querySelectorAll('input[data-length][type="email"], input[data-length][type="password"], input[data-length][type="text"], textarea[data-length]')); + M.CharacterCounter.init(document.querySelectorAll('input[data-length][type="text"], input[data-length][type="email"], input[data-length][type="search"], input[data-length][type="password"], input[data-length][type="tel"], input[data-length][type="url"]')); M.Dropdown.init( document.querySelectorAll('#nav-more-dropdown-trigger'), {alignment: 'right', constrainWidth: false, coverTrigger: false} diff --git a/app/templates/auth/register.html.j2 b/app/templates/auth/register.html.j2 index 99548a1e96d09e19dfc5112ea869484cfefa6bfb..5c195e568fca651278c5d8bc465b17edef5c28da 100644 --- a/app/templates/auth/register.html.j2 +++ b/app/templates/auth/register.html.j2 @@ -32,9 +32,9 @@ <form method="POST"> <div class="card-content"> {{ form.hidden_tag() }} - {{ wtf.render_field(form.username, data_length='64', material_icon='person') }} - {{ wtf.render_field(form.password, data_length='128', material_icon='vpn_key') }} - {{ wtf.render_field(form.password_confirmation, data_length='128', material_icon='vpn_key') }} + {{ wtf.render_field(form.username, material_icon='person') }} + {{ wtf.render_field(form.password, material_icon='vpn_key') }} + {{ wtf.render_field(form.password_confirmation, material_icon='vpn_key') }} {{ wtf.render_field(form.email, class_='validate', material_icon='email', type='email') }} </div> <div class="card-action right-align"> diff --git a/app/templates/auth/reset_password.html.j2 b/app/templates/auth/reset_password.html.j2 index 4be59edf218f3ec2e5a45197a86dfa2746f530d0..a0f4c32bf8d9a7b270813a33a31aafbe9cffbfd2 100644 --- a/app/templates/auth/reset_password.html.j2 +++ b/app/templates/auth/reset_password.html.j2 @@ -18,8 +18,8 @@ <form method="POST"> <div class="card-content"> {{ form.hidden_tag() }} - {{ wtf.render_field(form.password, data_length='128') }} - {{ wtf.render_field(form.password_confirmation, data_length='128') }} + {{ wtf.render_field(form.password) }} + {{ wtf.render_field(form.password_confirmation) }} </div> <div class="card-action right-align"> {{ wtf.render_field(form.submit, material_icon='send') }} diff --git a/app/templates/corpora/add_corpus.html.j2 b/app/templates/corpora/add_corpus.html.j2 index 9d7f24e4b907a536f36de4acf6347c40e3f4803e..50cbc6e79fb2d9639d63edda3b61b219892d2b6c 100644 --- a/app/templates/corpora/add_corpus.html.j2 +++ b/app/templates/corpora/add_corpus.html.j2 @@ -23,10 +23,10 @@ {{ form.hidden_tag() }} <div class="row"> <div class="col s12 m4"> - {{ wtf.render_field(form.title, data_length='32', material_icon='title') }} + {{ wtf.render_field(form.title, material_icon='title') }} </div> <div class="col s12 m8"> - {{ wtf.render_field(form.description, data_length='255', material_icon='description') }} + {{ wtf.render_field(form.description, material_icon='description') }} </div> </div> </div> diff --git a/app/templates/corpora/add_corpus_file.html.j2 b/app/templates/corpora/add_corpus_file.html.j2 index 8246821a76351f55dc884fedc2f19c462e4aa09e..034fb5c2107e32b7e03c03a1becd41ac3a0ffba0 100644 --- a/app/templates/corpora/add_corpus_file.html.j2 +++ b/app/templates/corpora/add_corpus_file.html.j2 @@ -23,16 +23,16 @@ {{ form.hidden_tag() }} <div class="row"> <div class="col s12 m4"> - {{ wtf.render_field(form.author, data_length='255', material_icon='person') }} + {{ wtf.render_field(form.author, material_icon='person') }} </div> <div class="col s12 m4"> - {{ wtf.render_field(form.title, data_length='255', material_icon='title') }} + {{ wtf.render_field(form.title, material_icon='title') }} </div> <div class="col s12 m4"> {{ wtf.render_field(form.publishing_year, material_icon='access_time') }} </div> <div class="col s12"> - {{ wtf.render_field(form.file, accept='.vrt', placeholder='Choose your .vrt file') }} + {{ wtf.render_field(form.vrt, accept='.vrt', placeholder='Choose your .vrt file') }} </div> </div> </div> @@ -47,7 +47,7 @@ <div class="collapsible-body"> {% for field in form if field.short_name not in ['author', 'csrf_token', 'file', 'publishing_year', 'submit', 'title'] %} - {{ wtf.render_field(field, data_length='255', material_icon=field.label.text[0:1]) }} + {{ wtf.render_field(field, material_icon=field.label.text[0:1]) }} {% endfor %} </div> </li> @@ -78,7 +78,7 @@ {{ super() }} <div id="progress-modal" class="modal"> <div class="modal-content"> - <h4><i class="material-icons prefix">file_upload</i> Uploading file...</h4> + <h4><i class="material-icons left">file_upload</i>Uploading files...</h4> <div class="progress"> <div class="determinate" style="width: 0%"></div> </div> diff --git a/app/templates/corpora/corpus_file.html.j2 b/app/templates/corpora/corpus_file.html.j2 index 53ac550ef5e2976f5b47e2b86a6673e7ec5234a8..781ea05915613f30b9855f4e2d5755c3ae045e1e 100644 --- a/app/templates/corpora/corpus_file.html.j2 +++ b/app/templates/corpora/corpus_file.html.j2 @@ -18,10 +18,10 @@ <div class="card-content"> <div class="row"> <div class="col s12 m4"> - {{ wtf.render_field(form.author, data_length='255', material_icon='person') }} + {{ wtf.render_field(form.author, material_icon='person') }} </div> <div class="col s12 m4"> - {{ wtf.render_field(form.title, data_length='255', material_icon='title') }} + {{ wtf.render_field(form.title, material_icon='title') }} </div> <div class="col s12 m4"> {{ wtf.render_field(form.publishing_year, material_icon='access_time') }} @@ -39,7 +39,7 @@ <div class="collapsible-body"> {% for field in form if field.short_name not in ['author', 'csrf_token', 'publishing_year', 'submit', 'title'] %} - {{ wtf.render_field(field, data_length='255', material_icon=field.label.text[0:1]) }} + {{ wtf.render_field(field, material_icon=field.label.text[0:1]) }} {% endfor %} </div> </li> diff --git a/app/templates/corpora/import_corpus.html.j2 b/app/templates/corpora/import_corpus.html.j2 index 865a33980b7e5a81525d25534206d89276166fcf..85db2646dadf3bcac85dbe72386cc6a378741161 100644 --- a/app/templates/corpora/import_corpus.html.j2 +++ b/app/templates/corpora/import_corpus.html.j2 @@ -23,10 +23,10 @@ {{ form.hidden_tag() }} <div class="row"> <div class="col s12 m4"> - {{ wtf.render_field(form.title, data_length='32', material_icon='title') }} + {{ wtf.render_field(form.title, material_icon='title') }} </div> <div class="col s12 m8"> - {{ wtf.render_field(form.description, data_length='255', material_icon='description') }} + {{ wtf.render_field(form.description, material_icon='description') }} </div> </div> <div class="row"> diff --git a/app/templates/services/file_setup_pipeline.html.j2 b/app/templates/services/file_setup_pipeline.html.j2 index ce3e21e8fa005991aa190505abab2ee7fc58fa5d..dfb11fec88f7a77416fb11c5ff6cefe06290989a 100644 --- a/app/templates/services/file_setup_pipeline.html.j2 +++ b/app/templates/services/file_setup_pipeline.html.j2 @@ -44,10 +44,10 @@ {{ form.hidden_tag() }} <div class="row"> <div class="col s12 l4"> - {{ wtf.render_field(form.title, data_length='32', material_icon='title') }} + {{ wtf.render_field(form.title, material_icon='title') }} </div> <div class="col s12 l8"> - {{ wtf.render_field(form.description, data_length='255', material_icon='description') }} + {{ wtf.render_field(form.description, material_icon='description') }} </div> <div class="col s12 l9"> {{ wtf.render_field(form.images, accept='image/jpeg, image/png, image/tiff', placeholder='Choose JPEG, PNG or TIFF files') }} diff --git a/app/templates/services/spacy_nlp_pipeline.html.j2 b/app/templates/services/spacy_nlp_pipeline.html.j2 index 5c911cd9163538848606aa3cce89f401ecbbfe7b..106e72e136e50367ba7260267c30ad851a304ff2 100644 --- a/app/templates/services/spacy_nlp_pipeline.html.j2 +++ b/app/templates/services/spacy_nlp_pipeline.html.j2 @@ -62,10 +62,10 @@ {{ form.hidden_tag() }} <div class="row"> <div class="col s12 l4"> - {{ wtf.render_field(form.title, data_length='32', material_icon='title') }} + {{ wtf.render_field(form.title, material_icon='title') }} </div> <div class="col s12 l8"> - {{ wtf.render_field(form.description, data_length='255', material_icon='description') }} + {{ wtf.render_field(form.description, material_icon='description') }} </div> <div class="col s12 l5"> {{ wtf.render_field(form.txt, accept='text/plain', placeholder='Choose a plain text file') }} diff --git a/app/templates/services/tesseract_ocr_pipeline.html.j2 b/app/templates/services/tesseract_ocr_pipeline.html.j2 index c14a8c71ab19ccb899a01c7965df5dad43a9c7ca..723e775869f2dc6b431dd7cd0ef847c0a702767a 100644 --- a/app/templates/services/tesseract_ocr_pipeline.html.j2 +++ b/app/templates/services/tesseract_ocr_pipeline.html.j2 @@ -44,10 +44,10 @@ {{ form.hidden_tag() }} <div class="row"> <div class="col s12 l4"> - {{ wtf.render_field(form.title, data_length='32', material_icon='title') }} + {{ wtf.render_field(form.title, material_icon='title') }} </div> <div class="col s12 l8"> - {{ wtf.render_field(form.description, data_length='255', material_icon='description') }} + {{ wtf.render_field(form.description, material_icon='description') }} </div> <div class="col s12 l5"> {{ wtf.render_field(form.pdf, accept='application/pdf', placeholder='Choose a PDF file') }} diff --git a/app/templates/services/transkribus_htr_pipeline.html.j2 b/app/templates/services/transkribus_htr_pipeline.html.j2 index f91d0bf555f8cd7a055fb25faa3b907687f9b56c..931c85dc896e7164abf4d585b70ed0112f187e9d 100644 --- a/app/templates/services/transkribus_htr_pipeline.html.j2 +++ b/app/templates/services/transkribus_htr_pipeline.html.j2 @@ -49,10 +49,10 @@ {{ form.hidden_tag() }} <div class="row"> <div class="col s12 l4"> - {{ wtf.render_field(form.title, data_length='32', material_icon='title') }} + {{ wtf.render_field(form.title, material_icon='title') }} </div> <div class="col s12 l8"> - {{ wtf.render_field(form.description, data_length='255', material_icon='description') }} + {{ wtf.render_field(form.description, material_icon='description') }} </div> <div class="col s12 l5"> {{ wtf.render_field(form.pdf, accept='application/pdf', placeholder='Choose a PDF file') }} diff --git a/app/templates/settings/index.html.j2 b/app/templates/settings/index.html.j2 index 1be9e284d61c58a0c1535ae1f599e8b8406829fd..0a814ea2eea1ca7961029504f6dba9232299f6f8 100644 --- a/app/templates/settings/index.html.j2 +++ b/app/templates/settings/index.html.j2 @@ -15,8 +15,8 @@ <div class="card"> <div class="card-content"> <span class="card-title">General settings</span> - {{ wtf.render_field(edit_general_settings_form.username, data_length='64', material_icon='person') }} - {{ wtf.render_field(edit_general_settings_form.email, data_length='254', material_icon='email') }} + {{ wtf.render_field(edit_general_settings_form.username, material_icon='person') }} + {{ wtf.render_field(edit_general_settings_form.email, material_icon='email') }} </div> <div class="card-action"> <div class="right-align">