Compare commits
No commits in common. "master" and "php-legacy-version" have entirely different histories.
master
...
php-legacy
|
@ -1,143 +0,0 @@
|
||||||
*.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/
|
|
After Width: | Height: | Size: 386 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 1.4 MiB |
After Width: | Height: | Size: 2.0 MiB |
After Width: | Height: | Size: 1.8 MiB |
After Width: | Height: | Size: 2.1 MiB |
After Width: | Height: | Size: 2.0 MiB |
After Width: | Height: | Size: 2.4 MiB |
After Width: | Height: | Size: 1019 KiB |
34
README.md
|
@ -2,17 +2,22 @@
|
||||||
WebFM is a media and file viewer aspiring to become a full fledged file manager in the browser.
|
WebFM is a media and file viewer aspiring to become a full fledged file manager in the browser.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
1. Install python, sqlite3, and ffmpeg on the system this will be on.
|
1. Install php7, php-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.
|
2. Use php -S 0.0.0.0:yourDesiredPort
|
||||||
4. Use hosts file (or other methods) to redirect webfm.com and ssoapps.com to local app.
|
3. Use ufw or gufw to open the port on your computer to the network.
|
||||||
5. Update client_secrets.json > 'client_secret' field with your Keycloak key. (Current one was local, not public, and has been expired)
|
4. Place files or start uploading some to the folders.
|
||||||
6. Place files or start uploading some to the folders.
|
5. Double click thumbnails and container outlines to open files.
|
||||||
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.
|
6. Double click the text name to change the file's or folder's name and press enter to set it.
|
||||||
7. Password protect folder based on core/utils/shellfm/windows/Settings.py file settings.
|
7. Right-click to get context menu options.
|
||||||
8. Save paths to favorites list for quick access.
|
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.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
n/a
|
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.
|
||||||
|
|
||||||
# TO-DO
|
# TO-DO
|
||||||
1. Allow for move and copy.
|
1. Allow for move and copy.
|
||||||
|
@ -20,8 +25,9 @@ n/a
|
||||||
|
|
||||||
|
|
||||||
# Images
|
# Images
|
||||||
![1 Videos List](images/pic1.png)
|
![1 Home](Images/pic1.png)
|
||||||
![2 Video Playing](images/pic2.png)
|
![2 Images Listed](Images/pic2.png)
|
||||||
![3 Images List](images/pic3.png)
|
![3 Videos Listed](Images/pic3.png)
|
||||||
![4 Context menu](images/pic4.png)
|
![4 Image Open](Images/pic4.png)
|
||||||
![5 Settings Pane With Upload And Create Functionality](images/pic5.png)
|
![5 Image Open And Video Playing](Images/pic5.png)
|
||||||
|
![6 Alternate Background](Images/pic6.png)
|
||||||
|
|
After Width: | Height: | Size: 1.9 MiB |
|
@ -1,40 +0,0 @@
|
||||||
#!/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 $@;
|
|
|
@ -0,0 +1 @@
|
||||||
|
LOL...Not really!
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
BIN
images/pic1.png
Before Width: | Height: | Size: 1.3 MiB |
BIN
images/pic2.png
Before Width: | Height: | Size: 1.3 MiB |
BIN
images/pic3.png
Before Width: | Height: | Size: 1.0 MiB |
BIN
images/pic4.png
Before Width: | Height: | Size: 370 KiB |
BIN
images/pic5.png
Before Width: | Height: | Size: 299 KiB |
|
@ -0,0 +1,120 @@
|
||||||
|
<!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')">⚙</button>
|
||||||
|
<button type="button" title="Refresh" onclick="getDir('./')">↻</button>
|
||||||
|
<button type="button" title="Back" onclick="getDir('../')">⇐</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 ↕</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..." >☆</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">∽</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>
|
|
@ -1,36 +0,0 @@
|
||||||
bcrypt==4.0.1
|
|
||||||
certifi==2022.12.7
|
|
||||||
charset-normalizer==3.0.1
|
|
||||||
click==7.1.2
|
|
||||||
dnspython==1.16.0
|
|
||||||
email-validator==1.1.2
|
|
||||||
eventlet==0.30.1
|
|
||||||
Flask==1.1.2
|
|
||||||
Flask-Bcrypt==0.7.1
|
|
||||||
Flask-Login==0.5.0
|
|
||||||
flask-oidc==1.4.0
|
|
||||||
Flask-SQLAlchemy==2.4.4
|
|
||||||
Flask-Uploads==0.2.1
|
|
||||||
Flask-WTF==0.14.3
|
|
||||||
greenlet==1.0.0
|
|
||||||
gunicorn==20.0.4
|
|
||||||
httplib2==0.19.0
|
|
||||||
idna==3.4
|
|
||||||
itsdangerous==1.1.0
|
|
||||||
Jinja2==2.11.3
|
|
||||||
MarkupSafe==1.1.1
|
|
||||||
oauth2client==4.1.3
|
|
||||||
Pillow==9.4.0
|
|
||||||
pyasn1==0.4.8
|
|
||||||
pyasn1-modules==0.2.8
|
|
||||||
pycairo==1.23.0
|
|
||||||
PyGObject==3.42.2
|
|
||||||
pyparsing==2.4.7
|
|
||||||
pyxdg==0.28
|
|
||||||
requests==2.28.2
|
|
||||||
rsa==4.7
|
|
||||||
six==1.15.0
|
|
||||||
SQLAlchemy==1.3.23
|
|
||||||
urllib3==1.26.14
|
|
||||||
Werkzeug==1.0.1
|
|
||||||
WTForms==2.3.3
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
html {
|
||||||
|
margin: 0em;
|
||||||
|
padding: 0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol, ul, li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
#controls, #fullPathHeader, #dynDiv,
|
||||||
|
.errorStyling, .dirStyle, .movieStyle, .fileStyle {
|
||||||
|
background-color: rgba(0,0,0,0.2);
|
||||||
|
}
|
|
@ -0,0 +1,304 @@
|
||||||
|
/* 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); }
|
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 |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 7.4 KiB |
|
@ -0,0 +1 @@
|
||||||
|
Place Holder File
|
|
@ -0,0 +1,54 @@
|
||||||
|
// 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);
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
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 "";
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
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("./");
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
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);
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
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);
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
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 = "↗";
|
||||||
|
aTag.appendChild(popOutDiv);
|
||||||
|
|
||||||
|
toLocDiv.title = "Open In Local Program";
|
||||||
|
toLocDiv.className = "popOutBttn";
|
||||||
|
toLocDiv.innerHTML = "∽";
|
||||||
|
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) { }
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
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 "";
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?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/";
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
?>
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,199 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,108 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?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();
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
function serverMessage($TYPE, $MESSAGE) {
|
||||||
|
$GeneratedJSON = array( 'message' =>
|
||||||
|
array(
|
||||||
|
'type' => $TYPE,
|
||||||
|
'text' => $MESSAGE
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo json_encode($GeneratedJSON);
|
||||||
|
}
|
||||||
|
?>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?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();
|
||||||
|
?>
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*START OF HOMEPAGE BUTTON SETTINGS*/
|
||||||
|
.homeSection {
|
||||||
|
float: left;
|
||||||
|
width: 40%;
|
||||||
|
height: 14em;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 100%;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: .2em;
|
||||||
|
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
||||||
|
background: rgba(19, 21, 21, .6);
|
||||||
|
}
|
||||||
|
/*END OF HOMEPAGE BUTTON SETTINGS*/
|
||||||
|
.header {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
width: 99%;
|
||||||
|
}
|
||||||
|
.section, .lnksNdirs, .header {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 100%;
|
||||||
|
padding-top: 1em;
|
||||||
|
margin: 1em;
|
||||||
|
background: rgba(19, 21, 21, .7);
|
||||||
|
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
||||||
|
/* border-style: solid;
|
||||||
|
border-width: .2em;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.backbutton {
|
||||||
|
top: 3.55em;
|
||||||
|
clear: both;
|
||||||
|
z-index: 2;
|
||||||
|
position: fixed;
|
||||||
|
float: left;
|
||||||
|
width: 5%;
|
||||||
|
height: 50px;
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
iframe {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 2em;
|
||||||
|
left: 25%;
|
||||||
|
width: 50%;
|
||||||
|
height: 360px;
|
||||||
|
background: rgba(0,0,0,.5);
|
||||||
|
transition: 0s;
|
||||||
|
transition-delay:2s;
|
||||||
|
}
|
||||||
|
iframe:hover {
|
||||||
|
left: 2%;
|
||||||
|
width: 99%;
|
||||||
|
height: 700px;
|
||||||
|
transition-delay:2s;
|
||||||
|
}
|
||||||
|
.lnkStyl {
|
||||||
|
clear: right;
|
||||||
|
float: left;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgsStyl {
|
||||||
|
margin-left: 50%;
|
||||||
|
opacity: 0.699999988079071044921875;
|
||||||
|
width: 32.333%;
|
||||||
|
height: 20em;
|
||||||
|
}
|
||||||
|
.imgsStyl:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
#bg {
|
||||||
|
position: fixed;
|
||||||
|
top: 0%;
|
||||||
|
left: 0%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
#bg img {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
body {
|
||||||
|
width: 600%;
|
||||||
|
}
|
||||||
|
/*START OF HOMEPAGE BUTTON SETTINGS*/
|
||||||
|
.homeSection {
|
||||||
|
float: left;
|
||||||
|
width: 40%;
|
||||||
|
height: 14em;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 100%;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: .2em;
|
||||||
|
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
||||||
|
background: rgba(19, 21, 21, .6);
|
||||||
|
}
|
||||||
|
/*END OF HOMEPAGE BUTTON SETTINGS*/
|
||||||
|
.header {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
width: 99%;
|
||||||
|
}
|
||||||
|
.section, .lnksNdirs, .header {
|
||||||
|
display: block;
|
||||||
|
clear: right;
|
||||||
|
float: left;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 100%;
|
||||||
|
padding-top: 1em;
|
||||||
|
margin: 1em;
|
||||||
|
background: rgba(19, 21, 21, .7);
|
||||||
|
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
||||||
|
/* border-style: solid;
|
||||||
|
border-width: .2em;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.backbutton {
|
||||||
|
top: 3.55em;
|
||||||
|
clear: both;
|
||||||
|
z-index: 2;
|
||||||
|
position: fixed;
|
||||||
|
float: left;
|
||||||
|
width: 5%;
|
||||||
|
height: 50px;
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
iframe {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 2em;
|
||||||
|
left: 25%;
|
||||||
|
width: 50%;
|
||||||
|
height: 360px;
|
||||||
|
background: rgba(0,0,0,.5)
|
||||||
|
transition: 0s;
|
||||||
|
transition-delay:2s;
|
||||||
|
}
|
||||||
|
.lnkStyl {
|
||||||
|
clear: right;
|
||||||
|
float: left;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgsStyl {
|
||||||
|
margin-left: 50%;
|
||||||
|
opacity: 0.699999988079071044921875;
|
||||||
|
width: 32.333%;
|
||||||
|
height: 20em;
|
||||||
|
}
|
||||||
|
.imgsStyl:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
#bg {
|
||||||
|
position: fixed;
|
||||||
|
top: 0%;
|
||||||
|
left: 0%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
#bg img {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*START OF HOMEPAGE BUTTON SETTINGS*/
|
||||||
|
.homeSection {
|
||||||
|
float: left;
|
||||||
|
width: 40%;
|
||||||
|
height: 14em;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 100%;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: .2em;
|
||||||
|
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
||||||
|
background: rgba(19, 21, 21, .6);
|
||||||
|
}
|
||||||
|
/*END OF HOMEPAGE BUTTON SETTINGS*/
|
||||||
|
.header {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
width: 99%;
|
||||||
|
}
|
||||||
|
.section, .lnksNdirs, .header {
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 100%;
|
||||||
|
padding-top: 1em;
|
||||||
|
margin-top: 1em;
|
||||||
|
background: rgba(19, 21, 21, .7);
|
||||||
|
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
||||||
|
/* border-style: solid;
|
||||||
|
border-width: .2em;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.backbutton {
|
||||||
|
top: 3.55em;
|
||||||
|
clear: both;
|
||||||
|
z-index: 2;
|
||||||
|
position: fixed;
|
||||||
|
float: left;
|
||||||
|
width: 5%;
|
||||||
|
height: 50px;
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
iframe {
|
||||||
|
clear: both;
|
||||||
|
z-index: 1;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 5%;
|
||||||
|
width: 40%;
|
||||||
|
height: 500px;
|
||||||
|
background: rgba(0,0,0,.5)
|
||||||
|
transition: 0s;
|
||||||
|
transition-delay:2s;
|
||||||
|
}
|
||||||
|
iframe:hover {
|
||||||
|
width: 99%;
|
||||||
|
transition-delay:2s;
|
||||||
|
}
|
||||||
|
.lnkStyl {
|
||||||
|
clear: right;
|
||||||
|
float: left;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgsStyl {
|
||||||
|
margin-left: 50%;
|
||||||
|
opacity: 0.699999988079071044921875;
|
||||||
|
width: 32.333%;
|
||||||
|
height: 20em;
|
||||||
|
}
|
||||||
|
.imgsStyl:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
#bg {
|
||||||
|
position: fixed;
|
||||||
|
top: 0%;
|
||||||
|
left: 0%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
#bg img {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*START OF HOMEPAGE BUTTON SETTINGS*/
|
||||||
|
.homeSection {
|
||||||
|
float: left;
|
||||||
|
width: 40%;
|
||||||
|
height: 14em;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 100%;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: .2em;
|
||||||
|
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
||||||
|
background: rgba(19, 21, 21, .6);
|
||||||
|
}
|
||||||
|
/*END OF HOMEPAGE BUTTON SETTINGS*/
|
||||||
|
|
||||||
|
.lnkStyl {
|
||||||
|
clear: right;
|
||||||
|
float: left;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
.imgsStyl {
|
||||||
|
clear: both;
|
||||||
|
float: left;
|
||||||
|
opacity: 0.699999988079071044921875;
|
||||||
|
width: 20%;
|
||||||
|
height: 15em;
|
||||||
|
}
|
||||||
|
.imgsStyl:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
width: 100%;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.lnksNdirs {
|
||||||
|
width: 20%;
|
||||||
|
float:left;
|
||||||
|
}
|
||||||
|
/* used to format sections of pgs*/
|
||||||
|
.section, .lnksNdirs, .header {
|
||||||
|
clear: both;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 1em;
|
||||||
|
margin-top: 1em;
|
||||||
|
font-size: 100%;
|
||||||
|
background: rgba(19, 21, 21, .6);
|
||||||
|
background: rgba(19, 21, 21, .7);
|
||||||
|
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
||||||
|
/* border-style: solid;
|
||||||
|
border-width: .2em;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.backbutton {
|
||||||
|
top: 3em;
|
||||||
|
clear: both;
|
||||||
|
z-index: 2;
|
||||||
|
position: fixed;
|
||||||
|
float: left;
|
||||||
|
width: 5%;
|
||||||
|
height: 55px;
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
.ifrmbutton2 {
|
||||||
|
top: 3em;
|
||||||
|
float: right;
|
||||||
|
right: .5em;
|
||||||
|
z-index:2;
|
||||||
|
position: fixed;
|
||||||
|
width: 5%;
|
||||||
|
height: 30px;
|
||||||
|
opacity: .6;
|
||||||
|
}
|
||||||
|
iframe {
|
||||||
|
margin-left: 2%;
|
||||||
|
z-index: 2;
|
||||||
|
position: fixed;
|
||||||
|
top: 8em;
|
||||||
|
right: 0.534em;
|
||||||
|
bottom: 2%;
|
||||||
|
width: 75%;
|
||||||
|
height: 525px;
|
||||||
|
background: rgba(0,0,0,.5)
|
||||||
|
transition: 0s;
|
||||||
|
transition-delay:2s;
|
||||||
|
}
|
||||||
|
iframe:hover {
|
||||||
|
height: 775px;
|
||||||
|
transition-delay:2s;
|
||||||
|
}
|
||||||
|
#bg {
|
||||||
|
position: fixed;
|
||||||
|
top: 0%;
|
||||||
|
left: 0%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
#bg img {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
Place Holder....
|
|
@ -1,94 +0,0 @@
|
||||||
# Python imports
|
|
||||||
import os
|
|
||||||
import builtins
|
|
||||||
import threading
|
|
||||||
import re
|
|
||||||
import secrets
|
|
||||||
|
|
||||||
# Lib imports
|
|
||||||
from flask import session
|
|
||||||
|
|
||||||
# Application imports
|
|
||||||
from core import app
|
|
||||||
from core.utils import Logger
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE: Threads WILL NOT die with parent's destruction.
|
|
||||||
def threaded_wrapper(fn):
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
# NOTE: Threads WILL die with parent's destruction.
|
|
||||||
def daemon_threaded_wrapper(fn):
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
def sizeof_fmt_def(num, suffix="B"):
|
|
||||||
for unit in ["", "K", "M", "G", "T", "Pi", "Ei", "Zi"]:
|
|
||||||
if abs(num) < 1024.0:
|
|
||||||
return f"{num:3.1f} {unit}{suffix}"
|
|
||||||
num /= 1024.0
|
|
||||||
return f"{num:.1f} Yi{suffix}"
|
|
||||||
|
|
||||||
|
|
||||||
def _get_file_size(file):
|
|
||||||
return "4K" if isdir(file) else sizeof_fmt_def(os.path.getsize(file))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE: Just reminding myself we can add to builtins two different ways...
|
|
||||||
# __builtins__.update({"event_system": Builtins()})
|
|
||||||
builtins.app_name = "WebFM"
|
|
||||||
builtins.threaded = threaded_wrapper
|
|
||||||
builtins.daemon_threaded = daemon_threaded_wrapper
|
|
||||||
builtins.sizeof_fmt = sizeof_fmt_def
|
|
||||||
builtins.get_file_size = _get_file_size
|
|
||||||
builtins.ROOT_FILE_PTH = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
builtins.BG_IMGS_PATH = ROOT_FILE_PTH + "/static/imgs/backgrounds/"
|
|
||||||
builtins.BG_FILE_TYPE = (".webm", ".mp4", ".gif", ".jpg", ".png", ".webp")
|
|
||||||
builtins.valid_fname_pat = re.compile(r"[a-z0-9A-Z-_\[\]\(\)\| ]{4,20}")
|
|
||||||
builtins.logger = Logger().get_logger()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE: Need threads defined befor instantiating
|
|
||||||
from core.utils.shellfm.windows.controller import WindowController # Get file manager controller
|
|
||||||
window_controllers = {}
|
|
||||||
def _get_view():
|
|
||||||
controller = None
|
|
||||||
try:
|
|
||||||
controller = window_controllers[ session["win_controller_id"] ].get_window_by_index(0).get_tab_by_index(0)
|
|
||||||
except Exception as e:
|
|
||||||
id = secrets.token_hex(16)
|
|
||||||
controller = WindowController()
|
|
||||||
view = controller.create_window().create_tab()
|
|
||||||
|
|
||||||
try:
|
|
||||||
view.ABS_THUMBS_PTH = app.config['ABS_THUMBS_PTH']
|
|
||||||
except Exception as e:
|
|
||||||
print("No ABS_THUMBS_PTH set by WebFM...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
view.REMUX_FOLDER = app.config['REMUX_FOLDER']
|
|
||||||
except Exception as e:
|
|
||||||
print("No REMUX_FOLDER set by WebFM...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
view.FFMPG_THUMBNLR = app.config['FFMPG_THUMBNLR']
|
|
||||||
except Exception as e:
|
|
||||||
print("No FFMPG_THUMBNLR set by WebFM...")
|
|
||||||
|
|
||||||
view.logger = logger
|
|
||||||
|
|
||||||
session['win_controller_id'] = id
|
|
||||||
window_controllers.update( {id: controller } )
|
|
||||||
controller = window_controllers[ session["win_controller_id"] ].get_window_by_index(0).get_tab_by_index(0)
|
|
||||||
|
|
||||||
return controller
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
builtins.get_view = _get_view
|
|
|
@ -1,45 +0,0 @@
|
||||||
# Python imports
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.config.from_object("core.config.ProductionConfig")
|
|
||||||
# app.config.from_object("core.config.DevelopmentConfig")
|
|
||||||
|
|
||||||
# Apoplication imports
|
|
||||||
from .__builtins__ import *
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
oidc = OpenIDConnect(app)
|
|
||||||
login_manager = LoginManager(app)
|
|
||||||
bcrypt = Bcrypt(app)
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
# 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 = f'{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 = f"{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 = f"{STATIC_FPTH}/imgs/thumbnails" # Used for thumbnail generation
|
|
||||||
REMUX_FOLDER = f"{STATIC_FPTH}/remuxs" # Remuxed files folder
|
|
||||||
FFMPG_THUMBNLR = f"{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
|
|
|
@ -1,24 +0,0 @@
|
||||||
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")
|
|
|
@ -1,41 +0,0 @@
|
||||||
# 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}']"
|
|
|
@ -1,119 +0,0 @@
|
||||||
# Python imports
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
# Lib imports
|
|
||||||
from flask import request
|
|
||||||
|
|
||||||
from flask_uploads import ALL
|
|
||||||
from flask_uploads import configure_uploads
|
|
||||||
from flask_uploads import UploadSet
|
|
||||||
|
|
||||||
|
|
||||||
# App imports
|
|
||||||
# Get from __init__
|
|
||||||
from core import app
|
|
||||||
from core import db
|
|
||||||
from core import Favorites
|
|
||||||
from core import oidc
|
|
||||||
|
|
||||||
from core.utils import MessageHandler # Get simple message processor
|
|
||||||
|
|
||||||
json_message = MessageHandler()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/delete/<_hash>', methods=['GET', 'POST'])
|
|
||||||
def delete_item(_hash = None):
|
|
||||||
if request.method == 'POST':
|
|
||||||
msg = "Log in with an Admin privlidged user to delete files!"
|
|
||||||
if not oidc.user_loggedin:
|
|
||||||
return json_message.create("danger", msg)
|
|
||||||
elif oidc.user_loggedin:
|
|
||||||
isAdmin = oidc.user_getfield("isAdmin")
|
|
||||||
if isAdmin != "yes" :
|
|
||||||
return json_message.create("danger", msg)
|
|
||||||
|
|
||||||
view = get_view()
|
|
||||||
folder = view.get_current_directory()
|
|
||||||
file = view.get_path_part_from_hash(_hash)
|
|
||||||
fpath = os.path.join(folder, file)
|
|
||||||
try:
|
|
||||||
msg = f"[Success] Deleted the file/folder -->: {file} !"
|
|
||||||
view.delete_file(fpath)
|
|
||||||
return json_message.create("success", msg)
|
|
||||||
except Exception as e:
|
|
||||||
msg = "[Error] Unable to delete the file/folder...."
|
|
||||||
return json_message.create("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 json_message.create("danger", msg)
|
|
||||||
elif oidc.user_loggedin:
|
|
||||||
isAdmin = oidc.user_getfield("isAdmin")
|
|
||||||
if isAdmin != "yes" :
|
|
||||||
return json_message.create("danger", msg)
|
|
||||||
|
|
||||||
TYPE = _type.strip()
|
|
||||||
if not TYPE in ["dir", "file"]:
|
|
||||||
msg = "Couldn't handle action type for api create..."
|
|
||||||
return json_message.create("danger", msg)
|
|
||||||
|
|
||||||
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 json_message.create("danger", msg)
|
|
||||||
|
|
||||||
try:
|
|
||||||
view = get_view()
|
|
||||||
folder = view.get_current_directory()
|
|
||||||
new_item = f"{folder}/{FNAME}"
|
|
||||||
view.create_file(new_item, TYPE)
|
|
||||||
except Exception as e:
|
|
||||||
print(repr(e))
|
|
||||||
msg = "Couldn't create file/folder. An unexpected error occured..."
|
|
||||||
return json_message.create("danger", msg)
|
|
||||||
|
|
||||||
|
|
||||||
msg = "[Success] created the file/dir..."
|
|
||||||
return json_message.create("success", msg)
|
|
||||||
else:
|
|
||||||
msg = "Can't manage the request type..."
|
|
||||||
return json_message.create("danger", msg)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/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 json_message.create("danger", msg)
|
|
||||||
elif oidc.user_loggedin:
|
|
||||||
isAdmin = oidc.user_getfield("isAdmin")
|
|
||||||
if isAdmin != "yes" :
|
|
||||||
return json_message.create("danger", msg)
|
|
||||||
|
|
||||||
view = get_view()
|
|
||||||
folder = view.get_current_directory()
|
|
||||||
UPLOADS_PTH = f'{folder}/'
|
|
||||||
files = UploadSet('files', ALL, default_dest=lambda x: UPLOADS_PTH)
|
|
||||||
configure_uploads(app, files)
|
|
||||||
|
|
||||||
try:
|
|
||||||
for file in request.files:
|
|
||||||
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 json_message.create("danger", msg)
|
|
||||||
|
|
||||||
msg = "[Success] Uploaded file(s)..."
|
|
||||||
return json_message.create("success", msg)
|
|
||||||
else:
|
|
||||||
msg = "Can't manage the request type..."
|
|
||||||
return json_message.create("danger", msg)
|
|
|
@ -1,71 +0,0 @@
|
||||||
# Python imports
|
|
||||||
|
|
||||||
# Lib imports
|
|
||||||
from flask import request
|
|
||||||
|
|
||||||
# App imports
|
|
||||||
from core import app
|
|
||||||
from core import db
|
|
||||||
from core import Favorites # Get from __init__
|
|
||||||
from core.utils import MessageHandler # Get simple message processor
|
|
||||||
|
|
||||||
|
|
||||||
json_message = MessageHandler()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@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 json_message.faves_list(faves)
|
|
||||||
else:
|
|
||||||
msg = "Can't manage the request type..."
|
|
||||||
return json_message.create("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_view()
|
|
||||||
view.set_path_with_sub_path(fave.link)
|
|
||||||
return '{"refresh": "true"}'
|
|
||||||
except Exception as e:
|
|
||||||
print(repr(e))
|
|
||||||
msg = "Incorrect Favorites ID..."
|
|
||||||
return json_message.create("danger", msg)
|
|
||||||
else:
|
|
||||||
msg = "Can't manage the request type..."
|
|
||||||
return json_message.create("danger", msg)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/manage-favorites/<_action>', methods=['GET', 'POST'])
|
|
||||||
def manageFavorites(_action):
|
|
||||||
if request.method == 'POST':
|
|
||||||
ACTION = _action.strip()
|
|
||||||
view = get_view()
|
|
||||||
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 json_message.create("danger", msg)
|
|
||||||
|
|
||||||
db.session.commit()
|
|
||||||
return json_message.create("success", msg)
|
|
||||||
else:
|
|
||||||
msg = "Can't manage the request type..."
|
|
||||||
return json_message.create("danger", msg)
|
|
|
@ -1,92 +0,0 @@
|
||||||
# Python imports
|
|
||||||
import os
|
|
||||||
import requests
|
|
||||||
import shutil
|
|
||||||
|
|
||||||
# Lib imports
|
|
||||||
from flask import request
|
|
||||||
|
|
||||||
|
|
||||||
# App imports
|
|
||||||
# Get from __init__
|
|
||||||
from core import app
|
|
||||||
|
|
||||||
from core.utils import MessageHandler # Get simple message processor
|
|
||||||
from core.utils.tmdbscraper import scraper # Get media art scraper
|
|
||||||
|
|
||||||
|
|
||||||
json_message = MessageHandler()
|
|
||||||
tmdb = scraper.get_tmdb_scraper()
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/get-background-poster-trailer', methods=['GET', 'POST'])
|
|
||||||
def getPosterTrailer():
|
|
||||||
if request.method == 'GET':
|
|
||||||
info = {}
|
|
||||||
view = get_view()
|
|
||||||
dot_dots = view.get_dot_dots()
|
|
||||||
|
|
||||||
sub_path = view.get_current_sub_path()
|
|
||||||
file = sub_path.split("/")[-1]
|
|
||||||
trailer = None
|
|
||||||
if "(" in file and ")" in file:
|
|
||||||
title = file.split("(")[0].strip()
|
|
||||||
startIndex = file.index('(') + 1
|
|
||||||
endIndex = file.index(')')
|
|
||||||
date = file[startIndex:endIndex]
|
|
||||||
|
|
||||||
try:
|
|
||||||
video_data = tmdb.search(title, date)[0]
|
|
||||||
video_id = video_data["id"]
|
|
||||||
background_url = video_data["backdrop_path"]
|
|
||||||
background_pth = f"{view.get_current_directory()}/000.jpg"
|
|
||||||
|
|
||||||
tmdb_videos = tmdb.tmdbapi.get_movie(str(video_id), append_to_response="videos")["videos"]["results"]
|
|
||||||
for tmdb_video in tmdb_videos:
|
|
||||||
if "YouTube" in tmdb_video["site"]:
|
|
||||||
trailer_key = tmdb_video["key"]
|
|
||||||
trailer = f"https://www.youtube-nocookie.com/embed/{trailer_key}?start=0&autoplay=1";
|
|
||||||
|
|
||||||
if not trailer:
|
|
||||||
raise Exception("No key found. Defering to none...")
|
|
||||||
except Exception as e:
|
|
||||||
print("No trailer found...")
|
|
||||||
trailer = None
|
|
||||||
|
|
||||||
if not os.path.isfile(background_pth):
|
|
||||||
r = requests.get(background_url, stream = True)
|
|
||||||
|
|
||||||
if r.status_code == 200:
|
|
||||||
r.raw.decode_content = True
|
|
||||||
with open(background_pth,'wb') as f:
|
|
||||||
shutil.copyfileobj(r.raw, f)
|
|
||||||
|
|
||||||
view.load_directory()
|
|
||||||
print('Cover Background Image sucessfully retreived...')
|
|
||||||
else:
|
|
||||||
print('Cover Background Image Couldn\'t be retreived...')
|
|
||||||
|
|
||||||
info.update({'trailer': trailer})
|
|
||||||
info.update({'poster': background_url})
|
|
||||||
|
|
||||||
return info
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/backgrounds', methods=['GET', 'POST'])
|
|
||||||
def backgrounds():
|
|
||||||
files = []
|
|
||||||
data = os.listdir(BG_IMGS_PATH)
|
|
||||||
for file in data:
|
|
||||||
if file.lower().endswith(BG_FILE_TYPE):
|
|
||||||
files.append(file)
|
|
||||||
|
|
||||||
return json_message.backgrounds(files)
|
|
||||||
|
|
||||||
@app.route('/api/get-thumbnails', methods=['GET', 'POST'])
|
|
||||||
def getThumbnails():
|
|
||||||
if request.method == 'GET':
|
|
||||||
view = get_view()
|
|
||||||
return json_message.thumbnails( view.get_video_icons() )
|
|
||||||
else:
|
|
||||||
msg = "Can't manage the request type..."
|
|
||||||
return json_message.create("danger", msg)
|
|
|
@ -1,125 +0,0 @@
|
||||||
# Python imports
|
|
||||||
import os
|
|
||||||
|
|
||||||
# Lib imports
|
|
||||||
from flask import redirect
|
|
||||||
from flask import request
|
|
||||||
from flask import render_template
|
|
||||||
from flask import send_from_directory
|
|
||||||
|
|
||||||
# App imports
|
|
||||||
# Get from __init__
|
|
||||||
from core import app
|
|
||||||
from core import db
|
|
||||||
from core import Favorites
|
|
||||||
from core import oidc
|
|
||||||
|
|
||||||
from core.utils import MessageHandler # Get simple message processor
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
json_message = MessageHandler()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/', methods=['GET', 'POST'])
|
|
||||||
def home():
|
|
||||||
if request.method == 'GET':
|
|
||||||
view = get_view()
|
|
||||||
_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_view()
|
|
||||||
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 json_message.create("danger", msg)
|
|
||||||
elif is_locked and oidc.user_loggedin:
|
|
||||||
isAdmin = oidc.user_getfield("isAdmin")
|
|
||||||
if isAdmin != "yes" :
|
|
||||||
return json_message.create("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:
|
|
||||||
view.unset_error_message()
|
|
||||||
return json_message.create("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 json_message.create("danger", msg)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/file-manager-action/<_type>/<_hash>', methods=['GET', 'POST'])
|
|
||||||
def fileManagerAction(_type, _hash = None):
|
|
||||||
view = get_view()
|
|
||||||
|
|
||||||
if _type == "reset-path" and _hash == "None":
|
|
||||||
view.set_to_home()
|
|
||||||
msg = "Returning to home directory..."
|
|
||||||
return json_message.create("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":
|
|
||||||
logger.debug(f"Downloading:\n\tDirectory: {folder}\n\tFile: {file}")
|
|
||||||
return send_from_directory(directory=folder, filename=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.remux_video(_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 json_message.create("success", msg)
|
|
||||||
|
|
||||||
if _type == "remux":
|
|
||||||
stream_target = view.remux_video(_hash, fpath)
|
|
||||||
|
|
||||||
|
|
||||||
# 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 json_message.create("danger", msg)
|
|
||||||
elif oidc.user_loggedin:
|
|
||||||
isAdmin = oidc.user_getfield("isAdmin")
|
|
||||||
if isAdmin != "yes" :
|
|
||||||
return json_message.create("danger", msg)
|
|
||||||
|
|
||||||
|
|
||||||
if _type == "run-locally":
|
|
||||||
msg = "Opened media..."
|
|
||||||
view.open_file_locally(fpath)
|
|
||||||
return json_message.create("success", msg)
|
|
|
@ -1,9 +0,0 @@
|
||||||
from . import Images
|
|
||||||
from . import CRUD
|
|
||||||
from . import Routes
|
|
||||||
from . import Favorites
|
|
||||||
from .pages import Flask_Login
|
|
||||||
from .pages import Flask_Register
|
|
||||||
from .pages import OIDC_Login
|
|
||||||
from .pages import OIDC_Register
|
|
||||||
from .pages import LoginManager
|
|
|
@ -1,37 +0,0 @@
|
||||||
# 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"))
|
|
|
@ -1,30 +0,0 @@
|
||||||
# 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)
|
|
|
@ -1,43 +0,0 @@
|
||||||
# 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"))
|
|
|
@ -1,27 +0,0 @@
|
||||||
# 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'])
|
|
|
@ -1,31 +0,0 @@
|
||||||
# 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)
|
|
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-123" viewBox="0 0 16 16">
|
|
||||||
<path d="M2.873 11.297V4.142H1.699L0 5.379v1.137l1.64-1.18h.06v5.961h1.174Zm3.213-5.09v-.063c0-.618.44-1.169 1.196-1.169.676 0 1.174.44 1.174 1.106 0 .624-.42 1.101-.807 1.526L4.99 10.553v.744h4.78v-.99H6.643v-.069L8.41 8.252c.65-.724 1.237-1.332 1.237-2.27C9.646 4.849 8.723 4 7.308 4c-1.573 0-2.36 1.064-2.36 2.15v.057h1.138Zm6.559 1.883h.786c.823 0 1.374.481 1.379 1.179.01.707-.55 1.216-1.421 1.21-.77-.005-1.326-.419-1.379-.953h-1.095c.042 1.053.938 1.918 2.464 1.918 1.478 0 2.642-.839 2.62-2.144-.02-1.143-.922-1.651-1.551-1.714v-.063c.535-.09 1.347-.66 1.326-1.678-.026-1.053-.933-1.855-2.359-1.845-1.5.005-2.317.88-2.348 1.898h1.116c.032-.498.498-.944 1.206-.944.703 0 1.206.435 1.206 1.07.005.64-.504 1.106-1.2 1.106h-.75v.96Z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 870 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-activity" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M6 2a.5.5 0 0 1 .47.33L10 12.036l1.53-4.208A.5.5 0 0 1 12 7.5h3.5a.5.5 0 0 1 0 1h-3.15l-1.88 5.17a.5.5 0 0 1-.94 0L6 3.964 4.47 8.171A.5.5 0 0 1 4 8.5H.5a.5.5 0 0 1 0-1h3.15l1.88-5.17A.5.5 0 0 1 6 2Z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 367 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-alarm-fill" viewBox="0 0 16 16">
|
|
||||||
<path d="M6 .5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1H9v1.07a7.001 7.001 0 0 1 3.274 12.474l.601.602a.5.5 0 0 1-.707.708l-.746-.746A6.97 6.97 0 0 1 8 16a6.97 6.97 0 0 1-3.422-.892l-.746.746a.5.5 0 0 1-.707-.708l.602-.602A7.001 7.001 0 0 1 7 2.07V1h-.5A.5.5 0 0 1 6 .5zm2.5 5a.5.5 0 0 0-1 0v3.362l-1.429 2.38a.5.5 0 1 0 .858.515l1.5-2.5A.5.5 0 0 0 8.5 9V5.5zM.86 5.387A2.5 2.5 0 1 1 4.387 1.86 8.035 8.035 0 0 0 .86 5.387zM11.613 1.86a2.5 2.5 0 1 1 3.527 3.527 8.035 8.035 0 0 0-3.527-3.527z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 626 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-alarm" viewBox="0 0 16 16">
|
|
||||||
<path d="M8.5 5.5a.5.5 0 0 0-1 0v3.362l-1.429 2.38a.5.5 0 1 0 .858.515l1.5-2.5A.5.5 0 0 0 8.5 9V5.5z"/>
|
|
||||||
<path d="M6.5 0a.5.5 0 0 0 0 1H7v1.07a7.001 7.001 0 0 0-3.273 12.474l-.602.602a.5.5 0 0 0 .707.708l.746-.746A6.97 6.97 0 0 0 8 16a6.97 6.97 0 0 0 3.422-.892l.746.746a.5.5 0 0 0 .707-.708l-.601-.602A7.001 7.001 0 0 0 9 2.07V1h.5a.5.5 0 0 0 0-1h-3zm1.038 3.018a6.093 6.093 0 0 1 .924 0 6 6 0 1 1-.924 0zM0 3.5c0 .753.333 1.429.86 1.887A8.035 8.035 0 0 1 4.387 1.86 2.5 2.5 0 0 0 0 3.5zM13.5 1c-.753 0-1.429.333-1.887.86a8.035 8.035 0 0 1 3.527 3.527A2.5 2.5 0 0 0 13.5 1z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 711 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-align-bottom" viewBox="0 0 16 16">
|
|
||||||
<rect width="4" height="12" x="6" y="1" rx="1"/>
|
|
||||||
<path d="M1.5 14a.5.5 0 0 0 0 1v-1zm13 1a.5.5 0 0 0 0-1v1zm-13 0h13v-1h-13v1z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 271 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-align-center" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 1a.5.5 0 0 1 .5.5V6h-1V1.5A.5.5 0 0 1 8 1zm0 14a.5.5 0 0 1-.5-.5V10h1v4.5a.5.5 0 0 1-.5.5zM2 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 315 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-align-end" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M14.5 1a.5.5 0 0 0-.5.5v13a.5.5 0 0 0 1 0v-13a.5.5 0 0 0-.5-.5z"/>
|
|
||||||
<path d="M13 7a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V7z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 318 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-align-middle" viewBox="0 0 16 16">
|
|
||||||
<path d="M6 13a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1v10zM1 8a.5.5 0 0 0 .5.5H6v-1H1.5A.5.5 0 0 0 1 8zm14 0a.5.5 0 0 1-.5.5H10v-1h4.5a.5.5 0 0 1 .5.5z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 316 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-align-start" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M1.5 1a.5.5 0 0 1 .5.5v13a.5.5 0 0 1-1 0v-13a.5.5 0 0 1 .5-.5z"/>
|
|
||||||
<path d="M3 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 318 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-align-top" viewBox="0 0 16 16">
|
|
||||||
<rect width="4" height="12" rx="1" transform="matrix(1 0 0 -1 6 15)"/>
|
|
||||||
<path d="M1.5 2a.5.5 0 0 1 0-1v1zm13-1a.5.5 0 0 1 0 1V1zm-13 0h13v1h-13V1z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 287 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-alt" viewBox="0 0 16 16">
|
|
||||||
<path d="M1 13.5a.5.5 0 0 0 .5.5h3.797a.5.5 0 0 0 .439-.26L11 3h3.5a.5.5 0 0 0 0-1h-3.797a.5.5 0 0 0-.439.26L5 13H1.5a.5.5 0 0 0-.5.5zm10 0a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 0-1h-3a.5.5 0 0 0-.5.5z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 326 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-app-indicator" viewBox="0 0 16 16">
|
|
||||||
<path d="M5.5 2A3.5 3.5 0 0 0 2 5.5v5A3.5 3.5 0 0 0 5.5 14h5a3.5 3.5 0 0 0 3.5-3.5V8a.5.5 0 0 1 1 0v2.5a4.5 4.5 0 0 1-4.5 4.5h-5A4.5 4.5 0 0 1 1 10.5v-5A4.5 4.5 0 0 1 5.5 1H8a.5.5 0 0 1 0 1H5.5z"/>
|
|
||||||
<path d="M16 3a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 387 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-app" viewBox="0 0 16 16">
|
|
||||||
<path d="M11 2a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h6zM5 1a4 4 0 0 0-4 4v6a4 4 0 0 0 4 4h6a4 4 0 0 0 4-4V5a4 4 0 0 0-4-4H5z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 282 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-apple" viewBox="0 0 16 16">
|
|
||||||
<path d="M11.182.008C11.148-.03 9.923.023 8.857 1.18c-1.066 1.156-.902 2.482-.878 2.516.024.034 1.52.087 2.475-1.258.955-1.345.762-2.391.728-2.43zm3.314 11.733c-.048-.096-2.325-1.234-2.113-3.422.212-2.189 1.675-2.789 1.698-2.854.023-.065-.597-.79-1.254-1.157a3.692 3.692 0 0 0-1.563-.434c-.108-.003-.483-.095-1.254.116-.508.139-1.653.589-1.968.607-.316.018-1.256-.522-2.267-.665-.647-.125-1.333.131-1.824.328-.49.196-1.422.754-2.074 2.237-.652 1.482-.311 3.83-.067 4.56.244.729.625 1.924 1.273 2.796.576.984 1.34 1.667 1.659 1.899.319.232 1.219.386 1.843.067.502-.308 1.408-.485 1.766-.472.357.013 1.061.154 1.782.539.571.197 1.111.115 1.652-.105.541-.221 1.324-1.059 2.238-2.758.347-.79.505-1.217.473-1.282z"/>
|
|
||||||
<path d="M11.182.008C11.148-.03 9.923.023 8.857 1.18c-1.066 1.156-.902 2.482-.878 2.516.024.034 1.52.087 2.475-1.258.955-1.345.762-2.391.728-2.43zm3.314 11.733c-.048-.096-2.325-1.234-2.113-3.422.212-2.189 1.675-2.789 1.698-2.854.023-.065-.597-.79-1.254-1.157a3.692 3.692 0 0 0-1.563-.434c-.108-.003-.483-.095-1.254.116-.508.139-1.653.589-1.968.607-.316.018-1.256-.522-2.267-.665-.647-.125-1.333.131-1.824.328-.49.196-1.422.754-2.074 2.237-.652 1.482-.311 3.83-.067 4.56.244.729.625 1.924 1.273 2.796.576.984 1.34 1.667 1.659 1.899.319.232 1.219.386 1.843.067.502-.308 1.408-.485 1.766-.472.357.013 1.061.154 1.782.539.571.197 1.111.115 1.652-.105.541-.221 1.324-1.059 2.238-2.758.347-.79.505-1.217.473-1.282z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.5 KiB |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-archive-fill" viewBox="0 0 16 16">
|
|
||||||
<path d="M12.643 15C13.979 15 15 13.845 15 12.5V5H1v7.5C1 13.845 2.021 15 3.357 15h9.286zM5.5 7h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1 0-1zM.8 1a.8.8 0 0 0-.8.8V3a.8.8 0 0 0 .8.8h14.4A.8.8 0 0 0 16 3V1.8a.8.8 0 0 0-.8-.8H.8z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 359 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-archive" viewBox="0 0 16 16">
|
|
||||||
<path d="M0 2a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1v7.5a2.5 2.5 0 0 1-2.5 2.5h-9A2.5 2.5 0 0 1 1 12.5V5a1 1 0 0 1-1-1V2zm2 3v7.5A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5V5H2zm13-3H1v2h14V2zM5 7.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 401 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-90deg-down" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M4.854 14.854a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L4 13.293V3.5A2.5 2.5 0 0 1 6.5 1h8a.5.5 0 0 1 0 1h-8A1.5 1.5 0 0 0 5 3.5v9.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 350 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-90deg-left" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M1.146 4.854a.5.5 0 0 1 0-.708l4-4a.5.5 0 1 1 .708.708L2.707 4H12.5A2.5 2.5 0 0 1 15 6.5v8a.5.5 0 0 1-1 0v-8A1.5 1.5 0 0 0 12.5 5H2.707l3.147 3.146a.5.5 0 1 1-.708.708l-4-4z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 349 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-90deg-right" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M14.854 4.854a.5.5 0 0 0 0-.708l-4-4a.5.5 0 0 0-.708.708L13.293 4H3.5A2.5 2.5 0 0 0 1 6.5v8a.5.5 0 0 0 1 0v-8A1.5 1.5 0 0 1 3.5 5h9.793l-3.147 3.146a.5.5 0 0 0 .708.708l4-4z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 350 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-90deg-up" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M4.854 1.146a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L4 2.707V12.5A2.5 2.5 0 0 0 6.5 15h8a.5.5 0 0 0 0-1h-8A1.5 1.5 0 0 1 5 12.5V2.707l3.146 3.147a.5.5 0 1 0 .708-.708l-4-4z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 349 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-bar-down" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M1 3.5a.5.5 0 0 1 .5-.5h13a.5.5 0 0 1 0 1h-13a.5.5 0 0 1-.5-.5zM8 6a.5.5 0 0 1 .5.5v5.793l2.146-2.147a.5.5 0 0 1 .708.708l-3 3a.5.5 0 0 1-.708 0l-3-3a.5.5 0 0 1 .708-.708L7.5 12.293V6.5A.5.5 0 0 1 8 6z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 375 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-bar-left" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M12.5 15a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5zM10 8a.5.5 0 0 1-.5.5H3.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L3.707 7.5H9.5a.5.5 0 0 1 .5.5z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 375 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-bar-right" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M6 8a.5.5 0 0 0 .5.5h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708.708L12.293 7.5H6.5A.5.5 0 0 0 6 8zm-2.5 7a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 375 B |