diff --git a/.eslintrc.json b/.eslintrc.json index 150a081d..b5e07288 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,7 +5,7 @@ "max-len": ["error", 250], "curly": "error", "camelcase": ["error", {"properties": "never"}], - "no-trailing-spaces": ["error"], + "no-trailing-spaces": ["error", {"ignoreComments": false }], "no-irregular-whitespace": ["error"] }, "env": { diff --git a/CHANGELOG.md b/CHANGELOG.md index 380ab383..c58f94b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,22 @@ This project adheres to [Semantic Versioning](http://semver.org/). **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install` ### Changed +- calender week is now handled with a variable translation in order to move number language specific ### Added - Add option to use [Nunjucks](https://mozilla.github.io/nunjucks/) templates in modules. (See `helloworld` module as an example.) +- Add Bulgarian translations for MagicMirror² and Alert module. +- Add graceful shutdown of modules by calling `stop` function of each `node_helper` on SIGINT before exiting. +- Link update subtext to Github diff of current version versus tracking branch. +- Add Catalan translation. ### Updated +### Fixed +- Fixed issue with calendar module showing more than `maximumEntries` allows +- WeatherForecast and CurrentWeather are now using HTTPS instead of HTTP +- Correcting translation for Indonesian language + ## [2.1.3] - 2017-10-01 **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install` diff --git a/README.md b/README.md index 9c9fcb3f..8fa876a6 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ The following properties can be configured: | **Option** | **Description** | | --- | --- | | `port` | The port on which the MagicMirror² server will run on. The default value is `8080`. | -| `address` | The ip address the accept connections. The default open bind `::` is IPv6 is available or `0.0.0.0` IPv4 run on. Example config: `192.168.10.100`. | +| `address` | The ip address the accept connections. The default open bind `localhost`. Example config: `192.168.10.100`. | | `ipWhitelist` | The list of IPs from which you are allowed to access the MagicMirror². The default value is `["127.0.0.1", "::ffff:127.0.0.1", "::1"]`. It is possible to specify IPs with subnet masks (`["127.0.0.1", "127.0.0.1/24"]`) or define ip ranges (`["127.0.0.1", ["192.168.0.1", "192.168.0.100"]]`). Set `[]` to allow all IP addresses. For more information about how configure this directive see the [follow post ipWhitelist HowTo](https://forum.magicmirror.builders/topic/1326/ipwhitelist-howto) | | `zoom` | This allows to scale the mirror contents with a given zoom factor. The default value is `1.0`| | `language` | The language of the interface. (Note: Not all elements will be localized.) Possible values are `en`, `nl`, `ru`, `fr`, etc., but the default value is `en`. | diff --git a/js/app.js b/js/app.js index 138fec70..bfec5baf 100644 --- a/js/app.js +++ b/js/app.js @@ -236,6 +236,33 @@ var App = function() { }); }); }; + + /* stop() + * This methods stops the core app. + * This calls each node_helper's STOP() function, if it exists. + * Added to fix #1056 + */ + this.stop = function() { + for (var h in nodeHelpers) { + var nodeHelper = nodeHelpers[h]; + if (typeof nodeHelper.stop === "function") { + nodeHelper.stop(); + } + } + }; + + /* Listen for SIGINT signal and call stop() function. + * + * Added to fix #1056 + * Note: this is only used if running `server-only`. Otherwise + * this.stop() is called by app.on("before-quit"... in `electron.js` + */ + process.on("SIGINT", () => { + console.log("[SIGINT] Received. Shutting down server..."); + setTimeout(() => { process.exit(0); }, 3000); // Force quit after 3 seconds + this.stop(); + process.exit(0); + }); }; module.exports = new App(); diff --git a/js/electron.js b/js/electron.js index 84842ed2..fc6ab98e 100644 --- a/js/electron.js +++ b/js/electron.js @@ -96,6 +96,20 @@ app.on("activate", function() { } }); +/* This method will be called when SIGINT is received and will call + * each node_helper's stop function if it exists. Added to fix #1056 + * + * Note: this is only used if running Electron. Otherwise + * core.stop() is called by process.on("SIGINT"... in `app.js` + */ +app.on("before-quit", (event) => { + console.log("Shutting down server..."); + event.preventDefault(); + setTimeout(() => { process.exit(0); }, 3000); // Force-quit after 3 seconds. + core.stop(); + process.exit(0); +}); + // Start the core application if server is run on localhost // This starts all node helpers and starts the webserver. if (["localhost", "127.0.0.1", "::1", "::ffff:127.0.0.1", undefined].indexOf(config.address) > -1) { diff --git a/js/module.js b/js/module.js index eeebb121..aa111ef0 100644 --- a/js/module.js +++ b/js/module.js @@ -27,7 +27,7 @@ var Module = Class.extend({ // visibility when hiding and showing module. lockStrings: [], - // Storage of the nunjuck Environment, + // Storage of the nunjuck Environment, // This should not be referenced directly. // Use the nunjucksEnvironment() to get it. _nunjucksEnvironment: null, @@ -89,6 +89,10 @@ var Module = Class.extend({ if (/^.*(\.html)$/.test(template)) { // the template is a filename this.nunjucksEnvironment().render(template, templateData, function (err, res) { + if (err) { + Log.error(err) + } + // The inner content of the div will be set after the template is received. // This isn't the most optimal way, but since it's near instant // it probably won't be an issue. @@ -156,7 +160,7 @@ var Module = Class.extend({ /** nunjucksEnvironment() * Returns the nunjucks environment for the current module. * The environment is checked in the _nunjucksEnvironment instance variable. - * + * @returns Nunjucks Environment */ nunjucksEnvironment: function() { diff --git a/modules/README.md b/modules/README.md index 12a42e59..76973996 100644 --- a/modules/README.md +++ b/modules/README.md @@ -555,6 +555,17 @@ start: function() { } ```` +#### `stop()` +This method is called when the MagicMirror server receives a `SIGINT` command and is shutting down. This method should include any commands needed to close any open connections, stop any sub-processes and gracefully exit the module. + +**Example:** +````javascript +stop: function() { + console.log("Shutting down MyModule"); + this.connection.close(); +} +```` + #### `socketNotificationReceived: function(notification, payload)` With this method, your node helper can receive notifications from your modules. When this method is called, it has 2 arguments: diff --git a/modules/default/alert/translations/bg.json b/modules/default/alert/translations/bg.json new file mode 100644 index 00000000..abf0c96f --- /dev/null +++ b/modules/default/alert/translations/bg.json @@ -0,0 +1,4 @@ +{ + "sysTitle": "MagicMirror нотификация", + "welcome": "Добре дошли, стартирането беше успешно" +} diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index ad3be8ce..ce0792eb 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -289,9 +289,9 @@ Module.register("calendar", { * This function accepts a number (either 12 or 24) and returns a moment.js LocaleSpecification with the * corresponding timeformat to be used in the calendar display. If no number is given (or otherwise invalid input) * it will a localeSpecification object with the system locale time format. - * + * * @param {number} timeFormat Specifies either 12 or 24 hour time format - * @returns {moment.LocaleSpecification} + * @returns {moment.LocaleSpecification} */ getLocaleSpecification: function(timeFormat) { switch (timeFormat) { @@ -356,7 +356,7 @@ Module.register("calendar", { return a.startDate - b.startDate; }); - return events; + return events.slice(0, this.config.maximumEntries); }, /* createEventList(url) @@ -430,7 +430,7 @@ Module.register("calendar", { /** * Shortens a string if it's longer than maxLength and add a ellipsis to the end - * + * * @param {string} string Text string to shorten * @param {number} maxLength The max length of the string * @param {boolean} wrapEvents Wrap the text after the line has reached maxLength diff --git a/modules/default/clock/clock.js b/modules/default/clock/clock.js index 9a0f57d7..705b4303 100644 --- a/modules/default/clock/clock.js +++ b/modules/default/clock/clock.js @@ -94,7 +94,7 @@ Module.register("clock",{ dateWrapper.innerHTML = now.format(this.config.dateFormat); } if (this.config.showWeek) { - weekWrapper.innerHTML = this.translate("WEEK") + " " + now.week(); + weekWrapper.innerHTML = this.translate("WEEK", { weekNumber: now.week() }); } timeWrapper.innerHTML = timeString; secondsWrapper.innerHTML = now.format("ss"); diff --git a/modules/default/currentweather/currentweather.js b/modules/default/currentweather/currentweather.js index 0352b7f9..f81f62db 100644 --- a/modules/default/currentweather/currentweather.js +++ b/modules/default/currentweather/currentweather.js @@ -33,7 +33,7 @@ Module.register("currentweather",{ retryDelay: 2500, apiVersion: "2.5", - apiBase: "http://api.openweathermap.org/data/", + apiBase: "https://api.openweathermap.org/data/", weatherEndpoint: "weather", appendLocationNameToHeader: true, diff --git a/modules/default/updatenotification/node_helper.js b/modules/default/updatenotification/node_helper.js index 6196552f..df989faa 100644 --- a/modules/default/updatenotification/node_helper.js +++ b/modules/default/updatenotification/node_helper.js @@ -64,7 +64,10 @@ module.exports = NodeHelper.create({ sg.git.fetch().status(function(err, data) { data.module = sg.module; if (!err) { - self.sendSocketNotification("STATUS", data); + sg.git.log({"-1": null}, function(err, data2) { + data.hash = data2.latest.hash; + self.sendSocketNotification("STATUS", data); + }); } }); }); diff --git a/modules/default/updatenotification/updatenotification.js b/modules/default/updatenotification/updatenotification.js index bf7ec2c1..306ac6ae 100644 --- a/modules/default/updatenotification/updatenotification.js +++ b/modules/default/updatenotification/updatenotification.js @@ -34,6 +34,17 @@ Module.register("updatenotification", { } }, + diffLink: function(text) { + var localRef = this.status.hash; + var remoteRef = this.status.tracking.replace(/.*\//, ""); + return "" + + text + + ""; + }, + // Override dom generator. getDom: function () { var wrapper = document.createElement("div"); @@ -47,9 +58,14 @@ Module.register("updatenotification", { icon.innerHTML = " "; message.appendChild(icon); + var subtextHtml = this.translate("UPDATE_INFO") + .replace("COMMIT_COUNT", this.status.behind + " " + ((this.status.behind == 1) ? "commit" : "commits")) + .replace("BRANCH_NAME", this.status.current); + var text = document.createElement("span"); if (this.status.module == "default") { text.innerHTML = this.translate("UPDATE_NOTIFICATION"); + subtextHtml = this.diffLink(subtextHtml); } else { text.innerHTML = this.translate("UPDATE_NOTIFICATION_MODULE").replace("MODULE_NAME", this.status.module); } @@ -58,9 +74,7 @@ Module.register("updatenotification", { wrapper.appendChild(message); var subtext = document.createElement("div"); - subtext.innerHTML = this.translate("UPDATE_INFO") - .replace("COMMIT_COUNT", this.status.behind + " " + ((this.status.behind == 1) ? "commit" : "commits")) - .replace("BRANCH_NAME", this.status.current); + subtext.innerHTML = subtextHtml; subtext.className = "xsmall dimmed"; wrapper.appendChild(subtext); } diff --git a/modules/default/weatherforecast/weatherforecast.js b/modules/default/weatherforecast/weatherforecast.js index e357387a..56e6a758 100644 --- a/modules/default/weatherforecast/weatherforecast.js +++ b/modules/default/weatherforecast/weatherforecast.js @@ -30,7 +30,7 @@ Module.register("weatherforecast",{ retryDelay: 2500, apiVersion: "2.5", - apiBase: "http://api.openweathermap.org/data/", + apiBase: "https://api.openweathermap.org/data/", forecastEndpoint: "forecast/daily", appendLocationNameToHeader: true, diff --git a/modules/node_modules/node_helper/index.js b/modules/node_modules/node_helper/index.js index 37c3e2cb..92931140 100644 --- a/modules/node_modules/node_helper/index.js +++ b/modules/node_modules/node_helper/index.js @@ -23,6 +23,16 @@ NodeHelper = Class.extend({ console.log("Starting module helper: " + this.name); }, + /* stop() + * Called when the MagicMirror server receives a `SIGINT` + * Close any open connections, stop any sub-processes and + * gracefully exit the module. + * + */ + stop: function() { + console.log("Stopping module helper: " + this.name); + }, + /* socketNotificationReceived(notification, payload) * This method is called when a socket notification arrives. * diff --git a/tests/e2e/without_modules.js b/tests/e2e/without_modules.js index e0eda168..f3b5d920 100644 --- a/tests/e2e/without_modules.js +++ b/tests/e2e/without_modules.js @@ -25,7 +25,7 @@ describe("Check configuration without modules", function () { }); before(function () { - // Set config sample for use in test + // Set config sample for use in test process.env.MM_CONFIG_FILE = "tests/configs/without_modules.js"; }); diff --git a/translations/bg.json b/translations/bg.json new file mode 100644 index 00000000..e45c1c46 --- /dev/null +++ b/translations/bg.json @@ -0,0 +1,32 @@ +{ + "LOADING": "Зареждане …", + + "TODAY": "Днес", + "TOMORROW": "Утре", + "DAYAFTERTOMORROW": "Вдругиден", + "RUNNING": "Свършва на", + "EMPTY": "Няма предстоящи събития.", + + "WEEK": "Седмица {weekNumber}", + + "N": "С", + "NNE": "ССИ", + "NE": "СИ", + "ENE": "ИСИ", + "E": "И", + "ESE": "ИЮИ", + "SE": "ЮИ", + "SSE": "ЮЮИ", + "S": "Ю", + "SSW": "ЮЮЗ", + "SW": "ЮЗ", + "WSW": "ЗЮЗ", + "W": "З", + "WNW": "ЗСЗ", + "NW": "СЗ", + "NNW": "ССЗ", + + "UPDATE_NOTIFICATION": "Налична актуализация за MagicMirror².", + "UPDATE_NOTIFICATION_MODULE": "Налична актуализация за MODULE_NAME модул.", + "UPDATE_INFO": "Текущата инсталация е изостанала с COMMIT_COUNT къмита на клон BRANCH_NAME." +} diff --git a/translations/ca.json b/translations/ca.json new file mode 100644 index 00000000..758aa7a1 --- /dev/null +++ b/translations/ca.json @@ -0,0 +1,32 @@ +{ + "LOADING": "Carregant …", + + "TODAY": "Avui", + "TOMORROW": "Demà", + "DAYAFTERTOMORROW": "Demà passat", + "RUNNING": "Acaba en", + "EMPTY": "No hi ha esdeveniments programats.", + + "WEEK": "Setmana", + + "N": "N", + "NNE": "NNE", + "NE": "NE", + "ENE": "ENE", + "E": "E", + "ESE": "ESE", + "SE": "SE", + "SSE": "SSE", + "S": "S", + "SSW": "SSO", + "SW": "SO", + "WSW": "OSO", + "W": "O", + "WNW": "ONO", + "NW": "NO", + "NNW": "NNO", + + "UPDATE_NOTIFICATION": "MagicMirror² actualizació disponible.", + "UPDATE_NOTIFICATION_MODULE": "Disponible una actualizació per al mòdul MODULE_NAME.", + "UPDATE_INFO": "La teva instal·lació actual està COMMIT_COUNT canvis darrere de la branca BRANCH_NAME." +} diff --git a/translations/cy.json b/translations/cy.json index c823b7f6..d0b2a077 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -7,7 +7,7 @@ "RUNNING": "Gorffen mewn", "EMPTY": "Dim digwyddiadau.", - "WEEK": "Wythnos", + "WEEK": "Wythnos {weekNumber}", "N": "Go", "NNE": "GoGoDw", diff --git a/translations/de.json b/translations/de.json index 2723bf86..278a4bba 100644 --- a/translations/de.json +++ b/translations/de.json @@ -7,7 +7,7 @@ "RUNNING": "noch", "EMPTY": "Keine Termine.", - "WEEK": "Woche", + "WEEK": "{weekNumber}. Kalenderwoche", "N": "N", "NNE": "NNO", diff --git a/translations/en.json b/translations/en.json index a1472cf1..7e048c8c 100644 --- a/translations/en.json +++ b/translations/en.json @@ -7,7 +7,7 @@ "RUNNING": "Ends in", "EMPTY": "No upcoming events.", - "WEEK": "Week", + "WEEK": "Week {weekNumber}", "N": "N", "NNE": "NNE", diff --git a/translations/es.json b/translations/es.json index adee4dfc..21634351 100644 --- a/translations/es.json +++ b/translations/es.json @@ -7,7 +7,7 @@ "RUNNING": "Termina en", "EMPTY": "No hay eventos programados.", - "WEEK": "Semana", + "WEEK": "Semana {weekNumber}", "N": "N", "NNE": "NNE", diff --git a/translations/fr.json b/translations/fr.json index c17b2df8..87c26a6a 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -7,7 +7,7 @@ "RUNNING": "Se termine dans", "EMPTY": "Aucun RDV à venir.", - "WEEK": "Semaine", + "WEEK": "Semaine {weekNumber}", "N": "N", "NNE": "NNE", diff --git a/translations/id.json b/translations/id.json index a311f960..1ffea7fc 100644 --- a/translations/id.json +++ b/translations/id.json @@ -6,25 +6,27 @@ "DAYAFTERTOMORROW": "Lusa", "RUNNING": "Berakhir dalam", "EMPTY": "Tidak ada agenda", - + + "WEEK": "Pekan", + "N": "U", - "NNE": "UUT", - "NE": "NE", - "ENE": "TUT", + "NNE": "UTL", + "NE": "TL", + "ENE": "TTL", "E": "T", - "ESE": "TST", - "SE": "ST", - "SSE": "SST", + "ESE": "TMg", + "SE": "TG", + "SSE": "SMg", "S": "S", - "SSW": "SSB", - "SW": "SB", - "WSW": "BSB", + "SSW": "SBD", + "SW": "BD", + "WSW": "BBD", "W": "B", - "WNW": "BUB", - "NW": "UB", - "NNW": "UUB", + "WNW": "BBL", + "NW": "BL", + "NNW": "UBL", - "UPDATE_NOTIFICATION": "Update MagicMirror² tersedia.", - "UPDATE_NOTIFICATION_MODULE": "Update tersedia untuk modul MODULE_NAME.", + "UPDATE_NOTIFICATION": "Memperbarui MagicMirror² tersedia.", + "UPDATE_NOTIFICATION_MODULE": "Memperbarui tersedia untuk modul MODULE_NAME.", "UPDATE_INFO": "Instalasi saat ini tertinggal COMMIT_COUNT pada cabang BRANCH_NAME." } diff --git a/translations/nb.json b/translations/nb.json index 3aad4263..36a28de2 100644 --- a/translations/nb.json +++ b/translations/nb.json @@ -7,7 +7,7 @@ "RUNNING": "Slutter om", "EMPTY": "Ingen kommende arrangementer.", - "WEEK": "Uke", + "WEEK": "Uke {weekNumber}", "N": "N", "NNE": "NNØ", diff --git a/translations/pl.json b/translations/pl.json index 1d30e395..3c69d3bc 100644 --- a/translations/pl.json +++ b/translations/pl.json @@ -7,7 +7,7 @@ "RUNNING": "Koniec za", "EMPTY": "Brak wydarzeń.", - "WEEK": "Tydzień", + "WEEK": "Tydzień {weekNumber}", "N": "N", "NNE": "NNE", diff --git a/translations/pt.json b/translations/pt.json index 07148c2c..c340ff51 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -6,7 +6,7 @@ "RUNNING": "Termina em", "EMPTY": "Sem eventos programados.", - "WEEK": "Semana", + "WEEK": "Semana {weekNumber}", "N": "N", "NNE": "NNE", diff --git a/translations/ro.json b/translations/ro.json index 4105763e..cf1eca73 100644 --- a/translations/ro.json +++ b/translations/ro.json @@ -7,7 +7,7 @@ "RUNNING": "Se termină în", "EMPTY": "Nici un eveniment.", - "WEEK": "Săptămâna", + "WEEK": "Săptămâna {weekNumber}", "N": "N", "NNE": "NNE", diff --git a/translations/ru.json b/translations/ru.json index c919af50..eee96ebc 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -7,7 +7,7 @@ "RUNNING": "Заканчивается через", "EMPTY": "Нет предстоящих событий", - "WEEK": "Неделя", + "WEEK": "Неделя {weekNumber}", "N": "С", "NNE": "ССВ", diff --git a/translations/sv.json b/translations/sv.json index 13288ec3..d33461ef 100644 --- a/translations/sv.json +++ b/translations/sv.json @@ -7,7 +7,7 @@ "RUNNING": "Slutar", "EMPTY": "Inga kommande händelser.", - "WEEK": "Vecka", + "WEEK": "Vecka {weekNumber}", "N": "N", "NNE": "NNO", diff --git a/translations/translations.js b/translations/translations.js index e088c5a3..d1249434 100644 --- a/translations/translations.js +++ b/translations/translations.js @@ -13,6 +13,7 @@ var translations = { "fr" : "translations/fr.json", // French "fy" : "translations/fy.json", // Frysk "es" : "translations/es.json", // Spanish + "ca" : "translations/ca.json", // Catalan "nb" : "translations/nb.json", // Norsk bokmål "nn" : "translations/nn.json", // Norsk nynorsk "pt" : "translations/pt.json", // Português @@ -34,7 +35,8 @@ var translations = { "et" : "translations/et.json", // Estonian "kr" : "translations/kr.json", // Korean "ro" : "translations/ro.json", // Romanian - "cy" : "translations/cy.json" // Welsh (Cymraeg) + "cy" : "translations/cy.json", // Welsh (Cymraeg) + "bg" : "translations/bg.json" // Bulgarian }; if (typeof module !== "undefined") {module.exports = translations;}