One More Take https://www.onemoretake.com Thu, 17 Mar 2016 00:21:38 +0000 en-US hourly 1 https://wordpress.org/?v=5.2.5 Importing Stripe Transactions Into Xero https://www.onemoretake.com/2016/03/16/importing-stripe-transactions-into-xero/ https://www.onemoretake.com/2016/03/16/importing-stripe-transactions-into-xero/#respond Thu, 17 Mar 2016 00:21:06 +0000 http://www.onemoretake.com/?p=472 … ]]> If you happen to be using Xero for your company accounts and Stripe for your transactions, you will probably have discovered that reconciling the bank records against the transactions in Stripe is not the easiest thing to. It means exporting from Stripe and messing around in Excel before importing into Xero.

This was way too much hassle for me, so I have thrown together a NodeJS script to do the boring work for me and I have made it available on Github:

https://github.com/danspam/stripe-xero

Instructions are in the readme and there is a little bit of manual work each time you run it to change the ‘since’ date but that is good enough for me. If anyone out there has the time and inclination to improve upon it and make it even more automatic, then I am very happy to take pull requests.

Enjoy!

]]>
https://www.onemoretake.com/2016/03/16/importing-stripe-transactions-into-xero/feed/ 0
Redirect Sub-sub Domains on Multi-Tenanted Websites https://www.onemoretake.com/2014/04/30/redirect-sub-sub-domains-on-multi-tenanted-websites/ https://www.onemoretake.com/2014/04/30/redirect-sub-sub-domains-on-multi-tenanted-websites/#respond Wed, 30 Apr 2014 23:03:44 +0000 http://www.onemoretake.com/?p=448 … ]]> I run the SOP creation and implementation website Way We Do. It is a multi-tenanted website in as far as each company that signs up chooses their own subdomain and can customize the branding. During the sign up process, we had to be very clear to the non-technical user exactly what choosing a subdomain was and what it meant. We think we have now minimised the confusion surrounding this but I recently received an email from Google Webmaster Tools adding a twist to the problem:

subsubdomainemail

It appears that even though out users were understanding what choosing a subdomain meant, they couldnt help themselves typing ‘www’ before the reset of their chosen URL. Now even though we own a wildcard SSL certificate for *.waywedo.com, this does not really mean wildcard-as-in-anything-before-the-domain-part. It just means any subdomain name and a sub-subdomain requires a different certificate. Unfortunately the result is a nasty certificate error for any user who unknowingly adds the www on the front of their subdomain.

The fix is fairly easy – a 301 redirect via .htaccess or IIS url rewrite. We use IIS and so this means adding the following to the web.config:

 

    
    
        
    
    
  

As long as a user does not type https before the www, their mistyped url should now be redirected seamlessly to the correct url without the nasty certificate error.

]]>
https://www.onemoretake.com/2014/04/30/redirect-sub-sub-domains-on-multi-tenanted-websites/feed/ 0
Minifying a RequireJS Website During Publish With Visual Studio https://www.onemoretake.com/2014/01/24/minifying-a-requirejs-website-during-publish-with-visual-studio/ https://www.onemoretake.com/2014/01/24/minifying-a-requirejs-website-during-publish-with-visual-studio/#respond Sat, 25 Jan 2014 05:42:02 +0000 http://www.onemoretake.com/?p=430 … ]]> I have recently been working on converting my web application to use requirejs. I have been very happy with the benefits of moving to a modular approach but optimizing using r.js during the deploy to test/production did not seem very elegant. Most solutions suggest building your javascript into a release folder within your project and then manipulating your initial requirejs starting point or the configuration to point the release folder (see here for an example). This seem to cause two issue:

  1. An additional folder and contents for release that I had no interest in seeing in my development workspace.
  2. A non-standard way of declaring your initial requirejs starting point.

What I wanted was for the optimization to occur during the deploy process – web publish in my case. I soon discovered that you can extend the web publish process in either the .pubxml publish profile files or by adding a [projectname].wpp.targets file. These files are msbuild files and thanks to a couple of posts by Sayed Ibrahim Hashimi and by poking around the msbuild files used for publishing, I figured out that during the publish I could:

  1. Run node.exe to build the requirejs javascript
  2. Replace the non-built javascript with the version I built in the previous step

This meant that my requirejs starting point would not need changing at all and the building of the javascript can occur outside my development workspace.

To build the javascript you need a build.js configuration file (see requirejs documentation), node.exe and r.js. You need to put them in a directory that can be referenced during the publish process. The build.js should be configured to output the built files to a directory that can also be accessed during the publish process. The rest of the magic is done in my .wpp.targets file,  which I share below with comments on how to customise it to suit your own setup:



  
    
    
    $(MSBuildProjectDirectory)\[PATH TO FOLDER]
    $(RequireJsBuildDir)\node.exe
    "$(NodeExePath)" r.js -o build.js 
    
    
      ReplaceJs;
      $(OnAfterPipelineCollectFilesPhase);
    
  

    
  
    
    
  
  
  
    
    
        
        <_BuiltJavascript Include="$(MSBuildThisFileDirectory)[PATH TO FOLDER]\**\*" />
        
        
         
        
            js\%(RecursiveDir)%(Filename)%(Extension)
        
    
  

]]>
https://www.onemoretake.com/2014/01/24/minifying-a-requirejs-website-during-publish-with-visual-studio/feed/ 0
ReCSS with Combres Support https://www.onemoretake.com/2012/05/08/recss-with-combres-support/ https://www.onemoretake.com/2012/05/08/recss-with-combres-support/#respond Wed, 09 May 2012 11:52:27 +0000 http://www.onemoretake.com/?p=418 … ]]> I have made extensive use of the great bookmarklet ReCSS that reloads a page’s css without reloading the entire page. It saves masses of time when developing UI but I noticed that on certain projects it has no effect.

I realised that this was because those projects were using the equally useful Combres resource combining library for .Net. Combres uses a number in the url path for caching, and without changing that, any updates to the css file will not be delivered to the browser.

Today I sat down and tweaked the ReCSS bookmarklet so that it checks the url for the /combres.axd path in the urls and changes the number in the url if it is present.

Here is the updated ReCSS bookmarklet. Enjoy.

]]>
https://www.onemoretake.com/2012/05/08/recss-with-combres-support/feed/ 0
TFS Build And Multiple Websites In One Web Role On Azure https://www.onemoretake.com/2011/06/08/tfs-build-and-multiple-websites-on-azure/ https://www.onemoretake.com/2011/06/08/tfs-build-and-multiple-websites-on-azure/#respond Thu, 09 Jun 2011 01:00:53 +0000 http://www.onemoretake.com/?p=391 … ]]> Since the 1.3 sdk update, setting up multiple websites on a single web role is pretty straight forward. However, there is a little bit of a gotcha when using TFS build to compile and publish the packages.

The problem is that when you combine two or more website projects into a single role, the azure project still only compiles the primary website within the project. The other projects seem to just be copied from the location specified physicalDirectory attribute and included in the published package. Ensuring those are included in the package during the build process on your TFS Build server is fairly straight forward – you just add them in the ‘Items to Build’ of your build definition (before the cloud project!). However, what that does not do is transform the web.config files of those projects. The build seems to only compile the code and none of the extra tasks.

Discovering this took up two days of investigating – the problem manifested in a strange authentication cookie domain issue because our config transform should have replaced the authentication settings. Once we realised what was happening, fixing it did not take so long.

We add an after build event that transforms the config files in the project files but only if it looks like we are on the build server. Unload the website project in question and edit the .csproj file. Add an AfterBuild target as follows:


    
    

What this does is run the transforms on the web.config files if the build server _PublishedWebsites directory exists.

The only thing lacking here is the file tidy up – the original config files still remain and are deployed in the package along with other files that are usually removed during the build. This does not effect the website in any way but it would be nice to remove those too. Any suggestions on how would be gratefully received.

]]>
https://www.onemoretake.com/2011/06/08/tfs-build-and-multiple-websites-on-azure/feed/ 0
A JQuery UI Dropdown List https://www.onemoretake.com/2011/04/17/a-better-jquery-ui-combo-box/ https://www.onemoretake.com/2011/04/17/a-better-jquery-ui-combo-box/#comments Mon, 18 Apr 2011 06:22:08 +0000 http://www.onemoretake.com/?p=363 … ]]> In a recent iteration of find.ly that I have been working on, I was asked to create a form that is far removed from the look of standard browser form controls. In particular, the dropdown lists look nothing like the native control and could not be reproduced using only css.

This led me to the jQuery UI combox which uses the jQuery UI autocomplete control. This worked very nicely except that it is not a dropdown list that users are most used to seeing. As soon as the new form entered QA, I was pinged with a message saying that it was a bit awkward to use and would probably be difficult for users. I couldn’t disagree with that and this motivated me to try and see if I could make it more like a dropdown list.

After a few hours of head scratching, I am ready to present the first version of my control, which I would say is about 90% faithful to the feel of native control. There are a few things it does not do but I feel I am close enough to release it to the world for criticism.

Try the demo

Here is the code:

(function ($) {
    $.widget("ui.combobox", {
        _create: function () {
            var self = this,
				select = this.element.hide(),
				selected = select.children(":selected"),
				value = selected.val() ? selected.text() : "",
				regSearch = /^[^a-zA-Z0-9]*([a-zA-Z0-9])/i,
				comboData = select.children("option").map(function () {
					if (this.value ) {
						var text = $(this).text(), 
							labelHtml = self.options.label ? self.options.label(this) : text; //allows list customization
						
						return {
							label: labelHtml,
							value: text,
							option: this
						};
					}
				});
				
            var input = this.input = $("")
					.insertAfter(select)
					.val(value)
					.keydown( function( event ) {
							var keyCode = $.ui.keyCode;
							switch( event.keyCode ) {
								case keyCode.PAGE_UP:
								case keyCode.PAGE_DOWN:
								case keyCode.UP:
								case keyCode.DOWN:
								case keyCode.ENTER:
								case keyCode.NUMPAD_ENTER:
								case keyCode.TAB:
								case keyCode.ESCAPE:
									//let autocomplete handle these
									break;
								default:
									//prevent autocomplete doing anything
									event.stopImmediatePropagation();
									//only react to [a-zA-Z0-9]
									if ((event.keyCode < 91 && event.keyCode > 59)
										|| (event.keyCode < 58 && event.keyCode > 47)) {
										
										var str = String.fromCharCode(event.keyCode).toLowerCase(), currVal = input.val(), opt;
										
										//find all options whose first alpha character matches that pressed
										var matchOpt = select.children().filter(function() {
											var test = regSearch.exec(this.text);
											return (test && test.length == 2 && test[1].toLowerCase() == str);
										});
										
										if (!matchOpt.length ) return false;
										
										//if there is something selected we need to find the next in the list
										if (currVal.length) {
											var test = regSearch.exec(currVal);
											if (test && test.length == 2 && test[1].toLowerCase() == str) {
												//the next one that begins with that letter
												matchOpt.each(function(ix, el) {
													if (el.selected) {
														if ((ix + 1) <= matchOpt.length-1) {
															opt = matchOpt[ix + 1];
														}
														return false;
													}
												});
											}
										} 
										
										//fallback to the first one that begins with that character
										if (!opt)
											opt = matchOpt[0];
										
										//select that item
										opt.selected = true;
										input.val(opt.text);
										
										//if the dropdown is open, find it in the list
										if (input.autocomplete("widget").is(":visible")) {
											input.data("autocomplete").widget().children('li').each(function() {		
												var $li = $(this);
												if ($li.data("item.autocomplete").option == opt) {
													input.data("autocomplete").menu.activate(event,$li);
													return false;
												}
											});
										}
									}
									//ignore all other keystrokes
									return false;
									break;
								}
					  })
					.autocomplete({
					    delay: 0,
					    minLength: 0,
					    source: function (request, response) { response(comboData); },
					    select: function (event, ui) {
					        ui.item.option.selected = true;
					        self._trigger("selected", event, {
					            item: ui.item.option
					        });
					    },
					    change: function (event, ui) {
							if (!ui.item) {					
								var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex($(this).val()) + "$", "i"),
									valid = false;
								select.children("option").each(function () {
									if ($(this).text().match(matcher)) {
										this.selected = valid = true;
										return false;
									}
								});
								if (!valid) {
									// remove invalid value, as it didn't match anything
									$(this).val("");
									select.val("");
									input.data("autocomplete").term = "";
									return false;
								}
							}
					    }
					})
					.addClass("ui-widget ui-widget-content ui-corner-left")
					.click(function() { self.button.click(); })
					.bind("autocompleteopen", function(event, ui){
						//find the currently selected item and highlight it in the list
						var opt = select.children(":selected")[0];
						input.data("autocomplete").widget().children('li').each(function() {		
							var $li = $(this);
							if ($li.data("item.autocomplete").option == opt) {
								input.data("autocomplete").menu.activate(event,$li);
								return false;
							}
						});
					});

            input.data("autocomplete")._renderItem = function (ul, item) {
                return $("
  • ") .data("item.autocomplete", item) .append("" + item.label + "") .appendTo(ul); }; this.button = $("") .attr("tabIndex", -1) .attr("title", "Show All Items") .insertAfter(input) .button({ icons: { primary: "ui-icon-triangle-1-s" }, text: false }) .removeClass("ui-corner-all") .addClass("ui-corner-right ui-button-icon") .click(function () { // close if already visible if (input.autocomplete("widget").is(":visible")) { input.autocomplete("close"); return; } // pass empty string as value to search for, displaying all results input.autocomplete("search", ""); input.focus(); }); }, //allows programmatic selection of combo using the option value setValue: function (value) { var $input = this.input; $("option", this.element).each(function () { if ($(this).val() == value) { this.selected = true; $input.val(this.text); return false; } }); }, destroy: function () { this.input.remove(); this.button.remove(); this.element.show(); $.Widget.prototype.destroy.call(this); } }); })(jQuery);

    The particular functions I tried to add are:

    • Removal of any standard textbox typing characteristics
    • Typing in the textbox jumps to the item begining with that letter
    • Hitting the same letter continuously cycles through the items beginning with that letter
    • Programatically select an item using the value
    • Retain navigation through list via arrow keys/paging
    • Opening the list jumps to the selected item

    Feel free to take it and use it but I am very keen on any improvements that can be made, so please let me know what you think.

    I know that performance is a bit of a problem with large lists but stopping autocomplete from rebuilding the list each time it is displayed is a little tricky and would mean changing more of it’s private functions – something I was hoping to avoid. Suggestions welcome.

    *Edit: I totally had my terminology messed up. I was calling my control a combobox which it absolutely isnt. It is a dropdown list, so I have updated the post to reflect that. *places dunce hat on*

    ]]>
    https://www.onemoretake.com/2011/04/17/a-better-jquery-ui-combo-box/feed/ 19
    Git Extensions Quick Tip – Updating remotes https://www.onemoretake.com/2011/02/21/git-extensions-updating-remotes/ https://www.onemoretake.com/2011/02/21/git-extensions-updating-remotes/#comments Tue, 22 Feb 2011 07:05:45 +0000 http://www.onemoretake.com/?p=357 … ]]> I have both a desktop and a laptop that I work on and use Git Extensions. Every so often I create a branch on one machine, commit it and then push it to my remote origin. My problem is that when I boot up my other machine, I can never remember how to see that new branch on the origin in order to pull it down and continue working on it. So mainly for my benefit, here are two different ways to do it:

    1. Open up a git bash and type ‘git fetch’ or…
    2. Click on Remotes->manage remote repositories, click on the Default Pull Behaviour tab, click Update all remote branch

    Its a little bit hidden away in the Git Extension UI, so thats probably why I have so much of a problem finding it.

    Update remote repositories

    ]]>
    https://www.onemoretake.com/2011/02/21/git-extensions-updating-remotes/feed/ 2
    Using a Resource File for NHibernate Validation Messages https://www.onemoretake.com/2010/11/19/resource-file-nhibernate-validation/ https://www.onemoretake.com/2010/11/19/resource-file-nhibernate-validation/#comments Sat, 20 Nov 2010 06:15:39 +0000 http://www.onemoretake.com/?p=344 … ]]> I have recently started using NHibernate for a project because I wanted something that would work in Mono on linux and coupled with Fluent NHibernate, seemed like the best choice to work with MySql.

    It wasn’t long before I began to use the NHibernate Validator framework to provide some validation on my models. Using it is similar to Data Annotation Validators which I had used in another project and I liked the way you could use resource files to provide the error messages the Data Annotation Validators produce.

    It seemed to take me ages to figure out the equivalent in the NHibernate Validator framework, as I had to piece it together from several other articles which talked about various customizations that could be done. In the end it turns out that its not very difficult to do at all. Here’s how:

    Firstly, when validating your models, you need to point the validation engine to your resource file in the configuration:

    //create engine
    var engine =  new ValidatorEngine();
    
    //configure and add resource file
    var configure = new FluentConfiguration();
    configure.SetCustomResourceManager("My.Website.Resources.ValidationMessages", Assembly.Load("MyWebsite"));
    engine.Configure(configure);
    
    //engine configured, now you can validate
    var errors = engine.Validate(myentity);
    

    To specifiy which resource value to use, you specify the resource key in the attribute wrapped in curly brackets as follows:

    
    public class MyEntity {
    
      [NotNullNotEmpty(Message = "{ResourceKeyWithCurlyBrackets}")]
      public string SomeProperty { get; set; }
    
    }
    
    

    …and thats all there is to it, the text in the resource file should appear in the validation results.

    ]]>
    https://www.onemoretake.com/2010/11/19/resource-file-nhibernate-validation/feed/ 1
    CodeIgniter Snippets for Visual Studio https://www.onemoretake.com/2010/07/19/codeigniter-snippets-for-visual-studio/ https://www.onemoretake.com/2010/07/19/codeigniter-snippets-for-visual-studio/#respond Tue, 20 Jul 2010 08:28:37 +0000 http://www.onemoretake.com/?p=336 … ]]> I thought I would share something I have been using for quite a while now and find incredibly useful. They are two snippets for Visual Studio that create the standard file start and file end for php files in the codeigniter framework. If you are not sure what I mean then check out the files that make up the framework – you will notice that they all tend to start with:

    
    

    and end with:

    /* End of file MyFile.php */
    /* Location: ./path/to/file/MyFile.php */
    

    The line at the start of the file is a security measure to stop files being executed outside the framework, whilst the end is just informative but useful anyhow.

    I like to do the same thing with all the files I create for my applications but found the typing tedious and because I use the VS.php in Visual Studio, I decided to write a couple of snippets to do it for me.

    If you are not familiar with snippets in Visual Studio or how to get the following xml to work as a snippet, you can read all about then on msdn. Anyhow, without further ado, here they are:

    filestart.snippet:

    
    
      
        
    filestart Dan Sargeant CodeIgniter File start to stop direct script access Expansion SurroundsWith filestart

    fileend.snippet:

    
    
      
        
    fileend Dan Sargeant CodeIgniter file ending fileend Expansion SurroundsWith
    filename Filename myfile.php directory Directory models
    ]]> https://www.onemoretake.com/2010/07/19/codeigniter-snippets-for-visual-studio/feed/ 0 Convert to Apple Lossless Codec: The Easy Way https://www.onemoretake.com/2010/07/02/convert-to-apple-lossless-codec/ https://www.onemoretake.com/2010/07/02/convert-to-apple-lossless-codec/#comments Fri, 02 Jul 2010 12:47:46 +0000 http://www.onemoretake.com/?p=331 … ]]> Playing lossless codecs like APE or FLAC in itunes or on your iphone is not so easy as there is no native support for them. Converting them to the Apple equivalent is also quite tricky. Many people recommend installing media players that support this conversion or purchasing a commercial product to do it. However, there is a free and easy way to convert your files in Windows that does not require any installation.

    The key to it is the use of the windows port of ffmpeg. It can convert just about anything to anything. However, it is a command line tool and converts one file at a time which means a lot of typing if you want to convert an entire album. I spent a little time the other day figuring out a small batch file that can be used to convert and entire albumn in on go. I thought I’d share it with you. Just create a file called say, convert.bat and place one of the following in it:

    For Monkey’s Audio APE files:

    for /f "tokens=*" %%a IN ('dir /b *.ape') do call ffmpeg -i "%%~na%%~xa" -acodec alac "%%~na.m4a"
    

    For FLAC files:

    for /f "tokens=*" %%a IN ('dir /b *.flac') do call ffmpeg -i "%%~na%%~xa" -acodec alac "%%~na.m4a"
    

    Then all you have to do is place your batch file, along with ffmpeg.exe into the directory that contains the files you want to convert and run the batch file. It will convert every file in the directory to apple lossless. Easy.

    ]]>
    https://www.onemoretake.com/2010/07/02/convert-to-apple-lossless-codec/feed/ 1