Example #1
0
/**
 * Updates config files in project based on app's config.xml and config munge,
 *   generated by plugins.
 *
 * @param   {ConfigParser}   sourceConfig  A project's configuration that will
 *   be merged into platform's config.xml
 * @param   {ConfigChanges}  configMunger  An initialized ConfigChanges instance
 *   for this platform.
 * @param   {Object}         locations     A map of locations for this platform
 *
 * @return  {ConfigParser}                 An instance of ConfigParser, that
 *   represents current project's configuration. When returned, the
 *   configuration is already dumped to appropriate config.xml file.
 */
function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
    // First cleanup current config and merge project's one into own
    var defaultConfig = locations.defaultConfigXml;
    var ownConfig = locations.configXml;
    var sourceCfg = sourceConfig.path;
    // If defaults.xml is present, overwrite platform config.xml with it.
    // Otherwise save whatever is there as defaults so it can be
    // restored or copy project config into platform if none exists.
    if (fs.existsSync(defaultConfig)) {
        events.emit('verbose', 'Generating platform-specific config.xml from defaults for windows at ' + ownConfig);
        shell.cp('-f', defaultConfig, ownConfig);
    } else if (fs.existsSync(ownConfig)) {
        shell.cp('-f', ownConfig, defaultConfig);
    } else {
        shell.cp('-f', sourceCfg, ownConfig);
    }

    // Then apply config changes from global munge to all config files
    // in project (including project's config)
    configMunger.reapply_global_munge().save_all();

    events.emit('verbose', 'Merging project\'s config.xml into platform-specific windows config.xml');
    // Merge changes from app's config.xml into platform's one
    var config = new ConfigParser(ownConfig);
    xmlHelpers.mergeXml(sourceConfig.doc.getroot(),
        config.doc.getroot(), 'windows', /* clobber= */true);

    config.write();
    return config;
}
AppxManifest.get = function (fileName, ignoreCache) {

    if (!ignoreCache && manifestCache[fileName]) {
        return manifestCache[fileName];
    }

    var root = xml.parseElementtreeSync(fileName).getroot();
    var prefixes = Object.keys(root.attrib)
    .reduce(function (result, attrib) {
        if (attrib.indexOf('xmlns') === 0 && attrib !== 'xmlns:mp') {
            result.push(attrib.replace('xmlns', '').replace(':', ''));
        }

        return result;
    }, []).sort();

    var prefix = prefixes[prefixes.length - 1];
    var Manifest = prefix === 'uap' ? Win10AppxManifest : AppxManifest;
    var result = new Manifest(fileName, prefix);

    if (!ignoreCache) {
        manifestCache[fileName] = result;
    }

    return result;
};
    package_name: function(project_dir, www_dir) {

        var configPaths = [
            // preferred location if cordova >= 3.4
            path.join(project_dir, 'config.xml'),
            // older location
            path.join(www_dir || path.join(project_dir, 'www'), 'config.xml'),
        ];

        var cordovaRoot = cordovaUtil.isCordova();
        if (cordovaRoot) {
            // CB-10662 If we're in cli project then add project's config as a fallback
            configPaths.push(path.join(cordovaRoot, 'config.xml'));
        }

        for (var i = 0; i < configPaths.length; i++) {
            var configPath = configPaths[i];
            // If no config there try next path
            if (!fs.existsSync(configPath)) continue;

            var widget_doc = xmlHelpers.parseElementtreeSync(configPath);
            return widget_doc._root.attrib.id;
        }

        // No configs found - fail with meaningful error message
        throw new CordovaError('Unable to find project\'s config in any of ' +
            'the following directories:\n\t' + configPaths.join('\n\t'));
    },
/** Wraps an AndroidManifest file */
function AndroidManifest(path) {
    this.path = path;
    this.doc = xml.parseElementtreeSync(path);
    if (this.doc.getroot().tag !== 'manifest') {
        throw new Error(path + ' has incorrect root node name (expected "manifest")');
    }
}
Example #5
0
function proj(location) {
    // Class to handle simple project xml operations
    if (!location) {
        throw new Error('Project file location can\'t be null or empty');
    }
    this.location = location;
    this.xml = xml_helpers.parseElementtreeSync(location);
}
Example #6
0
/**
 * Updates project structure and AndroidManifest according to project's configuration.
 *
 * @param   {ConfigParser}  platformConfig  A project's configuration that will
 *   be used to update project
 * @param   {Object}  locations       A map of locations for this platform
 */
function updateProjectAccordingTo(platformConfig, locations) {
    // Update app name by editing res/values/strings.xml
    var name = platformConfig.name();
    var strings = xmlHelpers.parseElementtreeSync(locations.strings);
    strings.find('string[@name="app_name"]').text = name;
    fs.writeFileSync(locations.strings, strings.write({indent: 4}), 'utf-8');
    events.emit('verbose', 'Wrote out android application name "' + name + '" to ' + locations.strings);

    // Java packages cannot support dashes
    var pkg = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');

    var manifest = new AndroidManifest(locations.manifest);
    var orig_pkg = manifest.getPackageId();

    manifest.getActivity()
        .setOrientation(platformConfig.getPreference('orientation'))
        .setLaunchMode(findAndroidLaunchModePreference(platformConfig));

    manifest.setVersionName(platformConfig.version())
        .setVersionCode(platformConfig.android_versionCode() || default_versionCode(platformConfig.version()))
        .setPackageId(pkg)
        .setMinSdkVersion(platformConfig.getPreference('android-minSdkVersion', 'android'))
        .setMaxSdkVersion(platformConfig.getPreference('android-maxSdkVersion', 'android'))
        .setTargetSdkVersion(platformConfig.getPreference('android-targetSdkVersion', 'android'))
        .write();

    var javaPattern = path.join(locations.root, 'src', orig_pkg.replace(/\./g, '/'), '*.java');
    var java_files = shell.ls(javaPattern).filter(function(f) {
        return shell.grep(/extends\s+CordovaActivity/g, f);
    });

    if (java_files.length === 0) {
        throw new CordovaError('No Java files found that extend CordovaActivity.');
    } else if(java_files.length > 1) {
        events.emit('log', 'Multiple candidate Java files that extend CordovaActivity found. Guessing at the first one, ' + java_files[0]);
    }

    var destFile = path.join(locations.root, 'src', pkg.replace(/\./g, '/'), path.basename(java_files[0]));
    shell.mkdir('-p', path.dirname(destFile));
    shell.sed(/package [\w\.]*;/, 'package ' + pkg + ';', java_files[0]).to(destFile);
    events.emit('verbose', 'Wrote out Android package name "' + pkg + '" to ' + destFile);

    if (orig_pkg !== pkg) {
        // If package was name changed we need to remove old java with main activity
        shell.rm('-Rf',java_files[0]);
        // remove any empty directories
        var currentDir = path.dirname(java_files[0]);
        var sourcesRoot = path.resolve(locations.root, 'src');
        while(currentDir !== sourcesRoot) {
            if(fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) {
                fs.rmdirSync(currentDir);
                currentDir = path.resolve(currentDir, '..');
            } else {
                break;
            }
        }
    }
}
Example #7
0
        it('should not write duplicate UAP capability declarations', function () {
            var manifest = AppxManifest.get(WINDOWS_10_MANIFEST);
            var capabilities = manifest.doc.find('.//Capabilities');
            capabilities.append(new et.Element('uap:Capability', { 'Name': 'enterpriseAuthentication' }));
            capabilities.append(new et.Element('uap:Capability', { 'Name': 'enterpriseAuthentication' }));
            
            var xml = manifest.writeToString();

            expect((xml.match(/enterpriseAuthentication/g) || []).length).toBe(1);
        });
/**
 * @constructor
 * @constructs AppxManifest
 *
 * Wraps an AppxManifest file. Shouldn't be instantiated directly.
 *   AppxManifest.get should be used instead to select proper manifest type
 *   (AppxManifest for Win 8/8.1/Phone 8.1, Win10AppxManifest for Win 10)
 *
 * @param  {string}  path    Path to appxmanifest to wrap
 * @param  {string}  prefix  A namespace prefix used to prepend some elements.
 *   Depends on manifest type.
 */
function AppxManifest(path, prefix) {
    this.path = path;
    // Append ':' to prefix if needed
    prefix = prefix || '';
    this.prefix = (prefix.indexOf(':') === prefix.length - 1) ? prefix : prefix + ':';
    this.doc = xml.parseElementtreeSync(path);
    if (this.doc.getroot().tag !== 'Package') {
        // Some basic validation
        throw new Error(path + ' has incorrect root node name (expected "Package")');
    }

    // Indicates that this manifest is for phone application (either WinPhone 8.1 or Universal Windows 10)
    this.hasPhoneIdentity = this.prefix === 'uap:' || this.prefix === 'm3:';
}
Example #9
0
    removeProjectReference: function (relative_path, targetConditions) {
        events.emit('verbose', 'jsprojManager.removeProjectReference(incText: ' + relative_path + ', targetConditions: ' + JSON.stringify(targetConditions) + ')');

        // relative_path is the actual path to the file in the current OS, where-as inserted_path is what we write in
        // the project file, and is always in Windows format.
        relative_path = path.normalize(relative_path);
        var inserted_path = relative_path.split('/').join('\\');

        // find the guid + name of the referenced project
        var pluginProjectXML = xml_helpers.parseElementtreeSync(relative_path);
        var projectGuid = pluginProjectXML.find("PropertyGroup/ProjectGuid").text;
        var projName = getProjectName(pluginProjectXML, relative_path);

        // get the project type
        var projectTypeGuid = getProjectTypeGuid(relative_path);
        if (!projectTypeGuid) {
            throw new Error("unrecognized project type");
        }

        var preInsertTextRegExp = getProjectReferencePreInsertRegExp(projectGuid);
        var postInsertTextRegExp = getProjectReferencePostInsertRegExp(projName, projectGuid, inserted_path, projectTypeGuid);

        // There may be multiple solutions (for different VS versions) - process them all
        getSolutionPaths(this.projectFolder).forEach(function (solutionPath) {
            var solText = fs.readFileSync(solutionPath, {encoding: "utf8"});

            // To be safe (to handle subtle changes in formatting, for example), use a RegExp to find and remove
            // preInsertText and postInsertText

            solText = solText.replace(preInsertTextRegExp, function () {
                return "";
            });

            solText = solText.replace(postInsertTextRegExp, function () {
                return "";
            });

            fs.writeFileSync(solutionPath, solText, {encoding: "utf8"});
        });

        this._getMatchingProjects(targetConditions).forEach(function (project) {
            project.removeItemGroupElement('ItemGroup/ProjectReference', inserted_path, targetConditions);
        });
    },
/**
 * Updates config files in project based on app's config.xml and config munge,
 *   generated by plugins.
 *
 * @param   {ConfigParser}   sourceConfig  A project's configuration that will
 *   be merged into platform's config.xml
 * @param   {ConfigChanges}  configMunger  An initialized ConfigChanges instance
 *   for this platform.
 * @param   {Object}         locations     A map of locations for this platform
 *
 * @return  {ConfigParser}                 An instance of ConfigParser, that
 *   represents current project's configuration. When returned, the
 *   configuration is already dumped to appropriate config.xml file.
 */
function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
    events.emit('verbose', 'Generating config.xml from defaults for platform "android"');

    // First cleanup current config and merge project's one into own
    // Overwrite platform config.xml with defaults.xml.
    shell.cp('-f', locations.defaultConfigXml, locations.configXml);

    // Then apply config changes from global munge to all config files
    // in project (including project's config)
    configMunger.reapply_global_munge().save_all();

    // Merge changes from app's config.xml into platform's one
    var config = new ConfigParser(locations.configXml);
    xmlHelpers.mergeXml(sourceConfig.doc.getroot(),
        config.doc.getroot(), 'android', /*clobber=*/true);

    config.write();
    return config;
}
Example #11
0
 package_name:function(project_dir) {
     // preferred location if cordova >= 3.4
     var preferred_path = path.join(project_dir, 'config.xml');
     var config_path;
     if (!fs.existsSync(preferred_path)) {
         // older location
         var old_config_path = path.join(module.exports.www_dir(project_dir), 'config.xml');
         if (!fs.existsSync(old_config_path)) {
             // output newer location and fail reading
             config_path = preferred_path;
             events.emit('verbose', 'unable to find '+config_path);
         } else {
             config_path = old_config_path;
         }
     } else {
         config_path = preferred_path;
     }
     var widget_doc = xml_helpers.parseElementtreeSync(config_path);
     return widget_doc._root.attrib['id'];
 },
Example #12
0
    package_name:function (project_dir) {
        var mDoc = xml_helpers.parseElementtreeSync(path.join(project_dir, 'AndroidManifest.xml'));

        return mDoc._root.attrib['package'];
    },
Example #13
0
windows_parser.prototype.update_from_config = function(config) {

    //check config parser
    if (config instanceof ConfigParser) {
    } else throw new Error('update_from_config requires a ConfigParser object');

    if (!this.isOldProjectTemplate) {
        // If there is platform-defined prepare script, require and exec it
        var platformPrepare = require(path.join(this.projDir, 'cordova', 'lib', 'prepare'));
        platformPrepare.applyPlatformConfig();
        return;
    }

    // code below is required for compatibility reason. New template version is not required this anymore.

    //Get manifest file
    var manifest = xml.parseElementtreeSync(this.manifestPath);

    var version = this.fixConfigVersion(config.version());
    var name = config.name();
    var pkgName = config.packageName();
    var author = config.author();

    var identityNode = manifest.find('.//Identity');
    if(identityNode) {
        // Update app name in identity
        var appIdName = identityNode['attrib']['Name'];
        if (appIdName != pkgName) {
            identityNode['attrib']['Name'] = pkgName;
        }

        // Update app version
        var appVersion = identityNode['attrib']['Version'];
        if(appVersion != version) {
            identityNode['attrib']['Version'] = version;
        }
    }

    // Update name (windows8 has it in the Application[@Id] and Application.VisualElements[@DisplayName])
    var app = manifest.find('.//Application');
    if(app) {

        var appId = app['attrib']['Id'];

        if (appId != pkgName) {
            app['attrib']['Id'] = pkgName;
        }

        var visualElems = manifest.find('.//VisualElements') || manifest.find('.//m2:VisualElements');

        if(visualElems) {
            var displayName = visualElems['attrib']['DisplayName'];
            if(displayName != name) {
                visualElems['attrib']['DisplayName'] = name;
            }
        }
        else {
            throw new Error('update_from_config expected a valid package.appxmanifest' +
                            ' with a <VisualElements> node');
        }
    }
    else {
        throw new Error('update_from_config expected a valid package.appxmanifest' +
                        ' with a <Application> node');
    }

    // Update properties
    var properties = manifest.find('.//Properties');
    if (properties && properties.find) {
        var displayNameElement = properties.find('.//DisplayName');
        if (displayNameElement && displayNameElement.text != name) {
            displayNameElement.text = name;
        }

        var publisherNameElement = properties.find('.//PublisherDisplayName');
        if (publisherNameElement && publisherNameElement.text != author) {
            publisherNameElement.text = author;
        }
    }

    // sort Capability elements as per CB-5350 Windows8 build fails due to invalid 'Capabilities' definition
    // to sort elements we remove them and then add again in the appropriate order
    var capabilitiesRoot = manifest.find('.//Capabilities'),
        capabilities = capabilitiesRoot._children || [];

    capabilities.forEach(function(elem){
        capabilitiesRoot.remove(elem);
    });
    capabilities.sort(function(a, b) {
        return (a.tag > b.tag)? 1: -1;
    });
    capabilities.forEach(function(elem){
        capabilitiesRoot.append(elem);
    });

    //Write out manifest
    fs.writeFileSync(this.manifestPath, manifest.write({indent: 4}), 'utf-8');

    // Update icons
    var icons = config.getIcons('windows8');
    var platformRoot = this.projDir;
    var appRoot = util.isCordova(platformRoot);

    // Icons, that should be added to platform
    var platformIcons = [
        {dest: 'images/logo.png', width: 150, height: 150},
        {dest: 'images/smalllogo.png', width: 30, height: 30},
        {dest: 'images/storelogo.png', width: 50, height: 50},
    ];

    platformIcons.forEach(function (item) {
        var icon = icons.getBySize(item.width, item.height) || icons.getDefault();
        if (icon){
            var src = path.join(appRoot, icon.src),
                dest = path.join(platformRoot, item.dest);
            events.emit('verbose', 'Copying icon from ' + src + ' to ' + dest);
            shell.cp('-f', src, dest);
        }
    });

    // Update splashscreen
    // Image size for Windows 8 should be 620 x 300 px
    // See http://msdn.microsoft.com/en-us/library/windows/apps/hh465338.aspx for reference
    var splash = config.getSplashScreens('windows8').getBySize(620, 300);
    if (splash){
        var src = path.join(appRoot, splash.src),
            dest = path.join(platformRoot, 'images/splashscreen.png');
        events.emit('verbose', 'Copying icon from ' + src + ' to ' + dest);
        shell.cp('-f', src, dest);
    }
};
Example #14
0
    addProjectReference: function (relative_path, targetConditions) {
        events.emit('verbose', 'jsprojManager.addProjectReference(incText: ' + relative_path + ', targetConditions: ' + JSON.stringify(targetConditions) + ')');

        // relative_path is the actual path to the file in the current OS, where-as inserted_path is what we write in
        // the project file, and is always in Windows format.
        relative_path = path.normalize(relative_path);
        var inserted_path = relative_path.split('/').join('\\');

        var pluginProjectXML = xml_helpers.parseElementtreeSync(relative_path);

        // find the guid + name of the referenced project
        var projectGuid = pluginProjectXML.find("PropertyGroup/ProjectGuid").text;
        var projName = getProjectName(pluginProjectXML, relative_path);

        // get the project type
        var projectTypeGuid = getProjectTypeGuid(relative_path);
        if (!projectTypeGuid) {
            throw new CordovaError("unrecognized project type");
        }

        var preInsertText = "\tProjectSection(ProjectDependencies) = postProject\r\n" +
            "\t\t" + projectGuid + "=" + projectGuid + "\r\n" +
            "\tEndProjectSection\r\n";
        var postInsertText = '\r\nProject("' + projectTypeGuid + '") = "' +
            projName + '", "' + inserted_path + '", ' +
            '"' + projectGuid + '"\r\nEndProject';

        var matchingProjects = this._getMatchingProjects(targetConditions);
        if (matchingProjects.length === 0) {
            // No projects meet the specified target and version criteria, so nothing to do.
            return;
        }

        // Will we be writing into the .projitems file rather than individual .jsproj files?
        var useProjItems = this.isUniversalWindowsApp && matchingProjects.length === 1 && matchingProjects[0] === this.master;

        // There may be multiple solution files (for different VS versions) - process them all
        getSolutionPaths(this.projectFolder).forEach(function (solutionPath) {
            var solText = fs.readFileSync(solutionPath, {encoding: "utf8"});

            if (useProjItems) {
                // Insert a project dependency into every jsproj in the solution.
                var jsProjectFound = false;
                solText = solText.replace(JSPROJ_REGEXP, function (match) {
                    jsProjectFound = true;
                    return match + preInsertText;
                });

                if (!jsProjectFound) {
                    throw new CordovaError("no jsproj found in solution");
                }
            } else {
                // Insert a project dependency only for projects that match specified target and version
                matchingProjects.forEach(function (project) {
                    solText = solText.replace(getJsProjRegExForProject(path.basename(project.location)), function (match) {
                        return match + preInsertText;
                    });
                });
            }

            // Add the project after existing projects. Note that this fairly simplistic check should be fine, since the last
            // EndProject in the file should actually be an EndProject (and not an EndProjectSection, for example).
            var pos = solText.lastIndexOf("EndProject");
            if (pos === -1) {
                throw new Error("no EndProject found in solution");
            }
            pos += 10; // Move pos to the end of EndProject text
            solText = solText.slice(0, pos) + postInsertText + solText.slice(pos);

            fs.writeFileSync(solutionPath, solText, {encoding: "utf8"});
        });

        // Add the ItemGroup/ProjectReference to each matching cordova project :
        // <ItemGroup><ProjectReference Include="blahblah.csproj"/></ItemGroup>
        var item = createItemGroupElement('ItemGroup/ProjectReference', inserted_path, targetConditions);
        matchingProjects.forEach(function (project) {
            project.appendToRoot(item);
        });
    },
 package_name:function(project_dir) {
     var config_path = path.join(module.exports.www_dir(project_dir), 'config.xml');
     var widget_doc = xml_helpers.parseElementtreeSync(config_path);
     return widget_doc._root.attrib['id'];
 },
Example #16
0
function csproj(location) {
    this.location = location;
    this.xml = xml_helpers.parseElementtreeSync(location);
    return this;
}