Improvements

- Use the same CSS and files as SSOwat
- Introduce locale
- Add support for community apps
This commit is contained in:
scith 2017-01-20 13:42:35 +01:00
parent 98f7138f8c
commit 115b79e025
40 changed files with 2269 additions and 94 deletions

960
assets/css/ynh-style.css Normal file

File diff suppressed because one or more lines are too long

737
assets/css/ynhpanel.css Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,17 @@
<?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="&#xe801;" 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="&#xe800;" 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="&#xe804;" 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="&#xe80c;" 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="&#xe803;" 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="&#xe802;" 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>

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,12 @@
<?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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
assets/icons/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

38
assets/img/logo-ynh.svg Normal file
View file

@ -0,0 +1,38 @@
<?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>

After

Width:  |  Height:  |  Size: 2.7 KiB

38
assets/js/global.js Normal file
View file

@ -0,0 +1,38 @@
document.addEventListener('DOMContentLoaded', function() {
// Variables
var liMenu = document.querySelectorAll('#apps a')
, colors = ['redbg','purpledarkbg','darkbluebg','orangebg','greenbg','darkbluebg','purpledarkbg','yellowbg','lightpinkbg','pinkbg','turquoisebg','yellowbg','lightbluebg','purpledarkbg', 'bluebg']
, addMailAlias = document.getElementById('add-mailalias')
, addMaildrop = document.getElementById('add-maildrop')
;
liMenu && [].forEach.call(liMenu, function(el, i) {
// Select a color value from the App label
randomColorNumber = parseInt(el.textContent, 36) % colors.length;
//randomColorNumber = i%colors.length; // Old value
// Add color class.
el.classList.add(colors[randomColorNumber]);
// Set first-letter data attribute.
el.querySelector('.first-letter').setAttribute('data-first-letter',el.textContent.substring(0, 2));
});
addMailAlias && addMailAlias.addEventListener('click', function(){
// Clone last input.
var inputAliasClone = document.querySelector('.mailalias-input').cloneNode(true);
// Empty value.
inputAliasClone.value = '';
// Append to form-group.
addMailAlias.parentNode.insertBefore(inputAliasClone, addMailAlias);
});
addMaildrop && addMaildrop.addEventListener('click', function(){
// Clone last input.
var inputDropClone = document.querySelector('.maildrop-input').cloneNode(true);
// Empty value.
inputDropClone.value = '';
// Append to form-group.
addMaildrop.parentNode.insertBefore(inputDropClone, addMaildrop);
});
});

303
assets/js/ynhpanel.js Normal file
View file

@ -0,0 +1,303 @@
/* ----------------------------------------------------------
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() {};
}
/* 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) {
if (el.addEventListener) {
el.addEventListener(eventName, callback, false);
}
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/
-------------------------- */
var dragg = function(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
// Start dragging
window.addEvent(elem, 'mousedown', function(e){
// Prevent firefox native D'n'D behavior
window.eventPreventDefault(e);
selected = elem;
x_elem = x_pos - selected.offsetLeft;
y_elem = y_pos - selected.offsetTop;
});
// Will be called when user dragging an element
window.addEvent(window, 'mousemove', function(e){
// Get position
x_pos = document.all ? window.event.clientX : e.pageX;
y_pos = document.all ? window.event.clientY : e.pageY;
if (selected !== null) {
dragged = true;
selected.style.left = (x_pos - x_elem) + 'px';
selected.style.top = (y_pos - y_elem) + 'px';
}
});
// Destroy the object when we are done
window.addEvent(window, 'mouseup', function(e){
selected = null;
});
// Handle click event
window.addEvent(elem, 'click', function(e){
// Prevent default event
window.eventPreventDefault(e);
// Do not prapagate to other click event if dragged out
if (dragged) {
e.stopImmediatePropagation();
}
// Reset dragging status
dragged = false;
});
}
/* Smallest DOMReady
http://dustindiaz.com/smallest-domready-ever
-------------------------- */
function domReady(cb) {
/in/.test(document.readyState) // in = loadINg
? setTimeout('domReady('+cb+')', 9)
: cb();
}
/* ----------------------------------------------------------
Main
---------------------------------------------------------- */
domReady(function(){
// Don't do this in iframe
if (window.self !== window.top) {return false;}
// 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');
// Add portal stylesheet
var portalStyle = document.createElement("link");
portalStyle.setAttribute("rel", "stylesheet");
portalStyle.setAttribute("type", "text/css");
portalStyle.setAttribute("href", '/ynhpanel.css');
document.getElementsByTagName("head")[0].insertBefore(portalStyle, null);
// Create portal link
var portal = document.createElement('a');
portal.setAttribute('id', 'ynh-overlay-switch');
portal.setAttribute('href', '/yunohost/sso/');
portal.setAttribute('class', 'disableAjax');
document.body.insertBefore(portal, null);
// Portal link is draggable, for user convenience
dragg('ynh-overlay-switch');
// Create overlay element
var overlay = document.createElement("div");
overlay.setAttribute("id","ynh-overlay");
overlay.setAttribute("style","display:none");
document.body.insertBefore(overlay, null);
//Color Application
var colors = ['redbg','purpledarkbg','darkbluebg','orangebg','greenbg','darkbluebg','purpledarkbg','yellowbg','lightpinkbg','pinkbg','turquoisebg','yellowbg','lightbluebg','purpledarkbg', 'bluebg'];
// Get user's app
var r = new XMLHttpRequest();
r.open("GET", "/ynhpanel.json", true);
r.onreadystatechange = function () {
// Die if error
if (r.readyState != 4 || r.status != 200) return;
// Response is JSON
response = JSON.parse(r.responseText);
// Add overlay header
overlay.innerHTML += '<div id="ynh-user" class="ynh-wrapper info">' +
'<ul class="ul-reset user-menu"><li><a class="icon icon-connexion disableAjax" href="'+ response.portal_url +'?action=logout">'+response.t_logout+'</a></li></ul>'+
'<a class="user-container user-container-info disableAjax" href="'+ response.portal_url +'edit.html">' +
'<h2 class="user-username">'+ response.uid +'</h2>' +
'<small class="user-fullname">'+ response.givenName + ' ' + response.sn +'</small>' +
'<span class="user-mail">'+ response.mail +'</span>' +
'</a>' +
'</div>';
// Add application links
var links = [];
Array.prototype.forEach.call(response.app, function(app, n){
randomColorNumber = parseInt(app.name, 36) % colors.length;
links.push('<li><a class="'+colors[randomColorNumber]+' disableAjax" href="//'+app.url+'"><span class="first-letter" data-first-letter="'+ app.name.substr(0,2) +'"></span><span class="name">'+app.name+'</span></a></li>');
});
overlay.innerHTML += '<div id="ynh-apps" class="ynh-wrapper apps"><ul class="listing-apps">'+ links.join("\n") +'</ul></div>';
// Add footer links
overlay.innerHTML += '<div id="ynh-footer" class="ynh-wrapper footer"><nav>' + "\n" +
'<a class="link-profile-edit" href="/yunohost/sso/edit.html">'+ response.t_footerlink_edit +'</a>' + "\n" +
'<a class="link-documentation" href="//yunohost.org/docs" target="_blank">'+ response.t_footerlink_documentation +'</a>' + "\n" +
'<a class="link-documentation" href="//yunohost.org/support" target="_blank">'+ response.t_footerlink_support +'</a>' + "\n" +
'<a class="link-admin" href="/yunohost/admin/" target="_blank">'+ response.t_footerlink_administration +'</a>' + "\n" +
'</nav></div>';
// Add overlay to DOM
var btn = document.getElementById('logo'),
yunoverlay = document.getElementById('ynh-overlay'),
user = document.getElementById('ynh-user'),
apps = document.getElementById('ynh-apps');
var pfx = ["webkit", "moz", "MS", "o", ""];
function PrefixedEvent(element, type, callback) {
for (var p = 0; p < pfx.length; p++) {
if (!pfx[p]) type = type.toLowerCase();
element.addEventListener(pfx[p]+type, callback, false);
}
}
// Bind YNH Button
window.addEvent(portal, 'click', function(e){
// Prevent default click
window.eventPreventDefault(e);
// Toggle overlay on YNHPortal button
//Element.toggleClass(overlay, 'visible');
Element.toggleClass(portal, 'visible');
Element.toggleClass(document.querySelector('html'), 'ynh-panel-active');
if(yunoverlay.classList.contains('ynh-active')) {
meta_viewport.setAttribute('content', meta_viewport_content);
yunoverlay.classList.add('ynh-fadeOut');
PrefixedEvent(yunoverlay, "AnimationEnd", function(){
if(yunoverlay.classList.contains('ynh-fadeOut')) {
yunoverlay.classList.remove('ynh-active');
}
});
}else {
meta_viewport.setAttribute('content', "width=device-width");
yunoverlay.classList.remove('ynh-fadeOut');
yunoverlay.classList.add('ynh-active');
}
});
};
r.send();
});

1
assets/js/ynhpanel.json Normal file
View file

@ -0,0 +1 @@
{}

231
index.php
View file

@ -1,115 +1,160 @@
<?php <?php
// Retrieve the app from the URL using GET
$app = htmlentities($_GET['app']); $app = htmlentities($_GET['app']);
if(isset($_POST['server']) AND !empty($_POST['server'])) {
$server = preg_replace('#^https?://#', '', $_POST['server']); // Retrieve the correct locale JSON
$user_locale = substr(Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']), 0, 2);
// Check if the user locale exists, otherwise switch to "en"
if (!file_exists("locales/".$user_locale.".json")) { $user_locale = 'en'; }
$locale=json_decode(file_get_contents("locales/".$user_locale.".json"), true);
// Parse the official app list
$official_json=file_get_contents("https://app.yunohost.org/official.json");
$official=json_decode($official_json, true);
// Check whether the app is official, community, or neither
if(array_key_exists($app, $official)) {
// The app is in the official list
$app_status = 'official'; // The app is in the official list
$app_name = $official[$app]['manifest']['name']; // Saves the app name
$app_git = $official[$app]['git']['url']; // Saves the git URL
$app_state = $official[$app]['state']; // Saves the app state
}
else {
// Parse the community app list
$community_json=file_get_contents("https://app.yunohost.org/community.json");
$community=json_decode($community_json, true);
// Check if the app is community
if(array_key_exists($app, $community)) {
// The app is in the community list
$app_status = 'community'; // The app is in the community list
$app_name = $community[$app]['manifest']['name']; // Saves the app name
$app_git = $community[$app]['git']['url']; // Saves the git URL
$app_state = $community[$app]['state']; // Saves the app state
}
else {
// The app is neither in the official, nor in the community list
$app_status = null;
$app_name = "";
}
}
// If the user submitted his or her server and the app is official, redirects to the server
if(isset($_POST['server']) AND !empty($_POST['server']) AND $app_status == 'official') {
$server = rtrim(preg_replace('#^https?://#', '', $_POST['server']),"/");
$url = 'https://'.$server.'/yunohost/admin/#/apps/install/'.$app; $url = 'https://'.$server.'/yunohost/admin/#/apps/install/'.$app;
header('Location: '.$url); header('Location: '.$url);
} }
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="utf-8">
<meta name="format-detection" content="telephone=no" /> <title><?php echo str_replace("{app_name}", $app_name, $locale['title']); ?></title>
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1" />
<meta name="robots" content="noindex, nofollow">
<title>Install with YunoHost</title>
<style>
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
html { <!-- Responsive -->
font-family: sans-serif; /* 1 */ <meta name="format-detection" content="telephone=no" />
-ms-text-size-adjust: 100%; /* 2 */ <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1" />
-webkit-text-size-adjust: 100%; /* 2 */
}
body { <!-- Do not index SSOWat pages -->
background: #41444f; <meta name="robots" content="noindex, nofollow">
font-family: arial;
overflow-y: scroll;
font-size: 1em;
line-height:1.5;
margin: 0;
padding: 0;
}
img { <!-- Stylesheets -->
max-width: 100%; <link rel="stylesheet" href="assets/css/ynh-style.css">
height :auto;
}
.logo { <!-- Icons -->
text-align: center; <link rel="shortcut icon" href="assets/icons/favicon.ico">
margin-bottom: 0; <link rel="apple-touch-icon" sizes="57x57" href="assets/icons/apple-touch-icon-57x57.png">
opacity: 0.7; <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">
.logo img { <link rel="apple-touch-icon" sizes="60x60" href="assets/icons/apple-touch-icon-60x60.png">
margin-top: 4%; <link rel="apple-touch-icon" sizes="120x120" href="assets/icons/apple-touch-icon-120x120.png">
width: 4em; <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">
.wrapper { <link rel="icon" type="image/png" href="assets/icons/favicon-160x160.png" sizes="160x160">
width: 90%; <link rel="icon" type="image/png" href="assets/icons/favicon-96x96.png" sizes="96x96">
margin: 2% 5%; <link rel="icon" type="image/png" href="assets/icons/favicon-16x16.png" sizes="16x16">
position: relative; <link rel="icon" type="image/png" href="assets/icons/favicon-32x32.png" sizes="32x32">
z-index: 1; <meta name="msapplication-TileColor" content="#41444f">
} <meta name="msapplication-TileImage" content="/mstile-144x144.png">
.form {
max-width: 21em;
margin: 0 auto;
}
input {
border:none;
box-shadow:none;
position: relative;
margin-bottom: 1em;
}
input[type="submit"] {
width: 100%;
padding: 0.8em 1.5em;
font-size: 1.1em;
background: #999;
display: inline-block;
padding: 0.5em 1em;
line-height: normal;
text-decoration: none;
color: #FFF;
background: #2980b9;
cursor: pointer;
transition: all 0.1s ease;
-webkit-transition: all 0.1s ease;
border:0;
cursor:pointer;
}
input[type="submit"]:hover {background: #3498db;}
input[type="text"] {
background: #fff;
color: #41444f;
width: 100%;
padding: 0.8em 0.8em 0.8em 3em;
}
</style>
</head> </head>
<body> <body>
<h1 id="logo" class="logo"> <h1 id="logo" class="logo">
<img src="logo-ynh-white.svg"/> <img src="logo-ynh-white.svg"/>
</h1> </h1>
<div class="overlay">
<div class="wrapper"> <div class="overlay">
<form class="form" name="input" action="" method="post">
<input id="server" type="text" name="server" placeholder="Link to your YunoHost server" autofocus required> <div class="ynh-wrapper login">
<input type="submit" value="Install <?php echo $app; ?>">
</form> <?php
// The app is official, display an install form
if($app_status == 'official') {
?>
<form class="login-form" name="input" action="" method="post">
<div class="form-group">
<label class="icon icon-connexion" for="server"><span class="element-invisible"><?php echo $locale['server_link']; ?></span></label>
<input id="server" type="text" name="server" placeholder="<?php echo $locale['server_link']; ?>" class="form-text" autofocus required>
</div>
<input type="submit" value="<?php echo str_replace("{app_name}", $app_name, $locale['install_button']); ?>" class="btn classic-btn large-btn">
</form>
<?php
}
// The app is community, display a specific form and a warning
else if($app_status == 'community') {
?>
<div class="wrapper messages warning">
<p><?php echo str_replace(["{app_name}", "{app_state}"], [$app_name, $locale[$app_state]], $locale['community_warning']); ?></p>
</div> </div>
<?php
// If the user submitted his or her server and the app is community, redirects to the server
if(isset($_POST['server']) AND !empty($_POST['server']) AND $app_status == 'community') {
$server = rtrim(preg_replace('#^https?://#', '', $_POST['server']),"/");
$url = 'https://'.$server.'/yunohost/admin/#/apps/install/apps';
?>
<form class="login-form" name="input" action="<?php echo $url; ?>" method="get">
<p style="text-align:center;color:white;"><?php echo $locale['community_instructions']; ?></p>
<div class="form-group">
<label class="icon icon-pencil" for="git"><span class="element-invisible"><?php echo $app_git; ?></span></label>
<input id="git" type="text" name="git" value="<?php echo $app_git; ?>" class="form-text" readonly onClick="this.select();">
</div>
<input type="submit" value="<?php echo $locale['community_redirect']; ?>" class="btn classic-btn large-btn">
</form>
<?php
}
else {
// Display the server form
?>
<form class="login-form" name="input" action="" method="post">
<div class="form-group">
<label class="icon icon-connexion" for="server"><span class="element-invisible"><?php echo $locale['server_link']; ?></span></label>
<input id="server" type="text" name="server" placeholder="<?php echo $locale['server_link']; ?>" class="form-text" autofocus required>
</div>
<input type="submit" value="<?php echo str_replace("{app_name}", $app_name, $locale['install_button']); ?> (community)" class="btn classic-btn large-btn">
</form>
<?php } ?>
<?php
}
// The app is neither official, nor community
else {
?>
<div class="wrapper messages danger">
<p><?php echo $locale['app_notfound']; ?></p>
</div> </div>
<?php
}
?>
</div>
<!-- Scripts -->
<script src="assets/js/global.js"></script>
</body> </body>
</html> </html>

12
locales/en.json Normal file
View file

@ -0,0 +1,12 @@
{
"title": "Install {app_name} with YunoHost",
"server_link": "Link to your YunoHost server",
"install_button": "Install {app_name}",
"community_warning": "<strong>WARNING</strong>: {app_name} is <strong>NOT officially supported</strong> by the YunoHost team yet. Install it <strong>at your own risk</strong>. The community maintainer has described this app to be in a {app_state} state.",
"community_instructions": "Copy the following link. You will then be redirected to your server apps installation page in which you will need to paste this link.",
"community_redirect": "Go to your server",
"app_notfound": "This application is neither officially nor community maintained.",
"working": "working",
"inprogress": "in progress",
"notworking": "not working"
}

12
locales/fr.json Normal file
View file

@ -0,0 +1,12 @@
{
"title": "Installer {app_name} avec YunoHost",
"server_link": "Lien vers votre serveur YunoHost",
"install_button": "Installer {app_name}",
"community_warning": "<strong>ATTENTION</strong> : {app_name} <strong>N'EST PAS officiellement prise en charge</strong> pour le moment par l'équipe YunoHost. Installez cette application <strong>à vos risques et périls</strong>. Le mainteneur de cette application la décrit comme étant dans un état {app_state}.",
"community_instructions": "Merci de copier le lien suivant. Vous serez ensuite redirigé vers la page d'installation d'applications de votre serveur dans laquelle vous devrez coller ce lien.",
"community_redirect": "Accéder au serveur",
"app_notfound": "Cette application n'est maintenue ni officiellemment, ni par la communauté.",
"working": "fonctionnel",
"inprogress": "non fonctionnel",
"notworking": "en cours de développement"
}