blob: ba25586de9017477b45f8e94a52ca3e6615e80b1 [file] [log] [blame]
/*
*
* Copyright 2013 Anis Kadri
*
* 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.
*
*/
var fs = require('fs') // use existsSync in 0.6.x
, path = require('path')
, util = require('util')
, shell = require('shelljs')
, et = require('elementtree')
, getConfigChanges = require('../util/config-changes')
, searchAndReplace = require('../util/search-and-replace')
, xml_helpers = require('../util/xml-helpers')
, assetsDir = 'assets/www'
, sourceDir = 'src';
exports.handlePlugin = function (action, project_dir, plugin_dir, plugin_et, variables) {
var plugin_id = plugin_et._root.attrib['id']
, version = plugin_et._root.attrib['version']
, external_hosts = []
, i = 0
// look for assets in the plugin
, assets = plugin_et.findall('./asset')
, platformTag = plugin_et.find('./platform[@name="android"]')
, sourceFiles = platformTag.findall('./source-file')
, libFiles = platformTag.findall('./library-file')
, configChanges = getConfigChanges(platformTag);
variables = variables || {}
// get config.xml filename
var config_xml_filename = 'res/xml/config.xml';
if(fs.existsSync(path.resolve(project_dir, 'res/xml/plugins.xml'))) {
config_xml_filename = 'res/xml/plugins.xml';
}
// collision detection
if(action.match(/force-/) == null) {
if(action == "install" && pluginInstalled(plugin_et, project_dir, config_xml_filename)) {
throw new Error("Plugin "+plugin_id+" already installed");
} else if(action == "uninstall" && !pluginInstalled(plugin_et, project_dir, config_xml_filename)) {
throw new Error("Plugin "+plugin_id+" not installed");
}
} else {
action = action.replace('force-', '');
}
root = et.Element("config-file");
root.attrib['parent'] = '.'
plugin_et.findall('./access').forEach(function (tag) {
root.append(tag);
});
if (root.len()) {
(configChanges[config_xml_filename]) ?
configChanges[config_xml_filename].push(root) :
configChanges[config_xml_filename] = [root];
}
// find which config-files we're interested in
Object.keys(configChanges).forEach(function (configFile) {
if (!fs.existsSync(path.resolve(project_dir, configFile))) {
delete configChanges[configFile];
}
});
// move asset files
assets.forEach(function (asset) {
var srcPath = path.resolve(
plugin_dir,
asset.attrib['src']);
var targetPath = path.resolve(
project_dir,
assetsDir,
asset.attrib['target']);
var stats = fs.statSync(srcPath);
if (action == 'install') {
if(stats.isDirectory()) {
shell.mkdir('-p', targetPath);
shell.cp('-R', srcPath, path.join(project_dir, assetsDir));
} else {
shell.cp(srcPath, targetPath);
}
} else {
shell.rm('-rf', targetPath);
}
});
// move source files
sourceFiles.forEach(function (sourceFile) {
var srcDir = path.resolve(project_dir,
sourceFile.attrib['target-dir'])
, destFile = path.resolve(srcDir,
path.basename(sourceFile.attrib['src']));
if (action == 'install') {
shell.mkdir('-p', srcDir);
var srcFile = srcPath(plugin_dir, sourceFile.attrib['src']);
shell.cp(srcFile, destFile);
} else {
fs.unlinkSync(destFile);
// check if directory is empty
var curDir = srcDir;
while(curDir !== project_dir + '/src') {
if(fs.readdirSync(curDir).length == 0) {
fs.rmdirSync(curDir);
curDir = path.resolve(curDir + '/..');
} else {
// directory not empty...do nothing
break;
}
}
}
})
// move library files
libFiles.forEach(function (libFile) {
var libDir = path.resolve(project_dir,
libFile.attrib['target-dir'])
if (action == 'install') {
shell.mkdir('-p', libDir);
var src = path.resolve(plugin_dir, 'src/android',
libFile.attrib['src']),
dest = path.resolve(libDir,
path.basename(libFile.attrib['src']));
shell.cp(src, dest);
} else {
var destFile = path.resolve(libDir,
path.basename(libFile.attrib['src']));
fs.unlinkSync(destFile);
// check if directory is empty
var files = fs.readdirSync(libDir);
if(files.length == 0) {
shell.rm('-rf', libDir);
}
}
})
// edit configuration files
Object.keys(configChanges).forEach(function (filename) {
var filepath = path.resolve(project_dir, filename),
xmlDoc = xml_helpers.parseElementtreeSync(filepath),
output;
configChanges[filename].forEach(function (configNode) {
var selector = configNode.attrib["parent"],
children = configNode.findall('*');
if( action == 'install') {
if (!xml_helpers.graftXML(xmlDoc, children, selector)) {
throw new Error('failed to add children to ' + filename);
}
} else {
if (!xml_helpers.pruneXML(xmlDoc, children, selector)) {
throw new Error('failed to remove children from' + filename);
}
}
});
output = xmlDoc.write({indent: 4});
fs.writeFileSync(filepath, output);
});
if (action == 'install') {
variables['PACKAGE_NAME'] = androidPackageName(project_dir);
searchAndReplace(path.resolve(project_dir, config_xml_filename), variables);
searchAndReplace(path.resolve(project_dir, 'AndroidManifest.xml'), variables);
}
}
function srcPath(pluginPath, filename) {
var prefix = /^src\/android/;
if (prefix.test(filename)) {
return path.resolve(pluginPath, filename);
} else {
return path.resolve(pluginPath, 'src/android', filename);
}
}
// reads the package name out of the Android Manifest file
// @param string project_dir the absolute path to the directory containing the project
// @return string the name of the package
function androidPackageName(project_dir) {
var mDoc = xml_helpers.parseElementtreeSync(
path.resolve(project_dir, 'AndroidManifest.xml'));
return mDoc._root.attrib['package'];
}
function pluginInstalled(plugin_et, project_dir, config_xml_filename) {
var tag_xpath = util.format('./platform[@name="android"]/config-file[@target="%s"]/plugin', config_xml_filename);
var plugin_tag = plugin_et.find(tag_xpath);
if (!plugin_tag) {
return false;
}
var plugin_name = plugin_tag.attrib.name,
plugin_id = plugin_et._root.attrib['id'],
readfile = fs.readFileSync(path.resolve(project_dir, config_xml_filename), 'utf8');
if ((readfile.match(new RegExp(plugin_name, "g")) != null) || (readfile.match(new RegExp(plugin_id, "g")) != null)){
return true;
}
}