mirror of
https://github.com/akuker/RASCSI.git
synced 2024-10-16 07:23:55 +00:00
Web Interface i18n (#564)
* Add flask_babel 2.0.0 to requirements * Partial i18n * Use current locale for protobuf requests * Don't store generated messages.pot in git * Internationalize all python code * Formatting fixes * Partial internationalization of html * Iterate on html i18n * Completed i18n of code * Improve i18n of strings * Blurb about i18n in the readme * Improve i18n strings * Add the compiled messages.mo files to .gitignore * Add complete Swedish localization * Generate localizations in start.sh * Only compile messages.mo in start.sh; better sequence * Fix bug
This commit is contained in:
parent
c19c814863
commit
ab82d6e4eb
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,3 +11,5 @@ src/oled_monitor/current
|
||||
src/oled_monitor/rascsi_interface_pb2.py
|
||||
src/raspberrypi/hfdisk/
|
||||
*~
|
||||
messages.pot
|
||||
messages.mo
|
||||
|
@ -46,3 +46,15 @@ $ cd ~/source/RASCSI
|
||||
$ git remote add pi ssh://pi@rascsi/home/pi/dev.git
|
||||
$ git push pi master
|
||||
```
|
||||
|
||||
## Localizing the Web Interface
|
||||
|
||||
We use the Flask-Babel library and Flask/Jinja2 extension for i18n.
|
||||
|
||||
To create a new localization, it needs to be added to accept_languages in
|
||||
the get_locale() method, and also to localizer.cpp in the RaSCSI C++ code.
|
||||
|
||||
Once this is done, follow the steps in the [Flask-Babel documentation](https://flask-babel.tkte.ch/#translating-applications)
|
||||
to generate the messages.po for the new language.
|
||||
|
||||
Updating an existing messages.po is also covered above.
|
||||
|
3
src/web/babel.cfg
Normal file
3
src/web/babel.cfg
Normal file
@ -0,0 +1,3 @@
|
||||
[python: **.py]
|
||||
[jinja2: **/templates/**.html]
|
||||
extensions=jinja2.ext.autoescape,jinja2.ext.with_
|
@ -6,6 +6,7 @@ import os
|
||||
import logging
|
||||
from pathlib import PurePath
|
||||
from flask import current_app
|
||||
from flask_babel import _
|
||||
|
||||
from ractl_cmds import (
|
||||
get_server_info,
|
||||
@ -65,6 +66,7 @@ def list_images():
|
||||
command = proto.PbCommand()
|
||||
command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
data = send_pb_command(command.SerializeToString())
|
||||
result = proto.PbResult()
|
||||
@ -121,6 +123,7 @@ def create_new_image(file_name, file_type, size):
|
||||
command = proto.PbCommand()
|
||||
command.operation = proto.PbOperation.CREATE_IMAGE
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
command.params["file"] = file_name + "." + file_type
|
||||
command.params["size"] = str(size)
|
||||
@ -141,6 +144,7 @@ def delete_image(file_name):
|
||||
command = proto.PbCommand()
|
||||
command.operation = proto.PbOperation.DELETE_IMAGE
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
command.params["file"] = file_name
|
||||
|
||||
@ -159,6 +163,7 @@ def rename_image(file_name, new_file_name):
|
||||
command = proto.PbCommand()
|
||||
command.operation = proto.PbOperation.RENAME_IMAGE
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
command.params["from"] = file_name
|
||||
command.params["to"] = new_file_name
|
||||
@ -176,8 +181,14 @@ def delete_file(file_path):
|
||||
"""
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
return {"status": True, "msg": f"File deleted: {file_path}"}
|
||||
return {"status": False, "msg": f"File to delete not found: {file_path}"}
|
||||
return {
|
||||
"status": True,
|
||||
"msg": _(u"File deleted: %(file_path)s", file_path=file_path),
|
||||
}
|
||||
return {
|
||||
"status": False,
|
||||
"msg": _(u"File to delete not found: %(file_path)s", file_path=file_path),
|
||||
}
|
||||
|
||||
|
||||
def rename_file(file_path, target_path):
|
||||
@ -187,8 +198,14 @@ def rename_file(file_path, target_path):
|
||||
"""
|
||||
if os.path.exists(PurePath(target_path).parent):
|
||||
os.rename(file_path, target_path)
|
||||
return {"status": True, "msg": f"File moved to: {target_path}"}
|
||||
return {"status": False, "msg": f"Unable to move to: {target_path}"}
|
||||
return {
|
||||
"status": True,
|
||||
"msg": _(u"File moved to: %(target_path)s", target_path=target_path),
|
||||
}
|
||||
return {
|
||||
"status": False,
|
||||
"msg": _(u"Unable to move file to: %(target_path)s", target_path=target_path),
|
||||
}
|
||||
|
||||
|
||||
def unzip_file(file_name, member=False, members=False):
|
||||
@ -303,7 +320,7 @@ def download_file_to_iso(url, *iso_args):
|
||||
|
||||
return {
|
||||
"status": True,
|
||||
"msg": f"Created CD-ROM ISO image with arguments \"" + " ".join(iso_args) + "\"",
|
||||
"msg": _(u"Created CD-ROM ISO image with arguments \"%(value)s\"", value=" ".join(iso_args)),
|
||||
"file_name": iso_filename,
|
||||
}
|
||||
|
||||
@ -331,7 +348,14 @@ def download_to_dir(url, save_dir):
|
||||
logging.info("Response content-type: %s", req.headers["content-type"])
|
||||
logging.info("Response status code: %s", req.status_code)
|
||||
|
||||
return {"status": True, "msg": f"{file_name} downloaded to {save_dir}"}
|
||||
return {
|
||||
"status": True,
|
||||
"msg": _(
|
||||
u"%(file_name)s downloaded to %(save_dir)s",
|
||||
file_name=file_name,
|
||||
save_dir=save_dir,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def write_config(file_name):
|
||||
@ -369,7 +393,7 @@ def write_config(file_name):
|
||||
json_file,
|
||||
indent=4
|
||||
)
|
||||
return {"status": True, "msg": f"Saved config to {file_name}"}
|
||||
return {"status": True, "msg": _(u"Saved configuration file to %(file_name)s", file_name=file_name)}
|
||||
except (IOError, ValueError, EOFError, TypeError) as error:
|
||||
logging.error(str(error))
|
||||
delete_file(file_name)
|
||||
@ -377,7 +401,10 @@ def write_config(file_name):
|
||||
except:
|
||||
logging.error("Could not write to file: %s", file_name)
|
||||
delete_file(file_name)
|
||||
return {"status": False, "msg": f"Could not write to file: {file_name}"}
|
||||
return {
|
||||
"status": False,
|
||||
"msg": _(u"Could not write to file: %(file_name)s", file_name=file_name),
|
||||
}
|
||||
|
||||
|
||||
def read_config(file_name):
|
||||
@ -434,14 +461,20 @@ def read_config(file_name):
|
||||
kwargs[param] = params[param]
|
||||
attach_image(row["id"], **kwargs)
|
||||
else:
|
||||
return {"status": False, "msg": "Invalid config file format."}
|
||||
return {"status": True, "msg": f"Loaded config from: {file_name}"}
|
||||
return {"status": False, "msg": _(u"Invalid configuration file format")}
|
||||
return {
|
||||
"status": True,
|
||||
"msg": _(u"Loaded configurations from: %(file_name)s", file_name=file_name),
|
||||
}
|
||||
except (IOError, ValueError, EOFError, TypeError) as error:
|
||||
logging.error(str(error))
|
||||
return {"status": False, "msg": str(error)}
|
||||
except:
|
||||
logging.error("Could not read file: %s", file_name)
|
||||
return {"status": False, "msg": f"Could not read file: {file_name}"}
|
||||
return {
|
||||
"status": False,
|
||||
"msg": _(u"Could not read configuration file: %(file_name)s", file_name=file_name),
|
||||
}
|
||||
|
||||
|
||||
def write_drive_properties(file_name, conf):
|
||||
@ -455,7 +488,10 @@ def write_drive_properties(file_name, conf):
|
||||
try:
|
||||
with open(file_path, "w") as json_file:
|
||||
dump(conf, json_file, indent=4)
|
||||
return {"status": True, "msg": f"Created file: {file_path}"}
|
||||
return {
|
||||
"status": True,
|
||||
"msg": _(u"Created properties file: %(file_path)s", file_path=file_path),
|
||||
}
|
||||
except (IOError, ValueError, EOFError, TypeError) as error:
|
||||
logging.error(str(error))
|
||||
delete_file(file_path)
|
||||
@ -463,23 +499,33 @@ def write_drive_properties(file_name, conf):
|
||||
except:
|
||||
logging.error("Could not write to file: %s", file_path)
|
||||
delete_file(file_path)
|
||||
return {"status": False, "msg": f"Could not write to file: {file_path}"}
|
||||
return {
|
||||
"status": False,
|
||||
"msg": _(u"Could not write to properties file: %(file_path)s", file_path=file_path),
|
||||
}
|
||||
|
||||
|
||||
def read_drive_properties(path_name):
|
||||
def read_drive_properties(file_path):
|
||||
"""
|
||||
Reads drive properties from json formatted file.
|
||||
Takes (str) path_name as argument.
|
||||
Takes (str) file_path as argument.
|
||||
Returns (dict) with (bool) status, (str) msg, (dict) conf
|
||||
"""
|
||||
from json import load
|
||||
try:
|
||||
with open(path_name) as json_file:
|
||||
with open(file_path) as json_file:
|
||||
conf = load(json_file)
|
||||
return {"status": True, "msg": f"Read from file: {path_name}", "conf": conf}
|
||||
return {
|
||||
"status": True,
|
||||
"msg": _(u"Read properties from file: %(file_path)s", file_path=file_path),
|
||||
"conf": conf,
|
||||
}
|
||||
except (IOError, ValueError, EOFError, TypeError) as error:
|
||||
logging.error(str(error))
|
||||
return {"status": False, "msg": str(error)}
|
||||
except:
|
||||
logging.error("Could not read file: %s", path_name)
|
||||
return {"status": False, "msg": f"Could not read file: {path_name}"}
|
||||
logging.error("Could not read file: %s", file_path)
|
||||
return {
|
||||
"status": False,
|
||||
"msg": _(u"Could not read properties from file: %(file_path)s", file_path=file_path),
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ Module for methods controlling and getting information about the Pi's Linux syst
|
||||
import subprocess
|
||||
import asyncio
|
||||
import logging
|
||||
from flask_babel import _
|
||||
from settings import AUTH_GROUP
|
||||
|
||||
|
||||
@ -175,6 +176,6 @@ def auth_active():
|
||||
if AUTH_GROUP in groups:
|
||||
return {
|
||||
"status": True,
|
||||
"msg": "You must log in to use this function!",
|
||||
"msg": _(u"You must log in to use this function"),
|
||||
}
|
||||
return {"status": False, "msg": ""}
|
||||
|
@ -5,6 +5,7 @@ Module for commands sent to the RaSCSI backend service.
|
||||
from settings import REMOVABLE_DEVICE_TYPES
|
||||
from socket_cmds import send_pb_command
|
||||
from flask import current_app
|
||||
from flask_babel import _
|
||||
import rascsi_interface_pb2 as proto
|
||||
|
||||
|
||||
@ -24,6 +25,7 @@ def get_server_info():
|
||||
command = proto.PbCommand()
|
||||
command.operation = proto.PbOperation.SERVER_INFO
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
data = send_pb_command(command.SerializeToString())
|
||||
result = proto.PbResult()
|
||||
@ -82,6 +84,7 @@ def get_reserved_ids():
|
||||
command = proto.PbCommand()
|
||||
command.operation = proto.PbOperation.RESERVED_IDS_INFO
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
data = send_pb_command(command.SerializeToString())
|
||||
result = proto.PbResult()
|
||||
@ -103,6 +106,7 @@ def get_network_info():
|
||||
command = proto.PbCommand()
|
||||
command.operation = proto.PbOperation.NETWORK_INTERFACES_INFO
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
data = send_pb_command(command.SerializeToString())
|
||||
result = proto.PbResult()
|
||||
@ -121,6 +125,7 @@ def get_device_types():
|
||||
command = proto.PbCommand()
|
||||
command.operation = proto.PbOperation.DEVICE_TYPES_INFO
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
data = send_pb_command(command.SerializeToString())
|
||||
result = proto.PbResult()
|
||||
@ -142,6 +147,8 @@ def get_image_files_info():
|
||||
"""
|
||||
command = proto.PbCommand()
|
||||
command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
data = send_pb_command(command.SerializeToString())
|
||||
result = proto.PbResult()
|
||||
@ -170,6 +177,7 @@ def attach_image(scsi_id, **kwargs):
|
||||
"""
|
||||
command = proto.PbCommand()
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
devices = proto.PbDeviceDefinition()
|
||||
devices.id = int(scsi_id)
|
||||
|
||||
@ -195,8 +203,12 @@ def attach_image(scsi_id, **kwargs):
|
||||
if current_type != device_type:
|
||||
return {
|
||||
"status": False,
|
||||
"msg": "Cannot insert an image for " + device_type + \
|
||||
" into a " + current_type + " device."
|
||||
"msg": _(
|
||||
u"Cannot insert an image for %(device_type)s into a "
|
||||
u"%(current_device_type)s device",
|
||||
device_type=device_type,
|
||||
current_device_type=current_type
|
||||
),
|
||||
}
|
||||
command.operation = proto.PbOperation.INSERT
|
||||
# Handling attaching a new device
|
||||
@ -241,6 +253,7 @@ def detach_by_id(scsi_id, unit=None):
|
||||
command.operation = proto.PbOperation.DETACH
|
||||
command.devices.append(devices)
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
data = send_pb_command(command.SerializeToString())
|
||||
result = proto.PbResult()
|
||||
@ -256,6 +269,7 @@ def detach_all():
|
||||
command = proto.PbCommand()
|
||||
command.operation = proto.PbOperation.DETACH_ALL
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
data = send_pb_command(command.SerializeToString())
|
||||
result = proto.PbResult()
|
||||
@ -278,6 +292,7 @@ def eject_by_id(scsi_id, unit=None):
|
||||
command.operation = proto.PbOperation.EJECT
|
||||
command.devices.append(devices)
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
data = send_pb_command(command.SerializeToString())
|
||||
result = proto.PbResult()
|
||||
@ -297,6 +312,7 @@ def list_devices(scsi_id=None, unit=None):
|
||||
command = proto.PbCommand()
|
||||
command.operation = proto.PbOperation.DEVICES_INFO
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
# If method is called with scsi_id parameter, return the info on those devices
|
||||
# Otherwise, return the info on all attached devices
|
||||
@ -374,6 +390,7 @@ def reserve_scsi_ids(reserved_scsi_ids):
|
||||
command.operation = proto.PbOperation.RESERVE_IDS
|
||||
command.params["ids"] = ",".join(reserved_scsi_ids)
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
data = send_pb_command(command.SerializeToString())
|
||||
result = proto.PbResult()
|
||||
@ -391,6 +408,7 @@ def set_log_level(log_level):
|
||||
command.operation = proto.PbOperation.LOG_LEVEL
|
||||
command.params["level"] = str(log_level)
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
data = send_pb_command(command.SerializeToString())
|
||||
result = proto.PbResult()
|
||||
@ -408,6 +426,7 @@ def shutdown_pi(mode):
|
||||
command.operation = proto.PbOperation.SHUT_DOWN
|
||||
command.params["mode"] = str(mode)
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
data = send_pb_command(command.SerializeToString())
|
||||
result = proto.PbResult()
|
||||
@ -424,6 +443,7 @@ def is_token_auth():
|
||||
command = proto.PbCommand()
|
||||
command.operation = proto.PbOperation.CHECK_AUTHENTICATION
|
||||
command.params["token"] = current_app.config["TOKEN"]
|
||||
command.params["locale"] = current_app.config["LOCALE"]
|
||||
|
||||
data = send_pb_command(command.SerializeToString())
|
||||
result = proto.PbResult()
|
||||
|
@ -7,3 +7,4 @@ MarkupSafe==2.0.1
|
||||
protobuf==3.17.3
|
||||
requests==2.26.0
|
||||
simplepam==0.1.5
|
||||
flask_babel==2.0.0
|
||||
|
@ -4,6 +4,7 @@ Module for sending and receiving data over a socket connection with the RaSCSI b
|
||||
|
||||
import logging
|
||||
from flask import abort
|
||||
from flask_babel import _
|
||||
from time import sleep
|
||||
|
||||
def send_pb_command(payload):
|
||||
@ -35,9 +36,12 @@ def send_pb_command(payload):
|
||||
logging.error(error_msg)
|
||||
|
||||
# After failing all attempts, throw a 404 error
|
||||
abort(404, "The RaSCSI Web Interface failed to connect to RaSCSI at " + str(host) + \
|
||||
":" + str(port) + " with error: " + error_msg + \
|
||||
". The RaSCSI service is not running or may have crashed.")
|
||||
abort(404, _(
|
||||
u"The RaSCSI Web Interface failed to connect to RaSCSI at %(host)s:%(port)s "
|
||||
u"with error: %(error_msg)s. The RaSCSI process is not running or may have crashed.",
|
||||
host=host, port=port, error_msg=error_msg,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def send_over_socket(sock, payload):
|
||||
@ -72,9 +76,11 @@ def send_over_socket(sock, payload):
|
||||
"RaSCSI may have crashed."
|
||||
)
|
||||
abort(
|
||||
503, "The RaSCSI Web Interface lost connection to RaSCSI. "
|
||||
"Please go back and try again. "
|
||||
"If the issue persists, please report a bug."
|
||||
503, _(
|
||||
u"The RaSCSI Web Interface lost connection to RaSCSI. "
|
||||
u"Please go back and try again. "
|
||||
u"If the issue persists, please report a bug."
|
||||
)
|
||||
)
|
||||
chunks.append(chunk)
|
||||
bytes_recvd = bytes_recvd + len(chunk)
|
||||
@ -86,8 +92,9 @@ def send_over_socket(sock, payload):
|
||||
"RaSCSI may have crashed."
|
||||
)
|
||||
abort(
|
||||
500,
|
||||
"The RaSCSI Web Interface did not get a valid response from RaSCSI. "
|
||||
"Please go back and try again. "
|
||||
"If the issue persists, please report a bug."
|
||||
500, _(
|
||||
u"The RaSCSI Web Interface did not get a valid response from RaSCSI. "
|
||||
u"Please go back and try again. "
|
||||
u"If the issue persists, please report a bug."
|
||||
)
|
||||
)
|
||||
|
@ -74,6 +74,8 @@ else
|
||||
fi
|
||||
set -e
|
||||
|
||||
pybabel compile -d translations
|
||||
|
||||
# parse arguments
|
||||
while [ "$1" != "" ]; do
|
||||
PARAM=$(echo "$1" | awk -F= '{print $1}')
|
||||
|
@ -26,12 +26,12 @@
|
||||
|
||||
<script type="application/javascript">
|
||||
var processNotify = function(Notification) {
|
||||
document.getElementById("flash").innerHTML = "<div class='message'>" + Notification + " This process may take a while, and will continue in the background if you navigate away from this page.</div>";
|
||||
document.getElementById("flash").innerHTML = "<div class='message'>" + Notification + "{{ _(" This process may take a while, and will continue in the background if you navigate away from this page.") }}</div>";
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
|
||||
var shutdownNotify = function(Notification) {
|
||||
document.getElementById("flash").innerHTML = "<div class='message'>" + Notification + " The Web Interface will become unresponsive momentarily. Reload this page after the Pi has started up again.</div>";
|
||||
document.getElementById("flash").innerHTML = "<div class='message'>" + Notification + "{{ _(" The Web Interface will become unresponsive momentarily. Reload this page after the Pi has started up again.") }}</div>";
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
</script>
|
||||
@ -45,19 +45,19 @@
|
||||
<div class="header">
|
||||
{% if auth_active %}
|
||||
{% if username %}
|
||||
<span style="display: inline-block; width: 100%; color: white; background-color: green; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">Logged in as <em>{{ username }}</em> – <a href="/logout">Log Out</a></span>
|
||||
<span style="display: inline-block; width: 100%; color: white; background-color: green; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">{{ _("Logged in as <em>%(username)s</em>", username=username) }} – <a href="/logout">{{ _("Log Out") }}</a></span>
|
||||
{% else %}
|
||||
<span style="display: inline-block; width: 100%; color: white; background-color: red; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">
|
||||
<form method="POST" action="/login">
|
||||
<div>Log In to Use Web Interface</div>
|
||||
<input type="text" name="username" placeholder="Username">
|
||||
<input type="password" name="password" placeholder="Password">
|
||||
<div>{{ _("Log In to Use Web Interface") }}</div>
|
||||
<input type="text" name="username" placeholder="{{ _("Username") }}">
|
||||
<input type="password" name="password" placeholder="{{ _("Password") }}">
|
||||
<input type="submit" value="Login">
|
||||
</form>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span style="display: inline-block; width: 100%; color: white; background-color: green; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">Web Interface Authentication Disabled – See <a href="https://github.com/akuker/RASCSI/wiki/Web-Interface#enable-authentication" target="_blank">Wiki</a> for more information</span>
|
||||
<span style="display: inline-block; width: 100%; color: white; background-color: green; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">{{ _("Web Interface Authentication Disabled") }} – {{ _("See <a href=\"%(url)s\" target=\"_blank\">Wiki</a> for more information", url="https://github.com/akuker/RASCSI/wiki/Web-Interface#enable-authentication") }}</span>
|
||||
{% endif %}
|
||||
<table width="100%">
|
||||
<tbody>
|
||||
@ -84,8 +84,8 @@
|
||||
{% block content %}{% endblock content %}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<center><tt>RaSCSI version: <strong>{{ version }} <a href="https://github.com/akuker/RASCSI/commit/{{ running_env['git'] }}" target="_blank">{{ running_env["git"][:7] }}</a></strong></tt></center>
|
||||
<center><tt>Pi environment: {{ running_env["env"] }}</tt></center>
|
||||
<center><tt>{{ _("RaSCSI version: ") }}<strong>{{ version }} <a href="https://github.com/akuker/RASCSI/commit/{{ running_env['git'] }}" target="_blank">{{ running_env["git"][:7] }}</a></strong></tt></center>
|
||||
<center><tt>{{ _("Pi environment: ") }}{{ running_env["env"] }}</tt></center>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
@ -1,19 +1,19 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<p><a href="/">Cancel</a></p>
|
||||
<h2>Disclaimer</h2>
|
||||
<p>These device profiles are provided as-is with no guarantee to work on the systems mentioned. You may need appropirate device drivers and/or configuration parameters. If you have improvement suggestions or success stories to share we would love to hear from you, so please connect with us at <a href="https://github.com/akuker/RASCSI">GitHub</a> or <a href="https://discord.gg/PyS58u6">Discord</a>!</p>
|
||||
<h2>Hard Drives</h2>
|
||||
<p><a href="/">{{ _("Cancel") }}</a></p>
|
||||
<h2>{{ _("Disclaimer") }}</h2>
|
||||
<p>{{ _("These device profiles are provided as-is with no guarantee to work equally to the actual physical device they are named after. You may need to provide appropirate device drivers and/or configuration parameters for them to function properly. If you would like to see data modified, or have additional devices to add to the list, please raise an issue ticket at <a href=\"%(url)s\">GitHub</a>.", url="https://github.com/akuker/RASCSI/issues") }}</p>
|
||||
<h2>{{ _("Hard Drives") }}</h2>
|
||||
|
||||
<table cellpadding="3" border="black">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>Name</b></td>
|
||||
<td><b>Size (MB)</b></td>
|
||||
<td><b>Description</b></td>
|
||||
<td><b>Ref.</b></td>
|
||||
<td><b>Action</b></td>
|
||||
<td><b>{{ _("Name") }}</b></td>
|
||||
<td><b>{{ _("Size (MB)") }}</b></td>
|
||||
<td><b>{{ _("Description") }}</b></td>
|
||||
<td><b>{{ _("Ref.") }}</b></td>
|
||||
<td><b>{{ _("Action") }}</b></td>
|
||||
</tr>
|
||||
{% for hd in hd_conf %}
|
||||
<tr>
|
||||
@ -22,7 +22,7 @@
|
||||
<td style="text-align:left">{{ hd.description }}</td>
|
||||
<td style="text-align:left">
|
||||
{% if hd.url != "" %}
|
||||
<a href="{{ hd.url }}">Link</a>
|
||||
<a href="{{ hd.url }}">{{ _("Link") }}</a>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
@ -36,9 +36,9 @@
|
||||
<input type="hidden" name="block_size" value="{{ hd.block_size }}">
|
||||
<input type="hidden" name="size" value="{{ hd.size }}">
|
||||
<input type="hidden" name="file_type" value="{{ hd.file_type }}">
|
||||
<label for="file_name">Save as:</label>
|
||||
<label for="file_name">{{ _("Save as:") }}</label>
|
||||
<input type="text" name="file_name" value="{{ hd.secure_name }}" required />.{{ hd.file_type }}
|
||||
<input type="submit" value="Create" />
|
||||
<input type="submit" value="{{ _("Create") }}" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@ -48,16 +48,16 @@
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>CD-ROM Drives</h2>
|
||||
<p><em>This will create a properties file for the given CD-ROM image. No new image file will be created.</em></p>
|
||||
<h2>{{ _("CD-ROM Drives") }}</h2>
|
||||
<p><em>{{ _("This will create a properties file for the given CD-ROM image. No new image file will be created.") }}</em></p>
|
||||
<table cellpadding="3" border="black">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>Name</b></td>
|
||||
<td><b>Size (MB)</b></td>
|
||||
<td><b>Description</b></td>
|
||||
<td><b>Ref.</b></td>
|
||||
<td><b>Action</b></td>
|
||||
<td><b>{{ _("Name") }}</b></td>
|
||||
<td><b>{{ _("Size (MB)") }}</b></td>
|
||||
<td><b>{{ _("Description") }}</b></td>
|
||||
<td><b>{{ _("Ref.") }}</b></td>
|
||||
<td><b>{{ _("Action") }}</b></td>
|
||||
</tr>
|
||||
{% for cd in cd_conf %}
|
||||
<tr>
|
||||
@ -66,7 +66,7 @@
|
||||
<td style="text-align:left">{{ cd.description }}</td>
|
||||
<td style="text-align:left">
|
||||
{% if cd.url != "" %}
|
||||
<a href="{{ cd.url }}">Link</a>
|
||||
<a href="{{ cd.url }}">{{ _("Link") }}</a>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
@ -77,7 +77,7 @@
|
||||
<input type="hidden" name="product" value="{{ cd.product }}">
|
||||
<input type="hidden" name="revision" value="{{ cd.revision }}">
|
||||
<input type="hidden" name="block_size" value="{{ cd.block_size }}">
|
||||
<label for="file_name">Create for:</label>
|
||||
<label for="file_name">{{ _("Create for:") }}</label>
|
||||
<select type="select" name="file_name">
|
||||
{% for f in files %}
|
||||
{% if f["name"].lower().endswith(cdrom_file_suffix) %}
|
||||
@ -85,7 +85,7 @@
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="Create" />
|
||||
<input type="submit" value="{{ _("Create") }}" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@ -95,15 +95,15 @@
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Removable Drives</h2>
|
||||
<h2>{{ _("Removable Drives") }}</h2>
|
||||
<table cellpadding="3" border="black">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>Name</b></td>
|
||||
<td><b>Size (MB)</b></td>
|
||||
<td><b>Description</b></td>
|
||||
<td><b>Ref.</b></td>
|
||||
<td><b>Action</b></td>
|
||||
<td><b>{{ _("Name") }}</b></td>
|
||||
<td><b>{{ _("Size (MB)") }}</b></td>
|
||||
<td><b>{{ _("Description") }}</b></td>
|
||||
<td><b>{{ _("Ref.") }}</b></td>
|
||||
<td><b>{{ _("Action") }}</b></td>
|
||||
</tr>
|
||||
{% for rm in rm_conf %}
|
||||
<tr>
|
||||
@ -112,7 +112,7 @@
|
||||
<td style="text-align:left">{{ rm.description }}</td>
|
||||
<td style="text-align:left">
|
||||
{% if rm.url != "" %}
|
||||
<a href="{{ rm.url }}">Link</a>
|
||||
<a href="{{ rm.url }}">{{ _("Link") }}</a>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
@ -126,16 +126,16 @@
|
||||
<input type="hidden" name="block_size" value="{{ rm.block_size }}">
|
||||
<input type="hidden" name="size" value="{{ rm.size }}">
|
||||
<input type="hidden" name="file_type" value="{{ rm.file_type }}">
|
||||
<label for="file_name">Save as:</label>
|
||||
<label for="file_name">{{ _("Save as:") }}</label>
|
||||
<input type="text" name="file_name" value="{{ rm.secure_name }}" required />.{{ rm.file_type }}
|
||||
<input type="submit" value="Create" />
|
||||
<input type="submit" value="{{ _("Create") }}" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p><small>Available disk space on the Pi: {{ free_disk }} MB</small></p>
|
||||
<p><a href="/">Cancel</a></p>
|
||||
<p><small>{{ _("%(disk_space)s MB disk space remaining on the Pi", disk_space=free_disk) }}</small></p>
|
||||
<p><a href="/">{{ _("Cancel") }}</a></p>
|
||||
|
||||
{% endblock content %}
|
||||
|
@ -3,12 +3,12 @@
|
||||
|
||||
<details>
|
||||
<summary class="heading">
|
||||
Current RaSCSI Configuration
|
||||
{{ _("Current RaSCSI Configuration") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>Displays the currently attached devices for each available SCSI ID.</li>
|
||||
<li>Save and load device configurations into <tt>{{ CFG_DIR }}</tt></li>
|
||||
<li>The <em>default</em> configuration will be loaded when the Web UI starts up, if available.</li>
|
||||
<li>{{ _("Displays the currently attached devices for each available SCSI ID.") }}</li>
|
||||
<li>{{ _("Save and load device configurations, stored as json files in <tt>%(config_dir)s</tt>", config_dir=CFG_DIR) }}</tt></li>
|
||||
<li>{{ _("To have a particular device configuration load when RaSCSI starts, save it as <em>default</em>.") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
@ -22,31 +22,31 @@
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<option disabled>
|
||||
No saved configs
|
||||
{{ _("No saved configurations") }}
|
||||
</option>
|
||||
{% endif %}
|
||||
</select>
|
||||
<input name="load" type="submit" value="Load" onclick="return confirm('Detach all current device and Load config?')">
|
||||
<input name="delete" type="submit" value="Delete" onclick="return confirm('Delete config file?')">
|
||||
<input name="load" type="submit" value="{{ _("Load") }}" onclick="return confirm('{{ _("Detach all current device and Load configuration?") }}')">
|
||||
<input name="delete" type="submit" value="{{ _("Delete") }}" onclick="return confirm('{{ _("Delete configuration file?") }}')">
|
||||
</form></p>
|
||||
|
||||
<p><form action="/config/save" method="post">
|
||||
<input name="name" placeholder="default" size="20">
|
||||
<input type="submit" value="Save">
|
||||
<input type="submit" value="{{ _("Save") }}">
|
||||
</form></p>
|
||||
|
||||
<table border="black" cellpadding="3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>ID</b></td>
|
||||
<td><b>{{ _("ID") }}</b></td>
|
||||
{% if units %}
|
||||
<td><b>LUN</b></td>
|
||||
<td><b>{{ _("LUN") }}</b></td>
|
||||
{% endif %}
|
||||
<td><b>Type</b></td>
|
||||
<td><b>Status</b></td>
|
||||
<td><b>File</b></td>
|
||||
<td><b>Product</b></td>
|
||||
<td><b>Actions</b></td>
|
||||
<td><b>{{ _("Type") }}</b></td>
|
||||
<td><b>{{ _("Status") }}</b></td>
|
||||
<td><b>{{ _("File") }}</b></td>
|
||||
<td><b>{{ _("Product") }}</b></td>
|
||||
<td><b>{{ _("Actions") }}</b></td>
|
||||
</tr>
|
||||
{% for device in devices %}
|
||||
<tr>
|
||||
@ -81,7 +81,7 @@
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="Attach">
|
||||
<input type="submit" value="{{ _("Attach") }}">
|
||||
</form>
|
||||
{% else %}
|
||||
{{ device.file }}
|
||||
@ -95,28 +95,28 @@
|
||||
<td style="text-align:center">
|
||||
{% if device.device_type != "-" %}
|
||||
{% if device.device_type in REMOVABLE_DEVICE_TYPES and "No Media" not in device.status %}
|
||||
<form action="/scsi/eject" method="post" onsubmit="return confirm('Eject Disk? WARNING: On Mac OS, eject the Disk in Finder instead!')">
|
||||
<form action="/scsi/eject" method="post" onsubmit="return confirm('{{ _("Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!") }}')">
|
||||
<input name="scsi_id" type="hidden" value="{{ device.id }}">
|
||||
<input name="unit" type="hidden" value="{{ device.unit }}">
|
||||
<input type="submit" value="Eject">
|
||||
<input type="submit" value="{{ _("Eject") }}">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="/scsi/detach" method="post" onsubmit="return confirm('Detach Device?')">
|
||||
<form action="/scsi/detach" method="post" onsubmit="return confirm('{{ _("Detach Device?") }}')">
|
||||
<input name="scsi_id" type="hidden" value="{{ device.id }}">
|
||||
<input name="unit" type="hidden" value="{{ device.unit }}">
|
||||
<input type="submit" value="Detach">
|
||||
<input type="submit" value="{{ _("Detach") }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
<form action="/scsi/info" method="post">
|
||||
<input name="scsi_id" type="hidden" value="{{ device.id }}">
|
||||
<input name="unit" type="hidden" value="{{ device.unit }}">
|
||||
<input type="submit" value="Info">
|
||||
<input type="submit" value="{{ _("Info") }}">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="/scsi/reserve" method="post" onsubmit="var memo = prompt('Enter a memo for this reservation'); if (memo === null) event.preventDefault(); document.getElementById('memo_{{ device.id }}').value = memo;">
|
||||
<form action="/scsi/reserve" method="post" onsubmit="var memo = prompt('{{ _("Enter a memo for this reservation") }}'); if (memo === null) event.preventDefault(); document.getElementById('memo_{{ device.id }}').value = memo;">
|
||||
<input name="scsi_id" type="hidden" value="{{ device.id }}">
|
||||
<input name="memo" id="memo_{{ device.id }}" type="hidden" value="">
|
||||
<input type="submit" value="Reserve">
|
||||
<input type="submit" value="{{ _("Reserve") }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
@ -126,13 +126,13 @@
|
||||
<td class="inactive"></td>
|
||||
{% endif %}
|
||||
<td class="inactive"></td>
|
||||
<td class="inactive">Reserved ID</td>
|
||||
<td class="inactive">{{ _("Reserved ID") }}</td>
|
||||
<td class="inactive">{{ RESERVATIONS[device.id] }}</td>
|
||||
<td class="inactive"></td>
|
||||
<td class="inactive">
|
||||
<form action="/scsi/unreserve" method="post">
|
||||
<input name="scsi_id" type="hidden" value="{{ device.id }}">
|
||||
<input type="submit" value="Unreserve">
|
||||
<input type="submit" value="{{ _("Unreserve") }}">
|
||||
</form>
|
||||
</td>
|
||||
{% endif %}
|
||||
@ -141,31 +141,31 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><form action="/scsi/detach_all" method="post" onsubmit="return confirm('Detach all SCSI Devices?')">
|
||||
<input type="submit" value="Detach All Devices">
|
||||
<p><form action="/scsi/detach_all" method="post" onsubmit="return confirm('{{ _("Detach all SCSI Devices?") }}')">
|
||||
<input type="submit" value="{{ _("Detach All Devices") }}">
|
||||
</form></p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<details>
|
||||
<summary class="heading">
|
||||
Image File Management
|
||||
{{ _("Image File Management") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>Manage image files in the active RaSCSI image directory: <tt>{{ base_dir }}</tt> with a scan depth of {{ scan_depth }}.</li>
|
||||
<li>Select a valid SCSI ID and <a href="https://en.wikipedia.org/wiki/Logical_unit_number">LUN</a> to attach to. Unless you know what you're doing, always use LUN 0.
|
||||
<li>{{ _("Manage image files in the active RaSCSI image directory: <tt>%(directory)s</tt> with a scan depth of %(scan_depth)s.", directory=base_dir, scan_depth=scan_depth) }}</li>
|
||||
<li>{{ _("Select a valid SCSI ID and <a href=\"%(url)s\">LUN</a> to attach to. Unless you know what you're doing, always use LUN 0.", url="https://en.wikipedia.org/wiki/Logical_unit_number") }}
|
||||
</li>
|
||||
<li>If RaSCSI was unable to detect the device type associated with the image, you can choose the type from the dropdown.</li>
|
||||
<li>Types: SAHD = SASI HDD | SCHD = SCSI HDD | SCRM = Removable | SCMO = Magneto-Optical | SCCD = CD-ROM | SCBR = Host Bridge | SCDP = DaynaPORT</li>
|
||||
<li>{{ _("If RaSCSI was unable to detect the device type associated with the image, you can choose the type from the dropdown.") }}</li>
|
||||
<li>{{ _("Types: SAHD = SASI HDD | SCHD = SCSI HDD | SCRM = Removable | SCMO = Magneto-Optical | SCCD = CD-ROM | SCBR = Host Bridge | SCDP = DaynaPORT") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<table border="black" cellpadding="3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>File</b></td>
|
||||
<td><b>Size</b></td>
|
||||
<td><b>Actions</b></td>
|
||||
<td><b>{{ _("File") }}</b></td>
|
||||
<td><b>{{ _("Size") }}</b></td>
|
||||
<td><b>{{ _("Actions") }}</b></td>
|
||||
</tr>
|
||||
{% for file in files %}
|
||||
<tr>
|
||||
@ -181,7 +181,7 @@
|
||||
{% endfor %}
|
||||
<form action="/files/download" method="post">
|
||||
<input name="file" type="hidden" value="{{ CFG_DIR }}/{{ file['name'].replace(base_dir, '') }}.{{ PROPERTIES_SUFFIX }}">
|
||||
<input type="submit" value="Properties File ↓">
|
||||
<input type="submit" value="{{ _("Properties File") }} ↓">
|
||||
</form>
|
||||
</ul>
|
||||
</details>
|
||||
@ -201,7 +201,7 @@
|
||||
<form action="/files/unzip" method="post">
|
||||
<input name="zip_file" type="hidden" value="{{ file['name'] }}">
|
||||
<input name="zip_member" type="hidden" value="{{ member }}">
|
||||
<input type="submit" value="Unzip" onclick="processNotify('Unzipping a single file...')">
|
||||
<input type="submit" value="{{ _("Unzip") }}" onclick="processNotify('{{ _("Unzipping a single file...") }}')">
|
||||
</form>
|
||||
</summary>
|
||||
<ul style="list-style: none;">
|
||||
@ -215,7 +215,7 @@
|
||||
<form action="/files/unzip" method="post">
|
||||
<input name="zip_file" type="hidden" value="{{ file['name'] }}">
|
||||
<input name="zip_member" type="hidden" value="{{ member }}">
|
||||
<input type="submit" value="Unzip" onclick="processNotify('Unzipping a single file...')">
|
||||
<input type="submit" value="{{ _("Unzip") }}" onclick="processNotify('{{ _("Unzipping a single file...") }}')">
|
||||
</form>
|
||||
{% endif %}
|
||||
</li>
|
||||
@ -230,26 +230,26 @@
|
||||
<td style="text-align:center">
|
||||
<form action="/files/download" method="post">
|
||||
<input name="file" type="hidden" value="{{ base_dir }}/{{ file['name'] }}">
|
||||
<input type="submit" value="{{ file['size_mb'] }} MB ↓">
|
||||
<input type="submit" value="{{ file['size_mb'] }} {{ _("MB") }} ↓">
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
{% if file["name"] in attached_images %}
|
||||
<center>
|
||||
Attached!
|
||||
{{ _("Attached!") }}
|
||||
</center>
|
||||
{% else %}
|
||||
{% if file["name"].lower().endswith(ARCHIVE_FILE_SUFFIX) %}
|
||||
<form action="/files/unzip" method="post">
|
||||
<input name="zip_file" type="hidden" value="{{ file['name'] }}">
|
||||
<input name="zip_members" type="hidden" value="{{ file['zip_members'] }}">
|
||||
<input type="submit" value="Unzip All" onclick="processNotify('Unzipping all files...')">
|
||||
<input type="submit" value="{{ _("Unzip All") }}" onclick="processNotify('{{ _("Unzipping all files...") }}')">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="/scsi/attach" method="post">
|
||||
<input name="file_name" type="hidden" value="{{ file['name'] }}">
|
||||
<input name="file_size" type="hidden" value="{{ file['size'] }}">
|
||||
<label for="id">ID</label>
|
||||
<label for="id">{{ _("ID") }}</label>
|
||||
<select name="scsi_id">
|
||||
{% for id in scsi_ids %}
|
||||
<option name="id" value="{{id}}"{% if id == recommended_id %} selected{% endif %}>
|
||||
@ -257,7 +257,7 @@
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="unit">LUN</label>
|
||||
<label for="unit">{{ _("LUN") }}</label>
|
||||
<input name="unit" type="number" size="2" value="0" min="0" max="31">
|
||||
{% if file["detected_type"] != "UNDEFINED" %}
|
||||
<input name="type" type="hidden" value="{{ file['detected_type'] }}">
|
||||
@ -265,7 +265,7 @@
|
||||
{% else %}
|
||||
<select name="type">
|
||||
<option selected value="">
|
||||
Type
|
||||
{{ _("Type") }}
|
||||
</option>
|
||||
{% for d in device_types %}
|
||||
<option value="{{ d }}">
|
||||
@ -274,17 +274,17 @@
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</select>
|
||||
<input type="submit" value="Attach">
|
||||
<input type="submit" value="{{ _("Attach") }}">
|
||||
{% endif %}
|
||||
</form>
|
||||
<form action="/files/rename" method="post" onsubmit="var new_file_name = prompt('Enter new file name for \'{{ file["name"] }}\'', '{{ file['name'] }}'); if (new_file_name === null) event.preventDefault(); document.getElementById('new_file_name_{{ loop.index }}').value = new_file_name;">
|
||||
<form action="/files/rename" method="post" onsubmit="var new_file_name = prompt('{{ _("Enter new file name for: %(file_name)s", file_name=file["name"]) }}', '{{ file['name'] }}'); if (new_file_name === null) event.preventDefault(); document.getElementById('new_file_name_{{ loop.index }}').value = new_file_name;">
|
||||
<input name="file_name" type="hidden" value="{{ file['name'] }}">
|
||||
<input name="new_file_name" id="new_file_name_{{ loop.index }}" type="hidden" value="">
|
||||
<input type="submit" value="Rename">
|
||||
<input type="submit" value="{{ _("Rename") }}">
|
||||
</form>
|
||||
<form action="/files/delete" method="post" onsubmit="return confirm('Delete file \'{{ file["name"] }}\'?')">
|
||||
<form action="/files/delete" method="post" onsubmit="return confirm('{{ _("Delete file: %(file_name)s?", file_name=file["name"]) }}')">
|
||||
<input name="file_name" type="hidden" value="{{ file['name'] }}">
|
||||
<input type="submit" value="Delete">
|
||||
<input type="submit" value="{{ _("Delete") }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
@ -292,22 +292,22 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p><small>Available disk space on the Pi: {{ free_disk }} MB</small></p>
|
||||
<p><small>{{ _("%(disk_space)s MB disk space remaining on the Pi", disk_space=free_disk) }}</small></p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<details>
|
||||
<summary class="heading">
|
||||
Attach Ethernet Adapter
|
||||
{{ _("Attach Ethernet Adapter") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>Emulates a SCSI DaynaPORT Ethernet Adapter. <a href="https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link#-macintosh-setup-instructions">Host drivers and configuration required</a>.
|
||||
<li>{{ _("Emulates a SCSI DaynaPORT Ethernet Adapter. <a href=\"%(url)s\">Host drivers and configuration required</a>.", url="https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link") }}
|
||||
</li>
|
||||
<li>If you have a DHCP setup, choose only the interface, and ignore the Static IP fields when attaching.</li>
|
||||
<li>Configure network forwarding by running easyinstall.sh, or follow the <a href="https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link#manual-setup">manual steps in the wiki</a>.
|
||||
<li>{{ _("If you have a DHCP setup, choose only the interface you have configured the bridge with. You can ignore the Static IP fields when attaching.") }}</li>
|
||||
<li>{{ _("Configure the network bridge by running easyinstall.sh, or follow the <a href=\"%(url)s\">manual steps in the wiki</a>.", url="https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link#manual-setup") }}
|
||||
</li>
|
||||
<li style="list-style: none">{% if bridge_configured %}</li>
|
||||
<li>The <tt>rascsi_bridge</tt> interface is active and ready to be used by DaynaPORT!</li>
|
||||
<li>{{ _("The <tt>rascsi_bridge</tt> interface is active and ready to be used by DaynaPORT!") }}</li>
|
||||
<li style="list-style: none">{% endif %}</li>
|
||||
</ul>
|
||||
</details>
|
||||
@ -315,7 +315,7 @@
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/daynaport/attach" method="post">
|
||||
<label for="if">Interface:</label>
|
||||
<label for="if">{{ _("Interface:") }}</label>
|
||||
<select name="if">
|
||||
{% for if in netinfo["ifs"] %}
|
||||
<option value="{{ if }}">
|
||||
@ -323,10 +323,10 @@
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="ip">Static IP (optional):</label>
|
||||
<label for="ip">{{ _("Static IP (optional):") }}</label>
|
||||
<input name="ip" type="text" size="15" placeholder="10.10.20.1" minlength="7" maxlength="15" pattern="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$">
|
||||
<input name="mask" type="number" size="2" placeholder="24" min="16" max="30">
|
||||
<label for="scsi_id">SCSI ID:</label>
|
||||
<label for="scsi_id">{{ _("SCSI ID:") }}</label>
|
||||
<select name="scsi_id">
|
||||
{% for id in scsi_ids %}
|
||||
<option value="{{ id }}"{% if id == recommended_id %} selected{% endif %}>
|
||||
@ -334,26 +334,26 @@
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="Attach">
|
||||
<input type="submit" value="{{ _("Attach") }}">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% if macproxy_configured %}
|
||||
<p><small>Macproxy is running at {{ ip_addr }} port 5000</small></p>
|
||||
<p><small>{{ _("Macproxy is running at %(ip_addr)s (default port 5000)", ip_addr=ip_addr) }}</small></p>
|
||||
{% else %}
|
||||
<p><small>Install <a href="https://github.com/akuker/RASCSI/wiki/Vintage-Web-Proxy#macproxy">Macproxy</a> to browse the Web with any vintage browser. It's not just for Macs!</small></p>
|
||||
<p><small>{{ _("Install <a href=\"%(url)s\">Macproxy</a> to browse the Web with any vintage browser. It's not just for Macs!", url="https://github.com/akuker/RASCSI/wiki/Vintage-Web-Proxy#macproxy") }}</small></p>
|
||||
{% endif %}
|
||||
|
||||
<hr/>
|
||||
<details>
|
||||
<summary class="heading">
|
||||
Upload File
|
||||
{{ _("Upload File") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>Uploads file to <tt>{{ base_dir }}</tt>. The largest file size accepted is {{ max_file_size }} MB.</li>
|
||||
<li>For unrecognized file types, try renaming hard drive images to '.hds' and CD-ROM images to '.iso' before uploading.</li>
|
||||
<li>Recognized file types: {{ valid_file_suffix }}</li>
|
||||
<li>{{ _("Uploads file to <tt>%(directory)s</tt>. The largest file size accepted is %(max_file_size)s MB.", directory=base_dir, max_file_size=max_file_size) }}</li>
|
||||
<li>{{ _("For unrecognized file types, try renaming hard drive images to '.hds', CD-ROM images to '.iso', and removable drive images to '.hdr' before uploading.") }}</li>
|
||||
<li>{{ _("Recognized file types: %(valid_file_suffix)s", valid_file_suffix=valid_file_suffix) }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
@ -380,10 +380,10 @@
|
||||
|
||||
<details>
|
||||
<summary class="heading">
|
||||
Download File to Images
|
||||
{{ _("Download File to Images") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>Given a URL, download that file to the <tt>{{ base_dir }}</tt> directory.</li>
|
||||
<li>{{ _("Given a URL, download that file to the <tt>%(directory)s</tt> directory.", directory=base_dir) }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
@ -391,9 +391,9 @@
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/files/download_to_images" method="post">
|
||||
<label for="url">URL:</label>
|
||||
<input name="url" placeholder="URL" required="" type="url">
|
||||
<input type="submit" value="Download" onclick="processNotify('Downloading File to Images...')">
|
||||
<label for="url">{{ _("URL:") }}</label>
|
||||
<input name="url" placeholder="{{ _("URL") }}" required="" type="url">
|
||||
<input type="submit" value="{{ _("Download") }}" onclick="processNotify('{{ _("Downloading File to Images...") }}')">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@ -403,13 +403,12 @@
|
||||
|
||||
<details>
|
||||
<summary class="heading">
|
||||
Download File to AppleShare
|
||||
{{ _("Download File to AppleShare") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>Given a URL, download that file to the <tt>{{ AFP_DIR }}</tt> directory and share it over AFP.</li>
|
||||
<li>Manage the files you download here through AppleShare on your vintage Mac.</li>
|
||||
<li>Requires <a href="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing">Netatalk</a> to be installed and configured correctly for your network.
|
||||
</li>
|
||||
<li>{{ _("Given a URL, download that file to the <tt>%(directory)s</tt> directory and share it over AFP.", directory=AFP_DIR) }}</li>
|
||||
<li>{{ _("Manage the files you download here through AppleShare on your vintage Mac.") }}</li>
|
||||
<li>{{ _("Requires <a href=\"%(url)s\">Netatalk</a> to be installed and configured correctly for your network.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
@ -418,43 +417,42 @@
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/files/download_to_afp" method="post">
|
||||
<label for="url">URL:</label>
|
||||
<input name="url" placeholder="URL" required="" type="url">
|
||||
<input type="submit" value="Download" onclick="processNotify('Downloading File to AppleShare...')">
|
||||
<label for="url">{{ _("URL:") }}</label>
|
||||
<input name="url" placeholder="{{ _("URL") }}" required="" type="url">
|
||||
<input type="submit" value="{{ _("Download") }}" onclick="processNotify('{{ _("Downloading File to AppleShare...") }}')">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{% if netatalk_configured == 1 %}
|
||||
<p><small>The AppleShare server is running. No active connections</small></p>
|
||||
<p><small>{{ _("The AppleShare server is running. No active connections.") }}</small></p>
|
||||
{% elif netatalk_configured == 2 %}
|
||||
<p><small>{{ netatalk_configured - 1 }} active AFP connection</small></p>
|
||||
<p><small>{{ _("%(value)d active AFP connection", value=(netatalk_configured - 1)) }}</small></p>
|
||||
{% elif netatalk_configured > 2 %}
|
||||
<p><small>{{ netatalk_configured - 1 }} active AFP connections</small></p>
|
||||
<p><small>{{ _("%(value)d active AFP connections", value=(netatalk_configured - 1)) }}</small></p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p>Install <a href="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing">Netatalk</a> to use the AppleShare File Server.</p>
|
||||
<p>{{ _("Install <a href=\"%(url)s\">Netatalk</a> to use the AppleShare File Server.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}</p>
|
||||
{% endif %}
|
||||
|
||||
<hr/>
|
||||
|
||||
<details>
|
||||
<summary class="heading">
|
||||
Download File and Create CD-ROM ISO image
|
||||
{{ _("Download File and Create CD-ROM image") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>Given a URL this will download a file, create a CD-ROM image with the selected file system, and mount it on the SCSI ID given.</li>
|
||||
<li>HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX.</li>
|
||||
<li>On Mac OS, requires a <a href="https://github.com/akuker/RASCSI/wiki/Drive-Setup#Mounting_CD_ISO_or_MO_images">compatible CD-ROM driver</a> installed on the target system.
|
||||
</li>
|
||||
<li>If the target file is a zip archive, we will attempt to unzip it and store the resulting files only.</li>
|
||||
<li>{{ _("Create an ISO file system CD-ROM image with the downloaded file, and mount it on the given SCSI ID.") }}</li>
|
||||
<li>{{ _("HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX.") }}</li>
|
||||
<li>{{ _("On Mac OS, a <a href=\"%(url)s\">compatible CD-ROM driver</a> is required.", url="https://github.com/akuker/RASCSI/wiki/Drive-Setup#Mounting_CD_ISO_or_MO_images") }}</li>
|
||||
<li>{{ _("If the downloaded file is a zip archive, we will attempt to unzip it and store the resulting files.") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<label for="scsi_id">SCSI ID:</label>
|
||||
<label for="scsi_id">{{ _("SCSI ID:") }}</label>
|
||||
<form action="/files/download_to_iso" method="post">
|
||||
<select name="scsi_id">
|
||||
{% for id in scsi_ids %}
|
||||
@ -463,9 +461,9 @@
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="url">URL:</label>
|
||||
<input name="url" placeholder="URL" required="" type="url">
|
||||
<label for="type">Type:</label>
|
||||
<label for="url">{{ _("URL:") }}</label>
|
||||
<input name="url" placeholder="{{ _("URL") }}" required="" type="url">
|
||||
<label for="type">{{ _("Type:") }}</label>
|
||||
<select name="type">
|
||||
<option value="-hfs">
|
||||
HFS
|
||||
@ -486,7 +484,7 @@
|
||||
Rock Ridge
|
||||
</option>
|
||||
</select>
|
||||
<input type="submit" value="Download and Mount ISO" onclick="processNotify('Downloading File as ISO...')">
|
||||
<input type="submit" value="{{ _("Download and Mount CD-ROM image") }}" onclick="processNotify('{{ _("Downloading File and generating CD-ROM image...") }}')">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||