Archive
Special links: phone calls, sms, e-mails, iPhone and Android apps, …
Everyone knows to include a link into a page, but what I want to discuss today is how you can include some special links.
Phone calls
This is something cool and with the explosion of smart phones with which you can easily browse this is definitely to consider having on the mobile version of your website. Such a link will initiate a call on your mobile to a specified number. But here the things are a little bit more complicated, requiring different links for different phones. You can easily find how to create such a link consulting the WURFL database and looking at the wml_make_phone_call_string
property in the wml_ui
category.
Basically this is done as follows:
- callto:[phone_number]
- mainly appropriate for iPhone and Nokia phones
- wtai://wp/mc;[phone_number]
- mainly appropriate for Android phones
- tel:[phone_number]
- reported to work on most of the newest devices. If you want to have only one type of URI, use this one.
In the phone number you can use +(plus) sign for international numbers. What’s also interesting to know is that this can even work on a desktop if you have an application like Skype installed. So maybe it’s a good idea to have this on the classic/desktop website too.
Later update from comments. Another interesting phone link will be how to call a teleconferencing phone number. There you call a phone number and then you enter your conference code, usually followed by hash(#)
. To do this from a link you will need a pause after the phone number and this is done with ,
(comma), usually entered by a long press on star(*)
. I tested this on Android and iOS and it works fine, but you usually need two pauses (,) between the phone number and conference code. Same way you can dial an extension line.
Examples
- callto:12345678
- call 12345678 on iPhone and Nokia
- wtai://wp/mc;12345678
- call 12345678 on Android
- wtai://wp/mc;+123456789
- call an international number on Android
- tel:12345678
- call 12345678 on most of the newer devices
- tel:12345678,,100200#
- join 100200 conference code on the conference line 12345678 on most of the newer devices
- tel:+12345678,,100200#
- join 100200 conference code on the international conference line +12345678 on most of the newer devices
SMS
From a web page you can open the SMS sending application on the user phone with a link like below:
sms:<phone_number>[,<phone_number>]*[?body=<message_body>]
The link contains a comma separated list of phone numbers and an optional message body. The phone numbers are specified as in the call links. Detailed information you can find in the URI Scheme for GSM Short Message Service (draft)
Examples
- sms:12345678
- SMS to 12345678
- sms:12345678?body=Hello my friend
- SMS “Hello my friend” to 12345678
- sms:123456789,+123456789?body=Hello
- SMS to multiple phone numbers, including an international one
There is also an URI version for MMS starting with mms:
. On some (mobile) browsers (devices) it is also reported to work smsto:
and mmsto:
, although I would recommend the first version.
iPhone/iPod/iTunes
When developing a website iPhone is definitely to be considered. You can include links to items in the iTunes store, such as movies, music or application. Apple provided for your convenience a web interface to create such links: ITMS Link Maker. Just specify the country, the search keyowrds and what type of iTunes items. You will get a list of items and when you click one you will get the link. You can even get the link for an author. These links will both work on the desktop and iPhone.
Example: Fluid application (free)
Android market
Android is gaining market share as we speak. Nexus One was just released and in my opinion will beat iPhone. As you include links to iTunes, you can include links to applications in Android Market.
market://search?q=<query>
or
market://details?id=<your.package.name>
The query
can include keywords or can identify a specific application using q=pname:your.package.name
and then the link will be market://search?q=pname:<your.package.name>
.
Ovi Store
Nokia created a new fresh application repository for their latest phone – Ovi Store. If you want to include a link to an application, search for it and then copy the link that it will look like http://store.ovi.com/content/XXXXX?clickSource=publisher+channel
. Just remove the last part and include http://store.ovi.com/content/21309
into your page, where XXXXX is the application Id. You can also include a link to a publisher’s page containing a summary of their application. The link is found on any of the publisher’s application (see by Publisher Name) and it will look like http://store.ovi.com/publisher/Publisher+Name
Windows Marketplace
We cannot exclude Microsoft from the list with their Windows Phone Marketplace. Same steps to find out the application link: search it, copy the link location and strip the last part. The link will look like http://marketplace.windowsphone.com/details.aspx?appId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
, where xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
is the application ID, clearly resembling a GUID. Here publishers don’t have a personal page.
BlackBerry App World
For all those BlackBerry fans, there is BlackBerry App World. Again same process: search for the application and copy the link location of the application icon. The format is http://appworld.blackberry.com/webstore/content/XXXXX
, where XXXXX is the application ID. The app authors have a page here with a summary of their apps. See the by Author link under each application. The link will be like http://appworld.blackberry.com/webstore/vendor/XXXX
, where XXXX is the author ID.
Geolocation
Nowadays you cannot even imagine a world without maps and GPS. More and more contact pages include a map too. Nowadays smart phones usually include a map application and opening a map with your location in it would be quite nice for the user
geopoint:latitude,longitude
As simple as this and you know where to go.
Messengers
From a web site you can also interact with the messenger applications installed on your machine.
Yahoo Messenger
ymsgr:ACTION?USERNAME&m=YOUR+MESSAGE
The possible actions are addfriend
, sendIM
and call
. The message can, of course, be specified only for sendIM
action. The USERNAME should be your.account@yahoo.com
or your.account@hotmail.com
.
Example: ymsgr:sendIM?beradrian&m=Hello – Say Hello in Yahoo Messenger.
Windows Messenger
msnim:ACTION?contact=USERNAME
The possible actions are chat
, add
(to add a contact), voice
(for voice call) and video
(for video call). The USERNAME should be your.account@yahoo.com
or your.account@hotmail.com
.
Example: msnim:chat?contact=beradrian@yahoo.com – Chat with me in Windows Messenger.
Google Talk
gtalk:ACTION?jid=USERNAME&from_jid=YOURNAME
The possible actions are chat
and call
(for voice call). The USERNAME and YOURNAME should be your.account@gmail.com
. The parameter from_jid
is optional and it should be specified only if you use multiple accounts.
Example: gtalk:chat?jid=beradrian@yahoo.com – Chat with me in Google Talk.
Skype
skype:USERNAME?ACTION
The possible actions are chat
, add
(to add a contact), userinfo
(to view a profile) and voicemail
(to leave a voicemail). The USERNAME is your Skype ID.
Example: skype:chat?jid=beradrian – Chat with me in Skype.
Lync
Updated on 03/15/2015
sip:USERNAME@DOMAIN
The main thing here is the sip
protocol. It is possible that this protocol can be used by other applications too, not being something specific to Lync.
Example: Call John.
Updated on 06/09/2016
whatsapp://send[/<phone_number>]?text=<message>
Example: Say ‘Hi John’ to number 0123456789 or pick a contact and Say ‘Hello World!’.
There are also other messengers but these are the most widely used. If you need another one, just post a comment.
It’s pretty easy to include a link for sending an email into your webpage. Basically it’s replacing the http
scheme with mailto
. So the link will look something like:
mailto:<email>[,<email>]*[?<arguments>]
Such a link will practically open the system application for sending emails (like Outlook or Thunderbird) and the message will be prepopulated with some values. As you can notice you can use multiple email addresses (To) separated by comma.
The possible arguments to be included are:
- subject
- the message Subject field
- cc
- the message CC field as a comma separated list of addresses
- bcc
- the message BCC field as a comma separated list of addresses
- body
- the message body. If you include a new line in your message you should include
%0A
.
Examples
- mailto:nobody@wordpress.com
- the simplest mailto link
- mailto:nobody@wordpress.com,no.one@wordpress.com
- multiple email addresses
- “mailto:nobody@wordpress.com?subject=Testing mailto
- specify a subject
- “mailto:nobody@wordpress.com?subject=Testing mailto&cc=no.one@wrodpress.com
- specify a subject and CC
Just as a side note in the end, it’s better not to rely on this kind of mechanism for handling email on your website. A contact form that sends an email could be a better idea.
Most of these URIs work not only on browsers, but on QR codes readers as well.
Happy linking!!!
Last update: June 9th, 2016.
Patu Digua – JavaScript/HTML/CSS Obfuscator/Compressor
I finally managed to get a first version done for a web obfuscator and compressor.
First of all, why to use such a program. Some may say to hide your HTML/JavaScript/CSS code. Right. This could be one option. And it is powerful enough to do so. But I would personally use it to reduce the size of the code. I tested on a few projects and the code gets reduced up to 60-70% from the initial size. It’s not like a zip but quite good if you take into account that you’re dealing only with scripts.
It is very good practice to comment and indent your code, but this doesn’t have any value at all for the end user, it only eats up his/her bandwidth.
The application has a very nice and intuitive interface (check out the screenshots), it is very customizable and it can be run on both UI and command line mode. A nice feature in the graphical interface is the drop zone, where you can drag and drop files or folders and they are automatically processed. Just switch first to the drop zone mode.
If you want to play with it you can download it from SourceForge or check out its home page.
I would gladly want to hear your opinion or how do you use it.
Mobile web
Mobile web is a new trend and a website for mobile devices is more like a must have these days. It is more likely that your company has a web site, but if you access it with a mobile device, the user experience could be unpleasant.
It is not easy to create a site for mobile devices and it is definitely hard to maintain. And not only because of the huge amount of mobile devices, but also because you have to keep it in sync with the normal one. Following I’ll give you a few pointers.
But first you have to clearly define your mobile web presence. Sometimes it doesn’t even make sense to invest time and effort into it. Let’s say that your solely company product is an FTP server. Definitely you won’t expect to sell it to mobile users. So your entire mobile website can consist of a page stating the company name and logo, product name and description, the link where you can find out more information by browsing with a non-mobile web browser. The best part is that you don’t have to update it and it could remain like this for many years. And that could be the case with many other products for which the target user is an enterprise one, with required access from a non-mobile web browser, like a SDK, online 3D highly graphical game etc.
On the other side of the coin, is when your company product is an exclusively mobile product, like a game for mobile devices, ring tones etc. Then you can simply have only a mobile web site. Of course the desktop users won’t an experience up to their device capabilities, but they aren’t actually your target and your site is not of any real use to them.
And there is the case where your presence has to be strong in both worlds. Following I will concentrate more on this, but most of the below pointers apply to the above cases as well.
- CSS. First of all, your web site has to be CSS-based. Forget about tables, use DIVs. Move all CSS rules into external files. Keep the HTML as clean, simple and free of layout settings as possible. Then supporting a mobile device will be as simply as defining a CSS file.
You can include a CSS in a page and have it active only for mobile devices using thehandheld
value for themedia
attribute of thelink
tag. Even tough this is very convenient, I wouldn’t recommend to be the only thing to rely on. I would define a series of CSS files for each device or set of devices. So, you would include in your HTML the following lines:<link rel='stylesheet' type='text/css' href='desktop.css' media='screen'/> <link rel='stylesheet' type='text/css' href='[mobile].css' media='handheld'/> <link rel='stylesheet' type='text/css' href='print.css' media='print'/>
[mobile]
should be dynamically generated and it should point to a file corresponding to the user mobile device. Notice also that I didn’t forget about the printable version :). The CSS media target can be specified also with<style type='text/css' href='[mobile].css' media='handheld'> </style>
or directly in the CSS
@media handheld { /* mobile specific CSS rules */ }
If you want to split the devices and optimize for groups, one important criteria could be the screen resolution.
Even tough this is a very clean approach, as I already said, I would not recommend this to be the only one, especially for very large and complex websites. The mobile website should be a stripped down version, both in terms of layout (most important) and content.
- XHTML. Use XHTML instead of HTML. It is better structured and much easier to maintain and to automatically transform.
- Dynamically generated content. Even after using CSS for layout, you still have to have different HTML code, then consider generating automatically on the server the specific parts inside the page. For performance purposes you should have in mind using some kind of cache for those automatically generated parts.
If your entire website is automatically generated, like a portal or an e-commerce website, then you could have different layouts. But don’t fall into having too many device specific code. That should be maintained as well. - Transcoded content. It is the case when you already have a website, specifically a larger one, more likely on the non-mobile web, and you want to reach the mobile users as well. You should consider implementing an automatic translation tool between the two layouts. Of course, here a CSS based website will make your life much more easier. XHTML too.
As a translation tool, I would recommend XSLT. If your code is HTML, then you have to convert it first to XHTML.
Don’t forget to resize the images too. Many devices supports images only up to a certain file size, width or height.
This is a fast and cheap solution, but probably not the best one. Also take into account here the size of your website, otherwise the translation tool could become more expensive then recreating your entire website. And remember that it comes to a cost in performance. - Real life testing. Consider using for testing real mobile devices, not emulators. Use mobile devices with different screen resolution, operating systems, browsers. Don’t forget about touch screens either.
- Performance. Test for performance here too, but be careful how you define it. The bandwidth here is different: smaller pages, smaller images, different number of users etc.
If you’re interested in recognizing user’s mobile device on the server side, see my other article: Mobile device recognition.
I hope these will help you in outlining your mobile web personality, but as I said before, first take a step back and think of what exactly you need, want and invest in this.
The mobile web is for sure a different, but thrilling experience.
Fixing IE: CSS fixed position
I will start a series of articles about how you can fix some IE bugs. We all agree that IE is very buggy, but instead of whining, let’s see what we can do about it. So I will present you the problem, the fix, explain the fix and how you can EASILY INTEGRATE it.
Before, I will explain you a few things that I will use in these fixes. There are few different things in IE when it comes to CSS.
If you prefix a CSS property with _, IE will still recognize it. If you think this can be very useful. You can specify values for CSS properties only for IE. Let’s take an example.
color: green;
_color: red;
The font color will be green in all the browsers, except IE, where it will be red.
Instead of _, you can also use *, + and some other special signs.
The CSS value supports in IE a special value: expression([expr]). The [expr]
is a JavaScript expression, which is evaluated and the CSS value is assigned to it. Again an example:
_height: expression((1 + 1) + 'px');
The CSS height in the above example is actually 2 pixels in IE. In the expression we can even use this
to refer to the element for which the CSS value is applied.
Now let’s get back to the current issue. If you specify the fixed
value for the position
property, the element should be absolutely positioned, but relatively to the viewport, instead of the document. For more detailed info, please see the CSS specification. Not surprisingly, this works in most of the browsers, except IE.
And now, the fix. Which is actually very simple. We will use absolute positioning but we will calculate the coordinates dynamically relative to the viewport.
So the CSS code
position: fixed;
top: 10px;
left: 20px;
will have the following IE equivalent
_position: absolute;
_top: expression(((ignoreMe
= document.documentElement.scrollTop
? document.documentElement.scrollTop
: document.body.scrollTop ) + 10) + 'px' );
_left: expression(((ignoreMe
= document.documentElement.scrollLeft
? document.documentElement.scrollLeft
: document.body.scrollLeft ) + 20) + 'px' );
.
Don’t remove the ignoreMe
variable as this will force the expression to be evaluated not only the first time the page is loaded, but always.
In the end, a live example. And if you complain that in Mozilla the things are smoother, then simply switch to Mozilla :).
XSLT handler in ASP.net
Next I will explain you a solution that may come handy to you. First of all, the problem. Quite simple: you have some HTML pages, that you would like to include them in your pages, but in a different format/layout. Unfortunately you cannot modify the pages as you’re not the author. Just to understand better, let’s say that you want to include some Wikipedia articles in your pages. But they have to match with your own layout and you want only some sections of them.
So you have to transform an HTML into another. This sounds pretty much like XSLT and this is what we will use. It will be your task to write the XSLT. I’m only going to tell you how to integrate in your web application.
For the sake of simplicity and reuse, we will write an ASP.net handler for XSLT file that it will take the XSLT file and receive as a parameter the XML/HTML/XHTML URL and it will output the transformed result.
Find below the code for it:
public class XSLTHandler : IHttpHandler { // process the request public void ProcessRequest(HttpContext context) { // the XSLT argument list XsltArgumentList xslArg = new XsltArgumentList(); String url = context.Request.QueryString.Get("u"); // if no URL, either special or from u parameter then send a NOT_FOUND error if (url == null) { send(context.Response, HttpStatusCode.NotFound); return; } // adding the URL as an XSLT parameter xslArg.AddParam("url", "", url); try { // load the XML document at the specified URL XmlDocument xmlDoc = loadXMLDocument(url, "POST".Equals(context.Request.HttpMethod) ? context.Request.Form.ToString() : null); // load xsl String xslPath = context.Request.MapPath(context.Request.Url.LocalPath); // try to get the compiled XSLT from cache XslCompiledTransform xslTransform = (XslCompiledTransform)context.Cache.Get(xslPath); if (xslTransform == null) { // if the XSLT file doesn't exist, send a 404 if (!File.Exists(xslPath)) { send(context.Response, HttpStatusCode.NotFound); return; } xslTransform.Load(xslPath); // put the XSLT into cache context.Cache.Insert(xslPath, xslTransform, new CacheDependency(xslPath)); } // transform context.Response.ContentType = "text/html"; context.Response.Clear(); xslTransform.Transform(xmlDoc, xslArg, context.Response.Output); } catch (HttpException exc) { if (exc.GetHttpCode() == (int) HttpStatusCode.Redirect) { // if it is to be redirected, redirect to the url context.Response.Redirect(url); } else { // if any other error, then send the error code send(context.Response, (HttpStatusCode)exc.GetHttpCode()); } } catch (Exception) { // if other error send an internal server error response send(context.Response, HttpStatusCode.InternalServerError); } } // IHttpHandler method public bool IsReusable { get { return false; } } private void send(HttpResponse Response, HttpStatusCode StatusCode) { Response.Clear(); Response.StatusCode = (int)StatusCode; Response.StatusDescription = StatusCode.ToString(); Response.End(); } /// /// Load and returns an XML document from the given url. /// /// the url where is the XML content /// if not null the request will be a POST request /// and the parameters are sent along with the request; if null then the request method is GET /// the loaded XML document public static XmlDocument loadXMLDocument(String url, String postParams) { Object reader; if (url.StartsWith("/") || (!url.Contains(Uri.SchemeDelimiter))) { // if it is a local request then use the internal execute instead of making a web request // also the form and query string parameters are preserved StringWriter writer = new StringWriter(); HttpContext.Current.Server.Execute(url, writer, true); reader = new StringReader(writer.ToString()); } else { // create webrequest HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.Timeout = RequestTimeout; // you can also add a proxy //request.Proxy = WebProxy; if (postParameters != null) { // Set values for the request back request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; request.ContentLength = postParameters.Length; // Write the request StreamWriter stOut = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.ASCII); stOut.Write(postParameters); stOut.Close(); } // get the response of the request HttpWebResponse response = (HttpWebResponse)request.GetResponse(); // if something went wrong don't try to parse the document if (response.StatusCode != HttpStatusCode.OK) throw new HttpException((int)response.StatusCode, response.StatusDescription); if (response.ContentType.StartsWith("text/html")) { // if it is HTML transform it with SgmlReader // create instance of SGMLReader and change settings SgmlReader sgmlReader = new SgmlReader(); sgmlReader.WebProxy = Configuration.Proxy; sgmlReader.DocType = "HTML"; sgmlReader.CaseFolding = CaseFolding.ToLower; sgmlReader.InputStream = new StreamReader(response.GetResponseStream(), Encoding.UTF8, true); reader = sgmlReader; } else if (response.ContentType.StartsWith("text/xml")) { // if not html simply get it from the response reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8, true); } else { // redirect to the normal page, as it is nor HTML nor XML throw new HttpException((int)HttpStatusCode.Redirect, url); } } // load the XML document XmlDocument xmlDoc = new XmlDocument(); if (reader is TextReader) xmlDoc.Load((TextReader)reader); else if (reader is XmlReader) xmlDoc.Load((XmlReader)reader); return xmlDoc; } }
To explain shortly what happens: when a file like t.xslt
is requested, the XSLTHandler
will process the request. First it will load and compile the XSLT file using XSLTCompiledTransform
, trying first to load the XSLT compiled object from cache.
Next step is to retrieve the XML/XHTML/HTML content from the URL given as the url
parameter. If the content is HTML, then it will be transformed using an SGML parser into XHTML, so it can be transformed with XSLT. Then the actual processing takes place and the result is generated into the response output.
Of course the above code can be extended. You can put in the app configuration if you want to use the cache, what proxy to use, if you want to allow only URLs from some hosts to be parsed and so on. Also other parameters can be added as XSLT arguments: the URL of the XSLT file, the HTTP headers, the request parameters etc.
You saw in the code references to an SgmlReader and I also told you above about feeding the HTML content first into an SGML parser. But how to do it? The SgmlReader is already implemented and you can download it from here and use it like an XmlReader.
Last step is to configure the handler into the Web.config
, by simply adding the handler to process *.xsl
files:
<httpHandlers> <add verb="*" path="*.xsl" type="XSLTHandler" /> </httpHandlers>
That’s it!
Web publishing system with Apache and Subversion – part 3
Authorization
The previous posts were covering setup and authentication. But definitely you would like special rules for different groups and modules in your organization. It is not uncommon that a different group of developers will work on different module(s).
For this you have to use the mod_authz_svn
module that comes with the Subversion module and activate it by adding the below line at the beginning of <apache-dir>/conf/extra/wps.conf
:
LoadModule authz_svn_module modules/mod_authz_svn.so
Then to configure the access policy you have to add to every <Location>
section that defines Subversion repositories:
AuthzSVNAccessFile "/wps/svnaccess.conf"
where /wps/svnaccess.conf
is the file holding your access policy.
I will try now to explain the syntax and semantic of this access policy file using a small example:
[groups] admins = admin1, admin2 web-developers = john, marry backstage-developers = harry, sally [/] * = @admins = rw [web:/] @web-developers = rw @backstage-developers = r editor = rw [stage:/] @web-developers = rw @backstage-developers = rw
You can see that the file is an usual properties files divided in sections, each section beginning with [<section-title>]
and ending at the beginning of the next section. The first section defines the groups of users. The users in group are comma separated. It is obvious that john and marry are web developers and there are two administrators (admin1, admin2).
The following sections describes access rules to repositories and directories. The section title has the format repository:path
and it specifies a rule for the given path inside the repository. If you don’t specify a repository or a path it will define the rules for all (repositories/paths). Inside these sections every line describing an access rule has the format
username = permissions
or
@groupname = permissions
If the username
is *
, then it refers to all the users.
The permission can be an empty string for granting no permission, r
for reading, w
for writing and rw
for reading/writing.
Taking into account all this you can see that by default nobody has access to the repositories and administrators have read/write access to everything. Further on, the backstage developers have read access to the website and read/write access to the backstage environment.
More information on these you can find on the SVN book in the section Per-Directory Access Control.
Backstage environment
You already saw that I used the term backstage. This refers to a website with access only for the internal users that it is used for reviewing purposes. Before putting something on your website you may want to be reviewed by certain groups and modified accordingly. Of course we will also have a corresponding Subversion repository as for the main website.
So we will do the same creation of the Subversion repository, create the directory for the web folder, checkout the repository, add the hook and configure Apache. I will only explain here how to write the configuration for Apache, as this is a little bit different because it should be only accessible for internal users. Practically you will add the same authentication configuration as for the Subversion repositories.
<Directory "/wps/backstage"> AllowOverride None Options None Order allow,deny Allow from all AuthName "Backstage Authentication" Require valid-user # for basic authentication AuthType basic AuthUserFile /wps/passwd </Directory> Alias /backstage /wps/backstage ScriptAlias /backstage/cgi-bin/ "/wps/backstage/cgi-bin/" # # Use name-based virtual hosting. # NameVirtualHost *:80 <VirtualHost *:80> ServerAdmin administrator@domain.com DocumentRoot "/wps/backstage" ServerName backstage.domain.com ServerAlias www.backstage.domain.com ErrorLog "logs/backstage.log" ScriptAlias /cgi-bin/ "/wps/backstage/cgi-bin/" </VirtualHost>
Conclusions
If we go back to the first post from this series then you can see that we met all the requirements for the desired web publishing system. It is free, OS independent and can be easily installed and setup (a few hours even for the very junior administrators).
The authentication and authorization mechanism can be customized to a very high degree to meet your needs.
Web developers can easily access the Subversion repository using TortoiseSVN. Or even from Dreamweaver (although this extension isn’t free and I haven’t tested it).
If you also install websvn, you will RSS feeds for all the changes made to your website.
And, most importantly, you have all the versioning system advantages brought to your website.
Web publishing system with Apache and Subversion – part 2
If you read the first part of this post, you probably know by now how to install and configure a web publishing system using only Apache and Subversion. But your system will miss one of the most important thing: authentication. So let’s get started and tackle this.
Authentication
We kept all the Apache configuration settings related to Subversion and the website in the file <apache-dir>/conf/extra/wps.conf
and further on we will modify this file.
Remember the below section located either in the main server or in a virtual one section?
<Location /svn > DAV svn SVNParentPath /wps/svnrepo SVNListParentPath On </Location>
We will modify this one to add authentication and authorization.
<Location /svn > DAV svn SVNParentPath /wps/svnrepo SVNListParentPath On AuthType basic AuthName "SVN repository" AuthUserFile /wps/passwd Require valid-user </Location>
The user database will be kept in the plain text file /wps/passwd
. To add or modify users you can use the htpasswd
utility. So let’s add a developer account:
htpasswd -c /wps/passwd developer
You will be prompted for the password. Later on you can change it with:
htpasswd /wps/passwd developer
.
There are also some other ways to authenticate users, by keeping the users in a database file or using LDAP. You have to specify the authentication provider and use the specific module settings: mod_authn_file
, mod_authn_dbm
,
mod_authn_dbd
,
and mod_authnz_ldap
.
Windows authentication
You can also use Windows domain authentication, but this will require just a little bit more work from your side. Anyway this may come in handy in some big organizations, where you don’t want to create special accounts only for this and enable users to use their usual Windows logon credentials.
First of all you have to download the SSPI authentication module and copy it to <apache-dir>/modules
. Then add the following line at the beginning of <apache-dir>/conf/extra/wps.conf
:
LoadModule sspi_auth_module modules/mod_auth_sspi.so
and the below lines to the Location
section corresponding to the SVN repository:
AuthName "Windows Authentication" AuthType SSPI SSPIAuth On SSPIAuthoritative On # set the domain to authorize against SSPIDomain your.windows.domain # keep domain name in userid string SSPIOmitDomain On SSPIUsernameCase lower SSPIOfferBasic On # basic authentication shouldn't # have higher priority SSPIBasicPreferred Off Require valid-user
Now lets’ discuss in a little bit more in detail the above configuration settings:
SSPIAuth
– this will turn on/off the Windows authentication moduleSSPIAuthoritative
– this will turn on/off if the the Windows authentication is mandatory or if other modules can be used as a fallbackSSPIDomain
– the IP address or name of your windows domain controller against which the authentication is runSSPIOmitDomain
– if it is On then the domain name is omitted from the user name; so if the user isDOMAIN\user
, the user name for Apache and Subversion will actually beuser
and notDOMAIN\user
.SSPIUsernameCase
– tells how the user name letter cases are converted. The possible values arelower
andupper
. If this is not specify then no conversion is made. If you specifylower
(recommended) then the user nameDOMAIN\User
will be transformed todomain\user
(if you also specifySSPIOmitDomain On
, then the name will becomeuser
)SSPIOfferBasic
– SSPI by default uses NTLM, a Microsoft proprietary protocol which only IE (and other Windows components/application) understand, so they are able to authenticate you automatically. If you setSSPIOfferBasic On
means that it is still authenticating against your Windows domain on the backend, but when it asks the client for a password, it does so using standard HTTP Basic authentication. So if you plan to use other clients to your Subversion repository than IE you must set this on and the client then will prompt you for the domain name and password. This is definately needed if you use TortoiseSVN.SSPIBasicPreferred
– if it is On then basic authentication will have higher priority
The authentication possibilities are endless and are depending only on your imagination and needs. I was focusing on these two types as they will probably appear more often: basic in a low or mid-size company and Windows authentication can be smoothly integrated in a big company infrastructure with Windows desktops for the big part of users.
Authorization, setting up a second repository and conclusions will follow soon.
Web publishing system with Apache and Subversion – part 1
Introduction
What does a versioning system have to do with the web and more specifically with a publishing system?
When developing small websites (mainly presentation ones) you usually don’t need a versioning system. This post will come very handy to you if you work on a bigger team developing an enterprise (not necessarily, presentation) website. There a versioning system is clearly needed: user concurrency, history backup, a central repository, basically the main features of such a system.
I have chosen Subversion as the concurrent versioning system, not only as being the latest in style ;), but also for some features which makes it perfect, easy to use and easy to setup. In a few words we want a web publishing system that:
- mandatory: is free
- mandatory: is easy to setup for administrators
- mandatory: is very easy to use for (web) developers
- mandatory: features versions for the web pages, so you will be able to see changes in time and revert to previous versions
- mandatory: provides an authentication and authorization system
- nice to have: the authorization system can be configured per module, meaning that different groups of developers can have read/write access to different sections of the website
- nice to have: able to send notifications every time a web page is modified
- nice to have: is customizable and able to perform specific tasks whenever a change is made (add/edit/delete web pages)
- nice to have: running on multiple OSes
Taking all this into account, what is the solution? I will continue by describing the steps how to setup and use such a system.
Installation
First of all, download and install Apache 2.2 and Subversion. The installation is very easy and I will not enter here in any details (there are even binary packages, aka installers, for all the main operating systems:) ).
Configuration
Now let’s get to the server configuration. For the sake of the example, let’s suppose that we will use the /wps
or c:\wps
(for Windows) folder for the entire thing. We will create the svnrepo
subfolder as a parent for all Subversion repositories and then create a Subversion repository for the website using the svnadmin
command:
svnadmin create /wps/svnrepo/web
or in Windows
svnadmin create c:\wps\svnrepo\web
Let’s go now and configure Apache so that you can access Subversion repository through it. We don’t use the lightweight svnserve standalone server because we already have Apache installed as a web server. The configuration steps are:
- Copy the files
mod_dav_svn.so
andmod_authz_svn.so
from<svn-dir>/bin
to<apache-dir>/modules
, where<svn-dir>
is the Subversion installation directory and<apache-dir>
is the Apache installation directory (usuallyC:\Program Files\Apache Software Foundation\Apache2.2
in Windows). - Add the following line to the Apache configuration file
<apache-dir>conf/httpd.conf
:
Include conf/extra/wps.conf
- Create and edit with your favorite text editor the file
<apache-dir>conf/extra/wps.conf
.Paste the below content into:
LoadModule dav_svn_module modules/mod_dav_svn.so LoadModule authz_svn_module modules/mod_authz_svn.so <IfModule dav_svn_module> # if the module was loaded succesfully # to exclude SVN files from web published folders <Directory ~ "/.svn"> Order allow,deny Deny from all </Directory> # if you want a virtual host for Subversion <VirtualHost *:80> ServerAdmin administrator@domain.com ServerName svn.domain.com <Location / > DAV svn SVNParentPath /wps/svnrepo SVNListParentPath On </Location> ErrorLog "logs/wps.log" </VirtualHost> # the Subversion folder <Location /svn > DAV svn SVNParentPath /wps/svnrepo SVNListParentPath On </Location>
If you restart your Apache web server you will be able to access the SVN repository at http://domain.com/svn/web or at http://svn.domain.com/web.
Now we will create a web
folder to host the website files and then checkout the web repository into it. Note that if you’re going to use a trunk/tags/branches
directory organization (which I definately recommend) in the repository then you should checkout in the web
folder only the trunk
:
svn checkout http://domain.com/svn/web/trunk /wps/web
As you noticed by now the /wps/web
is a working copy of the repository. But checking out the repository is not enough, you have to set up a commit hook to automatically update the working copy every time a change is made. Create a file post-commit
in Unix (don’t forget to change the x mode – chmod 775 post-commit) and post-commit.bat
in Windows with the following content:
svn update /wps/web
Every time a change is commited into the Subversion repository that change gets into the web
directory too. Of course a .svn
directory is created under each directory. You don’t have to delete them, the Directory ~ "\.svn"
Apache section in wps.conf
will deny access to these directories.
Now you have only to add a few lines to wps.conf
to enable access to the web
folder:
DocumentRoot "/wps/web" ServerAlias www.domain.com <Directory "/wps/web"> Options Indexes FollowSymLinks AllowOverride None Allow from all </Directory>
Note: Please don’t forget to restart Apache every time you change the configuration files.
Now everything should be up and ready. But everyone can commit to your web repository and you definately don’t want this.
In the next parts I will explain how to setup an authentication and authorization system and how to create a second web repository accessible only within your organization.
AJAX vs DHTML
AJAX is gaining a lot of adopters these days and it seems to be like the new cool kid in town. If you’re in the web development area, you must use AJAX, otherwise you’re not cool at all.
But more than this, AJAX became a buzzword. And I definitely don’t like buzzwords. Because amateurs would try to use that word for everything that is related to that area.
We have to clearly see the difference between AJAX and DHTML. AJAX stands for Asynchronous JavaScript and XML and it is simply a way to communicate with a web server without making a new request in the browser. And this will happen more likely without user really feeling it. DHTML stands for Dynamic HTML and it is a set of technologies used for dynamically modifying a web page, usually incorporating JavaScript and CSS.
AJAX and DHTML are not excluding each other, but working together. With DHTML you can dynamically modify a site using client-side code. But sometimes you cannot simply have everything on the client side (or it could be too expensive to have it) and you should interact in some way with the server-side code. And then AJAX comes into stage, just to simply create a connection between some JavaScript client-side code (on an DHTML site) and a web server. Before this, you could have used signed Java applets or Flash, but this offers the opportunity of a pure JavaScript solution.
Actually you could even say that AJAX is a small part from DHTML, but people are using it the other way around. I have to admit that even the name was chosen to fit a new upcoming buzzword. With AJAX (Asynchronous JavaScript and XML) you can even do synchronous requests and handle HTML or plain text, even tough the initial intent and good practice is not to do it.
I know that it is cool to use all these new buzzwords, but it will be much more correct to use the right term.
For a good AJAX reference you can use Wikipedia. Read also the articles referenced at the end, I would especially recommend the ones from IBM developerWorks.
Centering a DIV using CSS
Using DIVs instead of TABLEs and CSS formatting should be a good practice for any web developer/designer. But how would you center a DIV? If you’ll do a page and you’ll want to optimize the contents for a specific resolution and center (this should be default and desired way) the contents then this will be one of your first CSS questions.
The answer is very easy. Supposing you have the following HTML fragment
<body> <div id="content">My page</div> </body>
Then centering the content
DIV will be made using the following CSS code:
BODY { text-align: center; min-width: 800px; } DIV#content { margin-left: auto; margin-right: auto; width: 800px; text-align: left; }
Now let’s see what exactly the code is doing. Centering the div horizontally is pretty easy. Simply specify a width (this is mandatory, otherwise it won’t work – usually it will be the resolution for which you want to optimize) and then set the right and left margin widths to auto.
Unfortunately only this won’t do the trick in IE (big surprise :D) and it will require another small hack. You have to set the text-align
property for the BODY to center
and then redo it for the DIV.
One last thing: in Mozilla, when resizing the window, a part of the DIV will fall on the left of the page, making the page unusable. Simply specify a min-width
for the BODY
.
If you take into account that you can replace BODY with any other DIV
, then you have a general DIV centering method in just a few lines of CSS code.