Kyle Ward - Flash, Flex, AIR Developer   
  • Home
  • About
  • Contact
  • Linked In
  •  

    CIC – Communicator Installer Creator

    July 6th, 2010

    air2, flex4, robotlegs

    My employers [www.d6technology.com] provide a Desktop Application [PC/MAC] that allows companies/schools to communicate with their users. The application offers features such as News, Calendar, Gallery, Contacts, Resources etc.

    Although the same code base is used for each clients application, each application differs: Name, ID, Type, Skin etc. We also provide installers [PC/MAC] for our clients which they can either download or are provided by CD.  Like-wise each installer is different.

    Needless to say production is a tiresome process requiring above average IT knowhow, is prone to human error and is done by a non IT person with a turn around time is about 30mins.  I proposed therefore that we automate production by creating an application that would recieve input [details, images, assets], output the installers [exe/dmg] and remove all the work inbetween from our production department.

    The Process and The Solution:

    PC:

    1. Ensure the files the dtool.exe requires are named and path’d correctly.
    2. From the CMD inject dtool.exe with settings,  which will provide a customised exe that runs on the users machine.
    3. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
        private function createEXEFromDTOOL():void
        {
         var process:NativeProcess = new NativeProcess();  
         var info:NativeProcessStartupInfo = new NativeProcessStartupInfo();
         var args:Vector.<String> = new Vector.<String>;

         args.push("inject");
         args.push("-e");
         args.push(_detailsVO.type.pcDTOOLName);
         args.push("-d");
         args.push( new File(PathsConstants.WIN_TMP_PATH).nativePath );
         args.push("-a");
         args.push(StringEdit.removeExtension(_engineVO.customD6WinAppEXE.name));
         args.push("-f");
         args.push(_detailsVO.feedID);    
         args.push("-s");
         args.push(_engineVO.mProjector.name);    
         //args.push(_assetsVO.mProjector.name);  
         info.arguments = args;
         info.workingDirectory = returnDTOOLDirectory();
         info.executable = _dtool;
         
         process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, handleDTOOLProcessOutput,false,0,true);
         process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA,  handleDTOOLProcessError,false,0,true);
         process.addEventListener(NativeProcessExitEvent.EXIT,   handleDTOOLProcessExit,false,0,true);
         
         _message.sendMessage(MessageConstants.DEFAULT_SUCCESS_TITLE, "Starting DTOOL.", this);
         process.start(info);  
        }
    4. Edit the ISS [Inno Setup Script] with settings.
    5. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
        private function populateISSFile():void
        {
         var shortname:String = StringEdit.removeExtension(_engineVO.customD6WinAppEXE.name);// returnShortName(_detailsVO.title);
         var mProjector:String = StringEdit.removeExtension(_engineVO.mProjector.name).substr(0,18);
         var customD6WinAppExe:String = StringEdit.removeExtension(_engineVO.customD6WinAppEXE.name).substr(0,18);
           
         _issCopy = _issCopy.replace(/%SHORTNAME%/g,   shortname);
         _issCopy = _issCopy.replace(/%MPROJECTOR_PATH%/g, _engineVO.mProjector.nativePath);
         _issCopy = _issCopy.replace(/%MPROJECTOR_NAME%/g, _engineVO.mProjector.name);
         _issCopy = _issCopy.replace(/%TITLE%/g,    _detailsVO.title);
         _issCopy = _issCopy.replace(/%COMMON%/g,   _assetsVO.commonFiles.nativePath+File.separator);
         _issCopy = _issCopy.replace(/%SPECIFIC%/g,   _assetsVO.specificFiles.nativePath+File.separator);
         _issCopy = _issCopy.replace(/%ICON%/g,    _imagesVO.icon.nativePath);
         _issCopy = _issCopy.replace(/%INSTALLER_BG%/g,  _imagesVO.installerBG.nativePath);
         _issCopy = _issCopy.replace(/%D6_EXE%/g,   _engineVO.customD6WinAppEXE.nativePath);
         _issCopy = _issCopy.replace(/%D6_EXE_NAME%/g,  _engineVO.customD6WinAppEXE.name);
         _issCopy = _issCopy.replace(/%OUTPUT_DIR%/g,  _engineVO.installer.parent.nativePath);
         _issCopy = _issCopy.replace(/%SHORT_MPROJ_TITLE%/g, mProjector);
         _issCopy = _issCopy.replace(/%SHORT_EXE_TITLE%/g, customD6WinAppExe);
         _issCopy = _issCopy.replace(/%INSTALLER%/g,   StringEdit.removeExtension(_engineVO.installer.name));

         process(++_processSEQ);
        }

        private function openISSForUPDATE():void
        {
         var fs:FileStream = new FileStream();
         
         //fs.addEventListener(Event.COMPLETE,   handleISSForUPDATEComplete,false,0,true);
         //fs.addEventListener(IOErrorEvent.IO_ERROR, handleISSForUPDATEIOError, false, 0, true);
         
         _message.sendMessage(MessageConstants.DEFAULT_SUCCESS_TITLE, "Opening ISS for UPDATE.", this);
         
         fs.open
         (
          returnISSCreateInstallerFile(),
          FileMode.WRITE
         );
         
         fs.writeMultiByte(_issCopy, CharSetConstants.WINDOWS_1252);
         fs.close();  
         process(++_processSEQ);  
        }
    6. Ensure the files the ISS requires are named and path’d correctly.
    7. Run the ISS which creates the installer.
    8. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
        private function processISCC():void
        {
         var process:NativeProcess = new NativeProcess();  
         var info:NativeProcessStartupInfo = new NativeProcessStartupInfo();
         var args:Vector.<String> = new Vector.<String>;

         args.push("/Q");
         args.push(returnISSCreateInstallerFile().nativePath);  
       
         info.arguments = args;
         info.workingDirectory = returnWorkingDirectory();
         info.executable = _settingsVO.iscc;
         
         process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, handleISCCProcessOutput,false,0,true);
         process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA,  handleISCCProcessError,false,0,true);
         process.addEventListener(NativeProcessExitEvent.EXIT,   handleISCCProcessExit,false,0,true);
         
         _message.sendMessage(MessageConstants.DEFAULT_SUCCESS_TITLE, "Starting ISCC.", this);
         process.start(info);  
        }

    MAC:

    1. Ensure the files the dtool.app requires are named and path’d correctly.
    2. From the Terminal run dtool.app with settings, which will package a customised app that runs on the users machine.
    3. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
        public function process():void
        {
         _dtoolDirectory = new File(workingDirectory.nativePath+"/dtool");
         deleteDTOOLAppDirectory();
         
         _dtoolProcess = new NativeProcess();
         
         var info:NativeProcessStartupInfo = new NativeProcessStartupInfo();
         var args:Vector.<String> = new Vector.<String>;  
         
         args.push("package");
         args.push("-e");
         args.push(detailsVO.type.macDTOOLName);
         args.push("-r");
         args.push(assetsVO.specificFiles.nativePath);
         args.push("-s");
         args.push(engineVO.mProjector.nativePath);
         args.push("-f");
         args.push(detailsVO.feedID);
         args.push("-a");
         args.push(detailsVO.title);
         args.push("-t");
         args.push(detailsVO.title);
         args.push("-i");
         args.push(imagesVO.icon.nativePath);
         args.push("-d");
         args.push(_dtoolDirectory.nativePath);
         args.push("-c");
         args.push(_dtoolDirectory.nativePath+"/dtool.xml");
         args.push("-x");
         args.push(_dtoolDirectory.nativePath+"/d6.app/Contents/MacOS/d6");
         args.push("-d");
         args.push(_dtoolDirectory.nativePath+"/build");
         
         info.arguments = args;
         info.workingDirectory = _dtoolDirectory;
         info.executable = new File(_dtoolDirectory.nativePath+"/dtool");
         
         _dtoolProcess.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA,  handleDTOOLProcessOutput,false,0,true);
         _dtoolProcess.addEventListener(ProgressEvent.STANDARD_ERROR_DATA,  handleDTOOLProcessError,false,0,true);
         _dtoolProcess.addEventListener(NativeProcessExitEvent.EXIT,    handleDTOOLProcessExit,false,0,true);
         
         _dtoolProcess.start(info);  
        }
    4. Create a Packager [http://s.sudre.free.fr/Software/Packages.html] project with settings.
    5. Packaged with Installer

    6. Create the installer from the Packager project. [Updating the POST & PRE install scripts]
    7. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
        private function parseInstallScripts():void
        {
         var pretitle:String = detailsVO.title
         
         //pretitle = pretitle.replace(/'/g,"\'")
         
         _preinstallCopy = _preinstallCopy.replace(/%TITLE%/g,pretitle);
         _postinstallCopy = _postinstallCopy.replace(/%TITLE%/g,detailsVO.title);

         processInstallScripts(2);
        }
       
        private function updateInstallScripts():void
        {
         var preinstallFS:FileStream = new FileStream();
         var postinstallFS:FileStream = new FileStream();
         var scripts:File = returnScriptsDirectory();
         var preinstallF:File = new File(scripts.nativePath+File.separator+PathsConstants.PREINSTALL_SH.toLowerCase());
         var postinstallF:File = new File(scripts.nativePath+File.separator+PathsConstants.POSTINSTALL_SH.toLowerCase());
         
         
         preinstallFS.open(preinstallF,FileMode.WRITE)
         preinstallFS.writeMultiByte(_preinstallCopy, CharSetConstants.MACINTOSH);
         
         postinstallFS.open(postinstallF,FileMode.WRITE);
         postinstallFS.writeMultiByte(_postinstallCopy, CharSetConstants.MACINTOSH);
           
         preinstallFS.close();
         postinstallFS.close();
         
         processInstallScripts(3);  
        }

        private function createPackage():void
        {
         var process:NativeProcess = new NativeProcess();
         var packageDirectory:File = returnPackageDirectory();
         var installerPKG:File = returnInstallerPKG();
         var info:NativeProcessStartupInfo = new NativeProcessStartupInfo();
         var args:Vector.<String> = new Vector.<String>;
         
         args.push("--doc");
         args.push(packageDirectory.nativePath+File.separator+"TEMPLATE.pmdoc");
         args.push("--version");
         args.push("1.0");
         args.push("--title");
         args.push(detailsVO.title);
         args.push("--id");
         args.push(returnIDByTitle(detailsVO.title));
         args.push("-w");
         args.push("--verbose");
         args.push("--out");
         args.push(installerPKG.nativePath);
         
         info.arguments = args;
         info.workingDirectory = packageDirectory;
         info.executable = settingsVO.packageMaker;
         
         process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, handlePackageProcessOutput,false,0,true);
         process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA,  handlePackageProcessError,false,0,true);
         process.addEventListener(NativeProcessExitEvent.EXIT,   handlePackageProcessExit,false,0,true);
         
         process.start(info);
        }
    8. Init, Decorate, Compress DMG with background image and icon for Web/CD.[Using YourSway create-dmg]
    9. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
        private function implementYourSway(type:String):void
        {
         var process:NativeProcess = new NativeProcess();  
         var info:NativeProcessStartupInfo = new NativeProcessStartupInfo();
         var args:Vector.<String> = new Vector.<String>();
         
         args.push("--volname");
         args.push(detailsVO.title);
         args.push("--background");
         args.push(_backgroundTMP.nativePath);
         args.push("--icon-size");
         args.push("32");
         args.push("--window-size");
         args.push("550");
         args.push("450");
         args.push("--icon");
         args.push(INSTALLER_PKG);
         args.push("450");
         args.push("215");
           
         if(type == EngineConstants.CD)
         {
          args.push("--icon");
          args.push
          (
           (detailsVO.type.id == DetailsConstants.CLUB_ID) ? "faq.xml" : "faq_mac.xml"
          );
          args.push("450");
          args.push("355");
         
          args.push("--icon");
          args.push("About Communicator");
          args.push("450");
          args.push("285");
         
          args.push(engineVO.cdDMG.nativePath);
          args.push
          (
           (detailsVO.type.id == DetailsConstants.CLUB_ID) ?  returnCLUBTemplateDIR().nativePath : returnSCHOOLTemplateDIR().nativePath
          );
         
         }else{
          /*TODO: Fix HORRIBLE HACK*/
          args.push("--icon");
          args.push("About Communicator");
          args.push("450");
          args.push("285");
          /*TODO: HORRIBLE HACK*/
         
          args.push(engineVO.webDMG.nativePath);
          args.push(returnEMPTYTemplateDIR().nativePath);    
         }
         
         
         info.arguments = args;
         info.workingDirectory = returnYourSwayDIR()
         info.executable = returnYourSwayApp();
         
         process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, handleImplementProcessOutput,false,0,true);
         process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA,  handleImplementProcessError,false,0,true);
         process.addEventListener(NativeProcessExitEvent.EXIT,   handleImplementProcessExit,false,0,true);
         
         process.start(info);
        }

    I mainly used AIR 2′s Native Process for the steps above [a bit of a setup involved as well as having to create platform specific installers].
    This allowed me to interact with applications on the PC/MAC, send them commands and have them quitely [mostly] perform tasks for me:

    D6 Tool [Custom exe/app]: Custom/client specific exe/app.
    YourSway: Custom DMGs.
    Connecting to and instructing these tools was simple and straight forward.

    Inno Setup Compiler / PackageMaker: Create Installers.
    The Inno Setup Compiler just required its settings file edited and then passing as an argument with the instruction.
    PackageMaker was a total nightmare and took days to get it to work via the Terminal, eventually i got it right by a combination of a “TEMPLATE.pmdoc” and arguments which overwrote some of the settings in the doc.


    Freeworld Intranet Admin

    January 27th, 2010

    Intranet
    [Source sample on request]

    Flex
    RobotLegs [LoadUp/StateMachine Utility]

    This application is an interface to an intranet CMS to allow admin to popluate and maintain their intranet.

    Admin including:

    • Inspiration: Media,Links,Copy, Crud
    • Documents: Folders, Media, Permissions, Crud
    • Users: Access, Groups, Crud
    • Page Builder: Permissions, Crud
    • Notifications: Permissions, Crud
    • Careers: Permissions, Crud

    Using Flex for this project dramatically brought down development time and used in conjunction with the RobotLegs framework creates an application that is a pleasure to update and maintain.


    Toolbox

    January 27th, 2010


    http://urbian.co.za/toolbox/
    [Password Protected - Source sample on request]

    Flex
    SWFAddress
    Google Analytics
    RobotLegs [LoadUp/StateMachine Utility]

    The application allows clients for preview technologies/tools available for there campaigns. It is password protected so that selected technolgies are available for certain clients. It is tracked via Google Analytics and deep linked with SWFAddress.

    It is a fully skinned Flex application and is driven by a clearly updateable XML document.


    Midas Specifications Generator

    January 27th, 2010

    Intranet

    AS3
    Pure MVC

    This online, custom CMS driven, application allows its users to generate paint implementation specifications via a step based selection process as well as select copy templates [Forms, Preambles etc]. These specifications [mulitple specs can be added] and templates are editable/deletable and can be rolled back to the orginal version. Once the user has completed their requirements they can download a PDF and/or print it out .


    Big In Digital

    September 28th, 2009

    http://www.thebookmarks.co.za/bigindigital/

    AS3
    Pure MVC

    A fun site for the Bookmarks Awards site. You upload your portrait, select your body, hair style and other accessories. Then adjust your hue, saturation, brightness and color as well as masking out your jaw movement. Once done, you have your own rocker! Assets are “lazy loaded” to stop a huge initial load and are prioitised when required, much respect to the Bulkloader crew for a great loading library.