Sunday, 21 June 2015

Compile LESS with Gulp and Task Runner

A simple GULP compiling and minifying LESS files to CSS using Visual Studio Task Runner Explorer.

This example watches a folder containing LESS files and compiles them to CSS on any changes or when the project is built or cleaned.

By doing this you can have a primary LESS file, and when this or any referenced by it through “@import” statements, compiling to a single CSS file.



In this example, the primary LESS file is called “main.less”, it will be compiled to “main.css” and “main.min.css”.

/// 
var gulp = require('gulp');
var concat = require('gulp-concat');

var less = require('gulp-less');
var minifyCss = require('gulp-minify-css');
var rename = require("gulp-rename");
var watch = require('gulp-watch');
var del = require('delete');

var sass = require('gulp-ruby-sass')
var notify = require("gulp-notify")
var bower = require('gulp-bower');



var MainConfig = {
    watch: ['main/**/*.less'],          //folder to watch for changes
    src: ['main/main.less'],            //main LESS file to compile 
    targetDir: '../target-folder/css',  //folder to output compiled CSS
    targetName: 'main',                 //name of compiled CSS file
    targetMinName: 'main.min'           //name of minified CSS file
}


function BuildMain() {
    gulp.src(MainConfig.src) 
        .pipe(less())
        .on('error', swallowError)
        .pipe(gulp.dest(MainConfig.targetDir))
        .pipe(minifyCss())
        .on('error', swallowError)
        .pipe(rename({ basename: MainConfig.targetMinName, extname: ".css" }))
        .pipe(gulp.dest(MainConfig.targetDir));


    //If theres an error generating the css, delete the existing css files
    function swallowError(error) {
        var _cssName = MainConfig.targetDir + "/" + MainConfig.targetName + ".css";
        var _cssMinName = MainConfig.targetDir + "/" + MainConfig.targetMinName + ".css";

        del.sync(_cssName, { force: true });
        del.sync(_cssMinName, { force: true });

        this.emit('end');
    }
}

//Tasks to be used by Task Runner
gulp.task('project-start', function () {
    watch(MainConfig.watch, function () {
        BuildMain();
    });
});
gulp.task('project-clean', function () {
    BuildMain();
});
gulp.task('project-build', function () {
    BuildMain();
});


The script is saved as “gulpfile.js” in the root of the project

Tuesday, 7 October 2014

Bat SQL Script to restore Database

When developing a site I quite often have to restore SQL databases and this can take up a lot of time.

If you have to frequently restore SQL backups you can use this script:


@echo off

set BAKFILE=%cd%\[database-backup].bak
set DBNAME=[database-name]
SET SERVERINSTANCE=[.\SQLEXPRESS2008R2]
SET DBUSER=[DatabaseUser]

set /p RESPONSE="Restore %BAKFILE% to %DBNAME%? (Y/N)"

if NOT %RESPONSE%==y goto END

::clear any existing connections
sqlcmd -E -S %SERVERINSTANCE% -Q "ALTER DATABASE %DBNAME% SET SINGLE_USER WITH ROLLBACK IMMEDIATE"

::restore DB
set SQLCMD=-E -S %SERVERINSTANCE% -Q "RESTORE DATABASE %DBNAME% FROM DISK='%BAKFILE%'"
sqlcmd %SQLCMD%

::remove user if it already has access to the DB
sqlcmd -E -S %SERVERINSTANCE% -Q "USE %DBNAME%; EXEC sp_revokedbaccess '%DBUSER%'"

::add user to the DB and grant required access
sqlcmd -E -S %SERVERINSTANCE% -Q "USE %DBNAME%; EXEC sp_grantdbaccess '%DBUSER%', '%DBUSER%'"
sqlcmd -E -S %SERVERINSTANCE% -Q "USE %DBNAME%; EXEC sp_addrolemember 'db_owner', '%DBUSER%'"
sqlcmd -E -S %SERVERINSTANCE% -Q "USE %DBNAME%; EXEC sp_addrolemember 'db_datareader', '%DBUSER%'"
sqlcmd -E -S %SERVERINSTANCE% -Q "USE %DBNAME%; EXEC sp_addrolemember 'db_datawriter', '%DBUSER%'"

pause

:END



Update the following variables to match our needs:
  • BAKFILE – the name of the file to restore
  • DBNAME – the name of the database to overwrite
  • SERVERINSTANCE – name of the SQL server instance
  • DBUSER – Existing database user to grant access to 

Only change the parts in square brackets.

Save it as a .bat file in the same directory as the .bak file you want to restore.

When you run it you will be prompted to continue, then it will clear connections to the existing database, restore the backup and grant the required user read/write access.

Be careful if you use and make sure you keep backups, it might make things a little quicker but it's also easier to make mistake.

Thursday, 28 August 2014

Umbraco 7 - Highlighting selected items in custom tree

Custom Tree elements are fairly straight forward using the “umb-tree” directive.

If you want to highlight selected nodes in a custom tree the same way as they are in the main content tree, you’ll have to use the “treeNodeSelect” event in your controller.

Custom Tree Directive




Controller

function DialogControllerFunction($scope, dialogService, eventsService, navigationService, appState, treeService) {
    var dialogOptions = $scope.dialogOptions;
    var node = dialogOptions.currentNode;

    $scope.dialogTreeEventHandler = $({});


    $scope.dialogTreeEventHandler.bind("treeNodeSelect", function (ev, args) {
        args.event.preventDefault();
        args.event.stopPropagation();

        var _selectedNode = args.node;
        var _treeRootElement = $(args.event.currentTarget).closest(".umb-tree");

        //Clear previously selected items (remove if you want to select multiple)
        //  iterate through each child element in the current tree
        _treeRootElement.find("li[tree='child']").each(function () {
            $(this).scope().currentNode = null;
        })

        //this will highlight the current node
        args.element.currentNode = _selectedNode;
    });

}
angular.module("umbraco").controller("CustomApp.Namespace.DialogController", DialogControllerFunction);

In this example, my custom tree is a navigation dialog:



Friday, 8 August 2014

Add new Document Type icons to Umbraco 7

The process for adding new document type icons is a little more complicated in Umbraco 7.

Document type icons are now displayed as fonts (svg/woff/eot files), the icon sets are created using Icomoon.io.

To add new icons you have to create a new set, these can then be included in Umbraco by created a new plugin.

Step 1 - Create new icon set


Go to https://icomoon.io/app, here you can load in icons from other available sets or create your own.

Select any icons you want to include in your new set.



Next, click on the "Font" button at the bottom.

In the next screen:


You'll see a list of the selected icons, take note of the values below each icon, these "code points" are used in CSS to locate each icon in the final file.

Click download and unzip.


Step 2 - Add to Umbraco


To include the new icons in Umbraoo, you have to create a new plugin to load in CSS which references the font files.

Create a new folder in "App_Plugins" for your plugin, in here create a new "package.manifest" and CSS file, next copy in the downloaded font files.

My package is called "IcoElegant", here's my layout:



Package Manifest
{
    css: [
        '~/App_Plugins/NewIcons/css/icoelegant.css'
 ]
}


Change this to reference your CSS file.

CSS
@font-face {
    font-family: 'icoelegant';
    src:url('../Fonts/icoelegant.eot');
    src:url('../Fonts/icoelegant.eot?#iefix') format('embedded-opentype'),
    url('../Fonts/icoelegant.svg') format('svg'),
    url('../Fonts/icoelegant.woff') format('woff'),
    url('../Fonts/icoelegant.ttf') format('truetype');
}


[class^="icon-icoelegant-"], [class*=" icon-icoelegant-"] {font-family:'icoelegant';width:auto;height:auto;}
.icon-icoelegant-mobile:before{content:'\e000'}
.icon-icoelegant-laptop:before{content:'\e001'}
.icon-icoelegant-desktop:before{content:'\e002'}
.icon-icoelegant-tablet:before{content:'\e003'}
.icon-icoelegant-phone:before{content:'\e004'}
.icon-icoelegant-document:before{content:'\e005'}
.icon-icoelegant-documents:before{content:'\e006'}
.icon-icoelegant-search:before{content:'\e007'}
.icon-icoelegant-clipboard:before{content:'\e008'}
.icon-icoelegant-newspaper:before{content:'\e009'}
.icon-icoelegant-notebook:before{content:'\e00a'}
.icon-icoelegant-book-open:before{content:'\e00b'}
.icon-icoelegant-browser:before{content:'\e00c'}
.icon-icoelegant-calendar:before{content:'\e00d'}
.icon-icoelegant-presentation:before{content:'\e00e'}


Change the font-family name and src attributes to match your font files.

Each icon needs a prefix (starting with ".icon"), you'll also have to swap the content value for the "code point" value when exporting from Icomoon.

Save the files and refresh Umbraco, you may have to clear the ClientDependency cache "\App_Data\TEMP\ClientDependency" first, the new icons should now be available in the Document Type section of Umbraco.

Monday, 2 June 2014

Programmatically add new member to a group in Umbraco 7

If you want to assign a new member to an Umbraco member group in .net, you have to use the "MemberService.AssignRole" method.


IMember _newMember = ApplicationContext.Current.Services.MemberService.CreateMember("username", "user@email.com", "Full Name", "Member");
Services.MemberService.Save(_newMember);
Services.MemberService.AssignRole(_newMember.Id, "Group Name");


The new member has to be saved first before you can assign it to role/group.

Monday, 12 May 2014

Regular Expression Command Line Arguments Parser



Here's a simple way to extract a list of switch/flags and their parameters from a command line argument in C#.

Regex regex = new Regex(@"(?< switch> -{1,2}\S*)(?:[=:]?|\s+)(?< value> [^-\s].*?)?(?=\s+[-\/]|$)");

string commands = "--switch value1 --switch2 \"c:\\folder 1\\file1.txt\" -switch-3 value-3 --switch4 -switch5";

List< KeyValuePair< string, string> >  matches = (from match in regex.Matches(commands).Cast< match> ()

select new KeyValuePair< string, string> (match.Groups["switch"].Value, match.Groups["value"].Value)).ToList();

foreach (KeyValuePair< string, string>  _match in matches)
{
Response.Write("
switch:" + _match.Key + "  value:" + _match.Value);
}

You can pass in a command with multiple switches such as "-switch" or "--switch":

--switch1 value1 --switch2 "c:\folder 2\file2.txt" -switch-3 value-3 --switch4 -switch5

 the regex will extract the flags along with any optional values and return a list of key-value pairs.

I had looked at a couple of solutions such as http://www.codeproject.com/Articles/3111/C-NET-Command-Line-Arguments-Parser and http://lukasz-lademann.blogspot.co.uk/2013/01/c-command-line-arguments-parser.html but I think this way is a bit simpler and returns better results.

Also, this way allows optional parameters which can contain file paths, you can test here http://regexhero.net/tester/?id=2f4e252d-c89e-4134-8c7c-65c5186b3338

Thursday, 19 July 2012

Umbraco Examine - Indexing Custom XML Data

I recently ran into a problem with an Umbraco Examine indexer suddenly stopping after trying to index the first node in the content tree.

The only error information I could get was:

Exception information:
Exception type: NullReferenceException
Exception message: Object reference not set to an instance of an object.
at UmbracoExamine.UmbracoContentIndexer.OnGatheringNodeData(IndexingNodeDataEventArgs e)
at Examine.LuceneEngine.Providers.LuceneIndexer.GetDataToIndex(XElement node, String type)




I tried using NodeIndexing & GatheringNodeData handlers to find out more but all I could see was the Examine was trying to index the root node (100 in this case), then looking for Node 1 (Which didn't exist).

Eventually I found out it was due a custom data type (TheFarm - EmbeddedContent) saving content as XML.

The data was being stored as:


    
      
        Node 1 content
        Node 2 content
      
    


The problem seems to be with using "id" as an attribute as it is used in umbraco.config:

<NodeType id="100" parentID="99" level="4" ...



It appears the indexer assumes it is parsing a new node when it reads this.

There are a few options to resolve this problem:
  1. Wrap custom XML data in <![CDATA[
  2. Try using a NodeIndexing handler on the BaseIndexProvider to deal with the XML.
  3. Avoid using id as an attribute in custom XML data.
Option 3 seemed the easiest for me, I updated the  EmbeddedContent source code to use another id attribute.