Or in this case more css. I often run into limitations and annoyances with the limitations of Css rules. But there are solutions for this, namely Less the css preprocessor. It very simply lets you markup Css files with additional logic and features that css doesn't support. Then runes a processing function over the file to generate a second rewritten version in raw Css code. This quickly allows for things like variable, functions, inheritance, nested rules, and basic math.
Any way this can be implemented a few ways but I crated the simple package for Umbraco to completely automate this process. It very simply looks for *.less.css files in the normal umbraco Css folder and auto-compiles them on save. So install this and then simply create a new stylesheet called test.less in Umbraco and you are good to go. Just make sure you always edit the .less version as the compiled file is read only.
This is my personal blog and mainly contains technical solutions, open source code, and other projects I have worked on. Much of the technical solutions are very niche so your milage may vary.
Tuesday, September 10, 2013
Thursday, July 11, 2013
Using a Sophos UTM in Virtual Box
Sophos provides a free home user UTM. This is a direct update to the older Astaro ASG that I was running. I recently had to convert over to the new system. I run it on my FreeBSd server so prefer to use Virual Box to run my VM's. I ran into a bit of an issue getting the UTM onto the network, but now it's working perfectly. Here are some simple instructions to get it working.
First of all register and download the VMware x86 or x64 zip file(not the ESX version). Onew downloaded unzip this file and grab all the *.vmdk files. The other files can be discarded.
Now go into Virtual box and create a new VM. Select Linux as the OS and configure it as desired with the following changes.
Now you can boot up the VM. If you plan to run it as a service you can but for now you need to start it directly in VirtualBox so you can get at the console. Once booted up the system will list and IP to access it at but this won't actually work due to our VM/Nic setup. So instead go into the UTM's console and logging as root. The password will be blank and you will need to change it.
Now on the UTM do the following to reconfigure the default network settings. Tab completion will work here. Also be sure to substitute you settings for the following variables:
Each set of commands above will print out the new config when the
Now if everything is working you should be able to navigate to https://$ADDRESS:4444 and setup the UTM. If you get any error about not being able to change your active connection then one of the settings above must not match or the the itfhw in the last step didn't get set right.
First of all register and download the VMware x86 or x64 zip file(not the ESX version). Onew downloaded unzip this file and grab all the *.vmdk files. The other files can be discarded.
Now go into Virtual box and create a new VM. Select Linux as the OS and configure it as desired with the following changes.
- Add a new IDE disk and browser for and select the base VMDK file. (The one without a -s###.)
- Add two network cards and change both to use Bridged Networking and change the device driver to Intel PRO/1000 MT Server.
Now you can boot up the VM. If you plan to run it as a service you can but for now you need to start it directly in VirtualBox so you can get at the console. Once booted up the system will list and IP to access it at but this won't actually work due to our VM/Nic setup. So instead go into the UTM's console and logging as root. The password will be blank and you will need to change it.
Now on the UTM do the following to reconfigure the default network settings. Tab completion will work here. Also be sure to substitute you settings for the following variables:
$ADDRESS
(eg: 192.168.1.5) Internal Ip for the UTM.$NETMASK
(eg: 24) See here for help.$BROADCAST
(eg: 192.168.1.255) Subnet part of Ip filled in with 255 for the rest.$NETWORK
(eg: 192.168.1.0) Subnet of the Ip filled in with 0's.$INTERFACE
Use tab compleation for this and choose witch NIC you want as your internal.cc
RAW
lock_override
OBJS
itfparams
primary
REF_ItfParamsDefaultInternal
address=$ADDRESS
netmask=$NETMASK
w
/
network
interface_address
REF_DefaultInternalAddress
address=$ADDRESS
w
/
network
interface_broadcast
REF_DefaultInternalBroadcast
address=$BROADCAST
w
/
network
interface_network
REF_DefaultInternalNetwork
address=$NETWORK
netmask=$NETMASK
w
/
interface
ethernet
REF_DefaultInternal
itfhw=REF_ItfEth$INTERFACE
status=1
w
exit
Each set of commands above will print out the new config when the
w
line is entered so you should be able to identify any errors or problems. Once finished you can reboot the UTM by typing reboot
.Now if everything is working you should be able to navigate to https://$ADDRESS:4444 and setup the UTM. If you get any error about not being able to change your active connection then one of the settings above must not match or the the itfhw in the last step didn't get set right.
Wednesday, June 12, 2013
Automatic Script/CSS Compression/Rollup in Umbraco MVC Views
Similar to the article about doing this in Master Page Templates but for doing so within MVC Views.
When using Umbraco you may have noticed that the Backoffice Administrative interface uses the Client Dependency Framework to roll up and compress all the different scripts and css resources into two calls. This leads to a big improvement in responsiveness as the server doesn't need to download as much data nor complete as many requests to render a web page in the admin area.
The good new here is that you can use this same system in your actual site with very little setup. A few simple step to configure your main site views to use the Client Dependency Framework also.
When using Umbraco you may have noticed that the Backoffice Administrative interface uses the Client Dependency Framework to roll up and compress all the different scripts and css resources into two calls. This leads to a big improvement in responsiveness as the server doesn't need to download as much data nor complete as many requests to render a web page in the admin area.
The good new here is that you can use this same system in your actual site with very little setup. A few simple step to configure your main site views to use the Client Dependency Framework also.
Wednesday, May 8, 2013
Enable Drag and Drop on Content and Media trees in Umbraco
For Umbraco 7 go here.
Updated 8/20/2018:
This is a Javascript level Drag and Drop add-on for Umbraco. It will allow you to sort and move content and media items abound and into or out of the trash. Further more it will update the sorting of items and urls of documents are they are moved.
Unfortunately the changes require updating two core umbraco files so I can't make them into a package. Just follow these simple instructions and you to can have drag and drop nodes.
Updated 8/20/2018:
- Corrections and new UmbracoTree.js code for newer v6 an jQuery versions.
- Corrections for issues sorting nodes.
- Updated nestedSortable.js with a working version.
- Added allowed child types validation on move (and root level validation in Umbraco 6.0+).
- Updated error message and notification popup code.
- All of the code has been updated to please replace your local files/customizations with the new code.
Updated 6/17/2013:
- Added better isAllowed detection. Nodes will now move back if not allowed at a location.
This is a Javascript level Drag and Drop add-on for Umbraco. It will allow you to sort and move content and media items abound and into or out of the trash. Further more it will update the sorting of items and urls of documents are they are moved.
Unfortunately the changes require updating two core umbraco files so I can't make them into a package. Just follow these simple instructions and you to can have drag and drop nodes.
Monday, May 6, 2013
Automatically deploying all Embedded Resources when a Document is deployed with Umbraco Courier.
We use Umbraco and the Courier package to deploy changes from a staging instance to a production instance. The workflow our editors use is as follows.
- Update a document with new changes including embedded images or links to PDF/other files.
- Deploy the document only, no recursion/dependancies.
Tuesday, March 12, 2013
Javascript function to decode HTML escape sequences.
I found a few samples that will decode the escape sequences in the following html string but none quite took my fancy. (Not to be confused with unescape witch will decode URL escape sequences.)
Here is the version I wrote:
Here is the version I wrote:
Monday, March 11, 2013
Using LINQ to SQL for clean and quick Database Access/Interactions.
Update: October 26, 2016 - I would now recomend using this replacement guide using the built in PetaPoco instead of this method as it is much cleaner and requires significantly less DBA code and produced much smaller binaries. There are also T4 Templates that will generate models form your database so you can design in code and generate database constructs automatically, or create tables and then auto-generate code form those, whichever way you prefer. I still use the Columns features below for when creating forms.
This post is a demonstration/instructions for very easily and cleanly working with SQL data in a .NET application using LINQ to SQL. There are lots of resources out there about this technology but my goal here is to present a very clean and thin interface to utilizing LING to SQL. Read on for explanations and samples.
This post is a demonstration/instructions for very easily and cleanly working with SQL data in a .NET application using LINQ to SQL. There are lots of resources out there about this technology but my goal here is to present a very clean and thin interface to utilizing LING to SQL. Read on for explanations and samples.
Thursday, January 31, 2013
jQuery Vertical Totem Ticker Plugin - Updated
I found this nice jQuery plugin Totem Animated Vertical Ticker. but it doesn't seam to have some options I needed. As it does not appear to be to active I made some customizations myself to add the following options.
- Added support for mouse whee scrolling.
- Tickers now all get the vTicker class for css markup.
- If a row height is specified that the items are now actually restricted to that height. Add the following css to make longer items scroll properly.
- Added helper methods for easy scripting of the ticker.
The new version is available on GitHub at https://github.com/pynej/totem.
- Added support for mouse whee scrolling.
- Tickers now all get the vTicker class for css markup.
- If a row height is specified that the items are now actually restricted to that height. Add the following css to make longer items scroll properly.
.vTicker li { overflow: hidden; }
- Added helper methods for easy scripting of the ticker.
$(selector).totemticker("start");
$(selector).totemticker("stop");
$(selector).totemticker("previous");
$(selector).totemticker("next");
The new version is available on GitHub at https://github.com/pynej/totem.
Tuesday, January 29, 2013
Umbraco Full Text Search Razor Script File
We use the Umbraco Full Text Search package which is a great tool for simple full text site wide searching. It does however use a XSLT macro to render its search results. We have an extensive collection of Razor Macros and as such I wanted a Razor version of this package to simplify development. One does not exist So i took the time to convert the XSLT macro into a Razor macro included below. Just add it to you MacroScripts folder and then change the FullTextSearch Macro to use this instead of the original version. The output should be identical to the XSLT version.
FullTextSearch.cshtml
FullTextSearch.cshtml
@using FullTextSearch.XsltExtensions
@using System.Xml.XPath
@{
/*
FullTextSearch.xslt
======================================================================
Full Text Search
V0.25
======================================================================
This XSLT file sets up queries and sends them off to FullTextSearch's
XSLT helpers.
Feel free to modify any part of it to your own needs. HTML is near
the bottom in a couple of templates.
PARAMETERS:
queryType
Type of search to perform. Possible values are:
MultiRelevance ->
The default.
The index is searched for, in order of decreasing relevance
1) the exact phrase entered in any of the title properties
2) any of the terms entered in any of the title properties
3) a fuzzy match for any of the terms entered in any of the title properties
4) the exact phrase entered in any of the body properties
5) any of the terms entered in any of the body properties
6) a fuzzy match for any of the terms entered in any of the body properties
MultiAnd ->
Similar to MultiRelevance, but requires all terms be present
SimpleOr->
Similar to MultiRelevance again, but the exact phrase does not
get boosted, we just search for any term
AsEntered->
Search for the exact phrase entered, if more than one term is present
______________________________________________________________________
titleProperties
A comma separated list of properties that are part of the page title,
these will have their relevance boosted by a factor of 10
defaults to nodeName. Set to "ignore" not to search titles.
______________________________________________________________________
bodyProperties
A comma separtated list of properties that are part of the page body.
These properties and the titleProperties will be searched.
defaults to using the full text index only
______________________________________________________________________
summaryProperties
The list of properties, comma separated, in order of preference,
that you wish to use to create the summary to appear under
the title. All properties selected must be in the index, cos that's
where we pull the data from.
Defaults to Full Text
______________________________________________________________________
titleLinkProperties
The list of properties, comma separated, in order of preference,that you
wish to use to create the title link for each search result.
Defaults to titleProperties, or if that isn't set nodeName
______________________________________________________________________
rootNodes
Comma separated list of root node ids
Only nodes which have one of these nodes as a parent will be returned.
Default is to search all nodes
______________________________________________________________________
contextHighlighting
Set this to false to disable context highlighting
in the summary/title. You may wish to do this if you are having
performance issues as context highlighting is (relatively)
slow.
Defaults to on.
______________________________________________________________________
summaryLength
The maximum number of characters to show in the summary.
Defaults to 300
______________________________________________________________________
pageLength
Number of results on a page. Defaults to 20. Set to zero to disable paging.
______________________________________________________________________
fuzzyness
Lucene Queries can be "fuzzy" or exact.
A fuzzy query will match close variations of the search terms, such as
plurals etc. This sets how close the search term must be to a term in
the index. Values from zero to one. 1.0 = exact matching.
Note that fuzzy matching is slow compared to exact or even wildcard
matching, if you're having performance issues this is the first thing
to switch off.
Defaults to 0.8
______________________________________________________________________
useWildcards
Add a wildcard "*" to the end of every search term to make it match
anything starting with the search term. This is a slightly faster, but
less accurate way of achieving the same ends as fuzzy matching.
Note that fuzzyness is automatically set to 1.0 if a wildcards are enabled.
Defaults to off
______________________________________________________________________
*/
/* Variables */
var fullTextIndexName = "FullTextSearch";
var getPostTerms = "Search";
var getPostPage = "Page";
var numNumbers = 15;
/* Parameters */
var titleProperties = String.IsNullOrEmpty(Parameter.titleProperties) ? "nodeName" : Parameter.titleProperties;
if(titleProperties == "ignore") {
titleProperties = "";
}
var bodyProperties = String.IsNullOrEmpty(Parameter.bodyProperties) ? fullTextIndexName : Parameter.bodyProperties;
var summaryProperties = String.IsNullOrEmpty(Parameter.summaryProperties) ? fullTextIndexName : Parameter.summaryProperties;
var titleLinkProperties = String.IsNullOrEmpty(Parameter.titleLinkProperties) ? (String.IsNullOrEmpty(titleProperties) ? "nodeName" : titleProperties) : Parameter.titleLinkProperties;
var rootNodes = Parameter.rootNodes;
var contextHighlighting = Parameter.contextHighlighting == "0" ? 0 : 1;
int summaryLength;
if(!int.TryParse(Parameter.summaryLength, out summaryLength) || summaryLength <= 0) {
summaryLength = 300;
}
int pageLength;
if(!int.TryParse(Parameter.pageLength, out pageLength) || pageLength <= 0) {
pageLength = 20;
}
var queryType = String.IsNullOrEmpty(Parameter.queryType) ? "MultiRelevance" : Parameter.queryType;
var fuzzyness = String.IsNullOrEmpty(Parameter.fuzzyness) ? "0.8" : Parameter.fuzzyness;
var useWildcards = Parameter.useWildcards == "1" ? 1 : 0;
/* Call Helpers */
var pageNumber = String.IsNullOrEmpty(umbraco.library.RequestQueryString(getPostPage)) ? (String.IsNullOrEmpty(umbraco.library.Request(getPostPage)) ? 1 : int.Parse(umbraco.library.Request(getPostPage))) : int.Parse(umbraco.library.RequestQueryString(getPostPage));
var searchTerms = String.IsNullOrEmpty(umbraco.library.RequestQueryString(getPostTerms)) ? (String.IsNullOrEmpty(umbraco.library.Request(getPostTerms)) ? "" : umbraco.library.Request(getPostTerms)) : umbraco.library.RequestQueryString(getPostTerms);
var searchTermsUrlEncoded = umbraco.library.UrlEncode(searchTerms);
/* Run Search */
var results = SearchExtension.Search(queryType,searchTerms,titleProperties,bodyProperties,rootNodes,titleLinkProperties,summaryProperties,contextHighlighting,summaryLength,pageNumber,pageLength,fuzzyness,useWildcards);
}
<div class="fulltextsearch">
@if(results.Current.Evaluate("count(/results)") == 1) {
var resultsItem = results.Current.Select("/results");
<div class="fulltextsearch_results">
<h1 class="fulltextsearch_results_heading">
@String.Format(GeneralExtension.DictionaryHelper("SearchResultsFor"), searchTerms)
</h1>
@{
int numPages = (int)resultsItem.Current.Evaluate("number(/results/summary/@numPages)");
}
@if(numPages > 1)
{
@Pagination(numPages, pageNumber, getPostTerms, searchTermsUrlEncoded, getPostPage, numNumbers)
}
@foreach(var searchResult in resultsItem.Current.Select("/results/nodes/*")) {
var FullTextTitle = searchResult.Evaluate("string(./data [@alias='FullTextTitle'])");
var FullTextSummary = searchResult.Evaluate("string(./data [@alias='FullTextSummary'])");
<div class="fulltextsearch_result">
<h2 class="fulltextsearch_title">
<a class="fulltextsearch_titlelink" href="@umbraco.library.NiceUrl(1)">
@Html.Raw(FullTextTitle)
</a>
</h2>
<p class="fulltextsearch_summary">
@Html.Raw(FullTextSummary)
</p>
</div>
}
@if(numPages > 1)
{
@Pagination(numPages, pageNumber, getPostTerms, searchTermsUrlEncoded, getPostPage, numNumbers)
}
<p class="fulltextsearch_info">
@{
var summary = String.Format(GeneralExtension.DictionaryHelper("SummaryInfoFormat"),
resultsItem.Current.Evaluate("string(/results/summary/@firstResult)"),
resultsItem.Current.Evaluate("string(/results/summary/@lastResult)"),
resultsItem.Current.Evaluate("string(/results/summary/@numResults)"),
resultsItem.Current.Evaluate("string(/results/summary/@timeTaken)"));
var swinfo = resultsItem.Current.Evaluate("string(/results/summary/swinfo)");
}
@summary
@Html.Raw(swinfo)
</p>
</div>
} else {
string errorType = (string)results.Current.Evaluate("string(/error/@type)");
string error = (string)results.Current.Evaluate("string(/error)");
string dictionaryError = String.IsNullOrEmpty(errorType) ? "" : String.Format(GeneralExtension.DictionaryHelper(errorType), searchTerms, pageNumber);
var errormsg = String.IsNullOrEmpty(dictionaryError) ? (String.IsNullOrEmpty(error) ? GeneralExtension.DictionaryHelper("UnknownError"): error) : dictionaryError;
<div class="fulltextsearch_error">
<p>
@errormsg
</p>
</div>
}
</div>
@helper Pagination(int numPages, int pageNumber, string getPostTerms, string searchTermsUrlEncoded, string getPostPage, int numNumbers)
{
var langPrevious = GeneralExtension.DictionaryHelper("NavPrevious");
var langNext = GeneralExtension.DictionaryHelper("NavNext");
int startPage = (numNumbers / 2);
startPage = (pageNumber < startPage + 1) ? 1 : pageNumber - startPage;
<div class="fulltextsearch_pagination">
<ul class="fulltextsearch_pagination_ul" style="list-style-type:none;margin-bottom:15px;">
@if(pageNumber > 1) {
<li class="fulltextsearch_previous">
<a class="fulltextsearch_pagination_link" rel="prev" href="?@getPostTerms=@searchTermsUrlEncoded&@getPostPage=@(pageNumber - 1)">@langPrevious</a>
</li>
} else {
<li class="fulltextsearch_previous fulltextsearch_previous_inactive">
<a class="fulltextsearch_pagination_link">
@langPrevious
</a>
</li>
}
@for(int curPage = startPage; curPage <= numPages && curPage < startPage + numNumbers; curPage++) {
if(curPage == pageNumber) {
<li class="fulltextsearch_page fulltextsearch_thispage" style="margin-left:5px;">
@curPage
</li>
} else {
<li class="fulltextsearch_page" style="margin-left:5px;">
<a class="fulltextsearch_pagination_link" rel="nofollow" href="?@getPostTerms=@searchTermsUrlEncoded&@getPostPage=@curPage">
@curPage
</a>
</li>
}
}
@if(pageNumber < numPages) {
<li class="fulltextsearch_next" style="margin-left:5px;">
<a class="fulltextsearch_pagination_link" rel="next" href="?@getPostTerms=@searchTermsUrlEncoded&@getPostPage=@(pageNumber + 1)">
@langNext
</a>
</li>
} else {
<li class="fulltextsearch_next fulltextsearch_next_inactive" >
<a class="fulltextsearch_pagination_link" style="margin-left:5px;">
@langNext
</a>
</li>
}
</ul>
</div>
}
@helper ShowErrors(XPathNodeIterator results, string searchTerms, int pageNumber)
{
string errorType = (string)results.Current.Evaluate("string(/error/@type)");
string error = (string)results.Current.Evaluate("string(/error)");
string dictionaryError = String.IsNullOrEmpty(errorType) ? "" : String.Format(GeneralExtension.DictionaryHelper(errorType), searchTerms, pageNumber);
var errormsg = String.IsNullOrEmpty(dictionaryError) ? (String.IsNullOrEmpty(error) ? GeneralExtension.DictionaryHelper("UnknownError"): error) : dictionaryError;
<div class="fulltextsearch_error">
<p>
@errormsg
</p>
</div>
}
@helper SearchBox(string searchTerms, string getPostTerms)
{
var searchString = GeneralExtension.DictionaryHelper("SearchButton");
<form class="fulltextsearch_form" method="get" action="@umbraco.library.NiceUrl(Model.Id)">
<input class="fulltextsearch_searchbox" name="@getPostTerms" type="text" value="@searchTerms" />
<input class="fulltextsearch_searchbutton" type="submit" value="@searchString" />
</form>
}
Subscribe to:
Posts (Atom)
Project Licenses
These works by Jeremy Pyne are licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License