/*
* Copyright 2013 Jive Software
*
* 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.
*/
/**
* @module extensions
*/
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var fs = require('fs'),
q = require('q'),
jive = require('../../api'),
_ = require("underscore");
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// public
/**
* @returns {Promise} Promise
*/
exports.find = function() {
return jive.service.persistence().find( 'jiveExtension', { 'id' : 'jiveExtension' } ).then( function( records ) {
if ( records && records.length > 0 ) {
return records[0];
}
return null;
});
};
/**
* @param record
* @returns {Promise} Promise
*/
exports.save = function(record) {
return jive.service.persistence().save('jiveExtension', 'jiveExtension', record);
};
/**
* @param rootDir
* @param tilesDir
* @param appsDir
* @param cartridgesDir
* @param storagesDir
* @returns {Promise} Promise
*/
exports.prepare = function (rootDir, tilesDir, appsDir, cartridgesDir, storagesDir) {
var extensionSrcDir = ( rootDir ? rootDir + '/' : '' ) + 'extension_src';
var extensionZipDir = ( rootDir ? rootDir + '/' : '' ) + 'extension.zip';
return jive.util.fsexists(extensionSrcDir).then(function( exists ) {
if ( !exists ) jive.util.fsmkdir( extensionSrcDir)
}).then( function() {
return getPersistedExtensionInfo(jive.service.options['extensionInfo'] || {});
}).then( function(extensionInfo) {
return jive.util.recursiveCopy( __dirname + "/template", extensionSrcDir)
.then( function() {
return getTileDefinitions();
}).then( function(definitions) {
return setupExtensionDefinitionJson(
tilesDir,
appsDir,
cartridgesDir,
storagesDir,
extensionSrcDir,
extensionInfo,
definitions
).then( function(definitionsJson) {
// persist the extension metadata
var meta = fillExtensionMetadata(extensionInfo, definitionsJson);
var stringifiedMeta = JSON.stringify(meta, null, 4);
jive.logger.debug("Extension meta: \n" + stringifiedMeta);
return jive.util.fswrite( stringifiedMeta, extensionSrcDir + '/meta.json' );
});
});
}).then( function() {
// zip it all
return jive.util.zipFolder( extensionSrcDir, extensionZipDir );
}).catch( function(e) {
var error = new Error(e);
var stack = error.stack;
jive.logger.error( stack );
throw error;
});
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// private
function isValid(file ) {
return !(file.indexOf('.') == 0)
}
function limit(str, chars) {
return str.substring(0, str.length > chars ? chars : str.length );
}
function fillExtensionMetadata(extensionInfo, definitions) {
var description = extensionInfo['description'];
var name = extensionInfo['name'];
var type = 'client-app'; // by default
var id = extensionInfo['uuid'];
var hasCartridges = definitions['jabCartridges'] && definitions['jabCartridges'].length > 0;
var hasOsapps = definitions['osapps'] && definitions['osapps'].length > 0;
var hasTiles = definitions['tiles'] && definitions['tiles'].length > 0;
var hasTemplates = definitions['templates'] && definitions['templates'].length > 0;
if ( hasCartridges && (hasOsapps || hasTiles || hasTemplates ) ) {
throw Error("Extension cannot contain Jive Anywhere cartridges and other types.");
}
if ( hasCartridges ) {
type = 'jab-cartridges-app';
}
if (!name) {
// synthesize a reasonable name
name = "Add-on containing ";
var c = [];
if ( hasCartridges ) {
c.push('cartridge(s)');
}
if ( hasOsapps ) {
c.push('apps(s)');
}
if ( hasTiles ) {
c.push('tiles(s)');
}
name += c.join(', ').trim();
}
if (!description) {
description = '';
// synthesize a reasonable description
if ( hasCartridges ) {
var c = [];
definitions['jabCartridges'].forEach( function(cartridge) {
c.push(cartridge['name']);
});
description += 'Cartridges: [';
description += c.join(', ').trim();
description += '] ';
}
if ( hasTiles ) {
var c = [];
definitions['tiles'].forEach( function(tile) {
c.push(tile['name']);
});
description += 'Tiles: [';
description += c.join(', ').trim();
description += '] ';
}
if ( hasOsapps ) {
var c = [];
definitions['osapps'].forEach( function(osapp) {
c.push(osapp['name']);
});
description += 'Apps: [';
description += c.join(', ').trim();
description += '] ';
}
description = description.trim();
description = limit(description, 255);
}
return _.defaults({
"package_version": extensionInfo['packageVersion'] || '1.0',
"id": id,
"type": type,
"name": name,
"description": description,
"minimum_version": extensionInfo['minJiveVersion'] || '0000',
"icon_16": "extension-16.png",
"icon_48": "extension-48.png",
"icon_128": "extension-128.png",
"status": "available",
"released_on": "2013-03-08T19:11:11.234Z", // xxx todo
"register_url": jive.service.serviceURL() + "/jive/oauth/register",
"service_url": jive.service.serviceURL(),
"redirect_url": extensionInfo['redirectURL'] || jive.service.serviceURL()
}, jive.service.options['extensionInfo']);
}
function getTileDefinitions() {
var finalizeRequest = function (allDefinitions) {
var toReturn = [];
allDefinitions.forEach(function (batch) {
toReturn = toReturn.concat(batch);
});
return q.resolve(jive.service.getExpandedTileDefinitions(toReturn));
};
return q.all([ jive.tiles.definitions.findAll(), jive.extstreams.definitions.findAll() ]).then(finalizeRequest);
}
function setupExtensionDefinitionJson(tilesDir, appsDir, cartridgesDir, storagesDir, extensionSrcDir, extensionInfo, definitions) {
return getTemplates(tilesDir).then(function (templates) {
return getApps(appsDir, extensionInfo).then(function (apps) {
return getStorages(storagesDir, extensionInfo).then(function (storages) {
return getCartridges(cartridgesDir, extensionSrcDir).then(function (cartridges) {
jive.logger.debug("apps:\n" + JSON.stringify(apps, null, 4));
jive.logger.debug("cartridges:\n" + JSON.stringify(cartridges, null, 4));
var definitionsJson = {
'integrationUser': {
'systemAdmin': extensionInfo['jiveServiceSignature'] ? true : false,
'jiveServiceSignature': extensionInfo['jiveServiceSignature']
},
'tiles': (definitions && definitions.length > 0) ? definitions : undefined,
'templates': templates,
'osapps': apps,
'storageDefinitions': storages,
'jabCartridges': cartridges
};
var definitionJsonPath = extensionSrcDir + '/definition.json';
var stringifiedDefinitionJson = JSON.stringify(definitionsJson, null, 4);
return jive.util.fswrite(stringifiedDefinitionJson, definitionJsonPath).then( function() {
return definitionsJson;
})
});
});
});
});
}
function getStorages(storagesDir, extensionInfo) {
var storages = [];
return jive.util.fsexists( storagesDir).then( function(exists) {
if ( exists ) {
return q.nfcall(fs.readdir, storagesDir).then(function(dirContents){
var proms = [];
dirContents.forEach(function(item) {
if ( isValid(item) ) {
var definitionDir = storagesDir + '/' + item + '/definition.json';
proms.push( jive.util.fsreadJson(definitionDir).then(function(storage){
if ( !storage['name'] ) {
// generate a storage name if one is not provided
storage['name'] = jive.util.guid(extensionInfo['uuid']);
}
return storage;
}) );
}
});
return q.all(proms);
});
} else {
return storages;
}
});
}
function getApps(appsRootDir, extensionInfo) {
var apps = [];
return jive.util.fsexists( appsRootDir).then( function(exists) {
if ( exists ) {
return q.nfcall(fs.readdir, appsRootDir).then(function(dirContents){
var proms = [];
dirContents.forEach(function(item) {
if ( isValid(item) ) {
var definitionDir = appsRootDir + '/' + item + '/definition.json';
proms.push( jive.util.fsreadJson(definitionDir).then(function(app) {
if ( !app['name'] ) {
app['name'] = item;
}
if ( !app['id'] ) {
app['id'] = jive.util.guid(app['name'] + extensionInfo['uuid']);
}
if ( !app['appPath'] ) {
// generate an app path if one is not provided
var temp = app['id'];
temp = temp.replace(/[^a-zA-Z 0-9]+/g,'');
app['appPath'] = temp;
}
app['url'] = jive.service.serviceURL() + '/osapp/' + item + '/app.xml';
return app;
}) );
}
});
return q.all(proms);
});
} else {
return apps;
}
});
}
function getCartridges(cartridgesRootDir, extensionSrcDir) {
var cartridges = [];
return jive.util.fsexists( cartridgesRootDir).then( function(exists) {
if ( exists ) {
return q.nfcall(fs.readdir, cartridgesRootDir).then(function(dirContents){
var proms = [];
dirContents.forEach(function(item) {
if ( isValid(item) ) {
var definitionDir = cartridgesRootDir + '/' + item + '/definition.json';
var contentDir = cartridgesRootDir + '/' + item + '/content';
proms.push( jive.util.fsreadJson(definitionDir).then(function(cartridge) {
if ( !cartridge['name'] ) {
// generate an cartidge name if one is not provided
cartridge['name'] = jive.util.guid(item);
}
var zipFileName = cartridge['zipFileName'];
if ( !zipFileName ) {
zipFileName = cartridge['name'];
zipFileName = zipFileName.replace(/[^\S]+/, '');
zipFileName = zipFileName.replace(/[^a-zA-Z0-9]/, '-');
zipFileName += '.zip';
}
var zipFile = extensionSrcDir + '/data/' + zipFileName;
return jive.util.zipFolder( contentDir, zipFile, true).then( function() {
return q.resolve(cartridge);
})
}) );
}
});
return q.all(proms);
});
} else {
return cartridges;
}
});
}
function getTemplates(tilesDir) {
var templatesPath = tilesDir + '/templates.json';
var toTemplateArray = function(allDefinitions, templates) {
if ( !allDefinitions || allDefinitions.length < 1) {
return null;
}
if ( !templates ) {
return null;
}
var templateArray = [];
for (var key in templates) {
if (templates.hasOwnProperty(key)) {
var template = templates[key];
var templateTiles = template['tiles'];
var filteredTiles = [];
if ( templateTiles ) {
templateTiles.forEach( function(tileName) {
if ( allDefinitions.indexOf(tileName) > -1 ) {
filteredTiles.push(tileName);
}
});
}
template['tiles'] = filteredTiles;
templateArray.push(template);
}
}
return templateArray;
};
return getAllDefinitions().then( function(allDefinitions) {
return jive.util.fsexists(tilesDir).then(function(exists) {
if ( !exists ) {
return q.resolve([]);
} else {
return jive.service.extensions().find().then(function (extension) {
return jive.util.fsexists(templatesPath.trim()).then(function (exists) {
if (!exists) {
// create it
return buildDefaultTemplate(allDefinitions, extension).then( function(defaultTemplate) {
var templates = {};
templates['default'] = defaultTemplate;
return jive.util.fswrite(JSON.stringify(templates, null, 4), tilesDir + '/templates.json').then( function () {
return toTemplateArray(allDefinitions, templates);
})
});
} else {
// make sure there is a default template
return jive.util.fsread(templatesPath).then( function(data) {
var templates = JSON.parse( new Buffer(data).toString() );
jive.logger.debug(templates);
if ( !templates['default'] ) {
return buildDefaultTemplate(allDefinitions, extension).then( function(defaultTemplate) {
templates['default'] = defaultTemplate;
return jive.util.fswrite(JSON.stringify(templates, null, 4), tilesDir + '/templates.json').then( function() {
return toTemplateArray(allDefinitions, templates);
})
});
} else {
return toTemplateArray(allDefinitions, templates);
}
});
}
});
});
}
})
});
}
function getAllDefinitions() {
return q.all([ jive.tiles.definitions.findAll(), jive.extstreams.definitions.findAll() ]).then(
function (allDefinitions) {
var tiles = [];
allDefinitions.forEach(function (batch) {
batch.forEach(function (b) {
tiles.push(b['name']);
});
});
return tiles;
});
}
function buildDefaultTemplate(allDefinitions, extension) {
var extensionName = extension['name'];
var extensionDescription = extension['description'];
if ( !extensionName ) {
if ( allDefinitions ) {
var c = [];
extensionName = '';
extensionName += 'Contains: [';
extensionName += allDefinitions.join(', ').trim();
extensionName += '] ';
extensionName = limit(extensionName, 127);
} else {
extensionName = extension['uuid'];
}
}
if ( !extensionDescription ) {
extensionDescription = 'Extension UUID ' + extension['uuid'] + ' ' + extensionName;
}
var defaultTemplate = {
"name": "defaultTemplate",
"displayName": extensionName,
"description": extensionDescription,
"tiles": allDefinitions
};
return q.resolve( defaultTemplate );
}
function getPersistedExtensionInfo(config) {
return exports.find().then( function( record ) {
if ( record ) {
for (var key in config) {
if (config.hasOwnProperty(key)) {
// update
if ( !record[key] ) {
record[key] = config[key];
if ( key == 'uuid') {
record[key] = jive.util.guid();
}
} else if ( config[key] && config[key] !== record[key] ) {
record[key] = config[key];
}
}
}
return exports.save(record);
} else {
// create it
record = {
'id' : 'jiveExtension',
'uuid' : jive.util.guid()
};
for (var key in config) {
if (config.hasOwnProperty(key)) {
// update
if ( !record[key] ) {
record[key] = config[key];
if ( key == 'uuid') {
record[key] = jive.util.guid();
}
} else if ( config[key] && config[key] !== record[key] ) {
record[key] = config[key];
}
}
}
return exports.save(record);
}
});
}