Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
/*jshint -W083 */
2
var H5PUpgrades = H5PUpgrades || {};
3
 
4
H5P.ContentUpgradeProcess = (function (Version) {
5
 
6
  /**
7
   * @class
8
   * @namespace H5P
9
   */
10
  function ContentUpgradeProcess(name, oldVersion, newVersion, params, id, loadLibrary, done) {
11
    var self = this;
12
 
13
    // Make params possible to work with
14
    try {
15
      params = JSON.parse(params);
16
      if (!(params instanceof Object)) {
17
        throw true;
18
      }
19
    }
20
    catch (event) {
21
      return done({
22
        type: 'errorParamsBroken',
23
        id: id
24
      });
25
    }
26
 
27
    self.loadLibrary = loadLibrary;
28
    self.upgrade(name, oldVersion, newVersion, params.params, params.metadata, function (err, upgradedParams, upgradedMetadata) {
29
      if (err) {
30
        err.id = id;
31
        return done(err);
32
      }
33
 
34
      done(null, JSON.stringify({params: upgradedParams, metadata: upgradedMetadata}));
35
    });
36
  }
37
 
38
  /**
39
   * Run content upgrade.
40
   *
41
   * @public
42
   * @param {string} name
43
   * @param {Version} oldVersion
44
   * @param {Version} newVersion
45
   * @param {Object} params
46
   * @param {Object} metadata
47
   * @param {Function} done
48
   */
49
  ContentUpgradeProcess.prototype.upgrade = function (name, oldVersion, newVersion, params, metadata, done) {
50
    var self = this;
51
 
52
    // Load library details and upgrade routines
53
    self.loadLibrary(name, newVersion, function (err, library) {
54
      if (err) {
55
        return done(err);
56
      }
57
      if (library.semantics === null) {
58
        return done({
59
          type: 'libraryMissing',
60
          library: library.name + ' ' + library.version.major + '.' + library.version.minor
61
        });
62
      }
63
 
64
      // Run upgrade routines on params
65
      self.processParams(library, oldVersion, newVersion, params, metadata, function (err, params, metadata) {
66
        if (err) {
67
          return done(err);
68
        }
69
 
70
        // Check if any of the sub-libraries need upgrading
71
        asyncSerial(library.semantics, function (index, field, next) {
72
          self.processField(field, params[field.name], function (err, upgradedParams) {
73
            if (upgradedParams) {
74
              params[field.name] = upgradedParams;
75
            }
76
            next(err);
77
          });
78
        }, function (err) {
79
          done(err, params, metadata);
80
        });
81
      });
82
    });
83
  };
84
 
85
  /**
86
   * Run upgrade hooks on params.
87
   *
88
   * @public
89
   * @param {Object} library
90
   * @param {Version} oldVersion
91
   * @param {Version} newVersion
92
   * @param {Object} params
93
   * @param {Function} next
94
   */
95
  ContentUpgradeProcess.prototype.processParams = function (library, oldVersion, newVersion, params, metadata, next) {
96
    if (H5PUpgrades[library.name] === undefined) {
97
      if (library.upgradesScript) {
98
        // Upgrades script should be loaded so the upgrades should be here.
99
        return next({
100
          type: 'scriptMissing',
101
          library: library.name + ' ' + newVersion
102
        });
103
      }
104
 
105
      // No upgrades script. Move on
106
      return next(null, params, metadata);
107
    }
108
 
109
    // Run upgrade hooks. Start by going through major versions
110
    asyncSerial(H5PUpgrades[library.name], function (major, minors, nextMajor) {
111
      if (major < oldVersion.major || major > newVersion.major) {
112
        // Older than the current version or newer than the selected
113
        nextMajor();
114
      }
115
      else {
116
        // Go through the minor versions for this major version
117
        asyncSerial(minors, function (minor, upgrade, nextMinor) {
118
          minor =+ minor;
119
          if (minor <= oldVersion.minor || minor > newVersion.minor) {
120
            // Older than or equal to the current version or newer than the selected
121
            nextMinor();
122
          }
123
          else {
124
            // We found an upgrade hook, run it
125
            var unnecessaryWrapper = (upgrade.contentUpgrade !== undefined ? upgrade.contentUpgrade : upgrade);
126
 
127
            try {
128
              unnecessaryWrapper(params, function (err, upgradedParams, upgradedExtras) {
129
                params = upgradedParams;
130
                if (upgradedExtras && upgradedExtras.metadata) { // Optional
131
                  metadata = upgradedExtras.metadata;
132
                }
133
                nextMinor(err);
134
              }, {metadata: metadata});
135
            }
136
            catch (err) {
137
              if (console && console.error) {
138
                console.error("Error", err.stack);
139
                console.error("Error", err.name);
140
                console.error("Error", err.message);
141
              }
142
              next(err);
143
            }
144
          }
145
        }, nextMajor);
146
      }
147
    }, function (err) {
148
      next(err, params, metadata);
149
    });
150
  };
151
 
152
  /**
153
   * Process parameter fields to find and upgrade sub-libraries.
154
   *
155
   * @public
156
   * @param {Object} field
157
   * @param {Object} params
158
   * @param {Function} done
159
   */
160
  ContentUpgradeProcess.prototype.processField = function (field, params, done) {
161
    var self = this;
162
 
163
    if (params === undefined || params === null) {
164
      return done();
165
    }
166
 
167
    switch (field.type) {
168
      case 'library':
169
        if (params.library === undefined || params.params === undefined) {
170
          return done();
171
        }
172
 
173
        // Look for available upgrades
174
        var usedLib = params.library.split(' ', 2);
175
        for (var i = 0; i < field.options.length; i++) {
176
          var availableLib = (typeof field.options[i] === 'string') ? field.options[i].split(' ', 2) : field.options[i].name.split(' ', 2);
177
          if (availableLib[0] === usedLib[0]) {
178
            if (availableLib[1] === usedLib[1]) {
179
              return done(); // Same version
180
            }
181
 
182
            // We have different versions
183
            var usedVer = new Version(usedLib[1]);
184
            var availableVer = new Version(availableLib[1]);
185
            if (usedVer.major > availableVer.major || (usedVer.major === availableVer.major && usedVer.minor >= availableVer.minor)) {
186
              return done({
187
                type: 'errorTooHighVersion',
188
                used: usedLib[0] + ' ' + usedVer,
189
                supported: availableLib[0] + ' ' + availableVer
190
              }); // Larger or same version that's available
191
            }
192
 
193
            // A newer version is available, upgrade params
194
            return self.upgrade(availableLib[0], usedVer, availableVer, params.params, params.metadata, function (err, upgradedParams, upgradedMetadata) {
195
              if (!err) {
196
                params.library = availableLib[0] + ' ' + availableVer.major + '.' + availableVer.minor;
197
                params.params = upgradedParams;
198
                if (upgradedMetadata) {
199
                  params.metadata = upgradedMetadata;
200
                }
201
              }
202
              done(err, params);
203
            });
204
          }
205
        }
206
 
207
        // Content type was not supporte by the higher version
208
        done({
209
          type: 'errorNotSupported',
210
          used: usedLib[0] + ' ' + usedVer
211
        });
212
        break;
213
 
214
      case 'group':
215
        if (field.fields.length === 1 && field.isSubContent !== true) {
216
          // Single field to process, wrapper will be skipped
217
          self.processField(field.fields[0], params, function (err, upgradedParams) {
218
            if (upgradedParams) {
219
              params = upgradedParams;
220
            }
221
            done(err, params);
222
          });
223
        }
224
        else {
225
          // Go through all fields in the group
226
          asyncSerial(field.fields, function (index, subField, next) {
227
            var paramsToProcess = params ? params[subField.name] : null;
228
            self.processField(subField, paramsToProcess, function (err, upgradedParams) {
229
              if (upgradedParams) {
230
                params[subField.name] = upgradedParams;
231
              }
232
              next(err);
233
            });
234
 
235
          }, function (err) {
236
            done(err, params);
237
          });
238
        }
239
        break;
240
 
241
      case 'list':
242
        // Go trough all params in the list
243
        asyncSerial(params, function (index, subParams, next) {
244
          self.processField(field.field, subParams, function (err, upgradedParams) {
245
            if (upgradedParams) {
246
              params[index] = upgradedParams;
247
            }
248
            next(err);
249
          });
250
        }, function (err) {
251
          done(err, params);
252
        });
253
        break;
254
 
255
      default:
256
        done();
257
    }
258
  };
259
 
260
  /**
261
   * Helps process each property on the given object asynchronously in serial order.
262
   *
263
   * @private
264
   * @param {Object} obj
265
   * @param {Function} process
266
   * @param {Function} finished
267
   */
268
  var asyncSerial = function (obj, process, finished) {
269
    var id, isArray = obj instanceof Array;
270
 
271
    // Keep track of each property that belongs to this object.
272
    if (!isArray) {
273
      var ids = [];
274
      for (id in obj) {
275
        if (obj.hasOwnProperty(id)) {
276
          ids.push(id);
277
        }
278
      }
279
    }
280
 
281
    var i = -1; // Keeps track of the current property
282
 
283
    /**
284
     * Private. Process the next property
285
     */
286
    var next = function () {
287
      id = isArray ? i : ids[i];
288
      process(id, obj[id], check);
289
    };
290
 
291
    /**
292
     * Private. Check if we're done or have an error.
293
     *
294
     * @param {String} err
295
     */
296
    var check = function (err) {
297
      // We need to use a real async function in order for the stack to clear.
298
      setTimeout(function () {
299
        i++;
300
        if (i === (isArray ? obj.length : ids.length) || (err !== undefined && err !== null)) {
301
          finished(err);
302
        }
303
        else {
304
          next();
305
        }
306
      }, 0);
307
    };
308
 
309
    check(); // Start
310
  };
311
 
312
  return ContentUpgradeProcess;
313
})(H5P.Version);