AppExchange Package Upload to Include Custom Themes

  • 1
  • Question
  • Updated 3 years ago
  • Answered
After Banzai'ing our application and uploading it to AppExchange for Lightning certification we noticed that is does not include our custom theme and Font Awesome icons. Should we adjust our post install script? This is our Post Install Script.

public without sharing class StockCheckInstallScript implements InstallHandler {

       public static final String NAMESPACE_PREFIX = 'gbc';
    public static boolean IsRunning { 
        public get {
            if (IsRunning==null) IsRunning = false;
            return IsRunning;
        }
        public set; 
    }
    
    public void onInstall(InstallContext ctx) {
        IsRunning=true;
        RefreshPagesInModule(NAMESPACE_PREFIX);
        IsRunning=false;
    }
    
    public static List<skuid__Page__c> RefreshPagesInModule(String module) {
        
        // See if a StaticResource containing new pages for this module yet exists      
        StaticResource sr = [
            select Body
            from StaticResource 
            where Name = :(module + 'Pages')
            and ((NamespacePrefix = NULL) OR (NamespacePrefix = :module))
            limit 1
        ];
        
        // The new Pages for our module that we will be inserting       
        List<skuid__Page__c> newPages
             = (List<skuid__Page__c>) JSON.deserialize(sr.Body.toString(),List<skuid__Page__c>.class);
        List<Schema.SObjectField> layoutFields = new List<Schema.SObjectField>{
            skuid__Page__c.skuid__Layout__c,
            skuid__Page__c.skuid__Layout2__c,
            skuid__Page__c.skuid__Layout3__c,
            skuid__Page__c.skuid__Layout4__c,
            skuid__Page__c.skuid__Layout5__c
        };
        
        for (skuid__Page__c p : newPages) {
            // Get rid of the Ids so that upsert will proceed           
            p.Id = null;
            // Ensure that unused Layout fields are set to null         
            for (Schema.Sobjectfield f : layoutFields) {
            if (p.get(f)==null) p.put(f,null);
            }
        }
        
        // If we have successfully compiled new Pages for this module,        
       // delete the old ones and replace them with the new.        
       if (newPages != null && !newPages.isEmpty()) {
        Schema.SObjectField f = skuid__Page__c.skuid__UniqueId__c;
        List<Database.UpsertResult> cr = Database.upsert(newPages,f,false);
        }   
        return newPages;
    }
       
}
Photo of Gregg Baxter

Gregg Baxter, Official Rep

  • 3,080 Points 3k badge 2x thumb
  • electrified

Posted 3 years ago

  • 1
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Hi Greg,

Here is a modified InstallScript that contains methods which should enable you to auto-create skuid__Theme__c records when installing your package into a customer's org. In this example I'm assuming that you're only including one Theme with your package, whose Name is "StockCheck" and which is stored in a Static Resource that is included in your package(very important!) called "StockCheckTheme". This is just an example, you'll need to to adjust to be whatever your Theme's Name and Static Resource Name are.

Also, this post-install script does NOT automatically set the Org-Default Theme preference or change any Profile/User theme settings to be your Theme. If you want to do this this is possible but would take some more code.

 

public without sharing class StockCheckInstallScript implements InstallHandler {
    public static final String NAMESPACE_PREFIX = 'gbc';
    
    public static boolean IsRunning { 
        public get {
            if (IsRunning==null) IsRunning = false;
            return IsRunning;
        }
        public set; 
    }
    
    public void onInstall(InstallContext ctx) {
        IsRunning=true;
        RefreshPagesInModule(NAMESPACE_PREFIX);
        CreateDefaultThemes();
        IsRunning=false;
    }
    
    public static List RefreshPagesInModule(String module) {
        
        // See if a StaticResource containing new pages for this module yet exists      
        StaticResource sr = [
            select Body
            from StaticResource 
            where Name = :(module + 'Pages')
            and ((NamespacePrefix = NULL) OR (NamespacePrefix = :module))
            limit 1
        ];
        
        // The new Pages for our module that we will be inserting       
        List newPages
             = (List) JSON.deserialize(sr.Body.toString(),List.class);
        List layoutFields = new List{
            skuid__Page__c.skuid__Layout__c,
            skuid__Page__c.skuid__Layout2__c,
            skuid__Page__c.skuid__Layout3__c,
            skuid__Page__c.skuid__Layout4__c,
            skuid__Page__c.skuid__Layout5__c
        };
        
        for (skuid__Page__c p : newPages) {
            // Get rid of the Ids so that upsert will proceed           
            p.Id = null;
            // Ensure that unused Layout fields are set to null         
            for (Schema.Sobjectfield f : layoutFields) {
            if (p.get(f)==null) p.put(f,null);
            }
        }
        
        // If we have successfully compiled new Pages for this module,        
       // delete the old ones and replace them with the new.        
       if (newPages != null && !newPages.isEmpty()) {
        Schema.SObjectField f = skuid__Page__c.skuid__UniqueId__c;
        List cr = Database.upsert(newPages,f,false);
        }   
        return newPages;
    }
    
    // Returns a Map containing the definitions for expected default themes
   public static Map> GetDefaultThemes() {
      return new Map>{
         // Desktop Themes
         'StockCheck'    => new Map{ 
            'type' => 'Desktop', 
            'resourceName' => 'StockCheckTheme' 
         }
      };
   }
      
   // Create our default, expected Theme custom setting records if they don't exist 
   public static void CreateDefaultThemes() {
      
      Map existingThemes = skuid__Theme__c.getAll();
      Map> defaultThemes = GetDefaultThemes();
      Set themeNamesToCreate = new Set();
      for (String themeName : defaultThemes.keyset()) {
         if (!existingThemes.containsKey(themeName)) {
            themeNamesToCreate.add(themeName); 
         }  
      }
      
      // If there are some default themes that do not exist in this org,
      // create them!
      if (!themeNamesToCreate.isEmpty()) {
         List themesToCreate = new List();
         for (String themeName : themeNamesToCreate) {
            Map themeDef = defaultThemes.get(themeName);
            themesToCreate.add(new skuid__Theme__c(
               Name                    = themeName,
               skuid__Resource_Namespace__c  = NAMESPACE_PREFIX,
               skuid__Resource_Name__c       = themeDef.get('resourceName'),
               skuid__Active__c           = true,
               skuid__Type__c                = themeDef.get('type')
            ));
         }
         insert themesToCreate;  
      }
   }
       
}
(Edited)
Photo of Gregg Baxter

Gregg Baxter, Official Rep

  • 3,080 Points 3k badge 2x thumb
Hi Zach.  

Thanks for the reply and your time.  
I am getting the following error when saving the script

"Error: Compile Error: Method does not exist or incorrect signature: CreateThemes() at line 15 column 9"

I tried a few fumbling changes but no joy.

Thanks again - G
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
So to confirm your onInstall method looks like this:

public void onInstall(InstallContext ctx) {
        IsRunning=true;
        RefreshPagesInModule(NAMESPACE_PREFIX);
        CreateDefaultThemes();
        IsRunning=false;
    }

But when you run this, your Themes are created but Pages are not updated or modified?

If that's true, which is bizarre, I would try removing the call to CreateDefaultThemes(); from onInstall and then uploading a new version of your package and then trying to install it and see if your pages get updated as you'd expect. Basically go back to making sure that the page modifications are working as expected, then re-introduce the CreateDefaultThemes() part of the equation. I think it will be important in troubleshooting this to first establish that both routines are running as expected independently, then testing them running in conjunction. My guess is that something is preventing the RefreshPagesInModule functionality from working that is unrelated to creating default themes. But it is difficult to say.
Photo of Craig Rosenbaum

Craig Rosenbaum

  • 4,776 Points 4k badge 2x thumb
It seems the template packaging was a red hearing. I stripped out the theme portion and reverted back to just unpacking pages and am having the problem only with child pages.

My master page and any pages that are not child pages unpack and update just fine. The child pages do not update at all via the post install script and are untouched. Manually I can unpack the pages from the static resource and it works, so I know its not a problem with the static resource. 

What I don't get is if these pages are already created in the org I'm trying to update, why would they not be found and updated regardless of if they're child pages?
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Aha! That's the issue. The code above would need to adjusted to handle linking Child Pages with their Master Page in the target org. We will get back to you on a solution for handling this.
Photo of Craig Rosenbaum

Craig Rosenbaum

  • 4,776 Points 4k badge 2x thumb
Awesome thanks! Glad to know I'm not entirely crazy
Photo of Craig Rosenbaum

Craig Rosenbaum

  • 4,776 Points 4k badge 2x thumb
Hey Zach, any update here? Now that all of our package's pages are child pages of our master header, we aren't able to update pages via post install script.