Merge pull request #217 from YunoHost/portal-api
SSOwat epic refactoring / Portal API
|
@ -1,59 +0,0 @@
|
||||||
SSOwat contributors
|
|
||||||
===================
|
|
||||||
|
|
||||||
YunoHost is built and maintained by the YunoHost project community.
|
|
||||||
Everyone is encouraged to submit issues and changes, and to contribute in other ways -- see https://yunohost.org/contribute to find out how.
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
SSOwat was initially built by Kload, for YunoHost v2.
|
|
||||||
|
|
||||||
Design was created by Théodore 'Tozz' Faure and Thomas 'Courgette' Lebeau and implemented by Courgette himself.
|
|
||||||
|
|
||||||
Most of code was written by Kload and opi, with help of numerous contributors.
|
|
||||||
|
|
||||||
Translation is made by a bunch of lovely people over the world.
|
|
||||||
|
|
||||||
We would like to thank anyone who ever helped the YunoHost project, and especially the SSOwat project <3
|
|
||||||
|
|
||||||
|
|
||||||
SSOwat Contributors
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
- Kload
|
|
||||||
- opi
|
|
||||||
- Jérôme Lebleu
|
|
||||||
- Maniack Crudelis
|
|
||||||
- Julien 'ju' Malik
|
|
||||||
- M5oul
|
|
||||||
- Alexander Chalikiopoulos
|
|
||||||
- Adrien 'Beudbeud' Beudin
|
|
||||||
- Hnk Reno
|
|
||||||
- Laurent 'Bram' Peuch
|
|
||||||
- Loïc 'dzamlo' Damien
|
|
||||||
- sidddy
|
|
||||||
|
|
||||||
|
|
||||||
SSOwat Translators
|
|
||||||
------------------
|
|
||||||
|
|
||||||
### French
|
|
||||||
|
|
||||||
- Jean-Baptiste Holcroft
|
|
||||||
|
|
||||||
### German
|
|
||||||
|
|
||||||
- Felix Bartels
|
|
||||||
|
|
||||||
### Hindi
|
|
||||||
|
|
||||||
- Anmol
|
|
||||||
|
|
||||||
### Portuguese
|
|
||||||
|
|
||||||
- Deleted User
|
|
||||||
- Trollken
|
|
||||||
|
|
||||||
### Spanish
|
|
||||||
|
|
||||||
- Juanu
|
|
139
README.md
|
@ -7,29 +7,8 @@ A simple LDAP SSO for NGINX, written in Lua.
|
||||||
<img src="https://translate.yunohost.org/widgets/yunohost/-/287x66-white.png" alt="Translation status" />
|
<img src="https://translate.yunohost.org/widgets/yunohost/-/287x66-white.png" alt="Translation status" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
Issues
|
|
||||||
------
|
|
||||||
|
|
||||||
- [Please report issues to the YunoHost bugtracker](https://github.com/YunoHost/issues).
|
- [Please report issues to the YunoHost bugtracker](https://github.com/YunoHost/issues).
|
||||||
|
|
||||||
Requirements
|
|
||||||
------------
|
|
||||||
|
|
||||||
- `nginx-extras` from Debian wheezy-backports
|
|
||||||
- `lua-json`
|
|
||||||
- `lua-ldap`
|
|
||||||
- `lua-filesystem`
|
|
||||||
- `lua-socket`
|
|
||||||
- `lua-rex-pcre`
|
|
||||||
|
|
||||||
**OR**
|
|
||||||
|
|
||||||
- "OpenResty" flavored NGINX: https://openresty.org/
|
|
||||||
- `lua-ldap`
|
|
||||||
- `lua-filesystem`
|
|
||||||
- `lua-socket`
|
|
||||||
- `lua-rex-pcre`
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -74,117 +53,15 @@ If you use YunoHost, you may want to edit the `/etc/ssowat/conf.json.persistent`
|
||||||
|
|
||||||
Only the `portal_domain` SSOwat configuration parameters is required, but it is recommended to know the others to fully understand what you can do with it.
|
Only the `portal_domain` SSOwat configuration parameters is required, but it is recommended to know the others to fully understand what you can do with it.
|
||||||
|
|
||||||
---------------
|
- `cookie_secret_file`: Where the secret used for signing and encrypting cookie is stored. It should only be readable by root.
|
||||||
|
- `cookie_name`: The name of the cookie used for authentication. Its content is expected to be a JWT signed with the cookie secret and should contain a key `user` and `password` (which is needed for Basic HTTP Auth). Because JWT is only encoded and signed (not encrypted), the `password` is expected to be encrypted using the cookie secret.
|
||||||
|
- `portal_domain`: Domain of the authentication portal. It has to be a domain, IP addresses will not work with SSOwat (**Required**).
|
||||||
|
- `portal_path`: URI of the authentication portal (**default**: `/ssowat/`). This path **must** end with “`/`”.
|
||||||
|
- `domains`: List of handled domains (**default**: similar to `portal_domain`).
|
||||||
|
- `redirected_urls`: Array of URLs and/or URIs to redirect and their redirect URI/URL (**example**: `{ "/": "example.org/subpath" }`).
|
||||||
|
- `redirected_regex`: Array of regular expressions to be matched against URLs **and** URIs and their redirect URI/URL (**example**: `{ "example.org/megusta$": "example.org/subpath" }`).
|
||||||
|
|
||||||
### portal_domain
|
### `permissions`
|
||||||
|
|
||||||
Domain of the authentication portal. It has to be a domain, IP addresses will not work with SSOwat (**Required**).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### portal_path
|
|
||||||
|
|
||||||
URI of the authentication portal (**default**: `/ssowat/`). This path **must** end with “`/`”.
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### portal_port
|
|
||||||
|
|
||||||
Web port of the authentication portal (**default**: `443` for `https`, `80` for `http`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### portal_scheme
|
|
||||||
|
|
||||||
Whether authentication should use secure connection or not (**default**: `https`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### domains
|
|
||||||
|
|
||||||
List of handled domains (**default**: similar to `portal_domain`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### ldap_host
|
|
||||||
|
|
||||||
LDAP server hostname (**default**: `localhost`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### ldap_group
|
|
||||||
|
|
||||||
LDAP group to search in (**default**: `ou=users,dc=yunohost,dc=org`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### ldap_identifier
|
|
||||||
|
|
||||||
LDAP user identifier (**default**: `uid`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### ldap_attributes
|
|
||||||
|
|
||||||
User's attributes to fetch from LDAP (**default**: `["uid", "givenname", "sn", "cn", "homedirectory", "mail", "maildrop"]`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### ldap_enforce_crypt
|
|
||||||
|
|
||||||
Let SSOwat re-encrypt weakly-encrypted LDAP passwords into the safer sha-512 (crypt) (**default**: `true`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### allow_mail_authentication
|
|
||||||
|
|
||||||
Whether users can authenticate with their mail address (**default**: `true`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### login_arg
|
|
||||||
|
|
||||||
URI argument to use for cross-domain authentication (**default**: `sso_login`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### additional_headers
|
|
||||||
|
|
||||||
Array of additionnal HTTP headers to set once user is authenticated (**default**: `{ "Remote-User": "uid" }`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### session_timeout
|
|
||||||
|
|
||||||
The session expiracy time limit in seconds, since the last connection (**default**: `86400` / one day).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### session_max_timeout
|
|
||||||
|
|
||||||
The session expiracy time limit in seconds (**default**: `604800` / one week).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### redirected_urls
|
|
||||||
|
|
||||||
Array of URLs and/or URIs to redirect and their redirect URI/URL (**example**: `{ "/": "example.org/subpath" }`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### redirected_regex
|
|
||||||
|
|
||||||
Array of regular expressions to be matched against URLs **and** URIs and their redirect URI/URL (**example**: `{ "example.org/megusta$": "example.org/subpath" }`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### default_language
|
|
||||||
|
|
||||||
Language code used by default in views (**default**: `en`).
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
### permissions
|
|
||||||
|
|
||||||
The list of permissions depicted as follows:
|
The list of permissions depicted as follows:
|
||||||
|
|
||||||
|
|
555
access.lua
|
@ -2,273 +2,155 @@
|
||||||
-- access.lua
|
-- access.lua
|
||||||
--
|
--
|
||||||
-- This file is executed at every request on a protected domain or server.
|
-- This file is executed at every request on a protected domain or server.
|
||||||
-- You just have to read this file normally to understand how and when the
|
|
||||||
-- request is handled: redirected, forbidden, bypassed or served.
|
|
||||||
--
|
--
|
||||||
|
|
||||||
-- Get the `cache` persistent shared table
|
|
||||||
local cache = ngx.shared.cache
|
|
||||||
|
|
||||||
-- Generate a unique token if it has not been generated yet
|
|
||||||
srvkey = cache:get("srvkey")
|
|
||||||
if not srvkey then
|
|
||||||
srvkey = random_string()
|
|
||||||
cache:add("srvkey", srvkey)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Import helpers
|
|
||||||
local hlp = require "helpers"
|
|
||||||
|
|
||||||
-- Initialize and get configuration
|
|
||||||
hlp.refresh_config()
|
|
||||||
local conf = hlp.get_config()
|
|
||||||
|
|
||||||
-- Load logging module
|
|
||||||
local logger = require("log")
|
|
||||||
|
|
||||||
-- Just a note for the client to know that he passed through the SSO
|
-- Just a note for the client to know that he passed through the SSO
|
||||||
ngx.header["X-SSO-WAT"] = "You've just been SSOed"
|
ngx.header["X-SSO-WAT"] = "You've just been SSOed"
|
||||||
|
|
||||||
local is_logged_in = hlp.refresh_logged_in()
|
-- Misc imports
|
||||||
|
local jwt = require("vendor.luajwtjitsi.luajwtjitsi")
|
||||||
|
local cipher = require('openssl.cipher')
|
||||||
|
local rex = require("rex_pcre2")
|
||||||
|
|
||||||
--
|
-- ###########################################################################
|
||||||
-- 1. LOGIN
|
-- 0. Misc helpers because Lua has no sugar ...
|
||||||
--
|
-- ###########################################################################
|
||||||
-- example: https://mydomain.org/?sso_login=a6e5320f
|
|
||||||
--
|
|
||||||
-- If the `sso_login` URI argument is set, try a cross-domain authentication
|
|
||||||
-- with the token passed as argument
|
|
||||||
--
|
|
||||||
if ngx.var.host ~= conf["portal_domain"] and ngx.var.request_method == "GET" then
|
|
||||||
uri_args = ngx.req.get_uri_args()
|
|
||||||
if uri_args[conf.login_arg] then
|
|
||||||
cda_key = uri_args[conf.login_arg]
|
|
||||||
|
|
||||||
-- Use the `cache` shared table where a username is associated with
|
-- Get configuration (we do this here, the conf is re-read every time unless
|
||||||
-- a CDA key
|
-- the file's timestamp didnt change)
|
||||||
user = cache:get("CDA|"..cda_key)
|
local config = require("config")
|
||||||
if user then
|
local conf = config.get_config()
|
||||||
hlp.set_auth_cookie(user, ngx.var.host)
|
|
||||||
logger.info("Cross-domain authentication: "..user.." connected on "..ngx.var.host)
|
-- Cache expensive calculations
|
||||||
cache:delete("CDA|"..cda_key)
|
local cache = ngx.shared.cache
|
||||||
|
|
||||||
|
-- Hash a string using hmac_sha512, return a hexa string
|
||||||
|
function cached_jwt_verify(data, secret)
|
||||||
|
res = cache:get(data)
|
||||||
|
if res == nil then
|
||||||
|
logger:debug("Result not found in cache, checking login")
|
||||||
|
-- Perform expensive calculation
|
||||||
|
decoded, err = jwt.verify(data, "HS256", cookie_secret)
|
||||||
|
if not decoded then
|
||||||
|
logger:error(err)
|
||||||
|
return nil, nil, err
|
||||||
end
|
end
|
||||||
|
-- As explained in set_basic_auth_header(), user and hashed password do not contain ':'
|
||||||
uri_args[conf.login_arg] = nil
|
-- And cache cannot contain tables, so we use "user:password" format
|
||||||
return hlp.redirect(ngx.var.uri..hlp.uri_args_string(uri_args))
|
cached = decoded["user"]..":"..decoded["pwd"]
|
||||||
end
|
cache:set(data, cached, 120)
|
||||||
end
|
logger:debug("Result saved in cache")
|
||||||
|
return decoded["user"], decoded["pwd"], err
|
||||||
|
|
||||||
--
|
|
||||||
-- 2. PORTAL
|
|
||||||
--
|
|
||||||
-- example: https://mydomain.org/ssowat*
|
|
||||||
--
|
|
||||||
-- If the URL matches the portal URL, serve a portal file or proceed to a
|
|
||||||
-- portal operation
|
|
||||||
--
|
|
||||||
if (ngx.var.host == conf["portal_domain"] or is_logged_in)
|
|
||||||
and hlp.string.starts(ngx.var.uri, string.sub(conf["portal_path"], 1, -2))
|
|
||||||
then
|
|
||||||
|
|
||||||
-- `GET` method will serve a portal file
|
|
||||||
if ngx.var.request_method == "GET" then
|
|
||||||
|
|
||||||
-- Force portal scheme
|
|
||||||
if ngx.var.scheme ~= conf["portal_scheme"] then
|
|
||||||
return hlp.redirect(conf.portal_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add a trailing `/` if not present
|
|
||||||
if ngx.var.uri.."/" == conf["portal_path"] then
|
|
||||||
return hlp.redirect(conf.portal_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get request arguments
|
|
||||||
uri_args = ngx.req.get_uri_args()
|
|
||||||
|
|
||||||
-- Logout is also called via a `GET` method
|
|
||||||
-- TODO: change this ?
|
|
||||||
if uri_args.action and uri_args.action == 'logout' then
|
|
||||||
logger.debug("Logging out")
|
|
||||||
return hlp.logout()
|
|
||||||
|
|
||||||
-- If the `r` URI argument is set, it means that we want to
|
|
||||||
-- be redirected (typically after a login phase)
|
|
||||||
elseif is_logged_in and uri_args.r then
|
|
||||||
-- Decode back url
|
|
||||||
back_url = ngx.decode_base64(uri_args.r)
|
|
||||||
|
|
||||||
-- If `back_url` contains line break, someone is probably trying to
|
|
||||||
-- pass some additional headers
|
|
||||||
if string.match(back_url, "(.*)\n") then
|
|
||||||
hlp.flash("fail", hlp.t("redirection_error_invalid_url"))
|
|
||||||
logger.error("Redirection url is invalid")
|
|
||||||
return hlp.redirect(conf.portal_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get managed domains
|
|
||||||
local managed_domain = false
|
|
||||||
for _, domain in ipairs(conf["domains"]) do
|
|
||||||
local escaped_domain = domain:gsub("-", "%%-") -- escape dash for pattern matching
|
|
||||||
if string.match(back_url, "^http[s]?://"..escaped_domain.."/") then
|
|
||||||
logger.debug("Redirection to a managed domain found")
|
|
||||||
managed_domain = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If redirection does not match one of the managed domains
|
|
||||||
-- redirect to portal home page
|
|
||||||
if not managed_domain then
|
|
||||||
hlp.flash("fail", hlp.t("redirection_error_unmanaged_domain"))
|
|
||||||
logger.error("Redirection to an external domain aborted")
|
|
||||||
return hlp.redirect(conf.portal_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- In case the `back_url` is not on the same domain than the
|
|
||||||
-- current one, create a redirection with a CDA key
|
|
||||||
local ngx_host_escaped = ngx.var.host:gsub("-", "%%-") -- escape dash for pattern matching
|
|
||||||
if not string.match(back_url, "^http[s]?://"..ngx_host_escaped.."/")
|
|
||||||
and not string.match(back_url, ".*"..conf.login_arg.."=%d+$") then
|
|
||||||
local cda_key = hlp.set_cda_key()
|
|
||||||
if string.match(back_url, ".*?.*") then
|
|
||||||
back_url = back_url.."&"
|
|
||||||
else
|
|
||||||
back_url = back_url.."?"
|
|
||||||
end
|
|
||||||
back_url = back_url.."sso_login="..cda_key
|
|
||||||
end
|
|
||||||
|
|
||||||
return hlp.redirect(back_url)
|
|
||||||
|
|
||||||
|
|
||||||
-- In case we want to serve portal login or assets for portal, just
|
|
||||||
-- serve it
|
|
||||||
elseif is_logged_in
|
|
||||||
or ngx.var.uri == conf["portal_path"]
|
|
||||||
or (hlp.string.starts(ngx.var.uri, conf["portal_path"].."assets")
|
|
||||||
and (not ngx.var.http_referer
|
|
||||||
or hlp.string.starts(ngx.var.http_referer, conf.portal_url)))
|
|
||||||
then
|
|
||||||
-- If this is an asset, enable caching
|
|
||||||
if hlp.string.starts(ngx.var.uri, conf["portal_path"].."assets")
|
|
||||||
then
|
|
||||||
return hlp.serve(ngx.var.uri, "static_asset")
|
|
||||||
else
|
|
||||||
return hlp.serve(ngx.var.uri)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- If all the previous cases have failed, redirect to portal
|
|
||||||
else
|
|
||||||
hlp.flash("info", hlp.t("please_login"))
|
|
||||||
logger.debug("User should log in to be able to access "..ngx.var.uri)
|
|
||||||
-- Force the scheme to HTTPS. This is to avoid an issue with redirection loop
|
|
||||||
-- when trying to access http://main.domain.tld/ (SSOwat finds that user aint
|
|
||||||
-- logged in, therefore redirects to SSO, which redirects to the back_url, which
|
|
||||||
-- redirect to SSO, ..)
|
|
||||||
local back_url = "https://" .. ngx.var.host .. ngx.var.uri .. hlp.uri_args_string()
|
|
||||||
return hlp.redirect(conf.portal_url.."?r="..ngx.encode_base64(back_url))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- `POST` method is basically use to achieve editing operations
|
|
||||||
elseif ngx.var.request_method == "POST" then
|
|
||||||
|
|
||||||
-- CSRF protection, only proceed if we are editing from the same
|
|
||||||
-- domain
|
|
||||||
if hlp.string.starts(ngx.var.http_referer, conf.portal_url) then
|
|
||||||
if hlp.string.ends(ngx.var.uri, conf["portal_path"].."password.html")
|
|
||||||
or hlp.string.ends(ngx.var.uri, conf["portal_path"].."edit.html")
|
|
||||||
then
|
|
||||||
logger.debug("User attempts to edit its information")
|
|
||||||
return hlp.edit_user()
|
|
||||||
else
|
|
||||||
logger.debug("User attempts to log in")
|
|
||||||
return hlp.login()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Redirect to portal
|
|
||||||
hlp.flash("fail", hlp.t("please_login_from_portal"))
|
|
||||||
logger.debug("Invalid POST request not coming from the portal url...")
|
|
||||||
return hlp.redirect(conf.portal_url)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- 2 ... continued : portal assets that are available on every domains
|
|
||||||
--
|
|
||||||
-- For example: `https://whatever.org/ynhpanel.js` will serve the
|
|
||||||
-- `/yunohost/sso/assets/js/ynhpanel.js` file.
|
|
||||||
--
|
|
||||||
|
|
||||||
if is_logged_in then
|
|
||||||
assets = {
|
|
||||||
["/ynh_portal.js"] = "js/ynh_portal.js",
|
|
||||||
["/ynh_userinfo.json"] = "ynh_userinfo.json",
|
|
||||||
["/ynh_overlay.css"] = "css/ynh_overlay.css"
|
|
||||||
}
|
|
||||||
theme_dir = "/usr/share/ssowat/portal/assets/themes/"..conf.theme
|
|
||||||
local pfile = io.popen('find "'..theme_dir..'" -not -path "*/\\.*" -type f -exec realpath --relative-to "'..theme_dir..'" {} \\;')
|
|
||||||
for filename in pfile:lines() do
|
|
||||||
assets["/ynhtheme/"..filename] = "themes/"..conf.theme.."/"..filename
|
|
||||||
end
|
|
||||||
pfile:close()
|
|
||||||
|
|
||||||
for shortcut, full in pairs(assets) do
|
|
||||||
if ngx.var.uri == shortcut then
|
|
||||||
logger.debug("Serving static asset "..full)
|
|
||||||
return hlp.serve("/yunohost/sso/assets/"..full, "static_asset")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- 3. REDIRECTED URLS
|
|
||||||
--
|
|
||||||
-- If the URL matches one of the `redirected_urls` in the configuration file,
|
|
||||||
-- just redirect to the target URL/URI
|
|
||||||
--
|
|
||||||
|
|
||||||
function detect_redirection(redirect_url)
|
|
||||||
if hlp.string.starts(redirect_url, "http://")
|
|
||||||
or hlp.string.starts(redirect_url, "https://") then
|
|
||||||
return hlp.redirect(redirect_url)
|
|
||||||
elseif hlp.string.starts(redirect_url, "/") then
|
|
||||||
return hlp.redirect(ngx.var.scheme.."://"..ngx.var.host..redirect_url)
|
|
||||||
else
|
else
|
||||||
return hlp.redirect(ngx.var.scheme.."://"..redirect_url)
|
logger:debug("Result found in cache")
|
||||||
|
user, pwd = res:match("([^:]+):(.*)")
|
||||||
|
return user, pwd, nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The 'match' function uses PCRE regex as default
|
||||||
|
-- If '%.' is found in the regex, we assume it's a LUA regex (legacy code)
|
||||||
|
-- 'match' returns the matched text.
|
||||||
|
function match(s, regex)
|
||||||
|
if not string.find(regex, '%%%.') then
|
||||||
|
return rex.match(s, regex)
|
||||||
|
else
|
||||||
|
return string.match(s,regex)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Test whether a string starts with another
|
||||||
|
function string.starts(String, Start)
|
||||||
|
if not String then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return string.sub(String, 1, string.len(Start)) == Start
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Convert a table of arguments to an URI string
|
||||||
|
function uri_args_string(args)
|
||||||
|
if not args then
|
||||||
|
args = ngx.req.get_uri_args()
|
||||||
|
end
|
||||||
|
String = "?"
|
||||||
|
for k,v in pairs(args) do
|
||||||
|
String = String..tostring(k).."="..tostring(v).."&"
|
||||||
|
end
|
||||||
|
return string.sub(String, 1, string.len(String) - 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ###########################################################################
|
||||||
|
-- 1. AUTHENTICATION
|
||||||
|
-- Check wether or not this is a logged-in user
|
||||||
|
-- This is not run immediately but only if:
|
||||||
|
-- - the app is not public
|
||||||
|
-- - and/or auth_headers is enabled for this app
|
||||||
|
-- ###########################################################################
|
||||||
|
|
||||||
|
function check_authentication()
|
||||||
|
|
||||||
|
-- cf. src/authenticators/ldap_ynhuser.py in YunoHost to see how the cookie is actually created
|
||||||
|
|
||||||
|
local cookie = ngx.var["cookie_" .. conf["cookie_name"]]
|
||||||
|
if cookie == nil then
|
||||||
|
return false, nil, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
user, pwd, err = cached_jwt_verify(cookie, cookie_secret)
|
||||||
|
|
||||||
|
-- FIXME : maybe also check that the cookie was delivered for the requested domain (or a parent?)
|
||||||
|
|
||||||
|
-- FIXME : we might want also a way to identify expired/invalidated cookies,
|
||||||
|
-- e.g. a user that got deleted after being logged in, or a user that logged out ...
|
||||||
|
|
||||||
|
if err ~= nil then
|
||||||
|
return false, nil, nil
|
||||||
|
else
|
||||||
|
return true, user, pwd
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ###########################################################################
|
||||||
|
-- 2. REDIRECTED URLS
|
||||||
|
-- If the URL matches one of the `redirected_urls` in the configuration file,
|
||||||
|
-- just redirect to the target URL/URI
|
||||||
|
-- ###########################################################################
|
||||||
|
|
||||||
|
function convert_to_absolute_url(redirect_url)
|
||||||
|
if string.starts(redirect_url, "http://")
|
||||||
|
or string.starts(redirect_url, "https://") then
|
||||||
|
return redirect_url
|
||||||
|
elseif string.starts(redirect_url, "/") then
|
||||||
|
return ngx.var.scheme.."://"..ngx.var.host..redirect_url
|
||||||
|
else
|
||||||
|
return ngx.var.scheme.."://"..redirect_url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if conf["redirected_urls"] then
|
if conf["redirected_urls"] then
|
||||||
for url, redirect_url in pairs(conf["redirected_urls"]) do
|
for url, redirect_url in pairs(conf["redirected_urls"]) do
|
||||||
if url == ngx.var.host..ngx.var.uri..hlp.uri_args_string()
|
if url == ngx.var.host..ngx.var.uri..uri_args_string()
|
||||||
or url == ngx.var.scheme.."://"..ngx.var.host..ngx.var.uri..hlp.uri_args_string()
|
or url == ngx.var.scheme.."://"..ngx.var.host..ngx.var.uri..uri_args_string()
|
||||||
or url == ngx.var.uri..hlp.uri_args_string() then
|
or url == ngx.var.uri..uri_args_string() then
|
||||||
logger.debug("Requested URI is in redirected_urls")
|
logger:debug("Found in redirected_urls, redirecting to "..url)
|
||||||
detect_redirection(redirect_url)
|
ngx.redirect(convert_to_absolute_url(redirect_url))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if conf["redirected_regex"] then
|
if conf["redirected_regex"] then
|
||||||
for regex, redirect_url in pairs(conf["redirected_regex"]) do
|
for regex, redirect_url in pairs(conf["redirected_regex"]) do
|
||||||
if hlp.match(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex)
|
if match(ngx.var.host..ngx.var.uri..uri_args_string(), regex)
|
||||||
or hlp.match(ngx.var.scheme.."://"..ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex)
|
or match(ngx.var.scheme.."://"..ngx.var.host..ngx.var.uri..uri_args_string(), regex)
|
||||||
or hlp.match(ngx.var.uri..hlp.uri_args_string(), regex) then
|
or match(ngx.var.uri..uri_args_string(), regex) then
|
||||||
logger.debug("Requested URI is in redirected_regex")
|
logger:debug("Found in redirected_regex, redirecting to "..url)
|
||||||
detect_redirection(redirect_url)
|
ngx.redirect(convert_to_absolute_url(redirect_url))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
-- ###########################################################################
|
||||||
-- 4. IDENTIFY THE RELEVANT PERMISSION
|
-- 3. IDENTIFY PERMISSION MATCHING THE REQUESTED URL
|
||||||
--
|
--
|
||||||
-- In particular, the conf is filled with permissions such as:
|
-- In particular, the conf is filled with permissions such as:
|
||||||
--
|
--
|
||||||
|
@ -287,7 +169,7 @@ end
|
||||||
--
|
--
|
||||||
-- And we find the best matching permission by trying to match the request uri
|
-- And we find the best matching permission by trying to match the request uri
|
||||||
-- against all the uris rules/regexes from the conf and keep the longest matching one.
|
-- against all the uris rules/regexes from the conf and keep the longest matching one.
|
||||||
--
|
-- ###########################################################################
|
||||||
|
|
||||||
permission = nil
|
permission = nil
|
||||||
longest_url_match = ""
|
longest_url_match = ""
|
||||||
|
@ -305,7 +187,7 @@ for permission_name, permission_infos in pairs(conf["permissions"]) do
|
||||||
url = "^"..url
|
url = "^"..url
|
||||||
end
|
end
|
||||||
|
|
||||||
local m = hlp.match(ngx_full_url, url)
|
local m = match(ngx_full_url, url)
|
||||||
if m ~= nil and string.len(m) > string.len(longest_url_match) then
|
if m ~= nil and string.len(m) > string.len(longest_url_match) then
|
||||||
longest_url_match = m
|
longest_url_match = m
|
||||||
permission = permission_infos
|
permission = permission_infos
|
||||||
|
@ -315,65 +197,168 @@ for permission_name, permission_infos in pairs(conf["permissions"]) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- ###########################################################################
|
||||||
|
-- 4. CHECK USER HAS ACCESS
|
||||||
|
-- Either because the permission is set as "public: true",
|
||||||
|
-- Or because the logged-in user is listed in the "users" list of the perm
|
||||||
|
-- ###########################################################################
|
||||||
|
|
||||||
---
|
function element_is_in_table(element, table)
|
||||||
--- 5. CHECK CLIENT-PROVIDED AUTH HEADER (should almost never happen?)
|
if table then
|
||||||
---
|
for _, el in pairs(table) do
|
||||||
|
if el == element then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if permission ~= nil then
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check whether the app is public access
|
||||||
|
function check_public_access(permission)
|
||||||
|
if permission == nil then
|
||||||
|
logger:debug("No permission matching request for "..ngx.var.uri.." ... Assuming access is denied")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if permission["public"] then
|
||||||
|
logger:debug("Someone tries to access "..ngx.var.uri.." (corresponding perm: "..permission["id"]..")")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check whether a user is allowed to access a URL using the `permissions` directive
|
||||||
|
-- of the configuration file
|
||||||
|
function check_has_access(permission)
|
||||||
|
|
||||||
|
-- Public access
|
||||||
|
if authUser == nil or permission["public"] then
|
||||||
|
user = authUser or "A visitor"
|
||||||
|
logger:debug(user.." tries to access "..ngx.var.uri.." (corresponding perm: "..permission["id"]..")")
|
||||||
|
return permission["public"]
|
||||||
|
end
|
||||||
|
|
||||||
|
logger:debug("User "..authUser.." tries to access "..ngx.var.uri.." (corresponding perm: "..permission["id"]..")")
|
||||||
|
|
||||||
|
-- The user has permission to access the content if he is in the list of allowed users
|
||||||
|
if element_is_in_table(authUser, permission["users"]) then
|
||||||
|
logger:debug("User "..authUser.." can access "..ngx.var.host..ngx.var.uri..uri_args_string())
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
logger:debug("User "..authUser.." cannot access "..ngx.var.uri)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if check_public_access(permission) then
|
||||||
|
has_access = true
|
||||||
|
else
|
||||||
|
is_logged_in, authUser, authPasswordEnc = check_authentication()
|
||||||
|
has_access = check_has_access(permission)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ###########################################################################
|
||||||
|
-- 5. CLEAR USER-PROVIDED AUTH HEADER
|
||||||
|
--
|
||||||
|
-- Which could be spoofing attempts
|
||||||
|
-- Unfortunately we can't yolo-clear them on every route because some
|
||||||
|
-- apps use legit basic auth mechanism ...
|
||||||
|
--
|
||||||
|
-- "Remote user" refers to the fact that Basic Auth headers is coupled to
|
||||||
|
-- the $remote_user var in nginx, typically used by PHP apps
|
||||||
|
-- ###########################################################################
|
||||||
|
|
||||||
|
if permission ~= nil and ngx.req.get_headers()["Authorization"] ~= nil then
|
||||||
perm_user_remote_user_var_in_nginx_conf = permission["use_remote_user_var_in_nginx_conf"]
|
perm_user_remote_user_var_in_nginx_conf = permission["use_remote_user_var_in_nginx_conf"]
|
||||||
if perm_user_remote_user_var_in_nginx_conf == nil or perm_user_remote_user_var_in_nginx_conf == true then
|
if perm_user_remote_user_var_in_nginx_conf == nil or perm_user_remote_user_var_in_nginx_conf == true then
|
||||||
is_logged_in_with_basic_auth = hlp.validate_or_clear_basic_auth_header_provided_by_client()
|
-- Ignore if not a Basic auth header
|
||||||
|
-- otherwise, we interpret this as a Auth header spoofing attempt and clear it
|
||||||
-- NB: is_logged_in_with_basic_auth can be false, true or nil
|
local auth_header_from_client = ngx.req.get_headers()["Authorization"]
|
||||||
if is_logged_in_with_basic_auth == false then
|
_, _, b64_cred = string.find(auth_header_from_client, "^Basic%s+(.+)$")
|
||||||
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
|
if b64_cred ~= nil then
|
||||||
elseif is_logged_in_with_basic_auth == true then
|
ngx.req.clear_header("Authorization")
|
||||||
is_logged_in = true
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- ###########################################################################
|
||||||
|
-- 6. EFFECTIVELY PASS OR DENY ACCESS
|
||||||
--
|
--
|
||||||
|
-- If the user has access (either because app is public OR logged in + authorized)
|
||||||
|
-- -> pass + possibly inject the Basic Auth header on the fly such that the app can know which user is logged in
|
||||||
--
|
--
|
||||||
-- 6. APPLY PERMISSION
|
-- Otherwise, the user can't access
|
||||||
--
|
-- -> either because not logged in at all, in that case, redirect to the portal WITH a callback url to redirect to after logging in
|
||||||
--
|
-- -> or because user is logged in, but has no access .. in that case just redirect to the portal
|
||||||
|
-- ###########################################################################
|
||||||
|
|
||||||
|
function set_basic_auth_header()
|
||||||
|
|
||||||
|
-- cf. https://en.wikipedia.org/wiki/Basic_access_authentication
|
||||||
|
|
||||||
|
-- authPasswordEnc is actually a string formatted as <password_enc_b64>|<iv_b64>
|
||||||
|
-- For example: ctl8kk5GevYdaA5VZ2S88Q==|yTAzCx0Gd1+MCit4EQl9lA==
|
||||||
|
-- The password is encoded using AES-256-CBC with the IV being the right-side data
|
||||||
|
-- cf. src/authenticators/ldap_ynhuser.py in YunoHost to see how the cookie is actually created
|
||||||
|
local password_enc_b64, iv_b64 = authPasswordEnc:match("([^|]+)|([^|]+)")
|
||||||
|
local password_enc = ngx.decode_base64(password_enc_b64)
|
||||||
|
local iv = ngx.decode_base64(iv_b64)
|
||||||
|
local password = cipher.new('aes-256-cbc'):decrypt(cookie_secret, iv):final(password_enc)
|
||||||
|
|
||||||
|
-- Set `Authorization` header to enable HTTP authentification
|
||||||
|
ngx.req.set_header("Authorization", "Basic "..ngx.encode_base64(
|
||||||
|
authUser..":"..password
|
||||||
|
))
|
||||||
|
end
|
||||||
|
|
||||||
-- 1st case : client has access
|
-- 1st case : client has access
|
||||||
|
if has_access then
|
||||||
if hlp.has_access(permission) then
|
-- If Basic Authorization header are enable for this permission,
|
||||||
|
-- check if the user is actually logged in...
|
||||||
if is_logged_in then
|
if permission["auth_header"] then
|
||||||
-- If the user is logged in, refresh_cache
|
if is_logged_in == nil then
|
||||||
hlp.refresh_user_cache()
|
-- Login check was not performed yet because the app is public
|
||||||
|
logger:debug("Checking authentication because the app requires auth_header")
|
||||||
-- If Basic Authorization header are enable for this permission,
|
is_logged_in, authUser, authPasswordEnc = check_authentication()
|
||||||
-- add it to the response
|
end
|
||||||
if permission["auth_header"] then
|
if is_logged_in then
|
||||||
hlp.set_headers()
|
-- add it to the response
|
||||||
else
|
set_basic_auth_header()
|
||||||
hlp.clear_headers()
|
|
||||||
end
|
end
|
||||||
else
|
|
||||||
hlp.clear_headers()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return hlp.pass()
|
-- Pass
|
||||||
|
logger:debug("Allowing to pass through "..ngx.var.uri)
|
||||||
|
return
|
||||||
|
|
||||||
-- 2nd case : no access ... redirect to portal / login form
|
-- 2nd case : no access ... redirect to portal / login form
|
||||||
else
|
else
|
||||||
|
|
||||||
if is_logged_in then
|
portal_domain = conf["domain_portal_urls"][ngx.var.host]
|
||||||
return hlp.redirect(conf.portal_url)
|
if portal_domain == nil then
|
||||||
else
|
logger:debug("Domain " .. ngx.var.host .. " is not configured for SSOWat")
|
||||||
-- Only display this if HTTPS. For HTTP, we can't know if the user really is
|
ngx.status = 400
|
||||||
-- logged in or not, because the cookie is available only in HTTP...
|
ngx.header.content_type = "plain/text"
|
||||||
if ngx.var.scheme == "https" then
|
ngx.say("Unmanaged domain: " .. ngx.var.host)
|
||||||
hlp.flash("info", hlp.t("please_login"))
|
return
|
||||||
end
|
end
|
||||||
|
portal_url = "https://" .. portal_domain
|
||||||
|
logger:debug("Redirecting to portal : " .. portal_url)
|
||||||
|
|
||||||
local back_url = "https://" .. ngx.var.host .. ngx.var.uri .. hlp.uri_args_string()
|
if is_logged_in then
|
||||||
return hlp.redirect(conf.portal_url.."?r="..ngx.encode_base64(back_url))
|
return ngx.redirect(portal_url)
|
||||||
|
else
|
||||||
|
local back_url = "https://" .. ngx.var.host .. ngx.var.uri .. uri_args_string()
|
||||||
|
|
||||||
|
-- User ain't logged in, redirect to the portal where we expect the user to login,
|
||||||
|
-- then be redirected to the original URL by the portal, encoded as base64
|
||||||
|
--
|
||||||
|
-- NB. for security reason, the client/app handling the callback should check
|
||||||
|
-- that the back URL is legit, i.e it should be on the same domain (or a subdomain)
|
||||||
|
-- than the portal. Otherwise, a malicious actor could create a deceptive link
|
||||||
|
-- that would in fact redirect to a different domain, tricking the user that may
|
||||||
|
-- not realize this.
|
||||||
|
return ngx.redirect(portal_url.."?r="..ngx.encode_base64(back_url))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"additional_headers": {
|
"domain_portal_urls": [
|
||||||
"Auth-User": "uid",
|
"example.tld": "example.tld/yunohost/sso",
|
||||||
"Email": "mail",
|
"sub.example.tld": "example.tld/yunohost/sso",
|
||||||
"Name": "cn",
|
"foobar.org": "foobar.org/yunohost/sso"
|
||||||
"Remote-User": "uid"
|
|
||||||
},
|
|
||||||
"domains": [
|
|
||||||
"example.tld",
|
|
||||||
"example.org"
|
|
||||||
],
|
],
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"core_skipped": {
|
"core_skipped": {
|
||||||
|
@ -60,10 +55,8 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"portal_domain": "example.tld",
|
|
||||||
"portal_path": "/yunohost/sso/",
|
|
||||||
"redirected_regex": {
|
"redirected_regex": {
|
||||||
"example.tld/yunohost[\\/]?$": "https://example.tld/yunohost/sso/"
|
"example.tld/yunohost[\\/]?$": "https://example.tld/yunohost/sso/"
|
||||||
},
|
},
|
||||||
"redirected_urls": {}
|
"redirected_urls": {}
|
||||||
}
|
}
|
||||||
|
|
82
config.lua
|
@ -6,11 +6,31 @@
|
||||||
|
|
||||||
module('config', package.seeall)
|
module('config', package.seeall)
|
||||||
|
|
||||||
|
local lfs = require("lfs")
|
||||||
|
local json = require("json")
|
||||||
|
|
||||||
local config_attributes = nil
|
local config_attributes = nil
|
||||||
local config_persistent_attributes = nil
|
local config_persistent_attributes = nil
|
||||||
|
|
||||||
local conf = {}
|
local conf = {}
|
||||||
|
|
||||||
|
local conf_path = "/etc/ssowat/conf.json"
|
||||||
|
|
||||||
|
|
||||||
|
function get_cookie_secret()
|
||||||
|
|
||||||
|
local conf_file = assert(io.open(conf_path, "r"), "Configuration file is missing")
|
||||||
|
local conf_ = json.decode(conf_file:read("*all"))
|
||||||
|
conf_file:close()
|
||||||
|
|
||||||
|
local cookie_secret_path = conf_["cookie_secret_file"] or "/etc/yunohost/.ssowat_cookie_secret"
|
||||||
|
local cookie_secret_file = assert(io.open(cookie_secret_path, "r"), "Cookie secret file is missing")
|
||||||
|
local cookie_secret = cookie_secret_file:read("*all")
|
||||||
|
cookie_secret_file:close()
|
||||||
|
|
||||||
|
return cookie_secret
|
||||||
|
end
|
||||||
|
|
||||||
function compare_attributes(file_attributes1, file_attributes2)
|
function compare_attributes(file_attributes1, file_attributes2)
|
||||||
if file_attributes1 == nil and file_attributes2 == nil then
|
if file_attributes1 == nil and file_attributes2 == nil then
|
||||||
return true
|
return true
|
||||||
|
@ -20,15 +40,6 @@ function compare_attributes(file_attributes1, file_attributes2)
|
||||||
return file_attributes1["modification"] == file_attributes2["modification"] and file_attributes1["size"] == file_attributes2["size"]
|
return file_attributes1["modification"] == file_attributes2["modification"] and file_attributes1["size"] == file_attributes2["size"]
|
||||||
end
|
end
|
||||||
|
|
||||||
function update_language()
|
|
||||||
-- Set the prefered language from the `Accept-Language` header
|
|
||||||
conf.lang = ngx.req.get_headers()["Accept-Language"]
|
|
||||||
|
|
||||||
if conf.lang then
|
|
||||||
conf.lang = string.sub(conf.lang, 1, 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_config()
|
function get_config()
|
||||||
|
|
||||||
-- Get config files attributes (timestamp modification and size)
|
-- Get config files attributes (timestamp modification and size)
|
||||||
|
@ -36,11 +47,9 @@ function get_config()
|
||||||
local new_config_persistent_attributes = lfs.attributes(conf_path..".persistent", {"modification", "size"})
|
local new_config_persistent_attributes = lfs.attributes(conf_path..".persistent", {"modification", "size"})
|
||||||
|
|
||||||
if compare_attributes(new_config_attributes, config_attributes) and compare_attributes(new_config_persistent_attributes, config_persistent_attributes) then
|
if compare_attributes(new_config_attributes, config_attributes) and compare_attributes(new_config_persistent_attributes, config_persistent_attributes) then
|
||||||
update_language()
|
|
||||||
return conf
|
return conf
|
||||||
-- If the file is being written, its size may be 0 and reloading fails, return the last valid config
|
-- If the file is being written, its size may be 0 and reloading fails, return the last valid config
|
||||||
elseif new_config_attributes == nil or new_config_attributes["size"] == 0 then
|
elseif new_config_attributes == nil or new_config_attributes["size"] == 0 then
|
||||||
update_language()
|
|
||||||
return conf
|
return conf
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -78,55 +87,10 @@ function get_config()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Always skip the portal urls to avoid redirection looping.
|
||||||
-- Default configuration values
|
for domain, portal_url in pairs(conf["domain_portal_urls"]) do
|
||||||
default_conf = {
|
table.insert(conf["permissions"]["core_skipped"]["uris"], portal_url)
|
||||||
portal_scheme = "https",
|
|
||||||
portal_path = "/ssowat/",
|
|
||||||
local_portal_domain = "yunohost.local",
|
|
||||||
domains = { conf["portal_domain"], "yunohost.local" },
|
|
||||||
session_timeout = 60 * 60 * 24, -- one day
|
|
||||||
session_max_timeout = 60 * 60 * 24 * 7, -- one week
|
|
||||||
login_arg = "sso_login",
|
|
||||||
ldap_host = "localhost",
|
|
||||||
ldap_group = "ou=users,dc=yunohost,dc=org",
|
|
||||||
ldap_identifier = "uid",
|
|
||||||
ldap_enforce_crypt = true,
|
|
||||||
skipped_urls = {},
|
|
||||||
ldap_attributes = {"uid", "givenname", "sn", "cn", "homedirectory", "mail", "maildrop"},
|
|
||||||
allow_mail_authentication = true,
|
|
||||||
default_language = "en",
|
|
||||||
theme = "default",
|
|
||||||
logging = "fatal", -- Only log fatal messages by default (so apriori nothing)
|
|
||||||
permissions = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- Load default values unless they are set in the configuration file.
|
|
||||||
for param, default_value in pairs(default_conf) do
|
|
||||||
conf[param] = conf[param] or default_value
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- If you access the SSO by a local domain, change the portal domain to
|
|
||||||
-- avoid unwanted redirections.
|
|
||||||
if ngx.var.host == conf["local_portal_domain"] then
|
|
||||||
conf["original_portal_domain"] = conf["portal_domain"]
|
|
||||||
conf["portal_domain"] = conf["local_portal_domain"]
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Build portal full URL out of the configuration values
|
|
||||||
conf.portal_url = conf["portal_scheme"].."://"..
|
|
||||||
conf["portal_domain"]..
|
|
||||||
conf["portal_path"]
|
|
||||||
|
|
||||||
|
|
||||||
-- Always skip the portal to avoid redirection looping.
|
|
||||||
table.insert(conf["permissions"]["core_skipped"]["uris"], conf["portal_domain"]..conf["portal_path"])
|
|
||||||
|
|
||||||
update_language()
|
|
||||||
|
|
||||||
return conf
|
return conf
|
||||||
end
|
end
|
||||||
|
|
2
debian/control
vendored
|
@ -7,7 +7,7 @@ Standards-Version: 3.9.1
|
||||||
|
|
||||||
Package: ssowat
|
Package: ssowat
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: nginx-extras (>=1.6.2), lua-ldap (>=1.3.1), lua-json, lua-rex-pcre2, lua-filesystem, lua-socket, whois
|
Depends: nginx-extras (>=1.6.2), lua-json, lua-rex-pcre2, lua-basexx, lua-luaossl, lua-logging, whois
|
||||||
Homepage: https://yunohost.org
|
Homepage: https://yunohost.org
|
||||||
Description: user portal with single sign-on designed for Yunohost
|
Description: user portal with single sign-on designed for Yunohost
|
||||||
A minimalist user portal with single sign-on, designed to be
|
A minimalist user portal with single sign-on, designed to be
|
||||||
|
|
1121
helpers.lua
67
init.lua
|
@ -3,65 +3,52 @@
|
||||||
--
|
--
|
||||||
-- This is the initialization file of SSOwat. It is called once at the Nginx
|
-- This is the initialization file of SSOwat. It is called once at the Nginx
|
||||||
-- server's start.
|
-- server's start.
|
||||||
-- Consequently, all the variables declared (along with libraries and
|
-- Consequently, all the variables declared (along with libraries and
|
||||||
-- translations) in this file will be *persistent* from one HTTP request to
|
-- translations) in this file will be *persistent* from one HTTP request to
|
||||||
-- another.
|
-- another.
|
||||||
--
|
--
|
||||||
|
|
||||||
-- Path of the configuration
|
|
||||||
conf_path = "/etc/ssowat/conf.json"
|
|
||||||
log_file = "/var/log/nginx/ssowat.log"
|
|
||||||
|
|
||||||
-- Remove prepending '@' & trailing 'init.lua'
|
-- Remove prepending '@' & trailing 'init.lua'
|
||||||
script_path = string.sub(debug.getinfo(1).source, 2, -9)
|
script_path = string.sub(debug.getinfo(1).source, 2, -9)
|
||||||
|
|
||||||
-- Include local libs in package.path
|
-- Include local libs in package.path
|
||||||
package.path = package.path .. ";"..script_path.."?.lua"
|
package.path = package.path .. ";"..script_path.."?.lua"
|
||||||
|
|
||||||
-- Load libraries
|
-- Load cookie secret
|
||||||
local json = require "json"
|
-- IMPORTANT (though to be confirmed?)
|
||||||
local lualdap = require "lualdap"
|
-- in this context, the code is ran as root therefore we don't have to
|
||||||
local math = require "math"
|
-- add www-data in the file permissions, which could otherwise lead
|
||||||
local lfs = require "lfs"
|
-- to comprised apps running with the www-data group to read the secret file?
|
||||||
local socket = require "socket"
|
local config = require("config")
|
||||||
local config = require "config"
|
cookie_secret = config.get_cookie_secret()
|
||||||
lustache = require "lustache"
|
|
||||||
|
--
|
||||||
|
-- Init logger
|
||||||
|
--
|
||||||
|
|
||||||
|
local log_file = "/var/log/nginx/ssowat.log"
|
||||||
|
|
||||||
-- Make sure the log file exists and we can write in it
|
-- Make sure the log file exists and we can write in it
|
||||||
io.popen("touch "..log_file)
|
io.popen("touch "..log_file)
|
||||||
io.popen("chown www-data "..log_file)
|
io.popen("chown www-data "..log_file)
|
||||||
io.popen("chmod u+w "..log_file)
|
io.popen("chmod u+w "..log_file)
|
||||||
|
|
||||||
-- Persistent shared table
|
local Logging = require("logging")
|
||||||
flashs = {}
|
local appender = function(self, level, message)
|
||||||
i18n = {}
|
|
||||||
|
|
||||||
-- convert a string to a hex
|
-- Output to log file
|
||||||
function tohex(str)
|
local fp = io.open(log_file, "a")
|
||||||
return (str:gsub('.', function (c)
|
local str = string.format("[%-6s%s] %s\n", level:upper(), os.date(), message)
|
||||||
return string.format('%02X', string.byte(c))
|
fp:write(str)
|
||||||
end))
|
fp:close()
|
||||||
|
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Efficient function to get a random string
|
logger = Logging.new(appender)
|
||||||
function random_string()
|
|
||||||
local length = 64
|
-- FIXME : how to set logging level ?
|
||||||
local random_bytes = io.open("/dev/urandom"):read(length);
|
--logger:setLevel(logger.DEBUG) -- FIXME
|
||||||
if string.len(random_bytes) ~= length then
|
|
||||||
error("Not enough random bytes read")
|
|
||||||
end
|
|
||||||
return tohex(random_bytes);
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Load translations in the "i18n" above table
|
|
||||||
local locale_dir = script_path.."portal/locales/"
|
|
||||||
for file in lfs.dir(locale_dir) do
|
|
||||||
if string.sub(file, -4) == "json" then
|
|
||||||
local lang = string.sub(file, 1, 2)
|
|
||||||
local locale_file = io.open(locale_dir..file, "r")
|
|
||||||
i18n[lang] = json.decode(locale_file:read("*all"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- You should see that in your Nginx error logs by default
|
-- You should see that in your Nginx error logs by default
|
||||||
ngx.log(ngx.INFO, "SSOwat ready")
|
ngx.log(ngx.INFO, "SSOwat ready")
|
||||||
|
|
84
log.lua
|
@ -1,84 +0,0 @@
|
||||||
--
|
|
||||||
-- log.lua
|
|
||||||
--
|
|
||||||
-- Copyright (c) 2016 rxi
|
|
||||||
--
|
|
||||||
-- This library is free software; you can redistribute it and/or modify it
|
|
||||||
-- under the terms of the MIT license. See LICENSE for details.
|
|
||||||
--
|
|
||||||
|
|
||||||
local log = { _version = "0.1.0" }
|
|
||||||
local conf = config.get_config()
|
|
||||||
|
|
||||||
log.usecolor = true
|
|
||||||
log.level = conf.logging
|
|
||||||
|
|
||||||
local modes = {
|
|
||||||
{ name = "trace", color = "\27[34m", },
|
|
||||||
{ name = "debug", color = "\27[36m", },
|
|
||||||
{ name = "info", color = "\27[32m", },
|
|
||||||
{ name = "warn", color = "\27[33m", },
|
|
||||||
{ name = "error", color = "\27[31m", },
|
|
||||||
{ name = "fatal", color = "\27[35m", },
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
local levels = {}
|
|
||||||
for i, v in ipairs(modes) do
|
|
||||||
levels[v.name] = i
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local round = function(x, increment)
|
|
||||||
increment = increment or 1
|
|
||||||
x = x / increment
|
|
||||||
return (x > 0 and math.floor(x + .5) or math.ceil(x - .5)) * increment
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local _tostring = tostring
|
|
||||||
|
|
||||||
local tostring = function(...)
|
|
||||||
local t = {}
|
|
||||||
for i = 1, select('#', ...) do
|
|
||||||
local x = select(i, ...)
|
|
||||||
if type(x) == "number" then
|
|
||||||
x = round(x, .01)
|
|
||||||
end
|
|
||||||
t[#t + 1] = _tostring(x)
|
|
||||||
end
|
|
||||||
return table.concat(t, " ")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
for i, x in ipairs(modes) do
|
|
||||||
local nameupper = x.name:upper()
|
|
||||||
log[x.name] = function(...)
|
|
||||||
|
|
||||||
-- Return early if we're below the log level
|
|
||||||
if i < levels[log.level] then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local msg = tostring(...)
|
|
||||||
local info = debug.getinfo(2, "Sl")
|
|
||||||
|
|
||||||
-- Output to console
|
|
||||||
print(string.format("%s[%-6s%s]%s %s",
|
|
||||||
log.usecolor and x.color or "",
|
|
||||||
nameupper,
|
|
||||||
os.date("%H:%M:%S"),
|
|
||||||
log.usecolor and "\27[0m" or "",
|
|
||||||
msg))
|
|
||||||
|
|
||||||
-- Output to log file
|
|
||||||
local fp = io.open(log_file, "a")
|
|
||||||
local str = string.format("[%-6s%s] %s\n",
|
|
||||||
nameupper, os.date(), msg)
|
|
||||||
fp:write(str)
|
|
||||||
fp:close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
return log
|
|
29
lustache.lua
|
@ -1,29 +0,0 @@
|
||||||
-- lustache: Lua mustache template parsing.
|
|
||||||
-- Copyright 2013 Olivine Labs, LLC <projects@olivinelabs.com>
|
|
||||||
-- MIT Licensed.
|
|
||||||
|
|
||||||
module('lustache', package.seeall)
|
|
||||||
|
|
||||||
local string_gmatch = string.gmatch
|
|
||||||
|
|
||||||
function string.split(str, sep)
|
|
||||||
local out = {}
|
|
||||||
for m in string_gmatch(str, "[^"..sep.."]+") do out[#out+1] = m end
|
|
||||||
return out
|
|
||||||
end
|
|
||||||
|
|
||||||
local lustache = {
|
|
||||||
name = "lustache",
|
|
||||||
version = "1.3.1-0",
|
|
||||||
renderer = require("lustache.renderer"):new(),
|
|
||||||
}
|
|
||||||
|
|
||||||
return setmetatable(lustache, {
|
|
||||||
__index = function(self, idx)
|
|
||||||
if self.renderer[idx] then return self.renderer[idx] end
|
|
||||||
end,
|
|
||||||
__newindex = function(self, idx, val)
|
|
||||||
if idx == "partials" then self.renderer.partials = val end
|
|
||||||
if idx == "tags" then self.renderer.tags = val end
|
|
||||||
end
|
|
||||||
})
|
|
|
@ -1,22 +0,0 @@
|
||||||
The MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2012 Olivine Labs
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -1,66 +0,0 @@
|
||||||
local string_find, string_split, tostring, type =
|
|
||||||
string.find, string.split, tostring, type
|
|
||||||
|
|
||||||
local context = {}
|
|
||||||
context.__index = context
|
|
||||||
|
|
||||||
function context:clear_cache()
|
|
||||||
self.cache = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
function context:push(view)
|
|
||||||
return self:new(view, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
function context:lookup(name)
|
|
||||||
local value = self.cache[name]
|
|
||||||
|
|
||||||
if not value then
|
|
||||||
if name == "." then
|
|
||||||
value = self.view
|
|
||||||
else
|
|
||||||
local context = self
|
|
||||||
|
|
||||||
while context do
|
|
||||||
if string_find(name, ".") > 0 then
|
|
||||||
local names = string_split(name, ".")
|
|
||||||
local i = 0
|
|
||||||
|
|
||||||
value = context.view
|
|
||||||
|
|
||||||
if(type(value)) == "number" then
|
|
||||||
value = tostring(value)
|
|
||||||
end
|
|
||||||
|
|
||||||
while value and i < #names do
|
|
||||||
i = i + 1
|
|
||||||
value = value[names[i]]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
value = context.view[name]
|
|
||||||
end
|
|
||||||
|
|
||||||
if value then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
context = context.parent
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self.cache[name] = value
|
|
||||||
end
|
|
||||||
|
|
||||||
return value
|
|
||||||
end
|
|
||||||
|
|
||||||
function context:new(view, parent)
|
|
||||||
local out = {
|
|
||||||
view = view,
|
|
||||||
parent = parent,
|
|
||||||
cache = {},
|
|
||||||
}
|
|
||||||
return setmetatable(out, context)
|
|
||||||
end
|
|
||||||
|
|
||||||
return context
|
|
|
@ -1,388 +0,0 @@
|
||||||
local Scanner = require "lustache.scanner"
|
|
||||||
local Context = require "lustache.context"
|
|
||||||
|
|
||||||
local error, ipairs, loadstring, pairs, setmetatable, tostring, type =
|
|
||||||
error, ipairs, loadstring, pairs, setmetatable, tostring, type
|
|
||||||
local math_floor, math_max, string_find, string_gsub, string_split, string_sub, table_concat, table_insert, table_remove =
|
|
||||||
math.floor, math.max, string.find, string.gsub, string.split, string.sub, table.concat, table.insert, table.remove
|
|
||||||
|
|
||||||
local patterns = {
|
|
||||||
white = "%s*",
|
|
||||||
space = "%s+",
|
|
||||||
nonSpace = "%S",
|
|
||||||
eq = "%s*=",
|
|
||||||
curly = "%s*}",
|
|
||||||
tag = "[#\\^/>{&=!]"
|
|
||||||
}
|
|
||||||
|
|
||||||
local html_escape_characters = {
|
|
||||||
["&"] = "&",
|
|
||||||
["<"] = "<",
|
|
||||||
[">"] = ">",
|
|
||||||
['"'] = """,
|
|
||||||
["'"] = "'",
|
|
||||||
["/"] = "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
local function is_array(array)
|
|
||||||
if type(array) ~= "table" then return false end
|
|
||||||
local max, n = 0, 0
|
|
||||||
for k, _ in pairs(array) do
|
|
||||||
if not (type(k) == "number" and k > 0 and math_floor(k) == k) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
max = math_max(max, k)
|
|
||||||
n = n + 1
|
|
||||||
end
|
|
||||||
return n == max
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Low-level function that compiles the given `tokens` into a
|
|
||||||
-- function that accepts two arguments: a Context and a
|
|
||||||
-- Renderer.
|
|
||||||
|
|
||||||
local function compile_tokens(tokens, originalTemplate)
|
|
||||||
local subs = {}
|
|
||||||
|
|
||||||
local function subrender(i, tokens)
|
|
||||||
if not subs[i] then
|
|
||||||
local fn = compile_tokens(tokens, originalTemplate)
|
|
||||||
subs[i] = function(ctx, rnd) return fn(ctx, rnd) end
|
|
||||||
end
|
|
||||||
return subs[i]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function render(ctx, rnd)
|
|
||||||
local buf = {}
|
|
||||||
local token, section
|
|
||||||
for i, token in ipairs(tokens) do
|
|
||||||
local t = token.type
|
|
||||||
buf[#buf+1] =
|
|
||||||
t == "#" and rnd:_section(
|
|
||||||
token, ctx, subrender(i, token.tokens), originalTemplate
|
|
||||||
) or
|
|
||||||
t == "^" and rnd:_inverted(
|
|
||||||
token.value, ctx, subrender(i, token.tokens)
|
|
||||||
) or
|
|
||||||
t == ">" and rnd:_partial(token.value, ctx, originalTemplate) or
|
|
||||||
(t == "{" or t == "&") and rnd:_name(token.value, ctx, false) or
|
|
||||||
t == "name" and rnd:_name(token.value, ctx, true) or
|
|
||||||
t == "text" and token.value or ""
|
|
||||||
end
|
|
||||||
return table_concat(buf)
|
|
||||||
end
|
|
||||||
return render
|
|
||||||
end
|
|
||||||
|
|
||||||
local function escape_tags(tags)
|
|
||||||
|
|
||||||
return {
|
|
||||||
string_gsub(tags[1], "%%", "%%%%").."%s*",
|
|
||||||
"%s*"..string_gsub(tags[2], "%%", "%%%%"),
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function nest_tokens(tokens)
|
|
||||||
local tree = {}
|
|
||||||
local collector = tree
|
|
||||||
local sections = {}
|
|
||||||
local token, section
|
|
||||||
|
|
||||||
for i,token in ipairs(tokens) do
|
|
||||||
if token.type == "#" or token.type == "^" then
|
|
||||||
token.tokens = {}
|
|
||||||
sections[#sections+1] = token
|
|
||||||
collector[#collector+1] = token
|
|
||||||
collector = token.tokens
|
|
||||||
elseif token.type == "/" then
|
|
||||||
if #sections == 0 then
|
|
||||||
error("Unopened section: "..token.value)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Make sure there are no open sections when we're done
|
|
||||||
section = table_remove(sections, #sections)
|
|
||||||
|
|
||||||
if not section.value == token.value then
|
|
||||||
error("Unclosed section: "..section.value)
|
|
||||||
end
|
|
||||||
|
|
||||||
section.closingTagIndex = token.startIndex
|
|
||||||
|
|
||||||
if #sections > 0 then
|
|
||||||
collector = sections[#sections].tokens
|
|
||||||
else
|
|
||||||
collector = tree
|
|
||||||
end
|
|
||||||
else
|
|
||||||
collector[#collector+1] = token
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
section = table_remove(sections, #sections)
|
|
||||||
|
|
||||||
if section then
|
|
||||||
error("Unclosed section: "..section.value)
|
|
||||||
end
|
|
||||||
|
|
||||||
return tree
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Combines the values of consecutive text tokens in the given `tokens` array
|
|
||||||
-- to a single token.
|
|
||||||
local function squash_tokens(tokens)
|
|
||||||
local out, txt = {}, {}
|
|
||||||
local txtStartIndex, txtEndIndex
|
|
||||||
for _, v in ipairs(tokens) do
|
|
||||||
if v.type == "text" then
|
|
||||||
if #txt == 0 then
|
|
||||||
txtStartIndex = v.startIndex
|
|
||||||
end
|
|
||||||
txt[#txt+1] = v.value
|
|
||||||
txtEndIndex = v.endIndex
|
|
||||||
else
|
|
||||||
if #txt > 0 then
|
|
||||||
out[#out+1] = { type = "text", value = table_concat(txt), startIndex = txtStartIndex, endIndex = txtEndIndex }
|
|
||||||
txt = {}
|
|
||||||
end
|
|
||||||
out[#out+1] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #txt > 0 then
|
|
||||||
out[#out+1] = { type = "text", value = table_concat(txt), startIndex = txtStartIndex, endIndex = txtEndIndex }
|
|
||||||
end
|
|
||||||
return out
|
|
||||||
end
|
|
||||||
|
|
||||||
local function make_context(view)
|
|
||||||
if not view then return view end
|
|
||||||
return getmetatable(view) == Context and view or Context:new(view)
|
|
||||||
end
|
|
||||||
|
|
||||||
local renderer = { }
|
|
||||||
|
|
||||||
function renderer:clear_cache()
|
|
||||||
self.cache = {}
|
|
||||||
self.partial_cache = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
function renderer:compile(tokens, tags, originalTemplate)
|
|
||||||
tags = tags or self.tags
|
|
||||||
if type(tokens) == "string" then
|
|
||||||
tokens = self:parse(tokens, tags)
|
|
||||||
end
|
|
||||||
|
|
||||||
local fn = compile_tokens(tokens, originalTemplate)
|
|
||||||
|
|
||||||
return function(view)
|
|
||||||
return fn(make_context(view), self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function renderer:render(template, view, partials)
|
|
||||||
if type(self) == "string" then
|
|
||||||
error("Call mustache:render, not mustache.render!")
|
|
||||||
end
|
|
||||||
|
|
||||||
if partials then
|
|
||||||
-- remember partial table
|
|
||||||
-- used for runtime lookup & compile later on
|
|
||||||
self.partials = partials
|
|
||||||
end
|
|
||||||
|
|
||||||
if not template then
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
|
|
||||||
local fn = self.cache[template]
|
|
||||||
|
|
||||||
if not fn then
|
|
||||||
fn = self:compile(template, self.tags, template)
|
|
||||||
self.cache[template] = fn
|
|
||||||
end
|
|
||||||
|
|
||||||
return fn(view)
|
|
||||||
end
|
|
||||||
|
|
||||||
function renderer:_section(token, context, callback, originalTemplate)
|
|
||||||
local value = context:lookup(token.value)
|
|
||||||
|
|
||||||
if type(value) == "table" then
|
|
||||||
if is_array(value) then
|
|
||||||
local buffer = ""
|
|
||||||
|
|
||||||
for i,v in ipairs(value) do
|
|
||||||
buffer = buffer .. callback(context:push(v), self)
|
|
||||||
end
|
|
||||||
|
|
||||||
return buffer
|
|
||||||
end
|
|
||||||
|
|
||||||
return callback(context:push(value), self)
|
|
||||||
elseif type(value) == "function" then
|
|
||||||
local section_text = string_sub(originalTemplate, token.endIndex+1, token.closingTagIndex - 1)
|
|
||||||
|
|
||||||
local scoped_render = function(template)
|
|
||||||
return self:render(template, context)
|
|
||||||
end
|
|
||||||
|
|
||||||
return value(section_text, scoped_render) or ""
|
|
||||||
else
|
|
||||||
if value then
|
|
||||||
return callback(context, self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
|
|
||||||
function renderer:_inverted(name, context, callback)
|
|
||||||
local value = context:lookup(name)
|
|
||||||
|
|
||||||
-- From the spec: inverted sections may render text once based on the
|
|
||||||
-- inverse value of the key. That is, they will be rendered if the key
|
|
||||||
-- doesn't exist, is false, or is an empty list.
|
|
||||||
|
|
||||||
if value == nil or value == false or (type(value) == "table" and is_array(value) and #value == 0) then
|
|
||||||
return callback(context, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
|
|
||||||
function renderer:_partial(name, context, originalTemplate)
|
|
||||||
local fn = self.partial_cache[name]
|
|
||||||
|
|
||||||
-- check if partial cache exists
|
|
||||||
if (not fn and self.partials) then
|
|
||||||
|
|
||||||
local partial = self.partials[name]
|
|
||||||
if (not partial) then
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
|
|
||||||
-- compile partial and store result in cache
|
|
||||||
fn = self:compile(partial, nil, originalTemplate)
|
|
||||||
self.partial_cache[name] = fn
|
|
||||||
end
|
|
||||||
return fn and fn(context, self) or ""
|
|
||||||
end
|
|
||||||
|
|
||||||
function renderer:_name(name, context, escape)
|
|
||||||
local value = context:lookup(name)
|
|
||||||
|
|
||||||
if type(value) == "function" then
|
|
||||||
value = value(context.view)
|
|
||||||
end
|
|
||||||
|
|
||||||
local str = value == nil and "" or value
|
|
||||||
str = tostring(str)
|
|
||||||
|
|
||||||
if escape then
|
|
||||||
return string_gsub(str, '[&<>"\'/]', function(s) return html_escape_characters[s] end)
|
|
||||||
end
|
|
||||||
|
|
||||||
return str
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Breaks up the given `template` string into a tree of token objects. If
|
|
||||||
-- `tags` is given here it must be an array with two string values: the
|
|
||||||
-- opening and closing tags used in the template (e.g. ["<%", "%>"]). Of
|
|
||||||
-- course, the default is to use mustaches (i.e. Mustache.tags).
|
|
||||||
function renderer:parse(template, tags)
|
|
||||||
tags = tags or self.tags
|
|
||||||
local tag_patterns = escape_tags(tags)
|
|
||||||
local scanner = Scanner:new(template)
|
|
||||||
local tokens = {} -- token buffer
|
|
||||||
local spaces = {} -- indices of whitespace tokens on the current line
|
|
||||||
local has_tag = false -- is there a {{tag} on the current line?
|
|
||||||
local non_space = false -- is there a non-space char on the current line?
|
|
||||||
|
|
||||||
-- Strips all whitespace tokens array for the current line if there was
|
|
||||||
-- a {{#tag}} on it and otherwise only space
|
|
||||||
local function strip_space()
|
|
||||||
if has_tag and not non_space then
|
|
||||||
while #spaces > 0 do
|
|
||||||
table_remove(tokens, table_remove(spaces))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
spaces = {}
|
|
||||||
end
|
|
||||||
has_tag = false
|
|
||||||
non_space = false
|
|
||||||
end
|
|
||||||
|
|
||||||
local type, value, chr
|
|
||||||
|
|
||||||
while not scanner:eos() do
|
|
||||||
local start = scanner.pos
|
|
||||||
|
|
||||||
value = scanner:scan_until(tag_patterns[1])
|
|
||||||
|
|
||||||
if value then
|
|
||||||
for i = 1, #value do
|
|
||||||
chr = string_sub(value,i,i)
|
|
||||||
|
|
||||||
if string_find(chr, "%s+") then
|
|
||||||
spaces[#spaces+1] = #tokens + 1
|
|
||||||
else
|
|
||||||
non_space = true
|
|
||||||
end
|
|
||||||
|
|
||||||
tokens[#tokens+1] = { type = "text", value = chr, startIndex = start, endIndex = start }
|
|
||||||
start = start + 1
|
|
||||||
if chr == "\n" then
|
|
||||||
strip_space()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not scanner:scan(tag_patterns[1]) then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
has_tag = true
|
|
||||||
type = scanner:scan(patterns.tag) or "name"
|
|
||||||
|
|
||||||
scanner:scan(patterns.white)
|
|
||||||
|
|
||||||
if type == "=" then
|
|
||||||
value = scanner:scan_until(patterns.eq)
|
|
||||||
scanner:scan(patterns.eq)
|
|
||||||
scanner:scan_until(tag_patterns[2])
|
|
||||||
elseif type == "{" then
|
|
||||||
local close_pattern = "%s*}"..tags[2]
|
|
||||||
value = scanner:scan_until(close_pattern)
|
|
||||||
scanner:scan(patterns.curly)
|
|
||||||
scanner:scan_until(tag_patterns[2])
|
|
||||||
else
|
|
||||||
value = scanner:scan_until(tag_patterns[2])
|
|
||||||
end
|
|
||||||
|
|
||||||
if not scanner:scan(tag_patterns[2]) then
|
|
||||||
error("Unclosed tag at " .. scanner.pos)
|
|
||||||
end
|
|
||||||
|
|
||||||
tokens[#tokens+1] = { type = type, value = value, startIndex = start, endIndex = scanner.pos - 1 }
|
|
||||||
if type == "name" or type == "{" or type == "&" then
|
|
||||||
non_space = true --> what does this do?
|
|
||||||
end
|
|
||||||
|
|
||||||
if type == "=" then
|
|
||||||
tags = string_split(value, patterns.space)
|
|
||||||
tag_patterns = escape_tags(tags)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nest_tokens(squash_tokens(tokens))
|
|
||||||
end
|
|
||||||
|
|
||||||
function renderer:new()
|
|
||||||
local out = {
|
|
||||||
cache = {},
|
|
||||||
partial_cache = {},
|
|
||||||
tags = {"{{", "}}"}
|
|
||||||
}
|
|
||||||
return setmetatable(out, { __index = self })
|
|
||||||
end
|
|
||||||
|
|
||||||
return renderer
|
|
|
@ -1,57 +0,0 @@
|
||||||
local string_find, string_match, string_sub =
|
|
||||||
string.find, string.match, string.sub
|
|
||||||
|
|
||||||
local scanner = {}
|
|
||||||
|
|
||||||
-- Returns `true` if the tail is empty (end of string).
|
|
||||||
function scanner:eos()
|
|
||||||
return self.tail == ""
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Tries to match the given regular expression at the current position.
|
|
||||||
-- Returns the matched text if it can match, `null` otherwise.
|
|
||||||
function scanner:scan(pattern)
|
|
||||||
local match = string_match(self.tail, pattern)
|
|
||||||
|
|
||||||
if match and string_find(self.tail, pattern) == 1 then
|
|
||||||
self.tail = string_sub(self.tail, #match + 1)
|
|
||||||
self.pos = self.pos + #match
|
|
||||||
|
|
||||||
return match
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Skips all text until the given regular expression can be matched. Returns
|
|
||||||
-- the skipped string, which is the entire tail of this scanner if no match
|
|
||||||
-- can be made.
|
|
||||||
function scanner:scan_until(pattern)
|
|
||||||
|
|
||||||
local match
|
|
||||||
local pos = string_find(self.tail, pattern)
|
|
||||||
|
|
||||||
if pos == nil then
|
|
||||||
match = self.tail
|
|
||||||
self.pos = self.pos + #self.tail
|
|
||||||
self.tail = ""
|
|
||||||
elseif pos == 1 then
|
|
||||||
match = nil
|
|
||||||
else
|
|
||||||
match = string_sub(self.tail, 1, pos - 1)
|
|
||||||
self.tail = string_sub(self.tail, pos)
|
|
||||||
self.pos = self.pos + #match
|
|
||||||
end
|
|
||||||
|
|
||||||
return match
|
|
||||||
end
|
|
||||||
|
|
||||||
function scanner:new(str)
|
|
||||||
local out = {
|
|
||||||
str = str,
|
|
||||||
tail = str,
|
|
||||||
pos = 1
|
|
||||||
}
|
|
||||||
return setmetatable(out, { __index = self } )
|
|
||||||
end
|
|
||||||
|
|
||||||
return scanner
|
|
|
@ -1,182 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This file contains CSS rules loaded on all apps page (*if* the app nginx's
|
|
||||||
conf does include the appropriate snippet) for the small YunoHost button in
|
|
||||||
bottom-right corner + portal overlay.
|
|
||||||
|
|
||||||
The yunohost button corresponds to : #ynh-overlay-switch
|
|
||||||
The yunohost portal overlay / iframe corresponds to : #ynh-overlay
|
|
||||||
|
|
||||||
BE CAREFUL that you should *not* add too-general rules that apply to
|
|
||||||
non-yunohost elements (for instance all 'a' or 'p' elements...) as it will
|
|
||||||
likely break app's rendering
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* ******************************************************************
|
|
||||||
General
|
|
||||||
******************************************************************* */
|
|
||||||
|
|
||||||
html.ynh-panel-active {
|
|
||||||
/* Disable any scrolling on app */
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ynh-overlay-switch,
|
|
||||||
#ynh-overlay-switch *,
|
|
||||||
#ynh-overlay,
|
|
||||||
#ynh-overlay * {
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ******************************************************************
|
|
||||||
Button
|
|
||||||
******************************************************************* */
|
|
||||||
#ynh-overlay-switch {
|
|
||||||
display: block;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 10000000;
|
|
||||||
bottom: 20px;
|
|
||||||
right: 35px;
|
|
||||||
width: 100px;
|
|
||||||
height: 90px;
|
|
||||||
padding: 12px;
|
|
||||||
border: 12px solid #41444f;
|
|
||||||
border-radius: 5px;
|
|
||||||
background: #41444f;
|
|
||||||
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE1LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluICAtLT4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIiBbCgk8IUVOVElUWSBuc19mbG93cyAiaHR0cDovL25zLmFkb2JlLmNvbS9GbG93cy8xLjAvIj4KXT4KPHN2ZyB2ZXJzaW9uPSIxLjEiCgkgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6YT0iaHR0cDovL25zLmFkb2JlLmNvbS9BZG9iZVNWR1ZpZXdlckV4dGVuc2lvbnMvMy4wLyIKCSB4PSIwcHgiIHk9IjBweCIgd2lkdGg9Ijk4cHgiIGhlaWdodD0iODVweCIgdmlld0JveD0iLTAuMjUgLTAuMjUgOTggODUiCgkgb3ZlcmZsb3c9InZpc2libGUiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgLTAuMjUgLTAuMjUgOTggODUiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8ZGVmcz4KPC9kZWZzPgo8cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNOTcsNTFjLTIuMDIsNC45OC04LjMzLDUuNjctMTQsN2MtMC42MDksNi4yOSwzLjA1LDEwLjk1LTEsMTZjLTYuNDEtMC4yNi03LjQ3MS01Ljg1OS03LTEzYy0xLDAtMiwwLTMsMAoJYy0yLjA5LDIuNzcsMC45LDQuNTIsMCw4Yy0xLjEyLDQuMzQtNy44OCw3LjkxLTExLDdjLTIuMTgtMC42NDEtNS45Ni02LjYzLTUtMTJjMi44Mi0yLjcxLDIuNzYsMy4xMiw2LDNjNS4wNS03Ljg0LTkuNjMtOC41NS04LTE3CgljMS4yNC02LjQyLDExLjY2LTkuNjYsMTUtMWMxLjU0LDQuMjEtNS4xNywwLjE2LTUsM2MtMC4yNzksMS42MiwwLjk1LDEuNzIsMSwzYzIuNTIsMC43NywxLjY4LTIuMTYsMy0zYzEuODU5LTEuMTcsMy4wOS0wLjc1LDYtMQoJYzIuNDUtMi41NSwxLjA4LTguOTIsNC0xMWMzLjg3LDAuNDYsNi4wOCwyLjU5LDYsN0M5MS4wMSw0Ni4xMDksOTQuMyw0Ni4wNSw5Nyw1MXoiLz4KPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTg3LDEzYzAuNjA5LDMuMjEsMi4zMiw0Ljk4LDIsOGMtMC4zNCwzLjIxLTIuOSw4LjgzLTQsOWMtMS4xNywwLjE4LTEuMzQsMS43OC0yLDIKCWMtNC42NiwxLjU3LTEyLjM5MS0xLjQ4LTE0LTdjLTEuMTYtMy45NywxLjktMTMuMzcsNC0xN2MxLjMtMi4yNSwxLjIyMS0yLjk5LDUtNGMyLjQxLTAuNjUsMy42NS0yLjI1LDYsMAoJYzAuNDcxLDAuNDUsMS4zLDAuNDksMS44NSwwLjg5Yy0wLjE5OSwwLDIsMy4xNCwyLjE1LDQuMTFDODguMzIsMTEuMDcsODYuNzcsMTEuNzgsODcsMTN6IE03OSwyMmMxLjc3OS0xLjg5LDMuMjktNC4wNCwzLTgKCUM3Ny40OSwxMi4zMyw3NC42NywyMS4zLDc5LDIyeiIvPgo8cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNNjcsMjFjLTAuMDcsNS44MSwyLjQ4LDEwLjcsMCwxNWMtNi43MywxLjA2LTcuMjQtNC4xLTExLTZjLTEuOTM5LDEuMzktMS40OSw1LjE4LTMsNwoJYy0zLjc4LDAuNDQtNC42OS0xLjk3LTctM2MyLjQ3LTcuODEsMS4yNi0xOC45OCwyLTI2YzguNTgtMC41OCw3LjY4LDguMzIsMTIsMTJjMC41Mi00LjM0LTAuMzU5LTE1LjUyLDMtMjAKCUM3MC4zMywzLjI5LDY3LjA5LDEyLjk5LDY3LDIxeiIvPgo8cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNNTIsNTVjMS45Myw4LjQxLDAuMTIsMjIuNjg5LTEyLDIwYy0xLjU5LTAuMzUtOC40Mi01LjIyLTktN2MtMS42Mi01LDAuMzQtMTMuMzQsMy0xNgoJQzM5LjAzLDQ2Ljk3LDQ1LjQ4LDUwLjM1OSw1Miw1NXogTTM5LDY2YzQuNTUsMC45Niw2LjMtNC4yLDQtN0MzOS4zNyw1OS4wMywzOC42MSw2MS45MzksMzksNjZ6Ii8+CjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0zOSw4YzUuNTgsMC45LDYuNCw2LjgxLDUsMTVjLTEuNDMsOC4zOC0zLjAyLDE0LjU5LTksMTVjLTkuNTcsMC42NS0xMi4yNS0xNi42OS05LTI5CgljOC4zMiwxLjI3LDYuNTksMTAuMzYsNiwxN2MyLjcxLDAuODMsMi4yLTAuODUsMy0yQzM3LjA1LDIxLjA0LDM3LjgyLDEzLjYxLDM5LDh6Ii8+CjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0yOCw2MmMwLjEsNS42Nyw0LjQsMTEuMzMsMiwxN2MtNC4zMi0xLjAxLTYuNTctNC4wOS05LTdjLTMuMTUtMC40OC0yLjI2LDMuMDctNiwyCgljLTAuNjcsNS4wNjEsMi4yOSw3LjU3LTEsMTBjLTQuNy0wLjYzLTYuNjYtNC04LThjLTIuNjEtMS4zOC01LjQ4LTIuNTItNi02YzAuMTQtMy41Myw0LjQ4LTIuODUsNy00YzAuNDctNS41My0xLjQxLTEzLjQxLDItMTYKCWM4LjMxLDAuNDksOC4yMSw3LjEzLDcsMTVjNC4zNiwwLjI5LDQuOTQtNC4zNSw1LTdjMC4wNi0yLjQzLTEuODItOC4yNiwyLTExYzMuMDYtMC43MywyLjk0LDEuNzMsNiwxCglDMzIuMzUsNTIuNywyNy45Miw1Ny40MzksMjgsNjJ6Ii8+CjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0yNCwxMmMxLjA3LDcuMDctMy44Niw4LjE0LTYsMTJjMC4yMSw2Ljg4LTAuNDcsMTIuODYtMiwxOGMtNS44Ni0xLjMyLTguNy0xMC4zOC02LTE3CgljLTAuMzMtMy41Mi01LjI2LTQuMjItNy04Yy0wLjMtMC42Ni0wLjQ3LTQuNDMtMS03QzEuMDksNS42MywwLjU1LDQuMzEsMywxYzguMTYtMC40OSw3LjIxLDguMTMsOSwxNGM1LjA1LDAuMzksMy45MS01LjQyLDgtNgoJQzIwLjk4LDEwLjM1LDIyLjY3LDExLDI0LDEyeiIvPgo8L3N2Zz4K);
|
|
||||||
background-position: center center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: contain;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
/*#ynh-overlay-switch.visible,*/
|
|
||||||
#ynh-overlay-switch:hover {
|
|
||||||
background-color: #41444f;
|
|
||||||
border-color: #41444f;
|
|
||||||
background-color: #111;
|
|
||||||
border-color: #111;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ******************************************************************
|
|
||||||
Overlay
|
|
||||||
******************************************************************* */
|
|
||||||
|
|
||||||
/* Background */
|
|
||||||
#ynh-overlay {
|
|
||||||
overflow-y: hidden;
|
|
||||||
position: fixed;
|
|
||||||
top:0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 9999999;
|
|
||||||
display: none;
|
|
||||||
border: none;
|
|
||||||
color:#fff;
|
|
||||||
background: #41444F;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
-moz-transition: all 0.2s ease;
|
|
||||||
-webkit-transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ******************************************************************
|
|
||||||
Animation
|
|
||||||
******************************************************************* */
|
|
||||||
|
|
||||||
/*FadeIn*/
|
|
||||||
@-webkit-keyframes ynhFadeIn {
|
|
||||||
0% {
|
|
||||||
visibility: hidden;
|
|
||||||
opacity:0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes ynhFadeIn {
|
|
||||||
0% {
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ynh-fadeIn {
|
|
||||||
-webkit-animation-name: ynhFadeIn;
|
|
||||||
animation-name: ynhFadeIn;
|
|
||||||
-webkit-animation-duration: 0.5s;
|
|
||||||
animation-duration: 0.5s;
|
|
||||||
-webkit-animation-fill-mode: both;
|
|
||||||
animation-fill-mode: both;
|
|
||||||
-webkit-animation-timing-function: cubic-bezier(0.165, 0.840, 0.440, 1.000);
|
|
||||||
animation-timing-function: cubic-bezier(0.165, 0.840, 0.440, 1.000);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
.ynh-fadeIn.ynh-delay {
|
|
||||||
animation-delay: 0.5s;
|
|
||||||
-webkit-animation-delay: 0.5s;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*FadeOut*/
|
|
||||||
@-webkit-keyframes ynhFadeOut {
|
|
||||||
0% {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes ynhFadeOut {
|
|
||||||
0% {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ynh-fadeOut {
|
|
||||||
-webkit-animation-name: ynhFadeOut;
|
|
||||||
animation-name: ynhFadeOut;
|
|
||||||
-webkit-animation-duration: 0.2s;
|
|
||||||
animation-duration: 0.2s;
|
|
||||||
-webkit-animation-fill-mode: both;
|
|
||||||
animation-fill-mode: both;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
.ynh-fadeOut.ynh-delay {
|
|
||||||
animation-delay: 0.5s;
|
|
||||||
-webkit-animation-delay: 0.5s;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/* ******************************************************************
|
|
||||||
Media Queries
|
|
||||||
******************************************************************* */
|
|
||||||
|
|
||||||
@media screen and (max-width: 500px) {
|
|
||||||
#ynh-overlay-switch {
|
|
||||||
width: 80px;
|
|
||||||
height: 75px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
<?xml version="1.0" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<metadata>Copyright (C) 2014 by original authors @ fontello.com</metadata>
|
|
||||||
<defs>
|
|
||||||
<font id="ynh_ssowat" horiz-adv-x="1000" >
|
|
||||||
<font-face font-family="ynh_ssowat" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
|
||||||
<missing-glyph horiz-adv-x="1000" />
|
|
||||||
<glyph glyph-name="user" unicode="" d="m786 66q0-67-41-106t-108-39h-488q-67 0-108 39t-41 106q0 30 2 58t8 61 15 60 24 55 34 45 48 30 62 11q5 0 24-12t41-27 60-27 75-12 74 12 61 27 41 27 24 12q34 0 62-11t48-30 34-45 24-55 15-60 8-61 2-58z m-179 498q0-88-63-151t-151-63-152 63-62 151 62 152 152 63 151-63 63-152z" horiz-adv-x="785.7" />
|
|
||||||
<glyph glyph-name="lock" unicode="" d="m179 421h285v108q0 59-42 101t-101 41-101-41-41-101v-108z m464-53v-322q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v322q0 22 16 38t38 15h17v108q0 102 74 176t176 74 177-74 73-176v-108h18q23 0 38-15t16-38z" horiz-adv-x="642.9" />
|
|
||||||
<glyph glyph-name="pencil" unicode="" d="m203-7l50 51-131 131-51-51v-60h72v-71h60z m291 518q0 12-12 12-5 0-9-4l-303-302q-4-4-4-10 0-12 13-12 5 0 9 4l303 302q3 4 3 10z m-30 107l232-232-464-465h-232v233z m381-54q0-29-20-50l-93-93-232 233 93 92q20 21 50 21 29 0 51-21l131-131q20-22 20-51z" horiz-adv-x="857.1" />
|
|
||||||
<glyph glyph-name="trash" unicode="" d="m286 439v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m143 0v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m142 0v-321q0-8-5-13t-12-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q7 0 12-5t5-13z m72-404v529h-500v-529q0-12 4-22t8-15 6-5h464q2 0 6 5t8 15 4 22z m-375 601h250l-27 65q-4 5-9 6h-177q-6-1-10-6z m518-18v-36q0-8-5-13t-13-5h-54v-529q0-46-26-80t-63-34h-464q-37 0-63 33t-27 79v531h-53q-8 0-13 5t-5 13v36q0 8 5 13t13 5h172l39 93q9 21 31 35t44 15h178q22 0 44-15t30-35l39-93h173q8 0 13-5t5-13z" horiz-adv-x="785.7" />
|
|
||||||
<glyph glyph-name="angle-left" unicode="" d="m350 546q0-7-6-12l-219-220 219-219q6-6 6-13t-6-13l-28-28q-5-5-12-5t-13 5l-260 260q-6 6-6 13t6 13l260 260q5 6 13 6t12-6l28-28q6-5 6-13z" horiz-adv-x="357.1" />
|
|
||||||
<glyph glyph-name="off" unicode="" d="m857 350q0-87-34-166t-91-137-137-92-166-34-167 34-136 92-92 137-34 166q0 102 45 191t126 151q24 18 54 14t46-28q18-23 14-53t-28-47q-54-41-84-101t-30-127q0-58 22-111t62-91 91-61 111-23 110 23 92 61 61 91 22 111q0 68-30 127t-84 101q-24 18-28 47t14 53q17 24 47 28t53-14q81-61 126-151t45-191z m-357 429v-358q0-29-21-50t-50-21-51 21-21 50v358q0 29 21 50t51 21 50-21 21-50z" horiz-adv-x="857.1" />
|
|
||||||
</font>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
|
@ -1,12 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<browserconfig>
|
|
||||||
<msapplication>
|
|
||||||
<tile>
|
|
||||||
<square70x70logo src="/mstile-70x70.png"/>
|
|
||||||
<square150x150logo src="/mstile-150x150.png"/>
|
|
||||||
<square310x310logo src="/mstile-310x310.png"/>
|
|
||||||
<wide310x150logo src="/mstile-310x150.png"/>
|
|
||||||
<TileColor>#da532c</TileColor>
|
|
||||||
</tile>
|
|
||||||
</msapplication>
|
|
||||||
</browserconfig>
|
|
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 578 B |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 905 B |
Before Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 7.9 KiB |
|
@ -1,34 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In -->
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
|
||||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
|
||||||
]>
|
|
||||||
<svg version="1.1"
|
|
||||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
|
|
||||||
x="0px" y="0px" width="98px" height="85px" viewBox="-0.25 -0.25 98 85"
|
|
||||||
overflow="visible" enable-background="new -0.25 -0.25 98 85" xml:space="preserve">
|
|
||||||
<defs>
|
|
||||||
</defs>
|
|
||||||
<path fill="#FFFFFF" d="M97,51c-2.02,4.98-8.33,5.67-14,7c-0.609,6.29,3.05,10.95-1,16c-6.41-0.26-7.471-5.859-7-13c-1,0-2,0-3,0
|
|
||||||
c-2.09,2.77,0.9,4.52,0,8c-1.12,4.34-7.88,7.91-11,7c-2.18-0.641-5.96-6.63-5-12c2.82-2.71,2.76,3.12,6,3c5.05-7.84-9.63-8.55-8-17
|
|
||||||
c1.24-6.42,11.66-9.66,15-1c1.54,4.21-5.17,0.16-5,3c-0.279,1.62,0.95,1.72,1,3c2.52,0.77,1.68-2.16,3-3c1.859-1.17,3.09-0.75,6-1
|
|
||||||
c2.45-2.55,1.08-8.92,4-11c3.87,0.46,6.08,2.59,6,7C91.01,46.109,94.3,46.05,97,51z"/>
|
|
||||||
<path fill="#FFFFFF" d="M87,13c0.609,3.21,2.32,4.98,2,8c-0.34,3.21-2.9,8.83-4,9c-1.17,0.18-1.34,1.78-2,2
|
|
||||||
c-4.66,1.57-12.391-1.48-14-7c-1.16-3.97,1.9-13.37,4-17c1.3-2.25,1.221-2.99,5-4c2.41-0.65,3.65-2.25,6,0
|
|
||||||
c0.471,0.45,1.3,0.49,1.85,0.89c-0.199,0,2,3.14,2.15,4.11C88.32,11.07,86.77,11.78,87,13z M79,22c1.779-1.89,3.29-4.04,3-8
|
|
||||||
C77.49,12.33,74.67,21.3,79,22z"/>
|
|
||||||
<path fill="#FFFFFF" d="M67,21c-0.07,5.81,2.48,10.7,0,15c-6.73,1.06-7.24-4.1-11-6c-1.939,1.39-1.49,5.18-3,7
|
|
||||||
c-3.78,0.44-4.69-1.97-7-3c2.47-7.81,1.26-18.98,2-26c8.58-0.58,7.68,8.32,12,12c0.52-4.34-0.359-15.52,3-20
|
|
||||||
C70.33,3.29,67.09,12.99,67,21z"/>
|
|
||||||
<path fill="#FFFFFF" d="M52,55c1.93,8.41,0.12,22.689-12,20c-1.59-0.35-8.42-5.22-9-7c-1.62-5,0.34-13.34,3-16
|
|
||||||
C39.03,46.97,45.48,50.359,52,55z M39,66c4.55,0.96,6.3-4.2,4-7C39.37,59.03,38.61,61.939,39,66z"/>
|
|
||||||
<path fill="#FFFFFF" d="M39,8c5.58,0.9,6.4,6.81,5,15c-1.43,8.38-3.02,14.59-9,15c-9.57,0.65-12.25-16.69-9-29
|
|
||||||
c8.32,1.27,6.59,10.36,6,17c2.71,0.83,2.2-0.85,3-2C37.05,21.04,37.82,13.61,39,8z"/>
|
|
||||||
<path fill="#FFFFFF" d="M28,62c0.1,5.67,4.4,11.33,2,17c-4.32-1.01-6.57-4.09-9-7c-3.15-0.48-2.26,3.07-6,2
|
|
||||||
c-0.67,5.061,2.29,7.57-1,10c-4.7-0.63-6.66-4-8-8c-2.61-1.38-5.48-2.52-6-6c0.14-3.53,4.48-2.85,7-4c0.47-5.53-1.41-13.41,2-16
|
|
||||||
c8.31,0.49,8.21,7.13,7,15c4.36,0.29,4.94-4.35,5-7c0.06-2.43-1.82-8.26,2-11c3.06-0.73,2.94,1.73,6,1
|
|
||||||
C32.35,52.7,27.92,57.439,28,62z"/>
|
|
||||||
<path fill="#FFFFFF" d="M24,12c1.07,7.07-3.86,8.14-6,12c0.21,6.88-0.47,12.86-2,18c-5.86-1.32-8.7-10.38-6-17
|
|
||||||
c-0.33-3.52-5.26-4.22-7-8c-0.3-0.66-0.47-4.43-1-7C1.09,5.63,0.55,4.31,3,1c8.16-0.49,7.21,8.13,9,14c5.05,0.39,3.91-5.42,8-6
|
|
||||||
C20.98,10.35,22.67,11,24,12z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.6 KiB |
|
@ -1,38 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In -->
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
|
||||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
|
||||||
]>
|
|
||||||
<svg version="1.1"
|
|
||||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
|
|
||||||
x="0px" y="0px" width="98px" height="85px" viewBox="0 0 98 85" overflow="visible" enable-background="new 0 0 98 85"
|
|
||||||
xml:space="preserve">
|
|
||||||
<defs>
|
|
||||||
</defs>
|
|
||||||
<g id="XMLID_2_">
|
|
||||||
<g>
|
|
||||||
<path d="M97.25,51.25c-2.02,4.98-8.33,5.67-14,7c-0.61,6.29,3.05,10.95-1,16c-6.41-0.26-7.47-5.86-7-13c-1,0-2,0-3,0
|
|
||||||
c-2.09,2.77,0.9,4.52,0,8c-1.12,4.34-7.88,7.91-11,7c-2.18-0.64-5.96-6.63-5-12c2.82-2.71,2.76,3.12,6,3c5.05-7.84-9.63-8.55-8-17
|
|
||||||
c1.24-6.42,11.66-9.66,15-1c1.54,4.21-5.17,0.16-5,3c-0.28,1.62,0.95,1.72,1,3c2.52,0.77,1.68-2.16,3-3c1.86-1.17,3.09-0.75,6-1
|
|
||||||
c2.45-2.55,1.08-8.92,4-11c3.87,0.46,6.08,2.59,6,7C91.26,46.36,94.55,46.3,97.25,51.25z"/>
|
|
||||||
<path d="M87.25,13.25c0.61,3.21,2.32,4.98,2,8c-0.34,3.21-2.9,8.83-4,9c-1.17,0.18-1.34,1.78-2,2c-4.66,1.57-12.39-1.48-14-7
|
|
||||||
c-1.16-3.97,1.9-13.37,4-17c1.3-2.25,1.22-2.99,5-4c2.41-0.65,3.65-2.25,6,0c0.47,0.45,1.3,0.49,1.85,0.89
|
|
||||||
c-0.199,0,2,3.14,2.15,4.11C88.57,11.32,87.02,12.03,87.25,13.25z M79.25,22.25c1.78-1.89,3.29-4.04,3-8
|
|
||||||
C77.74,12.58,74.92,21.55,79.25,22.25z"/>
|
|
||||||
<path d="M67.25,21.25c-0.07,5.81,2.48,10.7,0,15c-6.73,1.06-7.24-4.1-11-6c-1.94,1.39-1.49,5.18-3,7c-3.78,0.44-4.69-1.97-7-3
|
|
||||||
c2.47-7.81,1.26-18.98,2-26c8.58-0.58,7.68,8.32,12,12c0.52-4.34-0.36-15.52,3-20C70.58,3.54,67.34,13.24,67.25,21.25z"/>
|
|
||||||
<path d="M52.25,55.25c1.93,8.41,0.12,22.69-12,20c-1.59-0.35-8.42-5.22-9-7c-1.62-5,0.34-13.34,3-16
|
|
||||||
C39.28,47.22,45.73,50.61,52.25,55.25z M39.25,66.25c4.55,0.96,6.3-4.2,4-7C39.62,59.28,38.86,62.19,39.25,66.25z"/>
|
|
||||||
<path d="M39.25,8.25c5.58,0.9,6.4,6.81,5,15c-1.43,8.38-3.02,14.59-9,15c-9.57,0.65-12.25-16.69-9-29c8.32,1.27,6.59,10.36,6,17
|
|
||||||
c2.71,0.83,2.2-0.85,3-2C37.3,21.29,38.07,13.86,39.25,8.25z"/>
|
|
||||||
<path d="M28.25,62.25c0.1,5.67,4.4,11.33,2,17c-4.32-1.01-6.57-4.09-9-7c-3.15-0.48-2.26,3.07-6,2c-0.67,5.06,2.29,7.57-1,10
|
|
||||||
c-4.7-0.63-6.66-4-8-8c-2.61-1.38-5.48-2.52-6-6c0.14-3.53,4.48-2.85,7-4c0.47-5.53-1.41-13.41,2-16c8.31,0.49,8.21,7.13,7,15
|
|
||||||
c4.36,0.29,4.94-4.35,5-7c0.06-2.43-1.82-8.26,2-11c3.06-0.73,2.94,1.73,6,1C32.6,52.95,28.17,57.69,28.25,62.25z"/>
|
|
||||||
<path d="M24.25,12.25c1.07,7.07-3.86,8.14-6,12c0.21,6.88-0.47,12.86-2,18c-5.86-1.32-8.7-10.38-6-17c-0.33-3.52-5.26-4.22-7-8
|
|
||||||
c-0.3-0.66-0.47-4.43-1-7c-0.91-4.37-1.45-5.69,1-9c8.16-0.49,7.21,8.13,9,14c5.05,0.39,3.91-5.42,8-6
|
|
||||||
C21.23,10.6,22.92,11.25,24.25,12.25z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.7 KiB |
|
@ -1,401 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This JS file is loaded :
|
|
||||||
- in the YunoHost user portal
|
|
||||||
- on every app page if the app nginx's conf does include the ynh snippet
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
=====================
|
|
||||||
Utilities
|
|
||||||
=====================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Console log fix */
|
|
||||||
if (typeof(console) === 'undefined') {
|
|
||||||
var console = {};
|
|
||||||
console.log = console.error = console.info = console.debug = console.warn = console.trace = console.dir = console.dirxml = console.group = console.groupEnd = console.time = console.timeEnd = console.assert = console.profile = function() {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cookies utilities */
|
|
||||||
function setCookie(cName, cValue, expDays) {
|
|
||||||
let date = new Date();
|
|
||||||
date.setTime(date.getTime() + (expDays * 24 * 60 * 60 * 1000));
|
|
||||||
const expires = "expires=" + date.toUTCString();
|
|
||||||
document.cookie = cName + "=" + cValue + "; " + expires + "; path=/";
|
|
||||||
}
|
|
||||||
function getCookie(cName) {
|
|
||||||
const name = cName + "=";
|
|
||||||
const cDecoded = decodeURIComponent(document.cookie); //to be careful
|
|
||||||
const cArr = cDecoded .split('; ');
|
|
||||||
let res;
|
|
||||||
cArr.forEach(val => {
|
|
||||||
if (val.indexOf(name) === 0) res = val.substring(name.length);
|
|
||||||
})
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Array utilities
|
|
||||||
https://github.com/Darklg/JavaScriptUtilities/blob/master/assets/js/vanilla-js/libs/vanilla-arrays.js
|
|
||||||
-------------------------- */
|
|
||||||
Array.contains = function(needle, haystack) {
|
|
||||||
var i = 0,
|
|
||||||
length = haystack.length;
|
|
||||||
|
|
||||||
for (; i < length; i++) {
|
|
||||||
if (haystack[i] === needle) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
Array.each = function(arrayToParse, callback) {
|
|
||||||
var i = 0,
|
|
||||||
length = arrayToParse.length;
|
|
||||||
for (; i < length; i++) {
|
|
||||||
callback(arrayToParse[i]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* CSS classes utilities
|
|
||||||
https://github.com/Darklg/JavaScriptUtilities/blob/master/assets/js/vanilla-js/libs/vanilla-classes.js
|
|
||||||
-------------------------- */
|
|
||||||
Element.getClassNames = function(element) {
|
|
||||||
var classNames = [],
|
|
||||||
elementClassName = element.className;
|
|
||||||
if (elementClassName !== '') {
|
|
||||||
elementClassName = elementClassName.replace(/\s+/g, ' ');
|
|
||||||
classNames = elementClassName.split(' ');
|
|
||||||
}
|
|
||||||
return classNames;
|
|
||||||
};
|
|
||||||
Element.hasClass = function(element, className) {
|
|
||||||
if (element.classList) {
|
|
||||||
return element.classList.contains(className);
|
|
||||||
}
|
|
||||||
return Array.contains(className, Element.getClassNames(element));
|
|
||||||
};
|
|
||||||
Element.addClass = function(element, className) {
|
|
||||||
if (element.classList) {
|
|
||||||
element.classList.add(className);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!Element.hasClass(element, className)) {
|
|
||||||
var elementClasses = Element.getClassNames(element);
|
|
||||||
elementClasses.push(className);
|
|
||||||
element.className = elementClasses.join(' ');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Element.removeClass = function(element, className) {
|
|
||||||
if (element.classList) {
|
|
||||||
element.classList.remove(className);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var elementClasses = Element.getClassNames(element);
|
|
||||||
var newElementClasses = [];
|
|
||||||
var i = 0,
|
|
||||||
arLength = elementClasses.length;
|
|
||||||
for (; i < arLength; i++) {
|
|
||||||
if (elementClasses[i] !== className) {
|
|
||||||
newElementClasses.push(elementClasses[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
element.className = newElementClasses.join(' ');
|
|
||||||
};
|
|
||||||
Element.toggleClass = function(element, className) {
|
|
||||||
if (!Element.hasClass(element, className)) {
|
|
||||||
Element.addClass(element, className);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Element.removeClass(element, className);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Add Event
|
|
||||||
https://github.com/Darklg/JavaScriptUtilities/blob/master/assets/js/vanilla-js/libs/vanilla-events.js
|
|
||||||
-------------------------- */
|
|
||||||
window.addEvent = function(el, eventName, callback, options) {
|
|
||||||
if (el == null) { return; }
|
|
||||||
if (el.addEventListener) {
|
|
||||||
if (!options || typeof(options) !== "object") {
|
|
||||||
options = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
options.capture = false;
|
|
||||||
el.addEventListener(eventName, callback, options);
|
|
||||||
}
|
|
||||||
else if (el.attachEvent) {
|
|
||||||
el.attachEvent("on" + eventName, function(e) {
|
|
||||||
return callback.call(el, e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
window.eventPreventDefault = function(event) {
|
|
||||||
return (event.preventDefault) ? event.preventDefault() : event.returnValue = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Draggable
|
|
||||||
|
|
||||||
Sources :
|
|
||||||
http://jsfiddle.net/5t3Ju/
|
|
||||||
http://stackoverflow.com/questions/9334084/moveable-draggable-div
|
|
||||||
http://jsfiddle.net/tovic/Xcb8d/light/
|
|
||||||
-------------------------- */
|
|
||||||
|
|
||||||
function make_element_draggable(id) {
|
|
||||||
|
|
||||||
// Variables
|
|
||||||
this.elem = document.getElementById(id),
|
|
||||||
this.selected = null, // Selected element
|
|
||||||
this.dragged = false, // Dragging status
|
|
||||||
this.x_pos = 0, this.y_pos = 0, // Stores x & y coordinates of the mouse pointer
|
|
||||||
this.x_elem = 0, this.y_elem = 0; // Stores top, left values (edge) of the element
|
|
||||||
|
|
||||||
var _initDrag = function(e){
|
|
||||||
if (e.type === "touchstart"){
|
|
||||||
x_pos = e.touches[0].clientX;
|
|
||||||
y_pos = e.touches[0].clientY;
|
|
||||||
}
|
|
||||||
|
|
||||||
selected = elem;
|
|
||||||
x_elem = x_pos - selected.offsetLeft;
|
|
||||||
y_elem = y_pos - selected.offsetTop;
|
|
||||||
|
|
||||||
// We add listening event for the iframe itself ...
|
|
||||||
// otherwise dragging the tile on the iframe doesn't
|
|
||||||
// work properly.
|
|
||||||
// We do this at click time to have a better chance
|
|
||||||
// that the iframe's body is indeed loaded ...
|
|
||||||
// (a bit hackish but meh)
|
|
||||||
portalOverlay = document.getElementById("ynh-overlay").contentDocument.body;
|
|
||||||
window.addEvent(portalOverlay, 'mousemove', _onMove);
|
|
||||||
window.addEvent(portalOverlay, 'touchmove', _onMove, {passive: false});
|
|
||||||
};
|
|
||||||
|
|
||||||
var _shutDrag = function(e){
|
|
||||||
selected = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
var _onMove = function(e){
|
|
||||||
// Get position
|
|
||||||
x_pos = document.all ? window.event: e.pageX;
|
|
||||||
y_pos = document.all ? window.event : e.pageY;
|
|
||||||
|
|
||||||
if (e.type === "touchmove") {
|
|
||||||
x_pos = e.touches[0].clientX;
|
|
||||||
y_pos = e.touches[0].clientY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selected !== null) {
|
|
||||||
if (e.type === "touchmove"){
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
dragged = true;
|
|
||||||
selected.style.left = (x_pos - x_elem) + 'px';
|
|
||||||
selected.style.top = (y_pos - y_elem) + 'px';
|
|
||||||
// Store positions in cookies
|
|
||||||
setCookie('ynh_overlay_top', selected.style.top, 30);
|
|
||||||
setCookie('ynh_overlay_left', selected.style.left, 30);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Prevent native D'n'D behavior
|
|
||||||
window.addEvent(elem, 'dragstart', function(e){
|
|
||||||
window.eventPreventDefault(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start dragging
|
|
||||||
window.addEvent(elem, 'mousedown', _initDrag);
|
|
||||||
window.addEvent(elem, 'touchstart', _initDrag);
|
|
||||||
|
|
||||||
// Will be called when user dragging an element
|
|
||||||
window.addEvent(window, 'mousemove', _onMove);
|
|
||||||
window.addEvent(window, 'touchmove', _onMove, {passive: false});
|
|
||||||
|
|
||||||
// Destroy the object when we are done
|
|
||||||
window.addEvent(window, 'mouseup', _shutDrag);
|
|
||||||
window.addEvent(window, 'touchend', _shutDrag);
|
|
||||||
window.addEvent(window, 'touchcancel', _shutDrag);
|
|
||||||
|
|
||||||
// Handle click event
|
|
||||||
window.addEvent(elem, 'click', function(e){
|
|
||||||
// Prevent default event
|
|
||||||
window.eventPreventDefault(e);
|
|
||||||
|
|
||||||
// Do not propagate to other click event if dragged out
|
|
||||||
if (dragged) {
|
|
||||||
e.stopImmediatePropagation();
|
|
||||||
}
|
|
||||||
// Reset dragging status
|
|
||||||
dragged = false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------
|
|
||||||
Main
|
|
||||||
---------------------------------------------------------- */
|
|
||||||
window.addEvent(document, 'DOMContentLoaded', function() {
|
|
||||||
|
|
||||||
// 3 different cases :
|
|
||||||
// - this script is loaded from inside an app
|
|
||||||
// - this script is loaded inside the portal, inside an iframe/overlay activated by clicking the portal button inside an app
|
|
||||||
// - this script is loaded inside the "regular" portal when going to /yunohost/sso.
|
|
||||||
|
|
||||||
var in_app = ! document.body.classList.contains('ynh-user-portal');
|
|
||||||
var in_overlay_iframe = (window.location != window.parent.location);
|
|
||||||
|
|
||||||
if (in_app)
|
|
||||||
{
|
|
||||||
// Do not load inside an app iframe (Roundcube visualisation panel for example).
|
|
||||||
if (window.frameElement == null) {
|
|
||||||
init_portal_button_and_overlay();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
init_portal();
|
|
||||||
if (in_overlay_iframe) { tweak_portal_when_in_iframe(); }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
|
||||||
// This function is called when ynh_portal.js is included in an app
|
|
||||||
//
|
|
||||||
// It will create the small yunohost "portal button" usually in the bottom
|
|
||||||
// right corner and initialize the portal overlay, shown when clicking the
|
|
||||||
// portal button meant to make it easier to switch between apps.
|
|
||||||
//
|
|
||||||
function init_portal_button_and_overlay()
|
|
||||||
{
|
|
||||||
// Set and store meta viewport
|
|
||||||
var meta_viewport = document.querySelector('meta[name="viewport"]');
|
|
||||||
if (meta_viewport === null) {
|
|
||||||
meta_viewport = document.createElement('meta');
|
|
||||||
meta_viewport.setAttribute('name', "viewport");
|
|
||||||
meta_viewport.setAttribute('content', "");
|
|
||||||
document.getElementsByTagName('head')[0].insertBefore(meta_viewport, null);
|
|
||||||
}
|
|
||||||
meta_viewport = document.querySelector('meta[name="viewport"]');
|
|
||||||
meta_viewport_content = meta_viewport.getAttribute('content');
|
|
||||||
|
|
||||||
// Prepare and inject the portal overlay (what is activated when clicking on the portal button)
|
|
||||||
var portalOverlay = document.createElement('iframe');
|
|
||||||
portalOverlay.src = "/yunohost/sso/portal.html";
|
|
||||||
portalOverlay.setAttribute("id","ynh-overlay");
|
|
||||||
portalOverlay.setAttribute("style","display: none;"); // make sure the overlay is invisible already when loading it
|
|
||||||
// portalOverlay.setAttribute("class","ynh-fadeOut"); // set overlay as masked when loading it
|
|
||||||
document.body.insertBefore(portalOverlay, null);
|
|
||||||
|
|
||||||
// Inject portal button
|
|
||||||
var portalButton = document.createElement('a');
|
|
||||||
portalButton.setAttribute('id', 'ynh-overlay-switch');
|
|
||||||
portalButton.setAttribute('href', '/yunohost/sso/');
|
|
||||||
portalButton.setAttribute('class', 'disableAjax');
|
|
||||||
// Checks if cookies exist and apply positioning
|
|
||||||
if (getCookie('ynh_overlay_top') != null && getCookie('ynh_overlay_left') != null) {
|
|
||||||
portalButton.style.top = getCookie('ynh_overlay_top');
|
|
||||||
portalButton.style.left = getCookie('ynh_overlay_left');
|
|
||||||
}
|
|
||||||
document.body.insertBefore(portalButton, null);
|
|
||||||
// Make portal button draggable, for user convenience
|
|
||||||
make_element_draggable('ynh-overlay-switch');
|
|
||||||
|
|
||||||
// Bind portal button
|
|
||||||
window.addEvent(portalButton, 'click', function(e){
|
|
||||||
// Prevent default click
|
|
||||||
window.eventPreventDefault(e);
|
|
||||||
// Toggle overlay on YNHPortal button click
|
|
||||||
Element.toggleClass(document.querySelector('html'), 'ynh-panel-active');
|
|
||||||
Element.toggleClass(portalOverlay, 'ynh-active');
|
|
||||||
|
|
||||||
if (Element.hasClass(portalOverlay, 'ynh-active')) {
|
|
||||||
portalOverlay.setAttribute("style","display: block;");
|
|
||||||
meta_viewport.setAttribute('content', meta_viewport_content);
|
|
||||||
Element.addClass(portalOverlay, 'ynh-fadeIn');
|
|
||||||
Element.removeClass(portalOverlay, 'ynh-fadeOut');
|
|
||||||
} else {
|
|
||||||
portalOverlay.setAttribute("style","display: none;");
|
|
||||||
meta_viewport.setAttribute('content', "width=device-width");
|
|
||||||
Element.removeClass(portalOverlay, 'ynh-fadeIn');
|
|
||||||
Element.addClass(portalOverlay, 'ynh-fadeOut');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// This function is called to initialize elements like the app tile colors and other things ...
|
|
||||||
//
|
|
||||||
function init_portal()
|
|
||||||
{
|
|
||||||
|
|
||||||
window.addEvent(document.getElementById('add-mailalias'), "click", function() {
|
|
||||||
// Clone last input.
|
|
||||||
var inputAliasClone = document.querySelector('.mailalias-input').cloneNode(true);
|
|
||||||
// Empty value.
|
|
||||||
inputAliasClone.value = '';
|
|
||||||
// Append to form-group.
|
|
||||||
this.parentNode.insertBefore(inputAliasClone, this);
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEvent(document.getElementById('add-maildrop'), "click", function() {
|
|
||||||
// Clone last input.
|
|
||||||
var inputDropClone = document.querySelector('.maildrop-input').cloneNode(true);
|
|
||||||
// Empty value.
|
|
||||||
inputDropClone.value = '';
|
|
||||||
// Append to form-group.
|
|
||||||
this.parentNode.insertBefore(inputDropClone, this);
|
|
||||||
});
|
|
||||||
|
|
||||||
Array.each(document.getElementsByClassName("app-tile"), function(el) {
|
|
||||||
// Set first-letter data attribute.
|
|
||||||
el.querySelector('.first-letter').innerHTML = el.getAttribute("data-appname").substring(0, 2);
|
|
||||||
// handle app links so they work both in plain info page and in the info iframe called from ynh_portal.js
|
|
||||||
window.addEvent(el, 'click', function(event) {
|
|
||||||
// if asked to open in new tab
|
|
||||||
if (event.ctrlKey || event.shiftKey || event.metaKey
|
|
||||||
|| (event.button && event.button == 1)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// if asked in current tab
|
|
||||||
else {
|
|
||||||
event.preventDefault();
|
|
||||||
parent.location.href=this.href;
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function tweak_portal_when_in_iframe()
|
|
||||||
{
|
|
||||||
// Set class to body to show we're in overlay
|
|
||||||
document.body.classList.add('in_app_overlay');
|
|
||||||
let userContainer = document.querySelector('a.user-container');
|
|
||||||
if (userContainer) {
|
|
||||||
userContainer.classList.replace('user-container-info', 'user-container-edit');
|
|
||||||
userContainer.setAttribute('href', userContainer
|
|
||||||
.getAttribute('href')
|
|
||||||
.replace('edit.html', ''));
|
|
||||||
window.addEvent(userContainer, 'click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
window.parent.location.href = userContainer.getAttribute('href');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let logoutButton = document.getElementById('ynh-logout');
|
|
||||||
if (logoutButton)
|
|
||||||
{
|
|
||||||
// We force to do the logout "globally", not just in the
|
|
||||||
// iframe, otherwise after login out the url might still be
|
|
||||||
// domain.tld/app which is weird ...
|
|
||||||
window.addEvent(logoutButton, 'click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
window.parent.location.href = logoutButton.getAttribute("href");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 299 KiB |
Before Width: | Height: | Size: 25 KiB |
|
@ -1,17 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This file may contain extra CSS rules loaded on all apps page (*if* the app
|
|
||||||
nginx's conf does include the appropriate snippet) for the small YunoHost
|
|
||||||
button in bottom-right corner + portal overlay.
|
|
||||||
|
|
||||||
The yunohost button corresponds to : #ynh-overlay-switch
|
|
||||||
The yunohost portal overlay / iframe corresponds to : #ynh-overlay
|
|
||||||
|
|
||||||
BE CAREFUL that you should *not* add too-general rules that apply to
|
|
||||||
non-yunohost elements (for instance all 'a' or 'p' elements...) as it will
|
|
||||||
likely break app's rendering
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
#ynh-overlay-switch {
|
|
||||||
background-image: url("./cloud.png");
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This file contain extra CSS rules to customize the YunoHost user portal and
|
|
||||||
can be used to customize app tiles, buttons, etc...
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Make page texts black */
|
|
||||||
.user-container h2,
|
|
||||||
.user-container small,
|
|
||||||
.user-container .user-mail,
|
|
||||||
.user-container .user-mail,
|
|
||||||
.content .footer a,
|
|
||||||
a.app-tile,
|
|
||||||
#ynh-logout {
|
|
||||||
color: black !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ynh-user-portal {
|
|
||||||
background-image: url("background.jpg");
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apps colors */
|
|
||||||
.app-tile {
|
|
||||||
background-color: rgba(255, 255, 255, 0.5) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-tile:hover:after,
|
|
||||||
.app-tile:focus:after,
|
|
||||||
.app-tile:hover:before,
|
|
||||||
.app-tile:focus:before {
|
|
||||||
background: rgba(255, 255, 255, 0.5) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use a custom logo image */
|
|
||||||
#ynh-logo {
|
|
||||||
z-index: 10;
|
|
||||||
background-image: url("./cloud.png");
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This JS file may be used to customize the YunoHost user portal *and* also
|
|
||||||
will be loaded in all app pages if the app nginx's conf does include the
|
|
||||||
appropriate snippet.
|
|
||||||
|
|
||||||
You can monkeypatch init_portal (loading of the user portal) and
|
|
||||||
init_portal_button_and_overlay (loading of the button and overlay...) to do
|
|
||||||
custom stuff
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Monkeypatch init_portal to customize the app tile style
|
|
||||||
*
|
|
||||||
init_portal_original = init_portal;
|
|
||||||
init_portal = function()
|
|
||||||
{
|
|
||||||
init_portal_original();
|
|
||||||
// Some stuff here
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Monkey patching example to do custom stuff when loading inside an app
|
|
||||||
*
|
|
||||||
init_portal_button_and_overlay_original = init_portal_button_and_overlay;
|
|
||||||
init_portal_button_and_overlay = function()
|
|
||||||
{
|
|
||||||
init_portal_button_and_overlay_original();
|
|
||||||
// Custom stuff to do when loading inside an app
|
|
||||||
}
|
|
||||||
*/
|
|
|
@ -1,14 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This file may contain extra CSS rules loaded on all apps page (*if* the app
|
|
||||||
nginx's conf does include the appropriate snippet) for the small YunoHost
|
|
||||||
button in bottom-right corner + portal overlay.
|
|
||||||
|
|
||||||
The yunohost button corresponds to : #ynh-overlay-switch
|
|
||||||
The yunohost portal overlay / iframe corresponds to : #ynh-overlay
|
|
||||||
|
|
||||||
BE CAREFUL that you should *not* add too-general rules that apply to
|
|
||||||
non-yunohost elements (for instance all 'a' or 'p' elements...) as it will
|
|
||||||
likely break app's rendering
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
|
@ -1,145 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This file contain extra CSS rules to customize the YunoHost user portal and
|
|
||||||
can be used to customize app tiles, buttons, etc...
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
.bluebg {
|
|
||||||
background: #3498DB!important;
|
|
||||||
}
|
|
||||||
.bluebg:hover:after,
|
|
||||||
.bluebg:focus:after,
|
|
||||||
.bluebg:hover:before,
|
|
||||||
.bluebg:focus:before {
|
|
||||||
background: #16527A!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.purplebg {
|
|
||||||
background: #9B59B6!important;
|
|
||||||
}
|
|
||||||
.purplebg:hover:after,
|
|
||||||
.purplebg:focus:after,
|
|
||||||
.purplebg:hover:before,
|
|
||||||
.purplebg:focus:before {
|
|
||||||
background: #532C64!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.redbg {
|
|
||||||
background: #E74C3C!important;
|
|
||||||
}
|
|
||||||
.redbg:hover:after,
|
|
||||||
.redbg:focus:after,
|
|
||||||
.redbg:hover:before,
|
|
||||||
.redbg:focus:before {
|
|
||||||
background: #921E12!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.orangebg {
|
|
||||||
background: #F39C12!important;
|
|
||||||
}
|
|
||||||
.orangebg:hover:after,
|
|
||||||
.orangebg:focus:after,
|
|
||||||
.orangebg:hover:before,
|
|
||||||
.orangebg:focus:before {
|
|
||||||
background: #7F5006!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.greenbg {
|
|
||||||
background: #2ECC71!important;
|
|
||||||
}
|
|
||||||
.greenbg:hover:after,
|
|
||||||
.greenbg:focus:after,
|
|
||||||
.greenbg:hover:before,
|
|
||||||
.greenbg:focus:before {
|
|
||||||
background: #176437!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.darkbluebg {
|
|
||||||
background: #34495E!important;
|
|
||||||
}
|
|
||||||
.darkbluebg:hover:after,
|
|
||||||
.darkbluebg:focus:after,
|
|
||||||
.darkbluebg:hover:before,
|
|
||||||
.darkbluebg:focus:before {
|
|
||||||
background: #07090C!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightbluebg {
|
|
||||||
background: #6A93D4!important;
|
|
||||||
}
|
|
||||||
.lightbluebg:hover:after,
|
|
||||||
.lightbluebg:focus:after,
|
|
||||||
.lightbluebg:hover:before,
|
|
||||||
.lightbluebg:focus:before {
|
|
||||||
background: #2B5394!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.yellowbg {
|
|
||||||
background: #F1C40F!important;
|
|
||||||
}
|
|
||||||
.yellowbg:hover:after,
|
|
||||||
.yellowbg:focus:after,
|
|
||||||
.yellowbg:hover:before,
|
|
||||||
.yellowbg:focus:before {
|
|
||||||
background: #796307!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.lightpinkbg {
|
|
||||||
background: #F76F87!important;
|
|
||||||
}
|
|
||||||
.lightpinkbg:hover:after,
|
|
||||||
.lightpinkbg:focus:after,
|
|
||||||
.lightpinkbg:hover:before,
|
|
||||||
.lightpinkbg:focus:before {
|
|
||||||
background: #DA0C31!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Following colors are not used yet */
|
|
||||||
.pinkbg {
|
|
||||||
background: #D66D92!important;
|
|
||||||
}
|
|
||||||
.pinkbg:hover:after,
|
|
||||||
.pinkbg:focus:after,
|
|
||||||
.pinkbg:hover:before,
|
|
||||||
.pinkbg:focus:before {
|
|
||||||
background: #992B52!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.turquoisebg {
|
|
||||||
background: #1ABC9C!important;
|
|
||||||
}
|
|
||||||
.turquoisebg:hover:after,
|
|
||||||
.turquoisebg:focus:after,
|
|
||||||
.turquoisebg:hover:before,
|
|
||||||
.turquoisebg:focus:before {
|
|
||||||
background: #0B4C3F!important;
|
|
||||||
}
|
|
||||||
.lightyellow {
|
|
||||||
background: #FFC973!important;
|
|
||||||
}
|
|
||||||
.lightyellow:hover:after,
|
|
||||||
.lightyellow:focus:after,
|
|
||||||
.lightyellow:hover:before,
|
|
||||||
.lightyellow:focus:before {
|
|
||||||
background: #F39500!important;
|
|
||||||
}
|
|
||||||
.lightgreen {
|
|
||||||
background: #B5F36D!important;
|
|
||||||
}
|
|
||||||
.lightgreen:hover:after,
|
|
||||||
.lightgreen:focus:after,
|
|
||||||
.lightgreen:hover:before,
|
|
||||||
.lightgreen:focus:before {
|
|
||||||
background: #77CF11!important;
|
|
||||||
}
|
|
||||||
.purpledarkbg {
|
|
||||||
background: #8E44AD!important;
|
|
||||||
}
|
|
||||||
.purpledarkbg:hover:after,
|
|
||||||
.purpledarkbg:focus:after,
|
|
||||||
.purpledarkbg:hover:before,
|
|
||||||
.purpledarkbg:focus:before {
|
|
||||||
background: #432051!important;
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This JS file may be used to customize the YunoHost user portal *and* also
|
|
||||||
will be loaded in all app pages if the app nginx's conf does include the
|
|
||||||
appropriate snippet.
|
|
||||||
|
|
||||||
You can monkeypatch init_portal (loading of the user portal) and
|
|
||||||
init_portal_button_and_overlay (loading of the button and overlay...) to do
|
|
||||||
custom stuff
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
var app_tile_colors = ['redbg','purpledarkbg','darkbluebg','orangebg','greenbg', 'yellowbg','lightpinkbg','pinkbg','turquoisebg','lightbluebg', 'bluebg'];
|
|
||||||
|
|
||||||
function set_app_tile_style(el)
|
|
||||||
{
|
|
||||||
// Select a color value from the App label
|
|
||||||
randomColorNumber = parseInt(el.textContent, 36) % app_tile_colors.length;
|
|
||||||
// Add color class.
|
|
||||||
el.classList.add(app_tile_colors[randomColorNumber]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Monkeypatch init_portal to customize the app tile style
|
|
||||||
init_portal_original = init_portal;
|
|
||||||
init_portal = function()
|
|
||||||
{
|
|
||||||
init_portal_original();
|
|
||||||
Array.each(document.getElementsByClassName("app-tile"), set_app_tile_style);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Monkey patching example to do custom stuff when loading inside an app
|
|
||||||
*
|
|
||||||
init_portal_button_and_overlay_original = init_portal_button_and_overlay;
|
|
||||||
init_portal_button_and_overlay = function()
|
|
||||||
{
|
|
||||||
init_portal_button_and_overlay_original();
|
|
||||||
// Custom stuff to do when loading inside an app
|
|
||||||
}
|
|
||||||
*/
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This file may contain extra CSS rules loaded on all apps page (*if* the app
|
|
||||||
nginx's conf does include the appropriate snippet) for the small YunoHost
|
|
||||||
button in bottom-right corner + portal overlay.
|
|
||||||
|
|
||||||
The yunohost button corresponds to : #ynh-overlay-switch
|
|
||||||
The yunohost portal overlay / iframe corresponds to : #ynh-overlay
|
|
||||||
|
|
||||||
BE CAREFUL that you should *not* add too-general rules that apply to
|
|
||||||
non-yunohost elements (for instance all 'a' or 'p' elements...) as it will
|
|
||||||
likely break app's rendering
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ynh-overlay-switch {
|
|
||||||
/* FIXME : idk if this is an issue or not to have /yunohost/sso hard-coded here */
|
|
||||||
background-image: url("/yunohost/sso/assets/img/logo-ynh.svg");
|
|
||||||
border-color: #eee;
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ynh-overlay-switch:hover {
|
|
||||||
border-color: #ccc;
|
|
||||||
background-color: #ccc;
|
|
||||||
}
|
|
|
@ -1,179 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This file contain extra CSS rules to customize the YunoHost user portal and
|
|
||||||
can be used to customize app tiles, buttons, etc...
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ynh-logo {
|
|
||||||
background-image: url("../../img/logo-ynh.svg");
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form .form-group {
|
|
||||||
border: 1px solid #bbb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-container,
|
|
||||||
.user-menu a,
|
|
||||||
.link-btn,
|
|
||||||
.footer a {
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-menu a:hover,
|
|
||||||
.footer a:hover {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-text:disabled:hover {
|
|
||||||
background: #797b83;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link-btn,
|
|
||||||
.link-btn:hover {
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.bluebg {
|
|
||||||
background: #3498DB!important;
|
|
||||||
}
|
|
||||||
.bluebg:hover:after,
|
|
||||||
.bluebg:focus:after,
|
|
||||||
.bluebg:hover:before,
|
|
||||||
.bluebg:focus:before {
|
|
||||||
background: #16527A!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.purplebg {
|
|
||||||
background: #9B59B6!important;
|
|
||||||
}
|
|
||||||
.purplebg:hover:after,
|
|
||||||
.purplebg:focus:after,
|
|
||||||
.purplebg:hover:before,
|
|
||||||
.purplebg:focus:before {
|
|
||||||
background: #532C64!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.redbg {
|
|
||||||
background: #E74C3C!important;
|
|
||||||
}
|
|
||||||
.redbg:hover:after,
|
|
||||||
.redbg:focus:after,
|
|
||||||
.redbg:hover:before,
|
|
||||||
.redbg:focus:before {
|
|
||||||
background: #921E12!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.orangebg {
|
|
||||||
background: #F39C12!important;
|
|
||||||
}
|
|
||||||
.orangebg:hover:after,
|
|
||||||
.orangebg:focus:after,
|
|
||||||
.orangebg:hover:before,
|
|
||||||
.orangebg:focus:before {
|
|
||||||
background: #7F5006!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.greenbg {
|
|
||||||
background: #2ECC71!important;
|
|
||||||
}
|
|
||||||
.greenbg:hover:after,
|
|
||||||
.greenbg:focus:after,
|
|
||||||
.greenbg:hover:before,
|
|
||||||
.greenbg:focus:before {
|
|
||||||
background: #176437!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.darkbluebg {
|
|
||||||
background: #34495E!important;
|
|
||||||
}
|
|
||||||
.darkbluebg:hover:after,
|
|
||||||
.darkbluebg:focus:after,
|
|
||||||
.darkbluebg:hover:before,
|
|
||||||
.darkbluebg:focus:before {
|
|
||||||
background: #07090C!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightbluebg {
|
|
||||||
background: #6A93D4!important;
|
|
||||||
}
|
|
||||||
.lightbluebg:hover:after,
|
|
||||||
.lightbluebg:focus:after,
|
|
||||||
.lightbluebg:hover:before,
|
|
||||||
.lightbluebg:focus:before {
|
|
||||||
background: #2B5394!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.yellowbg {
|
|
||||||
background: #F1C40F!important;
|
|
||||||
}
|
|
||||||
.yellowbg:hover:after,
|
|
||||||
.yellowbg:focus:after,
|
|
||||||
.yellowbg:hover:before,
|
|
||||||
.yellowbg:focus:before {
|
|
||||||
background: #796307!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.lightpinkbg {
|
|
||||||
background: #F76F87!important;
|
|
||||||
}
|
|
||||||
.lightpinkbg:hover:after,
|
|
||||||
.lightpinkbg:focus:after,
|
|
||||||
.lightpinkbg:hover:before,
|
|
||||||
.lightpinkbg:focus:before {
|
|
||||||
background: #DA0C31!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Following colors are not used yet */
|
|
||||||
.pinkbg {
|
|
||||||
background: #D66D92!important;
|
|
||||||
}
|
|
||||||
.pinkbg:hover:after,
|
|
||||||
.pinkbg:focus:after,
|
|
||||||
.pinkbg:hover:before,
|
|
||||||
.pinkbg:focus:before {
|
|
||||||
background: #992B52!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.turquoisebg {
|
|
||||||
background: #1ABC9C!important;
|
|
||||||
}
|
|
||||||
.turquoisebg:hover:after,
|
|
||||||
.turquoisebg:focus:after,
|
|
||||||
.turquoisebg:hover:before,
|
|
||||||
.turquoisebg:focus:before {
|
|
||||||
background: #0B4C3F!important;
|
|
||||||
}
|
|
||||||
.lightyellow {
|
|
||||||
background: #FFC973!important;
|
|
||||||
}
|
|
||||||
.lightyellow:hover:after,
|
|
||||||
.lightyellow:focus:after,
|
|
||||||
.lightyellow:hover:before,
|
|
||||||
.lightyellow:focus:before {
|
|
||||||
background: #F39500!important;
|
|
||||||
}
|
|
||||||
.lightgreen {
|
|
||||||
background: #B5F36D!important;
|
|
||||||
}
|
|
||||||
.lightgreen:hover:after,
|
|
||||||
.lightgreen:focus:after,
|
|
||||||
.lightgreen:hover:before,
|
|
||||||
.lightgreen:focus:before {
|
|
||||||
background: #77CF11!important;
|
|
||||||
}
|
|
||||||
.purpledarkbg {
|
|
||||||
background: #8E44AD!important;
|
|
||||||
}
|
|
||||||
.purpledarkbg:hover:after,
|
|
||||||
.purpledarkbg:focus:after,
|
|
||||||
.purpledarkbg:hover:before,
|
|
||||||
.purpledarkbg:focus:before {
|
|
||||||
background: #432051!important;
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This JS file may be used to customize the YunoHost user portal *and* also
|
|
||||||
will be loaded in all app pages if the app nginx's conf does include the
|
|
||||||
appropriate snippet.
|
|
||||||
|
|
||||||
You can monkeypatch init_portal (loading of the user portal) and
|
|
||||||
init_portal_button_and_overlay (loading of the button and overlay...) to do
|
|
||||||
custom stuff
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
var app_tile_colors = ['redbg','purpledarkbg','darkbluebg','orangebg','greenbg', 'yellowbg','lightpinkbg','pinkbg','turquoisebg','lightbluebg', 'bluebg'];
|
|
||||||
|
|
||||||
function set_app_tile_style(el)
|
|
||||||
{
|
|
||||||
// Select a color value from the App label
|
|
||||||
randomColorNumber = parseInt(el.textContent, 36) % app_tile_colors.length;
|
|
||||||
// Add color class.
|
|
||||||
el.classList.add(app_tile_colors[randomColorNumber]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Monkeypatch init_portal to customize the app tile style
|
|
||||||
init_portal_original = init_portal;
|
|
||||||
init_portal = function()
|
|
||||||
{
|
|
||||||
init_portal_original();
|
|
||||||
Array.each(document.getElementsByClassName("app-tile"), set_app_tile_style);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Monkey patching example to do custom stuff when loading inside an app
|
|
||||||
*
|
|
||||||
init_portal_button_and_overlay_original = init_portal_button_and_overlay;
|
|
||||||
init_portal_button_and_overlay = function()
|
|
||||||
{
|
|
||||||
init_portal_button_and_overlay_original();
|
|
||||||
// Custom stuff to do when loading inside an app
|
|
||||||
}
|
|
||||||
*/
|
|
Before Width: | Height: | Size: 25 KiB |
|
@ -1,17 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This file may contain extra CSS rules loaded on all apps page (*if* the app
|
|
||||||
nginx's conf does include the appropriate snippet) for the small YunoHost
|
|
||||||
button in bottom-right corner + portal overlay.
|
|
||||||
|
|
||||||
The yunohost button corresponds to : #ynh-overlay-switch
|
|
||||||
The yunohost portal overlay / iframe corresponds to : #ynh-overlay
|
|
||||||
|
|
||||||
BE CAREFUL that you should *not* add too-general rules that apply to
|
|
||||||
non-yunohost elements (for instance all 'a' or 'p' elements...) as it will
|
|
||||||
likely break app's rendering
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
#ynh-overlay-switch {
|
|
||||||
background-image: url("./cloud.png");
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This file contain extra CSS rules to customize the YunoHost user portal and
|
|
||||||
can be used to customize app tiles, buttons, etc...
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Make page texts white */
|
|
||||||
.user-container h2,
|
|
||||||
.user-container small,
|
|
||||||
.user-container .user-mail,
|
|
||||||
.user-container .user-mail,
|
|
||||||
.content .footer a,
|
|
||||||
a.app-tile,
|
|
||||||
#ynh-logout {
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: white !important;
|
|
||||||
text-shadow: 3px 4px 4px rgba(0,0,0,.4), -1px -1px 6px rgba(0,0,0,0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ynh-user-portal {
|
|
||||||
background-image: url('https://source.unsplash.com/random/featured/?nature') !important;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apps colors */
|
|
||||||
.app-tile {
|
|
||||||
background-color: rgba(255, 255, 255, 0.5) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-tile:hover:after,
|
|
||||||
.app-tile:focus:after,
|
|
||||||
.app-tile:hover:before,
|
|
||||||
.app-tile:focus:before {
|
|
||||||
background: rgba(255, 255, 255, 0.5) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use a custom logo image */
|
|
||||||
#ynh-logo {
|
|
||||||
z-index: 10;
|
|
||||||
background-image: url("./cloud.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Round the form */
|
|
||||||
.login-form label:before {
|
|
||||||
border-top-left-radius: 5em ;
|
|
||||||
border-bottom-left-radius: 5em ;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form * {
|
|
||||||
border-radius: 5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make form black */
|
|
||||||
|
|
||||||
.login-form label::before {
|
|
||||||
background: #000;
|
|
||||||
color: #FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form .form-group * {
|
|
||||||
background: #000;
|
|
||||||
color: #FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
background: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messages {
|
|
||||||
border-radius: .5em;
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This file may contain extra CSS rules loaded on all apps page (*if* the app
|
|
||||||
nginx's conf does include the appropriate snippet) for the small YunoHost
|
|
||||||
button in bottom-right corner + portal overlay.
|
|
||||||
|
|
||||||
The yunohost button corresponds to : #ynh-overlay-switch
|
|
||||||
The yunohost portal overlay / iframe corresponds to : #ynh-overlay
|
|
||||||
|
|
||||||
BE CAREFUL that you should *not* add too-general rules that apply to
|
|
||||||
non-yunohost elements (for instance all 'a' or 'p' elements...) as it will
|
|
||||||
likely break app's rendering
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
|
@ -1,109 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This file contain extra CSS rules to customize the YunoHost user portal and
|
|
||||||
can be used to customize app tiles, buttons, etc...
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
Vaporwave theme
|
|
||||||
========================================================================== */
|
|
||||||
.ynh-user-portal {
|
|
||||||
min-height: 100vh;
|
|
||||||
background: rgb(205, 118, 255) !important;
|
|
||||||
background: -moz-linear-gradient(45deg, rgb(205, 118, 255) 0%, rgb(93, 150, 168) 100%) !important;
|
|
||||||
background: -webkit-gradient(linear, left bottom, right top, color-stop(0%, rgb(205, 118, 255)), color-stop(100%, rgb(93, 150, 168))) !important;
|
|
||||||
background: -webkit-linear-gradient(45deg, rgb(205, 118, 255) 0%, rgb(93, 150, 168) 100%) !important;
|
|
||||||
background: -o-linear-gradient(45deg, rgb(205, 118, 255) 0%, rgb(93, 150, 168) 100%) !important;
|
|
||||||
background: -ms-linear-gradient(45deg, rgb(205, 118, 255) 0%, rgb(93, 150, 168) 100%) !important;
|
|
||||||
background: linear-gradient(45deg, rgb(205, 118, 255) 0%, rgb(93, 150, 168) 100%) !important;
|
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#C82BFF', endColorstr='#0C76A8', GradientType=1) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messages.danger { background: #c0392b80; }
|
|
||||||
.messages.warning { background: #e67e2280; }
|
|
||||||
.messages.success { background: #27ae6080; }
|
|
||||||
.messages.info { background: #2980b980; }
|
|
||||||
|
|
||||||
a, small, span,
|
|
||||||
.ynh-wrapper.footer a,
|
|
||||||
.user-menu a,
|
|
||||||
.user-container.user-container-info span,
|
|
||||||
input.btn.classic-btn.large-btn {
|
|
||||||
color: #e0e0e0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group input::placeholder,
|
|
||||||
.form-group input::-ms-input-placeholder,
|
|
||||||
.form-group input:-ms-input-placeholder {
|
|
||||||
color: #f4f4f4 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
form.login-form input {
|
|
||||||
color: #222 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover,
|
|
||||||
a:active,
|
|
||||||
a:focus,
|
|
||||||
.form-group input,
|
|
||||||
input.btn.classic-btn.large-btn:hover,
|
|
||||||
.ynh-wrapper.footer a:hover {
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ynh-wrapper.footer a:before {
|
|
||||||
color: #cc45ee !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ynh-wrapper.footer nav {
|
|
||||||
border-color: #cc45ee !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.listing-apps li a span,
|
|
||||||
.listing-apps li a:hover span,
|
|
||||||
.listing-apps li a:active span,
|
|
||||||
.listing-apps li a:focus span {
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.listing-apps li,
|
|
||||||
.listing-apps li a {
|
|
||||||
transition: all 0.3s ease-in-out, background 0ms; /* fix gray flicker on initial load */
|
|
||||||
border: none transparent !important;
|
|
||||||
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.1),
|
|
||||||
-2px -2px 3px 0 rgba(0, 0, 0, 0.1) inset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.listing-apps li:hover,
|
|
||||||
.listing-apps li a:hover {
|
|
||||||
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0),
|
|
||||||
-2px -2px 3px 0 rgba(0, 0, 0, 0) inset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn.large-btn.classic-btn,
|
|
||||||
.btn.large-btn.validate-btn {
|
|
||||||
background: rgba(200, 200, 200, 0.4) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn.large-btn.classic-btn:hover,
|
|
||||||
.btn.large-btn.validate-btn:hover {
|
|
||||||
background: rgba(255, 255, 255, 0.4) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* There are no colors, there is only vapor! */
|
|
||||||
.app-tile,
|
|
||||||
.form-group input,
|
|
||||||
.form-group label,
|
|
||||||
a.btn:hover,
|
|
||||||
.btn.large-btn {
|
|
||||||
background: rgba(200, 200, 200, 0.2) !important;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-tile:hover:after,
|
|
||||||
.app-tile:focus:after,
|
|
||||||
.app-tile:hover:before,
|
|
||||||
.app-tile:focus:before {
|
|
||||||
background: rgba(200, 200, 200, 0.4) !important;
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
===============================================================================
|
|
||||||
This JS file may be used to customize the YunoHost user portal *and* also
|
|
||||||
will be loaded in all app pages if the app nginx's conf does include the
|
|
||||||
appropriate snippet.
|
|
||||||
|
|
||||||
You can monkeypatch init_portal (loading of the user portal) and
|
|
||||||
init_portal_button_and_overlay (loading of the button and overlay...) to do
|
|
||||||
custom stuff
|
|
||||||
===============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Monkeypatch init_portal to customize the app tile style
|
|
||||||
*
|
|
||||||
init_portal_original = init_portal;
|
|
||||||
init_portal = function()
|
|
||||||
{
|
|
||||||
init_portal_original();
|
|
||||||
// Some stuff here
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Monkey patching example to do custom stuff when loading inside an app
|
|
||||||
*
|
|
||||||
init_portal_button_and_overlay_original = init_portal_button_and_overlay;
|
|
||||||
init_portal_button_and_overlay = function()
|
|
||||||
{
|
|
||||||
init_portal_button_and_overlay_original();
|
|
||||||
// Custom stuff to do when loading inside an app
|
|
||||||
}
|
|
||||||
*/
|
|
|
@ -1,59 +0,0 @@
|
||||||
<div class="ynh-wrapper user">
|
|
||||||
<ul class="user-menu">
|
|
||||||
<li><a id="ynh-logout" class="icon icon-connexion" href="?action=logout">{{t_logout}}</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<a class="user-container user-container-edit" href="portal.html">
|
|
||||||
<h2 class="user-username">{{{uid}}}</h2>
|
|
||||||
<small class="user-fullname">{{givenName}} {{sn}}</small>
|
|
||||||
<span class="user-mail">{{mail}}</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ynh-wrapper edit">
|
|
||||||
<form class="form-edit" role="form" method="POST" action="edit.html">
|
|
||||||
|
|
||||||
<div class="form-section">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="uid" class="control-label">{{t_username}}</label>
|
|
||||||
<input type="text" id="uid" name="uid" class="form-text" value="{{uid}}" disabled required>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="givenName" class="control-label">{{t_fullname}}</label>
|
|
||||||
<input type="text" id="givenName" name="givenName" class="form-text" value="{{givenName}}" placeholder="{{t_firstname}}" required>
|
|
||||||
<input type="text" name="sn" class="form-text" value="{{sn}}" placeholder="{{t_lastname}}" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-section">
|
|
||||||
<div class="form-group" id="form-add-mail-alias">
|
|
||||||
<label for="mail" class="control-label">{{t_mail_addresses}}</label>
|
|
||||||
<input type="email" name="mail" class="form-text" value="{{mail}}" placeholder="{{t_new_mail}}" required>
|
|
||||||
{{#mailalias}}
|
|
||||||
<input type="email" name="mailalias[]" class="form-text" value="{{.}}">
|
|
||||||
{{/mailalias}}
|
|
||||||
<input id="mail" type="email" name="mailalias[]" class="mailalias-input form-text" placeholder="{{t_new_mail}}">
|
|
||||||
<a class="btn link-btn" id="add-mailalias">{{t_add_mail}}</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group" id="form-add-mail-drop">
|
|
||||||
<label for="mailforward" class="control-label">{{t_mail_forward}}</label>
|
|
||||||
{{#maildrop}}
|
|
||||||
<input type="email" name="maildrop[]" class="form-text" value="{{.}}">
|
|
||||||
{{/maildrop}}
|
|
||||||
<input id="mailforward" type="email" name="maildrop[]" class="maildrop-input form-text" placeholder="{{t_new_forward}}">
|
|
||||||
<a class="btn link-btn" id="add-maildrop">{{t_add_forward}}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="btn-group">
|
|
||||||
<a role="button" href="portal.html" class="btn large-btn">{{t_cancel}}</a>
|
|
||||||
<input type="submit" class="btn classic-btn large-btn" value="{{t_ok}}">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="btn-group">
|
|
||||||
<a role="button" href="password.html" class="btn validate-btn large-btn">{{t_change_password}}</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</div>
|
|
|
@ -1,18 +0,0 @@
|
||||||
{{#connected}}
|
|
||||||
<div class="ynh-wrapper footer"><nav>
|
|
||||||
<a class="link-profile-edit" href="edit.html">{{t_footerlink_edit}}</a>
|
|
||||||
<a class="link-documentation" href="//yunohost.org/docs" target="_blank">{{t_footerlink_documentation}}</a>
|
|
||||||
<a class="link-documentation" href="//yunohost.org/help" target="_blank">{{t_footerlink_support}}</a>
|
|
||||||
<a class="link-admin" href="/yunohost/admin/" target="_blank">{{t_footerlink_administration}}</a>
|
|
||||||
</nav></div>
|
|
||||||
{{/connected}}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Scripts -->
|
|
||||||
<script src="assets/js/ynh_portal.js"></script>
|
|
||||||
{{#theme}}
|
|
||||||
<script src="assets/themes/{{theme}}/custom_portal.js"></script>
|
|
||||||
{{/theme}}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,53 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>{{t_portal}}</title>
|
|
||||||
|
|
||||||
<!-- Responsive -->
|
|
||||||
<meta name="format-detection" content="telephone=no" />
|
|
||||||
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1" />
|
|
||||||
|
|
||||||
<!-- Do not index SSOWat pages -->
|
|
||||||
<meta name="robots" content="noindex, nofollow">
|
|
||||||
|
|
||||||
<!-- Stylesheets -->
|
|
||||||
<link rel="stylesheet" href="assets/css/ynh_portal.css">
|
|
||||||
<link rel="stylesheet" href="assets/themes/{{theme}}/custom_portal.css">
|
|
||||||
|
|
||||||
<!-- Icons -->
|
|
||||||
<link rel="shortcut icon" href="assets/icons/favicon.ico">
|
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="assets/icons/apple-touch-icon-57x57.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="114x114" href="assets/icons/apple-touch-icon-114x114.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="assets/icons/apple-touch-icon-72x72.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="assets/icons/apple-touch-icon-144x144.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="assets/icons/apple-touch-icon-60x60.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="120x120" href="assets/icons/apple-touch-icon-120x120.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="76x76" href="assets/icons/apple-touch-icon-76x76.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="assets/icons/apple-touch-icon-152x152.png">
|
|
||||||
<link rel="icon" type="image/png" href="assets/icons/favicon-196x196.png" sizes="196x196">
|
|
||||||
<link rel="icon" type="image/png" href="assets/icons/favicon-160x160.png" sizes="160x160">
|
|
||||||
<link rel="icon" type="image/png" href="assets/icons/favicon-96x96.png" sizes="96x96">
|
|
||||||
<link rel="icon" type="image/png" href="assets/icons/favicon-16x16.png" sizes="16x16">
|
|
||||||
<link rel="icon" type="image/png" href="assets/icons/favicon-32x32.png" sizes="32x32">
|
|
||||||
<meta name="msapplication-TileColor" content="#41444f">
|
|
||||||
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
|
|
||||||
</head>
|
|
||||||
<body class="ynh-user-portal {{#connected}}logged{{/connected}}">
|
|
||||||
|
|
||||||
<div id="ynh-logo" class="ynh-logo">
|
|
||||||
<span class="element-invisible">Yunohost</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
{{#flash_win}}
|
|
||||||
<div class="wrapper messages success">{{.}}</div>
|
|
||||||
{{/flash_win}}
|
|
||||||
|
|
||||||
{{#flash_fail}}
|
|
||||||
<div class="wrapper messages danger">{{.}}</div>
|
|
||||||
{{/flash_fail}}
|
|
||||||
|
|
||||||
{{#flash_info}}
|
|
||||||
<div class="wrapper messages info">{{.}}</div>
|
|
||||||
{{/flash_info}}
|
|
59
portal/index.html
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<center id="loginform" style="padding-top: 10em">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="username"><b>Username</b></label>
|
||||||
|
<input id="username" type="text" placeholder="Enter Username" name="username" required>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="password"><b>Password</b></label>
|
||||||
|
<input id="password" type="password" placeholder="Enter Password" name="password" required>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button id="login">Login</button>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
<center id="userdata" style="color:green;">
|
||||||
|
</center>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById('login').onclick = function() {
|
||||||
|
var username = document.getElementById('username').value;
|
||||||
|
var password = document.getElementById('password').value;
|
||||||
|
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("credentials", username + ":" + password);
|
||||||
|
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("POST", "/yunohost/portalapi/login")
|
||||||
|
xhr.setRequestHeader('X-Requested-With', ''); // CSRF protection
|
||||||
|
xhr.send(formData)
|
||||||
|
xhr.onload = function() {
|
||||||
|
if (xhr.status != 200) {
|
||||||
|
// handle error
|
||||||
|
alert( 'Error: ' + xhr.status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let xhr2 = new XMLHttpRequest();
|
||||||
|
xhr2.open("GET", "/yunohost/portalapi/me")
|
||||||
|
xhr2.setRequestHeader('X-Requested-With', ''); // CSRF protection
|
||||||
|
xhr2.send();
|
||||||
|
xhr2.onload = function() {
|
||||||
|
if (xhr2.status != 200) {
|
||||||
|
// handle error
|
||||||
|
alert( 'Error fetching /me: ' + xhr2.status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('userdata').innerHTML = xhr2.response;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"portal": "بوابة يونوهوست",
|
|
||||||
"information": "معلوماتك",
|
|
||||||
"username": "إسم المستخدم",
|
|
||||||
"password": "كلمة السر",
|
|
||||||
"fullname": "الإسم الكامل",
|
|
||||||
"mail_addresses": "عناوين البريد الإلكترونية",
|
|
||||||
"mail_forward": "عناوين توجيه البريد الإلكتروني",
|
|
||||||
"new_mail": "newmail@mydomain.org",
|
|
||||||
"new_forward": "newforward@myforeigndomain.org",
|
|
||||||
"add_mail": "إضافة عنوان بريد إلكتروني مستعار",
|
|
||||||
"add_forward": "إضافة عنوان آخر لتوجيه البريد",
|
|
||||||
"ok": "موافق",
|
|
||||||
"cancel": "إلغاء",
|
|
||||||
"change_password": "تعديل كلمة السر",
|
|
||||||
"edit": "تعديل",
|
|
||||||
"current_password": "كلمة السر الحالية",
|
|
||||||
"new_password": "كلمة السر الجديدة",
|
|
||||||
"confirm": "تأكيد",
|
|
||||||
"login": "لِج",
|
|
||||||
"logout": "الخروج",
|
|
||||||
"password_changed": "تم تغيير الكلمة السرية",
|
|
||||||
"password_changed_error": "لا يمكن تعديل الكلمة السرية",
|
|
||||||
"password_not_match": "كلمات السر غير متطابقة",
|
|
||||||
"wrong_current_password": "كلمة السر الحالية خاطئة",
|
|
||||||
"invalid_mail": "عنوان البريد الإلكتروني غير صالح",
|
|
||||||
"invalid_domain": "النطاق غير صالح في",
|
|
||||||
"invalid_mailforward": "عنوان بريد التحويل غير صالح",
|
|
||||||
"mail_already_used": "عنوان البريد الإلكتروني مُستعمل مِن قَبل",
|
|
||||||
"information_updated": "تم تحديث المعلومات",
|
|
||||||
"user_saving_fail": "لا يمكن حفظ معلومات المستخدم",
|
|
||||||
"missing_required_fields": "يُرجى ملئ الخانات المطلوبة",
|
|
||||||
"wrong_username_password": "إسم المستخدم أو كلمة السر خاطئة",
|
|
||||||
"logged_out": "تم تسجيل خروجك",
|
|
||||||
"please_login": "يرجى تسجيل الدخول قصد النفاذ إلى هذا المحتوى",
|
|
||||||
"please_login_from_portal": "يرجى تسجيل الدخول عبر البوابة",
|
|
||||||
"redirection_error_invalid_url": "خطأ في التحويل : عنوان الرابط غير صالح",
|
|
||||||
"redirection_error_unmanaged_domain": "خطأ في التحويل : لا يمكن إدارة النطاق",
|
|
||||||
"footerlink_edit": "تعديل ملفي الشخصي",
|
|
||||||
"footerlink_documentation": "الدليل",
|
|
||||||
"footerlink_support": "المساعدة",
|
|
||||||
"footerlink_administration": "الإدارة",
|
|
||||||
"password_too_simple_1": "يجب أن يكون طول الكلمة السرية على الأقل 8 حروف",
|
|
||||||
"good_practices_about_user_password": "اختر كلمة مرور مكونة مِن 8 أحرف على الأقل - مع العِلم أنّه مِن الممارسات الجيدة استخدام الأطول (أي عبارة مرور) و/أو إستخدام أنواع مختلفة من الأحرف (الحروف الكبيرة والصغيرة والأرقان والحروف الخاصة).",
|
|
||||||
"password_too_simple_4": "يجب أن يكون طول الكلمة السرية 12 حرفًا على الأقل وأن تحتوي على أرقام وحروف علوية ودنيا وحروف رمزية",
|
|
||||||
"password_too_simple_3": "يجب أن يكون طول كلمة المرور 8 حروف على الأقل وأن تحتوي على أرقام وحروف علوية ودنيا وحروف رمزية",
|
|
||||||
"password_too_simple_2": "يجب أن يكون طول كلمة المرور 8 حروف على الأقل وأن تحتوي على أرقام وحروف علوية ودنيا",
|
|
||||||
"password_listed": "إنّ الكلمة السرية هذه من بين أكثر الكلمات السرية إستخداما في العالم. الرجاء إختيار شيء فريد مِن نوعه."
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"footerlink_administration": "প্রশাসন",
|
|
||||||
"footerlink_support": "সমর্থন",
|
|
||||||
"footerlink_documentation": "নথিপত্র",
|
|
||||||
"footerlink_edit": "আমার প্রোফাইল সম্পাদনা করুন",
|
|
||||||
"redirection_error_unmanaged_domain": "পুনঃনির্দেশ ত্রুটি: নিয়ন্ত্রণহীন ডোমেন",
|
|
||||||
"redirection_error_invalid_url": "পুনঃনির্দেশ ত্রুটি: অবৈধ ইউআরএল",
|
|
||||||
"please_login_from_portal": "পোর্টাল থেকে লগ ইন করুন",
|
|
||||||
"please_login": "এই সামগ্রীতে অ্যাক্সেস করতে লগ ইন করুন",
|
|
||||||
"logged_out": "প্রস্থান",
|
|
||||||
"wrong_username_password": "ভুল ব্যবহারকারী নাম বা পাসওয়ার্ড",
|
|
||||||
"missing_required_fields": "প্রয়োজনীয় ক্ষেত্রগুলি পূরণ করুন",
|
|
||||||
"user_saving_fail": "নতুন ব্যবহারকারীর তথ্য সংরক্ষণ করা যায়নি",
|
|
||||||
"information_updated": "তথ্য আপডেট হয়েছে",
|
|
||||||
"mail_already_used": "ই-মেইল ঠিকানা ইতিমধ্যে ব্যবহৃত",
|
|
||||||
"invalid_mailforward": "অবৈধ ইমেল ফরোয়ার্ডিং ঠিকানা",
|
|
||||||
"invalid_domain": "এতে অবৈধ ডোমেন",
|
|
||||||
"invalid_mail": "অকার্যকর ইমেইল ঠিকানা",
|
|
||||||
"wrong_current_password": "বর্তমান পাসওয়ার্ডটি ভুল",
|
|
||||||
"good_practices_about_user_password": "কমপক্ষে 8 টি অক্ষরের ব্যবহারকারীর পাসওয়ার্ডটি চয়ন করুন - যদিও এটি দীর্ঘতর (যেমন একটি পাসফ্রেজ) এবং / অথবা বিভিন্ন ধরণের অক্ষর (বড় হাতের অক্ষর, ছোট হাতের অক্ষর এবং বিশেষ অক্ষর) ব্যবহার করা ভাল অনুশীলন।",
|
|
||||||
"password_too_simple_4": "পাসওয়ার্ডটিতে কমপক্ষে 12 টি অক্ষর দীর্ঘ হওয়া দরকার এবং এতে অঙ্ক, উপরের, নিম্ন এবং বিশেষ অক্ষরগুলি থাকে",
|
|
||||||
"password_too_simple_3": "পাসওয়ার্ডটিতে কমপক্ষে 8 টি অক্ষর দীর্ঘ হওয়া দরকার এবং এতে অঙ্ক, উপরের, নিম্ন এবং বিশেষ অক্ষরগুলি থাকে",
|
|
||||||
"password_too_simple_2": "পাসওয়ার্ডটিতে কমপক্ষে 8 টি অক্ষর দীর্ঘ হওয়া দরকার এবং এতে অঙ্ক, উপরের এবং নীচের অক্ষরগুলি থাকে",
|
|
||||||
"password_too_simple_1": "পাসওয়ার্ডটি কমপক্ষে 8 টি অক্ষরের দীর্ঘ হওয়া দরকার",
|
|
||||||
"password_listed": "এই পাসওয়ার্ডটি বিশ্বের সর্বাধিক ব্যবহৃত পাসওয়ার্ডগুলির মধ্যে রয়েছে। দয়া করে কিছুটা অনন্য কিছু চয়ন করুন।",
|
|
||||||
"password_not_match": "পাসওয়ার্ড মেলে না",
|
|
||||||
"password_changed_error": "পাসওয়ার্ড পরিবর্তন করা যায়নি",
|
|
||||||
"password_changed": "পাসওয়ার্ড পরিবর্তন",
|
|
||||||
"logout": "প্রস্থান",
|
|
||||||
"login": "প্রবেশ করুন",
|
|
||||||
"confirm": "নিশ্চিত করুন",
|
|
||||||
"new_password": "নতুন পাসওয়ার্ড",
|
|
||||||
"current_password": "বর্তমান পাসওয়ার্ড",
|
|
||||||
"edit": "সম্পাদন করা",
|
|
||||||
"change_password": "পাসওয়ার্ড পরিবর্তন করুন",
|
|
||||||
"cancel": "বাতিল",
|
|
||||||
"ok": "ঠিক আছে",
|
|
||||||
"add_forward": "একটি ইমেল ফরোয়ার্ডিং ঠিকানা যুক্ত করুন",
|
|
||||||
"add_mail": "একটি ইমেল ওরফে যুক্ত করুন",
|
|
||||||
"new_forward": "newforward@myforeigndomain.org",
|
|
||||||
"new_mail": "newmail@mydomain.org",
|
|
||||||
"mail_forward": "ই-মেইল ফরওয়ার্ডিং ঠিকানা",
|
|
||||||
"mail_addresses": "ইমেইল ঠিকানা",
|
|
||||||
"fullname": "পুরো নাম",
|
|
||||||
"password": "পাসওয়ার্ড",
|
|
||||||
"username": "ব্যবহারকারীর নাম",
|
|
||||||
"information": "আপনার তথ্য",
|
|
||||||
"portal": "ইউনোহোস্ট পোর্টাল"
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
{}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"portal": "Portal YunoHost",
|
|
||||||
"information": "La teva informació",
|
|
||||||
"username": "Nom d'usuari",
|
|
||||||
"password": "Contrasenya",
|
|
||||||
"fullname": "Nom complet",
|
|
||||||
"mail_addresses": "Adreces de correu electrònic",
|
|
||||||
"new_mail": "nou_correu@domini.org",
|
|
||||||
"add_mail": "Afegir un àlies de correu electrònic",
|
|
||||||
"ok": "OK",
|
|
||||||
"cancel": "Cancel·lar",
|
|
||||||
"change_password": "Canvia la contrasenya",
|
|
||||||
"edit": "Editar",
|
|
||||||
"current_password": "Contrasenya actual",
|
|
||||||
"new_password": "Nova contrasenya",
|
|
||||||
"confirm": "Confirmar",
|
|
||||||
"login": "Iniciar sessió",
|
|
||||||
"logout": "Tancar sessió",
|
|
||||||
"password_changed": "Contrasenya canviada",
|
|
||||||
"password_changed_error": "No s'ha pogut canviar la contrasenya",
|
|
||||||
"password_not_match": "Les contrasenyes no coincideixen",
|
|
||||||
"wrong_current_password": "La contrasenya actual és incorrecta",
|
|
||||||
"invalid_mail": "El correu electrònic no és vàlid",
|
|
||||||
"invalid_domain": "Domini invàlid a",
|
|
||||||
"mail_already_used": "El correu electrònic ja utilitzat",
|
|
||||||
"information_updated": "Informació actualitzada",
|
|
||||||
"user_saving_fail": "No s'han pogut enregistrar les noves dades de l'usuari",
|
|
||||||
"missing_required_fields": "Ompliu els camps obligatoris",
|
|
||||||
"wrong_username_password": "Contrasenya o nom d'usuari incorrectes",
|
|
||||||
"logged_out": "Sessió tancada",
|
|
||||||
"please_login": "Inicieu sessió per accedir a aquest contingut",
|
|
||||||
"please_login_from_portal": "Si us plau, inicieu sessió des del portal",
|
|
||||||
"redirection_error_invalid_url": "Error de redirecció: URL no vàlida",
|
|
||||||
"redirection_error_unmanaged_domain": "Error de redirecció: domini no gestionat",
|
|
||||||
"footerlink_edit": "Editar el meu perfil",
|
|
||||||
"footerlink_documentation": "Documentació",
|
|
||||||
"footerlink_support": "Ajuda",
|
|
||||||
"footerlink_administration": "Administració",
|
|
||||||
"mail_forward": "Correu electrònic de reenviament",
|
|
||||||
"new_forward": "noureenviament@dominiextern.org",
|
|
||||||
"add_forward": "Afegir un correu electrònic de reenviament",
|
|
||||||
"invalid_mailforward": "Correu electrònic de reenviament invàlid",
|
|
||||||
"password_listed": "Aquesta contrasenya és una de les més utilitzades en el món. Si us plau utilitzeu-ne una més única.",
|
|
||||||
"password_too_simple_1": "La contrasenya ha de tenir un mínim de 8 caràcters",
|
|
||||||
"password_too_simple_2": "La contrasenya ha de tenir un mínim de 8 caràcters i ha de contenir dígits, majúscules i minúscules",
|
|
||||||
"password_too_simple_3": "La contrasenya ha de tenir un mínim de 8 caràcters i tenir dígits, majúscules, minúscules i caràcters especials",
|
|
||||||
"password_too_simple_4": "La contrasenya ha de tenir un mínim de 12 caràcters i tenir dígits, majúscules, minúscules i caràcters especials",
|
|
||||||
"good_practices_about_user_password": "Trieu una contrasenya d'un mínim de 8 caràcters ; tot i que és de bona pràctica utilitzar una contrasenya més llarga (és a dir una frase de contrasenya) i/o utilitzar diferents tipus de caràcters (majúscules, minúscules, dígits i caràcters especials)."
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
{}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"add_mail": "Přidat e-mail alias",
|
|
||||||
"new_forward": "newforward@myforeigndomain.org",
|
|
||||||
"new_mail": "newmail@mydomain.org",
|
|
||||||
"mail_forward": "E-mail pro přeposílání",
|
|
||||||
"mail_addresses": "E-mailová adresa",
|
|
||||||
"fullname": "Jméno a příjmení",
|
|
||||||
"password": "Heslo",
|
|
||||||
"username": "Uživatelské jméno",
|
|
||||||
"information": "Vaše údaje",
|
|
||||||
"portal": "YunoHost Portál",
|
|
||||||
"footerlink_administration": "Administrace",
|
|
||||||
"footerlink_support": "Podpora",
|
|
||||||
"footerlink_documentation": "Dokumentace",
|
|
||||||
"footerlink_edit": "Upravit svůj profil",
|
|
||||||
"redirection_error_unmanaged_domain": "Chyba přesměrování: Doména není spravována",
|
|
||||||
"redirection_error_invalid_url": "Chyba přesměrování: Neplatné URL",
|
|
||||||
"please_login_from_portal": "Prosím přihlašte se z portálu",
|
|
||||||
"please_login": "Pro přístup k obsahu se prosím přihlašte",
|
|
||||||
"logged_out": "Jste odhlášen/a",
|
|
||||||
"wrong_username_password": "Chybné uživatelské jméno nebo heslo",
|
|
||||||
"missing_required_fields": "Vyplňte povinné údaje",
|
|
||||||
"user_saving_fail": "Nelze uložit uživatelské údaje",
|
|
||||||
"information_updated": "Údaje upraveny",
|
|
||||||
"mail_already_used": "Tato e-mailová adresa se už používá",
|
|
||||||
"invalid_mailforward": "Neplatná e-mailová adresa pro přeposílání",
|
|
||||||
"invalid_domain": "Neplatná doména v",
|
|
||||||
"invalid_mail": "Neplatná e-mailová adresa",
|
|
||||||
"wrong_current_password": "Současné heslo je chybné",
|
|
||||||
"good_practices_about_user_password": "Vyberte si heslo aspoň 8 znaků dlouhé - dobrou praxí je ale používat delší frázi a používat různé druhy znaků (velká a malá písmena, číslice a speciální znaky).",
|
|
||||||
"password_too_simple_4": "Heslo musí být aspoň 12 znaků dlouhé a obsahovat čísla, velká a malá písmena a speciální znaky",
|
|
||||||
"password_too_simple_3": "Heslo musí být aspoň 8 znaků dlouhé a obsahovat čísla, velká a malá písmena a speciální znaky",
|
|
||||||
"password_too_simple_2": "Heslo musí být aspoň 8 znaků dlouhé a obsahovat číslici, velká a malá písmena",
|
|
||||||
"password_too_simple_1": "Heslo musí být aspoň 8 znaků dlouhé",
|
|
||||||
"password_listed": "Toto heslo je jedním z nejpoužívanějších na světě. Zvolte si prosím něco jediněčnějšího.",
|
|
||||||
"password_not_match": "Hesla se neshodují",
|
|
||||||
"password_changed_error": "Heslo nebylo změněno",
|
|
||||||
"password_changed": "Heslo změněno",
|
|
||||||
"logout": "Odhlásit se",
|
|
||||||
"login": "Přihlásit se",
|
|
||||||
"confirm": "Potvrdit",
|
|
||||||
"new_password": "Nové heslo",
|
|
||||||
"current_password": "Současné heslo",
|
|
||||||
"edit": "Upravit",
|
|
||||||
"change_password": "Změnit heslo",
|
|
||||||
"cancel": "Storno",
|
|
||||||
"ok": "OK",
|
|
||||||
"add_forward": "Přidat e-mailovou adresu pro přeposílání"
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
{}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"add_forward": "E-Mail-Weiterleitung hinzufügen",
|
|
||||||
"add_mail": "E-Mail-Alias hinzufügen",
|
|
||||||
"cancel": "Abbrechen",
|
|
||||||
"change_password": "Passwort ändern",
|
|
||||||
"confirm": "Bestätigen",
|
|
||||||
"current_password": "Aktuelles Passwort",
|
|
||||||
"edit": "Bearbeiten",
|
|
||||||
"footerlink_administration": "Verwaltung",
|
|
||||||
"footerlink_documentation": "Dokumentation",
|
|
||||||
"footerlink_edit": "Mein Profil bearbeiten",
|
|
||||||
"footerlink_support": "Support",
|
|
||||||
"fullname": "Vollständiger Name",
|
|
||||||
"information": "Ihre Informationen",
|
|
||||||
"information_updated": "Informationen aktualisiert",
|
|
||||||
"invalid_domain": "Ungültige Domäne angegeben",
|
|
||||||
"invalid_mail": "Ungültige E-Mail-Adresse",
|
|
||||||
"invalid_mailforward": "Ungültige E-Mail-Weiterleitung",
|
|
||||||
"logged_out": "Abgemeldet",
|
|
||||||
"login": "Anmelden",
|
|
||||||
"logout": "Abmelden",
|
|
||||||
"mail_addresses": "E-Mail-Adressen",
|
|
||||||
"mail_already_used": "Diese E-Mail-Adresse wird bereits verwendet",
|
|
||||||
"mail_forward": "E-Mail-Weiterleitung",
|
|
||||||
"missing_required_fields": "Die notwendigen Felder müssen ausgefüllt werden",
|
|
||||||
"new_forward": "neueweiterleitung@anderedomain.org",
|
|
||||||
"new_mail": "neueadresse@meinedomain.org",
|
|
||||||
"new_password": "Neues Passwort",
|
|
||||||
"ok": "OK",
|
|
||||||
"password": "Passwort",
|
|
||||||
"password_changed": "Passwort geändert",
|
|
||||||
"password_changed_error": "Passwort konnte nicht geändert werden",
|
|
||||||
"password_not_match": "Die Passwörter stimmen nicht überein",
|
|
||||||
"please_login": "Bitte melden Sie sich an, um auf diese Inhalte zuzugreifen",
|
|
||||||
"please_login_from_portal": "Bitte melden Sie sich über das Portal an",
|
|
||||||
"portal": "YunoHost-Portal",
|
|
||||||
"user_saving_fail": "Neue Kontoinformationen konnten nicht gespeichert werden",
|
|
||||||
"username": "Benutzername",
|
|
||||||
"wrong_current_password": "Aktuelles Passwort ist falsch",
|
|
||||||
"wrong_username_password": "Falscher Anmeldename oder Passwort",
|
|
||||||
"redirection_error_invalid_url": "Fehler bei Weiterleitung: Ungültige URL",
|
|
||||||
"redirection_error_unmanaged_domain": "Fehler bei Weiterleitung: Nicht-verwaltete Domain",
|
|
||||||
"good_practices_about_user_password": "Wählen Sie ein Benutzerpasswort mit mindestens 8 Zeichen - es ist jedoch empfehlenswert, ein längeres Passwort (z.B. eine Passphrase) und/oder verschiedene Arten von Zeichen (Groß- und Kleinschreibung, Ziffern und Sonderzeichen) zu verwenden.",
|
|
||||||
"password_too_simple_3": "Das Passwort muss mindestens 8 Zeichen lang sein und Grossbuchstaben, Kleinbuchstaben, Zahlen und Sonderzeichen enthalten",
|
|
||||||
"password_too_simple_2": "Das Passwort muss mindestens 8 Zeichen lang sein und Gross- und Kleinbuchstaben sowie Zahlen enthalten",
|
|
||||||
"password_listed": "Dieses Passwort zählt zu den meistgenutzten Passwörtern der Welt. Bitte wähle ein anderes, einzigartigeres Passwort.",
|
|
||||||
"password_too_simple_4": "Das Passwort muss mindestens 12 Zeichen lang sein und Grossbuchstaben, Kleinbuchstaben, Zahlen und Sonderzeichen enthalten",
|
|
||||||
"password_too_simple_1": "Das Passwort muss mindestens 8 Zeichen lang sein"
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"footerlink_administration": "Διαχείριση",
|
|
||||||
"footerlink_support": "Υποστήριξη",
|
|
||||||
"footerlink_documentation": "Τεκμηρίωση",
|
|
||||||
"footerlink_edit": "Επεξεργασία του προφίλ μου",
|
|
||||||
"redirection_error_unmanaged_domain": "Σφάλμα ανακατεύθυνσης: Μη διαχειριζόμενος τομέας",
|
|
||||||
"redirection_error_invalid_url": "Σφάλμα ανακατεύθυνσης: Μη έγκυρο URL",
|
|
||||||
"please_login_from_portal": "Συνδεθείτε από την πύλη",
|
|
||||||
"please_login": "Συνδεθείτε για πρόσβαση σε αυτό το περιεχόμενο",
|
|
||||||
"logged_out": "Αποσυνδέθηκα",
|
|
||||||
"wrong_username_password": "Λάθος όνομα χρήστη ή κωδικός",
|
|
||||||
"missing_required_fields": "Συμπληρώστε τα απαιτούμενα πεδία",
|
|
||||||
"user_saving_fail": "Δεν ήταν δυνατή η αποθήκευση νέων πληροφοριών χρήστη",
|
|
||||||
"information_updated": "Οι πληροφορίες ενημερώθηκαν",
|
|
||||||
"mail_already_used": "Γίνεται ήδη χρήση της διεύθυνσης ηλεκτρονικού ταχυδρομείου",
|
|
||||||
"invalid_mailforward": "Μη έγκυρη διεύθυνση προώθησης e-mail",
|
|
||||||
"invalid_domain": "Μη έγκυρος τομέας στο",
|
|
||||||
"invalid_mail": "Μη έγκυρη διεύθυνση e-mail",
|
|
||||||
"wrong_current_password": "Ο τρέχων κωδικός πρόσβασης είναι λάθος",
|
|
||||||
"good_practices_about_user_password": "Διαλέξτε έναν κωδικό πρόσβασης χρήστη με τουλάχιστον 8 χαρακτήρες - αν και είναι καλή πρακτική να χρησιμοποιείτε μακρύτερους (δηλαδή μια φράση πρόσβασης) ή / και να χρησιμοποιείτε διάφορους τύπους χαρακτήρων (κεφαλαία, πεζά, ψηφία και ειδικούς χαρακτήρες).",
|
|
||||||
"password_too_simple_4": "Ο κωδικός πρόσβασης πρέπει να έχει μήκος τουλάχιστον 12 χαρακτήρων και περιέχει ψηφία, άνω, κάτω και ειδικούς χαρακτήρες",
|
|
||||||
"password_too_simple_3": "Ο κωδικός πρόσβασης πρέπει να έχει τουλάχιστον 8 χαρακτήρες και περιέχει ψηφία, άνω, κάτω και ειδικούς χαρακτήρες",
|
|
||||||
"password_too_simple_2": "Ο κωδικός πρόσβασης πρέπει να έχει τουλάχιστον 8 χαρακτήρες και περιέχει ψηφία, άνω και κάτω χαρακτήρες",
|
|
||||||
"password_too_simple_1": "Ο κωδικός πρόσβασης πρέπει να έχει τουλάχιστον 8 χαρακτήρες",
|
|
||||||
"password_listed": "Αυτός ο κωδικός πρόσβασης είναι από τους πιο χρησιμοποιούμενους κωδικούς πρόσβασης στον κόσμο. Επιλέξτε κάτι λίγο πιο μοναδικό.",
|
|
||||||
"password_not_match": "Οι κωδικοί πρόσβασης δεν ταιριάζουν",
|
|
||||||
"password_changed_error": "Δεν ήταν δυνατή η αλλαγή κωδικού πρόσβασης",
|
|
||||||
"password_changed": "Ο κωδικός άλλαξε",
|
|
||||||
"logout": "Αποσύνδεση",
|
|
||||||
"login": "Σύνδεση",
|
|
||||||
"confirm": "Επιβεβαιώνω",
|
|
||||||
"new_password": "Νέος Κωδικός",
|
|
||||||
"current_password": "Τρέχων κωδικός πρόσβασης",
|
|
||||||
"edit": "Επεξεργασία",
|
|
||||||
"change_password": "Αλλαξε κωδικό",
|
|
||||||
"cancel": "Ματαίωση",
|
|
||||||
"ok": "Εντάξει",
|
|
||||||
"add_forward": "Προσθέστε μια διεύθυνση προώθησης email",
|
|
||||||
"add_mail": "Προσθέστε ένα ψευδώνυμο email",
|
|
||||||
"new_forward": "νέοπροςταεμπρός@οξένοςτομέαςμου.org",
|
|
||||||
"new_mail": "νέοταχυδρομείο@οτομέαςμου.org",
|
|
||||||
"mail_forward": "Διεύθυνση προώθησης ηλεκτρονικού ταχυδρομείου",
|
|
||||||
"mail_addresses": "Διευθύνσεις ηλεκτρονικού ταχυδρομείου",
|
|
||||||
"fullname": "Πλήρες όνομα",
|
|
||||||
"password": "Κωδικός πρόσβασης",
|
|
||||||
"username": "Όνομα χρήστη",
|
|
||||||
"information": "Τα στοιχεία σας",
|
|
||||||
"portal": "Πύλη YunoHost"
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"portal": "YunoHost Portal",
|
|
||||||
"information": "Your info",
|
|
||||||
"username": "Username",
|
|
||||||
"password": "Password",
|
|
||||||
"fullname": "Full name",
|
|
||||||
"mail_addresses": "E-mail addresses",
|
|
||||||
"mail_forward": "E-mail forwarding address",
|
|
||||||
"new_mail": "newmail@mydomain.org",
|
|
||||||
"new_forward": "newforward@myforeigndomain.org",
|
|
||||||
"add_mail": "Add an e-mail alias",
|
|
||||||
"add_forward": "Add an e-mail forwarding address",
|
|
||||||
"ok": "OK",
|
|
||||||
"cancel": "Cancel",
|
|
||||||
"change_password": "Change password",
|
|
||||||
"edit": "Edit",
|
|
||||||
"current_password": "Current password",
|
|
||||||
"new_password": "New password",
|
|
||||||
"confirm": "Confirm",
|
|
||||||
"login": "Log in",
|
|
||||||
"logout": "Log out",
|
|
||||||
"password_changed": "Password changed",
|
|
||||||
"password_changed_error": "Could not change password",
|
|
||||||
"password_not_match": "The passwords don't match",
|
|
||||||
"password_listed": "This password is among the most used passwords in the world. Please choose something a bit more unique.",
|
|
||||||
"password_too_simple_1": "The password needs to be at least 8 characters long",
|
|
||||||
"password_too_simple_2": "The password needs to be at least 8 characters long and contains digit, upper and lower characters",
|
|
||||||
"password_too_simple_3": "The password needs to be at least 8 characters long and contains digit, upper, lower and special characters",
|
|
||||||
"password_too_simple_4": "The password needs to be at least 12 characters long and contains digit, upper, lower and special characters",
|
|
||||||
"good_practices_about_user_password": "Pick a user password of at least 8 characters - though it is good practice to use longer ones (i.e. a passphrase) and/or use various kind of characters (uppercase, lowercase, digits and special characters).",
|
|
||||||
"wrong_current_password": "The current password is wrong",
|
|
||||||
"invalid_mail": "Invalid e-mail address",
|
|
||||||
"invalid_domain": "Invalid domain in",
|
|
||||||
"invalid_mailforward": "Invalid e-mail forwarding address",
|
|
||||||
"mail_already_used": "E-mail address already in use",
|
|
||||||
"information_updated": "Info updated",
|
|
||||||
"user_saving_fail": "Could not save new user info",
|
|
||||||
"missing_required_fields": "Fill in the required fields",
|
|
||||||
"wrong_username_password": "Wrong username or password",
|
|
||||||
"logged_out": "Logged out",
|
|
||||||
"please_login": "Please log in to access to this content",
|
|
||||||
"please_login_from_portal": "Please log in from the portal",
|
|
||||||
"redirection_error_invalid_url": "Redirection error: Invalid URL",
|
|
||||||
"redirection_error_unmanaged_domain": "Redirection error: Unmanaged domain",
|
|
||||||
"footerlink_edit": "Edit my profile",
|
|
||||||
"footerlink_documentation": "Documentation",
|
|
||||||
"footerlink_support": "Support",
|
|
||||||
"footerlink_administration": "Administration"
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"password": "Pasvorto",
|
|
||||||
"username": "Uzantnomo",
|
|
||||||
"mail_addresses": "Retpoŝtadresoj",
|
|
||||||
"information": "Via informoj",
|
|
||||||
"new_password": "Nova pasvorto",
|
|
||||||
"current_password": "Nuna pasvorto",
|
|
||||||
"login": "Ensaluti",
|
|
||||||
"logout": "Elsaluti",
|
|
||||||
"change_password": "Ŝanĝi pasvorton",
|
|
||||||
"edit": "Redakti",
|
|
||||||
"cancel": "Nuligi",
|
|
||||||
"portal": "Yunohost portalo",
|
|
||||||
"fullname": "Plena nomo",
|
|
||||||
"new_mail": "nova-adreso@mia-domajno.org",
|
|
||||||
"confirm": "Konfirmu",
|
|
||||||
"password_changed": "Pasvorto ŝanĝita",
|
|
||||||
"password_changed_error": "Ne povis ŝanĝi pasvorton",
|
|
||||||
"password_not_match": "La pasvortoj ne kongruas",
|
|
||||||
"footerlink_administration": "Administrado",
|
|
||||||
"footerlink_support": "Subteno",
|
|
||||||
"footerlink_documentation": "Dokumentado",
|
|
||||||
"footerlink_edit": "Redakti mian profilon",
|
|
||||||
"redirection_error_unmanaged_domain": "Redirekta eraro: Ne administrita domajno",
|
|
||||||
"redirection_error_invalid_url": "Redirekta eraro: Nevalida URL",
|
|
||||||
"please_login_from_portal": "Bonvolu ensaluti de la portalo",
|
|
||||||
"please_login": "Bonvolu ensaluti por aliri ĉi tiun enhavon",
|
|
||||||
"logged_out": "Ensalutinta",
|
|
||||||
"wrong_username_password": "Malĝusta uzantnomo aŭ pasvorto",
|
|
||||||
"missing_required_fields": "Plenigu la postulatajn kampojn",
|
|
||||||
"user_saving_fail": "Ne povis konservi novajn uzantinformojn",
|
|
||||||
"information_updated": "Informoj ĝisdatigitaj",
|
|
||||||
"mail_already_used": "Retpoŝtadreso jam en uzo",
|
|
||||||
"invalid_mailforward": "Nevalida retpoŝtadreso",
|
|
||||||
"invalid_domain": "Nevalida domajno en",
|
|
||||||
"invalid_mail": "Nevalida retpoŝta adreso",
|
|
||||||
"wrong_current_password": "Aktuala pasvorto estas malĝusta",
|
|
||||||
"good_practices_about_user_password": "Elektu uzantan pasvorton de almenaŭ 8 signoj - kvankam ĝi estas bona praktiko uzi pli longajn (I.E. Pasfraso) kaj / aŭ uzas diversajn specojn de karakteroj (majusklaj, minusklaj, ciferoj kaj specialaj signoj).",
|
|
||||||
"password_too_simple_4": "La pasvorto devas havi almenaŭ 12 signojn kaj enhavas ciferojn, suprajn, pli malaltajn kaj specialajn signojn",
|
|
||||||
"password_too_simple_3": "La pasvorto devas havi almenaŭ 8 signojn kaj enhavas ciferojn, suprajn, pli malaltajn kaj specialajn signojn",
|
|
||||||
"password_too_simple_2": "La pasvorto devas havi almenaŭ 8 signojn kaj enhavas ciferojn, suprajn kaj pli malaltajn signojn",
|
|
||||||
"password_too_simple_1": "Pasvorto devas esti almenaŭ 8 signojn longa",
|
|
||||||
"password_listed": "Ĉi tiu pasvorto estas inter la plej uzataj pasvortoj en la mondo. Bonvolu elekti ion pli unikan.",
|
|
||||||
"ok": "bone",
|
|
||||||
"add_forward": "Aldonu poŝton antaŭen",
|
|
||||||
"add_mail": "Aldonu poŝton alias",
|
|
||||||
"new_forward": "newforward@myforeigndomain.org",
|
|
||||||
"mail_forward": "Poŝti antaŭen"
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"add_forward": "Añadir una dirección de reenvío de correo electrónico",
|
|
||||||
"add_mail": "Añadir un alias de correo electrónico",
|
|
||||||
"cancel": "Cancelar",
|
|
||||||
"change_password": "Cambiar contraseña",
|
|
||||||
"confirm": "Confirmar",
|
|
||||||
"current_password": "Contraseña actual",
|
|
||||||
"edit": "Editar",
|
|
||||||
"footerlink_administration": "Administración",
|
|
||||||
"footerlink_documentation": "Documentación",
|
|
||||||
"footerlink_edit": "Editar mi perfil",
|
|
||||||
"footerlink_support": "Ayuda",
|
|
||||||
"fullname": "Nombre completo",
|
|
||||||
"information": "Su información",
|
|
||||||
"information_updated": "Información actualizada",
|
|
||||||
"invalid_domain": "Dominio no válido en",
|
|
||||||
"invalid_mail": "La dirección de correo electrónico no es válida",
|
|
||||||
"invalid_mailforward": "La dirección de reenvío de correo electrónico no es válida",
|
|
||||||
"logged_out": "Sesión cerrada",
|
|
||||||
"login": "Iniciar sesión",
|
|
||||||
"logout": "Cerrar sesión",
|
|
||||||
"mail_addresses": "Direcciones de correo electrónico",
|
|
||||||
"mail_already_used": "Dirección de correo electrónico ya está en uso",
|
|
||||||
"mail_forward": "Direcciones de reenvío de correo electrónico",
|
|
||||||
"missing_required_fields": "Faltan campos obligatorios",
|
|
||||||
"new_forward": "nuevoreenvio@midominioexterior.org",
|
|
||||||
"new_mail": "nuevomail@midominio.org",
|
|
||||||
"new_password": "Nueva contraseña",
|
|
||||||
"ok": "OK",
|
|
||||||
"password": "Contraseña",
|
|
||||||
"password_changed": "Contraseña cambiada correctamente",
|
|
||||||
"password_changed_error": "Se produjo un error cambiando la contraseña",
|
|
||||||
"password_not_match": "Las nuevas contraseñas no coinciden",
|
|
||||||
"please_login": "Inicie sesión para acceder a este contenido",
|
|
||||||
"please_login_from_portal": "Por favor, inicie sesión desde el portal",
|
|
||||||
"portal": "Portal YunoHost",
|
|
||||||
"user_saving_fail": "Se produjo un error al guardar los cambios del usuario",
|
|
||||||
"username": "Nombre de usuario",
|
|
||||||
"wrong_current_password": "La contraseña actual es incorrecta",
|
|
||||||
"wrong_username_password": "Nombre de usuario o contraseña incorrectos",
|
|
||||||
"redirection_error_invalid_url": "Error de redirección: url inválido",
|
|
||||||
"redirection_error_unmanaged_domain": "Error de redirección: Dominio no gestionado",
|
|
||||||
"password_listed": "Esta contraseña se encuentra entre las contraseñas más utilizadas en el mundo. Por favor, elija algo un poco más único.",
|
|
||||||
"password_too_simple_1": "La contraseña debe tener al menos 8 caracteres de longitud",
|
|
||||||
"password_too_simple_2": "La contraseña debe tener al menos 8 caracteres de longitud y contiene dígitos, mayúsculas y minúsculas",
|
|
||||||
"password_too_simple_3": "La contraseña debe ser de al menos 8 caracteres de longitud e incluir un número y caracteres en mayúsculas, minúsculas y caracteres especiales",
|
|
||||||
"password_too_simple_4": "La contraseña debe ser de al menos 12 caracteres de longitud e incluir un número, mayúsculas, minúsculas y caracteres especiales",
|
|
||||||
"good_practices_about_user_password": "Está a punto de establecer una nueva contraseña de usuario. La contraseña debería de ser de al menos 8 caracteres, aunque es una buena práctica usar una contraseña más larga (es decir, una frase de paso) y/o usar varias clases de caracteres (mayúsculas, minúsculas, dígitos y caracteres especiales)."
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"footerlink_administration": "Administrazioa",
|
|
||||||
"footerlink_support": "Laguntza",
|
|
||||||
"footerlink_documentation": "Dokumentazioa",
|
|
||||||
"footerlink_edit": "Editatu profila",
|
|
||||||
"redirection_error_unmanaged_domain": "Birzuzenketa errorea: kudeatu gabeko domeinua",
|
|
||||||
"redirection_error_invalid_url": "Birbideraketa errorea: URL okerra",
|
|
||||||
"please_login_from_portal": "Hasi saioa atarian",
|
|
||||||
"please_login": "Hasi saioa edukira sartzeko",
|
|
||||||
"logged_out": "Saioa amaituta",
|
|
||||||
"wrong_username_password": "Erabiltzaile-izen edo pasahitz okerra",
|
|
||||||
"missing_required_fields": "Bete beharreko eremuak",
|
|
||||||
"user_saving_fail": "Ezinezkoa izan da erabiltzailearen informazio berria gordetzea",
|
|
||||||
"information_updated": "Informazioa eguneratu da",
|
|
||||||
"mail_already_used": "Helbide elektroniko hori erabiltzen ari zara dagoeneko",
|
|
||||||
"invalid_mailforward": "Birbidalketarako helbide okerra",
|
|
||||||
"invalid_domain": "Domeinu okerra",
|
|
||||||
"invalid_mail": "Helbide elektronikoa ez da zuzena",
|
|
||||||
"wrong_current_password": "Oraingo pasahitza okerra da",
|
|
||||||
"good_practices_about_user_password": "Aukeratu gutxienez 8 karaktere dituen erabiltzaile-pasahitz bat — baina gomendioa pasahitz luzeagoak erabiltzea da (adibidez, esaldi bat) edota karaktere desberdinak erabiltzea (larriak, txikiak, zenbakiak eta karaktere bereziak).",
|
|
||||||
"password_too_simple_4": "Pasahitzak 12 karaktere izan behar ditu gutxienez eta zenbakiren bat, hizki larriren bat, txikiren bat eta karaktere bereziren bat izan behar ditu",
|
|
||||||
"password_too_simple_3": "Pasahitzak 8 karaktere izan behar ditu gutxienez eta zenbakiak, hizki larriak, hizki txikiak eta karaktere bereziak izan behar ditu",
|
|
||||||
"password_too_simple_2": "Pasahitzak 8 karaktere izan behar ditu gutxienez eta zenbakiak, hizki larriak eta hizki txikiak izan behar ditu",
|
|
||||||
"password_too_simple_1": "Pasahitzak 8 karaktere izan behar ditu gutxienez",
|
|
||||||
"password_listed": "Pasahitz hau munduko pasahitz erabilienen artean dago. Aukeratu bereziagoa den zerbait.",
|
|
||||||
"password_not_match": "Pasahitzak ez datoz bat",
|
|
||||||
"password_changed_error": "Ezin izan da pasahitza aldatu",
|
|
||||||
"password_changed": "Pasahitza aldatu da",
|
|
||||||
"logout": "Amaitu saioa",
|
|
||||||
"login": "Hasi saioa",
|
|
||||||
"confirm": "Berretsi",
|
|
||||||
"new_password": "Pasahitz berria",
|
|
||||||
"current_password": "Oraingo pasahitza",
|
|
||||||
"edit": "Editatu",
|
|
||||||
"change_password": "Aldatu pasahitza",
|
|
||||||
"cancel": "Utzi",
|
|
||||||
"ok": "Ados",
|
|
||||||
"add_forward": "Gehitu helbide elektronikoa birbidaltzeko e-maila",
|
|
||||||
"add_mail": "Gehitu e-mail ezizen bat",
|
|
||||||
"new_forward": "birbidalketaberria@nirekanpokodomeinua.eus",
|
|
||||||
"new_mail": "postaberria@niredomeinua.eus",
|
|
||||||
"mail_forward": "Birbidalketarako posta elektronikoa",
|
|
||||||
"mail_addresses": "Helbide elektronikoak",
|
|
||||||
"fullname": "Izen osoa",
|
|
||||||
"password": "Pasahitza",
|
|
||||||
"username": "Erabiltzaile-izena",
|
|
||||||
"information": "Zure informazioa",
|
|
||||||
"portal": "YunoHost ataria"
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"cancel": "لغو",
|
|
||||||
"logged_out": "خارج شده",
|
|
||||||
"password": "کلمه عبور",
|
|
||||||
"ok": "خوب",
|
|
||||||
"footerlink_administration": "مدیریت",
|
|
||||||
"footerlink_support": "پشتیبانی",
|
|
||||||
"footerlink_documentation": "مستندات",
|
|
||||||
"footerlink_edit": "ویرایش پروفایل من",
|
|
||||||
"redirection_error_unmanaged_domain": "خطای تغییر مسیر: دامنه مدیریت نشده",
|
|
||||||
"redirection_error_invalid_url": "خطای تغییر مسیر: نشانی اینترنتی نامعتبر است",
|
|
||||||
"please_login_from_portal": "لطفاً از درگاه پورتال وارد شوید",
|
|
||||||
"please_login": "لطفاً برای دسترسی به این محتوا وارد شوید",
|
|
||||||
"wrong_username_password": "نام کاربری یا رمز عبور اشتباه است",
|
|
||||||
"missing_required_fields": "فیلدهای مورد نیاز را پر کنید",
|
|
||||||
"user_saving_fail": "اطلاعات کاربر جدید ذخیره نشد",
|
|
||||||
"information_updated": "اطلاعات به روز شد",
|
|
||||||
"mail_already_used": "آدرس پست الکترونیکی قبلاً استفاده می شود",
|
|
||||||
"invalid_mailforward": "آدرس ارسال ایمیل نامعتبر است",
|
|
||||||
"invalid_domain": "دامنه نامعتبر در",
|
|
||||||
"invalid_mail": "آدرس ایمیل نامعتبر است",
|
|
||||||
"wrong_current_password": "رمز فعلی اشتباه است",
|
|
||||||
"good_practices_about_user_password": "گذرواژه کاربر متشکل ازانواع مختلف کاراکترها (بزرگ ، کوچک ، رقم و کاراکتر های خاص)را حداقل با 8 کاراکتر انتخاب کنید - هرچند استفاده از کلمات طولانی تر تمرین خوبی است (مانند عبارت عبور).",
|
|
||||||
"password_too_simple_4": "رمز عبور باید شامل اعداد ، حروف کوچک و بزرگ و کاراکترهای خاص باشد، و حداقل 12 کاراکتر طول داشته باشد",
|
|
||||||
"password_too_simple_3": "رمز عبور باید شامل اعداد ، حروف کوچک و بزرگ و کاراکترهای خاص باشد، و حداقل 8 کاراکتر طول داشته باشد",
|
|
||||||
"password_too_simple_2": "رمز عبور باید شامل اعداد و حروف کوچک و بزرگ، و حداقل 8 کاراکتر طول داشته باشد",
|
|
||||||
"password_too_simple_1": "رمز عبور باید حداقل 8 کاراکتر باشد",
|
|
||||||
"password_listed": "لطفاً گذرواژه کمی منحصر به فردتری انتخاب کنید. این رمز عبور جزو پر استفاده ترین رمزهای عبور جهان بشمار میرود.",
|
|
||||||
"password_not_match": "گذرواژه ها مطابقت ندارند",
|
|
||||||
"password_changed_error": "رمز عبور تغییر نکرد",
|
|
||||||
"password_changed": "رمز عبور تغییر کرد",
|
|
||||||
"logout": "خروج",
|
|
||||||
"login": "ورود به سیستم",
|
|
||||||
"confirm": "تائید کردن",
|
|
||||||
"new_password": "رمز عبور جدید",
|
|
||||||
"current_password": "رمز عبور فعلی",
|
|
||||||
"edit": "ویرایش",
|
|
||||||
"change_password": "تغییر رمز عبور",
|
|
||||||
"add_forward": "آدرس هدایت ایمیل را اضافه کنید",
|
|
||||||
"add_mail": "یک نام مستعار ایمیل اضافه کنید",
|
|
||||||
"new_forward": "newforward@myforeigndomain.org",
|
|
||||||
"new_mail": "newmail@mydomain.org",
|
|
||||||
"mail_forward": "آدرس ارسال به جلو ایمیل",
|
|
||||||
"mail_addresses": "آدرس ایمیل",
|
|
||||||
"fullname": "نام و نام خانوادگی",
|
|
||||||
"username": "نام کاربری",
|
|
||||||
"information": "اطلاعات شما",
|
|
||||||
"portal": "پورتال YunoHost"
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"cancel": "Peruuta",
|
|
||||||
"portal": "YunoHost-portaali",
|
|
||||||
"password": "Salasana",
|
|
||||||
"ok": "OK",
|
|
||||||
"information": "Sinun tiedot",
|
|
||||||
"username": "Käyttäjänimi",
|
|
||||||
"fullname": "Koko nimi",
|
|
||||||
"mail_addresses": "Sähköpostiosoitteet",
|
|
||||||
"mail_forward": "Sähköpostin välitysosoite",
|
|
||||||
"new_mail": "uusiosoite@minundomain.fi",
|
|
||||||
"new_forward": "uusivälitys@minunulkopuolinendomain.fi",
|
|
||||||
"add_mail": "Lisää sähköposti-alias",
|
|
||||||
"add_forward": "Lisää sähköpostin välitysosoite",
|
|
||||||
"change_password": "Vaihda salasana",
|
|
||||||
"edit": "Muokkaa",
|
|
||||||
"current_password": "Nykyinen salasana",
|
|
||||||
"new_password": "Uusi salasana",
|
|
||||||
"confirm": "Vahvista",
|
|
||||||
"login": "Kirjaudu sisään",
|
|
||||||
"logout": "Kirjaudu ulos",
|
|
||||||
"password_changed": "Salasana vaihdettu",
|
|
||||||
"password_changed_error": "Salasanaa ei voitu vaihtaa",
|
|
||||||
"password_not_match": "Salasanat eivät täsmänneet",
|
|
||||||
"password_listed": "Tämä salasana on yksi maailman käytetyimmistä salasanoista. Valitse jotain hieman ainutlaatuisempaa.",
|
|
||||||
"password_too_simple_1": "Salasanan pitää olla ainakin 8 merkin pituinen",
|
|
||||||
"password_too_simple_2": "Salasanan on oltava vähintään 8 merkkiä pitkä ja sen on sisällettävä numeroita, isoja ja pieniä merkkejä",
|
|
||||||
"wrong_current_password": "Nykyinen salasana on väärin",
|
|
||||||
"invalid_mail": "Virheellinen sähköpostiosoite",
|
|
||||||
"invalid_domain": "Virheellinen domain",
|
|
||||||
"invalid_mailforward": "Virheellinen välityssähköpostiosoite",
|
|
||||||
"mail_already_used": "Sähköpostiosoite on jo käytössä",
|
|
||||||
"information_updated": "Tiedot päivitetty",
|
|
||||||
"user_saving_fail": "Uuden käyttäjän tietoja ei voitu tallentaa",
|
|
||||||
"missing_required_fields": "Täytä pakolliset kentät",
|
|
||||||
"wrong_username_password": "Väärä käyttäjänimi tai salasana",
|
|
||||||
"logged_out": "Kirjauduttu ulos",
|
|
||||||
"please_login": "Kirjaudu sisään päästäksesi käsiksi tähän sisältöön",
|
|
||||||
"please_login_from_portal": "Kirjaudu sisään portaalista",
|
|
||||||
"redirection_error_invalid_url": "Uudelleenohjausvirhe: Virheellinen URL-osoite",
|
|
||||||
"redirection_error_unmanaged_domain": "Uudelleenohjausvirhe: Hallitsematon domain",
|
|
||||||
"footerlink_edit": "Muokkaa profiiliani",
|
|
||||||
"footerlink_documentation": "Dokumentaatio",
|
|
||||||
"footerlink_support": "Tuki",
|
|
||||||
"footerlink_administration": "Ylläpito",
|
|
||||||
"password_too_simple_3": "Salasanan on oltava vähintään 8 merkkiä pitkä ja sen on sisällettävä numeroita, isoja ja pieniä merkkejä",
|
|
||||||
"password_too_simple_4": "Salasanan on oltava vähintään 12 merkkiä pitkä ja sen on sisällettävä numeroita, isoja ja pieniä merkkejä",
|
|
||||||
"good_practices_about_user_password": "Valitse vähintään kahdeksan merkkiä pitkä salasana - on kuitenkin hyvä käyttää pidempiä salasanoja (esim. salasanalause) ja/tai erilaisia merkkejä (isoja ja pieniä kirjaimia, numeroita ja erikoismerkkejä)."
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"add_forward": "Ajouter une adresse de transfert",
|
|
||||||
"add_mail": "Ajouter un alias de courriel",
|
|
||||||
"cancel": "Annuler",
|
|
||||||
"change_password": "Changer de mot de passe",
|
|
||||||
"confirm": "Confirmation",
|
|
||||||
"current_password": "Mot de passe actuel",
|
|
||||||
"edit": "Éditer",
|
|
||||||
"footerlink_administration": "Administration",
|
|
||||||
"footerlink_documentation": "Documentation",
|
|
||||||
"footerlink_edit": "Éditer mon profil",
|
|
||||||
"footerlink_support": "Support",
|
|
||||||
"fullname": "Nom complet",
|
|
||||||
"information": "Vos infos",
|
|
||||||
"information_updated": "Info mises à jour",
|
|
||||||
"invalid_domain": "Nom de domaine invalide dans",
|
|
||||||
"invalid_mail": "Adresse de courriel invalide",
|
|
||||||
"invalid_mailforward": "Adresse courriel de transfert invalide",
|
|
||||||
"logged_out": "Déconnecté",
|
|
||||||
"login": "Connexion",
|
|
||||||
"logout": "Déconnexion",
|
|
||||||
"mail_addresses": "Adresses de courriel",
|
|
||||||
"mail_already_used": "Adresse de courriel déjà utilisée",
|
|
||||||
"mail_forward": "Adresses de transfert",
|
|
||||||
"missing_required_fields": "Remplir les champs obligatoires",
|
|
||||||
"new_forward": "nouveau_transfert@domainedistant.org",
|
|
||||||
"new_mail": "nouvelle_adresse@domaine.org",
|
|
||||||
"new_password": "Nouveau mot de passe",
|
|
||||||
"ok": "OK",
|
|
||||||
"password": "Mot de passe",
|
|
||||||
"password_changed": "Mot de passe modifié",
|
|
||||||
"password_changed_error": "Impossible de changer le mot de passe",
|
|
||||||
"password_not_match": "Les mots de passe ne correspondent pas",
|
|
||||||
"please_login": "Veuillez vous identifier pour accéder à cette page",
|
|
||||||
"please_login_from_portal": "Veuillez vous identifier depuis le portail",
|
|
||||||
"portal": "Portail YunoHost",
|
|
||||||
"user_saving_fail": "Impossible d'enregistrer les nouvelles informations utilisateur",
|
|
||||||
"username": "Nom d’utilisateur",
|
|
||||||
"wrong_current_password": "Le mot de passe actuel est incorrect",
|
|
||||||
"wrong_username_password": "Nom d’utilisateur ou mot de passe incorrect",
|
|
||||||
"redirection_error_invalid_url": "Erreur de redirection : URL invalide",
|
|
||||||
"redirection_error_unmanaged_domain": "Erreur de redirection : domaine non géré",
|
|
||||||
"password_listed": "Ce mot de passe est l'un des mots de passe les plus utilisés dans le monde. Veuillez choisir quelque chose d'un peu plus singulier.",
|
|
||||||
"password_too_simple_1": "Le mot de passe doit comporter au moins 8 caractères",
|
|
||||||
"password_too_simple_2": "Le mot de passe doit comporter au moins 8 caractères et contenir des chiffres, des majuscules et des minuscules",
|
|
||||||
"password_too_simple_3": "Le mot de passe doit comporter au moins 8 caractères et contenir des chiffres, des majuscules, des minuscules et des caractères spéciaux",
|
|
||||||
"password_too_simple_4": "Le mot de passe doit comporter au moins 12 caractères et contenir des chiffres, des majuscules, des minuscules et des caractères spéciaux",
|
|
||||||
"good_practices_about_user_password": "Choisissez un mot de passe utilisateur d’au moins 8 caractères, bien qu'il soit recommandé d'utiliser un mot de passe plus long (c'est-à-dire une phrase secrète) et/ou une combinaison de caractères (majuscules, minuscules, chiffres et caractères spéciaux)."
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"footerlink_administration": "Administración",
|
|
||||||
"footerlink_support": "Axuda",
|
|
||||||
"footerlink_documentation": "Documentación",
|
|
||||||
"footerlink_edit": "Editar o meu perfil",
|
|
||||||
"redirection_error_unmanaged_domain": "Erro na redirección: Dominio non xestionado",
|
|
||||||
"redirection_error_invalid_url": "Erro na redirección: URL non válido",
|
|
||||||
"please_login_from_portal": "Conéctate desde o portal",
|
|
||||||
"please_login": "Conéctate para acceder a este contido",
|
|
||||||
"logged_out": "Sesión pechada",
|
|
||||||
"wrong_username_password": "Credenciais incorrectas",
|
|
||||||
"missing_required_fields": "Completa os campos requeridos",
|
|
||||||
"user_saving_fail": "Non se gardou a info da nova usuaria",
|
|
||||||
"information_updated": "Info actualizada",
|
|
||||||
"mail_already_used": "Xa está en uso o enderezo de email",
|
|
||||||
"invalid_mailforward": "Enderezo de reenvío de email non válido",
|
|
||||||
"invalid_domain": "Dominio non válido",
|
|
||||||
"invalid_mail": "Enderezo de email non válido",
|
|
||||||
"wrong_current_password": "O contrasinal actual é incorrecto",
|
|
||||||
"good_practices_about_user_password": "Elixe un contrasinal con 8 caracteres como mínimo - é recomendable que sexa longo (ex. unha frase) e utilizar varios tipos de caracteres (maiúsculas, minúsculas, díxitos e caracteres especiais).",
|
|
||||||
"password_too_simple_4": "O contrasinal debe ter 12 caracteres como mínimo e ter díxitos, maiúsculas e minúsculas e caracteres especiais",
|
|
||||||
"password_too_simple_3": "O contrasinal debe ter 8 caracteres como mínimo e ter díxitos, maiúsculas e minúsculas e caracteres especiais",
|
|
||||||
"password_too_simple_2": "O contrasinal debe ter 8 caracteres como mínimo e ter díxitos e caracteres en maiúsculas e minúsculas",
|
|
||||||
"password_too_simple_1": "O contrasinal ten que ter 8 caracteres como mínimo",
|
|
||||||
"password_listed": "Este contrasinal é un dos máis utilizados no mundo. Mellor elixe un que sexa máis orixinal.",
|
|
||||||
"password_not_match": "Os contrasinais non concordan",
|
|
||||||
"password_changed_error": "Non se cambiou o contrasinal",
|
|
||||||
"password_changed": "Contrasinal cambiado",
|
|
||||||
"logout": "Pechar sesión",
|
|
||||||
"login": "Acceder",
|
|
||||||
"confirm": "Confirmar",
|
|
||||||
"new_password": "Novo contrasinal",
|
|
||||||
"current_password": "Contrasinal actual",
|
|
||||||
"edit": "Editar",
|
|
||||||
"change_password": "Cambiar contrasinal",
|
|
||||||
"cancel": "Cancelar",
|
|
||||||
"ok": "Ok",
|
|
||||||
"add_forward": "Engadir un enderezo de reenvío de email",
|
|
||||||
"add_mail": "Engadir un alias de email",
|
|
||||||
"new_forward": "novoreenvio@omeudominioexterno.org",
|
|
||||||
"new_mail": "novomail@omeudominio.org",
|
|
||||||
"mail_forward": "Enderezo de reenvío de email",
|
|
||||||
"mail_addresses": "Enderezos de email",
|
|
||||||
"fullname": "Nome completo",
|
|
||||||
"password": "Contrasinal",
|
|
||||||
"username": "Identificador",
|
|
||||||
"information": "A túa info",
|
|
||||||
"portal": "Portal YunoHost"
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
{}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"logged_out": "लॉग आउट",
|
|
||||||
"password": "पासवर्ड",
|
|
||||||
"footerlink_administration": "प्रशासन",
|
|
||||||
"footerlink_support": "समर्थन",
|
|
||||||
"footerlink_documentation": "प्रलेखन",
|
|
||||||
"footerlink_edit": "मेरे प्रोफ़ाइल संपादित करे",
|
|
||||||
"redirection_error_unmanaged_domain": "पुनर्निर्देशन त्रुटि: अप्रबंधित डोमेन",
|
|
||||||
"redirection_error_invalid_url": "पुनर्निर्देशन त्रुटि: अमान्य URL",
|
|
||||||
"please_login_from_portal": "कृपया पोर्टल से लॉग इन करें",
|
|
||||||
"please_login": "कृपया इस सामग्री तक पहुंचने के लिए लॉग इन करें",
|
|
||||||
"wrong_username_password": "उपयोगकर्ता का गलत नाम और पासवर्ड",
|
|
||||||
"missing_required_fields": "आवश्यक फ़ील्ड भरें",
|
|
||||||
"user_saving_fail": "नई उपयोगकर्ता जानकारी को सहेज नहीं सका",
|
|
||||||
"information_updated": "जानकारी अपडेट की गई",
|
|
||||||
"mail_already_used": "यह ईमेल अड्रेस पहले से ही उपयोग में है",
|
|
||||||
"invalid_mailforward": "अमान्य ई-मेल अग्रेषण पता",
|
|
||||||
"invalid_domain": "में अमान्य डोमेन",
|
|
||||||
"invalid_mail": "अमान्य ईमेल पता",
|
|
||||||
"wrong_current_password": "वर्तमान पासवर्ड गलत है",
|
|
||||||
"good_practices_about_user_password": "कम से कम 8 वर्णों का एक उपयोगकर्ता पासवर्ड चुनें - हालाँकि यह लंबे लोगों (यानी एक पासफ़्रेज़) और / या विभिन्न प्रकार के वर्ण (अपरकेस, लोअरकेस, अंक और विशेष वर्ण) का उपयोग करने के लिए अच्छा अभ्यास है।",
|
|
||||||
"password_too_simple_4": "पासवर्ड को कम से कम 12 वर्णों का होना चाहिए और इसमें अंक, ऊपरी, निचले और विशेष वर्ण शामिल होने चाहिए",
|
|
||||||
"password_too_simple_3": "पासवर्ड को कम से कम 8 वर्ण लंबा होना चाहिए और इसमें अंक, ऊपरी, निचले और विशेष वर्ण शामिल हैं",
|
|
||||||
"password_too_simple_2": "पासवर्ड को कम से कम 8 वर्ण लंबा होना चाहिए और इसमें अंक, ऊपरी और निचले वर्ण शामिल हैं",
|
|
||||||
"password_too_simple_1": "पासवर्ड को कम से कम 8 वर्ण लंबा होना चाहिए",
|
|
||||||
"password_listed": "यह पासवर्ड दुनिया में सबसे ज्यादा इस्तेमाल किए जाने वाले पासवर्ड में से है। कृपया कुछ और अनोखा चुनें।",
|
|
||||||
"password_not_match": "पासवर्ड मेल नहीं खाते",
|
|
||||||
"password_changed_error": "पासवर्ड नहीं बदल सका",
|
|
||||||
"password_changed": "पासवर्ड बदला गया",
|
|
||||||
"logout": "लोग आउट",
|
|
||||||
"login": "लॉग इन करें",
|
|
||||||
"confirm": "की पुष्टि करें",
|
|
||||||
"new_password": "नया पासवर्ड",
|
|
||||||
"current_password": "वर्तमान पासवर्ड",
|
|
||||||
"edit": "संपादित करें",
|
|
||||||
"change_password": "पासवर्ड बदलें",
|
|
||||||
"cancel": "रद्द करना",
|
|
||||||
"ok": "ठीक है",
|
|
||||||
"add_forward": "एक ई-मेल अग्रेषण पता जोड़ें",
|
|
||||||
"add_mail": "एक ईमेल उपनाम जोड़ें",
|
|
||||||
"new_forward": "newforward@myforeigndomain.org",
|
|
||||||
"new_mail": "newmail@mydomain.org",
|
|
||||||
"mail_forward": "ई-मेल अग्रेषण पता",
|
|
||||||
"mail_addresses": "ईमेल पता",
|
|
||||||
"fullname": "पूरा नाम",
|
|
||||||
"username": "उपयोगकर्ता नाम",
|
|
||||||
"information": "आपकी जानकारी",
|
|
||||||
"portal": "यूनोहास्ट पोर्टल"
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"footerlink_administration": "Adminisztráció",
|
|
||||||
"footerlink_support": "Támogatás",
|
|
||||||
"footerlink_documentation": "Dokumentáció",
|
|
||||||
"footerlink_edit": "Profilom szerkesztése",
|
|
||||||
"redirection_error_unmanaged_domain": "Átirányítási hiba: Nem kezelt domain",
|
|
||||||
"redirection_error_invalid_url": "Átirányítási hiba: érvénytelen URL",
|
|
||||||
"please_login_from_portal": "Kérjük, jelentkezzen be a portálról",
|
|
||||||
"please_login": "Kérjük, jelentkezzen be, hogy hozzáférjen ehhez a tartalomhoz",
|
|
||||||
"logged_out": "Kilépett",
|
|
||||||
"wrong_username_password": "Rossz felhasználónév vagy jelszó",
|
|
||||||
"missing_required_fields": "Töltse ki a kötelező mezőket",
|
|
||||||
"user_saving_fail": "Nem sikerült menteni az új felhasználói információkat",
|
|
||||||
"information_updated": "Az információ frissítve",
|
|
||||||
"mail_already_used": "Az e-mail cím már használatban van",
|
|
||||||
"invalid_mailforward": "Érvénytelen e-mail továbbító cím",
|
|
||||||
"invalid_domain": "Érvénytelen domain itt",
|
|
||||||
"invalid_mail": "Érvénytelen e-mail cím",
|
|
||||||
"wrong_current_password": "A jelenlegi jelszó helytelen",
|
|
||||||
"good_practices_about_user_password": "Válasszon legalább 8 karakterből álló felhasználói jelszót - jó gyakorlat azonban hosszabb jelszó használata (azaz egy jelmondat) és/vagy különféle karakterek (nagybetűk, kisbetűk, számjegyek és speciális karakterek) használata.",
|
|
||||||
"password_too_simple_4": "A jelszónak legalább 12 karakter hosszúnak kell lennie, és tartalmaznia kell számjegy, felső, alsó és speciális karaktereket",
|
|
||||||
"password_too_simple_3": "A jelszónak legalább 8 karakter hosszúnak kell lennie, és tartalmaznia kell számjegy, felső, alsó és speciális karaktereket",
|
|
||||||
"password_too_simple_2": "A jelszónak legalább 8 karakter hosszúnak kell lennie, és számjegyű, felső és alsó karaktereket kell tartalmaznia",
|
|
||||||
"password_too_simple_1": "A jelszónak legalább 8 karakter hosszúnak kell lennie",
|
|
||||||
"password_listed": "Ez a jelszó a világ egyik leggyakrabban használt jelszava. Kérjük, válasszon egy kicsit egyediabbat.",
|
|
||||||
"password_not_match": "A jelszavak nem egyeznek",
|
|
||||||
"password_changed_error": "Nem sikerült megváltoztatni a jelszót",
|
|
||||||
"password_changed": "A jelszó megváltozott",
|
|
||||||
"logout": "Kijelentkezés",
|
|
||||||
"login": "Belépés",
|
|
||||||
"confirm": "megerősít",
|
|
||||||
"new_password": "Új jelszó",
|
|
||||||
"current_password": "Jelenlegi jelszó",
|
|
||||||
"edit": "Ezerkesztése",
|
|
||||||
"change_password": "Jelszó módosítása",
|
|
||||||
"cancel": "Megszünteti",
|
|
||||||
"ok": "Rendben",
|
|
||||||
"add_forward": "Adjon hozzá egy e-mail továbbító címet",
|
|
||||||
"add_mail": "Adjon hozzá egy e-mail álnevet",
|
|
||||||
"new_forward": "newforward@myforeigndomain.org",
|
|
||||||
"new_mail": "newmail@mydomain.org",
|
|
||||||
"mail_forward": "E-mail továbbítási cím",
|
|
||||||
"mail_addresses": "Email címek",
|
|
||||||
"fullname": "Teljes név",
|
|
||||||
"password": "Jelszó",
|
|
||||||
"username": "Felhasználónév",
|
|
||||||
"information": "Az Ön adata",
|
|
||||||
"portal": "YunoHost portál"
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"cancel": "Batal",
|
|
||||||
"portal": "Portal YunoHost",
|
|
||||||
"information": "Info Anda",
|
|
||||||
"username": "Nama Pengguna",
|
|
||||||
"password": "Kata sandi",
|
|
||||||
"fullname": "Nama Lengkap",
|
|
||||||
"mail_addresses": "Alamat surel",
|
|
||||||
"mail_forward": "Alamat surel terusan",
|
|
||||||
"new_mail": "surelbaru@domainku.org",
|
|
||||||
"new_forward": "terusanbaru@domainlainku.org",
|
|
||||||
"add_mail": "Buat surel alias",
|
|
||||||
"add_forward": "Buat alamat surel terusan",
|
|
||||||
"ok": "Oke",
|
|
||||||
"change_password": "Ubah kata sandi",
|
|
||||||
"edit": "Sunting",
|
|
||||||
"current_password": "Kata sandi saat ini",
|
|
||||||
"new_password": "Kata sandi baru",
|
|
||||||
"confirm": "Konfirmasi",
|
|
||||||
"login": "Masuk",
|
|
||||||
"logout": "Keluar",
|
|
||||||
"password_changed": "Kata sandi diubah",
|
|
||||||
"password_changed_error": "Tidak dapat mengubah kata sandi",
|
|
||||||
"password_not_match": "Kata sandi tidak sama",
|
|
||||||
"password_listed": "Kata sandi ini merupakan salah satu kata sandi yang paling sering digunakan di dunia. Coba pilih sesuatu yang lebih unik.",
|
|
||||||
"password_too_simple_1": "Panjang kata sandi harus paling tidak 8 karakter",
|
|
||||||
"wrong_current_password": "Kata sandi saat ini salah",
|
|
||||||
"invalid_mail": "Alamat surel tidak valid",
|
|
||||||
"mail_already_used": "Alamat surel sudah digunakan",
|
|
||||||
"information_updated": "Info diperbarui",
|
|
||||||
"user_saving_fail": "Tidak dapat menyimpan info baru pengguna",
|
|
||||||
"wrong_username_password": "Nama pengguna atau kata sandi salah",
|
|
||||||
"logged_out": "Berhasil keluar",
|
|
||||||
"please_login": "Masuk untuk mengakses konten ini",
|
|
||||||
"please_login_from_portal": "Silakan masuk dari portal",
|
|
||||||
"redirection_error_invalid_url": "Kesalahan pengalihan: URL tidak valid",
|
|
||||||
"redirection_error_unmanaged_domain": "Kesalahan pengalihan: Domain tak dikelola",
|
|
||||||
"footerlink_edit": "Sunting profil saya",
|
|
||||||
"footerlink_documentation": "Dokumentasi",
|
|
||||||
"footerlink_support": "Dukungan",
|
|
||||||
"footerlink_administration": "Administrasi",
|
|
||||||
"password_too_simple_2": "Kata sandi harus sekurang-kurangnya 8 karakter dan memiliki angka, huruf kapital dan huruf kecil",
|
|
||||||
"password_too_simple_3": "Kata sandi harus sekurang-kurangnya 8 karakter dan memiliki angka, huruf kapital, huruf kecil, dan karakter spesial",
|
|
||||||
"password_too_simple_4": "Kata sandi harus sekurang-kurangnya 12 karakter dan memiliki angka, huruf kapital, huruf kecil, dan karakter spesial",
|
|
||||||
"good_practices_about_user_password": "Pilih kata sandi sekurang-kurangnya 8 karakter - meskipun memang adalah hal yang baik jika menggunakan yang lebih panjang (cth. parafrasa) dan/atau menggunakan berbagai macam karakter (kapital, huruf kecil, angka, dan karakter lainnya).",
|
|
||||||
"invalid_domain": "Domain tidak valid di",
|
|
||||||
"invalid_mailforward": "Alamat surel terusan tidak valid",
|
|
||||||
"missing_required_fields": "Isi bidang yang diperlukan"
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"add_forward": "Aggiungi un indirizzo di inoltro e-mail",
|
|
||||||
"add_mail": "Aggiungi un alias email",
|
|
||||||
"cancel": "Annulla",
|
|
||||||
"change_password": "Cambia password",
|
|
||||||
"confirm": "Conferma",
|
|
||||||
"current_password": "Password attuale",
|
|
||||||
"edit": "Modifica",
|
|
||||||
"footerlink_administration": "Amministrazione",
|
|
||||||
"footerlink_documentation": "Documentazione",
|
|
||||||
"footerlink_edit": "Modifica il mio profilo",
|
|
||||||
"footerlink_support": "Supporto",
|
|
||||||
"fullname": "Nome e cognome",
|
|
||||||
"information": "Le tue informazioni",
|
|
||||||
"information_updated": "Informazioni aggiornate",
|
|
||||||
"invalid_domain": "Dominio non valido in",
|
|
||||||
"invalid_mail": "Indirizzo email non valido",
|
|
||||||
"invalid_mailforward": "Indirizzo di inoltro e-mail non valido",
|
|
||||||
"logged_out": "Disconnesso",
|
|
||||||
"login": "Accedi",
|
|
||||||
"logout": "Esci",
|
|
||||||
"mail_addresses": "Indirizzi email",
|
|
||||||
"mail_already_used": "Indirizzo email già in uso",
|
|
||||||
"mail_forward": "Indirizzo di inoltro e-mail",
|
|
||||||
"missing_required_fields": "Compila i campi richiesti",
|
|
||||||
"new_forward": "nuovoinoltro@miodominiodifferente.org",
|
|
||||||
"new_mail": "nuovaemail@miodominio.org",
|
|
||||||
"new_password": "Nuova password",
|
|
||||||
"ok": "OK",
|
|
||||||
"password": "Password",
|
|
||||||
"password_changed": "Password cambiata",
|
|
||||||
"password_changed_error": "Impossibile cambiare la password",
|
|
||||||
"password_not_match": "Le password non corrispondono",
|
|
||||||
"please_login": "Per favore, accedi per visualizzare il contenuto",
|
|
||||||
"please_login_from_portal": "Per favore, accedi dal portale",
|
|
||||||
"portal": "Portale YunoHost",
|
|
||||||
"user_saving_fail": "Impossibile salvare le informazioni sul nuovo utente",
|
|
||||||
"username": "Nome utente",
|
|
||||||
"wrong_current_password": "La password attuale è sbagliata",
|
|
||||||
"wrong_username_password": "Nome utente o password sbagliati",
|
|
||||||
"redirection_error_invalid_url": "Errore di reindirizzamento: URL non valido",
|
|
||||||
"redirection_error_unmanaged_domain": "Errore di redirezionamento: dominio non gestito",
|
|
||||||
"password_listed": "Questa password è tra le password più utilizzate al mondo. Scegli qualcosa di un po 'più unico.",
|
|
||||||
"password_too_simple_1": "La password deve contenere almeno 8 caratteri",
|
|
||||||
"password_too_simple_2": "La password deve contenere almeno 8 caratteri e contiene cifre, caratteri superiori e inferiori",
|
|
||||||
"password_too_simple_3": "La password deve contenere almeno 8 caratteri e contiene caratteri numerici, superiori, inferiori e speciali",
|
|
||||||
"password_too_simple_4": "La password deve contenere almeno 12 caratteri e contiene caratteri numerici, superiori, inferiori e speciali",
|
|
||||||
"good_practices_about_user_password": "Scegli una password utente di almeno 8 caratteri, anche se è buona norma utilizzare quelli più lunghi (ad esempio una passphrase) e / o utilizzare vari tipi di caratteri (lettere maiuscole, minuscole, cifre e caratteri speciali)."
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"portal": "YunoHost ポータル",
|
|
||||||
"information": "あなたの情報",
|
|
||||||
"username": "ユーザー名",
|
|
||||||
"password": "パスワード",
|
|
||||||
"fullname": "フルネーム",
|
|
||||||
"mail_addresses": "電子メールアドレス",
|
|
||||||
"mail_forward": "電子メール転送アドレス",
|
|
||||||
"new_mail": "newmail@mydomain.org",
|
|
||||||
"add_mail": "電子メール エイリアスを追加",
|
|
||||||
"add_forward": "電子メール転送アドレスを追加",
|
|
||||||
"ok": "OK",
|
|
||||||
"change_password": "パスワード変更",
|
|
||||||
"edit": "編集",
|
|
||||||
"new_password": "新しいパスワード",
|
|
||||||
"confirm": "確認",
|
|
||||||
"logout": "ログアウト",
|
|
||||||
"password_changed": "パスワードが変更されました",
|
|
||||||
"password_not_match": "パスワードが一致しません",
|
|
||||||
"password_too_simple_1": "パスワードは8文字以上である必要があります",
|
|
||||||
"password_too_simple_2": "パスワードは8文字以上で、数字/大文字/小文字の全てを含む必要があります",
|
|
||||||
"password_too_simple_3": "パスワードは8文字以上で、数字/大文字/小文字/特殊文字の全てを含む必要があります",
|
|
||||||
"password_too_simple_4": "パスワードは12文字以上で、数字/大文字/小文字/特殊文字の全てを含む必要があります",
|
|
||||||
"wrong_current_password": "現在のパスワードが間違っています",
|
|
||||||
"invalid_mail": "不正な電子メールアドレス",
|
|
||||||
"invalid_domain": "不正なドメイン",
|
|
||||||
"invalid_mailforward": "不正な電子メール転送アドレス",
|
|
||||||
"mail_already_used": "電子メールアドレスは既に使われています",
|
|
||||||
"information_updated": "情報が更新されました",
|
|
||||||
"user_saving_fail": "新しいユーザー情報を保存できませんでした",
|
|
||||||
"missing_required_fields": "必須フィールドに入力してください",
|
|
||||||
"wrong_username_password": "ユーザー名かパスワードが間違っています",
|
|
||||||
"logged_out": "ログアウトしました",
|
|
||||||
"please_login": "このコンテンツにアクセスするにはログインしてください",
|
|
||||||
"please_login_from_portal": "ポータルからログインしてください",
|
|
||||||
"redirection_error_invalid_url": "リダイレクションエラー: 不正なURL",
|
|
||||||
"redirection_error_unmanaged_domain": "リダイレクションエラー: 管理されていないドメイン",
|
|
||||||
"footerlink_edit": "プロフィールを編集する",
|
|
||||||
"footerlink_documentation": "ドキュメント",
|
|
||||||
"footerlink_support": "サポート",
|
|
||||||
"footerlink_administration": "管理",
|
|
||||||
"cancel": "キャンセル",
|
|
||||||
"new_forward": "newforward@myforeigndomain.org",
|
|
||||||
"current_password": "現在のパスワード",
|
|
||||||
"login": "ログイン",
|
|
||||||
"password_changed_error": "パスワードは変更できませんでした",
|
|
||||||
"password_listed": "このパスワードは世界で最も使われているパスワードのひとつです。もう少しユニークなものを選んでください。",
|
|
||||||
"good_practices_about_user_password": "ユーザーパスワードは最低でも8文字、より長いもの(パスフレーズなど)にしたり、さまざまな種類の文字(大文字、小文字、数字、特殊文字)を使うことが望ましいです。"
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"username": "Nom d'utilisateur",
|
|
||||||
"password": "Awal n uɛeddi",
|
|
||||||
"fullname": "Isem inek ummid",
|
|
||||||
"ok": "Ih",
|
|
||||||
"cancel": "Sefsex",
|
|
||||||
"change_password": "Beddel awal n uffir",
|
|
||||||
"edit": "Édition",
|
|
||||||
"current_password": "Awal n uɛeddi amiran",
|
|
||||||
"new_password": "Awal uffir amaynut",
|
|
||||||
"confirm": "Sentem",
|
|
||||||
"login": "Qqen",
|
|
||||||
"logout": "Senser",
|
|
||||||
"logged_out": "Yeffeɣ",
|
|
||||||
"footerlink_documentation": "Tasemlit",
|
|
||||||
"footerlink_support": "Tallalt",
|
|
||||||
"footerlink_administration": "Tadbelt"
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
{}
|
|
|
@ -1 +0,0 @@
|
||||||
{}
|
|
|
@ -1,29 +0,0 @@
|
||||||
{
|
|
||||||
"footerlink_administration": "Administrasjon",
|
|
||||||
"footerlink_support": "Støtte",
|
|
||||||
"footerlink_documentation": "Dokumentasjon",
|
|
||||||
"footerlink_edit": "Rediger min profil",
|
|
||||||
"redirection_error_unmanaged_domain": "Videresendingsfeil: Uhåndtert domene",
|
|
||||||
"redirection_error_invalid_url": "Videresendingsfeil: Ugyldig nettadresse",
|
|
||||||
"please_login_from_portal": "Logg inn fra portalen",
|
|
||||||
"please_login": "Logg inn for å få tilgang til dette innholdet",
|
|
||||||
"logged_out": "Utlogget",
|
|
||||||
"wrong_username_password": "Feil brukernavn eller passord",
|
|
||||||
"information_updated": "Info oppdatert",
|
|
||||||
"invalid_domain": "Ugyldig domene i",
|
|
||||||
"wrong_current_password": "Nåværende passord er feil",
|
|
||||||
"password_changed": "Passord endret",
|
|
||||||
"logout": "Logg ut",
|
|
||||||
"login": "Logg inn",
|
|
||||||
"confirm": "Bekreft",
|
|
||||||
"new_password": "Nytt passord",
|
|
||||||
"current_password": "Nåværende passord",
|
|
||||||
"edit": "Rediger",
|
|
||||||
"change_password": "Endre passord",
|
|
||||||
"cancel": "Avbryt",
|
|
||||||
"ok": "OK",
|
|
||||||
"password": "Passord",
|
|
||||||
"username": "Brukernavn",
|
|
||||||
"information": "Din informasjon",
|
|
||||||
"portal": "YunoHost-portal"
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"footerlink_administration": "प्रशासन",
|
|
||||||
"footerlink_support": "समर्थन",
|
|
||||||
"footerlink_documentation": "कागजात",
|
|
||||||
"footerlink_edit": "मेरो प्रोफाइल सम्पादन गर्नुहोस्",
|
|
||||||
"redirection_error_unmanaged_domain": "पुनर्निर्देशन त्रुटि: अव्यवस्थित डोमेन",
|
|
||||||
"redirection_error_invalid_url": "रिडिरेसन त्रुटि: अवैध URL",
|
|
||||||
"please_login_from_portal": "कृपया पोर्टलबाट लग ईन गर्नुहोस्",
|
|
||||||
"please_login": "यस सामग्री पहुँच गर्न कृपया लग इन गर्नुहोस्",
|
|
||||||
"logged_out": "लग आउट",
|
|
||||||
"wrong_username_password": "गलत प्रयोगकर्ता नाम वा पासवर्ड",
|
|
||||||
"missing_required_fields": "आवश्यक फिल्डहरू भर्नुहोस्",
|
|
||||||
"user_saving_fail": "नयाँ प्रयोगकर्ता जानकारी बचत गर्न सकेन",
|
|
||||||
"information_updated": "जानकारी अपडेट गरियो",
|
|
||||||
"mail_already_used": "इ-मेल ठेगाना पहिले नै प्रयोगमा छ",
|
|
||||||
"invalid_mailforward": "अवैध ईमेल फर्वार्डिंग ठेगाना",
|
|
||||||
"invalid_domain": "अमान्य डोमेन भित्र",
|
|
||||||
"invalid_mail": "अवैध ईमेल ठेगाना",
|
|
||||||
"wrong_current_password": "हालको पासवर्ड गलत छ",
|
|
||||||
"good_practices_about_user_password": "कम्तिमा characters क्यारेक्टरहरूको प्रयोगकर्ता पासवर्ड छान्नुहोस् - यद्यपि यो लामो अभ्यास (अर्थात पासफ्रेज) प्रयोग गर्न राम्रो अभ्यास हो र / वा विभिन्न प्रकारका वर्णहरू (अपरकेस, लोअरकेस, अंक र विशेष क्यारेक्टर) प्रयोग गर्नुहोस्।",
|
|
||||||
"password_too_simple_4": "पासवर्ड कम्तिमा १२ वर्ण लामो हुनु पर्छ र अंक, माथिल्लो, तल्लो र विशेष क्यारेक्टर समावेश गर्दछ",
|
|
||||||
"password_too_simple_3": "पासवर्ड कम्तिमा characters वर्ण लामो हुनु पर्छ र अंक, माथिल्लो, तल्लो र विशेष क्यारेक्टर समावेश गर्दछ",
|
|
||||||
"password_too_simple_2": "पासवर्ड कम्तिमा characters क्यारेक्टर लामो हुनुपर्दछ र अंक, माथिल्लो र तल्लो वर्णहरू समावेश गर्दछ",
|
|
||||||
"password_too_simple_1": "पासवर्ड कम्तिमा characters अक्षर लामो हुनु आवश्यक छ",
|
|
||||||
"password_listed": "यो पासवर्ड विश्व मा सबै भन्दा बढी प्रयोग भएको पासवर्ड बीच हो। कृपया केहि अलि बढी अनौंठो छनौट गर्नुहोस्।",
|
|
||||||
"password_not_match": "पासवर्ड मेल खाँदैन",
|
|
||||||
"password_changed_error": "पासवर्ड परिवर्तन गर्न सकेन",
|
|
||||||
"password_changed": "पासवर्ड परिवर्तन भयो",
|
|
||||||
"logout": "बाहिर निस्कनु",
|
|
||||||
"login": "लग - इन",
|
|
||||||
"confirm": "पुष्टि गर्नुहोस्",
|
|
||||||
"new_password": "नया पासवर्ड",
|
|
||||||
"current_password": "वर्तमान पासवर्ड",
|
|
||||||
"edit": "सम्पादन गर्नुहोस्",
|
|
||||||
"change_password": "पासवर्ड परिवर्तन गर्नुहोस्",
|
|
||||||
"cancel": "रद्द गर्नुहोस्",
|
|
||||||
"ok": "ठिक छ",
|
|
||||||
"add_forward": "एक ईमेल अग्रेषण ठेगाना जोड्नुहोस्",
|
|
||||||
"add_mail": "ईमेल उपनाम थप्नुहोस्",
|
|
||||||
"new_forward": "नयाँअगाडी@माईफोरिगेन्डोमाइन.org",
|
|
||||||
"new_mail": "नयाँमेल@माईडोमेन.org",
|
|
||||||
"mail_forward": "इ-मेल फर्वार्डिंग ठेगाना",
|
|
||||||
"mail_addresses": "इ-मेल ठेगानाहरू",
|
|
||||||
"fullname": "पुरा नाम",
|
|
||||||
"password": "पासवर्ड",
|
|
||||||
"username": "प्रयोगकर्ता नाम",
|
|
||||||
"information": "तपाईको जानकारी",
|
|
||||||
"portal": "YunoHost पोर्टल"
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"add_forward": "Voeg een e-mail doorstuuradres toe",
|
|
||||||
"add_mail": "Voeg een e-mailalias toe",
|
|
||||||
"cancel": "Annuleren",
|
|
||||||
"change_password": "Verander wachtwoord",
|
|
||||||
"confirm": "Bevestig",
|
|
||||||
"current_password": "Huidig wachtwoord",
|
|
||||||
"edit": "Bewerken",
|
|
||||||
"footerlink_administration": "Administratie",
|
|
||||||
"footerlink_documentation": "Documentatie",
|
|
||||||
"footerlink_edit": "Bewerk mijn profiel",
|
|
||||||
"footerlink_support": "Ondersteuning",
|
|
||||||
"fullname": "Voor- en achternaam",
|
|
||||||
"information": "Uw gegevens",
|
|
||||||
"information_updated": "Informatie bijgewerkt",
|
|
||||||
"invalid_domain": "Ongeldig domein in",
|
|
||||||
"invalid_mail": "Ongeldig e-mailadres",
|
|
||||||
"invalid_mailforward": "Ongeldig email-doorstuuradres",
|
|
||||||
"logged_out": "Uitgelogd",
|
|
||||||
"login": "Inloggen",
|
|
||||||
"logout": "Uitloggen",
|
|
||||||
"mail_addresses": "E-mailadressen",
|
|
||||||
"mail_already_used": "E-mailadres al in gebruik",
|
|
||||||
"mail_forward": "E-mail doorstuuradres",
|
|
||||||
"missing_required_fields": "De verplichte velden moeten ingevuld worden",
|
|
||||||
"new_forward": "nieuw_doorstuuradres@mijndomein.org",
|
|
||||||
"new_mail": "nieuwe_email@mijndomein.org",
|
|
||||||
"new_password": "Nieuw wachtwoord",
|
|
||||||
"ok": "OK",
|
|
||||||
"password": "Wachtwoord",
|
|
||||||
"password_changed": "Wachtwoord veranderd",
|
|
||||||
"password_changed_error": "Kon wachtwoord niet veranderen",
|
|
||||||
"password_not_match": "De wachtwoorden komen niet overeen",
|
|
||||||
"please_login": "Log in om toegang te krijgen tot deze inhoud",
|
|
||||||
"please_login_from_portal": "Log in vanaf het portaal",
|
|
||||||
"portal": "YunoHost Portaal",
|
|
||||||
"user_saving_fail": "De nieuwe gebruikersinformatie kon niet opgeslagen worden",
|
|
||||||
"username": "Gebruikersnaam",
|
|
||||||
"wrong_current_password": "Het huidige wachtwoord is fout",
|
|
||||||
"wrong_username_password": "Verkeerde gebruikersnaam of wachtwoord",
|
|
||||||
"password_too_simple_2": "Het wachtwoord moet minimaal 8 tekens lang zijn en moet cijfers, hoofdletters en kleine letters bevatten",
|
|
||||||
"password_too_simple_1": "Het wachtwoord moet minimaal 8 tekens lang zijn",
|
|
||||||
"password_listed": "Dit wachtwoord is een van de meest gebruikte wachtwoorden ter wereld. Kies alstublieft iets wat minder voor de hand ligt.",
|
|
||||||
"redirection_error_unmanaged_domain": "Omleidingsfout: onbeheerd domein",
|
|
||||||
"redirection_error_invalid_url": "Omleidingsfout: ongeldige URL",
|
|
||||||
"good_practices_about_user_password": "Kies een gebruikerswachtwoord van minimaal 8 tekens - hoewel het een goede gewoonte is om langere (bijvoorbeeld een wachtwoordzin) te gebruiken en/of verschillende soorten tekens te gebruiken (hoofdletters, kleine letters, cijfers en speciale tekens).",
|
|
||||||
"password_too_simple_4": "Het wachtwoord moet minimaal 12 tekens lang zijn en moet cijfers, hoofdletters, kleine letters en speciale tekens bevatten",
|
|
||||||
"password_too_simple_3": "Het wachtwoord moet minimaal 8 tekens lang zijn en moet cijfers, hoofdletters, kleine letters en speciale tekens bevatten"
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"portal": "Portal YunoHost",
|
|
||||||
"information": "Vòstras informacions",
|
|
||||||
"username": "Nom d’utilizaire",
|
|
||||||
"password": "Senhal",
|
|
||||||
"fullname": "Nom complèt",
|
|
||||||
"mail_addresses": "Adreça de corrièl",
|
|
||||||
"mail_forward": "Adreças de transferiment",
|
|
||||||
"new_mail": "novela_adreça@domeni.org",
|
|
||||||
"new_forward": "novel_transferiment@domenialonhat.org",
|
|
||||||
"add_mail": "Ajustar un alias d’adreça electronica",
|
|
||||||
"add_forward": "Ajustar una adreça de transferiment",
|
|
||||||
"ok": "OK",
|
|
||||||
"cancel": "Anullar",
|
|
||||||
"change_password": "Cambiar lo senhal",
|
|
||||||
"edit": "Editar",
|
|
||||||
"current_password": "Senhal actual",
|
|
||||||
"new_password": "Nòu senhal",
|
|
||||||
"confirm": "Confirmar",
|
|
||||||
"login": "Connexion",
|
|
||||||
"logout": "Desconnexion",
|
|
||||||
"password_changed": "Senhal modificat",
|
|
||||||
"password_changed_error": "Una error s’es producha en cambiar lo senhal",
|
|
||||||
"password_not_match": "Los nòus senhals correspondon pas",
|
|
||||||
"wrong_current_password": "Lo senhal actual es incorrècte",
|
|
||||||
"invalid_mail": "Adreça de corrièl invalida",
|
|
||||||
"invalid_domain": "Nom de domeni invalid dins",
|
|
||||||
"invalid_mailforward": "Adreça de transferiment invalida",
|
|
||||||
"mail_already_used": "Adreça ja utilizada",
|
|
||||||
"information_updated": "Informacions actualizadas",
|
|
||||||
"user_saving_fail": "Enregistrament impossible de las nòvas informacions utilizaire",
|
|
||||||
"missing_required_fields": "Garnissètz los camps requesits",
|
|
||||||
"wrong_username_password": "Nom d’utilizaire o senhal incorrècte",
|
|
||||||
"logged_out": "Desconnectat",
|
|
||||||
"please_login": "Mercé de vos identificar per accedir a la pagina",
|
|
||||||
"please_login_from_portal": "Mercés de vos identificar dins del portal",
|
|
||||||
"redirection_error_invalid_url": "Error de redireccion : URL invalida",
|
|
||||||
"redirection_error_unmanaged_domain": "Error de redireccion : domeni pas gerit",
|
|
||||||
"footerlink_edit": "Editar lo perfil",
|
|
||||||
"footerlink_documentation": "Documentacion",
|
|
||||||
"footerlink_support": "Assisténcia",
|
|
||||||
"footerlink_administration": "Administracion",
|
|
||||||
"password_listed": "Aqueste senhal es un dels mai utilizats al monde. Se vos plai utilizatz-ne un mai unic.",
|
|
||||||
"password_too_simple_1": "Lo senhal deu conténer almens 8 caractèrs",
|
|
||||||
"password_too_simple_2": "Lo senhal deu conténer almens 8 caractèrs e nombres, majusculas e minusculas",
|
|
||||||
"password_too_simple_3": "Lo senhal deu conténer almens 8 caractèrs e nombres, majusculas e minusculas e caractèrs especials",
|
|
||||||
"password_too_simple_4": "Lo senhal deu conténer almens 12 caractèrs, de nombre, majusculas, minusculas e caractèrs especials",
|
|
||||||
"good_practices_about_user_password": "Causissètz un senhal d’almens 8 caractèrs, es de bon far d’utilizar un senhal mai long (es a dire una frasa de senhal) e/o utilizar mantun tipe de caractèrs (majusculas, minusculas, nombres e caractèrs especials)."
|
|
||||||
}
|
|