Initial push of flask version.
Before Width: | Height: | Size: 386 KiB |
BIN
Images/000.jpg
Before Width: | Height: | Size: 210 KiB |
BIN
Images/pic1.png
Before Width: | Height: | Size: 1.4 MiB |
BIN
Images/pic2.png
Before Width: | Height: | Size: 2.0 MiB |
BIN
Images/pic3.png
Before Width: | Height: | Size: 1.8 MiB |
BIN
Images/pic4.png
Before Width: | Height: | Size: 2.1 MiB |
BIN
Images/pic5.png
Before Width: | Height: | Size: 2.0 MiB |
BIN
Images/pic6.png
Before Width: | Height: | Size: 2.4 MiB |
339
LICENSE
@ -1,339 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
BIN
Music/000.jpg
Before Width: | Height: | Size: 1019 KiB |
33
README.md
@ -1,33 +0,0 @@
|
||||
# WebFM
|
||||
WebFM is a media and file viewer aspiring to become a full fledged file manager in the browser.
|
||||
|
||||
# Usage
|
||||
1. Install php7, php-sqlite3, and ffmpeg on the system this will be on.
|
||||
2. Use php -S 0.0.0.0:yourDesiredPort
|
||||
3. Use ufw or gufw to open the port on your computer to the network.
|
||||
4. Place files or start uploading some to the folders.
|
||||
5. Double click thumbnails and container outlines to open files.
|
||||
6. Double click the text name to change the file's or folder's name and press enter to set it.
|
||||
7. Right-click to get context menu options.
|
||||
8. Place an image such as a jpg, png, or gif labeled "000.itsExtension" in a directory then the viewer will use it as the background image for that folder/directory.
|
||||
9. Password protect folder based on resources/php/config.php file setting.
|
||||
10. Save paths to favorites list for quick access.
|
||||
|
||||
Notes:
|
||||
1. The provided folders except "resources" are optional. You can add and remove them as you please.
|
||||
2. The media and image pane can be moved by dragging from the transparentish bar that has the close button and other controls.
|
||||
3. Edit the resources/php/config.php file and put your own programs there.
|
||||
4. Edit your php.ini file "upload_max_filesize" and "post_max_size" to be higher to upload larger files.
|
||||
|
||||
# TO-DO
|
||||
1. Allow for move and copy.
|
||||
2. Implement themes functionality.
|
||||
|
||||
|
||||
# Images
|
||||
![1 Home](Images/pic1.png)
|
||||
![2 Images Listed](Images/pic2.png)
|
||||
![3 Videos Listed](Images/pic3.png)
|
||||
![4 Image Open](Images/pic4.png)
|
||||
![5 Image Open And Video Playing](Images/pic5.png)
|
||||
![6 Alternate Background](Images/pic6.png)
|
BIN
Videos/000.jpg
Before Width: | Height: | Size: 1.9 MiB |
@ -1 +0,0 @@
|
||||
LOL...Not really!
|
120
index.html
@ -1,120 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta content="" charset="utf-8"/>
|
||||
<title>Web File Manager</title>
|
||||
<link type="text/css" rel="stylesheet" href="resources/css/base.css"/>
|
||||
<link type="text/css" rel="stylesheet" href="resources/css/main.css"/>
|
||||
<link rel="shortcut icon" type="image/png" href="favicon.png"/>
|
||||
</head>
|
||||
<body onload="onloadSetBG(); getDir('/')" contextmenu="menu">
|
||||
<!-- Background -->
|
||||
<img id="bg" />
|
||||
|
||||
<video id="video" src="" controls >
|
||||
</video>
|
||||
|
||||
<!-- Controls -->
|
||||
<!-- Create the menu -->
|
||||
<menu type="context" id="menu">
|
||||
<menuitem label="Home Directory" onclick="clearDirCookie()"></menuitem>
|
||||
<menuitem label="Show Server Messages" onclick="tgglElmView('serverMsgView')"></menuitem>
|
||||
<menuitem label="Clear Upload List" onclick="clearDlList()"></menuitem>
|
||||
<menuitem label="Download" onclick="downloadItem()"></menuitem>
|
||||
<menuitem label="Delete" onclick="deleteItem()"></menuitem>
|
||||
</menu>
|
||||
|
||||
<!-- Uploader -->
|
||||
<h2 id="controls">
|
||||
<button type="button" title="Other Options" onclick="tgglElmView('popOutControls')">⚙</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>
|
9
requirements.txt
Normal file
@ -0,0 +1,9 @@
|
||||
Click==7.0
|
||||
Flask==1.1.1
|
||||
Flask-SQLAlchemy==2.4.1
|
||||
gunicorn==19.9.0
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.10.3
|
||||
MarkupSafe==1.1.1
|
||||
SQLAlchemy==1.3.11
|
||||
Werkzeug==0.16.0
|
@ -1,8 +0,0 @@
|
||||
html {
|
||||
margin: 0em;
|
||||
padding: 0em;
|
||||
}
|
||||
|
||||
ol, ul, li {
|
||||
list-style: none;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
#controls, #fullPathHeader, #dynDiv,
|
||||
.errorStyling, .dirStyle, .movieStyle, .fileStyle {
|
||||
background-color: rgba(0,0,0,0.2);
|
||||
}
|
@ -1,304 +0,0 @@
|
||||
/* IDs */
|
||||
|
||||
#DIRPATHUL {
|
||||
display: none;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
#video,
|
||||
#bg {
|
||||
position: fixed;
|
||||
top: 0%;
|
||||
left: 0%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -999;
|
||||
}
|
||||
|
||||
#video,
|
||||
#bg img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -999;
|
||||
}
|
||||
|
||||
#video {
|
||||
position: fixed;
|
||||
display: none;
|
||||
background-color: rgba(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
#controls, #dynUl,
|
||||
.errorStyling, .dirStyle, .movieStyle, .fileStyle {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
overflow: auto;
|
||||
padding-bottom: 0.5em;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
background-color: rgba(0,0,0,0.64);
|
||||
}
|
||||
|
||||
#favesList {
|
||||
border-style: solid;
|
||||
border-color: rgba(0, 0, 0, 0.5);
|
||||
border-width: 0.2em;
|
||||
background-color: rgba(7, 150, 159, 0.8);
|
||||
position: fixed;
|
||||
font-size: 2em;
|
||||
overflow-x: auto;
|
||||
overflow-y: scroll;
|
||||
padding: 1.5em;
|
||||
max-height: 632px;
|
||||
color: #ffffff;
|
||||
z-index: 888;
|
||||
}
|
||||
|
||||
#favesList > li:hover {
|
||||
cursor: pointer;
|
||||
background-color: rgba(92, 199, 35, 0.8);
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
#controls {
|
||||
display: block;
|
||||
position: fixed;
|
||||
z-index: 999;
|
||||
top: 0em;
|
||||
}
|
||||
|
||||
#dynUl {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(18em, 1fr));
|
||||
grid-column-gap: 1em;
|
||||
grid-row-gap: 1em;
|
||||
margin: 5em auto;
|
||||
width: 85%;
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
#imgView, #imgArea, #fileView {
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
#imgView, #fileView {
|
||||
position: fixed;
|
||||
bottom: 0em;
|
||||
z-index: 100;
|
||||
border-style: solid;
|
||||
border-color: rgb(114,184,199);
|
||||
|
||||
}
|
||||
|
||||
#fileView {
|
||||
display: block;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#fileViewInner {
|
||||
position: sticky;
|
||||
display: inline;
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#imgArea {
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#imgView {
|
||||
overflow: hidden;
|
||||
left: 15em;
|
||||
}
|
||||
|
||||
#NewItem {
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#popOutControls {
|
||||
position: fixed;
|
||||
top: 15%;
|
||||
width: 99%;
|
||||
height: 15em;
|
||||
padding-top: 6em;
|
||||
opacity: 0.94;
|
||||
background: radial-gradient(circle,#3f3f3f,#000000);
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
#serverMsgView {
|
||||
position: fixed;
|
||||
bottom: 0em;
|
||||
height: 5em;
|
||||
overflow-y: scroll;
|
||||
width: 100%;
|
||||
background-color: rgba(0,0,0,0.64);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
#searchField {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#searchField:focus {
|
||||
height: 2em;
|
||||
border-style: solid;
|
||||
border-width: thin;
|
||||
border-color: rgba(55, 204, 209, 1);
|
||||
}
|
||||
|
||||
/* Classes */
|
||||
.imgViewImg {
|
||||
width: inherit;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
||||
.dirStyle { background-color: rgba(0, 0, 0, 0.56); }
|
||||
.movieStyle, .fileStyle { background-color: rgba(101, 101, 101, 0.56); }
|
||||
|
||||
.movieStyle {
|
||||
min-height: 6.5em;
|
||||
overflow: hidden;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.videoInputField {
|
||||
width: 100%;
|
||||
margin-top: 5.5em;
|
||||
background-color: rgba(0, 0, 0, 0.64);
|
||||
color: rgb(255, 255, 255);
|
||||
text-align: center;
|
||||
border-top: 1px solid rgb(255, 255, 255);
|
||||
border-bottom: 1px solid rgb(255, 255, 255);
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.dirStyle:hover, .movieStyle:hover, .fileStyle:hover {
|
||||
background-color: rgba(0, 141, 166, 0.56);
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 0px 15px rgb(114,184,199);
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
.dirStyle:focus, .movieStyle:focus, .fileStyle:focus {
|
||||
background-color: rgba(0, 139, 35, 0.76);
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 0px 25px rgb(114, 199, 120);
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
.dirTitle, .fileTitle, .movieTitle {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
border-style: none;
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
.dirTitle, .fileTitle {
|
||||
width: auto;
|
||||
background-color: #00000000;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.movieTitle {
|
||||
width: 18em;
|
||||
background-color: #ffffff00;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
width: 12em;
|
||||
height: 6.5em;
|
||||
}
|
||||
|
||||
.systemIcon {
|
||||
width: 2em;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.iconImg {
|
||||
width: 18em;
|
||||
height: 12em;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.popOutBttn, .closeBttn {
|
||||
float: right;
|
||||
z-index: 2;
|
||||
width: 4em;
|
||||
height: 4em;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 4em; /* the same as your div height */
|
||||
background-color: rgba(0,0,0, 0.85);
|
||||
color: rgb(255,255,255);
|
||||
border-style:solid;
|
||||
border-color: rgb(255,255,255);
|
||||
}
|
||||
|
||||
.popOutBttnInner {
|
||||
float: right;
|
||||
z-index: 2;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
text-align: center;
|
||||
background-color: rgba(0,0,0, 0.85);
|
||||
color: rgb(255,255,255);
|
||||
border-style:solid;
|
||||
border-color: rgb(255,255,255);
|
||||
}
|
||||
|
||||
|
||||
.completionBar {
|
||||
float:left;
|
||||
clear:left;
|
||||
height: 0.1em;
|
||||
background-color: rgba(25, 125, 10, 1.0);
|
||||
}
|
||||
|
||||
/* Hover events */
|
||||
.dirTitle:hover,
|
||||
.iconImg:hover,
|
||||
.closeBttn:hover,
|
||||
.popOutBttnInner:hover,
|
||||
.popOutBttn:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.popOutBttnInner:hover,
|
||||
.popOutBttn:hover,
|
||||
.closeBttn:hover {
|
||||
background-color: rgba(255,255,255, 0.85);
|
||||
color: #000000;
|
||||
border-color: #000000;
|
||||
}
|
||||
|
||||
/* Messages coloring */
|
||||
.error, .warnning, .success {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.error { color: rgb(255, 0, 0); }
|
||||
.warning { color: rgb(255, 168, 0); }
|
||||
.success { color: rgb(136, 204, 39); }
|
Before Width: | Height: | Size: 7.4 KiB |
@ -1 +0,0 @@
|
||||
Place Holder File
|
@ -1,54 +0,0 @@
|
||||
// SSE events if supported
|
||||
if(typeof(EventSource) !== "undefined") {
|
||||
let source = new EventSource("resources/php/sse.php");
|
||||
source.onmessage = (event) => {
|
||||
if (event.data === "updateListing") {
|
||||
getDir("./");
|
||||
}
|
||||
};
|
||||
} else {
|
||||
console.log("SSE Not Supported In Browser...");
|
||||
}
|
||||
|
||||
const getFavesList = () => {
|
||||
doAjax("resources/php/dbController.php", "getTabs=true");
|
||||
}
|
||||
|
||||
const doAjax = async (actionPath, data) => {
|
||||
let xhttp = new XMLHttpRequest();
|
||||
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState === 4 && this.status === 200) {
|
||||
// Send the returned data to further process
|
||||
if (this.responseText != null) {
|
||||
handleJSONReturnData(JSON.parse(this.responseText));
|
||||
} else {
|
||||
document.getElementById('dynUl').innerHTML =
|
||||
"<p class=\"error\" style=\"width:100%;text-align:center;\"> "
|
||||
+ "No content returned. Check the folder path.</p>";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
xhttp.open("POST", actionPath, true);
|
||||
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xhttp.overrideMimeType('application/json'); // Force return to be JSON
|
||||
xhttp.send(data);
|
||||
}
|
||||
|
||||
const fileUploader = (data) => {
|
||||
let xhttp = new XMLHttpRequest();
|
||||
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState === 4 && this.status === 200) {
|
||||
// Send the returned data to further process
|
||||
if (this.responseXML != null) {
|
||||
handleXMLReturnData(this.responseXML);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
xhttp.open("POST", "resources/php/filesystemActions.php", true);
|
||||
xhttp.overrideMimeType('application/xml'); // Force return to be XML
|
||||
xhttp.send(data);
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
const getCookie = (cname) => {
|
||||
let decodedCookie = decodeURIComponent(document.cookie);
|
||||
let name = cname + "=";
|
||||
let ca = decodedCookie.split(';');
|
||||
for(let i = 0; i <ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
const faveManager = (elm) => {
|
||||
let path = document.getElementById("path").innerHTML;
|
||||
let data = "";
|
||||
|
||||
if (elm.style.backgroundColor != "") {
|
||||
elm.style.backgroundColor = "";
|
||||
elm.style.color = "";
|
||||
data = "deleteLink=true";
|
||||
} else {
|
||||
elm.style.backgroundColor = "rgb(255, 255, 255)";
|
||||
elm.style.color = "rgb(0, 0, 0)";
|
||||
data = "deleteLink=false";
|
||||
}
|
||||
|
||||
data += "&linkPath=" + path;
|
||||
doAjax("resources/php/dbController.php", data);
|
||||
}
|
||||
|
||||
// Basically resetting path nodes and setting them up
|
||||
// to the new path and just doing a refresh
|
||||
const loadFave = (elm) => {
|
||||
let path = elm.getAttribute("name");
|
||||
let parts = path.split("/");
|
||||
let size = parts.length;
|
||||
pathNodes = [];
|
||||
|
||||
pathNodes.push(parts[0] + "/");
|
||||
for (let i = 1; i < size - 1; i++) {
|
||||
pathNodes.push(parts[i] + "/");
|
||||
}
|
||||
pathNodes.push(parts[size - 1]);
|
||||
|
||||
getDir("./");
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
let binary = null;
|
||||
let pathNodes = [];
|
||||
|
||||
const lockFolders = () => {
|
||||
const data = "lockFolders=true";
|
||||
doAjax("resources/php/lockedFolders.php", data);
|
||||
getDir("./");
|
||||
}
|
||||
|
||||
const getDir = (query) => {
|
||||
document.getElementById("controls").style.opacity = "1";
|
||||
document.getElementById("dynUl").style.display = "grid";
|
||||
document.getElementById("video").src = "#";
|
||||
document.getElementById("video").style.display = "none";
|
||||
|
||||
let formUlPth = document.getElementById("DIRPATHUL");
|
||||
let mergeType = document.getElementById("MergeType");
|
||||
let passwd = undefined;
|
||||
let data = "";
|
||||
let cookies = "";
|
||||
let dirCookie = "";
|
||||
|
||||
// push or pop to path list
|
||||
if (query === "/") {
|
||||
// Process path from cookie and set to array/list
|
||||
dirCookie = getCookie("dirQuery");
|
||||
if (dirCookie != "" && dirCookie != "./") {
|
||||
dirCookie = dirCookie.split("/");
|
||||
dirCookie.pop(); // account for ending empty slot
|
||||
|
||||
let size = dirCookie.length;
|
||||
for (var i = 0; i < size; i++) {
|
||||
pathNodes.push(dirCookie[i] + "/");
|
||||
}
|
||||
} else {
|
||||
pathNodes = [];
|
||||
pathNodes.push("." + query);
|
||||
}
|
||||
} else if (query === "../") {
|
||||
// Only remove while not in root
|
||||
if (pathNodes.length > 1) {
|
||||
pathNodes.pop();
|
||||
}
|
||||
} else if (query === "./") {
|
||||
// Do nothing since re-scanning dir
|
||||
} else {
|
||||
pathNodes.push(query); // Add path
|
||||
}
|
||||
|
||||
// Create path from array of items
|
||||
for (pathNode of pathNodes) { data += pathNode; }
|
||||
|
||||
try {
|
||||
passwd = document.getElementById("PASSWD").value;
|
||||
} catch (e) {
|
||||
passwd = "";
|
||||
}
|
||||
|
||||
// Setup upload path for form and make a cookie for persistence during browser session....
|
||||
formUlPth.value = data;
|
||||
data = "dirQuery=" + encodeURIComponent(data);
|
||||
document.cookie = data + "; expires=Sun, 31 Dec 2034 12:00:00 UTC";
|
||||
data +="&mergeType=" + mergeType.checked
|
||||
+ "&passwd=" + passwd;
|
||||
|
||||
doAjax("resources/php/getDirList.php", data);
|
||||
}
|
||||
|
||||
const uploadFiles = async () => {
|
||||
let toUpload = document.getElementsByName("filesToUpload[]")[0];
|
||||
let path = document.getElementById("path").innerHTML;
|
||||
let reader = new FileReader();
|
||||
let data = new FormData();
|
||||
let size = toUpload.files.length;
|
||||
|
||||
data.append("UploadFiles", "trut");
|
||||
data.append("DIRPATHUL", path);
|
||||
|
||||
// Add files
|
||||
if (size > 0) {
|
||||
for (let i = 0; i < size; i++) {
|
||||
data.append("filesToUpload[]", toUpload.files[i]);
|
||||
}
|
||||
fileUploader(data);
|
||||
}
|
||||
}
|
||||
|
||||
const createItem = (type) => {
|
||||
let path = document.getElementById("path").innerHTML;
|
||||
let newItem = document.getElementById("NewItem");
|
||||
let fullPth = path + newItem.value;
|
||||
newItem.value = "";
|
||||
fullPth = encodeURIComponent(fullPth);
|
||||
|
||||
doAjax("resources/php/filesystemActions.php",
|
||||
"createItem=true&item=" + fullPth + "&type=" + type);
|
||||
}
|
||||
|
||||
const deleteItem = () => {
|
||||
let path = document.getElementById("path").innerHTML;
|
||||
// Clicked yes to delete and there is an item
|
||||
if (itemObj != undefined && itemObj != null) {
|
||||
let fullPth = path + itemObj;
|
||||
fullPth = encodeURIComponent(fullPth);
|
||||
let answer = confirm("Are you sure you want to delete: " + fullPth);
|
||||
if (answer == true) {
|
||||
doAjax("resources/php/filesystemActions.php",
|
||||
"deleteItem=true&item=" + fullPth);
|
||||
|
||||
console.log("Deleted: " + fullPth);
|
||||
itemObj = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const renameItem = (obj) => {
|
||||
let path = encodeURIComponent(document.getElementById("path").innerHTML);
|
||||
let oldName = encodeURIComponent(formerFileName);
|
||||
let newName = encodeURIComponent(obj.value);
|
||||
let formData = "renameItem=true&oldName=" + oldName + "&newName=" + newName + "&path=" + path;
|
||||
|
||||
console.log("Old name: " + oldName);
|
||||
console.log("New name: " + newName);
|
||||
|
||||
doAjax("resources/php/filesystemActions.php",
|
||||
formData);
|
||||
}
|
||||
|
||||
const openInLocalProg = (media) => {
|
||||
doAjax("resources/php/filesystemActions.php",
|
||||
"media=" + media);
|
||||
}
|
@ -1,167 +0,0 @@
|
||||
const insertArea = document.getElementById('dynUl');
|
||||
|
||||
|
||||
const handleJSONReturnData = (data) => {
|
||||
if (data.message) {
|
||||
if (data.message.type == "locked") {
|
||||
createPassField();
|
||||
} else {
|
||||
const text = document.createTextNode(data.message.text)
|
||||
document.getElementById("serverMsgView").appendChild(text);
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
if (data.list) {
|
||||
updateHTMLDirList(data);
|
||||
} else if (data.FAVES_LIST) {
|
||||
generateFavesList(data.FAVES_LIST);
|
||||
}
|
||||
}
|
||||
|
||||
const generateFavesList = (data) => {
|
||||
let listView = document.getElementById("favesList");
|
||||
listView.innerHTML = "";
|
||||
|
||||
data.forEach(fave => {
|
||||
let liTag = document.createElement("LI");
|
||||
let parts = (fave.includes("/")) ? fave.split("/") : fave.split("\\");
|
||||
let txtNode = document.createTextNode(parts[parts.length - 2]);
|
||||
|
||||
liTag.setAttribute("name", fave);
|
||||
liTag.setAttribute("title", fave);
|
||||
liTag.setAttribute("onclick", "loadFave(this)");
|
||||
liTag.appendChild(txtNode);
|
||||
listView.appendChild(liTag);
|
||||
});
|
||||
}
|
||||
|
||||
const updateHTMLDirList = async (data) => {
|
||||
var dirTemplate = document.querySelector('#dirTemplate');
|
||||
var vidTemplate = document.querySelector('#vidTemplate');
|
||||
var imgTemplate = document.querySelector('#imgTemplate');
|
||||
var filTemplate = document.querySelector('#filTemplate');
|
||||
let dirPath = data.PATH_HEAD;
|
||||
let isInFaves = data.IN_FAVE;
|
||||
let dirs = (data.list.dirs) ? data.list.dirs : [];
|
||||
let videos = (data.list.vids) ? data.list.vids : [];
|
||||
let images = (data.list.imgs) ? data.list.imgs : [];
|
||||
let files = (data.list.files) ? data.list.files : [];
|
||||
let i = 0;
|
||||
let size = 0;
|
||||
|
||||
document.getElementById("path").innerHTML = dirPath;
|
||||
insertArea.innerHTML = "";
|
||||
|
||||
// Setup background if there is a 000.* in selection
|
||||
let bgImgPth = images[0] ? images[0].image : "";
|
||||
if (bgImgPth.match(/000\.(jpg|png|gif)\b/) != null) {
|
||||
updateBG(dirPath + bgImgPth);
|
||||
} else {
|
||||
updateBG("resources/images/backgrounds/000.jpg");
|
||||
}
|
||||
|
||||
// determin whether to style faves or not
|
||||
let elm = document.getElementById("faves");
|
||||
if (isInFaves == "true") {
|
||||
elm.style.backgroundColor = "rgb(255, 255, 255)";
|
||||
elm.style.color = "rgb(0, 0, 0)";
|
||||
} else {
|
||||
elm.style.backgroundColor = "";
|
||||
elm.style.color = "";
|
||||
}
|
||||
|
||||
// Insert dirs
|
||||
let dirClone = document.importNode(dirTemplate.content, true);
|
||||
let dirImg = "resources/images/icons/folder.png";
|
||||
let dir = null;
|
||||
size = dirs.length;
|
||||
for (; i < size; i++) {
|
||||
dir = dirs[i].dir;
|
||||
const clone = dirClone.cloneNode(true);
|
||||
createElmBlock(clone, dirImg, dir);
|
||||
}
|
||||
|
||||
// Insert videos
|
||||
let vidClone = document.importNode(vidTemplate.content, true);
|
||||
let thumbnail = "";
|
||||
let title = "";
|
||||
size = videos.length;
|
||||
for (i = 0; i < size; i++) {
|
||||
title = videos[i].video.title;
|
||||
thumbnail = videos[i].video.thumbnail;
|
||||
const clone = vidClone.cloneNode(true);
|
||||
createElmBlock(clone, thumbnail, title, true, dirPath);
|
||||
}
|
||||
|
||||
// Insert images
|
||||
let imgClone = document.importNode(imgTemplate.content, true);
|
||||
thumbnail = "";
|
||||
size = images.length;
|
||||
for (i = 0; i < size; i++) {
|
||||
thumbnail = images[i].image;
|
||||
if (thumbnail.match(/000\.(jpg|png|gif)\b/) == null &&
|
||||
!thumbnail.includes("favicon.png")) {
|
||||
const clone = imgClone.cloneNode(true);
|
||||
let imgTag = clone.firstElementChild;
|
||||
imgTag.src = dirPath + '/' + thumbnail;
|
||||
imgTag.alt = thumbnail;
|
||||
insertArea.appendChild(clone);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert files
|
||||
let fileClone = document.importNode(filTemplate.content, true);
|
||||
size = files.length;
|
||||
for (i = 0; i < size; i++) {
|
||||
const clone = fileClone.cloneNode(true);
|
||||
let fileName = files[i].file;
|
||||
createElmBlock(clone, setFileIconType(fileName), fileName);
|
||||
}
|
||||
}
|
||||
|
||||
const createElmBlock = (elm, imgSrc, fileName, isVideo = null, path = null) => {
|
||||
contnrTag = elm.firstElementChild;
|
||||
let imgTag = null;
|
||||
let inputTag = elm.querySelector("input");
|
||||
|
||||
if (isVideo) {
|
||||
contnrTag.style = "background-image: url('/resources/images/thumbnails/" + imgSrc + "')";
|
||||
inputTag.className = "videoInputField";
|
||||
let fullMedia = path + fileName;
|
||||
elm.querySelector("span").addEventListener("click", function (eve) {
|
||||
openInLocalProg(fullMedia);
|
||||
});
|
||||
} else {
|
||||
imgTag = elm.querySelector("img");
|
||||
imgTag.src = imgSrc;
|
||||
imgTag.alt = fileName;
|
||||
}
|
||||
|
||||
contnrTag.title = fileName;
|
||||
inputTag.value = fileName;
|
||||
inputTag.addEventListener("focusout", function (eve) {
|
||||
disableEdits(eve.target);
|
||||
});
|
||||
insertArea.appendChild(elm);
|
||||
}
|
||||
|
||||
const setFileIconType = (fileName) => {
|
||||
if (fileName.match(/\.(doc|docx|xls|xlsx|rtf)\b/) != null) {
|
||||
return "resources/images/icons/doc.png";
|
||||
} else if (fileName.match(/\.(7z|7zip|zip|tar.gz|tar.xz|gz|rar|jar)\b/) != null) {
|
||||
return "resources/images/icons/arc.png";
|
||||
} else if (fileName.match(/\.(pdf)\b/) != null) {
|
||||
return "resources/images/icons/pdf.png";
|
||||
} else if (fileName.match(/\.(html)\b/) != null) {
|
||||
return "resources/images/icons/html.png";
|
||||
} else if (fileName.match(/\.(txt|conf)\b/) != null) {
|
||||
return "resources/images/icons/text.png";
|
||||
} else if (fileName.match(/\.(iso|img)\b/) != null) {
|
||||
return "resources/images/icons/img.png";
|
||||
} else if (fileName.match(/\.(sh|batch|exe)\b/) != null) {
|
||||
return "resources/images/icons/scrip.png";
|
||||
} else {
|
||||
return "resources/images/icons/bin.png";
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
const createPassField = () => {
|
||||
let passField = document.createElement("INPUT");
|
||||
let submitBttn = document.createElement("BUTTON");
|
||||
passField.id = "PASSWD";
|
||||
passField.type = "password";
|
||||
passField.placeholder = "Password...";
|
||||
submitBttn.innerHTML = "Submit";
|
||||
insertArea.innerHTML = "";
|
||||
|
||||
passField.onkeyup = (eve) => {
|
||||
if (eve.key == "Enter") {
|
||||
getDir("./");
|
||||
}
|
||||
};
|
||||
|
||||
submitBttn.onclick = () => {
|
||||
getDir("./");
|
||||
};
|
||||
|
||||
insertArea.appendChild(passField);
|
||||
insertArea.appendChild(submitBttn);
|
||||
}
|
@ -1,189 +0,0 @@
|
||||
let formerFileName = "";
|
||||
|
||||
const tgglElmView = (id) => {
|
||||
let elm = document.getElementById(id);
|
||||
if (elm.style.display == "none") {
|
||||
elm.style.display = "block";
|
||||
} else {
|
||||
elm.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
const searchPage = (elm) => {
|
||||
let query = elm.value.toLowerCase();
|
||||
let list = document.getElementById("dynUl").querySelectorAll("[title]");
|
||||
let size = list.length;
|
||||
|
||||
for (var i = 0; i < size; i++) {
|
||||
if (!list[i].title.toLowerCase().includes(query)) {
|
||||
list[i].style.display = "none";
|
||||
} else {
|
||||
list[i].style.display = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const clearSearch = () => {
|
||||
let list = document.getElementById("dynUl").querySelectorAll("[title]");
|
||||
let size = list.length;
|
||||
|
||||
for (var i = 0; i < size; i++) {
|
||||
list[i].style.display = "";
|
||||
}
|
||||
}
|
||||
|
||||
const enableEdit = (obj) => {
|
||||
obj.style.backgroundColor = "#ffffffff";
|
||||
obj.style.color = '#000000ff';
|
||||
obj.readOnly = '';
|
||||
formerFileName = obj.value;
|
||||
}
|
||||
|
||||
const disableEdits = (elm) => {
|
||||
elm.style.backgroundColor = "";
|
||||
elm.style.color = '';
|
||||
elm.value = formerFileName;
|
||||
elm.readOnly = "true";
|
||||
}
|
||||
|
||||
const showMedia = async (mediaLoc, type) => {
|
||||
let path = document.getElementById("path").innerHTML;
|
||||
let tempRef = mediaLoc.toLowerCase();
|
||||
let fullMedia = path + mediaLoc;
|
||||
|
||||
if (type === "video") {
|
||||
setupVideo(type, fullMedia, tempRef);
|
||||
} else {
|
||||
createFloatingPane(type, fullMedia);
|
||||
}
|
||||
}
|
||||
|
||||
const setupVideo = async (type, fullMedia, tempRef) => {
|
||||
try {
|
||||
let video = document.getElementById("video");
|
||||
video.autoplay = true;
|
||||
video.poster = "resources/images/loading.gif";
|
||||
|
||||
if ((/\.(mkv|avi|flv|mov|m4v|mpg|wmv|mpeg|mp4|mp3|webm|flac|ogg|pdf)$/i).test(tempRef)) {
|
||||
if ((/\.(mkv|avi|wmv)$/i).test(tempRef)) {
|
||||
const params = "remuxVideo=true&mediaPth=" + fullMedia;
|
||||
let response = await fetch("resources/php/filesystemActions.php",
|
||||
{method: "POST", body: new URLSearchParams(params)});
|
||||
let xml = new window.DOMParser().parseFromString(await response.text(), "text/xml");
|
||||
|
||||
if (xml.getElementsByTagName("REMUX_PATH")[0]) {
|
||||
fullMedia = xml.getElementsByTagName("REMUX_PATH")[0].innerHTML;
|
||||
} else {
|
||||
return ;
|
||||
}
|
||||
} else if ((/\.(avi|flv|mov|m4v|mpg|wmv)$/i).test(tempRef)) {
|
||||
openInLocalProg(fullMedia);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
// This is questionable in usage since it loads the full video
|
||||
// before showing; but, seeking doesn't work otherwise...
|
||||
let response = await fetch(fullMedia, {method: "GET"});
|
||||
var vidSrc = URL.createObjectURL(await response.blob()); // IE10+
|
||||
video.src = vidSrc;
|
||||
|
||||
document.getElementById("controls").style.opacity = "0";
|
||||
document.getElementById("video").style.display = "block";
|
||||
document.getElementById("dynUl").style.display = "none";
|
||||
} catch (e) {
|
||||
document.getElementById("controls").style.opacity = "1";
|
||||
document.getElementById("dynUl").style.display = "grid";
|
||||
document.getElementById("video").src = "#";
|
||||
document.getElementById("video").style.display = "none";
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const createFloatingPane = (type, fullMedia) => {
|
||||
let iframe = document.createElement("IFRAME");
|
||||
let outterDiv = document.createElement("DIV");
|
||||
let popOutDiv = document.createElement("DIV");
|
||||
let closeDiv = document.createElement("DIV");
|
||||
let toLocDiv = document.createElement("DIV");
|
||||
let imgDiv = document.createElement("DIV");
|
||||
let aTag = document.createElement("A");
|
||||
let imgTag = document.createElement("IMG");
|
||||
let closeText = document.createTextNode("X");
|
||||
|
||||
closeDiv.className = "closeBttn";
|
||||
closeDiv.title = "Close";
|
||||
closeDiv.setAttribute("onclick", "closeContainer(this)");
|
||||
closeDiv.appendChild(closeText);
|
||||
|
||||
aTag.title = "New Tab";
|
||||
aTag.target = "_blank";
|
||||
aTag.href = fullMedia;
|
||||
|
||||
popOutDiv.className = "popOutBttn";
|
||||
popOutDiv.innerHTML = "↗";
|
||||
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) { }
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
let itemObj = undefined;
|
||||
let interval = undefined;
|
||||
let cursorX;
|
||||
let cursorY;
|
||||
|
||||
document.getElementById("controls").onmouseover = (eve) => {
|
||||
let source = document.getElementById("video").src;
|
||||
let target = eve.target
|
||||
|
||||
if (interval)
|
||||
clearInterval(interval);
|
||||
|
||||
if (source !== "#") {
|
||||
eve.target.style.opacity = "1";
|
||||
document.getElementById("dynUl").style.display = "grid";
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("video").onmouseover = (eve) => {
|
||||
interval = setInterval(function () {
|
||||
elementMouseIsOver = document.elementFromPoint(cursorX, cursorY);
|
||||
if (elementMouseIsOver.tagName == "BODY" ||
|
||||
elementMouseIsOver.id == "video") {
|
||||
let controls = document.getElementById("controls");
|
||||
controls.style.opacity = "0";
|
||||
document.getElementById("dynUl").style.display = "none";
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 2500);
|
||||
}
|
||||
|
||||
// For context menu to have element
|
||||
document.onclick = (event) => {
|
||||
let obj = event.target;
|
||||
let callingID = obj.id;
|
||||
let classNM = obj.className;
|
||||
|
||||
// right-click detect
|
||||
if (event.which == 3) {
|
||||
if (callingID == "imageID") {
|
||||
setSelectedItem(obj.alt);
|
||||
} else if (callingID == "dirID" || callingID == "fileID" ||
|
||||
callingID == "movieID") {
|
||||
let node = obj.parentNode;
|
||||
setSelectedItem(node.children[1].value);
|
||||
} else if (classNM == "fileStyle" || classNM == "dirStyle" ||
|
||||
classNM == "movieStyle") {
|
||||
setSelectedItem(obj.children[1].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Actions for content
|
||||
document.ondblclick = (event) => {
|
||||
let obj = event.target;
|
||||
let callingID = obj.id;
|
||||
let classNM = obj.className;
|
||||
|
||||
// Left click detect
|
||||
if (event.which == 1) {
|
||||
// If clicking on container
|
||||
if (classNM === "fileStyle" || classNM === "movieStyle" ||
|
||||
classNM === "dirStyle") {
|
||||
if (classNM === "dirStyle") {
|
||||
getDir(obj.children[1].value);
|
||||
} else if (classNM === "movieStyle") {
|
||||
showMedia(obj.title, "video");
|
||||
} else {
|
||||
showMedia(obj.children[1].value, "file");
|
||||
}
|
||||
} else if (callingID === "dirID") { // If clicking on dir icon
|
||||
let node = obj.parentNode;
|
||||
getDir(node.children[1].value);
|
||||
} else if (callingID === "movieID") { // If clicking on movie thumbnail
|
||||
let node = obj.parentNode;
|
||||
showMedia(node.children[1].value, "video");
|
||||
} else if (callingID === "imageID") { // If clicking on image
|
||||
showMedia(obj.alt, "image");
|
||||
} else if (callingID === "titleID") { // If clicking on text title
|
||||
enableEdit(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mainly for rename event
|
||||
document.onkeydown = (event) => {
|
||||
let obj = event.target;
|
||||
let callingID = event.target.id;
|
||||
let keyCodeVal = event.keyCode;
|
||||
|
||||
// If keycode == Enter
|
||||
if (keyCodeVal == 13) {
|
||||
if (callingID == "titleID") {
|
||||
renameItem(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const setSelectedItem = (item) => { itemObj = item; }
|
||||
|
||||
// Drage event for the poped out image and media container
|
||||
const dragContainer = (elmnt) => {
|
||||
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
|
||||
elmnt.onmousedown = dragMouseDown;
|
||||
|
||||
function dragMouseDown(e) {
|
||||
e = e || window.event;
|
||||
pauseEvent(e);
|
||||
// get the mouse cursor position at startup:
|
||||
pos3 = e.clientX;
|
||||
pos4 = e.clientY;
|
||||
document.onmouseup = closeDragElement;
|
||||
// call a function whenever the cursor moves:
|
||||
document.onmousemove = elementDrag;
|
||||
}
|
||||
|
||||
function elementDrag(e) {
|
||||
e = e || window.event;
|
||||
pauseEvent(e);
|
||||
// calculate the new cursor position:
|
||||
pos1 = pos3 - e.clientX;
|
||||
pos2 = pos4 - e.clientY;
|
||||
pos3 = e.clientX;
|
||||
pos4 = e.clientY;
|
||||
// set the element's new position:
|
||||
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
|
||||
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
|
||||
}
|
||||
|
||||
function closeDragElement(e) {
|
||||
// stop moving when mouse button is released:
|
||||
document.onmouseup = null;
|
||||
document.onmousemove = null;
|
||||
}
|
||||
|
||||
function pauseEvent(e) {
|
||||
if(e.stopPropagation) e.stopPropagation();
|
||||
if(e.preventDefault) e.preventDefault();
|
||||
|
||||
e.cancelBubble=true;
|
||||
e.returnValue=false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Mouse position detection for control show/hide setup
|
||||
document.onmousemove = function(e){
|
||||
cursorX = e.pageX;
|
||||
cursorY = e.pageY;
|
||||
}
|
||||
|
||||
setInterval(checkCursor, 2000);
|
||||
function checkCursor() {
|
||||
return "";
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
<?php
|
||||
$MEDIAPLAYER = "mpv ";
|
||||
$MPLAYER_WH = " -xy 1600 -geometry 50%:50% ";
|
||||
$MUSICPLAYER = "/opt/deadbeef/bin/deadbeef";
|
||||
$IMGVIEWER = "mirage";
|
||||
$OFFICEPROG = "libreoffice";
|
||||
$PDFVIEWER = "evince";
|
||||
$TEXTVIEWER = "leafpad";
|
||||
$FILEMANAGER = "spacefm";
|
||||
$LOCKPASSWORD = "1234";
|
||||
$TMPFOLDERSIZE = 8000; // tmp folder size check for cleanup if above 8GB used.
|
||||
// NOTE: Split folders with ::::
|
||||
$LOCKEDFOLDERS = "./dirLockCheck/";
|
||||
|
||||
?>
|
@ -1,10 +0,0 @@
|
||||
<?php
|
||||
include_once 'serverMessenger.php';
|
||||
|
||||
chdir("../../");
|
||||
$db = new SQLite3('resources/db/webfm.db');
|
||||
if($db === false){
|
||||
$message = "Server: [Error] --> Database connection failed!";
|
||||
serverMessage("error", $message);
|
||||
}
|
||||
?>
|
@ -1,56 +0,0 @@
|
||||
<?php
|
||||
include_once 'connection.php';
|
||||
include_once 'serverMessenger.php';
|
||||
|
||||
function getTabLinks() {
|
||||
GLOBAL $db;
|
||||
|
||||
$res = $db->query('Select * FROM faves');
|
||||
$GeneratedJSON = array('FAVES_LIST' => array());
|
||||
while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
|
||||
$GeneratedJSON['FAVES_LIST'][] = $row['link'];
|
||||
}
|
||||
|
||||
echo json_encode($GeneratedJSON);
|
||||
}
|
||||
|
||||
function manageLink($ACTION, $PATH) {
|
||||
GLOBAL $db;
|
||||
$ACTION_TYPE = "";
|
||||
|
||||
// If action isn't true then we add else we delete or exit.
|
||||
if ($ACTION == "false") {
|
||||
$stmt = $db->prepare('INSERT INTO faves VALUES(:link)');
|
||||
$ACTION_TYPE = "added to";
|
||||
} elseif ($ACTION == "true") {
|
||||
$stmt = $db->prepare('DELETE FROM faves WHERE link = :link');
|
||||
$ACTION_TYPE = "deleted from";
|
||||
} else {
|
||||
$message = "Server: [Error] --> Action for adding or deleting isn't set properly!";
|
||||
serverMessage("error", $message);
|
||||
return;
|
||||
}
|
||||
|
||||
$stmt->bindValue(":link", $PATH, SQLITE3_TEXT);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
$message = "Server: [Success] --> Fave link: " .
|
||||
$PATH . " " . $ACTION_TYPE . " the database!";
|
||||
serverMessage("success", $message);
|
||||
}
|
||||
|
||||
|
||||
// Determin action
|
||||
chdir("../../");
|
||||
if (isset($_POST['getTabs'])) {
|
||||
getTabLinks();
|
||||
} elseif (isset($_POST['deleteLink'],
|
||||
$_POST['linkPath'])) {
|
||||
manageLink($_POST['deleteLink'], $_POST['linkPath']);
|
||||
} else {
|
||||
$message = "Server: [Error] --> Illegal Access Method!";
|
||||
serverMessage("error", $message);
|
||||
}
|
||||
|
||||
?>
|
@ -1,199 +0,0 @@
|
||||
<?php
|
||||
session_start();
|
||||
include_once 'serverMessenger.php';
|
||||
|
||||
// Create file or folder
|
||||
function createItem($FILE, $TYPE) {
|
||||
$FILE = trim($FILE);
|
||||
$FILE = preg_replace('/\.*$/','',$FILE); // removing dot . after file extension
|
||||
|
||||
if ($TYPE === "dir"){
|
||||
mkdir($FILE, 0755);
|
||||
} else if ($TYPE === "file") {
|
||||
$myfile = fopen($FILE, "w");
|
||||
fclose($myfile);
|
||||
} else {
|
||||
$message = "Server: [Error] --> Failed to create folder or file!";
|
||||
serverMessage("error", $message);
|
||||
return;
|
||||
}
|
||||
|
||||
$message = "Server: [Success] --> The file " . $FILE . " has been created.";
|
||||
serverMessage("success", $message);
|
||||
$_SESSION["refreshState"] = "updateListing";
|
||||
}
|
||||
|
||||
// File or folder delition
|
||||
function deleteItem($FILE) {
|
||||
if (filetype($FILE) == "dir"){
|
||||
//GLOB_MARK adds a slash to directories returned
|
||||
$files = glob($FILE . '*', GLOB_MARK);
|
||||
foreach ($files as $file) {
|
||||
deleteItem($file);
|
||||
}
|
||||
rmdir($FILE);
|
||||
} else if (filetype($FILE) == "file") {
|
||||
unlink($FILE);
|
||||
} else {
|
||||
$message = "Server: [Error] --> Failed to delete item! Not a folder or file!";
|
||||
serverMessage("error", $message);
|
||||
return;
|
||||
}
|
||||
|
||||
$message = "Server: [Success] --> The file(s) has/have been deleted.";
|
||||
serverMessage("success", $message);
|
||||
$_SESSION["refreshState"] = "updateListing";
|
||||
}
|
||||
|
||||
// Rename file or folder
|
||||
function renameItem($OLDFILE, $NEWNAME, $PATH) {
|
||||
rename($PATH . $OLDFILE, $PATH . $NEWNAME);
|
||||
$message = "Server: [Success] --> The file " . $OLDFILE . " has been renamed to " . $NEWNAME . " side.";
|
||||
serverMessage("success", $message);
|
||||
$_SESSION["refreshState"] = "updateListing";
|
||||
}
|
||||
|
||||
// Uploader
|
||||
function uploadFiles($targetDir) {
|
||||
$numberOfFiles = count($_FILES['filesToUpload']['name']);
|
||||
|
||||
if ($numberOfFiles === 0) {
|
||||
$message = "Server: [Error] --> No files were uploaded!";
|
||||
serverMessage("error", $message);
|
||||
return;
|
||||
}
|
||||
|
||||
$type = "";
|
||||
$message = "";
|
||||
for ($i=0; $i < $numberOfFiles; $i++) {
|
||||
$uploadOk = 1;
|
||||
$fileName = $_FILES['filesToUpload']['name'][$i];
|
||||
$fileTmpName = $_FILES['filesToUpload']['tmp_name'][$i];
|
||||
|
||||
// Check if file already exists
|
||||
$targetFile = $targetDir . $fileName;
|
||||
if (file_exists($targetFile)) {
|
||||
if (filetype($targetFile) == "file") {
|
||||
unlink($targetFile);
|
||||
$message = "Server: [Warning] --> This file already exists. Overwriting it.";
|
||||
} else {
|
||||
$message = "Server: [Warning] --> This file might be a directory. Or, no files were submitted for uploading.";
|
||||
$uploadOk = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check file size
|
||||
$fileSize = $_FILES['filesToUpload']['size'][$i];
|
||||
if ($fileSize > 500000000000) {
|
||||
$message = "Server: [Warning] --> This file is too large.";
|
||||
$uploadOk = 0;
|
||||
}
|
||||
|
||||
// Allow certain file formats
|
||||
// $ext = pathinfo($targetFile,PATHINFO_EXTENSION);
|
||||
// if(!preg_match('/^.*\.(rar|iso|img|tar|zip|7z|7zip|jpg|jpeg|png|gif|mpeg|mov|flv|avi|mp4|webm|mpg|mkv|m4a|mp3|ogg|docx|doc|odt|txt|pdf|)$/i', strtolower($ext))) {
|
||||
// $message = "Server: [Warning] --> This file type is not allowed.";
|
||||
// $uploadOk = 0;
|
||||
// }
|
||||
|
||||
// if everything is ok, try to upload file
|
||||
if ($uploadOk !== 0) {
|
||||
if (move_uploaded_file($fileTmpName, $targetFile)) {
|
||||
$type = "success";
|
||||
$message = "Server: [Success] --> The file " . $fileName . " has been uploaded.";
|
||||
$_SESSION["refreshState"] = "updateListing";
|
||||
}
|
||||
} else {
|
||||
$type = "error";
|
||||
$message .= "\nServer: [Error] --> Your file " . $fileName . " was not uploaded.";
|
||||
}
|
||||
}
|
||||
|
||||
serverMessage($type, $message);
|
||||
}
|
||||
|
||||
// Local program file access
|
||||
function openFile($FILE) {
|
||||
include 'config.php';
|
||||
$EXTNSN = strtolower(pathinfo($FILE, PATHINFO_EXTENSION));
|
||||
|
||||
if (preg_match('(mkv|avi|flv|mov|m4v|mpg|wmv|mpeg|mp4|webm)', $EXTNSN) === 1) {
|
||||
shell_exec($MEDIAPLAYER . "\"" . $FILE . "\" > /dev/null &");
|
||||
} else if (preg_match('(png|jpg|jpeg|gif)', $EXTNSN) === 1) {
|
||||
shell_exec($IMGVIEWER . ' "' . $FILE . '" > /dev/null &');
|
||||
} else if (preg_match('(psf|mp3|ogg|flac)', $EXTNSN) === 1) {
|
||||
shell_exec($MUSICPLAYER . ' "' . $FILE . '" > /dev/null &');
|
||||
} else if (preg_match('(odt|doc|docx|rtf)', $EXTNSN) === 1) {
|
||||
shell_exec($OFFICEPROG . ' "' . $FILE . '" > /dev/null &');
|
||||
} else if (preg_match('(txt)', $EXTNSN) === 1) {
|
||||
shell_exec($TEXTVIEWER . ' "' . $FILE . '" > /dev/null &');
|
||||
} else if (preg_match('(pdf)', $EXTNSN) === 1) {
|
||||
shell_exec($PDFVIEWER . ' "' . $FILE . '" > /dev/null &');
|
||||
}
|
||||
|
||||
$message = "Server: [Success] --> The file " . $FILE . " has been opened server side.";
|
||||
serverMessage("success", $message);
|
||||
}
|
||||
|
||||
function remuxVideo($FILE) {
|
||||
$FILE = trim($FILE);
|
||||
$PTH = "resources/tmp/";
|
||||
$HASHED_NAME = hash('sha256', $FILE) . '.mp4';
|
||||
$EXTNSN = strtolower(pathinfo($FILE, PATHINFO_EXTENSION));
|
||||
|
||||
|
||||
if (!file_exists($PTH . $HASHED_NAME)) {
|
||||
$io = popen('/usr/bin/du -sm ' . $PTH, 'r');
|
||||
$size = fgets($io, 4096);
|
||||
$size = (int) substr($size, 0, strpos ( $size, "\t" ));
|
||||
pclose ($io);
|
||||
|
||||
include 'config.php';
|
||||
if ($size > $TMPFOLDERSIZE) {
|
||||
$files = glob($PTH . '*');
|
||||
foreach($files as $file){
|
||||
if(is_file($file))
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('(mkv)', $EXTNSN) === 1)
|
||||
$COMMAND = 'ffmpeg -i "' . $FILE . '" -hide_banner -movflags +faststart -codec copy -strict -2 ' . $PTH . $HASHED_NAME;
|
||||
if (preg_match('(avi)', $EXTNSN) === 1)
|
||||
$COMMAND = 'ffmpeg -i "' . $FILE . '" -hide_banner -movflags +faststart -c:v libx264 -crf 21 -c:a aac -b:a 192k -ac 2 ' . $PTH . $HASHED_NAME;
|
||||
if (preg_match('(wmv)', $EXTNSN) === 1)
|
||||
$COMMAND = 'ffmpeg -i "' . $FILE . '" -hide_banner -movflags +faststart -c:v libx264 -crf 23 -c:a aac -strict -2 -q:a 100 ' . $PTH . $HASHED_NAME;
|
||||
|
||||
shell_exec($COMMAND . " 2> resources/vdata.txt");
|
||||
}
|
||||
|
||||
$GeneratedXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
|
||||
$GeneratedXML .= "<REMUX_PATH>" . $PTH . $HASHED_NAME ."</REMUX_PATH>";
|
||||
echo $GeneratedXML;
|
||||
}
|
||||
|
||||
|
||||
chdir("../../");
|
||||
if (isset($_POST["remuxVideo"], $_POST["mediaPth"])) {
|
||||
remuxVideo($_POST["mediaPth"]);
|
||||
} else if (isset($_POST["createItem"],
|
||||
$_POST["item"],
|
||||
$_POST["type"])) {
|
||||
createItem($_POST["item"], $_POST["type"]);
|
||||
} else if (isset($_POST["deleteItem"], $_POST["item"])) {
|
||||
deleteItem($_POST["item"]);
|
||||
} else if (isset($_POST["renameItem"],
|
||||
$_POST["oldName"],
|
||||
$_POST["newName"],
|
||||
$_POST["path"])) {
|
||||
renameItem($_POST["oldName"], $_POST["newName"], $_POST["path"]);
|
||||
} else if(isset($_POST["UploadFiles"], $_POST["DIRPATHUL"])) {
|
||||
uploadFiles($_POST["DIRPATHUL"]);
|
||||
} else if (isset($_POST["media"])) {
|
||||
openFile($_POST["media"]);
|
||||
} else {
|
||||
$message = "Server: [Error] --> Incorrect access attempt!";
|
||||
serverMessage("error", $message);
|
||||
}
|
||||
|
||||
?>
|
@ -1,108 +0,0 @@
|
||||
<?php
|
||||
session_start();
|
||||
include_once 'serverMessenger.php';
|
||||
|
||||
// Start of retrieving dir data
|
||||
function startListing($NEWPATH, $MERGESEASSONS, $PASSWD) {
|
||||
if (filetype($NEWPATH) == "dir") {
|
||||
include_once 'lockedFolders.php';
|
||||
|
||||
if (checkForLock($NEWPATH, $PASSWD) == false) {
|
||||
$subPath = ""; // This is used for season scanning as a means of properly getting
|
||||
// the video src.... It's left blank when not in a sub dir
|
||||
|
||||
$GeneratedJSON = array('PATH_HEAD' => $NEWPATH,
|
||||
'IN_FAVE' => isInDBCheck($NEWPATH),
|
||||
'list' => array()
|
||||
);
|
||||
|
||||
listDir($GeneratedJSON, $NEWPATH, $MERGESEASSONS, $subPath);
|
||||
echo json_encode($GeneratedJSON);
|
||||
} else {
|
||||
$message = "Server: [Error] --> Folder is locked.";
|
||||
serverMessage("locked", $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function listDir(&$GeneratedJSON, &$NEWPATH, &$MERGESEASSONS, &$subPath) {
|
||||
if ($MERGESEASSONS !== "true") {
|
||||
$files = array_diff(scandir($NEWPATH), array('..', '.', 'resources'));
|
||||
foreach ($files as $fileName) {
|
||||
$fullPath = $NEWPATH . '/' . $fileName;
|
||||
// error_log($fullPath, 4);
|
||||
processItem($GeneratedJSON, $fullPath, $fileName, $subPath);
|
||||
}
|
||||
} else {
|
||||
$files = array_diff(scandir($NEWPATH), array('..', '.', 'resources'));
|
||||
foreach ($files as $fileName) {
|
||||
$fullPath = $NEWPATH . $fileName;
|
||||
// error_log($fullPath, 4);
|
||||
if (filetype($fullPath) == "dir" && strpos(strtolower($fileName),
|
||||
'season') !== false) {
|
||||
$fileName .= "/";
|
||||
listDir($GeneratedJSON, $fullPath, $MERGESEASSONS, $fileName);
|
||||
} else {
|
||||
processItem($GeneratedJSON, $fullPath, $fileName, $subPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assign JSON Markup based on file type
|
||||
function processItem(&$GeneratedJSON, &$fullPath, &$fileName, $subPath) {
|
||||
if (preg_match('/^.*\.(mkv|avi|flv|mov|m4v|mpg|wmv|mpeg|mp4|webm)$/i', strtolower($fileName))) {
|
||||
$NAMEHASH = hash('sha256', $fileName);
|
||||
if (!file_exists('resources/images/thumbnails/' . $NAMEHASH . '.jpg')) {
|
||||
shell_exec('resources/ffmpegthumbnailer -t 65% -s 320 -c jpg '
|
||||
. '-i "' . $subPath . $fullPath . '" '
|
||||
. '-o resources/images/thumbnails/' . $NAMEHASH . '.jpg'
|
||||
);
|
||||
}
|
||||
|
||||
$GeneratedJSON['list']['vids'][] = array('video' =>
|
||||
array('title' => $subPath . $fileName,
|
||||
'thumbnail' => $NAMEHASH . '.jpg'
|
||||
)
|
||||
);
|
||||
} elseif (preg_match('/^.*\.(png|jpg|gif|jpeg)$/i', strtolower($fileName))) {
|
||||
$GeneratedJSON['list']['imgs'][] = array('image' => $subPath . $fileName);
|
||||
} elseif (filetype($fullPath) == "dir") {
|
||||
$GeneratedJSON['list']['dirs'][] = array('dir' => $fileName . "/");
|
||||
} else {
|
||||
$GeneratedJSON['list']['files'][] = array('file' => $subPath . $fileName);
|
||||
}
|
||||
}
|
||||
|
||||
function isInDBCheck($PATH) {
|
||||
$db = new SQLite3('resources/db/webfm.db');
|
||||
|
||||
if($db === false){
|
||||
$message = "Server: [Error] --> Database connection failed!";
|
||||
serverMessage("error", $message);
|
||||
die("ERROR: Could not connect to db.");
|
||||
}
|
||||
|
||||
$stmt = $db->prepare('SELECT 1 FROM faves WHERE link = :link');
|
||||
$stmt->bindValue(":link", $PATH, SQLITE3_TEXT);
|
||||
$result = $stmt->execute() ;
|
||||
$row = $result->fetchArray() ;
|
||||
|
||||
if ($row > 0) {
|
||||
return "true";
|
||||
} else {
|
||||
return "false";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Determin action
|
||||
chdir("../../");
|
||||
if (isset($_POST['dirQuery'])) {
|
||||
startListing(trim($_POST['dirQuery']), $_POST['mergeType'], $_POST['passwd']);
|
||||
} else {
|
||||
$message = "Server: [Error] --> Illegal Access Method!";
|
||||
serverMessage("error", $message);
|
||||
}
|
||||
|
||||
?>
|
@ -1,46 +0,0 @@
|
||||
<?php
|
||||
// Check if sub folder is in locked folder
|
||||
function checkForLock($NEWPATH, $PASSWD) {
|
||||
include 'config.php';
|
||||
|
||||
$LOCKS = explode("::::", $LOCKEDFOLDERS);
|
||||
$size = sizeof($LOCKS);
|
||||
|
||||
if (isset($_SESSION["unlockState"]) && $_SESSION["unlockState"] == true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
if (strpos($NEWPATH, $LOCKS[$i]) !== false) {
|
||||
if ($PASSWD === $LOCKPASSWORD) {
|
||||
$_SESSION["unlockState"] = true;
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function lockFolders() {
|
||||
session_start();
|
||||
include 'serverMessenger.php';
|
||||
|
||||
if (isset($_SESSION["unlockState"]) && $_SESSION["unlockState"] == true) {
|
||||
$_SESSION["unlockState"] = false;
|
||||
$message = "Server: [Success] --> Folders unlocked!";
|
||||
serverMessage("success", $message);
|
||||
} else {
|
||||
$message = "Server: [Warning] --> Folders aren't unlocked!"
|
||||
. "\n" . $_SESSION["unlockState"];
|
||||
serverMessage("warning", $message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (isset($_POST['lockFolders'])) {
|
||||
lockFolders();
|
||||
}
|
||||
|
||||
?>
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
function serverMessage($TYPE, $MESSAGE) {
|
||||
$GeneratedJSON = array( 'message' =>
|
||||
array(
|
||||
'type' => $TYPE,
|
||||
'text' => $MESSAGE
|
||||
)
|
||||
);
|
||||
|
||||
echo json_encode($GeneratedJSON);
|
||||
}
|
||||
?>
|
@ -1,16 +0,0 @@
|
||||
<?php
|
||||
// Start the session
|
||||
session_start();
|
||||
include_once 'config.php';
|
||||
|
||||
if (!isset($_SESSION["refreshState"])) {
|
||||
$_SESSION["refreshState"] = "none";
|
||||
}
|
||||
|
||||
header('Content-Type: text/event-stream');
|
||||
header('Cache-Control: no-cache');
|
||||
echo "data:" . $_SESSION["refreshState"] . "\n\n";
|
||||
|
||||
$_SESSION["refreshState"] = "none";
|
||||
flush();
|
||||
?>
|
@ -1 +0,0 @@
|
||||
Place Holder....
|
17
socket_run.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 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() {
|
||||
SCRIPTPATH="$( cd "$(dirname "")" >/dev/null 2>&1 ; pwd -P )"
|
||||
cd "${SCRIPTPATH}"
|
||||
echo "Working Dir: " $(pwd)
|
||||
mkdir /tmp/apps
|
||||
./venv/bin/gunicorn \
|
||||
--bind unix:/tmp/apps/webfm.sock wsgi:app
|
||||
|
||||
}
|
||||
main $@;
|
15
start.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 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() {
|
||||
SCRIPTPATH="$( cd "$(dirname "")" >/dev/null 2>&1 ; pwd -P )"
|
||||
cd "${SCRIPTPATH}"
|
||||
echo "Working Dir: " $(pwd)
|
||||
source "./venv/bin/activate"
|
||||
gunicorn wsgi:app -b 0.0.0.0:8080 # <module>:<app> IE <file>:<flask app variable>
|
||||
}
|
||||
main $@;
|
45
webfm/__init__.py
Normal file
@ -0,0 +1,45 @@
|
||||
# system import
|
||||
import os, json, secrets
|
||||
from datetime import timedelta
|
||||
|
||||
# Flask imports
|
||||
from flask import Flask, Blueprint
|
||||
from flask_login import current_user, login_user, logout_user, LoginManager
|
||||
from flask_bcrypt import Bcrypt
|
||||
|
||||
|
||||
|
||||
# Configs and 'init'
|
||||
app = Flask(__name__)
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///static/db/webfm.db"
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
app.config['TITLE'] = 'WebFM'
|
||||
|
||||
# For csrf and some other stuff...
|
||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days = 7)
|
||||
app.config['SECRET_KEY'] = secrets.token_hex(32)
|
||||
login_manager = LoginManager(app)
|
||||
bcrypt = Bcrypt(app)
|
||||
|
||||
# Settings data
|
||||
THIS_FILE_PTH = os.path.dirname(os.path.realpath(__file__))
|
||||
CONFIG_FILE = THIS_FILE_PTH + "/config.json"
|
||||
def retrieveSettings():
|
||||
returnData = []
|
||||
|
||||
with open(CONFIG_FILE) as infile:
|
||||
try:
|
||||
return json.load(infile)
|
||||
except Exception as e:
|
||||
print(repr(e))
|
||||
returnData = ['', 'mplayer', 'xdg-open']
|
||||
|
||||
config = retrieveSettings()
|
||||
|
||||
|
||||
from .forms import LoginForm, RegisterForm
|
||||
from .models import db, Favorites, Settings, User
|
||||
from webfm import routes
|
||||
|
||||
with app.app_context():
|
||||
db.create_all()
|
17
webfm/config.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"settings" : {
|
||||
"media_app": "mpv",
|
||||
"mplayer_options": "-quiet -really-quiet -xy 1600 -geometry 50%:50%",
|
||||
"music_app": "/opt/deadbeef/bin/deadbeef",
|
||||
"image_app": "mirage",
|
||||
"office_app": "libreoffice",
|
||||
"pdf_app": "evince",
|
||||
"text_app": "leafpad",
|
||||
"file_manager_app": "spacefm",
|
||||
"locked_folder_password": "toor",
|
||||
"do_refresh": "no",
|
||||
"remux_folder_max_disk_usage": "8589934592",
|
||||
"locked_folders": "venv::::flasks",
|
||||
"folders_locked": "true"
|
||||
}
|
||||
}
|
25
webfm/forms.py
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, PasswordField, SubmitField
|
||||
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
|
||||
from .models import User
|
||||
|
||||
|
||||
|
||||
class RegisterForm(FlaskForm):
|
||||
username = StringField('Username', validators=[DataRequired(), Length(min=4, max=24)])
|
||||
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")
|
40
webfm/models.py
Normal file
@ -0,0 +1,40 @@
|
||||
# System imports
|
||||
|
||||
# Lib imports
|
||||
from flask_login.mixins import UserMixin
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
# App imports
|
||||
from . import app, login_manager
|
||||
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return User.query.get(int(user_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, UserMixin):
|
||||
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}']"
|
||||
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
username = db.Column(db.String, unique=True, 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}']"
|
172
webfm/routes/Routes.py
Normal file
@ -0,0 +1,172 @@
|
||||
# Python imports
|
||||
import os, hashlib, json
|
||||
|
||||
# Lib imports
|
||||
from flask import request, render_template, send_from_directory, redirect, url_for, session
|
||||
from flask_login import current_user
|
||||
|
||||
# App imports
|
||||
from .. import app, db, config, utils, Favorites, Settings
|
||||
|
||||
|
||||
msgHandler = utils.MessageHandler()
|
||||
file_manager = utils.FileManager(db, Settings)
|
||||
logging = utils.Logger().get_logger("Routes")
|
||||
|
||||
THIS_FILE_PTH = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
@app.route('/', methods=['GET', 'POST'], subdomain='webfm')
|
||||
def home():
|
||||
_dHash = file_manager.getDotHash()
|
||||
_ddHash = file_manager.getDotDotHash()
|
||||
|
||||
return render_template('index.html', title='WebFM',
|
||||
dHash=_dHash, ddHash=_ddHash)
|
||||
|
||||
|
||||
@app.route('/list-files', methods=['GET', 'POST'])
|
||||
@app.route('/list-files', methods=['GET', 'POST'], subdomain='webfm')
|
||||
def listFilesRoute():
|
||||
if request.method == 'POST':
|
||||
HASH = str(request.values['hash']).strip()
|
||||
pathPart = file_manager.returnPathPartFromHash(HASH)
|
||||
lockedFolders = config["settings"]["locked_folders"].split("::::")
|
||||
path = file_manager.getPath().split('/')
|
||||
lockedFolderInPath = False
|
||||
|
||||
# Insure chilren folders are locked too.
|
||||
for folder in lockedFolders:
|
||||
if folder in path:
|
||||
lockedFolderInPath = True
|
||||
break
|
||||
|
||||
if (pathPart in lockedFolders or lockedFolderInPath) and not current_user.is_authenticated:
|
||||
return redirect("/login")
|
||||
|
||||
return listFiles(HASH)
|
||||
else:
|
||||
msg = "Can't manage the request type..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
|
||||
@app.route('/favorites', methods=['GET', 'POST'])
|
||||
@app.route('/favorites', methods=['GET', 'POST'], subdomain='webfm')
|
||||
def getAllFavoritesRoute():
|
||||
if request.method == 'POST':
|
||||
list = db.session.query(Favorites).all()
|
||||
faves = []
|
||||
|
||||
for fave in list:
|
||||
faves.append([fave.link, fave.id])
|
||||
|
||||
return '{"faves_list":' + json.dumps(faves) + '}'
|
||||
else:
|
||||
msg = "Can't manage the request type..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
|
||||
@app.route('/load-favorite', methods=['GET', 'POST'])
|
||||
@app.route('/load-favorite', methods=['GET', 'POST'], subdomain='webfm')
|
||||
def loadFavorite():
|
||||
if request.method == 'POST':
|
||||
ID = str(request.values['id']).strip()
|
||||
try:
|
||||
ID = int(ID)
|
||||
fave = db.session.query(Favorites).filter_by(id=ID).first()
|
||||
file_manager.setNewPathFromFavorites(fave.link)
|
||||
file_manager.loadPreviousPath()
|
||||
|
||||
return '{"refresh":"true"}'
|
||||
except Exception as e:
|
||||
print(repr(e))
|
||||
msg = "Incorrect Favorites ID..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
else:
|
||||
msg = "Can't manage the request type..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
|
||||
@app.route('/manage-favorites', methods=['GET', 'POST'])
|
||||
@app.route('/manage-favorites', methods=['GET', 'POST'], subdomain='webfm')
|
||||
def manageFavoritesRoute():
|
||||
if request.method == 'POST':
|
||||
ACTION = str(request.values['action']).strip()
|
||||
PATH = str(request.values['path']).strip()
|
||||
|
||||
if ACTION == "add":
|
||||
fave = Favorites(link=PATH)
|
||||
db.session.add(fave)
|
||||
msg = "Added to Favorites successfully..."
|
||||
else:
|
||||
fave = db.session.query(Favorites).filter_by(link=PATH).first()
|
||||
db.session.delete(fave)
|
||||
msg = "Deleted from Favorites successfully..."
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return msgHandler.createMessageJSON("success", msg)
|
||||
else:
|
||||
msg = "Can't manage the request type..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
|
||||
|
||||
@app.route('/reset-path', methods=['GET', 'POST'])
|
||||
@app.route('/reset-path', methods=['GET', 'POST'], subdomain='webfm')
|
||||
def resetPath():
|
||||
if request.method == 'GET':
|
||||
file_manager.reset_path()
|
||||
return redirect("/")
|
||||
|
||||
|
||||
# Used to get files from non gunicorn root path...
|
||||
# Allows us to pull images and stuff to user without simlinking.
|
||||
@app.route('/files/<hash>')
|
||||
def returnFile(hash):
|
||||
path = file_manager.getFullPath()
|
||||
pathPart = file_manager.returnPathPartFromHash(hash)
|
||||
return send_from_directory(path, pathPart)
|
||||
|
||||
@app.route('/remux/<hash>')
|
||||
def remuxRoute(hash):
|
||||
folder = file_manager.getFullPath()
|
||||
file = file_manager.returnPathPartFromHash(hash)
|
||||
fpath = os.path.join(folder, file)
|
||||
|
||||
logging.debug(fpath)
|
||||
|
||||
return file_manager.remuxVideo(hash, fpath)
|
||||
|
||||
@app.route('/run-locally/<hash>')
|
||||
def runLocallyRoute(hash):
|
||||
path = file_manager.getFullPath()
|
||||
pathPart = file_manager.returnPathPartFromHash(hash)
|
||||
fullpath = path + "/" + pathPart
|
||||
|
||||
logging.debug(fullpath)
|
||||
|
||||
file_manager.openFilelocally(fullpath)
|
||||
msg = "Opened media..."
|
||||
return msgHandler.createMessageJSON("success", msg)
|
||||
|
||||
|
||||
|
||||
def listFiles(HASH):
|
||||
state = file_manager.generateLists(HASH)
|
||||
if "error" in state:
|
||||
msg = "Listing files failed..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
|
||||
path = file_manager.getPath()
|
||||
fave = db.session.query(Favorites).filter_by(link=path).first()
|
||||
in_fave = "true" if fave else "false"
|
||||
|
||||
dirs = json.dumps( file_manager.getDirs() )
|
||||
vids = json.dumps( file_manager.getVids() )
|
||||
imgs = json.dumps( file_manager.getImgs() )
|
||||
files = json.dumps( file_manager.getFiles() )
|
||||
|
||||
return '{"path_head":"' + path + '"' + \
|
||||
',"in_fave":"' + in_fave + '"' + \
|
||||
',"list":{"dirs":' + dirs + \
|
||||
', "vids":' + vids + \
|
||||
', "imgs":' + imgs + \
|
||||
', "files":' + files + '}}'
|
2
webfm/routes/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from . import Routes
|
||||
from .pages import Login, Register
|
38
webfm/routes/pages/Login.py
Normal file
@ -0,0 +1,38 @@
|
||||
# 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 ... import app, bcrypt, db, User, LoginForm
|
||||
from ...utils import MessageHandler # Get simple message processor
|
||||
|
||||
|
||||
msgHandler = MessageHandler()
|
||||
TITLE = app.config['TITLE']
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def 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("/")
|
||||
|
||||
flash("Username or password incorrect! Please try again...", "danger")
|
||||
|
||||
return render_template('login.html', title=TITLE, form=_form)
|
||||
|
||||
|
||||
@app.route('/logout', methods=['GET', 'POST'])
|
||||
def logout():
|
||||
logout_user()
|
||||
flash("Logged out successfully!", "success")
|
||||
return redirect("/login")
|
31
webfm/routes/pages/Register.py
Normal file
@ -0,0 +1,31 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
from flask import request, render_template, url_for, redirect, flash
|
||||
|
||||
# App imports
|
||||
from ... import app, bcrypt, db, current_user, RegisterForm # Get from __init__
|
||||
from ...models import User
|
||||
from ...utils import MessageHandler # Get simple message processor
|
||||
|
||||
|
||||
msgHandler = MessageHandler()
|
||||
TITLE = app.config['TITLE']
|
||||
|
||||
@app.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
if current_user.is_authenticated:
|
||||
return redirect("/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, password=hashed_password)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
flash("Account created successfully!", "success")
|
||||
return redirect("/login")
|
||||
|
||||
return render_template('register.html',
|
||||
title=TITLE,
|
||||
form=_form)
|
19
webfm/static/css/base.css
Normal file
@ -0,0 +1,19 @@
|
||||
body {
|
||||
overflow: hidden; /* Used to prevent touch scroll down reloading page. Ref: https://stackoverflow.com/questions/29008194/disabling-androids-chrome-pull-down-to-refresh-feature */
|
||||
-webkit-touch-callout: none; /* iOS Safari */
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-khtml-user-select: none; /* Konqueror HTML */
|
||||
-moz-user-select: none; /* Old versions of Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none; /* Non-prefixed version, currently
|
||||
supported by Chrome, Opera and Firefox */
|
||||
}
|
||||
|
||||
ol, ul, li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
/* Pass click through text; for now... */
|
||||
li > input {
|
||||
pointer-events:none;
|
||||
}
|
471
webfm/static/css/bootstrap/bootstrap-datepicker.css
vendored
Normal file
@ -0,0 +1,471 @@
|
||||
/*!
|
||||
* Datepicker for Bootstrap v1.6.4 (https://github.com/eternicode/bootstrap-datepicker)
|
||||
*
|
||||
* Copyright 2012 Stefan Petre
|
||||
* Improvements by Andrew Rowls
|
||||
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||
*/
|
||||
.datepicker {
|
||||
padding: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
direction: ltr;
|
||||
}
|
||||
.datepicker-inline {
|
||||
width: 220px;
|
||||
}
|
||||
.datepicker.datepicker-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
.datepicker.datepicker-rtl table tr td span {
|
||||
float: right;
|
||||
}
|
||||
.datepicker-dropdown {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.datepicker-dropdown:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid #999;
|
||||
border-top: 0;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
position: absolute;
|
||||
}
|
||||
.datepicker-dropdown:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #fff;
|
||||
border-top: 0;
|
||||
position: absolute;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-left:before {
|
||||
left: 6px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-left:after {
|
||||
left: 7px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-right:before {
|
||||
right: 6px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-right:after {
|
||||
right: 7px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-bottom:before {
|
||||
top: -7px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-bottom:after {
|
||||
top: -6px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-top:before {
|
||||
bottom: -7px;
|
||||
border-bottom: 0;
|
||||
border-top: 7px solid #999;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-top:after {
|
||||
bottom: -6px;
|
||||
border-bottom: 0;
|
||||
border-top: 6px solid #fff;
|
||||
}
|
||||
.datepicker table {
|
||||
margin: 0;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.datepicker td,
|
||||
.datepicker th {
|
||||
text-align: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
.table-striped .datepicker table tr td,
|
||||
.table-striped .datepicker table tr th {
|
||||
background-color: transparent;
|
||||
}
|
||||
.datepicker table tr td.day:hover,
|
||||
.datepicker table tr td.day.focused {
|
||||
background: #eee;
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker table tr td.old,
|
||||
.datepicker table tr td.new {
|
||||
color: #999;
|
||||
}
|
||||
.datepicker table tr td.disabled,
|
||||
.datepicker table tr td.disabled:hover {
|
||||
background: none;
|
||||
color: #999;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td.highlighted {
|
||||
background: #d9edf7;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.today,
|
||||
.datepicker table tr td.today:hover,
|
||||
.datepicker table tr td.today.disabled,
|
||||
.datepicker table tr td.today.disabled:hover {
|
||||
background-color: #fde19a;
|
||||
background-image: -moz-linear-gradient(to bottom, #fdd49a, #fdf59a);
|
||||
background-image: -ms-linear-gradient(to bottom, #fdd49a, #fdf59a);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
|
||||
background-image: -webkit-linear-gradient(to bottom, #fdd49a, #fdf59a);
|
||||
background-image: -o-linear-gradient(to bottom, #fdd49a, #fdf59a);
|
||||
background-image: linear-gradient(to bottom, #fdd49a, #fdf59a);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
|
||||
border-color: #fdf59a #fdf59a #fbed50;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #000;
|
||||
}
|
||||
.datepicker table tr td.today:hover,
|
||||
.datepicker table tr td.today:hover:hover,
|
||||
.datepicker table tr td.today.disabled:hover,
|
||||
.datepicker table tr td.today.disabled:hover:hover,
|
||||
.datepicker table tr td.today:active,
|
||||
.datepicker table tr td.today:hover:active,
|
||||
.datepicker table tr td.today.disabled:active,
|
||||
.datepicker table tr td.today.disabled:hover:active,
|
||||
.datepicker table tr td.today.active,
|
||||
.datepicker table tr td.today:hover.active,
|
||||
.datepicker table tr td.today.disabled.active,
|
||||
.datepicker table tr td.today.disabled:hover.active,
|
||||
.datepicker table tr td.today.disabled,
|
||||
.datepicker table tr td.today:hover.disabled,
|
||||
.datepicker table tr td.today.disabled.disabled,
|
||||
.datepicker table tr td.today.disabled:hover.disabled,
|
||||
.datepicker table tr td.today[disabled],
|
||||
.datepicker table tr td.today:hover[disabled],
|
||||
.datepicker table tr td.today.disabled[disabled],
|
||||
.datepicker table tr td.today.disabled:hover[disabled] {
|
||||
background-color: #fdf59a;
|
||||
}
|
||||
.datepicker table tr td.today:active,
|
||||
.datepicker table tr td.today:hover:active,
|
||||
.datepicker table tr td.today.disabled:active,
|
||||
.datepicker table tr td.today.disabled:hover:active,
|
||||
.datepicker table tr td.today.active,
|
||||
.datepicker table tr td.today:hover.active,
|
||||
.datepicker table tr td.today.disabled.active,
|
||||
.datepicker table tr td.today.disabled:hover.active {
|
||||
background-color: #fbf069 \9;
|
||||
}
|
||||
.datepicker table tr td.today:hover:hover {
|
||||
color: #000;
|
||||
}
|
||||
.datepicker table tr td.today.active:hover {
|
||||
color: #fff;
|
||||
}
|
||||
.datepicker table tr td.range,
|
||||
.datepicker table tr td.range:hover,
|
||||
.datepicker table tr td.range.disabled,
|
||||
.datepicker table tr td.range.disabled:hover {
|
||||
background: #eee;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.range.today,
|
||||
.datepicker table tr td.range.today:hover,
|
||||
.datepicker table tr td.range.today.disabled,
|
||||
.datepicker table tr td.range.today.disabled:hover {
|
||||
background-color: #f3d17a;
|
||||
background-image: -moz-linear-gradient(to bottom, #f3c17a, #f3e97a);
|
||||
background-image: -ms-linear-gradient(to bottom, #f3c17a, #f3e97a);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a));
|
||||
background-image: -webkit-linear-gradient(to bottom, #f3c17a, #f3e97a);
|
||||
background-image: -o-linear-gradient(to bottom, #f3c17a, #f3e97a);
|
||||
background-image: linear-gradient(to bottom, #f3c17a, #f3e97a);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0);
|
||||
border-color: #f3e97a #f3e97a #edde34;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.range.today:hover,
|
||||
.datepicker table tr td.range.today:hover:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover:hover,
|
||||
.datepicker table tr td.range.today:active,
|
||||
.datepicker table tr td.range.today:hover:active,
|
||||
.datepicker table tr td.range.today.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled:hover:active,
|
||||
.datepicker table tr td.range.today.active,
|
||||
.datepicker table tr td.range.today:hover.active,
|
||||
.datepicker table tr td.range.today.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled:hover.active,
|
||||
.datepicker table tr td.range.today.disabled,
|
||||
.datepicker table tr td.range.today:hover.disabled,
|
||||
.datepicker table tr td.range.today.disabled.disabled,
|
||||
.datepicker table tr td.range.today.disabled:hover.disabled,
|
||||
.datepicker table tr td.range.today[disabled],
|
||||
.datepicker table tr td.range.today:hover[disabled],
|
||||
.datepicker table tr td.range.today.disabled[disabled],
|
||||
.datepicker table tr td.range.today.disabled:hover[disabled] {
|
||||
background-color: #f3e97a;
|
||||
}
|
||||
.datepicker table tr td.range.today:active,
|
||||
.datepicker table tr td.range.today:hover:active,
|
||||
.datepicker table tr td.range.today.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled:hover:active,
|
||||
.datepicker table tr td.range.today.active,
|
||||
.datepicker table tr td.range.today:hover.active,
|
||||
.datepicker table tr td.range.today.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled:hover.active {
|
||||
background-color: #efe24b \9;
|
||||
}
|
||||
.datepicker table tr td.selected,
|
||||
.datepicker table tr td.selected:hover,
|
||||
.datepicker table tr td.selected.disabled,
|
||||
.datepicker table tr td.selected.disabled:hover {
|
||||
background-color: #9e9e9e;
|
||||
background-image: -moz-linear-gradient(to bottom, #b3b3b3, #808080);
|
||||
background-image: -ms-linear-gradient(to bottom, #b3b3b3, #808080);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080));
|
||||
background-image: -webkit-linear-gradient(to bottom, #b3b3b3, #808080);
|
||||
background-image: -o-linear-gradient(to bottom, #b3b3b3, #808080);
|
||||
background-image: linear-gradient(to bottom, #b3b3b3, #808080);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0);
|
||||
border-color: #808080 #808080 #595959;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.selected:hover,
|
||||
.datepicker table tr td.selected:hover:hover,
|
||||
.datepicker table tr td.selected.disabled:hover,
|
||||
.datepicker table tr td.selected.disabled:hover:hover,
|
||||
.datepicker table tr td.selected:active,
|
||||
.datepicker table tr td.selected:hover:active,
|
||||
.datepicker table tr td.selected.disabled:active,
|
||||
.datepicker table tr td.selected.disabled:hover:active,
|
||||
.datepicker table tr td.selected.active,
|
||||
.datepicker table tr td.selected:hover.active,
|
||||
.datepicker table tr td.selected.disabled.active,
|
||||
.datepicker table tr td.selected.disabled:hover.active,
|
||||
.datepicker table tr td.selected.disabled,
|
||||
.datepicker table tr td.selected:hover.disabled,
|
||||
.datepicker table tr td.selected.disabled.disabled,
|
||||
.datepicker table tr td.selected.disabled:hover.disabled,
|
||||
.datepicker table tr td.selected[disabled],
|
||||
.datepicker table tr td.selected:hover[disabled],
|
||||
.datepicker table tr td.selected.disabled[disabled],
|
||||
.datepicker table tr td.selected.disabled:hover[disabled] {
|
||||
background-color: #808080;
|
||||
}
|
||||
.datepicker table tr td.selected:active,
|
||||
.datepicker table tr td.selected:hover:active,
|
||||
.datepicker table tr td.selected.disabled:active,
|
||||
.datepicker table tr td.selected.disabled:hover:active,
|
||||
.datepicker table tr td.selected.active,
|
||||
.datepicker table tr td.selected:hover.active,
|
||||
.datepicker table tr td.selected.disabled.active,
|
||||
.datepicker table tr td.selected.disabled:hover.active {
|
||||
background-color: #666666 \9;
|
||||
}
|
||||
.datepicker table tr td.active,
|
||||
.datepicker table tr td.active:hover,
|
||||
.datepicker table tr td.active.disabled,
|
||||
.datepicker table tr td.active.disabled:hover {
|
||||
background-color: #006dcc;
|
||||
background-image: -moz-linear-gradient(to bottom, #08c, #0044cc);
|
||||
background-image: -ms-linear-gradient(to bottom, #08c, #0044cc);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#0044cc));
|
||||
background-image: -webkit-linear-gradient(to bottom, #08c, #0044cc);
|
||||
background-image: -o-linear-gradient(to bottom, #08c, #0044cc);
|
||||
background-image: linear-gradient(to bottom, #08c, #0044cc);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#08c', endColorstr='#0044cc', GradientType=0);
|
||||
border-color: #0044cc #0044cc #002a80;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.active:hover,
|
||||
.datepicker table tr td.active:hover:hover,
|
||||
.datepicker table tr td.active.disabled:hover,
|
||||
.datepicker table tr td.active.disabled:hover:hover,
|
||||
.datepicker table tr td.active:active,
|
||||
.datepicker table tr td.active:hover:active,
|
||||
.datepicker table tr td.active.disabled:active,
|
||||
.datepicker table tr td.active.disabled:hover:active,
|
||||
.datepicker table tr td.active.active,
|
||||
.datepicker table tr td.active:hover.active,
|
||||
.datepicker table tr td.active.disabled.active,
|
||||
.datepicker table tr td.active.disabled:hover.active,
|
||||
.datepicker table tr td.active.disabled,
|
||||
.datepicker table tr td.active:hover.disabled,
|
||||
.datepicker table tr td.active.disabled.disabled,
|
||||
.datepicker table tr td.active.disabled:hover.disabled,
|
||||
.datepicker table tr td.active[disabled],
|
||||
.datepicker table tr td.active:hover[disabled],
|
||||
.datepicker table tr td.active.disabled[disabled],
|
||||
.datepicker table tr td.active.disabled:hover[disabled] {
|
||||
background-color: #0044cc;
|
||||
}
|
||||
.datepicker table tr td.active:active,
|
||||
.datepicker table tr td.active:hover:active,
|
||||
.datepicker table tr td.active.disabled:active,
|
||||
.datepicker table tr td.active.disabled:hover:active,
|
||||
.datepicker table tr td.active.active,
|
||||
.datepicker table tr td.active:hover.active,
|
||||
.datepicker table tr td.active.disabled.active,
|
||||
.datepicker table tr td.active.disabled:hover.active {
|
||||
background-color: #003399 \9;
|
||||
}
|
||||
.datepicker table tr td span {
|
||||
display: block;
|
||||
width: 23%;
|
||||
height: 54px;
|
||||
line-height: 54px;
|
||||
float: left;
|
||||
margin: 1%;
|
||||
cursor: pointer;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.datepicker table tr td span:hover,
|
||||
.datepicker table tr td span.focused {
|
||||
background: #eee;
|
||||
}
|
||||
.datepicker table tr td span.disabled,
|
||||
.datepicker table tr td span.disabled:hover {
|
||||
background: none;
|
||||
color: #999;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td span.active,
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active.disabled,
|
||||
.datepicker table tr td span.active.disabled:hover {
|
||||
background-color: #006dcc;
|
||||
background-image: -moz-linear-gradient(to bottom, #08c, #0044cc);
|
||||
background-image: -ms-linear-gradient(to bottom, #08c, #0044cc);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#0044cc));
|
||||
background-image: -webkit-linear-gradient(to bottom, #08c, #0044cc);
|
||||
background-image: -o-linear-gradient(to bottom, #08c, #0044cc);
|
||||
background-image: linear-gradient(to bottom, #08c, #0044cc);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#08c', endColorstr='#0044cc', GradientType=0);
|
||||
border-color: #0044cc #0044cc #002a80;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active:hover:hover,
|
||||
.datepicker table tr td span.active.disabled:hover,
|
||||
.datepicker table tr td span.active.disabled:hover:hover,
|
||||
.datepicker table tr td span.active:active,
|
||||
.datepicker table tr td span.active:hover:active,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.active,
|
||||
.datepicker table tr td span.active:hover.active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.active,
|
||||
.datepicker table tr td span.active.disabled,
|
||||
.datepicker table tr td span.active:hover.disabled,
|
||||
.datepicker table tr td span.active.disabled.disabled,
|
||||
.datepicker table tr td span.active.disabled:hover.disabled,
|
||||
.datepicker table tr td span.active[disabled],
|
||||
.datepicker table tr td span.active:hover[disabled],
|
||||
.datepicker table tr td span.active.disabled[disabled],
|
||||
.datepicker table tr td span.active.disabled:hover[disabled] {
|
||||
background-color: #0044cc;
|
||||
}
|
||||
.datepicker table tr td span.active:active,
|
||||
.datepicker table tr td span.active:hover:active,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.active,
|
||||
.datepicker table tr td span.active:hover.active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.active {
|
||||
background-color: #003399 \9;
|
||||
}
|
||||
.datepicker table tr td span.old,
|
||||
.datepicker table tr td span.new {
|
||||
color: #999;
|
||||
}
|
||||
.datepicker .datepicker-switch {
|
||||
width: 145px;
|
||||
}
|
||||
.datepicker .datepicker-switch,
|
||||
.datepicker .prev,
|
||||
.datepicker .next,
|
||||
.datepicker tfoot tr th {
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker .datepicker-switch:hover,
|
||||
.datepicker .prev:hover,
|
||||
.datepicker .next:hover,
|
||||
.datepicker tfoot tr th:hover {
|
||||
background: #eee;
|
||||
}
|
||||
.datepicker .cw {
|
||||
font-size: 10px;
|
||||
width: 12px;
|
||||
padding: 0 2px 0 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.input-append.date .add-on,
|
||||
.input-prepend.date .add-on {
|
||||
cursor: pointer;
|
||||
}
|
||||
.input-append.date .add-on i,
|
||||
.input-prepend.date .add-on i {
|
||||
margin-top: 3px;
|
||||
}
|
||||
.input-daterange input {
|
||||
text-align: center;
|
||||
}
|
||||
.input-daterange input:first-child {
|
||||
-webkit-border-radius: 3px 0 0 3px;
|
||||
-moz-border-radius: 3px 0 0 3px;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
.input-daterange input:last-child {
|
||||
-webkit-border-radius: 0 3px 3px 0;
|
||||
-moz-border-radius: 0 3px 3px 0;
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
.input-daterange .add-on {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
min-width: 16px;
|
||||
height: 18px;
|
||||
padding: 4px 5px;
|
||||
font-weight: normal;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
vertical-align: middle;
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
margin-left: -5px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-datepicker.css.map */
|
10
webfm/static/css/bootstrap/bootstrap-table.min.css
vendored
Normal file
12
webfm/static/css/bootstrap/bootstrap.min.css
vendored
Normal file
10
webfm/static/css/bootstrap/bootstrap4-toggle.min.css
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/*\
|
||||
|*| ========================================================================
|
||||
|*| Bootstrap Toggle: bootstrap4-toggle.css v3.6.1
|
||||
|*| https://gitbrent.github.io/bootstrap4-toggle/
|
||||
|*| ========================================================================
|
||||
|*| Copyright 2018-2019 Brent Ely
|
||||
|*| Licensed under MIT
|
||||
|*| ========================================================================
|
||||
\*/
|
||||
.btn-group-xs>.btn,.btn-xs{padding:.35rem .4rem .25rem .4rem;font-size:.875rem;line-height:.5;border-radius:.2rem}.checkbox label .toggle,.checkbox-inline .toggle{margin-left:-1.25rem;margin-right:.35rem}.toggle{position:relative;overflow:hidden}.toggle.btn.btn-light,.toggle.btn.btn-outline-light{border-color:rgba(0,0,0,.15)}.toggle input[type=checkbox]{display:none}.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none}.toggle-group label,.toggle-group span{cursor:pointer}.toggle.off .toggle-group{left:-100%}.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0}.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0;box-shadow:none}.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px;background-color:#fff}.toggle.btn-outline-primary .toggle-handle{background-color:var(--primary);border-color:var(--primary)}.toggle.btn-outline-secondary .toggle-handle{background-color:var(--secondary);border-color:var(--secondary)}.toggle.btn-outline-success .toggle-handle{background-color:var(--success);border-color:var(--success)}.toggle.btn-outline-danger .toggle-handle{background-color:var(--danger);border-color:var(--danger)}.toggle.btn-outline-warning .toggle-handle{background-color:var(--warning);border-color:var(--warning)}.toggle.btn-outline-info .toggle-handle{background-color:var(--info);border-color:var(--info)}.toggle.btn-outline-light .toggle-handle{background-color:var(--light);border-color:var(--light)}.toggle.btn-outline-dark .toggle-handle{background-color:var(--dark);border-color:var(--dark)}.toggle[class*=btn-outline]:hover .toggle-handle{background-color:var(--light);opacity:.5}.toggle.btn{min-width:3.7rem;min-height:2.15rem}.toggle-on.btn{padding-right:1.5rem}.toggle-off.btn{padding-left:1.5rem}.toggle.btn-lg{min-width:5rem;min-height:2.815rem}.toggle-on.btn-lg{padding-right:2rem}.toggle-off.btn-lg{padding-left:2rem}.toggle-handle.btn-lg{width:2.5rem}.toggle.btn-sm{min-width:3.125rem;min-height:1.938rem}.toggle-on.btn-sm{padding-right:1rem}.toggle-off.btn-sm{padding-left:1rem}.toggle.btn-xs{min-width:2.19rem;min-height:1.375rem}.toggle-on.btn-xs{padding-right:.8rem}.toggle-off.btn-xs{padding-left:.8rem}
|
5
webfm/static/css/iframe.css
Normal file
@ -0,0 +1,5 @@
|
||||
body,
|
||||
.container-fluid, .row, .col,
|
||||
.error-styling, .dir-style, .movie-style, .file-style {
|
||||
background-color: rgba(0,0,0,0.0);
|
||||
}
|
141
webfm/static/css/main.css
Normal file
@ -0,0 +1,141 @@
|
||||
/* IDs */
|
||||
#bg {
|
||||
position: fixed;
|
||||
top: 0%;
|
||||
left: 0%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -999;
|
||||
}
|
||||
|
||||
#bg img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -999;
|
||||
}
|
||||
|
||||
#file-grid {
|
||||
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;
|
||||
overflow-y: auto;
|
||||
max-height: 25em;
|
||||
}
|
||||
|
||||
#path { max-width: inherit; }
|
||||
#faves-list > li { width: 100%; }
|
||||
|
||||
|
||||
|
||||
/* CLASSES */
|
||||
.scroller {
|
||||
scrollbar-color: #00000084 #ffffff64;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.dir-style, .video-style, .file-style {
|
||||
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);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 42px 42px;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.dir-style, .file-style {
|
||||
background-position: 32px center;
|
||||
}
|
||||
|
||||
.image-style,
|
||||
.video-style {
|
||||
min-height: 6.5em;
|
||||
width: auto;
|
||||
overflow: hidden;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.card-popout-btn {
|
||||
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);
|
||||
}
|
||||
|
||||
.close-btn:hover,
|
||||
.card-popout-btn:hover,
|
||||
.popout-btn:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close-btn:hover,
|
||||
.card-popout-btn:hover,
|
||||
.popout-btn:hover {
|
||||
background-color: rgba(255,255,255, 0.85);
|
||||
color: #000000;
|
||||
border-color: #000000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.dir-style:hover, .image-style:hover, .video-style:hover, .file-style:hover {
|
||||
background-color: rgba(0, 141, 166, 0.56);
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 0px 15px rgb(114,184,199);
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
.dir-style:focus, .image-style:focus, .video-style:focus, .file-style:focus {
|
||||
background-color: rgba(0, 139, 35, 0.76);
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 0px 25px rgb(114, 199, 120);
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
|
||||
.dir-title, .file-title, .video-title {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
border-style: none;
|
||||
font-size: 75%;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.dir-title, .file-title {
|
||||
width: auto;
|
||||
background-color: #00000000;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.video-title {
|
||||
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;
|
||||
}
|
14
webfm/static/css/overrides.css
Normal file
@ -0,0 +1,14 @@
|
||||
.modal-content {
|
||||
background-color: #32383e74;
|
||||
border-color: #f8940674;
|
||||
}
|
||||
|
||||
.sticky-top,
|
||||
.card {
|
||||
background-color: rgba(50, 56, 62, 0.84);
|
||||
}
|
||||
|
||||
.label-as-badge {
|
||||
border-radius: 1em;
|
||||
cursor: pointer;
|
||||
}
|
BIN
webfm/static/db/webfm.db
Normal file
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 216 KiB After Width: | Height: | Size: 216 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
BIN
webfm/static/imgs/icons/link-icon.png
Normal file
After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
1
webfm/static/imgs/icons/octicons/alert.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"/></svg>
|
After Width: | Height: | Size: 366 B |
1
webfm/static/imgs/icons/octicons/archive.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M13 2H1v2h12V2zM0 4a1 1 0 0 0 1 1v9a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H1a1 1 0 0 0-1 1v2zm2 1h10v9H2V5zm2 3h6V7H4v1z"/></svg>
|
After Width: | Height: | Size: 265 B |
1
webfm/static/imgs/icons/octicons/arrow-both.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 20 16"><path d="M0 8l6-5v3h8V3l6 5-6 5v-3H6v3L0 8z"/></svg>
|
After Width: | Height: | Size: 135 B |
1
webfm/static/imgs/icons/octicons/arrow-down.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="16" viewBox="0 0 10 16"><path fill-rule="evenodd" d="M7 7V3H3v4H0l5 6 5-6H7z"/></svg>
|
After Width: | Height: | Size: 144 B |
1
webfm/static/imgs/icons/octicons/arrow-left.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="16" viewBox="0 0 10 16"><path fill-rule="evenodd" d="M6 3L0 8l6 5v-3h4V6H6V3z"/></svg>
|
After Width: | Height: | Size: 145 B |
1
webfm/static/imgs/icons/octicons/arrow-right.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="16" viewBox="0 0 10 16"><path fill-rule="evenodd" d="M10 8L4 3v3H0v4h4v3l6-5z"/></svg>
|
After Width: | Height: | Size: 145 B |
1
webfm/static/imgs/icons/octicons/arrow-small-down.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="6" height="16" viewBox="0 0 6 16"><path fill-rule="evenodd" d="M4 7V5H2v2H0l3 4 3-4H4z"/></svg>
|
After Width: | Height: | Size: 142 B |
1
webfm/static/imgs/icons/octicons/arrow-small-left.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="6" height="16" viewBox="0 0 6 16"><path fill-rule="evenodd" d="M4 7V5L0 8l4 3V9h2V7H4z"/></svg>
|
After Width: | Height: | Size: 142 B |
1
webfm/static/imgs/icons/octicons/arrow-small-right.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="6" height="16" viewBox="0 0 6 16"><path fill-rule="evenodd" d="M6 8L2 5v2H0v2h2v2l4-3z"/></svg>
|
After Width: | Height: | Size: 142 B |
1
webfm/static/imgs/icons/octicons/arrow-small-up.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="6" height="16" viewBox="0 0 6 16"><path fill-rule="evenodd" d="M3 5L0 9h2v2h2V9h2L3 5z"/></svg>
|
After Width: | Height: | Size: 142 B |
1
webfm/static/imgs/icons/octicons/arrow-up.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="16" viewBox="0 0 10 16"><path fill-rule="evenodd" d="M5 3L0 9h3v4h4V9h3L5 3z"/></svg>
|
After Width: | Height: | Size: 144 B |
1
webfm/static/imgs/icons/octicons/beaker.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M14.38 14.59L11 7V3h1V2H3v1h1v4L.63 14.59A1 1 0 0 0 1.54 16h11.94c.72 0 1.2-.75.91-1.41h-.01zM3.75 10L5 7V3h5v4l1.25 3h-7.5zM8 8h1v1H8V8zM7 7H6V6h1v1zm0-3h1v1H7V4zm0-3H6V0h1v1z"/></svg>
|
After Width: | Height: | Size: 297 B |
1
webfm/static/imgs/icons/octicons/bell.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="16" viewBox="0 0 15 16"><path fill-rule="evenodd" d="M14 12v1H0v-1l.73-.58c.77-.77.81-2.55 1.19-4.42C2.69 3.23 6 2 6 2c0-.55.45-1 1-1s1 .45 1 1c0 0 3.39 1.23 4.16 5 .38 1.88.42 3.66 1.19 4.42l.66.58H14zm-7 4c1.11 0 2-.89 2-2H5c0 1.11.89 2 2 2z"/></svg>
|
After Width: | Height: | Size: 311 B |
1
webfm/static/imgs/icons/octicons/bold.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="16" viewBox="0 0 10 16"><path fill-rule="evenodd" d="M1 2h3.83c2.48 0 4.3.75 4.3 2.95 0 1.14-.63 2.23-1.67 2.61v.06c1.33.3 2.3 1.23 2.3 2.86 0 2.39-1.97 3.52-4.61 3.52H1V2zm3.66 4.95c1.67 0 2.38-.66 2.38-1.69 0-1.17-.78-1.61-2.34-1.61H3.13v3.3h1.53zm.27 5.39c1.77 0 2.75-.64 2.75-1.98 0-1.27-.95-1.81-2.75-1.81h-1.8v3.8h1.8v-.01z"/></svg>
|
After Width: | Height: | Size: 397 B |
1
webfm/static/imgs/icons/octicons/book.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M3 5h4v1H3V5zm0 3h4V7H3v1zm0 2h4V9H3v1zm11-5h-4v1h4V5zm0 2h-4v1h4V7zm0 2h-4v1h4V9zm2-6v9c0 .55-.45 1-1 1H9.5l-1 1-1-1H2c-.55 0-1-.45-1-1V3c0-.55.45-1 1-1h5.5l1 1 1-1H15c.55 0 1 .45 1 1zm-8 .5L7.5 3H2v9h6V3.5zm7-.5H9.5l-.5.5V12h6V3z"/></svg>
|
After Width: | Height: | Size: 352 B |
1
webfm/static/imgs/icons/octicons/bookmark.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="16" viewBox="0 0 10 16"><path fill-rule="evenodd" d="M9 0H1C.27 0 0 .27 0 1v15l5-3.09L10 16V1c0-.73-.27-1-1-1zm-.78 4.25L6.36 5.61l.72 2.16c.06.22-.02.28-.2.17L5 6.6 3.12 7.94c-.19.11-.25.05-.2-.17l.72-2.16-1.86-1.36c-.17-.16-.14-.23.09-.23l2.3-.03.7-2.16h.25l.7 2.16 2.3.03c.23 0 .27.08.09.23h.01z"/></svg>
|
After Width: | Height: | Size: 366 B |
1
webfm/static/imgs/icons/octicons/briefcase.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M9 4V3c0-.55-.45-1-1-1H6c-.55 0-1 .45-1 1v1H1c-.55 0-1 .45-1 1v8c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1H9zM6 3h2v1H6V3zm7 6H8v1H6V9H1V5h1v3h10V5h1v4z"/></svg>
|
After Width: | Height: | Size: 283 B |
1
webfm/static/imgs/icons/octicons/broadcast.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M9 9H8c.55 0 1-.45 1-1V7c0-.55-.45-1-1-1H7c-.55 0-1 .45-1 1v1c0 .55.45 1 1 1H6c-.55 0-1 .45-1 1v2h1v3c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-3h1v-2c0-.55-.45-1-1-1zM7 7h1v1H7V7zm2 4H8v4H7v-4H6v-1h3v1zm2.09-3.5c0-1.98-1.61-3.59-3.59-3.59A3.593 3.593 0 0 0 4 8.31v1.98c-.61-.77-1-1.73-1-2.8 0-2.48 2.02-4.5 4.5-4.5S12 5.01 12 7.49c0 1.06-.39 2.03-1 2.8V8.31c.06-.27.09-.53.09-.81zm3.91 0c0 2.88-1.63 5.38-4 6.63v-1.05a6.553 6.553 0 0 0 3.09-5.58A6.59 6.59 0 0 0 7.5.91 6.59 6.59 0 0 0 .91 7.5c0 2.36 1.23 4.42 3.09 5.58v1.05A7.497 7.497 0 0 1 7.5 0C11.64 0 15 3.36 15 7.5z"/></svg>
|
After Width: | Height: | Size: 686 B |
1
webfm/static/imgs/icons/octicons/browser.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M5 3h1v1H5V3zM3 3h1v1H3V3zM1 3h1v1H1V3zm12 10H1V5h12v8zm0-9H7V3h6v1zm1-1c0-.55-.45-1-1-1H1c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V3z"/></svg>
|
After Width: | Height: | Size: 268 B |
1
webfm/static/imgs/icons/octicons/bug.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M11 10h3V9h-3V8l3.17-1.03-.34-.94L11 7V6c0-.55-.45-1-1-1V4c0-.48-.36-.88-.83-.97L10.2 2H12V1H9.8l-2 2h-.59L5.2 1H3v1h1.8l1.03 1.03C5.36 3.12 5 3.51 5 4v1c-.55 0-1 .45-1 1v1l-2.83-.97-.34.94L4 8v1H1v1h3v1L.83 12.03l.34.94L4 12v1c0 .55.45 1 1 1h1l1-1V6h1v7l1 1h1c.55 0 1-.45 1-1v-1l2.83.97.34-.94L11 11v-1zM9 5H6V4h3v1z"/></svg>
|
After Width: | Height: | Size: 438 B |
1
webfm/static/imgs/icons/octicons/calendar.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M13 2h-1v1.5c0 .28-.22.5-.5.5h-2c-.28 0-.5-.22-.5-.5V2H6v1.5c0 .28-.22.5-.5.5h-2c-.28 0-.5-.22-.5-.5V2H2c-.55 0-1 .45-1 1v11c0 .55.45 1 1 1h11c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1zm0 12H2V5h11v9zM5 3H4V1h1v2zm6 0h-1V1h1v2zM6 7H5V6h1v1zm2 0H7V6h1v1zm2 0H9V6h1v1zm2 0h-1V6h1v1zM4 9H3V8h1v1zm2 0H5V8h1v1zm2 0H7V8h1v1zm2 0H9V8h1v1zm2 0h-1V8h1v1zm-8 2H3v-1h1v1zm2 0H5v-1h1v1zm2 0H7v-1h1v1zm2 0H9v-1h1v1zm2 0h-1v-1h1v1zm-8 2H3v-1h1v1zm2 0H5v-1h1v1zm2 0H7v-1h1v1zm2 0H9v-1h1v1z"/></svg>
|
After Width: | Height: | Size: 588 B |
1
webfm/static/imgs/icons/octicons/check.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z"/></svg>
|
After Width: | Height: | Size: 162 B |
1
webfm/static/imgs/icons/octicons/checklist.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M16 8.5l-6 6-3-3L8.5 10l1.5 1.5L14.5 7 16 8.5zM5.7 12.2l.8.8H2c-.55 0-1-.45-1-1V3c0-.55.45-1 1-1h7c.55 0 1 .45 1 1v6.5l-.8-.8c-.39-.39-1.03-.39-1.42 0L5.7 10.8a.996.996 0 0 0 0 1.41v-.01zM4 4h5V3H4v1zm0 2h5V5H4v1zm0 2h3V7H4v1zM3 9H2v1h1V9zm0-2H2v1h1V7zm0-2H2v1h1V5zm0-2H2v1h1V3z"/></svg>
|
After Width: | Height: | Size: 399 B |
1
webfm/static/imgs/icons/octicons/chevron-down.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="16" viewBox="0 0 10 16"><path fill-rule="evenodd" d="M5 11L0 6l1.5-1.5L5 8.25 8.5 4.5 10 6l-5 5z"/></svg>
|
After Width: | Height: | Size: 164 B |
1
webfm/static/imgs/icons/octicons/chevron-left.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="16" viewBox="0 0 8 16"><path fill-rule="evenodd" d="M5.5 3L7 4.5 3.25 8 7 11.5 5.5 13l-5-5 5-5z"/></svg>
|
After Width: | Height: | Size: 162 B |
1
webfm/static/imgs/icons/octicons/chevron-right.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="16" viewBox="0 0 8 16"><path fill-rule="evenodd" d="M7.5 8l-5 5L1 11.5 4.75 8 1 4.5 2.5 3l5 5z"/></svg>
|
After Width: | Height: | Size: 161 B |
1
webfm/static/imgs/icons/octicons/chevron-up.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="16" viewBox="0 0 10 16"><path fill-rule="evenodd" d="M10 10l-1.5 1.5L5 7.75 1.5 11.5 0 10l5-5 5 5z"/></svg>
|
After Width: | Height: | Size: 166 B |
1
webfm/static/imgs/icons/octicons/circle-slash.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm0 1.3c1.3 0 2.5.44 3.47 1.17l-8 8A5.755 5.755 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zm0 11.41c-1.3 0-2.5-.44-3.47-1.17l8-8c.73.97 1.17 2.17 1.17 3.47 0 3.14-2.56 5.7-5.7 5.7z"/></svg>
|
After Width: | Height: | Size: 349 B |
1
webfm/static/imgs/icons/octicons/circuit-board.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M3 5c0-.55.45-1 1-1s1 .45 1 1-.45 1-1 1-1-.45-1-1zm8 0c0-.55-.45-1-1-1s-1 .45-1 1 .45 1 1 1 1-.45 1-1zm0 6c0-.55-.45-1-1-1s-1 .45-1 1 .45 1 1 1 1-.45 1-1zm2-10H5v2.17c.36.19.64.47.83.83h2.34c.42-.78 1.33-1.28 2.34-1.05.75.19 1.36.8 1.53 1.55.31 1.38-.72 2.59-2.05 2.59-.8 0-1.48-.44-1.83-1.09H5.83c-.42.8-1.33 1.28-2.34 1.03-.73-.17-1.34-.78-1.52-1.52C1.72 4.49 2.2 3.59 3 3.17V1H1c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1l5-5h2.17c.42-.78 1.33-1.28 2.34-1.05.75.19 1.36.8 1.53 1.55.31 1.38-.72 2.59-2.05 2.59-.8 0-1.48-.44-1.83-1.09H6.99L4 15h9c.55 0 1-.45 1-1V2c0-.55-.45-1-1-1z"/></svg>
|
After Width: | Height: | Size: 695 B |
1
webfm/static/imgs/icons/octicons/clippy.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M2 13h4v1H2v-1zm5-6H2v1h5V7zm2 3V8l-3 3 3 3v-2h5v-2H9zM4.5 9H2v1h2.5V9zM2 12h2.5v-1H2v1zm9 1h1v2c-.02.28-.11.52-.3.7-.19.18-.42.28-.7.3H1c-.55 0-1-.45-1-1V4c0-.55.45-1 1-1h3c0-1.11.89-2 2-2 1.11 0 2 .89 2 2h3c.55 0 1 .45 1 1v5h-1V6H1v9h10v-2zM2 5h8c0-.55-.45-1-1-1H8c-.55 0-1-.45-1-1s-.45-1-1-1-1 .45-1 1-.45 1-1 1H3c-.55 0-1 .45-1 1z"/></svg>
|
After Width: | Height: | Size: 455 B |
1
webfm/static/imgs/icons/octicons/clock.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M8 8h3v2H7c-.55 0-1-.45-1-1V4h2v4zM7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7z"/></svg>
|
After Width: | Height: | Size: 306 B |
1
webfm/static/imgs/icons/octicons/cloud-download.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M9 12h2l-3 3-3-3h2V7h2v5zm3-8c0-.44-.91-3-4.5-3C5.08 1 3 2.92 3 5 1.02 5 0 6.52 0 8c0 1.53 1 3 3 3h3V9.7H3C1.38 9.7 1.3 8.28 1.3 8c0-.17.05-1.7 1.7-1.7h1.3V5c0-1.39 1.56-2.7 3.2-2.7 2.55 0 3.13 1.55 3.2 1.8v1.2H12c.81 0 2.7.22 2.7 2.2 0 2.09-2.25 2.2-2.7 2.2h-2V11h2c2.08 0 4-1.16 4-3.5C16 5.06 14.08 4 12 4z"/></svg>
|
After Width: | Height: | Size: 429 B |