Вопрос

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!

Это было полезно?

Решение

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 });
    }));
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с sharepoint.stackexchange
scroll top