by Nik Kalyani
Monday, March 21, 2005 8:38:07 PM (Pacific Standard Time, UTC-08:00)
The April 2005 issue of Wired Magazine has interesting subject matter on the “infoporn” page. “The Outsourcing Myth” provides information on the state of outsourcing. As people who go by facts probably already know, U.S. companies import more jobs than they export — $18 billion worth in 2003 ($43.5B outsourced; $61.4B insourced). Most of the jobs insourced in the U.S. are corporate legal and accounting jobs according to the article.
Some other numbers;
Outsourced Insourced ========== ========= U.S. $43.5B $61.4B U.K. $19.7B $41.2B Germany $41.4B $26.5B France $23.2B $23.1B Netherlands $21.0B $20.1B India $11.8B $18.6B Japan $24.7B $17.4B Austria $16.6B $13.8B Singapore $9.2B $13.0B China $8.0B $10.4B
by Nik Kalyani
Sunday, March 20, 2005 12:49:43 PM (Pacific Standard Time, UTC-08:00)
The DotNetNuke Control Panel is generally positioned at the top of a page (although it can be positioned anywhere). It is displayed if a user has administrative rights to a page. After the initial layout of a page, the Control Panel is not often needed, but it continues to take up valuable screen real-estate. I saw a recent discussion on this issue in the ASP.net forums, and decided that it presents an interesting U.I. challenge. (Turns out it really isn’t much of a challenge.)
At Bryan Andrews’ suggestion, I had actually developed a script modeled after the Windows Terminal Services Client collapsible admin bar some months back. The bar is visible at the top, but rolls up after a few seconds. I posted the script to the Core Team forum, but unfortunately, there was no interest.
Anyway, getting back to the Control Panel solution. I like the Terminal Services client concept and modeled my solution after it. One of my design goals was to keep things really simple and not require any external images or the like.
My solution works as follows:
1) My script block needs to be inserted into any DNN skin, replacing the existing element with id=”ControlPanel”
2) It renders the Control Panel at the top of the page (coordinates 0,0). The Control Panel is initially hidden and is represented by a 5px tall red bar.
3) Move your mouse over the bar and the Control Panel is displayed over page content. This is an important difference from the standard Control Panel as the page content remains in place and does not scroll down.
4) When the mouse cursors is moved over the red bar one more time, the Control Panel smoothly rolls up.
Here is the code. Just paste it into a DNN skin ascx file, taking care to replace the element with id=”ControlPanel” that should already be present in the file.
<!-- Begin collapsible Control panel code -->
<script language="Javascript">
var rollUp = false; function collapseElement(objId) { o = document.getElementById(objId); h = parseInt(o.style.height); if (h > 6) { h--; o.style.height= h + "px"; window.setTimeout("collapseElement('" + objId + "')",5); } else rollUp = false; }
function displayElement(height) { obj = document.getElementById("<%= ControlPanel.ClientID %>");
if (rollUp) collapseElement("<%= ControlPanel.ClientID %>"); else { rollUp = true; obj.style.height = height; } } </script>
<style>
.ControlPanelHeader { height:5px; width:100%; background-color:#cc0000; padding-top:5px; overflow:hidden }
.RollUpControlPanel { position:absolute; top:0px; left:0px; z-Index:1000; height:5px; width:100%; overflow: hidden; }
</style>
<div class="RollUpControlPanel" id="ControlPanel" runat="server"> <div class="ControlPanelHeader" onmouseover="displayElement('110px')"> </div> </div>
<!-- End collapsible control panel code -->
by Nik Kalyani
Sunday, March 13, 2005 1:23:21 PM (Pacific Standard Time, UTC-08:00)
nokiko asks:
I am working on some new containers and dnn enhancements. I want to know in my container which moduleid is loaded so lets say i have an ascx container and in there i have the following:
<div id=''mod_<% functiontogetmoduleidhere'">rest of container ...</div>
Is there a function or another way that in an ascx container I would know which moduleid is loaded in there.
So that way lets say i have 2 modules one one page and i want the ascx container to render
<div id="mod_36">content here</div> <div id="mod_37">content here</div>
i would need this to imge replace techniques on my h3 tags for instance
#mod_36 h3 { background:url(announcements.gif) } #mod_37 h3 { background:url(events.gif)}
Nik’s answer:
That’s an excellent question nokiko. In DotNetNuke 3.x, the Container class has a public, static method called GetPortalModuleBase(). This method requires one parameter — a usercontrol that is present in the container. With this knowledge, it is easy to figure out a solution. For example, most of the default DNN containers have a title defined using: <dnn:TITLE runat="server" id="dnnTITLE" />. In any of these containers, you can add the following line to display the ModuleId of the module present in the container:
<%= DotNetNuke.UI.Containers.Container.GetPortalModuleBase(dnnTitle).ModuleId.ToString() %>
You can also use the same technique to display any other Portal, Tab, Module or User property to customize the appearance of containers dynamically:
<%= DotNetNuke.UI.Containers.Container.GetPortalModuleBase(dnnTitle).PortalSettings.PortalName %> <%= DotNetNuke.UI.Containers.Container.GetPortalModuleBase(dnnTitle).PortalSettings.ActiveTab.TabName %> <%= DotNetNuke.UI.Containers.Container.GetPortalModuleBase(dnnTitle).ModuleConfiguration.ModuleTitle %> <%= DotNetNuke.UI.Containers.Container.GetPortalModuleBase(dnnTitle).UserInfo.FirstName %>
If you don’t have a need for the DNN Title control in your container, that’s OK. You can add it anyway with an attribute of “Visible=false”. It is possible to achieve this result without using any skin control, however it requires a little more code and is not worth the effort in most situations.
by Nik Kalyani
Thursday, March 10, 2005 11:26:34 AM (Pacific Standard Time, UTC-08:00)
I have been a user of Groove products since its early beta. Mostly because of my Lotus Notes roots and my firm belief that Ray Ozzie is one of the few true visionaries in the software industry. It was a pleasant surprise when an MSN SMS a few minutes ago notified me that Microsoft is going to acquire Groove Networks (story).
This is fantastic news. Groove has always been strong on collaboration, and now I expect there will be better integration with .Net. I love it when my favorite technologies converge.
by Nik Kalyani
Wednesday, March 09, 2005 3:57:40 PM (Pacific Standard Time, UTC-08:00)
How do you create a page that allows a user to select the font size?
Nik’s answer:
You can do this using CSS or Javascript or a combination of the two. Here is some sample HTML code with embedded CSS that demonstrates how it is done:
<html> <style>
.xsmall { font-size: 0.5em; } .medium { font-size: 1.0em; } .large { font-size: 1.5em; } .xlarge { font-size: 2.0em; }
.fauxLink { text-decoration: underline; cursor: hand; }
</style>
<body> <p>This code demonstrates how to make a web page with user selectable font sizes. The trick is to use CSS with relative point sizes.</p> <p>You can set the "font-size" CSS attribute to an "em" value. This is the value of the character box for a given font (i.e. the "M" character). Using an em value sets the font size to a relative value. For example, 1.3em is the same as 130%.</p> <p> <span class="fauxLink" onClick="document.body.className='xsmall'">x-small</span> | <span class="fauxLink" onClick="document.body.className='medium'">medium</span> | <span class="fauxLink" onClick="document.body.className='large'">large</span> | <span class="fauxLink" onClick="document.body.className='xlarge'">x-large</span> </p>
<div> Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nullam eu sem. In commodo. Integer id lectus a enim fermentum molestie. Donec fringilla ligula vel justo. Aliquam elementum wisi luctus lacus. Etiam leo. Duis auctor elit quis mauris. Ut elit lectus, luctus quis, rhoncus sed, lacinia in, velit. Pellentesque vulputate congue magna. Proin luctus. Pellentesque rhoncus, metus eget laoreet ornare, lacus risus mollis nibh, non interdum augue sapien nec metus. Donec sem. Vestibulum rutrum, urna non cursus tristique, tellus erat sagittis turpis, at venenatis massa massa egestas pede. Praesent ultrices malesuada nisl. Etiam ac felis eget ligula venenatis suscipit. Suspendisse ultricies lacus eu eros. Aliquam bibendum adipiscing ante. </div> </body> </html>
As you can see, it is very easy to accomplish this. However, this is only half the solution. Presumably the user wants the size selection to persist so it’s a good idea to use a cookie to store their preference. Then, no matter which page they visit on your site, text will be displayed at their preferred size. Furthermore, when they return to your site at a later date, depending on the duration of the cookie, the site will automatically adjust the size.
For DotNetNuke, I created a component that does this for you (by allowing the user to select a stylesheet for the page and remembering it for future visits). This is part of the Speerio SkinWidgets suite. You can see a demo here.
by Nik Kalyani
Tuesday, March 08, 2005 10:36:49 PM (Pacific Standard Time, UTC-08:00)
I am working on a web app that consists of a treeview on the left and content on the right. The basic layout is a table with the height of the content determining the overall height of the table.
This basically means the treeview has to be scrollable, since it's possible that the table height is less than the tree height. Simple enough...put the treeview inside a DIV and change its "height" style attribute to 100%.
Works in IE6 but does not in FireFox. After Googling for hours and not finding a good solution, I devised a function to make this work. In my function, the params are the ID's of the main table and the DIV containing the treeview. The code first sets the height of the scrollable DIV to 0. This allows the main table to adjust to its normal height. Then the DIV's height is set to the same as the table height.
Works in IE6 and FireFox.
function setScrollerHeight(mainTableId, explorerScrollerId) { var mainTable = document.getElementById(mainTableId); var scroller = document.getElementById(explorerScrollerId); if (!scroller) return; if (mainTable.clientHeight > 0) { scroller.style.height = "0px"; scroller.style.height = mainTable.clientHeight; } else scroller.style.height = "500px"; }
by Nik Kalyani
Thursday, March 03, 2005 1:54:21 PM (Pacific Standard Time, UTC-08:00)
The current DotNetNuke release (3.0.11) is quite stable and I think it will not be very long before we see a Release Candidate.
This is the first release for which I have been a Core Team member from start to release and I have found the experience to be nothing short of amazing. The thing that fascinates me the most is how effective IM/Email/Forums are for team collaboration. If ~40 people, spread out all over the world in different timezones can collaborate to create such a fantastic product, it begs the question, is this a fundamentally better way to work in a global economy?
I know face time is important and that in certain businesses, it is essential. But maybe in the software industry, greater productivity gains are possible through online collaboration. And of course, savings too since you won’t need as many Aeron chairs.
Before I became a Core Team member, I always imagined that there was a lot of behind-the-scenes “bureaucracy” and structure with secrets and such. Now I know that this is far from the truth. The Core Team is really just a group of people with a shared vision, operating under some well-defined, but broad guidelines. Each person contributes to the extent her/his time and talent allows. Then thanks to a devoted few of the senior members, things magically fall into place. There are no mission statements, UML diagrams, team-building exercises and most important, no monetary compensation. Makes you wonder, doesn’t it?
by Nik Kalyani
Thursday, March 03, 2005 1:28:41 PM (Pacific Standard Time, UTC-08:00)
Das Blog has its own skinning solution which consists of “themes” defined in web.config, corresponding to theme sub-folders that contain templates. The templates are basically HTML fragments with tokens that indicate where content will reside. I have been tinkering with the templates quite a bit to make sure I understand all their capabilities as I progress in making Das Blog work with DNN.
One thing that already caught my eye is that the Das Blog skinning solution is not very granular, so skinning flexibility is limited. For instance, you can specify where a blog entry will appear in one template and in another, you can specify the layout for the item. However, there is no mechanism to control what appears between individual items or if you would like a different appearance for alternating items. Having this capability does not have much appeal in general, but there are some specific situations where I think it would be useful — displaying ads in-line, automatically displaying related items using the Technorati API etc. Now the latter is clearly not something that belongs in the skinning department, however, even if implemented in the BL, there is no mechanism to skin it differently from the blog item.
One really neat thing about DasBlog is that most of the user-facing aspx pages are just shells for user controls. This is promising because it makes the process of conversion to DNN modules that much easier. I think I should be done with my skinning analysis in another 3–4 days, at which time I will continue the code conversion.
by Nik Kalyani
Wednesday, March 02, 2005 9:56:49 PM (Pacific Standard Time, UTC-08:00)
I have had it with designing cross-browser web pages without tables.
OK, I get it. An HTML table was never intended to be used for pixel-perfect design layouts. For that matter, HTML was never intended to be used for pixel-perfect design layouts, period.
But, I have tried to go with the flow. The design gurus say, don’t use tables…use CSS. But from a productivity standpoint, that stinks. I have spent countless hours trying to get CSS-based <div> elements to do my bidding. And you know what. I am done. I will try and use CSS-based layouts when possible, but if I have to spend more than a few minutes trying to get things to position properly, I am back to using tables.
Now, I am no designer, but then I am not completely clueless about CSS either. I tell you, with the number of workarounds/hacks that you have to use to get cross-browser layouts to work using CSS only, I am surprised that people still continue to pursue this type of design. Doesn’t productivity matter more than “View source…”?
There is nothing inherently wrong with tables in HTML unless your overall page is one giant table. Then the user is going to wait until the browser can figure out how to lay the table out. For intermediate content, I don’t see how using tables is a big problem if my targeted browsers are IE and Firefox and I use them for layout of elements within the page. (Yes…I am aware that there are other browsers, but just as people have a choice of browser, I have a choice of the browsers I will support.)
I develop web applications. If my app enables the user to complete the task it was intended for quickly and easily, then as far as I am concerned, tables or no tables, my app is a success. So until all the browser developers get their act together, I am going to keep my tables.
by Nik Kalyani
Tuesday, March 01, 2005 11:30:03 AM (Pacific Standard Time, UTC-08:00)
Microsoft has altered the default security settings for web content running in the 'Local Machine Zone', that is, any web page loaded from a local source such as your hard disk or CD-ROM. The result is a warning message when such a page is loaded. To avoid this message you must place your web page into the secure 'Internet Zone'. To do this paste the following comment at the start of your html source:
<!-- saved from url=(0014)about:internet -->
by Nik Kalyani
Tuesday, March 01, 2005 1:14:05 AM (Pacific Standard Time, UTC-08:00)
If you blog often, then Das Blog and BlogJet are an amazing combination. I only started using BlogJet recently, and I tell you, I am hooked. This is an amazing program. It is quite intuitive and makes blogging very simple and efficient.
by Nik Kalyani
Tuesday, March 01, 2005 12:31:08 AM (Pacific Standard Time, UTC-08:00)
When DNN 3.x is released, it will be the most-changed of any version in DNN’s short history. In consolidating numerous breaking changes, the Core Team (of which I am proud to be a member) took it upon itself to also get rid of some legacy baggage that DNN was carrying around from its IBuySpy roots. A richer U.I., Search, static localization, performance optimizations, content syndication and import/export data are some of the cool features that DNN 3.x sports.
There are also some warts such as FriendlyUrls, stylesheets gone crazy and overall inconsistent user interaction.
However, this is to be expected in the evolution of any open source project. Change takes time and improvements come as needs evolve.
If you don’t have a huge dependency on third-party modules, I would highly recommend taking the plunge. DNN 3.x is a fantastic piece of software and will enable you to create websites that are better looking and more feature-rich. I am going to work on upgrading all of my modules as quickly as possible to run on DNN 3.x portals.
by Nik Kalyani
Tuesday, March 01, 2005 12:20:18 AM (Pacific Standard Time, UTC-08:00)
Microsoft Word creates awful HTML code. If you need to use it on a web page (especially in an HTML editor), you will want to clean it up a bit. Here’s a Javascript function that will take a string of text copied from Microsoft Word, and return it minus all the extraneous formatting that Word adds:
function cleanWordContent(wordContent) {
wordDiv = document.createElement("DIV"); wordDiv.innerHTML = wordContent;
for (var i=0;i<wordDiv.all.length;i++) { wordDiv.all[i].removeAttribute("className","",0); wordDiv.all[i].removeAttribute("style","",0); } wordContent = wordDiv.innerHTML; wordContent = String(wordContent).replace(/<\\?\?xml[^>]*>/g,""); wordContent = String(wordContent).replace(/<\/?o:p[^>]*>/g,""); wordContent = String(wordContent).replace(/<\/?v:[^>]*>/g,""); wordContent = String(wordContent).replace(/<\/?o:[^>]*>/g,""); wordContent = String(wordContent).replace(/ /g,"");//<p> </p> wordContent = String(wordContent).replace(/<\/?SPAN[^>]*>/g,""); wordContent = String(wordContent).replace(/<\/?FONT[^>]*>/g,""); wordContent = String(wordContent).replace(/<\/?STRONG[^>]*>/g,""); wordContent = String(wordContent).replace(/<\/?P[^>]*><\/P>/g,""); wordContent = String(wordContent).replace(/<\/?H1[^>]*>/g,""); wordContent = String(wordContent).replace(/<\/?H2[^>]*>/g,""); wordContent = String(wordContent).replace(/<\/?H3[^>]*>/g,""); wordContent = String(wordContent).replace(/<\/?H4[^>]*>/g,""); wordContent = String(wordContent).replace(/<\/?H5[^>]*>/g,""); wordContent = String(wordContent).replace(/<\/?H6[^>]*>/g,"");
return(wordContent);
}
by Nik Kalyani
Tuesday, March 01, 2005 12:15:14 AM (Pacific Standard Time, UTC-08:00)
.Net allows you to do some amazing things with graphics using GDI+. Its handling of image meta data leaves something to be desired though. It’s like they started to do something, but then abandoned it mid-way. In working on the Speerio File Manager Pro (for ASP.Net and DotNetNuke) product’s imaging functions, after considerable research, I decided that Omar Shahine’s PhotoLibrary was the best fit. It has some excellent functions especially in the EXIF area.
After playing with it for a while, I was disappointed to find that it, like just about every other EXIF-handler Google can find, does not preserve EXIF data on resize and rotate operations. I saw some commercial .Net components did it, so I knew could be done. But hours of searching later, all I knew for certain is that GDI+ can read EXIF data, but cannot write it to a new image. Bummer!
Not being one to give up easily, I experimenting a bit, and am pleased to have come up with a solution. Here is my JPEG resizing code which preserves EXIF data:
public static void ResizeJpeg(string filePath, int width, int height) { try
{
System.Drawing.Image imgPhoto = System.Drawing.Image.FromFile(filePath);
int sourceWidth = imgPhoto.Width;
int sourceHeight = imgPhoto.Height;
if (height == -1)
height = (int) ((float) sourceHeight * ((float) width/ (float) sourceWidth));
if (width == -1)
width = (int) ((float) sourceWidth * ((float) height/ (float) sourceHeight));
int sourceX = 0;
int sourceY = 0;
int destX = 0;
int destY = 0;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)width/(float)sourceWidth);
nPercentH = ((float)height/(float)sourceHeight);
//if we have to pad the height pad both the top and the bottom
//with the difference between the scaled height and the desired height
if(nPercentH < nPercentW)
{
nPercent = nPercentH;
destX = (int)((width - (sourceWidth * nPercent))/2);
}
else
{
nPercent = nPercentW;
destY = (int)((height - (sourceHeight * nPercent))/2);
}
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
Bitmap bmPhoto = new Bitmap(imgPhoto, width, height);
bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);
Graphics grPhoto = Graphics.FromImage(bmPhoto);
grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
grPhoto.DrawImage(imgPhoto,
new Rectangle(destX,destY,destWidth,destHeight),
new Rectangle(sourceX,sourceY,sourceWidth,sourceHeight),
GraphicsUnit.Pixel);
grPhoto.Dispose();
try
{
foreach (PropertyItem propertyItem in imgPhoto.PropertyItems)
{
try
{
bmPhoto.SetPropertyItem(propertyItem);
}
catch{}
}
}
catch{}
System.Drawing.Imaging.Encoder enc = System.Drawing.Imaging.Encoder.Quality;
System.Drawing.Imaging.EncoderParameters encParms = new EncoderParameters(1);
System.Drawing.Imaging.EncoderParameter encParm = new EncoderParameter(enc,100L);
encParms.Param[0] = encParm;
ImageCodecInfo[] codecInfo = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo codecInfoJpeg = codecInfo[1];
bmPhoto.Save(filePath+".tmp",codecInfoJpeg, encParms);
bmPhoto.Dispose();
imgPhoto.Dispose();
FileInfo fileInfo = new FileInfo(filePath);
fileInfo.Delete();
fileInfo = new FileInfo(filePath+".tmp");
fileInfo.MoveTo(filePath);
}
catch{}
}
|