Separated ŠOLAR from other portals.
This commit is contained in:
parent
7d9d2b175c
commit
cead80ed79
312
app.py
312
app.py
|
@ -12,16 +12,10 @@ from flask_script import Manager
|
|||
from flask_login import LoginManager, login_required, login_user, current_user, logout_user
|
||||
from portal.model import db, RegisteredUser
|
||||
|
||||
import portal.base
|
||||
import portal.solar
|
||||
import portal.regular
|
||||
|
||||
|
||||
# TODO: Implement user registration.
|
||||
# TODO: Integrate Shibboleth login.
|
||||
|
||||
|
||||
|
||||
# TODO: make logging level configurable
|
||||
logging.basicConfig(level=logging.DEBUG, format='[APP LOGGER] %(asctime)s %(levelname)s: %(message)s')
|
||||
|
||||
|
@ -44,8 +38,6 @@ CONTRACT_CLIENT_CONTACT = config['CONTRACT_CLIENT_CONTACT']
|
|||
MAIL_SUBJECT = config['MAIL_SUBJECT']
|
||||
MAIL_BODY = config['MAIL_BODY']
|
||||
SQL_CONN_STR = config['SQL_CONN_STR']
|
||||
DESC_PREVODI = config['DESC_PREVODI']
|
||||
DESC_GIGAFIDA = config['DESC_GIGAFIDA']
|
||||
|
||||
if 'UPLOADS_DIR' in config:
|
||||
UPLOADS_DIR = Path(config['UPLOADS_DIR'])
|
||||
|
@ -81,13 +73,6 @@ if 'PORTALDS4DS1_MAIL_BODY' in os.environ:
|
|||
MAIL_BODY = os.environ['PORTALDS4DS1_MAIL_BODY']
|
||||
if 'PORTALDS4DS1_SQL_CONN_STR' in os.environ:
|
||||
SQL_CONN_STR = os.environ['PORTALDS4DS1_SQL_CONN_STR']
|
||||
if 'PORTALDS4DS1_DESC_PREVODI' in os.environ:
|
||||
DESC_PREVODI = os.environ['PORTALDS4DS1_DESC_PREVODI']
|
||||
if 'PORTALDS4DS1_DESC_GIGAFIDA' in os.environ:
|
||||
DESC_GIGAFIDA = os.environ['PORTALDS4DS1_DESC_GIGAFIDA']
|
||||
|
||||
ENABLED_CORPUSES = ['prevodi', 'gigafida', 'solar']
|
||||
CORPUSES_LOGIN_REQUIRED = ['solar']
|
||||
|
||||
|
||||
######################
|
||||
|
@ -113,20 +98,6 @@ manager.add_command('db', MigrateCommand)
|
|||
# Set up dropzone.js to serve all the stuff for "file dropping" on the web interface.
|
||||
dropzone = Dropzone(app)
|
||||
|
||||
upload_handler_regular = portal.regular.UploadHandlerRegular(
|
||||
UPLOADS_DIR=UPLOADS_DIR,
|
||||
MAIL_HOST=MAIL_HOST,
|
||||
MAIL_LOGIN=MAIL_LOGIN,
|
||||
MAIL_PASS=MAIL_PASS,
|
||||
SMTP_PORT=SMTP_PORT,
|
||||
IMAP_PORT=IMAP_PORT,
|
||||
MAIL_SUBJECT=MAIL_SUBJECT,
|
||||
MAIL_BODY=MAIL_BODY,
|
||||
CONTRACT_CLIENT_CONTACT=CONTRACT_CLIENT_CONTACT,
|
||||
MAX_FILES_PER_UPLOAD=MAX_FILES_PER_UPLOAD,
|
||||
APP_SECRET_KEY=APP_SECRET_KEY
|
||||
)
|
||||
|
||||
upload_handler_solar = portal.solar.UploadHandlerSolar(
|
||||
UPLOADS_DIR=UPLOADS_DIR,
|
||||
MAIL_HOST=MAIL_HOST,
|
||||
|
@ -155,26 +126,9 @@ def redirect_url(default='/'):
|
|||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
|
||||
@app.route('/<corpus_name>')
|
||||
def index_corpus(corpus_name):
|
||||
if corpus_name not in ENABLED_CORPUSES:
|
||||
return 'Korpus "{}" ne obstaja.'.format(corpus_name), 404
|
||||
|
||||
description = ""
|
||||
if corpus_name == 'prevodi':
|
||||
description = DESC_PREVODI
|
||||
elif corpus_name == 'gigafida':
|
||||
description = DESC_GIGAFIDA
|
||||
elif corpus_name == 'solar':
|
||||
if current_user.is_authenticated:
|
||||
return redirect('/solar/oddaja')
|
||||
return redirect('/solar/login')
|
||||
|
||||
return render_template('basic.html',
|
||||
corpus_name=corpus_name, description=description, max_files=MAX_FILES_PER_UPLOAD)
|
||||
return redirect('/oddaja')
|
||||
return redirect('/login')
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
|
@ -183,120 +137,116 @@ def load_user(user_id):
|
|||
return user
|
||||
|
||||
|
||||
@app.route('/solar/login')
|
||||
@app.route('/login')
|
||||
def solar_login_get():
|
||||
return render_template('solar-login.html')
|
||||
|
||||
|
||||
@app.route('/solar/register')
|
||||
@app.route('/register')
|
||||
def solar_register_get():
|
||||
return render_template('solar-register.html')
|
||||
|
||||
|
||||
@app.route('/solar/login', methods=['POST'])
|
||||
@app.route('/login', methods=['POST'])
|
||||
def solar_login_post():
|
||||
email = request.form.get('email')
|
||||
password = request.form.get('password')
|
||||
remember = True if request.form.get('remember') else False
|
||||
|
||||
user = portal.base.get_user_obj_by_email(email)
|
||||
user = portal.solar.get_user_obj_by_email(email)
|
||||
|
||||
if not user or not check_password_hash(user.pass_hash, password):
|
||||
flash('Napačni podatki za prijavo. Poskusite ponovno.')
|
||||
return redirect('/solar/login')
|
||||
return redirect('/login')
|
||||
|
||||
if not user.active:
|
||||
flash('Vaš uporabniški račun še ni bil aktiviran.')
|
||||
return redirect('/solar/login')
|
||||
return redirect('/login')
|
||||
|
||||
#portal.base.add_user_session(user.id)
|
||||
#portal.solar.add_user_session(user.id)
|
||||
login_user(user, remember=remember)
|
||||
|
||||
return redirect('/solar/oddaja')
|
||||
return redirect('/oddaja')
|
||||
|
||||
|
||||
@app.route('/solar/register', methods=['POST'])
|
||||
@app.route('/register', methods=['POST'])
|
||||
def solar_register_post():
|
||||
name = request.form.get('name')
|
||||
email = request.form.get('email')
|
||||
password = request.form.get('password')
|
||||
institution_name = request.form.get('institution')
|
||||
institution_role = request.form.get('role')
|
||||
institution = portal.base.get_institution_obj_by_name(institution_name)
|
||||
institution = portal.solar.get_institution_obj_by_name(institution_name)
|
||||
|
||||
user = RegisteredUser.query.filter_by(email=email).first()
|
||||
|
||||
if user:
|
||||
flash('Uporabniški račun s tem emailom je že registriran.')
|
||||
return redirect('/solar/register')
|
||||
return redirect('/register')
|
||||
|
||||
if not name:
|
||||
flash('Prazno polje za ime.')
|
||||
return redirect('/solar/register')
|
||||
return redirect('/register')
|
||||
if len(name) > 100:
|
||||
flash('Predolgo ime.')
|
||||
return redirect('/solar/register')
|
||||
return redirect('/register')
|
||||
|
||||
if not email:
|
||||
flash('Prazno polje za elektronsko pošto.')
|
||||
return redirect('/solar/register')
|
||||
return redirect('/register')
|
||||
if len(email) > 100:
|
||||
flash('Predolgi email naslov')
|
||||
return redirect('/solar/register')
|
||||
elif not re.search(portal.base.REGEX_EMAIL, email):
|
||||
return redirect('/register')
|
||||
elif not re.search(portal.solar.REGEX_EMAIL, email):
|
||||
flash('Email napačnega formata.')
|
||||
return redirect('/solar/register')
|
||||
return redirect('/register')
|
||||
|
||||
if not password:
|
||||
flash('Prazno polje za geslo.')
|
||||
return redirect('/solar/register')
|
||||
return redirect('/register')
|
||||
if len(password) > 8:
|
||||
flash('Geslo mora biti vsaj 8 znakov dolgo.')
|
||||
return redirect('/solar/register')
|
||||
return redirect('/register')
|
||||
if len(password) > 100:
|
||||
flash('Predolgo geslo.')
|
||||
return redirect('/solar/register')
|
||||
return redirect('/register')
|
||||
|
||||
if institution_role not in ['coordinator', 'mentor', 'other']:
|
||||
flash('Neveljavna vloga v instituciji.')
|
||||
return redirect('/solar/register')
|
||||
return redirect('/register')
|
||||
|
||||
if not institution:
|
||||
institution_id = portal.base.add_institution(institution_name, "")
|
||||
portal.base.grant_institution_corpus_access(institution_id, "solar")
|
||||
institution_id = portal.solar.add_institution(institution_name, "")
|
||||
portal.solar.grant_institution_corpus_access(institution_id, "solar")
|
||||
else:
|
||||
institution_id = institution.id
|
||||
|
||||
user_id = portal.base.register_new_user(name, email, password, active=False)
|
||||
portal.base.add_user_to_institution(user_id, institution_id, institution_role)
|
||||
user_id = portal.solar.register_new_user(name, email, password, active=False)
|
||||
portal.solar.add_user_to_institution(user_id, institution_id, institution_role)
|
||||
|
||||
portal.base.send_admins_new_user_notification_mail(user_id, upload_handler_solar.config)
|
||||
portal.solar.send_admins_new_user_notification_mail(user_id, upload_handler_solar.config)
|
||||
|
||||
flash('Podatki so bili poslani v potrditev. Ko bo registracija potrjena, boste o tem obveščeni po e-mailu, ki ste ga posredovali zgoraj.')
|
||||
return redirect('/solar/login')
|
||||
return redirect('/login')
|
||||
|
||||
|
||||
|
||||
# TODO: Move solar stuff to seperate file using Flask blueprints.
|
||||
# TODO: Better routing logic.
|
||||
|
||||
@app.route('/solar/logout')
|
||||
@app.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect('/solar/login')
|
||||
return redirect('/login')
|
||||
|
||||
|
||||
@app.route('/solar/<path:text>')
|
||||
@app.route('/<path:text>')
|
||||
@login_required
|
||||
def solar(text):
|
||||
is_admin = current_user.role == 'admin'
|
||||
current_user_institution = portal.base.get_user_institution(current_user.id)
|
||||
current_user_obj = portal.base.get_user_obj(current_user.get_id())
|
||||
current_user_institution = portal.solar.get_user_institution(current_user.id)
|
||||
current_user_obj = portal.solar.get_user_obj(current_user.get_id())
|
||||
institution_contract = None
|
||||
if current_user_institution:
|
||||
current_user_institution_coordinator = portal.base.is_institution_coordinator(current_user.id, current_user_institution.id)
|
||||
institution_contract = portal.base.get_institution_contract(current_user_institution.id)
|
||||
current_user_institution_coordinator = portal.solar.is_institution_coordinator(current_user.id, current_user_institution.id)
|
||||
institution_contract = portal.solar.get_institution_contract(current_user_institution.id)
|
||||
else:
|
||||
current_user_institution_coordinator = False
|
||||
|
||||
|
@ -313,8 +263,8 @@ def solar(text):
|
|||
uploader_names = []
|
||||
institution_names = []
|
||||
for item in upload_items:
|
||||
uploader_names.append(portal.base.get_user_obj(item.upload_user).name)
|
||||
institution = portal.base.get_institution_obj(item.institution)
|
||||
uploader_names.append(portal.solar.get_user_obj(item.upload_user).name)
|
||||
institution = portal.solar.get_institution_obj(item.institution)
|
||||
if not institution:
|
||||
institution_names.append(None)
|
||||
else:
|
||||
|
@ -334,9 +284,9 @@ def solar(text):
|
|||
f_hash = filename.split('.')[0]
|
||||
|
||||
if contract_type == 'institucije':
|
||||
actual_filename = portal.base.get_actual_institution_contract_filename(f_hash)
|
||||
actual_filename = portal.solar.get_actual_institution_contract_filename(f_hash)
|
||||
else:
|
||||
actual_filename = portal.base.get_actual_studentparent_contract_filename(f_hash)
|
||||
actual_filename = portal.solar.get_actual_studentparent_contract_filename(f_hash)
|
||||
|
||||
safe_path = safe_join(str(upload_handler_solar.get_uploads_subdir('contracts')), prefix, suffix)
|
||||
try:
|
||||
|
@ -351,11 +301,11 @@ def solar(text):
|
|||
collaborators = []
|
||||
cooperation_history = dict()
|
||||
if current_user_institution:
|
||||
collaborators = portal.base.get_all_active_institution_users(current_user_institution.id)
|
||||
collaborators = portal.solar.get_all_active_institution_users(current_user_institution.id)
|
||||
show_upload_form = True
|
||||
contract_school = portal.solar.get_institution_contract(current_user_institution.id)
|
||||
cooperation_history = portal.base.get_institution_cooperation_history(current_user_institution.id)
|
||||
if portal.base.is_institution_coordinator(current_user_obj.id, current_user_institution.id):
|
||||
cooperation_history = portal.solar.get_institution_cooperation_history(current_user_institution.id)
|
||||
if portal.solar.is_institution_coordinator(current_user_obj.id, current_user_institution.id):
|
||||
contracts_students = portal.solar.get_institution_student_contracts(current_user_institution.id)
|
||||
enable_upload_school_contract = True
|
||||
else:
|
||||
|
@ -370,23 +320,23 @@ def solar(text):
|
|||
user_id=current_user.id,
|
||||
is_admin=is_admin, is_institution_coordinator=current_user_institution_coordinator)
|
||||
elif text.startswith('admin/') or text == 'admin':
|
||||
users = portal.base.get_all_users_join_institutions()
|
||||
inactive_users = portal.base.get_all_users_join_institutions(active=False)
|
||||
users = portal.solar.get_all_users_join_institutions()
|
||||
inactive_users = portal.solar.get_all_users_join_institutions(active=False)
|
||||
solar_institutions = portal.solar.get_all_institutions()
|
||||
cooperation_history = portal.base.get_cooperation_history()
|
||||
cooperation_history = portal.solar.get_cooperation_history()
|
||||
uploads = portal.solar.get_all_upload_history(-1)
|
||||
if is_admin:
|
||||
return render_template('solar-admin.html', users=users, user_cooperation_history=cooperation_history,
|
||||
institutions=solar_institutions, inactive_users=inactive_users, uploads=uploads)
|
||||
elif text.startswith('manage-institution/') or text == 'manage-institution':
|
||||
if portal.base.is_institution_coordinator(current_user.id, current_user_institution.id):
|
||||
solar_users = portal.base.get_all_active_users()
|
||||
institution_users = portal.base.get_all_active_institution_users(current_user_institution.id)
|
||||
if portal.solar.is_institution_coordinator(current_user.id, current_user_institution.id):
|
||||
solar_users = portal.solar.get_all_active_users()
|
||||
institution_users = portal.solar.get_all_active_institution_users(current_user_institution.id)
|
||||
return render_template('solar-manage-institution.html', users=solar_users,
|
||||
institution_users=institution_users)
|
||||
return '', 404
|
||||
|
||||
@app.route('/solar/pogodbe', methods=['POST'])
|
||||
@app.route('/pogodbe', methods=['POST'])
|
||||
@login_required
|
||||
def solar_upload_contract():
|
||||
msg = upload_handler_solar.handle_contract_upload(request, current_user.get_id())
|
||||
|
@ -394,11 +344,11 @@ def solar_upload_contract():
|
|||
return redirect(redirect_url())
|
||||
|
||||
|
||||
@app.route('/solar/adduser', methods=['POST'])
|
||||
@app.route('/adduser', methods=['POST'])
|
||||
@login_required
|
||||
def solar_add_user():
|
||||
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
if not portal.solar.is_admin(current_user.id):
|
||||
return '', 404
|
||||
|
||||
name = request.form.get('name')
|
||||
|
@ -418,7 +368,7 @@ def solar_add_user():
|
|||
if len(email) > 100:
|
||||
flash('Predolg email naslov.')
|
||||
return redirect(redirect_url())
|
||||
elif not re.search(portal.base.REGEX_EMAIL, email):
|
||||
elif not re.search(portal.solar.REGEX_EMAIL, email):
|
||||
flash('Email napačnega formata.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
|
@ -429,22 +379,22 @@ def solar_add_user():
|
|||
flash('Predolgo geslo.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
user = portal.base.get_user_obj_by_email(email)
|
||||
user = portal.solar.get_user_obj_by_email(email)
|
||||
if user:
|
||||
#portal.base.undo_remove_user(user.id)
|
||||
#portal.solar.undo_remove_user(user.id)
|
||||
flash('Uporabnik s tem emailom je že vnešen v sistem.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
portal.base.register_new_user(name, email, password)
|
||||
portal.solar.register_new_user(name, email, password)
|
||||
|
||||
flash('Uporabnik je bil uspešno dodan.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
|
||||
@app.route('/solar/activateuser', methods=['POST'])
|
||||
@app.route('/activateuser', methods=['POST'])
|
||||
@login_required
|
||||
def solar_activate_user():
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
if not portal.solar.is_admin(current_user.id):
|
||||
return '', 404
|
||||
|
||||
user_id = request.form.get('id')
|
||||
|
@ -452,34 +402,34 @@ def solar_activate_user():
|
|||
flash('Prazno polje za ID uporabnika.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
rowcount = portal.base.activate_user(user_id)
|
||||
rowcount = portal.solar.activate_user(user_id)
|
||||
if rowcount == 0:
|
||||
return '', 404
|
||||
|
||||
portal.base.send_user_activation_mail(user_id, upload_handler_solar.config)
|
||||
portal.solar.send_user_activation_mail(user_id, upload_handler_solar.config)
|
||||
|
||||
flash('Uporabnik je bil aktiviran.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
|
||||
@app.route('/solar/forgotpass')
|
||||
@app.route('/forgotpass')
|
||||
def solar_forgotpass():
|
||||
return render_template('solar-forgotpass.html')
|
||||
|
||||
|
||||
@app.route('/solar/sendresetpass', methods=['POST'])
|
||||
@app.route('/sendresetpass', methods=['POST'])
|
||||
def solar_sendresetpass():
|
||||
email = request.form.get('email')
|
||||
|
||||
portal.base.send_resetpass_mail(email, upload_handler_solar.config)
|
||||
portal.solar.send_resetpass_mail(email, upload_handler_solar.config)
|
||||
|
||||
flash('Povezava za ponastavitev gesla je bila poslana na vpisan email naslov.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
|
||||
@app.route('/solar/resetpass/<token>')
|
||||
@app.route('/resetpass/<token>')
|
||||
def solar_resetpass(token):
|
||||
user = portal.base.verify_reset_token(token, upload_handler_solar.config['APP_SECRET_KEY'])
|
||||
user = portal.solar.verify_reset_token(token, upload_handler_solar.config['APP_SECRET_KEY'])
|
||||
|
||||
if not user:
|
||||
return '', 404
|
||||
|
@ -487,42 +437,42 @@ def solar_resetpass(token):
|
|||
return render_template('solar-resetpass.html', user=user, token=token)
|
||||
|
||||
|
||||
@app.route('/solar/resetpass/<token>', methods=['POST'])
|
||||
@app.route('/resetpass/<token>', methods=['POST'])
|
||||
def solar_resetpass_post(token):
|
||||
new_password = request.form.get('new_password')
|
||||
user = portal.base.verify_reset_token(token, upload_handler_solar.config['APP_SECRET_KEY'])
|
||||
user = portal.solar.verify_reset_token(token, upload_handler_solar.config['APP_SECRET_KEY'])
|
||||
|
||||
if not user:
|
||||
return '', 404
|
||||
|
||||
rowcount = portal.base.update_user_password(user.id, new_password)
|
||||
rowcount = portal.solar.update_user_password(user.id, new_password)
|
||||
if rowcount == 0:
|
||||
return '', 404
|
||||
|
||||
flash('Ponastavitev gesla je bila uspešna.')
|
||||
return redirect('/solar/login')
|
||||
return redirect('/login')
|
||||
|
||||
|
||||
@app.route('/solar/topuploads')
|
||||
@app.route('/topuploads')
|
||||
@login_required
|
||||
def solar_topuploads_srednje():
|
||||
return jsonify(portal.solar.get_top_uploading_institutions())
|
||||
|
||||
|
||||
@app.route('/solar/deluser', methods=['POST'])
|
||||
@app.route('/deluser', methods=['POST'])
|
||||
@login_required
|
||||
def solar_del_user():
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
if not portal.solar.is_admin(current_user.id):
|
||||
return '', 404
|
||||
user_id = request.form.get('user_id')
|
||||
portal.base.remove_user(user_id)
|
||||
portal.solar.remove_user(user_id)
|
||||
flash('Uporabnik je bil odstranjen.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
@app.route('/solar/addinstitution', methods=['POST'])
|
||||
@app.route('/addinstitution', methods=['POST'])
|
||||
@login_required
|
||||
def add_institution():
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
if not portal.solar.is_admin(current_user.id):
|
||||
return '', 404
|
||||
|
||||
name = request.form.get('name')
|
||||
|
@ -539,15 +489,15 @@ def add_institution():
|
|||
flash('Neveljavna vrednost za regijo.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
institution_id = portal.base.add_institution(name, region)
|
||||
portal.base.grant_institution_corpus_access(institution_id, "solar") # TODO: throw out
|
||||
institution_id = portal.solar.add_institution(name, region)
|
||||
portal.solar.grant_institution_corpus_access(institution_id, "solar") # TODO: throw out
|
||||
flash('Institucija je bila dodana.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
@app.route('/solar/mergeinstitutions', methods=['POST'])
|
||||
@app.route('/mergeinstitutions', methods=['POST'])
|
||||
@login_required
|
||||
def merge_institutions():
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
if not portal.solar.is_admin(current_user.id):
|
||||
return '', 404
|
||||
|
||||
id_from = request.form.get('id-from')
|
||||
|
@ -557,8 +507,8 @@ def merge_institutions():
|
|||
flash('Prazno polje.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
institution_from = portal.base.get_institution_obj(id_from)
|
||||
institution_to = portal.base.get_institution_obj(id_to)
|
||||
institution_from = portal.solar.get_institution_obj(id_from)
|
||||
institution_to = portal.solar.get_institution_obj(id_to)
|
||||
|
||||
if not institution_from:
|
||||
flash('Institucija z ID "{}" ne obstaja.'.format(id_from))
|
||||
|
@ -569,18 +519,18 @@ def merge_institutions():
|
|||
return redirect(redirect_url())
|
||||
|
||||
|
||||
portal.base.transfer_users_institution(institution_from.id, institution_to.id)
|
||||
portal.base.transfer_uploads_institution(institution_from.id, institution_to.id)
|
||||
portal.base.transfer_contracts_institution(institution_from.id, institution_to.id)
|
||||
portal.base.remove_institution(institution_from.id)
|
||||
portal.solar.transfer_users_institution(institution_from.id, institution_to.id)
|
||||
portal.solar.transfer_uploads_institution(institution_from.id, institution_to.id)
|
||||
portal.solar.transfer_contracts_institution(institution_from.id, institution_to.id)
|
||||
portal.solar.remove_institution(institution_from.id)
|
||||
|
||||
flash('Instituciji uspešno združeni')
|
||||
return redirect(redirect_url())
|
||||
|
||||
@app.route('/solar/addcooperationhistoryitem', methods=['POST'])
|
||||
@app.route('/addcooperationhistoryitem', methods=['POST'])
|
||||
@login_required
|
||||
def add_cooperation_history_item():
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
if not portal.solar.is_admin(current_user.id):
|
||||
return '', 404
|
||||
|
||||
user_id = request.form.get('user')
|
||||
|
@ -589,8 +539,8 @@ def add_cooperation_history_item():
|
|||
school_year = request.form.get('school-year')
|
||||
badge_text = request.form.get('badge-text')
|
||||
|
||||
user = portal.base.get_user_obj(user_id)
|
||||
institution = portal.base.get_institution_obj(institution_id)
|
||||
user = portal.solar.get_user_obj(user_id)
|
||||
institution = portal.solar.get_institution_obj(institution_id)
|
||||
|
||||
if not user:
|
||||
flash('Uporabnik s tem ID-jem ne obstaja.')
|
||||
|
@ -608,15 +558,15 @@ def add_cooperation_history_item():
|
|||
flash('Šolsko leto mora biti formata "2021/22".')
|
||||
return redirect(redirect_url())
|
||||
|
||||
portal.base.add_cooperation_history_item(user_id, institution_id, role, school_year, badge_text)
|
||||
portal.solar.add_cooperation_history_item(user_id, institution_id, role, school_year, badge_text)
|
||||
|
||||
flash('Vnos dodan.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
@app.route('/solar/updateuploaditem', methods=['POST'])
|
||||
@app.route('/updateuploaditem', methods=['POST'])
|
||||
@login_required
|
||||
def update_upload_item():
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
if not portal.solar.is_admin(current_user.id):
|
||||
return '', 404
|
||||
|
||||
err_msg = portal.solar.UploadHandlerSolar.check_form(request.form)
|
||||
|
@ -652,22 +602,22 @@ def update_upload_item():
|
|||
return redirect(redirect_url())
|
||||
|
||||
|
||||
@app.route('/solar/delcooperationhistoryitem', methods=['POST'])
|
||||
@app.route('/delcooperationhistoryitem', methods=['POST'])
|
||||
@login_required
|
||||
def del_cooperation_history_item():
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
if not portal.solar.is_admin(current_user.id):
|
||||
return '', 404
|
||||
|
||||
entry_id = request.form.get('entry-id')
|
||||
portal.base.del_cooperation_history_item(entry_id)
|
||||
portal.solar.del_cooperation_history_item(entry_id)
|
||||
|
||||
flash('Vnos odstranjen.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
@app.route('/solar/changeinstitutiondata', methods=['POST'])
|
||||
@app.route('/changeinstitutiondata', methods=['POST'])
|
||||
@login_required
|
||||
def change_institution_data():
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
if not portal.solar.is_admin(current_user.id):
|
||||
return '', 404
|
||||
|
||||
institution_id = request.form.get('id')
|
||||
|
@ -685,34 +635,34 @@ def change_institution_data():
|
|||
flash('Neveljavna vrednost za regijo.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
portal.base.update_institution_data(institution_id, new_name, new_region)
|
||||
portal.solar.update_institution_data(institution_id, new_name, new_region)
|
||||
flash('Podatki institucije so bili spremenjeni.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
|
||||
@app.route('/solar/changeuseremail', methods=['POST'])
|
||||
@app.route('/changeuseremail', methods=['POST'])
|
||||
@login_required
|
||||
def change_user_email():
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
if not portal.solar.is_admin(current_user.id):
|
||||
return '', 404
|
||||
|
||||
user_id = request.form.get('user-id')
|
||||
email = request.form.get('email')
|
||||
|
||||
if not re.search(portal.base.REGEX_EMAIL, email):
|
||||
if not re.search(portal.solar.REGEX_EMAIL, email):
|
||||
flash('Email napačnega formata.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
portal.base.update_user_email(user_id, email)
|
||||
portal.solar.update_user_email(user_id, email)
|
||||
|
||||
flash('Email spremenjen.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
|
||||
@app.route('/solar/changeuserrole', methods=['POST'])
|
||||
@app.route('/changeuserrole', methods=['POST'])
|
||||
@login_required
|
||||
def change_user_role():
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
if not portal.solar.is_admin(current_user.id):
|
||||
return '', 404
|
||||
|
||||
user_id = request.form.get('user-id')
|
||||
|
@ -722,38 +672,35 @@ def change_user_role():
|
|||
flash('Neveljavna vloga.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
portal.base.update_user_role(user_id, role)
|
||||
portal.solar.update_user_role(user_id, role)
|
||||
|
||||
flash('Vloga spremenjena.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
@app.route('/solar/changeusername', methods=['POST'])
|
||||
@app.route('/changeusername', methods=['POST'])
|
||||
@login_required
|
||||
def change_user_name():
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
if not portal.solar.is_admin(current_user.id):
|
||||
return '', 404
|
||||
|
||||
user_id = request.form.get('user-id')
|
||||
name = request.form.get('name')
|
||||
|
||||
portal.base.update_user_name(user_id, name)
|
||||
portal.solar.update_user_name(user_id, name)
|
||||
|
||||
flash('Ime in priimek spremenjena.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
@app.route('/<corpus_name>/addusertoinstitution', methods=['POST'])
|
||||
@app.route('/addusertoinstitution', methods=['POST'])
|
||||
@login_required
|
||||
def add_user_institution_mapping(corpus_name):
|
||||
if not corpus_name in ENABLED_CORPUSES:
|
||||
return '', 404
|
||||
|
||||
def add_user_institution_mapping():
|
||||
institution_id = request.form.get('institution_id')
|
||||
if not institution_id:
|
||||
institution = portal.base.get_user_institution(current_user.id)
|
||||
institution = portal.solar.get_user_institution(current_user.id)
|
||||
if institution:
|
||||
institution_id = institution.id
|
||||
|
||||
if not (portal.base.is_admin(current_user.id) or portal.base.is_institution_coordinator(current_user.id, institution_id)):
|
||||
if not (portal.solar.is_admin(current_user.id) or portal.solar.is_institution_coordinator(current_user.id, institution_id)):
|
||||
return '', 404
|
||||
|
||||
user_id = request.form['user_id']
|
||||
|
@ -761,54 +708,37 @@ def add_user_institution_mapping(corpus_name):
|
|||
if role not in ['coordinator', 'mentor', 'other']:
|
||||
return '', 404
|
||||
|
||||
if portal.base.get_user_institution(user_id):
|
||||
if portal.solar.get_user_institution(user_id):
|
||||
flash('Uporabnik je že dodeljen instituciji.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
portal.base.add_user_to_institution(user_id, institution_id, role)
|
||||
portal.solar.add_user_to_institution(user_id, institution_id, role)
|
||||
flash('Uporabnik je bil dodeljen instituciji.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
@app.route('/solar/deluserfrominstitution', methods=['POST'])
|
||||
@app.route('/deluserfrominstitution', methods=['POST'])
|
||||
@login_required
|
||||
def del_user_institution_mapping():
|
||||
user_id = request.form['user_id']
|
||||
institution = portal.base.get_user_institution(user_id)
|
||||
institution = portal.solar.get_user_institution(user_id)
|
||||
if not institution:
|
||||
flash('Uporabnik ni član nobene institucije.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
if not portal.base.is_admin(current_user.id) \
|
||||
and not portal.base.is_institution_coordinator(current_user.id, institution.id):
|
||||
if not portal.solar.is_admin(current_user.id) \
|
||||
and not portal.solar.is_institution_coordinator(current_user.id, institution.id):
|
||||
flash('Nimate ustreznih pravic za odstranitev uporabnika iz institucije.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
portal.base.del_user_from_institution(user_id, institution.id)
|
||||
portal.solar.del_user_from_institution(user_id, institution.id)
|
||||
flash('Uporabnik je bil odstranjen iz institucije.')
|
||||
return redirect(redirect_url())
|
||||
|
||||
@app.route('/<corpus_name>/delinstitution', methods=['POST'])
|
||||
@login_required
|
||||
def del_institution(corpus_name):
|
||||
# TODO: check if valid corpus_name
|
||||
# TODO: check if user is admin
|
||||
# TODO: delete cascade - institution, user_institution_mapping, corpus_access, institution_contract
|
||||
return '', 404
|
||||
|
||||
|
||||
@app.route('/<corpus_name>/upload', methods=['POST'])
|
||||
def handle_upload(corpus_name):
|
||||
if corpus_name not in ENABLED_CORPUSES:
|
||||
return '', 404
|
||||
|
||||
if corpus_name == 'solar':
|
||||
@app.route('/upload', methods=['POST'])
|
||||
def handle_upload():
|
||||
if not current_user.is_authenticated:
|
||||
return '', 404
|
||||
#if not portal.base.has_user_corpus_access(current_user.id, corpus_name):
|
||||
# return '', 404
|
||||
return upload_handler_solar.handle_upload(request, current_user.get_id())
|
||||
else:
|
||||
return upload_handler_regular.handle_upload(request, corpus_name)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
41
migrations/versions/44dae32b13af_removed_non_solar_stuff.py
Normal file
41
migrations/versions/44dae32b13af_removed_non_solar_stuff.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
"""Removed non-solar stuff.
|
||||
|
||||
Revision ID: 44dae32b13af
|
||||
Revises: 84168f439c55
|
||||
Create Date: 2021-10-05 13:22:23.539298
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '44dae32b13af'
|
||||
down_revision = '84168f439c55'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('upload_regular')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('upload_regular',
|
||||
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||
sa.Column('upload_hash', sa.VARCHAR(), autoincrement=False, nullable=False),
|
||||
sa.Column('timestamp', postgresql.TIMESTAMP(), autoincrement=False, nullable=False),
|
||||
sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False),
|
||||
sa.Column('org', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||
sa.Column('address', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||
sa.Column('zipcode', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||
sa.Column('email', sa.VARCHAR(), autoincrement=False, nullable=False),
|
||||
sa.Column('file_contract', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||
sa.Column('upload_file_hashes', postgresql.ARRAY(sa.VARCHAR()), autoincrement=False, nullable=True),
|
||||
sa.Column('corpus_name', sa.TEXT(), autoincrement=False, nullable=False),
|
||||
sa.PrimaryKeyConstraint('id', name='upload_regular_pkey')
|
||||
)
|
||||
# ### end Alembic commands ###
|
604
portal/base.py
604
portal/base.py
|
@ -1,604 +0,0 @@
|
|||
import os
|
||||
import hashlib
|
||||
import time
|
||||
import ssl
|
||||
import traceback
|
||||
import re
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
import imaplib
|
||||
from smtplib import SMTP_SSL
|
||||
|
||||
import email
|
||||
from email import encoders
|
||||
from email.mime.base import MIMEBase
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.application import MIMEApplication
|
||||
|
||||
import pdfkit
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
import jwt
|
||||
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from . model import *
|
||||
|
||||
|
||||
#REGEX_EMAIL = re.compile('^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$')
|
||||
REGEX_EMAIL = re.compile('^(?:[a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$')
|
||||
|
||||
MAX_FNAME_LEN = 100
|
||||
|
||||
|
||||
class ContractCreator:
|
||||
|
||||
def __init__(self, base_path, template_path):
|
||||
self.base = base_path
|
||||
template_loader = FileSystemLoader(searchpath="./")
|
||||
template_env = Environment(loader=template_loader)
|
||||
self.template = template_env.get_template(template_path)
|
||||
|
||||
self.pdfkit_options = {
|
||||
'page-size': 'A4',
|
||||
'margin-top': '0.75in',
|
||||
'margin-right': '0.75in',
|
||||
'margin-bottom': '0.75in',
|
||||
'margin-left': '0.75in',
|
||||
'encoding': "UTF-8",
|
||||
'custom-header' : [
|
||||
('Accept-Encoding', 'gzip')
|
||||
]
|
||||
}
|
||||
|
||||
def fill_template(self, **kwargs):
|
||||
return self.template.render(**kwargs)
|
||||
|
||||
def create_pdf(self, f_name, fields_dict):
|
||||
sub_dir = self.base / Path(f_name[:2])
|
||||
if not sub_dir.exists():
|
||||
sub_dir.mkdir()
|
||||
out_f = sub_dir / Path(f_name[2:])
|
||||
html_str = self.fill_template(**fields_dict)
|
||||
pdfkit.from_string(html_str, out_f, options=self.pdfkit_options)
|
||||
|
||||
|
||||
class UploadHandler:
|
||||
|
||||
ENABLED_FILETYPES = ['txt', 'csv', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'xml', 'mxliff', 'tmx', 'jpg', 'jpeg', 'png']
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.config = kwargs
|
||||
|
||||
def set_contract_creator(self, contract_creator):
|
||||
assert isinstance(contract_creator, ContractCreator)
|
||||
self._contract_creator = contract_creator
|
||||
|
||||
def get_uploads_subdir(self, dir_name):
|
||||
subdir = Path(self.config['UPLOADS_DIR']) / dir_name
|
||||
if not subdir.exists():
|
||||
subdir.mkdir(parents=True)
|
||||
return subdir
|
||||
|
||||
@staticmethod
|
||||
def extract_upload_metadata(corpus_name, request):
|
||||
upload_metadata = dict()
|
||||
|
||||
file_hashes = UploadHandler.create_file_hashes(request.files)
|
||||
file_names = file_hashes.keys()
|
||||
form_data = request.form.copy()
|
||||
upload_timestamp = int(time.time())
|
||||
upload_id = UploadHandler.create_upload_id(corpus_name, form_data, upload_timestamp, file_hashes)
|
||||
|
||||
# Strip form fieds.
|
||||
for key, val in form_data.items():
|
||||
form_data[key] = val.strip()
|
||||
|
||||
upload_metadata['corpus_name'] = corpus_name
|
||||
upload_metadata['form_data'] = form_data
|
||||
upload_metadata['upload_id'] = upload_id
|
||||
upload_metadata['timestamp'] = upload_timestamp
|
||||
upload_metadata['file_hashes_dict'] = file_hashes
|
||||
upload_metadata['file_names'] = file_names
|
||||
upload_metadata['contract_file'] = upload_id + '.pdf'
|
||||
|
||||
return upload_metadata
|
||||
|
||||
@staticmethod
|
||||
def create_upload_id(corpus_name, form_data, upload_timestamp, file_hashes):
|
||||
# Order is important while hashing, hence the sorting.
|
||||
val_buff = [str(upload_timestamp)]
|
||||
for key in sorted(form_data):
|
||||
val_buff.append(form_data[key])
|
||||
|
||||
# This hash serves as an unique identifier for the whole upload.
|
||||
metahash = hashlib.md5((''.join(val_buff)).encode())
|
||||
# Include file hashes to avoid metafile name collisions if they have the same form values,
|
||||
# but different data files. Sort hashes first so upload order doesn't matter.
|
||||
sorted_f_hashes = list(file_hashes.values())
|
||||
sorted_f_hashes.sort()
|
||||
metahash.update(''.join(sorted_f_hashes).encode())
|
||||
metahash = metahash.hexdigest()
|
||||
|
||||
return metahash
|
||||
|
||||
@staticmethod
|
||||
def create_file_hashes(files):
|
||||
res = dict()
|
||||
for key, f in files.items():
|
||||
if key.startswith('file'):
|
||||
h = hashlib.md5(f.filename.encode())
|
||||
h.update(f.stream.read())
|
||||
res[f.filename] = h.hexdigest()
|
||||
f.seek(0)
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def store_model(model_obj):
|
||||
try:
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def store_datafiles(self, files, upload_metadata):
|
||||
base = self.get_uploads_subdir('files')
|
||||
file_hashes = upload_metadata['file_hashes_dict']
|
||||
|
||||
for key, f in files.items():
|
||||
if key.startswith('file'):
|
||||
f_hash = file_hashes[f.filename]
|
||||
|
||||
# First byte used for indexing, similarly like git does for example.
|
||||
sub_dir = base / f_hash[:2]
|
||||
if not sub_dir.exists():
|
||||
sub_dir.mkdir()
|
||||
|
||||
path = sub_dir / f_hash[2:]
|
||||
if not path.exists():
|
||||
path.mkdir()
|
||||
f.save(path / f.filename)
|
||||
|
||||
|
||||
def send_confirm_mail(self, upload_metadata, attach_contract_file=False):
|
||||
upload_id = upload_metadata['upload_id']
|
||||
|
||||
message = MIMEMultipart()
|
||||
message['From'] = self.config['MAIL_LOGIN']
|
||||
message['To'] = upload_metadata['form_data']['email']
|
||||
message['Subject'] = self.config['MAIL_SUBJECT'].format(upload_id=upload_id)
|
||||
body = self.config['MAIL_BODY'].format(upload_id=upload_id)
|
||||
message.attach(MIMEText(body, "plain"))
|
||||
|
||||
if attach_contract_file:
|
||||
contracts_dir = self.contract_creator.base
|
||||
f_name = upload_metadata['contract_file']
|
||||
sub_dir = contracts_dir / Path(f_name[:2])
|
||||
contract_file = sub_dir / Path(f_name[2:])
|
||||
with open(contract_file, "rb") as f:
|
||||
part = MIMEApplication(
|
||||
f.read(),
|
||||
Name = f_name
|
||||
)
|
||||
part['Content-Disposition'] = 'attachment; filename="%s"' % f_name
|
||||
message.attach(part)
|
||||
|
||||
text = message.as_string()
|
||||
|
||||
# Create a secure SSL context
|
||||
context = ssl.create_default_context()
|
||||
|
||||
# TODO: Implement timeout.
|
||||
try:
|
||||
with SMTP_SSL(self.config['MAIL_HOST'], self.config['SMTP_PORT'], context=context) as server:
|
||||
server.login(self.config['MAIL_LOGIN'], self.config['MAIL_PASS'])
|
||||
server.sendmail(message['From'], message['To'], text)
|
||||
|
||||
# Save copy of sent mail in Sent mailbox
|
||||
#imap = imaplib.IMAP4_SSL(self.config['MAIL_HOST'], self.config['IMAP_PORT'])
|
||||
#imap.login(self.config['MAIL_LOGIN'], self.config['MAIL_PASS'])
|
||||
#imap.append('Sent', '\\Seen', imaplib.Time2Internaldate(time.time()), text.encode('utf8'))
|
||||
#imap.logout()
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
def check_suffixes(self, files):
|
||||
for key, f in files.items():
|
||||
if key.startswith('file'):
|
||||
suffix = f.filename.split('.')[-1]
|
||||
if self.ENABLED_FILETYPES and suffix.lower() not in self.ENABLED_FILETYPES:
|
||||
return 'Datoteka "{}" ni pravilnega formata.'.format(f.filename)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def check_fname_lengths(files):
|
||||
for key, f in files.items():
|
||||
if key.startswith('file'):
|
||||
if len(f.filename) > MAX_FNAME_LEN:
|
||||
return 'Ime datoteke presega dolžino {} znakov.'.format(MAX_FNAME_LEN)
|
||||
return None
|
||||
|
||||
def check_upload_request(self, request):
|
||||
files = request.files
|
||||
max_files = self.config['MAX_FILES_PER_UPLOAD']
|
||||
if len(files) > max_files:
|
||||
return 'Naložite lahko do {} datotek hkrati.'.format(max_files), 400
|
||||
elif len(files) < 1:
|
||||
return 'Priložena ni bila nobena datoteka.', 400
|
||||
|
||||
err = self.check_suffixes(files)
|
||||
if err:
|
||||
return err, 400
|
||||
|
||||
err = UploadHandler.check_fname_lengths(files)
|
||||
if err:
|
||||
return err, 400
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_user_institution(user_id):
|
||||
mapping = UserInstitutionMapping.query.filter_by(user=user_id).first()
|
||||
if mapping:
|
||||
return Institution.query.filter_by(id=mapping.institution).first()
|
||||
return None
|
||||
|
||||
|
||||
def get_institution_contract(institution_id):
|
||||
return InstitutionContract.query.filter_by(institution=institution_id).first()
|
||||
|
||||
|
||||
def get_institution_cooperation_history(institution_id):
|
||||
#return CooperationToken.query.join(UserCooperationTokenMapping,
|
||||
# UserCooperationTokenMapping.cooperation_token == CooperationToken.id).filter(UserCooperationTokenMapping.user == user_id).all()
|
||||
#
|
||||
res = dict()
|
||||
|
||||
uch_rows = UserCooperationHistory.query.filter_by(institution=institution_id).order_by(UserCooperationHistory.school_year.desc()).all()
|
||||
for row in uch_rows:
|
||||
if row.user not in res:
|
||||
res[row.user] = {
|
||||
'coordinator': [],
|
||||
'mentor': [],
|
||||
'other': []
|
||||
}
|
||||
res[row.user][row.role].append((row.school_year, row.badge_text))
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def get_cooperation_history():
|
||||
return UserCooperationHistory.query.all()
|
||||
|
||||
|
||||
def add_cooperation_history_item(user_id, institution_id, role, school_year, badge_text):
|
||||
model_obj = UserCooperationHistory(
|
||||
user=user_id,
|
||||
institution=institution_id,
|
||||
role=role,
|
||||
school_year=school_year,
|
||||
badge_text=badge_text
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
def del_cooperation_history_item(entry_id):
|
||||
db.session.query(UserCooperationHistory).filter_by(id=entry_id).delete()
|
||||
db.session.commit()
|
||||
|
||||
def has_user_corpus_access(user_id, corpus_name):
|
||||
user = RegisteredUser.query.filter_by(id=user_id).first()
|
||||
|
||||
# TODO: check if user even is active?
|
||||
|
||||
# Admins always have access to everything.
|
||||
if user.role == 'admin':
|
||||
return True
|
||||
|
||||
# Check if user belongs to an institution, that has access to this corpus.
|
||||
institution = get_user_institution(user_id)
|
||||
has_access = False
|
||||
row = CorpusAccess.query.filter_by(institution=institution.id, corpus=corpus_name).first()
|
||||
if row:
|
||||
has_access = True
|
||||
return has_access
|
||||
|
||||
|
||||
def is_admin(user_id):
|
||||
user = RegisteredUser.query.filter_by(id=user_id).first()
|
||||
if user.role == 'admin':
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_user_obj(user_id):
|
||||
return RegisteredUser.query.filter_by(id=user_id).first()
|
||||
|
||||
def get_user_obj_by_email(email):
|
||||
return RegisteredUser.query.filter_by(email=email).first()
|
||||
|
||||
def get_institution_obj(institution_id):
|
||||
return Institution.query.filter_by(id=institution_id).first()
|
||||
|
||||
|
||||
def get_institution_obj_by_name(institution_name):
|
||||
return Institution.query.filter_by(name=institution_name).first()
|
||||
|
||||
|
||||
def register_new_user(name, email, password, active=True, admin=False):
|
||||
model_obj = RegisteredUser(
|
||||
name=name,
|
||||
email=email,
|
||||
role='admin' if admin else 'user',
|
||||
pass_hash=generate_password_hash(password),
|
||||
active=active,
|
||||
registered=datetime.now()
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
|
||||
def add_institution(name, region):
|
||||
model_obj = Institution(
|
||||
name=name,
|
||||
region=region
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
|
||||
def grant_institution_corpus_access(institution_id, corpus_name):
|
||||
model_obj = CorpusAccess(
|
||||
institution=institution_id,
|
||||
corpus=corpus_name
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
|
||||
def add_user_to_institution(user_id, institution_id, role):
|
||||
model_obj = UserInstitutionMapping(
|
||||
user=user_id,
|
||||
institution=institution_id,
|
||||
role=role
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
|
||||
def activate_user(user_id):
|
||||
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'active': True})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def update_user_password(user_id, new_password):
|
||||
phash = generate_password_hash(new_password)
|
||||
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'pass_hash': phash})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def update_user_role(user_id, role):
|
||||
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'role': role})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def update_user_email(user_id, new_email):
|
||||
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'email': new_email})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def update_user_name(user_id, new_name):
|
||||
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'name': new_name})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
def update_institution_data(institution_id, new_name, new_region):
|
||||
rowcount = db.session.query(Institution).filter_by(id=institution_id).update({'name': new_name, 'region': new_region})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def remove_user(user_id):
|
||||
db.session.query(UserCooperationHistory).filter(UserCooperationHistory.user == user_id).delete()
|
||||
db.session.query(UserInstitutionMapping).filter(UserInstitutionMapping.user == user_id).delete()
|
||||
db.session.query(RegisteredUser).filter(RegisteredUser.id == user_id).delete()
|
||||
db.session.commit()
|
||||
#db.session.query(RegisteredUser).filter(RegisteredUser.id == user_id).update({'is_removed': True})
|
||||
#db.session.commit()
|
||||
|
||||
#def undo_remove_user(user_id):
|
||||
# db.session.query(RegisteredUser).filter(RegisteredUser.id == user_id).update({'is_removed': False})
|
||||
# db.session.commit()
|
||||
|
||||
def remove_institution(institution_id):
|
||||
db.session.query(CorpusAccess).filter(CorpusAccess.institution == institution_id).delete()
|
||||
db.session.query(Institution).filter(Institution.id == institution_id).delete()
|
||||
db.session.commit()
|
||||
#db.session.query(Institution).filter(Institution.id == institution_id).update({'is_removed': True})
|
||||
#db.session.commit()
|
||||
|
||||
|
||||
def del_user_from_institution(user_id, institution_id):
|
||||
db.session.query(UserInstitutionMapping).filter(UserInstitutionMapping.institution == institution_id).filter(UserInstitutionMapping.user == user_id).delete()
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def get_all_active_users():
|
||||
return RegisteredUser.query.filter_by(active=True).order_by(RegisteredUser.id).all()
|
||||
|
||||
|
||||
def get_all_inactive_users():
|
||||
return RegisteredUser.query.filter_by(active=False).order_by(RegisteredUser.id).all()
|
||||
|
||||
|
||||
def get_all_users_join_institutions(active=True):
|
||||
#return RegisteredUser.query.filter_by(active=True).order_by(RegisteredUser.id).all()
|
||||
return db.session.query(RegisteredUser, UserInstitutionMapping).outerjoin(UserInstitutionMapping,
|
||||
RegisteredUser.id == UserInstitutionMapping.user).filter(RegisteredUser.active == active).order_by(RegisteredUser.id).all()
|
||||
|
||||
|
||||
|
||||
def get_all_active_institution_users(institution_id):
|
||||
return RegisteredUser.query.filter_by(active=True).join(UserInstitutionMapping,
|
||||
RegisteredUser.id == UserInstitutionMapping.user).filter(UserInstitutionMapping.institution == institution_id).all()
|
||||
|
||||
|
||||
def is_institution_coordinator(user_id, institution_id):
|
||||
user_inst_mapping = UserInstitutionMapping.query.filter_by(user=user_id).filter_by(institution=institution_id).first()
|
||||
if not user_inst_mapping:
|
||||
return False
|
||||
if user_inst_mapping.role != 'coordinator':
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def is_institution_member(user_id, institution_id):
|
||||
user_inst_mapping = UserInstitutionMapping.query.filter_by(user=user_id).filter_by(institution=institution_id).first()
|
||||
if not user_inst_mapping:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_actual_institution_contract_filename(f_hash):
|
||||
contract = InstitutionContract.query.filter_by(file_contract=f_hash).first()
|
||||
if contract:
|
||||
return contract.original_filename
|
||||
return None
|
||||
|
||||
|
||||
def get_actual_studentparent_contract_filename(f_hash):
|
||||
contract = ContractsSolar.query.filter_by(file_contract=f_hash).first()
|
||||
if contract:
|
||||
return contract.original_filename
|
||||
return None
|
||||
|
||||
|
||||
def get_password_reset_token(email, key, expires=600):
|
||||
return jwt.encode({'reset_password': email,
|
||||
'exp': int(time.time()) + expires},
|
||||
key=key, algorithm='HS256')
|
||||
|
||||
|
||||
def transfer_users_institution(institution_id_from, institution_id_to):
|
||||
rowcount = db.session.query(UserInstitutionMapping).filter_by(institution=institution_id_from).update(
|
||||
{'institution': institution_id_to})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def transfer_uploads_institution(institution_id_from, institution_id_to):
|
||||
rowcount = db.session.query(UploadSolar).filter_by(institution=institution_id_from).update(
|
||||
{'institution': institution_id_to})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def transfer_contracts_institution(institution_id_from, institution_id_to):
|
||||
rowcount = db.session.query(ContractsSolar).filter_by(institution=institution_id_from).update(
|
||||
{'institution': institution_id_to})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def verify_reset_token(token, key):
|
||||
try:
|
||||
message = jwt.decode(token,
|
||||
key=key, algorithms=["HS256"])
|
||||
email = message['reset_password']
|
||||
exp = message['exp']
|
||||
if int(time.time()) >= exp:
|
||||
# Token timed out
|
||||
return None
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
return
|
||||
return RegisteredUser.query.filter_by(email=email).first()
|
||||
|
||||
|
||||
def send_resetpass_mail(email, config):
|
||||
jwt_token = get_password_reset_token(email, config['APP_SECRET_KEY'])
|
||||
|
||||
body = '''
|
||||
Zahtevali ste ponastavitev gesla vašega uporabniškega računa.
|
||||
|
||||
Geslo lahko ponastavite na naslednji povezavi: https://zbiranje.slovenscina.eu/solar/resetpass/{}'''.format(jwt_token)
|
||||
|
||||
message = MIMEMultipart()
|
||||
message['From'] = config['MAIL_LOGIN']
|
||||
message['To'] = email
|
||||
message['Subject'] = 'Ponastavitev gesla'
|
||||
message.attach(MIMEText(body, "plain"))
|
||||
text = message.as_string()
|
||||
|
||||
# Create a secure SSL context
|
||||
context = ssl.create_default_context()
|
||||
|
||||
try:
|
||||
with SMTP_SSL(config['MAIL_HOST'], config['SMTP_PORT'], context=context) as server:
|
||||
server.login(config['MAIL_LOGIN'], config['MAIL_PASS'])
|
||||
server.sendmail(config['MAIL_LOGIN'], email, text)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def send_admins_new_user_notification_mail(user_id, config):
|
||||
user = RegisteredUser.query.filter_by(id=user_id).first()
|
||||
body = '''
|
||||
Nov uporabnik "{}" je ustvaril uporabniški račun na portalu za oddajanje besedil Šolar in čaka na odobritev.
|
||||
'''.format(user.name)
|
||||
|
||||
|
||||
admins = RegisteredUser.query.filter_by(role="admin").all()
|
||||
|
||||
# Create a secure SSL context
|
||||
context = ssl.create_default_context()
|
||||
|
||||
for admin in admins:
|
||||
message = MIMEMultipart()
|
||||
message['From'] = config['MAIL_LOGIN']
|
||||
message['To'] = admin.email
|
||||
message['Subject'] = 'Nova registracija'
|
||||
message.attach(MIMEText(body, "plain"))
|
||||
text = message.as_string()
|
||||
try:
|
||||
with SMTP_SSL(config['MAIL_HOST'], config['SMTP_PORT'], context=context) as server:
|
||||
server.login(config['MAIL_LOGIN'], config['MAIL_PASS'])
|
||||
server.sendmail(config['MAIL_LOGIN'], admin.email, text)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def send_user_activation_mail(user_id, config):
|
||||
user = RegisteredUser.query.filter_by(id=user_id).first()
|
||||
body = '''Vaš uporabniški račun "{}" na portalu Šolar je bil odobren.'''.format(user.name)
|
||||
|
||||
message = MIMEMultipart()
|
||||
message['From'] = config['MAIL_LOGIN']
|
||||
message['To'] = user.email
|
||||
message['Subject'] = 'Ponastavitev gesla'
|
||||
message.attach(MIMEText(body, "plain"))
|
||||
text = message.as_string()
|
||||
|
||||
# Create a secure SSL context
|
||||
context = ssl.create_default_context()
|
||||
|
||||
try:
|
||||
with SMTP_SSL(config['MAIL_HOST'], config['SMTP_PORT'], context=context) as server:
|
||||
server.login(config['MAIL_LOGIN'], config['MAIL_PASS'])
|
||||
server.sendmail(config['MAIL_LOGIN'], user.email, text)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
|
@ -12,22 +12,6 @@ from flask_login import UserMixin
|
|||
db = SQLAlchemy()
|
||||
|
||||
|
||||
# "prevodi" or "gigafida".
|
||||
class UploadRegular(db.Model):
|
||||
__tablename__ = 'upload_regular'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
upload_hash = db.Column(db.String, nullable=False)
|
||||
timestamp = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||
name = db.Column(db.String, nullable=False)
|
||||
org = db.Column(db.String, nullable=True)
|
||||
address = db.Column(db.String, nullable=True)
|
||||
zipcode = db.Column(db.String, nullable=True)
|
||||
email = db.Column(db.String, nullable=False)
|
||||
file_contract = db.Column(db.String, nullable=True)
|
||||
upload_file_hashes = db.Column(sqlalchemy.types.ARRAY(db.String), nullable=True)
|
||||
corpus_name = db.Column(db.String, nullable=False)
|
||||
|
||||
|
||||
class UploadSolar(db.Model):
|
||||
__tablename__ = 'upload_solar'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
@ -78,18 +62,6 @@ class UserInstitutionMapping(db.Model):
|
|||
role = db.Column(db.String, nullable=False)
|
||||
|
||||
|
||||
#class CooperationToken(db.Model):
|
||||
# __tablename__ = 'cooperation_token'
|
||||
# id = db.Column(db.Integer, primary_key=True)
|
||||
# name = db.Column(db.String, nullable=False)
|
||||
#
|
||||
#
|
||||
#class UserCooperationTokenMapping(db.Model):
|
||||
# __tablename__ = 'user_cooperation_token_mapping'
|
||||
# id = db.Column(db.Integer, primary_key=True)
|
||||
# user = db.Column(db.Integer, sqlalchemy.ForeignKey('registered_user.id'), nullable=False)
|
||||
# cooperation_token = db.Column(db.Integer, sqlalchemy.ForeignKey('cooperation_token.id'), nullable=False)
|
||||
|
||||
class UserCooperationHistory(db.Model):
|
||||
__tablename__ = 'user_cooperation'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
import logging
|
||||
import re
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
from portal.base import UploadHandler, ContractCreator, REGEX_EMAIL
|
||||
from portal.model import db, UploadRegular
|
||||
|
||||
|
||||
class UploadHandlerRegular(UploadHandler):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.contract_creator = ContractCreator(base_path=self.get_uploads_subdir('contracts'),
|
||||
template_path='contract/regular.html')
|
||||
|
||||
def generate_upload_contract_pdf(self, upload_metadata):
|
||||
base = self.get_uploads_subdir('contracts')
|
||||
form_data = upload_metadata['form_data']
|
||||
|
||||
files_table_str = []
|
||||
for file_name in upload_metadata['file_names']:
|
||||
files_table_str.append('<tr><td style="text-align: center;">')
|
||||
files_table_str.append(file_name)
|
||||
files_table_str.append('</td></tr>')
|
||||
files_table_str = ''.join(files_table_str)
|
||||
|
||||
data = {
|
||||
'ime_priimek': form_data['ime'],
|
||||
'naslov': form_data.get('naslov', ''),
|
||||
'posta': form_data.get('posta', ''),
|
||||
'kontakt_narocnik': self.config['CONTRACT_CLIENT_CONTACT'],
|
||||
'kontakt_imetnikpravic': form_data['ime'],
|
||||
'files_table_str': files_table_str
|
||||
}
|
||||
|
||||
self.contract_creator.create_pdf(upload_metadata['contract_file'], data)
|
||||
|
||||
@staticmethod
|
||||
def store_metadata(upload_metadata, corpus_name):
|
||||
timestamp = datetime.fromtimestamp(upload_metadata['timestamp'])
|
||||
form_data = upload_metadata['form_data']
|
||||
file_hashes = upload_metadata['file_hashes_dict']
|
||||
sorted_f_hashes = list(file_hashes.values())
|
||||
sorted_f_hashes.sort()
|
||||
|
||||
try:
|
||||
model_obj = UploadRegular(
|
||||
upload_hash=upload_metadata['upload_id'],
|
||||
timestamp=timestamp,
|
||||
name=form_data['ime'],
|
||||
org=form_data.get('podjetje'),
|
||||
address=form_data.get('naslov'),
|
||||
zipcode=form_data.get('posta'),
|
||||
email=form_data['email'],
|
||||
file_contract=upload_metadata['contract_file'],
|
||||
upload_file_hashes=sorted_f_hashes,
|
||||
corpus_name=corpus_name
|
||||
)
|
||||
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def handle_upload(self, request, corpus_name):
|
||||
err = self.check_upload_request(request)
|
||||
if err:
|
||||
return err, 400
|
||||
|
||||
err = self.check_form(request.form)
|
||||
if err:
|
||||
return err, 400
|
||||
|
||||
# Parse request.
|
||||
upload_metadata = self.extract_upload_metadata(corpus_name, request)
|
||||
|
||||
logging.info('Upload with id "{}" supplied form data: {}'.format(upload_metadata['upload_id'],
|
||||
str(upload_metadata['form_data'])))
|
||||
|
||||
# Generate contract PDF file based on the uploads metadata.
|
||||
self.generate_upload_contract_pdf(upload_metadata)
|
||||
|
||||
# Store uploaded files to disk.
|
||||
self.store_datafiles(request.files, upload_metadata)
|
||||
|
||||
# Store metadata to database.
|
||||
self.store_metadata(upload_metadata, corpus_name)
|
||||
|
||||
# Send confirmation mail along with the contract to the submitted email address.
|
||||
self.send_confirm_mail(upload_metadata, attach_contract_file=True)
|
||||
|
||||
return 'Uspešno ste oddali datotek(e). Št. datotek: {}'.format(len(request.files))
|
||||
|
||||
@staticmethod
|
||||
def check_form(form):
|
||||
ime = form.get('ime')
|
||||
podjetje = form.get('podjetje')
|
||||
naslov = form.get('naslov')
|
||||
posta = form.get('posta')
|
||||
email = form.get('email')
|
||||
telefon = form.get('telefon')
|
||||
|
||||
if not ime:
|
||||
return 'Prazno polje za ime.'
|
||||
if len(ime) > 100:
|
||||
return 'Predolgo ime.'
|
||||
|
||||
if podjetje and len(podjetje) > 100:
|
||||
return 'Predolgo ime institucije.'
|
||||
|
||||
if not email:
|
||||
return 'Prazno polje za elektronsko pošto.'
|
||||
if len(email) > 100:
|
||||
return 'Predolgi email naslov'
|
||||
elif not re.search(REGEX_EMAIL, email):
|
||||
return 'Email napačnega formata.'
|
||||
|
||||
if telefon and len(telefon) > 100:
|
||||
return 'Predolga telefonska št.'
|
||||
|
||||
if naslov and len(naslov) > 100:
|
||||
return 'Predolg naslov.'
|
||||
|
||||
if posta and len(posta) > 100:
|
||||
return 'Predolga pošta'
|
||||
|
||||
return None
|
||||
|
592
portal/solar.py
592
portal/solar.py
|
@ -1,11 +1,31 @@
|
|||
import logging
|
||||
import re
|
||||
import hashlib
|
||||
import time
|
||||
import traceback
|
||||
import ssl
|
||||
from datetime import datetime
|
||||
from sqlalchemy import desc
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
import imaplib
|
||||
from smtplib import SMTP_SSL
|
||||
|
||||
import email
|
||||
from email import encoders
|
||||
from email.mime.base import MIMEBase
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.application import MIMEApplication
|
||||
|
||||
import pdfkit
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
import jwt
|
||||
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from portal.base import UploadHandler, get_user_institution, has_user_corpus_access
|
||||
from . model import *
|
||||
|
||||
|
||||
|
@ -15,10 +35,119 @@ VALID_TEXT_TYPES = {'esej-spis', 'prakticno', 'solski-test', 'delo-v-razredu'}
|
|||
VALID_GRAMMAR_CORRECTIONS = {'popr-ne', 'brez-popr', 'popr-da'}
|
||||
VALID_REGIONS = {'CE', 'GO', 'KK', 'KP', 'KR', 'LJ', 'MB', 'MS', 'NM', 'PO', 'SG'}
|
||||
|
||||
#REGEX_EMAIL = re.compile('^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$')
|
||||
REGEX_EMAIL = re.compile('^(?:[a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$')
|
||||
|
||||
MAX_FNAME_LEN = 100
|
||||
MAXLEN_FORM = 150
|
||||
|
||||
class ContractCreator:
|
||||
|
||||
class UploadHandlerSolar(UploadHandler):
|
||||
def __init__(self, base_path, template_path):
|
||||
self.base = base_path
|
||||
template_loader = FileSystemLoader(searchpath="./")
|
||||
template_env = Environment(loader=template_loader)
|
||||
self.template = template_env.get_template(template_path)
|
||||
|
||||
self.pdfkit_options = {
|
||||
'page-size': 'A4',
|
||||
'margin-top': '0.75in',
|
||||
'margin-right': '0.75in',
|
||||
'margin-bottom': '0.75in',
|
||||
'margin-left': '0.75in',
|
||||
'encoding': "UTF-8",
|
||||
'custom-header' : [
|
||||
('Accept-Encoding', 'gzip')
|
||||
]
|
||||
}
|
||||
|
||||
def fill_template(self, **kwargs):
|
||||
return self.template.render(**kwargs)
|
||||
|
||||
def create_pdf(self, f_name, fields_dict):
|
||||
sub_dir = self.base / Path(f_name[:2])
|
||||
if not sub_dir.exists():
|
||||
sub_dir.mkdir()
|
||||
out_f = sub_dir / Path(f_name[2:])
|
||||
html_str = self.fill_template(**fields_dict)
|
||||
pdfkit.from_string(html_str, out_f, options=self.pdfkit_options)
|
||||
|
||||
|
||||
class UploadHandlerSolar():
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.config = kwargs
|
||||
|
||||
def set_contract_creator(self, contract_creator):
|
||||
assert isinstance(contract_creator, ContractCreator)
|
||||
self._contract_creator = contract_creator
|
||||
|
||||
def get_uploads_subdir(self, dir_name):
|
||||
subdir = Path(self.config['UPLOADS_DIR']) / dir_name
|
||||
if not subdir.exists():
|
||||
subdir.mkdir(parents=True)
|
||||
return subdir
|
||||
|
||||
@staticmethod
|
||||
def create_upload_id(corpus_name, form_data, upload_timestamp, file_hashes):
|
||||
# Order is important while hashing, hence the sorting.
|
||||
val_buff = [str(upload_timestamp)]
|
||||
for key in sorted(form_data):
|
||||
val_buff.append(form_data[key])
|
||||
|
||||
# This hash serves as an unique identifier for the whole upload.
|
||||
metahash = hashlib.md5((''.join(val_buff)).encode())
|
||||
# Include file hashes to avoid metafile name collisions if they have the same form values,
|
||||
# but different data files. Sort hashes first so upload order doesn't matter.
|
||||
sorted_f_hashes = list(file_hashes.values())
|
||||
sorted_f_hashes.sort()
|
||||
metahash.update(''.join(sorted_f_hashes).encode())
|
||||
metahash = metahash.hexdigest()
|
||||
|
||||
return metahash
|
||||
|
||||
@staticmethod
|
||||
def extract_upload_metadata(corpus_name, request):
|
||||
upload_metadata = dict()
|
||||
|
||||
file_hashes = UploadHandlerSolar.create_file_hashes(request.files)
|
||||
file_names = file_hashes.keys()
|
||||
form_data = request.form.copy()
|
||||
upload_timestamp = int(time.time())
|
||||
upload_id = UploadHandlerSolar.create_upload_id(corpus_name, form_data, upload_timestamp, file_hashes)
|
||||
|
||||
# Strip form fieds.
|
||||
for key, val in form_data.items():
|
||||
form_data[key] = val.strip()
|
||||
|
||||
upload_metadata['corpus_name'] = corpus_name
|
||||
upload_metadata['form_data'] = form_data
|
||||
upload_metadata['upload_id'] = upload_id
|
||||
upload_metadata['timestamp'] = upload_timestamp
|
||||
upload_metadata['file_hashes_dict'] = file_hashes
|
||||
upload_metadata['file_names'] = file_names
|
||||
upload_metadata['contract_file'] = upload_id + '.pdf'
|
||||
|
||||
return upload_metadata
|
||||
|
||||
@staticmethod
|
||||
def store_model(model_obj):
|
||||
try:
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
@staticmethod
|
||||
def create_file_hashes(files):
|
||||
res = dict()
|
||||
for key, f in files.items():
|
||||
if key.startswith('file'):
|
||||
h = hashlib.md5(f.filename.encode())
|
||||
h.update(f.stream.read())
|
||||
res[f.filename] = h.hexdigest()
|
||||
f.seek(0)
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def store_metadata(upload_metadata, user_id):
|
||||
|
@ -45,7 +174,25 @@ class UploadHandlerSolar(UploadHandler):
|
|||
grammar_corrections=form_data['jezikovni-popravki'],
|
||||
upload_file_hashes=sorted_f_hashes
|
||||
)
|
||||
UploadHandler.store_model(model_obj)
|
||||
UploadHandlerSolar.store_model(model_obj)
|
||||
|
||||
def store_datafiles(self, files, upload_metadata):
|
||||
base = self.get_uploads_subdir('files')
|
||||
file_hashes = upload_metadata['file_hashes_dict']
|
||||
|
||||
for key, f in files.items():
|
||||
if key.startswith('file'):
|
||||
f_hash = file_hashes[f.filename]
|
||||
|
||||
# First byte used for indexing, similarly like git does for example.
|
||||
sub_dir = base / f_hash[:2]
|
||||
if not sub_dir.exists():
|
||||
sub_dir.mkdir()
|
||||
|
||||
path = sub_dir / f_hash[2:]
|
||||
if not path.exists():
|
||||
path.mkdir()
|
||||
f.save(path / f.filename)
|
||||
|
||||
def handle_upload(self, request, user_id):
|
||||
err = self.check_upload_request(request)
|
||||
|
@ -159,6 +306,83 @@ class UploadHandlerSolar(UploadHandler):
|
|||
if len(val) > MAXLEN_FORM:
|
||||
return 'Value in form field "{}" exceeds max len of {}'.format(key, MAXLEN_FORM)
|
||||
|
||||
def send_confirm_mail(self, upload_metadata, attach_contract_file=False):
|
||||
upload_id = upload_metadata['upload_id']
|
||||
|
||||
message = MIMEMultipart()
|
||||
message['From'] = self.config['MAIL_LOGIN']
|
||||
message['To'] = upload_metadata['form_data']['email']
|
||||
message['Subject'] = self.config['MAIL_SUBJECT'].format(upload_id=upload_id)
|
||||
body = self.config['MAIL_BODY'].format(upload_id=upload_id)
|
||||
message.attach(MIMEText(body, "plain"))
|
||||
|
||||
if attach_contract_file:
|
||||
contracts_dir = self.contract_creator.base
|
||||
f_name = upload_metadata['contract_file']
|
||||
sub_dir = contracts_dir / Path(f_name[:2])
|
||||
contract_file = sub_dir / Path(f_name[2:])
|
||||
with open(contract_file, "rb") as f:
|
||||
part = MIMEApplication(
|
||||
f.read(),
|
||||
Name = f_name
|
||||
)
|
||||
part['Content-Disposition'] = 'attachment; filename="%s"' % f_name
|
||||
message.attach(part)
|
||||
|
||||
text = message.as_string()
|
||||
|
||||
# Create a secure SSL context
|
||||
context = ssl.create_default_context()
|
||||
|
||||
# TODO: Implement timeout.
|
||||
try:
|
||||
with SMTP_SSL(self.config['MAIL_HOST'], self.config['SMTP_PORT'], context=context) as server:
|
||||
server.login(self.config['MAIL_LOGIN'], self.config['MAIL_PASS'])
|
||||
server.sendmail(message['From'], message['To'], text)
|
||||
|
||||
# Save copy of sent mail in Sent mailbox
|
||||
#imap = imaplib.IMAP4_SSL(self.config['MAIL_HOST'], self.config['IMAP_PORT'])
|
||||
#imap.login(self.config['MAIL_LOGIN'], self.config['MAIL_PASS'])
|
||||
#imap.append('Sent', '\\Seen', imaplib.Time2Internaldate(time.time()), text.encode('utf8'))
|
||||
#imap.logout()
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
def check_suffixes(self, files):
|
||||
for key, f in files.items():
|
||||
if key.startswith('file'):
|
||||
suffix = f.filename.split('.')[-1]
|
||||
if self.ENABLED_FILETYPES and suffix.lower() not in self.ENABLED_FILETYPES:
|
||||
return 'Datoteka "{}" ni pravilnega formata.'.format(f.filename)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def check_fname_lengths(files):
|
||||
for key, f in files.items():
|
||||
if key.startswith('file'):
|
||||
if len(f.filename) > MAX_FNAME_LEN:
|
||||
return 'Ime datoteke presega dolžino {} znakov.'.format(MAX_FNAME_LEN)
|
||||
return None
|
||||
|
||||
def check_upload_request(self, request):
|
||||
files = request.files
|
||||
max_files = self.config['MAX_FILES_PER_UPLOAD']
|
||||
if len(files) > max_files:
|
||||
return 'Naložite lahko do {} datotek hkrati.'.format(max_files), 400
|
||||
elif len(files) < 1:
|
||||
return 'Priložena ni bila nobena datoteka.', 400
|
||||
|
||||
err = self.check_suffixes(files)
|
||||
if err:
|
||||
return err, 400
|
||||
|
||||
err = UploadHandlerSolar.check_fname_lengths(files)
|
||||
if err:
|
||||
return err, 400
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def get_upload_history(user_id, n=20):
|
||||
if n == -1:
|
||||
|
@ -231,3 +455,365 @@ def update_upload_item(item_id, program, subject, subject_custom, grade, text_ty
|
|||
})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
def get_user_institution(user_id):
|
||||
mapping = UserInstitutionMapping.query.filter_by(user=user_id).first()
|
||||
if mapping:
|
||||
return Institution.query.filter_by(id=mapping.institution).first()
|
||||
return None
|
||||
|
||||
|
||||
def get_institution_contract(institution_id):
|
||||
return InstitutionContract.query.filter_by(institution=institution_id).first()
|
||||
|
||||
|
||||
def get_institution_cooperation_history(institution_id):
|
||||
#return CooperationToken.query.join(UserCooperationTokenMapping,
|
||||
# UserCooperationTokenMapping.cooperation_token == CooperationToken.id).filter(UserCooperationTokenMapping.user == user_id).all()
|
||||
#
|
||||
res = dict()
|
||||
|
||||
uch_rows = UserCooperationHistory.query.filter_by(institution=institution_id).order_by(UserCooperationHistory.school_year.desc()).all()
|
||||
for row in uch_rows:
|
||||
if row.user not in res:
|
||||
res[row.user] = {
|
||||
'coordinator': [],
|
||||
'mentor': [],
|
||||
'other': []
|
||||
}
|
||||
res[row.user][row.role].append((row.school_year, row.badge_text))
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def get_cooperation_history():
|
||||
return UserCooperationHistory.query.all()
|
||||
|
||||
|
||||
def add_cooperation_history_item(user_id, institution_id, role, school_year, badge_text):
|
||||
model_obj = UserCooperationHistory(
|
||||
user=user_id,
|
||||
institution=institution_id,
|
||||
role=role,
|
||||
school_year=school_year,
|
||||
badge_text=badge_text
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
def del_cooperation_history_item(entry_id):
|
||||
db.session.query(UserCooperationHistory).filter_by(id=entry_id).delete()
|
||||
db.session.commit()
|
||||
|
||||
def has_user_corpus_access(user_id, corpus_name):
|
||||
user = RegisteredUser.query.filter_by(id=user_id).first()
|
||||
|
||||
# TODO: check if user even is active?
|
||||
|
||||
# Admins always have access to everything.
|
||||
if user.role == 'admin':
|
||||
return True
|
||||
|
||||
# Check if user belongs to an institution, that has access to this corpus.
|
||||
institution = get_user_institution(user_id)
|
||||
has_access = False
|
||||
row = CorpusAccess.query.filter_by(institution=institution.id, corpus=corpus_name).first()
|
||||
if row:
|
||||
has_access = True
|
||||
return has_access
|
||||
|
||||
|
||||
def is_admin(user_id):
|
||||
user = RegisteredUser.query.filter_by(id=user_id).first()
|
||||
if user.role == 'admin':
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_user_obj(user_id):
|
||||
return RegisteredUser.query.filter_by(id=user_id).first()
|
||||
|
||||
def get_user_obj_by_email(email):
|
||||
return RegisteredUser.query.filter_by(email=email).first()
|
||||
|
||||
def get_institution_obj(institution_id):
|
||||
return Institution.query.filter_by(id=institution_id).first()
|
||||
|
||||
|
||||
def get_institution_obj_by_name(institution_name):
|
||||
return Institution.query.filter_by(name=institution_name).first()
|
||||
|
||||
|
||||
def register_new_user(name, email, password, active=True, admin=False):
|
||||
model_obj = RegisteredUser(
|
||||
name=name,
|
||||
email=email,
|
||||
role='admin' if admin else 'user',
|
||||
pass_hash=generate_password_hash(password),
|
||||
active=active,
|
||||
registered=datetime.now()
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
|
||||
def add_institution(name, region):
|
||||
model_obj = Institution(
|
||||
name=name,
|
||||
region=region
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
|
||||
def grant_institution_corpus_access(institution_id, corpus_name):
|
||||
model_obj = CorpusAccess(
|
||||
institution=institution_id,
|
||||
corpus=corpus_name
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
|
||||
def add_user_to_institution(user_id, institution_id, role):
|
||||
model_obj = UserInstitutionMapping(
|
||||
user=user_id,
|
||||
institution=institution_id,
|
||||
role=role
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
|
||||
def activate_user(user_id):
|
||||
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'active': True})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def update_user_password(user_id, new_password):
|
||||
phash = generate_password_hash(new_password)
|
||||
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'pass_hash': phash})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def update_user_role(user_id, role):
|
||||
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'role': role})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def update_user_email(user_id, new_email):
|
||||
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'email': new_email})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def update_user_name(user_id, new_name):
|
||||
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'name': new_name})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
def update_institution_data(institution_id, new_name, new_region):
|
||||
rowcount = db.session.query(Institution).filter_by(id=institution_id).update({'name': new_name, 'region': new_region})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def remove_user(user_id):
|
||||
db.session.query(UserCooperationHistory).filter(UserCooperationHistory.user == user_id).delete()
|
||||
db.session.query(UserInstitutionMapping).filter(UserInstitutionMapping.user == user_id).delete()
|
||||
db.session.query(RegisteredUser).filter(RegisteredUser.id == user_id).delete()
|
||||
db.session.commit()
|
||||
#db.session.query(RegisteredUser).filter(RegisteredUser.id == user_id).update({'is_removed': True})
|
||||
#db.session.commit()
|
||||
|
||||
#def undo_remove_user(user_id):
|
||||
# db.session.query(RegisteredUser).filter(RegisteredUser.id == user_id).update({'is_removed': False})
|
||||
# db.session.commit()
|
||||
|
||||
def remove_institution(institution_id):
|
||||
db.session.query(CorpusAccess).filter(CorpusAccess.institution == institution_id).delete()
|
||||
db.session.query(Institution).filter(Institution.id == institution_id).delete()
|
||||
db.session.commit()
|
||||
#db.session.query(Institution).filter(Institution.id == institution_id).update({'is_removed': True})
|
||||
#db.session.commit()
|
||||
|
||||
|
||||
def del_user_from_institution(user_id, institution_id):
|
||||
db.session.query(UserInstitutionMapping).filter(UserInstitutionMapping.institution == institution_id).filter(UserInstitutionMapping.user == user_id).delete()
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def get_all_active_users():
|
||||
return RegisteredUser.query.filter_by(active=True).order_by(RegisteredUser.id).all()
|
||||
|
||||
|
||||
def get_all_inactive_users():
|
||||
return RegisteredUser.query.filter_by(active=False).order_by(RegisteredUser.id).all()
|
||||
|
||||
|
||||
def get_all_users_join_institutions(active=True):
|
||||
#return RegisteredUser.query.filter_by(active=True).order_by(RegisteredUser.id).all()
|
||||
return db.session.query(RegisteredUser, UserInstitutionMapping).outerjoin(UserInstitutionMapping,
|
||||
RegisteredUser.id == UserInstitutionMapping.user).filter(RegisteredUser.active == active).order_by(RegisteredUser.id).all()
|
||||
|
||||
|
||||
|
||||
def get_all_active_institution_users(institution_id):
|
||||
return RegisteredUser.query.filter_by(active=True).join(UserInstitutionMapping,
|
||||
RegisteredUser.id == UserInstitutionMapping.user).filter(UserInstitutionMapping.institution == institution_id).all()
|
||||
|
||||
|
||||
def is_institution_coordinator(user_id, institution_id):
|
||||
user_inst_mapping = UserInstitutionMapping.query.filter_by(user=user_id).filter_by(institution=institution_id).first()
|
||||
if not user_inst_mapping:
|
||||
return False
|
||||
if user_inst_mapping.role != 'coordinator':
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def is_institution_member(user_id, institution_id):
|
||||
user_inst_mapping = UserInstitutionMapping.query.filter_by(user=user_id).filter_by(institution=institution_id).first()
|
||||
if not user_inst_mapping:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_actual_institution_contract_filename(f_hash):
|
||||
contract = InstitutionContract.query.filter_by(file_contract=f_hash).first()
|
||||
if contract:
|
||||
return contract.original_filename
|
||||
return None
|
||||
|
||||
|
||||
def get_actual_studentparent_contract_filename(f_hash):
|
||||
contract = ContractsSolar.query.filter_by(file_contract=f_hash).first()
|
||||
if contract:
|
||||
return contract.original_filename
|
||||
return None
|
||||
|
||||
|
||||
def get_password_reset_token(email, key, expires=600):
|
||||
return jwt.encode({'reset_password': email,
|
||||
'exp': int(time.time()) + expires},
|
||||
key=key, algorithm='HS256')
|
||||
|
||||
|
||||
def transfer_users_institution(institution_id_from, institution_id_to):
|
||||
rowcount = db.session.query(UserInstitutionMapping).filter_by(institution=institution_id_from).update(
|
||||
{'institution': institution_id_to})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def transfer_uploads_institution(institution_id_from, institution_id_to):
|
||||
rowcount = db.session.query(UploadSolar).filter_by(institution=institution_id_from).update(
|
||||
{'institution': institution_id_to})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def transfer_contracts_institution(institution_id_from, institution_id_to):
|
||||
rowcount = db.session.query(ContractsSolar).filter_by(institution=institution_id_from).update(
|
||||
{'institution': institution_id_to})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def verify_reset_token(token, key):
|
||||
try:
|
||||
message = jwt.decode(token,
|
||||
key=key, algorithms=["HS256"])
|
||||
email = message['reset_password']
|
||||
exp = message['exp']
|
||||
if int(time.time()) >= exp:
|
||||
# Token timed out
|
||||
return None
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
return
|
||||
return RegisteredUser.query.filter_by(email=email).first()
|
||||
|
||||
|
||||
def send_resetpass_mail(email, config):
|
||||
jwt_token = get_password_reset_token(email, config['APP_SECRET_KEY'])
|
||||
|
||||
body = '''
|
||||
Zahtevali ste ponastavitev gesla vašega uporabniškega računa.
|
||||
|
||||
Geslo lahko ponastavite na naslednji povezavi: https://zbiranje.slovenscina.eu/solar/resetpass/{}'''.format(jwt_token)
|
||||
|
||||
message = MIMEMultipart()
|
||||
message['From'] = config['MAIL_LOGIN']
|
||||
message['To'] = email
|
||||
message['Subject'] = 'Ponastavitev gesla'
|
||||
message.attach(MIMEText(body, "plain"))
|
||||
text = message.as_string()
|
||||
|
||||
# Create a secure SSL context
|
||||
context = ssl.create_default_context()
|
||||
|
||||
try:
|
||||
with SMTP_SSL(config['MAIL_HOST'], config['SMTP_PORT'], context=context) as server:
|
||||
server.login(config['MAIL_LOGIN'], config['MAIL_PASS'])
|
||||
server.sendmail(config['MAIL_LOGIN'], email, text)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def send_admins_new_user_notification_mail(user_id, config):
|
||||
user = RegisteredUser.query.filter_by(id=user_id).first()
|
||||
body = '''
|
||||
Nov uporabnik "{}" je ustvaril uporabniški račun na portalu za oddajanje besedil Šolar in čaka na odobritev.
|
||||
'''.format(user.name)
|
||||
|
||||
|
||||
admins = RegisteredUser.query.filter_by(role="admin").all()
|
||||
|
||||
# Create a secure SSL context
|
||||
context = ssl.create_default_context()
|
||||
|
||||
for admin in admins:
|
||||
message = MIMEMultipart()
|
||||
message['From'] = config['MAIL_LOGIN']
|
||||
message['To'] = admin.email
|
||||
message['Subject'] = 'Nova registracija'
|
||||
message.attach(MIMEText(body, "plain"))
|
||||
text = message.as_string()
|
||||
try:
|
||||
with SMTP_SSL(config['MAIL_HOST'], config['SMTP_PORT'], context=context) as server:
|
||||
server.login(config['MAIL_LOGIN'], config['MAIL_PASS'])
|
||||
server.sendmail(config['MAIL_LOGIN'], admin.email, text)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def send_user_activation_mail(user_id, config):
|
||||
user = RegisteredUser.query.filter_by(id=user_id).first()
|
||||
body = '''Vaš uporabniški račun "{}" na portalu Šolar je bil odobren.'''.format(user.name)
|
||||
|
||||
message = MIMEMultipart()
|
||||
message['From'] = config['MAIL_LOGIN']
|
||||
message['To'] = user.email
|
||||
message['Subject'] = 'Ponastavitev gesla'
|
||||
message.attach(MIMEText(body, "plain"))
|
||||
text = message.as_string()
|
||||
|
||||
# Create a secure SSL context
|
||||
context = ssl.create_default_context()
|
||||
|
||||
try:
|
||||
with SMTP_SSL(config['MAIL_HOST'], config['SMTP_PORT'], context=context) as server:
|
||||
server.login(config['MAIL_LOGIN'], config['MAIL_PASS'])
|
||||
server.sendmail(config['MAIL_LOGIN'], user.email, text)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
|
|
|
@ -1,298 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Portal za oddajanje besedil</title>
|
||||
<!--{{ dropzone.load_css() }}-->
|
||||
<link rel="stylesheet" href="/static/dropzone.css" type="text/css">
|
||||
{{ dropzone.style('position: absolute;
|
||||
top: -0.5px;
|
||||
width: 388px;
|
||||
height: 732px;
|
||||
left: 385px;
|
||||
background: linear-gradient(198.62deg, rgba(255, 255, 255, 0.49) -1.62%, rgba(255, 255, 255, 0.73) -1.61%, rgba(255, 255, 255, 0.41) 79.34%);
|
||||
box-shadow: 20px 4px 40px rgba(0, 0, 0, 0.25);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 0px;
|
||||
border-radius: 0px 20px 20px 0px;') }}
|
||||
<link rel="stylesheet" href="/static/style.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="main-window">
|
||||
<div id="rect1">
|
||||
<div id="logo-container">
|
||||
<img src="/static/image/logo.svg" alt="logo"/>
|
||||
</div>
|
||||
|
||||
<form id="my-dropzone" class="dropzone">
|
||||
<div style="position: relative; right: 390px;">
|
||||
<h1 id="title">Portal za oddajanje besedil</h1>
|
||||
<div class="form-text">{{description|safe}}</div>
|
||||
|
||||
<label for="ime">* Ime:</label>
|
||||
<input type="text" id="ime" name="ime" required="required"/>
|
||||
|
||||
<label for="podjetje">Podjetje / institucija:</label>
|
||||
<input type="text" id="podjetje" name="podjetje"/>
|
||||
|
||||
<label for="naslov">Naslov:</label>
|
||||
<input type="text" id="naslov" name="naslov"/>
|
||||
|
||||
<label for="posta">Pošta:</label>
|
||||
<input type="text" id="posta" name="posta"/>
|
||||
|
||||
<label for="email">* E-Pošta:</label>
|
||||
<input type="text" id="email" name="email" required="required"/>
|
||||
|
||||
<label for="telefon">Telefon:</label>
|
||||
<input type="text" id="telefon" name="telefon"/>
|
||||
|
||||
<div class="form-text">*Po kliku na gumb “Oddaj” se bo prikazala vsebina pogodbe o odstopu avtorskih pravic. Če se z vsebino strinjate, kliknite gumb “Pošlji”, da podatke posredujete v korpus, po e-pošti pa boste prejeli svoj izvod pogodbe.</div>
|
||||
|
||||
<button id="button-submit" type="submit">Oddaj</button>
|
||||
</div>
|
||||
|
||||
<div class="dropzone-previews"></div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="popup-terms" style="display: none">
|
||||
<div id="popup-terms-text">
|
||||
<h2>POGODBA O PRENOSU AVTORSKIH PRAVIC</h2>
|
||||
|
||||
<h3>UVODNE DOLOČBE</h3>
|
||||
<h4><b>1. člen</b></h4>
|
||||
|
||||
<p>1.1. Stranki uvodoma ugotavljata, da naročnik izvaja projekt Razvoj slovenščine v digitalnem
|
||||
okolju – RSDO (v nadaljevanju projekt RSDO), ki je bil na javnem razpisu Razvoj slovenščine v
|
||||
digitalnem okolju – jezikovni viri in tehnologije (JR-ESRR-Razvoj slovenščine v digitalnem
|
||||
okolju), objavljenem v Uradnem listu RS št. 70/19 dne 29. 11. 2019, sprejet v sofinanciranje
|
||||
in katerega vsebina je razvidna s spletnih strani https://slovenscina.eu.</p>
|
||||
|
||||
<p>1.2. Stranki uvodoma ugotavljata, da bo naročnik v okviru projekta RSDO:
|
||||
- izdelal osrednjo digitalno slovarsko bazo, ki združuje različne tipe jezikovnih podatkov o
|
||||
slovenščini v odprtem dostopu,
|
||||
- izdelal terminološki portal z integriranim iskalnikom po slovenskih terminoloških virih, zlasti
|
||||
terminoloških slovarjih,
|
||||
- izdelal korpus prevodov po različnih domenah za učenje strojnega prevajalnika za jezikovni
|
||||
par angleščina-slovenščina in slovenščina-angleščina.</p>
|
||||
<p>1.3. Stranki uvodoma ugotavljata, da bo naročnik pri projektu RSDO za vse zgoraj opisane
|
||||
namene zbiral in uporabil besedilne vire, ki so navedeni v prilogi k tej pogodbi in ki so lahko
|
||||
avtorska dela ali drugi predmeti varstva v skladu z Zakonom o avtorski in sorodnih pravicah
|
||||
(Uradni list RS, št. 16/07 – uradno prečiščeno besedilo, 68/08, 110/13, 56/15, 63/16 – ZKUASP
|
||||
in 59/19; ZASP) in na katerih ima imetnik pravic avtorske, avtorski sorodne ali druge pravice v
|
||||
skladu z ZASP (v nadaljevanju avtorska dela).</p>
|
||||
|
||||
<p>1.4. Stranki ugotavljata, da bodo avtorska dela in vse njihove morebitne spremembe in
|
||||
predelave, ter zbirke podatkov, ki bodo med izvajanjem projekta RSDO nastale, javno
|
||||
dostopni pod pogoji prostih licenc (npr. CC BY-SA) in bodo na voljo za nekomercialen in
|
||||
komercialen razvoj tehnologij, za raziskave in za druge raziskovalne namene
|
||||
posameznikom, raziskovalnim in izobraževalnim institucijam, neprofitnim organizacijam,
|
||||
državnim organom, organizacijam z javnimi pooblastili in gospodarskim družbam v Sloveniji
|
||||
in tujini.</p>
|
||||
|
||||
<h3>PREDMET POGODBE</h3>
|
||||
<h4><b>2. člen</b></h4>
|
||||
|
||||
<p>2.1. Predmet pogodbe so vsa avtorska dela imetnika pravic, ki so navedena v prilogi k tej
|
||||
pogodbi.</p>
|
||||
|
||||
<p>2.2. S podpisom te pogodbe imetnik avtorskih pravic na naročnika prenaša avtorske pravice
|
||||
na avtorskih delih na način in v obsegu, kakor je navedeno v 3. členu te pogodbe.</p>
|
||||
|
||||
<h3>PRENOS AVTORSKIH PRAVIC</h3>
|
||||
<h4><b>3. člen</b></h4>
|
||||
|
||||
<p>3.1. S podpisom te pogodbe imetnik pravic na avtorskih delih, ki so predmet te pogodbe, na
|
||||
naročnika neizključno, brez časovnih in teritorialnih omejitev prenaša vse materialne avtorske
|
||||
pravice, avtorski sorodne pravice in druge pravice avtorja v skladu z ZASP, zlasti pravico
|
||||
reproduciranja (23. člen ZASP), distribuiranja (24. člena ZASP), dajanja v najem (25. člen ZASP),
|
||||
priobčitve javnosti (26. do 32.a člen ZASP), vključno s pravico dajanja na voljo javnosti (32.a
|
||||
člen ZASP) in pravico predelave (33. člen ZASP).</p>
|
||||
|
||||
<p>3.2. S podpisom te pogodbe imetnik pravic izrecno soglaša, da naročnik pravice iz točke 3.1.
|
||||
prenaša naprej na tretje osebe brez omejitev.</p>
|
||||
|
||||
<h3>JAMČEVANJE IMETNIKA PRAVIC</h3>
|
||||
|
||||
<h4><b>4. člen</b></h4>
|
||||
|
||||
<p>4.1. S podpisom te pogodbe imetnik pravic jamči, da je na avtorskih delih, ki so predmet te
|
||||
pogodbe, imetnik vseh avtorskih pravic, avtorski sorodnih pravic in drugih pravic avtorja v
|
||||
skladu z ZASP, ki so potrebne za prenos pravic po tej pogodbi, in da na avtorskih delih ne
|
||||
obstajajo pravice tretjih oseb, ki bi naročniku preprečevale njihovo uporabo.</p>
|
||||
|
||||
<p>4.2. Določbe te pogodbe ne vplivajo na prenos moralnih avtorskih pravic, ki so v skladu z
|
||||
določbami ZASP neprenosljive.</p>
|
||||
|
||||
<h3>OSEBNI PODATKI</h3>
|
||||
<h4><b>5. člen</b></h4>
|
||||
|
||||
<p>6.1. Stranki se zavezujeta, da bosta vse morebitne osebne podatke, ki jih bosta obdelovali za
|
||||
namene izvajanja te pogodbe, obdelovali na način, da bosta upoštevali vse veljavne predpise
|
||||
o varstvu osebnih podatkov in da bosta posameznikom, na katere se osebni podatki nanašajo,
|
||||
zagotovili vse potrebne informacije v skladu s predpisi o varstvu osebnih podatkov.<p>
|
||||
|
||||
<h3>KONTAKTNE OSEBE</h3>
|
||||
<h4><b>6. člen</b></h4>
|
||||
|
||||
<p>7.1 Kontaktna oseba za izvedbo te pogodbe na strani naročnika je [xxx].</p>
|
||||
<p>7.2. Kontaktna oseba za izvedbo te pogodbe na strani imetnika pravic je [xxx].</p>
|
||||
|
||||
<h3>KONČNE DOLOČBE</h3>
|
||||
<h4><b>7. člen</b></h4>
|
||||
|
||||
<p>8.1. Če je katerakoli določba te pogodbe nična, ostanejo druga določila te pogodbe v veljavi.</p>
|
||||
|
||||
<h4><b>8. člen</b></h4>
|
||||
|
||||
<p>9.1. Za razmerja v zvezi s to pogodbo se uporabljajo pravni predpisi Republike Slovenije.</p>
|
||||
<p>9.2. Spore iz te pogodbe bosta stranki reševali po mirni poti. V primeru, da mirna rešitev ne
|
||||
bo mogoča, je za vse spore v zvezi s to pogodbo pristojno sodišče v Ljubljani.</p>
|
||||
|
||||
<h4><b>9. člen</b></h4>
|
||||
|
||||
<p>10.1. Ta pogodba nadomešča vsa predhodna pogajanja, ponudbe in druge dogovore med
|
||||
strankama.</p>
|
||||
<p>10.2. Ta pogodba je sestavljena v [dveh] istovetnih izvodih, od katerih prejme vsaka stranka
|
||||
po enega.</p>
|
||||
<p>10.3. Pogodbeni stranki s podpisom potrjujeta veljavnost te pogodbe.</p>
|
||||
</div>
|
||||
<button id="button-submit-cancel" class="button-terms" style="background: #ff2d2d;">Prekliči</button>
|
||||
<button id="button-submit-final" class="button-terms">Pošlji</button>
|
||||
</div>
|
||||
|
||||
<!--{{ dropzone.load_js() }}-->
|
||||
<script src="/static/dropzone.js"></script>
|
||||
<script>
|
||||
/////////////////////////
|
||||
// Dropzone //
|
||||
/////////////////////////
|
||||
var btnSubmit = document.getElementById("button-submit");
|
||||
var btnSubmitFinal = document.getElementById("button-submit-final");
|
||||
var btnSubmitCancel = document.getElementById("button-submit-cancel");
|
||||
var elemTermsPopup = document.getElementById("popup-terms");
|
||||
var termsScrollbox = document.getElementById("popup-terms-text");
|
||||
var scrollboxTriggered = false;
|
||||
var form = document.forms["my-dropzone"];
|
||||
|
||||
function isEmptyOrSpaces(str){
|
||||
return str == null || str.match(/^ *$/) !== null;
|
||||
}
|
||||
|
||||
const reEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
Dropzone.options.myDropzone = { // The camelized version of the ID of the form element
|
||||
url: "/{{corpus_name}}/upload",
|
||||
autoProcessQueue: false,
|
||||
uploadMultiple: true,
|
||||
parallelUploads: {{max_files}},
|
||||
paramName: "file", // The name that will be used to transfer the file
|
||||
maxFilesize: 1000, // MB
|
||||
timeout: 5000000, // milliseconds
|
||||
acceptedFiles: ".txt, .csv, .pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx",
|
||||
maxFiles: {{max_files}},
|
||||
dictDefaultMessage: `Kliknite ali odložite datoteke sem.`,
|
||||
dictFallbackMessage: "Vaš brskalnik ne podpira izbiranje datotek z odlaganjem (\"drag & drop\").",
|
||||
dictInvalidFileType: "Datoteka je napačnega formata.",
|
||||
dictFileTooBig: "Datoteke je prevelika {{filesize}}. Največja dovoljena velikost: {{maxFilesize}}MiB.",
|
||||
dictResponseError: "Napaka strežnika: {{statusCode}}",
|
||||
dictMaxFilesExceeded: "Največje število datotek že doseženo.",
|
||||
dictCancelUpload: "Prekini prenos",
|
||||
dictRemoveFile: "Odstrani datoteko",
|
||||
dictCancelUploadConfirmation: "Ali res želite odstraniti to datoteko?",
|
||||
dictUploadCanceled: "Prenos prekinjen",
|
||||
|
||||
// The setting up of the dropzone
|
||||
init: function() {
|
||||
var dz = this;
|
||||
|
||||
|
||||
btnSubmit.addEventListener("click", function(e) {
|
||||
// Make sure that the form isn't actually being sent.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// Check form validity.
|
||||
var ime = form["ime"].value;
|
||||
var email = form["email"].value;
|
||||
var podjetje = form["podjetje"].value;
|
||||
var telefon = form["telefon"].value;
|
||||
if (isEmptyOrSpaces(ime) || isEmptyOrSpaces(email)) {
|
||||
alert("Izpolnite vsa obvezna polja!");
|
||||
} else if (!reEmail.test(email.toLowerCase())) {
|
||||
alert("Email napačnega formata!");
|
||||
} else if (ime.length > 100 || email.length > 100 || podjetje.length > 100 || telefon.length > 100) {
|
||||
alert("Velikost polj je omejena na 100 znakov.");
|
||||
} else {
|
||||
// Then make terms popup visible
|
||||
btnSubmit.disabled = true;
|
||||
btnSubmitFinal.disabled = true;
|
||||
elemTermsPopup.style.display = "inline";
|
||||
scrollboxTriggered = false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// First change the button to actually tell Dropzone to process the queue.
|
||||
btnSubmitFinal.addEventListener("click", function(e) {
|
||||
// Hand off data to dropzone
|
||||
dz.processQueue();
|
||||
|
||||
// Clear fields and hide popup agian
|
||||
btnSubmit.disabled = false;
|
||||
elemTermsPopup.style.display = "none";
|
||||
form.reset();
|
||||
scrollboxTriggered = false;
|
||||
});
|
||||
|
||||
btnSubmitCancel.addEventListener("click", function(e) {
|
||||
btnSubmit.disabled = false;
|
||||
scrollboxTriggered = false;
|
||||
elemTermsPopup.style.display = "none";
|
||||
});
|
||||
|
||||
// Enable final submit button only if user scrolls to the end of the terms.
|
||||
function checkScrollboxTrigger(event) {
|
||||
var element = event.target;
|
||||
if (!scrollboxTriggered
|
||||
&& element.scrollHeight - element.scrollTop <= element.clientHeight + 50
|
||||
) {
|
||||
scrollboxTriggered = true;
|
||||
btnSubmitFinal.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
termsScrollbox.addEventListener('scroll', function(event) {
|
||||
checkScrollboxTrigger(event);
|
||||
});
|
||||
|
||||
termsScrollbox.addEventListener("mouseenter", function(event) {
|
||||
checkScrollboxTrigger(event);
|
||||
});
|
||||
|
||||
// Listen to the sendingmultiple event. In this case, it's the sendingmultiple event instead
|
||||
// of the sending event because uploadMultiple is set to true.
|
||||
this.on("sendingmultiple", function() {
|
||||
// Gets triggered when the form is actually being sent.
|
||||
// Hide the success button or the complete form.
|
||||
});
|
||||
|
||||
this.on("successmultiple", function(files, response) {
|
||||
// Gets triggered when the files have successfully been sent.
|
||||
// Redirect user or notify of success.
|
||||
alert("Odgovor strežnika: " + response);
|
||||
location.reload();
|
||||
});
|
||||
|
||||
this.on("errormultiple", function(files, response) {
|
||||
// Gets triggered when there was an error sending the files.
|
||||
// Maybe show form again, and notify user of error
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,11 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<a href="/prevodi">Korpus paralelnih besdil ANG-SLO</a><br>
|
||||
<a href="/gigafida">Korpus Gigafida</a><br>
|
||||
<a href="/solar">Korpus Šolar</a><br>
|
||||
</body>
|
||||
</html>
|
|
@ -30,7 +30,7 @@
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/solar/oddaja">Nazaj na oddajo</a>
|
||||
<a href="../oddaja">Nazaj na oddajo</a>
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div style="background: blue;">
|
||||
|
@ -65,7 +65,7 @@
|
|||
</table>
|
||||
</div>
|
||||
<h3>Dodaj uporabnika</h3>
|
||||
<form action="/solar/adduser" method="post">
|
||||
<form action="../adduser" method="post">
|
||||
<label for="name">Ime in priimek:</label><br>
|
||||
<input type="text" id="name" name="name"><br>
|
||||
<label for="email">Email:</label><br>
|
||||
|
@ -75,7 +75,7 @@
|
|||
<input type="submit" value="Dodaj">
|
||||
</form>
|
||||
<h3>Spremeni email uporabnika</h3>
|
||||
<form action="/solar/changeuseremail" method="post">
|
||||
<form action="../changeuseremail" method="post">
|
||||
<label for="user-id">ID uporabnika:</label><br>
|
||||
<input type="text" id="user-id" name="user-id"><br>
|
||||
<label for="email">Nov email:</label><br>
|
||||
|
@ -83,7 +83,7 @@
|
|||
<input type="submit" value="Spremeni">
|
||||
</form>
|
||||
<h3>Spremeni ime in priimek uporabnika</h3>
|
||||
<form action="/solar/changeusername" method="post">
|
||||
<form action="../changeusername" method="post">
|
||||
<label for="user-id">ID uporabnika:</label><br>
|
||||
<input type="text" id="user-id" name="user-id"><br>
|
||||
<label for="name">Ime in priimek:</label><br>
|
||||
|
@ -91,13 +91,13 @@
|
|||
<input type="submit" value="Spremeni">
|
||||
</form>
|
||||
<h3>Odstrani uporabnika</h3>
|
||||
<form action="/solar/deluser" method="post">
|
||||
<form action="../deluser" method="post">
|
||||
<label for="user_id">ID uporabnika:</label><br>
|
||||
<input type="text" id="user_id" name="user_id"><br>
|
||||
<input type="submit" value="Odstrani">
|
||||
</form>
|
||||
<h3>Dodeli uporabnika instituciji</h3>
|
||||
<form action="/solar/addusertoinstitution" method="post">
|
||||
<form action="../addusertoinstitution" method="post">
|
||||
<label for="user_id">ID uporabnika:</label>
|
||||
<input type="text" id="user_id" name="user_id"><br>
|
||||
<label for="institution_id">ID institucije:</label>
|
||||
|
@ -111,13 +111,13 @@
|
|||
<input type="submit" value="Dodeli">
|
||||
</form>
|
||||
<h3>Odstrani uporabnika iz institucije</h3>
|
||||
<form action="/solar/deluserfrominstitution" method="post">
|
||||
<form action="../deluserfrominstitution" method="post">
|
||||
<label for="user_id">ID uporabnika:</label>
|
||||
<input type="text" id="user_id" name="user_id"><br>
|
||||
<input type="submit" value="Odstrani">
|
||||
</form>
|
||||
<h3>Spremeni vlogo uporabniškega računa</h3>
|
||||
<form action="/solar/changeuserrole" method="post">
|
||||
<form action="../changeuserrole" method="post">
|
||||
<label for="user-id">ID uporabnika:</label>
|
||||
<input type="text" id="user-id" name="user-id"><br>
|
||||
<label for="role">Vloga:</label>
|
||||
|
@ -152,7 +152,7 @@
|
|||
</table>
|
||||
</div>
|
||||
<h3>Aktiviraj uporabnika</h3>
|
||||
<form action="/solar/activateuser" method="post">
|
||||
<form action="../activateuser" method="post">
|
||||
<label for="id">ID uporabnika:</label>
|
||||
<input type="text" id="id" name="id"><br>
|
||||
<input type="submit" value="Aktiviraj">
|
||||
|
@ -160,7 +160,7 @@
|
|||
<div> </div>
|
||||
<h2>Institucije</h2>
|
||||
<h3>Dodaj institucijo</h3>
|
||||
<form action="/solar/addinstitution" method="post">
|
||||
<form action="../addinstitution" method="post">
|
||||
<label for="name">Naziv:</label>
|
||||
<input type="text" id="name" name="name"><br>
|
||||
<label for="region">Regija:</label>
|
||||
|
@ -200,7 +200,7 @@
|
|||
</table>
|
||||
</div>
|
||||
<h3>Združi instituciji</h3>
|
||||
<form action="/solar/mergeinstitutions" method="post">
|
||||
<form action="../mergeinstitutions" method="post">
|
||||
<label for="id-from">Institucijo z ID</label>
|
||||
<input type="text" id="id-from" name="id-from">
|
||||
<label for="id-to">združi v institucijo z ID</label>
|
||||
|
@ -208,7 +208,7 @@
|
|||
<input type="submit" value="Združi">
|
||||
</form>
|
||||
<h3>Spremeni podatke institucije</h3>
|
||||
<form action="/solar/changeinstitutiondata" method="post">
|
||||
<form action="../changeinstitutiondata" method="post">
|
||||
<label for="id">ID institucije</label>
|
||||
<input type="text" id="id" name="id"><br>
|
||||
<label for="name">Nov naziv:</label>
|
||||
|
@ -256,7 +256,7 @@
|
|||
</table>
|
||||
</div>
|
||||
<h3>Dodaj vnos</h3>
|
||||
<form action="/solar/addcooperationhistoryitem" method="post">
|
||||
<form action="../addcooperationhistoryitem" method="post">
|
||||
<label for="user">ID uporabnika</label>
|
||||
<input type="text" id="user" name="user"><br>
|
||||
<label for="institution">ID institucije</label>
|
||||
|
@ -274,7 +274,7 @@
|
|||
<input type="submit" value="Dodaj">
|
||||
</form>
|
||||
<h3>Odstrani vnos</h3>
|
||||
<form action="/solar/delcooperationhistoryitem" method="post">
|
||||
<form action="../delcooperationhistoryitem" method="post">
|
||||
<label for="entry-id">ID vnosa</label>
|
||||
<input type="text" id="entry-id" name="entry-id"><br>
|
||||
<input type="submit" value="Odstrani">
|
||||
|
@ -320,7 +320,7 @@
|
|||
</table>
|
||||
</div>
|
||||
<h3>Posodobi podatke nalaganja</h3>
|
||||
<form action="/solar/updateuploaditem" method="post">
|
||||
<form action="../updateuploaditem" method="post">
|
||||
<label for="item-id">ID nalaganja</label>
|
||||
<input type="text" id="item-id" name="item-id"/><br>
|
||||
<label for="program">Program</label>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<div id="logo-container">
|
||||
<img src="/static/image/logo.svg" alt="logo"/>
|
||||
</div>
|
||||
<a href="/solar/login" class="contract-item-button" style="float: none;">Nazaj na prijavo</a>
|
||||
<a href="../login" class="contract-item-button" style="float: none;">Nazaj na prijavo</a>
|
||||
<h3 id="title" style="font-size: 27px; text-align: left;">Pozabljeno geslo - ŠOLAR</h3>
|
||||
<div>
|
||||
{% with messages = get_flashed_messages() %}
|
||||
|
@ -22,7 +22,7 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<form method="POST" action="/solar/sendresetpass">
|
||||
<form method="POST" action="../sendresetpass">
|
||||
<div>
|
||||
<div>
|
||||
<input type="email" name="email" placeholder="Email" autofocus="">
|
||||
|
|
|
@ -27,10 +27,10 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<form method="POST" action="/solar/login">
|
||||
<form method="POST" action="../login">
|
||||
<div>
|
||||
<div>
|
||||
<img src="/static/image/user.png" alt="user" style="float: left; width: 10%;"/>
|
||||
<img src="../static/image/user.png" alt="user" style="float: left; width: 10%;"/>
|
||||
<input type="email" name="email" placeholder="Email" autofocus=""
|
||||
style="float: right; width: 85%; margin-bottom: 20px; margin-top: 10px;">
|
||||
</div>
|
||||
|
@ -45,8 +45,8 @@
|
|||
</div>
|
||||
<button class="button-general" style="margin-top: 30px; margin-bottom: 20px;">PRIJAVA</button>
|
||||
</form>
|
||||
<a href="/solar/forgotpass" class="contract-item-button">Pozabljeno geslo</a>
|
||||
<a href="/solar/register" class="contract-item-button"
|
||||
<a href="../forgotpass" class="contract-item-button">Pozabljeno geslo</a>
|
||||
<a href="../register" class="contract-item-button"
|
||||
style="-webkit-appearance: button;
|
||||
-moz-appearance: button;
|
||||
appearance: button;
|
||||
|
@ -54,7 +54,7 @@
|
|||
text-decoration: none;
|
||||
color: #46535b;
|
||||
width: 100%;"
|
||||
><img src="/static/image/register.png" alt="user" style="float: left; width: 15%; margin-right: 25px;"/>
|
||||
><img src="../static/image/register.png" alt="user" style="float: left; width: 15%; margin-right: 25px;"/>
|
||||
Registracija<br><div style="font-size: 11px; margin-top: 10px;">Še nimate uporabniškega računa? Registrirajte se!</div></a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/solar/oddaja">Nazaj na oddajo</a>
|
||||
<a href="../oddaja">Nazaj na oddajo</a>
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div style="background: blue;">
|
||||
|
@ -81,7 +81,7 @@
|
|||
</div>
|
||||
<br>
|
||||
<h3>Dodaj uporabnika instituciji</h3>
|
||||
<form action="/solar/addusertoinstitution" method="post">
|
||||
<form action="../addusertoinstitution" method="post">
|
||||
<label for="user_id">ID uporabnika:</label>
|
||||
<input type="text" id="user_id" name="user_id"><br>
|
||||
<label for="role">Vloga v instituciji:</label>
|
||||
|
@ -93,7 +93,7 @@
|
|||
<input type="submit" value="Dodeli">
|
||||
</form>
|
||||
<h3>Odstrani uporabnika iz institucije</h3>
|
||||
<form action="/solar/deluserfrominstitution" method="post">
|
||||
<form action="../deluserfrominstitution" method="post">
|
||||
<label for="user_id">ID uporabnika:</label>
|
||||
<input type="text" id="user_id" name="user_id"><br>
|
||||
<input type="submit" value="Odstrani">
|
||||
|
|
|
@ -18,12 +18,12 @@
|
|||
<link rel="stylesheet" href="/static/style.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<a href="/solar/logout">Odjavi se</a>
|
||||
<a href="../logout">Odjavi se</a>
|
||||
{% if is_institution_coordinator %}
|
||||
<br><a href="/solar/manage-institution">Upravljaj z institucijo</a>
|
||||
<br><a href="../manage-institution">Upravljaj z institucijo</a>
|
||||
{% endif %}
|
||||
{% if is_admin %}
|
||||
<br><a href="/solar/admin">Administracijski meni</a>
|
||||
<br><a href="../admin">Administracijski meni</a>
|
||||
{% endif %}
|
||||
<br><a href="mailto:email@example.com">Pomoč</a>
|
||||
<div class="bg"></div>
|
||||
|
@ -161,7 +161,7 @@
|
|||
const reEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
Dropzone.options.myDropzone = { // The camelized version of the ID of the form element
|
||||
url: "/solar/upload",
|
||||
url: "../upload",
|
||||
autoProcessQueue: false,
|
||||
uploadMultiple: true,
|
||||
parallelUploads: 20,
|
||||
|
@ -266,13 +266,13 @@
|
|||
btnZgodovina.addEventListener("click", function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
window.location.replace("/solar/zgodovina");
|
||||
window.location.replace("../zgodovina");
|
||||
});
|
||||
|
||||
btnPogodbe.addEventListener("click", function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
window.location.replace("/solar/pogodbe");
|
||||
window.location.replace("../pogodbe");
|
||||
});
|
||||
|
||||
// Enable final submit button only if user scrolls to the end of the terms.
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
<link rel="stylesheet" href="/static/style.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<a href="/solar/logout">Odjavi se</a>
|
||||
<a href="../logout">Odjavi se</a>
|
||||
{% if is_institution_coordinator %}
|
||||
<br><a href="/solar/manage-institution">Upravljaj z institucijo</a>
|
||||
<br><a href="../manage-institution">Upravljaj z institucijo</a>
|
||||
{% endif %}
|
||||
{% if is_admin %}
|
||||
<br><a href="/solar/admin">Administracijski meni</a>
|
||||
<br><a href="../admin">Administracijski meni</a>
|
||||
{% endif %}
|
||||
<br><a href="mailto:email@example.com">Pomoč</a>
|
||||
<div class="bg"></div>
|
||||
|
@ -23,9 +23,9 @@
|
|||
</div>
|
||||
<h1 id="title" style="font-size: 25px; position: relative;">Korpus ŠOLAR</h1>
|
||||
<div class="selection-tabs">
|
||||
<button onclick="window.location.replace('/solar/oddaja');" class="selection-tab-button">ODDAJA</button>
|
||||
<button onclick="window.location.replace('/solar/zgodovina');" class="selection-tab-button">ZGODOVINA</button>
|
||||
<button onclick="window.location.replace('/solar/pogodbe');" class="selection-tab-button selected">POGODBE</button>
|
||||
<button onclick="window.location.replace('../oddaja');" class="selection-tab-button">ODDAJA</button>
|
||||
<button onclick="window.location.replace('../zgodovina');" class="selection-tab-button">ZGODOVINA</button>
|
||||
<button onclick="window.location.replace('../pogodbe');" class="selection-tab-button selected">POGODBE</button>
|
||||
</div>
|
||||
</div>
|
||||
{% with messages = get_flashed_messages() %}
|
||||
|
@ -45,7 +45,7 @@
|
|||
<div class="contract-item-title">Pogodba s šolo</div>
|
||||
<div class="contract-item-data">{{contract_school.original_filename}}</div>
|
||||
<div class="contract-item-data">DODANO: {{contract_school.timestamp}}</div>
|
||||
<a href="/solar/pogodbe-institucije/{{ contract_school.file_contract }}.pdf" class="contract-item-button">Prenesi</a>
|
||||
<a href="../pogodbe-institucije/{{ contract_school.file_contract }}.pdf" class="contract-item-button">Prenesi</a>
|
||||
</div>
|
||||
</br>
|
||||
{% endif %}
|
||||
|
@ -55,7 +55,7 @@
|
|||
<div class="contract-item-title" style="font-size: 12px;">{{item.original_filename}}</div>
|
||||
<div class="contract-item-data">Pogodba o prenosu lastništva</div>
|
||||
<div class="contract-item-data">DODANO: {{item.timestamp}}</div>
|
||||
<a href="/solar/pogodbe-ucencistarsi/{{ item.file_contract }}.pdf" class="contract-item-button">Prenesi</a>
|
||||
<a href="../pogodbe-ucencistarsi/{{ item.file_contract }}.pdf" class="contract-item-button">Prenesi</a>
|
||||
</div>
|
||||
</br>
|
||||
{% endfor %}
|
||||
|
@ -65,7 +65,7 @@
|
|||
{% if show_upload_form %}
|
||||
<div style="padding: 20px; position: absolute; top: 500px; width: 89%;">
|
||||
<div class="section-desc">Oddaj pogodbo</div>
|
||||
<form action="/solar/pogodbe" method="post" enctype="multipart/form-data">
|
||||
<form action="../pogodbe" method="post" enctype="multipart/form-data">
|
||||
{% if enable_upload_school_contract %}
|
||||
<div style="display:flex; flex-direction: row; justify-content: left; align-items: center">
|
||||
<label style="width: 80%; text-align: right;" for="sola">Pogodba s šolo</label>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
<div class="form-text"><em>Zbiranje besedil za korpus Šolar poteka po naslednjem postopku, ki prinaša tudi točke za napredovanje.</em></div>
|
||||
|
||||
<a href="/solar/login" class="contract-item-button" style="float: none;">Nazaj na prijavo</a>
|
||||
<a href="../login" class="contract-item-button" style="float: none;">Nazaj na prijavo</a>
|
||||
<h3 id="title" style="font-size: 27px; text-align: left;">Registracija</h3>
|
||||
<div>
|
||||
{% with messages = get_flashed_messages() %}
|
||||
|
@ -27,7 +27,7 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<form method="POST" action="/solar/register">
|
||||
<form method="POST" action="../register">
|
||||
<div>
|
||||
<div>
|
||||
<input type="name" name="name" placeholder="Ime in priimek" autofocus="">
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
<script src="/static/chart.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/solar/logout">Odjavi se</a>
|
||||
<a href="../logout">Odjavi se</a>
|
||||
{% if is_institution_coordinator %}
|
||||
<br><a href="/solar/manage-institution">Upravljaj z institucijo</a>
|
||||
<br><a href="../manage-institution">Upravljaj z institucijo</a>
|
||||
{% endif %}
|
||||
{% if is_admin %}
|
||||
<br><a href="/solar/admin">Administracijski meni</a>
|
||||
<br><a href="../admin">Administracijski meni</a>
|
||||
{% endif %}
|
||||
<br><a href="mailto:email@example.com">Pomoč</a>
|
||||
<div class="bg"></div>
|
||||
|
@ -24,9 +24,9 @@
|
|||
</div>
|
||||
<h1 id="title" style="font-size: 25px; position: relative;">Korpus ŠOLAR</h1>
|
||||
<div class="selection-tabs">
|
||||
<button onclick="window.location.replace('/solar/oddaja');" class="selection-tab-button">ODDAJA</button>
|
||||
<button onclick="window.location.replace('/solar/zgodovina');" class="selection-tab-button selected">ZGODOVINA</button>
|
||||
<button onclick="window.location.replace('/solar/pogodbe');" class="selection-tab-button">POGODBE</button>
|
||||
<button onclick="window.location.replace('../oddaja');" class="selection-tab-button">ODDAJA</button>
|
||||
<button onclick="window.location.replace('../zgodovina');" class="selection-tab-button selected">ZGODOVINA</button>
|
||||
<button onclick="window.location.replace('../pogodbe');" class="selection-tab-button">POGODBE</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="history-container" style="padding: 20px;">
|
||||
|
@ -113,7 +113,7 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
fetch('/solar/topuploads').then(r => r.json()).then(j => drawChart(j));
|
||||
fetch('../topuploads').then(r => r.json()).then(j => drawChart(j));
|
||||
|
||||
</script>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue
Block a user