mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
Merge branch 'stretch-testing' into stretch-stable
This commit is contained in:
commit
3f01bc51b5
70 changed files with 2187 additions and 2626 deletions
|
@ -38,4 +38,4 @@ https://example.com/yunohost/admin/views/domain/domain_list.ms)
|
|||
* Font-Awesome 4.5.0
|
||||
* Handlebars 1.3.0
|
||||
* Sammy 0.7.6
|
||||
* Jquery-Cookie 2.1.0
|
||||
* JS-cookie 2.1.0
|
||||
|
|
76
debian/changelog
vendored
76
debian/changelog
vendored
|
@ -1,3 +1,79 @@
|
|||
yunohost-admin (3.8.3.1) testing; urgency=low
|
||||
|
||||
- Add tip about the diagnosis page if domain seems not ready for ACME (e7a4df1)
|
||||
- Support raw string in 'ask' fields from manifest (for upcoming thing in core about default strings for install questions) (ee20f55)
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Sat, 09 May 2020 20:24:00 +0200
|
||||
|
||||
yunohost-admin (3.8.3) testing; urgency=low
|
||||
|
||||
- [enh] Add a note and explanation about sharing the logs when viewing logs of shared operations
|
||||
- [enh] Filter non-relevant line in operation log view
|
||||
- [fix] Remove an unecessary call to app list on domain view
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 07 Apr 2020 04:15:00 +0000
|
||||
|
||||
yunohost-admin (3.8.2) testing; urgency=low
|
||||
|
||||
- [enh] Highlight error/warnings/... in tools > logs (#294)
|
||||
- [enh] Hide stop button for critical services ? (#295)
|
||||
- [fix] Stupid typo in upgrade controller prevented upgrading all apps (a3a0d8f)
|
||||
- [fix] Custom app install + misc detail in regular app install (1592ab4)
|
||||
- [fix] Make the 'all apps' button look like the others (9694d8a)
|
||||
- [mod] Simplify log view (4480836)
|
||||
- [enh] Save/restore collapse states when refreshing diagnosis view (a110e56)
|
||||
- [enh] Add an explanation text on top of the diagnosis view (ccefdd6)
|
||||
- [i18n] Improve translations for Esperanto, Spanish, French, Occitan, Polish, Nepali
|
||||
|
||||
Thanks to all contributors <3 ! (Quentí, Simon, amirale qt)
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 29 Apr 2020 23:20:00 +0000
|
||||
|
||||
yunohost-admin (3.8.1.1) testing; urgency=low
|
||||
|
||||
- [hotfix] Pacman hanging forever after fetching version number..
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 19 Apr 2020 16:20:00 +0000
|
||||
|
||||
yunohost-admin (3.8.1) testing; urgency=low
|
||||
|
||||
- Many small cosmetic and mechanics improvements for the Diagnosis view
|
||||
- Add a 'Restart' button for services
|
||||
- Drop 'Security feed' and 'Download self-CA auth cert' features from the Tools section
|
||||
- Move 'Share on Yunopaste' button to make it more obvious to users (dd2570e)
|
||||
- Display 'All apps' button at the top in app category selector (#291)
|
||||
- Improve translations for French, German, Catalan, Turkish
|
||||
|
||||
Thanks to all contributors <3 ! (Kay0u, Yasin S. T., Zeik0s, E.Gaspar, xaloc33)
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 19 Apr 2020 06:22:00 +0000
|
||||
|
||||
yunohost-admin (3.8.0) testing; urgency=low
|
||||
|
||||
# Major stuff
|
||||
|
||||
- [enh] New diagnosis interface (#209, 109f542, 95f8503, 9e0f2f7, 79839b5)
|
||||
- [enh] App categories (#279)
|
||||
- [enh] Rework service views (#274)
|
||||
|
||||
# Refactoring, cleaning
|
||||
|
||||
- Code refactoring, readability improvements, use buttons instead of links when it's not about changing page (#262, 4f544de, 44c27a5, 6177044)
|
||||
- Remove the whole monitoring / glances stuff (#263)
|
||||
- Remove the appslist system from the webadmin (#264)
|
||||
- Remove app debug button, follow-up of corresponding PR on yunohost core (#271)
|
||||
- Fix slider effect (787204b)
|
||||
- Propagate parameter name change from core when changing domain (#260)
|
||||
|
||||
# i18n
|
||||
|
||||
- String definition / usage tests + cleaning of stale strings (#288)
|
||||
- Update translations for Spanish, Esperanto, Basque, Turkish, Catalan, Hindi, German, Greek, Dutch, Polish, Portuguese, Chinese (Simplified), Nepali, French, Occitan,Italian, Bengali (Bangladesh), Hungarian
|
||||
|
||||
Thanks to all contributors <3 ! (Abdulkadir F. Ş., Aeris One, Aleks, Armando F. Bram, Giovanni G. Gustavo M., Hem S., Jeroen F., Juan, Kay0u, Patrick B., Quentí, Yifei D., advocatux, amirale qt, Elie G., frju365, ppr, Romain R., xaloc3)
|
||||
|
||||
-- Kay0u <pierre@kayou.io> Thu, 09 Apr 2020 20:18:35 +0000
|
||||
|
||||
yunohost-admin (3.7.1.1) stable; urgency=low
|
||||
|
||||
- [fix] Aleks forgot to properly fix the conflicts in oc.json ~.~
|
||||
|
|
2
debian/control
vendored
2
debian/control
vendored
|
@ -12,7 +12,7 @@ Architecture: all
|
|||
Conflicts: yunohost-apps-admin
|
||||
Replaces: yunohost-apps-admin
|
||||
Depends: ${misc:Depends}
|
||||
, yunohost (>= 3.7)
|
||||
, yunohost (>= 3.8)
|
||||
Description: web administration interface for yunohost
|
||||
YunoHost aims to make self-hosting accessible to everyone. It configures
|
||||
an email, Web and IM server alongside a LDAP base. It also provides
|
||||
|
|
|
@ -81,7 +81,7 @@ body {
|
|||
}
|
||||
|
||||
.btn {
|
||||
& + .btn {margin-left: 8px;}
|
||||
& + .btn {margin-left: 8px; margin-right: 8px;}
|
||||
}
|
||||
button {
|
||||
&:extend(.btn all);
|
||||
|
@ -101,10 +101,6 @@ button {
|
|||
color: transparent;
|
||||
}
|
||||
|
||||
.label {
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
/*
|
||||
* The top heading of the doc
|
||||
*
|
||||
|
@ -229,8 +225,7 @@ button {
|
|||
.clearfix;
|
||||
.make-row(12);
|
||||
margin: 20px 0;
|
||||
padding-top: 20px;
|
||||
margin-top: 40px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid #eee;
|
||||
color: #999;
|
||||
font-size: 0.9em;
|
||||
|
@ -641,13 +636,18 @@ input[type='radio'].nice-radio {
|
|||
background-color: darkorchid;
|
||||
}
|
||||
|
||||
.app-category-card {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// only one card for small screens
|
||||
.app-card {
|
||||
.app-card, .app-category-card {
|
||||
width: 100%;
|
||||
.btn-group {
|
||||
width: 100%;
|
||||
.btn{
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -663,13 +663,16 @@ input[type='radio'].nice-radio {
|
|||
}
|
||||
}
|
||||
|
||||
.app-state {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
.app-title, .app-category-title {
|
||||
margin-top: 5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.app-card-desc {
|
||||
.app-card-desc, .app-category-card-desc {
|
||||
height: 6rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
@ -680,6 +683,7 @@ input[type='radio'].nice-radio {
|
|||
margin-bottom: 3px;
|
||||
margin-right: 7px;
|
||||
margin-top: -5px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.auto-width {
|
||||
|
@ -696,6 +700,7 @@ input[type='radio'].nice-radio {
|
|||
|
||||
.app-card .panel-body {
|
||||
padding: 1.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
|
@ -707,17 +712,47 @@ input[type='radio'].nice-radio {
|
|||
}
|
||||
}
|
||||
|
||||
.app-category-card .panel-body {
|
||||
padding: 2em;
|
||||
height: 10em;
|
||||
color: #333 !important;
|
||||
}
|
||||
|
||||
.subtag-selector {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.app-category-card {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.app-category-card:hover {
|
||||
border-color: #777;
|
||||
}
|
||||
|
||||
.app-category-title {
|
||||
line-height: 1em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.app-category-card-desc {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
|
||||
/** Groups View **/
|
||||
#view-groups {
|
||||
.panel-heading a {
|
||||
text-decoration: none;
|
||||
&.group-delete {
|
||||
float:right;
|
||||
color:lighten(@label-danger-bg, 20%);
|
||||
:hover {
|
||||
color:@label-danger-bg;
|
||||
}
|
||||
}
|
||||
.group-delete {
|
||||
font-size: 24px;
|
||||
line-height: 24px;
|
||||
padding: 0;
|
||||
float:right;
|
||||
color:lighten(@label-danger-bg, 20%);
|
||||
:hover {
|
||||
color:@label-danger-bg;
|
||||
}
|
||||
}
|
||||
.panel-body {
|
||||
|
@ -731,6 +766,10 @@ input[type='radio'].nice-radio {
|
|||
.dropdown-menu {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
|
||||
button {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
.label-removable {
|
||||
// The following match properties from regular btn's
|
||||
|
@ -749,15 +788,18 @@ input[type='radio'].nice-radio {
|
|||
|
||||
margin-right:7px; // Spacing between labels
|
||||
|
||||
> a {
|
||||
> button {
|
||||
line-height: 12px;
|
||||
margin-left:6px;
|
||||
padding: 0;
|
||||
padding-left:6px;
|
||||
border-left: #ccc 1px solid;
|
||||
color:lighten(@label-info-bg,20);
|
||||
background-color:transparent;
|
||||
|
||||
text-decoration: none;
|
||||
}
|
||||
> a:hover {
|
||||
> button:hover {
|
||||
color:@label-info-bg;
|
||||
}
|
||||
}
|
||||
|
@ -807,7 +849,7 @@ input[type='radio'].nice-radio {
|
|||
}
|
||||
|
||||
// display 2 cards between 640 and 992px
|
||||
.app-card {
|
||||
.app-card, .app-category-card {
|
||||
width: 47.9%;
|
||||
margin: 1%;
|
||||
}
|
||||
|
@ -909,7 +951,7 @@ input[type='radio'].nice-radio {
|
|||
|
||||
// bootstrap breakpoint for large screen is 992px
|
||||
@media screen and (min-width: 992px) {
|
||||
.app-card {
|
||||
.app-card, .app-category-card {
|
||||
// display 3 cards by row
|
||||
width: 31.3%;
|
||||
margin: 1%;
|
||||
|
@ -931,3 +973,58 @@ input[type='radio'].nice-radio {
|
|||
float: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.notransition {
|
||||
-webkit-transition: none !important;
|
||||
-moz-transition: none !important;
|
||||
-o-transition: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
/* Diagnosis styles */
|
||||
|
||||
.alert-success-yo {
|
||||
background-color: #dff0d8;
|
||||
border-color: #d6e9c6;
|
||||
}
|
||||
|
||||
.alert-warning-yo {
|
||||
background-color: #fcf8e3;
|
||||
border-color: #faebcc;
|
||||
}
|
||||
|
||||
.alert-danger-yo {
|
||||
background-color: #f2dede;
|
||||
border-color: #ebccd1;
|
||||
}
|
||||
|
||||
.alert-info-yo {
|
||||
background-color: #d9edf7;
|
||||
border-color: #bce8f1;
|
||||
}
|
||||
|
||||
.alert-ignored-yo {
|
||||
background-color: ghostwhite;
|
||||
border-color: lightgrey;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.diagnosis-item {
|
||||
|
||||
code {
|
||||
word-break: break-all;
|
||||
border-radius: 5px;
|
||||
padding-top: 1px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
code.cmd {
|
||||
word-break: break-word;
|
||||
color: white;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
ul > li {
|
||||
padding-top: 0.3em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,8 +81,8 @@
|
|||
</header>
|
||||
<div class="content"></div>
|
||||
<footer>
|
||||
<button type="button" id="modal-cancel" data-action="cancel" data-y18n="cancel">Cancel</button>
|
||||
<button type="button" id="modal-confirm" data-action="confirm" data-y18n="ok">OK</button>
|
||||
<button type="button" id="modal-cancel" data-modal-action="cancel" data-y18n="cancel">Cancel</button>
|
||||
<button type="button" id="modal-confirm" data-modal-action="confirm" data-y18n="ok">OK</button>
|
||||
</footer>
|
||||
</div></div>
|
||||
</div>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
// List installed apps
|
||||
app.get('#/apps', function (c) {
|
||||
c.api('/apps?installed', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8
|
||||
c.api('GET', '/apps?full', {}, function(data) {
|
||||
var apps = data['apps'];
|
||||
c.arraySortById(apps);
|
||||
c.view('app/app_list', {apps: apps});
|
||||
|
@ -70,7 +70,7 @@
|
|||
}
|
||||
else
|
||||
{
|
||||
return 'success';
|
||||
return 'info';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,131 +107,213 @@
|
|||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
var apps = []
|
||||
$.each(data['apps'], function(k, v) {
|
||||
app = dataraw[v['id']];
|
||||
app.level = parseInt(app.level);
|
||||
|
||||
if (app.high_quality && app.level > 7)
|
||||
{
|
||||
app.state = "high-quality";
|
||||
}
|
||||
if ( app.maintained === false )
|
||||
{
|
||||
app.maintained = "orphaned";
|
||||
}
|
||||
else if ( app.maintained === true )
|
||||
{
|
||||
app.maintained = "maintained";
|
||||
}
|
||||
// Display catalog home page where users chooses to browse a specific category
|
||||
app.get('#/apps/catalog', function (c) {
|
||||
c.api('GET', '/appscatalog?full&with_categories', {}, function (data) {
|
||||
c.view('app/app_catalog_home', {categories: data["categories"]}, function() {
|
||||
// Configure layout / rendering for app-category-cards
|
||||
$('#category-selector').isotope({
|
||||
itemSelector: '.app-category-card',
|
||||
layoutMode: 'fitRows',
|
||||
transitionDuration: 200
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.manifest.maintainer = extractMaintainer(app.manifest);
|
||||
var isWorking = (app.state === 'working' || app.state === "high-quality") && app.level > 0;
|
||||
// Display app catalog for a specific category
|
||||
app.get('#/apps/catalog/:category', function (c) {
|
||||
var category_id = c.params['category'];
|
||||
c.api('GET', '/appscatalog?full&with_categories', {}, function (data) {
|
||||
var apps = [];
|
||||
$.each(data['apps'], function(name, app) {
|
||||
|
||||
// Keep only the first instance of each app and remove not working apps
|
||||
if (!v['id'].match(/__[0-9]{1,5}$/) && (app.state !== 'notworking')) {
|
||||
// Ignore not working apps
|
||||
if (app.state === 'notworking') { return; }
|
||||
|
||||
app.installable = (!v.installed || app.manifest.multi_instance)
|
||||
app.levelFormatted = isNaN(app.level) ? '?' : app.level;
|
||||
// Ignore apps not in this category
|
||||
if ((category_id !== "all") && (app.category !== category_id)) { return; }
|
||||
|
||||
app.levelColor = levelToColor(app.level);
|
||||
app.stateColor = stateToColor(app.state);
|
||||
app.maintainedColor = maintainedStateToColor(app.maintained);
|
||||
app.installColor = combineColors(app.stateColor, app.levelColor);
|
||||
app.id = app.manifest.id;
|
||||
app.level = parseInt(app.level);
|
||||
|
||||
app.updateDate = app.lastUpdate * 1000 || 0;
|
||||
app.isSafe = (app.installColor !== 'danger');
|
||||
app.isWorking = isWorking ? "isworking" : "notFullyWorking";
|
||||
app.isHighQuality = (app.state === "high-quality") ? "isHighQuality" : "";
|
||||
app.decentQuality = (app.level > 4)?"decentQuality":"badQuality";
|
||||
if (app.high_quality && app.level > 7)
|
||||
{
|
||||
app.state = "high-quality";
|
||||
}
|
||||
if ( app.maintained === false )
|
||||
{
|
||||
app.maintained = "orphaned";
|
||||
}
|
||||
else if ( app.maintained === true )
|
||||
{
|
||||
app.maintained = "maintained";
|
||||
}
|
||||
|
||||
jQuery.extend(app, v);
|
||||
apps.push(app);
|
||||
}
|
||||
app.manifest.maintainer = extractMaintainer(app.manifest);
|
||||
var isWorking = (app.state === 'working' || app.state === "high-quality") && app.level > 0;
|
||||
|
||||
app.installable = (!app.installed || app.manifest.multi_instance)
|
||||
app.levelFormatted = isNaN(app.level) ? '?' : app.level;
|
||||
|
||||
app.levelColor = levelToColor(app.level);
|
||||
app.stateColor = stateToColor(app.state);
|
||||
app.maintainedColor = maintainedStateToColor(app.maintained);
|
||||
app.installColor = combineColors(app.stateColor, app.levelColor);
|
||||
|
||||
app.updateDate = app.lastUpdate * 1000 || 0;
|
||||
app.isSafe = (app.installColor !== 'danger');
|
||||
app.isWorking = isWorking ? "isworking" : "notFullyWorking";
|
||||
app.isHighQuality = (app.state === "high-quality") ? "isHighQuality" : "";
|
||||
app.decentQuality = (app.level > 4)?"decentQuality":"badQuality";
|
||||
|
||||
apps.push(app);
|
||||
});
|
||||
|
||||
var category = undefined;
|
||||
$.each(data['categories'], function(i, this_category) {
|
||||
if (this_category.id === category_id) { category = this_category; }
|
||||
});
|
||||
|
||||
if (category_id === "all") {
|
||||
category = {title: y18n.t("all_apps"), icon: "search"};
|
||||
}
|
||||
|
||||
// Sort app list
|
||||
c.arraySortById(apps);
|
||||
|
||||
// setup filtering of apps once the view is loaded
|
||||
function setupFilterEvents () {
|
||||
// Uses plugin isotope to filter apps (we could had ordering to)
|
||||
var cardGrid = jQuery('#apps').isotope({
|
||||
itemSelector: '.app-card',
|
||||
layoutMode: 'fitRows',
|
||||
transitionDuration: 200
|
||||
});
|
||||
|
||||
// Sort app list
|
||||
c.arraySortById(apps);
|
||||
// Default filter is 'decent quality apps'
|
||||
cardGrid.isotope({ filter: '.decentQuality' });
|
||||
|
||||
// setup filtering of apps once the view is loaded
|
||||
function setupFilterEvents () {
|
||||
// Uses plugin isotope to filter apps (we could had ordering to)
|
||||
var cardGrid = jQuery('.grid').isotope({
|
||||
itemSelector: '.app-card',
|
||||
layoutMode: 'fitRows',
|
||||
transitionDuration: 200
|
||||
});
|
||||
$(".subtag-selector button").on("click", function() {
|
||||
var selector = $(this).parent();
|
||||
$("button", selector).removeClass("active");
|
||||
$(this).addClass("active");
|
||||
cardGrid.isotope({ filter: filterApps });
|
||||
});
|
||||
|
||||
filterByClassAndName = function () {
|
||||
var input = jQuery("#filter-app-cards").val().toLowerCase();
|
||||
var inputMatch = (jQuery(this).find('.app-title').text().toLowerCase().indexOf(input) > -1);
|
||||
filterApps = function () {
|
||||
|
||||
var filterClass = jQuery("#dropdownFilter").attr("data-filter");
|
||||
var classMatch = (filterClass === '*') ? true : jQuery(this).hasClass(filterClass);
|
||||
return inputMatch && classMatch;
|
||||
},
|
||||
// Check text search
|
||||
var input = jQuery("#filter-app-cards").val().toLowerCase();
|
||||
if (jQuery(this).find('.app-title').text().toLowerCase().indexOf(input) <= -1) return false;
|
||||
|
||||
// Default filter is 'decent quality apps'
|
||||
cardGrid.isotope({ filter: '.decentQuality' });
|
||||
// Check subtags
|
||||
var subtag = $(".subtag-selector button.active").data("subtag");
|
||||
var this_subtags = jQuery(this).data("subtags");
|
||||
if ((subtag !== undefined) && (subtag !== "all")) {
|
||||
if ((subtag === "others") && (this_subtags !== "")) return false;
|
||||
if ((subtag !== "others") && (this_subtags.split(",").indexOf(subtag) <= -1)) return false;
|
||||
}
|
||||
|
||||
jQuery('.dropdownFilter').on('click', function() {
|
||||
// change dropdown label
|
||||
jQuery('#app-cards-list-filter-text').text(jQuery(this).find('.menu-item').text());
|
||||
// change filter attribute
|
||||
jQuery('#dropdownFilter').attr("data-filter", jQuery(this).attr("data-filter"));
|
||||
// filter !
|
||||
cardGrid.isotope({ filter: filterByClassAndName });
|
||||
});
|
||||
// Check quality criteria
|
||||
var class_ = jQuery("#dropdownFilter").data("filter");
|
||||
if ((class_ !== '*') && (! jQuery(this).hasClass(class_))) return false;
|
||||
|
||||
jQuery("#filter-app-cards").on("keyup", function() {
|
||||
cardGrid.isotope({ filter: filterByClassAndName });
|
||||
});
|
||||
};
|
||||
return true;
|
||||
},
|
||||
|
||||
// render
|
||||
c.view('app/app_list_install', {apps: apps}, setupFilterEvents);
|
||||
jQuery('.dropdownFilter').on('click', function() {
|
||||
// change dropdown label
|
||||
jQuery('#app-cards-list-filter-text').text(jQuery(this).find('.menu-item').text());
|
||||
// change filter attribute
|
||||
jQuery('#dropdownFilter').data("filter", jQuery(this).data("filter"));
|
||||
// filter !
|
||||
cardGrid.isotope({ filter: filterApps });
|
||||
});
|
||||
|
||||
jQuery("#filter-app-cards").on("keyup", function() {
|
||||
cardGrid.isotope({ filter: filterApps });
|
||||
});
|
||||
|
||||
$("#install-custom-app a[role='button']").on('click', function() {
|
||||
|
||||
var url = $("#install-custom-app input[name='url']")[0].value;
|
||||
if (url.indexOf("github.com") < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
c.confirm(
|
||||
y18n.t('applications'),
|
||||
y18n.t('confirm_install_custom_app'),
|
||||
function(){
|
||||
c.redirect_to('#/apps/install/custom/' + encodeURIComponent(url));
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// render
|
||||
c.view('app/app_catalog_category', {apps: apps, category: category}, setupFilterEvents);
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 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
|
||||
c.api('/users/permissions', function(data_permissions) {
|
||||
c.api('GET', '/apps/'+c.params['app']+'?full', {}, function(data) {
|
||||
c.api('GET', '/users/permissions', {}, function(data_permissions) {
|
||||
|
||||
// Permissions
|
||||
data.permissions = data_permissions.permissions[c.params['app']+".main"]["allowed"];
|
||||
// Permissions
|
||||
data.permissions = data_permissions.permissions[c.params['app']+".main"]["allowed"];
|
||||
|
||||
// Multilingual description
|
||||
data.description = (typeof data.manifest.description[y18n.locale] !== 'undefined') ?
|
||||
data.manifest.description[y18n.locale] :
|
||||
data.manifest.description['en']
|
||||
;
|
||||
// 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 ? y18n.t('yes') : y18n.t('no');
|
||||
data.install_time = new Date(data.settings.install_time * 1000);
|
||||
// Multi Instance settings
|
||||
data.manifest.multi_instance = data.manifest.multi_instance ? y18n.t('yes') : y18n.t('no');
|
||||
data.install_time = new Date(data.settings.install_time * 1000);
|
||||
|
||||
c.view('app/app_info', data);
|
||||
c.view('app/app_info', data, function() {
|
||||
|
||||
// Button to set the app as default
|
||||
$('button[data-action="set-as-default"]').on("click", function() {
|
||||
var app = $(this).data("app");
|
||||
c.confirm(
|
||||
y18n.t('applications'),
|
||||
y18n.t('confirm_app_default'),
|
||||
function() { c.api('PUT', '/apps/'+app+'/default', {}, function() { c.refresh() }); }
|
||||
);
|
||||
});
|
||||
|
||||
// Button to uninstall the app
|
||||
$('button[data-action="uninstall"]').on("click", function() {
|
||||
var app = $(this).data("app");
|
||||
c.confirm(
|
||||
y18n.t('applications'),
|
||||
y18n.t('confirm_uninstall', [app]),
|
||||
function() {
|
||||
c.api('DELETE', '/apps/'+ app, {}, function() {
|
||||
c.redirect_to('#/apps');
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 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);
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
// App actions
|
||||
//
|
||||
|
||||
// Get app actions list
|
||||
app.get('#/apps/:app/actions', function (c) {
|
||||
c.api('/apps/'+c.params['app']+'/actions', function(data) {
|
||||
c.api('GET', '/apps/'+c.params['app']+'/actions', {}, function(data) {
|
||||
$.each(data.actions, function(_, action) {
|
||||
formatYunoHostStyleArguments(action.arguments, c.params);
|
||||
|
||||
|
@ -249,7 +331,7 @@
|
|||
});
|
||||
});
|
||||
|
||||
// Perform application
|
||||
// Perform app action
|
||||
app.put('#/apps/:app/actions/:action', function(c) {
|
||||
// taken from app install
|
||||
$.each(c.params, function(k, v) {
|
||||
|
@ -268,14 +350,18 @@
|
|||
'args': c.serialize(c.params.toHash())
|
||||
}
|
||||
|
||||
c.api('/apps/'+app_id+'/actions/'+action_id, function() { // http://api.yunohost.org/#!/app/app_install_post_2
|
||||
c.redirect('#/apps/'+app_id+'/actions');
|
||||
}, 'PUT', params);
|
||||
c.api('PUT', '/apps/'+app_id+'/actions/'+action_id, params, function() {
|
||||
c.redirect_to('#/apps/'+app_id+'/actions', {slide:false});
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
// App config panel
|
||||
//
|
||||
|
||||
// Get app config panel
|
||||
app.get('#/apps/:app/config-panel', function (c) {
|
||||
c.api('/apps/'+c.params['app']+'/config-panel', function(data) {
|
||||
c.api('GET', '/apps/'+c.params['app']+'/config-panel', {}, function(data) {
|
||||
$.each(data.config_panel.panel, function(_, panel) {
|
||||
$.each(panel.sections, function(_, section) {
|
||||
formatYunoHostStyleArguments(section.options, c.params);
|
||||
|
@ -301,18 +387,11 @@
|
|||
'args': c.serialize(c.params.toHash())
|
||||
}
|
||||
|
||||
c.api('/apps/'+app_id+'/config', function() { // http://api.yunohost.org/#!/app/app_install_post_2
|
||||
c.redirect('#/apps/'+app_id+'/config-panel');
|
||||
}, 'POST', params);
|
||||
c.api('POST', '/apps/'+app_id+'/config', params, function() {
|
||||
c.redirect_to('#/apps/'+app_id+'/config-panel', {slide:false});
|
||||
});
|
||||
})
|
||||
|
||||
// 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 formats YunoHost style arguments for generating a form
|
||||
function formatYunoHostStyleArguments(args, params) {
|
||||
if (!args) {
|
||||
|
@ -333,10 +412,16 @@
|
|||
args[k].helpLink = "";
|
||||
|
||||
// Multilingual label
|
||||
args[k].label = (typeof args[k].ask[y18n.locale] !== 'undefined') ?
|
||||
args[k].ask[y18n.locale] :
|
||||
args[k].ask['en']
|
||||
;
|
||||
if (typeof args[k].ask === "string")
|
||||
{
|
||||
args[k].label = args[k].ask;
|
||||
}
|
||||
else if (typeof args[k].ask[y18n.locale] !== 'undefined') {
|
||||
args[k].label = args[k].ask[y18n.locale];
|
||||
}
|
||||
else {
|
||||
args[k].label = args[k].ask['en'];
|
||||
}
|
||||
|
||||
// Multilingual help text
|
||||
if (typeof args[k].help !== 'undefined') {
|
||||
|
@ -463,16 +548,24 @@
|
|||
displayLicense: (manifest['license'] !== undefined && manifest['license'] !== 'free')
|
||||
};
|
||||
|
||||
formatYunoHostStyleArguments(data.manifest.arguments.install, params);
|
||||
formatYunoHostStyleArguments(manifest.arguments.install, params);
|
||||
|
||||
// Multilingual description
|
||||
data.description = (typeof data.manifest.description[y18n.locale] !== 'undefined') ?
|
||||
data.manifest.description[y18n.locale] :
|
||||
data.manifest.description['en']
|
||||
;
|
||||
if (typeof manifest.description === 'string')
|
||||
{
|
||||
data.description = manifest.description;
|
||||
}
|
||||
else if (typeof manifest.description[y18n.locale] !== 'undefined')
|
||||
{
|
||||
data.description = manifest.description[y18n.locale];
|
||||
}
|
||||
else
|
||||
{
|
||||
data.description = manifest.description['en'];
|
||||
}
|
||||
|
||||
// Multi Instance settings boolean to text
|
||||
data.manifest.multi_instance = data.manifest.multi_instance ? y18n.t('yes') : y18n.t('no');
|
||||
data.manifest.multi_instance = manifest.multi_instance ? y18n.t('yes') : y18n.t('no');
|
||||
|
||||
// View app install form
|
||||
c.view('app/app_install', data);
|
||||
|
@ -481,9 +574,9 @@
|
|||
|
||||
// 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.api('GET', '/appscatalog?full', {}, function(data) {
|
||||
var app_name = c.params["app"];
|
||||
var app_infos = data[app_name];
|
||||
var app_infos = data["apps"][app_name];
|
||||
if (app_infos['state'] === "validated")
|
||||
{
|
||||
app_infos['state'] = "official";
|
||||
|
@ -504,17 +597,16 @@
|
|||
c.params
|
||||
);
|
||||
},
|
||||
function(){
|
||||
$('div.loader').remove();
|
||||
c.redirect('#/apps/install');
|
||||
function () {
|
||||
c.redirect_to('#/apps/catalog');
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
c.appInstallForm(
|
||||
c.params['app'],
|
||||
data[c.params['app']].manifest,
|
||||
app_name,
|
||||
app_infos.manifest,
|
||||
c.params
|
||||
);
|
||||
}
|
||||
|
@ -547,124 +639,67 @@
|
|||
delete params['args'];
|
||||
}
|
||||
|
||||
c.api('/apps', function() { // http://api.yunohost.org/#!/app/app_install_post_2
|
||||
c.redirect('#/apps');
|
||||
}, 'POST', params);
|
||||
c.api('POST', '/apps', params, function() {
|
||||
c.redirect_to('#/apps');
|
||||
});
|
||||
}
|
||||
else {
|
||||
c.flash('warning', y18n.t('app_install_cancel'));
|
||||
store.clear('slide');
|
||||
c.redirect('#/apps/install');
|
||||
c.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
// Install custom app from github
|
||||
app.post('#/apps/install/custom', function(c) {
|
||||
app.get('#/apps/install/custom/:url', function(c) {
|
||||
|
||||
var params = {
|
||||
label: c.params['label'],
|
||||
app: c.params['url']
|
||||
};
|
||||
delete c.params['label'];
|
||||
delete c.params['url'];
|
||||
// Force trailing slash
|
||||
url = c.params['url'];
|
||||
url = url.replace(/\/?$/, '/');
|
||||
raw_manifest_url = url.replace('github.com', 'raw.githubusercontent.com') + 'master/manifest.json'
|
||||
|
||||
c.confirm(
|
||||
y18n.t('applications'),
|
||||
y18n.t('confirm_install_custom_app'),
|
||||
function(){
|
||||
// Fetch manifest.json
|
||||
jQuery.ajax({ url: raw_manifest_url, type: 'GET' })
|
||||
.done(function(manifest) {
|
||||
// raw.githubusercontent.com serve content as plain text
|
||||
manifest = jQuery.parseJSON(manifest) || {};
|
||||
|
||||
// Force trailing slash
|
||||
params.app = params.app.replace(/\/?$/, '/');
|
||||
c.appInstallForm(
|
||||
url,
|
||||
manifest,
|
||||
c.params
|
||||
);
|
||||
|
||||
// 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) || {};
|
||||
})
|
||||
.fail(function(xhr) {
|
||||
c.flash('fail', y18n.t('app_install_custom_no_manifest'));
|
||||
c.redirect("#/apps/catalog/");
|
||||
});
|
||||
|
||||
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']);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// 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']);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Get app change label page
|
||||
app.get('#/apps/:app/changelabel', function (c) {
|
||||
c.api('/apps/'+c.params['app']+'?raw', function(app_data) {
|
||||
data = {
|
||||
c.api('GET', '/apps/'+c.params['app']+'?full', {}, function(app_data) {
|
||||
data = {
|
||||
id: c.params['app'],
|
||||
label: app_data.settings.label,
|
||||
};
|
||||
c.view('app/app_changelabel', data);
|
||||
};
|
||||
c.view('app/app_changelabel', data);
|
||||
});
|
||||
});
|
||||
|
||||
// Change app label
|
||||
app.post('#/apps/:app/changelabel', function (c) {
|
||||
params = {'new_label': c.params['label']};
|
||||
c.api('/apps/' + c.params['app'] + '/label', function(data) { // Call changelabel API
|
||||
store.clear('slide');
|
||||
c.redirect('#/apps/'+ c.params['app']);
|
||||
}, 'PUT', params);
|
||||
c.api('PUT', '/apps/' + c.params['app'] + '/label', params, function(data) {
|
||||
c.redirect_to('#/apps/'+ c.params['app']);
|
||||
});
|
||||
});
|
||||
|
||||
// Get app change URL page
|
||||
app.get('#/apps/:app/changeurl', function (c) {
|
||||
c.api('/apps/'+c.params['app']+'?raw', function(app_data) {
|
||||
c.api('/domains', function(domain_data) { // http://api.yunohost.org/#!/domain/domain_list_get_2
|
||||
c.api('GET', '/apps/'+c.params['app']+'?full', {}, function(app_data) {
|
||||
c.api('GET', '/domains', {}, function(domain_data) {
|
||||
|
||||
// Display a list of available domains
|
||||
var domains = [];
|
||||
|
@ -696,14 +731,9 @@
|
|||
y18n.t('confirm_app_change_url', [c.params['app']]),
|
||||
function() {
|
||||
params = {'domain': c.params['domain'], 'path': c.params['path']};
|
||||
c.api('/apps/' + c.params['app'] + '/changeurl', function(data) { // Call changeurl API
|
||||
store.clear('slide');
|
||||
c.redirect('#/apps/'+ c.params['app']);
|
||||
}, 'PUT', params);
|
||||
},
|
||||
function() {
|
||||
store.clear('slide');
|
||||
c.redirect('#/apps/'+ c.params['app'] + '/changeurl');
|
||||
c.api('PUT', '/apps/' + c.params['app'] + '/changeurl', params, function(data) {
|
||||
c.redirect_to('#/apps/'+ c.params['app']);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -32,118 +32,9 @@
|
|||
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?with_backup', function(apps_list) {
|
||||
data['apps'] = apps_list.apps;
|
||||
c.view('backup/backup_create', data, c.selectAllOrNone);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.post('#/backup/:storage', function (c) {
|
||||
var params = c.ungroupHooks(c.params['system_parts'],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['system_parts'],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.system_parts = c.groupHooks(Object.keys(data['system']),data['system']);
|
||||
data.items = (data['system']!={} || data['apps']!=[]);
|
||||
data.locale = y18n.locale
|
||||
c.view('backup/backup_info', data, c.selectAllOrNone);
|
||||
});
|
||||
});
|
||||
|
||||
// Archive list
|
||||
app.get('#/backup/:storage', function (c) {
|
||||
c.api('/backup/archives?with_info', function(data) {
|
||||
c.api('GET', '/backup/archives?with_info', {}, function(data) {
|
||||
data.storage = {
|
||||
id: 'local',
|
||||
name: y18n.t('local_archives')
|
||||
|
@ -159,4 +50,173 @@
|
|||
});
|
||||
});
|
||||
|
||||
// View to 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('GET', '/hooks/backup', {}, function(hooks) {
|
||||
data['hooks'] = groupHooks(hooks['hooks']);
|
||||
data['apps'] = {};
|
||||
c.api('GET', '/apps?with_backup', {}, function(apps_list) {
|
||||
data['apps'] = apps_list.apps;
|
||||
c.view('backup/backup_create', data, function() {
|
||||
|
||||
// Configure buttons "select all" and "select none"
|
||||
|
||||
// Remove active style from buttons
|
||||
$(".select_all-none input").click(function(){ $(this).toggleClass("active"); });
|
||||
// Select all checkbox in this panel
|
||||
$(".select_all").click(function(){
|
||||
$(this).parents(".panel").children(".list-group").find("input").prop("checked", true);
|
||||
});
|
||||
// Deselect all checkbox in this panel
|
||||
$(".select_none").click(function(){
|
||||
$(this).parents(".panel").children(".list-group").find("input").prop("checked", false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Actually creating the backup
|
||||
app.post('#/backup/:storage', function (c) {
|
||||
var params = ungroupHooks(c.params['system_parts'],c.params['apps']);
|
||||
c.api('POST', '/backup', params, function() {
|
||||
c.redirect_to('#/backup/'+ c.params['storage']);
|
||||
});
|
||||
});
|
||||
|
||||
// Get archive info
|
||||
app.get('#/backup/:storage/:archive', function (c) {
|
||||
c.api('GET', '/backup/archives/'+c.params['archive']+'?with_details', {}, function(data) {
|
||||
data.storage = {
|
||||
id: c.params['storage'],
|
||||
name: y18n.t('local_archives')
|
||||
};
|
||||
data.name = c.params['archive'];
|
||||
data.system_parts = groupHooks(Object.keys(data['system']),data['system']);
|
||||
data.items = (data['system']!={} || data['apps']!=[]);
|
||||
data.locale = y18n.locale;
|
||||
c.view('backup/backup_info', data, function() {
|
||||
|
||||
// Configure buttons "select all" and "select none"
|
||||
|
||||
// Remove active style from buttons
|
||||
$(".select_all-none input").click(function(){ $(this).toggleClass("active"); });
|
||||
// Select all checkbox in this panel
|
||||
$(".select_all").click(function(){
|
||||
$(this).parents(".panel").children(".list-group").find("input").prop("checked", true);
|
||||
});
|
||||
// Deselect all checkbox in this panel
|
||||
$(".select_none").click(function(){
|
||||
$(this).parents(".panel").children(".list-group").find("input").prop("checked", false);
|
||||
});
|
||||
|
||||
// Delete button
|
||||
$('button[data-action="delete"]').on('click', function() {
|
||||
var storage = $(this).data('storage');
|
||||
var archive = $(this).data('archive');
|
||||
c.confirm(
|
||||
y18n.t('backup'),
|
||||
y18n.t('confirm_delete', [archive]),
|
||||
function(){
|
||||
c.api('DELETE', '/backup/archives/'+archive, {}, function(data) {
|
||||
c.redirect_to('#/backup/'+ storage);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 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 = ungroupHooks(c.params['system_parts'],c.params['apps']);
|
||||
params['force'] = '';
|
||||
c.api('POST', '/backup/restore/'+c.params['archive'], params, function(data) {
|
||||
c.redirect_to('#/backup/'+ c.params['storage']+'/'+c.params['archive']);
|
||||
});
|
||||
}, this, c)
|
||||
);
|
||||
});
|
||||
|
||||
function groupHooks(hooks, raw_infos) {
|
||||
var data = {};
|
||||
var rules = [
|
||||
{
|
||||
id:'configuration',
|
||||
isIn:function (hook) {
|
||||
return hook.indexOf('conf_')==0
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
$.each(hooks, function(i, hook) {
|
||||
var group_id=hook;
|
||||
var hook_size=(raw_infos && raw_infos[hook] && raw_infos[hook].size)?raw_infos[hook].size:0;
|
||||
$.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),
|
||||
size:data[group_id].size + hook_size
|
||||
};
|
||||
}
|
||||
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),
|
||||
size:hook_size
|
||||
};
|
||||
}
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
function ungroupHooks(system_parts, apps) {
|
||||
|
||||
var data = {};
|
||||
data['apps'] = apps || [];
|
||||
data['system'] = system_parts || [];
|
||||
|
||||
if (data['system'].constructor !== Array) {
|
||||
data['system'] = [data['system']];
|
||||
}
|
||||
if (data['apps'].constructor !== Array) {
|
||||
data['apps'] = [data['apps']];
|
||||
}
|
||||
|
||||
// Some hook value contains multiple hooks separated by commas
|
||||
var split_hooks = [];
|
||||
$.each(data['system'], function(i, hook) {
|
||||
split_hooks = split_hooks.concat(hook.split(','));
|
||||
});
|
||||
data['system'] = split_hooks;
|
||||
|
||||
if (data['system'].length == 0) {
|
||||
delete data['system'];
|
||||
}
|
||||
if (data['apps'].length == 0) {
|
||||
delete data['apps'];
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
|
||||
})();
|
||||
|
|
152
src/js/yunohost/controllers/diagnosis.js
Normal file
152
src/js/yunohost/controllers/diagnosis.js
Normal file
|
@ -0,0 +1,152 @@
|
|||
(function() {
|
||||
// Get application context
|
||||
var app = Sammy.apps['#main'];
|
||||
var store = app.store;
|
||||
|
||||
// *********
|
||||
// Diagnosis
|
||||
// *********
|
||||
|
||||
app.get('#/diagnosis', function (c) {
|
||||
c.api('POST', '/diagnosis/run?except-if-never-ran-yet', {}, function() {
|
||||
updateDiagnosisView();
|
||||
});
|
||||
});
|
||||
|
||||
function updateDiagnosisView(state) {
|
||||
|
||||
c.api('GET', '/diagnosis/show?full', {}, function(data) {
|
||||
|
||||
if (typeof(data.reports) === "undefined")
|
||||
{
|
||||
data.reports = [];
|
||||
}
|
||||
|
||||
// Prepare data to be displayed ...
|
||||
for (var i = 0 ; i < data.reports.length ; i++)
|
||||
{
|
||||
// Convert timestamp to datetime
|
||||
data.reports[i].time = new Date(data.reports[i].timestamp*1000);
|
||||
data.reports[i].warnings = 0;
|
||||
data.reports[i].errors = 0;
|
||||
data.reports[i].ignored = 0;
|
||||
for (var j = 0 ; j < data.reports[i].items.length ; j++)
|
||||
{
|
||||
var type_ = data.reports[i].items[j].status;
|
||||
type_ = type_.toLowerCase();
|
||||
var ignored = data.reports[i].items[j].ignored;
|
||||
var icon = "";
|
||||
var issue = false;
|
||||
|
||||
if (type_ == "success") {
|
||||
icon = "check-circle";
|
||||
}
|
||||
else if (type_ == "info") {
|
||||
icon = "info-circle";
|
||||
}
|
||||
else if (ignored == true) {
|
||||
icon = type_;
|
||||
if (type_ == "error") {
|
||||
icon = "times"
|
||||
}
|
||||
type_ = "ignored";
|
||||
data.reports[i].ignored++;
|
||||
}
|
||||
else if (type_ == "warning") {
|
||||
icon = "warning";
|
||||
issue = true;
|
||||
data.reports[i].warnings++;
|
||||
}
|
||||
else if (type_ == "error") {
|
||||
type_ = "danger";
|
||||
icon = "times";
|
||||
issue = true;
|
||||
data.reports[i].errors++;
|
||||
}
|
||||
data.reports[i].items[j].status = type_;
|
||||
data.reports[i].items[j].icon = icon;
|
||||
data.reports[i].items[j].issue = issue;
|
||||
// We want filter_args to be something like "dnsrecords,domain=yolo.test,category=xmpp"
|
||||
data.reports[i].items[j].filter_args = data.reports[i].id;
|
||||
for (prop in data.reports[i].items[j].meta) {
|
||||
data.reports[i].items[j].filter_args = data.reports[i].items[j].filter_args + ","+prop+"="+data.reports[i].items[j].meta[prop];
|
||||
}
|
||||
};
|
||||
data.reports[i].noIssues = data.reports[i].warnings + data.reports[i].errors ? false : true;
|
||||
};
|
||||
|
||||
// Render and display the view
|
||||
c.view('diagnosis/diagnosis_show', data, function() {
|
||||
|
||||
restoreDiagnosisViewState(state);
|
||||
|
||||
// Button for first diagnosis
|
||||
$("button[data-action='run-full-diagnosis']").click(function() {
|
||||
c.api('POST', '/diagnosis/run', {}, function(data) {
|
||||
updateDiagnosisView();
|
||||
});
|
||||
});
|
||||
|
||||
// Configure share with yunopaste button
|
||||
$("button[data-action='share']").click(function() {
|
||||
c.api('GET', '/diagnosis/show?share', {}, function(data) {
|
||||
c.hideLoader();
|
||||
window.open(data.url, '_blank');
|
||||
});
|
||||
});
|
||||
|
||||
// Configure 'rerun diagnosis' button behavior
|
||||
$("button[data-action='rerun-diagnosis']").click(function() {
|
||||
var category = $(this).data("category");
|
||||
c.api('POST', '/diagnosis/run?force', {"categories": [category]}, function(data) {
|
||||
updateDiagnosisView(saveDiagnosisViewState());
|
||||
});
|
||||
});
|
||||
|
||||
// Configure 'ignore' / 'unignore' buttons behavior
|
||||
$("button[data-action='ignore']").click(function() {
|
||||
var filter_args = $(this).data("filter-args");
|
||||
c.api('POST', '/diagnosis/ignore', {'add_filter': filter_args.split(',') }, function(data) {
|
||||
updateDiagnosisView(saveDiagnosisViewState());
|
||||
})
|
||||
});
|
||||
|
||||
$("button[data-action='unignore']").click(function() {
|
||||
var filter_args = $(this).data("filter-args");
|
||||
c.api('POST', '/diagnosis/ignore', {'remove_filter': filter_args.split(',') }, function(data) {
|
||||
updateDiagnosisView(saveDiagnosisViewState());
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Save current level of scroll + which panels are collapsed / not collapsed
|
||||
function saveDiagnosisViewState() {
|
||||
var collapse = {};
|
||||
$(".panel-diagnosis").each(function(i, el) {
|
||||
collapse[$(el).data("category")] = $($(".panel-body", el)[0]).hasClass("in");
|
||||
});
|
||||
return { "scroll": document.documentElement.scrollTop, "collapse": collapse };
|
||||
}
|
||||
|
||||
// Restore scroll + panel collapse state
|
||||
function restoreDiagnosisViewState(state) {
|
||||
if (typeof state === "undefined") { return; }
|
||||
|
||||
Object.keys(state.collapse).forEach(function(category) {
|
||||
if (state.collapse[category]) {
|
||||
$(".panel-diagnosis[data-category='"+category+"'] .panel-body").addClass("in");
|
||||
}
|
||||
else
|
||||
{
|
||||
$(".panel-diagnosis[data-category='"+category+"'] .panel-body").removeClass("in");
|
||||
}
|
||||
});
|
||||
|
||||
window.scroll(0,state.scroll);
|
||||
}
|
||||
|
||||
|
||||
|
||||
})();
|
|
@ -10,8 +10,8 @@
|
|||
|
||||
// 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) {
|
||||
c.api('GET', '/domains', {}, function(data) {
|
||||
c.api('PUT', '/domains/main', {}, function(data2) {
|
||||
var domains = [];
|
||||
$.each(data.domains, function(k, domain) {
|
||||
domains.push({
|
||||
|
@ -29,7 +29,7 @@
|
|||
domains: domains,
|
||||
main_domain_form: main_domain_form
|
||||
});
|
||||
}, 'PUT');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -68,8 +68,7 @@
|
|||
if (c.params['domain'] === '') {
|
||||
if (c.params['ddomain'] === '') {
|
||||
c.flash('fail', y18n.t('error_select_domain'));
|
||||
store.clear('slide');
|
||||
c.redirect('#/domains/add');
|
||||
c.redirect_to('#/domains/add');
|
||||
}
|
||||
params.domain = c.params['ddomain'] + c.params['ddomain-ext'];
|
||||
endurl = 'dyndns';
|
||||
|
@ -77,42 +76,53 @@
|
|||
params.domain = c.params['domain'];
|
||||
}
|
||||
|
||||
c.api('/domains?'+endurl, function(data) { // http://api.yunohost.org/#!/domain/domain_add_post_1
|
||||
c.redirect('#/domains');
|
||||
}, 'POST', params);
|
||||
c.api('POST', '/domains?'+endurl, params, function(data) {
|
||||
c.redirect_to('#/domains');
|
||||
});
|
||||
});
|
||||
|
||||
// Get existing domain info
|
||||
app.get('#/domains/:domain', function (c) {
|
||||
c.api('/domains/main', function(dataMain) {
|
||||
c.api('/apps?installed', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8
|
||||
c.api('PUT', '/domains/main', {}, function(dataMain) {
|
||||
var domain = {
|
||||
name: c.params['domain'],
|
||||
main: (c.params['domain'] == dataMain.current_main_domain) ? true : false,
|
||||
url: "https://"+c.params['domain']
|
||||
};
|
||||
c.view('domain/domain_info', domain, function() {
|
||||
|
||||
// FIXME - This dirty trick (along with the previous API call
|
||||
// for apps installed) should be removed once letsencrypt_ynh
|
||||
// is not used by many people anymore. Probably around 07/2017
|
||||
// or end of 2017...
|
||||
var enable_cert_management_ = true;
|
||||
$.each(data['apps'], function(k, v) {
|
||||
if (v.id == "letsencrypt") {
|
||||
enable_cert_management_ = false;
|
||||
}
|
||||
// Configure "set default" button
|
||||
$('button[data-action="set_default"]').on("click", function() {
|
||||
var domain = $(this).data("domain");
|
||||
c.confirm(
|
||||
y18n.t('domains'),
|
||||
y18n.t('confirm_change_maindomain'),
|
||||
function() {
|
||||
c.api('PUT', '/domains/main', {new_main_domain: domain}, function() { c.refresh() });
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
var domain = {
|
||||
name: c.params['domain'],
|
||||
main: (c.params['domain'] == dataMain.current_main_domain) ? true : false,
|
||||
url: "https://"+c.params['domain'],
|
||||
enable_cert_management: enable_cert_management_
|
||||
};
|
||||
c.view('domain/domain_info', domain);
|
||||
// Configure delete button
|
||||
$('button[data-action="delete"]').on("click", function() {
|
||||
var domain = $(this).data("domain");
|
||||
c.confirm(
|
||||
y18n.t('domains'),
|
||||
y18n.t('confirm_delete', [domain]),
|
||||
function(){
|
||||
c.api('DELETE', '/domains/'+ domain, {}, function() {
|
||||
c.redirect_to('#/domains');
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}, 'PUT');
|
||||
});
|
||||
});
|
||||
|
||||
// Domain DNS
|
||||
app.get('#/domains/:domain/dns', function (c) {
|
||||
c.api('/domains/' + c.params['domain'] + '/dns', function(data) {
|
||||
c.api('GET', '/domains/' + c.params['domain'] + '/dns', {}, function(data) {
|
||||
var domain = {
|
||||
name: c.params['domain'],
|
||||
dns: data
|
||||
|
@ -123,7 +133,7 @@
|
|||
|
||||
// Domain certificate
|
||||
app.get('#/domains/:domain/cert-management', function (c) {
|
||||
c.api('/domains/cert-status/' + c.params['domain'] + '?full', function(data) {
|
||||
c.api('GET', '/domains/cert-status/' + c.params['domain'] + '?full', {}, function(data) {
|
||||
|
||||
var s = data["certificates"][c.params['domain']];
|
||||
var status_ = {
|
||||
|
@ -199,132 +209,45 @@
|
|||
status: status_,
|
||||
actions_enabled : actions_enabled
|
||||
};
|
||||
c.view('domain/domain_cert', data_);
|
||||
|
||||
c.view('domain/domain_cert', data_, function() {
|
||||
// Configure install / renew buttons behavior
|
||||
$("button[data-action]").on("click", function () {
|
||||
var action = $(this).data("action"),
|
||||
domain = $(this).data("domain"),
|
||||
confirm_key = "",
|
||||
api_url = "";
|
||||
|
||||
switch (action) {
|
||||
case 'install-LE':
|
||||
confirm_key = 'confirm_cert_install_LE';
|
||||
api_url = '/domains/cert-install/' + domain;
|
||||
break;
|
||||
case 'regen-selfsigned':
|
||||
confirm_key = 'confirm_cert_regen_selfsigned';
|
||||
api_url = '/domains/cert-install/' + domain + "?self_signed";
|
||||
break;
|
||||
case 'renew-letsencrypt':
|
||||
confirm_key = 'confirm_cert_manual_renew_LE';
|
||||
api_url = '/domains/cert-renew/' + domain + "?force";
|
||||
break;
|
||||
case 'replace-with-selfsigned':
|
||||
confirm_key = 'confirm_cert_revert_to_selfsigned';
|
||||
api_url = '/domains/cert-install/' + domain + "?self_signed&force";
|
||||
break;
|
||||
default:
|
||||
c.flash('fail', y18n.t('unknown_action', [action]));
|
||||
return
|
||||
}
|
||||
|
||||
c.confirm(
|
||||
y18n.t('certificate'),
|
||||
y18n.t(confirm_key, [domain]),
|
||||
function(){ c.api('POST', api_url, {}, function() { c.refresh() }); }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Install let's encrypt certificate on domain
|
||||
app.get('#/domains/:domain/cert-install-LE', function (c) {
|
||||
c.confirm(
|
||||
y18n.t('certificate'),
|
||||
y18n.t('confirm_cert_install_LE', [c.params['domain']]),
|
||||
function(){
|
||||
c.api('/domains/cert-install/' + c.params['domain'], function(data) {
|
||||
store.clear('slide');
|
||||
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||
}, 'POST');
|
||||
},
|
||||
function(){
|
||||
store.clear('slide');
|
||||
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Regenerate a self-signed certificate
|
||||
app.get('#/domains/:domain/cert-regen-selfsigned', function (c) {
|
||||
c.confirm(
|
||||
y18n.t('certificate'),
|
||||
y18n.t('confirm_cert_regen_selfsigned', [c.params['domain']]),
|
||||
function(){
|
||||
c.api('/domains/cert-install/' + c.params['domain'] + "?self_signed", function(data) {
|
||||
store.clear('slide');
|
||||
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||
}, 'POST');
|
||||
},
|
||||
function(){
|
||||
store.clear('slide');
|
||||
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Manually renew a Let's Encrypt certificate
|
||||
app.get('#/domains/:domain/cert-renew-letsencrypt', function (c) {
|
||||
c.confirm(
|
||||
y18n.t('certificate'),
|
||||
y18n.t('confirm_cert_manual_renew_LE', [c.params['domain']]),
|
||||
function(){
|
||||
c.api('/domains/cert-renew/' + c.params['domain'] + "?force", function(data) {
|
||||
store.clear('slide');
|
||||
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||
}, 'POST');
|
||||
},
|
||||
function(){
|
||||
store.clear('slide');
|
||||
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Replace valid cert with self-signed
|
||||
app.get('#/domains/:domain/cert-replace-with-selfsigned', function (c) {
|
||||
c.confirm(
|
||||
y18n.t('certificate'),
|
||||
y18n.t('confirm_cert_revert_to_selfsigned', [c.params['domain']]),
|
||||
function(){
|
||||
c.api('/domains/cert-install/' + c.params['domain'] + "?self_signed&force", function(data) {
|
||||
store.clear('slide');
|
||||
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||
}, 'POST');
|
||||
},
|
||||
function(){
|
||||
store.clear('slide');
|
||||
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
// 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(){
|
||||
var 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
|
||||
var refreshDomain = window.setTimeout(function(){
|
||||
store.clear('slide');
|
||||
c.redirect('#/domains');
|
||||
}, 15000);
|
||||
},
|
||||
function(){
|
||||
store.clear('slide');
|
||||
c.redirect('#/domains');
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
// Firewall status
|
||||
app.get('#/tools/firewall', function (c) {
|
||||
c.api('/firewall?raw', function(data) {
|
||||
c.api('GET', '/firewall?raw', {}, function(data) {
|
||||
var firewall = {
|
||||
ports: {},
|
||||
upnp: false
|
||||
|
@ -30,28 +30,49 @@
|
|||
// Get UPnP status
|
||||
firewall.upnp = data.uPnP.enabled;
|
||||
|
||||
c.view('tools/tools_firewall', firewall);
|
||||
c.view('tools/tools_firewall', firewall, function() {
|
||||
|
||||
// Buttons in the 'ports' panel to open/close specific ports
|
||||
$("button[data-port]").on("click", function() {
|
||||
|
||||
var port = $(this).data("port");
|
||||
var action = $(this).data("action");
|
||||
var protocol = $(this).data("protocol");
|
||||
var connection = $(this).data("connection");
|
||||
c.confirm(
|
||||
y18n.t('firewall'),
|
||||
// confirm_firewall_open and confirm_firewall_close
|
||||
y18n.t('confirm_firewall_' + action, [ port, y18n.t(protocol), y18n.t(connection)]),
|
||||
function(){ c.togglePort(port, protocol, connection, action); }
|
||||
);
|
||||
});
|
||||
|
||||
// Buttons to enable / disable UPnP
|
||||
$("button[data-upnp]").on("click", function() {
|
||||
var action = $(this).data("upnp");
|
||||
c.confirm(
|
||||
y18n.t('firewall'),
|
||||
// confirm_upnp_enable and confirm_upnp_disable
|
||||
y18n.t('confirm_upnp_' + action),
|
||||
function(){ c.api('GET', '/firewall/upnp', {action: action}, function() { c.refresh() }); }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Enable/Disable UPnP
|
||||
app.get('#/tools/firewall/upnp/:action', function (c) {
|
||||
// Update port status from form
|
||||
app.post('#/tools/firewall/port', function (c) {
|
||||
c.confirm(
|
||||
y18n.t('firewall'),
|
||||
// confirm_upnp_enable and confirm_upnp_disable
|
||||
y18n.t('confirm_upnp_' + c.params['action'].toLowerCase()),
|
||||
y18n.t('confirm_firewall_' + c.params['action'].toLowerCase(), [ c.params['port'], y18n.t(c.params['protocol']), y18n.t(c.params['connection']) ]),
|
||||
function(){
|
||||
var 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');
|
||||
c.togglePort(
|
||||
c.params['port'],
|
||||
c.params['protocol'],
|
||||
c.params['connection'],
|
||||
c.params['action']
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -65,8 +86,7 @@
|
|||
|
||||
if (port != parseInt(port) || port < 0 || port > 65535) {
|
||||
c.flash('fail', y18n.t('unknown_argument', [port]));
|
||||
store.clear('slide');
|
||||
c.redirect('#/tools/firewall');
|
||||
c.refresh();
|
||||
}
|
||||
|
||||
switch (connection) {
|
||||
|
@ -98,75 +118,27 @@
|
|||
break;
|
||||
default:
|
||||
c.flash('fail', y18n.t('unknown_action', [action]));
|
||||
store.clear('slide');
|
||||
c.redirect('#/tools/firewall');
|
||||
c.refresh();
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
// port:
|
||||
// protocol:
|
||||
// - UDP
|
||||
// - TCP
|
||||
// - Both
|
||||
// --ipv4-only:
|
||||
// --ipv6-only:
|
||||
// --no-upnp:
|
||||
var params = {
|
||||
port : port,
|
||||
protocol : protocol
|
||||
};
|
||||
|
||||
c.api(method, '/firewall/port?'+endurl, params, function() { c.refresh() });
|
||||
|
||||
|
||||
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'),
|
||||
// confirm_firewall_open and confirm_firewall_close
|
||||
y18n.t( 'confirm_firewall_' + c.params['action'].toLowerCase(), [ 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_' + c.params['action'].toLowerCase(), [ 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');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
})();
|
||||
})();
|
||||
|
|
|
@ -24,45 +24,36 @@
|
|||
$('#masthead').show()
|
||||
.find('.logout-btn').hide();
|
||||
store.set('path-1', '#/login');
|
||||
if ($('div.loader').length === 0) {
|
||||
$('#main').append('<div class="loader loader-content"></div>');
|
||||
|
||||
c.showLoader();
|
||||
|
||||
// We gonna retry 3 times to check if yunohost is installed
|
||||
if (app.isInstalledTry === undefined) {
|
||||
app.isInstalledTry = 3;
|
||||
}
|
||||
|
||||
c.checkInstall(function(isInstalled) {
|
||||
|
||||
if (isInstalled) {
|
||||
// Remove loader
|
||||
$('div.loader').remove();
|
||||
// Pass domain to hide form field
|
||||
c.view('login', { 'domain': window.location.hostname });
|
||||
} 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;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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();
|
||||
if (typeof isInstalled !== 'undefined') {
|
||||
c.redirect('#/postinstall');
|
||||
return;
|
||||
}
|
||||
|
||||
// If the retry counter is still up, retry this function 5 sec
|
||||
// later
|
||||
if (app.isInstalledTry > 0) {
|
||||
app.isInstalledTry--;
|
||||
setTimeout(function() {
|
||||
c.redirect('#/');
|
||||
}, 5000);
|
||||
}
|
||||
else {
|
||||
c.flash('fail', y18n.t('api_not_responding'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -80,7 +71,7 @@
|
|||
var params = {
|
||||
password: c.params['password']
|
||||
};
|
||||
c.api('/login', function(data) {
|
||||
c.api('POST', '/login', params, function(data) {
|
||||
store.set('connected', true);
|
||||
c.trigger('login');
|
||||
$('#masthead .logout-btn').fadeIn();
|
||||
|
@ -90,19 +81,19 @@
|
|||
} else {
|
||||
c.redirect('#/');
|
||||
}
|
||||
}, 'POST', params, false);
|
||||
}, undefined, false);
|
||||
|
||||
});
|
||||
|
||||
app.get('#/logout', function (c) {
|
||||
c.api('/logout', function (data) {
|
||||
c.api('GET', '/logout', {}, function (data) {
|
||||
store.clear('url');
|
||||
store.clear('connected');
|
||||
store.set('path', '#/');
|
||||
c.trigger('logout');
|
||||
c.flash('success', y18n.t('logged_out'));
|
||||
c.redirect('#/login');
|
||||
}, 'GET', {}, false);
|
||||
}, undefined, false);
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
(function() {
|
||||
// Get application context
|
||||
var app = Sammy.apps['#main'];
|
||||
var store = app.store;
|
||||
|
||||
/**
|
||||
* Monitor
|
||||
*
|
||||
*/
|
||||
|
||||
// Server monitoring
|
||||
app.get('#/tools/monitor', function (c) {
|
||||
var 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('/diagnosis', function(diagnosis) {
|
||||
monitorData.versions = diagnosis.packages;
|
||||
c.view('tools/tools_monitoring', monitorData);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
monitorData.status = false;
|
||||
c.view('tools/tools_monitoring', monitorData);
|
||||
}
|
||||
|
||||
}, 'GET');
|
||||
});
|
||||
|
||||
})();
|
|
@ -13,7 +13,7 @@
|
|||
$('#masthead').hide();
|
||||
c.checkInstall(function(isInstalled) {
|
||||
if (isInstalled || typeof isInstalled === 'undefined') {
|
||||
c.redirect('#/login');
|
||||
c.redirect_to('#/login');
|
||||
} else {
|
||||
c.view('postinstall/postinstall_1');
|
||||
}
|
||||
|
@ -41,7 +41,6 @@
|
|||
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();
|
||||
|
@ -51,7 +50,7 @@
|
|||
}
|
||||
store.set('maindomain', domain);
|
||||
});
|
||||
}, false); // We disable enableSlide because that causes some issues with accordion when using the 'previous' button
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -59,49 +58,52 @@
|
|||
app.get('#/postinstall/password', function(c) {
|
||||
$('#masthead').hide();
|
||||
if (!store.get('maindomain')) {
|
||||
store.clear('slide');
|
||||
c.redirect('#/postinstall/domain');
|
||||
c.redirect_to('#/postinstall/domain');
|
||||
} else {
|
||||
c.view('postinstall/postinstall_3', { 'domain': store.get('maindomain').toLowerCase() },
|
||||
function() { },
|
||||
false); // We disable enableSlide because that causes some issues with accordion when using the 'previous' button
|
||||
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'] === '') {
|
||||
|
||||
var password = c.params['password'];
|
||||
var confirmation = c.params['confirmation'];
|
||||
var domain = c.params['domain'].toLowerCase();
|
||||
|
||||
// Check password ain't empty
|
||||
if (password === '' || confirmation === '') {
|
||||
c.flash('fail', y18n.t('password_empty'));
|
||||
return;
|
||||
}
|
||||
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 {
|
||||
var 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 {
|
||||
// Check password matches confirmation
|
||||
if (password !== confirmation) {
|
||||
c.flash('fail', y18n.t('passwords_dont_match'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check domain ain't empty...
|
||||
if (domain === '') {
|
||||
c.flash('fail', y18n.t('error_select_domain'));
|
||||
c.redirect_to('#/postinstall/domain', {slide: false});
|
||||
return;
|
||||
}
|
||||
|
||||
// Ask confirmation to the user
|
||||
c.confirm(
|
||||
y18n.t('postinstall'),
|
||||
y18n.t('confirm_postinstall', [c.params['domain']]),
|
||||
// Start the actual postinstall process
|
||||
function(){
|
||||
store.set('url', window.location.hostname +'/yunohost/api');
|
||||
store.set('user', 'admin');
|
||||
c.api('POST', '/postinstall', {domain: domain, password: password}, function() {
|
||||
c.flash('success', y18n.t('installation_complete'));
|
||||
c.redirect_to('#/login');
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
|
@ -10,21 +10,15 @@
|
|||
|
||||
// All services status
|
||||
app.get('#/services', function (c) {
|
||||
c.api('/services', function(data) { // ?
|
||||
c.api('GET', '/services', {}, function(data) {
|
||||
var 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.active=='active') ? true : false;
|
||||
// Translate status and loaded state
|
||||
v.status = y18n.t(v.status);
|
||||
v.loaded = y18n.t(v.loaded);
|
||||
if (v.active_at == 'unknown')
|
||||
if (v.last_state_change == 'unknown')
|
||||
{
|
||||
delete v.active_at;
|
||||
v.last_state_change = 0;
|
||||
}
|
||||
data2.services.push(v);
|
||||
});
|
||||
|
@ -45,28 +39,88 @@
|
|||
|
||||
// Status & actions for a service
|
||||
app.get('#/services/:service', function (c) {
|
||||
c.api('/services/'+ c.params['service'], function(data) { // ?
|
||||
var 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.active=='active') ? true : false;
|
||||
// Translate status and loaded state
|
||||
data2.service.active = y18n.t(data.active);
|
||||
data2.service.loaded = y18n.t(data.loaded);
|
||||
if (data.active_at != 'unknown')
|
||||
c.api('GET', '/services/'+ c.params['service'], {}, function(data) {
|
||||
c.api('GET', '/services/'+ c.params['service'] +'/log', {number: 50}, function(data_log) {
|
||||
|
||||
data.name = c.params['service'];
|
||||
if (data.last_state_change == 'unknown')
|
||||
{
|
||||
data2.service.active_at = data.active_at;
|
||||
data.last_state_change = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
data2.service.active_at = 0;
|
||||
}
|
||||
store.clear('slide');
|
||||
c.view('service/service_info', data2);
|
||||
}, 'GET');
|
||||
|
||||
data.logs = [];
|
||||
$.each(data_log, function(k, v) {
|
||||
data.logs.push({filename: k, filecontent: v.join('\n')});
|
||||
});
|
||||
|
||||
// Sort logs by filename, put the journalctl/systemd log on top
|
||||
data.logs.sort(function(a,b) { return a.filename === "journalctl" ? -1 : b.filename === "journalctl" ? 1 : a.filename < b.filename ? -1 : a.filename > b.filename ? 1 : 0; });
|
||||
|
||||
c.view('service/service_info', data, function() {
|
||||
|
||||
// Don't allow user to stop critical services from the webadmin
|
||||
$('button[data-action="stop"]').each(function() {
|
||||
|
||||
var critical = ['nginx', 'ssh', 'slapd', 'yunohost-api'];
|
||||
var service = $(this).data('service');
|
||||
|
||||
if (critical.indexOf(service) >= 0)
|
||||
{
|
||||
$(this).hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Configure behavior for enable/disable and start/stop buttons
|
||||
$('button[data-action="start"], button[data-action="restart"], button[data-action="stop"]').on('click', function() {
|
||||
|
||||
var service = $(this).data('service');
|
||||
var action = $(this).data('action');
|
||||
|
||||
c.confirm(y18n.t("services"), y18n.t('confirm_service_' + action, [service]), function(){
|
||||
|
||||
if (action == "start")
|
||||
{
|
||||
var method = "PUT";
|
||||
var url = "/services/" + service;
|
||||
}
|
||||
else if (action == "restart")
|
||||
{
|
||||
var method = "PUT";
|
||||
var url = "/services/" + service + "/restart";
|
||||
}
|
||||
else
|
||||
{
|
||||
var method = "DELETE";
|
||||
var url = "/services/" + service;
|
||||
}
|
||||
c.api(method, url, {}, function() { c.refresh(); });
|
||||
});
|
||||
});
|
||||
|
||||
// Configure behavior for enable/disable and start/stop buttons
|
||||
$('button[data-action="share"]').on('click', function() {
|
||||
|
||||
c.showLoader();
|
||||
|
||||
// Send to paste.yunohost.org
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: 'https://paste.yunohost.org/documents',
|
||||
data: $("#logs").text(),
|
||||
})
|
||||
.success(function(data, textStatus, jqXHR) {
|
||||
window.open('https://paste.yunohost.org/' + data.key, '_blank');
|
||||
})
|
||||
.fail(function() {
|
||||
c.flash('fail', y18n.t('paste_error'));
|
||||
})
|
||||
.always(function(){
|
||||
c.hideLoader();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Service log
|
||||
|
@ -74,63 +128,14 @@
|
|||
var params = {
|
||||
number: 50
|
||||
};
|
||||
c.api('/services/'+ c.params['service'] +'/log', function(data) { // ?
|
||||
c.api('GET', '/services/'+ c.params['service'] +'/log', params, 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",
|
||||
// confirm_service_start, confirm_service_stop, confirm_service_enable and confirm_service_disable
|
||||
y18n.t('confirm_service_' + c.params['action'].toLowerCase(), [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']);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
|
@ -26,77 +26,69 @@
|
|||
});
|
||||
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);
|
||||
c.refresh();
|
||||
return;
|
||||
}
|
||||
if (params['new_password'] !== params['confirm_new_password']) {
|
||||
c.flash('fail', y18n.t('passwords_dont_match'));
|
||||
c.refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
c.api('POST', '/login', { 'password': params['old_password'] }, function(data) {
|
||||
// Remove useless variable
|
||||
delete params['old_password'];
|
||||
delete params['confirm_new_password'];
|
||||
|
||||
// Update password and redirect to the home
|
||||
c.api('PUT', '/adminpw', params, function(data) {
|
||||
c.redirect_to('#/logout');
|
||||
});
|
||||
}, undefined, false);
|
||||
});
|
||||
|
||||
// System update & upgrade
|
||||
app.get('#/update', function (c) {
|
||||
c.api('/update', function(data) {
|
||||
c.view('update/update', data);
|
||||
}, 'PUT');
|
||||
});
|
||||
c.api('PUT', '/update', {}, function(data) {
|
||||
c.view('tools/tools_update', data, function() {
|
||||
// Configure buttons behaviors
|
||||
$("button[data-upgrade]").on("click", function() {
|
||||
|
||||
// Upgrade apps or packages
|
||||
app.get('#/upgrade/:type', function (c) {
|
||||
c.confirm(
|
||||
y18n.t('tools'),
|
||||
// confirm_update_apps and confirm_update_packages
|
||||
y18n.t('confirm_update_' + c.params['type'].toLowerCase()),
|
||||
function(){
|
||||
c.api('/upgrade?'+c.params["type"],
|
||||
function(data) {
|
||||
store.clear('slide');
|
||||
c.redirect('#/tools/logs');
|
||||
},
|
||||
'PUT');
|
||||
},
|
||||
function(){
|
||||
store.clear('slide');
|
||||
c.redirect('#/update');
|
||||
}
|
||||
);
|
||||
});
|
||||
var what = $(this).data("upgrade").toLowerCase();
|
||||
|
||||
// Upgrade a specific apps
|
||||
app.get('#/upgrade/apps/:app', function (c) {
|
||||
c.confirm(
|
||||
y18n.t('tools'),
|
||||
y18n.t('confirm_update_specific_app', [c.params['app']]),
|
||||
function(){
|
||||
c.api('/upgrade/apps?app='+c.params['app'].toLowerCase(),
|
||||
function(data) {
|
||||
store.clear('slide');
|
||||
c.redirect('#/tools/logs');
|
||||
},
|
||||
'PUT');
|
||||
},
|
||||
function(){
|
||||
store.clear('slide');
|
||||
c.redirect('#/update');
|
||||
}
|
||||
);
|
||||
// Upgrade all apps or the system
|
||||
|
||||
if ((what == "system") || (what == "apps"))
|
||||
{
|
||||
var confirm_message = y18n.t('confirm_update_' + what);
|
||||
var api_url = '/upgrade?'+what;
|
||||
}
|
||||
|
||||
// Upgrade a specific apps
|
||||
|
||||
else
|
||||
{
|
||||
var confirm_message = y18n.t('confirm_update_specific_app', [what]);
|
||||
var api_url = '/upgrade/apps?app='+what;
|
||||
}
|
||||
|
||||
c.confirm(
|
||||
y18n.t('tools'),
|
||||
confirm_message,
|
||||
function(){
|
||||
c.api('PUT', api_url, {}, function(data) {
|
||||
c.redirect_to('#/tools/logs');
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Display journals list
|
||||
app.get('#/tools/logs', function (c) {
|
||||
c.api("/logs?limit=25&with_details", function(categories) {
|
||||
c.api('GET', "/logs?limit=25&with_details", {}, function(categories) {
|
||||
data = [];
|
||||
category_icons = {
|
||||
'operation': 'wrench',
|
||||
|
@ -137,153 +129,85 @@
|
|||
app.get(/\#\/tools\/logs\/(.*)(\?number=(\d+))?/, function (c) {
|
||||
var params = "?path=" + c.params["splat"][0];
|
||||
var number = (c.params["number"])?c.params["number"]:50;
|
||||
params += "&number=" + number;
|
||||
|
||||
c.api("/logs/display" + params, function(log) {
|
||||
params += "&filter_irrelevant&number=" + number;
|
||||
|
||||
c.api('GET', "/logs/display" + params, {}, function(log) {
|
||||
if ('metadata' in log) {
|
||||
if (!'env' in log.metadata && 'args' in log.metadata) {
|
||||
log.metadata.env = log.metadata.args
|
||||
}
|
||||
}
|
||||
|
||||
c.view('tools/tools_log', {
|
||||
"log": log,
|
||||
"next_number": log.logs.length == number ? number * 10:false,
|
||||
"locale": y18n.locale
|
||||
}, function() {
|
||||
log = $("#main #log").html();
|
||||
log = log.replace(/.*: ERROR - .*/g, function (match) { return '<span class="alert-danger">'+match+'</span>'});
|
||||
log = log.replace(/.*: WARNING - .*/g, function (match) { return '<span class="alert-warning">'+match+'</span>'});
|
||||
log = log.replace(/.*: SUCCESS - .*/g, function (match) { return '<span class="alert-success">'+match+'</span>'});
|
||||
log = log.replace(/.*: INFO - .*/g, function (match) { return '<span class="alert-info">'+match+'</span>'});
|
||||
$("#main #log").html(log);
|
||||
|
||||
// Configure behavior for the button to share log on Yunohost (it calls display --share)
|
||||
$('button[data-action="share"]').on("click", function() {
|
||||
c.api('GET', '/logs/display?path='+$(this).data('log-id')+'&share', {},
|
||||
function(data) {
|
||||
c.hideLoader();
|
||||
window.open(data.url, '_blank');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Download SSL Certificate Authority
|
||||
app.get('#/tools/ca', function (c) {
|
||||
c.view('tools/tools_ca');
|
||||
});
|
||||
|
||||
// Security feed
|
||||
app.get('#/tools/security-feed', function (c) {
|
||||
var 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]));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Reboot or shutdown button
|
||||
app.get('#/tools/reboot', function (c) {
|
||||
c.view('tools/tools_reboot');
|
||||
});
|
||||
c.view('tools/tools_reboot', {}, function() {
|
||||
// Configure reboot/shutdown buttons behavior
|
||||
$("button[data-action]").on("click", function() {
|
||||
var action = $(this).data("action");
|
||||
|
||||
// Reboot or shutdown actions
|
||||
app.get('#/tools/reboot/:action', function (c) {
|
||||
var action = c.params['action'].toLowerCase();
|
||||
if (action == 'reboot' || action == 'shutdown') {
|
||||
c.confirm(
|
||||
y18n.t('tools_' + action),
|
||||
// confirm_reboot_action_reboot or confirm_reboot_action_shutdown
|
||||
y18n.t('confirm_reboot_action_' + action),
|
||||
function(){
|
||||
c.api('/'+action+'?force', function(data) {
|
||||
// This code is not executed due to 502 response (reboot or shutdown)
|
||||
c.redirect('#/logout');
|
||||
}, 'PUT', {}, false, function (xhr) {
|
||||
c.flash('success', y18n.t('tools_' + action + '_done'))
|
||||
// Disconnect from the webadmin
|
||||
store.clear('url');
|
||||
store.clear('connected');
|
||||
store.set('path', '#/');
|
||||
c.confirm(
|
||||
y18n.t('tools_' + action),
|
||||
y18n.t('confirm_reboot_action_' + action),
|
||||
function(){
|
||||
c.api('PUT', '/'+action+'?force', {}, function(data) {
|
||||
// This code is not executed due to 502 response (reboot or shutdown)
|
||||
c.redirect_to('#/logout');
|
||||
}, function (xhr) {
|
||||
c.flash('success', y18n.t('tools_' + action + '_done'))
|
||||
// Disconnect from the webadmin
|
||||
store.clear('url');
|
||||
store.clear('connected');
|
||||
store.set('path', '#/');
|
||||
|
||||
// Rename the page to allow refresh without ask for rebooting
|
||||
window.location.href = window.location.href.split('#')[0] + '#/';
|
||||
// Display reboot or shutdown info
|
||||
// We can't use template because now the webserver is off
|
||||
if (action == 'reboot') {
|
||||
$('#main').replaceWith('<div id="main"><div class="alert alert-warning"><i class="fa-refresh"></i> ' + y18n.t('tools_rebooting') + '</div></div>');
|
||||
}
|
||||
else {
|
||||
$('#main').replaceWith('<div id="main"><div class="alert alert-warning"><i class="fa-power-off"></i> ' + y18n.t('tools_shuttingdown') + '</div></div>');
|
||||
}
|
||||
// Rename the page to allow refresh without ask for rebooting
|
||||
window.location.href = window.location.href.split('#')[0] + '#/';
|
||||
// Display reboot or shutdown info
|
||||
// We can't use template because now the webserver is off
|
||||
if (action == 'reboot') {
|
||||
$('#main').replaceWith('<div id="main"><div class="alert alert-warning"><i class="fa-refresh"></i> ' + y18n.t('tools_rebooting') + '</div></div>');
|
||||
}
|
||||
else {
|
||||
$('#main').replaceWith('<div id="main"><div class="alert alert-warning"><i class="fa-power-off"></i> ' + y18n.t('tools_shuttingdown') + '</div></div>');
|
||||
}
|
||||
|
||||
// Remove loader if any
|
||||
$('div.loader').remove();
|
||||
c.hideLoader();
|
||||
|
||||
// Force scrollTop on page load
|
||||
$('html, body').scrollTop(0);
|
||||
store.clear('slide');
|
||||
});
|
||||
|
||||
},
|
||||
function(){
|
||||
store.clear('slide');
|
||||
c.redirect('#/tools/reboot');
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
c.flash('fail', y18n.t('unknown_action', [action]));
|
||||
store.clear('slide');
|
||||
c.redirect('#/tools/reboot');
|
||||
}
|
||||
});
|
||||
|
||||
// Diagnosis
|
||||
app.get('#/tools/diagnosis(/:private)?', function (c) {
|
||||
// See http://sammyjs.org/docs/routes for splat documentation
|
||||
var private = (c.params.splat[0] == 'private');
|
||||
|
||||
var endurl = (private) ? '?private' : '';
|
||||
c.api('/diagnosis'+endurl, function(diagnosis) {
|
||||
c.view('tools/tools_diagnosis', {
|
||||
'diagnosis' : JSON.stringify(diagnosis, undefined, 4),
|
||||
'raw' : diagnosis,
|
||||
'private' : private
|
||||
// Force scrollTop on page load
|
||||
$('html, body').scrollTop(0);
|
||||
}, false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Reboot or shutdown button
|
||||
// Migrations
|
||||
app.get('#/tools/migrations', function (c) {
|
||||
c.api('/migrations?pending', function(pending_migrations) {
|
||||
c.api('/migrations?done', function(done_migrations) {
|
||||
c.api('GET', '/migrations?pending', {}, function(pending_migrations) {
|
||||
c.api('GET', '/migrations?done', {}, function(done_migrations) {
|
||||
pending_migrations = pending_migrations.migrations;
|
||||
done_migrations = done_migrations.migrations;
|
||||
|
||||
|
@ -302,148 +226,39 @@
|
|||
c.view('tools/tools_migrations', {
|
||||
'pending_migrations' : pending_migrations.reverse(),
|
||||
'done_migrations' : done_migrations.reverse()
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}, function() {
|
||||
|
||||
app.get('#/tools/migrations/run', function (c) {
|
||||
var disclaimerAcks = $(".disclaimer-ack");
|
||||
var withAcceptDisclaimerFlag = false;
|
||||
for (var i = 0 ; i < disclaimerAcks.length ; i++)
|
||||
{
|
||||
console.log($(disclaimerAcks[i]).find("input:checked").val());
|
||||
if (! $(disclaimerAcks[i]).find("input:checked").val())
|
||||
{
|
||||
// FIXME / TODO i18n
|
||||
c.flash('fail', "Some of these migrations require you to acknowledge a disclaimer before running them.");
|
||||
c.redirect('#/tools/migrations');
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
withAcceptDisclaimerFlag = true;
|
||||
}
|
||||
};
|
||||
// Configure button 'Run'
|
||||
$('button[data-action="run"]').on("click", function() {
|
||||
|
||||
// Not sure if necessary, but this distinction is to avoid accidentally
|
||||
// triggering a migration with a disclaimer if one goes to the
|
||||
// /tools/migrations/run page "directly" somehow ...
|
||||
if (withAcceptDisclaimerFlag)
|
||||
{
|
||||
c.api('/migrations/migrate?accept_disclaimer',
|
||||
function (data) {
|
||||
store.clear('slide');
|
||||
c.redirect('#/tools/migrations');
|
||||
}, 'POST')
|
||||
}
|
||||
else
|
||||
{
|
||||
c.api('/migrations/migrate',
|
||||
function (data) {
|
||||
store.clear('slide');
|
||||
c.redirect('#/tools/migrations');
|
||||
}, 'POST')
|
||||
}
|
||||
});
|
||||
var disclaimerAcks = $(".disclaimer-ack");
|
||||
for (var i = 0 ; i < disclaimerAcks.length ; i++)
|
||||
{
|
||||
if (! $(disclaimerAcks[i]).find("input:checked").val())
|
||||
{
|
||||
// FIXME / TODO i18n
|
||||
c.flash('fail', "Some of these migrations require you to acknowledge a disclaimer before running them.");
|
||||
c.refresh();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
app.get('#/tools/migrations/skip/:migration_id', function (c) {
|
||||
c.confirm(
|
||||
y18n.t('migrations'),
|
||||
y18n.t('confirm_migrations_skip'),
|
||||
function(){
|
||||
c.api('/migrations/migrate?skip&targets=' + c.params['migration_id'], function(data) {
|
||||
store.clear('slide');
|
||||
c.redirect('#/tools/migrations');
|
||||
}, 'POST');
|
||||
},
|
||||
function(){
|
||||
store.clear('slide');
|
||||
c.redirect('#/tools/migrations');
|
||||
}
|
||||
);
|
||||
});
|
||||
c.api('POST', '/migrations/migrate?accept_disclaimer', {}, function() { c.refresh(); });
|
||||
});
|
||||
|
||||
// List available apps lists
|
||||
app.get('#/tools/appslists', function (c) {
|
||||
c.api('/appslists', function(data) {
|
||||
list = [];
|
||||
$.each(data, function(listname, listinfo) {
|
||||
list.push({
|
||||
'name': listname,
|
||||
'url': listinfo['url'],
|
||||
'lastUpdate': listinfo['lastUpdate']
|
||||
// Configure buttons 'Skip'
|
||||
$('button[data-action="skip"]').on("click", function() {
|
||||
var migration_id = $(this).data("migration");
|
||||
c.confirm(
|
||||
y18n.t('migrations'),
|
||||
y18n.t('confirm_migrations_skip'),
|
||||
function(){
|
||||
c.api('POST', '/migrations/migrate?skip&targets=' + migration_id, {}, function() { c.refresh() });
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
c.view('tools/tools_appslists_list', {
|
||||
appslists: list
|
||||
});
|
||||
}, 'GET');
|
||||
});
|
||||
|
||||
// Add a new apps list
|
||||
app.post('#/tools/appslists', function (c) {
|
||||
list = {
|
||||
'name' : c.params['appslist_name'],
|
||||
'url' : c.params['appslist_url']
|
||||
}
|
||||
|
||||
c.api('/appslists', function(data) {
|
||||
store.clear('slide');
|
||||
c.redirect('#/tools/appslists/' + list.name);
|
||||
}, 'PUT', list);
|
||||
});
|
||||
|
||||
// Show appslist info and operations
|
||||
app.get('#/tools/appslists/:appslist', function (c) {
|
||||
c.api('/appslists', function(data) {
|
||||
if (typeof data[c.params['appslist']] !== 'undefined') {
|
||||
list = {
|
||||
'name' : c.params['appslist'],
|
||||
'url': data[c.params['appslist']]['url'],
|
||||
'lastUpdate': data[c.params['appslist']]['lastUpdate'],
|
||||
'removable' : (c.params['appslist'] !== 'yunohost') ? true : false // Do not remove default apps list
|
||||
};
|
||||
c.view('tools/tools_appslists_info', {appslist: list});
|
||||
}
|
||||
else {
|
||||
c.flash('warning', y18n.t('appslists_unknown_list', [c.params['appslist']]));
|
||||
store.clear('slide');
|
||||
c.redirect('#/tools/appslists');
|
||||
}
|
||||
}, 'GET');
|
||||
});
|
||||
|
||||
// Refresh available apps list
|
||||
app.get('#/tools/appslists/refresh', function (c) {
|
||||
c.api('/appslists', function(data) {
|
||||
// c.redirect(store.get('path'));
|
||||
c.redirect('#/apps/install');
|
||||
}, 'PUT');
|
||||
});
|
||||
|
||||
// Refresh specific apps list
|
||||
app.get('#/tools/appslists/:appslist/refresh', function (c) {
|
||||
c.api('/appslists', function(data) {
|
||||
c.redirect('#/tools/appslists');
|
||||
}, 'PUT', {'name' : c.params['appslist']});
|
||||
});
|
||||
|
||||
// Remove apps list
|
||||
app.get('#/tools/appslists/:appslist/remove', function (c) {
|
||||
c.confirm(
|
||||
y18n.t('appslist'),
|
||||
y18n.t('appslists_confirm_remove', [c.params['app']]),
|
||||
function() {
|
||||
c.api('/appslists', function() {
|
||||
c.redirect('#/tools/appslists');
|
||||
}, 'DELETE', {'name' : c.params['appslist']});
|
||||
},
|
||||
function() {
|
||||
store.clear('slide');
|
||||
c.redirect('#/tools/appslists/'+ c.params['appslist']);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -31,20 +31,20 @@
|
|||
* @param.item Name of the user or the permission to add or remove
|
||||
* @param.group Name of the group affected
|
||||
*
|
||||
* This function is built to be apply with params generated by the use of
|
||||
* This function is built to be apply with params generated by the use of
|
||||
* HTML dataset attributes (e.g. link in the partial inline view "label" in group_list.ms)
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
function updateGroup(model, params) {
|
||||
var type = params.type;
|
||||
var operation = params.operation;
|
||||
var action = params.action;
|
||||
var item = params.item;
|
||||
var groupname = params.group;
|
||||
var group = data.groups[groupname];
|
||||
var to = (operation == 'add')?group[type]:group[type + 'Inv'];
|
||||
var from = (operation == 'add')?group[type+'Inv']:group[type];
|
||||
// Do nothing, if array of destination already contains the item
|
||||
var to = (action == 'add')?group[type]:group[type + 'Inv'];
|
||||
var from = (action == 'add')?group[type+'Inv']:group[type];
|
||||
// Do nothing, if array of destination already contains the item
|
||||
if (from.indexOf(item) === -1) return;
|
||||
|
||||
// Hack to disable pacman loader if any
|
||||
|
@ -52,22 +52,22 @@
|
|||
$('#main').append('<div class="loader loader-content" style="display: none"></div>');
|
||||
}
|
||||
$('div.loader').css('display', 'none');
|
||||
|
||||
|
||||
// Update group
|
||||
var params = {}; var url;
|
||||
if (type == 'members') {
|
||||
url = '/users/groups/' + groupname;
|
||||
params[operation] = [item];
|
||||
params[action] = [item];
|
||||
}
|
||||
else {
|
||||
url = '/users/permissions/' + item;
|
||||
params[operation] = [groupname];
|
||||
params[action] = [groupname];
|
||||
}
|
||||
c.api(url, function(data_update) {
|
||||
c.api('PUT', url, params, function(data_update) {
|
||||
to.push(item);
|
||||
from.splice(from.indexOf(item), 1);
|
||||
updateView(data);
|
||||
}, 'PUT', params);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,29 +85,41 @@
|
|||
model.groups[group].members.sort();
|
||||
model.groups[group].membersInv.sort();
|
||||
}
|
||||
|
||||
// Manual render, we don't use c.view to avoid scrollTop and other
|
||||
|
||||
// Manual render, we don't use c.view to avoid scrollTop and other
|
||||
// uneeded behaviour
|
||||
var rendered = c.render('views/user/group_list.ms', model);
|
||||
rendered.swap(function () {
|
||||
// Add click event to get a nice "reactive" interface
|
||||
jQuery(".group-update").on('click', function (e) {
|
||||
updateGroup(model, jQuery(this)[0].dataset);
|
||||
$("button[data-action='add'], button[data-action='remove']").on('click', function (e) {
|
||||
updateGroup(model, $(this)[0].dataset);
|
||||
return false;
|
||||
});
|
||||
jQuery(".group-add-user").on('click', function (e) {
|
||||
data.groups[$(this)[0].dataset.user].display = true;
|
||||
$('button[data-action="add-user-specific-permission"]').on('click', function (e) {
|
||||
data.groups[$(this).data("user")].display = true;
|
||||
updateView(data);
|
||||
return false;
|
||||
});
|
||||
$('button[data-action="delete-group"]').on('click', function (e) {
|
||||
|
||||
var group = $(this).data("group");
|
||||
|
||||
c.confirm(
|
||||
y18n.t('groups'),
|
||||
$('<div>'+ y18n.t('confirm_delete', [group]) +'</div>'),
|
||||
function() {
|
||||
c.api('DELETE', '/users/groups/'+ group, {}, function(data) { c.refresh(); });
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
app.get('#/groups', function (c) {
|
||||
c.api('/users/groups?full&include_primary_groups', function(data_groups) {
|
||||
c.api('/users', function(data_users) {
|
||||
c.api('/users/permissions?short', function(data_permissions) {
|
||||
c.api('GET', '/users/groups?full&include_primary_groups', {}, function(data_groups) {
|
||||
c.api('GET', '/users', {}, function(data_users) {
|
||||
c.api('GET', '/users/permissions?short', {}, function(data_permissions) {
|
||||
//var perms = data_permissions.permissions;
|
||||
var specific_perms = {};
|
||||
var all_perms = data_permissions.permissions;
|
||||
|
@ -129,7 +141,7 @@
|
|||
// Declare all_users and visitors has special
|
||||
data_groups.groups['all_users'].special = true;
|
||||
data_groups.groups['visitors'].special = true;
|
||||
|
||||
|
||||
// Data given to the view with 2 functions to convert technical
|
||||
// permission id to display names
|
||||
data = {
|
||||
|
@ -166,33 +178,9 @@
|
|||
|
||||
app.post('#/groups/create', function (c) {
|
||||
c.params['groupname'] = c.params['groupname'].replace(' ', '_').toLowerCase();
|
||||
c.api('/users/groups', function(data) {
|
||||
c.redirect('#/groups');
|
||||
}, 'POST', c.params.toHash());
|
||||
});
|
||||
|
||||
app.get('#/groups/:group/delete', function (c) {
|
||||
|
||||
var params = {};
|
||||
|
||||
// make confirm content
|
||||
var confirmModalContent = $('<div>'+ y18n.t('confirm_delete', [c.params['group']]) +'</div>');
|
||||
|
||||
// display confirm modal
|
||||
c.confirm(
|
||||
y18n.t('groups'),
|
||||
confirmModalContent,
|
||||
function(){
|
||||
c.api('/users/groups/'+ c.params['group'], function(data) {
|
||||
c.redirect('#/groups');
|
||||
}, 'DELETE', params);
|
||||
},
|
||||
function(){
|
||||
//store.clear('slide');
|
||||
c.redirect('#/groups');
|
||||
}
|
||||
);
|
||||
|
||||
c.api('POST', '/users/groups', c.params.toHash(), function(data) {
|
||||
c.redirect_to('#/groups');
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -202,14 +190,14 @@
|
|||
|
||||
// List existing users
|
||||
app.get('#/users', function (c) {
|
||||
c.api('/users', function(data) { // http://api.yunohost.org/#!/user/user_list_get_3
|
||||
c.api('GET', '/users', {}, function(data) {
|
||||
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.api('GET', '/domains', {}, function(data) {
|
||||
|
||||
// Password min length
|
||||
data.password_min_length = PASSWORD_MIN_LENGTH;
|
||||
|
@ -229,8 +217,7 @@
|
|||
app.post('#/users/create', function (c) {
|
||||
if (c.params['password'] == c.params['confirmation']) {
|
||||
if (c.params['password'].length < PASSWORD_MIN_LENGTH) {
|
||||
c.flash('fail', y18n.t('password_too_short'));
|
||||
store.clear('slide');
|
||||
c.flash('fail', y18n.t('passwords_too_short'));
|
||||
}
|
||||
else {
|
||||
// Force unit or disable quota
|
||||
|
@ -242,28 +229,63 @@
|
|||
// Compute email field
|
||||
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());
|
||||
c.api('POST', '/users', c.params.toHash(), function(data) {
|
||||
c.redirect_to('#/users');
|
||||
});
|
||||
}
|
||||
} 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);
|
||||
c.api('GET', '/users/'+ c.params['user'], {}, function(data) {
|
||||
c.view('user/user_info', data, function() {
|
||||
|
||||
// Configure delete button behavior
|
||||
$('button[data-action="delete"]').on("click", function() {
|
||||
var user = $(this).data("user");
|
||||
|
||||
var params = {};
|
||||
|
||||
// make confirm content
|
||||
var purgeCheckbox = '<div><input type="checkbox" id="purge-user-data" name="purge-user-data"> <label for="purge-user-data">'+ y18n.t('purge_user_data_checkbox', [user]) +'</label></div>';
|
||||
var purgeAlertMessage = '<div class="danger" style="display: none">⚠ '+ y18n.t('purge_user_data_warning') +'</div>';
|
||||
var confirmModalContent = $('<div>'+ y18n.t('confirm_delete', [user]) +'<br><br>'+ purgeCheckbox +'<br>'+ purgeAlertMessage +'</div>');
|
||||
|
||||
// display confirm modal
|
||||
c.confirm(
|
||||
y18n.t('users'),
|
||||
confirmModalContent,
|
||||
function(){
|
||||
c.api('DELETE', '/users/'+ user, params, function(data) {
|
||||
c.redirect_to('#/users');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// toggle purge warning and parameter
|
||||
confirmModalContent.find("input").click(function(){
|
||||
|
||||
if (confirmModalContent.find("input").is(':checked')) {
|
||||
params.purge = "";
|
||||
confirmModalContent.find(".danger").show();
|
||||
}
|
||||
else {
|
||||
delete params.purge;
|
||||
confirmModalContent.find(".danger").hide();
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 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
|
||||
c.api('GET', '/users/'+ c.params['user'], {}, function(data) {
|
||||
c.api('GET', '/domains', {}, function(dataDomains) {
|
||||
|
||||
// Password min length
|
||||
data.password_min_length = PASSWORD_MIN_LENGTH;
|
||||
|
@ -314,7 +336,7 @@
|
|||
// Update user information
|
||||
app.put('#/users/:user', function (c) {
|
||||
// Get full user object
|
||||
c.api('/users/'+ c.params['user'], function(user) {
|
||||
c.api('GET', '/users/'+ c.params['user'], {}, function(user) {
|
||||
// Force unit or disable quota
|
||||
if (c.params['mailbox_quota']) {
|
||||
c.params['mailbox_quota'] += "M";
|
||||
|
@ -353,79 +375,32 @@
|
|||
|
||||
if ($.isEmptyObject(params)) {
|
||||
c.flash('fail', y18n.t('error_modify_something'));
|
||||
store.clear('slide');
|
||||
c.redirect('#/users/'+ c.params['user'] + '/edit');
|
||||
c.redirect_to('#/users/'+ c.params['user'] + '/edit', {slide: false});
|
||||
} else {
|
||||
if (params['password']) {
|
||||
if (params['password'] == params['confirmation']) {
|
||||
if (params['password'].length < PASSWORD_MIN_LENGTH) {
|
||||
c.flash('fail', y18n.t('password_too_short'));
|
||||
store.clear('slide');
|
||||
c.redirect('#/users/'+ c.params['user'] + '/edit');
|
||||
c.flash('fail', y18n.t('passwords_too_short'));
|
||||
c.redirect_to('#/users/'+ c.params['user'] + '/edit', {slide: false});
|
||||
}
|
||||
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);
|
||||
c.api('PUT', '/users/'+ c.params['user'], params, function(data) {
|
||||
c.redirect_to('#/users/'+ c.params['user']);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
c.flash('fail', y18n.t('passwords_dont_match'));
|
||||
store.clear('slide');
|
||||
c.redirect('#/users/'+ c.params['user'] + '/edit');
|
||||
c.redirect_to('#/users/'+ c.params['user'] + '/edit', {slide: false});
|
||||
}
|
||||
}
|
||||
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);
|
||||
c.api('PUT', '/users/'+ c.params['user'], params, function(data) {
|
||||
c.redirect_to('#/users/'+ c.params['user']);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 'GET');
|
||||
});
|
||||
|
||||
// Remove existing user
|
||||
app.get('#/users/:user/delete', function (c) {
|
||||
|
||||
var params = {};
|
||||
|
||||
// make confirm content
|
||||
var purgeCheckbox = '<div><input type="checkbox" id="purge-user-data" name="purge-user-data"> <label for="purge-user-data">'+ y18n.t('purge_user_data_checkbox', [c.params['user']]) +'</label></div>';
|
||||
var purgeAlertMessage = '<div class="danger" style="display: none">⚠ '+ y18n.t('purge_user_data_warning') +'</div>';
|
||||
var confirmModalContent = $('<div>'+ y18n.t('confirm_delete', [c.params['user']]) +'<br><br>'+ purgeCheckbox +'<br>'+ purgeAlertMessage +'</div>');
|
||||
|
||||
// display confirm modal
|
||||
c.confirm(
|
||||
y18n.t('users'),
|
||||
confirmModalContent,
|
||||
function(){
|
||||
c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_delete_delete_4
|
||||
c.redirect('#/users');
|
||||
}, 'DELETE', params);
|
||||
},
|
||||
function(){
|
||||
store.clear('slide');
|
||||
c.redirect('#/users/'+ c.params['user']);
|
||||
}
|
||||
);
|
||||
|
||||
// toggle purge warning and parameter
|
||||
confirmModalContent.find("input").click(function(){
|
||||
|
||||
if (confirmModalContent.find("input").is(':checked')) {
|
||||
params.purge = "";
|
||||
confirmModalContent.find(".warning").show();
|
||||
}
|
||||
else {
|
||||
delete params.purge;
|
||||
confirmModalContent.find(".warning").hide();
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
})();
|
||||
|
|
|
@ -8,82 +8,15 @@
|
|||
*
|
||||
*/
|
||||
app.bind('login', function(e, data) {
|
||||
c.api('/users', function(data) {
|
||||
c.api('GET', '/users', {}, function(data) {
|
||||
// Warn admin if no users are created.
|
||||
if (typeof data.users !== 'undefined' && data.users.length === 0) {
|
||||
c.flash('warning', y18n.t('warning_first_user'));
|
||||
}
|
||||
|
||||
/*
|
||||
* Disabling this for now because there's a duplicate Access Allow
|
||||
* Origin header thing preventing it from working and people keep
|
||||
* complaining about it and we havent effectively used this in 2
|
||||
* years anyway despite various security issues.
|
||||
*
|
||||
*
|
||||
// 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
|
||||
var viewedItems = Cookies.get('ynhSecurityViewedItems') || [];
|
||||
|
||||
// Get 6 month earlier date
|
||||
var SixMonthEarlier = new Date();
|
||||
SixMonthEarlier.setMonth(SixMonthEarlier.getMonth() - 6);
|
||||
|
||||
// Loop through items in a reverse order (older first)
|
||||
$($('item', xml).get().reverse()).each(function(k, v) {
|
||||
var link = $('link', v).text();
|
||||
if (typeof link == 'string' && link !== '' && link.charAt(0) == '/') {
|
||||
link = forumUrl+link;
|
||||
}
|
||||
|
||||
// var description=$('description', v).text();
|
||||
// description=description.replace('href="/','href="'+forumUrl+'/');
|
||||
|
||||
var item = {
|
||||
guid: $('guid', v).text(),
|
||||
title: $('title', v).text(),
|
||||
url: link,
|
||||
// desc: description,
|
||||
date: new Date($('pubDate', v).text()),
|
||||
};
|
||||
|
||||
// If item is not already viewed and is not older than 6 month
|
||||
if (viewedItems.indexOf(item.guid) === -1 && (item.date.getTime() > SixMonthEarlier.getTime())) {
|
||||
// Show security message to administrator
|
||||
var warning = item.title + ' - ' +
|
||||
item.date.toISOString().substring(0, 10) +
|
||||
' (<a href="'+ item.url +'" class="alert-link" target="_blank">'+y18n.t('read_more')+'</a>)';
|
||||
c.flash('warning', warning);
|
||||
// Store viewed item
|
||||
viewedItems.push(item.guid);
|
||||
}
|
||||
});
|
||||
// Saved viewed items to cookie
|
||||
Cookies.set('ynhSecurityViewedItems', viewedItems, {
|
||||
expires: 7
|
||||
});
|
||||
})
|
||||
.fail(function(stuff) {
|
||||
c.flash('fail', y18n.t('error_retrieve_feed', [securityFeed]));
|
||||
});
|
||||
*/
|
||||
|
||||
c.api("/diagnosis", function(data) {
|
||||
versions = data.packages;
|
||||
$('#yunohost-version').html(y18n.t('footer_version', [versions.yunohost.version, versions.yunohost.repo]));
|
||||
if (data.security["CVE-2017-5754"].vulnerable) {
|
||||
c.flash('danger', y18n.t('meltdown'));
|
||||
}
|
||||
$('div.loader').remove();
|
||||
c.api('GET', '/versions', {}, function(data) {
|
||||
$('#yunohost-version').html(y18n.t('footer_version', [data.yunohost.version, data.yunohost.repo]));
|
||||
c.hideLoader();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
function prefetchDomains(req) {
|
||||
// Preload domains list.
|
||||
req.params.domains = [];
|
||||
req.api('/domains', function(data) {
|
||||
req.api('GET', '/domains', {}, function(data) {
|
||||
req.params.domains = data.domains;
|
||||
});
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
|||
function prefetchUsers(req){
|
||||
// Preload users lists.
|
||||
req.params.users = [];
|
||||
req.api('/users', function(data) {
|
||||
req.api('GET', '/users', {}, function(data) {
|
||||
req.params.users = data.users;
|
||||
});
|
||||
}
|
||||
|
@ -28,6 +28,8 @@
|
|||
app.before(/domains\/add/, prefetchDomains);
|
||||
app.before(/apps\/install\//, prefetchDomains);
|
||||
app.before(/apps\/install\//, prefetchUsers);
|
||||
app.before(/apps\/install\/custom\//, prefetchDomains);
|
||||
app.before(/apps\/install\/custom\//, prefetchUsers);
|
||||
app.before(/apps\/\w+\/actions/, prefetchUsers);
|
||||
app.before(/apps\/\w+\/actions/, prefetchDomains);
|
||||
app.before(/apps\/\w+\/config-panel/, prefetchUsers);
|
||||
|
|
|
@ -3,20 +3,64 @@
|
|||
var app = Sammy.apps['#main'];
|
||||
var store = app.store;
|
||||
|
||||
// The logic used to temporily disable transition is from
|
||||
// https://stackoverflow.com/a/16575811
|
||||
function whichTransitionEvent(){
|
||||
var t;
|
||||
var el = document.createElement('fakeelement');
|
||||
var transitions = {
|
||||
'transition':'transitionend',
|
||||
'OTransition':'oTransitionEnd',
|
||||
'MozTransition':'transitionend',
|
||||
'WebkitTransition':'webkitTransitionEnd'
|
||||
}
|
||||
|
||||
for(t in transitions){
|
||||
if( el.style[t] !== undefined ){
|
||||
return transitions[t];
|
||||
}
|
||||
}
|
||||
};
|
||||
var transitionEvent = whichTransitionEvent();
|
||||
|
||||
function resetSliders()
|
||||
{
|
||||
// Disable transition effects
|
||||
$('#slider-container').addClass('notransition');
|
||||
// Delete the left/right temporary stuff only used during animation
|
||||
$('#slideTo').css('display', 'none');
|
||||
$('#slideTo').html("");
|
||||
$('#slideBack').css('display', 'none');
|
||||
$('#slideBack').html("");
|
||||
// Set the margin-left back to 0
|
||||
$('#slider-container').css('margin-left', '0');
|
||||
// c.f. the stackoverflow thread
|
||||
$('#slider-container')[0].offsetHeight;
|
||||
// Remove the binding to this event handler for next times
|
||||
// Re-enable transition effects
|
||||
$('#slider-container').removeClass('notransition');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]));
|
||||
//
|
||||
// Pacman loader management
|
||||
//
|
||||
|
||||
showLoader: function() {
|
||||
app.loaded = false; // Not sure if that's really useful ... this is from old code with no explanation what it really does ...
|
||||
if ($('div.loader').length === 0) {
|
||||
$('#main').append('<div class="loader loader-content"></div>');
|
||||
}
|
||||
return str.join("&");
|
||||
},
|
||||
|
||||
hideLoader: function() {
|
||||
app.loaded = true; // Not sure if that's really useful ... this is from old code with no explanation what it really does ...
|
||||
$('div.loader').remove();
|
||||
},
|
||||
|
||||
// Flash helper to diplay instant notifications
|
||||
|
@ -85,7 +129,7 @@
|
|||
},
|
||||
|
||||
// API call
|
||||
api: function(uri, callback, method, data, websocket, callbackOnFailure) {
|
||||
api: function(method, uri, data, callback, callbackOnFailure, websocket) {
|
||||
c = this;
|
||||
|
||||
method = typeof method !== 'undefined' ? method : 'GET';
|
||||
|
@ -93,96 +137,69 @@
|
|||
if (window.navigator && window.navigator.language && (typeof data.locale === 'undefined')) {
|
||||
data.locale = y18n.locale || window.navigator.language.substr(0, 2);
|
||||
}
|
||||
app.loaded = false;
|
||||
if ($('div.loader').length === 0) {
|
||||
$('#main').append('<div class="loader loader-content"></div>');
|
||||
}
|
||||
|
||||
c.showLoader();
|
||||
|
||||
call = function(uri, callback, method, data, callbackOnFailure) {
|
||||
|
||||
var args = data;
|
||||
// TODO: change this code
|
||||
if (uri === '/postinstall') {
|
||||
var post_installing = false;
|
||||
setInterval(function () {
|
||||
post_installing = true;
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
// Define default callback for failures
|
||||
if (typeof callbackOnFailure !== 'function') {
|
||||
callbackOnFailure = function(xhr) {
|
||||
// Postinstall is a custom case, we have to wait that
|
||||
// operation is done before doing anything
|
||||
if ((uri === '/postinstall') && (post_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);
|
||||
if (xhr.status == 200) {
|
||||
// Fail with 200, WTF
|
||||
callback({});
|
||||
}
|
||||
// Regular errors
|
||||
// Unauthorized or wrong password
|
||||
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');
|
||||
}
|
||||
}
|
||||
// 500
|
||||
else if (xhr.status == 500) {
|
||||
try {
|
||||
error_log = JSON.parse(xhr.responseText);
|
||||
error_log.route = error_log.route.join(' ') + '\n';
|
||||
error_log.arguments = JSON.stringify(error_log.arguments);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
error_log = {};
|
||||
error_log.route = "Failed to parse route";
|
||||
error_log.arguments = "Failed to parse arguments";
|
||||
error_log.traceback = xhr.responseText;
|
||||
}
|
||||
c.flash('fail', y18n.t('internal_exception', [error_log.route, error_log.arguments, error_log.traceback]));
|
||||
}
|
||||
// 502 Bad gateway means API is down
|
||||
else if (xhr.status == 502) {
|
||||
c.flash('fail', y18n.t('api_not_responding'));
|
||||
}
|
||||
// More verbose error messages first
|
||||
else if (typeof xhr.responseText !== 'undefined') {
|
||||
c.flash('fail', xhr.responseText);
|
||||
}
|
||||
// 0 mean "the connexion has been closed" apparently
|
||||
else if (xhr.status == 0) {
|
||||
var errorMessage = xhr.status+' '+xhr.statusText;
|
||||
c.flash('fail', y18n.t('error_connection_interrupted', [errorMessage]));
|
||||
console.log(xhr);
|
||||
}
|
||||
// Return HTTP error code at least
|
||||
else {
|
||||
if (xhr.status == 200) {
|
||||
// Fail with 200, WTF
|
||||
callback({});
|
||||
}
|
||||
// Unauthorized or wrong password
|
||||
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');
|
||||
}
|
||||
}
|
||||
// 500
|
||||
else if (xhr.status == 500) {
|
||||
try {
|
||||
error_log = JSON.parse(xhr.responseText);
|
||||
error_log.route = error_log.route.join(' ') + '\n';
|
||||
error_log.arguments = JSON.stringify(error_log.arguments);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
error_log = {};
|
||||
error_log.route = "Failed to parse route";
|
||||
error_log.arguments = "Failed to parse arguments";
|
||||
error_log.traceback = xhr.responseText;
|
||||
}
|
||||
c.flash('fail', y18n.t('internal_exception', [error_log.route, error_log.arguments, error_log.traceback]));
|
||||
}
|
||||
// 502 Bad gateway means API is down
|
||||
else if (xhr.status == 502) {
|
||||
c.flash('fail', y18n.t('api_not_responding'));
|
||||
}
|
||||
// More verbose error messages first
|
||||
else if (typeof xhr.responseText !== 'undefined') {
|
||||
c.flash('fail', xhr.responseText);
|
||||
}
|
||||
// 0 mean "the connexion has been closed" apparently
|
||||
else if (xhr.status == 0) {
|
||||
var errorMessage = xhr.status+' '+xhr.statusText;
|
||||
c.flash('fail', y18n.t('error_connection_interrupted', [errorMessage]));
|
||||
console.log(xhr);
|
||||
}
|
||||
// Return HTTP error code at least
|
||||
else {
|
||||
var errorMessage = xhr.status+' '+xhr.statusText;
|
||||
c.flash('fail', y18n.t('error_server_unexpected', [errorMessage]));
|
||||
console.log(xhr);
|
||||
}
|
||||
|
||||
// Remove loader if any
|
||||
$('div.loader').remove();
|
||||
|
||||
// Force scrollTop on page load
|
||||
$('html, body').scrollTop(0);
|
||||
store.clear('slide');
|
||||
var errorMessage = xhr.status+' '+xhr.statusText;
|
||||
c.flash('fail', y18n.t('error_server_unexpected', [errorMessage]));
|
||||
console.log(xhr);
|
||||
}
|
||||
|
||||
c.hideLoader();
|
||||
|
||||
// Force scrollTop on page load
|
||||
$('html, body').scrollTop(0);
|
||||
store.clear('slide');
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -237,78 +254,17 @@
|
|||
|
||||
},
|
||||
|
||||
// Render view (cross-browser)
|
||||
view: function (view, data, callback, enableSlide) {
|
||||
|
||||
// Ask confirmation to the user through the modal window
|
||||
confirm: function(title, content, confirmCallback, cancelCallback) {
|
||||
c = this;
|
||||
|
||||
// Default
|
||||
callback = typeof callback !== 'undefined' ? callback : function() {};
|
||||
enableSlide = (typeof enableSlide !== 'undefined') ? enableSlide : true; // Change to false to disable animation
|
||||
|
||||
app.loaded = true;
|
||||
|
||||
// Hide loader and modal
|
||||
$('div.loader').remove();
|
||||
$('#modal').modal('hide');
|
||||
|
||||
// Render content
|
||||
var rendered = this.render('views/'+ view +'.ms', data);
|
||||
|
||||
// Update content helper
|
||||
var leSwap = function() {
|
||||
rendered.swap(function() {
|
||||
// Slide direction
|
||||
if (enableSlide) {
|
||||
$('.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');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Paste <pre> helper
|
||||
c.prePaste();
|
||||
|
||||
// Run callback
|
||||
callback();
|
||||
|
||||
// Force scrollTop on page load
|
||||
$('html, body').scrollTop(0);
|
||||
});
|
||||
};
|
||||
|
||||
// Slide back effect
|
||||
if (enableSlide && store.get('slide') == 'back') {
|
||||
store.clear('slide');
|
||||
$('#slideBack').css('display', 'none');
|
||||
$('#slider-container').css('margin-left', '-100%');
|
||||
$('#slideTo').show().html($('#main').html());
|
||||
leSwap();
|
||||
$('#slider-container').css('margin-left', '0px');
|
||||
}
|
||||
// Slide to effect
|
||||
else if (enableSlide && store.get('slide') == 'to') {
|
||||
store.clear('slide');
|
||||
$('#slideTo').css('display', 'none');
|
||||
$('#slider-container').css('margin-left', '0px');
|
||||
$('#slideBack').show().html($('#main').html());
|
||||
leSwap();
|
||||
$('#slider-container').css('margin-left', '-100%');
|
||||
}
|
||||
// No slideing effect
|
||||
else {
|
||||
leSwap();
|
||||
}
|
||||
},
|
||||
|
||||
confirm: function(title, content, confirmCallback, cancelCallback) {
|
||||
// Default callbacks
|
||||
confirmCallback = typeof confirmCallback !== 'undefined' ? confirmCallback : function() {};
|
||||
cancelCallback = typeof cancelCallback !== 'undefined' ? cancelCallback : function() {};
|
||||
|
||||
c.hideLoader();
|
||||
|
||||
// Get modal element
|
||||
var box = $('#modal');
|
||||
|
||||
|
@ -335,12 +291,10 @@
|
|||
|
||||
$('#modal footer button').unbind( "click" );
|
||||
// Reset & Hide modal
|
||||
box
|
||||
.removeClass('no-title')
|
||||
.modal('hide');
|
||||
box.removeClass('no-title').modal('hide');
|
||||
|
||||
// Do corresponding callback
|
||||
if ($(this).data('action') == 'confirm') {
|
||||
if ($(this).data('modal-action') == 'confirm') {
|
||||
confirmCallback();
|
||||
}
|
||||
else {
|
||||
|
@ -352,19 +306,162 @@
|
|||
return box.modal('show');
|
||||
},
|
||||
|
||||
selectAllOrNone: function () {
|
||||
// Remove active style from buttons
|
||||
$(".select_all-none input").click(function(){ $(this).toggleClass("active"); });
|
||||
// Select all checkbox in this panel
|
||||
$(".select_all").click(function(){
|
||||
$(this).parents(".panel").children(".list-group").find("input").prop("checked", true);
|
||||
});
|
||||
// Deselect all checkbox in this panel
|
||||
$(".select_none").click(function(){
|
||||
$(this).parents(".panel").children(".list-group").find("input").prop("checked", false);
|
||||
});
|
||||
|
||||
// Render view (cross-browser)
|
||||
view: function (view, data, callback) {
|
||||
c = this;
|
||||
|
||||
// Default
|
||||
callback = typeof callback !== 'undefined' ? callback : function() {};
|
||||
|
||||
// Hide loader and modal
|
||||
c.hideLoader();
|
||||
$('#modal').modal('hide');
|
||||
|
||||
// Render content
|
||||
var rendered = this.render('views/'+ view +'.ms', data);
|
||||
|
||||
// Update content helper
|
||||
var leSwap = function() {
|
||||
rendered.swap(function() {
|
||||
// Clicking on those kind of CSS elements will trigger a
|
||||
// slide effect i.e. the next view rendering will have
|
||||
// store.get('slide') set to 'back' or 'to'
|
||||
$('.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');
|
||||
}
|
||||
});
|
||||
|
||||
// Force scrollTop on page load
|
||||
$('html, body').scrollTop(0);
|
||||
|
||||
// Run callback
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
// Slide back effect
|
||||
if (store.get('slide') == 'back') {
|
||||
|
||||
store.clear('slide');
|
||||
// Disable transition while we tweak CSS
|
||||
$('#slider-container').addClass('notransition');
|
||||
// "Delete" the left part of the slider
|
||||
$('#slideBack').css('display', 'none');
|
||||
|
||||
// Push the slider to the left
|
||||
$('#slider-container').css('margin-left', '-100%');
|
||||
// slideTo is the right part, and should contain the old view,
|
||||
// so we copypasta what's in the "center" slider (#main)
|
||||
$('#slideTo').show().html($('#main').html());
|
||||
// leSwap will put the new view in the "center" slider (#main)
|
||||
leSwap();
|
||||
|
||||
// So now things look like:
|
||||
// | |
|
||||
// | the screen |
|
||||
// | |
|
||||
//
|
||||
// . #main . #slideTo .
|
||||
// . the new view . the old view .
|
||||
// ^ ^
|
||||
// margin-left: -100% currently shown
|
||||
//
|
||||
// =====>>> sliiiiide =====>>>
|
||||
|
||||
// Re-add transition effect
|
||||
$('#slider-container').removeClass('notransition');
|
||||
|
||||
// add the transition event to detect the end of the transition effect
|
||||
transitionEvent
|
||||
&& $("#slider-container").off(transitionEvent)
|
||||
&& $("#slider-container").on(transitionEvent, resetSliders);
|
||||
|
||||
// And actually play the transition effect that will move the container from left to right
|
||||
$('#slider-container').css('margin-left', '0px');
|
||||
}
|
||||
// Slide to effect
|
||||
else if (store.get('slide') == 'to') {
|
||||
|
||||
// Disable transition while we tweak CSS
|
||||
$('#slider-container').addClass('notransition');
|
||||
// "Delete" the right part of the slider
|
||||
$('#slideTo').css('display', 'none');
|
||||
// Push the slider to the right
|
||||
$('#slider-container').css('margin-left', '0px');
|
||||
// slideBack should contain the old view,
|
||||
// so we copypasta what's in the "center" slider (#main)
|
||||
$('#slideBack').show().html($('#main').html());
|
||||
leSwap();
|
||||
|
||||
// So now things look like:
|
||||
//
|
||||
// | |
|
||||
// | the screen |
|
||||
// | |
|
||||
//
|
||||
// . . #slideBack . #main .
|
||||
// . . the old view . the new view .
|
||||
// ^ ^ ^
|
||||
// margin-left: -100% currently shown
|
||||
//
|
||||
// <<<===== sliiiiide <<<=======
|
||||
|
||||
|
||||
// Re-add transition effect
|
||||
$('#slider-container').removeClass('notransition');
|
||||
|
||||
// add the transition event to detect the end of the transition effect
|
||||
var transitionEvent = whichTransitionEvent();
|
||||
transitionEvent
|
||||
&& $("#slider-container").off(transitionEvent)
|
||||
&& $("#slider-container").on(transitionEvent, resetSliders);
|
||||
|
||||
// And actually play the transition effect that will move the container from right to left
|
||||
$('#slider-container').css('margin-left', '-100%');
|
||||
}
|
||||
// No slideing effect
|
||||
else {
|
||||
leSwap();
|
||||
}
|
||||
},
|
||||
|
||||
redirect_to: function(destination, options) {
|
||||
c = this;
|
||||
|
||||
options = options !== undefined ? options : {};
|
||||
|
||||
// If destination if the same as current url,
|
||||
// we don't want to display the slide animation
|
||||
// (or if the code explicitly state to disable slide animation)
|
||||
if ((c.path.split("#")[1] == destination.split("#")[1]) || (options.slide == false))
|
||||
{
|
||||
store.clear('slide');
|
||||
}
|
||||
|
||||
// This is a copy-pasta of some of the redirect/refresh code of
|
||||
// sammy.js because for some reason calling the original
|
||||
// redirect/refresh function in some context does not work >.>
|
||||
// (e.g. if you're already on the page)
|
||||
c.trigger('redirect', {to: destination});
|
||||
c.app.last_location = c.path;
|
||||
c.app.setLocation(destination);
|
||||
c.app.trigger('location-changed');
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
c = this;
|
||||
c.redirect_to(c.path, {slide: false});
|
||||
},
|
||||
|
||||
//
|
||||
// Array / object helpers
|
||||
//
|
||||
|
||||
arraySortById: function(arr) {
|
||||
arr.sort(function(a, b){
|
||||
if (a.id > b.id) {
|
||||
|
@ -385,103 +482,15 @@
|
|||
});
|
||||
},
|
||||
|
||||
groupHooks: function(hooks, raw_infos){
|
||||
var data = {};
|
||||
var rules = [
|
||||
{
|
||||
id:'configuration',
|
||||
isIn:function (hook) {
|
||||
return hook.indexOf('conf_')==0
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
$.each(hooks, function(i, hook) {
|
||||
var group_id=hook;
|
||||
var hook_size=(raw_infos && raw_infos[hook] && raw_infos[hook].size)?raw_infos[hook].size:0;
|
||||
$.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),
|
||||
size:data[group_id].size + hook_size
|
||||
};
|
||||
}
|
||||
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),
|
||||
size:hook_size
|
||||
};
|
||||
}
|
||||
});
|
||||
return data;
|
||||
},
|
||||
|
||||
ungroupHooks: function(system_parts,apps) {
|
||||
var data = {};
|
||||
data['apps'] = apps || [];
|
||||
data['system'] = system_parts || [];
|
||||
|
||||
if (data['system'].constructor !== Array) {
|
||||
data['system'] = [data['system']];
|
||||
// Serialize an object
|
||||
serialize : function(obj) {
|
||||
var str = [];
|
||||
for(var p in obj)
|
||||
if (obj.hasOwnProperty(p)) {
|
||||
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
|
||||
}
|
||||
if (data['apps'].constructor !== Array) {
|
||||
data['apps'] = [data['apps']];
|
||||
}
|
||||
|
||||
// Some hook value contains multiple hooks separated by commas
|
||||
var split_hooks = [];
|
||||
$.each(data['system'], function(i, hook) {
|
||||
split_hooks = split_hooks.concat(hook.split(','));
|
||||
});
|
||||
data['system'] = split_hooks;
|
||||
|
||||
if (data['system'].length == 0) {
|
||||
delete data['system'];
|
||||
}
|
||||
if (data['apps'].length == 0) {
|
||||
delete data['apps'];
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
// Paste <pre>
|
||||
prePaste: function() {
|
||||
var pasteButtons = $('button[data-paste-content],a[data-paste-content]');
|
||||
pasteButtons.on('click', function(){
|
||||
// Get paste content element
|
||||
var preElement = $($(this).data('paste-content'));
|
||||
|
||||
// Add pacman loader
|
||||
$('#main').append('<div class="loader loader-content"></div>');
|
||||
|
||||
// Send to paste.yunohost.org
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: 'https://paste.yunohost.org/documents',
|
||||
data: preElement.text(),
|
||||
})
|
||||
.success(function(data, textStatus, jqXHR) {
|
||||
window.open('https://paste.yunohost.org/' + data.key, '_blank');
|
||||
})
|
||||
.fail(function() {
|
||||
c.flash('fail', y18n.t('paste_error'));
|
||||
})
|
||||
.always(function(){
|
||||
// Remove pacman
|
||||
$('div.loader').remove();
|
||||
});
|
||||
});
|
||||
return str.join("&");
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -181,9 +181,8 @@
|
|||
sam.store.set('url', window.location.hostname + '/yunohost/api');
|
||||
|
||||
if (sam.store.get('connected')) {
|
||||
this.api('/diagnosis', function(diagnosis) {
|
||||
versions = diagnosis.packages;
|
||||
$('#yunohost-version').html(y18n.t('footer_version', [versions.yunohost.version, versions.yunohost.repo]));
|
||||
this.api('GET', '/versions', {}, function(data) {
|
||||
$('#yunohost-version').html(y18n.t('footer_version', [data.yunohost.version, data.yunohost.repo]));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -249,7 +249,7 @@
|
|||
"select_user": "Seleccioneu un usuari",
|
||||
"service_description": "Descripció:",
|
||||
"service_log": "Registre %s",
|
||||
"service_start_on_boot": "Iniciar a l'engegada: ",
|
||||
"service_start_on_boot": "Iniciar a l'engegada",
|
||||
"service_status": "Estat: ",
|
||||
"services": "Serveis",
|
||||
"services_list": "Llista de serveis",
|
||||
|
@ -266,11 +266,11 @@
|
|||
"swap": "Memòria d'intercanvi",
|
||||
"system": "Sistema",
|
||||
"system_apps": "Aplicacions",
|
||||
"system_apps_nothing": "No hi ha aplicacions per actualitzar.",
|
||||
"system_apps_nothing": "Totes les aplicacions estan actualitzades!",
|
||||
"system_delayed_upgrade": "S'ha posposat l'actualització",
|
||||
"system_delayed_upgrade_warning": "<b>%s</b> serà actualitzat automàticament durant la pròxima hora.",
|
||||
"system_packages": "Paquets",
|
||||
"system_packages_nothing": "No hi ha paquets per actualitzar.",
|
||||
"system_packages": "Paquets del sistema",
|
||||
"system_packages_nothing": "Tots els paquets del sistema estan actualitzats!",
|
||||
"system_update": "Actualització del sistema",
|
||||
"system_upgrade": "Actualització del sistema",
|
||||
"system_upgrade_btn": "Actualització",
|
||||
|
@ -354,7 +354,7 @@
|
|||
"validity": "Validesa",
|
||||
"domain_is_eligible_for_ACME": "Aquest domini sembla estar configurat correctament per instal·lar el certificat Let's Encrypt!",
|
||||
"run": "Executar",
|
||||
"domain_not_eligible_for_ACME": "Aquest domini sembla que no està configurat per a un certificat Let's Encrypt. Comprova la configuració DNS i la accessibilitat del servidor HTTP.",
|
||||
"domain_not_eligible_for_ACME": "Aquest domini sembla que no està configurat per a un certificat Let's Encrypt. Comprova la configuració DNS i la accessibilitat del servidor HTTP. Les seccions «registres DNS» i «Web» de <a href='#/diagnosis'>la pàgina de diagnòstic</a> pot ajudar-vos a entendre el que està mal configurat.",
|
||||
"install_letsencrypt_cert": "Instal·la un certificat Let's Encrypt",
|
||||
"manually_renew_letsencrypt_message": "El certificat es renovarà automàticament durant els últims 15 dies de validesa. El podeu renovar manualment si ho desitgeu. (No recomanat).",
|
||||
"manually_renew_letsencrypt": "Renovar manualment ara",
|
||||
|
@ -471,5 +471,13 @@
|
|||
"app_state_low_quality": "baixa qualitat",
|
||||
"all": "Tot",
|
||||
"run_first_diagnosis": "Executa el diagnòstic inicial",
|
||||
"diagnosis_first_run": "La funció de diagnòstic intentarà identificar problemes habituals de diferents aspectes del servidor per tal d'assegurar que tot funcioni de la millor manera possible. No tingueu por si veieu uns quants errors just després de configurar el servidor: està precisament fet per ajudar a identificar els problemes i oferir una guia de com arreglar-los. El diagnòstic també s'executarà dues vegades al dia i enviarà un correu a l'administrador si apareix algun error."
|
||||
"diagnosis_first_run": "La funció de diagnòstic intentarà identificar problemes habituals de diferents aspectes del servidor per tal d'assegurar que tot funcioni de la millor manera possible. No tingueu por si veieu uns quants errors just després de configurar el servidor: està precisament fet per ajudar a identificar els problemes i oferir una guia de com arreglar-los. El diagnòstic també s'executarà dues vegades al dia i s'enviarà un correu a l'administrador si es troben errors.",
|
||||
"confirm_service_restart": "Esteu segur de voler reiniciar %s?",
|
||||
"group_explain_visitors_needed_for_external_client": "Vigileu ja que s'ha de permetre els visitants en algunes aplicacions si voleu utilitzar-les amb clients externs. És el cas, per exemple, de Nextcloud si voleu utilitzar el client de sincronització en el telèfon mòbil o en l'ordinador.",
|
||||
"groups": "Grups",
|
||||
"restart": "Reiniciar",
|
||||
"unmaintained_details": "Fa temps que no es manté aquesta aplicació i la persona que la mantenia ja no ho fa o no té temps per fer-ho. Us convidem a mirar el repositori de l'aplicació per oferir la vostra ajuda",
|
||||
"issues": "%s problemes",
|
||||
"operation_failed_explanation": "Aquesta operació ha fallat! Ens sap molt greu :( Podeu intentar <a href='https://yunohost.org/help'>demanar ajuda</a>. Si us plau doneu *el registre complet* de l'operació a la gent que intenta ajudar-vos. Podeu fer-ho fent clic al botó verd \"Compartir amb Yunopaste\". Quan compartiu els registres, YunoHost intentarà anonimitzar automàticament dades privades com els noms de domini i les IPs.",
|
||||
"diagnosis_explanation": "La funció de diagnòstic intentarà identificar els errors més comuns en diferents aspectes del servidor per verificar que tot funciona correctament. El diagnòstic s'executa automàticament dues vegades al dia i s'envia un correu electrònic a l'administrador si es troben errors. Tingueu en compte que no tots els tests seran rellevants si no s'utilitzen algunes funcions específiques (com per exemple XMPP) o pot ser que falli si teniu un sistema amb una configuració complexa. En aquests casos, i si sabeu el que feu, podeu ignorar els problemes o avisos corresponents."
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"add": "Hinzufügen",
|
||||
"administration_password": "Verwaltungspasswort",
|
||||
"allowed_users": "Zugelassene Benutzer",
|
||||
"api_not_responding": "API antwortet nicht",
|
||||
"api_not_responding": "Die YunoHost-API antwortet nicht. Vielleicht ist 'yunohost-api' ausgefallen oder wurde neu gestartet?",
|
||||
"app_access": "Zugriffsrechte",
|
||||
"app_access_addall_btn": "Zugriff für alle zulassen",
|
||||
"app_access_addall_desc": "Alle existierenden Benutzer werden Zugriff auf %s haben.",
|
||||
|
@ -14,7 +14,7 @@
|
|||
"app_access_title": "%s Zugriffsrechte",
|
||||
"app_debug_no_logs": "Anwendungslogs stehen nicht zur Verfügung",
|
||||
"app_debug_tab": "Debugging Informationen anzeigen",
|
||||
"app_info_access_desc": "Zugriffsrechte verwalten. Erlaubte Nutzer: %s",
|
||||
"app_info_access_desc": "Gruppen / Benutzer, die auf diese App zugreifen dürfen:",
|
||||
"app_info_debug_desc": "Debugging Informationen für diese Applikation anzeigen.",
|
||||
"app_info_default_desc": "Hauptdomain auf diese App (%s) weiterleiten.",
|
||||
"app_info_uninstall_desc": "Diese App löschen.",
|
||||
|
@ -120,7 +120,7 @@
|
|||
"hook_data_home": "Benutzerdaten",
|
||||
"hook_data_home_desc": "Die Daten des Benutzers werden gespeichert unter /home/USER",
|
||||
"hook_data_mail": "E-Mail",
|
||||
"hook_data_mail_desc": "E-Mail Adressen auf dem Server",
|
||||
"hook_data_mail_desc": "Roth-E-Mails auf dem Server gespeichert",
|
||||
"hostname": "Hostname",
|
||||
"id": "ID",
|
||||
"inactive": "Inaktiv",
|
||||
|
@ -143,7 +143,7 @@
|
|||
"local_ip": "Lokale IP",
|
||||
"log": "Log",
|
||||
"logged_in": "Angemeldet",
|
||||
"logged_out": "Ausgeloggt",
|
||||
"logged_out": "Abgemeldet",
|
||||
"login": "Anmelden",
|
||||
"logout": "Abmelden",
|
||||
"mailbox_quota_description": "Zum Beispiel, eine CD verfügt über 700M, eine über 4700M.",
|
||||
|
@ -202,7 +202,7 @@
|
|||
"save": "Speichern",
|
||||
"select_user": "Benutzer auswählen",
|
||||
"service_log": "%s Log",
|
||||
"service_start_on_boot": "Beim Hochfahren starten: ",
|
||||
"service_start_on_boot": "Beim Hochfahren starten",
|
||||
"service_status": "Status: ",
|
||||
"services": "Dienste",
|
||||
"services_list": "Dienstübersicht",
|
||||
|
@ -218,11 +218,11 @@
|
|||
"swap": "Auslagerungsspeicher",
|
||||
"system": "System",
|
||||
"system_apps": "Apps",
|
||||
"system_apps_nothing": "Es gibt keine Aktualisierungen für deine Apps.",
|
||||
"system_apps_nothing": "Allen Apps sind auf dem neuesten Stand!",
|
||||
"system_delayed_upgrade": "Zurückgestellte Aktualisierung",
|
||||
"system_delayed_upgrade_warning": "<b>%s</b> wird automatisch innerhalb der nächsten Stunde aktualisiert.",
|
||||
"system_packages": "Pakete",
|
||||
"system_packages_nothing": "Keine Pakete zur Aktualisierung gefunden.",
|
||||
"system_packages": "Systempakete",
|
||||
"system_packages_nothing": "Alle Systempakete sind auf dem neuesten Stand!",
|
||||
"system_update": "System aktualisieren",
|
||||
"system_upgrade": "Systemaktualisierung",
|
||||
"system_upgrade_btn": "Aktualisieren",
|
||||
|
@ -347,11 +347,11 @@
|
|||
"tools_reboot": "Starte deine Server neu",
|
||||
"tools_reboot_btn": "Neustart",
|
||||
"tools_reboot_done": "Starte neu...",
|
||||
"tools_rebooting": "Dein Server startet neu. Um zur Verwaltungsoberfläche zurückzukehren, musst du warten bis der Server hochgefahren ist. Feststellen kannst du es, in dem du die Seite neu lädst.",
|
||||
"tools_rebooting": "Dein Server startet neu. Um zur Verwaltungsoberfläche zurückzukehren, musst du warten bis der Server hochgefahren ist. Prüfen kannst du es, in dem du die Seite neu lädst (F5).",
|
||||
"tools_shutdown": "Fahre deinen Server herunter",
|
||||
"tools_shutdown_btn": "Herunterfahren",
|
||||
"tools_shutdown_done": "Fahre herunter...",
|
||||
"tools_shuttingdown": "Dein Server wird heruntergefahren. Solange dein Server ausgeschaltet ist, kannst du das Verwaltungsoberfläche nicht benutzen.",
|
||||
"tools_shuttingdown": "Dein Server wird heruntergefahren. Solange dein Server ausgeschaltet ist, kannst du die Verwaltungsoberfläche nicht benutzen.",
|
||||
"tools_shutdown_reboot": "Herunterfahren/Neustarten",
|
||||
"appslists": "Applikationslisten",
|
||||
"appslists_no_lists": "Keine Applikationslisten",
|
||||
|
@ -472,5 +472,13 @@
|
|||
"others": "Anderes",
|
||||
"catalog": "Katalog",
|
||||
"app_state_low_quality": "geringe Qualität",
|
||||
"all": "Alle"
|
||||
"all": "Alle",
|
||||
"confirm_service_restart": "Bist du sicher, dass du %s neustarten möchtest?",
|
||||
"run_first_diagnosis": "Initiale Diagnose läuft",
|
||||
"diagnosis_first_run": "Die Diagnose Funktion wird versuchen, gängige Probleme in verschiedenen Teilen deines Servers zu finden, damit alles reibungslos läuft. Bitte werde nicht panisch, wenn du ein paar Fehlermeldungen siehst nachdem du deinen Server aufgesetzt hast: es soll versuchen dir zu helfen, Probleme zu identifizieren und Tipps für Lösungen zu zeigen. Die Diagnose wird auch automatisch zweimal täglich ausgeführt, falls Fehler gefunden werden, bekommt der Administrator ein E-Mail.",
|
||||
"unmaintained_details": "Diese Anwendung wurde seit einiger Zeit nicht gewartet und der frühere Wart hat die Anwendung aufgegeben oder hat keine Zeit mehr für die Wartung. Du bist herzlich eingeladen dir die Quellen und Unterlagen anzusehen und Hilfe zu leisten",
|
||||
"group_explain_visitors_needed_for_external_client": "Sei vorsichtig und beachte, dass du manche Anwendungen für externe Besucher freigeben musst, falls du beabsichtigst, diese mit externen Clients aufzurufen. Zum Beispiel trifft das auf Nextcloud zu, wenn du eine Synchronisation auf dem Smartphone oder Desktop PC haben möchtest.",
|
||||
"groups": "Gruppen",
|
||||
"issues": "%s Probleme",
|
||||
"restart": "Neustart"
|
||||
}
|
||||
|
|
|
@ -3,63 +3,51 @@
|
|||
"active": "Active",
|
||||
"add": "Add",
|
||||
"advanced": "Advanced",
|
||||
"remove": "Remove",
|
||||
"administration_password": "Administration password",
|
||||
"all": "All",
|
||||
"all_apps": "All apps",
|
||||
"api_not_responding": "The YunoHost API is not responding. Maybe 'yunohost-api' is down or got restarted?",
|
||||
"app_change_label": "Change Label",
|
||||
"app_change_url": "Change URL",
|
||||
"app_debug_no_logs": "Application's logs are not available",
|
||||
"app_debug_tab": "Display debug information",
|
||||
"app_info_access_desc": "Groups / users currently allowed to access this app:",
|
||||
"app_info_changelabel_desc": "Change app label in the portal.",
|
||||
"app_info_debug_desc": "Display debugging information for this application.",
|
||||
"app_info_default_desc": "Redirect domain root to this application (%s).",
|
||||
"app_info_changeurl_desc": "Change the access URL of this application (domain and/or path).",
|
||||
"app_info_change_url_disabled_tooltip": "This feature hasn't been implemented in this app yet",
|
||||
"app_info_uninstall_desc": "Remove this application.",
|
||||
"app_install_cancel": "Installation cancelled.",
|
||||
"app_install_custom_no_manifest": "No manifest.json file",
|
||||
"app_list": "App list",
|
||||
"app_level": "App level",
|
||||
"app_make_default": "Make default",
|
||||
"app_no_actions": "This application doesn't have any actions",
|
||||
"app_repository": "Application origin: ",
|
||||
"app_state": "Application state: ",
|
||||
"app_state_inprogress": "in progress",
|
||||
"app_state_inprogress": "not yet working",
|
||||
"app_state_inprogress_explanation": "This maintainer of this app declared that this application is not ready yet for production use. BE CAREFUL!",
|
||||
"app_state_notworking": "not working",
|
||||
"app_state_notworking_explanation": "This maintainer of this app declared it as 'not working'. IT WILL BREAK YOUR SYSTEM!",
|
||||
"app_state_low_quality": "low quality",
|
||||
"app_state_low_quality_explanation": "This app may be functional, but may still contain issues, or is not fully integrated with YunoHost, or it does not respect the good practices.",
|
||||
"app_state_high-quality": "high quality",
|
||||
"app_state_high-quality_explanation": "This app is well-integrated with YunoHost. It has been (and is!) peer-reviewed by the YunoHost app team. It can be expected to be safe and maintained on the long-term.",
|
||||
"app_state_working": "working",
|
||||
"app_state_working_explanation": "The maintainer of this app declared it as 'working'. It means that it should be functional (c.f. application level) but is not necessarily peer-reviewed, it may still contain issues or is not fully integrated with YunoHost.",
|
||||
"application": "Application",
|
||||
"applications": "Applications",
|
||||
"archive_empty": "Empty archive",
|
||||
"available": "Available",
|
||||
"available_apps": "Available apps",
|
||||
"backup": "Backup",
|
||||
"backup_action": "Backup",
|
||||
"backup_archive_copy": "Copy this archive on another storage",
|
||||
"backup_archive_delete": "Delete this archive",
|
||||
"backup_archive_download": "Download this archive",
|
||||
"backup_content": "Backup content",
|
||||
"backup_create": "Create a backup",
|
||||
"backup_encryption_warning": "Don't forget this password, you'll need it if you want restore the archive",
|
||||
"backup_new": "New backup",
|
||||
"backup_optional_encryption": "Optional encryption",
|
||||
"backup_optional_password": "Optional password",
|
||||
"backup_type": "Type",
|
||||
"backups_no": "No backup",
|
||||
"begin": "Begin",
|
||||
"bit_rate": "Bit rate",
|
||||
"both": "Both",
|
||||
"cancel": "Cancel",
|
||||
"catalog": "Catalog",
|
||||
"check": "Check",
|
||||
"check_mx": "MX record",
|
||||
"check_stmp": "port 25 access",
|
||||
"close": "Close",
|
||||
"configuration": "Configuration",
|
||||
"confirm_app_change_url": "Are you sure you want to change the app access URL?",
|
||||
"confirm_app_default": "Are you sure you want to make this app default?",
|
||||
"confirm_change_maindomain": "Are you sure you want to change the main domain?",
|
||||
|
@ -73,10 +61,9 @@
|
|||
"confirm_migrations_skip": "Skipping migrations is not recommended. Are you sure you want to do that?",
|
||||
"confirm_postinstall": "You are about to launch the post-installation process on the domain %s. It may take a few minutes, *do not interrupt the operation*.",
|
||||
"confirm_restore": "Are you sure you want to restore %s?",
|
||||
"confirm_service_restart": "Are you sure you want to restart %s?",
|
||||
"confirm_service_start": "Are you sure you want to start %s?",
|
||||
"confirm_service_stop": "Are you sure you want to stop %s?",
|
||||
"confirm_service_enable": "Are you sure you want to enable %s?",
|
||||
"confirm_service_disable": "Are you sure you want to disable %s?",
|
||||
"confirm_uninstall": "Are you sure you want to uninstall %s?",
|
||||
"confirm_update_apps": "Are you sure you want to update all applications?",
|
||||
"confirm_update_system": "Are you sure you want to update all system packages?",
|
||||
|
@ -86,25 +73,21 @@
|
|||
"confirm_reboot_action_reboot": "Are you sure you want to reboot your server?",
|
||||
"confirm_reboot_action_shutdown": "Are you sure you want to shutdown your server?",
|
||||
"connection": "Connection",
|
||||
"copy": "Copy",
|
||||
"count_min": "%s min",
|
||||
"cpu_load": "CPU Load",
|
||||
"created_at": "Created at",
|
||||
"cumulative_usage": "Cumulative usage",
|
||||
"current_maintainer_title": "Current maintainer of this package",
|
||||
"custom_app_install": "Install custom app",
|
||||
"custom_app_url_only_github": "Currently only from GitHub",
|
||||
"default": "Default",
|
||||
"delete": "Delete",
|
||||
"description": "Description",
|
||||
"details": "Details",
|
||||
"domain_dns_conf_is_just_a_recommendation": "This page shows you the *recommended* configuration. It does *not* configure the DNS for you. It is your responsability to configure your DNS zone in your DNS registrar according to this recommendation.",
|
||||
"diagnosis": "Diagnosis",
|
||||
"diagnosis_hide_private": "Show diagnostic information without private data",
|
||||
"diagnosis_view_private": "Show diagnostic information including private data",
|
||||
"diagnosis_with_private": "Diagnosis with private data",
|
||||
"diagnosis_experimental_disclaimer": "Be aware that the diagnosis feature is still experimental and being polished, and it may not be fully reliable.",
|
||||
"diagnosis_first_run": "The diagnosis feature will attempt to identify common issues on the different aspects of your server to make sure everything runs smoothly. Please do not panic if you see a bunch of errors right after setting up your server: it is precisely meant to help you to identify issues and guide you to fix them. The diagnosis will also run automatically twice a day and an email is sent to the administrator if issues are found.",
|
||||
"diagnosis_explanation": "The diagnosis feature will attempt to identify common issues on the different aspects of your server to make sure everything runs smoothly. The diagnosis runs automatically twice a day and an email is sent to the administrator if issues are found. Note that some tests may not be relevant if you do not want to use some specific features (for example XMPP) or may fail if you have a complex setup. In such cases, and if you know what you are doing, it is alright to ignore the corresponding issues or warnings.",
|
||||
"run_first_diagnosis": "Run initial diagnosis",
|
||||
"disable": "Disable",
|
||||
"disabled": "Disabled",
|
||||
"disk": "Disk",
|
||||
"dns": "DNS",
|
||||
"domain": "Domain",
|
||||
"domain_add": "Add domain",
|
||||
|
@ -112,36 +95,28 @@
|
|||
"domain_add_dyndns_doc": "… and I want a dynamic DNS service.",
|
||||
"domain_add_panel_with_domain": "I already have a domain name…",
|
||||
"domain_add_panel_without_domain": "I don't have a domain name…",
|
||||
"domain_default": "Default domain",
|
||||
"domain_default_desc": "The default domain is the connection domain where users log in.",
|
||||
"domain_default_longdesc": "This is your default domain.",
|
||||
"domain_delete_longdesc": "Delete this domain",
|
||||
"domain_dns_config": "DNS configuration",
|
||||
"domain_dns_longdesc": "View DNS configuration",
|
||||
"domain_list": "Domain list",
|
||||
"domain_name": "Domain name",
|
||||
"domain_select": "Select domain",
|
||||
"domain_visit": "Visit",
|
||||
"domain_visit_url": "Visit %s",
|
||||
"domains": "Domains",
|
||||
"download": "Download",
|
||||
"enable": "Enable",
|
||||
"enabled": "Enabled",
|
||||
"error_modify_something": "You should modify something",
|
||||
"error_retrieve_feed": "Could not retrieve feed: %s. You might have a plugin prevent your browser from performing this request (or the website is down).",
|
||||
"error_select_domain": "You should indicate a domain",
|
||||
"error_server": "Server error",
|
||||
"error_server_unexpected": "Unexpected server error (%s)",
|
||||
"error_connection_interrupted": "The server closed the connection instead of answering it. Has nginx or the yunohost-api been restarted or stoppted for some reason? (Error code/message: %s)",
|
||||
"everything_good": "Everything good!",
|
||||
"experimental_warning": "Warning: this feature is experimental and not consider stable, you shouldn't be using it except if you know what you are doing.",
|
||||
"filesystem": "Filesystem",
|
||||
"firewall": "Firewall",
|
||||
"footer_version": "Powered by <a href='https://yunohost.org'>YunoHost</a> %s (%s).",
|
||||
"form_input_example": "Example: %s",
|
||||
"free": "Free",
|
||||
"from_to": "from %s to %s",
|
||||
"fs_type": "FS Type",
|
||||
"gateway": "Gateway: ",
|
||||
"good_practices_about_admin_password": "You are now about to define a new admin password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).",
|
||||
"good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).",
|
||||
"group": "Group",
|
||||
|
@ -156,6 +131,7 @@
|
|||
"group_explain_visitors": "This is a special group representing anonymous visitors",
|
||||
"group_explain_visitors_needed_for_external_client": "Be careful that you need to keep some applications allowed to visitors if you intend to use them with external clients. For example, this is the case for Nextcloud if you want intend to use a synchronization client on your smartphone or desktop computer.",
|
||||
"group_specific_permissions": "User specific permissions",
|
||||
"groups": "Groups",
|
||||
"groups_and_permissions": "Groups and permissions",
|
||||
"groups_and_permissions_manage": "Manage groups and permissions",
|
||||
"permissions": "Permissions",
|
||||
|
@ -175,8 +151,9 @@
|
|||
"hook_data_home_desc": "User data located in /home/USER",
|
||||
"hook_data_mail": "Mail",
|
||||
"hook_data_mail_desc": "Raw emails stored on the server",
|
||||
"hostname": "Hostname",
|
||||
"id": "ID",
|
||||
"ignore": "Ignore",
|
||||
"ignored": "%s ignored",
|
||||
"inactive": "Inactive",
|
||||
"infos": "Info",
|
||||
"install": "Install",
|
||||
|
@ -184,21 +161,16 @@
|
|||
"install_time": "Install time",
|
||||
"installation_complete": "Installation complete",
|
||||
"installed": "Installed",
|
||||
"installed_apps": "Installed apps",
|
||||
"installing": "Installing",
|
||||
"interface": "Interface",
|
||||
"internal_exception": "<strong>Yunohost encountered an internal error:/</strong><br><em>Really sorry about that.<br>You should look for help on <a href=\"https://forum.yunohost.org/\">the forum</a> or <a href=\"https://chat.yunohost.org/\">the chat</a> to fix the situation, or report the bug on <a href=\"https://github.com/YunoHost/issues\">the bugtracker</a>.</em><br>The following information might be useful for the person helping you:<h3>Action</h3><pre>%s%s</pre><h3>Traceback</h3><pre>%s</pre>",
|
||||
"io": "I/O",
|
||||
"ipv4": "IPv4",
|
||||
"ipv6": "IPv6",
|
||||
"issues": "%s issues",
|
||||
"label": "Label",
|
||||
"label_for_manifestname": "Label for %s",
|
||||
"level": "level",
|
||||
"last_ran": "Last time ran:",
|
||||
"license": "License",
|
||||
"loading": "Loading …",
|
||||
"local_archives": "Local archives",
|
||||
"local_ip": "Local IP",
|
||||
"log": "Log",
|
||||
"logged_in": "Logged in",
|
||||
"logged_out": "Logged out",
|
||||
"login": "Login",
|
||||
|
@ -210,49 +182,35 @@
|
|||
"manage_apps": "Manage apps",
|
||||
"manage_domains": "Manage domains",
|
||||
"manage_users": "Manage users",
|
||||
"memory": "Memory",
|
||||
"menu": "Menu",
|
||||
"migrations": "Migrations",
|
||||
"migrations_pending": "Pending migrations",
|
||||
"migrations_done": "Previous migrations",
|
||||
"migrations_no_pending": "No pending migrations",
|
||||
"migrations_no_done": "No previous migrations",
|
||||
"mode": "Mode",
|
||||
"monitoring": "Monitoring",
|
||||
"monitoring_check_glances": "Check <a href='#/services/glances'>glances</a> service status.",
|
||||
"monitoring_disabled": "Monitoring is not enabled.",
|
||||
"mount_point": "Mount point",
|
||||
"multi_instance": "Multi instance",
|
||||
"myserver": "myserver",
|
||||
"myserver_org": "myserver.org",
|
||||
"network": "Network",
|
||||
"next": "Next",
|
||||
"no": "No",
|
||||
"no_installed_apps": "No installed apps.",
|
||||
"no_log": "No log.",
|
||||
"nobody": "Nobody",
|
||||
"non_compatible_api": "Non-compatible API",
|
||||
"ok": "OK",
|
||||
"only_highquality_apps": "Only high-quality apps",
|
||||
"only_working_apps": "Only working apps",
|
||||
"only_decent_quality_apps": "Only decent quality apps",
|
||||
"open": "Open",
|
||||
"operations": "Operations",
|
||||
"orphaned": "not maintained",
|
||||
"orphaned_details": "This app is not maintained anymore. It may still be working but won't receive any upgrade. Feel free to come and revive it!",
|
||||
"os": "OS",
|
||||
"orphaned": "Not maintained",
|
||||
"orphaned_details": "This app has not been maintained for quite some time. It may still be working, but won't receive any upgrade until somebody volunteers to take care of it. Feel free to contribute to revive it!",
|
||||
"others": "Others",
|
||||
"operation_failed_explanation": "This operation failed! Really sorry about that :( You can try to <a href='https://yunohost.org/help'>ask for help</a>. Please provide *the full log* of the operation to the people helping you. You can do so by clicking on the 'Share with Yunopaste' green button. When sharing the logs, YunoHost will automatically attempt to anonymize private data like domain names and IPs.",
|
||||
"password": "Password",
|
||||
"password_confirmation": "Password confirmation",
|
||||
"password_description": "Password must be at least %s characters long.",
|
||||
"password_empty": "The password field is empty",
|
||||
"password_new": "New password",
|
||||
"passwords_dont_match": "Passwords don't match",
|
||||
"passwords_too_short": "Password is too short",
|
||||
"path": "Path",
|
||||
"diagnosis": "Diagnosis",
|
||||
"diagnosis_with_private": "Diagnosis with private data",
|
||||
"diagnosis_view_private": "Show diagnosis with private data",
|
||||
"diagnosis_hide_private": "Show diagnosis without private data",
|
||||
"logs": "Logs",
|
||||
"logs_operation": "Operations made on system with YunoHost",
|
||||
"logs_history": "History of command run on system",
|
||||
|
@ -262,7 +220,6 @@
|
|||
"logs_service": "Services logs",
|
||||
"logs_app": "Apps logs",
|
||||
"logs_no_logs_registered": "No log registered for this category",
|
||||
"logs_end_with_error": "This log finished with the error:",
|
||||
"logs_error": "Error",
|
||||
"logs_ended_at": "End",
|
||||
"logs_started_at": "Start",
|
||||
|
@ -280,54 +237,37 @@
|
|||
"postinstall_intro_3": "You can obtain more information by visiting the <a href='//yunohost.org/postinstall' target='_blank'>appropriate documentation page</a>",
|
||||
"postinstall_password": "This password will be used to manage everything on your server. Take the time to choose it wisely.",
|
||||
"previous": "Previous",
|
||||
"process": "Process",
|
||||
"protocol": "Protocol",
|
||||
"public_ip": "Public IP: ",
|
||||
"ram": "RAM",
|
||||
"read": "Read",
|
||||
"read_more": "Read more",
|
||||
"reception": "Reception",
|
||||
"refresh_app_list": "Refresh list",
|
||||
"rerun_diagnosis": "Rerun diagnosis",
|
||||
"request_adoption": "waiting adoption",
|
||||
"request_adoption_details": "The current maintainer would like to stop maintaining this app. Feel free to propose yourself as the new maintainer!",
|
||||
"request_help": "need help",
|
||||
"request_help_details": "The current maintainer would like some help with the maintainance of this app. Feel free to come contribute on it!",
|
||||
"restore": "Restore",
|
||||
"restart": "Restart",
|
||||
"run": "Run",
|
||||
"running": "Running",
|
||||
"save": "Save",
|
||||
"search_for_apps": "Search for apps...",
|
||||
"select_all": "Select all",
|
||||
"select_none": "Select none",
|
||||
"service_description": "Description:",
|
||||
"service_log": "%s log",
|
||||
"service_start_on_boot": "Start on boot: ",
|
||||
"service_status": "Status: ",
|
||||
"service_start_on_boot": "Start on boot",
|
||||
"services": "Services",
|
||||
"services_list": "Service list",
|
||||
"set_default": "Set default",
|
||||
"size": "Size",
|
||||
"since": "since",
|
||||
"skip": "Skip",
|
||||
"start": "Start",
|
||||
"started_at": "Started at:",
|
||||
"status": "Status",
|
||||
"stop": "Stop",
|
||||
"storage_create": "Add remote storage",
|
||||
"storages_new": "New remote storage",
|
||||
"storages_no": "No storages.",
|
||||
"swap": "Swap",
|
||||
"system": "System",
|
||||
"system_apps": "Apps",
|
||||
"system_apps_nothing": "All apps are up to date!",
|
||||
"system_packages": "System packages",
|
||||
"system_packages_nothing": "All system packages are up to date!",
|
||||
"system_update": "System update",
|
||||
"system_upgrade": "System upgrade",
|
||||
"system_upgrade_btn": "Upgrade",
|
||||
"system_upgrade_all_applications_btn": "Upgrade all applications",
|
||||
"system_upgrade_all_packages_btn": "Upgrade all packages",
|
||||
"tcp": "TCP",
|
||||
"time_since_update": "Time since update: ",
|
||||
"tools": "Tools",
|
||||
"tools_adminpw": "Change administration password",
|
||||
"tools_adminpw_confirm_placeholder": "Confirm the new password",
|
||||
|
@ -349,23 +289,20 @@
|
|||
"tools_shutdown_done": "Shutting down...",
|
||||
"tools_shuttingdown": "Your server is powering off. As long as your server is off, you won't be able to use the web administration.",
|
||||
"tools_shutdown_reboot": "Shutdown/Reboot",
|
||||
"total": "Total",
|
||||
"transmission": "Transmission",
|
||||
"udp": "UDP",
|
||||
"unauthorized": "Unauthorized",
|
||||
"unignore": "Unignore",
|
||||
"uninstall": "Uninstall",
|
||||
"unknown_action": "Unknown action %s",
|
||||
"unknown_argument": "Unknown argument: %s",
|
||||
"unmaintained": "Unmaintained",
|
||||
"unmaintained_details": "This app has not been update for quite a while and the previous maintainer has gone away or does not have time to maintain this app. Feel free to check the app repository to provide your help",
|
||||
"upload": "Upload",
|
||||
"upload_archive": "Archive upload",
|
||||
"upnp": "UPnP",
|
||||
"upnp_disabled": "UPnP is disabled.",
|
||||
"upnp_enabled": "UPnP is enabled.",
|
||||
"uptime": "Uptime",
|
||||
"url": "URL",
|
||||
"usage": "Usage",
|
||||
"used": "Used",
|
||||
"user_email": "Email",
|
||||
"user_emailaliases": "Mail aliases",
|
||||
"user_emailforward": "Mail forward",
|
||||
|
@ -378,13 +315,11 @@
|
|||
"user_username": "Username",
|
||||
"user_username_edit": "Edit %s’s account",
|
||||
"users": "Users",
|
||||
"users_list": "User list",
|
||||
"users_new": "New user",
|
||||
"users_no": "No users.",
|
||||
"versions": "Versions",
|
||||
"version": "Version",
|
||||
"warnings": "%s warnings",
|
||||
"warning_first_user": "You probably need to <a href='#/users/create' class='alert-link'>create a user</a> first.",
|
||||
"write": "Write",
|
||||
"wrong_password": "Wrong password",
|
||||
"yes": "Yes",
|
||||
"certificate_alert_not_valid": "CRITICAL: Current certificate is not valid! HTTPS won't work at all!",
|
||||
|
@ -395,7 +330,6 @@
|
|||
"certificate_alert_great": "Great! You're using a valid Let's Encrypt certificate!",
|
||||
"certificate_alert_unknown": "Unknown status",
|
||||
"certificate_manage": "Manage SSL certificate",
|
||||
"certificate_old_letsencrypt_app_conflict": "The 'letsencrypt' app is currently installed and conflicts with this feature. Please uninstall it first to use the new certificate management interface.",
|
||||
"ssl_certificate": "SSL certificate",
|
||||
"confirm_cert_install_LE": "Are you sure you want to install a Let's Encrypt certificate for this domain?",
|
||||
"confirm_cert_regen_selfsigned": "Are you sure you want to regenerate a self-signed certificate for this domain?",
|
||||
|
@ -406,25 +340,14 @@
|
|||
"certificate_authority": "Certification authority",
|
||||
"validity": "Validity",
|
||||
"domain_is_eligible_for_ACME": "This domain seems correctly configured to install a Let's Encrypt certificate!",
|
||||
"domain_not_eligible_for_ACME": "This domain doesn't seem ready for a Let's Encrypt certificate. Please check your DNS configuration and HTTP server reachability.",
|
||||
"domain_not_eligible_for_ACME": "This domain doesn't seem ready for a Let's Encrypt certificate. Please check your DNS configuration and HTTP server reachability. The 'DNS records' and 'Web' section in <a href='#/diagnosis'>the diagnosis page</a> can help you understand what is misconfigured.",
|
||||
"install_letsencrypt_cert": "Install a Let's Encrypt certificate",
|
||||
"manually_renew_letsencrypt_message": "Certificate will be automatically renewed during the last 15 days of validity. You can manually renew it if you want to. (Not recommended).",
|
||||
"manually_renew_letsencrypt": "Manually renew now",
|
||||
"meltdown": "You are vulnerable to the <a target=\"_blank\" href=\"https://meltdownattack.com/\">meltdown</a> critical security vulnerability. To fix that, you need to <a href=\"#/update\">update your system</a> then <a href=\"#/tools/reboot\">reboot it</a> to load the new linux kernel.",
|
||||
"regenerate_selfsigned_cert_message": "If you want, you can regenerate the self-signed certificate.",
|
||||
"regenerate_selfsigned_cert": "Regenerate self-signed certificate",
|
||||
"revert_to_selfsigned_cert_message": "If you really want to, you can reinstall a self-signed certificate. (Not recommended)",
|
||||
"revert_to_selfsigned_cert": "Revert to a self-signed certificate",
|
||||
"appslists": "Applications lists",
|
||||
"appslists_no_lists": "No applications lists",
|
||||
"appslists_custom": "Custom applications list",
|
||||
"appslists_manage": "Manage applications lists",
|
||||
"appslists_confirm_remove": "Are you sure you want to remove this applications list?",
|
||||
"appslists_info_refresh_desc": "Refresh applications status for this list.",
|
||||
"appslists_info_remove_desc": "Applications from this list will not be available anymore.",
|
||||
"appslists_last_update": "Last update",
|
||||
"appslists_unknown_list": "Unknown apps list: %s",
|
||||
"name": "Name",
|
||||
"purge_user_data_checkbox": "Purge %s's data? (This will remove the content of it's home and mail directories.)",
|
||||
"purge_user_data_checkbox": "Purge %s's data? (This will remove the content of its home and mail directories.)",
|
||||
"purge_user_data_warning": "Purging user's data is not reversible. Be sure you know what you're doing!"
|
||||
}
|
||||
|
|
|
@ -227,7 +227,7 @@
|
|||
"uninstall": "Malinstali",
|
||||
"users_new": "Nova uzanto",
|
||||
"users": "Uzantoj",
|
||||
"system_apps_nothing": "Ne estas programoj por ĝisdatigi.",
|
||||
"system_apps_nothing": "Ĉiuj aplikoj estas ĝisdatigitaj!",
|
||||
"used": "Uzata",
|
||||
"tools_security_feed_view_items": "Vidu ĉiujn sekurecajn sciigojn",
|
||||
"certificate_alert_letsencrypt_about_to_expire": "Nuna atestilo estas preskaŭ eksvalidiĝi. Ĝi baldaŭ renoviĝu aŭtomate.",
|
||||
|
@ -259,7 +259,7 @@
|
|||
"install_letsencrypt_cert": "Instalu atestilon Lasu-Ĉifri",
|
||||
"appslists_custom": "Propra listo listo",
|
||||
"no_log": "Neniu ŝtipo.",
|
||||
"system_packages_nothing": "Ne estas pakoj por ĝisdatigi.",
|
||||
"system_packages_nothing": "Ĉiuj sistemaj pakoj estas ĝisdatigitaj !",
|
||||
"system_upgrade_all_applications_btn": "Ĝisdatigu ĉiujn aplikojn",
|
||||
"postinstall": "post Instalaĵo",
|
||||
"service_description": "Priskribo:",
|
||||
|
@ -270,7 +270,7 @@
|
|||
"unauthorized": "Ne rajtigita",
|
||||
"wrong_password": "Erara pasvorto",
|
||||
"tools_security_feed": "Sekurecaj sciigoj",
|
||||
"system_packages": "Pakoj",
|
||||
"system_packages": "Sistempakaĵoj",
|
||||
"certificate_alert_unknown": "Nekonata stato",
|
||||
"tools_shutdown_reboot": "Ŝalti/Rekomenci",
|
||||
"logs_started_at": "Komencu",
|
||||
|
@ -325,7 +325,7 @@
|
|||
"tools_adminpw_current": "Aktuala Pasvorto",
|
||||
"appslists_manage": "Administri listojn de aplikoj",
|
||||
"menu": "menuo",
|
||||
"service_start_on_boot": "Komencu je ekkuro: ",
|
||||
"service_start_on_boot": "Komencu je ekkuro",
|
||||
"storages_new": "Nova fora stokado",
|
||||
"storage_create": "Aldonu forajn stokadojn",
|
||||
"regenerate_selfsigned_cert_message": "Se vi volas, vi povas regeneri la mem-subskribitan atestilon.",
|
||||
|
@ -457,5 +457,13 @@
|
|||
"app_state_low_quality": "malkvalita",
|
||||
"app_state_low_quality_explanation": "Ĉi tiu app eble funkcias, sed ankoraŭ enhavas problemojn, aŭ ne plene integras kun YunoHost, aŭ ĝi ne respektas la bonajn praktikojn.",
|
||||
"catalog": "Katalogo",
|
||||
"others": "Aliaj"
|
||||
"others": "Aliaj",
|
||||
"confirm_service_restart": "Ĉu vi certas, ke vi volas rekomenci %s ?",
|
||||
"diagnosis_first_run": "La diagnoza funkcio provos identigi oftajn problemojn pri la diversaj aspektoj de via servilo por certigi, ke ĉio funkcias glate. Bonvolu ne panikiĝi, se vi vidas amason da eraroj tuj post agordo de via servilo: ĝi ĝuste celas helpi vin identigi problemojn kaj gvidi vin ripari ilin. La diagnozo ankaŭ funkcios aŭtomate dufoje ĉiutage kaj retpoŝtu al la administranto se iuj problemoj ekestos.",
|
||||
"group_explain_visitors_needed_for_external_client": "Atentu, ke vi bezonas konservi iujn aplikaĵojn permesitajn al vizitantoj se vi intencas uzi ilin kun eksteraj klientoj. Ekzemple, ĉi tiu estas la kazo de Nextcloud se vi volas intenci uzi sinkronigan klienton en via inteligenta telefono aŭ labortabla komputilo.",
|
||||
"restart": "Rekomenci",
|
||||
"unmaintained_details": "Ĉi tiu app ne estis ĝisdatigita antaŭ tre tempo kaj la antaŭa prizorganto foriĝis aŭ ne havas tempon por subteni ĉi tiun app. Bonvolu kontroli la app-deponejon por doni vian helpon",
|
||||
"run_first_diagnosis": "Kuru komencan diagnozon",
|
||||
"groups": "Grupoj",
|
||||
"issues": "%s aferoj"
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"app_make_default": "Establecer como predeterminado",
|
||||
"app_repository": "Origen de la aplicación: ",
|
||||
"app_state": "Estado de la aplicación: ",
|
||||
"app_state_inprogress": "en marcha",
|
||||
"app_state_inprogress": "Todavía no trabajando",
|
||||
"app_state_notworking": "no funciona",
|
||||
"app_state_validated": "Validado",
|
||||
"app_state_working": "funciona",
|
||||
|
@ -222,7 +222,7 @@
|
|||
"save": "Guardar",
|
||||
"select_user": "Seleccionar usuario",
|
||||
"service_log": "%s log",
|
||||
"service_start_on_boot": "Inicio en el arranque: ",
|
||||
"service_start_on_boot": "Inicie en el arranque",
|
||||
"service_status": "Estado: ",
|
||||
"services": "Servicios",
|
||||
"services_list": "Lista de servicios",
|
||||
|
@ -238,11 +238,11 @@
|
|||
"swap": "Swap",
|
||||
"system": "Sistema",
|
||||
"system_apps": "Aplicaciones",
|
||||
"system_apps_nothing": "No hay aplicaciones para actualizar.",
|
||||
"system_apps_nothing": "¡Todas las aplicaciones están actualizadas!",
|
||||
"system_delayed_upgrade": "Retraso en actualización",
|
||||
"system_delayed_upgrade_warning": "<b>%s</b> será actualizada automáticamente en la próxima hora.",
|
||||
"system_packages": "Paquetes",
|
||||
"system_packages_nothing": "No hay paquetes para actualizar.",
|
||||
"system_packages_nothing": "¡Todos los paquetes del sistema están actualizados!",
|
||||
"system_update": "Actualización del sistema",
|
||||
"system_upgrade": "Actualización del sistema",
|
||||
"system_upgrade_btn": "Actualización",
|
||||
|
@ -376,14 +376,14 @@
|
|||
"from_to": "desde %s a %s",
|
||||
"only_highquality_apps": "Solo aplicaciones de alta calidad",
|
||||
"only_decent_quality_apps": "Solo aplicaciones de calidad aceptable",
|
||||
"orphaned": "sin mantenimiento",
|
||||
"orphaned": "No mantenido",
|
||||
"request_adoption": "pendiente de adopción",
|
||||
"request_adoption_details": "Al mantenedor actual le gustaría dejar de mantener esta aplicación. ¡No dude en ofrecerse como el nuevo mantenedor!",
|
||||
"request_help": "necesita ayuda",
|
||||
"app_state_inprogress_explanation": "El mantenedor de esta aplicación declara que aún no está lista para su uso en producción. ¡TENGA CUIDADO!",
|
||||
"app_state_high-quality_explanation": "Esta aplicación está bien integrada en YunoHost. Ha sido (¡y continua siendo!) revisada por especialistas del equipo de aplicaciones de YunoHost. Se puede esperar que sea segura y mantenida a largo plazo.",
|
||||
"app_state_working_explanation": "El mantenedor de esta aplicación declara que «funciona». Significa que debería ser funcional (comparada a nivel de aplicación) pero no está revisada por especialistas necesariamente, puede tener aún problemas o no está totalmente integrada en YunoHost.",
|
||||
"orphaned_details": "Esta aplicación ya no se mantiene. Puede que funcione pero no se actualizará. ¡No dude en venir y reanimarla!",
|
||||
"orphaned_details": "Esta aplicación no se ha mantenido durante bastante tiempo. Todavía puede estar funcionando, pero no recibirá ninguna actualización hasta que alguien se ofrezca como voluntario para encargarse de ello. ¡Siéntase libre de contribuir para revivirlo!",
|
||||
"request_help_details": "Al mantenedor actual le gustaría recibir alguna ayuda con el mantenimiento de esta aplicación. ¡No dude en venir a contribuir!",
|
||||
"tools_rebooting": "Su servidor se está reiniciando. Para volver a la interfaz de administración web necesita esperar a que el servidor esté listo. Puede comprobarlo recargando esta página (F5).",
|
||||
"tools_shutdown_btn": "Apagar",
|
||||
|
@ -466,5 +466,19 @@
|
|||
"last_ran": "Ejecutado por última vez:",
|
||||
"unignore": "Dejar de ignorar",
|
||||
"warnings": "%s avisos",
|
||||
"all": "Todo"
|
||||
"all": "Todo",
|
||||
"diagnosis_first_run": "El diagnostico intentara identificar los errores comunes en diferentes aspectos de su servidor para asegurar que todo funcione de la mejor manera. No te asustes si observas algunas errores justo después de la configuracion de tu servidor. Estos le ayudara a identificar los incidentes y le guiara para corregir los. El diagnostico correrá 2 veces al día y enviara un correo electrónico al administrador si encuentra incidentes.",
|
||||
"since": "desde",
|
||||
"others": "Otros",
|
||||
"run_first_diagnosis": "Procesando el primer diagnostico",
|
||||
"configuration": "Configuración",
|
||||
"catalog": "Catálogo",
|
||||
"app_state_low_quality_explanation": "Asta app esta probablemente funcional, pero puede fallar, no esta completamente integrada con YunoHost, o no respeta las buenas practicas.",
|
||||
"app_state_low_quality": "baja qualidad",
|
||||
"confirm_service_restart": "¿Estás seguro de que quieres reiniciar %s ?",
|
||||
"group_explain_visitors_needed_for_external_client": "Tenga cuidado de que necesita mantener algunas aplicaciones permitidas a los visitantes si tiene la intención de usarlas con clientes externos. Por ejemplo, este es el caso de Nextcloud si desea utilizar un cliente de sincronización en su teléfono inteligente o computadora de escritorio.",
|
||||
"issues": "%s problemas",
|
||||
"unmaintained_details": "Esta aplicación no se ha actualizado durante bastante tiempo y el responsable anterior se ha ido o no tiene tiempo para mantenerla. No dude en consultar el repositorio de aplicaciones para brindar su ayuda",
|
||||
"groups": "Grupos",
|
||||
"restart": "Reiniciar"
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"app_state_inprogress": "ne fonctionne pas encore",
|
||||
"app_state_notworking": "Non fonctionnelle",
|
||||
"app_state_validated": "Validée",
|
||||
"app_state_working": "Fonctionnelle",
|
||||
"app_state_working": "fonctionnelle",
|
||||
"application": "Application",
|
||||
"applications": "Applications",
|
||||
"archive_empty": "L’archive est vide",
|
||||
|
@ -65,7 +65,7 @@
|
|||
"confirm_change_maindomain": "Voulez-vous vraiment changer le domaine principal ?",
|
||||
"confirm_delete": "Voulez-vous vraiment supprimer %s ?",
|
||||
"confirm_firewall": "Voulez-vous vraiment %s le port %s (protocole: %s, connexion: %s)",
|
||||
"confirm_install_custom_app": "AVERTISSEMENT ! L’installation d’applications tierces peut compromettre l’intégrité et la sécurité de votre système. Vous ne devriez probablement PAS l’installer si vous ne savez pas ce que vous faites. Prenez-vous ce risque ?",
|
||||
"confirm_install_custom_app": "ATTENTION ! L’installation d’applications tierces peut compromettre l’intégrité et la sécurité de votre système. Vous ne devriez probablement PAS l’installer si vous ne savez pas ce que vous faites. Prenez-vous ce risque ?",
|
||||
"confirm_install_domain_root": "Vous ne pourrez pas installer d'autres applications sur %s. Continuer ?",
|
||||
"confirm_postinstall": "Vous êtes sur le point de lancer le processus de post-installation sur le domaine %s. Cela peut prendre du temps, *n'interrompez pas l'opération avant la fin*.",
|
||||
"confirm_restore": "Voulez-vous vraiment restaurer %s ?",
|
||||
|
@ -139,7 +139,7 @@
|
|||
"hook_data_home": "Données de l’utilisateur",
|
||||
"hook_data_home_desc": "Les données de l’utilisateur situées dans /home/USER",
|
||||
"hook_data_mail": "Courriel",
|
||||
"hook_data_mail_desc": "Les courriels qui sont stockés sur le serveur",
|
||||
"hook_data_mail_desc": "Courriels stockés sur le serveur",
|
||||
"hostname": "Nom d'hôte",
|
||||
"id": "ID",
|
||||
"inactive": "Inactif",
|
||||
|
@ -224,7 +224,7 @@
|
|||
"select_all": "Tout sélectionner",
|
||||
"select_none": "Tout désélectionner",
|
||||
"service_log": "Journal de %s",
|
||||
"service_start_on_boot": "Lancer au démarrage : ",
|
||||
"service_start_on_boot": "Lancement au démarrage :",
|
||||
"service_status": "Statut : ",
|
||||
"services": "Services",
|
||||
"services_list": "Liste des services",
|
||||
|
@ -239,11 +239,11 @@
|
|||
"swap": "Espace d’échange",
|
||||
"system": "Système",
|
||||
"system_apps": "Applications",
|
||||
"system_apps_nothing": "Il n'y a aucune application à mettre à jour.",
|
||||
"system_apps_nothing": "Toutes les applications sont à jour !",
|
||||
"system_delayed_upgrade": "Mise à jour différée",
|
||||
"system_delayed_upgrade_warning": "<b>%s</b> sera mis-à-jour automatiquement durant l'heure suivante.",
|
||||
"system_packages": "Paquets",
|
||||
"system_packages_nothing": "Aucun paquet n'est à mettre à jour.",
|
||||
"system_packages": "Paquets système",
|
||||
"system_packages_nothing": "Tous les packages système sont à jour !",
|
||||
"system_update": "Mettre à jour le système",
|
||||
"system_upgrade": "Mise à jour du système",
|
||||
"system_upgrade_btn": "Mettre à jour",
|
||||
|
@ -318,7 +318,7 @@
|
|||
"certificate_authority": "Autorité de certification",
|
||||
"validity": "Validité",
|
||||
"domain_is_eligible_for_ACME": "Ce domaine semble correctement configuré pour installer un certificat Let’s Encrypt !",
|
||||
"domain_not_eligible_for_ACME": "Ce domaine ne semble pas prêt pour installer un certificat Let’s Encrypt. Veuillez vérifier votre configuration DNS et l’accessibilité HTTP de votre serveur.",
|
||||
"domain_not_eligible_for_ACME": "Ce domaine ne semble pas prêt pour installer un certificat Let’s Encrypt. Veuillez vérifier votre configuration DNS et l’accessibilité HTTP de votre serveur. Les sections 'enregistrement DNS' et 'Web' dans <a href='#/diagnosis'>la page de diagnostic</a> peuvent vous aider à comprendre ce qui est mal configurée.",
|
||||
"install_letsencrypt_cert": "Installer un certificat Let’s Encrypt",
|
||||
"manually_renew_letsencrypt_message": "Le certificat sera renouvelé automatiquement durant les 15 derniers jours de sa validité. Vous pouvez le renouveler manuellement si vous le souhaitez (non recommandé).",
|
||||
"manually_renew_letsencrypt": "Renouveler manuellement maintenant",
|
||||
|
@ -399,7 +399,7 @@
|
|||
"error_connection_interrupted": "Le serveur a fermé la connexion au lieu d’y répondre. Est-ce que Nginx a été redémarré ou est-ce que l'API YunoHost s'est-elle arrêtée pour diverses raisons ? (code/message d’erreur : %s)",
|
||||
"experimental_warning": "Attention : cette fonctionnalité est expérimentale et ne doit pas être considérée comme stable, vous ne devriez pas l’utiliser à moins que vous ne sachiez ce que vous faites...",
|
||||
"good_practices_about_admin_password": "Vous êtes maintenant sur le point de définir un nouveau mot de passe administrateur. Le mot de passe doit comporter au moins 8 caractères — bien qu’il soit recommandé d’utiliser un mot de passe plus long (c’est-à-dire une phrase secrète) et/ou d’utiliser différents types de caractères (majuscules, minuscules, chiffres et caractères spéciaux).",
|
||||
"good_practices_about_user_password": "Vous êtes maintenant sur le point de définir un nouveau mot de passe utilisateur. Le mot de passe doit comporter au moins 8 caractères - bien qu’il soit recommandé d’utiliser un mot de passe plus long (c’est-à-dire une phrase secrète) et/ou d’utiliser différents types de caractères tels que : majuscules, minuscules, chiffres et caractères spéciaux.",
|
||||
"good_practices_about_user_password": "Vous êtes maintenant sur le point de définir un nouveau mot de passe pour l'utilisateur. Le mot de passe doit comporter au moins 8 caractères - bien qu’il soit recommandé d’utiliser un mot de passe plus long (c’est-à-dire une phrase secrète) et/ou d’utiliser différents types de caractères tels que : majuscules, minuscules, chiffres et caractères spéciaux.",
|
||||
"level": "niveau",
|
||||
"only_official_apps": "Applications officielles uniquement",
|
||||
"only_working_apps": "Applications fonctionnelles uniquement",
|
||||
|
@ -422,28 +422,28 @@
|
|||
"logs_more": "Afficher plus de lignes",
|
||||
"search_for_apps": "Recherche des applications…",
|
||||
"unmaintained": "Non maintenue",
|
||||
"purge_user_data_checkbox": "Purger les données de % s ? (Cela supprimera le contenu de ses répertoires home et mail.)",
|
||||
"purge_user_data_checkbox": "Purger les données de l'utilisateur %s ? (Cela supprimera le contenu de ses répertoires home et mail.)",
|
||||
"purge_user_data_warning": "La purge des données de l’utilisateur n’est pas réversible. Assurez-vous de savoir ce que vous faites !",
|
||||
"version": "Version",
|
||||
"confirm_update_system": "Voulez-vous vraiment mettre à jour tous les paquets système ?",
|
||||
"app_state_inprogress_explanation": "Le mainteneur de cette application a indiqué qu'elle n'est pas encore prête pour une utilisation en production. FAITES ATTENTION !",
|
||||
"app_state_notworking_explanation": "Le mainteneur de cette application a déclaré celle-ci comme \"non fonctionnelle\" SON INSTALLATION PEUT CASSER VOTRE SYSTÈME !",
|
||||
"app_state_inprogress_explanation": "Le responsable de cette application a indiqué qu'elle n'est pas encore prête pour une utilisation en production. SOYEZ VIGILANT(E) !",
|
||||
"app_state_notworking_explanation": "Le responsable de cette application a déclaré celle-ci comme \"non fonctionnelle\". SON INSTALLATION POURRAIT CASSER VOTRE SYSTÈME !",
|
||||
"app_state_high-quality": "Haute qualité",
|
||||
"app_state_high-quality_explanation": "Cette application est bien intégrée dans YunoHost. Elle a été (et est !) revue par l'équipe applicative de YunoHost. On peut s'attendre à ce qu'elle soit sûre et maintenue sur le long terme.",
|
||||
"app_state_working_explanation": "Le responsable de cette application l'a déclarée comme fonctionnelle. Cela signifie qu'elle doit être utilisable (c.f. niveau de l'application) mais n'est pas nécessairement revue, elle peut encore contenir des bogues ou bien n'est pas entièrement intégrée dans YunoHost.",
|
||||
"app_state_high-quality_explanation": "Cette application est bien intégrée à YunoHost. Elle a été (et est !) revue par l'équipe applicative de YunoHost. On peut s'attendre à ce qu'elle soit sûre et maintenue sur le long terme.",
|
||||
"app_state_working_explanation": "Le responsable de cette application l'a déclarée comme 'fonctionnelle'. Cela signifie qu'elle doit fonctionner (voir son niveau d'intégration) mais n'est pas nécessairement revue, elle peut encore contenir des bugs ou bien n'est pas entièrement intégrée à YunoHost.",
|
||||
"hook_conf_ynh_currenthost": "Domaine principal actuel",
|
||||
"license": "Licence",
|
||||
"maintained": "Maintenue",
|
||||
"maintained_details": "Cette application a été maintenue par son responsable au cours des tout derniers mois.",
|
||||
"only_highquality_apps": "Uniquement des applications de haute qualité",
|
||||
"maintained_details": "Cette application a été maintenue par son responsable au cours des derniers mois.",
|
||||
"only_highquality_apps": "Uniquement des applications de haute-qualité",
|
||||
"only_decent_quality_apps": "Seulement des applications d'une qualité décente",
|
||||
"orphaned": "Non maintenue",
|
||||
"orphaned_details": "Cette application n'a pas été maintenue depuis un certain temps. Il peut encore fonctionner, mais ne recevra aucune mise à niveau jusqu'à ce que quelqu'un se porte volontaire pour s'en occuper. N'hésitez pas à contribuer à la faire revivre !",
|
||||
"request_adoption": "en attente de repreneur",
|
||||
"request_adoption_details": "Le responsable actuel aimerait arrêter de maintenir cette application. N'hésitez pas à vous proposer comme nouveau responsable !",
|
||||
"request_adoption": "en attente de responsable",
|
||||
"request_adoption_details": "Le responsable actuel ne souhaite plus s'occuper de cette application. N'hésitez pas à vous proposer comme nouveau responsable !",
|
||||
"request_help": "besoin d'assistance",
|
||||
"request_help_details": "Le responsable actuel aimerait de l'aide pour la maintenance de cette application. N'hésitez pas à y contribuer !",
|
||||
"advanced": "Avancée",
|
||||
"advanced": "Avancé",
|
||||
"from_to": "de %s à %s",
|
||||
"group_name": "Nom du groupe",
|
||||
"nobody": "Personne",
|
||||
|
@ -473,10 +473,18 @@
|
|||
"configuration": "Configuration",
|
||||
"since": "depuis",
|
||||
"all": "Tout",
|
||||
"app_state_low_quality": "basse qualité",
|
||||
"app_state_low_quality": "faible qualité",
|
||||
"app_state_low_quality_explanation": "Cette application peut être fonctionnelle, mais peut toujours contenir des problèmes, ou n'est pas entièrement intégrée à YunoHost, ou elle ne respecte pas les bonnes pratiques.",
|
||||
"catalog": "Catalogue",
|
||||
"others": "Autres",
|
||||
"diagnosis_first_run": "La fonctionnalité de diagnostic va tenter de trouver certains problèmes communs sur différents aspects de votre serveur pour être sûr que tout fonctionne normalement. Merci de ne pas paniquer si vous voyez une multitude d'erreurs après avoir configuré votre serveur : la fonctionnalité est précisément prévue pour les identifier et vous aider à les résoudre. Le diagnostic sera également effectué deux fois par jour et enverra un mail à l'administrateur si des erreurs sont détectées.",
|
||||
"run_first_diagnosis": "Démarrer le diagnostic initial"
|
||||
"run_first_diagnosis": "Démarrer le diagnostic initial",
|
||||
"confirm_service_restart": "Êtes-vous certain de vouloir redémarrer %s ?",
|
||||
"groups": "Groupes",
|
||||
"restart": "Redémarrer",
|
||||
"unmaintained_details": "Cette application n'a pas été mise à jour depuis un bon moment et le responsable précédent est parti ou n'a pas le temps de maintenir cette application. N'hésitez pas à consulter le référentiel des applications pour apporter votre aide",
|
||||
"group_explain_visitors_needed_for_external_client": "Veillez à ce que certaines applications soient autorisées pour les visiteurs si vous avez l'intention de les utiliser avec des clients externes. Par exemple, c'est le cas pour Nextcloud si vous souhaitez avoir l'intention d'utiliser un client de synchronisation sur votre smartphone ou ordinateur de bureau.",
|
||||
"issues": "%s problèmes",
|
||||
"operation_failed_explanation": "L'opération a échoué ! Veuillez-nous excuser pour ça :( Vous pouvez essayer de <a href='https://yunohost.org/help'>demander de l'aide</a>. Merci de fournir *le log complet* de l'opération pour les personnes qui vont vous aider. Vous pouvez cliquer sur le bouton vert 'Partager avec Yunopaste'. Quand vous partagez les logs, YunoHost essaie automatiquement d'anonymiser les informations privées comme le nom de domaine et l'adresses IP.",
|
||||
"diagnosis_explanation": "La fonctionnalité de diagnostic va tenter de trouver certains problèmes communs sur différents aspects de votre serveur pour être sûr que tout fonctionne normalement. Le diagnostic sera également effectué deux fois par jour et enverra un mail à l'administrateur si des erreurs sont détectées. À noter que certains tests ne seront pas montrés si vous n'utilisez pas certaines fonctions spécifiques (XMPP, par exemple) ou s'ils échouent à cause d'une configuration trop complexe. Dans ce cas, et si vous savez ce que vous avez modifié, vous pouvez ignorer les problèmes et les avertissements correspondantes."
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"add": "Aggiungi",
|
||||
"administration_password": "Password amministrazione",
|
||||
"allowed_users": "Utenti consentiti",
|
||||
"api_not_responding": "Le API non rispondono",
|
||||
"api_not_responding": "Le API YunoHost non rispondono. Forse 'yunohost-api' è giù o è stato riavviato?",
|
||||
"app_access": "Accesso",
|
||||
"app_access_addall_btn": "Attiva l'accesso per tutti",
|
||||
"app_access_addall_desc": "Tutti gli utenti esistenti avranno accesso a %s.",
|
||||
|
@ -425,5 +425,26 @@
|
|||
"purge_user_data_checkbox": "Eliminare i dati di %s? (Questo rimuoverà il contenuto delle sue cartelle home e mail.)",
|
||||
"purge_user_data_warning": "L'eliminazione dei dati utente non è annullabile. Assicurati di sapere cosa stai facendo!",
|
||||
"hook_conf_ynh_currenthost": "Dominio principale attuale",
|
||||
"confirm_update_system": "Sei sicuro di voler aggiornare tutti i pacchetti di sistema?"
|
||||
"confirm_update_system": "Sei sicuro di voler aggiornare tutti i pacchetti di sistema?",
|
||||
"app_state_inprogress_explanation": "Questo maintainer di questa applicazione ha dichiarato che l'applicazione non è ancora pronta per l'uso in produzione. ATTENZIONE!",
|
||||
"app_state_notworking_explanation": "Questo maintainer di questa applicazione ha dichiarato che \"non funziona\". SI ROMPERÀ IL VOSTRO SISTEMA!",
|
||||
"app_state_high-quality": "alta qualità",
|
||||
"group_add_permission": "Aggiungere un permesso",
|
||||
"group_explain_all_users": "Questo è un gruppo speciale contenente tutti gli account di tutti gli utenti sul server",
|
||||
"group": "Gruppo",
|
||||
"group_name": "Nome del gruppo",
|
||||
"group_all_users": "Tutti gli utenti",
|
||||
"group_visitors": "Visitori",
|
||||
"group_format_name_help": "È possibile utilizzare caratteri alfanumerici e spazio",
|
||||
"group_add_member": "Aggiungere un utente",
|
||||
"app_state_high-quality_explanation": "Questa applicazione è ben integrata con YunoHost. E 'stato (ed è!) peer-reviewed dal team di YunoHost app. Ci si può aspettare che sia sicuro e mantenuto a lungo termine.",
|
||||
"details": "Dettagli",
|
||||
"diagnosis_experimental_disclaimer": "Siate consapevoli che la funzione di diagnosi è ancora sperimentale e in fase di perfezionamento, e potrebbe non essere completamente affidabile.",
|
||||
"errors": "%s errori",
|
||||
"everything_good": "Tutto bene!",
|
||||
"from_to": "da %s a %s",
|
||||
"configuration": "Configuratione",
|
||||
"advanced": "Avanzate",
|
||||
"app_state_working_explanation": "Il manutentore di questa applicazione lo ha dichiarato \"funzionante\". Significa che dovrebbe essere funzionale (c.f. livello di applicazione) ma non è necessariamente sottoposta a peer-reviewing, può ancora contenere problemi o non è pienamente integrata con YunoHost.",
|
||||
"group_new": "Nuovo gruppo"
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"configuration": "कन्फिगरेसन",
|
||||
"close": "बन्द",
|
||||
"check": "जाँच गर्नुहोस्",
|
||||
"cancel": "रद्द",
|
||||
"cancel": "रद्द गर्नुहोस्",
|
||||
"both": "दुबै",
|
||||
"begin": "सुरु गर्नुहोस्",
|
||||
"backups_no": "कुनै ब्याकअप छैन",
|
||||
|
@ -54,5 +54,8 @@
|
|||
"remove": "हटाउनुहोस्",
|
||||
"add": "थप्नुहोस्",
|
||||
"active": "सक्रिय",
|
||||
"action": "कार्य"
|
||||
"action": "कार्य",
|
||||
"password": "पासवर्ड",
|
||||
"ok": "ठिक छ",
|
||||
"logged_out": "लग आउट"
|
||||
}
|
||||
|
|
|
@ -241,7 +241,7 @@
|
|||
"save": "Salvagardar",
|
||||
"select_user": "Causissètz un utilizaire",
|
||||
"service_log": "Jornal de %s",
|
||||
"service_start_on_boot": "Lançar en aviar : ",
|
||||
"service_start_on_boot": "Lançar en aviar",
|
||||
"service_status": "Estatut : ",
|
||||
"services": "Servicis",
|
||||
"services_list": "Lista dels servicis",
|
||||
|
@ -257,7 +257,7 @@
|
|||
"swap": "Espaci d’escambi",
|
||||
"system": "Sistèma",
|
||||
"system_apps": "Aplicacions",
|
||||
"system_apps_nothing": "I a pas cap d’aplicacion de metre a jorn.",
|
||||
"system_apps_nothing": "Totas las aplicacions son a jorn !",
|
||||
"system_delayed_upgrade": "Mesa a jorn reportada",
|
||||
"system_delayed_upgrade_warning": "<b>%s</b> serà mes a jorn dins l’ora venenta.",
|
||||
"system_packages": "Paquets sistèma",
|
||||
|
@ -374,7 +374,7 @@
|
|||
"certificate_alert_about_to_expire": "Atencion : lo certificat actual es a man d’expirar ! Serà pas renovat automaticament !",
|
||||
"certificate_alert_great": "Perfièch ! Sètz a utilizar un certificat Let’s Encrypt valid !",
|
||||
"certificate_old_letsencrypt_app_conflict": "L’aplicacion « letsencrypt » es actualament installada e es en conflicte amb aquesta foncionalitat. Mercés de la desinstallar per utilizar la novèla interfàcia de gestion dels certificats.",
|
||||
"domain_not_eligible_for_ACME": "Aqueste domeni sembla pas prèst per un certificat Let’s Encrypt. Mercés de verificar la configuracion DNS e l’accessibilitat del servidor HTTP.",
|
||||
"domain_not_eligible_for_ACME": "Aqueste domeni sembla pas prèst per un certificat Let’s Encrypt. Mercés de verificar la configuracion DNS e l’accessibilitat del servidor HTTP. Las seccions « enregistrament DNS » e « Web » de <a href='#/diagnosis'>la pagina de diagnostic</a> pòt vos ajudar a comprendre çò qu’es pas configurat coma cal.",
|
||||
"manually_renew_letsencrypt_message": "Lo certificat serà renovat automaticament pendent los 15 darrièrs jorns de validitat. Podètz lo renovar manualament se volètz. (Pas recomandat).",
|
||||
"meltdown": "Sètz vulnerable a la falha de seguretat critica <a target=\"_blank\" href=\"https://meltdownattack.com/\">meltdown</a>. Per dire de resòlvre aqueste problèma, vos cal <a href=\"#/update\">actualizar vòstre sistèma</a> puèi <a href=\"#/tools/reboot\"> lo tornar aviar</a> per cargar lo nòu nuclèu linux.",
|
||||
"revert_to_selfsigned_cert_message": "S’o volètz vertadièrament, podètz tornar installar lo certificat auto-signat. (Pas recomandat)",
|
||||
|
@ -470,5 +470,10 @@
|
|||
"all": "Totas",
|
||||
"app_state_low_quality": "qualitat bassa",
|
||||
"catalog": "Catalòg",
|
||||
"others": "Autras"
|
||||
"others": "Autras",
|
||||
"run_first_diagnosis": "Executar lo diagnostic inicial",
|
||||
"confirm_service_restart": "Volètz vertadièrament reaviar %s ?",
|
||||
"restart": "Reaviar",
|
||||
"groups": "Grops",
|
||||
"issues": "%s problèmas"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"password": "hasło",
|
||||
"password": "Hasło",
|
||||
"action": "Akcja",
|
||||
"active": "Aktywny",
|
||||
"add": "Dodaj",
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
"free": "Livre",
|
||||
"fs_type": "Tipo de Sistema de Ficheiros",
|
||||
"gateway": "Gateway: ",
|
||||
"hook_conf_ldap": "LDAP",
|
||||
"hook_conf_ldap": "Base de dados LDAP",
|
||||
"hook_conf_nginx": "Nginx",
|
||||
"hook_conf_ssh": "SSH",
|
||||
"hook_conf_ssowat": "SSOwat",
|
||||
|
@ -224,7 +224,7 @@
|
|||
"yes": "Sim",
|
||||
"cancel": "Cancelar",
|
||||
"remove": "Remover",
|
||||
"api_not_responding": "API não está respondendo",
|
||||
"api_not_responding": "A API YunoHost não está respondendo. Talvez 'yunohost-api' esteja desligada ou foi reiniciada?",
|
||||
"app_change_label": "Mudar etiqueta",
|
||||
"app_change_url": "Mudar URL",
|
||||
"app_debug_no_logs": "Logs da aplicação não estão disponíveis",
|
||||
|
@ -243,7 +243,7 @@
|
|||
"app_no_actions": "Esta aplicação não tem nenhuma ação",
|
||||
"app_repository": "Origem da aplicação: ",
|
||||
"app_state": "Estado da aplicação: ",
|
||||
"app_state_inprogress": "Em curso",
|
||||
"app_state_inprogress": "ainda não está funcionando",
|
||||
"app_state_inprogress_explanation": "O responsável desta aplicação afirmou que não está pronta ainda para ser usada em produção. TENHA CUIDADO!",
|
||||
"app_state_notworking": "Não funcionando",
|
||||
"app_state_notworking_explanation": "O responsável desta aplicação afirmou que não está funcionando. INSTALANDO-A PODE QUEBRAR SEU SISTEMA!",
|
||||
|
@ -318,5 +318,6 @@
|
|||
"diagnosis_experimental_disclaimer": "Esteja ciente de que a ferramenta de diagnóstico ainda é experimental e está em fase de aperfeiçoamento, e pode não ser totalmente confiável.",
|
||||
"details": "Detalhes",
|
||||
"catalog": "Catálogo",
|
||||
"configuration": "Configuração"
|
||||
"configuration": "Configuração",
|
||||
"confirm_service_restart": "Tem certeza que deseja reiniciar %s?"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"add": "Ekle",
|
||||
"administration_password": "Yönetici parolası",
|
||||
"api_not_responding": "YunoHost API'si cevap vermiyor. Belki 'yunohost-api' çalışmıyor veya yeniden başlatıldı?",
|
||||
"api_not_responding": "YunoHost API'si yanıt vermiyor. Belki 'yunohost-api' çalışmıyor veya yeniden başlatıldı?",
|
||||
"both": "İkisi birden",
|
||||
"cancel": "İptal etmek",
|
||||
"close": "Kapat",
|
||||
|
@ -15,13 +15,13 @@
|
|||
"delete": "Sil",
|
||||
"description": "Açıklama",
|
||||
"disable": "Devredışı bırak",
|
||||
"domain": "Domain",
|
||||
"domain": "Alan adı",
|
||||
"domain_name": "Domain ismi",
|
||||
"domains": "Domainler",
|
||||
"enable": "Devreye al",
|
||||
"error_modify_something": "Bir şeyleri değiştirmelisiniz",
|
||||
"error_occured": "Bir hata oluştu, tekrar deneyiniz",
|
||||
"error_retrieve_feed": "Akış alınamadı : %s",
|
||||
"error_retrieve_feed": "Feed alınamadı:% s. Tarayıcınızın bu isteği yerine getirmesini engelleyen bir eklentiniz olabilir (veya web sitesi kapalı).",
|
||||
"error_select_domain": "Domain belirtmelisiniz",
|
||||
"error_server": "Sunucu hatası",
|
||||
"home": "Başlangıç",
|
||||
|
@ -80,5 +80,24 @@
|
|||
"remove": "Kaldır",
|
||||
"advanced": "Gelişmiş",
|
||||
"active": "Etkin",
|
||||
"action": "Eylem"
|
||||
"action": "Eylem",
|
||||
"app_state_notworking_explanation": "Bu uygulamanın bu geliştiricisi 'çalışmıyor' olarak ilan etti. SİSTEMİNİZİ KIRACAK!",
|
||||
"app_install_custom_no_manifest": "Manifest.json dosyası yok",
|
||||
"app_state_inprogress_explanation": "Bu uygulamanın bu sürdürücü, bu uygulamanın henüz üretim kullanımına hazır olmadığını açıkladı. DİKKATLİ OL!",
|
||||
"app_state_high-quality_explanation": "Bu uygulama YunoHost ile iyi entegre edilmiştir. YunoHost uygulama ekibi tarafından hakemli bir incelemedir. Güvenli olması ve uzun vadede korunması beklenebilir.",
|
||||
"last_ran": "Son sefer:",
|
||||
"app_state_high-quality": "yüksek kalite",
|
||||
"app_info_uninstall_desc": "Bu uygulamayı kaldırın.",
|
||||
"app_state_low_quality": "Düşük kalite",
|
||||
"app_state_low_quality_explanation": "Bu uygulama işlevsel olabilir, ancak yine de sorunlar içerebilir veya YunoHost ile tamamen entegre değildir veya iyi uygulamalara uymaz.",
|
||||
"purge_user_data_checkbox": "%s verileri temizlensin mi? (Bu işlem ana ve posta dizinlerinin içeriğini silecektir.)",
|
||||
"app_info_change_url_disabled_tooltip": "Bu özellik bu uygulamada henüz uygulanmadı",
|
||||
"app_state_notworking": "çalışmıyor",
|
||||
"app_info_default_desc": "Alan adı kökünü bu uygulamaya yönlendirin (% s).",
|
||||
"app_state_inprogress": "Henüz çalışmıyor",
|
||||
"app_info_changeurl_desc": "Bu uygulamanın erişim URL'sini değiştirin (alan adı ve / veya yol).",
|
||||
"app_install_cancel": "Yükleme iptal edildi.",
|
||||
"app_info_changelabel_desc": "Portaldaki uygulama etiketini değiştirin.",
|
||||
"app_make_default": "Varsayılan yap",
|
||||
"app_no_actions": "Bu uygulamanın herhangi bir eylemi yok"
|
||||
}
|
||||
|
|
|
@ -11,5 +11,6 @@
|
|||
"logged_out": "登出",
|
||||
"cancel": "取消",
|
||||
"ok": "好",
|
||||
"password": "密码"
|
||||
"password": "密码",
|
||||
"logged_in": "登录"
|
||||
}
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/apps">{{t 'applications'}}</a>
|
||||
<a href="#/apps/install">{{t 'install'}}</a>
|
||||
<a href="#/apps/catalog">{{t 'catalog'}}</a>
|
||||
<a href="#/apps/catalog/{{category.id}}">{{category.title}}</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="input-group" id="app-filter-input">
|
||||
<span class="input-group-addon"><i class="fas fa-search"></i></span>
|
||||
<div class="input-group-btn"><a class="btn btn-primary" href="#/apps/catalog"><i class="fa-arrow-left"></i></a></div>
|
||||
<span class="input-group-addon"><i class="fa-fw fa-{{category.icon}}"></i> {{category.title}}</span>
|
||||
<span class="input-group-addon" style="background: white;border: none;"> </span>
|
||||
<span class="input-group-addon"><i class="fa-search"></i></span>
|
||||
<input type="text" id="filter-app-cards" class="form-control" role="textbox" placeholder="{{t 'search_for_apps'}}" aria-describedby="basic-addon0"/>
|
||||
<div class="input-group-btn">
|
||||
<button type="button" role="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span id="app-cards-list-filter-text">{{t 'only_decent_quality_apps'}}</span> <span class="caret"></span>
|
||||
</button>
|
||||
<ul id="dropdownFilter" class="dropdown-menu" data-filter="decentQuality" role="menu">
|
||||
|
@ -24,32 +28,51 @@
|
|||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="list-group grid">
|
||||
<div class="subtag-selector">
|
||||
{{#if category.subtags}}
|
||||
<button class="btn btn-default active" data-subtag="all">{{t 'all'}}</button>
|
||||
{{/if}}
|
||||
{{#category.subtags}}
|
||||
<button class="btn btn-default" data-subtag="{{id}}">{{title}}</button>
|
||||
{{/category.subtags}}
|
||||
{{#if category.subtags}}
|
||||
<button class="btn btn-default" data-subtag="others">{{t 'others'}}</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div id="apps" class="list-group grid">
|
||||
{{#apps}}
|
||||
<div class="app-card panel panel-default {{status}} {{state}} {{isWorking}} {{isHighQuality}} {{decentQuality}} {{level}}-level">
|
||||
<div class="app-card panel panel-default {{state}} {{isWorking}} {{isHighQuality}} {{decentQuality}} {{level}}-level" data-subtags="{{#subtags}}{{.}}{{#unless @last}},{{/unless}}{{/subtags}}">
|
||||
<div class="panel-body">
|
||||
<h2 class="app-title">{{name}}</h2>
|
||||
<div class="category">
|
||||
<span class="label label-{{stateColor}} label-as-badge app-state" title="{{t (concat 'app_state_' state '_explanation') }}">{{t (concat 'app_state_' state) }}</span>
|
||||
<a target="_BLANK" href="https://yunohost.org/#/packaging_apps_levels"><span class="label label-{{levelColor}} label-as-badge app-level" title="{{t 'app_level'}}">{{t 'level'}} {{levelFormatted}}</span></a>
|
||||
<span class="label label-{{maintainedColor}} label-as-badge maintained-status" title="{{t (concat maintained '_details') }}"> {{t maintained}}</span>
|
||||
</div>
|
||||
<div class="app-card-desc">{{description}}</div>
|
||||
<h2 class="app-title">
|
||||
{{manifest.name}}
|
||||
{{#if (eq state 'working') }}
|
||||
{{#if (eq decentQuality 'badQuality')}}
|
||||
<span class="label label-warning label-as-badge app-state" title="{{t 'app_state_low_quality_explanation' }}">{{t 'app_state_low_quality' }}</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<span class="label label-{{stateColor}} label-as-badge app-state" title="{{t (concat 'app_state_' state '_explanation') }}">{{t (concat 'app_state_' state) }}</span>
|
||||
{{/if}}
|
||||
</h2>
|
||||
<div class="app-card-desc">{{manifest.description}}</div>
|
||||
</div>
|
||||
<div class="app-card-date-maintainer">
|
||||
<i class="fa-refresh"></i> {{formatDate updateDate day="numeric" month="long" year="numeric"}} -
|
||||
<span title="{{t 'current_maintainer_title'}}" class="maintained"></span><i class="fa-user"></i> {{manifest.maintainer}}</span>
|
||||
{{#if (eq maintainedColor 'danger') }}
|
||||
<span class="text text-warning maintained-status" title="{{t (concat maintained '_details') }}"><i class="fa-fw fa-warning"></i> {{t maintained}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{git.url}}" target="_BLANK" type="button" role="button" class="btn btn-default col-xs-4">
|
||||
<i class="fa-globe"></i> Code
|
||||
<i class="fa-fw fa-code"></i> Code
|
||||
</a>
|
||||
<a href="{{git.url}}/blob/master/README.md" target="_BLANK" type="button" role="button" class="btn btn-default col-xs-4">
|
||||
<i class="fa-book"></i> Readme
|
||||
<i class="fa-fw fa-book"></i> Readme
|
||||
</a>
|
||||
{{#installable}}
|
||||
<a href="#/apps/install/{{id}}" type="button" role="button" class="btn btn-{{installColor}} col-xs-4 active">
|
||||
<i class="fa-plus"></i> {{t 'install'}}{{^isSafe}} <i class="fa-warning"></i>{{/isSafe}}
|
||||
<a href="#/apps/install/{{manifest.id}}" type="button" role="button" class="btn btn-{{installColor}} col-xs-4 active">
|
||||
<i class="fa-fw fa-plus"></i> {{t 'install'}}{{^isSafe}} <i class="fa-fw fa-warning"></i>{{/isSafe}}
|
||||
</a>
|
||||
{{/installable}}
|
||||
{{^installable}}
|
||||
|
@ -60,28 +83,29 @@
|
|||
{{/apps}}
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div id="install-custom-app" class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title"><span class="fa-fw fa-download"></span> {{t 'custom_app_install'}}</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="alert alert-warning">
|
||||
<span class="fa-warning"></span>
|
||||
<span class="fa-fw fa-warning"></span>
|
||||
{{t 'confirm_install_custom_app'}}
|
||||
</p>
|
||||
<form action="#/apps/install/custom" method="POST" class="form-horizontal">
|
||||
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group has-feedback">
|
||||
<label for="url" class="col-sm-12">{{t 'url'}}</label>
|
||||
<div class="col-sm-12">
|
||||
<input type="url" id="url" name="url" class="form-control" value="" placeholder="https://github.com/USER/REPOSITORY" required pattern="^https://github.com/[a-zA-Z0-9-_.]+/[a-zA-Z0-9-_.]+[/]?$">
|
||||
<p class="text-warning">
|
||||
<span class="fa-github"></span> {{t 'custom_app_url_only_github'}}
|
||||
<span class="fa-fw fa-github"></span> {{t 'custom_app_url_only_github'}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="text-center">
|
||||
<input type="submit" role="button" class="btn btn-success slide" value="{{t 'install'}}">
|
||||
<a role="button" class="btn btn-success slide">{{t 'install'}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
23
src/views/app/app_catalog_home.ms
Normal file
23
src/views/app/app_catalog_home.ms
Normal file
|
@ -0,0 +1,23 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/apps">{{t 'applications'}}</a>
|
||||
<a href="#/apps/catalog">{{t 'catalog'}}</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div id="category-selector" class="list-group grid">
|
||||
<a class="app-category-card panel panel-default" href="#/apps/catalog/all">
|
||||
<div class="panel-body">
|
||||
<h2 class="app-category-title" style="padding-top: 1em;"><span class="fa-fw fa-search"></span> {{t 'all_apps'}}</h2>
|
||||
</div>
|
||||
</a>
|
||||
{{#categories}}
|
||||
<a class="app-category-card panel panel-default" href="#/apps/catalog/{{id}}">
|
||||
<div class="panel-body">
|
||||
<h2 class="app-category-title"><span class="fa-fw fa-{{icon}}"></span> {{title}}</h2>
|
||||
<h3 class="app-category-card-desc">{{description}}</h3>
|
||||
</div>
|
||||
</a>
|
||||
{{/categories}}
|
||||
</div>
|
|
@ -1,39 +0,0 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/apps">{{t 'applications'}}</a>
|
||||
<a href="#/apps/{{name}}">{{label}}</a>
|
||||
<a href="#/apps/{{name}}/debug">{{t 'debug'}}</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
{{#if services}}
|
||||
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
|
||||
{{#services}}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="heading-{{ @index }}">
|
||||
<h2 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse-{{ @index }}" aria-expanded="true" aria-controls="collapse-{{ @index }}">
|
||||
<span class="fa-fw fa-info-circle"></span> {{t 'service_log' name }}
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapse-{{ @index }}" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading-{{ @index }}">
|
||||
<div class="panel-body">
|
||||
{{#logs}}
|
||||
<h3>{{file_name}}</h3>
|
||||
<pre id="service-log-{{ @index }}" class="service-log">{{file_content}}</pre>
|
||||
<button data-paste-content="#service-log-{{ @index }}"><i class="fa-cloud-upload"></i> {{t 'upload'}}</button>
|
||||
{{/logs}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/services}}
|
||||
</div>
|
||||
|
||||
{{else}}
|
||||
<div class="alert alert-warning">
|
||||
<span class="fa-exclamation-triangle"></span>
|
||||
{{t 'app_debug_no_logs'}}
|
||||
</div>
|
||||
{{/if}}
|
|
@ -22,7 +22,7 @@
|
|||
<dt>{{t 'version'}}</dt>
|
||||
<dd>{{version}}</dd>
|
||||
<dt>{{t 'multi_instance'}}</dt>
|
||||
<dd>{{manifest.multi_instance}}</dd>
|
||||
<dd>{{supports_multi_instance}}</dd>
|
||||
<dt>{{t 'install_time'}}</dt>
|
||||
<dd>{{formatTime install_time day="numeric" month="long" year="numeric" hour="numeric" minute="numeric"}}</dd>
|
||||
{{#if settings.domain}}
|
||||
|
@ -57,14 +57,14 @@
|
|||
<hr>
|
||||
<div class="container">
|
||||
<p>{{t 'app_info_default_desc' settings.domain}}</p>
|
||||
<a role="button" href="#/apps/{{settings.id}}/default" class="btn btn-success slide">
|
||||
<button class="btn btn-success" data-action="set-as-default" data-app="{{settings.id}}">
|
||||
<span class="fa-star"></span> {{t 'app_make_default'}}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="container">
|
||||
<p>{{t 'app_info_changeurl_desc' settings.domain}}</p>
|
||||
{{#if change_url}}
|
||||
{{#if supports_change_url}}
|
||||
<a href="#/apps/{{settings.id}}/changeurl" role="button" class="btn btn-info slide">
|
||||
<span class="fa-exchange"></span> {{t 'app_change_url'}}
|
||||
</a>
|
||||
|
@ -79,16 +79,9 @@
|
|||
<hr>
|
||||
<div class="container">
|
||||
<p>{{t 'app_info_uninstall_desc'}}</p>
|
||||
<a href="#/apps/{{settings.id}}/uninstall" role="button" class="btn btn-danger slide back">
|
||||
<button class="btn btn-danger slide back" data-action="uninstall" data-app="{{settings.id}}">
|
||||
<span class="fa-trash-o"></span> {{t 'uninstall'}}
|
||||
</a>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="container">
|
||||
<p>{{t 'app_info_debug_desc'}}</p>
|
||||
<a href="#/apps/{{settings.id}}/debug" role="button" class ="btn btn-warning slide">
|
||||
<span class="fa-bug"></span> {{t 'app_debug_tab'}}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/apps">{{t 'applications'}}</a>
|
||||
<a href="#/apps/install">{{t 'install'}}</a>
|
||||
<a href="#/apps/install/{{id}}">{{manifest.name}}</a>
|
||||
<a href="#/apps/catalog">{{t 'catalog'}}</a>
|
||||
<a href="#/apps/install/{{id}}">{{t 'install_name' manifest.name}}</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
@ -16,7 +16,7 @@
|
|||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{{t 'id'}}</dt>
|
||||
<dd>{{id}}</dd>
|
||||
<dd>{{manifest.id}}</dd>
|
||||
<dt>{{t 'description'}}</dt>
|
||||
<dd>{{description}}</dd>
|
||||
{{#displayLicense}}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
</div>
|
||||
|
||||
<div class="actions-group">
|
||||
<a role="button" href="#/apps/install" class="btn btn-success slide">
|
||||
<a role="button" href="#/apps/catalog" class="btn btn-success slide">
|
||||
<span class="fa-plus"></span> {{t 'install'}}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -16,7 +16,7 @@
|
|||
<a href="#/apps/{{id}}" class="list-group-item slide" title="{{t 'infos'}}">
|
||||
<span class="fa-chevron-right pull-right"></span>
|
||||
<h2 class="list-group-item-heading">
|
||||
{{label}} <small>{{name}}</small>
|
||||
{{settings.label}} <small>{{name}}</small>
|
||||
</h2>
|
||||
<p class="list-group-item-text">{{description}}</p>
|
||||
</a>
|
||||
|
|
|
@ -3,12 +3,6 @@
|
|||
<a href="#/backup">{{t 'backup'}}</a>
|
||||
</div>
|
||||
|
||||
<div class="actions-group">
|
||||
<!--<a role="button" href="#/storages/create" class="btn btn-success slide">
|
||||
<span class="fa-plus"></span> {{t 'storages_new'}}
|
||||
</a>-->
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="list-group">
|
||||
|
@ -18,10 +12,6 @@
|
|||
<h2 class="list-group-item-heading">{{name}} <small>{{id}}</small></h2>
|
||||
<p class="list-group-item-text">{{uri}}</p>
|
||||
</a>
|
||||
{{else}}
|
||||
<div class="alert alert-warning">
|
||||
<span class="fa-exclamation-triangle"></span>
|
||||
{{t 'storages_no'}}
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
|
|
|
@ -83,41 +83,11 @@
|
|||
</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<!--<div class="container">
|
||||
<p>{{t 'backup_archive_download'}}</p>
|
||||
<a role="button" class="btn btn-info slide" href="#/backup/{{storage.id}}/{{name}}/download">
|
||||
<span class="fa-download"></span> {{t 'download'}}
|
||||
</a>
|
||||
</div>
|
||||
<hr>-->
|
||||
<div class="container">
|
||||
<p>{{t 'backup_archive_delete'}}</p>
|
||||
<a href="#/backup/{{storage.id}}/{{name}}/delete" role="button" class="btn btn-danger slide">
|
||||
<button class="btn btn-danger slide" data-action="delete" data-storage="{{storage.id}}" data-archive="{{name}}">
|
||||
<span class="fa-trash-o"></span> {{t 'delete'}}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
{{#if other_storages}}
|
||||
<hr>
|
||||
<div class="container">
|
||||
<p>{{t 'backup_archive_copy'}}</p>
|
||||
<form action="#/backup/{{storage.id}}/{{name}}/copy" method="POST" class="form-horizontal">
|
||||
<div class="form-group has-feedback">
|
||||
<label for="label" class="col-sm-12">{{t 'url'}}</label>
|
||||
<div class="col-sm-12">
|
||||
<select id="storage" name="storage" class="form-control" required>
|
||||
{{#each storages}}
|
||||
<option value="{{id}}">{{name}} ({{uri}})</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="text-center">
|
||||
<input type="submit" role="button" class="btn btn-success slide" value="{{t 'copy'}}">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
|
||||
<div class="btn-breadcrumb">
|
||||
<a href="#/"><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/backup">{{t 'backup'}}</a>
|
||||
<a href="#/backup/{{storage.id}}/create">{{t 'storage_create'}}</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<form action="#/storages" method="POST" class="form-horizontal">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label for="type" class="col-sm-3 control-label">{{t 'backup_type'}}</label>
|
||||
<div class="col-sm-9">
|
||||
<select class="form-control" name="type">
|
||||
<option>sftp</option>
|
||||
<option>ftp</option>
|
||||
<option>rsync</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="domain" class="col-sm-3 control-label">{{t 'domain'}}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="domain" name="domain" class="form-control" placeholder="monserver.fr" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="username" class="col-sm-3 control-label">{{t 'user_username'}}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="username" name="username" class="form-control" placeholder="johndoe" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password" class="col-sm-3 control-label">{{t 'password'}}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="password" id="password" name="password" class="form-control" placeholder="•••••" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="path" class="col-sm-3 control-label">{{t 'path'}}</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="path" name="path" class="form-control" placeholder="~/" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<input type="submit" role="button" class="btn btn-success slide back" value="{{t 'save'}}">
|
||||
</div>
|
||||
</form>
|
67
src/views/diagnosis/diagnosis_show.ms
Normal file
67
src/views/diagnosis/diagnosis_show.ms
Normal file
|
@ -0,0 +1,67 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/diagnosis">{{t 'diagnosis'}}</a>
|
||||
</div>
|
||||
|
||||
<div class="actions-group">
|
||||
<button class="btn btn-success" data-action="share">
|
||||
<span class="fa-cloud-upload"></span> {{t 'logs_share_with_yunopaste'}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="alert alert-info text-center">
|
||||
{{#if reports}}
|
||||
<p>{{t 'diagnosis_explanation'}}</p>
|
||||
{{else}}
|
||||
<p>{{t 'diagnosis_first_run'}}</p>
|
||||
<br>
|
||||
<button class="btn btn-info" data-action="run-full-diagnosis"><span class="fa-fw fa-stethoscope"></span> {{t 'run_first_diagnosis'}}</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning text-center">{{t 'diagnosis_experimental_disclaimer'}}</div>
|
||||
|
||||
{{#reports}}
|
||||
<div class="panel panel-default panel-diagnosis" data-category="{{id}}">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title" style="display: inline-block; margin-right: 10px;">
|
||||
<a data-toggle="collapse" href="#diagnosis-body-{{id}}">{{ description }}</a>
|
||||
</h2>
|
||||
{{#if noIssues}}{{#if items}}<span class="label label-success">{{t 'everything_good'}}</span>{{/if}}{{/if}}
|
||||
{{#if errors}}<span class="label label-danger">{{t 'issues' errors }}</span>{{/if}}
|
||||
{{#if warnings}}<span class="label label-warning">{{t 'warnings' warnings }}</span>{{/if}}
|
||||
{{#if ignored}}<span class="label label-default">{{t 'ignored' ignored }}</span>{{/if}}
|
||||
<button class="btn btn-sm {{#if items}}btn-info{{else}}btn-success{{/if}} pull-right" data-action="rerun-diagnosis" data-category="{{ id }}"><span class="fa-fw fa-refresh"></span> {{t 'rerun_diagnosis'}}</button>
|
||||
</div>
|
||||
<div class="panel-body collapse {{#if errors}}in{{/if}}" id="diagnosis-body-{{id}}">
|
||||
<ul class="list-group" style="margin-bottom: 0px">
|
||||
<p>{{t 'last_ran' }} {{formatRelative time day="numeric" month="long" year="numeric" hour="numeric" minute="numeric" }}</p>
|
||||
{{#items}}
|
||||
<li class="list-group-item alert alert-{{status}} alert-{{status}}-yo clearfix diagnosis-item">
|
||||
{{#if icon}}
|
||||
<span class="fa-fw fa-{{icon}}"></span>
|
||||
{{/if}}
|
||||
{{{summary}}}
|
||||
{{#if ignored}}
|
||||
<button class="btn btn-sm btn-default pull-right" data-action="unignore" data-filter-args="{{ filter_args }}"><span class="fa-fw fa-bell"></span> {{t 'unignore'}}</button>
|
||||
{{else}}
|
||||
{{#if issue}}
|
||||
<button class="btn btn-sm btn-warning pull-right" data-action="ignore" data-filter-args="{{ filter_args }}"><span class="fa-fw fa-bell-slash"></span> {{t 'ignore'}}</button>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if details}}
|
||||
<a role="button" class="btn btn-sm btn-default pull-right" data-toggle="collapse" href="#details-{{../id}}-{{@index}}" aria-expanded="false" aria-controls="details-{{../id}}-{{@index}}"><span class="fa-fw fa-level-down"></span>{{t 'details'}}</a>
|
||||
<div class="collapse diagnosis-details" id="details-{{../id}}-{{@index}}">
|
||||
<ul>
|
||||
{{#details}}<li>{{{.}}}</li>{{/details}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{/items}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{/reports}}
|
|
@ -46,36 +46,36 @@
|
|||
<p><span class="fa-fw fa-meh-o"></span>
|
||||
{{t 'domain_not_eligible_for_ACME'}}</p>
|
||||
{{/if}}
|
||||
<a role="button" href="#/domains/{{name}}/cert-install-LE" class="btn btn-success {{#unless status.ACME_eligible}}disabled{{/unless}}">
|
||||
<button class="btn btn-success {{#unless status.ACME_eligible}}disabled{{/unless}}" data-domain="{{name}}" data-action="install-LE" >
|
||||
<span class="fa-star"></span> {{t 'install_letsencrypt_cert'}}
|
||||
</a>
|
||||
</button>
|
||||
<hr>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if actions_enabled.manual_renew_letsencrpt}}
|
||||
<div class="container">
|
||||
<p>{{t 'manually_renew_letsencrypt_message'}}</p>
|
||||
<a role="button" href="#/domains/{{name}}/cert-renew-letsencrypt" class="btn btn-warning">
|
||||
<button class="btn btn-warning" data-domain="{{name}}" data-action="renew-letsencrypt">
|
||||
<span class="fa-refresh"></span> {{t 'manually_renew_letsencrypt'}}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<hr>
|
||||
{{/if}}
|
||||
{{#if actions_enabled.regen_selfsigned}}
|
||||
<div class="container">
|
||||
<p>{{t 'regenerate_selfsigned_cert_message'}}</p>
|
||||
<a href="#/domains/{{name}}/cert-regen-selfsigned" role="button" class="btn btn-warning">
|
||||
<button class="btn btn-warning" data-domain="{{name}}" data-action="regen-selfsigned">
|
||||
<span class="fa-refresh"></span> {{t 'regenerate_selfsigned_cert'}}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<hr>
|
||||
{{/if}}
|
||||
{{#if actions_enabled.replace_with_selfsigned}}
|
||||
<div class="container">
|
||||
<p>{{t 'revert_to_selfsigned_cert_message'}}</p>
|
||||
<a href="#/domains/{{name}}/cert-replace-with-selfsigned" role="button" class="btn btn-danger">
|
||||
<button class="btn btn-danger" data-domain="{{name}}" data-action="replace-with-selfsigned">
|
||||
<span class="fa-exclamation-triangle"></span> {{t 'revert_to_selfsigned_cert'}}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
@ -12,67 +12,46 @@
|
|||
<span class="fa-fw fa-globe"></span> {{name}}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{#if main}}
|
||||
<p class="alert alert-success">
|
||||
<span class="fa-star" title="{{t 'default'}}"></span> {{t 'domain_default_longdesc'}}
|
||||
</p>
|
||||
{{/if}}
|
||||
<p><a href="{{url}}" target="_blank">{{url}}</a></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
<span class="fa-fw fa-wrench"></span> {{t 'operations'}}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="container">
|
||||
<p>{{t 'domain_visit_url' url}}</p>
|
||||
<a role="button" href="{{url}}" class="btn btn-success" target="_blank">
|
||||
{{t 'domain_visit'}} <span class="fa-fw fa-external-link"></span>
|
||||
<span class="fa-fw fa-external-link"></span> {{t 'domain_visit'}}
|
||||
</a>
|
||||
</div>
|
||||
{{#unless main}}
|
||||
<hr>
|
||||
<div class="container">
|
||||
<p>{{t 'domain_default_desc'}}</p>
|
||||
<form method="POST" action="#/domains">
|
||||
<input type="hidden" name="domain" value="{{name}}" required class="form-control">
|
||||
<button type="submit" class="btn btn-primary slide back" value="{{t 'set_default'}}">
|
||||
{{t 'set_default'}} <span class="fa-fw fa-star"></span>
|
||||
</button>
|
||||
</form>
|
||||
{{#if main}}
|
||||
<p class="alert alert-info">
|
||||
<span class="fa-star" title="{{t 'default'}}"></span> {{t 'domain_default_longdesc'}}
|
||||
</p>
|
||||
{{else}}
|
||||
<button class="btn btn-primary" data-action="set_default" data-domain="{{name}}">
|
||||
<span class="fa-fw fa-star"></span> {{t 'set_default'}}
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
<hr>
|
||||
<div class="container">
|
||||
<p>{{t 'domain_dns_longdesc'}}</p>
|
||||
<a role="button" href="#/domains/{{name}}/dns" class="btn btn-default slide">
|
||||
{{t 'domain_dns_config'}} <span class="fa-fw fa-globe"></span>
|
||||
<span class="fa-fw fa-globe"></span> {{t 'domain_dns_config'}}
|
||||
</a>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="container">
|
||||
<p>{{t 'certificate_manage'}}</p>
|
||||
{{#unless enable_cert_management}}
|
||||
<p><span class="fa-fw fa-exclamation-circle"></span>
|
||||
{{t 'certificate_old_letsencrypt_app_conflict'}}
|
||||
</p>
|
||||
{{/unless}}
|
||||
<a href="#/domains/{{name}}/cert-management" role="button" class="btn btn-default slide {{#unless enable_cert_management}}disabled{{/unless}}">
|
||||
{{t 'ssl_certificate'}} <span class="fa-fw fa-lock"></span>
|
||||
<a href="#/domains/{{name}}/cert-management" role="button" class="btn btn-default slide">
|
||||
<span class="fa-fw fa-lock"></span> {{t 'ssl_certificate'}}
|
||||
</a>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="container">
|
||||
<p>{{t 'domain_delete_longdesc' name}}</p>
|
||||
<a href="#/domains/{{name}}/delete" role="button" class="btn btn-danger slide back">
|
||||
{{t 'delete'}} <span class="fa-fw fa-trash-o"></span>
|
||||
</a>
|
||||
<button class="btn btn-danger" data-action="delete" data-domain="{{name}}">
|
||||
<span class="fa-fw fa-trash-o"></span> {{t 'delete'}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -21,10 +21,5 @@
|
|||
</h2>
|
||||
<p class="list-group-item-text">https://{{url}}</p>
|
||||
</a>
|
||||
{{else}}
|
||||
<div class="alert alert-warning">
|
||||
<span class="fa-exclamation-triangle"></span>
|
||||
{{t 'domains_no'}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
|
|
@ -23,6 +23,10 @@
|
|||
<span class="pull-right fa-chevron-right"></span>
|
||||
<h2 class="list-group-item-heading"><span class="fa-fw fa-wrench"></span> {{t 'tools'}}</h2>
|
||||
</a>
|
||||
<a href="#/diagnosis" class="list-group-item slide clearfix">
|
||||
<span class="pull-right fa-chevron-right"></span>
|
||||
<h2 class="list-group-item-heading"><span class="fa-fw fa-stethoscope"></span> {{t 'diagnosis'}}</h2>
|
||||
</a>
|
||||
<a href="#/backup" class="list-group-item slide clearfix">
|
||||
<span class="pull-right fa-chevron-right"></span>
|
||||
<h2 class="list-group-item-heading"><span class="fa-fw fa-archive"></span> {{t 'backup'}}</h2>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/services">{{t 'services'}}</a>
|
||||
<a href="#/services/{{service.name}}">{{service.name}}</a>
|
||||
<a href="#/services/{{service.name}}">{{name}}</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
@ -9,76 +9,70 @@
|
|||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title"><span class="fa-fw fa-info-circle"></span> {{t 'infos'}}</h2>
|
||||
<h2 class="panel-title" style="display: inline-block; margin-right: 10px;"><span class="fa-fw fa-info-circle"></span> {{name}}</h2>
|
||||
{{#if (eq status "running")}}
|
||||
<button class="btn btn-sm btn-danger pull-right" data-service="{{name}}" data-action="stop">
|
||||
<span class="fa-fw fa-warning"></span> {{t 'stop'}}
|
||||
</button>
|
||||
<button class="btn btn-sm btn-warning pull-right" data-service="{{name}}" data-action="restart">
|
||||
<span class="fa-fw fa-refresh"></span> {{t 'restart'}}
|
||||
</button>
|
||||
{{else}}
|
||||
<button class="btn btn-sm btn-success pull-right" data-service="{{name}}" data-action="start">
|
||||
<span class="fa-fw fa-play"></span> {{t 'start'}}
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{#service}}
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{{t 'name'}}</dt>
|
||||
<dd>{{name}}</dd>
|
||||
<dt>{{t 'description'}}</dt>
|
||||
<dd>{{description}}</dd>
|
||||
</dl>
|
||||
{{/service}}
|
||||
<dl class="dl-horizontal">
|
||||
|
||||
<dt>{{t 'description'}}</dt>
|
||||
<dd>{{description}}</dd>
|
||||
|
||||
<dt>{{t 'status'}}</dt>
|
||||
<dd>
|
||||
{{#if (eq status "running")}}
|
||||
<span class="text-success">
|
||||
<span class="fa-fw fa-check-circle"></span>
|
||||
{{else}}
|
||||
<span class="text-danger">
|
||||
<span class="fa-fw fa-times"></span>
|
||||
{{/if}}
|
||||
{{t status}} </span> {{t 'since'}} {{formatRelative last_state_change day="numeric" month="long" year="numeric" hour="numeric" minute="numeric" }}
|
||||
</dd>
|
||||
|
||||
<dt>{{t 'service_start_on_boot'}}</dt>
|
||||
{{#if (eq start_on_boot "enabled")}}
|
||||
<dd class="text-success">
|
||||
{{else}}
|
||||
<dd class="text-danger">
|
||||
{{/if}}
|
||||
{{t start_on_boot}}
|
||||
</dd>
|
||||
|
||||
<dt>{{t 'configuration'}}</dt>
|
||||
{{#if (eq configuration "valid")}}
|
||||
<dd class="text-success">
|
||||
{{else if (eq configuration "broken")}}
|
||||
<dd class="text-danger">
|
||||
{{else}}
|
||||
<dd>
|
||||
{{/if}}
|
||||
{{t configuration}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title"><span class="fa-fw fa-wrench"></span> {{t 'status'}}</h2>
|
||||
<h2 class="panel-title" style="display: inline-block; margin-right: 10px;"><span class="fa-fw fa-book"></span> {{t 'logs'}}</h2>
|
||||
<button class="btn btn-sm btn-success pull-right" data-action="share"><span class="fa-cloud-upload"></span> {{t 'logs_share_with_yunopaste'}}</button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
{{#service}}
|
||||
<div class="pull-left">
|
||||
{{t 'service_start_on_boot'}}
|
||||
<span class="text-{{#is_loaded}}success{{/is_loaded}}{{^is_loaded}}danger{{/is_loaded}}">
|
||||
{{loaded}}
|
||||
</span>
|
||||
<br>
|
||||
{{t 'service_status'}}
|
||||
<span class="text-{{#is_running}}success{{/is_running}}{{^is_running}}danger{{/is_running}}">
|
||||
{{active}}
|
||||
</span>
|
||||
<br>
|
||||
{{t 'started_at'}}
|
||||
{{#active_at}}
|
||||
{{formatTime . day="numeric" month="long" year="numeric" hour="numeric" minute="numeric"}}
|
||||
{{/active_at}}
|
||||
{{^active_at}}
|
||||
{{t 'unknown'}}
|
||||
{{/active_at}}
|
||||
</div>
|
||||
|
||||
<div class="pull-right">
|
||||
{{#is_loaded}}
|
||||
<a href="#/services/{{name}}/disable" role="button" class="btn btn-danger">
|
||||
<span class="fa-square-o"></span> {{t 'disable'}}
|
||||
</a>
|
||||
{{/is_loaded}}
|
||||
{{^is_loaded}}
|
||||
<a href="#/services/{{name}}/enable" role="button" class="btn btn-success">
|
||||
<span class="fa-check-square-o"></span> {{t 'enable'}}
|
||||
</a>
|
||||
{{/is_loaded}}
|
||||
|
||||
{{#is_running}}
|
||||
<a href="#/services/{{name}}/stop" role="button" class="btn btn-danger">
|
||||
<span class="fa-stop"></span> {{t 'stop'}}
|
||||
</a>
|
||||
{{/is_running}}
|
||||
{{^is_running}}
|
||||
<a href="#/services/{{name}}/start" role="button" class="btn btn-success">
|
||||
<span class="fa-play"></span> {{t 'start'}}
|
||||
</a>
|
||||
{{/is_running}}
|
||||
<a href="#/services/{{name}}/log" role="button" class="btn btn-default slide">
|
||||
<span class="fa-book"></span> {{t 'log'}}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{/service}}
|
||||
</dl>
|
||||
<div id="logs" class="panel-body">
|
||||
{{#logs}}
|
||||
<h2>{{filename}}</h2>
|
||||
<pre class="service-log">{{filecontent}}</pre>
|
||||
{{/logs}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -11,18 +11,16 @@
|
|||
<span class="fa-chevron-right pull-right"></span>
|
||||
<h2 class="list-group-item-heading">{{name}} <small>{{description}}</small></h2>
|
||||
<div class="list-group-item-text">
|
||||
{{t 'service_status'}}
|
||||
<span class="text-{{#is_running}}success{{/is_running}}{{^is_running}}danger{{/is_running}}">
|
||||
{{active}}
|
||||
</span>
|
||||
<br>
|
||||
{{t 'started_at'}}
|
||||
{{#active_at}}
|
||||
{{formatTime . day="numeric" month="long" year="numeric" hour="numeric" minute="numeric"}}
|
||||
{{/active_at}}
|
||||
{{^active_at}}
|
||||
{{t 'unknown'}}
|
||||
{{/active_at}}
|
||||
{{#if (eq status "running")}}
|
||||
<span class="text-success">
|
||||
<span class="fa-fw fa-check-circle"></span>
|
||||
{{else}}
|
||||
<span class="text-danger">
|
||||
<span class="fa-fw fa-times"></span>
|
||||
{{/if}}
|
||||
{{t status}}
|
||||
</span>
|
||||
{{t 'since'}} {{formatRelative last_state_change day="numeric" month="long" year="numeric" hour="numeric" minute="numeric" }}
|
||||
</div>
|
||||
</a>
|
||||
{{/services}}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/services">{{t 'services'}}</a>
|
||||
<a href="#/services/{{name}}">{{name}}</a>
|
||||
<a href="#/services/{{name}}/log">{{t 'log'}}</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="container">
|
||||
{{#logs}}
|
||||
<h2>{{filename}}</h2>
|
||||
<pre id="log" class="service-log">{{filecontent}}</pre>
|
||||
<button data-paste-content="#log"><i class="fa-cloud-upload"></i> {{t 'upload'}}</button>
|
||||
{{/logs}}
|
||||
</div>
|
|
@ -1,49 +0,0 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/tools">{{t 'tools'}}</a>
|
||||
<a href="#/tools/appslists">{{t 'appslists'}}</a>
|
||||
<a href="#/tools/appslists/{{name}}">{{appslist.name}}</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title"><span class="fa-fw fa-info-circle"></span> {{t 'infos'}}</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{{t 'name'}}</dt>
|
||||
<dd>{{appslist.name}}</dd>
|
||||
<dt>{{t 'url'}}</dt>
|
||||
<dd>{{appslist.url}}</dd>
|
||||
<dt>{{t 'appslists_last_update'}}</dt>
|
||||
<dd>{{formatTime appslist.lastUpdate day="numeric" month="long" year="numeric" hour="numeric" minute="numeric"}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
<span class="fa-fw fa-wrench"></span> {{t 'operations'}}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="container">
|
||||
<p>{{t 'appslists_info_refresh_desc'}}</p>
|
||||
<a href="#/tools/appslists/{{appslist.name}}/refresh" role="button" class="btn btn-info slide">
|
||||
<span class="fa-refresh"></span> {{t 'refresh_app_list'}}
|
||||
</a>
|
||||
</div>
|
||||
{{#appslist.removable}}
|
||||
<hr>
|
||||
<div class="container">
|
||||
<p>{{t 'appslists_info_remove_desc'}}</p>
|
||||
<a role="button" href="#/tools/appslists/{{appslist.name}}/remove" class="btn btn-danger slide back">
|
||||
<span class="fa-trash-o"></span> {{t 'remove'}}
|
||||
</a>
|
||||
</div>
|
||||
{{/appslist.removable}}
|
||||
</div>
|
||||
</div>
|
|
@ -1,57 +0,0 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/tools">{{t 'tools'}}</a>
|
||||
<a href="#/tools/appslists">{{t 'appslists'}}</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title"><span class="fa-fw fa-list"></span> {{t 'appslists'}}</h2>
|
||||
</div>
|
||||
|
||||
<div class="list-group">
|
||||
{{#appslists}}
|
||||
<a href="#/tools/appslists/{{name}}" class="list-group-item">
|
||||
<span class="fa-chevron-right pull-right"></span>
|
||||
<h2 class="list-group-item-heading">
|
||||
{{name}}
|
||||
</h2>
|
||||
</a>
|
||||
{{/appslists}}
|
||||
{{^appslists}}
|
||||
<p class="list-group-item text-warning">
|
||||
<span class="fa-exclamation-triangle"></span>
|
||||
{{t 'appslists_no_lists'}}
|
||||
</p>
|
||||
{{/appslists}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title"><span class="fa-fw fa-plus"></span> {{t 'appslists_custom'}}</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form action="#/tools/appslists" method="POST" class="form-horizontal">
|
||||
<div class="form-group has-feedback">
|
||||
<label for="appslist_name" class="col-md-2 col-sm-12 control-label">{{t 'name'}}</label>
|
||||
<div class="col-md-10 col-sm-12">
|
||||
<input type="text" id="appslist_name" name="appslist_name" class="form-control" value="" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label for="appslist_url" class="col-md-2 col-sm-12 control-label">{{t 'url'}}</label>
|
||||
<div class="col-md-10 col-sm-12">
|
||||
<input type="url" id="appslist_url" name="appslist_url" class="form-control" value="" placeholder="https://some.domain.tld/somelist.json" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-push-2 col-sm-12">
|
||||
<input type="submit" class="btn btn-success slide" value="{{t 'add'}}">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -1,14 +0,0 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/tools">{{t 'tools'}}</a>
|
||||
<a href="#/tools/ca">{{t 'tools_download_ca'}}</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<p>{{t 'tools_download_ca_desc'}}</p>
|
||||
<a role="button" href="ca.crt" class="btn btn-success">CA.crt</a>
|
||||
</div>
|
||||
</div>
|
|
@ -1,25 +0,0 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/tools">{{t 'tools'}}</a>
|
||||
<a href="#/tools/diagnosis">{{t 'diagnosis'}}</a>
|
||||
{{#private}}
|
||||
<a href="#/tools/diagnosis/private">{{t 'diagnosis_with_private'}}</a>
|
||||
{{/private}}
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title"><span class="fa-fw fa-stethoscope"></span> {{t 'diagnosis'}}</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<pre id="diagnosis">{{ diagnosis }}</pre>
|
||||
{{#if private}}
|
||||
<a class="btn btn-primary" role="button" href="#/tools/diagnosis"><i class="fa-eye-slash"></i> {{t 'diagnosis_hide_private'}}</a>
|
||||
{{else}}
|
||||
<a class="btn btn-primary" role="button" href="#/tools/diagnosis/private"><i class="fa-eye"></i> {{t 'diagnosis_view_private'}}</a>
|
||||
{{/if}}
|
||||
<button data-paste-content="#diagnosis"><i class="fa-cloud-upload"></i> {{t 'upload'}}</button>
|
||||
</div>
|
||||
</div>
|
|
@ -29,20 +29,20 @@
|
|||
<td>
|
||||
{{#if this.ipv4}}
|
||||
<span class="fa-check"></span>
|
||||
<a class="btn btn-xs btn-danger" href="#/tools/firewall/port/{{@key}}/tcp/ipv4/close">{{t 'close'}}</a>
|
||||
<button class="btn btn-xs btn-danger" data-action="close" data-port="{{@key}}" data-protocol="tcp" data-connection="ipv4">{{t 'close'}}</button>
|
||||
{{else}}
|
||||
<span></span>
|
||||
<span class="fa-times"></span>
|
||||
<a class="btn btn-xs btn-success" href="#/tools/firewall/port/{{@key}}/tcp/ipv4/open">{{t 'open'}}</a>
|
||||
<button class="btn btn-xs btn-success" data-action="open" data-port="{{@key}}" data-protocol="tcp" data-connection="ipv4">{{t 'open'}}</button>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>
|
||||
{{#if this.ipv6}}
|
||||
<span class="fa-check"></span>
|
||||
<a class="btn btn-xs btn-danger" href="#/tools/firewall/port/{{@key}}/tcp/ipv6/close">{{t 'close'}}</a>
|
||||
<button class="btn btn-xs btn-danger" data-action="close" data-port="{{@key}}" data-protocol="tcp" data-connection="ipv6">{{t 'close'}}</button>
|
||||
{{else}}
|
||||
<span class="fa-times"></span>
|
||||
<a class="btn btn-xs btn-success" href="#/tools/firewall/port/{{@key}}/tcp/ipv6/open">{{t 'open'}}</a>
|
||||
<button class="btn btn-xs btn-success" data-action="open" data-port="{{@key}}" data-protocol="tcp" data-connection="ipv6">{{t 'open'}}</button>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>
|
||||
|
@ -75,20 +75,20 @@
|
|||
<td>
|
||||
{{#if this.ipv4}}
|
||||
<span class="fa-check"></span>
|
||||
<a class="btn btn-xs btn-danger" href="#/tools/firewall/port/{{@key}}/udp/ipv4/close">{{t 'close'}}</a>
|
||||
<button class="btn btn-xs btn-danger" data-action="close" data-port="{{@key}}" data-protocol="udp" data-connection="ipv4">{{t 'close'}}</button>
|
||||
{{else}}
|
||||
<span></span>
|
||||
<span class="fa-times"></span>
|
||||
<a class="btn btn-xs btn-success" href="#/tools/firewall/port/{{@key}}/udp/ipv4/open">{{t 'open'}}</a>
|
||||
<button class="btn btn-xs btn-success" data-action="open" data-port="{{@key}}" data-protocol="udp" data-connection="ipv4">{{t 'open'}}</button>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>
|
||||
{{#if this.ipv6}}
|
||||
<span class="fa-check"></span>
|
||||
<a class="btn btn-xs btn-danger" href="#/tools/firewall/port/{{@key}}/udp/ipv6/close">{{t 'close'}}</a>
|
||||
<button class="btn btn-xs btn-danger" data-action="close" data-port="{{@key}}" data-protocol="udp" data-connection="ipv6">{{t 'close'}}</button>
|
||||
{{else}}
|
||||
<span class="fa-times"></span>
|
||||
<a class="btn btn-xs btn-success" href="#/tools/firewall/port/{{@key}}/udp/ipv6/open">{{t 'open'}}</a>
|
||||
<button class="btn btn-xs btn-success" data-action="open" data-port="{{@key}}" data-protocol="udp" data-connection="ipv6">{{t 'open'}}</button>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>
|
||||
|
@ -168,10 +168,10 @@
|
|||
<div class="panel-body">
|
||||
{{#if upnp}}
|
||||
<p class="text-success">{{t 'upnp_enabled'}}</p>
|
||||
<a role="button" href="#/tools/firewall/upnp/disable" class="btn btn-danger">{{t 'disable'}}</a>
|
||||
<button class="btn btn-danger" data-upnp="disable">{{t 'disable'}}</button>
|
||||
{{else}}
|
||||
<p class="text-danger">{{t 'upnp_disabled'}}</p>
|
||||
<a role="button" href="#/tools/firewall/upnp/enable" class="btn btn-success">{{t 'enable'}}</a>
|
||||
<button class="btn btn-success" data-upnp="enable">{{t 'enable'}}</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,10 +6,6 @@
|
|||
<div class="separator"></div>
|
||||
|
||||
<div class="list-group">
|
||||
<a href="#/tools/diagnosis" class="list-group-item slide clearfix">
|
||||
<span class="pull-right fa-chevron-right"></span>
|
||||
<h2 class="list-group-item-heading">{{t 'diagnosis'}}</h2>
|
||||
</a>
|
||||
<a href="#/tools/logs" class="list-group-item slide clearfix">
|
||||
<span class="pull-right fa-chevron-right"></span>
|
||||
<h2 class="list-group-item-heading">{{t 'logs'}}</h2>
|
||||
|
@ -18,41 +14,17 @@
|
|||
<span class="pull-right fa-chevron-right"></span>
|
||||
<h2 class="list-group-item-heading">{{t 'migrations'}}</h2>
|
||||
</a>
|
||||
<a href="#/tools/reboot" class="list-group-item slide clearfix">
|
||||
<a href="#/tools/firewall" class="list-group-item slide clearfix">
|
||||
<span class="pull-right fa-chevron-right"></span>
|
||||
<h2 class="list-group-item-heading">{{t 'tools_shutdown_reboot'}}</h2>
|
||||
<h2 class="list-group-item-heading">{{t 'firewall'}}</h2>
|
||||
</a>
|
||||
<a href="#/tools/adminpw" class="list-group-item slide">
|
||||
<span class="fa-chevron-right pull-right"></span>
|
||||
<h2 class="list-group-item-heading">{{t 'tools_adminpw'}}</h2>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
<div class="separator"></div>
|
||||
|
||||
<h2 style="font-weight:600; padding-left:0.5em;">{{t 'advanced'}}</h2>
|
||||
|
||||
<div class="list-group">
|
||||
<a href="#/tools/monitor" class="list-group-item slide clearfix">
|
||||
<a href="#/tools/reboot" class="list-group-item slide clearfix">
|
||||
<span class="pull-right fa-chevron-right"></span>
|
||||
<h2 class="list-group-item-heading">{{t 'monitoring'}}</h2>
|
||||
</a>
|
||||
<a href="#/tools/firewall" class="list-group-item slide clearfix">
|
||||
<span class="pull-right fa-chevron-right"></span>
|
||||
<h2 class="list-group-item-heading">{{t 'firewall'}}</h2>
|
||||
</a>
|
||||
<a href="#/tools/security-feed" class="list-group-item slide">
|
||||
<span class="fa-chevron-right pull-right"></span>
|
||||
<h2 class="list-group-item-heading">{{t 'tools_security_feed'}}</h2>
|
||||
</a>
|
||||
<a href="#/tools/appslists" class="list-group-item slide clearfix">
|
||||
<span class="pull-right fa-chevron-right"></span>
|
||||
<h2 class="list-group-item-heading">{{t 'appslists'}}</h2>
|
||||
</a>
|
||||
<a href="#/tools/ca" class="list-group-item slide">
|
||||
<span class="fa-chevron-right pull-right"></span>
|
||||
<h2 class="list-group-item-heading">{{t 'tools_download_ca'}}</h2>
|
||||
<h2 class="list-group-item-heading">{{t 'tools_shutdown_reboot'}}</h2>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -9,30 +9,31 @@
|
|||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="actions-group">
|
||||
<a href="javascript:void(null);" onclick="c.api('/logs/display?path={{#if log.name}}{{ log.name }}{{else}}{{ log.log_path }}{{/if}}&share', function(data) { $('div.loader').remove(); window.open(data.url, '_blank'); });" class="btn btn-success">
|
||||
<span class="fa-cloud-upload"></span> {{t 'logs_share_with_yunopaste'}}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
{{#intl locales=locale}}
|
||||
{{#if log.metadata}}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<!-- PLEASE DON'T INDENT THIS CODE, IT IS PASTED ON YUNOPASTE -->
|
||||
<h2 class="panel-title" id="description"><span class="fa-fw fa-info-circle"></span> {{ log.description }}</h2>
|
||||
<h2 class="panel-title" id="description" style="display: inline-block; margin-right: 10px;"><span class="fa-fw fa-info-circle"></span> {{ log.description }}</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal" id="metadata">
|
||||
<dt>{{t 'logs_path'}}</dt> <dd>{{ log.log_path }}</dd>
|
||||
{{#if log.metadata.started_at}}<dt>{{t 'logs_started_at'}}</dt> <dd>{{formatTime log.metadata.started_at day="numeric" month="long" year="numeric" hour="numeric" minute="numeric"}}</dd>
|
||||
{{/if}}{{#if log.metadata.ended_at}}<dt>{{t 'logs_ended_at'}}</dt> <dd>{{formatTime log.metadata.ended_at day="numeric" month="long" year="numeric" hour="numeric" minute="numeric"}}</dd>{{/if}}
|
||||
{{#if log.metadata.error}}<dt>{{t 'logs_error'}}</dt> <dd>{{t 'logs_end_with_error'}} {{log.metadata.error}}</dd>{{/if}}
|
||||
{{#if log.metadata.error}}<dt>{{t 'logs_error'}}</dt> <dd>{{log.metadata.error}}</dd>{{/if}}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if log.metadata.error}}
|
||||
<div class="alert alert-danger text-center">
|
||||
<p>{{t 'operation_failed_explanation'}}</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<!--
|
||||
{{#if log.metadata.env}}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="heading-context">
|
||||
|
@ -52,17 +53,27 @@
|
|||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
-->
|
||||
|
||||
{{/if}}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title"><span class="fa-fw fa-file-text"></span> {{#if log.metadata}}{{t 'logs'}}{{else}}{{log.log_path}}{{/if}}</h2>
|
||||
<h2 class="panel-title" style="display: inline-block; margin-right: 10px;">
|
||||
<span class="fa-fw fa-file-text"></span> {{#if log.metadata}}{{t 'logs'}}{{else}}{{log.log_path}}{{/if}}</h2>
|
||||
<button class="btn btn-sm btn-success pull-right" data-action="share" data-log-id="{{#if log.name}}{{ log.name }}{{else}}{{ log.log_path }}{{/if}}">
|
||||
<span class="fa-cloud-upload"></span> {{t 'logs_share_with_yunopaste'}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="panel-body overflow-auto">
|
||||
{{#if next_number}}<a href="#/tools/logs/{{#if log.name}}{{ log.name }}{{else}}{{ log.log_path }}{{/if}}?number={{ next_number }}" class="btn btn-default full-width"><span class="fa-fw fa-plus"></span> {{t 'logs_more'}}</a>{{/if}}
|
||||
<!-- no indent because pre is sensible to whitespaces -->
|
||||
<pre id="log" class="full-width">{{#log.logs}}{{.}}
|
||||
{{/log.logs}}</pre>
|
||||
<center>
|
||||
<button class="btn btn-success" data-action="share" data-log-id="{{#if log.name}}{{ log.name }}{{else}}{{ log.log_path }}{{/if}}">
|
||||
<span class="fa-cloud-upload"></span> {{t 'logs_share_with_yunopaste'}}
|
||||
</button>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
{{/intl}}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<div id="collapse-{{key}}" class="panel-collapse{{#if @first}}{{else}} collapse{{/if}}" role="tabpanel" aria-labelledby="heading-{{key}}">
|
||||
<div class="list-group">
|
||||
{{#value}}
|
||||
<a href="#/tools/logs/{{ name }}" class="list-group-item" title='{{formatTime started_at day="numeric" month="long" year="numeric" hour="numeric" minute="numeric"}}'><small style="margin-right:20px;" >{{formatRelative started_at}}</small>
|
||||
<a href="#/tools/logs/{{ name }}" class="list-group-item slide" title='{{formatTime started_at day="numeric" month="long" year="numeric" hour="numeric" minute="numeric"}}'><small style="margin-right:20px;" >{{formatRelative started_at}}</small>
|
||||
<span class="fa-fw fa-{{success_icon}}"></span> {{ description }}</a>
|
||||
{{/value}}
|
||||
</div>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<h2 class="panel-title"><span class="fa-fw fa-cogs"></span> {{t 'migrations_pending'}}
|
||||
{{#if pending_migrations}}
|
||||
<div class="btn-toolbar pull-right">
|
||||
<a href="#/tools/migrations/run" class="btn btn-sm btn-success"><span class="fa-fw fa-play"></span> {{t 'run'}}</a>
|
||||
<button class="btn btn-sm btn-success" data-action="run"><span class="fa-fw fa-play"></span> {{t 'run'}}</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
</h2>
|
||||
|
@ -24,7 +24,7 @@
|
|||
<h3 class="list-group-item-heading">
|
||||
{{ number }}. {{ description }}
|
||||
<div class="btn-toolbar pull-right">
|
||||
<a href="#/tools/migrations/skip/{{ id }}" class="btn btn-xs btn-warning" style="color:white;"><span class="fa-fw fa-close"></span> {{t 'skip'}}</a>
|
||||
<button class="btn btn-xs btn-warning" style="color:white;" data-action="skip" data-migration="{{id}}"><span class="fa-fw fa-close"></span> {{t 'skip'}}</button>
|
||||
</div>
|
||||
</h3>
|
||||
{{#if disclaimer }}
|
||||
|
|
|
@ -1,283 +0,0 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/tools">{{t 'tools'}}</a>
|
||||
<a href="#/tools/monitor">{{t 'monitoring'}}</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
{{#if status}}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title"><span class="fa-fw fa-info-circle"></span> {{t 'infos'}}</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{{t 'hostname'}}</dt>
|
||||
<dd>{{system.infos.hostname}}</dd>
|
||||
<dt>{{t 'os'}}</dt>
|
||||
<dd>{{ucwords system.infos.linux_distro}} {{system.infos.platform}} ({{system.infos.os_name}} {{system.infos.os_version}})</dd>
|
||||
<dt>{{t 'uptime'}}</dt>
|
||||
<dd>{{system.uptime}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title"><span class="fa-fw fa-cogs"></span> {{t 'versions'}}</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
{{#each versions}}
|
||||
<dt>{{@key}}</dt>
|
||||
<dd>{{version}} ({{repo}})</dd>
|
||||
{{/each}}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-group" id="accordion">
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
<span class="fa-fw fa-check-square-o"></span>
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#check">{{t 'check'}}</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="check" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{{t 'check_stmp'}}</dt>
|
||||
<dd>{{network.check.smtp_check}}</dd>
|
||||
<dt>{{t 'check_mx'}}</dt>
|
||||
<dd>
|
||||
{{#if network.check.mx_check.mx0}}
|
||||
<ul>
|
||||
{{#each network.check.mx_check}}
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{else}}
|
||||
{{network.check.mx_check}}
|
||||
{{/if}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
<span class="fa-fw fa-cog"></span>
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#system">{{t 'system'}}</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="system" class="panel-collapse collapse">
|
||||
<div class="panel-body row">
|
||||
|
||||
<div class="col-md-4">
|
||||
<h3>{{t 'memory'}}</h3>
|
||||
<h4>{{t 'ram'}}</h4>
|
||||
<table class="table table-condensed">
|
||||
<tr>
|
||||
<td>{{t 'used'}}</td>
|
||||
<td>{{humanSize system.memory.ram.used}} ({{system.memory.ram.percent}} %)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{t 'free'}}</td>
|
||||
<td>{{humanSize system.memory.ram.free}}</td>
|
||||
</tr>
|
||||
<tr class="active">
|
||||
<td>{{t 'total'}}</td>
|
||||
<td>{{humanSize system.memory.ram.total}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h4>{{t 'swap'}}</h4>
|
||||
<table class="table table-condensed">
|
||||
<tr>
|
||||
<td>{{t 'used'}}</td>
|
||||
<td>{{humanSize system.memory.swap.used}} ({{system.memory.swap.percent}} %)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{t 'free'}}</td>
|
||||
<td>{{humanSize system.memory.swap.free}}</td>
|
||||
</tr>
|
||||
<tr class="active">
|
||||
<td>{{t 'total'}}</td>
|
||||
<td>{{humanSize system.memory.swap.total}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<h3>{{t 'cpu_load'}}</h3>
|
||||
<table class="table table-condensed">
|
||||
<tr>
|
||||
<td>{{t 'count_min' "1"}}</td>
|
||||
<td>{{system.cpu.load.min1}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{t 'count_min' "5"}}</td>
|
||||
<td>{{system.cpu.load.min5}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{t 'count_min' "15"}}</td>
|
||||
<td>{{system.cpu.load.min15}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<h3>{{t 'process'}}</h3>
|
||||
<table class="table table-condensed">
|
||||
<tr>
|
||||
<td>{{t 'running'}}</td>
|
||||
<td>{{system.process.running}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{t 'sleeping'}}</td>
|
||||
<td>{{system.process.sleeping}}</td>
|
||||
</tr>
|
||||
<tr class="active">
|
||||
<td>{{t 'total'}}</td>
|
||||
<td>{{system.process.total}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
<span class="fa-fw fa-cloud"></span>
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#network">{{t 'network'}}</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="network" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<b>{{t 'public_ip'}}</b>{{network.infos.public_ip}}
|
||||
<br>
|
||||
<b>{{t 'gateway'}}</b>{{network.infos.gateway}}
|
||||
|
||||
<h3>{{t 'local_ip'}}</h3>
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{t 'interface'}}</th>
|
||||
<th>{{t 'ipv4'}}</th>
|
||||
<th>{{t 'ipv6'}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each network.infos.local_ip}}
|
||||
<tr>
|
||||
<td>{{@key}}</td>
|
||||
<td>{{ ipv4 }}</td>
|
||||
<td>{{ ipv6 }}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>{{t 'usage'}}</h3>
|
||||
{{#each network.usage}}
|
||||
<div class="clearfix">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<h4>
|
||||
{{@key}}
|
||||
<small>{{t 'time_since_update'}}{{humanTime time_since_update}}</small>
|
||||
</h4>
|
||||
</th>
|
||||
<th>{{t 'bit_rate'}}</th>
|
||||
<th>{{t 'cumulative_usage'}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{t 'transmission'}}</td>
|
||||
<td>{{bitRate tx time_since_update}}</td>
|
||||
<td>{{humanSize cumulative_tx}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{t 'reception'}}</td>
|
||||
<td>{{bitRate rx time_since_update}}</td>
|
||||
<td>{{humanSize cumulative_rx}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
<span class="fa-fw fa-hdd-o"></span>
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#disk">{{t 'disk'}}</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="disk" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
{{#each disk}}
|
||||
<div class="clearfix">
|
||||
<h3>{{@key}}</h3>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h4>{{t 'filesystem'}}</h4>
|
||||
<table class="table table-condensed">
|
||||
<tr>
|
||||
<td>{{t 'fs_type'}}</td><td>{{ filesystem.fs_type }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{t 'mount_point'}}</td><td>{{ filesystem.mnt_point }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{t 'size'}}</td><td>{{humanSize filesystem.size }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{t 'used'}}</td><td>{{humanSize filesystem.used }}</td>
|
||||
</tr>
|
||||
<tr class="active">
|
||||
<td>{{t 'available'}}</td><td>{{humanSize filesystem.avail }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4>{{t 'io'}} <small>{{t 'time_since_update'}}{{humanTime io.time_since_update }}</small></h4>
|
||||
<table class="table table-condensed">
|
||||
<tr>
|
||||
<td>{{t 'read'}}</td><td>{{humanSize io.read_bytes }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{t 'write'}}</td><td>{{humanSize io.write_bytes }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- .panel-group -->
|
||||
|
||||
{{else}}
|
||||
<div class="alert alert-warning">
|
||||
<span class="fa-exclamation-triangle"></span>
|
||||
{{t 'monitoring_disabled'}}
|
||||
<br>
|
||||
{{t 'monitoring_check_glances'}}
|
||||
</div>
|
||||
|
||||
{{/if}}
|
|
@ -15,14 +15,14 @@
|
|||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
<a role="button" href="#/tools/reboot/reboot" class="btn btn-danger">
|
||||
<button class="btn btn-danger" data-action="reboot">
|
||||
<i class="fa-refresh"></i> {{t 'tools_reboot_btn'}}
|
||||
</a>
|
||||
</button>
|
||||
</p>
|
||||
<p>
|
||||
<a role="button" href="#/tools/reboot/shutdown" class="btn btn-danger">
|
||||
<button class="btn btn-danger" data-action="shutdown">
|
||||
<i class="fa-power-off"></i> {{t 'tools_shutdown_btn'}}
|
||||
</a>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<div class="alert alert-warning"><i class="fa-refresh"></i> {{t 'tools_rebooting'}}</div>
|
|
@ -1,34 +0,0 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/tools">{{t 'tools'}}</a>
|
||||
<a href="#/tools/ca">{{t 'tools_security_feed'}}</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
{{#if items}}
|
||||
<div class="list-group">
|
||||
{{#items}}
|
||||
<div class="list-group-item">
|
||||
<!-- <a href="{{url}}" class="fa-chevron-right pull-right"></a> -->
|
||||
<h2 class="list-group-item-heading"><a href="{{url}}">{{title}}</a></h2>
|
||||
<div class="list-group-item-text">
|
||||
<p class="pub-date"><em>{{date}}</em></p>
|
||||
{{{desc}}}
|
||||
<p class="text-right">
|
||||
<a href="{{url}}" role="button" class="btn btn-default">{{t 'read_more'}}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{{/items}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="alert alert-success">
|
||||
<span class="fa-thumbs-o-up"></span> {{t 'tools_security_feed_no_items'}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div>
|
||||
<a role="button" href="{{url.web}}" class="btn btn-success" target="_blank"><span class="fa-list"></span> {{t 'tools_security_feed_view_items'}}</a>
|
||||
<a role="button" href="{{url.rss}}" class="btn btn-warning" target="_blank"><span class="fa-rss"></span> {{t 'tools_security_feed_subscribe_rss'}}</a>
|
||||
</div>
|
|
@ -1 +0,0 @@
|
|||
<div class="alert alert-warning"><i class="fa-power-off"></i> {{t 'tools_shuttingdown'}}</div>
|
|
@ -20,7 +20,7 @@
|
|||
{{/system}}
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<a href="#/upgrade/system" role="button" class="btn btn-success">{{t 'system_upgrade_all_packages_btn'}}</a>
|
||||
<button class="btn btn-success" data-upgrade="system">{{t 'system_upgrade_all_packages_btn'}}</button>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="panel-body">
|
||||
|
@ -37,14 +37,14 @@
|
|||
<div class="list-group">
|
||||
{{#apps}}
|
||||
<div class="list-group-item clearfix">
|
||||
<a href="#/upgrade/apps/{{id}}" role="button" class="btn btn-success pull-right">{{t 'system_upgrade_btn'}}</a>
|
||||
<button class="btn btn-success pull-right" data-upgrade="{{id}}">{{t 'system_upgrade_btn'}}</button>
|
||||
<h3 class="list-group-item-heading">{{label}} <small>{{id}}</small></h3>
|
||||
<p class="list-group-item-text">{{t 'from_to' current_version new_version}}</p>
|
||||
<span class="list-group-item-text">{{t 'from_to' current_version new_version}}</span>
|
||||
</div>
|
||||
{{/apps}}
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<a role="button" href="#/upgrade/apps" class="btn btn-success">{{t 'system_upgrade_all_applications_btn'}}</a>
|
||||
<button class="btn btn-success" data-upgrade="apps">{{t 'system_upgrade_all_applications_btn'}}</button>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="panel-body">
|
|
@ -1,18 +0,0 @@
|
|||
<div class="btn-breadcrumb">
|
||||
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||
<a href="#/update">{{t 'system_update'}}</a>
|
||||
<a href="#/upgrade">{{t 'system_upgrade'}}</a>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
{{#if logs}}
|
||||
<pre id="upgrade-log" class="upgrade-log log">
|
||||
{{#logs}}
|
||||
{{.}}
|
||||
{{/logs}}
|
||||
</pre>
|
||||
<button data-paste-content="#upgrade-log"><i class="fa-cloud-upload"></i> {{t 'upload'}}</button>
|
||||
{{else}}
|
||||
{{t 'no_log'}}
|
||||
{{/if}}
|
|
@ -3,7 +3,7 @@
|
|||
<a href="#/users" class="visible-xs">…</a>
|
||||
<a href="#/users" class="hidden-xs">{{t 'users'}}</a>
|
||||
<a href="#/groups" class="visible-xs">…</a>
|
||||
<a href="#/groups" class="hidden-xs">{{t 'group_permissions'}}</a>
|
||||
<a href="#/groups" class="hidden-xs">{{t 'groups_and_permissions'}}</a>
|
||||
<a href="#/groups/create">{{t 'group_new'}}</a>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
<span class="label label-default label-removable">
|
||||
<span class="fa-fw fa-{{icon}}"></span>
|
||||
{{text}}
|
||||
<a role="button" data-type="{{type}}s" data-operation="remove" data-item="{{value}}" data-group="{{group}}" class="group-update">
|
||||
<button data-type="{{type}}s" data-action="remove" data-item="{{value}}" data-group="{{group}}">
|
||||
<span class="fa-close" style="margin-left:5px"></span>
|
||||
<span class="sr-only">{{t 'delete'}}</span>
|
||||
</a>
|
||||
</button>
|
||||
</span>
|
||||
{{/inline}}
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
|||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{{#each inv}}
|
||||
<li><a href="#" data-type="{{../type}}s" data-operation="add" data-item="{{.}}" data-group="{{../group}}" class="group-update">{{call ../display .}}</a></li>
|
||||
<li><button data-type="{{../type}}s" data-action="add" data-item="{{.}}" data-group="{{../group}}" >{{call ../display .}}</button></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -53,10 +53,10 @@
|
|||
<span class="fa-fw fa-group"></span> {{#if special}}{{t (concat 'group_' @key)}}{{else}}{{t 'group'}} "{{ucwords @key}}"{{/if}}
|
||||
</a>
|
||||
{{#unless special}}
|
||||
<a href="#/groups/{{@key}}/delete" role="button" class="group-delete">
|
||||
<button class="group-delete" data-action="delete-group" data-group="{{@key}}">
|
||||
<span class="fa-close"></span>
|
||||
<span class="sr-only">{{t 'delete'}}</span>
|
||||
</a>
|
||||
</button>
|
||||
{{/unless}}
|
||||
</h2>
|
||||
</div>
|
||||
|
@ -125,7 +125,7 @@
|
|||
{{#each groups}}
|
||||
{{#if primary}}
|
||||
{{#unless (or permissions display)}}
|
||||
<li><a href="#" data-user="{{@key}}" class="group-add-user">{{@key}}</a></li>
|
||||
<li><button data-action="add-user-specific-permission" data-user="{{@key}}">{{@key}}</button></li>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
</table>
|
||||
<span class="pull-right">
|
||||
<a role="button" href="#/users/{{username}}/edit" class="btn btn-info slide"><span class="fa-pencil-square-o"/> {{t 'user_username_edit' username}}</a>
|
||||
<a role="button" href="#/users/{{username}}/delete" class="btn btn-danger slide back"><span class="fa-trash-o"/> {{t 'delete'}}</a>
|
||||
<button class="btn btn-danger" data-action="delete" data-user="{{username}}"><span class="fa-trash-o"/> {{t 'delete'}}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
120
tests/test_i18n_keys.py
Normal file
120
tests/test_i18n_keys.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import re
|
||||
import glob
|
||||
import json
|
||||
import yaml
|
||||
import subprocess
|
||||
|
||||
###############################################################################
|
||||
# Find used keys in python code #
|
||||
###############################################################################
|
||||
|
||||
|
||||
def find_expected_string_keys():
|
||||
|
||||
# Try to find :
|
||||
# y18n.t("foo" +) # (the real key is a concatenation of 'foo' with something else)
|
||||
# y18n.t("foo") or y18n.t('foo', ...) # actual full key
|
||||
js_p1 = re.compile(r'y18n\.t\(\s*[\"\'](\w+)[\"\']\s*[\,\)]')
|
||||
js_p2 = re.compile(r'y18n\.t\(\s*[\"\'](\w+)[\"\']\s*\+')
|
||||
|
||||
js_files = glob.glob("../src/js/yunohost/controllers/*.js")
|
||||
js_files.extend(glob.glob("../src/js/yunohost/*.js"))
|
||||
|
||||
for js_file in js_files:
|
||||
content = open(js_file).read()
|
||||
for m in js_p1.findall(content):
|
||||
yield m
|
||||
for m in js_p2.findall(content):
|
||||
yield m
|
||||
|
||||
# In views we have stuff like {{t 'foo' arg}}
|
||||
views_p1 = re.compile(r'{{t\s*[\"\'](\w+)[\"\']')
|
||||
# Somes are inside {{ (t 'foo')))
|
||||
views_p2 = re.compile(r'\(t\s*[\"\'](\w+)[\"\']\)')
|
||||
views_p3 = re.compile(r't \(concat\s*[\"\'](\w+)[\"\']')
|
||||
views_p4 = re.compile(r'data-y18n=[\"\'](\w+)[\"\']')
|
||||
|
||||
view_files = glob.glob("../src/*.html")
|
||||
view_files.extend(glob.glob("../src/views/*.ms"))
|
||||
view_files.extend(glob.glob("../src/views/*/*.ms"))
|
||||
|
||||
for view_file in view_files:
|
||||
content = open(view_file).read()
|
||||
for m in views_p1.findall(content):
|
||||
yield m
|
||||
for m in views_p2.findall(content):
|
||||
yield m
|
||||
for m in views_p3.findall(content):
|
||||
yield m
|
||||
for m in views_p4.findall(content):
|
||||
yield m
|
||||
|
||||
# App maintenance state
|
||||
for state in ['maintained', 'orphaned', 'request_adoption', 'request_help','unmaintained']:
|
||||
yield state
|
||||
yield state + "_details"
|
||||
|
||||
# Service states
|
||||
for state in ['active', 'disabled', 'enabled', 'inactive']:
|
||||
yield state
|
||||
|
||||
yield "confirm_cert_"
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Load en locale json keys #
|
||||
###############################################################################
|
||||
|
||||
|
||||
def keys_defined_for_en():
|
||||
return json.loads(open("../src/locales/en.json").read()).keys()
|
||||
|
||||
###############################################################################
|
||||
# Compare keys used and keys defined #
|
||||
###############################################################################
|
||||
|
||||
|
||||
expected_string_keys = set(find_expected_string_keys())
|
||||
keys_defined = set(keys_defined_for_en())
|
||||
|
||||
def test_undefined_i18n_keys():
|
||||
undefined_keys = expected_string_keys.difference(keys_defined)
|
||||
undefined_keys = sorted(undefined_keys)
|
||||
|
||||
undefined_keys = [k for k in undefined_keys if not k.endswith("_")]
|
||||
|
||||
return undefined_keys
|
||||
|
||||
if undefined_keys:
|
||||
raise Exception("Those i18n keys should be defined in en.json:\n"
|
||||
" - " + "\n - ".join(undefined_keys))
|
||||
|
||||
|
||||
def test_unused_i18n_keys():
|
||||
|
||||
unused_keys = keys_defined.difference(expected_string_keys)
|
||||
unused_keys = sorted(unused_keys)
|
||||
|
||||
partial_match = [k for k in unused_keys if any(k.startswith(m) for m in expected_string_keys if m.endswith("_")) ]
|
||||
|
||||
unused_keys_2 = []
|
||||
for k in unused_keys:
|
||||
if not any(k.startswith(m) for m in expected_string_keys if m.endswith("_")):
|
||||
unused_keys_2.append(k)
|
||||
|
||||
return unused_keys_2
|
||||
|
||||
if unused_keys:
|
||||
raise Exception("Those i18n keys appears unused:\n"
|
||||
" - " + "\n - ".join(unused_keys))
|
||||
|
||||
|
||||
print("--------- undefined")
|
||||
print()
|
||||
print(test_undefined_i18n_keys())
|
||||
print("--------- unused")
|
||||
print()
|
||||
print(test_unused_i18n_keys())
|
Loading…
Reference in a new issue