Use apex to package and unpackage pages in a module?

  • 1
  • Question
  • Updated 3 years ago
  • Answered
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?
Photo of Jonathan Gillespie

Jonathan Gillespie

  • 870 Points 500 badge 2x thumb

Posted 4 years ago

  • 1
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,556 Points 20k badge 2x thumb
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<String>{'MyModulePages'}); 

If the StaticResource is in a Namespace, there is another variant where you can pass the method a List<StaticResource> 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. 
Photo of Jonathan Gillespie

Jonathan Gillespie

  • 870 Points 500 badge 2x thumb
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.
Photo of Matt Bathersby

Matt Bathersby

  • 110 Points 100 badge 2x thumb
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&lt;String&gt;)

Any ideas what I'm doing wrong?

Cheers!
Photo of Matt Bathersby

Matt Bathersby

  • 110 Points 100 badge 2x thumb
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.
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,556 Points 20k badge 2x thumb
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<StaticResource> 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.

Photo of Matt Bathersby

Matt Bathersby

  • 110 Points 100 badge 2x thumb
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.

Photo of Matt Bathersby

Matt Bathersby

  • 110 Points 100 badge 2x thumb
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.
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,148 Points 20k badge 2x thumb
that's progress for ya.... (Mutters the crotchety geezer as he totters away )
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,556 Points 20k badge 2x thumb
Matt, I've experienced this lately as well, seems like a bug with the Developer Console. Glad you got it working!
Photo of Craig Rosenbaum

Craig Rosenbaum

  • 4,776 Points 4k badge 2x thumb
Is there any way to have this same script unpack page assignments? 
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,556 Points 20k badge 2x thumb
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.
Photo of Irvin Waldman

Irvin Waldman, Champion

  • 9,006 Points 5k badge 2x thumb
This information should not be buried in a post.  This would be a good topic for 8. Build Appexchange Apps with Skuid.
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,556 Points 20k badge 2x thumb
Irvin, I totally agree. It's on our to-do list.
Photo of Craig Rosenbaum

Craig Rosenbaum

  • 4,776 Points 4k badge 2x thumb
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!
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,556 Points 20k badge 2x thumb
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.
Photo of Craig Rosenbaum

Craig Rosenbaum

  • 4,776 Points 4k badge 2x thumb
Thanks for the info Zach. Any insight on how to make sure customers have permission sets deployed to all users?
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,556 Points 20k badge 2x thumb
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.
Photo of Craig Rosenbaum

Craig Rosenbaum

  • 4,776 Points 4k badge 2x thumb
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?
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,556 Points 20k badge 2x thumb
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. 
Photo of Craig Rosenbaum

Craig Rosenbaum

  • 4,776 Points 4k badge 2x thumb
Hi Zach, wanted to follow up and see if you had information on an auto update of page assignments like you mentioned above. 
Photo of Craig Rosenbaum

Craig Rosenbaum

  • 4,776 Points 4k badge 2x thumb
Hi Zach, wanted to follow up and see if you had information on an auto update of page assignments like you mentioned above. 
Photo of Craig Rosenbaum

Craig Rosenbaum

  • 4,776 Points 4k badge 2x thumb
Zach, just a bump to see if you could send some examples of the auto deployment page assignments like you mentioned above. 
Photo of Craig Rosenbaum

Craig Rosenbaum

  • 4,776 Points 4k badge 2x thumb
Zach, just a bump to see if you could send some examples of the auto deployment page assignments like you mentioned above. 
Photo of Gyan

Gyan

  • 1,420 Points 1k badge 2x thumb
Hi Zach,  to upsert the pages form pagepack (static resource) we were using another following version of skuid Utils method: 

static skuid.Utils.PageRefreshResult RefreshPagesFromStaticResource(List srList)

in our postinstall script . However, its preventing us from package install now ( I got error while installing Beta package in test org). 

Here's how our postinstall class looks like: 


global class PostInstallScript implements InstallHandler {    
    public PostInstallScript() {}
    global void onInstall(InstallContext context) {
        // if this is an upgrade
        if(context.isUpgrade() || context.isPush()) {
            // upsert Skuid pages
List<StaticResource> srList = new List<StaticResource>();
for(StaticResource s : [select Id, body from StaticResource where Name like '%allOther_skuidUpdate']){
srList.add(s);
}            
try {
skuid.Utils.RefreshPagesFromStaticResource(srlist);
}
catch(exception e) {
system.debug(e);
}
                        
        }        
    }
}



Any idea why we are getting package install failure?

Thanks.
Gyan
(Edited)
Photo of J.

J., Official Rep

  • 7,694 Points 5k badge 2x thumb
Jnanendra,

I think we've had issues using the Utils methods in Post Install Scripts because Utils.cls is declared with sharing and the "Ghost User" that is actually running the script doesn't handle it. It could be something else, but that's the first thing that comes to mind. If you try running the query and call to Utils.RefreshPagesFromStaticResource via Anonymous Apex, does it work?
Photo of Gyan

Gyan

  • 1,420 Points 1k badge 2x thumb
Hi J., 

Thank you for your reply. Yes running Utils.RefreshPagesFromStaticResource  from Anonymous apex works. Not only that, it worked from PostInstall script as well previously (in previous version of PractiFI). Then I used another approach that skuid provided in tutorial which worked well so I was happy until I got issue with Master page. Please see my post at: 

Deploying Child Pages with Post Install Apex Script

https://community.skuid.com/skuid/topics/deploying-child-pages-with-post-install-apex-script?topic-r...

And lastly, I am not getting any notification for the community post update (I have opted in for getting notifications). Any Idea?

Kind regards,

Gyan (Jnanendra)