Merge remote-tracking branch 'origin/master'
17
README.md
@ -1,12 +1,17 @@
|
||||
# DashMachine
|
||||
### Another web application bookmark dashboard, with fun features.
|
||||
|
||||
## Before Installing
|
||||
Please read the latest update post: https://www.reddit.com/r/DashMachine/comments/fqk8gl/version_05/
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Features
|
||||
* creates a dashboard to view web pages
|
||||
* uses a single .ini file for configuration
|
||||
@ -18,6 +23,7 @@
|
||||
* user login system
|
||||
* 'app templates' which are sample config entries for popular self hosted apps
|
||||
* powerful plugin system for adding data from various sources to display on cards
|
||||
* multiple card types including collections and custom cards
|
||||
* multiple users, access groups, access settings
|
||||
* tagging system
|
||||
|
||||
@ -41,12 +47,14 @@ docker create \
|
||||
--restart unless-stopped \
|
||||
rmountjoy/dashmachine:latest
|
||||
```
|
||||
### Synology
|
||||
Check out this awesome guide: https://nashosted.com/manage-your-self-hosted-applications-using-dashmachine/
|
||||
### Python
|
||||
Instructions are for linux.
|
||||
```
|
||||
virtualenv --python=python3 DashMachineEnv
|
||||
cd DashMachineEnv && source bin/activate
|
||||
git clone https://git.wolf-house.net/ross/DashMachine.git
|
||||
git clone https://github.com/rmountjoy92/DashMachine.git
|
||||
cd DashMachine && pip install -r requirements.txt
|
||||
python3 run.py
|
||||
```
|
||||
@ -55,16 +63,12 @@ Then open a web browser and go to localhost:5000
|
||||
## Default user/password
|
||||
```
|
||||
User: admin
|
||||
Password: adminadmin
|
||||
Password: admin
|
||||
```
|
||||
|
||||
## Updating
|
||||
For python, use git. For docker, just pull the latest image and recreate the container.
|
||||
|
||||
**Note:** if you update DashMachine and it fails to start, it's possible something is messed up
|
||||
with your database file. Backup your files in the user_data folder, delete the contents and
|
||||
restart DashMachine. This will reset your user table, so log in with the default user/pass.
|
||||
|
||||
## Configuration
|
||||
The user data folder is located at DashMachine/dashmachine/user_data. This is where the config.ini, custom backgrounds/icons, and the database file live. A reference for what can go into the config.ini file can be found on the settings page of the dashmachine by clicking the info icon next to 'Config'.
|
||||
|
||||
@ -99,7 +103,6 @@ Bountysource faq: https://github.com/bountysource/core/wiki/Frequently-Asked-Que
|
||||
* Jinja2
|
||||
* Materialize css
|
||||
* JavaScript/jQuery/jQueryUI
|
||||
* Requests (python)
|
||||
|
||||
## FAQs
|
||||
1. application does not work in iframe
|
||||
|
119
config_readme.md
@ -1,119 +0,0 @@
|
||||
#### Config.ini Readme
|
||||
|
||||
##### Settings
|
||||
This is the configuration entry for DashMachine's settings. DashMachine will not work if
|
||||
this is missing. As for all config entries, [Settings] can only appear once in the config.
|
||||
If you change the config.ini file, you either have to restart the container
|
||||
(or python script) or click the ‘save’ button in the config section of settings for the
|
||||
config to be applied.
|
||||
```ini
|
||||
[Settings]
|
||||
theme = light
|
||||
accent = orange
|
||||
background = None
|
||||
roles = admin,user,public_user
|
||||
home_access_groups = admin_only
|
||||
settings_access_groups = admin_only
|
||||
custom_app_title = DashMachine
|
||||
```
|
||||
|
||||
| Variable | Required | Description | Options |
|
||||
|------------------------|----------|----------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [Settings] | Yes | Config section name. | [Settings] |
|
||||
| theme | Yes | UI theme. | light, dark |
|
||||
| accent | Yes | UI accent color | orange, red, pink, purple, deepPurple, indigo, blue, lightBlue,cyan, teal, green, lightGreen, lime, yellow, amber, deepOrange, brown, grey, blueGrey |
|
||||
| background | Yes | Background image for the UI | /static/images/backgrounds/yourpicture.png, external link to image, None, random |
|
||||
| roles | No | User roles for access groups. | comma separated string, if not defined, this is set to 'admin,user,public_user'. Note: admin, user, public_user roles are required and will be added automatically if omitted. |
|
||||
| home_access_groups | No | Define which access groups can access the /home page | Groups defined in your config. If not defined, default is admin_only |
|
||||
| settings_access_groups | No | Define which access groups can access the /settings page | Groups defined in your config. If not defined, default is admin_only |
|
||||
| custom_app_title | No | Change the title of the app for browser tabs | string |
|
||||
|
||||
##### Apps
|
||||
These entries are the cards that you see one the home page, as well as the sidenav. Entries
|
||||
must be unique. They are displayed in the order that they appear in config.ini
|
||||
```ini
|
||||
[App Name]
|
||||
prefix = https://
|
||||
url = your-website.com
|
||||
icon = static/images/apps/default.png
|
||||
sidebar_icon = static/images/apps/default.png
|
||||
description = Example description
|
||||
open_in = iframe
|
||||
data_sources = None
|
||||
tags = Example Tag
|
||||
groups = admin_only
|
||||
```
|
||||
|
||||
| Variable | Required | Description | Options |
|
||||
|--------------|----------|-------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------|
|
||||
| [App Name] | Yes | The name of your app. | [App Name] |
|
||||
| prefix | Yes | The prefix for the app's url. | web prefix, e.g. http:// or https:// |
|
||||
| url | Yes | The url for your app. | web url, e.g. myapp.com |
|
||||
| open_in | Yes | open the app in the current tab, an iframe or a new tab | iframe, new_tab, this_tab |
|
||||
| icon | No | Icon for the dashboard. | /static/images/icons/yourpicture.png, external link to image |
|
||||
| sidebar_icon | No | Icon for the sidenav. | /static/images/icons/yourpicture.png, external link to image |
|
||||
| description | No | A short description for the app. | string |
|
||||
| data_sources | No | Data sources to be included on the app's card.*Note: you must have a data source set up in the config above this application entry. | comma separated string |
|
||||
| tags | No | Optionally specify tags for organization on /home | comma separated string |
|
||||
| groups | No | Optionally the access groups that can see this app. | comma separated string |
|
||||
|
||||
##### Access Groups
|
||||
You can create access groups to control what user roles can access parts of the ui. Access groups are just a collection of roles, and each user has an attribute 'role'. Each
|
||||
application can have an access group, if the user's role is not in the group, the app will be hidden.
|
||||
Also, in the settings entry you can specify `home_access_groups` and `settings_access_groups` to control
|
||||
which groups can access /home and /settings
|
||||
```ini
|
||||
[admin_only]
|
||||
roles = admin
|
||||
```
|
||||
|
||||
| Variable | Required | Description | Options |
|
||||
|--------------|----------|--------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
|
||||
| [Group Name] | Yes | Name for access group. | [Group Name] |
|
||||
| roles | Yes | A comma separated list of user roles allowed to view apps in this access group | Roles defined in your config. If not defined, defaults are admin and public_user |
|
||||
|
||||
> Say we wanted to create a limited user that still has a login, but can only access `/home` and certain apps we would first create a group:
|
||||
>```ini
|
||||
>[users]
|
||||
>roles = admin, user
|
||||
>```
|
||||
>then we would change in the `[Settings]` entry:
|
||||
>```ini
|
||||
>home_access_groups = users
|
||||
>```
|
||||
>By default here, the `user` user could access `/home`, but would see no apps. To allow access, we would add to apps:
|
||||
>```ini
|
||||
>groups = users
|
||||
>```
|
||||
>Say we then wanted to allow some access for users without a login (`public_user`), we would add:
|
||||
>```ini
|
||||
>[public]
|
||||
>roles = admin, user, public_user
|
||||
>```
|
||||
>then we would change in the `[Settings]` entry:
|
||||
>```ini
|
||||
>home_access_groups = public
|
||||
>```
|
||||
>By default here, the `public_user` user could access `/home`, but would see no apps. To allow access, we would add to apps:
|
||||
>```ini
|
||||
>groups = public
|
||||
>```
|
||||
|
||||
|
||||
>It’s also important to note, when setting up roles in `[Settings]`, say we had roles set like this:
|
||||
>```ini
|
||||
>roles = my_people
|
||||
>```
|
||||
>Dashmachine will automatically add `admin,user,public_user`, so really you would have 4 roles: `my_people,admin,user,public_user`. Also, the `admin_only` group is required and added by default if omitted.
|
||||
|
||||
#### Data Source Platforms
|
||||
DashMachine includes several different 'platforms' for displaying data on your dash applications.
|
||||
Platforms are essentially plugins. All data source config entries require the `platform` variable,
|
||||
which tells DashMachine which platform file in the platform folder to load. **Note:** you are able to
|
||||
load your own platform files by placing them in the platform folder and referencing them in the config.
|
||||
However currently they will be deleted if you update the application, if you would like to make them
|
||||
permanent, submit a pull request for it to be added by default!
|
||||
|
||||
> To add a data source to your app, add a data source config entry from one of the samples below
|
||||
**above** the application entry in config.ini, then add the following to your app config entry:
|
||||
`data_source = variable_name`
|
@ -30,6 +30,7 @@ api = Api(app)
|
||||
app.config["AVATARS_IDENTICON_BG"] = (255, 255, 255)
|
||||
app.config["SECRET_KEY"] = "66532a62c4048f976e22a39638b6f10e"
|
||||
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///user_data/site.db"
|
||||
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
||||
app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 0
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
|
@ -28,6 +28,8 @@ class Apps(db.Model):
|
||||
data_template = db.Column(db.String())
|
||||
groups = db.Column(db.String())
|
||||
tags = db.Column(db.String())
|
||||
type = db.Column(db.String())
|
||||
urls = db.Column(db.String())
|
||||
|
||||
|
||||
class DataSources(db.Model):
|
||||
@ -58,3 +60,5 @@ class Groups(db.Model):
|
||||
class Tags(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String())
|
||||
icon = db.Column(db.String())
|
||||
sort_pos = db.Column(db.Integer)
|
||||
|
@ -1,6 +1,13 @@
|
||||
import os
|
||||
import json
|
||||
from configparser import ConfigParser
|
||||
from dashmachine.main.models import Apps, Groups, DataSources, DataSourcesArgs, Tags
|
||||
from dashmachine.user_system.models import User
|
||||
from dashmachine.user_system.utils import (
|
||||
hash_and_cache_password,
|
||||
get_cached_password,
|
||||
clean_auth_cache,
|
||||
)
|
||||
from dashmachine.settings_system.models import Settings
|
||||
from dashmachine.paths import user_data_folder
|
||||
from dashmachine import db
|
||||
@ -15,7 +22,7 @@ def row2dict(row):
|
||||
|
||||
|
||||
def read_config():
|
||||
config = ConfigParser()
|
||||
config = ConfigParser(interpolation=None)
|
||||
try:
|
||||
config.read(os.path.join(user_data_folder, "config.ini"))
|
||||
except Exception as e:
|
||||
@ -30,6 +37,7 @@ def read_config():
|
||||
Settings.query.delete()
|
||||
Groups.query.delete()
|
||||
Tags.query.delete()
|
||||
User.query.delete()
|
||||
|
||||
for section in config.sections():
|
||||
|
||||
@ -61,15 +69,57 @@ def read_config():
|
||||
settings.settings_access_groups = config["Settings"].get(
|
||||
"settings_access_groups", "admin_only"
|
||||
)
|
||||
settings.home_view_mode = config["Settings"].get("home_view_mode", "grid")
|
||||
|
||||
settings.custom_app_title = config["Settings"].get(
|
||||
"custom_app_title", "DashMachine"
|
||||
)
|
||||
|
||||
settings.sidebar_default = config["Settings"].get("sidebar_default", "open")
|
||||
|
||||
settings.tags_expanded = config["Settings"].get("tags_expanded", "True")
|
||||
|
||||
db.session.add(settings)
|
||||
db.session.commit()
|
||||
|
||||
# User creation
|
||||
elif "role" in config[section]:
|
||||
user = User()
|
||||
user.username = section
|
||||
user.role = config[section]["role"]
|
||||
user.sidebar_default = config[section].get("sidebar_default", None)
|
||||
user.theme = config[section].get("theme", None)
|
||||
user.accent = config[section].get("accent", None)
|
||||
user.tags_expanded = config[section].get("tags_expanded", None)
|
||||
user.password = ""
|
||||
if not User.query.filter_by(role="admin").first() and user.role != "admin":
|
||||
print(
|
||||
f"Invalid Config: admin user not specified, or not specified first. {user.username} role set to admin"
|
||||
)
|
||||
user.role = "admin"
|
||||
config.set(section, "role", "admin")
|
||||
config.write(open(os.path.join(user_data_folder, "config.ini"), "w"))
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
new_password = config[section].get("password", None)
|
||||
if new_password:
|
||||
if new_password == config[section].get("confirm_password", None):
|
||||
password = hash_and_cache_password(new_password, user.id)
|
||||
user.password = password
|
||||
db.session.merge(user)
|
||||
db.session.commit()
|
||||
else:
|
||||
password = get_cached_password(user.id)
|
||||
if password == "error":
|
||||
print(
|
||||
f"Invalid Config: Password for {user.username} must be specified. Using 'admin' by default"
|
||||
)
|
||||
user.password = password
|
||||
db.session.merge(user)
|
||||
db.session.commit()
|
||||
config.set(section, "password", "")
|
||||
config.set(section, "confirm_password", "")
|
||||
config.write(open(os.path.join(user_data_folder, "config.ini"), "w"))
|
||||
|
||||
# Groups creation
|
||||
elif "roles" in config[section]:
|
||||
group = Groups()
|
||||
@ -98,35 +148,25 @@ def read_config():
|
||||
# App creation
|
||||
app = Apps()
|
||||
app.name = section
|
||||
if "prefix" in config[section]:
|
||||
app.prefix = config[section]["prefix"]
|
||||
else:
|
||||
app.type = config[section].get("type", "app")
|
||||
|
||||
app.prefix = config[section].get("prefix", None)
|
||||
if app.type == "app" and not app.prefix:
|
||||
return {"msg": f"Invalid Config: {section} does not contain prefix."}
|
||||
|
||||
if "url" in config[section]:
|
||||
app.url = config[section]["url"]
|
||||
else:
|
||||
app.url = config[section].get("url", None)
|
||||
if app.type == "app" and not app.url:
|
||||
return {"msg": f"Invalid Config: {section} does not contain url."}
|
||||
|
||||
if "icon" in config[section]:
|
||||
app.icon = config[section]["icon"]
|
||||
else:
|
||||
app.icon = None
|
||||
app.icon = config[section].get("icon", None)
|
||||
|
||||
if "sidebar_icon" in config[section]:
|
||||
app.sidebar_icon = config[section]["sidebar_icon"]
|
||||
else:
|
||||
app.sidebar_icon = app.icon
|
||||
app.sidebar_icon = config[section].get("sidebar_icon", None)
|
||||
|
||||
if "description" in config[section]:
|
||||
app.description = config[section]["description"]
|
||||
else:
|
||||
app.description = None
|
||||
app.description = config[section].get("description", None)
|
||||
|
||||
if "open_in" in config[section]:
|
||||
app.open_in = config[section]["open_in"]
|
||||
else:
|
||||
app.open_in = "this_tab"
|
||||
app.open_in = config[section].get("open_in", "this_tab")
|
||||
|
||||
app.urls = config[section].get("urls", None)
|
||||
|
||||
if "groups" in config[section]:
|
||||
for group_name in config[section]["groups"].split(","):
|
||||
@ -138,23 +178,24 @@ def read_config():
|
||||
else:
|
||||
app.groups = None
|
||||
|
||||
# Tags creation
|
||||
if "tags" in config[section]:
|
||||
app.tags = config[section]["tags"].title()
|
||||
app.tags = config[section]["tags"]
|
||||
for tag in app.tags.split(","):
|
||||
tag = tag.strip().title()
|
||||
tag = tag.strip()
|
||||
if not Tags.query.filter_by(name=tag).first():
|
||||
tag_db = Tags(name=tag)
|
||||
db.session.add(tag_db)
|
||||
db.session.commit()
|
||||
else:
|
||||
if Tags.query.first():
|
||||
app.tags = "Untagged"
|
||||
if not Tags.query.filter_by(name="Untagged").first():
|
||||
tag_db = Tags(name="Untagged")
|
||||
db.session.add(tag_db)
|
||||
tag_db.sort_pos = tag_db.id
|
||||
db.session.merge(tag_db)
|
||||
db.session.commit()
|
||||
else:
|
||||
app.tags = None
|
||||
else:
|
||||
app.tags = "Untagged"
|
||||
if not Tags.query.filter_by(name="Untagged").first():
|
||||
tag_db = Tags(name="Untagged")
|
||||
db.session.add(tag_db)
|
||||
db.session.commit()
|
||||
|
||||
db.session.add(app)
|
||||
db.session.commit()
|
||||
@ -178,4 +219,33 @@ def read_config():
|
||||
group.roles = "admin"
|
||||
db.session.add(group)
|
||||
db.session.commit()
|
||||
|
||||
tags_settings = config["Settings"].get("tags", None)
|
||||
if tags_settings:
|
||||
tags_settings = tags_settings.replace("},{", "}%,%{").split("%,%")
|
||||
|
||||
for tag_setting in tags_settings:
|
||||
tag_json = json.loads(tag_setting)
|
||||
tag = Tags.query.filter_by(name=tag_json.get("name", None)).first()
|
||||
if tag:
|
||||
icon = tag_json.get("icon", None)
|
||||
if icon:
|
||||
tag.icon = icon
|
||||
sort_pos = tag_json.get("sort_pos", None)
|
||||
if icon:
|
||||
tag.sort_pos = sort_pos
|
||||
db.session.merge(tag)
|
||||
db.session.commit()
|
||||
|
||||
clean_auth_cache()
|
||||
if not User.query.first():
|
||||
user = User()
|
||||
user.username = "admin"
|
||||
user.role = "admin"
|
||||
user.password = ""
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
user.password = hash_and_cache_password("admin", user.id)
|
||||
db.session.merge(user)
|
||||
db.session.commit()
|
||||
return {"msg": "success", "settings": row2dict(settings)}
|
||||
|
@ -5,13 +5,13 @@ from htmlmin.main import minify
|
||||
from configparser import ConfigParser
|
||||
from flask import render_template, url_for, redirect, request, Blueprint, jsonify
|
||||
from flask_login import current_user
|
||||
from dashmachine.main.models import Files, Apps, DataSources, Tags
|
||||
from dashmachine.main.forms import TagsForm
|
||||
from dashmachine.main.models import Files, Apps, DataSources
|
||||
from dashmachine.main.utils import (
|
||||
public_route,
|
||||
check_groups,
|
||||
get_data_source,
|
||||
mark_update_message_read,
|
||||
)
|
||||
from dashmachine.user_system.models import User
|
||||
from dashmachine.settings_system.models import Settings
|
||||
from dashmachine.paths import cache_folder, user_data_folder
|
||||
from dashmachine import app, db
|
||||
@ -35,28 +35,9 @@ def response_minify(response):
|
||||
return response
|
||||
|
||||
|
||||
# blocks access to all pages (except public routes) unless the user is
|
||||
# signed in.
|
||||
@main.before_app_request
|
||||
def check_valid_login():
|
||||
|
||||
if any(
|
||||
[
|
||||
request.endpoint.startswith("static"),
|
||||
current_user.is_authenticated,
|
||||
getattr(app.view_functions[request.endpoint], "is_public", False),
|
||||
]
|
||||
):
|
||||
return
|
||||
|
||||
else:
|
||||
return redirect(url_for("user_system.login"))
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# /home
|
||||
# ------------------------------------------------------------------------------
|
||||
@public_route
|
||||
@main.route("/")
|
||||
@main.route("/home", methods=["GET"])
|
||||
def home():
|
||||
@ -66,7 +47,6 @@ def home():
|
||||
return render_template("main/home.html")
|
||||
|
||||
|
||||
@public_route
|
||||
@main.route("/app_view?<app_id>", methods=["GET"])
|
||||
def app_view(app_id):
|
||||
settings = Settings.query.first()
|
||||
@ -85,18 +65,10 @@ def load_data_source():
|
||||
return data
|
||||
|
||||
|
||||
@public_route
|
||||
@main.route("/change_home_view_mode?<mode>", methods=["GET"])
|
||||
def change_home_view_mode(mode):
|
||||
config = ConfigParser()
|
||||
config.read(os.path.join(user_data_folder, "config.ini"))
|
||||
config.set("Settings", "home_view_mode", mode)
|
||||
config.write(open(os.path.join(user_data_folder, "config.ini"), "w"))
|
||||
settings = Settings.query.first()
|
||||
settings.home_view_mode = mode
|
||||
db.session.merge(settings)
|
||||
db.session.commit()
|
||||
return redirect(url_for("main.home"))
|
||||
@main.route("/update_message_read", methods=["GET"])
|
||||
def update_message_read():
|
||||
mark_update_message_read()
|
||||
return "ok"
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -1,13 +1,17 @@
|
||||
import os
|
||||
import importlib
|
||||
from shutil import copyfile
|
||||
from PIL import Image, ImageOps
|
||||
from dashmachine.paths import dashmachine_folder, images_folder
|
||||
from PIL import Image
|
||||
from markdown2 import markdown
|
||||
from dashmachine.paths import (
|
||||
dashmachine_folder,
|
||||
images_folder,
|
||||
root_folder,
|
||||
user_data_folder,
|
||||
)
|
||||
from dashmachine.main.models import Groups
|
||||
from dashmachine.main.read_config import read_config
|
||||
from dashmachine.settings_system.models import Settings
|
||||
from dashmachine.user_system.models import User
|
||||
from dashmachine.user_system.utils import add_edit_user
|
||||
from dashmachine.version import version as dashmachine_version
|
||||
from dashmachine import db
|
||||
|
||||
|
||||
@ -54,20 +58,6 @@ def dashmachine_init():
|
||||
|
||||
read_config()
|
||||
|
||||
user = User.query.first()
|
||||
if not user:
|
||||
settings = Settings.query.first()
|
||||
add_edit_user(
|
||||
username="admin",
|
||||
password="adminadmin",
|
||||
role=settings.roles.split(",")[0].strip(),
|
||||
)
|
||||
|
||||
users = User.query.all()
|
||||
for user in users:
|
||||
if not user.role:
|
||||
user.role = "admin"
|
||||
|
||||
|
||||
def check_groups(groups, current_user):
|
||||
if current_user.is_anonymous:
|
||||
@ -111,3 +101,35 @@ def resize_template_app_images():
|
||||
image = Image.open(fp)
|
||||
image.thumbnail((64, 64))
|
||||
image.save(fp)
|
||||
|
||||
|
||||
def get_update_message_html():
|
||||
try:
|
||||
with open(os.path.join(user_data_folder, ".has_read_update"), "r") as has_read:
|
||||
has_read_version = has_read.read()
|
||||
except FileNotFoundError:
|
||||
has_read_version = None
|
||||
if not has_read_version or has_read_version.strip() != dashmachine_version:
|
||||
with open(
|
||||
os.path.join(root_folder, "update_message.md"), "r"
|
||||
) as update_message:
|
||||
md = update_message.read()
|
||||
|
||||
config_html = markdown(
|
||||
md,
|
||||
extras=[
|
||||
"tables",
|
||||
"fenced-code-blocks",
|
||||
"break-on-newline",
|
||||
"header-ids",
|
||||
"code-friendly",
|
||||
],
|
||||
)
|
||||
return config_html
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def mark_update_message_read():
|
||||
with open(os.path.join(user_data_folder, ".has_read_update"), "w") as has_read:
|
||||
has_read.write(dashmachine_version)
|
||||
|
@ -19,6 +19,11 @@ platform_folder = os.path.join(dashmachine_folder, "platform")
|
||||
|
||||
user_data_folder = os.path.join(dashmachine_folder, "user_data")
|
||||
|
||||
auth_cache = os.path.join(user_data_folder, "auth_cache")
|
||||
|
||||
if not os.path.isdir(auth_cache):
|
||||
os.mkdir(auth_cache)
|
||||
|
||||
static_folder = os.path.join(dashmachine_folder, "static")
|
||||
|
||||
images_folder = os.path.join(static_folder, "images")
|
||||
|
@ -14,7 +14,7 @@ response_type = json
|
||||
| Variable | Required | Description | Options |
|
||||
|-----------------|----------|-----------------------------------------------------------------|-------------------|
|
||||
| [variable_name] | Yes | Name for the data source. | [variable_name] |
|
||||
| plaform | Yes | Name of the platform. | curl |
|
||||
| platform | Yes | Name of the platform. | curl |
|
||||
| resource | Yes | Url to curl | url |
|
||||
| value_template | Yes | Jinja template for how the returned data from api is displayed. | jinja template |
|
||||
| response_type | No | Response type. Use json if response is a JSON. Default is plain.| plain,json |
|
||||
|
102
dashmachine/platform/http_status.py
Normal file
@ -0,0 +1,102 @@
|
||||
"""
|
||||
|
||||
##### http_status
|
||||
Make a http call on a given URL and display if the service is online.
|
||||
```ini
|
||||
[variable_name]
|
||||
platform = http_status
|
||||
resource = https://your-website.com/api
|
||||
method = get
|
||||
authentication = basic
|
||||
username = my_username
|
||||
password = my_password
|
||||
headers = {"Content-Type": "application/json"}
|
||||
return_codes = 2xx,3xx
|
||||
```
|
||||
> **Returns:** a right-aligned colored bullet point on the app card.
|
||||
|
||||
| Variable | Required | Description | Options |
|
||||
|-----------------|----------|-----------------------------------------------------------------|-------------------|
|
||||
| [variable_name] | Yes | Name for the data source. | [variable_name] |
|
||||
| platform | Yes | Name of the platform. | rest |
|
||||
| resource | Yes | Url of rest api resource. | url |
|
||||
| method | No | Method for the api call, default is GET | GET,HEAD,OPTIONS,TRACE|
|
||||
| authentication | No | Authentication for the api call, default is None | None,basic,digest |
|
||||
| username | No | Username to use for auth. | string |
|
||||
| password | No | Password to use for auth. | string |
|
||||
| headers | No | Request headers | json |
|
||||
| return_codes | No | Acceptable http status codes, x is handled as wildcard | string |
|
||||
|
||||
> **Working example:**
|
||||
>```ini
|
||||
>[http_status_test]
|
||||
>platform = http_status
|
||||
>resource = https://google.com
|
||||
>return_codes = 2xx,3xx
|
||||
>
|
||||
>[Google]
|
||||
>prefix = https://
|
||||
>url = google.com
|
||||
>icon = static/images/apps/default.png
|
||||
>open_in = this_tab
|
||||
>data_sources = http_status_test
|
||||
>```
|
||||
|
||||
"""
|
||||
|
||||
from requests import Request, Session
|
||||
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
|
||||
|
||||
|
||||
class Platform:
|
||||
def __init__(self, *args, **kwargs):
|
||||
# parse the user's options from the config entries
|
||||
for key, value in kwargs.items():
|
||||
self.__dict__[key] = value
|
||||
|
||||
# set defaults for omitted options
|
||||
if not hasattr(self, "method"):
|
||||
self.method = "GET"
|
||||
if not hasattr(self, "authentication"):
|
||||
self.authentication = None
|
||||
if not hasattr(self, "headers"):
|
||||
self.headers = None
|
||||
if not hasattr(self, "return_codes"):
|
||||
self.return_codes = "2xx,3xx"
|
||||
if not hasattr(self, "ssl_ignore"):
|
||||
self.ssl_ignore = "No"
|
||||
|
||||
def process(self):
|
||||
# Check if method is within allowed methods for http_status
|
||||
if self.method.upper() not in ["GET", "HEAD", "OPTIONS", "TRACE"]:
|
||||
raise NotImplementedError
|
||||
|
||||
s = Session()
|
||||
# prepare Authentication mechanism
|
||||
if self.authentication:
|
||||
if self.authentication.lower() == "digest":
|
||||
auth = HTTPDigestAuth(self.username, self.password)
|
||||
else:
|
||||
auth = HTTPBasicAuth(self.username, self.password)
|
||||
else:
|
||||
auth = None
|
||||
|
||||
# Send request
|
||||
req = Request(
|
||||
self.method.upper(), self.resource, headers=self.headers, auth=auth
|
||||
)
|
||||
prepped = req.prepare()
|
||||
if self.ssl_ignore == "yes":
|
||||
resp = s.send(prepped,verify=False)
|
||||
else:
|
||||
resp = s.send(prepped)
|
||||
resp = s.send(prepped)
|
||||
|
||||
return_codes = tuple([x.replace("x", "") for x in self.return_codes.split(",")])
|
||||
|
||||
if str(resp.status_code).startswith(return_codes):
|
||||
icon_class = "theme-success-text"
|
||||
else:
|
||||
icon_class = "theme-failure-text"
|
||||
|
||||
return f"<i class='material-icons right {icon_class}'>fiber_manual_record </i>"
|
@ -1,3 +1,58 @@
|
||||
"""
|
||||
|
||||
##### PiHole
|
||||
Display information from the PiHole API
|
||||
```ini
|
||||
[variable_name]
|
||||
platform = pihole
|
||||
host = 192.168.1.101
|
||||
password = {{ PiHole password }}
|
||||
value_template = {{ value_template }}
|
||||
```
|
||||
> **Returns:** `value_template` as rendered string
|
||||
|
||||
| Variable | Required | Description | Options |
|
||||
|-----------------|----------|-----------------------------------------------------------------|-------------------|
|
||||
| [variable_name] | Yes | Name for the data source. | [variable_name] |
|
||||
| platform | Yes | Name of the platform. | pihole |
|
||||
| host | Yes | Host of the PiHole | host |
|
||||
| password | Yes | Password for the PiHole | password |
|
||||
| value_template | Yes | Jinja template for how the returned data from API is displayed. | jinja template |
|
||||
|
||||
|
||||
<br />
|
||||
###### **Available fields for value_template**
|
||||
|
||||
* domain_count
|
||||
* queries
|
||||
* blocked
|
||||
* ads_percentage
|
||||
* unique_domains
|
||||
* forwarded
|
||||
* cached
|
||||
* total_clients
|
||||
* unique_clients
|
||||
* total_queries
|
||||
* gravity_last_updated
|
||||
|
||||
> **Working example:**
|
||||
>```ini
|
||||
> [pihole-data]
|
||||
> platform = pihole
|
||||
> host = 192.168.1.101
|
||||
> password = password123
|
||||
> value_template = Ads Blocked Today: {{ blocked }}<br>Status: {{ status }}<br>Queries today: {{ queries }}
|
||||
>
|
||||
> [PiHole]
|
||||
> prefix = http://
|
||||
> url = 192.168.1.101
|
||||
> icon = static/images/apps/pihole.png
|
||||
> description = A black hole for Internet advertisements
|
||||
> open_in = new_tab
|
||||
> data_sources = pihole-data
|
||||
>```
|
||||
"""
|
||||
|
||||
from flask import render_template_string
|
||||
|
||||
|
||||
|
@ -12,6 +12,8 @@ authentication = basic
|
||||
username = my_username
|
||||
password = my_password
|
||||
payload = {"var1": "hi", "var2": 1}
|
||||
headers = {"Content-Type": "application/json"}
|
||||
verify = false
|
||||
```
|
||||
> **Returns:** `value_template` as rendered string
|
||||
|
||||
@ -26,6 +28,8 @@ payload = {"var1": "hi", "var2": 1}
|
||||
| username | No | Username to use for auth. | string |
|
||||
| password | No | Password to use for auth. | string |
|
||||
| payload | No | Payload for post request. | json |
|
||||
| headers | No | Custom headers for get or post | json |
|
||||
| verify | No | Turn TLS verification on or off, default is True | true,false |
|
||||
|
||||
> **Working example:**
|
||||
>```ini
|
||||
@ -55,6 +59,8 @@ class Platform:
|
||||
def __init__(self, *args, **kwargs):
|
||||
# parse the user's options from the config entries
|
||||
for key, value in kwargs.items():
|
||||
if key == "headers":
|
||||
value = json.loads(value)
|
||||
self.__dict__[key] = value
|
||||
|
||||
# set defaults for omitted options
|
||||
@ -62,24 +68,38 @@ class Platform:
|
||||
self.method = "GET"
|
||||
if not hasattr(self, "authentication"):
|
||||
self.authentication = None
|
||||
if not hasattr(self, "headers"):
|
||||
self.headers = None
|
||||
if not hasattr(self, "verify"):
|
||||
self.verify = True
|
||||
|
||||
def process(self):
|
||||
if self.authentication:
|
||||
if self.authentication.lower() == "digest":
|
||||
auth = HTTPDigestAuth(self.username, self.password)
|
||||
else:
|
||||
auth = HTTPBasicAuth(self.username, self.password)
|
||||
else:
|
||||
auth = None
|
||||
|
||||
verify = False if str(self.verify).lower() == "false" else True
|
||||
|
||||
if self.method.upper() == "GET":
|
||||
try:
|
||||
value = get(self.resource).json()
|
||||
value = get(
|
||||
self.resource, auth=auth, headers=self.headers, verify=verify
|
||||
).json()
|
||||
except Exception as e:
|
||||
value = f"{e}"
|
||||
|
||||
elif self.method.upper() == "POST":
|
||||
if self.authentication:
|
||||
if self.authentication.lower() == "digest":
|
||||
auth = HTTPDigestAuth(self.username, self.password)
|
||||
else:
|
||||
auth = HTTPBasicAuth(self.username, self.password)
|
||||
else:
|
||||
auth = None
|
||||
|
||||
payload = json.loads(self.payload.replace("'", '"'))
|
||||
value = post(self.resource, data=payload, auth=auth)
|
||||
value = post(
|
||||
self.resource,
|
||||
data=payload,
|
||||
auth=auth,
|
||||
headers=self.headers,
|
||||
verify=verify,
|
||||
)
|
||||
value_template = render_template_string(self.value_template, value=value)
|
||||
return value_template
|
||||
|
@ -1,3 +1,57 @@
|
||||
"""
|
||||
|
||||
##### Transmission
|
||||
Display information from the Trasnmission API
|
||||
```ini
|
||||
[variable_name]
|
||||
platform = transmission
|
||||
host = localhost
|
||||
port = 9091
|
||||
user = {{ transmission Web UI username }}
|
||||
password = {{ Transmission Web UI password }}
|
||||
value_template = {{ value_template }}
|
||||
```
|
||||
> **Returns:** `value_template` as rendered string
|
||||
|
||||
| Variable | Required | Description | Options |
|
||||
|-----------------|----------|-----------------------------------------------------------------|-------------------|
|
||||
| [variable_name] | Yes | Name for the data source. | [variable_name] |
|
||||
| platform | Yes | Name of the platform. | transmission |
|
||||
| host | Yes | Host of Transmission Web UI | host |
|
||||
| port | Yes | Port of Transmission Web UI | port |
|
||||
| user | Yes | Username for Transmission Web UI | username |
|
||||
| password | Yes | Password for Transmission Web UI | password |
|
||||
| value_template | Yes | Jinja template for how the returned data from API is displayed. | jinja template |
|
||||
|
||||
<br />
|
||||
###### **Available fields for value_template**
|
||||
|
||||
* downloadSpeed
|
||||
* uploadSpeed
|
||||
* activeTorrentCount
|
||||
* pausedTorrentCount
|
||||
* torrentCount
|
||||
|
||||
> **Working example:**
|
||||
>```ini
|
||||
> [transmission-data]
|
||||
> platform = transmission
|
||||
> host = 192.168.1.30
|
||||
> port = 9091
|
||||
> user = admin
|
||||
> password = password123
|
||||
> value_template = 🔽 {{(downloadSpeed/1024/1024)|round(2)}} MB/s<br>🔼 {{(uploadSpeed/1024/1024)|round(2)}} MB/s<br><strong>Active:</strong> {{activeTorrentCount}}<br>
|
||||
>
|
||||
> [Transmission]
|
||||
> prefix = http://
|
||||
> url = 192.168.1.30:9091
|
||||
> icon = static/images/apps/transmission.png
|
||||
> description = A Fast, Easy, and Free BitTorrent Client
|
||||
> open_in = new_tab
|
||||
> data_sources = transmission-data
|
||||
>```
|
||||
"""
|
||||
|
||||
import json
|
||||
from flask import render_template_string
|
||||
import transmissionrpc
|
||||
|
139
dashmachine/platform/weather.py
Normal file
@ -0,0 +1,139 @@
|
||||
"""
|
||||
|
||||
##### Weather
|
||||
Weather is a great example of how you can populate a custom card on the dash. This plugin creates a custom card with weather data from [metaweather.com](https://www.metaweather.com)
|
||||
```ini
|
||||
[variable_name]
|
||||
platform = weather
|
||||
woeid = 2514815
|
||||
temp_unit = c
|
||||
wind_speed_unit = kph
|
||||
air_pressure_unit = mbar
|
||||
visibility_unit = km
|
||||
```
|
||||
> **Returns:** HTML for custom card
|
||||
|
||||
| Variable | Required | Description | Options |
|
||||
|-----------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------|-------------------|
|
||||
| [variable_name] | Yes | Name for the data source. | [variable_name] |
|
||||
| platform | Yes | Name of the platform. | weather |
|
||||
| woeid | Yes | woeid of location to use. Go here to get (replace lat and long): https://www.metaweather.com/api/location/search/?lattlong=50.068,-5.316 | url |
|
||||
| temp_unit | No | The unit to be used for temperature | c,f |
|
||||
| wind_speed_unit | No | The unit to be used for wind speed | kph,mph |
|
||||
| air_pressure_unit | No | The unit to be used for air pressure | mbar, inHg |
|
||||
| visibility_unit | No | The unit to be used for visibility | km,mi |
|
||||
|
||||
> **Working example:**
|
||||
>```ini
|
||||
>[variable_name]
|
||||
>platform = weather
|
||||
>woeid = 2514815
|
||||
>
|
||||
>[custom_card_name]
|
||||
>type = custom
|
||||
>data_sources = variable_name
|
||||
>```
|
||||
|
||||
"""
|
||||
|
||||
import requests
|
||||
from flask import render_template_string
|
||||
|
||||
|
||||
class Platform:
|
||||
def __init__(self, *args, **kwargs):
|
||||
# parse the user's options from the config entries
|
||||
for key, value in kwargs.items():
|
||||
self.__dict__[key] = value
|
||||
|
||||
# set defaults for omitted options
|
||||
if not hasattr(self, "woeid"):
|
||||
self.woeid = 2514815
|
||||
if not hasattr(self, "temp_unit"):
|
||||
self.temp_unit = "c"
|
||||
if not hasattr(self, "wind_speed_unit"):
|
||||
self.wind_speed_unit = "kph"
|
||||
if not hasattr(self, "air_pressure_unit"):
|
||||
self.air_pressure_unit = "x"
|
||||
if not hasattr(self, "visibility_unit"):
|
||||
self.visibility_unit = "km"
|
||||
|
||||
self.html_template = """
|
||||
<div class="row">
|
||||
<div class="col s6">
|
||||
<span class="mt-0 mb-0 theme-primary-text font-weight-700" style="font-size: 36px">{{ value.consolidated_weather[0].the_temp|round(1, 'floor') }}°</h3>
|
||||
</div>
|
||||
<div class="col s6 right-align">
|
||||
<img height="48px" src="https://www.metaweather.com/static/img/weather/{{ value.consolidated_weather[0].weather_state_abbr }}.svg">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<h6 class="font-weight-900 center theme-muted-text">{{ value.title }}</h6>
|
||||
</div>
|
||||
<div class="row center-align">
|
||||
<i class="material-icons-outlined">keyboard_arrow_down</i>
|
||||
</div>
|
||||
<div class="row center-align">
|
||||
<div class="col s12">
|
||||
<div class="collection theme-muted-text">
|
||||
<div class="collection-item"><span class="font-weight-900">Currently: </span>{{ value.consolidated_weather[0].weather_state_name }}</div>
|
||||
<div class="collection-item"><span class="font-weight-900">Min: </span>{{ value.consolidated_weather[0].min_temp|round(1, 'floor') }}° <span class="font-weight-900">Max: </span>{{ value.consolidated_weather[0].max_temp|round(1, 'floor') }}°</div>
|
||||
<div class="collection-item"><span class="font-weight-900">Wind: </span>{{ value.consolidated_weather[0].wind_direction_compass }} at {{ value.consolidated_weather[0].wind_speed|round(1, 'floor') }} {{ wind_speed_unit }}</div>
|
||||
<div class="collection-item"><span class="font-weight-900">Humidity: </span>{{ value.consolidated_weather[0].humidity }}%</div>
|
||||
<div class="collection-item"><span class="font-weight-900">Air Pressure: </span>{{ value.consolidated_weather[0].air_pressure|round(1, 'floor') }} {{ air_pressure_unit }}</div>
|
||||
<div class="collection-item"><span class="font-weight-900">Visibility: </span>{{ value.consolidated_weather[0].visibility|round(1, 'floor') }} {{ visibility_unit }} </div>
|
||||
<div class="collection-item"><span class="font-weight-900">Predictability: </span>{{ value.consolidated_weather[0].predictability }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
self.error_template = """
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<span class="theme-failure-text font-weight-900">Check your config. This error was returned: {{ error }}</span>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
def process(self):
|
||||
try:
|
||||
value = requests.get(
|
||||
f"https://www.metaweather.com/api/location/{self.woeid}"
|
||||
).json()
|
||||
except Exception as error:
|
||||
return render_template_string(self.error_template, error=error)
|
||||
|
||||
if self.temp_unit.lower() == "f":
|
||||
value["consolidated_weather"][0]["the_temp"] = (
|
||||
value["consolidated_weather"][0]["the_temp"] * 1.8
|
||||
) + 32
|
||||
value["consolidated_weather"][0]["min_temp"] = (
|
||||
value["consolidated_weather"][0]["min_temp"] * 1.8
|
||||
) + 32
|
||||
value["consolidated_weather"][0]["max_temp"] = (
|
||||
value["consolidated_weather"][0]["max_temp"] * 1.8
|
||||
) + 32
|
||||
|
||||
if self.wind_speed_unit.lower() == "mph":
|
||||
value["consolidated_weather"][0]["wind_speed"] = (
|
||||
value["consolidated_weather"][0]["wind_speed"] * 1.609
|
||||
)
|
||||
|
||||
if self.air_pressure_unit.lower() == "inhg":
|
||||
value["consolidated_weather"][0]["air_pressure"] = (
|
||||
value["consolidated_weather"][0]["air_pressure"] / 33.864
|
||||
)
|
||||
|
||||
if self.visibility_unit.lower() == "mi":
|
||||
value["consolidated_weather"][0]["visibility"] = (
|
||||
value["consolidated_weather"][0]["visibility"] / 1.609
|
||||
)
|
||||
|
||||
return render_template_string(
|
||||
self.html_template,
|
||||
value=value,
|
||||
wind_speed_unit=self.wind_speed_unit,
|
||||
air_pressure_unit=self.air_pressure_unit,
|
||||
visibility_unit=self.visibility_unit,
|
||||
)
|
@ -9,5 +9,6 @@ class Settings(db.Model):
|
||||
roles = db.Column(db.String())
|
||||
home_access_groups = db.Column(db.String())
|
||||
settings_access_groups = db.Column(db.String())
|
||||
home_view_mode = db.Column(db.String())
|
||||
custom_app_title = db.Column(db.String())
|
||||
sidebar_default = db.Column(db.String())
|
||||
tags_expanded = db.Column(db.String())
|
||||
|
@ -4,7 +4,6 @@ from configparser import ConfigParser
|
||||
from flask_login import current_user
|
||||
from flask import render_template, request, Blueprint, jsonify, redirect, url_for
|
||||
from dashmachine.user_system.forms import UserForm
|
||||
from dashmachine.user_system.utils import add_edit_user
|
||||
from dashmachine.user_system.models import User
|
||||
from dashmachine.main.utils import public_route, check_groups
|
||||
from dashmachine.main.read_config import read_config
|
||||
@ -18,8 +17,7 @@ from dashmachine.paths import (
|
||||
user_data_folder,
|
||||
template_apps_folder,
|
||||
)
|
||||
from dashmachine.version import version
|
||||
from dashmachine import db
|
||||
from dashmachine.version import version, revision_number
|
||||
|
||||
settings_system = Blueprint("settings_system", __name__)
|
||||
|
||||
@ -54,6 +52,7 @@ def settings():
|
||||
user_form=user_form,
|
||||
template_apps=",".join(template_apps),
|
||||
version=version,
|
||||
revision_number=revision_number,
|
||||
users=users,
|
||||
config_readme=config_readme,
|
||||
)
|
||||
@ -92,55 +91,7 @@ def delete_file():
|
||||
|
||||
@settings_system.route("/settings/get_app_template", methods=["GET"])
|
||||
def get_app_template():
|
||||
# template_app = TemplateApps.query.filter_by(name=request.args.get("name")).first()
|
||||
# template = f"[{template_app.name}]<br>"
|
||||
# for key, value in row2dict(template_app).items():
|
||||
# if key not in ["id", "name"]:
|
||||
# template += f"{key} = {value}<br>"
|
||||
|
||||
fn = os.path.join(template_apps_folder, f"{request.args.get('name')}.ini")
|
||||
with open(fn, "r") as template_app_ini:
|
||||
template = template_app_ini.read().replace("\n", "<br>")
|
||||
return template
|
||||
|
||||
|
||||
@settings_system.route("/settings/edit_user", methods=["POST"])
|
||||
def edit_user():
|
||||
form = UserForm()
|
||||
settings_db = Settings.query.first()
|
||||
form.role.choices += [(role, role) for role in settings_db.roles.split(",")]
|
||||
if form.validate_on_submit():
|
||||
if form.password.data != form.confirm_password.data:
|
||||
return jsonify(data={"err": "Passwords don't match"})
|
||||
err = add_edit_user(
|
||||
form.username.data,
|
||||
form.password.data,
|
||||
user_id=form.id.data,
|
||||
role=form.role.data,
|
||||
)
|
||||
if err:
|
||||
return jsonify(data={"err": err})
|
||||
else:
|
||||
err_str = ""
|
||||
for fieldName, errorMessages in form.errors.items():
|
||||
err_str += f"{fieldName}: "
|
||||
for err in errorMessages:
|
||||
err_str += f"{err} "
|
||||
return jsonify(data={"err": err_str})
|
||||
users = User.query.all()
|
||||
html = render_template("settings_system/user.html", users=users)
|
||||
return jsonify(data={"err": "success", "html": html})
|
||||
|
||||
|
||||
@settings_system.route("/settings/delete_user", methods=["GET"])
|
||||
def delete_user():
|
||||
admin_users = User.query.filter_by(role="admin").all()
|
||||
user = User.query.filter_by(id=request.args.get("id")).first()
|
||||
if len(admin_users) < 2 and user.role == "admin":
|
||||
return jsonify(data={"err": "You must have at least one admin user"})
|
||||
else:
|
||||
User.query.filter_by(id=request.args.get("id")).delete()
|
||||
db.session.commit()
|
||||
users = User.query.all()
|
||||
html = render_template("settings_system/user.html", users=users)
|
||||
return jsonify(data={"err": "success", "html": html})
|
||||
|
@ -18,19 +18,8 @@ def load_files_html():
|
||||
)
|
||||
|
||||
|
||||
def get_config_html():
|
||||
with open(os.path.join(root_folder, "config_readme.md")) as readme_file:
|
||||
md = readme_file.read()
|
||||
platforms = os.listdir(platform_folder)
|
||||
platforms = sorted(platforms)
|
||||
for platform in platforms:
|
||||
name, extension = os.path.splitext(platform)
|
||||
if extension.lower() == ".py":
|
||||
module = importlib.import_module(f"dashmachine.platform.{name}", ".")
|
||||
if module.__doc__:
|
||||
md += module.__doc__
|
||||
|
||||
config_html = markdown(
|
||||
def convert_html_to_md(md):
|
||||
html = markdown(
|
||||
md,
|
||||
extras=[
|
||||
"tables",
|
||||
@ -40,4 +29,28 @@ def get_config_html():
|
||||
"code-friendly",
|
||||
],
|
||||
)
|
||||
return config_html
|
||||
return html
|
||||
|
||||
|
||||
def get_config_html():
|
||||
with open(os.path.join(root_folder, "readme_settings.md")) as readme_file:
|
||||
md = readme_file.read()
|
||||
html = {"settings": convert_html_to_md(md)}
|
||||
|
||||
with open(os.path.join(root_folder, "readme_cards.md")) as readme_file:
|
||||
md = readme_file.read()
|
||||
html["cards"] = convert_html_to_md(md)
|
||||
|
||||
with open(os.path.join(root_folder, "readme_data_sources.md")) as readme_file:
|
||||
md = readme_file.read()
|
||||
platforms = os.listdir(platform_folder)
|
||||
platforms = sorted(platforms)
|
||||
for platform in platforms:
|
||||
name, extension = os.path.splitext(platform)
|
||||
if extension.lower() == ".py":
|
||||
module = importlib.import_module(f"dashmachine.platform.{name}", ".")
|
||||
if module.__doc__:
|
||||
md += module.__doc__
|
||||
html["data_sources"] = convert_html_to_md(md)
|
||||
|
||||
return html
|
||||
|
@ -1,10 +1,11 @@
|
||||
import os
|
||||
import json
|
||||
import random
|
||||
from jsmin import jsmin
|
||||
from flask_login import current_user
|
||||
from dashmachine import app
|
||||
from dashmachine.main.models import Apps
|
||||
from dashmachine.main.utils import check_groups
|
||||
from dashmachine.main.models import Apps, Tags
|
||||
from dashmachine.main.utils import check_groups, get_update_message_html
|
||||
from dashmachine.main.forms import TagsForm
|
||||
from dashmachine.settings_system.models import Settings
|
||||
from dashmachine.paths import static_folder, backgrounds_images_folder
|
||||
@ -73,24 +74,40 @@ def process_css_sources(process_bundle=None, src=None, app_global=False):
|
||||
return html
|
||||
|
||||
|
||||
def tag_sort_func(e):
|
||||
if not e.sort_pos:
|
||||
e.sort_pos = 99999
|
||||
return e.sort_pos
|
||||
|
||||
|
||||
@app.context_processor
|
||||
def context_processor():
|
||||
apps = []
|
||||
temp_tags = []
|
||||
tags = []
|
||||
apps_db = Apps.query.all()
|
||||
for app_db in apps_db:
|
||||
if app_db.urls:
|
||||
url_list = app_db.urls.replace("},{", "}%,%{").split("%,%")
|
||||
app_db.urls_json = []
|
||||
for url in url_list:
|
||||
app_db.urls_json.append(json.loads(url))
|
||||
if not app_db.groups:
|
||||
app_db.groups = None
|
||||
if check_groups(app_db.groups, current_user):
|
||||
apps.append(app_db)
|
||||
if app_db.tags:
|
||||
tags += app_db.tags.split(",")
|
||||
temp_tags += app_db.tags.split(",")
|
||||
|
||||
tags_form = TagsForm()
|
||||
if len(tags) > 0:
|
||||
tags = [tag.strip() for tag in tags]
|
||||
tags = list(dict.fromkeys(tags))
|
||||
tags_form.tags.choices += [(tag, tag) for tag in tags]
|
||||
if len(temp_tags) > 0:
|
||||
temp_tags = list(dict.fromkeys([tag.strip() for tag in temp_tags]))
|
||||
tags_form.tags.choices += [(tag, tag) for tag in temp_tags]
|
||||
for tag in temp_tags:
|
||||
tag_db = Tags.query.filter_by(name=tag).first()
|
||||
if tag_db:
|
||||
tags.append(tag_db)
|
||||
tags.sort(key=tag_sort_func)
|
||||
settings = Settings.query.first()
|
||||
if settings.background == "random":
|
||||
if len(os.listdir(backgrounds_images_folder)) < 1:
|
||||
@ -100,11 +117,14 @@ def context_processor():
|
||||
f"static/images/backgrounds/"
|
||||
f"{random.choice(os.listdir(backgrounds_images_folder))}"
|
||||
)
|
||||
update_message = get_update_message_html()
|
||||
return dict(
|
||||
test_key="test",
|
||||
process_js_sources=process_js_sources,
|
||||
process_css_sources=process_css_sources,
|
||||
apps=apps,
|
||||
settings=settings,
|
||||
tags=tags,
|
||||
tags_form=tags_form,
|
||||
update_message=update_message,
|
||||
)
|
||||
|
@ -36,6 +36,10 @@
|
||||
overflow-y: scroll !important;
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
.scrollbar-x {
|
||||
overflow-y: scroll !important;
|
||||
overflow-x: scroll !important;
|
||||
}
|
||||
|
||||
/* ELEMENT STLYES */
|
||||
body {
|
||||
@ -333,15 +337,27 @@ input:disabled {
|
||||
.navbar .navbar-light ul a {
|
||||
color: var(--theme-color-font);
|
||||
}
|
||||
/*@media (min-width: 900px)*/
|
||||
/*{*/
|
||||
/* .sidenav-overlay*/
|
||||
/* {*/
|
||||
/* display: none !important;*/
|
||||
.sidenav {
|
||||
background-color: var(--theme-surface);
|
||||
top: unset;
|
||||
overflow-y: scroll;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
/* opacity: 0 !important;*/
|
||||
/* }*/
|
||||
/*}*/
|
||||
#sidenav-mobile-toggle-btn {
|
||||
position: fixed;
|
||||
top: unset;
|
||||
bottom: 10px;
|
||||
}
|
||||
.sidenav-active-rounded .sidenav li > a.active > i {
|
||||
color: var(--theme-on-primary) !important;
|
||||
}
|
||||
.sidenav-active-rounded .sidenav li > a.active > span {
|
||||
color: var(--theme-on-primary) !important;
|
||||
}
|
||||
.sidenav-active-rounded .sidenav li > a.active {
|
||||
min-width: 3.6rem;
|
||||
}
|
||||
|
||||
/* MODALS AND CARDS */
|
||||
.modal {
|
||||
@ -383,7 +399,10 @@ input:disabled {
|
||||
border-radius: 8px;
|
||||
background: var(--theme-surface);
|
||||
}
|
||||
|
||||
.card .card-reveal {
|
||||
border-radius: 8px;
|
||||
background: var(--theme-surface);
|
||||
}
|
||||
|
||||
/* TABS */
|
||||
.tabs {
|
||||
@ -461,71 +480,6 @@ input:disabled {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* DATE AND TIME PICKERS */
|
||||
.datepicker-date-display {
|
||||
background-color: var(--theme-primary)
|
||||
}
|
||||
|
||||
.datepicker-cancel {
|
||||
color: var(--theme-primary)
|
||||
}
|
||||
|
||||
.datepicker-done {
|
||||
color: var(--theme-primary)
|
||||
}
|
||||
|
||||
.datepicker-modal {
|
||||
border-radius: 12px;
|
||||
z-index: 2001 !important;
|
||||
}
|
||||
|
||||
.is-today {
|
||||
color: var(--theme-primary) !important
|
||||
}
|
||||
|
||||
.is-selected {
|
||||
background-color: var(--theme-primary) !important
|
||||
}
|
||||
|
||||
.timepicker-close {
|
||||
color: var(--theme-primary)
|
||||
}
|
||||
|
||||
.timepicker-digital-display {
|
||||
background: var(--theme-primary)
|
||||
}
|
||||
|
||||
.timepicker-canvas-bg,
|
||||
.timepicker-canvas-bearing {
|
||||
fill: var(--theme-primary);
|
||||
}
|
||||
|
||||
.timepicker-canvas line {
|
||||
stroke: var(--theme-primary);
|
||||
}
|
||||
|
||||
.timepicker input[type=text]:not(.browser-default) {
|
||||
border-bottom: 1px solid var(--theme-primary) !important;
|
||||
box-shadow: 0 1px 0 0 var(--theme-primary) !important;
|
||||
}
|
||||
|
||||
.date-field, .time-field {
|
||||
color: var(--theme-primary) !important;
|
||||
max-width: 60px;
|
||||
max-height: 20px;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
cursor: pointer;
|
||||
font-size: 13.5px !important;
|
||||
position: relative;
|
||||
bottom: 6px;
|
||||
}
|
||||
|
||||
.date-field:focus {
|
||||
color: var(--theme-primary) !important;
|
||||
}
|
||||
|
||||
|
||||
/* "CARD FILTER" GLOBAL SEXY SEARCH BAR */
|
||||
.card-filter-container {
|
||||
position: -webkit-sticky;
|
||||
@ -572,109 +526,6 @@ input:disabled {
|
||||
color: var(--theme-secondary);
|
||||
}
|
||||
|
||||
/* CARD SECTIONS USED IN DEALS AND PROFILES */
|
||||
.tabs-card {
|
||||
border-bottom-right-radius: 0px !important;
|
||||
border-bottom-left-radius: 0px !important;
|
||||
z-index: 1;
|
||||
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0);
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
border-radius: 0 0 0 0 !important;
|
||||
z-index: 3;
|
||||
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.rows-card {
|
||||
z-index: 2;
|
||||
border-top-right-radius: 0px !important;
|
||||
border-top-left-radius: 0px !important;
|
||||
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
/* FEED */
|
||||
#feed-body {
|
||||
min-height: calc(100vh - 220px);
|
||||
max-height: calc(100vh - 220px);
|
||||
}
|
||||
#slide-out-right.sidenav #activity .header-search-input {
|
||||
background-color: var(--theme-background);
|
||||
}
|
||||
|
||||
#slide-out-right.sidenav {
|
||||
max-height: calc(100vh - 64px);
|
||||
bottom: 0;
|
||||
top: unset;
|
||||
width: 380px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.slide-out-right-body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidenav {
|
||||
background-color: var(--theme-surface);
|
||||
top: unset;
|
||||
overflow-y: scroll;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
#sidenav-mobile-toggle-btn {
|
||||
position: fixed;
|
||||
top: unset;
|
||||
bottom: 10px;
|
||||
}
|
||||
.sidenav-active-rounded .sidenav li > a.active > i {
|
||||
color: var(--theme-on-primary) !important;
|
||||
}
|
||||
.sidenav-active-rounded .sidenav li > a.active > span {
|
||||
color: var(--theme-on-primary) !important;
|
||||
}
|
||||
.sidenav-active-rounded .sidenav li > a.active {
|
||||
min-width: 3.6rem;
|
||||
}
|
||||
|
||||
.border-bottom-1 {
|
||||
border-bottom: 1px solid var(--theme-background);
|
||||
}
|
||||
|
||||
#feed-rows-ul .feed-item-li {
|
||||
background-color: var(--theme-surface);
|
||||
}
|
||||
|
||||
/* Settings collapsible */
|
||||
.settings-collapsible {
|
||||
border-radius: 12px;
|
||||
border: transparent;
|
||||
}
|
||||
|
||||
.settings-collapsible li {
|
||||
background-color: var(--theme-surface-1);
|
||||
border-radius: 12px;
|
||||
border: transparent;
|
||||
}
|
||||
|
||||
.settings-collapsible .collapsible-header {
|
||||
border-radius: 12px;
|
||||
border: transparent;
|
||||
background-color: var(--theme-surface-1);
|
||||
}
|
||||
|
||||
.settings-collapsible .collapsible-body {
|
||||
border-radius: 12px;
|
||||
border: transparent;
|
||||
background-color: var(--theme-surface-1);
|
||||
}
|
||||
|
||||
.settings-collapsible .collection-item {
|
||||
border: transparent;
|
||||
}
|
||||
|
||||
/* MISC COMPONENTS */
|
||||
.divider {
|
||||
margin-top: 15px;
|
||||
@ -688,12 +539,6 @@ span.badge.new {
|
||||
float: unset;
|
||||
}
|
||||
|
||||
#breadcrumbs-wrapper {
|
||||
padding-top: 0px;
|
||||
padding-bottom: 10px;
|
||||
background: var(--theme-background);
|
||||
}
|
||||
|
||||
.toast {
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
@ -712,29 +557,6 @@ span.badge.new {
|
||||
max-width: 25vh;
|
||||
}
|
||||
|
||||
.task-filter-button {
|
||||
margin: 5px;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.avatar-img {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.big-icon-btn {
|
||||
width: 100%;
|
||||
border-radius: 20px;
|
||||
padding: 15px;
|
||||
background: var(--theme-surface-1);
|
||||
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.big-icon-btn .material-icons-outlined {
|
||||
font-size: 5rem;
|
||||
color: var(--theme-secondary);
|
||||
}
|
||||
|
||||
.icon-btn, .icon-btn-warning {
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
@ -759,19 +581,6 @@ span.badge.new {
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.label-filter .material-icons-outlined,
|
||||
.contact-filter .material-icons-outlined,
|
||||
.deal-filter .material-icons-outlined,
|
||||
.task-filter .material-icons-outlined {
|
||||
position: relative;
|
||||
top: 6px
|
||||
}
|
||||
|
||||
.deals-collection .deal-row-img {
|
||||
height: 64px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: var(--theme-background);
|
||||
}
|
||||
@ -780,15 +589,6 @@ span.badge.new {
|
||||
background-color: var(--theme-primary);
|
||||
}
|
||||
|
||||
.task-type-icon {
|
||||
background: var(--theme-primary);
|
||||
border-radius: 24px;
|
||||
padding: 5px;
|
||||
font-size: 16px;
|
||||
position: relative;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
/*FAB*/
|
||||
.tap-target-wave::before, .tap-target-wave::after {
|
||||
background-color: var(--theme-background);
|
||||
|
@ -17,11 +17,16 @@
|
||||
@media screen and (max-width: 992px) {
|
||||
.tags-select-col {
|
||||
top: 0;
|
||||
width: calc(100vw - 45px) !important;
|
||||
margin-left: 15px !important;
|
||||
/*width: calc(100vw - 45px) !important;*/
|
||||
/*margin-left: 15px !important;*/
|
||||
}
|
||||
}
|
||||
|
||||
.app-card .card-reveal {
|
||||
position:
|
||||
|
||||
}
|
||||
|
||||
#list-view-collection .app-a {
|
||||
background: rgba(var(--theme-surface-rgb), 0.8);
|
||||
}
|
||||
|
@ -28,18 +28,18 @@
|
||||
background: var(--theme-surface-1);
|
||||
}
|
||||
|
||||
#config-readme h5 {
|
||||
#settings-readme h5, #cards-readme h5, #data-sources-readme h5 {
|
||||
color: var(--theme-primary);
|
||||
margin-top: 5%;
|
||||
}
|
||||
#config-readme h4 {
|
||||
#settings-readme h4, #cards-readme h4, #data-sources-readme h4 {
|
||||
color: var(--theme-color-font-muted);
|
||||
margin-top: 5%;
|
||||
}
|
||||
#configini-readme {
|
||||
margin-top: 2% !important;
|
||||
}
|
||||
#config-readme code {
|
||||
#settings-readme code, #cards-readme code, #data-sources-readme code {
|
||||
-webkit-touch-callout: all;
|
||||
-webkit-user-select: all;
|
||||
-khtml-user-select: all;
|
||||
@ -47,11 +47,12 @@
|
||||
-ms-user-select: all;
|
||||
user-select: all;
|
||||
cursor: text;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
#config-readme th {
|
||||
#settings-readme th, #cards-readme th, #data-sources-readme th {
|
||||
color: var(--theme-primary);
|
||||
}
|
||||
#config-readme td {
|
||||
#settings-readme td, #cards-readme td, #data-sources-readme td {
|
||||
-webkit-touch-callout: text !important;
|
||||
-webkit-user-select: text !important;
|
||||
-khtml-user-select: text !important;
|
||||
@ -60,6 +61,6 @@
|
||||
user-select: text !important;
|
||||
cursor: text;
|
||||
}
|
||||
#config-readme strong {
|
||||
#settings-readme strong, #cards-readme strong, #data-sources-readme strong {
|
||||
font-weight: 900;
|
||||
}
|
BIN
dashmachine/static/images/apps/alertmanager.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
dashmachine/static/images/apps/aria2.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
dashmachine/static/images/apps/cloudcmd.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.5 KiB |
BIN
dashmachine/static/images/apps/filerun.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
dashmachine/static/images/apps/glances.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
dashmachine/static/images/apps/gogs.png
Executable file
After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 4.7 KiB |
BIN
dashmachine/static/images/apps/guacamole.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 10 KiB |
BIN
dashmachine/static/images/apps/healthchecks.png
Executable file
After Width: | Height: | Size: 1.9 KiB |
BIN
dashmachine/static/images/apps/homebridge.png
Executable file
After Width: | Height: | Size: 5.2 KiB |
BIN
dashmachine/static/images/apps/hp-ilo.png
Executable file
After Width: | Height: | Size: 3.9 KiB |
BIN
dashmachine/static/images/apps/icon.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
dashmachine/static/images/apps/jeedom.png
Executable file
After Width: | Height: | Size: 2.6 KiB |
BIN
dashmachine/static/images/apps/jenkins.png
Executable file
After Width: | Height: | Size: 5.6 KiB |
BIN
dashmachine/static/images/apps/karma.png
Normal file
After Width: | Height: | Size: 561 B |
BIN
dashmachine/static/images/apps/kodi.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
dashmachine/static/images/apps/netdata.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 712 B After Width: | Height: | Size: 3.7 KiB |
BIN
dashmachine/static/images/apps/openeats.png
Executable file
After Width: | Height: | Size: 3.9 KiB |
BIN
dashmachine/static/images/apps/openwrt.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 5.5 KiB |
BIN
dashmachine/static/images/apps/pfsense.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
dashmachine/static/images/apps/phoscon.png
Executable file
After Width: | Height: | Size: 2.1 KiB |
BIN
dashmachine/static/images/apps/phpmyadmin.png
Executable file
After Width: | Height: | Size: 5.2 KiB |
BIN
dashmachine/static/images/apps/privatebin.png
Executable file
After Width: | Height: | Size: 4.8 KiB |
BIN
dashmachine/static/images/apps/projectsend.png
Executable file
After Width: | Height: | Size: 1.9 KiB |
BIN
dashmachine/static/images/apps/prometheus.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 6.0 KiB |
BIN
dashmachine/static/images/apps/statping.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
dashmachine/static/images/apps/synology-active-backup.png
Executable file
After Width: | Height: | Size: 7.0 KiB |
BIN
dashmachine/static/images/apps/synology-audio-station.png
Executable file
After Width: | Height: | Size: 3.8 KiB |
BIN
dashmachine/static/images/apps/synology-calendar.png
Executable file
After Width: | Height: | Size: 2.1 KiB |
BIN
dashmachine/static/images/apps/synology-download-station.png
Executable file
After Width: | Height: | Size: 3.4 KiB |
BIN
dashmachine/static/images/apps/synology-drive.png
Executable file
After Width: | Height: | Size: 3.9 KiB |
BIN
dashmachine/static/images/apps/synology-mailplus.png
Executable file
After Width: | Height: | Size: 4.0 KiB |
BIN
dashmachine/static/images/apps/synology-moments.png
Executable file
After Width: | Height: | Size: 4.9 KiB |
BIN
dashmachine/static/images/apps/synology-note-station.png
Executable file
After Width: | Height: | Size: 3.9 KiB |
BIN
dashmachine/static/images/apps/synology-office.png
Executable file
After Width: | Height: | Size: 4.6 KiB |
BIN
dashmachine/static/images/apps/synology-video-station.png
Executable file
After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 5.6 KiB |
BIN
dashmachine/static/images/apps/vmware-esxi.png
Executable file
After Width: | Height: | Size: 3.2 KiB |
BIN
dashmachine/static/images/apps/wallabag.png
Executable file
After Width: | Height: | Size: 1.6 KiB |
BIN
dashmachine/static/images/apps/wetty.png
Executable file
After Width: | Height: | Size: 1.3 KiB |
@ -67,6 +67,13 @@ function hide_sidenav() {
|
||||
localStorage.setItem('sidenav_hidden', 'true');
|
||||
}
|
||||
|
||||
function no_sidebar() {
|
||||
$("#main-sidenav").remove();
|
||||
$("#main.main-full").css('padding-left', 0);
|
||||
$("#no-sidenav").removeClass('hide');
|
||||
localStorage.setItem('sidenav_hidden', 'no_sidebar');
|
||||
}
|
||||
|
||||
function show_sidenav(){
|
||||
$("#main-sidenav").removeClass('hide');
|
||||
$("#main.main-full").css('padding-left', 64);
|
||||
@ -74,26 +81,80 @@ function show_sidenav(){
|
||||
localStorage.setItem('sidenav_hidden', null);
|
||||
}
|
||||
|
||||
function apply_settings(settings_theme, settings_accent){
|
||||
localStorage.setItem('mode', settings_theme);
|
||||
document.documentElement.setAttribute('data-theme', settings_theme);
|
||||
localStorage.setItem('accent', settings_accent);
|
||||
document.documentElement.setAttribute('data-accent', settings_accent);
|
||||
function apply_settings(settings){
|
||||
// theme
|
||||
if (settings['user_theme'] != "None" && settings['user_theme'].length > 1) {
|
||||
console.log(settings['user_theme'].length)
|
||||
localStorage.setItem('mode', settings['user_theme']);
|
||||
document.documentElement.setAttribute('data-theme', settings['user_theme']);
|
||||
} else {
|
||||
localStorage.setItem('mode', settings['settings_theme']);
|
||||
document.documentElement.setAttribute('data-theme', settings['settings_theme']);
|
||||
}
|
||||
// accent
|
||||
if (settings['user_accent'] != "None" && settings['user_accent'].length > 1) {
|
||||
localStorage.setItem('accent', settings['user_accent']);
|
||||
document.documentElement.setAttribute('data-accent', settings['user_accent']);
|
||||
} else {
|
||||
localStorage.setItem('accent', settings['settings_accent']);
|
||||
document.documentElement.setAttribute('data-accent', settings['settings_accent']);
|
||||
}
|
||||
if (settings['settings_sidebar_default'] == "closed"){
|
||||
localStorage.setItem('sidenav_hidden', 'true');
|
||||
} else if (settings['settings_sidebar_default'] == "open"){
|
||||
localStorage.setItem('sidenav_hidden', 'false');
|
||||
} else if (settings['settings_sidebar_default'] == "no_sidebar"){
|
||||
localStorage.setItem('sidenav_hidden', 'no_sidebar');
|
||||
}
|
||||
if (settings['user_sidebar_default'] == "closed"){
|
||||
localStorage.setItem('sidenav_hidden', 'true');
|
||||
} else if (settings['user_sidebar_default'] == "open"){
|
||||
localStorage.setItem('sidenav_hidden', 'false');
|
||||
} else if (settings['user_sidebar_default'] == "no_sidebar"){
|
||||
localStorage.setItem('sidenav_hidden', 'no_sidebar');
|
||||
}
|
||||
if (localStorage.getItem('sidenav_hidden') === 'true'){
|
||||
hide_sidenav();
|
||||
} else if (localStorage.getItem('sidenav_hidden') === 'no_sidebar'){
|
||||
no_sidebar();
|
||||
} else if (settings['user_name'].length < 1) {
|
||||
no_sidebar();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Document ready function
|
||||
//--------------------------------------------------------------------------------------
|
||||
$(document).ready(function () {
|
||||
apply_settings($("#settings-theme").val(), $("#settings-accent").val());
|
||||
"use strict";
|
||||
apply_settings({
|
||||
settings_theme: $("#settings-theme").val(),
|
||||
settings_accent: $("#settings-accent").val(),
|
||||
settings_sidebar_default: $("#settings-sidebar_default").val(),
|
||||
user_name: $("#user-name").val(),
|
||||
user_theme: $("#user-theme").val(),
|
||||
user_accent: $("#user-accent").val(),
|
||||
user_sidebar_default: $("#user-sidebar_default").val(),
|
||||
});
|
||||
|
||||
// INITS
|
||||
init_select();
|
||||
|
||||
if (localStorage.getItem('sidenav_hidden') === 'true'){
|
||||
hide_sidenav();
|
||||
$("#update-message-modal").modal({
|
||||
dismissible: false
|
||||
});
|
||||
if ($("#update-message-content").text().length > 1){
|
||||
$("#update-message-modal").modal('open');
|
||||
}
|
||||
$("#update-message-read-btn").on('click', function(e) {
|
||||
$.ajax({
|
||||
url: $(this).attr('data-url'),
|
||||
type: 'GET',
|
||||
success: function(data){
|
||||
$("#update-message-modal").modal('close');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#hide-sidenav").on('click', function(e) {
|
||||
hide_sidenav();
|
||||
|
@ -1,22 +1,49 @@
|
||||
var d = document.getElementById("dashboard-sidenav");
|
||||
d.className += " active theme-primary";
|
||||
|
||||
function get_data_source(el){
|
||||
el.html("");
|
||||
el.closest('.col').find('.data-source-loading').removeClass('hide');
|
||||
$.ajax({
|
||||
async: true,
|
||||
url: el.attr('data-url'),
|
||||
type: 'GET',
|
||||
data: {id: el.attr('data-id')},
|
||||
success: function(data){
|
||||
el.closest('.col').find('.data-source-loading').addClass('hide');
|
||||
el.html(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$( document ).ready(function() {
|
||||
$(".tooltipped").tooltip();
|
||||
$("#apps-filter").on('keyup', function(e) {
|
||||
$(".toggle-tag-expand-btn").each(function(e) {
|
||||
if ($(this).attr("data-expanded") == 'false'){
|
||||
$(this)[0].click();
|
||||
}
|
||||
});
|
||||
var value = $(this).val().toLowerCase();
|
||||
$(".app-a").each(function(i, e) {
|
||||
if ($(this).attr("data-name").toLowerCase().indexOf(value) > -1
|
||||
|| $(this).attr("data-description").toLowerCase().indexOf(value) > -1) {
|
||||
|
||||
$(".app-card").each(function(e) {
|
||||
var x = 0
|
||||
$(this).find('.searchable').each(function(e) {
|
||||
if ($(this).text().toLowerCase().indexOf(value) > -1) {
|
||||
x = x + 1
|
||||
}
|
||||
});
|
||||
if (x > 0){
|
||||
$(this).removeClass('hide');
|
||||
} else {
|
||||
$(this).addClass('hide');
|
||||
}
|
||||
});
|
||||
|
||||
$(".tag-group").each(function(i, e) {
|
||||
var x = 0
|
||||
$(this).find('.app-a').each(function(i, e) {
|
||||
$(this).find('.app-card').each(function(i, e) {
|
||||
if ($(this).hasClass("hide") === false){
|
||||
x = x + 1
|
||||
}
|
||||
@ -30,21 +57,22 @@ $( document ).ready(function() {
|
||||
});
|
||||
|
||||
$(".data-source-container").each(function(e) {
|
||||
var el = $(this);
|
||||
$.ajax({
|
||||
url: el.attr('data-url'),
|
||||
type: 'GET',
|
||||
data: {id: el.attr('data-id')},
|
||||
success: function(data){
|
||||
el.closest('.col').find('.data-source-loading').addClass('hide');
|
||||
el.html(data);
|
||||
}
|
||||
get_data_source($(this));
|
||||
});
|
||||
|
||||
$(".refresh-data-source-btn").on('click', function(e) {
|
||||
e.preventDefault();
|
||||
$(this).closest('.app-card').find(".data-source-container").each(function(e) {
|
||||
get_data_source($(this));
|
||||
});
|
||||
});
|
||||
|
||||
$("#tags-select").on('change', function(e) {
|
||||
var value = $(this).val();
|
||||
$(".tag-group").each(function(i, e) {
|
||||
if ($(this).find('.toggle-tag-expand-btn').attr("data-expanded") == "false"){
|
||||
$(this).find('.toggle-tag-expand-btn')[0].click();
|
||||
}
|
||||
if ($(this).attr("data-tag").indexOf(value) > -1 || value === "All tags") {
|
||||
$(this).removeClass('filtered');
|
||||
} else {
|
||||
@ -52,4 +80,53 @@ $( document ).ready(function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(".toggle-tag-expand-btn").on('click', function(e) {
|
||||
if ($(this).attr("data-expanded") == "true"){
|
||||
$(this).attr("data-expanded", "false");
|
||||
$(this).text('keyboard_arrow_down');
|
||||
$(this).closest('.tag-group').find('.tag-apps-row').addClass('hide');
|
||||
} else {
|
||||
$(this).attr("data-expanded", "true");
|
||||
$(this).text('keyboard_arrow_up');
|
||||
$(this).closest('.tag-group').find('.tag-apps-row').removeClass('hide');
|
||||
}
|
||||
var x = 0
|
||||
$(".toggle-tag-expand-btn").each(function(e) {
|
||||
if ($(this).attr("data-expanded") == "true") {
|
||||
x = x + 1
|
||||
}
|
||||
});
|
||||
if (x > 0) {
|
||||
$("#toggle-tag-expand-all-btn").text('unfold_less');
|
||||
} else {
|
||||
$("#toggle-tag-expand-all-btn").text('unfold_more');
|
||||
}
|
||||
});
|
||||
|
||||
$("#toggle-tag-expand-all-btn").on('click', function(e) {
|
||||
if ($(this).text() == "unfold_more") {
|
||||
$(".toggle-tag-expand-btn").each(function(e) {
|
||||
$(this)[0].click();
|
||||
});
|
||||
} else {
|
||||
$(".toggle-tag-expand-btn").each(function(e) {
|
||||
if ($(this).attr("data-expanded") == "true"){
|
||||
$(this)[0].click();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if ($("#settings-tags_expanded").val() == "False" || $("#user-tags_expanded").val() == "False"){
|
||||
$(".toggle-tag-expand-btn").each(function(e) {
|
||||
$(this)[0].click();
|
||||
});
|
||||
if ($("#user-tags_expanded").val() == "True"){
|
||||
$(".toggle-tag-expand-btn").each(function(e) {
|
||||
$(this)[0].click();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
@ -2,7 +2,7 @@ var d = document.getElementById("settings-sidenav");
|
||||
d.className += " active theme-primary";
|
||||
|
||||
$( document ).ready(function() {
|
||||
$("#config-readme table").addClass('responsive-table');
|
||||
$("#settings-readme table").addClass('responsive-table');
|
||||
initTCdrop('#images-tcdrop');
|
||||
$("#user-modal").modal({
|
||||
onCloseEnd: function () {
|
||||
|
@ -1 +0,0 @@
|
||||
!function(){var f,c,u,p,d,s=[];d="undefined"!=typeof global?global:window,p=d.jQuery;var v=function(){return d.tinymce};p.fn.tinymce=function(o){var e,t,i,l=this,r="";if(!l.length)return l;if(!o)return v()?v().get(l[0].id):null;l.css("visibility","hidden");var n=function(){var a=[],c=0;u||(m(),u=!0),l.each(function(e,t){var n,i=t.id,r=o.oninit;i||(t.id=i=v().DOM.uniqueId()),v().get(i)||(n=v().createEditor(i,o),a.push(n),n.on("init",function(){var e,t=r;l.css("visibility",""),r&&++c==a.length&&("string"==typeof t&&(e=-1===t.indexOf(".")?null:v().resolve(t.replace(/\.\w+$/,"")),t=v().resolve(t)),t.apply(e||v(),a))}))}),p.each(a,function(e,t){t.render()})};if(d.tinymce||c||!(e=o.script_url))1===c?s.push(n):n();else{c=1,t=e.substring(0,e.lastIndexOf("/")),-1!=e.indexOf(".min")&&(r=".min"),d.tinymce=d.tinyMCEPreInit||{base:t,suffix:r},-1!=e.indexOf("gzip")&&(i=o.language||"en",e=e+(/\?/.test(e)?"&":"?")+"js=true&core=true&suffix="+escape(r)+"&themes="+escape(o.theme||"modern")+"&plugins="+escape(o.plugins||"")+"&languages="+(i||""),d.tinyMCE_GZ||(d.tinyMCE_GZ={start:function(){var n=function(e){v().ScriptLoader.markDone(v().baseURI.toAbsolute(e))};n("langs/"+i+".js"),n("themes/"+o.theme+"/theme"+r+".js"),n("themes/"+o.theme+"/langs/"+i+".js"),p.each(o.plugins.split(","),function(e,t){t&&(n("plugins/"+t+"/plugin"+r+".js"),n("plugins/"+t+"/langs/"+i+".js"))})},end:function(){}}));var a=document.createElement("script");a.type="text/javascript",a.onload=a.onreadystatechange=function(e){e=e||window.event,2===c||"load"!=e.type&&!/complete|loaded/.test(a.readyState)||(v().dom.Event.domLoaded=1,c=2,o.script_loaded&&o.script_loaded(),n(),p.each(s,function(e,t){t()}))},a.src=e,document.body.appendChild(a)}return l},p.extend(p.expr[":"],{tinymce:function(e){var t;return!!(e.id&&"tinymce"in d&&(t=v().get(e.id))&&t.editorManager===v())}});var m=function(){var r=function(e){"remove"===e&&this.each(function(e,t){var n=l(t);n&&n.remove()}),this.find("span.mceEditor,div.mceEditor").each(function(e,t){var n=v().get(t.id.replace(/_parent$/,""));n&&n.remove()})},o=function(i){var e,t=this;if(null!=i)r.call(t),t.each(function(e,t){var n;(n=v().get(t.id))&&n.setContent(i)});else if(0<t.length&&(e=v().get(t[0].id)))return e.getContent()},l=function(e){var t=null;return e&&e.id&&d.tinymce&&(t=v().get(e.id)),t},u=function(e){return!!(e&&e.length&&d.tinymce&&e.is(":tinymce"))},s={};p.each(["text","html","val"],function(e,t){var a=s[t]=p.fn[t],c="text"===t;p.fn[t]=function(e){var t=this;if(!u(t))return a.apply(t,arguments);if(e!==f)return o.call(t.filter(":tinymce"),e),a.apply(t.not(":tinymce"),arguments),t;var i="",r=arguments;return(c?t:t.eq(0)).each(function(e,t){var n=l(t);i+=n?c?n.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g,""):n.getContent({save:!0}):a.apply(p(t),r)}),i}}),p.each(["append","prepend"],function(e,t){var n=s[t]=p.fn[t],r="prepend"===t;p.fn[t]=function(i){var e=this;return u(e)?i!==f?("string"==typeof i&&e.filter(":tinymce").each(function(e,t){var n=l(t);n&&n.setContent(r?i+n.getContent():n.getContent()+i)}),n.apply(e.not(":tinymce"),arguments),e):void 0:n.apply(e,arguments)}}),p.each(["remove","replaceWith","replaceAll","empty"],function(e,t){var n=s[t]=p.fn[t];p.fn[t]=function(){return r.call(this,t),n.apply(this,arguments)}}),s.attr=p.fn.attr,p.fn.attr=function(e,t){var n=this,i=arguments;if(!e||"value"!==e||!u(n))return s.attr.apply(n,i);if(t!==f)return o.call(n.filter(":tinymce"),t),s.attr.apply(n.not(":tinymce"),i),n;var r=n[0],a=l(r);return a?a.getContent({save:!0}):s.attr.apply(p(r),i)}}}();
|
@ -1,3 +0,0 @@
|
||||
This is where language files should be placed.
|
||||
|
||||
Please DO NOT translate these directly use this service: https://www.transifex.com/projects/p/tinymce/
|
504
dashmachine/static/vendors/tinymce/license.txt
vendored
@ -1,504 +0,0 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 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.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
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 and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, 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 library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete 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 distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
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 Library or any portion
|
||||
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
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 Library, 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 Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you 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.
|
||||
|
||||
If distribution of 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 satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be 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.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library 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.
|
||||
|
||||
9. 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 Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
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 with
|
||||
this License.
|
||||
|
||||
11. 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 Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library 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 Library.
|
||||
|
||||
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.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library 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.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser 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 Library
|
||||
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 Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
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
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "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
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. 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 LIBRARY 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
|
||||
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. 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 library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; 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.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager"),a=tinymce.util.Tools.resolve("tinymce.util.Tools"),s=function(t,e,n){var r="UL"===e?"InsertUnorderedList":"InsertOrderedList";t.execCommand(r,!1,!1===n?null:{"list-style-type":n})},o=function(n){n.addCommand("ApplyUnorderedListStyle",function(t,e){s(n,"UL",e["list-style-type"])}),n.addCommand("ApplyOrderedListStyle",function(t,e){s(n,"OL",e["list-style-type"])})},e=function(t){var e=t.getParam("advlist_number_styles","default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman");return e?e.split(/[ ,]/):[]},n=function(t){var e=t.getParam("advlist_bullet_styles","default,circle,disc,square");return e?e.split(/[ ,]/):[]},u=function(t){return t&&/^(TH|TD)$/.test(t.nodeName)},c=function(r){return function(t){return t&&/^(OL|UL|DL)$/.test(t.nodeName)&&(n=t,(e=r).$.contains(e.getBody(),n));var e,n}},d=function(t){var e=t.dom.getParent(t.selection.getNode(),"ol,ul");return t.dom.getStyle(e,"listStyleType")||""},p=function(t){return a.map(t,function(t){return{text:t.replace(/\-/g," ").replace(/\b\w/g,function(t){return t.toUpperCase()}),data:"default"===t?"":t}})},f=function(i,l){return function(t){var o=t.control;i.on("NodeChange",function(t){var e=function(t,e){for(var n=0;n<t.length;n++)if(e(t[n]))return n;return-1}(t.parents,u),n=-1!==e?t.parents.slice(0,e):t.parents,r=a.grep(n,c(i));o.active(0<r.length&&r[0].nodeName===l)})}},m=function(e,t,n,r,o,i){var l;e.addButton(t,{active:!1,type:"splitbutton",tooltip:n,menu:p(i),onPostRender:f(e,o),onshow:(l=e,function(t){var e=d(l);t.control.items().each(function(t){t.active(t.settings.data===e)})}),onselect:function(t){s(e,o,t.control.settings.data)},onclick:function(){e.execCommand(r)}})},r=function(t,e,n,r,o,i){var l,a,s,u,c;0<i.length?m(t,e,n,r,o,i):(a=e,s=n,u=r,c=o,(l=t).addButton(a,{active:!1,type:"button",tooltip:s,onPostRender:f(l,c),onclick:function(){l.execCommand(u)}}))},i=function(t){r(t,"numlist","Numbered list","InsertOrderedList","OL",e(t)),r(t,"bullist","Bullet list","InsertUnorderedList","UL",n(t))};t.add("advlist",function(t){var e,n,r;n="lists",r=(e=t).settings.plugins?e.settings.plugins:"",-1!==a.inArray(r.split(/[ ,]/),n)&&(i(t),o(t))})}();
|
@ -1 +0,0 @@
|
||||
!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager"),a=function(t){return/^[A-Za-z][A-Za-z0-9\-:._]*$/.test(t)},e=function(t){var e=t.selection.getNode();return"A"===e.tagName&&""===t.dom.getAttrib(e,"href")?e.id||e.name:""},i=function(t,e){var n=t.selection.getNode();"A"===n.tagName&&""===t.dom.getAttrib(n,"href")?(n.removeAttribute("name"),n.id=e,t.undoManager.add()):(t.focus(),t.selection.collapse(!0),t.execCommand("mceInsertContent",!1,t.dom.createHTML("a",{id:e})))},n=function(r){var t=e(r);r.windowManager.open({title:"Anchor",body:{type:"textbox",name:"id",size:40,label:"Id",value:t},onsubmit:function(t){var e,n,o=t.data.id;e=r,(a(n=o)?(i(e,n),0):(e.windowManager.alert("Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores."),1))&&t.preventDefault()}})},o=function(t){t.addCommand("mceAnchor",function(){n(t)})},r=function(o){return function(t){for(var e=0;e<t.length;e++)(n=t[e]).attr("href")||!n.attr("id")&&!n.attr("name")||n.firstChild||t[e].attr("contenteditable",o);var n}},c=function(t){t.on("PreInit",function(){t.parser.addNodeFilter("a",r("false")),t.serializer.addNodeFilter("a",r(null))})},d=function(t){t.addButton("anchor",{icon:"anchor",tooltip:"Anchor",cmd:"mceAnchor",stateSelector:"a:not([href])"}),t.addMenuItem("anchor",{icon:"anchor",text:"Anchor",context:"insert",cmd:"mceAnchor"})};t.add("anchor",function(t){c(t),o(t),d(t)})}();
|
@ -1 +0,0 @@
|
||||
!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),i=tinymce.util.Tools.resolve("tinymce.Env"),m=function(e){return e.getParam("autolink_pattern",/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+\-]+@)(.+)$/i)},y=function(e){return e.getParam("default_link_target","")},o=function(e,t){if(t<0&&(t=0),3===e.nodeType){var n=e.data.length;n<t&&(t=n)}return t},k=function(e,t,n){1!==t.nodeType||t.hasChildNodes()?e.setStart(t,o(t,n)):e.setStartBefore(t)},p=function(e,t,n){1!==t.nodeType||t.hasChildNodes()?e.setEnd(t,o(t,n)):e.setEndAfter(t)},r=function(e,t,n){var i,o,r,a,f,s,d,l,c,u,g=m(e),h=y(e);if("A"!==e.selection.getNode().tagName){if((i=e.selection.getRng(!0).cloneRange()).startOffset<5){if(!(l=i.endContainer.previousSibling)){if(!i.endContainer.firstChild||!i.endContainer.firstChild.nextSibling)return;l=i.endContainer.firstChild.nextSibling}if(c=l.length,k(i,l,c),p(i,l,c),i.endOffset<5)return;o=i.endOffset,a=l}else{if(3!==(a=i.endContainer).nodeType&&a.firstChild){for(;3!==a.nodeType&&a.firstChild;)a=a.firstChild;3===a.nodeType&&(k(i,a,0),p(i,a,a.nodeValue.length))}o=1===i.endOffset?2:i.endOffset-1-t}for(r=o;k(i,a,2<=o?o-2:0),p(i,a,1<=o?o-1:0),o-=1," "!==(u=i.toString())&&""!==u&&160!==u.charCodeAt(0)&&0<=o-2&&u!==n;);var C;(C=i.toString())===n||" "===C||160===C.charCodeAt(0)?(k(i,a,o),p(i,a,r),o+=1):(0===i.startOffset?k(i,a,0):k(i,a,o),p(i,a,r)),"."===(s=i.toString()).charAt(s.length-1)&&p(i,a,r-1),(d=(s=i.toString().trim()).match(g))&&("www."===d[1]?d[1]="http://www.":/@$/.test(d[1])&&!/^mailto:/.test(d[1])&&(d[1]="mailto:"+d[1]),f=e.selection.getBookmark(),e.selection.setRng(i),e.execCommand("createlink",!1,d[1]+d[2]),h&&e.dom.setAttrib(e.selection.getNode(),"target",h),e.selection.moveToBookmark(f),e.nodeChanged())}},t=function(t){var n;t.on("keydown",function(e){13!==e.keyCode||r(t,-1,"")}),i.ie?t.on("focus",function(){if(!n){n=!0;try{t.execCommand("AutoUrlDetect",!1,!0)}catch(e){}}}):(t.on("keypress",function(e){41!==e.keyCode||r(t,-1,"(")}),t.on("keyup",function(e){32!==e.keyCode||r(t,0,"")}))};e.add("autolink",function(e){t(e)})}();
|
@ -1 +0,0 @@
|
||||
!function(){"use strict";var i=function(t){var e=t,n=function(){return e};return{get:n,set:function(t){e=t},clone:function(){return i(n())}}},t=tinymce.util.Tools.resolve("tinymce.PluginManager"),y=tinymce.util.Tools.resolve("tinymce.Env"),r=tinymce.util.Tools.resolve("tinymce.util.Delay"),h=function(t){return parseInt(t.getParam("autoresize_min_height",t.getElement().offsetHeight),10)},v=function(t){return parseInt(t.getParam("autoresize_max_height",0),10)},o=function(t){return t.getParam("autoresize_overflow_padding",1)},a=function(t){return t.getParam("autoresize_bottom_margin",50)},n=function(t){return t.getParam("autoresize_on_init",!0)},u=function(t,e,n,i,o){r.setEditorTimeout(t,function(){_(t,e),n--?u(t,e,n,i,o):o&&o()},i)},S=function(t,e){var n=t.getBody();n&&(n.style.overflowY=e?"":"hidden",e||(n.scrollTop=0))},_=function(t,e){var n,i,o,r,a,u,s,l,g,c,f,d=t.dom;if(i=t.getDoc())if((m=t).plugins.fullscreen&&m.plugins.fullscreen.isFullscreen())S(t,!0);else{var m;o=i.body,r=h(t),u=d.getStyle(o,"margin-top",!0),s=d.getStyle(o,"margin-bottom",!0),l=d.getStyle(o,"padding-top",!0),g=d.getStyle(o,"padding-bottom",!0),c=d.getStyle(o,"border-top-width",!0),f=d.getStyle(o,"border-bottom-width",!0),a=o.offsetHeight+parseInt(u,10)+parseInt(s,10)+parseInt(l,10)+parseInt(g,10)+parseInt(c,10)+parseInt(f,10),(isNaN(a)||a<=0)&&(a=y.ie?o.scrollHeight:y.webkit&&0===o.clientHeight?0:o.offsetHeight),a>h(t)&&(r=a);var p=v(t);p&&p<a?(r=p,S(t,!0)):S(t,!1),r!==e.get()&&(n=r-e.get(),d.setStyle(t.iframeElement,"height",r+"px"),e.set(r),y.webkit&&n<0&&_(t,e))}},s={setup:function(i,e){i.on("init",function(){var t,e,n=i.dom;t=o(i),e=a(i),!1!==t&&n.setStyles(i.getBody(),{paddingLeft:t,paddingRight:t}),!1!==e&&n.setStyles(i.getBody(),{paddingBottom:e})}),i.on("nodechange setcontent keyup FullscreenStateChanged",function(t){_(i,e)}),n(i)&&i.on("init",function(){u(i,e,20,100,function(){u(i,e,5,1e3)})})},resize:_},l=function(t,e){t.addCommand("mceAutoResize",function(){s.resize(t,e)})};t.add("autoresize",function(t){if(!t.inline){var e=i(0);l(t,e),s.setup(t,e)}})}();
|
@ -1 +0,0 @@
|
||||
!function(){"use strict";var a=function(t){var e=t,n=function(){return e};return{get:n,set:function(t){e=t},clone:function(){return a(n())}}},t=tinymce.util.Tools.resolve("tinymce.PluginManager"),r=tinymce.util.Tools.resolve("tinymce.util.LocalStorage"),o=tinymce.util.Tools.resolve("tinymce.util.Tools"),i=function(t,e){var n=t||e,r=/^(\d+)([ms]?)$/.exec(""+n);return(r[2]?{s:1e3,m:6e4}[r[2]]:1)*parseInt(n,10)},u=function(t){var e=t.getParam("autosave_prefix","tinymce-autosave-{path}{query}{hash}-{id}-");return e=(e=(e=(e=e.replace(/\{path\}/g,document.location.pathname)).replace(/\{query\}/g,document.location.search)).replace(/\{hash\}/g,document.location.hash)).replace(/\{id\}/g,t.id)},s=function(t,e){var n=t.settings.forced_root_block;return""===(e=o.trim(void 0===e?t.getBody().innerHTML:e))||new RegExp("^<"+n+"[^>]*>((\xa0| |[ \t]|<br[^>]*>)+?|)</"+n+">|<br>$","i").test(e)},c=function(t){var e=parseInt(r.getItem(u(t)+"time"),10)||0;return!((new Date).getTime()-e>i(t.settings.autosave_retention,"20m")&&(f(t,!1),1))},f=function(t,e){var n=u(t);r.removeItem(n+"draft"),r.removeItem(n+"time"),!1!==e&&t.fire("RemoveDraft")},l=function(t){var e=u(t);!s(t)&&t.isDirty()&&(r.setItem(e+"draft",t.getContent({format:"raw",no_events:!0})),r.setItem(e+"time",(new Date).getTime().toString()),t.fire("StoreDraft"))},m=function(t){var e=u(t);c(t)&&(t.setContent(r.getItem(e+"draft"),{format:"raw"}),t.fire("RestoreDraft"))},v=function(t,e){var n=i(t.settings.autosave_interval,"30s");e.get()||(setInterval(function(){t.removed||l(t)},n),e.set(!0))},d=function(t){t.undoManager.transact(function(){m(t),f(t)}),t.focus()};function g(r){for(var o=[],t=1;t<arguments.length;t++)o[t-1]=arguments[t];return function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];var n=o.concat(t);return r.apply(null,n)}}var y=tinymce.util.Tools.resolve("tinymce.EditorManager");y._beforeUnloadHandler=function(){var e;return o.each(y.get(),function(t){t.plugins.autosave&&t.plugins.autosave.storeDraft(),!e&&t.isDirty()&&t.getParam("autosave_ask_before_unload",!0)&&(e=t.translate("You have unsaved changes are you sure you want to navigate away?"))}),e};var p=function(n,r){return function(t){var e=t.control;e.disabled(!c(n)),n.on("StoreDraft RestoreDraft RemoveDraft",function(){e.disabled(!c(n))}),v(n,r)}};t.add("autosave",function(t){var e,n,r,o=a(!1);return window.onbeforeunload=y._beforeUnloadHandler,n=o,(e=t).addButton("restoredraft",{title:"Restore last draft",onclick:function(){d(e)},onPostRender:p(e,n)}),e.addMenuItem("restoredraft",{text:"Restore last draft",onclick:function(){d(e)},onPostRender:p(e,n),context:"file"}),t.on("init",function(){t.getParam("autosave_restore_when_empty",!1)&&t.dom.isEmpty(t.getBody())&&m(t)}),{hasDraft:g(c,r=t),storeDraft:g(l,r),restoreDraft:g(m,r),removeDraft:g(f,r),isEmpty:g(s,r)}})}();
|
@ -1 +0,0 @@
|
||||
!function(){"use strict";var o=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.util.Tools"),e=function(e){e=t.trim(e);var o=function(o,t){e=e.replace(o,t)};return o(/<a.*?href=\"(.*?)\".*?>(.*?)<\/a>/gi,"[url=$1]$2[/url]"),o(/<font.*?color=\"(.*?)\".*?class=\"codeStyle\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),o(/<font.*?color=\"(.*?)\".*?class=\"quoteStyle\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),o(/<font.*?class=\"codeStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),o(/<font.*?class=\"quoteStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),o(/<span style=\"color: ?(.*?);\">(.*?)<\/span>/gi,"[color=$1]$2[/color]"),o(/<font.*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[color=$1]$2[/color]"),o(/<span style=\"font-size:(.*?);\">(.*?)<\/span>/gi,"[size=$1]$2[/size]"),o(/<font>(.*?)<\/font>/gi,"$1"),o(/<img.*?src=\"(.*?)\".*?\/>/gi,"[img]$1[/img]"),o(/<span class=\"codeStyle\">(.*?)<\/span>/gi,"[code]$1[/code]"),o(/<span class=\"quoteStyle\">(.*?)<\/span>/gi,"[quote]$1[/quote]"),o(/<strong class=\"codeStyle\">(.*?)<\/strong>/gi,"[code][b]$1[/b][/code]"),o(/<strong class=\"quoteStyle\">(.*?)<\/strong>/gi,"[quote][b]$1[/b][/quote]"),o(/<em class=\"codeStyle\">(.*?)<\/em>/gi,"[code][i]$1[/i][/code]"),o(/<em class=\"quoteStyle\">(.*?)<\/em>/gi,"[quote][i]$1[/i][/quote]"),o(/<u class=\"codeStyle\">(.*?)<\/u>/gi,"[code][u]$1[/u][/code]"),o(/<u class=\"quoteStyle\">(.*?)<\/u>/gi,"[quote][u]$1[/u][/quote]"),o(/<\/(strong|b)>/gi,"[/b]"),o(/<(strong|b)>/gi,"[b]"),o(/<\/(em|i)>/gi,"[/i]"),o(/<(em|i)>/gi,"[i]"),o(/<\/u>/gi,"[/u]"),o(/<span style=\"text-decoration: ?underline;\">(.*?)<\/span>/gi,"[u]$1[/u]"),o(/<u>/gi,"[u]"),o(/<blockquote[^>]*>/gi,"[quote]"),o(/<\/blockquote>/gi,"[/quote]"),o(/<br \/>/gi,"\n"),o(/<br\/>/gi,"\n"),o(/<br>/gi,"\n"),o(/<p>/gi,""),o(/<\/p>/gi,"\n"),o(/ |\u00a0/gi," "),o(/"/gi,'"'),o(/</gi,"<"),o(/>/gi,">"),o(/&/gi,"&"),e},i=function(e){e=t.trim(e);var o=function(o,t){e=e.replace(o,t)};return o(/\n/gi,"<br />"),o(/\[b\]/gi,"<strong>"),o(/\[\/b\]/gi,"</strong>"),o(/\[i\]/gi,"<em>"),o(/\[\/i\]/gi,"</em>"),o(/\[u\]/gi,"<u>"),o(/\[\/u\]/gi,"</u>"),o(/\[url=([^\]]+)\](.*?)\[\/url\]/gi,'<a href="$1">$2</a>'),o(/\[url\](.*?)\[\/url\]/gi,'<a href="$1">$1</a>'),o(/\[img\](.*?)\[\/img\]/gi,'<img src="$1" />'),o(/\[color=(.*?)\](.*?)\[\/color\]/gi,'<font color="$1">$2</font>'),o(/\[code\](.*?)\[\/code\]/gi,'<span class="codeStyle">$1</span> '),o(/\[quote.*?\](.*?)\[\/quote\]/gi,'<span class="quoteStyle">$1</span> '),e};o.add("bbcode",function(){return{init:function(o){o.on("beforeSetContent",function(o){o.content=i(o.content)}),o.on("postProcess",function(o){o.set&&(o.content=i(o.content)),o.get&&(o.content=e(o.content))})}}})}();
|
@ -1 +0,0 @@
|
||||
!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager"),n=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),o=function(t){return t.getParam("code_dialog_width",600)},i=function(t){return t.getParam("code_dialog_height",Math.min(n.DOM.getViewPort().h-200,500))},c=function(t,n){t.focus(),t.undoManager.transact(function(){t.setContent(n)}),t.selection.setCursorLocation(),t.nodeChanged()},d=function(t){return t.getContent({source_view:!0})},e=function(n){var t=o(n),e=i(n);n.windowManager.open({title:"Source code",body:{type:"textbox",name:"code",multiline:!0,minWidth:t,minHeight:e,spellcheck:!1,style:"direction: ltr; text-align: left"},onSubmit:function(t){c(n,t.data.code)}}).find("#code").value(d(n))},u=function(t){t.addCommand("mceCodeEditor",function(){e(t)})},a=function(t){t.addButton("code",{icon:"code",tooltip:"Source code",onclick:function(){e(t)}}),t.addMenuItem("code",{icon:"code",text:"Source code",onclick:function(){e(t)}})};t.add("code",function(t){return u(t),a(t),{}})}();
|
@ -1,138 +0,0 @@
|
||||
/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #a67f59;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),l=tinymce.util.Tools.resolve("tinymce.util.Color"),a=function(e,n){e.find("#preview")[0].getEl().style.background=n},o=function(e,n){var i=l(n),t=i.toRgb();e.fromJSON({r:t.r,g:t.g,b:t.b,hex:i.toHex().substr(1)}),a(e,i.toHex())},t=function(e,n,i){var t=e.windowManager.open({title:"Color",items:{type:"container",layout:"flex",direction:"row",align:"stretch",padding:5,spacing:10,items:[{type:"colorpicker",value:i,onchange:function(){var e=this.rgb();t&&(t.find("#r").value(e.r),t.find("#g").value(e.g),t.find("#b").value(e.b),t.find("#hex").value(this.value().substr(1)),a(t,this.value()))}},{type:"form",padding:0,labelGap:5,defaults:{type:"textbox",size:7,value:"0",flex:1,spellcheck:!1,onchange:function(){var e,n,i=t.find("colorpicker")[0];if(e=this.name(),n=this.value(),"hex"===e)return o(t,n="#"+n),void i.value(n);n={r:t.find("#r").value(),g:t.find("#g").value(),b:t.find("#b").value()},i.value(n),o(t,n)}},items:[{name:"r",label:"R",autofocus:1},{name:"g",label:"G"},{name:"b",label:"B"},{name:"hex",label:"#",value:"000000"},{name:"preview",type:"container",border:1}]}]},onSubmit:function(){n("#"+t.toJSON().hex)}});o(t,i)};e.add("colorpicker",function(i){i.settings.color_picker_callback||(i.settings.color_picker_callback=function(e,n){t(i,e,n)})})}();
|
@ -1 +0,0 @@
|
||||
!function(){"use strict";var o=function(t){var n=t,e=function(){return n};return{get:e,set:function(t){n=t},clone:function(){return o(e())}}},t=tinymce.util.Tools.resolve("tinymce.PluginManager"),i=function(t){return{isContextMenuVisible:function(){return t.get()}}},r=function(t){return t.settings.contextmenu_never_use_native},u=function(t){return t.getParam("contextmenu","link openlink image inserttable | cell row column deletetable")},l=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),s=function(t){return l.DOM.select(t.settings.ui_container)[0]},a=function(t,n){return{x:t,y:n}},f=function(t,n,e){return a(t.x+n,t.y+e)},m=function(t,n){if(t&&"static"!==l.DOM.getStyle(t,"position",!0)){var e=l.DOM.getPos(t),o=e.x-t.scrollLeft,i=e.y-t.scrollTop;return f(n,-o,-i)}return f(n,0,0)},c=function(t,n){if(t.inline)return m(s(t),a((u=n).pageX,u.pageY));var e,o,i,r,u,c=(e=t.getContentAreaContainer(),o=a((r=n).clientX,r.clientY),i=l.DOM.getPos(e),f(o,i.x,i.y));return m(s(t),c)},g=tinymce.util.Tools.resolve("tinymce.ui.Factory"),v=tinymce.util.Tools.resolve("tinymce.util.Tools"),y=function(t,n,e,o){null===o.get()?o.set(function(e,n){var t,o,i=[];o=u(e),v.each(o.split(/[ ,]/),function(t){var n=e.menuItems[t];"|"===t&&(n={text:t}),n&&(n.shortcut="",i.push(n))});for(var r=0;r<i.length;r++)"|"===i[r].text&&(0!==r&&r!==i.length-1||i.splice(r,1));return(t=g.create("menu",{items:i,context:"contextmenu",classes:"contextmenu"})).uiContainer=s(e),t.renderTo(s(e)),t.on("hide",function(t){t.control===this&&n.set(!1)}),e.on("remove",function(){t.remove(),t=null}),t}(t,e)):o.get().show(),o.get().moveTo(n.x,n.y),e.set(!0)},x=function(e,o,i){e.on("contextmenu",function(t){var n;n=e,(!t.ctrlKey||r(n))&&(t.preventDefault(),y(e,c(e,t),o,i))})};t.add("contextmenu",function(t){var n=o(null),e=o(!1);return x(t,e,n),i(e)})}();
|
@ -1 +0,0 @@
|
||||
!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager"),c=tinymce.util.Tools.resolve("tinymce.util.Tools"),e=function(t,e){var i,n=t.dom,o=t.selection.getSelectedBlocks();o.length&&(i=n.getAttrib(o[0],"dir"),c.each(o,function(t){n.getParent(t.parentNode,'*[dir="'+e+'"]',n.getRoot())||n.setAttrib(t,"dir",i!==e?e:null)}),t.nodeChanged())},i=function(t){t.addCommand("mceDirectionLTR",function(){e(t,"ltr")}),t.addCommand("mceDirectionRTL",function(){e(t,"rtl")})},n=function(e){var i=[];return c.each("h1 h2 h3 h4 h5 h6 div p".split(" "),function(t){i.push(t+"[dir="+e+"]")}),i.join(",")},o=function(t){t.addButton("ltr",{title:"Left to right",cmd:"mceDirectionLTR",stateSelector:n("ltr")}),t.addButton("rtl",{title:"Right to left",cmd:"mceDirectionRTL",stateSelector:n("rtl")})};t.add("directionality",function(t){i(t),o(t)})}();
|
Before Width: | Height: | Size: 354 B |
Before Width: | Height: | Size: 329 B |
Before Width: | Height: | Size: 331 B |
Before Width: | Height: | Size: 342 B |
Before Width: | Height: | Size: 340 B |
Before Width: | Height: | Size: 336 B |
Before Width: | Height: | Size: 338 B |
Before Width: | Height: | Size: 343 B |
Before Width: | Height: | Size: 321 B |
Before Width: | Height: | Size: 323 B |
Before Width: | Height: | Size: 344 B |
Before Width: | Height: | Size: 338 B |
Before Width: | Height: | Size: 328 B |