Showing posts with label Extensions. Show all posts
Showing posts with label Extensions. Show all posts

Friday, May 29, 2015

Monday, October 1, 2012

Automatic Unobtrusive Binding of Labels to Form Elements

One commonly forgotten feaure in HTML is that when creating web forms the labels related to input elements should be related to them.  In the simplest terms it is done like so:

<label for="myItem">My Label</label><input id="myItem">
The benifit to doing this is that browser will automatically focus the input element when the label is selected.  A little thing I know but it's not use in most sites doe to inconvenience.

Thus I have the following bit of javascript to automatically do the same thing.  It looks for any unbound  labels and binds them to the next input, text area, or select item down the DOM tree from them.  That is the next matching element within the same parent that the label is in.  This method will also work if the input fields don't have ID's associated with them as an added bonus.

 To use just include this in a global script running on your site and it will do the work with no noticeable overhead.  This does require jQuery to work.
$(document).ready(function () {
  $("form label").each(function() {
    if (typeof $(this).attr("for") == "undefined" || $("#" + $(this).attr("for")).length == 0) {
      nextControl = $(this).next("input,textarea,select");
      if (nextControl.length > 0) {
        if (typeof nextControl.attr("id") != "undefined") {
          $(this).attr("for", nextControl.attr("id"));
        } else {
          $(this).removeAttr("for");
          $(this).click(function() {
            $(this).next("input,textarea,select").focus()
          });
        }
      }
    }
  });
});

Monday, September 24, 2012

LINQ OrderBy sorting without a Strong Typed parameter expression.

When setting up a simple ASPX page with a DataGrid to quickly display a LINQ to SQL result without having to write a bunch of paging and sorting code I ran into a bit of an issue. The loading and paging worked perfectly with the following sample but I could not get sorting to work and a clean and concise manner.

<asp:DataGrid ID="Data" runat="server" AllowPaging="True" AllowSorting="True" 
    onpageindexchanged="Data_PageIndexChanged" onsortcommand="Data_SortCommand">
</asp:DataGrid>
FormLandingPageRepository repo = new FormLandingPageRepository();

protected void Page_Load(object sender, EventArgs e)
{
    Data.DataSource = repo.Find().OrderByDescending(i => i.dateAdded);
    Data.DataBind();
}

protected void Data_PageIndexChanged(object source, DataGridPageChangedEventArgs e)
{
    Data.CurrentPageIndex = e.NewPageIndex;
    
    Data.DataSource = repo.Find().OrderByDescending(i => i.dateAdded);
    Data.DataBind();
}

protected void Data_SortCommand(object source, DataGridSortCommandEventArgs e)
{     
    repo.Find().OrderBy(e.SortExpression);
    Data.DataBind();
}

The problem turned out to be that the OrderBy operations in LINQ require strong types clauses in Expressions but my DataGrid is returning a simple string as the order by expression.  I could get around this by creating a switch statment and converting to a Strong Typed expression but that would require a lot of hard coded logic for every table.

Instead I found this Extensions after much searching and testing that adds the ability to do OrderBy statements in LINQ with a string value instead.  Thus after adding the Extension code the above sort by works as exacted. (Note that the sort expression will need to be stored in the ViewState and sorting logic applied in PageIndexChanged as well for Paging+Sorting to work.)

SortExstensions.cs
using System;
using System.Text.RegularExpressions;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace JSP
{
    public static class SortExtensions
    {
        /// <summary>
        /// Sort Directions.
        /// </summary>
        enum SortMode
        {
            OrderBy,
            OrderByDescending,
            ThenBy,
            ThenByDescending
        }

        /// <summary>
        /// Sorts the elements of a sequence in ascending order according to a key.
        /// </summary>
        /// <typeparam name="T">The type of the elements of source.</typeparam>
        /// <param name="source">A sequence of values to order.</param>
        /// <param name="property">Property name to sort on.</param>
        /// <returns>An System.Linq.IOrderedQueryable<T> whose elements are sorted according to a key.</returns>
        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
        {
            // If this is a sort list then OrderBy the first element and then ThenBy the rest.
            if (property.Contains(','))
            {
                string[] parts = property.Split(new char[] {','}, 2);
                return ApplyOrder<T>(source, parts[0], SortMode.OrderBy).ThenBy(parts[1]);
            }
                
            return ApplyOrder<T>(source, property, SortMode.OrderBy);
        }

        /// <summary>
        /// Sorts the elements of a sequence in descending order according to a key.
        /// </summary>
        /// <typeparam name="T">The type of the elements of source.</typeparam>
        /// <param name="source">A sequence of values to order.</param>
        /// <param name="property">Property name to sort on.</param>
        /// <returns>An System.Linq.IOrderedQueryable<T> whose elements are sorted according to a key.</returns>
        public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
        {
            // If this is a sort list then OrderBy the first element and then ThenBy the rest.
            if (property.Contains(','))
            {
                string[] parts = property.Split(new char[] { ',' }, 2);
                return ApplyOrder<T>(source, parts[0], SortMode.OrderByDescending).ThenByDescending(parts[1]);
            }

            return ApplyOrder<T>(source, property, SortMode.OrderByDescending);
        }

        /// <summary>
        ///  Performs a subsequent ordering of the elements in a sequence in ascending order according to a key.
        /// </summary>
        /// <typeparam name="T">The type of the elements of source.</typeparam>
        /// <param name="source">A sequence of values to order.</param>
        /// <param name="property">Property name to sort on.</param>
        /// <returns>An System.Linq.IOrderedQueryable<T> whose elements are sorted according to a key.</returns>
        public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
        {
            // If ther are multiple sort items then sort by each in order.
            if (property.Contains(','))
            {
                string[] parts = property.Split(new char[] { ',' }, 2);
                return ApplyOrder<T>(source, parts[0], SortMode.ThenBy).ThenBy(parts[1]);
            }

            return ApplyOrder<T>(source, property, SortMode.ThenBy);
        }

        /// <summary>
        ///  Performs a subsequent ordering of the elements in a sequence in descending order, according to a key.
        /// </summary>
        /// <typeparam name="T">The type of the elements of source.</typeparam>
        /// <param name="source">A sequence of values to order.</param>
        /// <param name="property">Property name to sort on.</param>
        /// <returns>An System.Linq.IOrderedQueryable<T> whose elements are sorted according to a key.</returns>
        public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
        {
            // If ther are multiple sort items then sort by each in order.
            if (property.Contains(','))
            {
                string[] parts = property.Split(new char[] { ',' }, 2);
                return ApplyOrder<T>(source, parts[0], SortMode.ThenByDescending).ThenByDescending(parts[1]);
            }

            return ApplyOrder<T>(source, property, SortMode.ThenByDescending);
        }

        /// <summary>
        /// Apply a custom Order By statment to an IQueryable.
        /// </summary>
        /// <typeparam name="T">The type of the elements of source.</typeparam>
        /// <param name="source">A sequence of values to order.</param>
        /// <param name="property">Property name to sort on.</param>
        /// <param name="methodName">Sort method to use.</param>
        /// <returns>An System.Linq.IOrderedQueryable<T> whose elements are sorted according to a key.</returns>
        static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, SortMode methodName)
        {
            string[] props = property.Split('.');

            Type type = typeof(T);
            ParameterExpression arg = Expression.Parameter(type, "x");
            Expression expr = arg;

            foreach (string prop in props)
            {
                PropertyInfo pi = type.GetProperty(prop);
                expr = Expression.Property(expr, pi);
                type = pi.PropertyType;
            }

            Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
            LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);

            object result = typeof(Queryable).GetMethods().Single(
                    method => method.Name == methodName.ToString("g")
                            && method.IsGenericMethodDefinition
                            && method.GetGenericArguments().Length == 2
                            && method.GetParameters().Length == 2)
                    .MakeGenericMethod(typeof(T), type)
                    .Invoke(null, new object[] { source, lambda });
            return (IOrderedQueryable<T>)result;
        }
    }
}

Wednesday, April 13, 2011

Adding Better Ajax/JSON methods to HighCharts

I recently started using Highcharts as the charting options in Crystal Reports are rather limited. Here is a small patch I made to simplify the loading of chart data from JSON server calls via AJAX. This does not do anything incredibly unique, just simplifies the code to do so by adding an ajaxSource property to the chart and series configurations that is loaded on init and a reloadAjax method to automatically reload the data.

05/03/2011 - Rewrote the code as a extension instead of a patch and added support for setting multiple series via options.chart.ajaxSource.

Tuesday, February 9, 2010

Switch Statment for Smarty 3

Here is the updated {switch} statement for Smarty 3. The new version is NOT backwards compatible but the Smarty 2 version is still maintained here.

11/23/2010 - Updated to version 3.5
Updated to work with Smarty 3.0 release. (Tested on 3.0.5).
I removed the code from this posting, it is now available on github here: https://github.com/pynej/Smarty-Switch-Statement/archives/Smarty-3.0

10/28/2010 - Updated to version 3.3
I have added this project to GitHub at http://github.com/pynej/Smarty-Switch-Statement.

02/25/2010 - Updated to version 3.3
Please note that this update is required for version 3.0b6 or grater. The change is simple renaming the execute methods to compile but it not backwards compatible. The Smarty3.0b5 and below version is still available here.

02/09/2010 - Updated to version 3.2
Fixed a bug when chaining case statements without a break.

02/09/2010 - Updated to version 3.1
Updated the plug-in to once again support the shorthand format, {switch $myvar}. To enable this feature you must add the following line to you code somewhere before the template is executed.
$smarty->loadPlugin('smarty_compiler_switch');
If you do not add said line the long hand form will still work correctly.

sample.php
/**
* Sample usage:
* <code>
* {foreach item=$debugItem from=$debugData}
*  // Switch on $debugItem.type
*    {switch $debugItem.type}
*       {case 1}
*       {case "invalid_field"}
*          // Case checks for string and numbers.
*       {/case}
*       {case $postError}
*       {case $getError|cat:"_ajax"|lower}
*          // Case checks can also use variables and modifiers.
*          {break}
*       {default}
*          // Default case is supported.
*    {/switch}
* {/foreach}
* </code>
*
* Note in the above example that the break statements work exactly as expected.  Also the switch and default
*    tags can take the break attribute. If set they will break automatically before the next case is printed.
*
* Both blocks produce the same switch logic:
* <code>
*    {case 1 break}
*       Code 1
*    {case 2}
*       Code 2
*    {default break}
*       Code 3
* </code>
*
* <code>
*    {case 1}
*     Code 1
*       {break}
*    {case 2}
*       Code 2
*    {default}
*       Code 3
*       {break}
* </code>
*
* Finally, there is an alternate long hand style for the switch statements that you may need to use in some cases.
*
* <code>
* {switch var=$type}
*    {case value="box" break=true}
*    {case value="line"}
*       {break}
*    {default}
* {/switch}
* </code>
*/

Wednesday, October 22, 2008

Google Chrome theme for Firefox



This is how my Firefox looks. Note that this isn't maximized or trimmed, there just isn't a title of menu bar. The menu is accessible via a drop down on the top right as well as window controls.

Here is a list of the addon's and tweaks I used to get this look:
  • Chromifox: This is the primary theme used.
  • Stylish: For minor CSS tweaks.
  • Autohide: This extension allows you to go into Zombie mode and hide the titlebar.
  • Compact Menu 2: This extension allows you to hide the main menu and add a toolbar icon instead.
  • FaviconizeTab: Lets you minimize tabs to just show their icon.
Extra settings:
  • When using Autohide you will need to open up about:config and set the setting extensions.autohide.zombieBorder to 0. This will remove the titlebar and border when you go into zombie mode. (Shift-F11)

Monday, October 20, 2008

My Firefox Extentions

Here is how I see the world.

(Picture coming soon)

Ubiquity Command: Open in New Tab

Here is a Ubiquity script to open a link in a new tab.

open_in_new_tab.js
CmdUtils.CreateCommand({
name: "open_in_new_tab",
homepage: "http://pynej.blogspot.com/",
author: { name: "Jeremy Pyne", email: "jeremy.pyne@gmail.com"},
description: "Open the selected url in a new tab.",
takes: {"URL": noun_type_url},
preview: function( pblock, url ) {
pblock.innerHTML = "Will open: " + url.text;
},
execute: function( url ) {
Utils.openUrlInBrowser(url.text);
}
})

Ubiquity Command: Note in Reader

Update: Monday, August 18, 2009 9:54 am
Updated script to work with newer framework.

Update: Monday, October 20, 2008 2:52 pm
Updated the script to actually work. Also you can now enter your note on the command line itself.



Here is a Ubiquity script to note a page in Google Reader.
note-in-reader.js
CmdUtils.CreateCommand({
name: ["note-in-reader", "add-to-reader", "share-to-reader"],
homepage: "http://pynej.blogspot.com/",
author: { name: "Jeremy Pyne", email: "jeremy.pyne@gmail.com"},
description: "Note this page in Google Reader.",
arguments: [ {role: 'object', nountype: noun_arb_text, label: 'note'} ],
execute: function( note ) {
var document = Application.activeWindow.activeTab.document;
var gc = "var b=document.body;var GR________bookmarklet_domain='https://www.google.com';if(b&&!document.xmlVersion){void(z=document.createElement('script'));void(z.src='https://www.google.com/reader/ui/link-bookmarklet.js');void(b.appendChild(z));}else{}";

void(z=document.createElement('script'));
z.appendChild(document.createTextNode(gc));
document.body.appendChild(z);

if(note.text)
{
var timer = Components.classes["@mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
timer.initWithCallback(
function() {
document.getElementById("GR________link_bookmarklet_frame").contentDocument.getElementById("annotation").innerHTML= note.text;
},
1500,
Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}
}
})

Monday, October 6, 2008

Auto Mount/Unmout Disk Images

Here are two scripts to automatically mount and unmout image files in Linux.

Console Script:
/usr/local/bin/isomnt
#!/bin/bash
if [ -e "$*" ]; then
if [ -d /media/"$*" ]; then
echo "Unmounting '$*'..."
sudo umount -d /media/"$*"
sudo rmdir /media/"$*"
else
echo "Mounting '$*'..."
sudo mkdir /media/"$*"
sudo mount -o loop "$*" /media/"$*"
fi
else
echo "No image found to mount."
fi

Nautilus Script:
~/.gnome2/nautilus-scripts/(Un)Mount ISO
#!/bin/bash
if [ -d /media/"$*" ]; then
gksudo ls /media
sudo umount -d /media/"$*"
sudo rmdir /media/"$*"
else
gksudo mkdir /media/"$*"
wd=${NAUTILUS_SCRIPT_CURRENT_URI#file://}
sudo mount -o loop "$wd/$*" /media/"$*"
fi

MediaWiki Blacklist Extension

This is an older extension I wrote for MediaWiki to allow the blacklisting of various pages.

To install the extention just ave the code below to blacklist.php in MediaWiki's extensions folder and add the following line in the LocalSettings.php file:
include_once("extensions/blacklist.php");

Example: To block some special pages for normal users, but not sysops, do this.
$wgWhitelist['sysop']['read']  = $wgBlacklist['*']['read'] = array("Special:Export", "Special:Listusers", "Special:Ipblocklist", "Special:Log", "Special:Allmessages");

Or with a RegEx:
$wgBlacklistOps["useRegex"] = true;
$wgWhitelist['sysop']['read'] = $wgBlacklist['*']['read'] = array("^Special:(Export|Listusers|Ipblocklist|Log|Allmessages)$");

blacklist.php
<?php
/*
Blacklist Mediawiki Extension
By Jeremy Pyne jeremy.pyne@gmail.com

This extension adds support for a $wgBlacklist array, layed out like $wgGroupPermissions, to support overrides.
For example I can set $wgBlacklist['*']['read'] to diable specific special pages or
make some pages of the site only visible for special groups.
This blacklisting is done from lowest to highest powered groups and is implisit. IE if you deny Main Page to User, it also denies it for all parent's of user.
To override a blacklist at a higher level ou have to add an entry to $$wgWhitelist['sysop']['read'] to re-enable the pages if you are a sysop.

Options:
$wgBlacklistOps["useRegex"] = true;
This setting dictates whether to tread the page lists as regular expressions or not. Though turning regular expressions off is much faster, you can not
mark page groups, partial page titles, or variations of title formating.

Example: To block some special pages for normal users, but not sysops do this.
$wgWhitelist['sysop']['read'] = $wgBlacklist['*']['read'] = array("Special:Export", "Special:Listusers", "Special:Ipblocklist", "Special:Log", "Special:Allmessages");
Or wth a RegEx
$wgBlacklistOps["useRegex"] = true;
$wgWhitelist['sysop']['read'] = $wgBlacklist['*']['read'] = array("^Special:(Export|Listusers|Ipblocklist|Log|Allmessages)$");

Note: This is not flawless method as page inclusions and such can get around this.
*/

if (!defined('MEDIAWIKI')) die();

$wgExtensionCredits['other'][] = array(
'name' => 'blacklist',
'description' => 'adds $wgBlacklist array to provide blacklist overrides',
'url' => 'http://www.mediawiki.org/wiki/Extension:Blacklist',
'author' => 'Jeremy Pyne',
'version' => '1.0'
);

$wgHooks['userCan'][] = 'checkBlacklist';

/**
* Is this page blacklisted
* @param &$title the concerned page
* @param &$wgUser the current mediawiki user
* @param $action the action performed
* @param &$result (out) true or false, or null if we don't care about the parameters
*/
function checkBlacklist(&$title, &$wgUser, $action, &$result) {
global $wgBlacklist;
global $wgWhitelist;
global $wgBlacklistOps;
$hideMe = false;

$groupPower = array(
0 => "*",
1 => "user",
2 => "autoconfirmed",
3 => "emailconfirmed",
4 => "bot",
5 => "sysop",
6 => "bureaucrat");
$myGroups = array_intersect($groupPower, $wgUser->getEffectiveGroups());

foreach($myGroups as $myGroup) {
if(array_key_exists($myGroup, $wgBlacklist) && array_key_exists($action, $wgBlacklist[$myGroup]) && is_array($wgBlacklist[$myGroup][$action]))
{
if($wgBlacklistOps["useRegex"]) {
foreach($wgBlacklist[$myGroup][$action] as $myBlacklist)
if(preg_match("/$myBlacklist/", $title->getPrefixedText()))
{
$hideMe = true;
break;
}
} else {
$myBlacklist = array_flip($wgBlacklist[$myGroup][$action]);
if(array_key_exists($title->getPrefixedText(), $myBlacklist))
$hideMe = true;
}
}

if(array_key_exists($myGroup, $wgWhitelist) && array_key_exists($action, $wgWhitelist[$myGroup]) && is_array($wgWhitelist[$myGroup][$action]))
{
if($wgBlacklistOps["useRegex"]) {
foreach($wgWhitelist[$myGroup][$action] as $myWhitelist)
if(preg_match("/$myWhitelist/", $title->getPrefixedText()))
{
$hideMe = false;
break;
}
} else {
$myWhitelist = array_flip($wgWhitelist[$myGroup][$action]);
if(array_key_exists($title->getPrefixedText(), $myWhitelist))
$hideMe = false;
}
}
}

if($hideMe)
$result = false;

return !$hideMe;
}

?>

Switch Statment for Smarty

There is a complete and working switch plugin. It works exactly as a php switch and you can even use variables and modifiers for the conditions. Feel free to use it and let me know if you have and problems.

11/23/2010
I moved the code to github for matinance. https://github.com/pynej/Smarty-Switch-Statement/archives/Smarty-2.0

03/08/2008 - Updated to version 2
This update changes the break attribute to work how you would expect. That is, setting the break attribute will cause smarty to automatically render a {break} tag before it starts the NEXT case or default section. I strongly recommend updating to this version of the switch plugin, as the older version was very counter intuitive in this aspect.

02/09/2010 - Posted Smarty 3 Version
A separate version of this plug-in is available for Smarty 3 here.

(Also, this does work with nested switches, witch is why you see it accessing the _switchData variable. Be sure to not alter this variable in smarty code, as it will make the switches behave incorrectly.)

Both of these blocks will produce identical switch logic.
{case 1 break}
Code 1
{case 2}
Code 2
{default break}
Code 3

{case 1}
Code 1
{break}
{case 2}
Code 2
{default}
Code 3
{break}


03/08/2008 - Updated to version 2.1
Added {/case} tag, this is identical to {break}.

MediaWiki Tidy URLs

This article describes how to configure nice URLs on MediaWiki. The wiki page on the topic is rather massive and hard to understand so here are the instructions for Apache2.
Before:
http://my.domain.com/wiki/index.php?title=Main_Page&action=view

After:
http://my.domain.com/wiki/Main_Page/view


Add the following likes at the end of LocalSettings.php. Update $wgScriptPath to reflect you install.
LocalSettings.php
$wgScriptPath = "/wiki";

$wgArticlePath = "$wgScriptPath/$1";

$actions = array('view', 'edit', 'watch', 'unwatch', 'delete','revert', 'rollback', 'protect',
'unprotect','info','markpatrolled','validate','render','deletetrackback','print',
'dublincore','creativecommons','credits','submit','viewsource','history','purge');

foreach ($actions as $a)
$wgActionPaths[$a] = "$wgScriptPath/$1/$a";


Enable the loading of the mod_rewrite.c module.
LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so


Add the following to your httpd.conf or virtual site. Again, replace /wiki as needed.
RewriteCond %{REQUEST_URI} /(view|edit|watch|unwatch|delete|revert|rollback|protect|unprotect|info|markpatrolled|validate|render|deletetrackback|print$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/wiki/(.*)/([a-z]*)$ /wiki/index.php?title=$1&action=$2 [L,QSA]

RewriteCond %{REQUEST_URI} !/wiki/images
RewriteCond %{REQUEST_URI} !/wiki/skins
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/wiki/(.*)$ /wiki/index.php?title=$1 [PT,L,QSA]