diff --git a/www/core/components/sidemenu/controllers/iframe.js b/www/core/components/sidemenu/controllers/iframe.js new file mode 100644 index 00000000000..5f575fb9f90 --- /dev/null +++ b/www/core/components/sidemenu/controllers/iframe.js @@ -0,0 +1,27 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +angular.module('mm.core.sidemenu') + +/** + * Controller of the iframe viewer. + * + * @module mm.core.sidemenu + * @ngdoc controller + * @name mmSideMenuIframeViewCtrl + */ +.controller('mmSideMenuIframeViewCtrl', function($scope, $stateParams, $sce) { + $scope.title = $stateParams.title; + $scope.url = $sce.trustAsResourceUrl($stateParams.url); +}); diff --git a/www/core/components/sidemenu/controllers/menu.js b/www/core/components/sidemenu/controllers/menu.js index 5aef487ba79..d5795bb84f6 100644 --- a/www/core/components/sidemenu/controllers/menu.js +++ b/www/core/components/sidemenu/controllers/menu.js @@ -27,8 +27,7 @@ angular.module('mm.core.sidemenu') $mmSideMenu.setScope($scope); $scope.handlers = $mmSideMenuDelegate.getNavHandlers(); $scope.areNavHandlersLoaded = $mmSideMenuDelegate.areNavHandlersLoaded; - $scope.siteinfo = $mmSite.getInfo(); - loadLogoutLabel(); + loadSiteInfo(); $scope.logout = function() { $mmSitesManager.logout().finally(function() { @@ -40,25 +39,29 @@ angular.module('mm.core.sidemenu') $scope.docsurl = docsurl; }); + function loadSiteInfo() { + var config = $mmSite.getStoredConfig(); + + $scope.siteinfo = $mmSite.getInfo(); + $scope.logoutLabel = 'mm.sidemenu.' + (config && config.tool_mobile_forcelogout == "1" ? 'logout': 'changesite'); + + $mmSite.getDocsUrl().then(function(docsurl) { + $scope.docsurl = docsurl; + }); + + $mmSideMenu.getCustomMenuItems().then(function(items) { + $scope.customItems = items; + }); + } + function updateSiteInfo() { // We need to use $timeout to force a $digest and make $watch notice the variable change. $scope.siteinfo = undefined; $timeout(function() { - $scope.siteinfo = $mmSite.getInfo(); - loadLogoutLabel(); - - // Update docs URL, maybe the Moodle release has changed. - $mmSite.getDocsUrl().then(function(docsurl) { - $scope.docsurl = docsurl; - }); + loadSiteInfo(); }); } - function loadLogoutLabel() { - var config = $mmSite.getStoredConfig(); - $scope.logoutLabel = 'mm.sidemenu.' + (config && config.tool_mobile_forcelogout == "1" ? 'logout': 'changesite'); - } - var langObserver = $mmEvents.on(mmCoreEventLanguageChanged, updateSiteInfo); var updateSiteObserver = $mmEvents.on(mmCoreEventSiteUpdated, function(siteid) { if ($mmSite.getId() === siteid) { diff --git a/www/core/components/sidemenu/main.js b/www/core/components/sidemenu/main.js index 33e8a2a2e18..7391da33eaa 100644 --- a/www/core/components/sidemenu/main.js +++ b/www/core/components/sidemenu/main.js @@ -33,6 +33,20 @@ angular.module('mm.core.sidemenu', []) $state.go('mm_login.init'); } } + }) + + .state('site.iframe-view', { + url: '/iframe-view', + params: { + title: null, + url: null + }, + views: { + 'site': { + templateUrl: 'core/components/sidemenu/templates/iframe.html', + controller: 'mmSideMenuIframeViewCtrl' + } + } }); }) diff --git a/www/core/components/sidemenu/services/sidemenu.js b/www/core/components/sidemenu/services/sidemenu.js index bf9f3cf3b21..871410137a9 100644 --- a/www/core/components/sidemenu/services/sidemenu.js +++ b/www/core/components/sidemenu/services/sidemenu.js @@ -21,12 +21,102 @@ angular.module('mm.core.sidemenu') * @ngdoc service * @name $mmSideMenu */ -.factory('$mmSideMenu', function($log) { +.factory('$mmSideMenu', function($log, $mmLang, $mmSitesManager, mmCoreConfigConstants) { $log = $log.getInstance('$mmSideMenu'); var self = {}, scope; + /** + * Get a list of custom menu items for a certain site. + * + * @module mm.core.sidemenu + * @ngdoc method + * @name $mmSideMenu#getCustomMenuItems + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Object[]} List of custom menu items. + */ + self.getCustomMenuItems = function(siteId) { + return $mmSitesManager.getSite(siteId).then(function(site) { + var itemsString = site.getStoredConfig('tool_mobile_custommenuitems'), + items, + position = 0, // Position of each item, to keep the same order as it's configured. + map = {}, + result = []; + + if (!itemsString || typeof itemsString != 'string') { + // Setting not valid. + return result; + } + + // Add items to the map. + items = itemsString.split(/(?:\r\n|\r|\n)/); + angular.forEach(items, function(item) { + var values = item.split('|'), + id, + label = values[0] ? values[0].trim() : values[0], + url = values[1] ? values[1].trim() : values[1], + type = values[2] ? values[2].trim() : values[2], + lang = (values[3] ? values[3].trim() : values[3]) || 'none', + icon = values[4] ? values[4].trim() : values[4]; + + if (!label || !url || !type) { + // Invalid item, ignore it. + return; + } + + id = url + '#' + type; + if (!icon) { + // Icon not defined, use default one. + icon = type == 'embedded' ? 'ion-qr-scanner' : 'ion-link'; + } + + if (!map[id]) { + // New entry, add it to the map. + map[id] = { + url: url, + type: type, + position: position, + labels: {} + }; + position++; + } + + map[id].labels[lang.toLowerCase()] = { + label: label, + icon: icon + }; + }); + + if (!position) { + // No valid items found, stop. + return result; + } + + return $mmLang.getCurrentLanguage().then(function(currentLang) { + var fallbackLang = mmCoreConfigConstants.default_lang || 'en'; + + // Get the right label for each entry and add it to the result. + angular.forEach(map, function(entry) { + var data = entry.labels[currentLang] || entry.labels.none || entry.labels[fallbackLang]; + if (!data) { + // No valid label found, get the first one. + data = entry.labels[Object.keys(entry.labels)[0]]; + } + + result[entry.position] = { + url: entry.url, + type: entry.type, + label: data.label, + icon: data.icon + }; + }); + + return result; + }); + }); + }; + /** * Hide the right side menu. * diff --git a/www/core/components/sidemenu/templates/iframe.html b/www/core/components/sidemenu/templates/iframe.html new file mode 100644 index 00000000000..dc6516e41f9 --- /dev/null +++ b/www/core/components/sidemenu/templates/iframe.html @@ -0,0 +1,6 @@ + + {{ title }} + + + + \ No newline at end of file diff --git a/www/core/components/sidemenu/templates/menu.html b/www/core/components/sidemenu/templates/menu.html index b04674940a6..2e479aca015 100644 --- a/www/core/components/sidemenu/templates/menu.html +++ b/www/core/components/sidemenu/templates/menu.html @@ -44,6 +44,14 @@

{{siteinfo.fullname}}

+
  • + + {{item.label}} + + + {{item.label}} + +
  • {{ 'mm.sidemenu.website' | translate}} diff --git a/www/core/directives/iframe.js b/www/core/directives/iframe.js index 91d2345bddc..860c5556934 100644 --- a/www/core/directives/iframe.js +++ b/www/core/directives/iframe.js @@ -14,6 +14,8 @@ angular.module('mm.core') +.constant('mmCoreIframeTimeout', 15000) + /** * Directive to display content in an iframe. * @@ -27,7 +29,7 @@ angular.module('mm.core') * @param {Mixed} [width=100%] Width of the iframe. If not defined, use 100%. * @param {Mixed} [height=100%] Height of the iframe. If not defined, use 100%. */ -.directive('mmIframe', function($log, $mmUtil, $mmText, $mmSite, $mmFS) { +.directive('mmIframe', function($log, $mmUtil, $mmText, $mmSite, $mmFS, $timeout, mmCoreIframeTimeout) { $log = $log.getInstance('mmIframe'); var tags = ['iframe', 'frame', 'object', 'embed']; @@ -195,17 +197,38 @@ angular.module('mm.core') return { restrict: 'E', - template: '
    ', + templateUrl: 'core/templates/iframe.html', scope: { src: '=' }, link: function(scope, element, attrs) { + var url = (scope.src && scope.src.toString()) || '', // Convert $sce URLs to string URLs. + iframe = angular.element(element.find('iframe')[0]); + scope.width = $mmUtil.formatPixelsSize(attrs.iframeWidth) || '100%'; scope.height = $mmUtil.formatPixelsSize(attrs.iframeHeight) || '100%'; - var iframe = angular.element(element.find('iframe')[0]); + // Show loading only with external URLs. + scope.loading = !!url.match(/^https?:\/\//i); + treatFrame(iframe); + if (scope.loading) { + iframe.on('load', function() { + scope.loading = false; + $timeout(); // Use $timeout to force a digest and update the view. + }); + + iframe.on('error', function() { + scope.loading = false; + $mmUtil.showErrorModal('mm.core.errorloadingcontent', true); + $timeout(); // Use $timeout to force a digest and update the view. + }); + + $timeout(function() { + scope.loading = false; + }, mmCoreIframeTimeout); + } } }; }); diff --git a/www/core/directives/link.js b/www/core/directives/link.js index 6a0e66d2f1d..1edfb179a87 100644 --- a/www/core/directives/link.js +++ b/www/core/directives/link.js @@ -22,6 +22,7 @@ angular.module('mm.core') * @name mmLink * * @param {Boolean} [captureLink=false] If the link needs to be captured by the app. + * @param {Boolean} [inApp=false] True to open in embedded browser, false to open in system browser. * @param {String} [autoLogin=check] If the link should be open with auto-login. Accepts the following values: * "yes" -> Always auto-login. * "no" -> Never auto-login. @@ -33,9 +34,11 @@ angular.module('mm.core') * Convenience function to correctly navigate, open file or url in the browser. * * @param {String} href HREF to be opened. + * @param {Mixed} [inApp] True to open in embedded browser, false to open in system browser. * @param {String} [autoLogin=check] Whether to auto-login. "yes", "no" or "check". */ - function navigate(href, autoLogin) { + function navigate(href, inApp, autoLogin) { + inApp = inApp && inApp !== 'false'; autoLogin = autoLogin || 'check'; if (href.indexOf('cdvfile://') === 0 || href.indexOf('file://') === 0) { @@ -56,13 +59,29 @@ angular.module('mm.core') // It's an external link, we will open with browser. Check if we need to auto-login. if (!$mmSite.isLoggedIn()) { // Not logged in, cannot auto-login. - $mmUtil.openInBrowser(href); + if (inApp) { + $mmUtil.openInApp(href); + } else { + $mmUtil.openInBrowser(href); + } } else if (autoLogin == 'yes') { - $mmSite.openInBrowserWithAutoLogin(href); + if (inApp) { + $mmSite.openInAppWithAutoLogin(href); + } else { + $mmSite.openInBrowserWithAutoLogin(href); + } } else if (autoLogin == 'no') { - $mmUtil.openInBrowser(href); + if (inApp) { + $mmUtil.openInApp(href); + } else { + $mmUtil.openInBrowser(href); + } } else { - $mmSite.openInBrowserWithAutoLoginIfSameSite(href); + if (inApp) { + $mmSite.openInAppWithAutoLoginIfSameSite(href); + } else { + $mmSite.openInBrowserWithAutoLoginIfSameSite(href); + } } } } @@ -82,11 +101,11 @@ angular.module('mm.core') if (attrs.captureLink && attrs.captureLink !== 'false') { $mmContentLinksHelper.handleLink(href).then(function(treated) { if (!treated) { - navigate(href, attrs.autoLogin); + navigate(href, attrs.inApp, attrs.autoLogin); } }); } else { - navigate(href, attrs.autoLogin); + navigate(href, attrs.inApp, attrs.autoLogin); } } } diff --git a/www/core/lang/en.json b/www/core/lang/en.json index 9b372fb8d23..1fa1861487b 100644 --- a/www/core/lang/en.json +++ b/www/core/lang/en.json @@ -60,6 +60,7 @@ "errorfileexistssamename": "There's already a file with this name.", "errorinvalidform": "The form contains invalid data. Please make sure to fill all required fields and that the data is valid.", "errorinvalidresponse": "Invalid response received. Please contact your Moodle site administrator if the error persists.", + "errorloadingcontent": "Error loading content.", "erroropenfilenoapp": "Error opening the file: no app found to open this kind of file.", "erroropenfilenoextension": "Error opening the file: the file doesn't have extension.", "erroropenpopup": "This activity is trying to open a popup. This is not supported in this app.", diff --git a/www/core/templates/iframe.html b/www/core/templates/iframe.html new file mode 100644 index 00000000000..c6e9b741fc4 --- /dev/null +++ b/www/core/templates/iframe.html @@ -0,0 +1,4 @@ +
    + + +
    \ No newline at end of file