Welcome To My Web Site!

I'm a Software Developer and Musician living in Concord, North Carolina.  I've created this site as a means to share my passions for Software Development and Music.  I consider myself one of the lucky few who really enjoy their work. I do a lot of open source software development, my mojoPortal project was actively developed from 2004 until late 2014 and was very popular for quite a while but the older technology it was built on is going out of style these days. More recently I'm very excited about my new open source projects built on the new ASP.NET Core framework. cloudscribe Core and cloudscribe SimpleContent are the primary projects but you can see the full list of my open source projects on github. I also like to help people with ASP.NET Core questions on stackoverflow.

This site is currently running on an older version of mojoPortal, but I am working on rebuilding the site with my new SimpleContent project and plan to start blogging more regularly once I finish that. You can see that I've been a not very prolific blogger on this site but I've been blogging a little for many years. During the years when mojoPortal was popular I blogged more frequently on the mojoPortal.com site and this one fell into neglect. An interesting point is I've always used software that I wrote myself for blogging, I built the blog module for a thing called RainbowPortal way back in 2003, then I built a new one with mojoPortal in 2004, and now in 2016 I'm building cloudscribe.SimpleContent. Hopefully the new one will be good for the next 15 years or so, but no doubt technology will keep evolving. Follow me on Twitter.
 

profile for Joe Audette at Stack Overflow, Q&A for professional and enthusiast programmers

what was i thinking...

Sending SMTP Email on ASP.NET Core with MailKit 

Sunday, May 8, 2016 8:15:00 AM Categories: ASP.NETCore Open Source
Rate this Content 0 Votes

If you are porting web applications to ASP.NET Core or building new web applications, you may notice that the System.Net.Mail namespace is not implemented in .NET Core. My understanding is that there are intentions to implement that namespace later, but it may be a little stumbling block for early adopters, since sending email is a very fundamental thing that most web applications need to do for common tasks such as verifying an email address for a new account or facilitating password reset.

There are some examples out there for sending email with various 3rd party services such as SendGrid, MailGun, Elastic Email, and the like, by using REST APIs to send the mail instead of using SMTP, and that is certainly a good option to consider. But for those who already have an SMTP server that they want to use, a better solution is needed. The good news is a better solution already exists and it works currently with RC1 of ASP.NET Core and will most surely also be available for RC2 and later releases.

There are actually 2 related projects that you should know about, MailKit and MimeKit. The goal of the MailKit project is “to provide the .NET world with robust, fully featured and RFC-compliant SMTP, POP3, and IMAP client implementations”, and indeed it meets that goal and in many ways is actually more powerful and flexible than the traditional System.Net.Mail components.

Here I will show some working example code that comes from my cloudscribe Core project. More specifically this example code is from my EmailSender class which is a work in progress, but functional enough to be a good example.

Lets start with a simple class to represent and encapsulate the settings we need to connect and authenticate with an SMTP Server:

    public class SmtpOptions
    {
        public string Server { get; set; } = string.Empty;
        public int Port { get; set; } = 25;
        public string User { get; set; } = string.Empty;
        public string Password { get; set; } = string.Empty;
        public bool UseSsl { get; set; } = false;
        public bool RequiresAuthentication { get; set; } = false;
        public string PreferredEncoding { get; set; } = string.Empty;
    }

You would new up one of these and set the properties according to your SMTP server configuration, then you would pass that in as one of the parameters to an EmailSender class that looks something like this:

using MailKit.Net.Smtp;
using MimeKit;
using System;
using System.Threading.Tasks;

namespace cloudscribe.Messaging.Email
{

    public class EmailSender
    {
        public EmailSender()
        {
        }

        public async Task SendEmailAsync(
            SmtpOptions smtpOptions,
            string to,
            string from,
            string subject,
            string plainTextMessage,
            string htmlMessage,
            string replyTo = null)
        {
            if (string.IsNullOrWhiteSpace(to))
            {
                throw new ArgumentException("no to address provided");
            }

            if (string.IsNullOrWhiteSpace(from))
            {
                throw new ArgumentException("no from address provided");
            }

            if (string.IsNullOrWhiteSpace(subject))
            {
                throw new ArgumentException("no subject provided");
            }

            var hasPlainText = !string.IsNullOrWhiteSpace(plainTextMessage);
            var hasHtml = !string.IsNullOrWhiteSpace(htmlMessage);
            if (!hasPlainText && !hasHtml)
            {
                throw new ArgumentException("no message provided");
            }

            var m = new MimeMessage();
          
            m.From.Add(new MailboxAddress("", from));
            if(!string.IsNullOrWhiteSpace(replyTo))
            {
                m.ReplyTo.Add(new MailboxAddress("", replyTo));
            }
            m.To.Add(new MailboxAddress("", to));
            m.Subject = subject;

            //m.Importance = MessageImportance.Normal;
            //Header h = new Header(HeaderId.Precedence, "Bulk");
            //m.Headers.Add()

            BodyBuilder bodyBuilder = new BodyBuilder();
            if(hasPlainText)
            {
                bodyBuilder.TextBody = plainTextMessage;
            }

            if (hasHtml)
            {
                bodyBuilder.HtmlBody = htmlMessage;
            }

            m.Body = bodyBuilder.ToMessageBody();
           
            using (var client = new SmtpClient())
            {
                await client.ConnectAsync(
                    smtpOptions.Server,
                    smtpOptions.Port,
                    smtpOptions.UseSsl)
                    .ConfigureAwait(false);
               
                // Note: since we don't have an OAuth2 token, disable
                // the XOAUTH2 authentication mechanism.
                client.AuthenticationMechanisms.Remove("XOAUTH2");

                // Note: only needed if the SMTP server requires authentication
                if(smtpOptions.RequiresAuthentication)
                {
                    await client.AuthenticateAsync(smtpOptions.User, smtpOptions.Password)
                        .ConfigureAwait(false);
                }
               
                await client.SendAsync(m).ConfigureAwait(false);
                await client.DisconnectAsync(true).ConfigureAwait(false);
            }

        }

        public async Task SendMultipleEmailAsync(
            SmtpOptions smtpOptions,
            string toCsv,
            string from,
            string subject,
            string plainTextMessage,
            string htmlMessage)
        {
            if (string.IsNullOrWhiteSpace(toCsv))
            {
                throw new ArgumentException("no to addresses provided");
            }

            if (string.IsNullOrWhiteSpace(from))
            {
                throw new ArgumentException("no from address provided");
            }

            if (string.IsNullOrWhiteSpace(subject))
            {
                throw new ArgumentException("no subject provided");
            }

            var hasPlainText = !string.IsNullOrWhiteSpace(plainTextMessage);
            var hasHtml = !string.IsNullOrWhiteSpace(htmlMessage);
            if (!hasPlainText && !hasHtml)
            {
                throw new ArgumentException("no message provided");
            }

            var m = new MimeMessage();
            m.From.Add(new MailboxAddress("", from));
            string[] adrs = toCsv.Split(',');

            foreach (string item in adrs)
            {
                if (!string.IsNullOrEmpty(item)) { m.To.Add(new MailboxAddress("", item)); ; }
            }

            m.Subject = subject;
            m.Importance = MessageImportance.High;
          
            BodyBuilder bodyBuilder = new BodyBuilder();
            if (hasPlainText)
            {
                bodyBuilder.TextBody = plainTextMessage;
            }

            if (hasHtml)
            {
                bodyBuilder.HtmlBody = htmlMessage;
            }

            m.Body = bodyBuilder.ToMessageBody();

            using (var client = new SmtpClient())
            {
                await client.ConnectAsync(
                    smtpOptions.Server,
                    smtpOptions.Port,
                    smtpOptions.UseSsl).ConfigureAwait(false);
               
                // Note: since we don't have an OAuth2 token, disable
                // the XOAUTH2 authentication mechanism.
                client.AuthenticationMechanisms.Remove("XOAUTH2");

                // Note: only needed if the SMTP server requires authentication
                if (smtpOptions.RequiresAuthentication)
                {
                    await client.AuthenticateAsync(
                        smtpOptions.User,
                        smtpOptions.Password).ConfigureAwait(false);
                }

                await client.SendAsync(m).ConfigureAwait(false);
                await client.DisconnectAsync(true).ConfigureAwait(false);
            }

        }

    }
}

Note that I’ve implemented 2 methods here, one that sends email to a single recipient and one that takes a comma separated list of recipients. This code could probably be refactored a bit to reduce duplication, I actually plan to implement more overloads for handling things like attachments. This is just an initial working stub that I plan to evolve as I encounter more varied needs in my project. Note that you can pass in either or both an html formatted message or plain text, but you must of course at least pass in one of them. I’ve left a few comments in the code to show how things like message importance can be set, but really I’ve only scratched the surface of what MailKit/MimeKit can do for you, so I encourage you to explore the available api surface of those projects.

Feel free to borrow this code and use it in your own projects, and I hope you will also take a look at my various open source projects on github that may be of use or value to you on your projects.

Happy Coding!!!

An Open Letter to Harris Teeter 

Monday, May 2, 2016 1:43:00 PM Categories: society
Rate this Content 2 Votes

Dear Harris Teeter,

I really did not want to have to write this post, but I told you I would and I’m a man of my word.

First let me say that I like Harris Teeter in general, you have good produce and good meats, staff is generally very nice and helpful, prices are reasonable. You were born here in North Carolina and I like to support businesses from my home state. If it were not for your good qualities I would not bother with this post I would just stop shopping there, but I like Harris Teeter enough to try and make it better.

Therefore I’m going to try to get you to change the one really bad thing you do that makes me very uncomfortable when shopping there (and I assume it makes some other people feel uncomfortable too). If I cannot get you to change after trying my best then I will have no choice as a man of principle than to stop shopping there.

So What’s My Beef?

I’m really sick of hearing “security scan cameras”, “security scan and record all cameras”, “security scan cameras E and F”, and all the other variants that I’ve been hearing for many years while shopping at your store.

I have complained in the past at my local store and I have emailed your headquarters and tweeted at you about this in hopes that you would do the right thing. I told you that 140 characters on twitter is not enough to express why this practice is wrong and must be stopped and that I would write a blog post to shame you into changing if needed. I waited, but you have not done the right thing, so here we are.

The thing is, this voice that announces variations of “security scan cameras” is not a robot voice, it is one of the staff. When I first heard it I thought hmm, must be someone suspicious in the store. But for someone who shops frequently, when you tend to hear that about 80-90% of the times you shop there, when you hear it soon after entering the store, you start thinking maybe it is me they think is suspicious. And that makes me feel uncomfortable. And the more I think about it the more it bothers me. Even if it isn’t me, who is it and what makes them suspicious? The way they are dressed, the color of their skin, etc. Being a person who has empathy for my fellow humans this bothers me and I cannot support a business that does this.

Now when I have complained I have been told those announcements are random. But you know what, having a policy to randomly do that also gives cover to do it for other reasons so while I would like to believe you are not profiling people, I really cannot rule that out, and that bothers me.  If it really is random, does it make sense to randomly make customers feel like suspects? Business marketing 101 says no!

It does not make sense to make customers feel uncomfortable while shopping, and it does not make sense for me to keep shopping at a place that persists in a practice that makes me uncomfortable.

So I started thinking about why you have this policy, you must think it is beneficial, you have made it clear it is part of “loss prevention” strategy. I think the reason this has not backfired on you until now is because the average clean cut white person who hears that is not going to think they are the suspicious one in the store. Some people might say you are using “white priviledge” as part of the psychology for your loss prevention scheme. Here in the South, you might could get away with that for years in the past but the time has come for that to stop. For the record, I’m a white person but I am not immune to negative stereotype profiling because I have long hair. Some conservative folks frown on that sort of thing and make negative assumptions about you based on that. But there are many superficial things that people get profiled on, skin color, tattoos, skin piercing etc, etc, so even white people may not feel the white privilege in every case. I applaud the unique individuals who have the courage to be who they are and express themselves through personal style in spite of knowing how some people will judge them. I have long felt the societal pressure to conform, and even today if I were not self employed I think I would have a hard time getting some jobs for which I am very qualified without cutting my hair. I “could” cut my hair and perhaps then I could go back to assuming those announcements are not targeted at me, but that would be giving up a little piece of my self dignity. For people of color, my empathy says they probably also feel uncomfortable when they hear those announcements in your store.

I don’t want to stop shopping there without first doing my best to make you change this practice. So I plan to tweet a link to this post and my dismay every time I hear that when I shop at your store. If after 12 months of doing that you won’t change then I will stop shopping there and I will also encourage everyone I know to boycott your store.

The bottom line is I will not tolerate this treatment towards me, and I will not tolerate this treatment towards others indefinitely. I implore you to do the right thing and change this policy.

What Do I Suggest?

Look, I can hear the devil’s advocate arguments in my head saying “but loss prevention is important, if our losses go up we have to charge you more and you don’t want that right?” My answer to that is, I don’t hear this in other stores, and I would rather pay for my groceries with money than pay with my dignity.

What I suggest is make a recording and play it as often as you wish, but the recording should not imply that there is someone suspicious in the store. A good message alternative that I can think of is:

“For your safety this store is monitored by full coverage video surveillance.” That still sends a message to potential would be shop lifters to make them think twice, and it puts a positive spin on the surveillance policy. People expect to be under surveillance while shopping so this kind of message is acceptable. But a message that makes customers feel like suspects is simply not acceptable and it must stop for the long term benefit of the Harris Teeter brand. Your new parent company Kroger does not make customers feel like suspects, so maybe you need your parent to have a talk with you.

I Invite help from anyone who reads this and who agrees with me to join me in the effort to make Harris Teeter do the right thing. If you shop there pay attention, and if you hear similar messages and you agree they should stop, then tweet a link to this post and express your dismay to @HarrisTeeter on twitter. And if you are a human with empathy towards other humans I think you should care about this. The world is full of bigger problems but this is something where a small positive change is needed and it would take very little effort to help make that small change happen. Please also keep an eye on my tweets and retweet me when I tweet my dismay towards @HarrisTeeter, you can follow my tweets at @joeaudette

Companies use twitter as an important tool in their marketing and they do not like to see a lot of negative sentiment tweets about their brand. If we join together using the power of social media we can influence Harris Teeter to do the right thing. I’m going to try that for up to a year from now. If they tell me they have changed the policy and if I stop hearing the dreaded announcements then I will update this post or even take it down. I’m trying to create positive change, I did not want to make a stink about this, but it seems that is what it will take.

UPDATE 2016-05-10
Today was my first time shopping at Harris Teeter since making this post. In less than 30 seconds after entering the store I hear a man's voice come over the speaker and say "security scan and record camera A". I'm pretty sure that is their way of flipping me the bird and telling me they are dug in on this policy and don't want me shopping there. So I'm about to tweet "another #badexperience shopping at #harristeeter today with a link to this post and cc @HarrisTeeter. Guess I will also make another public post about it on Facebook too. So to me it seems that they are not so worried about loss prevention because they don't care if they lose customers who are bothered by this policy. Nevertheless, I intend to keep shopping there and keep tweeting and posting whenever this happens and try to spread more awareness about this bad practice. They seem pretty dug in though so I'm not optimistic that there is any humanistic leadership at that organization and in time I may have to go full boycott mode.

UPDATE 2016-05-10 - evening

After reflecting on today's experience I've decided life is too short, and I'm just not going back to Harris Teeter, it is not worth the effort and they clearly don't want me there. If you decide yourself to stop shopping there for the same reason as me, please at least send them a parting tweet linking to this post and telling them they lost another customer.

Getting to Know The Jazz Standards 

Saturday, May 10, 2014 1:57:00 PM Categories: Music
Rate this Content 1 Votes

I've been a mediocre guitar player for many years, good enough to accompany myself singing and I've done fairly well playing solo gigs and in bands in years past, but I always felt that I have not learned music all the way and have not developed my musicianship to its full potential. So about 2 years ago I decided I wanted to go back and really learn music on piano/keyboard and really learn music theory and learn to read and write music and develop my ear to a level where I could transcribe music I hear and be able to write down musical ideas that I hear in my mind. I've still got a ways to go on those goals but I feel like I'm making steady progress on my piano/keyboard playing and now one of my sub goals is to learn to play from Fake Books aka Real Books which is what jazz musicians usually do. A lead sheet from a Fake Book/Real Book usually has the melody line notated and the chord symbols and the idea is to improvise around the melody and improvise your own arrangement for voicing the chords to harmonize the melody.

The Real Book

Aside: Historically, "Fake Books" were typically under the counter books that jazz musicians would buy from music stores or make for themselves in loose leaf binders and share with others. But this practice was often outside the rules of copyright law which is why they were sold under the counter as opposed to over the counter. The music publishing industry has tried to remedy this by publishing "Real Books" which have the licenses for publishing these copyrighted works so that the authors and composers get their royalties from the sale of the books.

Ok, so I got myself a Real Book of Jazz Standards to learn from but quickly realized that only a few of the songs were familiar to me and it got me to wondering how these songs became jazz standards and how I could get more familiar with them. So I found this book, The Jazz Standards: A Guide to the Repertoire, which gives a bit of the history of the songs that the author considers as Standards and tells you about the various recordings and interpretations that have been done by various jazz artists. Some of the songs have come in and out of favor over the years so there are many opinions about which tunes should be considered standards and there are 6 volumes of Real Books for Jazz Standards reflecting some of those changes over time in the common repertoire. But the book is a good starting point for learning about the jazz standards and not intended to be all inclusive. I got the Kindle version of the book but in hindsight I would rather have the printed copy. While it is a book that you could read from front to back it is also the kind of book you could skip around and browse and the printed version would be better for that I think.

The Jazz Stsndards: A Guide to the Repertoire

Now that is all well and good and stimulating to the curiosity and it makes me want to listen to those recordings mentioned in the book to hear the different ways that these tunes have been interpreted over time, but it would cost a whole lot of money to just go on a buying spree to obtain all of those recordings. Fortunately for those seeking a jazz education, in the age of Spotify one can listen to those songs at any time for free with advertisements or with no advertisements and a reasonable subscription price. I know I know streaming is killing the music business so they say, and records killed live music, and video killed the radio star and all that yadda yadda, but nevertheless there is something to be said for being able to listen to all that music that otherwise would be out of reach to a poor aspiring musician who wanted to educate him or herself with exposure to this large repertoire.

Anyway I worked my way through the book making a playlist on Spotify of all the ones I could find of the recordings mentioned in the book. I was able to find the majority of them and it took quite a while to create the playlist so I thought I should share it with others who like me may want to learn about the Jazz Standards repertoire.

 

This playlist can be useful for listening to multiple renditions of each song in a row to see how different the interpretations are, or you can set playback on shuffle and just enjoy a random stream of great jazz music.

I Like Windows 8 

Sunday, November 11, 2012 8:43:00 AM Categories: Software Technology
Rate this Content 2 Votes

I was a little reluctant to upgrade my main development machine to Windows 8 because I don’t have a touch screen and wondered whether this would be another Vista or Windows Me (eek).  But then I thought about it and Vista was a big improvement over XP and Windows 7 has been great since it ironed out the rough edges of Vista. Finally the $39 upgrade price was just too tempting so I decided to go for it, after all in my line of work upgrading at some point seems inevitable anyway.

After 2 weeks of using Windows 8 I’m liking it a lot. In many ways it seems like an improved version of Windows 7 with some extra stuff baked in for touch screens (Win RT and Modern UI formerly known as Metro). So far I’m not really using that stuff much since I don’t have a touch screen, I pretty much keep to the desktop side of things. The desktop side of Windows 8 is a lot like Windows 7 except that there is no longer a Start Menu. The Start Screen on the RT side is supposed to be the replacement for that, but for folks like me I think Microsoft would have been wise to keep a setting somewhere to allow enabling the old Start Menu on the desktop. Nevertheless, with a little tweaking to my desktop I’ve got things setup to where I don’t really miss the start menu. In this post I’ll share a few tips that helped me feel more at home in Windows 8. Basically, what I did was create task bar shortcuts for the programs I use the most and a few desktop shortcuts for other things, some of which are kind of hidden in Windows 8 but still there.

joes-win8-desktop

A Replacement For the Start Menu

The first shortcut I added (right click the Desktop and choose New > Shortcut) is the one shown in the top left corner which serves as a replacement for the old Start Menu and makes it easy to find programs I use less often, the shortcut points to "C:\ProgramData\Microsoft\Windows\Start Menu\Programs" and I browsed to find a suitable icon for it. I tried to then pin the shortcut to the taskbar but for some reason that isn’t an option for this shortcut, but for me its good enough on the desktop.

Administrative Tools

I was accustomed to the Administrative Tools menu that was available in the old Start Menu and while it is also in the pseudo start menu I created above, I decided to eliminate the extra clicks and create a direct shortcut to "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools"

System Backup and Recovery

Windows 8 has a new “File History” feature that you can use to backup user files but at first glance I could not find a way to make a system backup. My first google of it found an article saying this feature no longer exists and people lamenting it in the comments, but a little further searching revealed that was false information. Windows 8 does have the same features for System backup as Windows 7 but it is not shown in Control Panel on my system so I created a desktop shortcut to C:\Windows\System32\sdclt.exe

Shutdown and Restart

I guess these days the hibernate features are supposed to be good enough and people are expected to not shutdown their machines very much, a paradigm from the tablet world I guess, but I’m still kind of old school and I like to shutdown my laptop at the end of the day so I created desktop shortcuts for Restart C:\Windows\System32\shutdown.exe -r -t 00 and Shutdown C:\Windows\System32\shutdown.exe -s -t 00 and browsed for decent icons. I then pinned the Shutdown shortcut to my taskbar and moved it all the way to the left.

With those changes Windows 8 feels very much like Windows 7 to me except it does seem faster, the UI seems a little cleaner somehow maybe because the old Aero stuff is gone, and it seems rock solid stable.

Windows 8 should be able to run any software that ran on Windows 7 as far as I know and I have not had any problems with software I use. There were a few warnings when it analyzed the system for upgrade. It says you may need 3rd party software to play DVDs, I can’t remember for sure but I think Windows 7 did have something for playing DVDs so if that is important to you its worth considering before upgrading. I don’t watch DVDs on my laptop often but it dual boots to Mac OS and I could watch one there if I really needed to. It did make me uninstall some Bluetooth software which worried me a little because I use wireless Bluetooth headphones with a little Bluetooth dongle to listen to music while I work. As it turned out that still works fine but the built in Bluetooth drivers for Windows 8 are better so there is no longer a need for the Broadcom software. It also warned me that Visual Studio 2010 was not compatible but it did not make me uninstall it. I suspect it would have worked but I uninstalled it after the upgrade since I also have Visual Studio 2012.

In the end I was very glad I upgraded and I’ve been a happy camper so far even though I don’t have much use yet for the fancy WinRT side of the house. Still I’m glad its there and I may play around with it more at some point. Maybe someday I’ll buy an external monitor that supports touch. For me it was a worthwhile upgrade at a very reasonable price and I’m feeling very productive. I also upgraded my Father’s machine and created some of the same shortcuts for him and he hasn’t had any troubles though he mainly uses the web and Office and not much else.

Additional Resources

Scott Hanselman has some good tips and keyboard shortcuts in his blog post Windows 8 Productivity: Who Moved My Cheese? Oh There it is.

IE 9 Bug Not All Links are Right Clickable 

Thursday, March 17, 2011 6:56:00 AM Categories: mojoPortal Web Design
Rate this Content 4 Votes

Since yesterday I've been using IE 9 most of the time so I can find out if there are any issues in mojoPortal skins and functionality related to using the new version. I use the right click on the mouse all the time for opening links in a new tab, and I began to notice that some links do not show me options for opening the link when I right click it. It seems as if it has no context as being a link and I get the other typical items like to view the source of the page etc.

I noticed it first in Gmail when I tried to right click the Calendar link at the top to open Google Calendar (yes I know it opens in a new tab when you click it normally anyway, but I'm a creature of habit). In other browsers I'm able to right click those links and get a context menu for a link, but not in IE 9.

gmail link

Next I noticed it in the main menu on www.mojoportal.com

mojo menu

I also cannot right click those links in IE 9 and get options to open the link.

So most links do work with right clicking in IE 9 but some do not, I wanted to fix it, because it seems like a usability issue to me that might confuse some users. I did some trial and error experimenting and I figured out that the cause of the problem is when you have a span wrapping the text inside the link and the span is styled with display:block;

I have not yet figured out a good solution for this. In the case of the mojoportal menu the span is used to hang a background image for the top right corner of the tab and removing the display:block; breaks the design.

I have narrowed it down to a small html example that shows the problem:

<!DOCTYPE html>
<html>
<head>
<title>IE 9 Usability Right Clicking Links Problem Demo</title>
<style type="text/css">
  .myclass{ display:block; }
</style>
</head>
<body>
<p>
<a href="http://www.mojoportal.com"><span class="myclass">Not Right-Clickable in IE9 But is Right-Cickable in All Other Browsers</span></a>
</p>
<p>
<a href="http://www.mojoportal.com"><span>Right-Clickable in IE9</span></a>
</p>
</body>
</html>
 

Note that the same problem happens if you use an XHTML doctype, but if you remove the doctype altogether the problem goes away.

So, what do you think? Am I doing something wrong by styling an inline element with block? Is it a bug or a feature of the "most modern web browser"?

I imagine others will find this problem with some designs, though I will say that so far most of the skins/designs we ship with mojoPortal have tested out well in IE 9, so maybe it won't be that common.

SEO Phone Spam 

Wednesday, June 30, 2010 11:05:32 AM Categories: Humor Technology
Rate this Content 4 Votes

I often get these contact form submissions on joeaudette.com and on mojoportal.com where people are pitching to get my site to the top of google, no big deal, I delete them, but yesterday was the first time I got one by phone.

Yesterday at about 2PM I got a phone call on my cell phone from 951-813-2184 that went like this:

  1. me: hello?
  2. caller: is this the tree service?
  3. me: i think you have the wrong number
  4. caller: are you Source Tree Solutions?
  5. me: that is my company but it is a software company not a tree service
  6. caller: oh, well you're listed in the yellow pages under tree service
  7. me: that's news to me, I didn't know I had a listing in the yellow pages
  8. caller: well are you interested in getting your site to the top of google?
  9. me: oh my God, you gotta be kidding me
  10. caller: well what do you do for advertising
  11. me: Dude! you don't know the first thing about me, my business or my web site, I don't need your SEO spam phone calls, please never call this number again, click

I chuckled for about 15 minutes after that, but hope it is not the start of a trend of spammy phone calls.

Solving the ASP.NET UpdateProgress Div Problem 

Monday, November 9, 2009 9:58:00 AM Categories: Developer Resources Development mojoPortal Open Source
Rate this Content 2 Votes

 Introduction - What is the ASP.NET UpdateProgress Control?

The UpdateProgress control is a very handy control meant for use in conjunction with an ASP.NET UpdatePanel. The UpdatePanel is an ajax control that makes it very easy to update part of a page using an ajax postback rather than a full postback. This avoids page flicker and also leaves the page not in a postback state, that is, a subsequent refresh of the browser won't force a postback. The UpdateProgress control is desinged to make it easy to show some markup while the update is happening to give the user a visible cue that something is happening. So a typical trick is to show an animated working indicator something like this working indicator

So you would add the UpdateProgress control inside the UpdatePanel something like this:

<asp:Button ID="btnSavePreferences" runat="server" />
<asp:UpdateProgress ID="progress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1">
<ProgressTemplate>
<img src='<%= Page.ResolveUrl("~/Data/SiteImages/indicators/indicator1.gif") %>' alt=' ' />
</ProgressTemplate>
</asp:UpdateProgress>

The Problem - Block Rendering instead of Inline Rendering

The problem is that the UpdateProgress control renders as a div element which is a block element rather than an inline element, so instead of rendering the indicator right next to your button, it renders it below your button on a new line. In some cases that may be ok, but if you really want the indicator to just be right next to the button it's a problem. I came across this situation recently in my work on mojoPortal and I really wanted to show the indicator in the same line as the button, it gave a bad effect for the indicator to jump below the button. So I did some searching on the internet and found a number of people complaining about this problem and a few less than satisfactory suggestions for solving it.

So, in summary, the problem is that it renders as a div which is a block element and the javascript associated with the control changes from display:none; to display:block when the UpdatePanel is updating.

The Solution- Borrow The UpdateProgress control from the Mono Project and modify it.

What I really wanted was to make the UpdateProgress control render as a span which is an inline element and I wanted the javascript to toggle between display:none; and display:inline;. Often, when I face a situation where an ASP.NET control cannot be coerced into rendering or behaving as I would like, I have found that I can borrow the Mono Project implementation of the ASP.NET control and modify it to meet my needs. Sometimes its more difficult than others to use this approach depending on what mono internals it may be using, but in this case it was fairly easy.

First I grabbed the UpdateProgress.cs from the Mono source code and added it to my project as UpdateProgressSpan.cs and changed the namespace to match my own project. Then it was fairly simple to change the rendering to use a span instead of a div. However, the problem still remained that the javascript would set the display to block which would still make the span render as a block just like a div. So I scrounged around in the javascript for ms ajax and found the relevant part for UpdateProgress. I copied it into my own javascript file, changed the namespace, and modified it to use display:inline instead of display:block.

The only changes I had to make in my UpdateProgressSpan.cs were:

1. In OnPreRender I had to tell it about my javascript like this:

ScriptReference SRef = new ScriptReference();
SRef.Path = "~/ClientScript/ajaxupdateprogressspan.js";
ScriptManager.Scripts.Add(SRef);
ScriptManager.RegisterScriptControl(this);

2. In Render, I changed it to use Span instead of div and use inline instead of block.

3. In the GetScriptDescriptors method I changed it from referencing the original ms ajax to use my custom one like this:

ScriptControlDescriptor descriptor = new ScriptControlDescriptor("mojo._UpdateProgress", this.ClientID);

Finally in my page I used my custom control instead of te ASP.NET version like this:

<asp:Button ID="btnSavePreferences" runat="server" />
<portal:UpdateProgressSpan ID="UpdateProgress2" runat="server" AssociatedUpdatePanelID="UpdatePanel1" >
<ProgressTemplate>
<img src='<%= Page.ResolveUrl("~/Data/SiteImages/indicators/indicator1.gif") %>' alt=' ' />
</ProgressTemplate>
</portal:UpdateProgressSpan>

Problem solved, the working indicator displays right next to the button.

If you would like to use this in your project, feel free, here is a .zip with the files.

How To Use jQueryUI Tabs in Right To Left Layout 

Saturday, May 9, 2009 11:00:00 AM Categories: mojoPortal Web Design
Rate this Content 34 Votes

Recently I've begun using the jQueryUI tabs in mojoPortal as an alternative to YUI tabs. I still like the YUI tabs but there is only 1 skin available currently for YUI tabs, whereas there are a 18 themes for the jQuery UI tabs, so its likely that at least one of them will look good with a particular mojoPortal skin. This has got me thinking about switching to use the jQuery tabs in many or most places where we use YUI tabs. I still need to test a few things like making sure I can use FCKeditor inside the tabs like I can with the YUI tabs. One thing I like about the YUI tabs is that they automatically adjust to right to left layout if they are contained within and element with direction:rtl in the css.

I was worried at first whether the jQuery Tabs would support right to left layout because when I googled for it I could not find any explnations how to make the tabs layout from right to left. I found a number of people asking about it on mailing lists and forums but no-one offering any answers. So I used Firebug to study the css classes assigned to the elements and figured out the things that need to be overridden to make it layout from right to left. I thought I should post it since clearly there are people looking for hep with this. Its actually very straightforward, you include the normal css for the jquery ui theme, and you add another css file below it in the page (it must be lower in the page in order to override the style settings above it in the jquery ui css). There is only a little css needed because we want to override the minimum possible style settings, this is what is needed:

.ui-tabs { direction: rtl; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected,
.ui-tabs .ui-tabs-nav li.ui-state-default {float: right; }
.ui-tabs .ui-tabs-nav li a { float: right; }

I tested it with all 18 jQuery UI themes and it worked great. I hope this is helpful to others.

screen shot of jquery tabs in right to left layout

 

System.Configuration.ConfigurationPermission in Medium Trust 

Friday, October 10, 2008 2:53:40 PM Categories: Development Security
Rate this Content 6 Votes

As I mentioned in my previous post, I was doing some testing today of mojoPortal under Medium Trust and was trying to resolve some issues. In most situations mojoPortal can be configured to run in Medium Trust, but occasionaly people have reported System.Configuration.ConfigurationPermission exceptions. I have not been able to reproduce that problem on my development machine. I add this to Web.config to configure medium trust for testing purposes:
<trust level="Medium" originUrl="" />

So today I encountered this exception on a new machine running in Medium Trust. Its not always easy to find out what is causing an exception like this because you don't get a lot of information in the stack trace, but I was able to pin it down to System.Web.Extensions.dll which is the MS AJAX library. We include this dll in the /bin folder with mojoPortal releases and in Full Trust this is ideal because we don't care if the dll is installed on the machine or not, but in Medium Trust, this dll can not run from the bin folder, it must be installed in the GAC (Global Assembly Cache) on the server. If it is installed in the GAC, the copy in the /bin folder will be ignored and the one in the GAC will be used. But if its not installed and the machine is configured for Medium Trust it will cause this mysterious SecurityException with System.Configuration.ConfigurationPermission.

So the reason I was never able to reproduce the problem on my development machine was because I already had this installed in the GAC. If you install the ASP.NET 2.0 AJAX 1.0 using  ASPAJAXExtSetup.msi, it installs it in the GAC on your machine. Your only solution to this at a web host is for the host to install it on the server.

log4net Messages Truncated - The Fix! 

Friday, October 10, 2008 11:37:40 AM Categories: Development
Rate this Content 3 Votes

I was doing some testing today of mojoPortal under Medium Trust and was trying to resolve some issues. In most Medium Trust hosting situations mojoPortal can be configured to run in Medium Trust, but occasionaly people have reported System.Configuration.ConfigurationPermission exceptions. I have not been able to reproduce that problem on my development machine. I add this to Web.config to configure medium trust for testing purposes:
<trust level="Medium" originUrl="" />

I found this blog post by Phil Haack, where he mentioned that using an external config file with log4net could cause this exception and that moving the configuration into Web.config could solve it. Though he later updated that post and now says that it can work with an external file, I figured it was at least worth a try in case it is log4net causing this exception on some installations of mojoPortal. So I move the configuration into the Web.config file and a strange thing happened, the log messages started getting truncated to about 30 characters. So I did some googling for "log4net messages truncated" and found a number of messages about it but no real solution mentioned so I started reading more of the log4net documentation about the PatternLayout. I thought maybe this truncation was related to PatternLayout so I changed the config to use SimpleLayout and sure enough the messages were no longer truncated. But I wanted to use PatternLayout as I have all along if possible. Originally my configuration was like this:

<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{url}] - %message%newline" />
</layout>

So I started chopping things out to see if any particular setting was the culprit and indeed it turned out to be the [%property{url}] that was causing the problem. It still seems like a log4net bug, but its strange that it only happens when not using an external config file for log4net.

So for me the solution was just to remove that part of the pattern so my current configuration is like this:

<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level %logger - %message %newline" />
</layout>

Just figured after all the time I spent figuring this out that I should blog it in case others are having the same trouble.

Now I'm still not sure if moving the log4net configuration into Web.config is going to solve the rogue medium trust issue that happens on some machines. If I can figure that one out it will be worth posting about it as well.

Page 1 of 17 1 2 3 4 5 6 7 8 9 10 > >>
Copyright 2003-2010 Joe Audette
Donate Money to support the mojoPortal Project. View Joe Audette's profile on LinkedIn View Joe Audette's profile on The Guild of Accessible Web Designers site