mirror of
				https://github.com/simon987/Simple-Incremental-Search-Tool.git
				synced 2025-10-30 23:16:52 +00:00 
			
		
		
		
	
						commit
						7ccc1f14d1
					
				
							
								
								
									
										31
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								README.md
									
									
									
									
									
								
							| @ -1,6 +1,7 @@ | |||||||
| # Simple incremental search tool | # Simple incremental search tool | ||||||
| 
 | 
 | ||||||
| Work in progress! Shouldn't be used in production environnments. | 
 | ||||||
|  | Portable search tool for local files using Elasticsearch. | ||||||
| 
 | 
 | ||||||
| ### Features | ### Features | ||||||
| * Incremental search (Search as you type) | * Incremental search (Search as you type) | ||||||
| @ -19,37 +20,21 @@ Work in progress! Shouldn't be used in production environnments. | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # Installation | # Installation | ||||||
| Java and python3 are required. To parse video and audio files, `ffmpeg` needs to be installed     | Java and python3 are required.     | ||||||
| Once the web server is running, you can connect to the search interface by typing `localhost:8080` in your browser. | Once the web server is running, you can connect to the search interface by typing `localhost:8080` in your browser. | ||||||
| 
 | 
 | ||||||
| ## Setup on Windows | ## Setup on Windows/Mac/linux (Python 3.5+) | ||||||
|  | 
 | ||||||
|  | * Download and install [Elasticsearch](https://www.elastic.co/downloads/elasticsearch) | ||||||
|  | 
 | ||||||
| ```bash | ```bash | ||||||
| git clone https://github.com/simon987/Simple-Incremental-Search-Tool | git clone https://github.com/simon987/Simple-Incremental-Search-Tool | ||||||
| cd Simple-Incremental-Search-Tool |  | ||||||
| ``` |  | ||||||
| [Download latest elasticsearch version](https://www.elastic.co/downloads/elasticsearch) and extract to `Simple-Incremental-Search-Tool\elasticsearch` |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| sudo pip3 install -r requirements.txt |  | ||||||
| 
 |  | ||||||
| python3 run.py |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ## Setup on Mac/linux |  | ||||||
| ```bash |  | ||||||
| git clone https://github.com/simon987/Simple-Incremental-Search-Tool |  | ||||||
| cd Simple-Incremental-Search-Tool |  | ||||||
| wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.2.4.zip |  | ||||||
| unzip elasticsearch-6.2.4.zip |  | ||||||
| rm elasticsearch-6.2.4.zip |  | ||||||
| mv elasticsearch-6.2.4 elasticsearch     |  | ||||||
| 
 | 
 | ||||||
| sudo pip3 install -r requirements.txt     | sudo pip3 install -r requirements.txt     | ||||||
| 
 |  | ||||||
| python3 run.py | python3 run.py | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Running unit tests | ## Running unit tests | ||||||
| ```bash | ```bash | ||||||
| python3 -m unittest | python3 -m unittest | ||||||
| ``` | ``` | ||||||
| @ -25,6 +25,9 @@ bcrypt_rounds = 14 | |||||||
| # sqlite3 database path | # sqlite3 database path | ||||||
| db_path = "./local_storage.db" | db_path = "./local_storage.db" | ||||||
| 
 | 
 | ||||||
|  | # Set to true to allow guests to search any directory | ||||||
|  | allow_guests = False | ||||||
|  | 
 | ||||||
| try: | try: | ||||||
|     import cairosvg |     import cairosvg | ||||||
|     cairosvg = True |     cairosvg = True | ||||||
|  | |||||||
| @ -12,7 +12,6 @@ from thumbnail import ThumbnailGenerator | |||||||
| from storage import Directory | from storage import Directory | ||||||
| import shutil | import shutil | ||||||
| import config | import config | ||||||
| from ctypes import c_char_p |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class RunningTask: | class RunningTask: | ||||||
| @ -145,9 +144,9 @@ class TaskManager: | |||||||
|                      TextFileParser(chksum_calcs, int(directory.get_option("TextFileContentLength")), directory.path), |                      TextFileParser(chksum_calcs, int(directory.get_option("TextFileContentLength")), directory.path), | ||||||
|                      PictureFileParser(chksum_calcs, directory.path), |                      PictureFileParser(chksum_calcs, directory.path), | ||||||
|                      FontParser(chksum_calcs, directory.path), |                      FontParser(chksum_calcs, directory.path), | ||||||
|                      PdfFileParser(chksum_calcs, int(directory.get_option("TextFileContentLength")), directory.path),  # todo get content len from other opt |                      PdfFileParser(chksum_calcs, int(directory.get_option("PdfFileContentLength")), directory.path), | ||||||
|                      DocxParser(chksum_calcs, int(directory.get_option("TextFileContentLength")), directory.path),  # todo get content len from other opt |                      DocxParser(chksum_calcs, int(directory.get_option("SpreadsheetContentLength")), directory.path), | ||||||
|                      EbookParser(chksum_calcs, int(directory.get_option("TextFileContentLength")), directory.path)],  # todo get content len from other opt |                      EbookParser(chksum_calcs, int(directory.get_option("EbookContentLength")), directory.path)], | ||||||
|                     mime_guesser, self.indexer, directory.id) |                     mime_guesser, self.indexer, directory.id) | ||||||
|         c.crawl(directory.path, counter) |         c.crawl(directory.path, counter) | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										33
									
								
								indexer.py
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								indexer.py
									
									
									
									
									
								
							| @ -1,10 +1,9 @@ | |||||||
| import json | import json | ||||||
|  | 
 | ||||||
| import elasticsearch | import elasticsearch | ||||||
| from threading import Thread |  | ||||||
| import subprocess |  | ||||||
| import requests | import requests | ||||||
|  | 
 | ||||||
| import config | import config | ||||||
| import platform |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Indexer: | class Indexer: | ||||||
| @ -14,30 +13,12 @@ class Indexer: | |||||||
|         self.index_name = index |         self.index_name = index | ||||||
|         self.es = elasticsearch.Elasticsearch() |         self.es = elasticsearch.Elasticsearch() | ||||||
| 
 | 
 | ||||||
|         try: |         requests.head("http://localhost:9200") | ||||||
|             requests.head("http://localhost:9200") |         if self.es.indices.exists(self.index_name): | ||||||
| 
 |             print("Index is already setup") | ||||||
|         except requests.exceptions.ConnectionError: |  | ||||||
|             import time |  | ||||||
|             t = Thread(target=Indexer.run_elasticsearch) |  | ||||||
|             t.daemon = True |  | ||||||
|             t.start() |  | ||||||
| 
 |  | ||||||
|             time.sleep(15) |  | ||||||
| 
 |  | ||||||
|             if self.es.indices.exists(self.index_name): |  | ||||||
|                 print("Index is already setup") |  | ||||||
|             else: |  | ||||||
|                 print("First time setup...") |  | ||||||
|                 self.init() |  | ||||||
| 
 |  | ||||||
|     @staticmethod |  | ||||||
|     def run_elasticsearch(): |  | ||||||
| 
 |  | ||||||
|         if platform.system() == "Windows": |  | ||||||
|             subprocess.Popen(["elasticsearch\\bin\\elasticsearch.bat"]) |  | ||||||
|         else: |         else: | ||||||
|             subprocess.Popen(["elasticsearch/bin/elasticsearch"]) |             print("First time setup...") | ||||||
|  |             self.init() | ||||||
| 
 | 
 | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def create_bulk_index_string(docs: list, directory: int): |     def create_bulk_index_string(docs: list, directory: int): | ||||||
|  | |||||||
| @ -272,7 +272,7 @@ class TextFileParser(GenericFileParser): | |||||||
|             "text/x-bibtex", "text/x-tcl", "text/x-c++", "text/x-shellscript", "text/x-msdos-batch", |             "text/x-bibtex", "text/x-tcl", "text/x-c++", "text/x-shellscript", "text/x-msdos-batch", | ||||||
|             "text/x-makefile", "text/rtf", "text/x-objective-c", "text/troff", "text/x-m4", |             "text/x-makefile", "text/rtf", "text/x-objective-c", "text/troff", "text/x-m4", | ||||||
|             "text/x-lisp", "text/x-php", "text/x-gawk", "text/x-awk", "text/x-ruby", "text/x-po", |             "text/x-lisp", "text/x-php", "text/x-gawk", "text/x-awk", "text/x-ruby", "text/x-po", | ||||||
|             "text/x-makefile", "application/javascript", "application/rtf" |             "text/x-makefile", "application/javascript", "application/rtf", "application/json", | ||||||
|         ] |         ] | ||||||
| 
 | 
 | ||||||
|     def parse(self, full_path: str): |     def parse(self, full_path: str): | ||||||
|  | |||||||
| @ -16,4 +16,5 @@ html2text | |||||||
| docx2txt | docx2txt | ||||||
| xlrd | xlrd | ||||||
| six | six | ||||||
| cairosvg | cairosvg | ||||||
|  | ffmpeg-python | ||||||
							
								
								
									
										415
									
								
								run.py
									
									
									
									
									
								
							
							
						
						
									
										415
									
								
								run.py
									
									
									
									
									
								
							| @ -1,17 +1,18 @@ | |||||||
| from flask import Flask, render_template, request, redirect, flash, session, abort, send_file |  | ||||||
| from storage import Directory, Option, Task, User |  | ||||||
| from storage import LocalStorage, DuplicateDirectoryException, DuplicateUserException |  | ||||||
| from crawler import RunningTask, TaskManager |  | ||||||
| import json | import json | ||||||
| import os | import os | ||||||
| import shutil | import shutil | ||||||
| import bcrypt |  | ||||||
| import config |  | ||||||
| import humanfriendly |  | ||||||
| from search import Search |  | ||||||
| from PIL import Image |  | ||||||
| from io import BytesIO | from io import BytesIO | ||||||
| 
 | 
 | ||||||
|  | import bcrypt | ||||||
|  | import humanfriendly | ||||||
|  | from PIL import Image | ||||||
|  | from flask import Flask, render_template, request, redirect, flash, session, abort, send_file | ||||||
|  | 
 | ||||||
|  | import config | ||||||
|  | from crawler import TaskManager | ||||||
|  | from search import Search | ||||||
|  | from storage import Directory, Option, Task, User | ||||||
|  | from storage import LocalStorage, DuplicateDirectoryException, DuplicateUserException | ||||||
| 
 | 
 | ||||||
| app = Flask(__name__) | app = Flask(__name__) | ||||||
| app.secret_key = "A very secret key" | app.secret_key = "A very secret key" | ||||||
| @ -22,12 +23,10 @@ search = Search("changeme") | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_dir_size(path): | def get_dir_size(path): | ||||||
| 
 |  | ||||||
|     size = 0 |     size = 0 | ||||||
| 
 | 
 | ||||||
|     for root, dirs, files in os.walk(path): |     for root, dirs, files in os.walk(path): | ||||||
|         for filename in files: |         for filename in files: | ||||||
| 
 |  | ||||||
|             full_path = os.path.join(root, filename) |             full_path = os.path.join(root, filename) | ||||||
|             size += os.path.getsize(full_path) |             size += os.path.getsize(full_path) | ||||||
| 
 | 
 | ||||||
| @ -36,8 +35,11 @@ def get_dir_size(path): | |||||||
| 
 | 
 | ||||||
| @app.route("/user/<user>") | @app.route("/user/<user>") | ||||||
| def user_manage(user): | def user_manage(user): | ||||||
| 
 |     if "admin" in session and session["admin"]: | ||||||
|     return user |         return render_template("user_manage.html", directories=storage.dirs(), user=storage.users()[user]) | ||||||
|  |     else: | ||||||
|  |         flash("You are not authorized to access this page", "warning") | ||||||
|  |         return redirect("/") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/logout") | @app.route("/logout") | ||||||
| @ -57,7 +59,6 @@ def login(): | |||||||
|         session["username"] = username |         session["username"] = username | ||||||
|         session["admin"] = storage.users()[username].admin |         session["admin"] = storage.users()[username].admin | ||||||
| 
 | 
 | ||||||
|         print(session["admin"]) |  | ||||||
|         flash("Successfully logged in", "success") |         flash("Successfully logged in", "success") | ||||||
|     else: |     else: | ||||||
|         flash("Invalid username or password", "danger") |         flash("Invalid username or password", "danger") | ||||||
| @ -67,35 +68,108 @@ def login(): | |||||||
| 
 | 
 | ||||||
| @app.route("/user") | @app.route("/user") | ||||||
| def user_page(): | def user_page(): | ||||||
|  |     admin_account_present = False | ||||||
| 
 | 
 | ||||||
|     return render_template("user.html", users=storage.users()) |     for user in storage.users(): | ||||||
|  |         if storage.users()[user].admin: | ||||||
|  |             admin_account_present = True | ||||||
|  |             break | ||||||
|  | 
 | ||||||
|  |     if not admin_account_present or ("admin" in session and session["admin"]): | ||||||
|  |         return render_template("user.html", users=storage.users(), admin_account_present=admin_account_present) | ||||||
|  |     else: | ||||||
|  |         flash("You are not authorized to access this page", "warning") | ||||||
|  |         return redirect("/") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @app.route("/user/<username>/set_access") | ||||||
|  | def user_set_access(username): | ||||||
|  |     if "admin" in session and session["admin"]: | ||||||
|  |         dir_id = request.args["dir_id"] | ||||||
|  |         user = storage.users()[username] | ||||||
|  | 
 | ||||||
|  |         if request.args["access"] == "1": | ||||||
|  |             user.readable_directories.add(int(dir_id)) | ||||||
|  |         else: | ||||||
|  |             if int(dir_id) in user.readable_directories: | ||||||
|  |                 user.readable_directories.remove(int(dir_id)) | ||||||
|  | 
 | ||||||
|  |         storage.update_user(user) | ||||||
|  | 
 | ||||||
|  |         flash("Permissions mises à jour", "success") | ||||||
|  |         return redirect("/user/" + username) | ||||||
|  |     else: | ||||||
|  |         flash("You are not authorized to access this page", "warning") | ||||||
|  |         return redirect("/") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @app.route("/user/<username>/set_admin") | ||||||
|  | def user_set_admin(username): | ||||||
|  |     if "admin" in session and session["admin"]: | ||||||
|  | 
 | ||||||
|  |         user = storage.users()[username] | ||||||
|  | 
 | ||||||
|  |         if user.username == session["username"]: | ||||||
|  |             flash("You cannot modifiy your own account", "warning") | ||||||
|  |         else: | ||||||
|  |             user.admin = request.args["admin"] == "1" | ||||||
|  |             storage.update_user(user) | ||||||
|  | 
 | ||||||
|  |             flash("Permissions updated", "success") | ||||||
|  | 
 | ||||||
|  |         return redirect("/user/" + username) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/user/add", methods=['POST']) | @app.route("/user/add", methods=['POST']) | ||||||
| def user_add(): | def user_add(): | ||||||
|  |     admin_account_present = False | ||||||
| 
 | 
 | ||||||
|     username = request.form["username"] |     for user in storage.users(): | ||||||
|     password = bcrypt.hashpw(request.form["password"].encode("utf-8"), bcrypt.gensalt(config.bcrypt_rounds)) |         if storage.users()[user].admin: | ||||||
|     is_admin = True if "is_admin" in request.form else False |             admin_account_present = True | ||||||
|  |             break | ||||||
| 
 | 
 | ||||||
|     try: |     if not admin_account_present or ("admin" in session and session["admin"]): | ||||||
|         storage.save_user(User(username, password, is_admin)) |         username = request.form["username"] | ||||||
|         flash("Created new user", "success") |         password = bcrypt.hashpw(request.form["password"].encode("utf-8"), bcrypt.gensalt(config.bcrypt_rounds)) | ||||||
|     except DuplicateUserException: |         is_admin = True if "is_admin" in request.form else False | ||||||
|         flash("<strong>Couldn't create user</strong> Make sure that the username is unique", "danger") |  | ||||||
| 
 | 
 | ||||||
|     return redirect("/user") |         try: | ||||||
|  |             storage.save_user(User(username, password, is_admin)) | ||||||
|  |             flash("Created new user", "success") | ||||||
|  |         except DuplicateUserException: | ||||||
|  |             flash("<strong>Couldn't create user</strong> " | ||||||
|  |                   "Make sure that the username is unique", "danger") | ||||||
|  | 
 | ||||||
|  |         return redirect("/user") | ||||||
|  |     else: | ||||||
|  |         flash("You are not authorized to access this page", "warning") | ||||||
|  |         return redirect("/") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @app.route("/user/<username>/del") | ||||||
|  | def user_del(username): | ||||||
|  |     if "admin" in session and session["admin"]: | ||||||
|  | 
 | ||||||
|  |         if session["username"] == username: | ||||||
|  |             flash("You cannot delete your own account", "warning") | ||||||
|  |             return redirect("/user/" + username) | ||||||
|  |         else: | ||||||
|  |             storage.remove_user(username) | ||||||
|  |             flash("User deleted", "success") | ||||||
|  |             return redirect("/user") | ||||||
|  |     else: | ||||||
|  |         flash("You are not authorized to access this page", "warning") | ||||||
|  |         return redirect("/") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/suggest") | @app.route("/suggest") | ||||||
| def suggest(): | def suggest(): | ||||||
| 
 |  | ||||||
|     return json.dumps(search.suggest(request.args.get("prefix"))) |     return json.dumps(search.suggest(request.args.get("prefix"))) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/document/<doc_id>") | @app.route("/document/<doc_id>") | ||||||
| def document(doc_id): | def document(doc_id): | ||||||
| 
 |  | ||||||
|     doc = search.get_doc(doc_id)["_source"] |     doc = search.get_doc(doc_id)["_source"] | ||||||
|     directory = storage.dirs()[doc["directory"]] |     directory = storage.dirs()[doc["directory"]] | ||||||
| 
 | 
 | ||||||
| @ -106,7 +180,6 @@ def document(doc_id): | |||||||
| 
 | 
 | ||||||
| @app.route("/dl/<doc_id>") | @app.route("/dl/<doc_id>") | ||||||
| def file(doc_id): | def file(doc_id): | ||||||
| 
 |  | ||||||
|     doc = search.get_doc(doc_id)["_source"] |     doc = search.get_doc(doc_id)["_source"] | ||||||
|     directory = storage.dirs()[doc["directory"]] |     directory = storage.dirs()[doc["directory"]] | ||||||
| 
 | 
 | ||||||
| @ -121,7 +194,6 @@ def file(doc_id): | |||||||
| 
 | 
 | ||||||
| @app.route("/file/<doc_id>") | @app.route("/file/<doc_id>") | ||||||
| def download(doc_id): | def download(doc_id): | ||||||
| 
 |  | ||||||
|     doc = search.get_doc(doc_id)["_source"] |     doc = search.get_doc(doc_id)["_source"] | ||||||
|     directory = storage.dirs()[doc["directory"]] |     directory = storage.dirs()[doc["directory"]] | ||||||
|     extension = "" if doc["extension"] is None or doc["extension"] == "" else "." + doc["extension"] |     extension = "" if doc["extension"] is None or doc["extension"] == "" else "." + doc["extension"] | ||||||
| @ -135,7 +207,6 @@ def download(doc_id): | |||||||
| 
 | 
 | ||||||
| @app.route("/thumb/<doc_id>") | @app.route("/thumb/<doc_id>") | ||||||
| def thumb(doc_id): | def thumb(doc_id): | ||||||
| 
 |  | ||||||
|     doc = search.get_doc(doc_id) |     doc = search.get_doc(doc_id) | ||||||
| 
 | 
 | ||||||
|     if doc is not None: |     if doc is not None: | ||||||
| @ -145,14 +216,12 @@ def thumb(doc_id): | |||||||
|         if os.path.isfile(tn_path): |         if os.path.isfile(tn_path): | ||||||
|             return send_file(tn_path) |             return send_file(tn_path) | ||||||
|         else: |         else: | ||||||
|             print("tn not found") |  | ||||||
|             default_thumbnail = BytesIO() |             default_thumbnail = BytesIO() | ||||||
|             Image.new("RGB", (255, 128), (0, 0, 0)).save(default_thumbnail, "JPEG") |             Image.new("RGB", (255, 128), (0, 0, 0)).save(default_thumbnail, "JPEG") | ||||||
|             default_thumbnail.seek(0) |             default_thumbnail.seek(0) | ||||||
|             return send_file(default_thumbnail, "image/jpeg") |             return send_file(default_thumbnail, "image/jpeg") | ||||||
| 
 | 
 | ||||||
|     else: |     else: | ||||||
|         print("doc is none") |  | ||||||
|         default_thumbnail = BytesIO() |         default_thumbnail = BytesIO() | ||||||
|         Image.new("RGB", (255, 128), (0, 0, 0)).save(default_thumbnail, "JPEG") |         Image.new("RGB", (255, 128), (0, 0, 0)).save(default_thumbnail, "JPEG") | ||||||
|         default_thumbnail.seek(0) |         default_thumbnail.seek(0) | ||||||
| @ -161,11 +230,14 @@ def thumb(doc_id): | |||||||
| 
 | 
 | ||||||
| @app.route("/") | @app.route("/") | ||||||
| def search_page(): | def search_page(): | ||||||
| 
 |  | ||||||
|     mime_map = search.get_mime_map() |     mime_map = search.get_mime_map() | ||||||
|     mime_map.append({"id": "any", "text": "Any"}) |     mime_map.append({"id": "any", "text": "All"}) | ||||||
| 
 | 
 | ||||||
|     return render_template("search.html", directories=storage.dirs(), mime_map=mime_map) |     directories = [storage.dirs()[x] for x in get_allowed_dirs(session["username"] if "username" in session else None)] | ||||||
|  | 
 | ||||||
|  |     return render_template("search.html", | ||||||
|  |                            directories=directories, | ||||||
|  |                            mime_map=mime_map) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/list") | @app.route("/list") | ||||||
| @ -173,9 +245,19 @@ def search_liste_page(): | |||||||
|     return render_template("searchList.html") |     return render_template("searchList.html") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def get_allowed_dirs(username): | ||||||
|  |     if config.allow_guests: | ||||||
|  |         return [x for x in storage.dirs() if x.enabled] | ||||||
|  |     else: | ||||||
|  |         if username: | ||||||
|  |             user = storage.users()[username] | ||||||
|  |             return [x for x in storage.dirs() if storage.dirs()[x].enabled and x in user.readable_directories] | ||||||
|  |         else: | ||||||
|  |             return list() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @app.route("/search", methods=['POST']) | @app.route("/search", methods=['POST']) | ||||||
| def search_route(): | def search_route(): | ||||||
| 
 |  | ||||||
|     query = request.json["q"] |     query = request.json["q"] | ||||||
|     query = "" if query is None else query |     query = "" if query is None else query | ||||||
| 
 | 
 | ||||||
| @ -186,19 +268,8 @@ def search_route(): | |||||||
|     directories = request.json["directories"] |     directories = request.json["directories"] | ||||||
| 
 | 
 | ||||||
|     # Remove disabled & non-existing directories |     # Remove disabled & non-existing directories | ||||||
|     for search_directory in directories: |     allowed_dirs = get_allowed_dirs(session["username"] if "username" in session else None) | ||||||
|         directory_exists = False |     directories = [x for x in directories if x in allowed_dirs] | ||||||
| 
 |  | ||||||
|         for dir_id in storage.dirs(): |  | ||||||
|             if search_directory == dir_id: |  | ||||||
|                 directory_exists = True |  | ||||||
| 
 |  | ||||||
|                 if not storage.dirs()[dir_id].enabled: |  | ||||||
|                     directories.remove(search_directory) |  | ||||||
|                 break |  | ||||||
| 
 |  | ||||||
|         if not directory_exists: |  | ||||||
|             directories.remove(search_directory) |  | ||||||
| 
 | 
 | ||||||
|     path = request.json["path"] |     path = request.json["path"] | ||||||
| 
 | 
 | ||||||
| @ -209,7 +280,6 @@ def search_route(): | |||||||
| 
 | 
 | ||||||
| @app.route("/scroll") | @app.route("/scroll") | ||||||
| def scroll_route(): | def scroll_route(): | ||||||
| 
 |  | ||||||
|     scroll_id = request.args.get("scroll_id") |     scroll_id = request.args.get("scroll_id") | ||||||
| 
 | 
 | ||||||
|     page = search.scroll(scroll_id) |     page = search.scroll(scroll_id) | ||||||
| @ -219,184 +289,237 @@ def scroll_route(): | |||||||
| 
 | 
 | ||||||
| @app.route("/directory") | @app.route("/directory") | ||||||
| def dir_list(): | def dir_list(): | ||||||
| 
 |     if "admin" in session and session["admin"]: | ||||||
|     return render_template("directory.html", directories=storage.dirs()) |         return render_template("directory.html", directories=storage.dirs()) | ||||||
|  |     else: | ||||||
|  |         flash("You are not authorized to access this page", "warning") | ||||||
|  |         return redirect("/") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/directory/add") | @app.route("/directory/add") | ||||||
| def directory_add(): | def directory_add(): | ||||||
|  |     if "admin" in session and session["admin"]: | ||||||
|  |         path = request.args.get("path") | ||||||
|  |         name = request.args.get("name") | ||||||
| 
 | 
 | ||||||
|     path = request.args.get("path") |         if path is not None and name is not None: | ||||||
|     name = request.args.get("name") |             d = Directory(path, True, [], name) | ||||||
| 
 | 
 | ||||||
|     if path is not None and name is not None: |             try: | ||||||
|         d = Directory(path, True, [], name) |                 d.set_default_options() | ||||||
|  |                 storage.save_directory(d) | ||||||
|  |                 flash("<strong>Directory created</strong>", "success") | ||||||
|  |             except DuplicateDirectoryException: | ||||||
|  |                 flash("<strong>The directory couldn't be created</strong> Make sure to chose a unique name", | ||||||
|  |                       "danger") | ||||||
| 
 | 
 | ||||||
|         try: |         return redirect("/directory") | ||||||
|             d.set_default_options() |     else: | ||||||
|             storage.save_directory(d) |         flash("You are not authorized to access this page", "warning") | ||||||
|             flash("<strong>Created directory</strong>", "success") |         return redirect("/") | ||||||
|         except DuplicateDirectoryException: |  | ||||||
|             flash("<strong>Couldn't create directory</strong> Make sure that the path is unique", "danger") |  | ||||||
| 
 |  | ||||||
|     return redirect("/directory") |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/directory/<int:dir_id>") | @app.route("/directory/<int:dir_id>") | ||||||
| def directory_manage(dir_id): | def directory_manage(dir_id): | ||||||
|  |     if "admin" in session and session["admin"]: | ||||||
|  |         directory = storage.dirs()[dir_id] | ||||||
|  |         tn_size = get_dir_size("static/thumbnails/" + str(dir_id)) | ||||||
|  |         tn_size_formatted = humanfriendly.format_size(tn_size) | ||||||
| 
 | 
 | ||||||
|     directory = storage.dirs()[dir_id] |         return render_template("directory_manage.html", directory=directory, tn_size=tn_size, | ||||||
|     tn_size = get_dir_size("static/thumbnails/" + str(dir_id)) |                                tn_size_formatted=tn_size_formatted) | ||||||
|     tn_size_formatted = humanfriendly.format_size(tn_size) |     else: | ||||||
| 
 |         flash("You are not authorized to access this page", "warning") | ||||||
|     return render_template("directory_manage.html", directory=directory, tn_size=tn_size, |         return redirect("/") | ||||||
|                            tn_size_formatted=tn_size_formatted) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/directory/<int:dir_id>/update") | @app.route("/directory/<int:dir_id>/update") | ||||||
| def directory_update(dir_id): | def directory_update(dir_id): | ||||||
|  |     if "admin" in session and session["admin"]: | ||||||
|  |         directory = storage.dirs()[dir_id] | ||||||
| 
 | 
 | ||||||
|     directory = storage.dirs()[dir_id] |         name = request.args.get("name") | ||||||
|  |         name = directory.name if name is None else name | ||||||
| 
 | 
 | ||||||
|     name = request.args.get("name") |         enabled = request.args.get("enabled") | ||||||
|     name = directory.name if name is None else name |         enabled = directory.enabled if enabled is None else int(enabled) | ||||||
| 
 | 
 | ||||||
|     enabled = request.args.get("enabled") |         path = request.args.get("path") | ||||||
|     enabled = directory.enabled if enabled is None else int(enabled) |         path = directory.path if path is None else path | ||||||
| 
 | 
 | ||||||
|     path = request.args.get("path") |         # Only name and enabled status can be updated | ||||||
|     path = directory.path if path is None else path |         updated_dir = Directory(path, enabled, directory.options, name) | ||||||
|  |         updated_dir.id = dir_id | ||||||
| 
 | 
 | ||||||
|     # Only name and enabled status can be updated |         try: | ||||||
|     updated_dir = Directory(path, enabled, directory.options, name) |             storage.update_directory(updated_dir) | ||||||
|     updated_dir.id = dir_id |             flash("<strong>Updated directory</strong>", "success") | ||||||
| 
 | 
 | ||||||
|     try: |         except DuplicateDirectoryException: | ||||||
|         storage.update_directory(updated_dir) |             flash("<strong>The directory couldn't be updated</strong> Make the that the path is unique", | ||||||
|         flash("<strong>Updated directory</strong>", "success") |                   "danger") | ||||||
| 
 | 
 | ||||||
|     except DuplicateDirectoryException: |         return redirect("/directory/" + str(dir_id)) | ||||||
|         flash("<strong>Couldn't update directory</strong> Make sure that the path is unique", "danger") |     else: | ||||||
| 
 |         flash("You are not authorized to access this page", "warning") | ||||||
|     return redirect("/directory/" + str(dir_id)) |         return redirect("/") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/directory/<int:dir_id>/update_opt") | @app.route("/directory/<int:dir_id>/update_opt") | ||||||
| def directory_update_opt(dir_id): | def directory_update_opt(dir_id): | ||||||
|  |     if "admin" in session and session["admin"]: | ||||||
|  |         opt_id = request.args.get("id") | ||||||
|  |         opt_key = request.args.get("key") | ||||||
|  |         opt_value = request.args.get("value") | ||||||
| 
 | 
 | ||||||
|     opt_id = request.args.get("id") |         storage.update_option(Option(opt_key, opt_value, dir_id, opt_id)) | ||||||
|     opt_key = request.args.get("key") |  | ||||||
|     opt_value = request.args.get("value") |  | ||||||
| 
 | 
 | ||||||
|     storage.update_option(Option(opt_key, opt_value, dir_id, opt_id)) |         return redirect("/directory/" + str(dir_id)) | ||||||
| 
 |     else: | ||||||
|     return redirect("/directory/" + str(dir_id)) |         flash("You are not authorized to access this page", "warning") | ||||||
|  |         return redirect("/") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/directory/<int:dir_id>/del") | @app.route("/directory/<int:dir_id>/del") | ||||||
| def directory_del(dir_id): | def directory_del(dir_id): | ||||||
|  |     if "admin" in session and session["admin"]: | ||||||
|  |         search.delete_directory(dir_id) | ||||||
|  |         if os.path.exists("static/thumbnails/" + str(dir_id)): | ||||||
|  |             shutil.rmtree("static/thumbnails/" + str(dir_id)) | ||||||
| 
 | 
 | ||||||
|     search.delete_directory(dir_id) |         storage.remove_directory(dir_id) | ||||||
|     if os.path.exists("static/thumbnails/" + str(dir_id)): |         flash("<strong>Deleted folder</strong>", "success") | ||||||
|         shutil.rmtree("static/thumbnails/" + str(dir_id)) |  | ||||||
| 
 | 
 | ||||||
|     storage.remove_directory(dir_id) |         return redirect("/directory") | ||||||
|     flash("<strong>Deleted directory</strong>", "success") |     else: | ||||||
| 
 |         flash("You are not authorized to access this page", "warning") | ||||||
|     return redirect("/directory") |         return redirect("/") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/directory/<int:dir_id>/reset") | @app.route("/directory/<int:dir_id>/reset") | ||||||
| def directory_reset(dir_id): | def directory_reset(dir_id): | ||||||
|     directory = storage.dirs()[dir_id] |     if "admin" in session and session["admin"]: | ||||||
|  |         directory = storage.dirs()[dir_id] | ||||||
| 
 | 
 | ||||||
|     for opt in directory.options: |         for opt in directory.options: | ||||||
|         storage.del_option(opt.id) |             storage.del_option(opt.id) | ||||||
| 
 | 
 | ||||||
|     directory.set_default_options() |         directory.set_default_options() | ||||||
| 
 | 
 | ||||||
|     for opt in directory.options: |         for opt in directory.options: | ||||||
|         opt.dir_id = dir_id |             opt.dir_id = dir_id | ||||||
|         storage.save_option(opt) |             storage.save_option(opt) | ||||||
| 
 | 
 | ||||||
|     storage.dir_cache_outdated = True |         storage.dir_cache_outdated = True | ||||||
| 
 | 
 | ||||||
|     search.delete_directory(dir_id) |         search.delete_directory(dir_id) | ||||||
| 
 | 
 | ||||||
|     flash("<strong>Reset directory options to default settings</strong>", "success") |         flash("<strong>Reset directory options</strong>", "success") | ||||||
|     return redirect("directory/" + str(dir_id)) |         return redirect("directory/" + str(dir_id)) | ||||||
|  |     else: | ||||||
|  |         flash("You are not authorized to access this page", "warning") | ||||||
|  |         return redirect("/") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/task") | @app.route("/task") | ||||||
| def task(): | def task(): | ||||||
| 
 |     if "admin" in session and session["admin"]: | ||||||
|     return render_template("task.html", tasks=storage.tasks(), directories=storage.dirs(), |         return render_template("task.html", tasks=storage.tasks(), directories=storage.dirs(), | ||||||
|                            task_list=json.dumps(list(storage.tasks().keys()))) |                                task_list=json.dumps(list(storage.tasks().keys()))) | ||||||
|     # return render_template("task.html", tasks=storage.tasks(), directories=storage.dirs()) |     else: | ||||||
|  |         flash("You are not authorized to access this page", "warning") | ||||||
|  |         return redirect("/") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/task/current") | @app.route("/task/current") | ||||||
| def get_current_task(): | def get_current_task(): | ||||||
|  |     if "admin" in session and session["admin"]: | ||||||
| 
 | 
 | ||||||
|     if tm and tm.current_task: |         if tm and tm.current_task: | ||||||
|         return tm.current_task.to_json() |             return tm.current_task.to_json() | ||||||
|  |         else: | ||||||
|  |             return "" | ||||||
|     else: |     else: | ||||||
|         return "" |         flash("You are not authorized to access this page", "warning") | ||||||
|  |         return redirect("/") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/task/add") | @app.route("/task/add") | ||||||
| def task_add(): | def task_add(): | ||||||
|     type = request.args.get("type") |     if "admin" in session and session["admin"]: | ||||||
|     directory = request.args.get("directory") |         task_type = request.args.get("type") | ||||||
|  |         directory = request.args.get("directory") | ||||||
| 
 | 
 | ||||||
|     storage.save_task(Task(type, directory)) |         if task_type not in ("1", "2"): | ||||||
|  |             flash("Please choose a task type", "danger") | ||||||
|  |             return redirect("/task") | ||||||
| 
 | 
 | ||||||
|     return redirect("/task") |         if directory.isdigit() and int(directory) in storage.dirs(): | ||||||
|  |             storage.save_task(Task(task_type, directory)) | ||||||
|  |         else: | ||||||
|  |             flash("You must choose a directory", "danger") | ||||||
|  | 
 | ||||||
|  |         return redirect("/task") | ||||||
|  |     else: | ||||||
|  |         flash("You are not authorized to access this page", "warning") | ||||||
|  |         return redirect("/") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/task/<int:task_id>/del") | @app.route("/task/<int:task_id>/del") | ||||||
| def task_del(task_id): | def task_del(task_id): | ||||||
|     storage.del_task(task_id) |     if "admin" in session and session["admin"]: | ||||||
|  |         storage.del_task(task_id) | ||||||
| 
 | 
 | ||||||
|     if tm.current_task is not None and task_id == tm.current_task.task.id: |         if tm.current_task is not None and task_id == tm.current_task.task.id: | ||||||
|         tm.cancel_task() |             tm.cancel_task() | ||||||
| 
 | 
 | ||||||
|     return redirect("/task") |         return redirect("/task") | ||||||
|  |     else: | ||||||
|  |         flash("You are not authorized to access this page", "warning") | ||||||
|  |         return redirect("/") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/reset_es") | @app.route("/reset_es") | ||||||
| def reset_es(): | def reset_es(): | ||||||
|  |     if "admin" in session and session["admin"]: | ||||||
|  |         flash("Elasticsearch index has been reset. Modifications made in <b>config.py</b> have been applied.", | ||||||
|  |               "success") | ||||||
| 
 | 
 | ||||||
|     flash("Elasticsearch index has been reset. Modifications made in <b>config.py</b> have been applied.", "success") |         tm.indexer.init() | ||||||
|  |         if os.path.exists("static/thumbnails"): | ||||||
|  |             shutil.rmtree("static/thumbnails") | ||||||
| 
 | 
 | ||||||
|     tm.indexer.init() |         return redirect("/dashboard") | ||||||
|     if os.path.exists("static/thumbnails"): |     else: | ||||||
|         shutil.rmtree("static/thumbnails") |         flash("You are not authorized to access this page", "warning") | ||||||
| 
 |         return redirect("/") | ||||||
|     return redirect("/dashboard") |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.route("/dashboard") | @app.route("/dashboard") | ||||||
| def dashboard(): | def dashboard(): | ||||||
|  |     if "admin" in session and session["admin"]: | ||||||
|  |         tn_sizes = {} | ||||||
|  |         tn_size_total = 0 | ||||||
|  |         for directory in storage.dirs(): | ||||||
|  |             tn_size = get_dir_size("static/thumbnails/" + str(directory)) | ||||||
|  |             tn_size_formatted = humanfriendly.format_size(tn_size) | ||||||
| 
 | 
 | ||||||
|     tn_sizes = {} |             tn_sizes[directory] = tn_size_formatted | ||||||
|     tn_size_total = 0 |             tn_size_total += tn_size | ||||||
|     for directory in storage.dirs(): |  | ||||||
|         tn_size = get_dir_size("static/thumbnails/" + str(directory)) |  | ||||||
|         tn_size_formatted = humanfriendly.format_size(tn_size) |  | ||||||
| 
 | 
 | ||||||
|         tn_sizes[directory] = tn_size_formatted |         tn_size_total_formatted = humanfriendly.format_size(tn_size_total) | ||||||
|         tn_size_total += tn_size |  | ||||||
| 
 | 
 | ||||||
|     tn_size_total_formatted = humanfriendly.format_size(tn_size_total) |         return render_template("dashboard.html", version=config.VERSION, tn_sizes=tn_sizes, | ||||||
|  |                                tn_size_total=tn_size_total_formatted, | ||||||
|  |                                doc_size=humanfriendly.format_size(search.get_doc_size()), | ||||||
|  |                                doc_count=search.get_doc_count(), | ||||||
|  |                                db_path=config.db_path, | ||||||
|  |                                elasticsearch_url=config.elasticsearch_url, | ||||||
|  |                                index_size=humanfriendly.format_size(search.get_index_size())) | ||||||
| 
 | 
 | ||||||
|     return render_template("dashboard.html", version=config.VERSION, tn_sizes=tn_sizes, |     else: | ||||||
|                            tn_size_total=tn_size_total_formatted, |         flash("You are not authorized to access this page", "warning") | ||||||
|                            doc_size=humanfriendly.format_size(search.get_doc_size()), |         return redirect("/") | ||||||
|                            doc_count=search.get_doc_count(), |  | ||||||
|                            db_path=config.db_path, |  | ||||||
|                            elasticsearch_url=config.elasticsearch_url, |  | ||||||
|                            index_size=humanfriendly.format_size(search.get_index_size())) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								static/css/bootstrap.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								static/css/bootstrap.min.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -173,8 +173,8 @@ function gifOver(thumbnail, documentId) { | |||||||
| 
 | 
 | ||||||
| function downloadPopover(element, documentId) { | function downloadPopover(element, documentId) { | ||||||
|     element.setAttribute("data-content", |     element.setAttribute("data-content", | ||||||
|         '<a class="btn btn-sm btn-primary" href="/dl/'+ documentId +'"><i class="fas fa-download"></i> Download</a>' + |         '<a class="btn btn-sm btn-primary" href="/dl/'+ documentId +'"><i class="fas fa-download"></i> Télécharger</a>' + | ||||||
|         '<a class="btn btn-sm btn-success" style="margin-left:3px;" href="/file/'+ documentId + '" target="_blank"><i class="fas fa-eye"></i> View</a>'); |         '<a class="btn btn-sm btn-success" style="margin-left:3px;" href="/file/'+ documentId + '" target="_blank"><i class="fas fa-eye"></i> Consulter</a>'); | ||||||
|     element.setAttribute("data-toggle", "popover"); |     element.setAttribute("data-toggle", "popover"); | ||||||
|     element.addEventListener("mouseover", function() { |     element.addEventListener("mouseover", function() { | ||||||
|         element.focus(); |         element.focus(); | ||||||
| @ -592,4 +592,4 @@ document.getElementById("pathBar").addEventListener("keyup", function () { | |||||||
|     searchQueued = true; |     searchQueued = true; | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| window.setInterval(search, 150); | window.setInterval(search, 300); | ||||||
							
								
								
									
										20
									
								
								storage.py
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								storage.py
									
									
									
									
									
								
							| @ -7,6 +7,9 @@ import config | |||||||
| 
 | 
 | ||||||
| class CheckSumCalculator: | class CheckSumCalculator: | ||||||
| 
 | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|     def checksum(self, string: str): |     def checksum(self, string: str): | ||||||
|         return flask_bcrypt.generate_password_hash(string, config.bcrypt_rounds) |         return flask_bcrypt.generate_password_hash(string, config.bcrypt_rounds) | ||||||
| 
 | 
 | ||||||
| @ -28,6 +31,7 @@ class User: | |||||||
|         self.username = username |         self.username = username | ||||||
|         self.hashed_password = hashed_password |         self.hashed_password = hashed_password | ||||||
|         self.admin = admin |         self.admin = admin | ||||||
|  |         self.readable_directories = set() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Option: | class Option: | ||||||
| @ -224,7 +228,15 @@ class LocalStorage: | |||||||
|             db_users = c.fetchall() |             db_users = c.fetchall() | ||||||
| 
 | 
 | ||||||
|             for db_user in db_users: |             for db_user in db_users: | ||||||
|                 self.cached_users[db_user[0]] = User(db_user[0], b"", bool(db_user[1])) |                 user = User(db_user[0], b"", bool(db_user[1])) | ||||||
|  | 
 | ||||||
|  |                 c.execute("SELECT username, directory_id FROM User_canRead_Directory WHERE username=?", (db_user[0],)) | ||||||
|  |                 db_accesses = c.fetchall() | ||||||
|  | 
 | ||||||
|  |                 for db_access in db_accesses: | ||||||
|  |                     user.readable_directories.add(db_access[1]) | ||||||
|  | 
 | ||||||
|  |                 self.cached_users[db_user[0]] = user | ||||||
| 
 | 
 | ||||||
|             conn.close() |             conn.close() | ||||||
| 
 | 
 | ||||||
| @ -256,6 +268,12 @@ class LocalStorage: | |||||||
|         c = conn.cursor() |         c = conn.cursor() | ||||||
|         c.execute("UPDATE User SET is_admin=? WHERE username=?", (user.admin, user.username)) |         c.execute("UPDATE User SET is_admin=? WHERE username=?", (user.admin, user.username)) | ||||||
| 
 | 
 | ||||||
|  |         c.execute("DELETE FROM User_canRead_Directory WHERE username=?", (user.username, )) | ||||||
|  |         conn.commit() | ||||||
|  | 
 | ||||||
|  |         for access in user.readable_directories: | ||||||
|  |             c.execute("INSERT INTO User_canRead_Directory VALUES (?,?)", (user.username, access)) | ||||||
|  | 
 | ||||||
|         c.close() |         c.close() | ||||||
|         conn.commit() |         conn.commit() | ||||||
|         conn.close() |         conn.close() | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ | |||||||
| 
 | 
 | ||||||
|     <div class="container"> |     <div class="container"> | ||||||
|         <div class="card"> |         <div class="card"> | ||||||
|             <div class="card-header">FSE Information</div> |             <div class="card-header">Global information</div> | ||||||
|             <div class="card-body"> |             <div class="card-body"> | ||||||
|                 <table class="info-table table-hover table-striped"> |                 <table class="info-table table-hover table-striped"> | ||||||
|                     <tbody> |                     <tbody> | ||||||
| @ -26,7 +26,7 @@ | |||||||
|                         <td><pre>{{ doc_size }}</pre></td> |                         <td><pre>{{ doc_size }}</pre></td> | ||||||
|                     </tr> |                     </tr> | ||||||
|                     <tr> |                     <tr> | ||||||
|                         <th>Total index size</th> |                         <th>total index size</th> | ||||||
|                         <td><pre>{{ index_size }}</pre></td> |                         <td><pre>{{ index_size }}</pre></td> | ||||||
|                     </tr> |                     </tr> | ||||||
|                     <tr> |                     <tr> | ||||||
| @ -52,7 +52,7 @@ | |||||||
|             <div class="card-header">Actions</div> |             <div class="card-header">Actions</div> | ||||||
|             <div class="card-body"> |             <div class="card-body"> | ||||||
|                 <button class="btn btn-danger" onclick="resetAll()"> |                 <button class="btn btn-danger" onclick="resetAll()"> | ||||||
|                     <i class="fas fa-exclamation-triangle"></i> Reset elasticsearch index |                     <i class="fas fa-exclamation-triangle"></i> Reset Elasticsearch | ||||||
|                 </button> |                 </button> | ||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
|  | |||||||
| @ -1,14 +1,14 @@ | |||||||
| {% extends "layout.html" %} | {% extends "layout.html" %} | ||||||
| {% set active_page = "directory" %} | {% set active_page = "directory" %} | ||||||
| 
 | 
 | ||||||
| {% block title %}An Excellent title{% endblock title %} | {% block title %}Directory list{% endblock title %} | ||||||
| 
 | 
 | ||||||
| {% block body %} | {% block body %} | ||||||
|     <div class="container"> |     <div class="container"> | ||||||
| 
 | 
 | ||||||
|         {# Add directory form #} |         {# Add directory form #} | ||||||
|         <div class="card"> |         <div class="card"> | ||||||
|             <div class="card-header">An excellent form</div> |             <div class="card-header">Add a directory</div> | ||||||
|             <div class="card-body"> |             <div class="card-body"> | ||||||
| 
 | 
 | ||||||
|                 <form method="GET" action="/directory/add"> |                 <form method="GET" action="/directory/add"> | ||||||
| @ -18,7 +18,7 @@ | |||||||
|                     <div class="form-group"> |                     <div class="form-group"> | ||||||
|                         <input type="text" class="form-control" placeholder="Absolute path" name="path"> |                         <input type="text" class="form-control" placeholder="Absolute path" name="path"> | ||||||
|                     </div> |                     </div> | ||||||
|                     <button type="submit" class="btn btn-success"><i class="fas fa-plus"></i> Add Directory</button> |                     <button type="submit" class="btn btn-success"><i class="fas fa-plus"></i> Add directory</button> | ||||||
|                 </form> |                 </form> | ||||||
| 
 | 
 | ||||||
|             </div> |             </div> | ||||||
| @ -26,16 +26,15 @@ | |||||||
| 
 | 
 | ||||||
|         {# List of directories #} |         {# List of directories #} | ||||||
|         <div class="card"> |         <div class="card"> | ||||||
|             <div class="card-header">An excellent list</div> |             <div class="card-header">Directory list</div> | ||||||
|             <div class="card-body"> |             <div class="card-body"> | ||||||
| 
 | 
 | ||||||
|                 <table class="info-table table-hover table-striped"> |                 <table class="info-table table-hover table-striped"> | ||||||
|                     <thead> |                     <thead> | ||||||
|                     <tr> |                     <tr> | ||||||
|                         <th>Display Name</th> |                         <th>Nom</th> | ||||||
|                         <th>Path</th> |                         <th>Chemin</th> | ||||||
|                         <th>Enabled</th> |                         <th>Activé</th> | ||||||
|                         <th>Last indexed</th> |  | ||||||
|                         <th>Action</th> |                         <th>Action</th> | ||||||
|                     </tr> |                     </tr> | ||||||
|                     </thead> |                     </thead> | ||||||
| @ -45,8 +44,7 @@ | |||||||
|                             <td>{{ directories[dir].name }}</td> |                             <td>{{ directories[dir].name }}</td> | ||||||
|                             <td style="word-break: break-all"><pre>{{ directories[dir].path }}</pre></td> |                             <td style="word-break: break-all"><pre>{{ directories[dir].path }}</pre></td> | ||||||
|                             <td><i class="far {{ "fa-check-square" if directories[dir].enabled else "fa-square" }}"></i></td> |                             <td><i class="far {{ "fa-check-square" if directories[dir].enabled else "fa-square" }}"></i></td> | ||||||
|                             <td>2018-02-21</td> |                             <td><a href="directory/{{ dir }}" class="btn btn-primary"><i class="fas fa-cog"></i> </a> Manage</td> | ||||||
|                             <td><a href="directory/{{ dir }}" class="btn btn-primary"><i class="fas fa-cog"></i> Manage</a> </td> |  | ||||||
|                         </tr> |                         </tr> | ||||||
|                     {% endfor %} |                     {% endfor %} | ||||||
|                     </tbody> |                     </tbody> | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| {%  extends "layout.html" %} | {% extends "layout.html" %} | ||||||
| {% set active_page = "directory" %} | {% set active_page = "directory" %} | ||||||
| 
 | 
 | ||||||
| {% block title %}An excellent title{% endblock title %} | {% block title %}{{ directory.name }}{% endblock title %} | ||||||
| 
 | 
 | ||||||
| {% block body %} | {% block body %} | ||||||
| 
 | 
 | ||||||
| @ -75,7 +75,7 @@ | |||||||
|                     </tr> |                     </tr> | ||||||
| 
 | 
 | ||||||
|                     <tr onclick="modifyPath()"> |                     <tr onclick="modifyPath()"> | ||||||
|                         <th style="width: 20%">Path</th>Task |                         <th style="width: 20%">Path</th> | ||||||
|                         <td> |                         <td> | ||||||
|                             <pre id="path" title="Click to update">{{ directory.path }}</pre> |                             <pre id="path" title="Click to update">{{ directory.path }}</pre> | ||||||
|                         </td> |                         </td> | ||||||
| @ -84,7 +84,8 @@ | |||||||
|                     <tr> |                     <tr> | ||||||
|                         <th style="width: 20%">Enabled</th> |                         <th style="width: 20%">Enabled</th> | ||||||
|                         <td> |                         <td> | ||||||
|                             <form action="/directory/{{ directory.id }}/update"  style="display: inline;margin-left: 6px;"> |                             <form action="/directory/{{ directory.id }}/update" | ||||||
|  |                                   style="display: inline;margin-left: 6px;"> | ||||||
|                                 <input type="hidden" name="enabled" value="{{ "0" if directory.enabled else "1" }}"> |                                 <input type="hidden" name="enabled" value="{{ "0" if directory.enabled else "1" }}"> | ||||||
|                                 <button class="btn btn-sm {{ "btn-danger" if directory.enabled else "btn-success" }}"> |                                 <button class="btn btn-sm {{ "btn-danger" if directory.enabled else "btn-success" }}"> | ||||||
|                                     <i class="far {{ "fa-check-square" if directory.enabled else "fa-square" }}"></i> |                                     <i class="far {{ "fa-check-square" if directory.enabled else "fa-square" }}"></i> | ||||||
| @ -96,7 +97,9 @@ | |||||||
| 
 | 
 | ||||||
|                     <tr> |                     <tr> | ||||||
|                         <th>Thumbnail cache size</th> |                         <th>Thumbnail cache size</th> | ||||||
|                         <td><pre>{{ tn_size_formatted }} ({{ tn_size }} bytes)</pre></td> |                         <td> | ||||||
|  |                             <pre>{{ tn_size_formatted }} ({{ tn_size }} bytes)</pre> | ||||||
|  |                         </td> | ||||||
|                     </tr> |                     </tr> | ||||||
|                 </table> |                 </table> | ||||||
|             </div> |             </div> | ||||||
| @ -112,7 +115,7 @@ | |||||||
|                     <form action="/task/add" class="p-2"> |                     <form action="/task/add" class="p-2"> | ||||||
|                         <input type="hidden" value="1" name="type"> |                         <input type="hidden" value="1" name="type"> | ||||||
|                         <input type="hidden" value="{{ directory.id }}" name="directory"> |                         <input type="hidden" value="{{ directory.id }}" name="directory"> | ||||||
|                         <button class="btn btn-primary" href="/task/" value="Generate thumbnails"> |                         <button class="btn btn-primary" href="/task/"> | ||||||
|                             <i class="fas fa-book"></i> Generate index |                             <i class="fas fa-book"></i> Generate index | ||||||
|                         </button> |                         </button> | ||||||
|                     </form> |                     </form> | ||||||
| @ -120,7 +123,7 @@ | |||||||
|                     <form action="/task/add" class="p-2"> |                     <form action="/task/add" class="p-2"> | ||||||
|                         <input type="hidden" value="2" name="type"> |                         <input type="hidden" value="2" name="type"> | ||||||
|                         <input type="hidden" value="{{ directory.id }}" name="directory"> |                         <input type="hidden" value="{{ directory.id }}" name="directory"> | ||||||
|                         <button class="btn btn-primary" href="/task/" value="Generate thumbnails"> |                         <button class="btn btn-primary" href="/task/"> | ||||||
|                             <i class="far fa-images"></i> Generate thumbnails |                             <i class="far fa-images"></i> Generate thumbnails | ||||||
|                         </button> |                         </button> | ||||||
|                     </form> |                     </form> | ||||||
| @ -140,7 +143,8 @@ | |||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <div class="card"> |         <div class="card"> | ||||||
|             <div class="card-header">Options <a href="#" style="float:right">Learn more <i class="fas fa-external-link-alt"></i></a></div> |             <div class="card-header">Options <a href="#" style="float:right">Learn more <i | ||||||
|  |                     class="fas fa-external-link-alt"></i></a></div> | ||||||
|             <div class="card-body"> |             <div class="card-body"> | ||||||
|                 <table class="info-table table-striped table-hover"> |                 <table class="info-table table-striped table-hover"> | ||||||
|                     <thead> |                     <thead> | ||||||
| @ -154,7 +158,9 @@ | |||||||
| 
 | 
 | ||||||
|                         <tr> |                         <tr> | ||||||
|                             <td style="width: 30%"><span>{{ option.key }}</span></td> |                             <td style="width: 30%"><span>{{ option.key }}</span></td> | ||||||
|                             <td onclick="modifyVal({{ option.id }}, '{{ option.key }}')" title="Click to update"><pre id="val-{{ option.id }}">{{ option.value }}</pre></td> |                             <td onclick="modifyVal({{ option.id }}, '{{ option.key }}')" title="Click to update"> | ||||||
|  |                                 <pre id="val-{{ option.id }}">{{ option.value }}</pre> | ||||||
|  |                             </td> | ||||||
|                         </tr> |                         </tr> | ||||||
| 
 | 
 | ||||||
|                     {% endfor %} |                     {% endfor %} | ||||||
|  | |||||||
| @ -26,7 +26,7 @@ | |||||||
| 
 | 
 | ||||||
|                 <hr> |                 <hr> | ||||||
| 
 | 
 | ||||||
|                 <h3>Raw json</h3> |                 <h3>Raw JSON</h3> | ||||||
|                 <textarea class="form-control" style="min-height: 100px" readonly>{{ doc | tojson }}</textarea> |                 <textarea class="form-control" style="min-height: 100px" readonly>{{ doc | tojson }}</textarea> | ||||||
| 
 | 
 | ||||||
|                 <hr> |                 <hr> | ||||||
|  | |||||||
| @ -1,71 +0,0 @@ | |||||||
| <!DOCTYPE html> |  | ||||||
| <html lang="en"> |  | ||||||
| <head> |  | ||||||
|     <meta charset="UTF-8"> |  | ||||||
|     <title>Title</title> |  | ||||||
| </head> |  | ||||||
| <body> |  | ||||||
| 
 |  | ||||||
| <style> |  | ||||||
|     .fit:hover { |  | ||||||
|         -webkit-transform:scale(2.5); |  | ||||||
|         -moz-transform:scale(2.5); |  | ||||||
|         -ms-transform:scale(2.5); |  | ||||||
|         -o-transform:scale(2.5); |  | ||||||
|         transform:scale(2.5); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     .fit { |  | ||||||
|         width: 100%; |  | ||||||
|         height: 100%; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     .image-container{ |  | ||||||
|         width: 305px; |  | ||||||
|         height: 300px; |  | ||||||
|         background-color: #ccc; |  | ||||||
|         overflow: hidden; |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     .doc-container { |  | ||||||
|         height: 330px; |  | ||||||
|         display: inline-block; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     .doc-caption { |  | ||||||
|         display: inline-block; |  | ||||||
|     } |  | ||||||
| </style> |  | ||||||
| 
 |  | ||||||
| <div class="photos"> |  | ||||||
| 
 |  | ||||||
|     {% for doc in docs %} |  | ||||||
|     {% if doc.type == "audio" %} |  | ||||||
|     <div class="image-container"> |  | ||||||
|         <audio controls class="fit"> |  | ||||||
|             <!--<source src="files/{{doc.doc_id}}">--> |  | ||||||
|         </audio> |  | ||||||
|     </div> |  | ||||||
|     {% else %} |  | ||||||
|     <a href="/files/{{doc.doc_id}}"> |  | ||||||
|         <div class="doc-container"> |  | ||||||
|             <div class="image-container"> |  | ||||||
|                 <img class="fit" src="/thumbs/{{doc.doc_id}}"> |  | ||||||
|             </div> |  | ||||||
|             <span class="doc-caption" style="font-size: 8pt">{{doc.name}}</span> |  | ||||||
| 
 |  | ||||||
|         </div> |  | ||||||
| 
 |  | ||||||
|     </a> |  | ||||||
|     {% endif %} |  | ||||||
|     {% endfor %} |  | ||||||
| 
 |  | ||||||
| </div> |  | ||||||
| 
 |  | ||||||
| </body> |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| </html> |  | ||||||
| 
 |  | ||||||
| @ -34,11 +34,13 @@ | |||||||
|                         <label for="directories" >Search in directories</label> |                         <label for="directories" >Search in directories</label> | ||||||
| 
 | 
 | ||||||
|                         <select class="custom-select" id="directories" multiple size="6"> |                         <select class="custom-select" id="directories" multiple size="6"> | ||||||
|                             {% for dir_id in directories %} |                             {% if directories | length >  0%} | ||||||
|                                 {% if directories[dir_id].enabled %} |                                 {% for dir in directories %} | ||||||
|                                     <option selected value="{{ directories[dir_id].id }}">{{ directories[dir_id].name }}</option> |                                     <option selected value="{{ dir.id }}">{{ dir.name }}</option> | ||||||
|                                 {% endif %} |                                 {% endfor %} | ||||||
|                             {% endfor %} |                             {% else %} | ||||||
|  |                                 <option disabled>There are no active directories which you have access to</option> | ||||||
|  |                             {% endif %} | ||||||
|                         </select> |                         </select> | ||||||
|                     </div> |                     </div> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| {% extends "layout.html" %} | {% extends "layout.html" %} | ||||||
| {% set active_page = "task" %} | {% set active_page = "task" %} | ||||||
| 
 | 
 | ||||||
| {% block title %}An excellent title{% endblock title %} | {% block title %}Tasks{% endblock title %} | ||||||
| 
 | 
 | ||||||
| {% block body %} | {% block body %} | ||||||
| 
 | 
 | ||||||
| @ -49,7 +49,7 @@ | |||||||
|                     <select title="Select task type" class="form-control" id="type" name="type"> |                     <select title="Select task type" class="form-control" id="type" name="type"> | ||||||
|                         <option hidden>Create task...</option> |                         <option hidden>Create task...</option> | ||||||
|                         <option value="1">Indexing</option> |                         <option value="1">Indexing</option> | ||||||
|                         <option value="2">Thumbnail Generation</option> |                         <option value="2">Thumnail Generation</option> | ||||||
|                     </select> |                     </select> | ||||||
| 
 | 
 | ||||||
|                     <select title="Select directory" class="form-control" id="directory" name="directory" > |                     <select title="Select directory" class="form-control" id="directory" name="directory" > | ||||||
|  | |||||||
| @ -1,14 +1,18 @@ | |||||||
| {% extends "layout.html" %} | {% extends "layout.html" %} | ||||||
| 
 | 
 | ||||||
| {% set active_page = "user" %} | {% set active_page = "user" %} | ||||||
|  | {% block title %}User list{% endblock title %} | ||||||
| 
 | 
 | ||||||
| {% block body %} | {% block body %} | ||||||
| 
 | 
 | ||||||
|     <div class="container"> |     <div class="container"> <div class="card"> | ||||||
|         <div class="card"> |  | ||||||
|             <div class="card-header">Create user</div> |             <div class="card-header">Create user</div> | ||||||
|             <div class="card-body"> |             <div class="card-body"> | ||||||
| 
 | 
 | ||||||
|  |                 {% if not admin_account_present %} | ||||||
|  |                     <p>This page is unlocked because there are no admin accounts</p> | ||||||
|  |                 {% endif %} | ||||||
|  | 
 | ||||||
|                 <form method="POST" action="/user/add"> |                 <form method="POST" action="/user/add"> | ||||||
| 
 | 
 | ||||||
|                     <div class="input-group form-group"> |                     <div class="input-group form-group"> | ||||||
| @ -24,7 +28,7 @@ | |||||||
|                     <div class="form-group"> |                     <div class="form-group"> | ||||||
|                         <input type="password" class="form-control" placeholder="Password" name="password"> |                         <input type="password" class="form-control" placeholder="Password" name="password"> | ||||||
|                     </div> |                     </div> | ||||||
|                     <button type="submit" class="btn btn-success"><i class="fas fa-plus"></i> Add User</button> |                     <button type="submit" class="btn btn-success"><i class="fas fa-plus"></i> Add user</button> | ||||||
|                 </form> |                 </form> | ||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
| @ -39,12 +43,10 @@ | |||||||
|                         <th>User</th> |                         <th>User</th> | ||||||
|                         <th>Admin</th> |                         <th>Admin</th> | ||||||
|                         <th>Actions</th> |                         <th>Actions</th> | ||||||
|                     </tr> |                     </tr> </thead> | ||||||
|                     </thead> |  | ||||||
|                     <tbody> |                     <tbody> | ||||||
|                     {% for user in users %} |                     {% for user in users %} <tr> | ||||||
|                         <tr> |                             <td style="width: 80%;">{% if session["username"] == user %}<b>{{ user }}{% else %}{{ user }}{% endif %}</b></td> | ||||||
|                             <td>{{ user }}</td> |  | ||||||
|                             <td><i class="far {{ "fa-check-square" if users[user].admin else "fa-square" }}"></i></td> |                             <td><i class="far {{ "fa-check-square" if users[user].admin else "fa-square" }}"></i></td> | ||||||
|                             <td><a href="/user/{{ user }}" class="btn btn-primary">Manage</a></td> |                             <td><a href="/user/{{ user }}" class="btn btn-primary">Manage</a></td> | ||||||
|                         </tr> |                         </tr> | ||||||
|  | |||||||
| @ -1,33 +1,71 @@ | |||||||
| {% extends "layout.html" %} | {% extends "layout.html" %} | ||||||
| 
 | 
 | ||||||
|  | {% set active_page = "user" %} | ||||||
|  | {% block title %}Gestion utilisateur{% endblock title %} | ||||||
|  | 
 | ||||||
| {% block body %} | {% block body %} | ||||||
| 
 | 
 | ||||||
|  |     <div class="container"> | ||||||
|         <div class="card"> |         <div class="card"> | ||||||
|             <div class="card-header">Directory permissions</div> |             <div class="card-header">Manage permissions of <strong>{{ user.username }}</strong></div> | ||||||
|             <div class="card-body"> |             <div class="card-body"> | ||||||
| 
 | 
 | ||||||
|  |                 <div class="row"> | ||||||
|  |                     <div class="col"> | ||||||
|  |                         <h5>Admin: </h5> | ||||||
|  |                     </div> | ||||||
|  |                     <div class="col"> | ||||||
|  |                         <form action="/user/{{ user.username }}/set_admin"  style="display: inline;margin-left: 6px;"> | ||||||
|  |                             <input type="hidden" name="admin" value="{{ "0" if user.admin else "1" }}"> | ||||||
|  |                             <button class="btn btn-sm {{ "btn-danger" if  user.admin else "btn-success" }}"> | ||||||
|  |                                 <i class="far {{ "fa-check-square" if  user.admin else "fa-square" }}"></i> | ||||||
|  |                                 {{ "Remove admin" if  user.admin else "Set admin" }} | ||||||
|  |                             </button> | ||||||
|  |                             <input type="hidden" name="dir_id" value="{{ dir_id }}"> | ||||||
|  |                         </form> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  | 
 | ||||||
|  |                 <hr> | ||||||
|  | 
 | ||||||
|                 <table class="info-table table-hover table-striped"> |                 <table class="info-table table-hover table-striped"> | ||||||
|                     <thead> |                     <thead> | ||||||
|                     <tr> |                     <tr> | ||||||
|                         <th>Directory</th> |                         <th>Directory</th> | ||||||
|                         <th>Search access</th> |                         <th>Access</th> | ||||||
|                     </tr> |                     </tr> | ||||||
|                     </thead> |                     </thead> | ||||||
|                     <tbody> |                     <tbody> | ||||||
|                     {% for user in users %} |                     {% for dir_id in directories %} | ||||||
|                         <tr> |                         <tr> | ||||||
|                             <td>{{ user }}</td> |                             <td>{{ directories[dir_id].name }}</td> | ||||||
|                             <td><i class="far {{ "fa-check-square" if users[user].admin else "fa-square" }}"></i></td> |                             <td><form action="/user/{{ user.username }}/set_access"  style="display: inline;margin-left: 6px;"> | ||||||
|                             <td><a href="/user/{{ user }}" class="btn btn-primary">Manage</a></td> |                                 <input type="hidden" name="access" value="{{ "0" if dir_id in user.readable_directories else "1" }}"> | ||||||
|  |                                 <button class="btn btn-sm {{ "btn-danger" if  dir_id in user.readable_directories else "btn-success" }}"> | ||||||
|  |                                     <i class="far {{ "fa-check-square" if  dir_id in user.readable_directories else "fa-square" }}"></i> | ||||||
|  |                                     {{ "Disable" if  dir_id in user.readable_directories else "Enable" }} | ||||||
|  |                                 </button> | ||||||
|  |                                 <input type="hidden" name="dir_id" value="{{ dir_id }}"> | ||||||
|  |                             </form></td> | ||||||
|                         </tr> |                         </tr> | ||||||
|                     {% endfor %} |                     {% endfor %} | ||||||
|                     </tbody> |                     </tbody> | ||||||
| 
 |  | ||||||
|                 </table> |                 </table> | ||||||
| 
 | 
 | ||||||
|  |             <hr> | ||||||
|  |                 <button class="btn btn-danger" onclick="userDelete()">Delete account</button> | ||||||
|  | 
 | ||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|  |         <script> | ||||||
|  |         function userDelete() { | ||||||
|  |             if (confirm("Are you sure?")) { | ||||||
|  |                 window.location = "/user/{{ user.username }}/del" | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     </script> | ||||||
|  | 
 | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
| {% endblock body %} | {% endblock body %} | ||||||
| @ -12,7 +12,7 @@ class MimeGuesserTest(TestCase): | |||||||
|         guesser = ContentMimeGuesser() |         guesser = ContentMimeGuesser() | ||||||
| 
 | 
 | ||||||
|         self.assertEqual("text/x-shellscript", guesser.guess_mime(dir_name + "/test_folder/test_utf8.sh")) |         self.assertEqual("text/x-shellscript", guesser.guess_mime(dir_name + "/test_folder/test_utf8.sh")) | ||||||
|         self.assertEqual("text/plain", guesser.guess_mime(dir_name + "/test_folder/more_books.json")) |         self.assertTrue(guesser.guess_mime(dir_name + "/test_folder/more_books.json") in ["application/json", "text/plain"]) | ||||||
|         self.assertEqual("application/java-archive", guesser.guess_mime(dir_name + "/test_folder/post.jar")) |         self.assertEqual("application/java-archive", guesser.guess_mime(dir_name + "/test_folder/post.jar")) | ||||||
|         self.assertEqual("image/jpeg", guesser.guess_mime(dir_name + "/test_folder/sample_1.jpg")) |         self.assertEqual("image/jpeg", guesser.guess_mime(dir_name + "/test_folder/sample_1.jpg")) | ||||||
| 
 | 
 | ||||||
| @ -20,7 +20,8 @@ class MimeGuesserTest(TestCase): | |||||||
| 
 | 
 | ||||||
|         guesser = ExtensionMimeGuesser() |         guesser = ExtensionMimeGuesser() | ||||||
| 
 | 
 | ||||||
|         self.assertEqual("text/x-sh", guesser.guess_mime(dir_name + "/test_folder/test_utf8.sh")) |         self.assertTrue(guesser.guess_mime(dir_name + "/test_folder/test_utf8.sh") in ["text/x-sh", "application/x-sh"]) | ||||||
|         self.assertEqual("application/json", guesser.guess_mime(dir_name + "/test_folder/more_books.json")) |         self.assertEqual("application/json", guesser.guess_mime(dir_name + "/test_folder/more_books.json")) | ||||||
|         self.assertEqual("application/java-archive", guesser.guess_mime(dir_name + "/test_folder/post.jar")) |         self.assertTrue(guesser.guess_mime(dir_name + "/test_folder/post.jar") | ||||||
|  |                         in ["application/java-archive", "application/x-java-archive"]) | ||||||
|         self.assertEqual("image/jpeg", guesser.guess_mime(dir_name + "/test_folder/sample_1.jpg")) |         self.assertEqual("image/jpeg", guesser.guess_mime(dir_name + "/test_folder/sample_1.jpg")) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user