Creating an Extension Installer (4/5)
Writing an extension - Part 4 - The installer
After we have created and validated the configuration data, we should now finally install the software. The install method gives you the possibility to do nearly whatever you like within the Lucee boundaries. In order to have some support methods available you can extend some existing helper CFC's. These helper CFC's provide some methods to help you create Mappings, Datasources etc. In my particular case I am using the installFolderMapping.cfc helper class. It has the following super methods:
- install
- update
installFolderMapping.cfc extends the CFC InstallSupport.cfc which comes with the following methods:
- updateMapping
- unzip
- removeMapping
- throwErrors
We will publish all helper components with all kinds of support methods so that you only have to extend the corresponding ones in order to have the necessary tools at hand. The install method of the CFC install.cfc gets invoked as soon as the last page of the config.xml form is validated properly. It takes three arguments:
- error
- path
- config
The important ones are path and config. The struct error can be filled in order to throw errors upon install. When you receive the config struct please note that it contains an array with all elements of the corresponding steps and a common struct called mixed that merges all the variables into one struct. If you have ambiguous variables (which you anyway should prevent) you can address them over the step array. Now let's look at the install method:
<cffunction name="install" returntype="string" output="yes" hint="called from Lucee to install application">
<cfargument name="error" type="struct">
<cfargument name="path" type="string">
<cfargument name="config" type="struct">
<cfset var sReturn = "">
<span class="nb"><cfset</span> <span class="k">var</span> <span class="nv">message</span><span class="o">=</span><span class="nf">super.install</span><span class="p">(</span><span class="nv">argumentCollection</span><span class="p">:</span><span class="nv">arguments</span><span class="p">)</span><span class="nb">></span>
<span class="nb"><cfset</span> <span class="nv">sReturn</span> <span class="o">=</span> <span class="nf">createDatabaseTables</span><span class="p">(</span><span class="nv">argumentCollection</span><span class="o">=</span><span class="nv">arguments</span><span class="p">)</span><span class="nb">></span>
<span class="nb"><cfif</span> <span class="k">len</span><span class="p">(</span><span class="nf">trim</span><span class="p">(</span><span class="nv">sReturn</span><span class="p">))</span> <span class="o">eq</span> <span class="m">0</span><span class="nb">></span>
<span class="nb"><cfsavecontent</span> <span class="nv">variable</span><span class="o">=</span><span class="s2">"sReturn"</span><span class="nb">></span>
<span class="nt"><p></span>Done!<span class="nt"></p></span><span class="nb"><cfoutput></span><span class="nt"><p></span>You can now start posting from your administration at:
<span class="nt"><a</span> <span class="na">href=</span><span class="s">"#config.mixed.blog_address#/admin/index.cfm?first=1"</span><span class="err">]#</span><span class="na">contractPath</span><span class="err">(</span><span class="na">config</span><span class="err">.</span><span class="na">mixed</span><span class="err">.</span><span class="na">destination_path</span><span class="err">)#/</span><span class="na">admin</span><span class="err">/</</span><span class="na">a</span><span class="nt">></p></span>
<span class="nt"><p></span>Then you can view your blog at: <span class="nt"><a</span> <span class="na">href=</span><span class="s">"#config.mixed.blog_address#"</span> <span class="na">target=</span><span class="s">"_blank"</span><span class="nt">></span><span class="p">#</span><span class="nf">contractPath</span><span class="p">(</span><span class="nv">config.mixed.destination_path</span><span class="p">)#</span><span class="nt"></a></p></span>
<span class="nb"></cfoutput></cfsavecontent></span>
<span class="nb"></cfif></span>
<span class="nb"><cfreturn</span> <span class="nv">sReturn</span><span class="nb">></span>
</cffunction>
The installer first calls the super.install method that will unzip the files into the destination path. Here's the super.install method:
<cffunction name="install" returntype="string" output="no" hint="called from Lucee to install application">
<cfargument name="error" type="struct">
<cfargument name="path" type="string">
<cfargument name="config" type="struct">
<span class="nb"><cfzip</span> <span class="nv">action</span> <span class="o">=</span> <span class="s2">"unzip"</span> <span class="nv">destination</span> <span class="o">=</span> <span class="s2">"</span><span class="s-Interp">#config.mixed.destination_path#</span><span class="s2">"</span> <span class="nv">file</span> <span class="o">=</span> <span class="s2">"</span><span class="s-Interp">#path#</span><span class="s2">content.zip"</span> <span class="nv">overwrite</span> <span class="o">=</span> <span class="s2">"yes"</span> <span class="nv">recurse</span> <span class="o">=</span> <span class="s2">"yes"</span> <span class="nv">storePath</span> <span class="o">=</span> <span class="s2">"yes"</span><span class="nb">></span>
<span class="nb"><cfreturn</span> <span class="s2">""</span><span class="nb">></span>
</cffunction>
Nothing special here. The next thing that happens then is that the method CreateDatabaseTables is called.
At the end we get the result that Mango has been installed and that we could either follow the link to the administrator or the website itself. We have SUCCEEDED!!! This Method creates the database tables and a datasource in Lucee if necessary. So all is in your hand. You can use the tag in order to create necessary entries in the Lucee config. The complete extension can be downloaded here.
When the update and uninstall buttons are clicked the corresponding methods get invoked. They always get the complete configuration as an argument, as it was entered when the application was originally installed or updated. So the uninstall method looks like this:
<cffunction name="uninstall" returntype="string" output="no" hint="called by Lucee to uninstall the application">
<cfargument name="path" type="string">
<cfargument name="config" type="struct">
<cfset arguments.config.path = arguments.config.mixed.destination_path>
<span class="nb"><cfset</span> <span class="nf">deleteFiles</span><span class="p">(</span><span class="nv">argumentCollection</span><span class="o">=</span><span class="nv">arguments</span><span class="p">)</span><span class="nb">></span>
<span class="nb"><cfset</span> <span class="nf">dropDatabaseTables</span><span class="p">(</span><span class="nv">argumentCollection</span><span class="o">=</span><span class="nv">arguments</span><span class="p">)</span><span class="nb">></span>
<span class="nb"><cfreturn</span> <span class="s2">"Mango blog has been successfully removed. Tables have been dropped and the datasource and the corresponding files have been removed."</span><span class="nb">></span>
</cffunction>
Please note that you cannot delete the complete install path if the original install path was the webroot itself. There might be WEB-INF directories in there that either must not be deleted or are not deletable because they are protected (open for read). So mostly the installation is up to you so that you have full control over everything what happens.