From dc21d65dd7814d7c2c2d45ec879798b155021f16 Mon Sep 17 00:00:00 2001 From: Damien De Paoli Date: Sun, 9 Jan 2022 12:20:29 +1100 Subject: [PATCH] Put new functionality in to allow choosing existing folder in move code - it goes back and forwards 7 days from the date of this file and finds matching files and uses those dirs, or just dirname matches for those dates and offers them up. Also improved Move code to reject dodgy paths --- TODO | 5 ----- files.py | 42 +++++++++++++++++++++++++++++++++++- internal/js/files_support.js | 20 ++++++++++++++++- pa_job_manager.py | 9 ++++++++ 4 files changed, 69 insertions(+), 7 deletions(-) diff --git a/TODO b/TODO index a4f1e9b..4cbef2b 100644 --- a/TODO +++ b/TODO @@ -4,8 +4,6 @@ * remember last import dir, so you can just go straight back to it - * offer similar existing folders (based on date range +/- say 2 days) - * in Fullscreen mode and next/prev dropped out of FS when calling /viewlist route -- only way to fix this, is for when we POST to viewlist, it returns json, and we never leave /view/X -- then we can stay on-page, and stay in FS and then just call ViewImageOrVide() @@ -18,9 +16,6 @@ * why .xcf is seen as a video??? - actually only 1 is... I think if type == 'Unknown' then do file display and use ? as the image again - * maybe strip unnecessary / at end of directory name. i think i have left behind empty folders, e.g. check switzerland and austria - - also should allow move to existing folder soon... - * add an option on the person menu to run_ai_on all photos (or at least import/storage) * when hitting back button to a search, it doesnt handle the post, etc. diff --git a/files.py b/files.py index eb702e9..4ffe8f0 100644 --- a/files.py +++ b/files.py @@ -1,6 +1,6 @@ from wtforms import SubmitField, StringField, HiddenField, validators, Form from flask_wtf import FlaskForm -from flask import request, render_template, redirect, send_from_directory, url_for +from flask import request, render_template, redirect, send_from_directory, url_for, jsonify from path import MovePathDetails from main import db, app, ma from sqlalchemy import Sequence @@ -18,6 +18,7 @@ import cv2 import time import re import json +import datetime from flask_login import login_required, current_user from options import Options @@ -615,3 +616,42 @@ def _jinja2_filter_toplevelfolderof(path, cwd): @app.template_filter('ParentPath') def _jinja2_filter_parentpath(path): return os.path.dirname(path) + +############################################################################### +# route to allow the Move Dialog Box to pass a date (YYYYMMDD) and returns a +# json? list of existing dir names that could be near it in time. Starting +# simple, by using YYYYMM-1, YYYYMM, YYYYMM+1 dirs +############################################################################### +@app.route("/getexistingpaths/
", methods=["POST"]) +@login_required +def GetExistingPathsAsDiv(dt): + dir_ft=FileType.query.filter(FileType.name=='Directory').first() + dirs_arr=[] + for delta in range(-7, 8): + new_dtime=datetime.datetime.strptime(dt, "%Y%m%d") + datetime.timedelta(days=delta) + new_dt=new_dtime.strftime('%Y%m%d') + dirs_arr+=Dir.query.distinct(Dir.rel_path).filter(Dir.rel_path.ilike('%'+new_dt+'%')).all(); + dirs_arr+=Dir.query.distinct(Dir.rel_path).join(EntryDirLink).join(Entry).filter(Entry.type_id!=dir_ft.id).filter(Entry.name.ilike('%'+new_dt+'%')).all() + + # remove duplicates from array + dirs = set(dirs_arr) + + # turn DB output into json and return it to the f/e + ret='[ ' + first_dir=1 + for dir in dirs: + print( f"process dir matching for {new_dt} => {dir}") + if not first_dir: + ret +=", " + bits=dir.rel_path.split('-') + + ret+= '{ ' + ret+= '"prefix":"' + bits[0] + '", ' + if len(bits)>1: + ret+= '"suffix":"' + bits[1] + '"' + else: + ret+= '"suffix":"''"' + ret+= ' } ' + first_dir=0 + ret+= ' ]' + return ret diff --git a/internal/js/files_support.js b/internal/js/files_support.js index 3d0f581..c311678 100644 --- a/internal/js/files_support.js +++ b/internal/js/files_support.js @@ -37,6 +37,22 @@ function change_rp_sel() $('#move_path_type').val( $('option:selected', '#rp_sel').attr('path_type') ) } +function GetExistingDirsAsDiv( dt, divname ) +{ + $.ajax({ + type: 'POST', data: null, url: '/getexistingpaths/'+dt, + success: function(data) { + $('#'+divname).html(data) + dirs = JSON.parse(data) + s='' + dirs.forEach( function(item, index) { + s+= '' + } ) + $('#'+divname).html(s) + } + } ) +} + // show the DBox for a move file, includes all thumbnails of selected files to move // and a pre-populated folder to move them into, with text field to add a suffix function MoveDBox(path_details, db_url) @@ -53,6 +69,8 @@ function MoveDBox(path_details, db_url) div+=GetSelnAsDiv() yr=$('.highlight').first().attr('yr') dt=$('.highlight').first().attr('date') + div+='
Use Existing:
' + GetExistingDirsAsDiv( dt, "existing" ) div+=`
@@ -71,7 +89,7 @@ function MoveDBox(path_details, db_url) div+="value="+yr+'/'+dt+"-" div+=` "> - +
diff --git a/pa_job_manager.py b/pa_job_manager.py index f99890e..25addf0 100644 --- a/pa_job_manager.py +++ b/pa_job_manager.py @@ -1650,6 +1650,15 @@ def JobMoveFiles(job): JobProgressState( job, "In Progress" ) prefix=[jex.value for jex in job.extra if jex.name == "prefix"][0] suffix=[jex.value for jex in job.extra if jex.name == "suffix"][0] + # Sanity check, if prefix starts with /, reject it -> no /etc/shadow potentials + # Sanity check, if .. in prefix or suffix, reject it -> no ../../etc/shadow potentials + # Sanity check, if // in prefix or suffix, reject it -> not sure code wouldnt try to make empty dirs, and I dont want to chase /////// cases, any 2 in a row is enough to reject + if '..' in prefix or '..' in suffix or prefix[0] == '/' or '//' in prefix or '//' in suffix: + FinishJob( job, f"ERROR: Not processing move as the paths contain illegal chars", "Failed" ) + return + # also remove unecessary slashes, jic + prefix=prefix.rstrip('/') + suffix=suffix.lstrip('/').rstrip('/') path_type=[jex.value for jex in job.extra if jex.name == "move_path_type"][0] rel_path=[jex.value for jex in job.extra if jex.name == "rel_path"][0] dst_storage_path = session.query(Path).filter(Path.path_prefix=='static/' + path_type + '/'+ rel_path).first()