Use apex to package and unpackage pages in a module?

I’m working on trying to better automated some of the steps in our deployment process, and am wondering if there’s a way to run the package & unpackage pages features (where the pages are stored/pulled from a static resource) from apex. I’m using the metadata API to pull down our changes from our dev sandbox & push them to our QA sandbox, but right now, I have to go into the dev sandbox, package the pages, then remember to unpackage them once the deployment is done in the QA sandbox.

I think I have a way to execute anonymous apex with the metadata API - does Skuid currently give us a way to execute these 2 functions in apex?

Here are some the methods Skuid provides in Apex. The “PackagePages” variations do almost everything that the “Build Page Pack” and “Package Pages in Module” buttons on the Skuid Pages List page do, except for actually creating/updating the StaticResources. In the Pages List page, Skuid uses the Tooling API to create/update the pages. We have so far opted not to do any Metadata creation / updates in Apex land because that would require us to add a Remote Site for every Salesforce Instance (na1, na2, cs30, etc…) to our package. However, in Visualforce land, we don’t need to do this: Visualforce can run the Tooling API without any security settings modification.

With that note of background, here’s some of what you can use from Apex:

** UNPACKING PAGES **

To unpack pages from a StaticResource and do an UPSERT, use the following:

skuid.Utils.RefreshPagesFromStaticResource(staticResourceNames);

for example if you want to unpack a PagePack called “MyModulePages”, do:

skuid.Utils.RefreshPagesFromStaticResource(new List{‘MyModulePages’});

If the StaticResource is in a Namespace, there is another variant where you can pass the method a List records.

To unpack pages from a StaticResource, delete all existing pages in a Module, then an insert on the unpacked pages, use the following:

skuid.Utils.RefreshPagesInModule(moduleName);

** PACKAGING PAGES **

To grab all pages in a Module as a JSON string suitable for inclusion in a StaticResource body:

skuid.Utils.PackagePagesInModule(moduleName,sendEmail)

This method returns all Pages in the given Module as a JSON string. If sendEmail is true, it will also send a .json file to you as an email attachment.

So what I imaging you could do, since you’re doing Metadata API stuff already (are you using the Financial Force MDAPI package?) is to grab this String, convert it to a Blob / BASE64-encode it, and then put it into the Body of a StaticResource and do an Update.

Zach, this is awesome - I’ll spend some time in the next few days trying to incorporate this. Right now, I’m just using the standard Force.com migration tool, but I’m actually looking into using the Financial Force package instead - seems like it’s a great tool that they’ve put a lot of work into.

Hi Zach,

This is extremely useful for us!
Unfortunately though, I am getting the following error:

Package Visibility: Method is not visible: skuid.Utils.RefreshPagesFromStaticResource(List<String>)


Any ideas what I’m doing wrong?

Cheers!

Looking at the exposed methods, this is what we seem to be able to use:

static skuid.Utils.PageRefreshResult RefreshPagesFromStaticResource(List srList)

Don’t seem to be having much luck getting to it though. Any pointers would be great!
Thanks again.

Are you running this from Anonymous Apex, or from another Apex Class? If you are running it from Anonymous Apex, I would not expect any issues running this, just pass in a List to the above method (the result of a SOQL Query on the StaticResource object. If you are running it from another Apex Class, then you will have to make sure that the “Version Settings” for the Skuid Installed Package associated with that Apex Class is the latest version of Skuid.

Hey Zach. Thanks for the feedback. We are indeed able to execute this from an anonymous block. Still having trouble in the class though. Have made sure the Skuid version is 6.8, but still getting ‘Method is not visible’ error.

So it looks like this might be an issue/limitation of the Developer Console. Adding the same line to the class without using the console (“the old fashioned way”) allowed me to save without issue.

that’s progress for ya… (Mutters the crotchety geezer as he totters away )

Matt, I’ve experienced this lately as well, seems like a bug with the Developer Console. Glad you got it working!

Is there any way to have this same script unpack page assignments? 

Craig, if you’re just trying to move Page Assignments between two orgs, e.g. Sandbox and Production, I would use the “Package all Page Assignments” or “Package selected Page Assignments” buttons on the Page Assignments tab to build a “Page Assignments Pack”, and then use the “Unpack Page Assignments from Packs” button in the target org to unpackage these Page Assignments.

However, if you’re building a managed package and want to automatically unpack your package’s needed Page Assignments after install using a Post-Install Script, this is a bit more complicated than just running a one-line “RefreshPageAssignments” script because of the skuid__Page__c lookup field on the Page Assignment object — the linked Page Ids of the Page Assignments you’re unpacking have to be matched up correctly to existing Page records in the org where you’re doing the unpacking, which is tricky because the Page Ids will never be the same from one org to another. So depending on how exactly you’d like this process to work, you need to have your script match up the Page Assignment records to corresponding Pages in the target org. Also, if you’re building a managed package, you don’t want to overwrite a customer’s existing Page Assignment overrides for a given Object / Action / Record Type scenario, you just want to make sure that your managed package’s default Page Assignment is in place, so a custom “upsert” process is recommended.

That being said, we’ve assisted other ISV/OEM partners in doing this many times, and we have examples of this kind of Apex logic that we can share with you. If you are building a managed package and want to do this, let us know and we can assist further.

This information should not be buried in a post.  This would be a good topic for 8. Build Appexchange Apps with Skuid.

Irvin, I totally agree. It’s on our to-do list.

Zach, the managed package post install script is exactly what we’re looking for as we’re OEM/ISV partners. I would definitely appreciate any help on this subject. 

Related to this, if you have any experience granting field level security settings to new custom fields in managed package updates through the same post install script, that would be very helpful. This is our first managed package and we’ve discovered the immense pain of adding new fields, pushing an upgrade, and then having to direct the subscriber to go into profile settings and enable FLS for the new fields. I saw a salesforce partner post describing this issue and vaguely how to resolve it with permission sets, but haven’t been able to get that to work correctly in our package. Any help there would be really helpful as well!

Craig, will follow up soon regarding Page Assignments post install script.

Re: field level security settings, the only manageable way I would recommend is to make use of Permission Sets. Because changes to Permission Sets get pushed automatically with push upgrades, you don’t have to worry about the dynamics of ensure the correct users get your updated FLS settings — if a user has been assigned the correct Permission Set, the upgrade is a cinch! The biggest hurdle to overcome here is to make the switch from reliance on Profiles to complete reliance on Permission Sets. In this model, your Profile is a minimal shell, and during your push upgrade you basically ignore the whole question of propagating packaged Profile Settings to Profiles in a target org — forget Profiles and rely on Permission Sets, this is my recommendation. That being said, each Profile has an associated Permission Set that it “owns”, as indicated by the “IsOwnedByProfile” field on a PermissionSet record. Technically you could make changes to the contents of these “owned by Profile” Permission Sets as part of a post-install script, but I don’t recommend that. Permission settings should be managed from the Setup menu, and permission changes should be propagated to customer orgs through regular package deployment processes.

Thanks for the info Zach. Any insight on how to make sure customers have permission sets deployed to all users?

Absolutely, two things can be helpful here:

1. For ensuring assignment of Permission Sets to users on initial installation of your package, use a post-install Apex script that checks all users to see if they have your Permission Sets assigned, and if not, insert PermissionSetAssignment records for each user for each needed Permission Set.
2. For ongoing assurance that users have Permission Sets assigned, leverage Skuid’s Automatic License and Permission Set assignments tool (which you can find on the Skuid Settings tab under “User Licensing and Permissions” sub-tab). Make sure that your customers’ orgs are configured to automatically assign your Permission Sets when new users are activated, either at the Org-Default or Profile-specific level. If you want to automatically configure this in your Customer’s orgs, you can do an upsert in your Post-Install Apex Script on the skuid__Preferences__c Custom Setting object to configure this automatically.

  1. I spent a little bit of time on this in the past, but will look back and see if I can get it working
    2. I did not realize it was possible to use the Skuid auto permission set assignment tool for my own permission sets. I see the wrench now that allows me to select multiple permission sets. This is incredibly useful and I will definitely be using it. As far as auto-configuring this setting…if I configure this in our trialforce template, I shouldn’t need to add an upsert to the post-install script, right?

Craig, that’s a good question — I believe that if you’re using Trialforce you will not need to add an upsert to the post-install script, Trialforce should take whatever custom settings you have configured and use those.