- fixed lack of feedback on login screen

- fixed broken images on README.md
- updated README.md
- updated the PR template
- adding option for changing the tab name
- added tag headers for grid and list view
- added function that resizes template app images to 64x64 on startup
- fixed error that was blocking image types from being uploaded
This commit is contained in:
Ross Mountjoy 2020-03-08 11:55:21 -04:00
parent cf16c549fb
commit 00786d9eb6
56 changed files with 224 additions and 67 deletions

View File

@ -2,9 +2,11 @@
# DashMachine # DashMachine
### Another web application bookmark dashboard, with fun features. ### Another web application bookmark dashboard, with fun features.
![screenshot](https://i.ibb.co/chbBkzk/2020-02-02-09-46.png) ![screenshot](https://i.ibb.co/HxS4SPd/2020-03-08-10-40.png)
![screenshot](https://i.ibb.co/HXbfhnp/2020-02-02-09-47.png) ![screenshot](https://i.ibb.co/7nmJR3J/2020-03-08-10-43.png)
![screenshot](https://i.ibb.co/y8Gr4C7/2020-03-08-10-45.png)
### Features ### Features
* creates a dashboard to view web pages * creates a dashboard to view web pages
@ -60,6 +62,10 @@ Password: adminadmin
## Updating ## Updating
For python, use git. For docker, just pull the latest image and recreate the container. 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 ## 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'. 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'.
@ -67,8 +73,11 @@ The user data folder is located at DashMachine/dashmachine/user_data. This is wh
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. Pictures added to the backgrounds/icons folders are available immediately. 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. Pictures added to the backgrounds/icons folders are available immediately.
## Want to contribute? ## Want to contribute?
Check out the pull request template! Please use the pull request template at:
https://git.wolf-house.net/ross/DashMachine/src/branch/master/pull_request_template.md https://github.com/rmountjoy92/DashMachine/blob/master/pull_request_template.md
See this link for how to create a pull request:
https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request
## Tech used ## Tech used
* Flask * Flask
@ -77,3 +86,7 @@ https://git.wolf-house.net/ross/DashMachine/src/branch/master/pull_request_templ
* Materialize css * Materialize css
* JavaScript/jQuery/jQueryUI * JavaScript/jQuery/jQueryUI
* Requests (python) * Requests (python)
## FAQs
1. <application> does not work in iframe
see https://github.com/rmountjoy92/DashMachine/issues/6

View File

@ -14,6 +14,7 @@ background = None
roles = admin,user,public_user roles = admin,user,public_user
home_access_groups = admin_only home_access_groups = admin_only
settings_access_groups = admin_only settings_access_groups = admin_only
custom_app_title = DashMachine
``` ```
| Variable | Required | Description | Options | | Variable | Required | Description | Options |
@ -25,6 +26,7 @@ settings_access_groups = admin_only
| 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. | | 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 | | 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 | | 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 ##### Apps
These entries are the cards that you see one the home page, as well as the sidenav. Entries These entries are the cards that you see one the home page, as well as the sidenav. Entries

View File

@ -63,6 +63,10 @@ def read_config():
) )
settings.home_view_mode = config["Settings"].get("home_view_mode", "grid") settings.home_view_mode = config["Settings"].get("home_view_mode", "grid")
settings.custom_app_title = config["Settings"].get(
"custom_app_title", "DashMachine"
)
db.session.add(settings) db.session.add(settings)
db.session.commit() db.session.commit()
@ -143,7 +147,14 @@ def read_config():
db.session.add(tag_db) db.session.add(tag_db)
db.session.commit() db.session.commit()
else: else:
app.tags = None 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)
db.session.commit()
else:
app.tags = None
db.session.add(app) db.session.add(app)
db.session.commit() db.session.commit()

View File

@ -1,7 +1,8 @@
import os import os
import importlib import importlib
from shutil import copyfile from shutil import copyfile
from dashmachine.paths import dashmachine_folder, images_folder, root_folder from PIL import Image, ImageOps
from dashmachine.paths import dashmachine_folder, images_folder
from dashmachine.main.models import Groups from dashmachine.main.models import Groups
from dashmachine.main.read_config import read_config from dashmachine.main.read_config import read_config
from dashmachine.settings_system.models import Settings from dashmachine.settings_system.models import Settings
@ -26,6 +27,7 @@ def public_route(decorated_function):
def dashmachine_init(): def dashmachine_init():
resize_template_app_images()
db.create_all() db.create_all()
db.session.commit() db.session.commit()
@ -100,3 +102,12 @@ def get_data_source(data_source):
) )
platform = module.Platform(data_source, **data_source_args) platform = module.Platform(data_source, **data_source_args)
return platform.process() return platform.process()
def resize_template_app_images():
folder = os.path.join(images_folder, "apps")
for file in os.listdir(folder):
fp = os.path.join(folder, file)
image = Image.open(fp)
image.thumbnail((64, 64))
image.save(fp)

View File

@ -10,3 +10,4 @@ class Settings(db.Model):
home_access_groups = db.Column(db.String()) home_access_groups = db.Column(db.String())
settings_access_groups = db.Column(db.String()) settings_access_groups = db.Column(db.String())
home_view_mode = db.Column(db.String()) home_view_mode = db.Column(db.String())
custom_app_title = db.Column(db.String())

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -14,6 +14,19 @@ $( document ).ready(function() {
$(this).addClass('hide'); $(this).addClass('hide');
} }
}); });
$(".tag-group").each(function(i, e) {
var x = 0
$(this).find('.app-a').each(function(i, e) {
if ($(this).hasClass("hide") === false){
x = x + 1
}
});
if (x === 0){
$(this).addClass('hide');
} else {
$(this).removeClass('hide');
}
});
}); });
$(".data-source-container").each(function(e) { $(".data-source-container").each(function(e) {
@ -31,8 +44,8 @@ $( document ).ready(function() {
$("#tags-select").on('change', function(e) { $("#tags-select").on('change', function(e) {
var value = $(this).val(); var value = $(this).val();
$(".app-a").each(function(i, e) { $(".tag-group").each(function(i, e) {
if ($(this).attr("data-tags").indexOf(value) > -1 || value === "All tags") { if ($(this).attr("data-tag").indexOf(value) > -1 || value === "All tags") {
$(this).removeClass('filtered'); $(this).removeClass('filtered');
} else { } else {
$(this).addClass('filtered'); $(this).addClass('filtered');

View File

@ -0,0 +1,17 @@
$( document ).ready(function() {
$(".login-form").on('submit', function(e) {
e.preventDefault();
$.ajax({
url: $(this).attr('data-url'),
type: 'POST',
data: $(this).serialize(),
success: function(data){
if (data.data.err){
M.toast({html: data.data.err, classes: 'theme-warning'})
} else {
$(location).attr('href', data.data.url)
}
}
});
});
});

View File

@ -21,9 +21,9 @@
content="Another web application bookmark dashboard, with fun features."> content="Another web application bookmark dashboard, with fun features.">
<meta name="author" content="rmountjoy"> <meta name="author" content="rmountjoy">
{% if title %} {% if title %}
<title>{{ title }} - DashMachine</title> <title>{{ title }} - {{ settings.custom_app_title }}</title>
{% else %} {% else %}
<title>DashMachine</title> <title>{{ settings.custom_app_title }}</title>
{% endif %} {% endif %}
<link rel="apple-touch-icon" href="static/images/favicon/apple-touch-icon-152x152.png"> <link rel="apple-touch-icon" href="static/images/favicon/apple-touch-icon-152x152.png">
<link rel="shortcut icon" type="image/x-icon" href="static/images/favicon/favicon-32x32.png"> <link rel="shortcut icon" type="image/x-icon" href="static/images/favicon/favicon-32x32.png">

View File

@ -49,15 +49,71 @@
</div> </div>
<div class="row"> <div class="row">
{% if apps %} {% if apps %}
{# If tags are enabled, render the apps like this #}
{% if tags_form.tags.choices|count > 1 %}
{% if settings.home_view_mode == "list" %}
<div class="col s12 m12 l8">
<div id="list-view-collection" class="collection">
{% for tag in tags_form.tags.choices %}
{% if tag[0] != 'All tags' %}
<div class="tag-group" data-tag="{{ tag[0] }}">
<a class="collection-item font-weight-600 theme-on-primary-text theme-primary" style="font-size: 1.2em">{{ tag[0] }}</a>
{% for app in apps %}
{% if app.tags and tag[0] in app.tags %}
{{ ListViewApp(app) }}
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% else %}
{% for tag in tags_form.tags.choices %}
{% if tag[0] != 'All tags' %}
<div class="tag-group" data-tag="{{ tag[0] }}">
<div class="row">
<div class="col s12 m6 l2">
<div class="card center-align theme-primary">
<h5 class="theme-on-primary-text">{{ tag[0] }}</h5>
</div>
</div>
</div>
<div class="row">
{% for app in apps %}
{% if app.tags and tag[0] in app.tags %}
{{ GridViewApp(app) }}
{% endif %}
{% endfor %}
</div>
<div class="divider"></div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% if settings.home_view_mode == "list" %}
{{ ListViewApp(apps) }}
{% else %} {% else %}
{% for app in apps %} {# otherwise, render the apps like this #}
{{ GridViewApp(app) }} {% if settings.home_view_mode == "list" %}
{% endfor %}
<div class="col s12 m12 l8">
<div id="list-view-collection" class="collection">
{% for app in apps %}
{{ ListViewApp(app) }}
{% endfor %}
</div>
</div>
{% else %}
{% for app in apps %}
{{ GridViewApp(app) }}
{% endfor %}
{% endif %}
{% endif %} {% endif %}
{% else %} {% else %}
<a href="{{ url_for('settings_system.settings') }}"> <a href="{{ url_for('settings_system.settings') }}">
<div class="col s12 m6 l3"> <div class="col s12 m6 l3">

View File

@ -54,39 +54,33 @@
{# </a> This closes AppAnchor() #} {# </a> This closes AppAnchor() #}
{% endmacro %} {% endmacro %}
{% macro ListViewApp(apps) %} {% macro ListViewApp(app) %}
<div class="col s12 m12 l8"> {{ AppAnchor(app, classes="collection-item") }}
<div id="list-view-collection" class="collection"> <div class="row">
{% for app in apps %} <div class="col s12
{{ AppAnchor(app, classes="collection-item") }} {% if app.data_sources.count() > 0 %}
<div class="row"> l6
<div class="col s12 {% else %}
{% if app.data_sources.count() > 0 %} l12
l6 {% endif %}">
{% else %} <img src="{{ app.icon }}" class="app-icon">
l12 <span class="theme-muted-text app-name">{{ app.name }}</span>
{% endif %}"> {% if app.description %}
<img src="{{ app.icon }}" class="app-icon"> <i class="material-icons-outlined tooltipped" data-position="top" data-tooltip="{{ app.description }}">info</i>
<span class="theme-muted-text app-name">{{ app.name }}</span> {% endif %}
{% if app.description %} </div>
<i class="material-icons-outlined tooltipped" data-position="top" data-tooltip="{{ app.description }}">info</i> <div class="col s12 l6 right-align">
{% endif %} {% if app.data_sources.count() > 0 %}
</div> <span class="data-source-loading">{{ preload_circle() }}</span>
<div class="col s12 l6 right-align"> {% for data_source in app.data_sources %}
{% if app.data_sources.count() > 0 %} <span class="data-source-container theme-primary-text"
<span class="data-source-loading">{{ preload_circle() }}</span> data-url="{{ url_for('main.load_data_source') }}"
{% for data_source in app.data_sources %} data-id="{{ data_source.id }}">
<span class="data-source-container theme-primary-text" </span>
data-url="{{ url_for('main.load_data_source') }}" {% endfor %}
data-id="{{ data_source.id }}"> {% endif %}
</span>
{% endfor %}
{% endif %}
</div>
</div>
</a>
{# </a> This closes AppAnchor() #}
{% endfor %}
</div> </div>
</div> </div>
</a>
{# </a> This closes AppAnchor() #}
{% endmacro %} {% endmacro %}

View File

@ -12,7 +12,7 @@
<input name="files" id="add-images-input" class="hide"> <input name="files" id="add-images-input" class="hide">
</form> </form>
<div class="col s12"> <div class="col s12">
{{ tcdrop(allowed_types='jpg,jpeg,png,gif', id="images-tcdrop", max_files="30") }} {{ tcdrop(allowed_types='apng,bmp,gif,ico,cur,jpg,jpeg,jfif,pjpeg,pjp,png,svg,tif,tiff,webp', id="images-tcdrop", max_files="30") }}
{{ button(text="save", icon="save", id="save-images-btn", float="left", data={"url": url_for('settings_system.add_images')}) }} {{ button(text="save", icon="save", id="save-images-btn", float="left", data={"url": url_for('settings_system.add_images')}) }}
</div> </div>
</div> </div>

View File

@ -9,7 +9,7 @@
<img width="120px" src="static/images/logo/logo.svg" alt="DashMachine Logo"> <img width="120px" src="static/images/logo/logo.svg" alt="DashMachine Logo">
</div> </div>
<div class="row"> <div class="row">
<form class="login-form" method="POST"> <form class="login-form" method="POST" data-url="{{ url_for('user_system.check_login') }}">
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<div class="input-field col s12"> <div class="input-field col s12">
@ -44,3 +44,7 @@
</div> </div>
</div> </div>
{% endblock content %} {% endblock content %}
{% block page_lvl_js %}
{{ process_js_sources(src="main/login.js")|safe }}
{% endblock page_lvl_js %}

View File

@ -1,4 +1,4 @@
from flask import render_template, url_for, redirect, Blueprint from flask import render_template, url_for, redirect, Blueprint, jsonify
from flask_login import login_user, logout_user from flask_login import login_user, logout_user
from dashmachine.user_system.forms import LoginForm from dashmachine.user_system.forms import LoginForm
from dashmachine.user_system.models import User from dashmachine.user_system.models import User
@ -14,25 +14,30 @@ user_system = Blueprint("user_system", __name__)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# login page # login page
@public_route @public_route
@user_system.route("/login", methods=["GET", "POST"]) @user_system.route("/login", methods=["GET"])
def login(): def login():
user = User.query.first()
form = LoginForm() form = LoginForm()
return render_template("user/login.html", title="Login", form=form)
@public_route
@user_system.route("/check_login", methods=["POST"])
def check_login():
form = LoginForm()
if form.validate_on_submit(): if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data.lower()).first() user = User.query.filter_by(username=form.username.data.lower()).first()
if not user:
response = {"err": "User not found"}
if user and bcrypt.check_password_hash(user.password, form.password.data): elif bcrypt.check_password_hash(user.password, form.password.data):
login_user(user, remember=form.remember.data) login_user(user, remember=form.remember.data)
response = {"url": url_for("main.home")}
return redirect(url_for("main.home"))
else: else:
print("password was wrong") response = {"err": "Password is wrong"}
return redirect(url_for("user_system.login")) else:
response = {"err": str(form.errors)}
return render_template("user/login.html", title="Login", form=form) return jsonify(data=response)
# this logs the user out and redirects to the login page # this logs the user out and redirects to the login page

View File

@ -1 +1 @@
version = "v0.34" version = "v0.4"

View File

@ -5,3 +5,4 @@ background = None
roles = admin,user,public_user roles = admin,user,public_user
home_access_groups = admin_only home_access_groups = admin_only
settings_access_groups = admin_only settings_access_groups = admin_only
custom_app_title = DashMachine

View File

@ -0,0 +1,28 @@
"""empty message
Revision ID: ce94252d9023
Revises: a36cddc6266e
Create Date: 2020-03-08 10:56:30.470619
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "ce94252d9023"
down_revision = "a36cddc6266e"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("settings", sa.Column("custom_app_title", sa.String(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("settings", "custom_app_title")
# ### end Alembic commands ###

View File

@ -2,7 +2,8 @@
- [ ] I have added a .ini file to DashMachine with the same name used for [App Name] in the .ini file (e.g. 'App Name.ini') - [ ] I have added a .ini file to DashMachine with the same name used for [App Name] in the .ini file (e.g. 'App Name.ini')
- [ ] My .ini has the exact format as the others in the folder, only changing the name, icon location, and description. - [ ] My .ini has the exact format as the others in the folder, only changing the name, icon location, and description.
- [ ] I used the official description and name for the app, found on their repository/website. - [ ] I used the official description and name for the app, found on their repository/website.
- [ ] I chose an icon (.png file) sized under 200px, with a transparent background, preferably the icon only version (not icon w/ text) of the app's icon. - [ ] I chose an icon (.png file) sized over 64px, with a transparent background, square aspect ratio, preferably the icon only version (not icon w/ text) of the app's icon.
- [ ] I understand that the icon will be resized to 64px x 64px and have verified that it looks good at that size.
- [ ] I have added the icon to static/images/apps with the correct name matching what's in the .ini. - [ ] I have added the icon to static/images/apps with the correct name matching what's in the .ini.
- [ ] I tested it to make sure it looks good in the interface - [ ] I tested it to make sure it looks good in the interface

View File

@ -1,4 +1,4 @@
[Wiki.js] [Wikijs]
prefix = https:// prefix = https://
url = your-website.com url = your-website.com
icon = static/images/apps/wikijs.png icon = static/images/apps/wikijs.png