Wednesday, September 4, 2013

Handling Twitter Bootstrap Modals and UpdatePanels with PostBacks

In the process of updating a web application from an extremely rigid design to a responsive design using the Twitter Bootstrap framework. The goal is to keep the changes to the application to the HTML layout as possible. There are some situations where server side code can be removed or updated for display purposes.

This works great until you have situations which are more complicated like Bootstrap modal dialogs which need to display after a UpdatePanels and UpdatePanels which need to update in modal dialogs.

Searching the internet did not provide very fruitful results. Some involved registering script blocks, but I was not able to get them to work. This was not my preferred implementation because it deviated from my initial goal, but I was looking for solutions.

I remembered that I could execute code on an UpdatePanel PostBack and that led me to the following solution. I think there should be a better way to handle it, but it will do for a first pass. In my situation, I had a master page with a script tag using the PageRequestManager like the example below. I added the highlighted lines to call a function (RequestCallback) if it had been declared.

var prm = Sys.WebForms.PageRequestManager.getInstance();

prm.add_initializeRequest(initializeRequest);
prm.add_endRequest(endRequest);

function initializeRequest(sender, args) {
    if (prm.get_isInAsyncPostBack()) {
        args.set_cancel(true);
    }
}

function endRequest(sender, args) {
    ToggleProgressMsg('Please Wait While Your Content Loads...');
    if (RequestCallback) {
        RequestCallback(sender, args);
    }
}

Now we can declare the RequestCallback function on any page to execute when a call back occurs. This leaves us in an situation where we need to know what panel was updating or what control initiated the request. I found the following variable referenced in several places on the web.

sender._postBackSettings.panelID

I found that in several situations, this variable is not reliable. I found that the following variable was always what I expected.

sender._postBackSettings.asyncTarget

In a situation where a grid needs to populate fields on a modal dialog and display the dialog. The grid and modal dialogs were already wrapped in an UpdatePanel so I can leverage the RequestCallback function to clean up the backdrop (the modal closes, but leaves the backdrop) and then open or reopen the dialog based on the control that executed the request.

function RequestCallback(sender, args) {
    var editLink = "lnkEditCar",
        altAddr = "lnkbtnAlternativeAddress",
        trigger = sender ? sender._postBackSettings.asyncTarget : "";

    console.log("RequestCallback", sender, args, trigger, "END");
    
    // Clean up the backdrop
    $('body').removeClass('modal-open');
    $('.modal-backdrop').remove();
    
    // Handle Alternate Address request callback
    if (trigger.indexOf(altAddr, trigger.length - altAddr.length) !== -1) {
        console.log('Reopening Dialog', altAddr);
        $('#pManageCar').modal();
    }
    // Handle Edit Card request callback
    if ($('span[id$="tabpCars_tab"]').hasClass("ajax__tab_active") && trigger.indexOf(editLink , trigger.length - editLink .length) !== -1) {
        console.log("tabpCars_tab");
        $('#pManageCars').modal('show');
    } else if ($('span[id$="tabpTrucks_tab"]').hasClass("ajax__tab_active") && trigger.indexOf(up, trigger.length - up.length) !== -1) {
        console.log("tabpTrucks_tab");
        $('#pManageTrucks').modal('show');
    }
}

The result is that the modal behaves pretty much as expected. I am sure I will revisit this code, but that is how refactoring works.

Wednesday, August 28, 2013

Strip Time From Date and Last Moment in SQL

Pretty simple SQL statement that strips the time off of a date and gets the last moment of today.

SELECT 
    DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()))
  , DATEADD(ms, -100, DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()) + 1))

Tuesday, August 20, 2013

SQL Server Table Space Used

I recently found a bit of SQL that I haven't run across in a while and thought I would share. From time to time I need to know the space used by large tables. In tables that require millions of records, it can be useful. Below is the SQL script that I use to get the space used by each table and the total number of rows in the database.

DECLARE @t TABLE
(
    [name] NVARCHAR(128),
    [rows] int,
    reserved VARCHAR(18),
    data VARCHAR(18),
    index_size VARCHAR(18),
    unused VARCHAR(18)
)

INSERT @t EXEC sp_msForEachTable 'EXEC sp_spaceused ''?'''

SELECT *
FROM   @t

-- # of rows.
SELECT SUM([rows]) AS [rows]
FROM   @t

Thursday, August 8, 2013

Chocolatey Goodness and Environment Setup

I have been using Chocolatey (apt-get for Windows) - get it, use it, love it - for many months now and absolutely love the project. I think there are things that could be improved, but from a just do it approach, it works perfectly. I recently acquired a new laptop and what better way to get it setup than to create a reusable script.

First install Chocolatey from the Command Prompt. You can copy the script block below and paste it into the prompt and let it install.

@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%systemdrive%\chocolatey\bin

Now I have Chocolatey installed, I need to get a list of the applications that need to be installed. My list is pretty short compared to other bloggers on the net, but my list grows over time.

cinst git
#cinst ruby  # I uncomment this if I want ruby installed
#cinst ruby.devkit  # I uncomment this if I want ruby installed
cinst nodejs.install
cinst notepad2
cinst sublimetext2
cinst SublimeText2.PackageControl
cinst EthanBrown.SublimeText2.GitPackages
cinst PowerGUI
cinst FoxitReader
cinst ilspy
cinst linqpad4
cinst sysinternals
cinst SkyDrive
cinst paint.net
cinst 7zip
cinst virtualbox
cinst Firefox
cinst GoogleChrome
cinst fiddler4
#cinst fiddler
cinst lastpass
cinst Devbox-Clink

I also have some general setup that I like to automate which I run after the applications are installed. Eg. Mounting network drives, make the window border width smaller (it is incredibly thick, by default), and fixing an issue that prevents Netflix from playing videos.

Below is the full script

# @powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%systemdrive%\chocolatey\bin
cinst git
#cinst ruby  # I uncomment this if I want ruby installed
#cinst ruby.devkit  # I uncomment this if I want ruby installed
cinst nodejs.install
cinst notepad2
cinst sublimetext2
cinst SublimeText2.PackageControl
cinst EthanBrown.SublimeText2.GitPackages
cinst PowerGUI
cinst FoxitReader
cinst ilspy
cinst linqpad4
cinst sysinternals
cinst SkyDrive
cinst paint.net
cinst 7zip
cinst virtualbox
cinst Firefox
cinst GoogleChrome
cinst fiddler4
#cinst fiddler
cinst lastpass
cinst Devbox-Clink

cmd /C net use N: \\srv1\Common /Persistent:Yes
cmd /C net use G: \\srv1\GIT /Persistent:Yes

# Windows 8 Smaller Chrome Border
Set-ItemProperty -Path "HKCU:\Control Panel\Desktop\WindowMetrics" BorderWidth "-4"

# Fix for Netflix audio issue
Remove-Item -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Audio -Name DisableProtectedAudioDG
net stop audiosrv;
net start audiosrv;

write-host @"
Sublime Text 2 User Preferences file

{
    "color_scheme": "Packages/Theme - Flatland/Flatland Monokai.tmTheme",
    "draw_white_space": "all",
    "ensure_newline_at_eof_on_save": true,
    "flatland_square_tabs": true,
    "ignored_packages":
    [
        "Vintage"
    ],
    "rulers":
    [
        50,
        72,
        80,
        120
    ],
    "theme": "Flatland Dark.sublime-theme",
    "translate_tabs_to_spaces": true,
    "trim_trailing_white_space_on_save": true
}
"@

Thursday, July 25, 2013

Powershell Script to Periodically Monitor a Web Page, Parse, and Notify

Since Apple's developer portals have been down for a while, I got tired of checking the website. I created a script that I will hourly check Apple's System Status page, parse and see if the iOS Developer Center is back online. If it is online, it will send an email to me letting me know and remove the scheduled task. The code could be repurposed to look for the other things in the html if needed.

First the scheduled task needs to be created, it requires a PowerShell console in Administrator mode.

$dailyTrigger = New-JobTrigger -RepetitionInterval (New-TimeSpan -Hours 1) -RepetitionDuration (New-TimeSpan -Days 7) -At "11:00 AM" -Once # -At 
$option = New-ScheduledJobOption -StartIfOnBattery –StartIfIdle
Register-ScheduledJob -Name PingAppleSystemStatus -ScriptBlock `
{ powershell -command C:\\PingAppleSystemStatus.ps1 } -Trigger $dailyTrigger -ScheduledJobOption $option
The code below requires a little setup.
  • Set the SMTP Server
  • Change the 2 instances of someone@mail.com
function Send-Email([string] $From, $To, [string] $Subject, [string] $Body, $AttachmentPaths = $null, [bool] $IsImportant = $FALSE) {
 $msg = new-object Net.Mail.MailMessage;
 $smtp = new-object Net.Mail.SmtpClient("yoursmtpserver.com");
 $atts = @();
 $msg.From = $From;
 $To | % { $msg.To.Add($_); }
 $msg.Subject = $Subject;
 $msg.Body = $Body;
 if ($AttachmentPaths -ne $null) {
  $AttachmentPaths | % {
   $att = new-object Net.Mail.Attachment($_);
   $msg.Attachments.Add($att);
   $atts += $att;
  }
 }
 if ($IsImportant) {
  $msg.Priority = [System.Net.Mail.MailPriority] "High";
 }
 $smtp.Send($msg);

 $atts | % { $_.Dispose(); }
 $msg.Dispose();
}

$url = 'https://developer.apple.com/support/system-status/';
$result = Invoke-WebRequest $url;
$foundElement = $result.AllElements | 
    Where Class -eq "online" | 
    Where innerText -eq "iOS Dev Center" | 
    Select -ExpandProperty innerText;

if ($foundElement) {
 Write-Host "$([DateTime]::Now) - iOS Dev Center LIVE";
 Send-Email "Me <someone@mail.com>" @("<someone@mail.com>") "$([DateTime]::Now) - iOS Dev Center LIVE" "$([DateTime]::Now) - iOS Dev Center LIVE" $null $TRUE;
 Unregister-ScheduledJob  -Name PingAppleSystemStatus
} else {
 Write-Host "$([DateTime]::Now) - iOS Dev Center Offline";
}

Saturday, July 13, 2013

NodeJS Sublime Text Auto Start Debugger On Build

A quick awesome note. When developing in NodeJs, it is rather nice to have the nodejs automatically start again. This simple little change to helped with debugging in Sublime Text.

Edit the following file:
C:\Users\Brock\AppData\Roaming\Sublime Text 2\Packages\Nodejs\Nodejs.sublime-build
I'll leave it to the read to find it on linux.

Merge the following code into the file:
{
"cmd": ["node --debug", "$file"],
"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
"selector": "source.js",
"shell":true,
"encoding": "cp1252",
"windows":
{
"cmd": ["taskkill /F /IM node.exe & node --debug", "$file"]
},
"linux":
{
"cmd": ["killall node; node", "$file"]
}
}
Now every time you build, it will restart the node process with debugging enabled.