Wednesday, January 27, 2016

Salesforce - working with large paging sets greater then 2000


The Use Case : You have a visual force page/s that need to display large amounts of data, possible hundreds of thousands of records.

The Problem : SOQL limits and other force.com limits would prevent you from return large amounts of data to a page in one hit.

The Solution : To solve this problem we can make remote calls either synchronously or asynchronously to return the data in smaller chucks. This can be achieved by ensuring the following:

  •  passing your remote method the id of the last record in the last retrieved record set and using that as the starting point to retrieve the next set of records in your where clause.
  • Order the records by Id
  • Setting a limit of the number of records that are returned.


eg

@RemoteAction @ReadOnly
    public static List GetCostDistLines(String projectId, Id offsetId) // projectNum)
    {

        String offsetIdSearch = (offsetId==null)?'0':offsetId;

        return [select id
//, GLDate__c
                //, GLPeriodName__c
                , PADate__c
                , PAPeriodName__c
                , ExpenditureItem__r.Quantity__c
                , Name
                , ExpenditureItem__r.PersonId__c
                , ExpenditureItem__r.ExpenditureType__c, ExpenditureItem__r.Name
                , ExpenditureItem__r.VendorId__c
                , ExpenditureItem__r.UOM__c
                , ExpenditureItem__r.ProjectTask__r.Name
                , ExpenditureItem__r.ProjectTask__r.ProjectTaskNumber__c
                , ExpenditureItem__r.ProjectTask__r.Id
                from CostDistLine__c
                //where ExpenditureItem__r.ProjectTask__r.ProjectNumber__c = :projectNum];
                where ExpenditureItem__r.ProjectTask__r.pse__Project__r.Id = :projectId and
                Id > :offsetIdSearch ORDER BY Id ASC LIMIT 10000];
    }

Tuesday, October 20, 2015

Fix Visualforce Sidebar and Header when using Bootstrap

The Problem

When using Bootstrap on a visualforce page with a sidebar and header showing you will undoubtedly notice that the styling of the sidebar and header will have changed to something rather undesirable. This is because Bootstrap is overriding some of the salesforce styling like the body text size. See the difference below, the left side is the undesirable bootstrap version.


Thankfully there is a way around this issue though it will involve compiling your own version of Bootstrap to use your own custom namespace.

Prerequisites  

There are a few thing you will need for this exercise: 
  • download the Bootstrap source code - you can get this from the bootstrap download page 
  • node.js - You will need to download and install node.js to get and run grunt.
  • Grunt - Grunt is what you need to build/compile and test Bootstrap. To install grunt globally from the node.js command line run the following npm install -g grunt-cli.

Now What

Now that we have bootstrap, node.js and grunt we can alter bootstrap to include a custom namespace so that our visualforce paged can differentiate between when/where to use salesforce css and when/where to use bootstrap css.

Follow the following steps:
  • In node.js command line  navigate to the root /bootstrap/ directory, then run npm install. npm will look at the package.json file and automatically install the necessary local dependencies listed there
  • From the bootstrap source open /less/bootstrap.less
  • wrap the contents of /less/bootstrap.less in a class like .bs{}
eg
  • still in node.js command line and root /bootstrap/ directory run grunt or grunt dist. This  will regenerate the /dist/ directory
  • Zip the contents of  the /dist/ directory and upload to the salesforce static resources.
  • Now you can wrap only the content you want affected by bootstrap style in a tag with a bs class like below.

Friday, October 16, 2015

Using Telerik kendo ui charts in salesforce

Salesforces provides a wide range of reporting and charting capabilities out-of-the-box like Salesforce1 / Lightning Reports and Dashboards for business users and Visaulforce Charts for developers that provide useful tooling in a wide of business cases. For the situations where you find salesforce just can provide what you need  OOB you can fall back on an alternative chart library such as google charts, highcharts, jscharts etc.... to extend salesforces already probabilities. In this case i'm going to show how to use 'Telerik Kendo UI' to extend your charting needs to build a multi axis double line / double bar chart like the following.



Friday, August 9, 2013

SQL Server - Executing Very long querys from a string

We have been linking out SQL servers to oracle lately as a mean of working around a particular issue which I will not go into. We have been needing to run oracle query from the linked server by wrapping the oracle queries in a string then using the 'EXECUTE (@query) AT [LINKED SERVER NAME'.

If the oracle query is less then 4000 characters the following would work:

declare @query as nvarchar(max)  =  '[a smaller then 4000 char oracle SQL] ' + '[Some more parts of the statement]'
EXECUTE (@query) AT [LINKEDSERVER]

Now as soon as the SQL statement is larger then 4000 characters you will begin to see errors when executing the statements. This is because when we concatenated the two strings in the above statement they concatenate into nvarchar(4000) in stead of nvarchar(max). To work around this issue cast all stings into nvarchar(max):

declare @query as nvarchar(max) = CAST('some sql' as nvarchar(max)) + CAST('some other sql' as nvarchar(max))
EXECUTE (@query) AT [LINKEDSERVER]
 

VoilĂ , we can now run excessively long queries from a string using EXECUTE.

Sunday, September 9, 2012

Copy SharePoint Designer workflow 2010



I think of SharePoint designer workflow as more of a end user tool rather then a developer tool, for something a bit more helpful for developers look at K2 or Nintex.  Thou if you need to update a production SharePoint designer workflow here are the steps that would recommend to making the changes off line on a development environment and deploying to production or test.




  1. Do a Database content migration of the  site collection that contain your workflow to you dev environment. Doing this will ensure all of the GUID's in the background are the exact same a production.
  2. Update your workflow and associated infopath form (if any) in Dev
  3. Now, before moving  to your test or prod environment I would highly recommend backing up you SharePoint area that we are updating as a just in case precaution.
  4. Publish your infopath form to production site.
  5. Open dev SharePoint and prod SharePoint in SharePoint designer and copy your workflows  .XOML and .XOML.RULES  files from dev to prod.
  6. Close and reopen the prod SharePoint designer. This forces the designer to pick up the new changes. I find with out this step SharePoint designer shows the old workflow.
  7. Open the workflow and validate that there are no missing action data.
  8. Publish the workflow 
Hey presto,

Wednesday, September 5, 2012

WSP Deployment stuck in "Deploying" state

While updating a wsp package in  noticed the package was stuck in "deploying" state for a few hours. Think this was a little to long i ran the follow command:

 stsadm -o execadmsvcjobs

Its now as happy as Larry

Friday, August 24, 2012

Lightswitch and Left Outer Join


                

Ever needed to do a left outer join in a lightswitch App using linq? Yes, well its pretty simple. Just add  .DefaultIfEmpty() to any join object that requires the left outer.



query = from pp in query
   from pbl in pp.ProjectBusinessLines.DefaultIfEmpty()
   from s in pbl.ProjectBusinessLineServices.DefaultIfEmpty()
   where pp.ProjectSummary.Contains(KeyWordSearch)
   || pp.CompanyName.Contains(KeyWordSearch)
   || pp.ProjectName.Contains(KeyWordSearch)
   || pbl.OurRole.Contains(KeyWordSearch)
   || pp.Location.Contains(KeyWordSearch)
   || s.Service.ServiceName.Contains(KeyWordSearch)  
   select pp;

Thursday, August 23, 2012

Very Sleepy SharePoint Workflow Pauses

Ahh, the dreaded sleepy 5 minute SharePoint workflow action pause that takes days even weeks to complete. Classic symptom of this problem in the workflow history logs reveals a quite unhelpfully  message such as 

[Workflow Name]failed to run

The one helpful thing the error in the workflow history logs does give is the time of the error. Using this error time I would suggest to go though the SharePoint event logs of each server and find out which server servers that this error occurs on. In my case this was occurring only on a front end server that we had set up to to only serve search index requests. it appeared that every workflow that tried to run on this server in OWSTimer was basically spewing out a error message then  putting the putting the workflow back to sleep. 

The errors it spews out are:

Workflow Compile Failed while loading AuthorizedTypes from config: Configuration system failed to initialize

and 

RunWorkflow: Microsoft.SharePoint.SPException: Configuration system failed to initialize     at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.SubCompiler.DoCompile(WorkflowCompilerParameters parameters, String xomlSource, String assemblyName, CompilationPacket& packet, DirectoryInfo& tempDir)     at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.SubCompiler.DoCompile(WorkflowCompilerParameters parameters, String xomlSource, String assemblyName, CompilationPacket& packet, DirectoryInfo& tempDir)     at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.<>c__DisplayClasse.b__c()     at Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode)     at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.DoCompileNewAppDomain(WorkflowCompilerParameters parameters, String xomlSource, String assemblyName, SPWeb web, CompilationPacket& packet, DirectoryInfo& tempDir)     at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.CompileBytes(Byte[] xomlBytes, Byte[] rulesBytes, Boolean doTestCompilation, String assemblyName, SPWeb web, Boolean forceNewAppDomain)     at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.LoadXomlAssembly(SPWorkflowAssociation association, SPWeb web)     at Microsoft.SharePoint.Workflow.SPWinOeHostServices.LoadDeclarativeAssembly(SPWorkflowAssociation association)     at Microsoft.SharePoint.Workflow.SPWinOeHostServices.CreateInstance(SPWorkflow workflow)     at Microsoft.SharePoint.Workflow.SPWinOeEngine.RunWorkflow(SPWorkflowHostService host, SPWorkflow workflow, Collection`1 events, TimeSpan timeOut)     at Microsoft.SharePoint.Workflow.SPWorkflowManager.RunWorkflowElev(SPWorkflow workflow, Collection`1 events, SPWorkflowRunOptionsInternal runOptions)

and 

08/23/2012 08:51:01.55 OWSTIMER.EXE (0x0CBC) 0x0DB8 SharePoint Foundation Workflow Infrastructure 98d8 Unexpected Microsoft.SharePoint.SPException: Configuration system failed to initialize     at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.SubCompiler.DoCompile(WorkflowCompilerParameters parameters, String xomlSource, String assemblyName, CompilationPacket& packet, DirectoryInfo& tempDir)     at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.SubCompiler.DoCompile(WorkflowCompilerParameters parameters, String xomlSource, String assemblyName, CompilationPacket& packet, DirectoryInfo& tempDir)     at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.<>c__DisplayClasse.b__c()     at Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode)     at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.DoCompileNewAppDomain(WorkflowCompilerParameters parameters, String xomlSource, String assemblyName, SPWeb web, CompilationPacket& packet, DirectoryInfo& tempDir)     at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.CompileBytes(Byte[] xomlBytes, Byte[] rulesBytes, Boolean doTestCompilation, String assemblyName, SPWeb web, Boolean forceNewAppDomain)     at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.LoadXomlAssembly(SPWorkflowAssociation association, SPWeb web)     at Microsoft.SharePoint.Workflow.SPWinOeHostServices.LoadDeclarativeAssembly(SPWorkflowAssociation association)     at Microsoft.SharePoint.Workflow.SPWinOeHostServices.CreateInstance(SPWorkflow workflow)     at Microsoft.SharePoint.Workflow.SPWinOeEngine.RunWorkflow(SPWorkflowHostService host, SPWorkflow workflow, Collection`1 events, TimeSpan timeOut)     at Microsoft.SharePoint.Workflow.SPWorkflowManager.RunWorkflowElev(SPWorkflow workflow, Collection`1 events, SPWorkflowRunOptionsInternal runOptions) 3010f8ef-7f1c-4adf-abba-7abfd48a28db

Now if the same workflow tried to use the same failing server to process the request the length of pause of our original 5 minute would grow experientially leading to the timer job  in some cases not even attempting to wake to workflow for days.

After reviewing the workflow counter in perfmon for this server it became apparent that no workflow has ever run properly on the this troubled server. This lead to step one of helping resolve this problem which was turning off the "Microsoft SharePoint Foundation Workflow Timer Service" on the troubled server. This stopped new occurrences [Workflow Name]failed to run in the workflow history. Happy me.

Step 2 how ever is how do we get the workflows to run on the troubled server? this still remains a mystery. Maybe ill find the answer for a different post.

Wednesday, August 22, 2012

Retrieving the COM class factory for component failed due to the following error: 80040154

Retrieving the COM class factory for component with CLSID {XXXXXXXX-XX...} failed due to the following error: 80040154

This error came out of nowhere today on one of our IIS boxes. There are appears to be several ways to resolve this issue:


  1. Modify your project's platform from 'Any CPU' to 'X86' in Project Properties, Build/Platform's Target in Visual Studio
  2. If your Application is a Web-Based Application, try setting IIS to run in 32-bit mode. You can do this by going to IIS Manager, selecting the Application Pool, then choosing "Advanced Settings". The second option from the top is "Enable 32-bit Applications".
  3. If your Application is Web-Based, it could be that the com object was not configured to allow launch and access permissions for the aspnet user identity. Under administrative tools > Component services under the tree view, go to Component Services > Computers > My Computer > DCOM Config and find the registered com object. Right click for properties. Under the security tag, customize the Permissions to allow asp.net user
  4. Check that the app pool is running on the correct version of .net. 
The problem for us was fix number 4, some how the app pool had switch from 4.0 to 2.0 .net. The suspect was a recent patch update.

Easy SQL split field



Here is an easy way to split a field into a left and right side use a particular splitting char (in this case a space)

SELECT     
    LEFT(TaskNo, charindex( ' ', TaskNo) ) as TheLeftSide, 
    RIGHT(TaskNo, len(TaskNo) - charindex( ' ', TaskNo) ) as  TheRightSide 
FROM         sometablename 

Tuesday, March 23, 2010

LINQ and Left Outer Joins


Left outer joins in LINQ are not so obvious in comparison to plain old SQL.
The following shows how an out join can be costucted in LINQ

var query = (from e in dc.GetTable<Employee>()

join ea in dc.GetTable<EmployeeAddress>()on e.Id equals ea.EmployeeId into tempAddresses

from addresses in tempAddresses.DefaultIfEmpty()

select new { e.FirstName, e.LastName, addresses.State, addresses.Town});


The SQL equivalent would be:

select [t0].[FirstName], [t0].[LastName], [t1].[State] as[State], [t1].[Town]as [Town]

from [dbo].[Employee] as  [t0

Left outer join  [dbo].[EmployeeAddress]as[t1] on[t0].[Id] = [t1].[PersonID]

Thursday, March 18, 2010

Get Recently Ran Query - SQL SERVER 2005

Have you ever wanted to see what queries are running in SQL server on the fly? Yes? Well everyone’s favourite SQL server guru Pinalkumar Dave has once again provided the perfect solution with the follow simple SQL script.

SELECT deqs.last_execution_time AS [Time], dest.TEXT AS [Query]
FROM sys.dm_exec_query_stats AS deqs
CROSS APPLY sys.dm_exec_sql_text(deqs.sql_handle) AS dest
ORDER BY deqs.last_execution_time DESC

One thing it doesn’t seem to do is recover last run quires where the database connection/session has finished. Now, Id like to see that as it whould have saved me from the silly mistake of not saving my work :(.

Wednesday, March 17, 2010

Pass DataContext to Silverlight IValueConverter

When passing DataContext to a Silverlight IValueConverter the following method is not supported:

<TextBox Text="{Binding Detail,
Mode
=TwoWay, NotifyOnValidationError=True,  ValidatesOnExceptions=True}" 
IsEnabled={Binding ,Converter="{StaticResource contactDetailsColorConverter}} >
</TextBox>

Instead you need to translate the the above out to its expanded tag version


<TextBox Text="{Binding Detail, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"  >
<TextBox.IsEnabled>
<Binding Converter="{StaticResource contactDetailsColorConverter}"/>
</TextBox.IsEnabled>
</TextBox>

Monday, March 15, 2010

SSRS - Use URL to Render to Excel

So many times have I had to render a SSRS report to excel from an application? The easiest way to do this I have found is to use the URL of the report to set the output format.

This can be done by appending "&rs:Format=Excel" to the end of the reports URL string. For example:

http://servername/ReportServer?foldername/reportname&rs:Format=Excel

If however I needed to pass a parameter to the report from a calling application I can simply add the parameter as a url parameter as well. The following example shows who to pass a parameter called "eventID" to the SSRS report and render the output to Excel format:

http://servername/ReportServer?foldername/reportname&rs:Format=Excell&EventId=23

Tuesday, March 17, 2009

SharePoint Farm Backup spbackup (codeplex)

Jesper M. Christensen has provided a simple yet affective tool for backing up a SharePoint farm, http://www.codeplex.com/spbackup.

This tool has been developed in PowerShell script and is quite configurable with the following options:
catastrophicbackup: Set this value to 1 to perform a complete farm backup and 0 to skip this backuptype
catastrophicmethod: Set this to full or differential
sitecollectionbackup: Set this value to 1 to perform an individual backup of a site collection and subsites and 0 to skip this backuptype
sitecollectionurl: Set this to the site collection url you want to perform an individual backup of
hivebackup: Set this value to 1 to perform a backup of your 12-hive
hive: Path to your 12-hive (usually located at C:\Program Files\Common Files\Microsoft Shared\web server extensions\12
iisbackup: Set this value to 1 to perform a backup of the IIS Metadata and 0 to skip this backuptype
iisencryptopassword: Type a password here to make the IIS6 Metadata backup restorable on other servers
smtpserver: Type a smtp server to send mail through
fromemail: E-mail address to send from
backupdestination: Type a valid path for the backupfiles
backupdestinationmaxkeepdays: If set to 0 no files are deleted. Set another number to delete files older .zip/.backup-files. Full/differential backup files are NOT deleted (from spbackup version 1.2)


Wes Macdonald has provided some useful tips to extend this PwerShell script to achieve additional backups to comply closer to the Disaster and Recovery Guide from Microsoft. This includes Alternate Access Mappings and the GAC.
See http://spbackup.codeplex.com/Thread/View.aspx?ThreadId=48260


My implantation of combinding Jaspers spback with Wes's suggestions are as follows:


#Get XML configuration file parameter
param (
[string]$Configurationfile = "c:\SPBackup\SPBackup.xml"
)

function Logging {
$logmsg = $args[0]
Write-Host $logmsg
$jobreport+[Environment]::NewLine+[System.DateTime]::Now
$logmsg
}

function sendemail
{
# ---
# --- Report to e-mail if email and smtpserver is defined ---
# ---
If ($fromemail)
{
If ($toemail)
{
If ($smtpserver)
{
$jobreport = logging("Ending with job status """+$jobstatus+""" for site id "+$id)
$jobreport = logging("Sending job report on email")
$subject = $jobstatus +": SPBackup has completed on "+$env:computername+" with "+[System.DateTime]::Now
$msg = new-object System.Net.Mail.MailMessage $fromemail, $toemail, $subject, $jobreport
$client = new-object System.Net.Mail.SmtpClient $smtpserver
$client.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
$client.Send($msg)
}
else
{
$jobreport = logging("Skipping Report to e-mail because of missing smtpserver")
}
}
else
{
$jobreport = logging("Skipping Report to e-mail because of missing toemail address")
}
}
else
{
$jobreport = logging("Skipping Report to e-mail because of missing fromemail address")
}
}

function out-zip {
$path = $args[0]
$files = $input

if (-not $path.EndsWith('.zip')) {$path += '.zip'}

if (-not (test-path $path))
{
set-content $path ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
}

$zipfile = (new-object -com shell.application).NameSpace($path)
$files foreach {$zipfile.CopyHere($_.fullname) }
}

If (Test-Path $Configurationfile) {
$cfg = [xml] (get-content $Configurationfile)
$jobreport = logging("Started backup with configuration file "+$Configurationfile)
}
Else
{
$jobstatus="Error"
$jobreport = logging( "Error: Configuration XML file not found!")
sendemail
exit
}
$QueryOS = Gwmi Win32_OperatingSystem -Comp localhost
$QueryOS = $QueryOS.Caption

If ($QueryOS.contains("2008") -or $QueryOS.contains("Vista"))
{
$jobreport = logging("Windows Server 2008 or Vista detected")
}

If ($QueryOS.contains("2003"))
{
$jobreport = logging("Windows Server 2003 detected")
}

#Go through each site-configuration in the file
$nodelist = $cfg.selectnodes("/confSPBackup/site")
foreach ($item in $nodelist) {
#Read this site parameters
$jobstatus="Success"
$aambackup = $item.alternateaccessmapping
$id = $item.getAttribute("id")
$catastrophicbackup = $item.catastrophicbackup
$catastrophicmethod = $item.catastrophicmethod
$sitecollectionbackup = $item.sitecollectionbackup
$sitecollectionurl = $item.sitecollectionurl
$hivebackup = $item.hivebackup
$hive = $item.hive
$iisbackup = $item.iisbackup
$iisencryptopassword = $item.iisencryptopassword
$smtpserver = $item.smtpserver
$fromemail = $item.fromemail
$toemail = $item.toemail
$backupdestination = $item.backupdestination
$backupdestinationmaxkeepdays = $item.backupdestinationmaxkeepdays
$backupfilename = $sitecollectionurl.replace("http://","")
$backupfilename = $backupfilename.replace("/","_")
$guid = "-" + [Guid]::NewGuid().ToString()

#Print the configuration parameter-details
$jobreport = logging("Starting session"+$guid)
$item

# ---
# --- Delete old files from backupdestination if backupdestinationmaxkeepdays<>0 ---
# ---
If ([string]::Compare($backupdestinationmaxkeepdays, "0", $True))
{
#Perform deletion if backup destination path is valid
if(test-path $backupdestination)
{
& dir $backupdestination ? {$_.CreationTime -lt (get-date).AddDays(-$backupdestinationmaxkeepdays) -and $_.name.EndsWith('.zip')} del -force
& dir $backupdestination ? {$_.CreationTime -lt (get-date).AddDays(-$backupdestinationmaxkeepdays) -and $_.name.EndsWith('.backup')} del -force
[DateTime]::Now.ToString() + ": Files on backup destination "+$backupdestination+" older than "+$backupdestinationmaxkeepdays+" days deleted!"
}
else
{
$jobstatus="Error"
$jobreport = logging("Error: The backupdestination Path doesn't exists for site id "+$id)
}
}
else
{
$jobreport = logging("Skipping deletion of old files on backupdestination")
}


$gacbackup = $item.gacbackup
$gac = $env:windir+"\assembly\gac_msil\"

# ---
# --- Do a INETPUB backup if inetpubbackup=1 ---
# ---



# ---
# --- Do a GAC backup if gacbackup=1 ---
# ---

#Global Assembly Cache
#Added element in the xml configuration file and modified the portion of the script which reads the XML to include

$gacbackup = $item.gacbackup
$gac = $env:windir+"\assembly\gac_msil\"

If ([string]::Compare($gacbackup, "0", $True))
{
$jobreport = logging("Starting backup of GAC to "+$backupdestination+"\"+$backupfilename+"-gac"+$guid+".zip")
if(test-path $backupdestination)
{
if(test-path $gac)
{
Write-Zip -Path $gac -IncludeEmptyDirectories -OutputPath $backupdestination\$backupfilename"-gac"$guid".zip" > $null
[DateTime]::Now.ToString() + ": GAC backup Done! File name is $backupdestination\$backupfilename-gac$guid.zip" >> "$backupdestination\log.txt"
}
else
{
$jobstatus = "Error"
$jobreport = logging("Error: The GAC Path doesn't exist for site id "+$id)
}
}
else
{
$jobstatus = "Error"
$jobreport = logging("Error: The backupdestination Path doesn't exists for site id "+$id)
}
}
else
{
$jobreport = logging("Skipping backup of GAC "+$gac)
}

Start-Sleep 10

# ---
# --- Do a hive backup if hivebackup=1 ---
# ---
If ([string]::Compare($hivebackup, "0", $True))
{
$jobreport = logging("Starting backup of hive "+$hive+" to "+$backupdestination+"\"+$backupfilename+"-12hive"+$guid+".zip")
if(test-path $backupdestination)
{
if(test-path $hive)
{
gi $hive out-zip $backupdestination"\"$backupfilename"-12hive"$guid".zip" $_
[DateTime]::Now.ToString() + ": Hive backup Done! File name is $backupdestination\$backupfilename-12hive$guid.zip" >> "$backupdestination\log.txt"
}
else
{
$jobstatus="Error"
$jobreport = logging("Error: The 12hive Path doesn't exists for site id "+$id)
}
}
else
{
$jobstatus="Error"
$jobreport = logging("Error: The backupdestination Path doesn't exists for site id "+$id)
}
}
else
{
$jobreport = logging("Skipping backup of hive "+$hive)
}
Start-sleep 30





# ---
# --- Do a IIS Metadata backup if iisbackup=1 ---
# ---
If ([string]::Compare($iisbackup, "0", $True))
{
$jobreport = logging("Starting backup of IIS Metadata to "+$backupdestination+"\"+$backupfilename+"-IISMetadata"+$guid+".zip")
if(test-path $backupdestination)
{
If ($QueryOS.contains("2003"))
{
if(test-path $env:windir\system32\inetsrv\metaback)
{
#Check if iisuser and iispassword are entered if not run the iisback.vbs with local machine encryption
#With local machine encryption the IIS Metadata can only be restored on this particullar IIS instance/Server
If ($iisencryptopassword)
{
$jobreport = logging("The IIS Metadata is done using encryption password specified")
& cscript.exe $env:windir\system32\iisback.vbs /backup /e $iisencryptopassword /b $env:computername /overwrite
}
else
{
$jobreport = logging("The IIS Metadata is done using machine credentials (restore on same IIS instance/server only)")
& cscript.exe $env:windir\system32\iisback.vbs /backup /b $env:computername /overwrite
}
ls $env:windir\system32\inetsrv\metaback\$env:computername.* out-zip $backupdestination"\"$backupfilename"-IISMetadata"$guid".zip" $_
[DateTime]::Now.ToString() + ": IIS Metadata backup Done! File name is $backupdestination\$backupfilename-IISMetadata$guid.zip" >> "$backupdestination\log.txt"
}
else
{
$jobstatus="Error"
$jobreport = logging("Error: The IIS Metadata Path doesn't exists for site id "+$id)
}
}
If ($QueryOS.contains("2008"))
{
& $env:windir\system32\inetsrv\appcmd.exe add backup
gi $env:windir\system32\inetsrv\backup out-zip $backupdestination"\"$backupfilename"-IISMetadata"$guid".zip" $_
[DateTime]::Now.ToString() + ": IIS Metadata backup Done! File name is $backupdestination\$backupfilename-IISMetadata$guid.zip" >> "$backupdestination\log.txt"

}
}
else
{
$jobstatus="Error"
$jobreport = logging("Error: The backupdestination Path doesn't exists for site id "+$id)
}
}
else
{
$jobreport = logging("Skipping backup of IIS Metadata ")
}
Start-sleep 10

# ---

# --- Do a Alternate Access Mappings backup if aambackup=1 ---
# ---
If ([string]::Compare($aambackup, "0", $True))
{
$jobreport = logging("Starting backup of Alternate Access Mappings to "+$backupdestination+"\"+$backupfilename+"-aam"+$guid+".xml")
if(test-path $backupdestination) {
& $hive\BIN\stsadm.exe -o enumalternatedomains >> $backupdestination"\"$backupfilename"-aam"$guid.xml
[DateTime]::Now.ToString() + ": Alternate Access Mappings backup Done! File name is $backupdestination\$backupfilename-aam$guid.xml" >> "$backupdestination\log.txt"
}
else
{
$jobstatus = "Error"
$jobreport = logging("Error: The backupdestination Path doesn't exist for site id "+$id)
}
}
else
{
$jobreport = logging("Skipping backup of Alernate Access Mappings.")
}



# ---
# --- Do a site collection crawl and backup if sitecollectionbackup=1 ---
# ---
If ([string]::Compare($sitecollectionbackup, "0", $True))
{
$jobreport = logging("Starting backup of individual site "+$sitecollectionurl+" to "+$backupdestination+"\"+$backupfilename+$guid+".backup")

#Perform backup if backup destination path is valid
if(test-path $backupdestination)
{
& $hive\BIN\stsadm.exe -o backup -url $sitecollectionurl -filename $backupdestination"\"$backupfilename$guid.backup -overwrite > $null
[DateTime]::Now.ToString() + ": Site backup Done! File name is $backupdestination\$backupfilename$guid.backup" >> "$backupdestination\log.txt"
}
else
{
$jobstatus="Error"
$jobreport = logging("Error: The backupdestination Path doesn't exists for site id "+$id)
}
}
else
{
$jobreport = logging("Skipping backup of individual site "+$sitecollectionurl)
}

Start-Sleep 10

# ---
# --- Do a farm backup if catastrophicbackup=1 ---
# ---
If ([string]::Compare($catastrophicbackup, "0", $True))
{
$jobreport = logging("Starting catastrophic backup of farm to "+$backupdestination+"\")

#Perform backup if backup destination path is valid
if(test-path $backupdestination)
{
& $hive\BIN\stsadm.exe -o backup -directory $backupdestination"\" -backupmethod $catastrophicmethod -overwrite > $null
[DateTime]::Now.ToString() + ": Farm backup Done! Destination is $backupdestination\" >> "$backupdestination\log.txt"
}
else
{
$jobstatus="Error"
$jobreport = logging("Error: The backupdestination Path doesn't exists for site id "+$id)
}
}
else
{
$jobreport = logging("Skipping catastrophic backup of farm with xml site id "+$id)
}
sendemail
$jobreport=""
}
Write-host([Environment]::NewLine+[Environment]::NewLine+"SPBackup has ended, waiting 40 seconds for jobs to complete...")
Start-Sleep 40