diff --git a/src/dist/js/script.min.js b/src/dist/js/script.min.js index 02a8c802..8acdf01d 100644 --- a/src/dist/js/script.min.js +++ b/src/dist/js/script.min.js @@ -10,5 +10,16 @@ this.inlineStack=[],this.compileChildren(t,e);var n,r=t.opcodes;this.i=0;for(var !function(t){"function"==typeof define&&define.amd?define(["jquery","sammy"],t):(window.Sammy=window.Sammy||{}).Storage=t(window.jQuery,window.Sammy)}(function(t,e){return e.Store=function(i){var n=this;this.options=i||{},this.name=this.options.name||"store",this.element=this.options.element||"body",this.$element=t(this.element),t.isArray(this.options.type)?t.each(this.options.type,function(t,i){return e.Store.isAvailable(i)?(n.type=i,!1):void 0}):this.type=this.options.type||"memory",this.meta_key=this.options.meta_key||"__keys__",this.storage=new e.Store[e.Store.stores[this.type]](this.name,this.element,this.options)},e.Store.stores={memory:"Memory",data:"Data",local:"LocalStorage",session:"SessionStorage",cookie:"Cookie"},t.extend(e.Store.prototype,{isAvailable:function(){return t.isFunction(this.storage.isAvailable)?this.storage.isAvailable():!0},exists:function(t){return this.storage.exists(t)},set:function(t,e){var i="string"==typeof e?e:JSON.stringify(e);return t=t.toString(),this.storage.set(t,i),t!=this.meta_key&&(this._addKey(t),this.$element.trigger("set-"+this.name,{key:t,value:e}),this.$element.trigger("set-"+this.name+"-"+t,{key:t,value:e})),e},get:function(t){var e=this.storage.get(t);if("undefined"==typeof e||null==e||""==e)return e;try{return JSON.parse(e)}catch(i){return e}},clear:function(t){return this._removeKey(t),this.storage.clear(t)},clearAll:function(){var t=this;this.each(function(e,i){t.clear(e)})},keys:function(){return this.get(this.meta_key)||[]},each:function(t){var e,i=0,n=this.keys();for(i;ithis.$items.length-1||0>e?void 0:this.sliding?this.$element.one("slid",function(){i.to(e)}):o==e?this.pause().cycle():this.slide(e>o?"next":"prev",t(this.$items[e]))},e.prototype.pause=function(e){return e||(this.paused=!0),this.$element.find(".next, .prev").length&&t.support.transition.end&&(this.$element.trigger(t.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},e.prototype.next=function(){return this.sliding?void 0:this.slide("next")},e.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},e.prototype.slide=function(e,i){var o=this.$element.find(".item.active"),n=i||o[e](),s=this.interval,a="next"==e?"left":"right",r="next"==e?"first":"last",l=this;if(!n.length){if(!this.options.wrap)return;n=this.$element.find(".item")[r]()}this.sliding=!0,s&&this.pause();var h=t.Event("slide.bs.carousel",{relatedTarget:n[0],direction:a});if(!n.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var e=t(l.$indicators.children()[l.getActiveIndex()]);e&&e.addClass("active")})),t.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(h),h.isDefaultPrevented())return;n.addClass(e),n[0].offsetWidth,o.addClass(a),n.addClass(a),o.one(t.support.transition.end,function(){n.removeClass([e,a].join(" ")).addClass("active"),o.removeClass(["active",a].join(" ")),l.sliding=!1,setTimeout(function(){l.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(h),h.isDefaultPrevented())return;o.removeClass("active"),n.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var i=t.fn.carousel;t.fn.carousel=function(i){return this.each(function(){var o=t(this),n=o.data("bs.carousel"),s=t.extend({},e.DEFAULTS,o.data(),"object"==typeof i&&i),a="string"==typeof i?i:s.slide;n||o.data("bs.carousel",n=new e(this,s)),"number"==typeof i?n.to(i):a?n[a]():s.interval&&n.pause().cycle()})},t.fn.carousel.Constructor=e,t.fn.carousel.noConflict=function(){return t.fn.carousel=i,this},t(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(e){var i,o=t(this),n=t(o.attr("data-target")||(i=o.attr("href"))&&i.replace(/.*(?=#[^\s]+$)/,"")),s=t.extend({},n.data(),o.data()),a=o.attr("data-slide-to");a&&(s.interval=!1),n.carousel(s),(a=o.attr("data-slide-to"))&&n.data("bs.carousel").to(a),e.preventDefault()}),t(window).on("load",function(){t('[data-ride="carousel"]').each(function(){var e=t(this);e.carousel(e.data())})})}(window.jQuery),+function(t){"use strict";var e=function(i,o){this.$element=t(i),this.options=t.extend({},e.DEFAULTS,o),this.transitioning=null,this.options.parent&&(this.$parent=t(this.options.parent)),this.options.toggle&&this.toggle()};e.DEFAULTS={toggle:!0},e.prototype.dimension=function(){var t=this.$element.hasClass("width");return t?"width":"height"},e.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var e=t.Event("show.bs.collapse");if(this.$element.trigger(e),!e.isDefaultPrevented()){var i=this.$parent&&this.$parent.find("> .panel > .in");if(i&&i.length){var o=i.data("bs.collapse");if(o&&o.transitioning)return;i.collapse("hide"),o||i.data("bs.collapse",null)}var n=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[n](0),this.transitioning=1;var s=function(){this.$element.removeClass("collapsing").addClass("in")[n]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!t.support.transition)return s.call(this);var a=t.camelCase(["scroll",n].join("-"));this.$element.one(t.support.transition.end,t.proxy(s,this)).emulateTransitionEnd(350)[n](this.$element[0][a])}}},e.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var e=t.Event("hide.bs.collapse");if(this.$element.trigger(e),!e.isDefaultPrevented()){var i=this.dimension();this.$element[i](this.$element[i]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var o=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return t.support.transition?void this.$element[i](0).one(t.support.transition.end,t.proxy(o,this)).emulateTransitionEnd(350):o.call(this)}}},e.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var i=t.fn.collapse;t.fn.collapse=function(i){return this.each(function(){var o=t(this),n=o.data("bs.collapse"),s=t.extend({},e.DEFAULTS,o.data(),"object"==typeof i&&i);n||o.data("bs.collapse",n=new e(this,s)),"string"==typeof i&&n[i]()})},t.fn.collapse.Constructor=e,t.fn.collapse.noConflict=function(){return t.fn.collapse=i,this},t(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(e){var i,o=t(this),n=o.attr("data-target")||e.preventDefault()||(i=o.attr("href"))&&i.replace(/.*(?=#[^\s]+$)/,""),s=t(n),a=s.data("bs.collapse"),r=a?"toggle":o.data(),l=o.attr("data-parent"),h=l&&t(l);a&&a.transitioning||(h&&h.find('[data-toggle=collapse][data-parent="'+l+'"]').not(o).addClass("collapsed"),o[s.hasClass("in")?"addClass":"removeClass"]("collapsed")),s.collapse(r)})}(window.jQuery),+function(t){"use strict";function e(){t(o).remove(),t(n).each(function(e){var o=i(t(this));o.hasClass("open")&&(o.trigger(e=t.Event("hide.bs.dropdown")),e.isDefaultPrevented()||o.removeClass("open").trigger("hidden.bs.dropdown"))})}function i(e){var i=e.attr("data-target");i||(i=e.attr("href"),i=i&&/#/.test(i)&&i.replace(/.*(?=#[^\s]*$)/,""));var o=i&&t(i);return o&&o.length?o:e.parent()}var o=".dropdown-backdrop",n="[data-toggle=dropdown]",s=function(e){t(e).on("click.bs.dropdown",this.toggle)};s.prototype.toggle=function(o){var n=t(this);if(!n.is(".disabled, :disabled")){var s=i(n),a=s.hasClass("open");if(e(),!a){if("ontouchstart"in document.documentElement&&!s.closest(".navbar-nav").length&&t(''}),e.prototype=t.extend({},t.fn.tooltip.Constructor.prototype),e.prototype.constructor=e,e.prototype.getDefaults=function(){return e.DEFAULTS},e.prototype.setContent=function(){var t=this.tip(),e=this.getTitle(),i=this.getContent();t.find(".popover-title")[this.options.html?"html":"text"](e),t.find(".popover-content")[this.options.html?"html":"text"](i),t.removeClass("fade top bottom left right in"),t.find(".popover-title").html()||t.find(".popover-title").hide()},e.prototype.hasContent=function(){return this.getTitle()||this.getContent()},e.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},e.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},e.prototype.tip=function(){return this.$tip||(this.$tip=t(this.options.template)),this.$tip};var i=t.fn.popover;t.fn.popover=function(i){return this.each(function(){var o=t(this),n=o.data("bs.popover"),s="object"==typeof i&&i;n||o.data("bs.popover",n=new e(this,s)),"string"==typeof i&&n[i]()})},t.fn.popover.Constructor=e,t.fn.popover.noConflict=function(){return t.fn.popover=i,this}}(window.jQuery),+function(t){"use strict";function e(i,o){var n,s=t.proxy(this.process,this);this.$element=t(t(i).is("body")?window:i),this.$body=t("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",s),this.options=t.extend({},e.DEFAULTS,o),this.selector=(this.options.target||(n=t(i).attr("href"))&&n.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=t([]),this.targets=t([]),this.activeTarget=null,this.refresh(),this.process()}e.DEFAULTS={offset:10},e.prototype.refresh=function(){var e=this.$element[0]==window?"offset":"position";this.offsets=t([]),this.targets=t([]);var i=this;this.$body.find(this.selector).map(function(){var o=t(this),n=o.data("target")||o.attr("href"),s=/^#\w/.test(n)&&t(n);return s&&s.length&&[[s[e]().top+(!t.isWindow(i.$scrollElement.get(0))&&i.$scrollElement.scrollTop()),n]]||null}).sort(function(t,e){return t[0]-e[0]}).each(function(){i.offsets.push(this[0]),i.targets.push(this[1])})},e.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,i=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,o=i-this.$scrollElement.height(),n=this.offsets,s=this.targets,a=this.activeTarget;if(e>=o)return a!=(t=s.last()[0])&&this.activate(t);for(t=n.length;t--;)a!=s[t]&&e>=n[t]&&(!n[t+1]||e<=n[t+1])&&this.activate(s[t])},e.prototype.activate=function(e){this.activeTarget=e,t(this.selector).parents(".active").removeClass("active");var i=this.selector+'[data-target="'+e+'"],'+this.selector+'[href="'+e+'"]',o=t(i).parents("li").addClass("active");o.parent(".dropdown-menu").length&&(o=o.closest("li.dropdown").addClass("active")),o.trigger("activate")};var i=t.fn.scrollspy;t.fn.scrollspy=function(i){return this.each(function(){var o=t(this),n=o.data("bs.scrollspy"),s="object"==typeof i&&i;n||o.data("bs.scrollspy",n=new e(this,s)),"string"==typeof i&&n[i]()})},t.fn.scrollspy.Constructor=e,t.fn.scrollspy.noConflict=function(){return t.fn.scrollspy=i,this},t(window).on("load",function(){t('[data-spy="scroll"]').each(function(){var e=t(this);e.scrollspy(e.data())})})}(window.jQuery),+function(t){"use strict";var e=function(e){this.element=t(e)};e.prototype.show=function(){var e=this.element,i=e.closest("ul:not(.dropdown-menu)"),o=e.attr("data-target");if(o||(o=e.attr("href"),o=o&&o.replace(/.*(?=#[^\s]*$)/,"")),!e.parent("li").hasClass("active")){var n=i.find(".active:last a")[0],s=t.Event("show.bs.tab",{relatedTarget:n});if(e.trigger(s),!s.isDefaultPrevented()){var a=t(o);this.activate(e.parent("li"),i),this.activate(a,a.parent(),function(){e.trigger({type:"shown.bs.tab",relatedTarget:n})})}}},e.prototype.activate=function(e,i,o){function n(){s.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),e.addClass("active"),a?(e[0].offsetWidth,e.addClass("in")):e.removeClass("fade"),e.parent(".dropdown-menu")&&e.closest("li.dropdown").addClass("active"),o&&o()}var s=i.find("> .active"),a=o&&t.support.transition&&s.hasClass("fade");a?s.one(t.support.transition.end,n).emulateTransitionEnd(150):n(),s.removeClass("in")};var i=t.fn.tab;t.fn.tab=function(i){return this.each(function(){var o=t(this),n=o.data("bs.tab");n||o.data("bs.tab",n=new e(this)),"string"==typeof i&&n[i]()})},t.fn.tab.Constructor=e,t.fn.tab.noConflict=function(){return t.fn.tab=i,this},t(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(e){e.preventDefault(),t(this).tab("show")})}(window.jQuery),+function(t){"use strict";var e=function(i,o){this.options=t.extend({},e.DEFAULTS,o),this.$window=t(window).on("scroll.bs.affix.data-api",t.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",t.proxy(this.checkPositionWithEventLoop,this)),this.$element=t(i),this.affixed=this.unpin=null,this.checkPosition()};e.RESET="affix affix-top affix-bottom",e.DEFAULTS={offset:0},e.prototype.checkPositionWithEventLoop=function(){setTimeout(t.proxy(this.checkPosition,this),1)},e.prototype.checkPosition=function(){if(this.$element.is(":visible")){var i=t(document).height(),o=this.$window.scrollTop(),n=this.$element.offset(),s=this.options.offset,a=s.top,r=s.bottom;"object"!=typeof s&&(r=a=s),"function"==typeof a&&(a=s.top()),"function"==typeof r&&(r=s.bottom());var l=null!=this.unpin&&o+this.unpin<=n.top?!1:null!=r&&n.top+this.$element.height()>=i-r?"bottom":null!=a&&a>=o?"top":!1;this.affixed!==l&&(this.unpin&&this.$element.css("top",""),this.affixed=l,this.unpin="bottom"==l?n.top-o:null,this.$element.removeClass(e.RESET).addClass("affix"+(l?"-"+l:"")),"bottom"==l&&this.$element.offset({top:document.body.offsetHeight-r-this.$element.height()}))}};var i=t.fn.affix;t.fn.affix=function(i){return this.each(function(){var o=t(this),n=o.data("bs.affix"),s="object"==typeof i&&i;n||o.data("bs.affix",n=new e(this,s)),"string"==typeof i&&n[i]()})},t.fn.affix.Constructor=e,t.fn.affix.noConflict=function(){return t.fn.affix=i,this},t(window).on("load",function(){t('[data-spy="affix"]').each(function(){var e=t(this),i=e.data();i.offset=i.offset||{},i.offsetBottom&&(i.offset.bottom=i.offsetBottom),i.offsetTop&&(i.offset.top=i.offsetTop),e.affix(i)})})}(window.jQuery); !function(t){"use strict";var e={defaultLocale:"en",locale:"en",placeholder:/(?:\{\{|%\{)(.*?)(?:\}\}?)/gm,translations:{}};t.init=function(){for(var n in e)t[n]="undefined"!=typeof t[n]?t[n]:e[n]},t.init(),t.translateInlineHTML=function(){[].forEach.call(document.querySelectorAll("[data-y18n]"),function(e){e.innerHTML=t.translate(e.getAttribute("data-y18n"))}),[].forEach.call(document.querySelectorAll("[data-y18n-title]"),function(e){e.title=t.translate(e.getAttribute("data-y18n-title"))})},t.translate=function(e,n){n=n||{locale:t.locale},n.locale=n.locale||t.locale;var l=this.lookup(e,n);return"undefined"!=typeof l&&l!==e||n.locale===t.defaultLocale?l?l.printf(n):e:(n.locale=t.defaultLocale,this.translate(e,n))},t.lookup=function(e,n){return"undefined"==typeof n.locale&&(n.locale=t.locale),"undefined"!=typeof t.translations[n.locale]&&"undefined"!=typeof t.translations[n.locale][e]?t.translations[n.locale][e]:void 0},t.t=t.translate}("undefined"==typeof exports?this.y18n||(this.y18n={}):exports),String.prototype.printf=function(t){var e=!1,n=arguments,l=-1;return"string"==typeof n[0]&&(e=!0),t instanceof Array||e?this.replace(/\%s/g,function(a,o){if(l++,e){if("string"==typeof n[l])return n[l];throw new Error("Arguments element is an invalid type")}return t[l]}):this.replace(/{([^{}]*)}/g,function(e,n){var l=t[n];return"string"==typeof l||"number"==typeof l?l:e})}; -var app=Sammy("#main",function(a){a.bind("run",function(){$("#slider-container").width(2*$("#slider").width()+"px"),$(window).resize(function(){$("#slideBack").css("display","none"),$("#slideTo").css("display","none"),$("#slider-container").width(2*$("#slider").width()+"px").removeClass("move").css("margin-left","0px")});var a=$("#flashMessage");$("#toggle-btn",a).click(function(e){a.toggleClass("open")}),$("#clear-btn",a).click(function(e){a.removeClass("open").find(".messages").html(""),$("#slider").removeClass("with-flashMessage")})}),a.use("Handlebars","ms"),Handlebars.registerHelper("ucwords",function(a){return(a+"").replace(/^([a-z\u00E0-\u00FC])|\s+([a-z\u00E0-\u00FC])/g,function(a){return a.toUpperCase()})}),Handlebars.registerHelper("humanSize",function(a){var e=["Bytes","KB","MB","GB","TB"];if(0===a)return"n/a";var n=parseInt(Math.floor(Math.log(a)/Math.log(1024)));return Math.round(a/Math.pow(1024,n),2)+" "+e[[n]]}),Handlebars.registerHelper("humanTime",function(a){return Math.round(a)+"s"}),Handlebars.registerHelper("bitRate",function(a,e){var n=["b","Kb","Mb","Gb","Tb"];if(0===e)return"n/a";var s=a/e*8,t=parseInt(Math.floor(Math.log(s)/Math.log(1024)));return Math.round(s/Math.pow(1024,t),2)+" "+n[[t]]+"/s"}),Handlebars.registerHelper("t",function(a){var e=y18n.t(a,Array.prototype.slice.call(arguments,1));return new Handlebars.SafeString(e)});var e;e=Sammy.Store.isAvailable("session")?"session":Sammy.Store.isAvailable("cookie")?"cookie":"memory";var n=new Sammy.Store({name:"storage",type:e}),s=!1,t=3;a.helpers({serialize:function(a){var e=[];for(var n in a)a.hasOwnProperty(n)&&e.push(encodeURIComponent(n)+"="+encodeURIComponent(a[n]));return e.join("&")},flash:function(a,e){n.get("flash")||n.set("flash",!0),$("#slider").addClass("with-flashMessage"),"info"===a&&"+"===e.charAt(0)&&(a="log"),$("#flashMessage .messages").prepend('

'+e+"

")},checkInstall:function(a){domain=window.location.hostname,$.ajax({dataType:"json",url:"https://"+domain+"/yunohost/api/installed",timeout:3e3}).success(function(e){a(e.installed)}).fail(function(){a(void 0)})},api:function(a,e,t,i,r){c=this,call=function(a,e,t,i){t="undefined"!=typeof t?t:"GET",i="undefined"!=typeof i?i:{},window.navigator&&window.navigator.language&&"undefined"==typeof i.locale&&(i.locale=y18n.locale||window.navigator.language.substr(0,2));var r=i;if("/postinstall"===a){var o=!1;setInterval(function(){o=!0},1500)}s=!1,0===$("div.loader").length&&setInterval(function(){s||0!==$("div.loader").length||$("#main").append('
')},500),jQuery.ajax({url:"https://"+n.get("url")+a,type:t,crossdomain:!0,data:i,traditional:!0,dataType:"json"}).always(function(a,e,n){}).done(function(a){a=a||{},e(a)}).fail(function(s){200==s.status?e({}):401==s.status?"/login"===a?c.flash("fail",y18n.t("wrong_password")):(c.flash("fail",y18n.t("unauthorized")),c.redirect("#/login")):"undefined"!=typeof s.responseJSON?c.flash("fail",s.responseJSON.error):"undefined"!=typeof s.statusText&&"/postinstall"!==a?c.flash("fail",y18n.t("api_not_responding",[s.status+" "+s.statusText])):"/postinstall"===a?o?(interval=window.location.hostname===r.domain?2e4:5e3,checkInstall=setInterval(function(){c.checkInstall(function(a){(a||"undefined"==typeof a)&&(c.flash("success",y18n.t("installation_complete")),clearInterval(checkInstall),window.location.href="https://"+window.location.hostname+"/yunohost/admin/")})},interval)):c.flash("fail",y18n.t("error_occured")):c.flash("fail",y18n.t("error_server")),"/postinstall"!==a&&(n.clear("slide"),c.view(n.get("path-1"),null,null,!1))})},r="undefined"!=typeof r?r:!0,r?(ws=new WebSocket("wss://"+n.get("url")+"/messages"),ws.onmessage=function(a){$.each($.parseJSON(a.data),function(a,e){c.flash(a,e)})},ws.onerror=ws.onopen,ws.onclose=function(){},ws.onopen=call(a,e,t,i)):call(a,e,t,i)},view:function(a,e,t,i){if(t="undefined"!=typeof t?t:function(){},rendered=this.render("views/"+a+".ms",e),i="undefined"!=typeof i?i:!0,s=!0,$("div.loader").remove(),$("#modal").modal("hide"),i){var r=function(){rendered.swap(function(){$(".slide, .btn-breadcrumb a:not(:last-child)").on("click",function(){$(this).addClass("active"),$(this).hasClass("back")||$(this).parent(".btn-breadcrumb").length?n.set("slide","back"):n.set("slide","to")}),t(),$("html, body").scrollTop(0),setTimeout(function(){$("body").resize()},210)})};blockSize=$("#slider").width(),"back"==n.get("slide")?(n.clear("slide"),$("#slideBack").css("display","none"),$("#slider-container").removeClass("move").css("margin-left","-"+blockSize+"px"),$("#slideTo").show().html($("#main").html()),r(),$("#slider-container").addClass("move").css("margin-left","0px")):"to"==n.get("slide")?(n.clear("slide"),$("#slideTo").css("display","none"),$("#slider-container").removeClass("move").css("margin-left","-"+blockSize+"px"),$("#slider-container").removeClass("move").css("margin-left","0px"),$("#slideBack").show().html($("#main").html()),r(),$("#slider-container").addClass("move").css("margin-left","-"+blockSize+"px")):r()}else rendered.swap(function(){t(),$("html, body").scrollTop(0),$("body").resize()})},confirm:function(a,e,n,s){return n="undefined"!=typeof n?n:function(){},s="undefined"!=typeof s?s:function(){},box=$("#modal"),"string"==typeof a&&a.length?$(".title",box).html(a):box.addClass("no-title"),$(".content",box).html(e),$("footer button",box).click(function(a){a.preventDefault(),$("#modal footer button").unbind("click"),box.removeClass("no-title").modal("hide"),"confirm"==$(this).data("action")?n():s()}),box.modal("show")},arraySortById:function(a){a.sort(function(a,e){return a.id>e.id?1:a.id'+y18n.t("read_more")+")";a.flash("warning",l),n.push(o.guid)}}),$.cookie("ynhSecurityViewedItems",n,{expire:7})}).fail(function(){a.flash("fail",y18n.t("error_retrieve_feed",[n]))}),a.view("home")})}),a.get("#/login",function(a){$("#masthead").show(),$("#masthead .logout-btn").hide(),n.set("path-1","#/login"),0===$("div.loader").length&&setInterval(function(){s||0!==$("div.loader").length||$("#main").append('
')},500),a.checkInstall(function(e){e?(domain=window.location.hostname,$("div.loader").remove(),a.view("login",{domain:domain})):"undefined"==typeof e?t>0?(t--,s=!1,setTimeout(function(){a.redirect("#/")},5e3)):(t=3,$(document).ajaxError(function(e,n,s){200!==n.status&&a.flash("fail",y18n.t("api_not_responding",[n.status+" "+n.statusText])),$(document).off("ajaxError")}),s=!0,$("div.loader").remove()):($("div.loader").remove(),a.redirect("#/postinstall"))})}),a.post("#/login",function(a){n.set("url",a.params.domain+"/yunohost/api"),params={password:a.params.password},a.api("/login",function(e){n.set("connected",!0),$("#masthead .logout-btn").fadeIn(),a.flash("success",y18n.t("logged_in")),n.get("path")?a.redirect(n.get("path")):a.redirect("#/")},"POST",params,!1)}),a.get("#/logout",function(a){a.api("/logout",function(e){n.clear("url"),n.clear("connected"),n.set("path","#/"),a.flash("success",y18n.t("logged_out")),a.redirect("#/login")},"GET",{},!1)}),a.get("#/postinstall",function(a){$("#masthead").hide(),a.checkInstall(function(e){e||"undefined"==typeof e?a.redirect("#/login"):a.view("postinstall/postinstall_1")})}),a.get("#/postinstall/domain",function(a){$("#masthead").hide(),$.get("https://dyndns.yunohost.org/domains",function(){}).done(function(e){a.params.ddomains=e.map(function(a){return"."+a})}).fail(function(){a.params.ddomains=[".nohost.me",".noho.st"]}).always(function(){a.view("postinstall/postinstall_2",a.params,function(){$("#domain, #ddomain").keyup(function(a){13==a.keyCode&&$("a.savedomain").click()}),$("a.savedomain").on("click",function(e){""===$("#domain").val()?""===$("#ddomain").val()?(e.preventDefault(),n.clear("slide"),a.flash("fail",y18n.t("error_select_domain"))):domain=$("#ddomain").val()+$('select[name="ddomain-ext"]').val():domain=$("#domain").val(),n.set("maindomain",domain)})})})}),a.get("#/postinstall/password",function(a){$("#masthead").hide(),n.get("maindomain")?a.view("postinstall/postinstall_3",{domain:n.get("maindomain").toLowerCase()}):(n.clear("slide"),a.redirect("#/postinstall/domain"))}),a.post("#/postinstall",function(a){""===a.params.password||""===a.params.confirmation?a.flash("fail",y18n.t("password_empty")):a.params.password==a.params.confirmation?(""===a.params.domain?(a.flash("fail",y18n.t("error_select_domain")),n.clear("slide"),a.redirect("#/postinstall/domain")):params={domain:a.params.domain.toLowerCase()},a.confirm(y18n.t("postinstall"),y18n.t("confirm_postinstall",[a.params.domain]),function(){params.password=a.params.password,n.set("url",window.location.hostname+"/yunohost/api"),n.set("user","admin"),a.api("/postinstall",function(e){a.redirect("#/login")},"POST",params)},function(){})):a.flash("fail",y18n.t("passwords_dont_match"))}),a.get("#/users",function(a){a.api("/users",function(e){a.view("user/user_list",e)})}),a.get("#/users/create",function(a){a.api("/domains",function(e){a.view("user/user_create",e)})}),a.post("#/users",function(a){a.params.password==a.params.confirmation?a.params.password.length<4?(a.flash("fail",y18n.t("password_too_short")),n.clear("slide")):(a.params.mail=a.params.email+a.params.domain,a.api("/users",function(e){a.redirect("#/users")},"POST",a.params.toHash())):(a.flash("fail",y18n.t("passwords_dont_match")),n.clear("slide"))}),a.get("#/users/:user",function(a){a.api("/users/"+a.params.user,function(e){a.view("user/user_info",e)})}),a.get("#/users/:user/edit",function(a){a.api("/users/"+a.params.user,function(e){a.api("/domains",function(n){email=e.mail.split("@"),e.email={username:email[0],domain:email[1]},e.domains=[],$.each(n.domains,function(a,n){e.domains.push({domain:n,selected:n==e.email.domain?!0:!1})}),a.view("user/user_edit",e)})})}),a.put("#/users/:user",function(a){a.api("/users/"+a.params.user,function(e){a.params.mail!==a.params.email+a.params.domain?a.params.mail=a.params.email+a.params.domain:a.params.mail="",a.params.email=a.params.domain="","string"==typeof a.params.mailalias&&(a.params.mailalias=[a.params.mailalias]),"string"==typeof a.params.mailforward&&(a.params.mailforward=[a.params.mailforward]),a.params.add_mailalias=a.arrayDiff(a.params.mailalias,e["mail-aliases"]),a.params.remove_mailalias=a.arrayDiff(e["mail-aliases"],a.params.mailalias),a.params.add_mailforward=a.arrayDiff(a.params.mailforward,e["mail-forward"]),a.params.remove_mailforward=a.arrayDiff(e["mail-forward"],a.params.mailforward),a.params.mailalias=a.params.mailforward="",params={},$.each(a.params.toHash(),function(a,e){e.length>0&&"user"!==a&&(params[a]=e)}),$.isEmptyObject(params)?(a.flash("fail",y18n.t("error_modify_something")),n.clear("slide"),a.redirect("#/users/"+a.params.user+"/edit")):params.password?params.password==params.confirmation?params.password.length<4?(a.flash("fail",y18n.t("password_too_short")),n.clear("slide"),a.redirect("#/users/"+a.params.user+"/edit")):(params.change_password=params.password,a.api("/users/"+a.params.user,function(e){a.redirect("#/users/"+a.params.user)},"PUT",params)):(a.flash("fail",y18n.t("passwords_dont_match")),n.clear("slide"),a.redirect("#/users/"+a.params.user+"/edit")):a.api("/users/"+a.params.user,function(e){a.redirect("#/users/"+a.params.user)},"PUT",params)},"GET")}),a.get("#/users/:user/delete",function(a){a.confirm(y18n.t("users"),y18n.t("confirm_delete",[a.params.user]),function(){a.api("/users/"+a.params.user,function(e){a.redirect("#/users")},"DELETE")},function(){n.clear("slide"),a.redirect("#/users/"+a.params.user)})}),a.get("#/domains",function(a){a.api("/domains",function(e){a.api("/domains/main",function(n){domains=[],$.each(e.domains,function(a,e){domains.push({url:e,main:e==n.current_main_domain?!0:!1})}),main_domain_form=domains.length>1?!0:!1,domains.sort(function(a,e){return-2*a.main+1}),a.view("domain/domain_list",{domains:domains,main_domain_form:main_domain_form})},"PUT")})}),a.get("#/domains/add",function(a){$.get("https://dyndns.yunohost.org/domains",function(){}).done(function(e){a.params.ddomains=e.map(function(a){return"."+a})}).fail(function(){a.params.ddomains=[".nohost.me",".noho.st"]}).always(function(){data={ddomains:a.params.ddomains,domains:a.params.domains,allowDyndnsDomain:!0};var e=data.ddomains.join("|");$.each(data.domains,function(a,n){n.search(e)>0&&(data.allowDyndnsDomain=!1)}),a.view("domain/domain_add",data)})}),a.post("#/domains/add",function(a){""===a.params.domain?(""===a.params.ddomain&&(a.flash("fail",y18n.t("error_select_domain")),n.clear("slide"),a.redirect("#/domains/add")),params={domain:a.params.ddomain+a.params["ddomain-ext"]},endurl="dyndns"):(params={domain:a.params.domain},endurl=""),a.api("/domains?"+endurl,function(e){a.redirect("#/domains")},"POST",params)}),a.get("#/domains/:domain/delete",function(a){a.confirm(y18n.t("domains"),y18n.t("confirm_delete",[a.params.domain]),function(){a.api("/domains/"+a.params.domain,function(e){n.clear("slide"),a.redirect("#/domains")},"DELETE")},function(){n.clear("slide"),a.redirect("#/domains")})}),a.post("#/domains",function(a){""===a.params.domain?(a.flash("fail",y18n.t("error_select_domain")),n.clear("slide"),a.redirect("#/domains")):a.confirm(y18n.t("domains"),y18n.t("confirm_change_maindomain"),function(){params={new_domain:a.params.domain},a.api("/domains/main",function(e){n.clear("slide"),a.redirect("#/domains")},"PUT",params),refreshDomain=window.setTimeout(function(){n.clear("slide"),a.redirect("#/domains")},15e3)},function(){n.clear("slide"),a.redirect("#/domains")})}),a.get("#/apps",function(a){a.api("/apps",function(e){data2={apps:[],installed_apps:!0},$.each(e.apps,function(a,e){e.installed&&(e.name=e.label?e.label:e.name,data2.apps.push(e))}),a.arraySortById(data2.apps),a.view("app/app_list",data2)})}),a.get("#/apps/install",function(a){a.api("/apps",function(e){a.api("/apps?raw",function(n){data2={apps:[],installed_apps:!1},$.each(e.apps,function(a,e){e.installed&&"true"!=n[e.id].manifest.multi_instance||e.id.match(/__[0-9]{1,5}$/)||(e.install_link=!0,data2.apps.push(e))}),a.arraySortById(data2.apps),a.view("app/app_list",data2)})})}),a.get("#/apps/refresh",function(a){a.api("/appslists",function(e){a.redirect("#/apps/install")},"PUT")}),a.get("#/apps/:app",function(a){a.api("/apps/"+a.params.app+"?raw",function(e){e.settings.allowed_users=e.settings.allowed_users?e.settings.allowed_users.replace(",",", ")+".":y18n.t("everyone_has_access"),e.description="undefined"!=typeof e.manifest.description[y18n.locale]?e.manifest.description[y18n.locale]:e.manifest.description.en,e.manifest.multi_instance="true"==e.manifest.multi_instance?y18n.t("yes"):y18n.t("no");var n=new Date(1e3*e.settings.install_time);e.install_time=n.getDate()+"/"+(n.getMonth()+1)+"/"+n.getFullYear(),a.view("app/app_info",e)})}),a.get("#/apps/:app/debug",function(a){a.api("/apps/"+a.params.app+"/debug",function(e){a.view("app/app_debug",e)})}),a.get("#/apps/install/custom",function(a){a.redirect("#/apps/install")}),a.helper("appInstallForm",function(a,e,n){data={id:a,manifest:e},"undefined"!=typeof data.manifest.arguments.install&&$.each(data.manifest.arguments.install,function(a,e){data.manifest.arguments.install[a].type="undefined"!=typeof e.type?e.type:"string",data.manifest.arguments.install[a].inputType="text",data.manifest.arguments.install[a].required="undefined"!=typeof e.optional&&"true"==e.optional?"":"required","undefined"!=typeof data.manifest.arguments.install[a].choices&&$.each(data.manifest.arguments.install[a].choices,function(e,n){data.manifest.arguments.install[a].choices[e]={value:n,label:n,selected:n==data.manifest.arguments.install[a]["default"]?!0:!1}}),("domain"==e.name||"domain"==data.manifest.arguments.install[a].type)&&(data.manifest.arguments.install[a].choices=[],$.each(n.domains,function(e,n){data.manifest.arguments.install[a].choices.push({value:n,label:n,selected:!1})}),data.manifest.arguments.install[a].help=""+y18n.t("manage_domains")+""),("admin"==e.name||"user"==data.manifest.arguments.install[a].type)&&(data.manifest.arguments.install[a].choices=[],$.each(n.users,function(e,n){data.manifest.arguments.install[a].choices.push({value:e,label:n.fullname+" ("+n.mail+")",selected:!1})}),data.manifest.arguments.install[a].help=""+y18n.t("manage_users")+""),"app"==data.manifest.arguments.install[a].type&&(data.manifest.arguments.install[a].choices=[],$.each(n.apps,function(e,n){data.manifest.arguments.install[a].choices.push({value:n.id,label:n.name,selected:!1})}),data.manifest.arguments.install[a].help=""+y18n.t("manage_apps")+""),("password"==e.name||"password"==data.manifest.arguments.install[a].type)&&(data.manifest.arguments.install[a].inputType="password"),data.manifest.arguments.install[a].label="undefined"!=typeof data.manifest.arguments.install[a].ask[y18n.locale]?data.manifest.arguments.install[a].ask[y18n.locale]:data.manifest.arguments.install[a].ask.en}),data.description="undefined"!=typeof data.manifest.description[y18n.locale]?data.manifest.description[y18n.locale]:data.manifest.description.en,data.manifest.multi_instance="true"==data.manifest.multi_instance?y18n.t("yes"):y18n.t("no"),c.view("app/app_install",data)}),a.get("#/apps/install/:app",function(a){a.api("/apps?raw",function(e){a.appInstallForm(a.params.app,e[a.params.app].manifest,a.params)})}),a.post("#/apps",function(a){"/"!==a.params.path||confirm(y18n.t("confirm_install_domain_root",[a.params.domain]))?(params={label:a.params.label,app:a.params.app},delete a.params.label,delete a.params.app,params.args=a.serialize(a.params.toHash()),""===params.args&&delete params.args,a.api("/apps",function(){a.redirect("#/apps")},"POST",params)):(a.flash("warning",y18n.t("app_install_cancel")),n.clear("slide"),a.redirect("#/apps/install"))}),a.post("#/apps/install/custom",function(a){params={label:a.params.label,app:a.params.url},delete a.params.label,delete a.params.url,a.confirm(y18n.t("applications"),y18n.t("confirm_install_custom_app"),function(){params.app=params.app.replace(/\/?$/,"/"),jQuery.ajax({url:params.app.replace("github.com","raw.githubusercontent.com")+"master/manifest.json",type:"GET"}).done(function(e){e=jQuery.parseJSON(e)||{},a.appInstallForm(params.app,e,a.params)}).fail(function(e){a.flash("fail",y18n.t("app_install_custom_no_manifest")),n.clear("slide"),a.redirect("#/apps/install")})},function(){a.flash("warning",y18n.t("app_install_cancel")),n.clear("slide"),a.redirect("#/apps/install")})}),a.get("#/apps/:app/uninstall",function(a){a.confirm(y18n.t("applications"),y18n.t("confirm_uninstall",[a.params.app]),function(){a.api("/apps/"+a.params.app,function(){a.redirect("#/apps")},"DELETE")},function(){n.clear("slide"),a.redirect("#/apps/"+a.params.app)})}),a.get("#/apps/:app/access",function(a){a.api("/apps/"+a.params.app+"?raw",function(e){a.api("/users",function(n){"undefined"!=typeof e.settings.allowed_users?0===e.settings.allowed_users.length?e.settings.allowed_users=[]:e.settings.allowed_users=e.settings.allowed_users.split(","):(e.settings.allowed_users=[],e.settings.allow_everyone=!0),e.users=[],$.each(n.users,function(a,n){-1===e.settings.allowed_users.indexOf(a)?e.users.push({value:a,label:n.fullname+" ("+n.mail+")"}):e.settings.allowed_users[e.settings.allowed_users.indexOf(a)]={username:a,fullname:n.fullname,mail:n.mail}}),a.view("app/app_access",e)})})}),a.get("#/apps/:app/access/remove",function(a){a.confirm(y18n.t("applications"),y18n.t("confirm_access_remove_all",[a.params.app]),function(){params={apps:a.params.app,users:[]},a.api("/access?"+a.serialize(params),function(e){n.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")},"DELETE",params)},function(){n.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")})}),a.get("#/apps/:app/access/remove/:user",function(a){a.confirm(y18n.t("applications"),y18n.t("confirm_access_remove_user",[a.params.app,a.params.user]),function(){params={apps:a.params.app,users:a.params.user},a.api("/access?"+a.serialize(params),function(e){n.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")},"DELETE",params)},function(){n.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")})}),a.get("#/apps/:app/access/add",function(a){a.confirm(y18n.t("applications"),y18n.t("confirm_access_add",[a.params.app]),function(){params={apps:a.params.app,users:null},a.api("/access",function(){n.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")},"PUT",params)},function(){n.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")})}),a.post("#/apps/:app/access/add",function(a){params={users:a.params.user,apps:a.params.app},a.api("/access",function(){n.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")},"PUT",params)}),a.get("#/apps/:app/access/clear",function(a){a.confirm(y18n.t("applications"),y18n.t("confirm_access_clear",[a.params.app]),function(){params={apps:a.params.app},a.api("/access",function(){n.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")},"POST",params)},function(){n.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")})}),a.get("#/apps/:app/default",function(a){a.confirm(y18n.t("applications"),y18n.t("confirm_app_default"),function(){a.api("/apps/"+a.params.app+"/default",function(){n.clear("slide"),a.redirect("#/apps/"+a.params.app)},"PUT")},function(){n.clear("slide"),a.redirect("#/apps/"+a.params.app)})}),a.get("#/services",function(a){a.api("/services",function(e){data2={services:[]},$.each(e,function(a,e){e.name=a,e.is_loaded="enabled"==e.loaded?!0:!1,e.is_running="running"==e.status?!0:!1,e.status=y18n.t(e.status),e.loaded=y18n.t(e.loaded),data2.services.push(e)}),a.view("service/service_list",data2)})}),a.get("#/services/:service",function(a){a.api("/services/"+a.params.service,function(e){data2={service:e},data2.service.name=a.params.service,data2.service.is_loaded="enabled"==e.loaded?!0:!1,data2.service.is_running="running"==e.status?!0:!1,data2.service.status=y18n.t(e.status),data2.service.loaded=y18n.t(e.loaded),n.clear("slide"),a.view("service/service_info",data2)},"GET")}),a.get("#/services/:service/log",function(a){params={number:50},a.api("/services/"+a.params.service+"/log",function(e){data2={logs:[],name:a.params.service},$.each(e,function(a,e){data2.logs.push({filename:a,filecontent:e.join("\n")})}),a.view("service/service_log",data2)},"GET",params)}),a.get("#/services/:service/:action",function(a){a.confirm("Service",y18n.t("confirm_service_action",[y18n.t(a.params.action),a.params.service]),function(){var e=null,s=a.params.service;switch(a.params.action){case"start":e="PUT";break;case"stop":e="DELETE";break;case"enable":e="PUT",s+="/enable";break;case"disable":e="DELETE",s+="/enable";break;default:a.flash("fail",y18n.t("unknown_action",[a.params.action])),n.clear("slide"),a.redirect("#/services/"+a.params.service)}e&&s?a.api("/services/"+s,function(e){n.clear("slide"),a.redirect("#/services/"+a.params.service)},e):(n.clear("slide"),a.redirect("#/services/"+a.params.service))},function(){n.clear("slide"),a.redirect("#/services/"+a.params.service)})}),a.get("#/tools/firewall",function(a){a.api("/firewall?raw",function(e){var n={ports:{},upnp:!1};$.each(["ipv4","ipv6","uPnP"],function(a,s){$.each(["TCP","UDP"],function(a,t){n.ports[t]=n.ports[t]||{},$.each(e[s][t],function(a,e){n.ports[t][e]=n.ports[t][e]||{},n.ports[t][e][s]=!0})})}),n.upnp=e.uPnP.enabled,a.view("tools/tools_firewall",n)})}),a.get("#/tools/firewall/upnp/:action",function(a){a.confirm(y18n.t("firewall"),y18n.t("confirm_upnp_action",[y18n.t(a.params.action)]),function(){params={action:a.params.action},a.api("/firewall/upnp",function(e){n.clear("slide"),a.redirect("#/tools/firewall")},"GET",params)},function(){n.clear("slide"),a.redirect("#/tools/firewall")})}),a.helper("togglePort",function(a,e,s,t){var i=null,r=[],o=this;switch((a!=parseInt(a)||0>a||a>65535)&&(o.flash("fail",y18n.t("unknown_argument",[a])),n.clear("slide"),o.redirect("#/tools/firewall")),s){case"ipv4":r="ipv4_only";break;case"ipv6":r="ipv6_only"}switch(e){case"udp":e="UDP";break;case"both":e="Both";break;default:e="TCP"}switch(t){case"open":i="POST";break;case"close":i="DELETE";break;default:o.flash("fail",y18n.t("unknown_action",[t])),n.clear("slide"),o.redirect("#/tools/firewall")}if(null!==i&&null!==e&&null!==a){var l={port:a,protocol:e};o.api("/firewall/port?"+r,function(a){n.clear("slide"),o.redirect("#/tools/firewall")},i,l)}else n.clear("slide"),o.redirect("#/tools/firewall")}),a.get("#/tools/firewall/port/:port/:protocol/:connection/:action",function(a){a.confirm(y18n.t("firewall"),y18n.t("confirm_firewall",[y18n.t(a.params.action),a.params.port,y18n.t(a.params.protocol),y18n.t(a.params.connection)]),function(){a.togglePort(a.params.port,a.params.protocol,a.params.connection,a.params.action)},function(){n.clear("slide"),a.redirect("#/tools/firewall")})}),a.post("#/tools/firewall/port",function(a){a.confirm(y18n.t("firewall"),y18n.t("confirm_firewall",[y18n.t(a.params.action),a.params.port,y18n.t(a.params.protocol),y18n.t(a.params.connection)]),function(){a.togglePort(a.params.port,a.params.protocol,a.params.connection,a.params.action)},function(){n.clear("slide"),a.redirect("#/tools/firewall")})}),a.get("#/tools/monitor",function(a){monitorData={},a.api("/services/glances",function(e){monitorData.status=!0,"running"==e.status?a.api("/monitor/system",function(e){monitorData.system=e,a.api("/monitor/disk",function(e){monitorData.disk=e,a.api("/monitor/network",function(e){monitorData.network=e,delete monitorData.network.usage.lo,a.api("/version",function(e){monitorData.versions=e,a.view("tools/tools_monitoring",monitorData)})})})}):(monitorData.status=!1,a.view("tools/tools_monitoring",monitorData))},"GET")}),a.get("#/tools",function(a){a.view("tools/tools_list")}),a.get("#/tools/adminpw",function(a){a.view("tools/tools_adminpw")}),a.put("#/tools/adminpw",function(a){params={},$.each(a.params.toHash(),function(a,e){""!==e&&(params[a]=e)}),$.isEmptyObject(params)?(a.flash("fail",y18n.t("error_modify_something")),n.clear("slide"),a.redirect("#/tools/adminpw")):params.new_password!==params.confirm_new_password?(a.flash("fail",y18n.t("passwords_dont_match")),n.clear("slide"),a.redirect("#/tools/adminpw")):a.api("/login",function(e){delete params.old_password,delete params.confirm_new_password,a.api("/adminpw",function(e){a.redirect("#/logout")},"PUT",params)},"POST",{password:params.old_password},!1)}),a.get("#/update",function(a){a.api("/update",function(e){packagesLength=e.packages.length;for(var n=0;n"),-1!=["moulinette","moulinette-yunohost","yunohost-admin","yunohost-config-nginx","ssowat","python"].indexOf(e.packages[n].name)&&(a.flash("warning",y18n.t("system_delayed_upgrade_warning",[e.packages[n].name])),e.packages[n].delayed=!0);a.view("update/update",e)},"PUT")}),a.get("#/upgrade/:type",function(a){"apps"!==a.params.type&&"packages"!==a.params.type?(a.flash("fail",y18n.t("unknown_argument",[a.params.type])),n.clear("slide"),a.redirect("#/update")):a.confirm(y18n.t("tools"),y18n.t("confirm_update_type",[y18n.t("system_"+a.params.type).toLowerCase()]),function(){endurl="","packages"==a.params.type?endurl="ignore_apps":"apps"==a.params.type&&(endurl="ignore_packages"),a.api("/upgrade?"+endurl,function(e){e.logs=e.log,a.view("upgrade/upgrade",e)},"PUT")},function(){n.clear("slide"),a.redirect("#/update")})}),a.get("#/tools/ca",function(a){a.view("tools/tools_ca")}),a.get("#/tools/security-feed",function(a){data={items:[]};var e="https://forum.yunohost.org",n="https://forum.yunohost.org/c/security",s="https://yunohost.org/security.rss";data.url={web:n,rss:s},$.ajax({url:s,dataType:"xml"}).done(function(n){$("item",n).each(function(a,n){var s=$("link",n)[0].innerHTML;"string"==typeof s&&""!==s&&"/"==s.charAt(0)&&(s=e+s);var t=$("description",n)[0].textContent;t=t.replace('href="/','href="'+e+"/");var i={guid:$("guid",n)[0].innerHTML,title:$("title",n)[0].innerHTML,url:s,desc:t,date:$("pubDate",n)[0].innerHTML.split(" +")[0]};data.items.push(i)}),a.view("tools/tools_security_feed",data)}).fail(function(){a.flash("fail",y18n.t("error_retrieve_feed",[s]))})}),a.get("#/tools/versions",function(a){a.api("/version",function(e){a.view("tools/tools_versions",{versions:e})})});a.get("#/backup",function(a){var e=[],n={id:"local",name:y18n.t("local_archives"),uri:"/home/yunohost.backup/"};e.push(n),a.view("backup/backup",{storages:e})}),a.get("#/storages/create",function(a){a.view("backup/storage_create",{})}),a.post("#/storages",function(a){n.clear("slide"),a.redirect("#/storages")}),a.get("#/backup/:storage/create",function(a){var e=[];e.storage={id:a.params.storage,name:y18n.t("local_archives")},a.api("/hooks/backup",function(n){e.hooks=a.groupHooks(n.hooks),e.apps={},a.api("/apps?raw",function(n){$.each(n,function(a,n){n.installed&&(e.apps[a]={id:a,name:n.manifest.name})}),a.view("backup/backup_create",e)})})}),a.post("#/backup/:storage",function(a){var e=a.ungroupHooks(a.params.hooks,a.params.apps);a.api("/backup",function(){n.clear("slide"),a.redirect("#/backup/"+a.params.storage)},"POST",e)}),a.post("#/backup/:storage/:archive/restore",function(a){a.confirm(y18n.t("backup"),y18n.t("confirm_restore",[a.params.archive]),$.proxy(function(a){var e=a.ungroupHooks(a.params.hooks,a.params.apps);e.force="",a.api("/backup/restore/"+a.params.archive,function(e){n.clear("slide"),a.redirect("#/backup/"+a.params.storage+"/"+a.params.archive)},"POST",e)},this,a),function(){n.clear("slide"),a.redirect("#/backup/"+a.params.storage+"/"+a.params.archive)})}),a.get("#/backup/:storage/:archive/delete",function(a){ -a.confirm(y18n.t("backup"),y18n.t("confirm_delete",[a.params.archive]),function(){a.api("/backup/archives/"+a.params.archive,function(e){a.redirect("#/backup/"+a.params.storage)},"DELETE")},function(){n.clear("slide"),a.redirect("#/backup/"+a.params.storage+"/"+a.params.archive)})}),a.get("#/backup/:storage/:archive/download",function(a){a.api("/backup/"+a.params.archive+"/download",function(e){a.redirect("#/backup/"+a.params.storage+"/"+a.params.archive)},"GET")}),a.get("#/backup/:storage/:archive/copy",function(a){n.clear("slide"),a.redirect("#/backup/"+a.params.storage+"/"+a.params.archive)}),a.get("#/backup/:storage/:archive/upload",function(a){n.clear("slide"),a.redirect("#/backup/"+a.params.storage+"/"+a.params.archive)}),a.get("#/backup/:storage/:archive",function(a){a.api("/backup/archives/"+a.params.archive+"?with_details",function(e){e.storage={id:a.params.storage,name:y18n.t("local_archives")},e.other_storages=[],e.name=a.params.archive,e.hooks=a.groupHooks(Object.keys(e.hooks)),e.items=e.hooks!={}||e.apps!=[],a.view("backup/backup_info",e)})}),a.get("#/backup/:storage",function(a){a.api("/backup/archives?with_info",function(e){e.storage={id:"local",name:y18n.t("local_archives")},e.archives2=[],$.each(e.archives,function(a,n){n.name=a,e.archives2.unshift(n)}),e.archives=e.archives2,a.view("backup/backup_list",e)})})});$.getJSON("locales/en.json",function(a){y18n.translations.en=a,y18n.translateInlineHTML()}),window.navigator&&window.navigator.language&&(y18n.locale=window.navigator.language.substr(0,2),"en"!==y18n.locale&&$.getJSON("locales/"+y18n.locale+".json",function(a){y18n.translations[y18n.locale]=a,y18n.translateInlineHTML()})),$(document).ready(function(){app.run("#/")}); \ No newline at end of file +!function(){var n=Sammy("#main",function(n){n.bind("run",function(){$("#slider-container").width(2*$("#slider").width()+"px"),$(window).resize(function(){$("#slideBack").css("display","none"),$("#slideTo").css("display","none"),$("#slider-container").width(2*$("#slider").width()+"px").removeClass("move").css("margin-left","0px")});var n=$("#flashMessage");$("#toggle-btn",n).click(function(e){n.toggleClass("open")}),$("#clear-btn",n).click(function(e){n.removeClass("open").find(".messages").html(""),$("#slider").removeClass("with-flashMessage")})}),n.use("Handlebars","ms"),Handlebars.registerHelper("ucwords",function(n){return(n+"").replace(/^([a-z\u00E0-\u00FC])|\s+([a-z\u00E0-\u00FC])/g,function(n){return n.toUpperCase()})}),Handlebars.registerHelper("humanSize",function(n){var e=["Bytes","KB","MB","GB","TB"];if(0===n)return"n/a";var a=parseInt(Math.floor(Math.log(n)/Math.log(1024)));return Math.round(n/Math.pow(1024,a),2)+" "+e[[a]]}),Handlebars.registerHelper("humanTime",function(n){return Math.round(n)+"s"}),Handlebars.registerHelper("bitRate",function(n,e){var a=["b","Kb","Mb","Gb","Tb"];if(0===e)return"n/a";var r=n/e*8,t=parseInt(Math.floor(Math.log(r)/Math.log(1024)));return Math.round(r/Math.pow(1024,t),2)+" "+a[[t]]+"/s"}),Handlebars.registerHelper("t",function(n){var e=y18n.t(n,Array.prototype.slice.call(arguments,1));return new Handlebars.SafeString(e)});var e;e=Sammy.Store.isAvailable("session")?"session":Sammy.Store.isAvailable("cookie")?"cookie":"memory",n.store=new Sammy.Store({name:"storage",type:e}),n.loaded=!1,n.isInstalledTry=3,n.notFound=function(){window.location="#/"}});$.getJSON("locales/en.json",function(n){y18n.translations.en=n,y18n.translateInlineHTML()}),window.navigator&&window.navigator.language&&(y18n.locale=window.navigator.language.substr(0,2),"en"!==y18n.locale&&$.getJSON("locales/"+y18n.locale+".json",function(n){y18n.translations[y18n.locale]=n,y18n.translateInlineHTML()})),$(document).ready(function(){n.run("#/")})}(); +!function(){var n=Sammy.apps["#main"],e=n.store;n.helpers({serialize:function(n){var e=[];for(var o in n)n.hasOwnProperty(o)&&e.push(encodeURIComponent(o)+"="+encodeURIComponent(n[o]));return e.join("&")},flash:function(n,o){e.get("flash")||e.set("flash",!0),$("#slider").addClass("with-flashMessage"),"info"===n&&"+"===o.charAt(0)&&(n="log"),$("#flashMessage .messages").prepend('

'+o+"

")},checkInstall:function(n){domain=window.location.hostname,$.ajax({dataType:"json",url:"https://"+domain+"/yunohost/api/installed",timeout:3e3}).success(function(e){n(e.installed)}).fail(function(){n(void 0)})},api:function(n,o,t,a,s){c=this,call=function(n,o,t,a){t="undefined"!=typeof t?t:"GET",a="undefined"!=typeof a?a:{},window.navigator&&window.navigator.language&&"undefined"==typeof a.locale&&(a.locale=y18n.locale||window.navigator.language.substr(0,2));var s=a;if("/postinstall"===n){var i=!1;setInterval(function(){i=!0},1500)}loaded=!1,0===$("div.loader").length&&setInterval(function(){loaded||0!==$("div.loader").length||$("#main").append('
')},500),jQuery.ajax({url:"https://"+e.get("url")+n,type:t,crossdomain:!0,data:a,traditional:!0,dataType:"json"}).always(function(n,e,o){}).done(function(n){n=n||{},o(n)}).fail(function(t){200==t.status?o({}):401==t.status?"/login"===n?c.flash("fail",y18n.t("wrong_password")):(c.flash("fail",y18n.t("unauthorized")),c.redirect("#/login")):"undefined"!=typeof t.responseJSON?c.flash("fail",t.responseJSON.error):"undefined"!=typeof t.statusText&&"/postinstall"!==n?c.flash("fail",y18n.t("api_not_responding",[t.status+" "+t.statusText])):"/postinstall"===n?i?(interval=window.location.hostname===s.domain?2e4:5e3,checkInstall=setInterval(function(){c.checkInstall(function(n){(n||"undefined"==typeof n)&&(c.flash("success",y18n.t("installation_complete")),clearInterval(checkInstall),window.location.href="https://"+window.location.hostname+"/yunohost/admin/")})},interval)):c.flash("fail",y18n.t("error_occured")):c.flash("fail",y18n.t("error_server")),"/postinstall"!==n&&(e.clear("slide"),c.view(e.get("path-1"),null,null,!1))})},s="undefined"!=typeof s?s:!0,s?(ws=new WebSocket("wss://"+e.get("url")+"/messages"),ws.onmessage=function(n){$.each($.parseJSON(n.data),function(n,e){c.flash(n,e)})},ws.onerror=ws.onopen,ws.onclose=function(){},ws.onopen=call(n,o,t,a)):call(n,o,t,a)},view:function(n,o,t,a){if(t="undefined"!=typeof t?t:function(){},rendered=this.render("views/"+n+".ms",o),a="undefined"!=typeof a?a:!0,loaded=!0,$("div.loader").remove(),$("#modal").modal("hide"),a){var s=function(){rendered.swap(function(){$(".slide, .btn-breadcrumb a:not(:last-child)").on("click",function(){$(this).addClass("active"),$(this).hasClass("back")||$(this).parent(".btn-breadcrumb").length?e.set("slide","back"):e.set("slide","to")}),t(),$("html, body").scrollTop(0),setTimeout(function(){$("body").resize()},210)})};blockSize=$("#slider").width(),"back"==e.get("slide")?(e.clear("slide"),$("#slideBack").css("display","none"),$("#slider-container").removeClass("move").css("margin-left","-"+blockSize+"px"),$("#slideTo").show().html($("#main").html()),s(),$("#slider-container").addClass("move").css("margin-left","0px")):"to"==e.get("slide")?(e.clear("slide"),$("#slideTo").css("display","none"),$("#slider-container").removeClass("move").css("margin-left","-"+blockSize+"px"),$("#slider-container").removeClass("move").css("margin-left","0px"),$("#slideBack").show().html($("#main").html()),s(),$("#slider-container").addClass("move").css("margin-left","-"+blockSize+"px")):s()}else rendered.swap(function(){t(),$("html, body").scrollTop(0),$("body").resize()})},confirm:function(n,e,o,t){return o="undefined"!=typeof o?o:function(){},t="undefined"!=typeof t?t:function(){},box=$("#modal"),"string"==typeof n&&n.length?$(".title",box).html(n):box.addClass("no-title"),$(".content",box).html(e),$("footer button",box).click(function(n){n.preventDefault(),$("#modal footer button").unbind("click"),box.removeClass("no-title").modal("hide"),"confirm"==$(this).data("action")?o():t()}),box.modal("show")},arraySortById:function(n){n.sort(function(n,e){return n.id>e.id?1:n.id"+y18n.t("manage_domains")+""),("admin"==s.name||"user"==data.manifest.arguments.install[a].type)&&(data.manifest.arguments.install[a].choices=[],$.each(e.users,function(s,e){data.manifest.arguments.install[a].choices.push({value:s,label:e.fullname+" ("+e.mail+")",selected:!1})}),data.manifest.arguments.install[a].help=""+y18n.t("manage_users")+""),"app"==data.manifest.arguments.install[a].type&&(data.manifest.arguments.install[a].choices=[],$.each(e.apps,function(s,e){data.manifest.arguments.install[a].choices.push({value:e.id,label:e.name,selected:!1})}),data.manifest.arguments.install[a].help=""+y18n.t("manage_apps")+""),("password"==s.name||"password"==data.manifest.arguments.install[a].type)&&(data.manifest.arguments.install[a].inputType="password"),data.manifest.arguments.install[a].label="undefined"!=typeof data.manifest.arguments.install[a].ask[y18n.locale]?data.manifest.arguments.install[a].ask[y18n.locale]:data.manifest.arguments.install[a].ask.en}),data.description="undefined"!=typeof data.manifest.description[y18n.locale]?data.manifest.description[y18n.locale]:data.manifest.description.en,data.manifest.multi_instance="true"==data.manifest.multi_instance?y18n.t("yes"):y18n.t("no"),c.view("app/app_install",data)}),a.get("#/apps/install/:app",function(a){a.api("/apps?raw",function(s){a.appInstallForm(a.params.app,s[a.params.app].manifest,a.params)})}),a.post("#/apps",function(a){"/"!==a.params.path||confirm(y18n.t("confirm_install_domain_root",[a.params.domain]))?(params={label:a.params.label,app:a.params.app},delete a.params.label,delete a.params.app,params.args=a.serialize(a.params.toHash()),""===params.args&&delete params.args,a.api("/apps",function(){a.redirect("#/apps")},"POST",params)):(a.flash("warning",y18n.t("app_install_cancel")),s.clear("slide"),a.redirect("#/apps/install"))}),a.post("#/apps/install/custom",function(a){params={label:a.params.label,app:a.params.url},delete a.params.label,delete a.params.url,a.confirm(y18n.t("applications"),y18n.t("confirm_install_custom_app"),function(){params.app=params.app.replace(/\/?$/,"/"),jQuery.ajax({url:params.app.replace("github.com","raw.githubusercontent.com")+"master/manifest.json",type:"GET"}).done(function(s){s=jQuery.parseJSON(s)||{},a.appInstallForm(params.app,s,a.params)}).fail(function(e){a.flash("fail",y18n.t("app_install_custom_no_manifest")),s.clear("slide"),a.redirect("#/apps/install")})},function(){a.flash("warning",y18n.t("app_install_cancel")),s.clear("slide"),a.redirect("#/apps/install")})}),a.get("#/apps/:app/uninstall",function(a){a.confirm(y18n.t("applications"),y18n.t("confirm_uninstall",[a.params.app]),function(){a.api("/apps/"+a.params.app,function(){a.redirect("#/apps")},"DELETE")},function(){s.clear("slide"),a.redirect("#/apps/"+a.params.app)})}),a.get("#/apps/:app/access",function(a){a.api("/apps/"+a.params.app+"?raw",function(s){a.api("/users",function(e){"undefined"!=typeof s.settings.allowed_users?0===s.settings.allowed_users.length?s.settings.allowed_users=[]:s.settings.allowed_users=s.settings.allowed_users.split(","):(s.settings.allowed_users=[],s.settings.allow_everyone=!0),s.users=[],$.each(e.users,function(a,e){-1===s.settings.allowed_users.indexOf(a)?s.users.push({value:a,label:e.fullname+" ("+e.mail+")"}):s.settings.allowed_users[s.settings.allowed_users.indexOf(a)]={username:a,fullname:e.fullname,mail:e.mail}}),a.view("app/app_access",s)})})}),a.get("#/apps/:app/access/remove",function(a){a.confirm(y18n.t("applications"),y18n.t("confirm_access_remove_all",[a.params.app]),function(){params={apps:a.params.app,users:[]},a.api("/access?"+a.serialize(params),function(e){s.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")},"DELETE",params)},function(){s.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")})}),a.get("#/apps/:app/access/remove/:user",function(a){a.confirm(y18n.t("applications"),y18n.t("confirm_access_remove_user",[a.params.app,a.params.user]),function(){params={apps:a.params.app,users:a.params.user},a.api("/access?"+a.serialize(params),function(e){s.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")},"DELETE",params)},function(){s.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")})}),a.get("#/apps/:app/access/add",function(a){a.confirm(y18n.t("applications"),y18n.t("confirm_access_add",[a.params.app]),function(){params={apps:a.params.app,users:null},a.api("/access",function(){s.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")},"PUT",params)},function(){s.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")})}),a.post("#/apps/:app/access/add",function(a){params={users:a.params.user,apps:a.params.app},a.api("/access",function(){s.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")},"PUT",params)}),a.get("#/apps/:app/access/clear",function(a){a.confirm(y18n.t("applications"),y18n.t("confirm_access_clear",[a.params.app]),function(){params={apps:a.params.app},a.api("/access",function(){s.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")},"POST",params)},function(){s.clear("slide"),a.redirect("#/apps/"+a.params.app+"/access")})}),a.get("#/apps/:app/default",function(a){a.confirm(y18n.t("applications"),y18n.t("confirm_app_default"),function(){a.api("/apps/"+a.params.app+"/default",function(){s.clear("slide"),a.redirect("#/apps/"+a.params.app)},"PUT")},function(){s.clear("slide"),a.redirect("#/apps/"+a.params.app)})})}(); +!function(){var a=Sammy.apps["#main"],e=a.store;a.get("#/backup",function(a){var e=[],r={id:"local",name:y18n.t("local_archives"),uri:"/home/yunohost.backup/"};e.push(r),a.view("backup/backup",{storages:e})}),a.get("#/storages/create",function(a){a.view("backup/storage_create",{})}),a.post("#/storages",function(a){e.clear("slide"),a.redirect("#/storages")}),a.get("#/backup/:storage/create",function(a){var e=[];e.storage={id:a.params.storage,name:y18n.t("local_archives")},a.api("/hooks/backup",function(r){e.hooks=a.groupHooks(r.hooks),e.apps={},a.api("/apps?raw",function(r){$.each(r,function(a,r){r.installed&&(e.apps[a]={id:a,name:r.manifest.name})}),a.view("backup/backup_create",e)})})}),a.post("#/backup/:storage",function(a){var r=a.ungroupHooks(a.params.hooks,a.params.apps);a.api("/backup",function(){e.clear("slide"),a.redirect("#/backup/"+a.params.storage)},"POST",r)}),a.post("#/backup/:storage/:archive/restore",function(a){a.confirm(y18n.t("backup"),y18n.t("confirm_restore",[a.params.archive]),$.proxy(function(a){var r=a.ungroupHooks(a.params.hooks,a.params.apps);r.force="",a.api("/backup/restore/"+a.params.archive,function(r){e.clear("slide"),a.redirect("#/backup/"+a.params.storage+"/"+a.params.archive)},"POST",r)},this,a),function(){e.clear("slide"),a.redirect("#/backup/"+a.params.storage+"/"+a.params.archive)})}),a.get("#/backup/:storage/:archive/delete",function(a){a.confirm(y18n.t("backup"),y18n.t("confirm_delete",[a.params.archive]),function(){a.api("/backup/archives/"+a.params.archive,function(e){a.redirect("#/backup/"+a.params.storage)},"DELETE")},function(){e.clear("slide"),a.redirect("#/backup/"+a.params.storage+"/"+a.params.archive)})}),a.get("#/backup/:storage/:archive/download",function(a){a.api("/backup/"+a.params.archive+"/download",function(e){a.redirect("#/backup/"+a.params.storage+"/"+a.params.archive)},"GET")}),a.get("#/backup/:storage/:archive/copy",function(a){e.clear("slide"),a.redirect("#/backup/"+a.params.storage+"/"+a.params.archive)}),a.get("#/backup/:storage/:archive/upload",function(a){e.clear("slide"),a.redirect("#/backup/"+a.params.storage+"/"+a.params.archive)}),a.get("#/backup/:storage/:archive",function(a){a.api("/backup/archives/"+a.params.archive+"?with_details",function(e){e.storage={id:a.params.storage,name:y18n.t("local_archives")},e.other_storages=[],e.name=a.params.archive,e.hooks=a.groupHooks(Object.keys(e.hooks)),e.items=e.hooks!={}||e.apps!=[],a.view("backup/backup_info",e)})}),a.get("#/backup/:storage",function(a){a.api("/backup/archives?with_info",function(e){e.storage={id:"local",name:y18n.t("local_archives")},e.archives2=[],$.each(e.archives,function(a,r){r.name=a,e.archives2.unshift(r)}),e.archives=e.archives2,a.view("backup/backup_list",e)})})}(); +!function(){var n=Sammy.apps["#main"],a=n.store;n.get("#/domains",function(n){n.api("/domains",function(a){n.api("/domains/main",function(i){domains=[],$.each(a.domains,function(n,a){domains.push({url:a,main:a==i.current_main_domain?!0:!1})}),main_domain_form=domains.length>1?!0:!1,domains.sort(function(n,a){return-2*n.main+1}),n.view("domain/domain_list",{domains:domains,main_domain_form:main_domain_form})},"PUT")})}),n.get("#/domains/add",function(n){$.get("https://dyndns.yunohost.org/domains",function(){}).done(function(a){n.params.ddomains=a.map(function(n){return"."+n})}).fail(function(){n.params.ddomains=[".nohost.me",".noho.st"]}).always(function(){data={ddomains:n.params.ddomains,domains:n.params.domains,allowDyndnsDomain:!0};var a=data.ddomains.join("|");$.each(data.domains,function(n,i){i.search(a)>0&&(data.allowDyndnsDomain=!1)}),n.view("domain/domain_add",data)})}),n.post("#/domains/add",function(n){""===n.params.domain?(""===n.params.ddomain&&(n.flash("fail",y18n.t("error_select_domain")),a.clear("slide"),n.redirect("#/domains/add")),params={domain:n.params.ddomain+n.params["ddomain-ext"]},endurl="dyndns"):(params={domain:n.params.domain},endurl=""),n.api("/domains?"+endurl,function(a){n.redirect("#/domains")},"POST",params)}),n.get("#/domains/:domain/delete",function(n){n.confirm(y18n.t("domains"),y18n.t("confirm_delete",[n.params.domain]),function(){n.api("/domains/"+n.params.domain,function(i){a.clear("slide"),n.redirect("#/domains")},"DELETE")},function(){a.clear("slide"),n.redirect("#/domains")})}),n.post("#/domains",function(n){""===n.params.domain?(n.flash("fail",y18n.t("error_select_domain")),a.clear("slide"),n.redirect("#/domains")):n.confirm(y18n.t("domains"),y18n.t("confirm_change_maindomain"),function(){params={new_domain:n.params.domain},n.api("/domains/main",function(i){a.clear("slide"),n.redirect("#/domains")},"PUT",params),refreshDomain=window.setTimeout(function(){a.clear("slide"),n.redirect("#/domains")},15e3)},function(){a.clear("slide"),n.redirect("#/domains")})})}(); +!function(){var o=Sammy.apps["#main"],a=o.store;o.get("#/tools/firewall",function(o){o.api("/firewall?raw",function(a){var t={ports:{},upnp:!1};$.each(["ipv4","ipv6","uPnP"],function(o,r){$.each(["TCP","UDP"],function(o,n){t.ports[n]=t.ports[n]||{},$.each(a[r][n],function(o,a){t.ports[n][a]=t.ports[n][a]||{},t.ports[n][a][r]=!0})})}),t.upnp=a.uPnP.enabled,o.view("tools/tools_firewall",t)})}),o.get("#/tools/firewall/upnp/:action",function(o){o.confirm(y18n.t("firewall"),y18n.t("confirm_upnp_action",[y18n.t(o.params.action)]),function(){params={action:o.params.action},o.api("/firewall/upnp",function(t){a.clear("slide"),o.redirect("#/tools/firewall")},"GET",params)},function(){a.clear("slide"),o.redirect("#/tools/firewall")})}),o.helper("togglePort",function(o,t,r,n){var l=null,i=[],e=this;switch((o!=parseInt(o)||0>o||o>65535)&&(e.flash("fail",y18n.t("unknown_argument",[o])),a.clear("slide"),e.redirect("#/tools/firewall")),r){case"ipv4":i="ipv4_only";break;case"ipv6":i="ipv6_only"}switch(t){case"udp":t="UDP";break;case"both":t="Both";break;default:t="TCP"}switch(n){case"open":l="POST";break;case"close":l="DELETE";break;default:e.flash("fail",y18n.t("unknown_action",[n])),a.clear("slide"),e.redirect("#/tools/firewall")}if(null!==l&&null!==t&&null!==o){var c={port:o,protocol:t};e.api("/firewall/port?"+i,function(o){a.clear("slide"),e.redirect("#/tools/firewall")},l,c)}else a.clear("slide"),e.redirect("#/tools/firewall")}),o.get("#/tools/firewall/port/:port/:protocol/:connection/:action",function(o){o.confirm(y18n.t("firewall"),y18n.t("confirm_firewall",[y18n.t(o.params.action),o.params.port,y18n.t(o.params.protocol),y18n.t(o.params.connection)]),function(){o.togglePort(o.params.port,o.params.protocol,o.params.connection,o.params.action)},function(){a.clear("slide"),o.redirect("#/tools/firewall")})}),o.post("#/tools/firewall/port",function(o){o.confirm(y18n.t("firewall"),y18n.t("confirm_firewall",[y18n.t(o.params.action),o.params.port,y18n.t(o.params.protocol),y18n.t(o.params.connection)]),function(){o.togglePort(o.params.port,o.params.protocol,o.params.connection,o.params.action)},function(){a.clear("slide"),o.redirect("#/tools/firewall")})})}(); +!function(){var e=Sammy.apps["#main"],t=e.store;e.get("#/",function(e){e.api("/users",function(t){0===t.users.length&&e.flash("warning",y18n.t("warning_first_user"));var n="https://yunohost.org/security.rss",a="https://forum.yunohost.org";$.ajax({url:n,dataType:"xml"}).done(function(t){$.cookie.json=!0;var n=$.cookie("ynhSecurityViewedItems")||[];$("item",t).each(function(t,i){var o=$("link",i)[0].innerHTML;"string"==typeof o&&""!==o&&"/"==o.charAt(0)&&(o=a+o);var r=$("description",i)[0].textContent;r=r.replace('href="/','href="'+a+"/");var s={guid:$("guid",i)[0].innerHTML,title:$("title",i)[0].innerHTML,url:o,desc:r,date:$("pubDate",i)[0].innerHTML.split(" +")[0]};if(-1===n.indexOf(s.guid)){var l=s.title+' ('+y18n.t("read_more")+")";e.flash("warning",l),n.push(s.guid)}}),$.cookie("ynhSecurityViewedItems",n,{expire:7})}).fail(function(){e.flash("fail",y18n.t("error_retrieve_feed",[n]))}),e.view("home")})}),e.get("#/login",function(n){$("#masthead").show(),$("#masthead .logout-btn").hide(),t.set("path-1","#/login"),0===$("div.loader").length&&setInterval(function(){e.loaded||0!==$("div.loader").length||$("#main").append('
')},500),n.checkInstall(function(t){t?(domain=window.location.hostname,$("div.loader").remove(),n.view("login",{domain:domain})):"undefined"==typeof t?e.isInstalledTry>0?(e.isInstalledTry--,e.loaded=!1,setTimeout(function(){n.redirect("#/")},5e3)):(e.isInstalledTry=3,$(document).ajaxError(function(e,t,a){200!==t.status&&n.flash("fail",y18n.t("api_not_responding",[t.status+" "+t.statusText])),$(document).off("ajaxError")}),e.loaded=!0,$("div.loader").remove()):($("div.loader").remove(),n.redirect("#/postinstall"))})}),e.post("#/login",function(e){t.set("url",e.params.domain+"/yunohost/api"),params={password:e.params.password},e.api("/login",function(n){t.set("connected",!0),$("#masthead .logout-btn").fadeIn(),e.flash("success",y18n.t("logged_in")),t.get("path")?e.redirect(t.get("path")):e.redirect("#/")},"POST",params,!1)}),e.get("#/logout",function(e){e.api("/logout",function(n){t.clear("url"),t.clear("connected"),t.set("path","#/"),e.flash("success",y18n.t("logged_out")),e.redirect("#/login")},"GET",{},!1)})}(); +!function(){var o=Sammy.apps["#main"];o.store;o.get("#/tools/monitor",function(o){monitorData={},o.api("/services/glances",function(t){monitorData.status=!0,"running"==t.status?o.api("/monitor/system",function(t){monitorData.system=t,o.api("/monitor/disk",function(t){monitorData.disk=t,o.api("/monitor/network",function(t){monitorData.network=t,delete monitorData.network.usage.lo,o.api("/version",function(t){monitorData.versions=t,o.view("tools/tools_monitoring",monitorData)})})})}):(monitorData.status=!1,o.view("tools/tools_monitoring",monitorData))},"GET")})}(); +!function(){var a=Sammy.apps["#main"],n=a.store;a.get("#/postinstall",function(a){$("#masthead").hide(),a.checkInstall(function(n){n||"undefined"==typeof n?a.redirect("#/login"):a.view("postinstall/postinstall_1")})}),a.get("#/postinstall/domain",function(a){$("#masthead").hide(),$.get("https://dyndns.yunohost.org/domains",function(){}).done(function(n){a.params.ddomains=n.map(function(a){return"."+a})}).fail(function(){a.params.ddomains=[".nohost.me",".noho.st"]}).always(function(){a.view("postinstall/postinstall_2",a.params,function(){$("#domain, #ddomain").keyup(function(a){13==a.keyCode&&$("a.savedomain").click()}),$("a.savedomain").on("click",function(o){""===$("#domain").val()?""===$("#ddomain").val()?(o.preventDefault(),n.clear("slide"),a.flash("fail",y18n.t("error_select_domain"))):domain=$("#ddomain").val()+$('select[name="ddomain-ext"]').val():domain=$("#domain").val(),n.set("maindomain",domain)})})})}),a.get("#/postinstall/password",function(a){$("#masthead").hide(),n.get("maindomain")?a.view("postinstall/postinstall_3",{domain:n.get("maindomain").toLowerCase()}):(n.clear("slide"),a.redirect("#/postinstall/domain"))}),a.post("#/postinstall",function(a){""===a.params.password||""===a.params.confirmation?a.flash("fail",y18n.t("password_empty")):a.params.password==a.params.confirmation?(""===a.params.domain?(a.flash("fail",y18n.t("error_select_domain")),n.clear("slide"),a.redirect("#/postinstall/domain")):params={domain:a.params.domain.toLowerCase()},a.confirm(y18n.t("postinstall"),y18n.t("confirm_postinstall",[a.params.domain]),function(){params.password=a.params.password,n.set("url",window.location.hostname+"/yunohost/api"),n.set("user","admin"),a.api("/postinstall",function(n){a.redirect("#/login")},"POST",params)},function(){})):a.flash("fail",y18n.t("passwords_dont_match"))})}(); +!function(){var e=Sammy.apps["#main"],a=e.store;e.get("#/services",function(e){e.api("/services",function(a){data2={services:[]},$.each(a,function(e,a){a.name=e,a.is_loaded="enabled"==a.loaded?!0:!1,a.is_running="running"==a.status?!0:!1,a.status=y18n.t(a.status),a.loaded=y18n.t(a.loaded),data2.services.push(a)}),e.view("service/service_list",data2)})}),e.get("#/services/:service",function(e){e.api("/services/"+e.params.service,function(s){data2={service:s},data2.service.name=e.params.service,data2.service.is_loaded="enabled"==s.loaded?!0:!1,data2.service.is_running="running"==s.status?!0:!1,data2.service.status=y18n.t(s.status),data2.service.loaded=y18n.t(s.loaded),a.clear("slide"),e.view("service/service_info",data2)},"GET")}),e.get("#/services/:service/log",function(e){params={number:50},e.api("/services/"+e.params.service+"/log",function(a){data2={logs:[],name:e.params.service},$.each(a,function(e,a){data2.logs.push({filename:e,filecontent:a.join("\n")})}),e.view("service/service_log",data2)},"GET",params)}),e.get("#/services/:service/:action",function(e){e.confirm("Service",y18n.t("confirm_service_action",[y18n.t(e.params.action),e.params.service]),function(){var s=null,i=e.params.service;switch(e.params.action){case"start":s="PUT";break;case"stop":s="DELETE";break;case"enable":s="PUT",i+="/enable";break;case"disable":s="DELETE",i+="/enable";break;default:e.flash("fail",y18n.t("unknown_action",[e.params.action])),a.clear("slide"),e.redirect("#/services/"+e.params.service)}s&&i?e.api("/services/"+i,function(s){a.clear("slide"),e.redirect("#/services/"+e.params.service)},s):(a.clear("slide"),e.redirect("#/services/"+e.params.service))},function(){a.clear("slide"),e.redirect("#/services/"+e.params.service)})})}(); +!function(){var e=Sammy.apps["#main"],a=e.store;e.get("#/tools",function(e){e.view("tools/tools_list")}),e.get("#/tools/adminpw",function(e){e.view("tools/tools_adminpw")}),e.put("#/tools/adminpw",function(e){params={},$.each(e.params.toHash(),function(e,a){""!==a&&(params[e]=a)}),$.isEmptyObject(params)?(e.flash("fail",y18n.t("error_modify_something")),a.clear("slide"),e.redirect("#/tools/adminpw")):params.new_password!==params.confirm_new_password?(e.flash("fail",y18n.t("passwords_dont_match")),a.clear("slide"),e.redirect("#/tools/adminpw")):e.api("/login",function(a){delete params.old_password,delete params.confirm_new_password,e.api("/adminpw",function(a){e.redirect("#/logout")},"PUT",params)},"POST",{password:params.old_password},!1)}),e.get("#/update",function(e){e.api("/update",function(a){packagesLength=a.packages.length;for(var t=0;t"),-1!=["moulinette","moulinette-yunohost","yunohost-admin","yunohost-config-nginx","ssowat","python"].indexOf(a.packages[t].name)&&(e.flash("warning",y18n.t("system_delayed_upgrade_warning",[a.packages[t].name])),a.packages[t].delayed=!0);e.view("update/update",a)},"PUT")}),e.get("#/upgrade/:type",function(e){"apps"!==e.params.type&&"packages"!==e.params.type?(e.flash("fail",y18n.t("unknown_argument",[e.params.type])),a.clear("slide"),e.redirect("#/update")):e.confirm(y18n.t("tools"),y18n.t("confirm_update_type",[y18n.t("system_"+e.params.type).toLowerCase()]),function(){endurl="","packages"==e.params.type?endurl="ignore_apps":"apps"==e.params.type&&(endurl="ignore_packages"),e.api("/upgrade?"+endurl,function(a){a.logs=a.log,e.view("upgrade/upgrade",a)},"PUT")},function(){a.clear("slide"),e.redirect("#/update")})}),e.get("#/tools/ca",function(e){e.view("tools/tools_ca")}),e.get("#/tools/security-feed",function(e){data={items:[]};var a="https://forum.yunohost.org",t="https://forum.yunohost.org/c/security",o="https://yunohost.org/security.rss";data.url={web:t,rss:o},$.ajax({url:o,dataType:"xml"}).done(function(t){$("item",t).each(function(e,t){var o=$("link",t)[0].innerHTML;"string"==typeof o&&""!==o&&"/"==o.charAt(0)&&(o=a+o);var n=$("description",t)[0].textContent;n=n.replace('href="/','href="'+a+"/");var s={guid:$("guid",t)[0].innerHTML,title:$("title",t)[0].innerHTML,url:o,desc:n,date:$("pubDate",t)[0].innerHTML.split(" +")[0]};data.items.push(s)}),e.view("tools/tools_security_feed",data)}).fail(function(){e.flash("fail",y18n.t("error_retrieve_feed",[o]))})}),e.get("#/tools/versions",function(e){e.api("/version",function(a){e.view("tools/tools_versions",{versions:a})})})}(); +!function(){var a=Sammy.apps["#main"],s=a.store;a.get("#/users",function(a){a.api("/users",function(s){a.view("user/user_list",s)})}),a.get("#/users/create",function(a){a.api("/domains",function(s){a.view("user/user_create",s)})}),a.post("#/users",function(a){a.params.password==a.params.confirmation?a.params.password.length<4?(a.flash("fail",y18n.t("password_too_short")),s.clear("slide")):(a.params.mail=a.params.email+a.params.domain,a.api("/users",function(s){a.redirect("#/users")},"POST",a.params.toHash())):(a.flash("fail",y18n.t("passwords_dont_match")),s.clear("slide"))}),a.get("#/users/:user",function(a){a.api("/users/"+a.params.user,function(s){a.view("user/user_info",s)})}),a.get("#/users/:user/edit",function(a){a.api("/users/"+a.params.user,function(s){a.api("/domains",function(r){email=s.mail.split("@"),s.email={username:email[0],domain:email[1]},s.domains=[],$.each(r.domains,function(a,r){s.domains.push({domain:r,selected:r==s.email.domain?!0:!1})}),a.view("user/user_edit",s)})})}),a.put("#/users/:user",function(a){a.api("/users/"+a.params.user,function(r){a.params.mail!==a.params.email+a.params.domain?a.params.mail=a.params.email+a.params.domain:a.params.mail="",a.params.email=a.params.domain="","string"==typeof a.params.mailalias&&(a.params.mailalias=[a.params.mailalias]),"string"==typeof a.params.mailforward&&(a.params.mailforward=[a.params.mailforward]),a.params.add_mailalias=a.arrayDiff(a.params.mailalias,r["mail-aliases"]),a.params.remove_mailalias=a.arrayDiff(r["mail-aliases"],a.params.mailalias),a.params.add_mailforward=a.arrayDiff(a.params.mailforward,r["mail-forward"]),a.params.remove_mailforward=a.arrayDiff(r["mail-forward"],a.params.mailforward),a.params.mailalias=a.params.mailforward="",params={},$.each(a.params.toHash(),function(a,s){s.length>0&&"user"!==a&&(params[a]=s)}),$.isEmptyObject(params)?(a.flash("fail",y18n.t("error_modify_something")),s.clear("slide"),a.redirect("#/users/"+a.params.user+"/edit")):params.password?params.password==params.confirmation?params.password.length<4?(a.flash("fail",y18n.t("password_too_short")),s.clear("slide"),a.redirect("#/users/"+a.params.user+"/edit")):(params.change_password=params.password,a.api("/users/"+a.params.user,function(s){a.redirect("#/users/"+a.params.user)},"PUT",params)):(a.flash("fail",y18n.t("passwords_dont_match")),s.clear("slide"),a.redirect("#/users/"+a.params.user+"/edit")):a.api("/users/"+a.params.user,function(s){a.redirect("#/users/"+a.params.user)},"PUT",params)},"GET")}),a.get("#/users/:user/delete",function(a){a.confirm(y18n.t("users"),y18n.t("confirm_delete",[a.params.user]),function(){a.api("/users/"+a.params.user,function(s){a.redirect("#/users")},"DELETE")},function(){s.clear("slide"),a.redirect("#/users/"+a.params.user)})})}(); \ No newline at end of file diff --git a/src/gulpfile.js b/src/gulpfile.js index 2a872876..e56950d1 100644 --- a/src/gulpfile.js +++ b/src/gulpfile.js @@ -39,8 +39,11 @@ gulp.task('js', function() { 'js/vendor/sammy.json.js', 'js/vendor/sammy.storage.js', 'js/vendor/bootstrap.js', - 'js/y18n.js', - 'js/app.js' + 'js/yunohost/y18n.js', + 'js/yunohost/app.js', + 'js/yunohost/helpers.js', + 'js/yunohost/filters.js', + 'js/yunohost/controllers/*.js', ]) .pipe(uglify()) .pipe(concat('script.min.js')) diff --git a/src/js/app.js b/src/js/app.js deleted file mode 100644 index f6730b0c..00000000 --- a/src/js/app.js +++ /dev/null @@ -1,2124 +0,0 @@ -var app = Sammy('#main', function (sam) { - - - /** - * Application bootstrap - * - */ - sam.bind('run', function () { - - // Fixes for sliding effect - $('#slider-container').width(2*$('#slider').width() +'px'); - $(window).resize(function() { - $('#slideBack').css('display', 'none'); - $('#slideTo').css('display', 'none'); - $('#slider-container').width(2*$('#slider').width() +'px').removeClass('move').css('margin-left', '0px'); - }); - - // Flash messages - var flashMessage = $('#flashMessage'); - $('#toggle-btn', flashMessage).click(function(e) { - flashMessage.toggleClass('open'); - }); - $('#clear-btn', flashMessage).click(function(e) { - flashMessage.removeClass('open').find('.messages').html(''); - $('#slider').removeClass('with-flashMessage'); - }); - - }); - - - /** - * Sammy Configuration - * - */ - // Plugins - sam.use('Handlebars', 'ms'); - - Handlebars.registerHelper('ucwords', function(str) { - return (str + '').replace(/^([a-z\u00E0-\u00FC])|\s+([a-z\u00E0-\u00FC])/g, function ($1) { - return $1.toUpperCase(); - }); - }); - Handlebars.registerHelper('humanSize', function(bytes) { - var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; - if (bytes === 0) return 'n/a'; - var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); - return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[[i]]; - }); - Handlebars.registerHelper('humanTime', function(time) { - return Math.round(time) + 's'; - }); - Handlebars.registerHelper('bitRate', function(bytes, time) { - var sizes = ['b', 'Kb', 'Mb', 'Gb', 'Tb']; - if (time === 0) return 'n/a'; - var bps = bytes / time * 8; - var i = parseInt(Math.floor(Math.log(bps) / Math.log(1024))); - return Math.round(bps / Math.pow(1024, i), 2) + ' ' + sizes[[i]] + '/s'; - }); - - Handlebars.registerHelper('t', function(y18n_key) { - var result = y18n.t(y18n_key, Array.prototype.slice.call(arguments, 1)); - return new Handlebars.SafeString(result); - }); - - - // Look for supported type of storage to use - var storageType; - if (Sammy.Store.isAvailable('session')) { - storageType = 'session'; - } else if (Sammy.Store.isAvailable('cookie')) { - storageType = 'cookie'; - } else { - storageType = 'memory'; - } - - // Initialize storage - var store = new Sammy.Store({name: 'storage', type: storageType}); - var loaded = false; - var isInstalledTry = 3; - - /** - * Helpers - * - */ - sam.helpers({ - - // Serialize an object - serialize : function(obj) { - var str = []; - for(var p in obj) - if (obj.hasOwnProperty(p)) { - str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); - } - return str.join("&"); - }, - - // Flash helper to diplay instant notifications - flash: function (level, message) { - if (!store.get('flash')) { - store.set('flash', true); - } - - // Helper CSS class on main wrapper - $('#slider').addClass('with-flashMessage'); - - // If the line is a bash command - if (level === 'info' && message.charAt(0) === '+') { - level = 'log'; - } - - $('#flashMessage .messages') - .prepend('
'+ - '

'+ message +'

'); - }, - - checkInstall: function(callback) { - domain = window.location.hostname; - $.ajax({ - dataType: "json", - url: 'https://'+ domain +'/yunohost/api/installed', - timeout: 3000 - }) - .success(function(data) { - callback(data.installed); - }) - .fail(function() { - callback(undefined); - }); - }, - - // API call - api: function(uri, callback, method, data, websocket) { - c = this; - - call = function(uri, callback, method, data) { - method = typeof method !== 'undefined' ? method : 'GET'; - data = typeof data !== 'undefined' ? data : {}; - if (window.navigator && window.navigator.language && (typeof data.locale === 'undefined')) { - data.locale = y18n.locale || window.navigator.language.substr(0, 2); - } - - var args = data; - if (uri === '/postinstall') { - var installing = false; - setInterval(function () { - installing = true; - }, 1500); - } - - loaded = false; - if ($('div.loader').length === 0) { - setInterval(function () { - if (!loaded && $('div.loader').length === 0) { - $('#main').append('
'); - } - }, 500); - } - - jQuery.ajax({ - url: 'https://'+ store.get('url') + uri, - type: method, - crossdomain: true, - data: data, - traditional: true, - dataType: 'json' - }) - .always(function(xhr, ts, error) { - }) - .done(function(data) { - data = data || {}; - callback(data); - }) - .fail(function(xhr) { - if (xhr.status == 200) { - // Fail with 200, WTF - callback({}); - } else if (xhr.status == 401) { - if (uri === '/login') { - c.flash('fail', y18n.t('wrong_password')); - } else { - c.flash('fail', y18n.t('unauthorized')); - c.redirect('#/login'); - } - } else if (typeof xhr.responseJSON !== 'undefined') { - c.flash('fail', xhr.responseJSON.error); - } else if (typeof xhr.statusText !== 'undefined' && uri !== '/postinstall') { - c.flash('fail', y18n.t('api_not_responding', [xhr.status+' '+xhr.statusText])); - } else { - if (uri === '/postinstall') { - if (installing) { - interval = window.location.hostname === args.domain ? 20000 : 5000; - checkInstall = setInterval(function () { - c.checkInstall(function(isInstalled) { - if (isInstalled || typeof isInstalled === 'undefined') { - c.flash('success', y18n.t('installation_complete')); - clearInterval(checkInstall); - window.location.href = 'https://'+ window.location.hostname +'/yunohost/admin/'; - } - }); - }, interval); - } else { - c.flash('fail', y18n.t('error_occured')); - } - } else { - c.flash('fail', y18n.t('error_server')); - } - } - if (uri !== '/postinstall') { - store.clear('slide'); - c.view(store.get('path-1'), null, null, false); - } - }); - }; - - websocket = typeof websocket !== 'undefined' ? websocket : true; - if (websocket) { - - // Open a WebSocket connection to retrieve live messages from the moulinette - ws = new WebSocket('wss://'+ store.get('url') +'/messages'); - ws.onmessage = function(evt) { - // console.log(evt.data); - $.each($.parseJSON(evt.data), function(k, v) { - c.flash(k, v); - }); - }; - - // If not connected, WebSocket connection will raise an error, but we do not want to interrupt API request - ws.onerror = ws.onopen; - - ws.onclose = function() {}; - - ws.onopen = call(uri, callback, method, data); - } else { - call(uri, callback, method, data); - } - - }, - - // Render view (cross-browser) - view: function (view, data, callback, enableSlide) { - callback = typeof callback !== 'undefined' ? callback : function() {}; - rendered = this.render('views/'+ view +'.ms', data); - - enableSlide = (typeof enableSlide !== 'undefined') ? enableSlide : true; // Change to false to disable animation - - loaded = true; - $('div.loader').remove(); - $('#modal').modal('hide'); - - if (enableSlide) { - var leSwap = function() { - rendered.swap(function() { - $('.slide, .btn-breadcrumb a:not(:last-child)').on('click', function() { - $(this).addClass('active'); - if ($(this).hasClass('back') || $(this).parent('.btn-breadcrumb').length) { - store.set('slide', 'back'); - } else { - store.set('slide', 'to'); - } - }); - callback(); - // Force scrollTop on page load - $('html, body').scrollTop(0); - - // Resize body after the animation finishes (0.2s css transition) - // https://github.com/YunoHost/yunohost-admin/blob/231aac076a3aa836409b0d33fe02e48975990b7a/src/css/style.less#L92 - setTimeout(function() { - $('body').resize(); - }, 210); - }); - }; - - blockSize = $('#slider').width(); - - // Slide back effect - if (store.get('slide') == 'back') { - store.clear('slide'); - $('#slideBack').css('display', 'none'); - $('#slider-container').removeClass('move').css('margin-left', '-'+ blockSize +'px'); - $('#slideTo').show().html($('#main').html()); - leSwap(); - $('#slider-container').addClass('move').css('margin-left', '0px'); - - // Slide to effect - } else if (store.get('slide') == 'to') { - store.clear('slide'); - $('#slideTo').css('display', 'none'); - $('#slider-container').removeClass('move').css('margin-left', '-'+ blockSize +'px'); - $('#slider-container').removeClass('move').css('margin-left', '0px'); - $('#slideBack').show().html($('#main').html()); - leSwap(); - $('#slider-container').addClass('move').css('margin-left', '-'+ blockSize +'px'); - - } else { - leSwap(); - } - } else { - rendered.swap(function(){ - callback(); - // Force scrollTop on page load - $('html, body').scrollTop(0); - $('body').resize(); - }); - } - }, - - confirm: function(title, content, confirmCallback, cancelCallback) { - // Default callbacks - confirmCallback = typeof confirmCallback !== 'undefined' ? confirmCallback : function() {}; - cancelCallback = typeof cancelCallback !== 'undefined' ? cancelCallback : function() {}; - - // Get modal element - box = $('#modal'); - - // Modal title - if (typeof title === 'string' && title.length) { - $('.title', box).html(title); - } - else { - box.addClass('no-title'); - } - - // Modal content - $('.content', box).html(content); - - // Handle buttons - $('footer button', box) - .click(function(e){ - e.preventDefault(); - - $('#modal footer button').unbind( "click" ); - // Reset & Hide modal - box - .removeClass('no-title') - .modal('hide'); - - // Do corresponding callback - if ($(this).data('action') == 'confirm') { - confirmCallback(); - } - else { - cancelCallback(); - } - }); - - // Show modal - return box.modal('show'); - }, - - arraySortById: function(arr) { - arr.sort(function(a, b){ - if (a.id > b.id) { - return 1; - } - else if (a.id < b.id) { - return -1; - } - return 0; - }); - }, - - arrayDiff: function(arr1, arr2) { - arr1 = arr1 || []; - arr2 = arr2 || []; - return arr1.filter(function (a) { - return ((arr2.indexOf(a) == -1) && (a !== "")); - }); - }, - - groupHooks: function(hooks) { - data={}; - var rules=[ - { - id:'configuration', - isIn:function (hook) { - return hook.indexOf('conf_')==0 - } - } - ]; - - $.each(hooks, function(i, hook) { - var group_id=hook; - $.each(rules, function(i, rule) { - if (rule.isIn(hook)) - { - group_id='adminjs_group_'+rule.id; - return false; - } - }); - if(group_id in data) - data[group_id]={ - name:y18n.t('hook_'+group_id), - value:data[group_id].value+','+hook, - description:data[group_id].description+', '+y18n.t('hook_'+hook) - }; - else - data[group_id]={ - name:y18n.t('hook_'+group_id), - value:hook, - description:(group_id==hook)?y18n.t('hook_'+hook+'_desc'):y18n.t('hook_'+hook) - }; - }); - return data; - }, - - ungroupHooks: function(hooks,apps) { - var data={}; - data['apps']=apps; - data['hooks']=hooks; - if (data['hooks']==undefined) - data['hooks']=[]; - else if (data['hooks'].constructor !== Array) - data['hooks']=[data['hooks']]; - if (data['apps']==undefined) - data['apps']=[]; - else if (data['apps'].constructor !== Array) - data['apps']=[data['apps']]; - if (data['hooks'].length==0) - data['ignore_hooks']=''; - if (data['apps'].length==0) - data['ignore_apps']=''; - - // Some hook value contains multiple hooks separated by commas - var split_hooks=[]; - $.each(data['hooks'], function(i, hook) { - split_hooks = split_hooks.concat(hook.split(',')); - }); - - data['hooks']=split_hooks; - return data; - }, - }); - - - /** - * Filters - * - */ - sam.before(/domains\/add/, function (req){ - // Preload domains list. - req.params.domains = []; - req.api('/domains', function(data) { - req.params.domains = data.domains; - }); - }); - sam.before(/apps\/install\//, function (req){ - // Preload domains list. - req.params.domains = []; - req.api('/domains', function(data) { - req.params.domains = data.domains; - }); - }); - sam.before(/apps\/install\//, function (req){ - // Preload users lists. - req.params.users = []; - req.api('/users', function(data) { - req.params.users = data.users; - }); - }); - - - sam.before({except: {path: ['#/logout', '#/login', '#/postinstall', '#/postinstall/domain', '#/postinstall/password']}}, function (req) { - // Store path for further redirections - store.set('path-1', store.get('path')); - store.set('path', req.path); - - // Redirect to login page if no credentials stored - if (!store.get('connected')) { - req.redirect('#/login'); - return false; - } - }); - - sam.after(function () {}); - - - /** - * Errors - */ - sam.notFound = function(){ - // Redirect to home page on 404. - window.location = '#/'; - }; - - - /** - * Routes - * - * Note: var "c" is Sammy's route context - * @doc http://sammyjs.org/docs/api/#Sammy.EventContext - * - */ - - // Home page - sam.get('#/', function (c) { - c.api('/users', function(data) { - // Warn admin if no users are created. - if (data.users.length === 0) { - c.flash('warning', y18n.t('warning_first_user')); - } - - // Get security feed and display new items - var securityFeed = 'https://yunohost.org/security.rss'; - var forumUrl = 'https://forum.yunohost.org'; - - $.ajax({ - url: securityFeed, - // dataType: (jQuery.browser.msie) ? "text" : "xml", - dataType: "xml" - }) - .done(function(xml){ - // Get viewed security alerts from cookie - $.cookie.json = true; - var viewedItems = $.cookie('ynhSecurityViewedItems') || []; - - // Loop through items - $('item', xml).each(function(k, v) { - var link=$('link', v)[0].innerHTML; - if (typeof link == 'string' && link !== '' && link.charAt(0) == '/') - link=forumUrl+link; - var description=$('description', v)[0].textContent; - description=description.replace('href="/','href="'+forumUrl+'/'); - - var item = { - guid: $('guid', v)[0].innerHTML, - title: $('title', v)[0].innerHTML, - url: link, - desc: description, - date: $('pubDate', v)[0].innerHTML.split(' +')[0], - }; - if (viewedItems.indexOf(item.guid) === -1) { - // Show security message to administrator - // var warning = '

'+ item.title +'

'+ item.desc - var warning = item.title + ' ('+y18n.t('read_more')+')'; - c.flash('warning', warning); - - // Store viewed item - viewedItems.push(item.guid); - } - }); - - // Saved viewed items to cookie - $.cookie('ynhSecurityViewedItems', viewedItems, { - expire: 7 - }); - }) - .fail(function() { - c.flash('fail', y18n.t('error_retrieve_feed', [securityFeed])); - }); - - c.view('home'); - }); - }); - - - - /** - * Login - * - */ - - sam.get('#/login', function (c) { - $('#masthead').show(); - $('#masthead .logout-btn').hide(); - store.set('path-1', '#/login'); - if ($('div.loader').length === 0) { - setInterval(function () { - if (!loaded && $('div.loader').length === 0) { - $('#main').append('
'); - } - }, 500); - } - - c.checkInstall(function(isInstalled) { - if (isInstalled) { - domain = window.location.hostname; - $('div.loader').remove(); - c.view('login', { 'domain': domain }); - } else if (typeof isInstalled === 'undefined') { - if (isInstalledTry > 0) { - isInstalledTry--; - loaded = false; // Show pacman - setTimeout(function() { - c.redirect('#/'); - }, 5000); - } - else { - // Reset count - isInstalledTry = 3; - - // API is not responding after 3 try - $( document ).ajaxError(function( event, request, settings ) { - // Display error if status != 200. - // .ajaxError fire even with status code 200 because json is sometimes not valid. - if (request.status !== 200) c.flash('fail', y18n.t('api_not_responding', [request.status+' '+request.statusText] )); - - // Unbind directly - $(document).off('ajaxError'); - }); - - // Remove pacman - loaded = true; - $('div.loader').remove(); - } - } else { - $('div.loader').remove(); - c.redirect('#/postinstall'); - } - }); - }); - - - /** - * Logout - * - */ - - sam.post('#/login', function (c) { - store.set('url', c.params['domain'] +'/yunohost/api'); - - params = { - 'password': c.params['password'] - }; - c.api('/login', function(data) { - store.set('connected', true); - - $('#masthead .logout-btn').fadeIn(); - c.flash('success', y18n.t('logged_in')); - if (store.get('path')) { - c.redirect(store.get('path')); - } else { - c.redirect('#/'); - } - }, 'POST', params, false); - - }); - - sam.get('#/logout', function (c) { - c.api('/logout', function (data) { - store.clear('url'); - store.clear('connected'); - store.set('path', '#/'); - c.flash('success', y18n.t('logged_out')); - c.redirect('#/login'); - }, 'GET', {}, false); - }); - - - - - /** - * Post installation - * - */ - - // Step 1 : introduction - sam.get('#/postinstall', function(c) { - $('#masthead').hide(); - c.checkInstall(function(isInstalled) { - if (isInstalled || typeof isInstalled === 'undefined') { - c.redirect('#/login'); - } else { - c.view('postinstall/postinstall_1'); - } - }); - }); - - // Step 2 : domain - sam.get('#/postinstall/domain', function(c) { - $('#masthead').hide(); - $.get('https://dyndns.yunohost.org/domains', function() {}) - .done(function(data){ - c.params.ddomains = data.map(function(dom){return '.'+dom;}); - }) - .fail(function() { - c.params.ddomains = ['.nohost.me', '.noho.st']; - }) - .always(function() { - c.view('postinstall/postinstall_2', c.params, function() { - $('#domain, #ddomain').keyup(function(event){ - if(event.keyCode == 13){ - $('a.savedomain').click(); - } - }); - $('a.savedomain').on('click', function(e) { - if ($('#domain').val() === '') { - if ($('#ddomain').val() === '') { - e.preventDefault(); - store.clear('slide'); - c.flash('fail', y18n.t('error_select_domain')); - } else { - domain = $('#ddomain').val() + $('select[name="ddomain-ext"]').val(); - } - } else { - domain = $('#domain').val(); - } - store.set('maindomain', domain); - }); - }); - }); - }); - - // Step 3 : administration passowrd - sam.get('#/postinstall/password', function(c) { - $('#masthead').hide(); - if (!store.get('maindomain')) { - store.clear('slide'); - c.redirect('#/postinstall/domain'); - } else { - c.view('postinstall/postinstall_3', { 'domain': store.get('maindomain').toLowerCase() }); - } - }); - - // Execute post-installation - sam.post('#/postinstall', function (c) { - if (c.params['password'] === '' || c.params['confirmation'] === '') { - c.flash('fail', y18n.t('password_empty')); - } - else if (c.params['password'] == c.params['confirmation']) { - if (c.params['domain'] === '') { - c.flash('fail', y18n.t('error_select_domain')); - store.clear('slide'); - c.redirect('#/postinstall/domain'); - } else { - params = { 'domain': c.params['domain'].toLowerCase() }; - } - - c.confirm( - y18n.t('postinstall'), - y18n.t('confirm_postinstall', [c.params['domain']]), - function(){ - params['password'] = c.params['password']; - - store.set('url', window.location.hostname +'/yunohost/api'); - store.set('user', 'admin'); - c.api('/postinstall', function(data) { // http://api.yunohost.org/#!/tools/tools_postinstall_post_0 - c.redirect('#/login'); - }, 'POST', params); - }, - function(){ - } - ); - } else { - c.flash('fail', y18n.t('passwords_dont_match')); - } - }); - - - /** - * Users - * - */ - - // List existing users - sam.get('#/users', function (c) { - c.api('/users', function(data) { // http://api.yunohost.org/#!/user/user_list_get_3 - c.view('user/user_list', data); - }); - }); - - // Create user form - sam.get('#/users/create', function (c) { - c.api('/domains', function(data) { // http://api.yunohost.org/#!/domain/domain_list_get_2 - c.view('user/user_create', data); - }); - }); - - // Create user (POST) - sam.post('#/users', function (c) { - if (c.params['password'] == c.params['confirmation']) { - if (c.params['password'].length < 4) { - c.flash('fail', y18n.t('password_too_short')); - store.clear('slide'); - } - else { - c.params['mail'] = c.params['email'] + c.params['domain']; - c.api('/users', function(data) { // http://api.yunohost.org/#!/user/user_create_post_2 - c.redirect('#/users'); - }, 'POST', c.params.toHash()); - } - } else { - c.flash('fail', y18n.t('passwords_dont_match')); - store.clear('slide'); - //c.redirect('#/users/create'); - } - }); - - // Show user information - sam.get('#/users/:user', function (c) { - c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_info_get_0 - c.view('user/user_info', data); - }); - }); - - // Edit user form - sam.get('#/users/:user/edit', function (c) { - c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_info_get_0 - c.api('/domains', function(dataDomains) { // http://api.yunohost.org/#!/domain/domain_list_get_2 - - // User email use a fake splitted field - email = data.mail.split('@'); - data.email = { - username : email[0], - domain : email[1] - }; - - // Domains - data.domains = []; - $.each(dataDomains.domains, function(key, value) { - data.domains.push({ - domain: value, - selected: (value == data.email.domain) ? true : false - }); - }); - - c.view('user/user_edit', data); - }); - }); - }); - - // Update user information - sam.put('#/users/:user', function (c) { - // Get full user object - c.api('/users/'+ c.params['user'], function(user) { - - // concat email/domain pseudo field - if (c.params['mail'] !== c.params['email'] + c.params['domain']) { - c.params['mail'] = c.params['email'] + c.params['domain']; - } - else { - c.params['mail'] = ''; - } - // Clear temporary inputs - c.params['email'] = c.params['domain'] = ''; - - - // force array type for mail aliases and redirections - if (typeof c.params['mailalias'] == 'string') {c.params['mailalias'] = [c.params['mailalias']];} - if (typeof c.params['mailforward'] == 'string') {c.params['mailforward'] = [c.params['mailforward']];} - - // Check for added/removed aliases and redirections - c.params['add_mailalias'] = c.arrayDiff(c.params['mailalias'], user['mail-aliases']); - c.params['remove_mailalias'] = c.arrayDiff(user['mail-aliases'], c.params['mailalias']); - c.params['add_mailforward'] = c.arrayDiff(c.params['mailforward'], user['mail-forward']); - c.params['remove_mailforward'] = c.arrayDiff(user['mail-forward'], c.params['mailforward']); - - // Clear temporary inputs - c.params['mailalias'] = c.params['mailforward'] = ''; - - // Remove empty inputs - params = {}; - $.each(c.params.toHash(), function(key, value) { - if (value.length > 0 && key !== 'user') { params[key] = value; } - }); - - if ($.isEmptyObject(params)) { - c.flash('fail', y18n.t('error_modify_something')); - store.clear('slide'); - c.redirect('#/users/'+ c.params['user'] + '/edit'); - } else { - if (params['password']) { - if (params['password'] == params['confirmation']) { - if (params['password'].length < 4) { - c.flash('fail', y18n.t('password_too_short')); - store.clear('slide'); - c.redirect('#/users/'+ c.params['user'] + '/edit'); - } - else { - params['change_password'] = params['password']; - c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_update_put_1 - c.redirect('#/users/'+ c.params['user']); - }, 'PUT', params); - } - } else { - c.flash('fail', y18n.t('passwords_dont_match')); - store.clear('slide'); - c.redirect('#/users/'+ c.params['user'] + '/edit'); - } - } - else { - c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_update_put_1 - c.redirect('#/users/'+ c.params['user']); - }, 'PUT', params); - } - } - }, 'GET'); - }); - - // Remove existing user - sam.get('#/users/:user/delete', function (c) { - c.confirm( - y18n.t('users'), - y18n.t('confirm_delete', [c.params['user']]), - function(){ - c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_delete_delete_4 - c.redirect('#/users'); - }, 'DELETE'); - }, - function(){ - store.clear('slide'); - c.redirect('#/users/'+ c.params['user']); - } - ); - }); - - - /** - * Domains - * - */ - - // List existing domains - sam.get('#/domains', function (c) { - c.api('/domains', function(data) { // http://api.yunohost.org/#!/domain/domain_list_get_2 - c.api('/domains/main', function(data2) { - domains = []; - $.each(data.domains, function(k, domain) { - domains.push({ - url: domain, - main: (domain == data2.current_main_domain) ? true : false - }); - }); - - // Do not show main domain form if we have only 1 domain - main_domain_form = (domains.length > 1) ? true: false; - - // Sort domains with main domain first - domains.sort(function(a, b){ return -2*(a.main) + 1; }); - c.view('domain/domain_list', {domains: domains, main_domain_form: main_domain_form}); - }, 'PUT'); - }); - }); - - // Add domain form - sam.get('#/domains/add', function (c) { - $.get('https://dyndns.yunohost.org/domains', function() {}) - .done(function(data){ - c.params.ddomains = data.map(function(dom){return '.'+dom;}); - }) - .fail(function() { - c.params.ddomains = ['.nohost.me', '.noho.st']; - }) - .always(function() { - data = { - ddomains : c.params.ddomains, - domains : c.params.domains, - allowDyndnsDomain : true - }; - - // Allow only 1 DynDns domain. - var regex = data.ddomains.join('|'); - $.each(data.domains, function(k, domain) { - if ( domain.search(regex) > 0 ) { - data.allowDyndnsDomain = false; - } - }); - - c.view('domain/domain_add', data); - }); - }); - - // Add domain (POST) - sam.post('#/domains/add', function (c) { - if (c.params['domain'] === '') { - if (c.params['ddomain'] === '') { - c.flash('fail', y18n.t('error_select_domain')); - store.clear('slide'); - c.redirect('#/domains/add'); - } - params = {'domain': c.params['ddomain'] + c.params['ddomain-ext']}; - endurl = 'dyndns'; - } else { - params = { 'domain': c.params['domain'] }; - endurl = ''; - } - - c.api('/domains?'+endurl, function(data) { // http://api.yunohost.org/#!/domain/domain_add_post_1 - c.redirect('#/domains'); - }, 'POST', params); - }); - - // Remove existing domain - sam.get('#/domains/:domain/delete', function (c) { - c.confirm( - y18n.t('domains'), - y18n.t('confirm_delete', [c.params['domain']]), - function(){ - c.api('/domains/'+ c.params['domain'], function(data) { // http://api.yunohost.org/#!/domain/domain_remove_delete_3 - store.clear('slide'); - c.redirect('#/domains'); - }, 'DELETE'); - }, - function(){ - store.clear('slide'); - c.redirect('#/domains'); - } - ); - }); - - // Set default domain - sam.post('#/domains', function (c) { - if (c.params['domain'] === '') { - c.flash('fail', y18n.t('error_select_domain')); - store.clear('slide'); - c.redirect('#/domains'); - } else { - c.confirm( - y18n.t('domains'), - y18n.t('confirm_change_maindomain'), - function(){ - params = {'new_domain': c.params['domain']}; - c.api('/domains/main', function(data) { // http://api.yunohost.org/#!/tools/tools_maindomain_put_1 - store.clear('slide'); - c.redirect('#/domains'); - }, 'PUT', params); - - // Wait 15s and refresh the page - refreshDomain = window.setTimeout(function(){ - store.clear('slide'); - c.redirect('#/domains'); - }, 15000); - }, - function(){ - store.clear('slide'); - c.redirect('#/domains'); - } - ); - } - }); - - - /** - * Apps - * - */ - - // List installed apps - sam.get('#/apps', function (c) { - c.api('/apps', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8 - // Keep only installed apps - data2 = { 'apps': [], 'installed_apps': true }; - $.each(data['apps'], function(k, v) { - if (v['installed']) { - // On installed app, override name with current label - v.name = (v.label) ? v.label : v.name; - data2['apps'].push(v); - } - }); - - c.arraySortById(data2.apps); - c.view('app/app_list', data2); - }); - }); - - // List available apps - sam.get('#/apps/install', function (c) { - c.api('/apps', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8 - c.api('/apps?raw', function(dataraw) { // http://api.yunohost.org/#!/app/app_list_get_8 - // Keep only uninstalled apps, or multi-instance apps - data2 = { 'apps': [], 'installed_apps': false }; - $.each(data['apps'], function(k, v) { - if ((!v['installed'] || dataraw[v['id']].manifest.multi_instance == "true") && !v['id'].match(/__[0-9]{1,5}$/)) { - v['install_link']=true; - data2['apps'].push(v); - } - }); - - // Sort app list - c.arraySortById(data2.apps); - c.view('app/app_list', data2); - }); - }); - }); - - // Refresh available apps list - sam.get('#/apps/refresh', function (c) { - c.api('/appslists', function(data) { // http://api.yunohost.org/#!/app/app_fetchlist_put_5 - // c.redirect(store.get('path')); - c.redirect('#/apps/install'); - }, 'PUT'); - }); - - // Get app information - sam.get('#/apps/:app', function (c) { - c.api('/apps/'+c.params['app']+'?raw', function(data) { // http://api.yunohost.org/#!/app/app_info_get_9 - // Presentation - data.settings.allowed_users = (data.settings.allowed_users) ? data.settings.allowed_users.replace(',', ', ')+"." : y18n.t('everyone_has_access'); - - // Multilingual description - data.description = (typeof data.manifest.description[y18n.locale] !== 'undefined') ? - data.manifest.description[y18n.locale] : - data.manifest.description['en'] - ; - - // Multi Instance settings - data.manifest.multi_instance = (data.manifest.multi_instance == 'true') ? y18n.t('yes') : y18n.t('no'); - - // Installation date - var d = new Date(data.settings.install_time * 1000); - data.install_time = d.getDate() + '/' + (d.getMonth()+1) + '/' + d.getFullYear(); - - c.view('app/app_info', data); - }); - }); - - // Get app debug page - sam.get('#/apps/:app/debug', function (c) { - c.api('/apps/'+c.params['app']+'/debug', function(data) { - c.view('app/app_debug', data); - }); - }); - - // Special case for custom app installation. - sam.get('#/apps/install/custom', function (c) { - // If we try to GET /apps/install/custom, it means that installation fail. - // Need to redirect to apps/install to get rid of pacamn and see the log. - c.redirect('#/apps/install'); - }); - - // Helper function that build app installation form - sam.helper('appInstallForm', function(appId, manifest, params) { - data = { - id: appId, - manifest: manifest - }; - - if (typeof data.manifest.arguments.install !== 'undefined') { - $.each(data.manifest.arguments.install, function(k, v) { - - // Default values - data.manifest.arguments.install[k].type = (typeof v.type !== 'undefined') ? v.type : 'string'; - data.manifest.arguments.install[k].inputType = 'text'; - data.manifest.arguments.install[k].required = (typeof v.optional !== 'undefined' && v.optional == "true") ? '' : 'required'; - - // Input with choices becomes select list - if (typeof data.manifest.arguments.install[k].choices !== 'undefined') { - // Update choices values with key and checked data - $.each(data.manifest.arguments.install[k].choices, function(ck, cv){ - data.manifest.arguments.install[k].choices[ck] = { - value: cv, - label: cv, - selected: (cv == data.manifest.arguments.install[k].default) ? true : false, - }; - }); - } - - // Special case for domain input. - // Display a list of available domains - if (v.name == 'domain' || data.manifest.arguments.install[k].type == 'domain') { - data.manifest.arguments.install[k].choices = []; - $.each(params.domains, function(key, domain){ - data.manifest.arguments.install[k].choices.push({ - value: domain, - label: domain, - selected: false - }); - }); - data.manifest.arguments.install[k].help = ""+y18n.t('manage_domains')+""; - } - - // Special case for admin / user input. - // Display a list of available users - if (v.name == 'admin' || data.manifest.arguments.install[k].type == 'user') { - data.manifest.arguments.install[k].choices = []; - $.each(params.users, function(username, user){ - data.manifest.arguments.install[k].choices.push({ - value: username, - label: user.fullname+' ('+user.mail+')', - selected: false - }); - }); - data.manifest.arguments.install[k].help = ""+y18n.t('manage_users')+""; - } - - // 'app' type input display a list of available apps - if (data.manifest.arguments.install[k].type == 'app') { - data.manifest.arguments.install[k].choices = []; - $.each(params.apps, function(key, app){ - data.manifest.arguments.install[k].choices.push({ - value: app.id, - label: app.name, - selected: false - }); - }); - data.manifest.arguments.install[k].help = ""+y18n.t('manage_apps')+""; - } - - // 'password' type input. - if (v.name == 'password' || data.manifest.arguments.install[k].type == 'password') { - // Change html input type - data.manifest.arguments.install[k].inputType = 'password'; - } - - - // Multilingual label - data.manifest.arguments.install[k].label = (typeof data.manifest.arguments.install[k].ask[y18n.locale] !== 'undefined') ? - data.manifest.arguments.install[k].ask[y18n.locale] : - data.manifest.arguments.install[k].ask['en'] - ; - }); - } - - // Multilingual description - data.description = (typeof data.manifest.description[y18n.locale] !== 'undefined') ? - data.manifest.description[y18n.locale] : - data.manifest.description['en'] - ; - - // Multi Instance settings boolean to text - data.manifest.multi_instance = (data.manifest.multi_instance == 'true') ? y18n.t('yes') : y18n.t('no'); - - // View app install form - c.view('app/app_install', data); - return; - }); - - // App installation form - sam.get('#/apps/install/:app', function (c) { - c.api('/apps?raw', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8 - - c.appInstallForm( - c.params['app'], - data[c.params['app']].manifest, - c.params - ); - - }); - }); - - // Install app (POST) - sam.post('#/apps', function(c) { - // Warn admin if app is going to be installed on domain root. - if (c.params['path'] !== '/' || confirm(y18n.t('confirm_install_domain_root', [c.params['domain']]))) { - params = { 'label': c.params['label'], 'app': c.params['app'] }; - delete c.params['label']; - delete c.params['app']; - params['args'] = c.serialize(c.params.toHash()); - // Do not pass empty args. - if (params['args'] === "") { - delete params['args']; - } - - c.api('/apps', function() { // http://api.yunohost.org/#!/app/app_install_post_2 - c.redirect('#/apps'); - }, 'POST', params); - } - else { - c.flash('warning', y18n.t('app_install_cancel')); - store.clear('slide'); - c.redirect('#/apps/install'); - } - }); - - // Install custom app from github - sam.post('#/apps/install/custom', function(c) { - - params = { 'label': c.params['label'], 'app': c.params['url'] }; - delete c.params['label']; - delete c.params['url']; - - c.confirm( - y18n.t('applications'), - y18n.t('confirm_install_custom_app'), - function(){ - - // Force trailing slash - params.app = params.app.replace(/\/?$/, '/'); - - // Get manifest.json to get additional parameters - jQuery.ajax({ - url: params.app.replace('github.com', 'raw.githubusercontent.com') + 'master/manifest.json', - type: 'GET', - }) - .done(function(manifest) { - // raw.githubusercontent.com serve content as plain text - manifest = jQuery.parseJSON(manifest) || {}; - - c.appInstallForm( - params.app, - manifest, - c.params - ); - - }) - .fail(function(xhr) { - c.flash('fail', y18n.t('app_install_custom_no_manifest')); - store.clear('slide'); - c.redirect('#/apps/install'); - }); - }, - function(){ - c.flash('warning', y18n.t('app_install_cancel')); - store.clear('slide'); - c.redirect('#/apps/install'); - } - ); - }); - - // Remove installed app - sam.get('#/apps/:app/uninstall', function (c) { - c.confirm( - y18n.t('applications'), - y18n.t('confirm_uninstall', [c.params['app']]), - function() { - c.api('/apps/'+ c.params['app'], function() { // http://api.yunohost.org/#!/app/app_remove_delete_4 - c.redirect('#/apps'); - }, 'DELETE'); - }, - function() { - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']); - } - ); - }); - - // Manage app access - sam.get('#/apps/:app/access', function (c) { - c.api('/apps/'+c.params['app']+'?raw', function(data) { // http://api.yunohost.org/#!/app/app_info_get_9 - c.api('/users', function(dataUsers) { - - // allowed_users as array - if (typeof data.settings.allowed_users !== 'undefined') { - if (data.settings.allowed_users.length === 0) { - // Force empty array, means no user has access - data.settings.allowed_users = []; - } - else { - data.settings.allowed_users = data.settings.allowed_users.split(','); - } - } else { - data.settings.allowed_users = []; // Force array - // if 'allowed_users' is undefined, everyone has access - // that means that undefined is different from empty array - data.settings.allow_everyone = true; - } - - // Available users - data.users = []; - $.each(dataUsers.users, function(username, user){ - // Do not list allowed_users in select list - if ( data.settings.allowed_users.indexOf(username) === -1 ) { - data.users.push({ - value: username, - label: user.fullname+' ('+user.mail+')' - }); - } else { - // Complete allowed_users data - data.settings.allowed_users[data.settings.allowed_users.indexOf(username)] = { - username: username, - fullname: user.fullname, - mail: user.mail, - }; - } - }); - - c.view('app/app_access', data); - }); - }); - }); - - // Remove all access - sam.get('#/apps/:app/access/remove', function (c) { - c.confirm( - y18n.t('applications'), - y18n.t('confirm_access_remove_all', [c.params['app']]), - function() { - params = {'apps': c.params['app'], 'users':[]}; - c.api('/access?'+c.serialize(params), function(data) { // http://api.yunohost.org/#!/app/app_removeaccess_delete_12 - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']+ '/access'); - }, 'DELETE', params); - }, - function() { - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']+ '/access'); - } - ); - }); - - // Remove access to a specific user - sam.get('#/apps/:app/access/remove/:user', function (c) { - c.confirm( - y18n.t('applications'), - y18n.t('confirm_access_remove_user', [c.params['app'], c.params['user']]), - function() { - params = {'apps': c.params['app'], 'users': c.params['user']}; - c.api('/access?'+c.serialize(params), function(data) { // http://api.yunohost.org/#!/app/app_removeaccess_delete_12 - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']+ '/access'); - }, 'DELETE', params); // passing 'params' here is useless because jQuery doesn't handle ajax datas for DELETE requests. Passing parameters through uri. - }, - function() { - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']+ '/access'); - } - ); - }); - - // Grant all access - sam.get('#/apps/:app/access/add', function (c) { - c.confirm( - y18n.t('applications'), - y18n.t('confirm_access_add', [c.params['app']]), - function() { - params = {'apps': c.params['app'], 'users': null}; - c.api('/access', function() { // http://api.yunohost.org/#!/app/app_addaccess_put_13 - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app'] +'/access'); - }, 'PUT', params); - }, - function() { - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']+ '/access'); - } - ); - }); - - // Grant access for a specific user - sam.post('#/apps/:app/access/add', function (c) { - params = {'users': c.params['user'], 'apps': c.params['app']}; - c.api('/access', function() { // http://api.yunohost.org/#!/app/app_addaccess_put_13 - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app'] +'/access'); - }, 'PUT', params); - }); - - // Clear access (reset) - sam.get('#/apps/:app/access/clear', function (c) { - c.confirm( - y18n.t('applications'), - y18n.t('confirm_access_clear', [c.params['app']]), - function() { - params = {'apps': c.params['app']}; - c.api('/access', function() { // - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app'] +'/access'); - }, 'POST', params); - }, - function() { - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']+ '/access'); - } - ); - }); - - // Make app default - sam.get('#/apps/:app/default', function (c) { - c.confirm( - y18n.t('applications'), - y18n.t('confirm_app_default'), - function() { - c.api('/apps/'+ c.params['app'] +'/default', function() { // - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']); - }, 'PUT'); - }, - function() { - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']); - } - ); - }); - - - /** - * Services - * - */ - - // All services status - sam.get('#/services', function (c) { - c.api('/services', function(data) { // ? - data2 = { 'services': [] }; - $.each(data, function(k, v) { - v.name = k; - // Handlebars want booleans - v.is_loaded = (v.loaded=='enabled') ? true : false; - v.is_running = (v.status=='running') ? true : false; - // Translate status and loaded state - v.status = y18n.t(v.status); - v.loaded = y18n.t(v.loaded); - data2.services.push(v); - }); - c.view('service/service_list', data2); - }); - }); - - // Status & actions for a service - sam.get('#/services/:service', function (c) { - c.api('/services/'+ c.params['service'], function(data) { // ? - data2 = { 'service': data }; - data2.service.name = c.params['service']; - // Handlebars want booleans - data2.service.is_loaded = (data.loaded=='enabled') ? true : false; - data2.service.is_running = (data.status=='running') ? true : false; - // Translate status and loaded state - data2.service.status = y18n.t(data.status); - data2.service.loaded = y18n.t(data.loaded); - store.clear('slide'); - c.view('service/service_info', data2); - }, 'GET'); - }); - - // Service log - sam.get('#/services/:service/log', function (c) { - params = { 'number': 50 }; - c.api('/services/'+ c.params['service'] +'/log', function(data) { // ? - data2 = { 'logs': [], 'name': c.params['service'] }; - $.each(data, function(k, v) { - data2.logs.push({filename: k, filecontent: v.join('\n')}); - }); - - c.view('service/service_log', data2); - }, 'GET', params); - }); - - // Enable/Disable & Start/Stop service - sam.get('#/services/:service/:action', function (c) { - c.confirm( - "Service", - y18n.t('confirm_service_action', [y18n.t(c.params['action']), c.params['service']]), - function(){ - var method = null, endurl = c.params['service']; - - switch (c.params['action']) { - case 'start': - method = 'PUT'; - break; - case 'stop': - method = 'DELETE'; - break; - case 'enable': - method = 'PUT'; - endurl += '/enable'; - break; - case 'disable': - method = 'DELETE'; - endurl += '/enable'; - break; - default: - c.flash('fail', y18n.t('unknown_action', [c.params['action']])); - store.clear('slide'); - c.redirect('#/services/'+ c.params['service']); - } - - if (method && endurl) { - c.api('/services/'+ endurl, function(data) { - store.clear('slide'); - c.redirect('#/services/'+ c.params['service']); - }, method); - } - else { - store.clear('slide'); - c.redirect('#/services/'+ c.params['service']); - } - }, - function(){ - store.clear('slide'); - c.redirect('#/services/'+ c.params['service']); - } - ); - }); - - - /** - * Firewall - * - */ - - // Firewall status - sam.get('#/tools/firewall', function (c) { - c.api('/firewall?raw', function(data) { - var firewall = { - ports : {}, - upnp : false - }; - - // Reorganize ports - $.each(['ipv4', 'ipv6', 'uPnP'], function(i, protocol) { - $.each(['TCP', 'UDP'], function(j, connection) { - firewall.ports[connection] = firewall.ports[connection] || {};  - $.each(data[protocol][connection], function(k, port) { - firewall.ports[connection][port] = firewall.ports[connection][port] || {};  - firewall.ports[connection][port][protocol] = true; - }); - }); - }); - - // Get UPnP status - firewall.upnp = data.uPnP.enabled; - - c.view('tools/tools_firewall', firewall); - }); - }); - - // Enable/Disable UPnP - sam.get('#/tools/firewall/upnp/:action', function (c) { - c.confirm( - y18n.t('firewall'), - y18n.t('confirm_upnp_action', [y18n.t(c.params['action'])]), - function(){ - params = {'action' : c.params['action']}; - c.api('/firewall/upnp', function(data) { - store.clear('slide'); - c.redirect('#/tools/firewall'); - }, 'GET', params); - }, - function(){ - store.clear('slide'); - c.redirect('#/tools/firewall'); - } - ); - }); - - // Toggle port status helper (available in every controller) - sam.helper('togglePort', function(port, protocol, connection, action) { - var method = null, - endurl = [], - c = this - ; - - if (port != parseInt(port) || port < 0 || port > 65535) { - c.flash('fail', y18n.t('unknown_argument', [port])); - store.clear('slide'); - c.redirect('#/tools/firewall'); - } - - switch (connection) { - case 'ipv4': - endurl = 'ipv4_only'; - break; - case 'ipv6': - endurl = 'ipv6_only'; - break; - } - - switch (protocol) { - case 'udp': - protocol = 'UDP'; - break; - case 'both': - protocol = 'Both'; - break; - default: - protocol = 'TCP'; - } - - switch (action) { - case "open": - method = 'POST'; - break; - case "close": - method = 'DELETE'; - break; - default: - c.flash('fail', y18n.t('unknown_action', [action])); - store.clear('slide'); - c.redirect('#/tools/firewall'); - } - - if (method !== null && protocol !== null && port !== null) { - // port: - // protocol: - // - UDP - // - TCP - // - Both - // --ipv4-only: - // --ipv6-only: - // --no-upnp: - var params = { - 'port' : port, - 'protocol' : protocol, - }; - c.api('/firewall/port?'+endurl, function(data) { - store.clear('slide'); - c.redirect('#/tools/firewall'); - }, method, params); - } - else { - store.clear('slide'); - c.redirect('#/tools/firewall'); - } - return; - }); - - // Update port status from direct link - // #/firewall/port/{{@key}}/tcp/ipv4/close - sam.get('#/tools/firewall/port/:port/:protocol/:connection/:action', function (c) { - c.confirm( - y18n.t('firewall'), - y18n.t( 'confirm_firewall', [ y18n.t(c.params['action']), c.params['port'], y18n.t(c.params['protocol']), y18n.t(c.params['connection'])]), - function(){ - c.togglePort( - c.params['port'], - c.params['protocol'], - c.params['connection'], - c.params['action'] - ); - }, - function(){ - store.clear('slide'); - c.redirect('#/tools/firewall'); - } - ); - }); - - // Update port status from form - sam.post('#/tools/firewall/port', function (c) { - c.confirm( - y18n.t('firewall'), - y18n.t('confirm_firewall', [ y18n.t(c.params['action']), c.params['port'], y18n.t(c.params['protocol']), y18n.t(c.params['connection']) ]), - function(){ - c.togglePort( - c.params['port'], - c.params['protocol'], - c.params['connection'], - c.params['action'] - ); - }, - function(){ - store.clear('slide'); - c.redirect('#/tools/firewall'); - } - ); - }); - - - /** - * Monitor - * - */ - - // Server monitoring - sam.get('#/tools/monitor', function (c) { - monitorData = {}; - - // Why this method ? - c.api('/services/glances', function(data) { // ? - monitorData.status = true; - - if (data.status == 'running') { - c.api('/monitor/system', function(data) { - monitorData.system = data; - - c.api('/monitor/disk', function(data) { - monitorData.disk = data; - - c.api('/monitor/network', function(data) { - monitorData.network = data; - - // Remove useless interface - delete monitorData.network.usage.lo; - - // Get YunoHost versions too - c.api('/version', function(versions) { - monitorData.versions = versions; - c.view('tools/tools_monitoring', monitorData); - }); - }); - - }); - }); - } - else { - monitorData.status = false; - c.view('tools/tools_monitoring', monitorData); - } - - }, 'GET'); - }); - - - /** - * Tools - * - */ - - // Tools list - sam.get('#/tools', function (c) { - c.view('tools/tools_list'); - }); - - // Update administration password - sam.get('#/tools/adminpw', function (c) { - c.view('tools/tools_adminpw'); - }); - - // Update administration password (PUT) - sam.put('#/tools/adminpw', function (c) { - params = {}; - $.each(c.params.toHash(), function(key, value) { - if (value !== '') { params[key] = value; } - }); - if ($.isEmptyObject(params)) { - c.flash('fail', y18n.t('error_modify_something')); - store.clear('slide'); - c.redirect('#/tools/adminpw'); - } else if (params['new_password'] !== params['confirm_new_password']) { - c.flash('fail', y18n.t('passwords_dont_match')); - store.clear('slide'); - c.redirect('#/tools/adminpw'); - } else { - c.api('/login', function(data) { - // Remove useless variable - delete params['old_password']; - delete params['confirm_new_password']; - - // Update password and redirect to the home - c.api('/adminpw', function(data) { // http://api.yunohost.org/#!/tools/tools_adminpw_put_3 - c.redirect('#/logout'); - }, 'PUT', params); - }, 'POST', { 'password': params['old_password'] }, false); - } - }); - - // System update & upgrade - sam.get('#/update', function (c) { - c.api('/update', function(data) { - packagesLength = data.packages.length; - for(var i = 0; i < packagesLength; i++) { - data.packages[i].delayed = false; - data.packages[i].changelog = data.packages[i].changelog.replace(/\n/g, '
'); - - // Check for special packages that need delayed upgrade. - if (["moulinette", "moulinette-yunohost", "yunohost-admin", "yunohost-config-nginx", "ssowat", "python"].indexOf(data.packages[i].name) != -1) { - c.flash('warning', y18n.t('system_delayed_upgrade_warning', [data.packages[i].name])); - data.packages[i].delayed = true; - } - } - c.view('update/update', data); - }, 'PUT'); - }); - - // Upgrade apps or packages - sam.get('#/upgrade/:type', function (c) { - if (c.params['type'] !== 'apps' && c.params['type'] !== 'packages') { - c.flash('fail', y18n.t('unknown_argument', [c.params['type']])); - store.clear('slide'); - c.redirect('#/update'); - } - else { - c.confirm( - y18n.t('tools'), - y18n.t('confirm_update_type', [y18n.t('system_'+c.params['type']).toLowerCase()]), - function(){ - endurl = ''; - if (c.params['type'] == 'packages') {endurl = 'ignore_apps';} - else if (c.params['type'] == 'apps') {endurl = 'ignore_packages';} - - c.api('/upgrade?'+endurl, function(data) { - // 'log' is a reserved name, maybe in handlebars - data.logs = data.log; - c.view('upgrade/upgrade', data); - }, 'PUT'); - - }, - function(){ - store.clear('slide'); - c.redirect('#/update'); - } - ); - } - }); - - // Download SSL Certificate Authority - sam.get('#/tools/ca', function (c) { - c.view('tools/tools_ca'); - }); - - // Security feed - sam.get('#/tools/security-feed', function (c) { - data = { - items: [] - }; - - // Get security feed and display items - var forumUrl = 'https://forum.yunohost.org'; - var securityUrl = 'https://forum.yunohost.org/c/security'; - var securityFeed = 'https://yunohost.org/security.rss'; - - data.url = { - web: securityUrl, - rss: securityFeed - }; - - $.ajax({ - url: securityFeed, - // dataType: (jQuery.browser.msie) ? "text" : "xml", - dataType: "xml" - }) - .done(function(xml){ - // Loop through items - $('item', xml).each(function(k, v) { - var link=$('link', v)[0].innerHTML; - if (typeof link == 'string' && link !== '' && link.charAt(0) == '/') - link=forumUrl+link; - var description=$('description', v)[0].textContent; - description=description.replace('href="/','href="'+forumUrl+'/'); - - var item = { - guid: $('guid', v)[0].innerHTML, - title: $('title', v)[0].innerHTML, - url: link, - desc: description, - date: $('pubDate', v)[0].innerHTML.split(' +')[0], - }; - data.items.push(item); - }); - - c.view('tools/tools_security_feed', data); - }) - .fail(function() { - c.flash('fail', y18n.t('error_retrieve_feed', [securityFeed])); - }); - - }); - - // Security feed - sam.get('#/tools/versions', function (c) { - c.api('/version', function(versions) { - c.view('tools/tools_versions', {'versions' : versions}); - }); - }); - - - /** - * Backup - * - */ - - var config_hooks = [ - 'system_ldap', - 'system_ssowat', - 'system_cron', - 'system_ssh', - 'system_xmpp', - 'system_mysql', - 'system_yunohost', - 'system_nginx' - ]; - // Storage list - sam.get('#/backup', function (c) { - var storages = []; - - var item = { - id: 'local', - name: y18n.t('local_archives'), - uri: '/home/yunohost.backup/' - }; - storages.push(item); - - c.view('backup/backup', {'storages':storages}); - }); - - // Storage list - sam.get('#/storages/create', function (c) { - c.view('backup/storage_create', {}); - }); - - // Create a storage - sam.post('#/storages', function (c) { - store.clear('slide'); - c.redirect('#/storages'); - }); - - // Create a backup - sam.get('#/backup/:storage/create', function (c) { - var data=[]; - data['storage']={ - id:c.params['storage'], - name:y18n.t('local_archives') - }; - c.api('/hooks/backup', function(hooks) { - data['hooks']=c.groupHooks(hooks['hooks']); - data['apps']={}; - c.api('/apps?raw', function(apps) { // http://api.yunohost.org/#!/app/app_list_get_8 - $.each(apps, function(app_id, app) { - if (app['installed']) - data['apps'][app_id]={ - id: app_id, - name: app['manifest']['name'] - }; - }); - c.view('backup/backup_create', data); - }); - }); - - }); - - - sam.post('#/backup/:storage', function (c) { - var params = c.ungroupHooks(c.params['hooks'],c.params['apps']); - c.api('/backup', function() { - store.clear('slide'); - c.redirect('#/backup/'+ c.params['storage']); - }, 'POST', params); - }); - - // Restore a backup - sam.post('#/backup/:storage/:archive/restore', function (c) { - c.confirm( - y18n.t('backup'), - y18n.t('confirm_restore', [c.params['archive']]), - $.proxy(function(c){ - var params=c.ungroupHooks(c.params['hooks'],c.params['apps']); - params['force']=''; - c.api('/backup/restore/'+c.params['archive'], function(data) { - store.clear('slide'); - c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); - }, 'POST', params); - },this,c), - function(){ - store.clear('slide'); - c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); - } - ); - - }); - - // Delete a backup - sam.get('#/backup/:storage/:archive/delete', function (c) { - c.confirm( - y18n.t('backup'), - y18n.t('confirm_delete', [c.params['archive']]), - function(){ - c.api('/backup/archives/'+c.params['archive'], function(data) { - c.redirect('#/backup/'+ c.params['storage']); - }, 'DELETE'); - }, - function(){ - store.clear('slide'); - c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); - } - ); - }); - - // Download a backup - sam.get('#/backup/:storage/:archive/download', function (c) { - c.api('/backup/'+c.params['archive']+'/download', function(data) { - c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); - }, 'GET'); - }); - - // Copy a backup - sam.get('#/backup/:storage/:archive/copy', function (c) { - store.clear('slide'); - c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); - }); - - // Upload a backup - sam.get('#/backup/:storage/:archive/upload', function (c) { - store.clear('slide'); - c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); - }); - - - // Get archive info - sam.get('#/backup/:storage/:archive', function (c) { - c.api('/backup/archives/'+c.params['archive']+'?with_details', function(data) { - data['storage']={ - id:c.params['storage'], - name:y18n.t('local_archives') - }; - data['other_storages']=[]; - data['name']=c.params['archive']; - data['hooks']=c.groupHooks(Object.keys(data['hooks'])); - data['items']=(data['hooks']!={} || data['apps']!=[]); - c.view('backup/backup_info', data); - }); - }); - - - // Archive list - sam.get('#/backup/:storage', function (c) { - c.api('/backup/archives?with_info', function(data) { - data['storage']={ - id:'local', - name:y18n.t('local_archives') - }; - data['archives2']=[]; - $.each(data['archives'], function(name, info) { - info['name']=name; - data['archives2'].unshift(info) - }); - data['archives']=data['archives2']; - c.view('backup/backup_list', data); - }); - }); - - -}); - - -/** - * Translations - */ -$.getJSON('locales/en.json', function(data){ - y18n.translations['en'] = data; - y18n.translateInlineHTML(); -}); - -// User defined language -if (window.navigator && window.navigator.language) { - y18n.locale = window.navigator.language.substr(0, 2); - if (y18n.locale !== 'en') { - $.getJSON('locales/'+ y18n.locale +'.json', function(data){ - y18n.translations[y18n.locale] = data; - y18n.translateInlineHTML(); - }); - } -} - - -/** - * Run the app - * - */ -$(document).ready(function () { - - /** - * Application - */ - app.run('#/'); -}); diff --git a/src/js/yunohost/app.js b/src/js/yunohost/app.js new file mode 100644 index 00000000..a3d5ed19 --- /dev/null +++ b/src/js/yunohost/app.js @@ -0,0 +1,124 @@ +(function() { + + var app = Sammy('#main', function (sam) { + + + /** + * Application bootstrap + * + */ + sam.bind('run', function () { + + // Fixes for sliding effect + $('#slider-container').width(2*$('#slider').width() +'px'); + $(window).resize(function() { + $('#slideBack').css('display', 'none'); + $('#slideTo').css('display', 'none'); + $('#slider-container').width(2*$('#slider').width() +'px').removeClass('move').css('margin-left', '0px'); + }); + + // Flash messages + var flashMessage = $('#flashMessage'); + $('#toggle-btn', flashMessage).click(function(e) { + flashMessage.toggleClass('open'); + }); + $('#clear-btn', flashMessage).click(function(e) { + flashMessage.removeClass('open').find('.messages').html(''); + $('#slider').removeClass('with-flashMessage'); + }); + + }); + + + /** + * Sammy Configuration + * + */ + + // Plugins + sam.use('Handlebars', 'ms'); + + Handlebars.registerHelper('ucwords', function(str) { + return (str + '').replace(/^([a-z\u00E0-\u00FC])|\s+([a-z\u00E0-\u00FC])/g, function ($1) { + return $1.toUpperCase(); + }); + }); + Handlebars.registerHelper('humanSize', function(bytes) { + var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + if (bytes === 0) return 'n/a'; + var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); + return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[[i]]; + }); + Handlebars.registerHelper('humanTime', function(time) { + return Math.round(time) + 's'; + }); + Handlebars.registerHelper('bitRate', function(bytes, time) { + var sizes = ['b', 'Kb', 'Mb', 'Gb', 'Tb']; + if (time === 0) return 'n/a'; + var bps = bytes / time * 8; + var i = parseInt(Math.floor(Math.log(bps) / Math.log(1024))); + return Math.round(bps / Math.pow(1024, i), 2) + ' ' + sizes[[i]] + '/s'; + }); + + Handlebars.registerHelper('t', function(y18n_key) { + var result = y18n.t(y18n_key, Array.prototype.slice.call(arguments, 1)); + return new Handlebars.SafeString(result); + }); + + + // Look for supported type of storage to use + var storageType; + if (Sammy.Store.isAvailable('session')) { + storageType = 'session'; + } else if (Sammy.Store.isAvailable('cookie')) { + storageType = 'cookie'; + } else { + storageType = 'memory'; + } + + // Initialize storage + sam.store = new Sammy.Store({name: 'storage', type: storageType}); + sam.loaded = false; + sam.isInstalledTry = 3; + + + /** + * Errors + */ + sam.notFound = function(){ + // Redirect to home page on 404. + window.location = '#/'; + }; + + }); + + + /** + * Translations + */ + $.getJSON('locales/en.json', function(data){ + y18n.translations['en'] = data; + y18n.translateInlineHTML(); + }); + + // User defined language + if (window.navigator && window.navigator.language) { + y18n.locale = window.navigator.language.substr(0, 2); + if (y18n.locale !== 'en') { + $.getJSON('locales/'+ y18n.locale +'.json', function(data){ + y18n.translations[y18n.locale] = data; + y18n.translateInlineHTML(); + }); + } + } + + + /** + * Run the application + */ + $(document).ready(function () { + // Run Sammy.js application + app.run('#/'); + }); + +})(); \ No newline at end of file diff --git a/src/js/yunohost/controllers/apps.js b/src/js/yunohost/controllers/apps.js new file mode 100644 index 00000000..a099211a --- /dev/null +++ b/src/js/yunohost/controllers/apps.js @@ -0,0 +1,434 @@ +(function() { + // Get application context + var app = Sammy.apps['#main']; + var store = app.store; + + /** + * Apps + * + */ + + // List installed apps + app.get('#/apps', function (c) { + c.api('/apps', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8 + // Keep only installed apps + data2 = { 'apps': [], 'installed_apps': true }; + $.each(data['apps'], function(k, v) { + if (v['installed']) data2['apps'].push(v); + }); + + c.arraySortById(data2.apps); + c.view('app/app_list', data2); + }); + }); + + // List available apps + app.get('#/apps/install', function (c) { + c.api('/apps', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8 + c.api('/apps?raw', function(dataraw) { // http://api.yunohost.org/#!/app/app_list_get_8 + // Keep only uninstalled apps, or multi-instance apps + data2 = { 'apps': [], 'installed_apps': false }; + $.each(data['apps'], function(k, v) { + if ((!v['installed'] || dataraw[v['id']].manifest.multi_instance == "true") && !v['id'].match(/__[0-9]{1,5}$/)) { + v['install_link']=true; + data2['apps'].push(v); + } + }); + + // Sort app list + c.arraySortById(data2.apps); + c.view('app/app_list', data2); + }); + }); + }); + + // Refresh available apps list + app.get('#/apps/refresh', function (c) { + c.api('/appslists', function(data) { // http://api.yunohost.org/#!/app/app_fetchlist_put_5 + // c.redirect(store.get('path')); + c.redirect('#/apps/install'); + }, 'PUT'); + }); + + // Get app information + app.get('#/apps/:app', function (c) { + c.api('/apps/'+c.params['app']+'?raw', function(data) { // http://api.yunohost.org/#!/app/app_info_get_9 + // Presentation + data.settings.allowed_users = (data.settings.allowed_users) ? data.settings.allowed_users.replace(',', ', ')+"." : y18n.t('everyone_has_access'); + + // Multilingual description + data.description = (typeof data.manifest.description[y18n.locale] !== 'undefined') ? + data.manifest.description[y18n.locale] : + data.manifest.description['en'] + ; + + // Multi Instance settings + data.manifest.multi_instance = (data.manifest.multi_instance == 'true') ? y18n.t('yes') : y18n.t('no'); + + // Installation date + var d = new Date(data.settings.install_time * 1000); + data.install_time = d.getDate() + '/' + (d.getMonth()+1) + '/' + d.getFullYear(); + + c.view('app/app_info', data); + }); + }); + + // Get app debug page + app.get('#/apps/:app/debug', function (c) { + c.api('/apps/'+c.params['app']+'/debug', function(data) { + c.view('app/app_debug', data); + }); + }); + + // Special case for custom app installation. + app.get('#/apps/install/custom', function (c) { + // If we try to GET /apps/install/custom, it means that installation fail. + // Need to redirect to apps/install to get rid of pacamn and see the log. + c.redirect('#/apps/install'); + }); + + // Helper function that build app installation form + app.helper('appInstallForm', function(appId, manifest, params) { + data = { + id: appId, + manifest: manifest + }; + + if (typeof data.manifest.arguments.install !== 'undefined') { + $.each(data.manifest.arguments.install, function(k, v) { + + // Default values + data.manifest.arguments.install[k].type = (typeof v.type !== 'undefined') ? v.type : 'string'; + data.manifest.arguments.install[k].inputType = 'text'; + data.manifest.arguments.install[k].required = (typeof v.optional !== 'undefined' && v.optional == "true") ? '' : 'required'; + + // Input with choices becomes select list + if (typeof data.manifest.arguments.install[k].choices !== 'undefined') { + // Update choices values with key and checked data + $.each(data.manifest.arguments.install[k].choices, function(ck, cv){ + data.manifest.arguments.install[k].choices[ck] = { + value: cv, + label: cv, + selected: (cv == data.manifest.arguments.install[k].default) ? true : false, + }; + }); + } + + // Special case for domain input. + // Display a list of available domains + if (v.name == 'domain' || data.manifest.arguments.install[k].type == 'domain') { + data.manifest.arguments.install[k].choices = []; + $.each(params.domains, function(key, domain){ + data.manifest.arguments.install[k].choices.push({ + value: domain, + label: domain, + selected: false + }); + }); + data.manifest.arguments.install[k].help = ""+y18n.t('manage_domains')+""; + } + + // Special case for admin / user input. + // Display a list of available users + if (v.name == 'admin' || data.manifest.arguments.install[k].type == 'user') { + data.manifest.arguments.install[k].choices = []; + $.each(params.users, function(username, user){ + data.manifest.arguments.install[k].choices.push({ + value: username, + label: user.fullname+' ('+user.mail+')', + selected: false + }); + }); + data.manifest.arguments.install[k].help = ""+y18n.t('manage_users')+""; + } + + // 'app' type input display a list of available apps + if (data.manifest.arguments.install[k].type == 'app') { + data.manifest.arguments.install[k].choices = []; + $.each(params.apps, function(key, app){ + data.manifest.arguments.install[k].choices.push({ + value: app.id, + label: app.name, + selected: false + }); + }); + data.manifest.arguments.install[k].help = ""+y18n.t('manage_apps')+""; + } + + // 'password' type input. + if (v.name == 'password' || data.manifest.arguments.install[k].type == 'password') { + // Change html input type + data.manifest.arguments.install[k].inputType = 'password'; + } + + + // Multilingual label + data.manifest.arguments.install[k].label = (typeof data.manifest.arguments.install[k].ask[y18n.locale] !== 'undefined') ? + data.manifest.arguments.install[k].ask[y18n.locale] : + data.manifest.arguments.install[k].ask['en'] + ; + }); + } + + // Multilingual description + data.description = (typeof data.manifest.description[y18n.locale] !== 'undefined') ? + data.manifest.description[y18n.locale] : + data.manifest.description['en'] + ; + + // Multi Instance settings boolean to text + data.manifest.multi_instance = (data.manifest.multi_instance == 'true') ? y18n.t('yes') : y18n.t('no'); + + // View app install form + c.view('app/app_install', data); + return; + }); + + // App installation form + app.get('#/apps/install/:app', function (c) { + c.api('/apps?raw', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8 + + c.appInstallForm( + c.params['app'], + data[c.params['app']].manifest, + c.params + ); + + }); + }); + + // Install app (POST) + app.post('#/apps', function(c) { + // Warn admin if app is going to be installed on domain root. + if (c.params['path'] !== '/' || confirm(y18n.t('confirm_install_domain_root', [c.params['domain']]))) { + params = { 'label': c.params['label'], 'app': c.params['app'] }; + delete c.params['label']; + delete c.params['app']; + params['args'] = c.serialize(c.params.toHash()); + // Do not pass empty args. + if (params['args'] === "") { + delete params['args']; + } + + c.api('/apps', function() { // http://api.yunohost.org/#!/app/app_install_post_2 + c.redirect('#/apps'); + }, 'POST', params); + } + else { + c.flash('warning', y18n.t('app_install_cancel')); + store.clear('slide'); + c.redirect('#/apps/install'); + } + }); + + // Install custom app from github + app.post('#/apps/install/custom', function(c) { + + params = { 'label': c.params['label'], 'app': c.params['url'] }; + delete c.params['label']; + delete c.params['url']; + + c.confirm( + y18n.t('applications'), + y18n.t('confirm_install_custom_app'), + function(){ + + // Force trailing slash + params.app = params.app.replace(/\/?$/, '/'); + + // Get manifest.json to get additional parameters + jQuery.ajax({ + url: params.app.replace('github.com', 'raw.githubusercontent.com') + 'master/manifest.json', + type: 'GET', + }) + .done(function(manifest) { + // raw.githubusercontent.com serve content as plain text + manifest = jQuery.parseJSON(manifest) || {}; + + c.appInstallForm( + params.app, + manifest, + c.params + ); + + }) + .fail(function(xhr) { + c.flash('fail', y18n.t('app_install_custom_no_manifest')); + store.clear('slide'); + c.redirect('#/apps/install'); + }); + }, + function(){ + c.flash('warning', y18n.t('app_install_cancel')); + store.clear('slide'); + c.redirect('#/apps/install'); + } + ); + }); + + // Remove installed app + app.get('#/apps/:app/uninstall', function (c) { + c.confirm( + y18n.t('applications'), + y18n.t('confirm_uninstall', [c.params['app']]), + function() { + c.api('/apps/'+ c.params['app'], function() { // http://api.yunohost.org/#!/app/app_remove_delete_4 + c.redirect('#/apps'); + }, 'DELETE'); + }, + function() { + store.clear('slide'); + c.redirect('#/apps/'+ c.params['app']); + } + ); + }); + + // Manage app access + app.get('#/apps/:app/access', function (c) { + c.api('/apps/'+c.params['app']+'?raw', function(data) { // http://api.yunohost.org/#!/app/app_info_get_9 + c.api('/users', function(dataUsers) { + + // allowed_users as array + if (typeof data.settings.allowed_users !== 'undefined') { + if (data.settings.allowed_users.length === 0) { + // Force empty array, means no user has access + data.settings.allowed_users = []; + } + else { + data.settings.allowed_users = data.settings.allowed_users.split(','); + } + } else { + data.settings.allowed_users = []; // Force array + // if 'allowed_users' is undefined, everyone has access + // that means that undefined is different from empty array + data.settings.allow_everyone = true; + } + + // Available users + data.users = []; + $.each(dataUsers.users, function(username, user){ + // Do not list allowed_users in select list + if ( data.settings.allowed_users.indexOf(username) === -1 ) { + data.users.push({ + value: username, + label: user.fullname+' ('+user.mail+')' + }); + } else { + // Complete allowed_users data + data.settings.allowed_users[data.settings.allowed_users.indexOf(username)] = { + username: username, + fullname: user.fullname, + mail: user.mail, + }; + } + }); + + c.view('app/app_access', data); + }); + }); + }); + + // Remove all access + app.get('#/apps/:app/access/remove', function (c) { + c.confirm( + y18n.t('applications'), + y18n.t('confirm_access_remove_all', [c.params['app']]), + function() { + params = {'apps': c.params['app'], 'users':[]}; + c.api('/access?'+c.serialize(params), function(data) { // http://api.yunohost.org/#!/app/app_removeaccess_delete_12 + store.clear('slide'); + c.redirect('#/apps/'+ c.params['app']+ '/access'); + }, 'DELETE', params); + }, + function() { + store.clear('slide'); + c.redirect('#/apps/'+ c.params['app']+ '/access'); + } + ); + }); + + // Remove access to a specific user + app.get('#/apps/:app/access/remove/:user', function (c) { + c.confirm( + y18n.t('applications'), + y18n.t('confirm_access_remove_user', [c.params['app'], c.params['user']]), + function() { + params = {'apps': c.params['app'], 'users': c.params['user']}; + c.api('/access?'+c.serialize(params), function(data) { // http://api.yunohost.org/#!/app/app_removeaccess_delete_12 + store.clear('slide'); + c.redirect('#/apps/'+ c.params['app']+ '/access'); + }, 'DELETE', params); // passing 'params' here is useless because jQuery doesn't handle ajax datas for DELETE requests. Passing parameters through uri. + }, + function() { + store.clear('slide'); + c.redirect('#/apps/'+ c.params['app']+ '/access'); + } + ); + }); + + // Grant all access + app.get('#/apps/:app/access/add', function (c) { + c.confirm( + y18n.t('applications'), + y18n.t('confirm_access_add', [c.params['app']]), + function() { + params = {'apps': c.params['app'], 'users': null}; + c.api('/access', function() { // http://api.yunohost.org/#!/app/app_addaccess_put_13 + store.clear('slide'); + c.redirect('#/apps/'+ c.params['app'] +'/access'); + }, 'PUT', params); + }, + function() { + store.clear('slide'); + c.redirect('#/apps/'+ c.params['app']+ '/access'); + } + ); + }); + + // Grant access for a specific user + app.post('#/apps/:app/access/add', function (c) { + params = {'users': c.params['user'], 'apps': c.params['app']}; + c.api('/access', function() { // http://api.yunohost.org/#!/app/app_addaccess_put_13 + store.clear('slide'); + c.redirect('#/apps/'+ c.params['app'] +'/access'); + }, 'PUT', params); + }); + + // Clear access (reset) + app.get('#/apps/:app/access/clear', function (c) { + c.confirm( + y18n.t('applications'), + y18n.t('confirm_access_clear', [c.params['app']]), + function() { + params = {'apps': c.params['app']}; + c.api('/access', function() { // + store.clear('slide'); + c.redirect('#/apps/'+ c.params['app'] +'/access'); + }, 'POST', params); + }, + function() { + store.clear('slide'); + c.redirect('#/apps/'+ c.params['app']+ '/access'); + } + ); + }); + + // Make app default + app.get('#/apps/:app/default', function (c) { + c.confirm( + y18n.t('applications'), + y18n.t('confirm_app_default'), + function() { + c.api('/apps/'+ c.params['app'] +'/default', function() { // + store.clear('slide'); + c.redirect('#/apps/'+ c.params['app']); + }, 'PUT'); + }, + function() { + store.clear('slide'); + c.redirect('#/apps/'+ c.params['app']); + } + ); + }); + +})(); \ No newline at end of file diff --git a/src/js/yunohost/controllers/backup.js b/src/js/yunohost/controllers/backup.js new file mode 100644 index 00000000..3f4767ed --- /dev/null +++ b/src/js/yunohost/controllers/backup.js @@ -0,0 +1,165 @@ +(function() { + // Get application context + var app = Sammy.apps['#main']; + var store = app.store; + + /** + * Backup + * + */ + + var config_hooks = [ + 'system_ldap', + 'system_ssowat', + 'system_cron', + 'system_ssh', + 'system_xmpp', + 'system_mysql', + 'system_yunohost', + 'system_nginx' + ]; + // Storage list + app.get('#/backup', function (c) { + var storages = []; + var item = { + id: 'local', + name: y18n.t('local_archives'), + uri: '/home/yunohost.backup/' + }; + storages.push(item); + + c.view('backup/backup', {'storages':storages}); + }); + + // Storage list + app.get('#/storages/create', function (c) { + c.view('backup/storage_create', {}); + }); + + // Create a storage + app.post('#/storages', function (c) { + store.clear('slide'); + c.redirect('#/storages'); + }); + + // Create a backup + app.get('#/backup/:storage/create', function (c) { + var data=[]; + data['storage']={ + id:c.params['storage'], + name:y18n.t('local_archives') + }; + c.api('/hooks/backup', function(hooks) { + data['hooks']=c.groupHooks(hooks['hooks']); + data['apps']={}; + c.api('/apps?raw', function(apps) { // http://api.yunohost.org/#!/app/app_list_get_8 + $.each(apps, function(app_id, app) { + if (app['installed']) + data['apps'][app_id]={ + id: app_id, + name: app['manifest']['name'] + }; + }); + c.view('backup/backup_create', data); + }); + }); + }); + + + app.post('#/backup/:storage', function (c) { + var params = c.ungroupHooks(c.params['hooks'],c.params['apps']); + c.api('/backup', function() { + store.clear('slide'); + c.redirect('#/backup/'+ c.params['storage']); + }, 'POST', params); + }); + + // Restore a backup + app.post('#/backup/:storage/:archive/restore', function (c) { + c.confirm( + y18n.t('backup'), + y18n.t('confirm_restore', [c.params['archive']]), + $.proxy(function(c){ + var params=c.ungroupHooks(c.params['hooks'],c.params['apps']); + params['force']=''; + c.api('/backup/restore/'+c.params['archive'], function(data) { + store.clear('slide'); + c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); + }, 'POST', params); + },this,c), + function(){ + store.clear('slide'); + c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); + } + ); + }); + + // Delete a backup + app.get('#/backup/:storage/:archive/delete', function (c) { + c.confirm( + y18n.t('backup'), + y18n.t('confirm_delete', [c.params['archive']]), + function(){ + c.api('/backup/archives/'+c.params['archive'], function(data) { + c.redirect('#/backup/'+ c.params['storage']); + }, 'DELETE'); + }, + function(){ + store.clear('slide'); + c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); + } + ); + }); + + // Download a backup + app.get('#/backup/:storage/:archive/download', function (c) { + c.api('/backup/'+c.params['archive']+'/download', function(data) { + c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); + }, 'GET'); + }); + + // Copy a backup + app.get('#/backup/:storage/:archive/copy', function (c) { + store.clear('slide'); + c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); + }); + + // Upload a backup + app.get('#/backup/:storage/:archive/upload', function (c) { + store.clear('slide'); + c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); + }); + + // Get archive info + app.get('#/backup/:storage/:archive', function (c) { + c.api('/backup/archives/'+c.params['archive']+'?with_details', function(data) { + data['storage']={ + id:c.params['storage'], + name:y18n.t('local_archives') + }; + data['other_storages']=[]; + data['name']=c.params['archive']; + data['hooks']=c.groupHooks(Object.keys(data['hooks'])); + data['items']=(data['hooks']!={} || data['apps']!=[]); + c.view('backup/backup_info', data); + }); + }); + + // Archive list + app.get('#/backup/:storage', function (c) { + c.api('/backup/archives?with_info', function(data) { + data['storage']={ + id:'local', + name:y18n.t('local_archives') + }; + data['archives2']=[]; + $.each(data['archives'], function(name, info) { + info['name']=name; + data['archives2'].unshift(info) + }); + data['archives']=data['archives2']; + c.view('backup/backup_list', data); + }); + }); + +})(); \ No newline at end of file diff --git a/src/js/yunohost/controllers/domains.js b/src/js/yunohost/controllers/domains.js new file mode 100644 index 00000000..d1b7f7ed --- /dev/null +++ b/src/js/yunohost/controllers/domains.js @@ -0,0 +1,130 @@ +(function() { + // Get application context + var app = Sammy.apps['#main']; + var store = app.store; + + /** + * Domains + * + */ + + // List existing domains + app.get('#/domains', function (c) { + c.api('/domains', function(data) { // http://api.yunohost.org/#!/domain/domain_list_get_2 + c.api('/domains/main', function(data2) { + domains = []; + $.each(data.domains, function(k, domain) { + domains.push({ + url: domain, + main: (domain == data2.current_main_domain) ? true : false + }); + }); + + // Do not show main domain form if we have only 1 domain + main_domain_form = (domains.length > 1) ? true: false; + + // Sort domains with main domain first + domains.sort(function(a, b){ return -2*(a.main) + 1; }); + c.view('domain/domain_list', {domains: domains, main_domain_form: main_domain_form}); + }, 'PUT'); + }); + }); + + // Add domain form + app.get('#/domains/add', function (c) { + $.get('https://dyndns.yunohost.org/domains', function() {}) + .done(function(data){ + c.params.ddomains = data.map(function(dom){return '.'+dom;}); + }) + .fail(function() { + c.params.ddomains = ['.nohost.me', '.noho.st']; + }) + .always(function() { + data = { + ddomains : c.params.ddomains, + domains : c.params.domains, + allowDyndnsDomain : true + }; + + // Allow only 1 DynDns domain. + var regex = data.ddomains.join('|'); + $.each(data.domains, function(k, domain) { + if ( domain.search(regex) > 0 ) { + data.allowDyndnsDomain = false; + } + }); + + c.view('domain/domain_add', data); + }); + }); + + // Add domain (POST) + app.post('#/domains/add', function (c) { + if (c.params['domain'] === '') { + if (c.params['ddomain'] === '') { + c.flash('fail', y18n.t('error_select_domain')); + store.clear('slide'); + c.redirect('#/domains/add'); + } + params = {'domain': c.params['ddomain'] + c.params['ddomain-ext']}; + endurl = 'dyndns'; + } else { + params = { 'domain': c.params['domain'] }; + endurl = ''; + } + + c.api('/domains?'+endurl, function(data) { // http://api.yunohost.org/#!/domain/domain_add_post_1 + c.redirect('#/domains'); + }, 'POST', params); + }); + + // Remove existing domain + app.get('#/domains/:domain/delete', function (c) { + c.confirm( + y18n.t('domains'), + y18n.t('confirm_delete', [c.params['domain']]), + function(){ + c.api('/domains/'+ c.params['domain'], function(data) { // http://api.yunohost.org/#!/domain/domain_remove_delete_3 + store.clear('slide'); + c.redirect('#/domains'); + }, 'DELETE'); + }, + function(){ + store.clear('slide'); + c.redirect('#/domains'); + } + ); + }); + + // Set default domain + app.post('#/domains', function (c) { + if (c.params['domain'] === '') { + c.flash('fail', y18n.t('error_select_domain')); + store.clear('slide'); + c.redirect('#/domains'); + } else { + c.confirm( + y18n.t('domains'), + y18n.t('confirm_change_maindomain'), + function(){ + params = {'new_domain': c.params['domain']}; + c.api('/domains/main', function(data) { // http://api.yunohost.org/#!/tools/tools_maindomain_put_1 + store.clear('slide'); + c.redirect('#/domains'); + }, 'PUT', params); + + // Wait 15s and refresh the page + refreshDomain = window.setTimeout(function(){ + store.clear('slide'); + c.redirect('#/domains'); + }, 15000); + }, + function(){ + store.clear('slide'); + c.redirect('#/domains'); + } + ); + } + }); + +})(); \ No newline at end of file diff --git a/src/js/yunohost/controllers/firewall.js b/src/js/yunohost/controllers/firewall.js new file mode 100644 index 00000000..d71c35a7 --- /dev/null +++ b/src/js/yunohost/controllers/firewall.js @@ -0,0 +1,168 @@ +(function() { + // Get application context + var app = Sammy.apps['#main']; + var store = app.store; + + /** + * Firewall + * + */ + + // Firewall status + app.get('#/tools/firewall', function (c) { + c.api('/firewall?raw', function(data) { + var firewall = { + ports : {}, + upnp : false + }; + + // Reorganize ports + $.each(['ipv4', 'ipv6', 'uPnP'], function(i, protocol) { + $.each(['TCP', 'UDP'], function(j, connection) { + firewall.ports[connection] = firewall.ports[connection] || {};  + $.each(data[protocol][connection], function(k, port) { + firewall.ports[connection][port] = firewall.ports[connection][port] || {};  + firewall.ports[connection][port][protocol] = true; + }); + }); + }); + + // Get UPnP status + firewall.upnp = data.uPnP.enabled; + + c.view('tools/tools_firewall', firewall); + }); + }); + + // Enable/Disable UPnP + app.get('#/tools/firewall/upnp/:action', function (c) { + c.confirm( + y18n.t('firewall'), + y18n.t('confirm_upnp_action', [y18n.t(c.params['action'])]), + function(){ + params = {'action' : c.params['action']}; + c.api('/firewall/upnp', function(data) { + store.clear('slide'); + c.redirect('#/tools/firewall'); + }, 'GET', params); + }, + function(){ + store.clear('slide'); + c.redirect('#/tools/firewall'); + } + ); + }); + + // Toggle port status helper (available in every controller) + app.helper('togglePort', function(port, protocol, connection, action) { + var method = null, + endurl = [], + c = this + ; + + if (port != parseInt(port) || port < 0 || port > 65535) { + c.flash('fail', y18n.t('unknown_argument', [port])); + store.clear('slide'); + c.redirect('#/tools/firewall'); + } + + switch (connection) { + case 'ipv4': + endurl = 'ipv4_only'; + break; + case 'ipv6': + endurl = 'ipv6_only'; + break; + } + + switch (protocol) { + case 'udp': + protocol = 'UDP'; + break; + case 'both': + protocol = 'Both'; + break; + default: + protocol = 'TCP'; + } + + switch (action) { + case "open": + method = 'POST'; + break; + case "close": + method = 'DELETE'; + break; + default: + c.flash('fail', y18n.t('unknown_action', [action])); + store.clear('slide'); + c.redirect('#/tools/firewall'); + } + + if (method !== null && protocol !== null && port !== null) { + // port: + // protocol: + // - UDP + // - TCP + // - Both + // --ipv4-only: + // --ipv6-only: + // --no-upnp: + var params = { + 'port' : port, + 'protocol' : protocol, + }; + c.api('/firewall/port?'+endurl, function(data) { + store.clear('slide'); + c.redirect('#/tools/firewall'); + }, method, params); + } + else { + store.clear('slide'); + c.redirect('#/tools/firewall'); + } + return; + }); + + // Update port status from direct link + // #/firewall/port/{{@key}}/tcp/ipv4/close + app.get('#/tools/firewall/port/:port/:protocol/:connection/:action', function (c) { + c.confirm( + y18n.t('firewall'), + y18n.t( 'confirm_firewall', [ y18n.t(c.params['action']), c.params['port'], y18n.t(c.params['protocol']), y18n.t(c.params['connection'])]), + function(){ + c.togglePort( + c.params['port'], + c.params['protocol'], + c.params['connection'], + c.params['action'] + ); + }, + function(){ + store.clear('slide'); + c.redirect('#/tools/firewall'); + } + ); + }); + + // Update port status from form + app.post('#/tools/firewall/port', function (c) { + c.confirm( + y18n.t('firewall'), + y18n.t('confirm_firewall', [ y18n.t(c.params['action']), c.params['port'], y18n.t(c.params['protocol']), y18n.t(c.params['connection']) ]), + function(){ + c.togglePort( + c.params['port'], + c.params['protocol'], + c.params['connection'], + c.params['action'] + ); + }, + function(){ + store.clear('slide'); + c.redirect('#/tools/firewall'); + } + ); + }); + +})(); \ No newline at end of file diff --git a/src/js/yunohost/controllers/home.js b/src/js/yunohost/controllers/home.js new file mode 100644 index 00000000..ac149fbe --- /dev/null +++ b/src/js/yunohost/controllers/home.js @@ -0,0 +1,165 @@ +(function() { + // Get application context + var app = Sammy.apps['#main']; + var store = app.store; + + /** + * Home + * + */ + + // Home page + app.get('#/', function (c) { + c.api('/users', function(data) { + // Warn admin if no users are created. + if (data.users.length === 0) { + c.flash('warning', y18n.t('warning_first_user')); + } + + // Get security feed and display new items + var securityFeed = 'https://yunohost.org/security.rss'; + var forumUrl = 'https://forum.yunohost.org'; + + $.ajax({ + url: securityFeed, + // dataType: (jQuery.browser.msie) ? "text" : "xml", + dataType: "xml" + }) + .done(function(xml){ + // Get viewed security alerts from cookie + $.cookie.json = true; + var viewedItems = $.cookie('ynhSecurityViewedItems') || []; + + // Loop through items + $('item', xml).each(function(k, v) { + var link=$('link', v)[0].innerHTML; + if (typeof link == 'string' && link !== '' && link.charAt(0) == '/') + link=forumUrl+link; + var description=$('description', v)[0].textContent; + description=description.replace('href="/','href="'+forumUrl+'/'); + + var item = { + guid: $('guid', v)[0].innerHTML, + title: $('title', v)[0].innerHTML, + url: link, + desc: description, + date: $('pubDate', v)[0].innerHTML.split(' +')[0], + }; + if (viewedItems.indexOf(item.guid) === -1) { + // Show security message to administrator + // var warning = '

'+ item.title +'

'+ item.desc + var warning = item.title + ' ('+y18n.t('read_more')+')'; + c.flash('warning', warning); + + // Store viewed item + viewedItems.push(item.guid); + } + }); + + // Saved viewed items to cookie + $.cookie('ynhSecurityViewedItems', viewedItems, { + expire: 7 + }); + }) + .fail(function() { + c.flash('fail', y18n.t('error_retrieve_feed', [securityFeed])); + }); + + c.view('home'); + }); + }); + + + + /** + * Login + * + */ + + app.get('#/login', function (c) { + $('#masthead').show(); + $('#masthead .logout-btn').hide(); + store.set('path-1', '#/login'); + if ($('div.loader').length === 0) { + setInterval(function () { + if (!app.loaded && $('div.loader').length === 0) { + $('#main').append('
'); + } + }, 500); + } + + c.checkInstall(function(isInstalled) { + if (isInstalled) { + domain = window.location.hostname; + $('div.loader').remove(); + c.view('login', { 'domain': domain }); + } else if (typeof isInstalled === 'undefined') { + if (app.isInstalledTry > 0) { + app.isInstalledTry--; + app.loaded = false; // Show pacman + setTimeout(function() { + c.redirect('#/'); + }, 5000); + } + else { + // Reset count + app.isInstalledTry = 3; + + // API is not responding after 3 try + $( document ).ajaxError(function( event, request, settings ) { + // Display error if status != 200. + // .ajaxError fire even with status code 200 because json is sometimes not valid. + if (request.status !== 200) c.flash('fail', y18n.t('api_not_responding', [request.status+' '+request.statusText] )); + + // Unbind directly + $(document).off('ajaxError'); + }); + + // Remove pacman + app.loaded = true; + $('div.loader').remove(); + } + } else { + $('div.loader').remove(); + c.redirect('#/postinstall'); + } + }); + }); + + + /** + * Logout + * + */ + + app.post('#/login', function (c) { + store.set('url', c.params['domain'] +'/yunohost/api'); + + params = { + 'password': c.params['password'] + }; + c.api('/login', function(data) { + store.set('connected', true); + + $('#masthead .logout-btn').fadeIn(); + c.flash('success', y18n.t('logged_in')); + if (store.get('path')) { + c.redirect(store.get('path')); + } else { + c.redirect('#/'); + } + }, 'POST', params, false); + + }); + + app.get('#/logout', function (c) { + c.api('/logout', function (data) { + store.clear('url'); + store.clear('connected'); + store.set('path', '#/'); + c.flash('success', y18n.t('logged_out')); + c.redirect('#/login'); + }, 'GET', {}, false); + }); + +})(); \ No newline at end of file diff --git a/src/js/yunohost/controllers/monitor.js b/src/js/yunohost/controllers/monitor.js new file mode 100644 index 00000000..3dbbd72d --- /dev/null +++ b/src/js/yunohost/controllers/monitor.js @@ -0,0 +1,50 @@ +(function() { + // Get application context + var app = Sammy.apps['#main']; + var store = app.store; + + /** + * Monitor + * + */ + + // Server monitoring + app.get('#/tools/monitor', function (c) { + monitorData = {}; + + // Why this method ? + c.api('/services/glances', function(data) { // ? + monitorData.status = true; + + if (data.status == 'running') { + c.api('/monitor/system', function(data) { + monitorData.system = data; + + c.api('/monitor/disk', function(data) { + monitorData.disk = data; + + c.api('/monitor/network', function(data) { + monitorData.network = data; + + // Remove useless interface + delete monitorData.network.usage.lo; + + // Get YunoHost versions too + c.api('/version', function(versions) { + monitorData.versions = versions; + c.view('tools/tools_monitoring', monitorData); + }); + }); + + }); + }); + } + else { + monitorData.status = false; + c.view('tools/tools_monitoring', monitorData); + } + + }, 'GET'); + }); + +})(); \ No newline at end of file diff --git a/src/js/yunohost/controllers/postinstall.js b/src/js/yunohost/controllers/postinstall.js new file mode 100644 index 00000000..e775b778 --- /dev/null +++ b/src/js/yunohost/controllers/postinstall.js @@ -0,0 +1,103 @@ +(function() { + // Get application context + var app = Sammy.apps['#main']; + var store = app.store; + + /** + * Post installation + * + */ + + // Step 1 : introduction + app.get('#/postinstall', function(c) { + $('#masthead').hide(); + c.checkInstall(function(isInstalled) { + if (isInstalled || typeof isInstalled === 'undefined') { + c.redirect('#/login'); + } else { + c.view('postinstall/postinstall_1'); + } + }); + }); + + // Step 2 : domain + app.get('#/postinstall/domain', function(c) { + $('#masthead').hide(); + $.get('https://dyndns.yunohost.org/domains', function() {}) + .done(function(data){ + c.params.ddomains = data.map(function(dom){return '.'+dom;}); + }) + .fail(function() { + c.params.ddomains = ['.nohost.me', '.noho.st']; + }) + .always(function() { + c.view('postinstall/postinstall_2', c.params, function() { + $('#domain, #ddomain').keyup(function(event){ + if(event.keyCode == 13){ + $('a.savedomain').click(); + } + }); + $('a.savedomain').on('click', function(e) { + if ($('#domain').val() === '') { + if ($('#ddomain').val() === '') { + e.preventDefault(); + store.clear('slide'); + c.flash('fail', y18n.t('error_select_domain')); + } else { + domain = $('#ddomain').val() + $('select[name="ddomain-ext"]').val(); + } + } else { + domain = $('#domain').val(); + } + store.set('maindomain', domain); + }); + }); + }); + }); + + // Step 3 : administration passowrd + app.get('#/postinstall/password', function(c) { + $('#masthead').hide(); + if (!store.get('maindomain')) { + store.clear('slide'); + c.redirect('#/postinstall/domain'); + } else { + c.view('postinstall/postinstall_3', { 'domain': store.get('maindomain').toLowerCase() }); + } + }); + + // Execute post-installation + app.post('#/postinstall', function (c) { + if (c.params['password'] === '' || c.params['confirmation'] === '') { + c.flash('fail', y18n.t('password_empty')); + } + else if (c.params['password'] == c.params['confirmation']) { + if (c.params['domain'] === '') { + c.flash('fail', y18n.t('error_select_domain')); + store.clear('slide'); + c.redirect('#/postinstall/domain'); + } else { + params = { 'domain': c.params['domain'].toLowerCase() }; + } + + c.confirm( + y18n.t('postinstall'), + y18n.t('confirm_postinstall', [c.params['domain']]), + function(){ + params['password'] = c.params['password']; + + store.set('url', window.location.hostname +'/yunohost/api'); + store.set('user', 'admin'); + c.api('/postinstall', function(data) { // http://api.yunohost.org/#!/tools/tools_postinstall_post_0 + c.redirect('#/login'); + }, 'POST', params); + }, + function(){ + } + ); + } else { + c.flash('fail', y18n.t('passwords_dont_match')); + } + }); + +})(); \ No newline at end of file diff --git a/src/js/yunohost/controllers/services.js b/src/js/yunohost/controllers/services.js new file mode 100644 index 00000000..e6583aaf --- /dev/null +++ b/src/js/yunohost/controllers/services.js @@ -0,0 +1,105 @@ +(function() { + // Get application context + var app = Sammy.apps['#main']; + var store = app.store; + + /** + * Services + * + */ + + // All services status + app.get('#/services', function (c) { + c.api('/services', function(data) { // ? + data2 = { 'services': [] }; + $.each(data, function(k, v) { + v.name = k; + // Handlebars want booleans + v.is_loaded = (v.loaded=='enabled') ? true : false; + v.is_running = (v.status=='running') ? true : false; + // Translate status and loaded state + v.status = y18n.t(v.status); + v.loaded = y18n.t(v.loaded); + data2.services.push(v); + }); + c.view('service/service_list', data2); + }); + }); + + // Status & actions for a service + app.get('#/services/:service', function (c) { + c.api('/services/'+ c.params['service'], function(data) { // ? + data2 = { 'service': data }; + data2.service.name = c.params['service']; + // Handlebars want booleans + data2.service.is_loaded = (data.loaded=='enabled') ? true : false; + data2.service.is_running = (data.status=='running') ? true : false; + // Translate status and loaded state + data2.service.status = y18n.t(data.status); + data2.service.loaded = y18n.t(data.loaded); + store.clear('slide'); + c.view('service/service_info', data2); + }, 'GET'); + }); + + // Service log + app.get('#/services/:service/log', function (c) { + params = { 'number': 50 }; + c.api('/services/'+ c.params['service'] +'/log', function(data) { // ? + data2 = { 'logs': [], 'name': c.params['service'] }; + $.each(data, function(k, v) { + data2.logs.push({filename: k, filecontent: v.join('\n')}); + }); + + c.view('service/service_log', data2); + }, 'GET', params); + }); + + // Enable/Disable & Start/Stop service + app.get('#/services/:service/:action', function (c) { + c.confirm( + "Service", + y18n.t('confirm_service_action', [y18n.t(c.params['action']), c.params['service']]), + function(){ + var method = null, endurl = c.params['service']; + + switch (c.params['action']) { + case 'start': + method = 'PUT'; + break; + case 'stop': + method = 'DELETE'; + break; + case 'enable': + method = 'PUT'; + endurl += '/enable'; + break; + case 'disable': + method = 'DELETE'; + endurl += '/enable'; + break; + default: + c.flash('fail', y18n.t('unknown_action', [c.params['action']])); + store.clear('slide'); + c.redirect('#/services/'+ c.params['service']); + } + + if (method && endurl) { + c.api('/services/'+ endurl, function(data) { + store.clear('slide'); + c.redirect('#/services/'+ c.params['service']); + }, method); + } + else { + store.clear('slide'); + c.redirect('#/services/'+ c.params['service']); + } + }, + function(){ + store.clear('slide'); + c.redirect('#/services/'+ c.params['service']); + } + ); + }); + +})(); \ No newline at end of file diff --git a/src/js/yunohost/controllers/tools.js b/src/js/yunohost/controllers/tools.js new file mode 100644 index 00000000..bcf7a408 --- /dev/null +++ b/src/js/yunohost/controllers/tools.js @@ -0,0 +1,158 @@ +(function() { + // Get application context + var app = Sammy.apps['#main']; + var store = app.store; + + /** + * Tools + * + */ + + // Tools list + app.get('#/tools', function (c) { + c.view('tools/tools_list'); + }); + + // Update administration password + app.get('#/tools/adminpw', function (c) { + c.view('tools/tools_adminpw'); + }); + + // Update administration password (PUT) + app.put('#/tools/adminpw', function (c) { + params = {}; + $.each(c.params.toHash(), function(key, value) { + if (value !== '') { params[key] = value; } + }); + if ($.isEmptyObject(params)) { + c.flash('fail', y18n.t('error_modify_something')); + store.clear('slide'); + c.redirect('#/tools/adminpw'); + } else if (params['new_password'] !== params['confirm_new_password']) { + c.flash('fail', y18n.t('passwords_dont_match')); + store.clear('slide'); + c.redirect('#/tools/adminpw'); + } else { + c.api('/login', function(data) { + // Remove useless variable + delete params['old_password']; + delete params['confirm_new_password']; + + // Update password and redirect to the home + c.api('/adminpw', function(data) { // http://api.yunohost.org/#!/tools/tools_adminpw_put_3 + c.redirect('#/logout'); + }, 'PUT', params); + }, 'POST', { 'password': params['old_password'] }, false); + } + }); + + // System update & upgrade + app.get('#/update', function (c) { + c.api('/update', function(data) { + packagesLength = data.packages.length; + for(var i = 0; i < packagesLength; i++) { + data.packages[i].delayed = false; + data.packages[i].changelog = data.packages[i].changelog.replace(/\n/g, '
'); + + // Check for special packages that need delayed upgrade. + if (["moulinette", "moulinette-yunohost", "yunohost-admin", "yunohost-config-nginx", "ssowat", "python"].indexOf(data.packages[i].name) != -1) { + c.flash('warning', y18n.t('system_delayed_upgrade_warning', [data.packages[i].name])); + data.packages[i].delayed = true; + } + } + c.view('update/update', data); + }, 'PUT'); + }); + + // Upgrade apps or packages + app.get('#/upgrade/:type', function (c) { + if (c.params['type'] !== 'apps' && c.params['type'] !== 'packages') { + c.flash('fail', y18n.t('unknown_argument', [c.params['type']])); + store.clear('slide'); + c.redirect('#/update'); + } + else { + c.confirm( + y18n.t('tools'), + y18n.t('confirm_update_type', [y18n.t('system_'+c.params['type']).toLowerCase()]), + function(){ + endurl = ''; + if (c.params['type'] == 'packages') {endurl = 'ignore_apps';} + else if (c.params['type'] == 'apps') {endurl = 'ignore_packages';} + + c.api('/upgrade?'+endurl, function(data) { + // 'log' is a reserved name, maybe in handlebars + data.logs = data.log; + c.view('upgrade/upgrade', data); + }, 'PUT'); + + }, + function(){ + store.clear('slide'); + c.redirect('#/update'); + } + ); + } + }); + + // Download SSL Certificate Authority + app.get('#/tools/ca', function (c) { + c.view('tools/tools_ca'); + }); + + // Security feed + app.get('#/tools/security-feed', function (c) { + data = { + items: [] + }; + + // Get security feed and display items + var forumUrl = 'https://forum.yunohost.org'; + var securityUrl = 'https://forum.yunohost.org/c/security'; + var securityFeed = 'https://yunohost.org/security.rss'; + + data.url = { + web: securityUrl, + rss: securityFeed + }; + + $.ajax({ + url: securityFeed, + // dataType: (jQuery.browser.msie) ? "text" : "xml", + dataType: "xml" + }) + .done(function(xml){ + // Loop through items + $('item', xml).each(function(k, v) { + var link=$('link', v)[0].innerHTML; + if (typeof link == 'string' && link !== '' && link.charAt(0) == '/') + link=forumUrl+link; + var description=$('description', v)[0].textContent; + description=description.replace('href="/','href="'+forumUrl+'/'); + + var item = { + guid: $('guid', v)[0].innerHTML, + title: $('title', v)[0].innerHTML, + url: link, + desc: description, + date: $('pubDate', v)[0].innerHTML.split(' +')[0], + }; + data.items.push(item); + }); + + c.view('tools/tools_security_feed', data); + }) + .fail(function() { + c.flash('fail', y18n.t('error_retrieve_feed', [securityFeed])); + }); + + }); + + // Security feed + app.get('#/tools/versions', function (c) { + c.api('/version', function(versions) { + c.view('tools/tools_versions', {'versions' : versions}); + }); + }); + +})(); \ No newline at end of file diff --git a/src/js/yunohost/controllers/users.js b/src/js/yunohost/controllers/users.js new file mode 100644 index 00000000..feafa742 --- /dev/null +++ b/src/js/yunohost/controllers/users.js @@ -0,0 +1,163 @@ +(function() { + // Get application context + var app = Sammy.apps['#main']; + var store = app.store; + + /** + * Users + * + */ + + // List existing users + app.get('#/users', function (c) { + c.api('/users', function(data) { // http://api.yunohost.org/#!/user/user_list_get_3 + c.view('user/user_list', data); + }); + }); + + // Create user form + app.get('#/users/create', function (c) { + c.api('/domains', function(data) { // http://api.yunohost.org/#!/domain/domain_list_get_2 + c.view('user/user_create', data); + }); + }); + + // Create user (POST) + app.post('#/users', function (c) { + if (c.params['password'] == c.params['confirmation']) { + if (c.params['password'].length < 4) { + c.flash('fail', y18n.t('password_too_short')); + store.clear('slide'); + } + else { + c.params['mail'] = c.params['email'] + c.params['domain']; + c.api('/users', function(data) { // http://api.yunohost.org/#!/user/user_create_post_2 + c.redirect('#/users'); + }, 'POST', c.params.toHash()); + } + } else { + c.flash('fail', y18n.t('passwords_dont_match')); + store.clear('slide'); + //c.redirect('#/users/create'); + } + }); + + // Show user information + app.get('#/users/:user', function (c) { + c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_info_get_0 + c.view('user/user_info', data); + }); + }); + + // Edit user form + app.get('#/users/:user/edit', function (c) { + c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_info_get_0 + c.api('/domains', function(dataDomains) { // http://api.yunohost.org/#!/domain/domain_list_get_2 + + // User email use a fake splitted field + email = data.mail.split('@'); + data.email = { + username : email[0], + domain : email[1] + }; + + // Domains + data.domains = []; + $.each(dataDomains.domains, function(key, value) { + data.domains.push({ + domain: value, + selected: (value == data.email.domain) ? true : false + }); + }); + + c.view('user/user_edit', data); + }); + }); + }); + + // Update user information + app.put('#/users/:user', function (c) { + // Get full user object + c.api('/users/'+ c.params['user'], function(user) { + + // concat email/domain pseudo field + if (c.params['mail'] !== c.params['email'] + c.params['domain']) { + c.params['mail'] = c.params['email'] + c.params['domain']; + } + else { + c.params['mail'] = ''; + } + // Clear temporary inputs + c.params['email'] = c.params['domain'] = ''; + + + // force array type for mail aliases and redirections + if (typeof c.params['mailalias'] == 'string') {c.params['mailalias'] = [c.params['mailalias']];} + if (typeof c.params['mailforward'] == 'string') {c.params['mailforward'] = [c.params['mailforward']];} + + // Check for added/removed aliases and redirections + c.params['add_mailalias'] = c.arrayDiff(c.params['mailalias'], user['mail-aliases']); + c.params['remove_mailalias'] = c.arrayDiff(user['mail-aliases'], c.params['mailalias']); + c.params['add_mailforward'] = c.arrayDiff(c.params['mailforward'], user['mail-forward']); + c.params['remove_mailforward'] = c.arrayDiff(user['mail-forward'], c.params['mailforward']); + + // Clear temporary inputs + c.params['mailalias'] = c.params['mailforward'] = ''; + + // Remove empty inputs + params = {}; + $.each(c.params.toHash(), function(key, value) { + if (value.length > 0 && key !== 'user') { params[key] = value; } + }); + + if ($.isEmptyObject(params)) { + c.flash('fail', y18n.t('error_modify_something')); + store.clear('slide'); + c.redirect('#/users/'+ c.params['user'] + '/edit'); + } else { + if (params['password']) { + if (params['password'] == params['confirmation']) { + if (params['password'].length < 4) { + c.flash('fail', y18n.t('password_too_short')); + store.clear('slide'); + c.redirect('#/users/'+ c.params['user'] + '/edit'); + } + else { + params['change_password'] = params['password']; + c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_update_put_1 + c.redirect('#/users/'+ c.params['user']); + }, 'PUT', params); + } + } else { + c.flash('fail', y18n.t('passwords_dont_match')); + store.clear('slide'); + c.redirect('#/users/'+ c.params['user'] + '/edit'); + } + } + else { + c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_update_put_1 + c.redirect('#/users/'+ c.params['user']); + }, 'PUT', params); + } + } + }, 'GET'); + }); + + // Remove existing user + app.get('#/users/:user/delete', function (c) { + c.confirm( + y18n.t('users'), + y18n.t('confirm_delete', [c.params['user']]), + function(){ + c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_delete_delete_4 + c.redirect('#/users'); + }, 'DELETE'); + }, + function(){ + store.clear('slide'); + c.redirect('#/users/'+ c.params['user']); + } + ); + }); + +})(); \ No newline at end of file diff --git a/src/js/yunohost/filters.js b/src/js/yunohost/filters.js new file mode 100644 index 00000000..1fab600a --- /dev/null +++ b/src/js/yunohost/filters.js @@ -0,0 +1,48 @@ +(function() { + // Get application context + var app = Sammy.apps['#main']; + var store = app.store; + + + /** + * Filters + * + */ + app.before(/domains\/add/, function (req){ + // Preload domains list. + req.params.domains = []; + req.api('/domains', function(data) { + req.params.domains = data.domains; + }); + }); + app.before(/apps\/install\//, function (req){ + // Preload domains list. + req.params.domains = []; + req.api('/domains', function(data) { + req.params.domains = data.domains; + }); + }); + app.before(/apps\/install\//, function (req){ + // Preload users lists. + req.params.users = []; + req.api('/users', function(data) { + req.params.users = data.users; + }); + }); + + + app.before({except: {path: ['#/logout', '#/login', '#/postinstall', '#/postinstall/domain', '#/postinstall/password']}}, function (req) { + // Store path for further redirections + store.set('path-1', store.get('path')); + store.set('path', req.path); + + // Redirect to login page if no credentials stored + if (!store.get('connected')) { + req.redirect('#/login'); + return false; + } + }); + + app.after(function () {}); + +})(); \ No newline at end of file diff --git a/src/js/yunohost/helpers.js b/src/js/yunohost/helpers.js new file mode 100644 index 00000000..93b9e421 --- /dev/null +++ b/src/js/yunohost/helpers.js @@ -0,0 +1,363 @@ +(function() { + // Get application context + var app = Sammy.apps['#main']; + var store = app.store; + + /** + * Helpers + * + */ + app.helpers({ + + // Serialize an object + serialize : function(obj) { + var str = []; + for(var p in obj) + if (obj.hasOwnProperty(p)) { + str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); + } + return str.join("&"); + }, + + // Flash helper to diplay instant notifications + flash: function (level, message) { + if (!store.get('flash')) { + store.set('flash', true); + } + + // Helper CSS class on main wrapper + $('#slider').addClass('with-flashMessage'); + + // If the line is a bash command + if (level === 'info' && message.charAt(0) === '+') { + level = 'log'; + } + + $('#flashMessage .messages') + .prepend('
'+ + '

'+ message +'

'); + }, + + checkInstall: function(callback) { + domain = window.location.hostname; + $.ajax({ + dataType: "json", + url: 'https://'+ domain +'/yunohost/api/installed', + timeout: 3000 + }) + .success(function(data) { + callback(data.installed); + }) + .fail(function() { + callback(undefined); + }); + }, + + // API call + api: function(uri, callback, method, data, websocket) { + c = this; + + call = function(uri, callback, method, data) { + method = typeof method !== 'undefined' ? method : 'GET'; + data = typeof data !== 'undefined' ? data : {}; + if (window.navigator && window.navigator.language && (typeof data.locale === 'undefined')) { + data.locale = y18n.locale || window.navigator.language.substr(0, 2); + } + + var args = data; + if (uri === '/postinstall') { + var installing = false; + setInterval(function () { + installing = true; + }, 1500); + } + + loaded = false; + if ($('div.loader').length === 0) { + setInterval(function () { + if (!loaded && $('div.loader').length === 0) { + $('#main').append('
'); + } + }, 500); + } + + jQuery.ajax({ + url: 'https://'+ store.get('url') + uri, + type: method, + crossdomain: true, + data: data, + traditional: true, + dataType: 'json' + }) + .always(function(xhr, ts, error) { + }) + .done(function(data) { + data = data || {}; + callback(data); + }) + .fail(function(xhr) { + if (xhr.status == 200) { + // Fail with 200, WTF + callback({}); + } else if (xhr.status == 401) { + if (uri === '/login') { + c.flash('fail', y18n.t('wrong_password')); + } else { + c.flash('fail', y18n.t('unauthorized')); + c.redirect('#/login'); + } + } else if (typeof xhr.responseJSON !== 'undefined') { + c.flash('fail', xhr.responseJSON.error); + } else if (typeof xhr.statusText !== 'undefined' && uri !== '/postinstall') { + c.flash('fail', y18n.t('api_not_responding', [xhr.status+' '+xhr.statusText])); + } else { + if (uri === '/postinstall') { + if (installing) { + interval = window.location.hostname === args.domain ? 20000 : 5000; + checkInstall = setInterval(function () { + c.checkInstall(function(isInstalled) { + if (isInstalled || typeof isInstalled === 'undefined') { + c.flash('success', y18n.t('installation_complete')); + clearInterval(checkInstall); + window.location.href = 'https://'+ window.location.hostname +'/yunohost/admin/'; + } + }); + }, interval); + } else { + c.flash('fail', y18n.t('error_occured')); + } + } else { + c.flash('fail', y18n.t('error_server')); + } + } + if (uri !== '/postinstall') { + store.clear('slide'); + c.view(store.get('path-1'), null, null, false); + } + }); + }; + + websocket = typeof websocket !== 'undefined' ? websocket : true; + if (websocket) { + + // Open a WebSocket connection to retrieve live messages from the moulinette + ws = new WebSocket('wss://'+ store.get('url') +'/messages'); + ws.onmessage = function(evt) { + // console.log(evt.data); + $.each($.parseJSON(evt.data), function(k, v) { + c.flash(k, v); + }); + }; + + // If not connected, WebSocket connection will raise an error, but we do not want to interrupt API request + ws.onerror = ws.onopen; + + ws.onclose = function() {}; + + ws.onopen = call(uri, callback, method, data); + } else { + call(uri, callback, method, data); + } + + }, + + // Render view (cross-browser) + view: function (view, data, callback, enableSlide) { + callback = typeof callback !== 'undefined' ? callback : function() {}; + rendered = this.render('views/'+ view +'.ms', data); + + enableSlide = (typeof enableSlide !== 'undefined') ? enableSlide : true; // Change to false to disable animation + + loaded = true; + $('div.loader').remove(); + $('#modal').modal('hide'); + + if (enableSlide) { + var leSwap = function() { + rendered.swap(function() { + $('.slide, .btn-breadcrumb a:not(:last-child)').on('click', function() { + $(this).addClass('active'); + if ($(this).hasClass('back') || $(this).parent('.btn-breadcrumb').length) { + store.set('slide', 'back'); + } else { + store.set('slide', 'to'); + } + }); + callback(); + // Force scrollTop on page load + $('html, body').scrollTop(0); + + // Resize body after the animation finishes (0.2s css transition) + // https://github.com/YunoHost/yunohost-admin/blob/231aac076a3aa836409b0d33fe02e48975990b7a/src/css/style.less#L92 + setTimeout(function() { + $('body').resize(); + }, 210); + }); + }; + + blockSize = $('#slider').width(); + + // Slide back effect + if (store.get('slide') == 'back') { + store.clear('slide'); + $('#slideBack').css('display', 'none'); + $('#slider-container').removeClass('move').css('margin-left', '-'+ blockSize +'px'); + $('#slideTo').show().html($('#main').html()); + leSwap(); + $('#slider-container').addClass('move').css('margin-left', '0px'); + + // Slide to effect + } else if (store.get('slide') == 'to') { + store.clear('slide'); + $('#slideTo').css('display', 'none'); + $('#slider-container').removeClass('move').css('margin-left', '-'+ blockSize +'px'); + $('#slider-container').removeClass('move').css('margin-left', '0px'); + $('#slideBack').show().html($('#main').html()); + leSwap(); + $('#slider-container').addClass('move').css('margin-left', '-'+ blockSize +'px'); + + } else { + leSwap(); + } + } else { + rendered.swap(function(){ + callback(); + // Force scrollTop on page load + $('html, body').scrollTop(0); + $('body').resize(); + }); + } + }, + + confirm: function(title, content, confirmCallback, cancelCallback) { + // Default callbacks + confirmCallback = typeof confirmCallback !== 'undefined' ? confirmCallback : function() {}; + cancelCallback = typeof cancelCallback !== 'undefined' ? cancelCallback : function() {}; + + // Get modal element + box = $('#modal'); + + // Modal title + if (typeof title === 'string' && title.length) { + $('.title', box).html(title); + } + else { + box.addClass('no-title'); + } + + // Modal content + $('.content', box).html(content); + + // Handle buttons + $('footer button', box) + .click(function(e){ + e.preventDefault(); + + $('#modal footer button').unbind( "click" ); + // Reset & Hide modal + box + .removeClass('no-title') + .modal('hide'); + + // Do corresponding callback + if ($(this).data('action') == 'confirm') { + confirmCallback(); + } + else { + cancelCallback(); + } + }); + + // Show modal + return box.modal('show'); + }, + + arraySortById: function(arr) { + arr.sort(function(a, b){ + if (a.id > b.id) { + return 1; + } + else if (a.id < b.id) { + return -1; + } + return 0; + }); + }, + + arrayDiff: function(arr1, arr2) { + arr1 = arr1 || []; + arr2 = arr2 || []; + return arr1.filter(function (a) { + return ((arr2.indexOf(a) == -1) && (a !== "")); + }); + }, + + groupHooks: function(hooks) { + data={}; + var rules=[ + { + id:'configuration', + isIn:function (hook) { + return hook.indexOf('conf_')==0 + } + } + ]; + + $.each(hooks, function(i, hook) { + var group_id=hook; + $.each(rules, function(i, rule) { + if (rule.isIn(hook)) { + group_id = 'adminjs_group_'+rule.id; + return false; + } + }); + + if(group_id in data) { + data[group_id] = { + name:y18n.t('hook_'+group_id), + value:data[group_id].value+','+hook, + description:data[group_id].description+', '+y18n.t('hook_'+hook) + }; + } + else { + data[group_id] = { + name:y18n.t('hook_'+group_id), + value:hook, + description:(group_id==hook)?y18n.t('hook_'+hook+'_desc'):y18n.t('hook_'+hook) + }; + } + }); + return data; + }, + + ungroupHooks: function(hooks,apps) { + var data={}; + data['apps'] = apps || []; + data['hooks'] = hooks || []; + + if (data['hooks'].constructor !== Array) { + data['hooks'] = [data['hooks']]; + } + if (data['apps'].constructor !== Array) { + data['apps'] = [data['apps']]; + } + + if (data['hooks'].length == 0) { + data['ignore_hooks'] = ''; + } + if (data['apps'].length == 0) { + data['ignore_apps'] = ''; + } + + // Some hook value contains multiple hooks separated by commas + var split_hooks = []; + $.each(data['hooks'], function(i, hook) { + split_hooks = split_hooks.concat(hook.split(',')); + }); + data['hooks'] = split_hooks; + + return data; + }, + }); + +})(); \ No newline at end of file diff --git a/src/js/y18n.js b/src/js/yunohost/y18n.js similarity index 100% rename from src/js/y18n.js rename to src/js/yunohost/y18n.js