1

I have a customer who has a fully functioning PWA site at one URL which is already on SharePoint Online, and wants a ‘playground’ for testing.

We are about to begin a full-scale migration of our on-prem SharePoint 2013 environment to Sharepoint Online. I have used a 3rd party tool to migrate all the PWA Project sites, which works just fine for moving the content, but the customer wants a full clone.

  1. Is there a way to copy the PWA settings programmatically (i.e.: CSOM, JSOM, powershell)?
  2. Since I’m a SharePoint guy and not a Project server specialist, I am curious as to where the PWA settings are stored (e.g.: the project sites’ property bags?)?

Thanks in advance!

1

I previously wrote an add-in on the SharePoint store called ConfigTool to do this, however the CSOM/JSOM API was limited in a number of ways that made a full-fidelity configuration copy impossible. Specifically around objects such as custom fields with lookup tables and workflow objects (PDP's, stages / phases, etc). I have since discontinued the add-in and removed it from the app store, for this reason.

So my suggestion if you want a complete copy is to use the program Fluent Pro https://fluentpro.com/fluentbooks-for-project-online/ which will do what you want (AFAIK it uses a combination of CSOM and the old PSI).

If however you have a lot of time on your hands and would like to implement something yourself, here's a quick JSON function I pulled from some old code to get you started (it is incomplete - no guarantees).

This will create a custom field from properties previously loaded from another system via the _api/ProjectServer endpoint.

// Create a CF with the contained properties of the given entity type
function createCF(properties, entityType) {
    var projContext = PS.ProjectContext.get_current();
    var customFields = projContext.get_customFields();

    var cfInfo = new PS.CustomFieldCreationInformation();
    cfInfo.set_name(properties.Name);
    cfInfo.set_description(properties.Description);
    cfInfo.set_fieldType(properties.FieldType);

    cfInfo.set_entityType(entityType);

    cfInfo.set_id(properties.Id);

    cfInfo.set_isEditableInVisibility(properties.IsEditableInVisibility);
    cfInfo.set_isMultilineText(properties.IsMultilineText);
    cfInfo.set_isRequired(properties.IsRequired);
    cfInfo.set_isWorkflowControlled(properties.IsWorkflowControlled);
    cfInfo.set_formula(properties.Formula);

    cfInfo.set_lookupAllowMultiSelect(properties.LookupAllowMultiSelect);
    cfInfo.set_lookupDefaultValue(properties.LookupDefaultValue);

    if (!!properties.LookupTable) {
        // Lookup table field not handled
    }

    var newCF = customFields.add(cfInfo);
    newCF.set_rollsDownToAssignments(properties.RollsDownToAssignments);
    newCF.set_rollupType(properties.RollupType);

    customFields.update();

    projContext.executeQueryAsync(Function.createDelegate(this, function () {
        var row = getRowWithName(properties.Name, entityType);
        //updateRowPublishStatus(row, "Ok", properties.ResultMsg);
        $.event.trigger("customFieldCreated", { Name: properties.Name, Entity: entityType, result: true });
    }), Function.createDelegate(this, function () {
        var row = getRowWithName(properties.Name, entityType);

        // Check if this is a Shadowed UID, if so retry with new UID
        if (arguments[1].get_errorValue() === "CustomFieldInvalidUID") {
            properties.Id = guid();
            properties.ResultMsg = warningCFnewUID;
            dataView.getItem(row).Id = properties.Id;

            dataView.refresh();
            grid.render();

            createCF.call(this, properties, entityType);
            return;
        }

        if (arguments[1].get_errorValue() === "CustomFieldFormulaContainsInvalidFieldReference") {
            //updateRowPublishStatus(row, "Error", errorCFformulaInvField);
            $.event.trigger("customFieldCreated", { Name: properties.Name, Entity: entityType, result: false, retryable: true });
            return;
        }

        //updateRowPublishStatus(row, "Error", arguments[1].get_errorValue());
        $.event.trigger("customFieldCreated", { Name: properties.Name, Entity: entityType, result: false });
    }));
}
  • much thanks for answer. To my surprise PMO took actual ownership over their site collections AND their permissions provisioning AND getting a certified Project Server 3rd party company to revamp and replicate Everything. – A.k.A. Fritz Aug 1 '18 at 4:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.