1414
1515angular . module ( 'mm.core' )
1616
17+ . constant ( 'mmAddonManagerComponent' , 'mmAddonManager' )
18+
1719/**
1820 * @ngdoc service
1921 * @name $mmAddonManager
2022 * @module mm.core
2123 * @description
2224 * This service provides functions related to addons, like checking if an addon is available.
2325 */
24- . factory ( '$mmAddonManager' , function ( $log , $injector ) {
26+ . factory ( '$mmAddonManager' , function ( $log , $injector , $ocLazyLoad , $mmFilepool , $mmSite , $mmFS , $mmLang , $mmSitesManager , $q ,
27+ $mmUtil , mmAddonManagerComponent , mmCoreNotDownloaded ) {
2528
2629 $log = $log . getInstance ( '$mmAddonManager' ) ;
2730
2831 var self = { } ,
29- instances = { } ;
32+ instances = { } ,
33+ remoteAddonsFolderName = 'remoteaddons' ,
34+ remoteAddonFilename = 'addon.js' ,
35+ remoteAddonCssFilename = 'styles.css' ,
36+ pathWildcardRegex = / \$ A D D O N P A T H \$ / g,
37+ headEl = angular . element ( document . querySelector ( 'head' ) ) ,
38+ loadedAddons = [ ] ;
39+
40+ /**
41+ * Download a remote addon if it's not downloaded already.
42+ *
43+ * @module mm.core
44+ * @ngdoc method
45+ * @name $mmAddonManager#downloadRemoteAddon
46+ * @param {Object } addon Addon to download.
47+ * @param {String } [siteId] Site ID. If not defined, current site.
48+ * @return {Promise } Promise resolved when the file is downloaded. Data returned is not reliable.
49+ */
50+ self . downloadRemoteAddon = function ( addon , siteId ) {
51+ siteId = siteId || $mmSite . getId ( ) ;
52+
53+ var name = self . getRemoteAddonName ( addon ) ,
54+ dirPath = self . getRemoteAddonDirectoryPath ( addon ) ,
55+ revision = addon . filehash ,
56+ file = {
57+ filename : name + '.zip' ,
58+ fileurl : addon . fileurl
59+ } ;
60+
61+ // Get the status to check if it's already downloaded.
62+ return $mmFilepool . getPackageStatus ( siteId , mmAddonManagerComponent , name , revision , 0 ) . then ( function ( status ) {
63+ if ( status !== $mmFilepool . FILEDOWNLOADED ) {
64+ // Not downloaded or outdated. Download the ZIP file in the filepool folder.
65+ return $mmFilepool . downloadPackage ( siteId , [ file ] , mmAddonManagerComponent , name , revision , 0 ) . then ( function ( ) {
66+ // Remove the destination folder to prevent having old unused files.
67+ return $mmFS . removeDir ( dirPath ) . catch ( function ( ) { } ) ;
68+ } ) . then ( function ( ) {
69+ // Get the ZIP file path.
70+ return $mmFilepool . getFilePathByUrl ( siteId , addon . fileurl ) ;
71+ } ) . then ( function ( zipPath ) {
72+ // Unzip and delete the zip when finished.
73+ return $mmFS . unzipFile ( zipPath , dirPath ) . then ( function ( ) {
74+ return $mmFilepool . removeFileByUrl ( siteId , addon . fileurl ) . catch ( function ( ) { } ) ;
75+ } ) ;
76+ } ) . then ( function ( ) {
77+ // Get the directory to get the absolute dirPath.
78+ return $mmFS . getDir ( dirPath ) ;
79+ } ) . then ( function ( dir ) {
80+ var absoluteDirPath = $mmFS . getInternalURL ( dir ) ;
81+
82+ // Remove / in the end if it's there.
83+ if ( absoluteDirPath . slice ( - 1 ) == '/' ) {
84+ absoluteDirPath = absoluteDirPath . substring ( 0 , absoluteDirPath . length - 1 ) ;
85+ }
86+
87+ // Replace path wildcards with the right path.
88+ var addonMainFile = $mmFS . concatenatePaths ( dirPath , remoteAddonFilename ) ;
89+ return $mmFS . replaceInFile ( addonMainFile , pathWildcardRegex , absoluteDirPath ) ;
90+ } ) . catch ( function ( ) {
91+ // Error, set status as it was before.
92+ return self . setRemoteAddonStatus ( addon , status ) . then ( function ( ) {
93+ return $q . reject ( ) ;
94+ } ) ;
95+ } ) ;
96+ }
97+ } ) ;
98+ } ;
99+
100+ /**
101+ * Download the remote addons for a certain site.
102+ *
103+ * @module mm.core
104+ * @ngdoc method
105+ * @name $mmAddonManager#downloadRemoteAddons
106+ * @param {String } [siteId] Site ID. If not defined, current site.
107+ * @return {Promise } Promise resolved when done. Returns the list of downloaded addons.
108+ */
109+ self . downloadRemoteAddons = function ( siteId ) {
110+ siteId = siteId || $mmSite . getId ( ) ;
111+
112+ var downloaded = [ ] ,
113+ preSets = { } ;
114+
115+ return $mmSitesManager . getSite ( siteId ) . then ( function ( site ) {
116+ // Get the list of addons. Try not to use cache.
117+ preSets . getFromCache = 0 ;
118+ return site . read ( 'tool_mobile_get_plugins_supporting_mobile' , { } , preSets ) . then ( function ( data ) {
119+ var promises = [ ] ;
120+
121+ angular . forEach ( data . plugins , function ( addon ) {
122+ promises . push ( self . downloadRemoteAddon ( addon , siteId ) . then ( function ( ) {
123+ downloaded . push ( addon ) ;
124+ } ) ) ;
125+ } ) ;
126+
127+ return $mmUtil . allPromises ( promises ) . then ( function ( ) {
128+ return downloaded ;
129+ } ) . catch ( function ( ) {
130+ // Some download failed, return the downloaded ones anyway.
131+ return downloaded ;
132+ } ) ;
133+ } ) ;
134+ } ) ;
135+ } ;
30136
31137 /**
32138 * Get a service instance if it's available.
@@ -43,6 +149,49 @@ angular.module('mm.core')
43149 }
44150 } ;
45151
152+ /**
153+ * Gets the relative path of an addon directory.
154+ * It should be {FILEPOOLPATH}/{remoteAddonsFolderName}/{addonname}.
155+ *
156+ * @module mm.core
157+ * @ngdoc method
158+ * @name $mmAddonManager#getRemoteAddonDirectoryPath
159+ * @param {Object } addon Remote addon.
160+ * @param {String } [siteId] Site ID. If not defined, current site.
161+ * @return {String } Directory path.
162+ */
163+ self . getRemoteAddonDirectoryPath = function ( addon , siteId ) {
164+ siteId = siteId || $mmSite . getId ( ) ;
165+
166+ var subPath = remoteAddonsFolderName + '/' + self . getRemoteAddonName ( addon ) ;
167+ return $mmFilepool . _getFilePath ( siteId , subPath ) ;
168+ } ;
169+
170+ /**
171+ * Get the name of a remote addon.
172+ *
173+ * @module mm.core
174+ * @ngdoc method
175+ * @name $mmAddonManager#getRemoteAddonName
176+ * @param {Object } addon Remote addon.
177+ * @return {String } Name.
178+ */
179+ self . getRemoteAddonName = function ( addon ) {
180+ return addon . component + '_' + addon . addon ;
181+ } ;
182+
183+ /**
184+ * Check if there are remote addons loaded.
185+ *
186+ * @module mm.core
187+ * @ngdoc method
188+ * @name $mmAddonManager#hasRemoteAddonsLoaded
189+ * @return {Boolean } True if remote addons loaded, false otherwise.
190+ */
191+ self . hasRemoteAddonsLoaded = function ( ) {
192+ return loadedAddons . length ;
193+ } ;
194+
46195 /**
47196 * Check if a service is available.
48197 *
@@ -70,5 +219,99 @@ angular.module('mm.core')
70219 }
71220 } ;
72221
222+ /**
223+ * Load a remote addon.
224+ *
225+ * @module mm.core
226+ * @ngdoc method
227+ * @name $mmAddonManager#loadRemoteAddon
228+ * @param {Object } addon Addon to load.
229+ * @return {Promise } Promise resolved when loaded.
230+ */
231+ self . loadRemoteAddon = function ( addon ) {
232+ var dirPath = self . getRemoteAddonDirectoryPath ( addon ) ,
233+ absoluteDirPath ;
234+
235+ // Get the absolute path to the directory.
236+ return $mmFS . getDir ( dirPath ) . then ( function ( dir ) {
237+ absoluteDirPath = $mmFS . getInternalURL ( dir ) ;
238+
239+ // Register language folder so the language strings of the addon are loaded.
240+ $mmLang . registerLanguageFolder ( $mmFS . concatenatePaths ( absoluteDirPath , 'lang' ) ) ;
241+ // Load the addon.
242+ return $ocLazyLoad . load ( $mmFS . concatenatePaths ( absoluteDirPath , remoteAddonFilename ) ) . then ( function ( ) {
243+ loadedAddons . push ( addon ) ;
244+ // Check if the addon has a CSS file.
245+ return $mmFS . getFile ( $mmFS . concatenatePaths ( dirPath , remoteAddonCssFilename ) ) . then ( function ( file ) {
246+ // The file exists, add it in the head.
247+ headEl . append ( '<link class="remoteaddonstyles" rel="stylesheet" href="' + $mmFS . getInternalURL ( file ) + '">' ) ;
248+ } ) . catch ( function ( ) { } ) ;
249+ } ) ;
250+ } , function ( ) {
251+ // Directory not found, set status as not downloaded.
252+ return self . setRemoteAddonStatus ( addon , mmCoreNotDownloaded ) . then ( function ( ) {
253+ return $q . reject ( ) ;
254+ } ) ;
255+ } ) ;
256+ } ;
257+
258+ /**
259+ * Load a list of remote addons.
260+ *
261+ * @module mm.core
262+ * @ngdoc method
263+ * @name $mmAddonManager#loadRemoteAddons
264+ * @param {Object[] } addons Addons to load.
265+ * @return {Promise } Promise resolved when all have been loaded.
266+ */
267+ self . loadRemoteAddons = function ( addons ) {
268+ var promises = [ ] ;
269+ angular . forEach ( addons , function ( addon ) {
270+ promises . push ( self . loadRemoteAddon ( addon ) ) ;
271+ } ) ;
272+ return $mmUtil . allPromises ( promises ) ;
273+ } ;
274+
275+ /**
276+ * Set status of a remote addon.
277+ *
278+ * @module mm.core
279+ * @ngdoc method
280+ * @name $mmAddonManager#setRemoteAddonStatus
281+ * @param {Object } addon Addon.
282+ * @param {String } status Status to set.
283+ * @param {String } [siteId] Site ID. If not defined, current site.
284+ * @return {Promise } Promise resolved when set.
285+ */
286+ self . setRemoteAddonStatus = function ( addon , status , siteId ) {
287+ siteId = siteId || $mmSite . getId ( ) ;
288+
289+ var name = self . getRemoteAddonName ( addon ) ,
290+ revision = addon . filehash ;
291+ return $mmFilepool . storePackageStatus ( siteId , mmAddonManagerComponent , name , status , revision , 0 ) ;
292+ } ;
293+
73294 return self ;
295+ } )
296+
297+ . run ( function ( $mmAddonManager , $mmEvents , mmCoreEventLogin , mmCoreEventLogout , mmCoreEventRemoteAddonsLoaded , $mmSite , $window ) {
298+ // Download and load remote addons on login.
299+ $mmEvents . on ( mmCoreEventLogin , function ( ) {
300+ var siteId = $mmSite . getId ( ) ;
301+ $mmAddonManager . downloadRemoteAddons ( siteId ) . then ( function ( addons ) {
302+ return $mmAddonManager . loadRemoteAddons ( addons ) . finally ( function ( ) {
303+ if ( $mmSite . getId ( ) == siteId && $mmAddonManager . hasRemoteAddonsLoaded ( ) ) {
304+ $mmEvents . trigger ( mmCoreEventRemoteAddonsLoaded ) ;
305+ }
306+ } ) ;
307+ } ) ;
308+ } ) ;
309+
310+ // Unload remote addons on logout if any.
311+ $mmEvents . on ( mmCoreEventLogout , function ( ) {
312+ if ( $mmAddonManager . hasRemoteAddonsLoaded ( ) ) {
313+ // Temporary fix. Reload the page to unload all remote addons.
314+ $window . location . reload ( ) ;
315+ }
316+ } ) ;
74317} ) ;
0 commit comments