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" />
|
||||
</a>
|
||||
|
||||
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
|
||||
------------
|
||||
|
||||
|
@ -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.
|
||||
|
||||
---------------
|
||||
- `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
|
||||
|
||||
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
|
||||
### `permissions`
|
||||
|
||||
The list of permissions depicted as follows:
|
||||
|
||||
|
|
519
access.lua
|
@ -2,273 +2,155 @@
|
|||
-- access.lua
|
||||
--
|
||||
-- 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
|
||||
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
|
||||
--
|
||||
-- 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]
|
||||
-- ###########################################################################
|
||||
-- 0. Misc helpers because Lua has no sugar ...
|
||||
-- ###########################################################################
|
||||
|
||||
-- Use the `cache` shared table where a username is associated with
|
||||
-- a CDA key
|
||||
user = cache:get("CDA|"..cda_key)
|
||||
if user then
|
||||
hlp.set_auth_cookie(user, ngx.var.host)
|
||||
logger.info("Cross-domain authentication: "..user.." connected on "..ngx.var.host)
|
||||
cache:delete("CDA|"..cda_key)
|
||||
-- Get configuration (we do this here, the conf is re-read every time unless
|
||||
-- the file's timestamp didnt change)
|
||||
local config = require("config")
|
||||
local conf = config.get_config()
|
||||
|
||||
-- Cache expensive calculations
|
||||
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
|
||||
|
||||
uri_args[conf.login_arg] = nil
|
||||
return hlp.redirect(ngx.var.uri..hlp.uri_args_string(uri_args))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- 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.."&"
|
||||
-- As explained in set_basic_auth_header(), user and hashed password do not contain ':'
|
||||
-- And cache cannot contain tables, so we use "user:password" format
|
||||
cached = decoded["user"]..":"..decoded["pwd"]
|
||||
cache:set(data, cached, 120)
|
||||
logger:debug("Result saved in cache")
|
||||
return decoded["user"], decoded["pwd"], err
|
||||
else
|
||||
back_url = back_url.."?"
|
||||
logger:debug("Result found in cache")
|
||||
user, pwd = res:match("([^:]+):(.*)")
|
||||
return user, pwd, nil
|
||||
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")
|
||||
-- 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 hlp.serve(ngx.var.uri)
|
||||
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
|
||||
|
||||
-- If all the previous cases have failed, redirect to portal
|
||||
-- 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
|
||||
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
|
||||
return true, user, pwd
|
||||
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
|
||||
--
|
||||
-- ###########################################################################
|
||||
-- 2. 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)
|
||||
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 hlp.redirect(ngx.var.scheme.."://"..redirect_url)
|
||||
return ngx.var.scheme.."://"..redirect_url
|
||||
end
|
||||
end
|
||||
|
||||
if conf["redirected_urls"] then
|
||||
for url, redirect_url in pairs(conf["redirected_urls"]) do
|
||||
if url == ngx.var.host..ngx.var.uri..hlp.uri_args_string()
|
||||
or url == ngx.var.scheme.."://"..ngx.var.host..ngx.var.uri..hlp.uri_args_string()
|
||||
or url == ngx.var.uri..hlp.uri_args_string() then
|
||||
logger.debug("Requested URI is in redirected_urls")
|
||||
detect_redirection(redirect_url)
|
||||
if url == ngx.var.host..ngx.var.uri..uri_args_string()
|
||||
or url == ngx.var.scheme.."://"..ngx.var.host..ngx.var.uri..uri_args_string()
|
||||
or url == ngx.var.uri..uri_args_string() then
|
||||
logger:debug("Found in redirected_urls, redirecting to "..url)
|
||||
ngx.redirect(convert_to_absolute_url(redirect_url))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if conf["redirected_regex"] then
|
||||
for regex, redirect_url in pairs(conf["redirected_regex"]) do
|
||||
if hlp.match(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex)
|
||||
or hlp.match(ngx.var.scheme.."://"..ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex)
|
||||
or hlp.match(ngx.var.uri..hlp.uri_args_string(), regex) then
|
||||
logger.debug("Requested URI is in redirected_regex")
|
||||
detect_redirection(redirect_url)
|
||||
if match(ngx.var.host..ngx.var.uri..uri_args_string(), regex)
|
||||
or match(ngx.var.scheme.."://"..ngx.var.host..ngx.var.uri..uri_args_string(), regex)
|
||||
or match(ngx.var.uri..uri_args_string(), regex) then
|
||||
logger:debug("Found in redirected_regex, redirecting to "..url)
|
||||
ngx.redirect(convert_to_absolute_url(redirect_url))
|
||||
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:
|
||||
--
|
||||
|
@ -287,7 +169,7 @@ end
|
|||
--
|
||||
-- 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.
|
||||
--
|
||||
-- ###########################################################################
|
||||
|
||||
permission = nil
|
||||
longest_url_match = ""
|
||||
|
@ -305,7 +187,7 @@ for permission_name, permission_infos in pairs(conf["permissions"]) do
|
|||
url = "^"..url
|
||||
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
|
||||
longest_url_match = m
|
||||
permission = permission_infos
|
||||
|
@ -315,65 +197,168 @@ for permission_name, permission_infos in pairs(conf["permissions"]) do
|
|||
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
|
||||
-- ###########################################################################
|
||||
|
||||
---
|
||||
--- 5. CHECK CLIENT-PROVIDED AUTH HEADER (should almost never happen?)
|
||||
---
|
||||
function element_is_in_table(element, table)
|
||||
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"]
|
||||
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()
|
||||
|
||||
-- NB: is_logged_in_with_basic_auth can be false, true or nil
|
||||
if is_logged_in_with_basic_auth == false then
|
||||
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
|
||||
elseif is_logged_in_with_basic_auth == true then
|
||||
is_logged_in = true
|
||||
-- Ignore if not a Basic auth header
|
||||
-- otherwise, we interpret this as a Auth header spoofing attempt and clear it
|
||||
local auth_header_from_client = ngx.req.get_headers()["Authorization"]
|
||||
_, _, b64_cred = string.find(auth_header_from_client, "^Basic%s+(.+)$")
|
||||
if b64_cred ~= nil then
|
||||
ngx.req.clear_header("Authorization")
|
||||
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
|
||||
|
||||
if hlp.has_access(permission) then
|
||||
|
||||
if is_logged_in then
|
||||
-- If the user is logged in, refresh_cache
|
||||
hlp.refresh_user_cache()
|
||||
|
||||
if has_access then
|
||||
-- If Basic Authorization header are enable for this permission,
|
||||
-- add it to the response
|
||||
-- check if the user is actually logged in...
|
||||
if permission["auth_header"] then
|
||||
hlp.set_headers()
|
||||
else
|
||||
hlp.clear_headers()
|
||||
if is_logged_in == nil then
|
||||
-- Login check was not performed yet because the app is public
|
||||
logger:debug("Checking authentication because the app requires auth_header")
|
||||
is_logged_in, authUser, authPasswordEnc = check_authentication()
|
||||
end
|
||||
if is_logged_in then
|
||||
-- add it to the response
|
||||
set_basic_auth_header()
|
||||
end
|
||||
else
|
||||
hlp.clear_headers()
|
||||
end
|
||||
|
||||
return hlp.pass()
|
||||
-- Pass
|
||||
logger:debug("Allowing to pass through "..ngx.var.uri)
|
||||
return
|
||||
|
||||
-- 2nd case : no access ... redirect to portal / login form
|
||||
else
|
||||
|
||||
if is_logged_in then
|
||||
return hlp.redirect(conf.portal_url)
|
||||
else
|
||||
-- Only display this if HTTPS. For HTTP, we can't know if the user really is
|
||||
-- logged in or not, because the cookie is available only in HTTP...
|
||||
if ngx.var.scheme == "https" then
|
||||
hlp.flash("info", hlp.t("please_login"))
|
||||
portal_domain = conf["domain_portal_urls"][ngx.var.host]
|
||||
if portal_domain == nil then
|
||||
logger:debug("Domain " .. ngx.var.host .. " is not configured for SSOWat")
|
||||
ngx.status = 400
|
||||
ngx.header.content_type = "plain/text"
|
||||
ngx.say("Unmanaged domain: " .. ngx.var.host)
|
||||
return
|
||||
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()
|
||||
return hlp.redirect(conf.portal_url.."?r="..ngx.encode_base64(back_url))
|
||||
if is_logged_in then
|
||||
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
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
{
|
||||
"additional_headers": {
|
||||
"Auth-User": "uid",
|
||||
"Email": "mail",
|
||||
"Name": "cn",
|
||||
"Remote-User": "uid"
|
||||
},
|
||||
"domains": [
|
||||
"example.tld",
|
||||
"example.org"
|
||||
"domain_portal_urls": [
|
||||
"example.tld": "example.tld/yunohost/sso",
|
||||
"sub.example.tld": "example.tld/yunohost/sso",
|
||||
"foobar.org": "foobar.org/yunohost/sso"
|
||||
],
|
||||
"permissions": {
|
||||
"core_skipped": {
|
||||
|
@ -60,8 +55,6 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"portal_domain": "example.tld",
|
||||
"portal_path": "/yunohost/sso/",
|
||||
"redirected_regex": {
|
||||
"example.tld/yunohost[\\/]?$": "https://example.tld/yunohost/sso/"
|
||||
},
|
||||
|
|
82
config.lua
|
@ -6,11 +6,31 @@
|
|||
|
||||
module('config', package.seeall)
|
||||
|
||||
local lfs = require("lfs")
|
||||
local json = require("json")
|
||||
|
||||
local config_attributes = nil
|
||||
local config_persistent_attributes = nil
|
||||
|
||||
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)
|
||||
if file_attributes1 == nil and file_attributes2 == nil then
|
||||
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"]
|
||||
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()
|
||||
|
||||
-- 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"})
|
||||
|
||||
if compare_attributes(new_config_attributes, config_attributes) and compare_attributes(new_config_persistent_attributes, config_persistent_attributes) then
|
||||
update_language()
|
||||
return conf
|
||||
-- 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
|
||||
update_language()
|
||||
return conf
|
||||
end
|
||||
|
||||
|
@ -78,55 +87,10 @@ function get_config()
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
-- Default configuration values
|
||||
default_conf = {
|
||||
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
|
||||
-- Always skip the portal urls to avoid redirection looping.
|
||||
for domain, portal_url in pairs(conf["domain_portal_urls"]) do
|
||||
table.insert(conf["permissions"]["core_skipped"]["uris"], portal_url)
|
||||
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
|
||||
end
|
||||
|
|
2
debian/control
vendored
|
@ -7,7 +7,7 @@ Standards-Version: 3.9.1
|
|||
|
||||
Package: ssowat
|
||||
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
|
||||
Description: user portal with single sign-on designed for Yunohost
|
||||
A minimalist user portal with single sign-on, designed to be
|
||||
|
|
1121
helpers.lua
65
init.lua
|
@ -8,60 +8,47 @@
|
|||
-- another.
|
||||
--
|
||||
|
||||
-- Path of the configuration
|
||||
conf_path = "/etc/ssowat/conf.json"
|
||||
log_file = "/var/log/nginx/ssowat.log"
|
||||
|
||||
-- Remove prepending '@' & trailing 'init.lua'
|
||||
script_path = string.sub(debug.getinfo(1).source, 2, -9)
|
||||
|
||||
-- Include local libs in package.path
|
||||
package.path = package.path .. ";"..script_path.."?.lua"
|
||||
|
||||
-- Load libraries
|
||||
local json = require "json"
|
||||
local lualdap = require "lualdap"
|
||||
local math = require "math"
|
||||
local lfs = require "lfs"
|
||||
local socket = require "socket"
|
||||
local config = require "config"
|
||||
lustache = require "lustache"
|
||||
-- Load cookie secret
|
||||
-- IMPORTANT (though to be confirmed?)
|
||||
-- in this context, the code is ran as root therefore we don't have to
|
||||
-- add www-data in the file permissions, which could otherwise lead
|
||||
-- to comprised apps running with the www-data group to read the secret file?
|
||||
local config = require("config")
|
||||
cookie_secret = config.get_cookie_secret()
|
||||
|
||||
--
|
||||
-- Init logger
|
||||
--
|
||||
|
||||
local log_file = "/var/log/nginx/ssowat.log"
|
||||
|
||||
-- Make sure the log file exists and we can write in it
|
||||
io.popen("touch "..log_file)
|
||||
io.popen("chown www-data "..log_file)
|
||||
io.popen("chmod u+w "..log_file)
|
||||
|
||||
-- Persistent shared table
|
||||
flashs = {}
|
||||
i18n = {}
|
||||
local Logging = require("logging")
|
||||
local appender = function(self, level, message)
|
||||
|
||||
-- convert a string to a hex
|
||||
function tohex(str)
|
||||
return (str:gsub('.', function (c)
|
||||
return string.format('%02X', string.byte(c))
|
||||
end))
|
||||
-- Output to log file
|
||||
local fp = io.open(log_file, "a")
|
||||
local str = string.format("[%-6s%s] %s\n", level:upper(), os.date(), message)
|
||||
fp:write(str)
|
||||
fp:close()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Efficient function to get a random string
|
||||
function random_string()
|
||||
local length = 64
|
||||
local random_bytes = io.open("/dev/urandom"):read(length);
|
||||
if string.len(random_bytes) ~= length then
|
||||
error("Not enough random bytes read")
|
||||
end
|
||||
return tohex(random_bytes);
|
||||
end
|
||||
logger = Logging.new(appender)
|
||||
|
||||
-- FIXME : how to set logging level ?
|
||||
--logger:setLevel(logger.DEBUG) -- FIXME
|
||||
|
||||
-- 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
|
||||
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)."
|
||||
}
|