AppExchange Package Upload to Include Custom Themes

Gregg Baxter
edited February 17, 2020 in Questions
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;
    }
       
}

Comments

  • Zach McElrath
    edited March 2, 2017
    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;  
          }
       }
           
    }
  • Gregg Baxter
    edited February 17, 2020
    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

  • Zach McElrath
    edited December 21, 2016
    Oops, copy and paste error. I edited the script above, try copying it again and resaving.
  • Gregg Baxter
    edited September 17, 2016
    Thanks Zach.  

    Singing like Skynnard!
  • Craig Rosenbaum
    Craig Rosenbaum ✭✭✭✭
    edited November 12, 2015
    I'm actually getting the same CreateThemes() error despite using the code above. Am I missing something?

    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);
            CreateThemes();
            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;
        }
        
        // Returns a Map containing the definitions for expected default themes
       public static Map<String,Map<String,String>> GetDefaultThemes() {
          return new Map<String,Map<String,String>>{
             // Desktop Themes
             'StockCheck'    => new Map<String,String>{ 
                'type' => 'Desktop', 
                'resourceName' => 'StockCheckTheme' 
             }
          };
       }
          
       // Create our default, expected Theme custom setting records if they don't exist 
       public static void CreateDefaultThemes() {
          
          Map<String,skuid__Theme__c> existingThemes = skuid__Theme__c.getAll();
          Map<String,Map<String,String>> defaultThemes = GetDefaultThemes();
          Set<String> themeNamesToCreate = new Set<String>();
          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<skuid__Theme__c> themesToCreate = new List<skuid__Theme__c>();
             for (String themeName : themeNamesToCreate) {
                Map<String,String> 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;  
          }
       }
           
    }
  • Zach McElrath
    edited December 21, 2016
    I'm not sure why my change didn't take last time---i changed it again. In the "onInstall" method, the method call should be "CreateDefaultThemes()" not CreateThemes.
  • Craig Rosenbaum
    Craig Rosenbaum ✭✭✭✭
    edited November 12, 2015
    Zach, since updating to this, my pages haven't been auto-deploying. The theme has, which is great. Not sure if you see anything below, I don't think I changed anything that would affect pages not unpacking. I can still manually unpack them, so its not a permissions or static resource issue. Thoughts?

    public without sharing class InstallScript_CheetahBMS implements InstallHandler { 
        public static final String NAMESPACE_PREFIX = 'CheetahBMS';
        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<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;
        }
        
            // Returns a Map containing the definitions for expected default themes
       public static Map<String,Map<String,String>> GetDefaultThemes() {
          return new Map<String,Map<String,String>>{
             // Desktop Themes
             'CheetahOrange'    => new Map<String,String>{ 
                'type' => 'Desktop', 
                'resourceName' => 'CheetahOrangeTheme_2' 
             }
          };
       }
          
     // Create our default, expected Theme custom setting records if they don't exist 
       public static void CreateDefaultThemes() {
          
          Map<String,skuid__Theme__c> existingThemes = skuid__Theme__c.getAll();
          Map<String,Map<String,String>> defaultThemes = GetDefaultThemes();
          Set<String> themeNamesToCreate = new Set<String>();
          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<skuid__Theme__c> themesToCreate = new List<skuid__Theme__c>();
             for (String themeName : themeNamesToCreate) {
                Map<String,String> 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;  
          }
       }
    }
  • Zach McElrath
    edited December 21, 2016
    That's very odd. I would have thought that you'd get no pages and no themes given how your code is ordered. I'm not sure.
  • Craig Rosenbaum
    Craig Rosenbaum ✭✭✭✭
    edited November 12, 2015
    So...what would be the proper order? :)

    I looked at the example and mine and am not seeing the difference
  • Craig Rosenbaum
    Craig Rosenbaum ✭✭✭✭
    edited November 12, 2015
    Can anyone spot my error above? Like Zach mentioned, I'm not getting themes or pages and for the life of me I can't see what I did differently than before when pages worked fine.
  • Zach McElrath
    edited December 21, 2016
    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.
  • Craig Rosenbaum
    Craig Rosenbaum ✭✭✭✭
    edited November 12, 2015
    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?
  • Zach McElrath
    edited December 21, 2016
    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.
  • Craig Rosenbaum
    Craig Rosenbaum ✭✭✭✭
    edited November 12, 2015
    Awesome thanks! Glad to know I'm not entirely crazy
  • Craig Rosenbaum
    Craig Rosenbaum ✭✭✭✭
    edited November 12, 2015
    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.
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!