Featured Post

Organize and rename photos by EXIF data with PowerShell

This PowerShell script organizes and renames all photos in a selected folder using EXIF data. It will also create thumbnails of the images i...

Thursday, May 22, 2014

Organize and rename photos by EXIF data with PowerShell

This PowerShell script organizes and renames all photos in a selected folder using EXIF data. It will also create thumbnails of the images into a thumbs folder.

Simply copy the script into a PowerShell ISE script window and execute! You will be prompted for a Source and Target folder.

Example:

Source:

  • unsorted\DSC_0236.jpg
  • unsorted\DSC_0258.jpg
  • unsorted\IMG_0258.jpg

Target:
  • sorted\2014\05_May\2014-05-01_19-08-26_258.jpg
  • sorted\2014\05_May\thumbs\2014-05-01_19-08-26_258_thumb_400x400.jpg

# ==============================================================================================
# 
# Microsoft PowerShell Source File 
# 
# This script will organize photo and video files by renaming the file based on the date the
# file was created and moving them into folders based on the year and month. It will also append
# an index number to the end of the file name just to avoid name collisions. The script will
# look in the SourceRootPath (recursing through all subdirectories) for any files matching
# the extensions in FileTypesToOrganize. It will rename the files and move them to folders under
# DestinationRootPath, e.g. DestinationRootPath\2011\02_February\2011-02-09_21-41-47_680.jpg
# 
# JPG files contain EXIF data which has a DateTaken value. Other media files have a MediaCreated
# date. 
#
# The code for extracting the EXIF DateTaken is based on a script by Kim Oppalfens:
# http://blogcastrepository.com/blogs/kim_oppalfenss_systems_management_ideas/archive/2007/12/02/organize-your-digital-photos-into-folders-using-powershell-and-exif-data.aspx
# ============================================================================================== 
# Modified by Brock Hensley brock@brockhensley.com
# - Prompts for many options (source/destination folders, label, thumbnail sizes (multiple at once even))
# - Resize thumbnail rotates orientation appropriately
# - Named by incremental number instead of random number
# - Ability to apply time fix for multiple cameras with out of sync clocks
# ============================================================================================== 

[Reflection.Assembly]::LoadFile("C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll") 

function Select-Folder($message='Select folder', $path = 0) { 
    $object = New-Object -comObject Shell.Application  
    $folder = $object.BrowseForFolder(0, $message, 0, $path) 
    if ($folder -ne $null) { 
        return $folder.self.Path 
    } 
} 

# Defaults
$FileTypesToOrganize = @("*.jpg","*.jpeg")

# Prompts to override defaults
$Label = ""
$prompt = Read-Host "What to name files (default is date taken timestamp + index#: YYYY-MM-DD_HH-MM-SS_x#)"
if ($prompt -ne $null -and $prompt -ne "") {$Label = $prompt}

$Index = 10000
$prompt = Read-Host "Index starting number (default: $Index)"
if ($prompt -ne $null -and $prompt -ne "") { $Index = [Convert]::ToInt32($prompt) }

$SourceRootPath = "C:\UnsortedPhotos"
$prompt = Select-Folder "Select source folder (default: $SourceRootPath )"
if ($prompt -ne $null -and $prompt -ne "") {$SourceRootPath = $prompt}

$DestinationRootPath = "C:\SortedPhotos"
$prompt = Select-Folder "Select destination folder (default: $DestinationRootPath )"
if ($prompt -ne $null -and $prompt -ne "") {$DestinationRootPath = $prompt}

$CopyRenameOrig = $true
$prompt = Read-Host "Copy and Rename originals by date? ([y]/n)"
if ($prompt -ne $null -and $prompt -ne "" -and $prompt.ToLower() -eq "n") {
    $CopyRenameOrig = $false
}

$CreateThumb = $true
$prompt = Read-Host "Create thumbnails? ([y]/n)"
if ($prompt -ne $null -and $prompt -ne "" -and $prompt.ToLower() -eq "n") {
    $CreateThumb = $false
}

if ($CreateThumb -eq $true)
{
    $ThumbSizes = "128, 256, 512, 1024"
    Write-Host "Enter thumbnail sizes to create in comma seperated list. Default:" $ThumbSizes
    Write-Host "or enter N to skip creating thumbnails"
    $prompt = Read-Host "Thumbnail sizes"
    if ($prompt -ne $null -and $prompt -ne "") {
        if ($prompt.ToLower() -eq "n")
        {
            $CreateThumb = $false
        }
        else {
            $ThumbSizes = $prompt
        }
    }
}

# Functions
function GetMediaCreatedDate($File) {
 $Shell = New-Object -ComObject Shell.Application
 $Folder = $Shell.Namespace($File.DirectoryName)
 $CreatedDate = $Folder.GetDetailsOf($Folder.Parsename($File.Name), 191).Replace([char]8206, ' ').Replace([char]8207, ' ')
 if (($CreatedDate -as [DateTime]) -ne $null) {
  return [DateTime]::Parse($CreatedDate)
 } else {
  return $null
 }
}

function ConvertAsciiArrayToString($CharArray) {
 $ReturnVal = ""
 foreach ($Char in $CharArray) {
  $ReturnVal += [char]$Char
 }
 return $ReturnVal
}

function GetExifDateTaken($File) {
 $FileDetail = New-Object -TypeName System.Drawing.Bitmap -ArgumentList $File.Fullname
    if ($FileDetail -ne $null)
    {
        $DateTimePropertyItem = $FileDetail.GetPropertyItem(36867)
        $FileDetail.Dispose()
    }
    if ($DateTimePropertyItem -eq $null)
    {
        return $null
    }

 $Year = ConvertAsciiArrayToString $DateTimePropertyItem.value[0..3]
 $Month = ConvertAsciiArrayToString $DateTimePropertyItem.value[5..6]
 $Day = ConvertAsciiArrayToString $DateTimePropertyItem.value[8..9]
 $Hour = ConvertAsciiArrayToString $DateTimePropertyItem.value[11..12]
 $Minute = ConvertAsciiArrayToString $DateTimePropertyItem.value[14..15]
 $Second = ConvertAsciiArrayToString $DateTimePropertyItem.value[17..18] 

    # CameraA and CameraB taking pictures of same event
    # ...CameraB timestamps don't match CameraA due to DST etc.
    # Set $timefix to $true to make adustments here
    # otherwise by default this should be set to $false
    $timefix = $false
    if ($timefix -eq $true)
    {
        $Hour = ([Convert]::ToInt32($Hour) + 1).ToString()
        $Minute = ([Convert]::ToInt32($Minute) - 18).ToString()
    }

 $DateString = [String]::Format("{0}-{1}-{2} {3}:{4}:{5}", $Year, $Month, $Day, $Hour, $Minute, $Second).Trim()
 if (($DateString -as [DateTime]) -ne $null) {
  return [DateTime]::Parse($DateString)
 } else {
  return $null
 }
}

function GetCreationDate($File) {
    if ($File.Extension.ToLower() -eq ".jpg" -or $File.Extension.ToLower() -eq ".jpeg")
    {
        $CreationDate = GetExifDateTaken($File)
    }
    else
    {
        $CreationDate = GetMediaCreatedDate($File)
    }
 return $CreationDate
}

function BuildDesinationPath($Path, $Date) {
 return [String]::Format("{0}\{1}\{2}_{3}", $Path, $Date.Year, $Date.ToString("MM"), $Date.ToString("MMMM"))
}

function BuildThumbDesinationPath($Path, $Date, $ThumbSizeLabel) {
 return [String]::Format("{0}\{1}\{2}_{3}\thumbs\{4}", $Path, $Date.Year, $Date.ToString("MM"), $Date.ToString("MMMM"), $ThumbSizeLabel)
}

function BuildNewFilePath($Path, $Date, $Extension, $Index, $Label) {
    if ($Label -eq "")
    {$Label = $Date.ToString("yyyy-MM-dd_HH-mm-ss")}
    return [String]::Format("{0}\{1}_x{2}{3}", $Path, $Label, $Index, $Extension.ToLower())

    if ($Label -ne "")
    {
        return [String]::Format("{0}\{1}_x{2}{3}", $Path, $Label, $Index, $Extension.ToLower())
    }
    return [String]::Format("{0}\{1}_x{2}{3}", $Path, $Date.ToString("yyyy-MM-dd_HH-mm-ss"), $Index, $Extension.ToLower())
}

function BuildThumbFilePath($Path, $Date, $Extension, $Size, $Index, $Label) {
    if ($Label -eq "")
    {$Label = $Date.ToString("yyyy-MM-dd_HH-mm-ss")}
    return [String]::Format("{0}\thumbs\{5}\{1}_x{2}_{3}{4}", $Path, $Label, $Index, $Size, $Extension.ToLower(), $Size)
}

function ScaleImage($image, $maxWidth, $maxHeight)
{
    try { $prop = $image.GetPropertyItem(274) } catch {}
    if ($prop -ne $null) {
        $orientation = [int]$prop.Value[0];
    }
    else
    {
        $orientation = 1
    }
    switch ($orientation)
    {
        1 {
            # No rotation required.
            break;
            }
        2 {
            $image.RotateFlip("RotateNoneFlipX");
            break;
            }
        3 {
            $image.RotateFlip("Rotate180FlipNone");
            break;
            }
        4 {
            $image.RotateFlip("Rotate180FlipX");
            break;
            }
        5 {
            $image.RotateFlip("Rotate90FlipX");
            break;
            }
        6 {
            $image.RotateFlip("Rotate90FlipNone");
            break;
            }
        7 {
            $image.RotateFlip("Rotate270FlipX");
            break;
            }
        8 {
            $image.RotateFlip("Rotate270FlipNone");
            break;
            }
        }

    [Double]$ratioX = $maxWidth / $image.Width;
    [Double]$ratioY = $maxHeight / $image.Height;
    $ratio = [System.Math]::Min($ratioX, $ratioY);

    [int]$newWidth = $image.Width * $ratio;
    [int]$newHeight = $image.Height * $ratio;

    # Now calculate the X,Y position of the upper-left corner (one of these will always be zero)
    [int]$posX = [Convert]::ToInt32(($maxWidth - ($image.Height * $ratio)) / 2);
    [int]$posY = [Convert]::ToInt32(($maxHeight - ($image.Width * $ratio)) / 2);

    Write-Host -NoNewline " "$newWidth"x"$newHeight

    $newImage = New-Object -TypeName System.Drawing.Bitmap -ArgumentList $newWidth,$newHeight

    $g=[System.Drawing.Graphics]::FromImage($newImage);
    $g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic # use high quality resize algorythm
    $g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality
    $g.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality
    $g.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality

    $g.Clear([System.Drawing.Color]::White)
    $g.DrawImage($image, 0, 0, $newWidth, $newHeight)

    return $newImage;
}

function CreateDirectory($Path){
 if (!(Test-Path $Path)) {
  New-Item $Path -Type Directory | Out-Null
 }
}

function ConfirmContinueProcessing() {
 $Response = Read-Host "Continue? (Y/N)"
 if ($Response.Substring(0,1).ToUpper() -ne "Y") { 
  break 
 }
}

# Begin
$StartTime = Get-Date
Write-Host "*Begin" $StartTime
$Files = Get-ChildItem $SourceRootPath -Recurse -Include $FileTypesToOrganize
$Count = 0
$Existing = 0
$ThumbCount = 0
$ExistingThumb = 0
$Unknown = 0
$LastCreationDate = $null
foreach ($File in $Files) {

 $CreationDate = GetCreationDate($File)
    if ($CreationDate -eq $null -or ($CreationDate -as [DateTime]) -eq $null) {
        $Unknown++
  Write-Host " Unable to determine creation date of file. " $File.FullName
        if ($LastCreationDate -eq $null)
        {
            $CreationDate = [DateTime]::Now
        }
        else
        {
            $CreationDate = $LastCreationDate
        }
        Write-Host " (borrowing creation date: " $CreationDate " )"
        #ConfirmContinueProcessing
    }
    $LastCreationDate = $CreationDate

 $DestinationPath = BuildDesinationPath $DestinationRootPath $CreationDate
 CreateDirectory $DestinationPath

    $NewFilePath = BuildNewFilePath $DestinationPath $CreationDate $File.Extension $Index $Label

    Write-Host ""
    Write-Host ""
 Write-Host "[Orig] " $File.FullName
    Write-Host "--New: " $NewFilePath
    if ($CopyRenameOrig -eq $true)
    {
     if (Test-Path $NewFilePath) {
            $Existing++
      Write-Host " -- **** (skipping) Unable to make file. File already exists: $NewFilePath ****"
      #ConfirmContinueProcessing
     } else {
            Copy-Item $File.FullName $NewFilePath
            $Count++
     }
    }

    if ($CreateThumb -eq $true)
    {
        Write-Host " -- Creating thumbnails: " -NoNewline
        $ThumbSizes.Split(",") | ForEach {
            $ThumbSize = $_.Trim();
            $ThumbSizeLabel = $ThumbSize + "x" + $ThumbSize

            $DestinationThumbPath = BuildThumbDesinationPath $DestinationRootPath $CreationDate $ThumbSizeLabel
            CreateDirectory $DestinationThumbPath

            $ThumbFilePath = BuildThumbFilePath $DestinationPath $CreationDate $File.Extension $ThumbSizeLabel $Index $Label
            if (Test-Path $ThumbFilePath) {
                $ExistingThumb++;
                Write-Host " -- **** (skipping thumb: $ThumbFilePath) ****"
            } else {
                $full = [System.Drawing.Image]::FromFile($File.FullName);
                $newImage = ScaleImage $full $ThumbSize $ThumbSize

                #Encoder parameter for image quality 
                $myEncoder = [System.Drawing.Imaging.Encoder]::Quality
                $encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(1) 
                $encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter($myEncoder, 100)

                # get codec
                $myImageCodecInfo = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders()|where {$_.MimeType -eq 'image/jpeg'}
    
                if ($newImage -ne $null){
                    $newImage.Save($ThumbFilePath, $myImageCodecInfo, $($encoderParams));
                }
                $full.Dispose();
                if ($newImage -ne $null){$newImage.Dispose();}
            }
            $ThumbCount++
        }
    }
    $Index++
}
$TimeSpan = New-TimeSpan $StartTime (Get-Date)
Write-Host ""
Write-Host "**** Done! Total:" $Count " Existing (skipped):" $Existing " Unknown dates:" $Unknown " Duration:" $TimeSpan.Minutes"min " $TimeSpan.Seconds "sec"

Thursday, May 15, 2014

Guide for creating the perfect Windows Apache MySQL PHP (WAMP) Dev machine

You don't need any pre-built dev environment for Windows such as EasyPHP, UniformServer, XAMPP, Z-WAMP, WampServer or any other kind of WAMP that gets spun up, neglected and eventually abandoned.

It is much better to install the components you need and manage them as they were intended rather than rely on some 3rd party tool to re-invent the wheel.

If you're thinking "but I want to save time..." stop right there and realize that this will save you time in the long run and bring you the ability to do anything if not more than what any pre-built bundle can offer.

It really doesn't take long to do this, especially following this guide!

Getting Started

We will install:
  • Apache
  • PHP
  • MySQL
  1. Create a folder to house all the services we are about to install. I create it on the root drive  to ensure a short path with no spaces, preferably a secondary drive with lots of free space. My folder is E:\Services
  2. Create a folder for each service we will install, these folders will house different versions of each service to allow for switching of versions such as PHP4, PHP5, PHP5.5 etc.
    1. E:\Services\Apache
    2. E:\Services\MySQL
    3. E:\Services\PHP
Note: Even though you may be using a 64-bit machine, it is recommended to install the 32-bit versions of both Apache and PHP as some issues may occur otherwise with some modules etc. that do not yet support 64-bit.

Apache

  1. Download whichever version of Apache HTTPD Server you want from: Closest Apache Download Mirror but if you are looking for v2.4 you will need to get it from Apache Lounge.
    1. I will be using: Apache 2.4 binaries VC11 IPv6 Crypto apr-1.5.0 apr-util-1.5.3 apr-iconv-1.2.1 openssl-1.0.1g zlib-1.2.8 pcre-8.34 libxml2-2.9.1 lua-5.1.5 expat-2.1.0
  2. BEFORE YOU EXTRACT THE DOWNLOAD be sure to Right-Click on the file, go to Properties and on the bottom if you see an Unblock button, click it (then OK)! This will ensure all files are trusted when you unzip it otherwise you could run into problems later.
  3. Extract the download and move the contents of the Apache24 folder into your Apache services folder, retaining the version number in the folder for future reference such as:
    1. E:\Services\Apache\httpd-2.4.9-win32-VC11
      1. Inside this folder should be many folders such as bin, conf, modules
  4. Now edit the configuration file: E:\Services\Apache\httpd-2.4.9-win32-VC11\conf\httpd.conf
    1. NOTE: Where filenames are specified, you must use forward slashes 
    2. instead of backslashes (e.g., "c:/apache" instead of "c:\apache")
    3. Search for "ServerRoot" and set the value appropriately, for example:
      1. ServerRoot "E:/Services/Apache/httpd-2.4.9-win32-VC11"
    4. Search for "DocumentRoot" and set the values appropriately, for example:
      1. DocumentRoot "E:/Services/Apache/httpd-2.4.9-win32-VC11/htdocs"
      2. <Directory "E:/Services/Apache/httpd-2.4.9-win32-VC11/htdocs">
    5. Search for "ScriptAlias" and set the value appropriately, for example:
      1. ScriptAlias /cgi-bin/ "E:/Services/Apache/httpd-2.4.9-win32-VC11/cgi-bin/"
      2. <Directory "E:/Services/Apache/httpd-2.4.9-win32-VC11/cgi-bin">
    6. Uncomment (remove the # in front) the following line, this will allow for simple management of your project websites (virtual hosts):
      1. Include conf/extra/httpd-vhosts.conf
  5. You can either run httpd.exe whenever you need this version of Apache, otherwise you can install it as a Service and set it to Automatic start if you use it regularly.
    1. Install as service: Open Command Prompt and change directories to the apache bin folder. Run the following command to install it as a service with a unique name:
      1. C:\Windows\System32>E:
      2. E:\>cd E:\Services\Apache\httpd-2.4.9-win32-VC11\bin
      3. E:\Services\Apache\httpd-2.4.9-win32-VC11\bin>httpd.exe -k install -n httpd-2.4.9-win32-VC11
  6. Known Issues:
    1. AH00112: Warning: DocumentRoot [C:/Apache24/docs/dummy-host.example.com] does not exist
      1. This is due to the conf/extra/vhost.conf file we uncommented in the httpd.conf. You will need to configure your vhost.conf file appropriately to point to your project folders as needed. Otherwise you can simply remove or comment out the vhost section until you need it.
    2. AH00558: httpd.exe: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1. Set the 'ServerName' directive globally to suppress this message
      1. This can be safely ignored
    3. (OS 10013)An attempt was made to access a socket in a way forbidden by its access permissions.  : AH00072: make_sock: could not bind to address [::]:80
      1. This is due to another service already listening on port 80. Either change your existing service configuration to use another port, or change your new httpd.conf Listen 80 setting to a unique port to run multiple Apache services simultaneously.

PHP

  1. Download whichever version of PHP you want from: windows.php.net
    1. I will be using VC11 x86 Non Thread Safe
    2. Note: x64 versions should be avoided even if you are on x64
  2. BEFORE YOU EXTRACT THE DOWNLOAD be sure to Right-Click on the file, go to Properties and on the bottom if you see an Unblock button, click it (then OK)! This will ensure all files are trusted when you unzip it otherwise you could run into problems later.
  3. Extract the download and move the php folder into your PHP services folder, retaining the version number in the folder for future reference such as:
    1. E:\Services\PHP\php-5.5.12-nts-Win32-VC11-x86
      1. Inside this folder should be folders such as dev, ext, extras
  4. Now copy the example php.ini-development file to php.ini file: E:\Services\PHP\php-5.5.12-nts-Win32-VC11-x86\php.ini and edit the php.ini file:
    1. Search for "extension_dir" and uncomment (remove the leading ;) this line:
      1. extension_dir = "ext"
  5. Now we must tell Apache to use PHP, only we want to use the FastCGI module to do this (recommended) so first download the module:
    1. Download the Apache 2.4 Modules VC11 from Apache Lounge.
      1. I will be using modules-2.4-win32-VC11
    2. BEFORE YOU EXTRACT THE DOWNLOAD be sure to Right-Click on the file, go to Properties and on the bottom if you see an Unblock button, click it (then OK)! This will ensure all files are trusted when you unzip it otherwise you could run into problems later.
    3. Extract the download to a temporary location
    4. Drill down the folder structure to find the mod_fcgid.so file
      1. modules-2.4-win32-VC11\mod_fcgid-2.3.9\mod_fcgid\mod_fcgid.so
    5. Copy the file to your httpd\modules folder:
      1. E:\Services\Apache\httpd-2.4.9-win32-VC11\modules\mod_fcgid.so
  6. Edit the Apache configuration file: E:\Services\Apache\httpd-2.4.9-win32-VC11\conf\httpd.conf
    1. At the end of the file, paste the following (update the PHP paths appropriately):
LoadModule fcgid_module modules/mod_fcgid.so
# Where is your php.ini file?
FcgidInitialEnv PHPRC "E:/Services/PHP/php-5.5.12-nts-Win32-VC11-x86"
AddHandler fcgid-script .php
FcgidWrapper "E:/Services/PHP/php-5.5.12-nts-Win32-VC11-x86/php-cgi.exe" .php

Now, restart the Apache service and you will be able to serve PHP files.

MySQL

  1. Download the manual install ZIP Archive from http://dev.mysql.com/downloads/mysql/
    1. I will be using Windows (x86, 64-bit), ZIP Archive
  2. BEFORE YOU EXTRACT THE DOWNLOAD be sure to Right-Click on the file, go to Properties and on the bottom if you see an Unblock button, click it (then OK)! This will ensure all files are trusted when you unzip it otherwise you could run into problems later.
  3. Extract the download and move the mysql folder into your MySQL services folder, retaining the version number in the folder for future reference such as:
    1. E:\Services\MySQL\mysql-5.6.17-winx64
      1. Inside this folder should be folders such as bin, data, docs
  4. Now copy the example my-default.ini file to my.ini file: E:\Services\MySQL\mysql-5.6.17-winx64\my.ini
  5. Install as service: Open Command Prompt and change directories to the mysql bin folder. Run the following command to install it as a service with a unique name:
    1. C:\Windows\System32>E:
    2. E:\>cd E:\Services\MySQL\mysql-5.6.17-winx64\bin
    3. E:\Services\MySQL\mysql-5.6.17-winx64\bin>mysqld --install MySQL5.6.17 --defaults-file=E:\Services\MySQL\mysql-5.6.17-winx64\my.ini
  6. Finally, start the service:
    1. net start MySQL5.6.17
Note: After MySQL is installed, I recommend using the MySQL Workbench to connect to the service and manage users, databases, queries etc.

Now What?

Now you just need to configure your Virtual Hosts (httpd/conf/extra/httpd-vhosts.conf) to serve content out of your projects folder, wherever that may be. Otherwise you can simply host 1 site at a time out of the httpd/htdocs folder and browse to http://127.0.0.1/

How to test if PHP is working?

Create a file in your htdocs folder with a php extension such as "phpinfo.php"

Paste the following into the file:

<?php
phpinfo();
?>

Save the file, and browse to http://127.0.0.1/phpinfo.php

If you do not see a long output of PHP configuration settings, or you see error messages, check the Apache logs in httpd/logs for more information.
  • For 403 Forbidden message, ensure that your <Directory> configuration in Apache's httpd.conf has ExecCGI added to it's Options, for example:
<Directory "E:/Services/Apache/httpd-2.4.9-win32-VC11/htdocs">
    Options Indexes FollowSymLinks ExecCGI


Virtual Host Configuration

Example:

<VirtualHost *:80>
    ServerAdmin admin@test.com
    DocumentRoot "E:/Projects/test.com/public_html"
    ServerName dev.test.com
    ServerAlias dev.test.org dev.test.net
    
    ErrorLog "E:/Projects/test.com/logs/error.log"
    CustomLog "E:/Projects/test.com/logs/access.log" common
    
    DirectoryIndex index.html index.htm index.php
    
    <Directory "E:/Projects/test.com/private">
        Options All
        AllowOverride All
        Require all granted
    </Directory>
    
    <Directory "E:/Projects/test.com/public_html">
        Options All
        AllowOverride All
        Require all granted
    </Directory>

</VirtualHost>

XDebug

  1. Download XDebug from http://xdebug.org/download.php be sure to get the version that matches your PHP (In my case PHP 5.5 VC11 (32 bit))
  2. Be sure to Right-Click on the file, go to Properties and on the bottom if you see an Unblock button, click it (then OK)! This will ensure the file is trusted otherwise you could run into problems later.
  3. Move the dll into the php\ext folder: E:\Services\PHP\php-5.5.12-nts-Win32-VC11-x86\ext\php_xdebug-2.2.5-5.5-vc11-nts.dll
  4. Edit the php.ini file E:\Services\PHP\php-5.5.12-nts-Win32-VC11-x86\php.ini
    1. Search for "extension=php" and at the end of the extension list, add the following:
; XDebug
zend_extension="E:\Services\PHP\php-5.5.12-nts-Win32-VC11-x86\ext\php_xdebug-2.2.5-5.5-vc11-nts.dll"

; XDebug Recommended settings:
xdebug.remote_enable=1
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.default_enable=1
xdebug.auto_trace=on
xdebug.remote_log="E:\Services\XDebug.log"
xdebug.extended_info=1

; XDebug Remote Host/Port is where this server will try to send debug info to
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000
xdebug.idekey="netbeans-xdebug"

; XDebug These adjust the truncating limits of the var_dump data
xdebug.var_display_max_children=-1
xdebug.var_display_max_data=-1
xdebug.var_display_max_depth=-1

Now Restart Apache and use phpinfo() to ensure the XDebug is enabled

Wednesday, May 7, 2014

Updating Drupal Core using Drush

Log into your server through SSH, and change directories to your root Drupal installation directory which contains the sites folder.

cd /home/myuser/public_html/

Run the following command which will ask you to confirm you want to update the found Drupal installation, enter "y" to finish

drush pm-update


Example Output:

sh-4.1$ drush pm-update
 Name    Installed Version  Proposed version  Message
 Drupal  7.26               7.27              SECURITY UPDATE available


Update information last refreshed: Wed, 05/07/2014 - 04:32
Code updates will be made to drupal core.
WARNING:  Updating core will discard any modifications made to Drupal core files, most noteworthy among these are .htaccess and robots.txt.  If you have made any modifications to these files, please back them up before updating so that you can re-create your modifications in the updated version of the file.
Note: Updating core can potentially break your site. It is NOT recommended to update production sites without prior testing.

Do you really want to continue? (y/n): y
Project drupal was updated successfully. Installed version is now 7.27.
Backups were saved into the directory /home/myuser/drush-backups/mysite/20140507150101/drupal.                    [ok]
No database updates required                                                                                      [success]
'all' cache was cleared.                                                                                          [success]
Finished performing updates.                                                                                      [ok]

Installing Drupal's Drush

Installing Drush on CentOS using Yum and PEAR

Check if PEAR is on the server:

pear version

If PEAR is not on the server then install using yum:

yum install php-pear

Prepare PEAR

sudo pear channel-discover pear.drush.org

Install:

sudo pear install drush/drush

Friday, May 2, 2014

Configure SQL Server IPAll Static Port using C#

Note: You will need to add references for a few Microsoft.SqlServer dll's
        // using Microsoft.SqlServer.Management.Smo.Wmi;
        public static void SetStaticPort(string ServiceName = "MSSQL$SQLEXPRESS", string InstanceName = "SQLEXPRESS", int PortNumber = 2433)
        {
            try
            {
                ManagedComputer c = new ManagedComputer();
                c.ConnectionSettings.ProviderArchitecture = ProviderArchitecture.Use32bit;

                int count = 0;
                foreach (ServerInstance si in c.ServerInstances) { count++; }

                if (count == 0)
                {
                    c = new ManagedComputer();
                    c.ConnectionSettings.ProviderArchitecture = ProviderArchitecture.Use64bit;
                    foreach (ServerInstance si in c.ServerInstances) { count++; }
                }

                if (count == 0) throw new Exception("Unable to locate SQL Instances, Please contact support.");

                Service svc = c.Services[ServiceName];
                var state = svc.ServiceState;
                if (state == ServiceState.Running) svc.Stop();
                ServerInstance s = c.ServerInstances[InstanceName];

                if (null == s) throw new Exception("Unable to locate SQL Service, Please contact support.");

                ServerProtocol prot = s.ServerProtocols["Tcp"];

                foreach (ServerIPAddress ip in prot.IPAddresses)
                {
                    if (ip.Name == "IPAll")
                    {
                        ip.IPAddressProperties["TcpPort"].Value = PortNumber.ToString();
                        ip.IPAddressProperties["TcpDynamicPorts"].Value = String.Empty;
                    }
                }

                prot.Alter();

                svc.Start();
            }
            catch (Exception ex)
            {
                throw;
            }
        }