Merge pull request 'Converted to python flask' (#1) from develop into master

Reviewed-on: #1
master
itdominator 3 years ago
commit cec3ee0993

143
.gitignore vendored

@ -0,0 +1,143 @@
*.db
*.pyc
app.pid
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1019 KiB

@ -2,22 +2,17 @@
WebFM is a media and file viewer aspiring to become a full fledged file manager in the browser.
# Usage
1. Install php7, php-sqlite3, and ffmpeg on the system this will be on.
2. Use php -S 0.0.0.0:yourDesiredPort
3. Use ufw or gufw to open the port on your computer to the network.
4. Place files or start uploading some to the folders.
5. Double click thumbnails and container outlines to open files.
6. Double click the text name to change the file's or folder's name and press enter to set it.
7. Right-click to get context menu options.
8. Place an image such as a jpg, png, or gif labeled "000.itsExtension" in a directory then the viewer will use it as the background image for that folder/directory.
9. Password protect folder based on resources/php/config.php file setting.
10. Save paths to favorites list for quick access.
1. Install python, sqlite3, and ffmpeg on the system this will be on.
3. Use ufw or gufw to open the port on your computer to the local network.
4. Use hosts file (or other methods) to redirect webfm.com and ssoapps.com to local app.
5. Update client_secrets.json > 'client_secret' field with your Keycloak key. (Current one is local to me and not public)
6. Place files or start uploading some to the folders.
7. Place an image such as a jpg, png, or gif labeled "000.itsExtension" in a directory and the viewer will use it as the background image for that folder/directory.
7. Password protect folder based on core/utils/shellfm/windows/Settings.py file settings.
8. Save paths to favorites list for quick access.
Notes:
1. The provided folders except "resources" are optional. You can add and remove them as you please.
2. The media and image pane can be moved by dragging from the transparentish bar that has the close button and other controls.
3. Edit the resources/php/config.php file and put your own programs there.
4. Edit your php.ini file "upload_max_filesize" and "post_max_size" to be higher to upload larger files.
n/a
# TO-DO
1. Allow for move and copy.
@ -25,9 +20,8 @@ Notes:
# Images
![1 Home](Images/pic1.png)
![2 Images Listed](Images/pic2.png)
![3 Videos Listed](Images/pic3.png)
![4 Image Open](Images/pic4.png)
![5 Image Open And Video Playing](Images/pic5.png)
![6 Alternate Background](Images/pic6.png)
![1 Videos List](images/pic1.png)
![2 Video Playing](images/pic2.png)
![3 Images List](images/pic3.png)
![4 Context menu](images/pic4.png)
![5 Settings Pane With Upload And Create Functionality](images/pic5.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

@ -0,0 +1,40 @@
#!/bin/bash
. CONFIG.sh
# set -o xtrace ## To debug scripts
# set -o errexit ## To exit on error
# set -o errunset ## To exit if a variable is referenced but not set
function main() {
rm -rf venv/
clear
python -m venv venv/
sleep 2
source "./venv/bin/activate"
ANSR="-1"
while [[ $ANSR != "0" ]] && [[ $ANSR != "1" ]] && [[ $ANSR != "2" ]]; do
clear
menu_mesage
read -p "--> : " ANSR
done
case $ANSR in
"1" ) pip install -r linux-requirements.txt;;
"2" ) pip install -r windows-requirements.txt;;
"0" ) exit;;
* ) echo "Don't know how you got here but that's a bad sign...";;
esac
}
function menu_mesage() {
echo "NOTE: Make sure to have Python 3 installed!"
echo -e "\nWhat do you want to do?"
echo -e "\t1) Generate Linux/Mac supported venv. (Installs Repuirements)"
echo -e "\t2) Generate Windows supported venv. (Installs Repuirements)"
echo -e "\t0) EXIT"
}
main $@;

@ -1 +0,0 @@
LOL...Not really!

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

@ -1,120 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta content="" charset="utf-8"/>
<title>Web File Manager</title>
<link type="text/css" rel="stylesheet" href="resources/css/base.css"/>
<link type="text/css" rel="stylesheet" href="resources/css/main.css"/>
<link rel="shortcut icon" type="image/png" href="favicon.png"/>
</head>
<body onload="onloadSetBG(); getDir('/')" contextmenu="menu">
<!-- Background -->
<img id="bg" />
<video id="video" src="" controls >
</video>
<!-- Controls -->
<!-- Create the menu -->
<menu type="context" id="menu">
<menuitem label="Home Directory" onclick="clearDirCookie()"></menuitem>
<menuitem label="Show Server Messages" onclick="tgglElmView('serverMsgView')"></menuitem>
<menuitem label="Clear Upload List" onclick="clearDlList()"></menuitem>
<menuitem label="Download" onclick="downloadItem()"></menuitem>
<menuitem label="Delete" onclick="deleteItem()"></menuitem>
</menu>
<!-- Uploader -->
<h2 id="controls">
<button type="button" title="Other Options" onclick="tgglElmView('popOutControls')">&#9881;</button>
<button type="button" title="Refresh" onclick="getDir('./')">&#8635;</button>
<button type="button" title="Back" onclick="getDir('../')">&lArr;</button>
<input type="text" placeholder="Search..." onkeyup="searchPage(this)" name="" value="">
<button type="button" onclick="clearSearch()" title="Clears search..." >Clear Search</button>
<button onclick="getFavesList(); tgglElmView('favesList')">Faves List &#8597;</button>
<button type="button" onclick="lockFolders()" title="Lock unlocked folders..." >Lock Unlocked Folders</button>
<br/>
<button type="button" id="faves" onclick="faveManager(this)" title="Add/Delete from favorites..." >&#9734;</button>
Path:<span id="path"></span>
</h2>
<div id="popOutControls" style="display:none;">
<center>
<form>
<input class="ulFile" type="file" title="files To Upload" name="filesToUpload[]" data-multiple-caption="{count} files selected" multiple />
<input type="button" onclick="uploadFiles()" name="UploadFiles" title="Upload File(s)" value="Upload File(s)" />
<input type="reset" title="Clear" id="CLEARBTTN" value="Clear" style="display:none;">
<input type="text" id="DIRPATHUL" name="DIRPATHUL" value="">
</form>
<br/>
<input type="text" id="NewItem" value=""/>
<input type="button" value="New Dir" onclick="createItem('dir')"/>
<input type="button" value="New File" onclick="createItem('file')"/>
<input type="button" value="Show Server Messages" onclick="tgglElmView('serverMsgView')"/>
<br/>
<input id="MergeType" type="checkbox" onchange="getDir('./')" />
<label for="MergeType">Show seassons in same list.</label>
</center>
</div>
<!-- Dynamic content targets -->
<ul id="favesList" style="display: none;"> </ul>
<ul id="dynUl"></ul>
<!-- Uploader processor -->
<div id="serverMsgView" style="display:none;"> </div>
<!-- Templates -->
<template id="dirTemplate">
<li class="dirStyle" tabindex="1">
<img id="dirID" class="systemIcon" src="" />
<input id="titleID" class="dirTitle" type="text" value="" readonly="true" />
</li>
</template>
<template id="vidTemplate">
<li id="movieID" class="movieStyle" tabindex="1" style="background-image: url('')">
<span class="popOutBttnInner" title="Open In Local Program">&#8765;</span>
<input id="titleID" class="movieTitle" type="text" value="" readonly="true" />
</li>
</template>
<template id="imgTemplate">
<img id="imageID" class="iconImg" src="" alt="">
</template>
<template id="filTemplate">
<li class="fileStyle">
<img id="fileID" class="systemIcon" src="" />
<input id="titleID" class="fileTitle" type="text" value="" readonly="true" />
</li>
</template>
<script type="text/javascript">
if (window.self !== window.top) {
setTimeout(function () {
let elm = document.getElementById("bg");
elm.parentElement.removeChild(elm);
// Stylesheet for iframe views
var link = document.createElement("link");
link.href = "resources/css/iframe.css";
link.type = "text/css";
link.rel = "stylesheet";
document.getElementsByTagName("head")[0].appendChild(link);
}, 500);
}
</script>
<script type="text/javascript" src="resources/js/favorites.js" charset="utf-8"></script>
<script type="text/javascript" src="resources/js/passwordFieldInsert.js" charset="utf-8"></script>
<script type="text/javascript" src="resources/js/cookieHandler.js" charset="utf-8"></script>
<script type="text/javascript" src="resources/js/jsonParser.js" charset="utf-8"></script>
<script type="text/javascript" src="resources/js/ajax.js" charset="utf-8"></script>
<script type="text/javascript" src="resources/js/uiActions.js" charset="utf-8"></script>
<script type="text/javascript" src="resources/js/filesystemActions.js" charset="utf-8"></script>
<script type="text/javascript" src="resources/js/uiEvents.js" charset="utf-8"></script>
</body>
</html>

@ -0,0 +1,25 @@
click==7.1.2
dnspython==2.1.0
eventlet==0.30.1
email-validator==1.1.2
Flask==1.1.2
Flask-Login==0.5.0
flask-oidc==1.4.0
Flask-Bcrypt==0.7.1
Flask-SQLAlchemy==2.4.4
Flask-WTF==0.14.3
greenlet==1.0.0
gunicorn==20.0.4
httplib2==0.19.0
itsdangerous==1.1.0
Jinja2==2.11.3
MarkupSafe==1.1.1
oauth2client==4.1.3
pyasn1==0.4.8
pyasn1-modules==0.2.8
pyparsing==2.4.7
rsa==4.7
six==1.15.0
SQLAlchemy==1.3.23
Werkzeug==1.0.1
WTForms==2.3.3

@ -1,8 +0,0 @@
html {
margin: 0em;
padding: 0em;
}
ol, ul, li {
list-style: none;
}

@ -1,4 +0,0 @@
#controls, #fullPathHeader, #dynDiv,
.errorStyling, .dirStyle, .movieStyle, .fileStyle {
background-color: rgba(0,0,0,0.2);
}

@ -1,304 +0,0 @@
/* IDs */
#DIRPATHUL {
display: none;
width: 1px;
height: 1px;
}
#video,
#bg {
position: fixed;
top: 0%;
left: 0%;
width: 100%;
height: 100%;
z-index: -999;
}
#video,
#bg img {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
z-index: -999;
}
#video {
position: fixed;
display: none;
background-color: rgba(0, 0, 0, 1);
}
#controls, #dynUl,
.errorStyling, .dirStyle, .movieStyle, .fileStyle {
display: block;
width: 100%;
height: auto;
overflow: auto;
padding-bottom: 0.5em;
color: #ffffff;
text-align: center;
font-size: 1.2em;
background-color: rgba(0,0,0,0.64);
}
#favesList {
border-style: solid;
border-color: rgba(0, 0, 0, 0.5);
border-width: 0.2em;
background-color: rgba(7, 150, 159, 0.8);
position: fixed;
font-size: 2em;
overflow-x: auto;
overflow-y: scroll;
padding: 1.5em;
max-height: 632px;
color: #ffffff;
z-index: 888;
}
#favesList > li:hover {
cursor: pointer;
background-color: rgba(92, 199, 35, 0.8);
padding-left: 1em;
padding-right: 1em;
}
#controls {
display: block;
position: fixed;
z-index: 999;
top: 0em;
}
#dynUl {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(18em, 1fr));
grid-column-gap: 1em;
grid-row-gap: 1em;
margin: 5em auto;
width: 85%;
padding: 2em;
}
#imgView, #imgArea, #fileView {
width: 800px;
height: 600px;
}
#imgView, #fileView {
position: fixed;
bottom: 0em;
z-index: 100;
border-style: solid;
border-color: rgb(114,184,199);
}
#fileView {
display: block;
overflow: auto;
}
#fileViewInner {
position: sticky;
display: inline;
width: 100%;
height: 500px;
}
#imgArea {
width: 800px;
height: 600px;
overflow-y: scroll;
}
#imgView {
overflow: hidden;
left: 15em;
}
#NewItem {
background-color: #ffffff;
color: #000000;
text-align: center;
}
#popOutControls {
position: fixed;
top: 15%;
width: 99%;
height: 15em;
padding-top: 6em;
opacity: 0.94;
background: radial-gradient(circle,#3f3f3f,#000000);
color: #ffffff;
text-align: center;
z-index: 999;
}
#serverMsgView {
position: fixed;
bottom: 0em;
height: 5em;
overflow-y: scroll;
width: 100%;
background-color: rgba(0,0,0,0.64);
z-index: 999;
}
#searchField {
text-align: center;
}
#searchField:focus {
height: 2em;
border-style: solid;
border-width: thin;
border-color: rgba(55, 204, 209, 1);
}
/* Classes */
.imgViewImg {
width: inherit;
height: auto;
}
.dirStyle { background-color: rgba(0, 0, 0, 0.56); }
.movieStyle, .fileStyle { background-color: rgba(101, 101, 101, 0.56); }
.movieStyle {
min-height: 6.5em;
overflow: hidden;
background-repeat: no-repeat;
background-size: 100% 100%;
}
.videoInputField {
width: 100%;
margin-top: 5.5em;
background-color: rgba(0, 0, 0, 0.64);
color: rgb(255, 255, 255);
text-align: center;
border-top: 1px solid rgb(255, 255, 255);
border-bottom: 1px solid rgb(255, 255, 255);
text-overflow: ellipsis;
}
.dirStyle:hover, .movieStyle:hover, .fileStyle:hover {
background-color: rgba(0, 141, 166, 0.56);
cursor: pointer;
box-shadow: 0px 0px 15px rgb(114,184,199);
border-radius: 0.5em;
}
.dirStyle:focus, .movieStyle:focus, .fileStyle:focus {
background-color: rgba(0, 139, 35, 0.76);
cursor: pointer;
box-shadow: 0px 0px 25px rgb(114, 199, 120);
border-radius: 0.5em;
}
.dirTitle, .fileTitle, .movieTitle {
white-space: nowrap;
text-overflow: ellipsis;
text-align: center;
overflow: hidden;
border-style: none;
font-size: 75%;
}
.dirTitle, .fileTitle {
width: auto;
background-color: #00000000;
color: #ffffff;
}
.movieTitle {
width: 18em;
background-color: #ffffff00;
color: #ffffff;
}
.thumbnail {
width: 12em;
height: 6.5em;
}
.systemIcon {
width: 2em;
height: auto;
}
.iconImg {
width: 18em;
height: 12em;
margin: 1em;
}
.popOutBttn, .closeBttn {
float: right;
z-index: 2;
width: 4em;
height: 4em;
text-align: center;
vertical-align: middle;
line-height: 4em; /* the same as your div height */
background-color: rgba(0,0,0, 0.85);
color: rgb(255,255,255);
border-style:solid;
border-color: rgb(255,255,255);
}
.popOutBttnInner {
float: right;
z-index: 2;
width: 2em;
height: 2em;
text-align: center;
background-color: rgba(0,0,0, 0.85);
color: rgb(255,255,255);
border-style:solid;
border-color: rgb(255,255,255);
}
.completionBar {
float:left;
clear:left;
height: 0.1em;
background-color: rgba(25, 125, 10, 1.0);
}
/* Hover events */
.dirTitle:hover,
.iconImg:hover,
.closeBttn:hover,
.popOutBttnInner:hover,
.popOutBttn:hover {
cursor: pointer;
}
.popOutBttnInner:hover,
.popOutBttn:hover,
.closeBttn:hover {
background-color: rgba(255,255,255, 0.85);
color: #000000;
border-color: #000000;
}
/* Messages coloring */
.error, .warnning, .success {
float: left;
clear: both;
}
.error { color: rgb(255, 0, 0); }
.warning { color: rgb(255, 168, 0); }
.success { color: rgb(136, 204, 39); }

Binary file not shown.

@ -1,54 +0,0 @@
// SSE events if supported
if(typeof(EventSource) !== "undefined") {
let source = new EventSource("resources/php/sse.php");
source.onmessage = (event) => {
if (event.data === "updateListing") {
getDir("./");
}
};
} else {
console.log("SSE Not Supported In Browser...");
}
const getFavesList = () => {
doAjax("resources/php/dbController.php", "getTabs=true");
}
const doAjax = async (actionPath, data) => {
let xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
// Send the returned data to further process
if (this.responseText != null) {
handleJSONReturnData(JSON.parse(this.responseText));
} else {
document.getElementById('dynUl').innerHTML =
"<p class=\"error\" style=\"width:100%;text-align:center;\"> "
+ "No content returned. Check the folder path.</p>";
}
}
};
xhttp.open("POST", actionPath, true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.overrideMimeType('application/json'); // Force return to be JSON
xhttp.send(data);
}
const fileUploader = (data) => {
let xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
// Send the returned data to further process
if (this.responseXML != null) {
handleXMLReturnData(this.responseXML);
}
}
};
xhttp.open("POST", "resources/php/filesystemActions.php", true);
xhttp.overrideMimeType('application/xml'); // Force return to be XML
xhttp.send(data);
}

@ -1,15 +0,0 @@
const getCookie = (cname) => {
let decodedCookie = decodeURIComponent(document.cookie);
let name = cname + "=";
let ca = decodedCookie.split(';');
for(let i = 0; i <ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}

@ -1,34 +0,0 @@
const faveManager = (elm) => {
let path = document.getElementById("path").innerHTML;
let data = "";
if (elm.style.backgroundColor != "") {
elm.style.backgroundColor = "";
elm.style.color = "";
data = "deleteLink=true";
} else {
elm.style.backgroundColor = "rgb(255, 255, 255)";
elm.style.color = "rgb(0, 0, 0)";
data = "deleteLink=false";
}
data += "&linkPath=" + path;
doAjax("resources/php/dbController.php", data);
}
// Basically resetting path nodes and setting them up
// to the new path and just doing a refresh
const loadFave = (elm) => {
let path = elm.getAttribute("name");
let parts = path.split("/");
let size = parts.length;
pathNodes = [];
pathNodes.push(parts[0] + "/");
for (let i = 1; i < size - 1; i++) {
pathNodes.push(parts[i] + "/");
}
pathNodes.push(parts[size - 1]);
getDir("./");
}

@ -1,132 +0,0 @@
let binary = null;
let pathNodes = [];
const lockFolders = () => {
const data = "lockFolders=true";
doAjax("resources/php/lockedFolders.php", data);
getDir("./");
}
const getDir = (query) => {
document.getElementById("controls").style.opacity = "1";
document.getElementById("dynUl").style.display = "grid";
document.getElementById("video").src = "#";
document.getElementById("video").style.display = "none";
let formUlPth = document.getElementById("DIRPATHUL");
let mergeType = document.getElementById("MergeType");
let passwd = undefined;
let data = "";
let cookies = "";
let dirCookie = "";
// push or pop to path list
if (query === "/") {
// Process path from cookie and set to array/list
dirCookie = getCookie("dirQuery");
if (dirCookie != "" && dirCookie != "./") {
dirCookie = dirCookie.split("/");
dirCookie.pop(); // account for ending empty slot
let size = dirCookie.length;
for (var i = 0; i < size; i++) {
pathNodes.push(dirCookie[i] + "/");
}
} else {
pathNodes = [];
pathNodes.push("." + query);
}
} else if (query === "../") {
// Only remove while not in root
if (pathNodes.length > 1) {
pathNodes.pop();
}
} else if (query === "./") {
// Do nothing since re-scanning dir
} else {
pathNodes.push(query); // Add path
}
// Create path from array of items
for (pathNode of pathNodes) { data += pathNode; }
try {
passwd = document.getElementById("PASSWD").value;
} catch (e) {
passwd = "";
}
// Setup upload path for form and make a cookie for persistence during browser session....
formUlPth.value = data;
data = "dirQuery=" + encodeURIComponent(data);
document.cookie = data + "; expires=Sun, 31 Dec 2034 12:00:00 UTC";
data +="&mergeType=" + mergeType.checked
+ "&passwd=" + passwd;
doAjax("resources/php/getDirList.php", data);
}
const uploadFiles = async () => {
let toUpload = document.getElementsByName("filesToUpload[]")[0];
let path = document.getElementById("path").innerHTML;
let reader = new FileReader();
let data = new FormData();
let size = toUpload.files.length;
data.append("UploadFiles", "trut");
data.append("DIRPATHUL", path);
// Add files
if (size > 0) {
for (let i = 0; i < size; i++) {
data.append("filesToUpload[]", toUpload.files[i]);
}
fileUploader(data);
}
}
const createItem = (type) => {
let path = document.getElementById("path").innerHTML;
let newItem = document.getElementById("NewItem");
let fullPth = path + newItem.value;
newItem.value = "";
fullPth = encodeURIComponent(fullPth);
doAjax("resources/php/filesystemActions.php",
"createItem=true&item=" + fullPth + "&type=" + type);
}
const deleteItem = () => {
let path = document.getElementById("path").innerHTML;
// Clicked yes to delete and there is an item
if (itemObj != undefined && itemObj != null) {
let fullPth = path + itemObj;
fullPth = encodeURIComponent(fullPth);
let answer = confirm("Are you sure you want to delete: " + fullPth);
if (answer == true) {
doAjax("resources/php/filesystemActions.php",
"deleteItem=true&item=" + fullPth);
console.log("Deleted: " + fullPth);
itemObj = null;
}
}
}
const renameItem = (obj) => {
let path = encodeURIComponent(document.getElementById("path").innerHTML);
let oldName = encodeURIComponent(formerFileName);
let newName = encodeURIComponent(obj.value);
let formData = "renameItem=true&oldName=" + oldName + "&newName=" + newName + "&path=" + path;
console.log("Old name: " + oldName);
console.log("New name: " + newName);
doAjax("resources/php/filesystemActions.php",
formData);
}
const openInLocalProg = (media) => {
doAjax("resources/php/filesystemActions.php",
"media=" + media);
}

@ -1,167 +0,0 @@
const insertArea = document.getElementById('dynUl');
const handleJSONReturnData = (data) => {
if (data.message) {
if (data.message.type == "locked") {
createPassField();
} else {
const text = document.createTextNode(data.message.text)
document.getElementById("serverMsgView").appendChild(text);
}
return ;
}
if (data.list) {
updateHTMLDirList(data);
} else if (data.FAVES_LIST) {
generateFavesList(data.FAVES_LIST);
}
}
const generateFavesList = (data) => {
let listView = document.getElementById("favesList");
listView.innerHTML = "";
data.forEach(fave => {
let liTag = document.createElement("LI");
let parts = (fave.includes("/")) ? fave.split("/") : fave.split("\\");
let txtNode = document.createTextNode(parts[parts.length - 2]);
liTag.setAttribute("name", fave);
liTag.setAttribute("title", fave);
liTag.setAttribute("onclick", "loadFave(this)");
liTag.appendChild(txtNode);
listView.appendChild(liTag);
});
}
const updateHTMLDirList = async (data) => {
var dirTemplate = document.querySelector('#dirTemplate');
var vidTemplate = document.querySelector('#vidTemplate');
var imgTemplate = document.querySelector('#imgTemplate');
var filTemplate = document.querySelector('#filTemplate');
let dirPath = data.PATH_HEAD;
let isInFaves = data.IN_FAVE;
let dirs = (data.list.dirs) ? data.list.dirs : [];
let videos = (data.list.vids) ? data.list.vids : [];
let images = (data.list.imgs) ? data.list.imgs : [];
let files = (data.list.files) ? data.list.files : [];
let i = 0;
let size = 0;
document.getElementById("path").innerHTML = dirPath;
insertArea.innerHTML = "";
// Setup background if there is a 000.* in selection
let bgImgPth = images[0] ? images[0].image : "";
if (bgImgPth.match(/000\.(jpg|png|gif)\b/) != null) {
updateBG(dirPath + bgImgPth);
} else {
updateBG("resources/images/backgrounds/000.jpg");
}
// determin whether to style faves or not
let elm = document.getElementById("faves");
if (isInFaves == "true") {
elm.style.backgroundColor = "rgb(255, 255, 255)";
elm.style.color = "rgb(0, 0, 0)";
} else {
elm.style.backgroundColor = "";
elm.style.color = "";
}
// Insert dirs
let dirClone = document.importNode(dirTemplate.content, true);
let dirImg = "resources/images/icons/folder.png";
let dir = null;
size = dirs.length;
for (; i < size; i++) {
dir = dirs[i].dir;
const clone = dirClone.cloneNode(true);
createElmBlock(clone, dirImg, dir);
}
// Insert videos
let vidClone = document.importNode(vidTemplate.content, true);
let thumbnail = "";
let title = "";
size = videos.length;
for (i = 0; i < size; i++) {
title = videos[i].video.title;
thumbnail = videos[i].video.thumbnail;
const clone = vidClone.cloneNode(true);
createElmBlock(clone, thumbnail, title, true, dirPath);
}
// Insert images
let imgClone = document.importNode(imgTemplate.content, true);
thumbnail = "";
size = images.length;
for (i = 0; i < size; i++) {
thumbnail = images[i].image;
if (thumbnail.match(/000\.(jpg|png|gif)\b/) == null &&
!thumbnail.includes("favicon.png")) {
const clone = imgClone.cloneNode(true);
let imgTag = clone.firstElementChild;
imgTag.src = dirPath + '/' + thumbnail;
imgTag.alt = thumbnail;
insertArea.appendChild(clone);
}
}
// Insert files
let fileClone = document.importNode(filTemplate.content, true);
size = files.length;
for (i = 0; i < size; i++) {
const clone = fileClone.cloneNode(true);
let fileName = files[i].file;
createElmBlock(clone, setFileIconType(fileName), fileName);
}
}
const createElmBlock = (elm, imgSrc, fileName, isVideo = null, path = null) => {
contnrTag = elm.firstElementChild;
let imgTag = null;
let inputTag = elm.querySelector("input");
if (isVideo) {
contnrTag.style = "background-image: url('/resources/images/thumbnails/" + imgSrc + "')";
inputTag.className = "videoInputField";
let fullMedia = path + fileName;
elm.querySelector("span").addEventListener("click", function (eve) {
openInLocalProg(fullMedia);
});
} else {
imgTag = elm.querySelector("img");
imgTag.src = imgSrc;
imgTag.alt = fileName;
}
contnrTag.title = fileName;
inputTag.value = fileName;
inputTag.addEventListener("focusout", function (eve) {
disableEdits(eve.target);
});
insertArea.appendChild(elm);
}
const setFileIconType = (fileName) => {
if (fileName.match(/\.(doc|docx|xls|xlsx|rtf)\b/) != null) {
return "resources/images/icons/doc.png";
} else if (fileName.match(/\.(7z|7zip|zip|tar.gz|tar.xz|gz|rar|jar)\b/) != null) {
return "resources/images/icons/arc.png";
} else if (fileName.match(/\.(pdf)\b/) != null) {
return "resources/images/icons/pdf.png";
} else if (fileName.match(/\.(html)\b/) != null) {
return "resources/images/icons/html.png";
} else if (fileName.match(/\.(txt|conf)\b/) != null) {
return "resources/images/icons/text.png";
} else if (fileName.match(/\.(iso|img)\b/) != null) {
return "resources/images/icons/img.png";
} else if (fileName.match(/\.(sh|batch|exe)\b/) != null) {
return "resources/images/icons/scrip.png";
} else {
return "resources/images/icons/bin.png";
}
}

@ -1,22 +0,0 @@
const createPassField = () => {
let passField = document.createElement("INPUT");
let submitBttn = document.createElement("BUTTON");
passField.id = "PASSWD";
passField.type = "password";
passField.placeholder = "Password...";
submitBttn.innerHTML = "Submit";
insertArea.innerHTML = "";
passField.onkeyup = (eve) => {
if (eve.key == "Enter") {
getDir("./");
}
};
submitBttn.onclick = () => {
getDir("./");
};
insertArea.appendChild(passField);
insertArea.appendChild(submitBttn);
}

@ -1,189 +0,0 @@
let formerFileName = "";
const tgglElmView = (id) => {
let elm = document.getElementById(id);
if (elm.style.display == "none") {
elm.style.display = "block";
} else {
elm.style.display = "none";
}
}
const searchPage = (elm) => {
let query = elm.value.toLowerCase();
let list = document.getElementById("dynUl").querySelectorAll("[title]");
let size = list.length;
for (var i = 0; i < size; i++) {
if (!list[i].title.toLowerCase().includes(query)) {
list[i].style.display = "none";
} else {
list[i].style.display = "";
}
}
}
const clearSearch = () => {
let list = document.getElementById("dynUl").querySelectorAll("[title]");
let size = list.length;
for (var i = 0; i < size; i++) {
list[i].style.display = "";
}
}
const enableEdit = (obj) => {
obj.style.backgroundColor = "#ffffffff";
obj.style.color = '#000000ff';
obj.readOnly = '';
formerFileName = obj.value;
}
const disableEdits = (elm) => {
elm.style.backgroundColor = "";
elm.style.color = '';
elm.value = formerFileName;
elm.readOnly = "true";
}
const showMedia = async (mediaLoc, type) => {
let path = document.getElementById("path").innerHTML;
let tempRef = mediaLoc.toLowerCase();
let fullMedia = path + mediaLoc;
if (type === "video") {
setupVideo(type, fullMedia, tempRef);
} else {
createFloatingPane(type, fullMedia);
}
}
const setupVideo = async (type, fullMedia, tempRef) => {
try {
let video = document.getElementById("video");
video.autoplay = true;
video.poster = "resources/images/loading.gif";
if ((/\.(mkv|avi|flv|mov|m4v|mpg|wmv|mpeg|mp4|mp3|webm|flac|ogg|pdf)$/i).test(tempRef)) {
if ((/\.(mkv|avi|wmv)$/i).test(tempRef)) {
const params = "remuxVideo=true&mediaPth=" + fullMedia;
let response = await fetch("resources/php/filesystemActions.php",
{method: "POST", body: new URLSearchParams(params)});
let xml = new window.DOMParser().parseFromString(await response.text(), "text/xml");
if (xml.getElementsByTagName("REMUX_PATH")[0]) {
fullMedia = xml.getElementsByTagName("REMUX_PATH")[0].innerHTML;
} else {
return ;
}
} else if ((/\.(avi|flv|mov|m4v|mpg|wmv)$/i).test(tempRef)) {
openInLocalProg(fullMedia);
return ;
}
}
// This is questionable in usage since it loads the full video
// before showing; but, seeking doesn't work otherwise...
let response = await fetch(fullMedia, {method: "GET"});
var vidSrc = URL.createObjectURL(await response.blob()); // IE10+
video.src = vidSrc;
document.getElementById("controls").style.opacity = "0";
document.getElementById("video").style.display = "block";
document.getElementById("dynUl").style.display = "none";
} catch (e) {
document.getElementById("controls").style.opacity = "1";
document.getElementById("dynUl").style.display = "grid";
document.getElementById("video").src = "#";
document.getElementById("video").style.display = "none";
console.log(e);
}
}
const createFloatingPane = (type, fullMedia) => {
let iframe = document.createElement("IFRAME");
let outterDiv = document.createElement("DIV");
let popOutDiv = document.createElement("DIV");
let closeDiv = document.createElement("DIV");
let toLocDiv = document.createElement("DIV");
let imgDiv = document.createElement("DIV");
let aTag = document.createElement("A");
let imgTag = document.createElement("IMG");
let closeText = document.createTextNode("X");
closeDiv.className = "closeBttn";
closeDiv.title = "Close";
closeDiv.setAttribute("onclick", "closeContainer(this)");
closeDiv.appendChild(closeText);
aTag.title = "New Tab";
aTag.target = "_blank";
aTag.href = fullMedia;
popOutDiv.className = "popOutBttn";
popOutDiv.innerHTML = "&#8599;";
aTag.appendChild(popOutDiv);
toLocDiv.title = "Open In Local Program";
toLocDiv.className = "popOutBttn";
toLocDiv.innerHTML = "&#8765;";
toLocDiv.setAttribute("onclick", "openInLocalProg('" + fullMedia + "')");
imgDiv.id = "imgArea";
imgTag.className = "imgViewImg";
imgTag.src = fullMedia;
imgDiv.appendChild(imgTag);
iframe.id = "fileViewInner";
iframe.src = fullMedia;
outterDiv.appendChild(closeDiv);
outterDiv.appendChild(aTag);
outterDiv.appendChild(toLocDiv);
if (type === "image") {
outterDiv.id = "imgView";
outterDiv.appendChild(imgDiv);
} else {
outterDiv.id = "fileView";
outterDiv.appendChild(iframe);
}
document.body.appendChild(outterDiv);
dragContainer(outterDiv); // Set for dragging events
}
const closeContainer = (elm) => {
elm.parentElement.parentElement.removeChild(elm.parentElement);
}
const clearDirCookie = () => {
let expireDate = "Thu, 01 Jan 1970 00:00:00 UTC";
document.cookie = "dirQuery=; expires=" + expireDate;
getDir("/");
}
const downloadItem = () => {
let partialPath = document.getElementById("path").innerHTML;
let brTag = document.createElement("BR");
let aTag = document.createElement("A");
let text = document.createTextNode(itemObj);
let fullPath = partialPath + itemObj;
aTag.setAttribute("href", fullPath);
aTag.setAttribute("target", "_blank");
aTag.setAttribute("id", itemObj);
aTag.append(text);
document.getElementById("serverMsgView").append(aTag, brTag);
aTag.click();
}
const clearDlList = () => { document.getElementById("CLEARBTTN").click(); }
const onloadSetBG = () => { updateBG("resources/images/backgrounds/000.jpg"); }
const updateBG = (bgImg) => {
try {
document.getElementById("bg").src = bgImg;
} catch (e) { }
}

@ -1,155 +0,0 @@
let itemObj = undefined;
let interval = undefined;
let cursorX;
let cursorY;
document.getElementById("controls").onmouseover = (eve) => {
let source = document.getElementById("video").src;
let target = eve.target
if (interval)
clearInterval(interval);
if (source !== "#") {
eve.target.style.opacity = "1";
document.getElementById("dynUl").style.display = "grid";
}
}
document.getElementById("video").onmouseover = (eve) => {
interval = setInterval(function () {
elementMouseIsOver = document.elementFromPoint(cursorX, cursorY);
if (elementMouseIsOver.tagName == "BODY" ||
elementMouseIsOver.id == "video") {
let controls = document.getElementById("controls");
controls.style.opacity = "0";
document.getElementById("dynUl").style.display = "none";
clearInterval(interval);
}
}, 2500);
}
// For context menu to have element
document.onclick = (event) => {
let obj = event.target;
let callingID = obj.id;
let classNM = obj.className;
// right-click detect
if (event.which == 3) {
if (callingID == "imageID") {
setSelectedItem(obj.alt);
} else if (callingID == "dirID" || callingID == "fileID" ||
callingID == "movieID") {
let node = obj.parentNode;
setSelectedItem(node.children[1].value);
} else if (classNM == "fileStyle" || classNM == "dirStyle" ||
classNM == "movieStyle") {
setSelectedItem(obj.children[1].value);
}
}
}
// Actions for content
document.ondblclick = (event) => {
let obj = event.target;
let callingID = obj.id;
let classNM = obj.className;
// Left click detect
if (event.which == 1) {
// If clicking on container
if (classNM === "fileStyle" || classNM === "movieStyle" ||
classNM === "dirStyle") {
if (classNM === "dirStyle") {
getDir(obj.children[1].value);
} else if (classNM === "movieStyle") {
showMedia(obj.title, "video");
} else {
showMedia(obj.children[1].value, "file");
}
} else if (callingID === "dirID") { // If clicking on dir icon
let node = obj.parentNode;
getDir(node.children[1].value);
} else if (callingID === "movieID") { // If clicking on movie thumbnail
let node = obj.parentNode;
showMedia(node.children[1].value, "video");
} else if (callingID === "imageID") { // If clicking on image
showMedia(obj.alt, "image");
} else if (callingID === "titleID") { // If clicking on text title
enableEdit(obj);
}
}
}
// Mainly for rename event
document.onkeydown = (event) => {
let obj = event.target;
let callingID = event.target.id;
let keyCodeVal = event.keyCode;
// If keycode == Enter
if (keyCodeVal == 13) {
if (callingID == "titleID") {
renameItem(obj);
}
}
}
const setSelectedItem = (item) => { itemObj = item; }
// Drage event for the poped out image and media container
const dragContainer = (elmnt) => {
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
elmnt.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e = e || window.event;
pauseEvent(e);
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
pauseEvent(e);
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement(e) {
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
}
function pauseEvent(e) {
if(e.stopPropagation) e.stopPropagation();
if(e.preventDefault) e.preventDefault();
e.cancelBubble=true;
e.returnValue=false;
return false;
}
}
// Mouse position detection for control show/hide setup
document.onmousemove = function(e){
cursorX = e.pageX;
cursorY = e.pageY;
}
setInterval(checkCursor, 2000);
function checkCursor() {
return "";
}

@ -1,15 +0,0 @@
<?php
$MEDIAPLAYER = "mpv ";
$MPLAYER_WH = " -xy 1600 -geometry 50%:50% ";
$MUSICPLAYER = "/opt/deadbeef/bin/deadbeef";
$IMGVIEWER = "mirage";
$OFFICEPROG = "libreoffice";
$PDFVIEWER = "evince";
$TEXTVIEWER = "leafpad";
$FILEMANAGER = "spacefm";
$LOCKPASSWORD = "1234";
$TMPFOLDERSIZE = 8000; // tmp folder size check for cleanup if above 8GB used.
// NOTE: Split folders with ::::
$LOCKEDFOLDERS = "./dirLockCheck/";
?>

@ -1,10 +0,0 @@
<?php
include_once 'serverMessenger.php';
chdir("../../");
$db = new SQLite3('resources/db/webfm.db');
if($db === false){
$message = "Server: [Error] --> Database connection failed!";
serverMessage("error", $message);
}
?>

@ -1,56 +0,0 @@
<?php
include_once 'connection.php';
include_once 'serverMessenger.php';
function getTabLinks() {
GLOBAL $db;
$res = $db->query('Select * FROM faves');
$GeneratedJSON = array('FAVES_LIST' => array());
while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
$GeneratedJSON['FAVES_LIST'][] = $row['link'];
}
echo json_encode($GeneratedJSON);
}
function manageLink($ACTION, $PATH) {
GLOBAL $db;
$ACTION_TYPE = "";
// If action isn't true then we add else we delete or exit.
if ($ACTION == "false") {
$stmt = $db->prepare('INSERT INTO faves VALUES(:link)');
$ACTION_TYPE = "added to";
} elseif ($ACTION == "true") {
$stmt = $db->prepare('DELETE FROM faves WHERE link = :link');
$ACTION_TYPE = "deleted from";
} else {
$message = "Server: [Error] --> Action for adding or deleting isn't set properly!";
serverMessage("error", $message);
return;
}
$stmt->bindValue(":link", $PATH, SQLITE3_TEXT);
$stmt->execute();
$stmt->close();
$message = "Server: [Success] --> Fave link: " .
$PATH . " " . $ACTION_TYPE . " the database!";
serverMessage("success", $message);
}
// Determin action
chdir("../../");
if (isset($_POST['getTabs'])) {
getTabLinks();
} elseif (isset($_POST['deleteLink'],
$_POST['linkPath'])) {
manageLink($_POST['deleteLink'], $_POST['linkPath']);
} else {
$message = "Server: [Error] --> Illegal Access Method!";
serverMessage("error", $message);
}
?>

@ -1,199 +0,0 @@
<?php
session_start();
include_once 'serverMessenger.php';
// Create file or folder
function createItem($FILE, $TYPE) {
$FILE = trim($FILE);
$FILE = preg_replace('/\.*$/','',$FILE); // removing dot . after file extension
if ($TYPE === "dir"){
mkdir($FILE, 0755);
} else if ($TYPE === "file") {
$myfile = fopen($FILE, "w");
fclose($myfile);
} else {
$message = "Server: [Error] --> Failed to create folder or file!";
serverMessage("error", $message);
return;
}
$message = "Server: [Success] --> The file " . $FILE . " has been created.";
serverMessage("success", $message);
$_SESSION["refreshState"] = "updateListing";
}
// File or folder delition
function deleteItem($FILE) {
if (filetype($FILE) == "dir"){
//GLOB_MARK adds a slash to directories returned
$files = glob($FILE . '*', GLOB_MARK);
foreach ($files as $file) {
deleteItem($file);
}
rmdir($FILE);
} else if (filetype($FILE) == "file") {
unlink($FILE);
} else {
$message = "Server: [Error] --> Failed to delete item! Not a folder or file!";
serverMessage("error", $message);
return;
}
$message = "Server: [Success] --> The file(s) has/have been deleted.";
serverMessage("success", $message);
$_SESSION["refreshState"] = "updateListing";
}
// Rename file or folder
function renameItem($OLDFILE, $NEWNAME, $PATH) {
rename($PATH . $OLDFILE, $PATH . $NEWNAME);
$message = "Server: [Success] --> The file " . $OLDFILE . " has been renamed to " . $NEWNAME . " side.";
serverMessage("success", $message);
$_SESSION["refreshState"] = "updateListing";
}
// Uploader
function uploadFiles($targetDir) {
$numberOfFiles = count($_FILES['filesToUpload']['name']);
if ($numberOfFiles === 0) {
$message = "Server: [Error] --> No files were uploaded!";
serverMessage("error", $message);
return;
}
$type = "";
$message = "";
for ($i=0; $i < $numberOfFiles; $i++) {
$uploadOk = 1;
$fileName = $_FILES['filesToUpload']['name'][$i];
$fileTmpName = $_FILES['filesToUpload']['tmp_name'][$i];
// Check if file already exists
$targetFile = $targetDir . $fileName;
if (file_exists($targetFile)) {
if (filetype($targetFile) == "file") {
unlink($targetFile);
$message = "Server: [Warning] --> This file already exists. Overwriting it.";
} else {
$message = "Server: [Warning] --> This file might be a directory. Or, no files were submitted for uploading.";
$uploadOk = 0;
}
}
// Check file size
$fileSize = $_FILES['filesToUpload']['size'][$i];
if ($fileSize > 500000000000) {
$message = "Server: [Warning] --> This file is too large.";
$uploadOk = 0;
}
// Allow certain file formats
// $ext = pathinfo($targetFile,PATHINFO_EXTENSION);
// if(!preg_match('/^.*\.(rar|iso|img|tar|zip|7z|7zip|jpg|jpeg|png|gif|mpeg|mov|flv|avi|mp4|webm|mpg|mkv|m4a|mp3|ogg|docx|doc|odt|txt|pdf|)$/i', strtolower($ext))) {
// $message = "Server: [Warning] --> This file type is not allowed.";
// $uploadOk = 0;
// }
// if everything is ok, try to upload file
if ($uploadOk !== 0) {
if (move_uploaded_file($fileTmpName, $targetFile)) {
$type = "success";
$message = "Server: [Success] --> The file " . $fileName . " has been uploaded.";
$_SESSION["refreshState"] = "updateListing";
}
} else {
$type = "error";
$message .= "\nServer: [Error] --> Your file " . $fileName . " was not uploaded.";
}
}
serverMessage($type, $message);
}
// Local program file access
function openFile($FILE) {
include 'config.php';
$EXTNSN = strtolower(pathinfo($FILE, PATHINFO_EXTENSION));
if (preg_match('(mkv|avi|flv|mov|m4v|mpg|wmv|mpeg|mp4|webm)', $EXTNSN) === 1) {
shell_exec($MEDIAPLAYER . "\"" . $FILE . "\" > /dev/null &");
} else if (preg_match('(png|jpg|jpeg|gif)', $EXTNSN) === 1) {
shell_exec($IMGVIEWER . ' "' . $FILE . '" > /dev/null &');
} else if (preg_match('(psf|mp3|ogg|flac)', $EXTNSN) === 1) {
shell_exec($MUSICPLAYER . ' "' . $FILE . '" > /dev/null &');
} else if (preg_match('(odt|doc|docx|rtf)', $EXTNSN) === 1) {
shell_exec($OFFICEPROG . ' "' . $FILE . '" > /dev/null &');
} else if (preg_match('(txt)', $EXTNSN) === 1) {
shell_exec($TEXTVIEWER . ' "' . $FILE . '" > /dev/null &');
} else if (preg_match('(pdf)', $EXTNSN) === 1) {
shell_exec($PDFVIEWER . ' "' . $FILE . '" > /dev/null &');
}
$message = "Server: [Success] --> The file " . $FILE . " has been opened server side.";
serverMessage("success", $message);
}
function remuxVideo($FILE) {
$FILE = trim($FILE);
$PTH = "resources/tmp/";
$HASHED_NAME = hash('sha256', $FILE) . '.mp4';
$EXTNSN = strtolower(pathinfo($FILE, PATHINFO_EXTENSION));
if (!file_exists($PTH . $HASHED_NAME)) {
$io = popen('/usr/bin/du -sm ' . $PTH, 'r');
$size = fgets($io, 4096);
$size = (int) substr($size, 0, strpos ( $size, "\t" ));
pclose ($io);
include 'config.php';
if ($size > $TMPFOLDERSIZE) {
$files = glob($PTH . '*');
foreach($files as $file){
if(is_file($file))
unlink($file);
}
}
if (preg_match('(mkv)', $EXTNSN) === 1)
$COMMAND = 'ffmpeg -i "' . $FILE . '" -hide_banner -movflags +faststart -codec copy -strict -2 ' . $PTH . $HASHED_NAME;
if (preg_match('(avi)', $EXTNSN) === 1)
$COMMAND = 'ffmpeg -i "' . $FILE . '" -hide_banner -movflags +faststart -c:v libx264 -crf 21 -c:a aac -b:a 192k -ac 2 ' . $PTH . $HASHED_NAME;
if (preg_match('(wmv)', $EXTNSN) === 1)
$COMMAND = 'ffmpeg -i "' . $FILE . '" -hide_banner -movflags +faststart -c:v libx264 -crf 23 -c:a aac -strict -2 -q:a 100 ' . $PTH . $HASHED_NAME;
shell_exec($COMMAND . " 2> resources/vdata.txt");
}
$GeneratedXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
$GeneratedXML .= "<REMUX_PATH>" . $PTH . $HASHED_NAME ."</REMUX_PATH>";
echo $GeneratedXML;
}
chdir("../../");
if (isset($_POST["remuxVideo"], $_POST["mediaPth"])) {
remuxVideo($_POST["mediaPth"]);
} else if (isset($_POST["createItem"],
$_POST["item"],
$_POST["type"])) {
createItem($_POST["item"], $_POST["type"]);
} else if (isset($_POST["deleteItem"], $_POST["item"])) {
deleteItem($_POST["item"]);
} else if (isset($_POST["renameItem"],
$_POST["oldName"],
$_POST["newName"],
$_POST["path"])) {
renameItem($_POST["oldName"], $_POST["newName"], $_POST["path"]);
} else if(isset($_POST["UploadFiles"], $_POST["DIRPATHUL"])) {
uploadFiles($_POST["DIRPATHUL"]);
} else if (isset($_POST["media"])) {
openFile($_POST["media"]);
} else {
$message = "Server: [Error] --> Incorrect access attempt!";
serverMessage("error", $message);
}
?>

@ -1,108 +0,0 @@
<?php
session_start();
include_once 'serverMessenger.php';
// Start of retrieving dir data
function startListing($NEWPATH, $MERGESEASSONS, $PASSWD) {
if (filetype($NEWPATH) == "dir") {
include_once 'lockedFolders.php';
if (checkForLock($NEWPATH, $PASSWD) == false) {
$subPath = ""; // This is used for season scanning as a means of properly getting
// the video src.... It's left blank when not in a sub dir
$GeneratedJSON = array('PATH_HEAD' => $NEWPATH,
'IN_FAVE' => isInDBCheck($NEWPATH),
'list' => array()
);
listDir($GeneratedJSON, $NEWPATH, $MERGESEASSONS, $subPath);
echo json_encode($GeneratedJSON);
} else {
$message = "Server: [Error] --> Folder is locked.";
serverMessage("locked", $message);
}
}
}
function listDir(&$GeneratedJSON, &$NEWPATH, &$MERGESEASSONS, &$subPath) {
if ($MERGESEASSONS !== "true") {
$files = array_diff(scandir($NEWPATH), array('..', '.', 'resources'));
foreach ($files as $fileName) {
$fullPath = $NEWPATH . '/' . $fileName;
// error_log($fullPath, 4);
processItem($GeneratedJSON, $fullPath, $fileName, $subPath);
}
} else {
$files = array_diff(scandir($NEWPATH), array('..', '.', 'resources'));
foreach ($files as $fileName) {
$fullPath = $NEWPATH . $fileName;
// error_log($fullPath, 4);
if (filetype($fullPath) == "dir" && strpos(strtolower($fileName),
'season') !== false) {
$fileName .= "/";
listDir($GeneratedJSON, $fullPath, $MERGESEASSONS, $fileName);
} else {
processItem($GeneratedJSON, $fullPath, $fileName, $subPath);
}
}
}
}
// Assign JSON Markup based on file type
function processItem(&$GeneratedJSON, &$fullPath, &$fileName, $subPath) {
if (preg_match('/^.*\.(mkv|avi|flv|mov|m4v|mpg|wmv|mpeg|mp4|webm)$/i', strtolower($fileName))) {
$NAMEHASH = hash('sha256', $fileName);
if (!file_exists('resources/images/thumbnails/' . $NAMEHASH . '.jpg')) {
shell_exec('resources/ffmpegthumbnailer -t 65% -s 320 -c jpg '
. '-i "' . $subPath . $fullPath . '" '
. '-o resources/images/thumbnails/' . $NAMEHASH . '.jpg'
);
}
$GeneratedJSON['list']['vids'][] = array('video' =>
array('title' => $subPath . $fileName,
'thumbnail' => $NAMEHASH . '.jpg'
)
);
} elseif (preg_match('/^.*\.(png|jpg|gif|jpeg)$/i', strtolower($fileName))) {
$GeneratedJSON['list']['imgs'][] = array('image' => $subPath . $fileName);
} elseif (filetype($fullPath) == "dir") {
$GeneratedJSON['list']['dirs'][] = array('dir' => $fileName . "/");
} else {
$GeneratedJSON['list']['files'][] = array('file' => $subPath . $fileName);
}
}
function isInDBCheck($PATH) {
$db = new SQLite3('resources/db/webfm.db');
if($db === false){
$message = "Server: [Error] --> Database connection failed!";
serverMessage("error", $message);
die("ERROR: Could not connect to db.");
}
$stmt = $db->prepare('SELECT 1 FROM faves WHERE link = :link');
$stmt->bindValue(":link", $PATH, SQLITE3_TEXT);
$result = $stmt->execute() ;
$row = $result->fetchArray() ;
if ($row > 0) {
return "true";
} else {
return "false";
}
}
// Determin action
chdir("../../");
if (isset($_POST['dirQuery'])) {
startListing(trim($_POST['dirQuery']), $_POST['mergeType'], $_POST['passwd']);
} else {
$message = "Server: [Error] --> Illegal Access Method!";
serverMessage("error", $message);
}
?>

@ -1,46 +0,0 @@
<?php
// Check if sub folder is in locked folder
function checkForLock($NEWPATH, $PASSWD) {
include 'config.php';
$LOCKS = explode("::::", $LOCKEDFOLDERS);
$size = sizeof($LOCKS);
if (isset($_SESSION["unlockState"]) && $_SESSION["unlockState"] == true) {
return false;
}
for ($i = 0; $i < $size; $i++) {
if (strpos($NEWPATH, $LOCKS[$i]) !== false) {
if ($PASSWD === $LOCKPASSWORD) {
$_SESSION["unlockState"] = true;
return false;
} else {
return true;
}
}
}
return false;
}
function lockFolders() {
session_start();
include 'serverMessenger.php';
if (isset($_SESSION["unlockState"]) && $_SESSION["unlockState"] == true) {
$_SESSION["unlockState"] = false;
$message = "Server: [Success] --> Folders unlocked!";
serverMessage("success", $message);
} else {
$message = "Server: [Warning] --> Folders aren't unlocked!"
. "\n" . $_SESSION["unlockState"];
serverMessage("warning", $message);
}
}
if (isset($_POST['lockFolders'])) {
lockFolders();
}
?>

@ -1,12 +0,0 @@
<?php
function serverMessage($TYPE, $MESSAGE) {
$GeneratedJSON = array( 'message' =>
array(
'type' => $TYPE,
'text' => $MESSAGE
)
);
echo json_encode($GeneratedJSON);
}
?>

@ -1,16 +0,0 @@
<?php
// Start the session
session_start();
include_once 'config.php';
if (!isset($_SESSION["refreshState"])) {
$_SESSION["refreshState"] = "none";
}
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
echo "data:" . $_SESSION["refreshState"] . "\n\n";
$_SESSION["refreshState"] = "none";
flush();
?>

@ -1 +0,0 @@
Place Holder....

@ -0,0 +1,48 @@
# Python imports
import os
# Lib imports
from flask import Flask
#OIDC Login path
from flask_oidc import OpenIDConnect
# Flask Login Path
from flask_bcrypt import Bcrypt
from flask_login import current_user, login_user, logout_user, LoginManager
# Apoplication imports
from core.utils import Logger
app = Flask(__name__)
app.config.from_object("core.config.ProductionConfig")
# app.config.from_object("core.config.DevelopmentConfig")
oidc = OpenIDConnect(app)
login_manager = LoginManager(app)
bcrypt = Bcrypt(app)
logger = Logger().get_logger()
def oidc_loggedin():
return oidc.user_loggedin
def oidc_isAdmin():
if oidc_loggedin():
isAdmin = oidc.user_getfield("isAdmin")
if isAdmin == "yes" :
return True
return False
app.jinja_env.globals['oidc_loggedin'] = oidc_loggedin
app.jinja_env.globals['oidc_isAdmin'] = oidc_isAdmin
app.jinja_env.globals['TITLE'] = app.config["TITLE"]
from core.models import db, User, Favorites
db.init_app(app)
with app.app_context():
db.create_all()
from core.forms import RegisterForm, LoginForm
from core import routes

@ -0,0 +1,14 @@
{
"web": {
"auth_uri": "https://www.ssoapps.com/auth/realms/apps/protocol/openid-connect/auth",
"client_id": "apps",
"issuer": "https://www.ssoapps.com/auth/realms/apps",
"client_secret": "9028c2ac-d6e0-4d96-86bd-02624b91695d",
"redirect_uris": [
"https%3A%2F%2Fwww.webfm.com%2F"
],
"userinfo_uri": "https://www.ssoapps.com/auth/realms/apps/protocol/openid-connect/userinfo",
"token_uri": "https://www.ssoapps.com/auth/realms/apps/protocol/openid-connect/token",
"token_introspection_uri": "https://www.ssoapps.com/auth/realms/apps/protocol/openid-connect/token/introspect"
}
}

@ -0,0 +1,66 @@
# System import
import os, secrets
from datetime import timedelta
# Lib imports
# Apoplication imports
# Configs
APP_NAME = 'WebFM'
ROOT_FILE_PTH = os.path.dirname(os.path.realpath(__file__))
class Config(object):
TITLE = APP_NAME
DEBUG = False
TESTING = False
THREADED = True
SECRET_KEY = "2A#GQafbREoblgMSQYomZSxbaPE6dt#"
# SECRET_KEY = secrets.token_hex(32)
PERMANENT_SESSION_LIFETIME = timedelta(days = 7).total_seconds()
SQLALCHEMY_DATABASE_URI = "sqlite:///static/db/webfm.db"
SQLALCHEMY_TRACK_MODIFICATIONS = False
LOGIN_PATH = "OIDC" # Value can be OIDC or FLASK_LOGIN
OIDC_TOKEN_TYPE_HINT = 'access_token'
APP_REDIRECT_URI = "https%3A%2F%2Fwww.webfm.com%2F" # This path is submitted as the redirect URI in certain code flows
OIDC_CLIENT_SECRETS = ROOT_FILE_PTH + '/client_secrets.json'
OIDC_ID_TOKEN_COOKIE_SECURE = True
OIDC_REQUIRE_VERIFIED_EMAIL = False
OIDC_USER_INFO_ENABLED = True
OIDC_VALID_ISSUERS = [
'http://www.ssoapps.com/auth/realms/apps',
'https://www.ssoapps.com/auth/realms/apps'
]
STATIC_FPTH = ROOT_FILE_PTH + "/static"
REL_THUMBS_PTH = "static/imgs/thumbnails" # Used for flask thumbnail return
# We are overiding some of the the shellmen view settings with these to make it all work with flask.
# These are passed along to the shellmen view from the Routes file upon the window controller creation.
ABS_THUMBS_PTH = STATIC_FPTH + "/imgs/thumbnails" # Used for thumbnail generation
REMUX_FOLDER = STATIC_FPTH + "/remuxs" # Remuxed files folder
FFMPG_THUMBNLR = STATIC_FPTH + "/ffmpegthumbnailer" # Thumbnail generator binary
class ProductionConfig(Config):
pass
class DevelopmentConfig(Config):
DEBUG = True
USE_RELOADER = True
OIDC_ID_TOKEN_COOKIE_SECURE = False
OIDC_REQUIRE_VERIFIED_EMAIL = False
class TestingConfig(Config):
TESTING = True

@ -0,0 +1,24 @@
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
from core import User
class RegisterForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=4, max=24)])
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired(), Length(min=8)])
confirm_password = PasswordField('Confirm Password',
validators=[DataRequired(), EqualTo('password', message="Passwords must match!")])
submit = SubmitField("Sign Up")
def validate_username(self, username):
user = User.query.filter_by(username=username.data).first()
if user:
raise ValidationError("User exists already! Please use a different name!")
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=4, max=24)])
password = PasswordField('Password', validators=[DataRequired(), Length(min=8, max=32)])
submit = SubmitField("Login")

@ -0,0 +1,41 @@
# System imports
# Lib imports
from flask_sqlalchemy import SQLAlchemy
# App imports
from . import app, login_manager
from flask_login import UserMixin
db = SQLAlchemy(app)
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class User(db.Model, UserMixin):
username = db.Column(db.String, unique=True, nullable=False)
email = db.Column(db.String, nullable=False)
password = db.Column(db.String, nullable=False)
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
def __repr__(self):
return f"['{self.username}', '{self.email}', '{self.password}', '{self.id}']"
class Favorites(db.Model):
link = db.Column(db.String, nullable=False, unique=True)
id = db.Column(db.Integer, nullable=False, primary_key=True, unique=True, autoincrement=True)
def __repr__(self):
return f"['{self.link}', '{self.id}']"
class Settings(db.Model):
key = db.Column(db.String, nullable=False)
value = db.Column(db.String, nullable=False)
id = db.Column(db.Integer, nullable=False, primary_key=True, unique=True, autoincrement=True)
def __repr__(self):
return f"['{self.key}', '{self.value}', '{self.id}']"

@ -0,0 +1,279 @@
# Python imports
import os, json, secrets, re, shutil
# Lib imports
from flask import request, session, render_template, send_from_directory, redirect
from flask_uploads import UploadSet, configure_uploads, ALL
from flask_login import current_user
# App imports
from core import app, logger, oidc, db, Favorites # Get from __init__
from core.utils import MessageHandler # Get simple message processor
from core.utils.shellfm import WindowController # Get file manager controller
msgHandler = MessageHandler()
window_controllers = {}
# valid_fname_pat = re.compile(r"/^[a-zA-Z0-9-_\[\]\(\)| ]+$/")
valid_fname_pat = re.compile(r"[a-z0-9A-Z-_\[\]\(\)\| ]{4,20}")
def get_window_controller():
controller = None
try:
controller = window_controllers[ session["win_controller_id"] ]
except Exception as e:
id = secrets.token_hex(16)
controller = WindowController()
view = controller.get_window(1).get_view(0)
view.ABS_THUMBS_PTH = app.config['ABS_THUMBS_PTH']
view.REMUX_FOLDER = app.config['REMUX_FOLDER']
view.FFMPG_THUMBNLR = app.config['FFMPG_THUMBNLR']
view.logger = logger
session['win_controller_id'] = id
window_controllers.update( {id: controller } )
controller = window_controllers[ session["win_controller_id"] ]
return controller
@app.route('/', methods=['GET', 'POST'])
def home():
if request.method == 'GET':
view = get_window_controller().get_window(1).get_view(0)
_dot_dots = view.get_dot_dots()
_current_directory = view.get_current_directory()
return render_template('pages/index.html', current_directory = _current_directory, dot_dots = _dot_dots)
return render_template('error.html', title = 'Error!',
message = 'Must use GET request type...')
@app.route('/api/list-files/<_hash>', methods=['GET', 'POST'])
def listFiles(_hash = None):
if request.method == 'POST':
view = get_window_controller().get_window(1).get_view(0)
dot_dots = view.get_dot_dots()
if dot_dots[0][1] == _hash: # Refresh
view.load_directory()
elif dot_dots[1][1] == _hash: # Pop from dir
view.pop_from_path()
msg = "Log in with an Admin privlidged user to view the requested path!"
is_locked = view.is_folder_locked(_hash)
if is_locked and not oidc.user_loggedin:
return msgHandler.createMessageJSON("danger", msg)
elif is_locked and oidc.user_loggedin:
isAdmin = oidc.user_getfield("isAdmin")
if isAdmin != "yes" :
return msgHandler.createMessageJSON("danger", msg)
if dot_dots[0][1] != _hash and dot_dots[1][1] != _hash:
path = view.get_path_part_from_hash(_hash)
view.push_to_path(path)
error_msg = view.get_error_message()
if error_msg != None:
view.unset_error_message()
return msgHandler.createMessageJSON("danger", error_msg)
sub_path = view.get_current_sub_path()
files = view.get_files_formatted()
fave = db.session.query(Favorites).filter_by(link = sub_path).first()
in_fave = "true" if fave else "false"
files.update({'in_fave': in_fave})
return files
else:
msg = "Can't manage the request type..."
return msgHandler.createMessageJSON("danger", msg)
@app.route('/api/file-manager-action/<_type>/<_hash>', methods=['GET', 'POST'])
def fileManagerAction(_type, _hash = None):
view = get_window_controller().get_window(1).get_view(0)
if _type == "reset-path" and _hash == "None":
view.set_to_home()
msg = "Returning to home directory..."
return msgHandler.createMessageJSON("success", msg)
folder = view.get_current_directory()
file = view.get_path_part_from_hash(_hash)
fpath = os.path.join(folder, file)
logger.debug(fpath)
if _type == "files":
return send_from_directory(folder, file)
if _type == "remux":
# NOTE: Need to actually implimint a websocket to communicate back to client that remux has completed.
# As is, the remux thread hangs until completion and client tries waiting until server reaches connection timeout.
# I.E....this is stupid but for now works better than nothing
good_result = view.remuxVideo(_hash, fpath)
if good_result:
return '{"path":"static/remuxs/' + _hash + '.mp4"}'
else:
msg = "Remuxing: Remux failed or took too long; please, refresh the page and try again..."
return msgHandler.createMessageJSON("success", msg)
if _type == "run-locally":
msg = "Opened media..."
view.openFilelocally(fpath)
return msgHandler.createMessageJSON("success", msg)
# NOTE: Positionally protecting actions further down that are privlidged
# Be aware of ordering!
msg = "Log in with an Admin privlidged user to do this action!"
if not oidc.user_loggedin:
return msgHandler.createMessageJSON("danger", msg)
elif oidc.user_loggedin:
isAdmin = oidc.user_getfield("isAdmin")
if isAdmin != "yes" :
return msgHandler.createMessageJSON("danger", msg)
if _type == "delete":
try:
msg = f"[Success] Deleted the file/folder -->: {file} !"
if os.path.isfile(fpath):
os.unlink(fpath)
else:
shutil.rmtree(fpath)
return msgHandler.createMessageJSON("success", msg)
except Exception as e:
msg = "[Error] Unable to delete the file/folder...."
return msgHandler.createMessageJSON("danger", msg)
@app.route('/api/list-favorites', methods=['GET', 'POST'])
def listFavorites():
if request.method == 'POST':
list = db.session.query(Favorites).all()
faves = []
for fave in list:
faves.append([fave.link, fave.id])
return '{"faves_list":' + json.dumps(faves) + '}'
else:
msg = "Can't manage the request type..."
return msgHandler.createMessageJSON("danger", msg)
@app.route('/api/load-favorite/<_id>', methods=['GET', 'POST'])
def loadFavorite(_id):
if request.method == 'POST':
try:
ID = int(_id)
fave = db.session.query(Favorites).filter_by(id = ID).first()
view = get_window_controller().get_window(1).get_view(0)
view.set_path_with_sub_path(fave.link)
return '{"refresh": "true"}'
except Exception as e:
print(repr(e))
msg = "Incorrect Favorites ID..."
return msgHandler.createMessageJSON("danger", msg)
else:
msg = "Can't manage the request type..."
return msgHandler.createMessageJSON("danger", msg)
@app.route('/api/manage-favorites/<_action>', methods=['GET', 'POST'])
def manageFavorites(_action):
if request.method == 'POST':
ACTION = _action.strip()
view = get_window_controller().get_window(1).get_view(0)
sub_path = view.get_current_sub_path()
if ACTION == "add":
fave = Favorites(link = sub_path)
db.session.add(fave)
msg = "Added to Favorites successfully..."
elif ACTION == "delete":
fave = db.session.query(Favorites).filter_by(link = sub_path).first()
db.session.delete(fave)
msg = "Deleted from Favorites successfully..."
else:
msg = "Couldn't handle action for favorites item..."
return msgHandler.createMessageJSON("danger", msg)
db.session.commit()
return msgHandler.createMessageJSON("success", msg)
else:
msg = "Can't manage the request type..."
return msgHandler.createMessageJSON("danger", msg)
@app.route('/api/create/<_type>', methods=['GET', 'POST'])
def create_item(_type = None):
if request.method == 'POST':
msg = "Log in with an Admin privlidged user to upload files!"
if not oidc.user_loggedin:
return msgHandler.createMessageJSON("danger", msg)
elif oidc.user_loggedin:
isAdmin = oidc.user_getfield("isAdmin")
if isAdmin != "yes" :
return msgHandler.createMessageJSON("danger", msg)
TYPE = _type.strip()
FNAME = str(request.values['fname']).strip()
if not re.fullmatch(valid_fname_pat, FNAME):
msg = "A new item name can only contain alphanumeric, -, _, |, [], (), or spaces and must be minimum of 4 and max of 20 characters..."
return msgHandler.createMessageJSON("danger", msg)
view = get_window_controller().get_window(1).get_view(0)
folder = view.get_current_directory()
new_item = folder + '/' + FNAME
try:
if TYPE == "dir":
os.mkdir(new_item)
elif TYPE == "file":
open(new_item + ".txt", 'a').close()
else:
msg = "Couldn't handle action type for api create..."
return msgHandler.createMessageJSON("danger", msg)
except Exception as e:
print(repr(e))
msg = "Couldn't create file/folder. An unexpected error occured..."
return msgHandler.createMessageJSON("danger", msg)
msg = "[Success] created the file/dir..."
return msgHandler.createMessageJSON("success", msg)
else:
msg = "Can't manage the request type..."
return msgHandler.createMessageJSON("danger", msg)
@app.route('/upload', methods=['GET', 'POST'])
def upload():
if request.method == 'POST' and len(request.files) > 0:
msg = "Log in with an Admin privlidged user to upload files!"
if not oidc.user_loggedin:
return msgHandler.createMessageJSON("danger", msg)
elif oidc.user_loggedin:
isAdmin = oidc.user_getfield("isAdmin")
if isAdmin != "yes" :
return msgHandler.createMessageJSON("danger", msg)
view = get_window_controller().get_window(1).get_view(0)
folder = view.get_current_directory()
UPLOADS_PTH = folder + '/'
files = UploadSet('files', ALL, default_dest=lambda x: UPLOADS_PTH)
configure_uploads(app, files)
for file in request.files:
try:
files.save(request.files[file])
except Exception as e:
print(repr(e))
msg = "[Error] Failed to upload some or all of the file(s)..."
return msgHandler.createMessageJSON("danger", msg)
msg = "[Success] Uploaded file(s)..."
return msgHandler.createMessageJSON("success", msg)
else:
msg = "Can't manage the request type..."
return msgHandler.createMessageJSON("danger", msg)

@ -0,0 +1,6 @@
from . import Routes
from .pages import Flask_Login
from .pages import Flask_Register
from .pages import OIDC_Login
from .pages import OIDC_Register
from .pages import LoginManager

@ -0,0 +1,37 @@
# Python imports
# Lib imports
from flask import request, render_template, flash, redirect, url_for
from flask_login import current_user, login_user, logout_user
# App imports
from core import app, bcrypt, db, User, LoginForm
from core.utils import MessageHandler # Get simple message processor
msgHandler = MessageHandler()
@app.route('/app-login', methods=['GET', 'POST'])
def app_login():
if current_user.is_authenticated:
return redirect(url_for("home"))
_form = LoginForm()
if _form.validate_on_submit():
user = db.session.query(User).filter(User.username == _form.username.data).first()
if user and bcrypt.check_password_hash(user.password, _form.password.data):
login_user(user, remember=False)
flash("Logged in successfully!", "success")
return redirect(url_for("home"))
flash("Username or password incorrect! Please try again...", "danger")
return render_template('pages/login.html', form = _form)
@app.route('/app-logout')
def app_logout():
logout_user()
flash("Logged out successfully!", "success")
return redirect(url_for("home"))

@ -0,0 +1,30 @@
# Python imports
# Lib imports
from flask import request, render_template, url_for, redirect, flash
# App imports
from core import app, bcrypt, db, current_user, RegisterForm # Get from __init__
from core.models import User
from core.utils import MessageHandler # Get simple message processor
msgHandler = MessageHandler()
@app.route('/app-register', methods=['GET', 'POST'])
def app_register():
if current_user.is_authenticated:
return redirect(url_for("home"))
_form = RegisterForm()
if _form.validate_on_submit():
hashed_password = bcrypt.generate_password_hash(_form.password.data).decode("utf-8")
user = User(username = _form.username.data, email = _form.email.data, password = hashed_password)
db.session.add(user)
db.session.commit()
flash("Account created successfully!", "success")
return redirect(url_for("login"))
return render_template('pages/register.html',
form = _form)

@ -0,0 +1,43 @@
# Python imports
# Lib imports
from flask import redirect, url_for, flash
# App imports
from core import app
ROUTE = app.config['LOGIN_PATH']
@app.route('/login', methods=['GET', 'POST'])
def login():
if ROUTE == "OIDC":
return redirect(url_for("oidc_login"))
if ROUTE == "FLASK_LOGIN":
return redirect(url_for("app_login"))
flash("No Login Path Accessable! Please contact an Administrator!", "danger")
return redirect(url_for("home"))
@app.route('/logout')
def logout():
if ROUTE == "OIDC":
return redirect(url_for("oidc_logout"))
if ROUTE == "FLASK_LOGIN":
return redirect(url_for("app_logout"))
flash("No Logout Path Accessable! Please contact an Administrator!", "danger")
return redirect(url_for("home"))
@app.route('/register', methods=['GET', 'POST'])
def register():
if ROUTE == "OIDC":
return redirect(url_for("oidc_register"))
if ROUTE == "FLASK_LOGIN":
return redirect(url_for("app_register"))
flash("No Register Path Accessable! Please contact an Administrator!", "danger")
return redirect(url_for("home"))

@ -0,0 +1,27 @@
# Python imports
# Lib imports
from flask import request, redirect, flash
# App imports
from ... import app, oidc
@app.route('/oidc-login', methods=['GET', 'POST'])
@oidc.require_login
def oidc_login():
print(request)
return redirect("/")
@app.route('/oidc-logout', methods=['GET', 'POST'])
@oidc.require_login
def oidc_logout():
oidc.logout()
flash("Logged out successfully!", "success")
# NOTE: Need to redirect to logout on OIDC server to end session there too.
# If not, we can hit login url again and get same token until it expires.
return redirect( oidc.client_secrets.get('issuer')
+ '/protocol/openid-connect/logout?redirect_uri='
+ app.config['APP_REDIRECT_URI'])

@ -0,0 +1,31 @@
# Python imports
# Lib imports
from flask import request, render_template, url_for, redirect, flash
# App imports
from ... import app, oidc, db # Get from __init__
from ...utils import MessageHandler # Get simple message processor
msgHandler = MessageHandler()
@app.route('/oidc-register', methods=['GET', 'POST'])
def oidc_register():
if oidc.user_loggedin:
return redirect("/home")
_form = RegisterForm()
if _form.validate_on_submit():
# TODO: Create...
# NOTE: Do a requests api here maybe??
# hashed_password = bcrypt.generate_password_hash(_form.password.data).decode("utf-8")
# user = User(username=_form.username.data, password=hashed_password)
# db.session.add(user)
# db.session.commit()
flash("Account created successfully!", "success")
return redirect("/login")
return render_template('pages/register.html', form = _form)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,50 @@
.menu {
width: 165px;
z-index: 999;
box-shadow: 0 4px 5px 3px rgba(0, 0, 0, 0.2);
background-color: rgba(0, 0, 0, 0.64);
position: fixed;
display: none;
transition: 0.2s display ease-in;
}
.menu .menu-options {
list-style: none;
padding: 10px 0;
z-index: 1;
}
.menu .menu-options .menu-option {
font-weight: 500;
z-index: 1;
padding: 10px 40px 10px 20px;
cursor: pointer;
}
.menu .menu-options .menu-option:hover {
background: rgba(255, 255, 255, 0.64);
color: rgba(0, 0, 0, 0.5);
}
button {
background: grey;
border: none;
}
button .next {
color: green;
}
button[disabled="false"]:hover .next {
color: red;
animation: move 0.5s;
animation-iteration-count: 2;
}
@keyframes move {
from {
transform: translate(0%);
}
50% {
transform: translate(-40%);
}
to {
transform: transform(0%);
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,5 @@
body,
.container-fluid, .row, .col,
.error-styling, .dir-style, .movie-style, .file-style {
background-color: rgba(0,0,0,0.0);
}

@ -0,0 +1,80 @@
#bg {
position: fixed;
top: 0%;
left: 0%;
width: 100%;
height: 100%;
z-index: -999;
object-fit: cover;
}
#bg img {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
z-index: -999;
}
#master-container {
height: 90vh;
overflow-x: hidden;
overflow-y: auto;
}
#video-controls {
position: relative;
bottom: 2.5em;
}
/* CLASSES */
.scroller {
scrollbar-color: #00000084 #ffffff64;
scrollbar-width: thin;
}
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome, Edge, Opera and Firefox */
}
.card-title-text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.volume-control-positioner {
position: absolute;
bottom: 3.5em;
right: 2.5%;
}
.icon-style {
width: 2em;
height: auto;
}
.viewer {
max-width: 55em;
}
/* Other message text colors */
.errorTxt { color: rgb(170, 18, 18); }
.warningTxt { color: rgb(255, 168, 0); }
.successTxt { color: rgb(136, 204, 39); }

@ -0,0 +1,81 @@
@font-face {
font-family: Dosis;
src: url("/static/css/fonts/dosis/Dosis-Regular.ttf"),
url("/static/css/fonts/dosis/Dosis-Medium.ttf"),
url("/static/css/fonts/dosis/Dosis-Light.ttf"),
url("/static/css/fonts/dosis/Dosis-ExtraLight.ttf"),
url("/static/css/fonts/dosis/Dosis-Bold.ttf"),
url("/static/css/fonts/dosis/Dosis-ExtraBold.ttf"),
url("/static/css/fonts/dosis/Dosis-SemiBold.ttf"),
url("/static/css/fonts/Dosis-VariableFont_wght.ttf");
font-style: normal;
font-display: auto;
}
body {
font-family: Dosis;
}
ul, li {
list-style: none;
}
/* Vertical slider */
input[type=range][orient=vertical] {
writing-mode: bt-lr; /* IE */
-webkit-appearance: slider-vertical; /* WebKit */
width: 8px;
height: 175px;
padding: 0 5px;
}
[class*="col-"] {
background-clip: padding-box;
border: 10px solid transparent;
margin: 0em !important;
padding: 0em !important;
}
#video-container::-webkit-media-controls {
display: none !important;
}
#video-container::-webkit-media-controls-enclosure {
display: none !important;
}
.modal-content {
background-color: #32383e74;
border-color: #f8940674;
}
.sticky-top,
.card {
background-color: rgba(50, 56, 62, 0.84);
}
.card-body {
min-height: 326px;
font-size: x-large;
vertical-align: middle !important;
align-items: center;
display: grid;
}
.card-img, .card-img-top {
max-height: 285px !important;
object-fit: contain;
}
.list-group-item {
color: rgba(255, 255, 255, 1) !important;
}
.label-as-badge {
border-radius: 1em;
cursor: pointer;
}

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 KiB

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save