Ken Robertson's Blog

Ramblings of a .NET developer

April 2004 - Posts

nGallery v1.6.1 released!

nGallery v1.6.1 is now available for download!

Visit the nGallery site at http://www.ngallery.org to download!

nGallery is a FREEWARE, OPEN SOURCE implementation of a image gallery written purely in managed .NET and C#.

nGallery provides a solution to store and display your image galleries on your own Web site, as well as providing means for customizing and extending nGallery to your own personal likings.

This release features many new enhancements including EXIF support, RSS feeds, brand new automated installer, friendly URL support, completely rewritten image handling, watermarking, refactor XML support, and much much more!

System Requirements:
> Microsoft Internet Information Server (IIS)
> Microsoft .NET Framework 1.1 *
> Ability to manage directory-level permissions
> Ability to configure a directory as an application directory (for global.asax processing)
> While a database is not required, if you wish to run the SQL Server data provider you obviously must have access to Microsoft SQL Server 7.0 or above.

* = nGallery is currently tested with Mono as of April 22nd, 2004 snapshot.

New in this release:
* Configuration built in to administration interface
* Rewritten image handlers
* Image watermarking
* Ability to send invitations to friends/family and see when they've visited
* Reading of EXIF data
* RSS feeds for whole site or for individual albums
* Sorting options
* Friendly URLs to album name or by album ID
* Rewritten XML data provider that uses a Singleton for improved performance
* Brand new installer allowing allowing full customization of the nGallery install and ready to run out of the box.
* Mono compatible!  nGallery has been tested on Mono and is ready to run.
* Admin: Displaying of item's IDs
* Admin: Migration tool to upgrade from pre-v1.6 repository to new structure
* Admin: Picture thumbnail generated when images are uploaded
* Fixed: Added support to specify the character set
* Fixed: Picture view count added to front-end
* Added ability to read IPTC values when images are uploaded
* Added link to picture from the album's summary image
* Added client side caching of images
* Fixed: Problems with specifying image quality
* Fixed: Error in returning next album ID in SQL data provider
* Fixed: Foreign key error on deleting in SQL data provider
* Fixed: Location of admin directory no longer hard coded
* Fixed: Fix errors when website is on a non-default port
* Fixed: Bulk Add now properly sets created date


Features:
> Allows creation and customization of Albums to contain your images
> Provides automatic thumbnailing, and caching, of your images for faster loading
> Allows users to comment or caption images
> Allows users to select a specific picture within an album to be used to highlight the album's contents
> Contains a full Web-based administration tool for managing your albums, images, comments/captions, including uploading your images for you, or bulk adding images that have been previously uploaded
> Allows you to quickly send an e-mail with a link of your gallery, a specific album or a specific image to a friend or list of friends
> Allows you to maintain an address book of friends and family, as well as to send them invitations to your albums and track when they've visited.
> Ability to read and display EXIF and IPTC data from pictures.
> Ability for users to subscribe to RSS feeds to keep up on updates.
> The look and feel of your nGallery is fully customizable, without touching any code
> Supports the concept of "skinning" where users can customize the look and feel of nGallery and then easily share those with the community
> Integrates with Shutterfly so that you can transfer and print images
> Allows you to customize the album-listing page to be either in line or a grid layout
> Configures almost all features in one central text file
> Allows you to secure the administration tool
> Allows you to password protect your entire photo gallery


We encourage you to grab a copy, subscribe to the mail list (http://www.aspadvice.com/SignUp/list.aspx?l=155&c=25) and contribute!

Please feel free to visit the nGallery forum at ASP.NET Forums or by linking from the Support menu at ngallery.org.


Thanks, and hope everyone enjoys it,
Ken Robertson (ken@ngallery.org) and the whole nGallery Team (ngallery-dev@ngallery.org)

Posted: Apr 26 2004, 10:10 AM by qgyen | with 13 comment(s)
Filed under: ,
Page 23 Bandwagon

I guess I'll join the bandwagon.

Grab the nearest book. Open the book to page 23. Find the fifth sentence. Post the text of the sentence in your journal along with these instructions.

Here's my addition:

Its orientation is towards the use of structured methods for requirements engineering.

- Requirements Engineering: Processes and Techniques by Gerald Kotonya and Ian Sommerville

 

Odd SelectedIndexChanged behavior on DropDownLists

Been coding away on my senior project to meet our deadline for delivery in like 4 weeks and ran into this interesting problem over the weekend.

Had three drop down lists, Campus, Course, and Semester.  As one was selected, the next one was populated with the courses at the campus, and then the semesters a course was offered.

All looked good until I went to test it.  The course list would get populated, but when I selected a course, it would post back, but it was never getting to the SelectedIndexChanged method.  It was posting, it had a selected index, the event was wired up, but nothing would work.

After going through trial and error, I found that it was because I was populating the course drop down with ListItems ( dropCourse.Items.Add(new ListItem(course.Name, course.ID.ToString()); ).  If I populated it with just the straight string ( dropCourse.Items.Add(course.Name); ) then it would work.

Anyone encountered this behavior before?  I found it very odd that populating it one way would override the SelectedIndexChanged.  I filled the campus drop down with ListItems, so it must have something to do with clearing it and refilling.  I'd read some about the need to do ClearSelection(), and I was doing that, but it did no good either.

Posted: Apr 19 2004, 10:49 PM by qgyen | with 3 comment(s)
Filed under: ,
Not sure how I am supposed to look at this...

So we're almost done with the coding on my senior project, which is to write a survey and reporting system in C#.  There is this one person on our team who has done very little coding, and has taken like 2 weeks on this simple function that'd take anyone else maybe an hour or two to do.  It was basically all laid out what was left.

I'd talked to them earlier in the week about getting it done and to ask if they have any problems.  They said that they were getting some syntax errors and couldn't figure them out.  Tonight, when I got home from dinner, they'd email me saying they were getting them again.

Now as you look at this, keep in mind this is a graduating senior in Computer Science who wrote this.  I'm not sure if I'm supposed to laugh at this or cry at it.

Here is the function they were using:

public QuestionCollection GetStudentQuestions(int campusID, Definitions.Types.StudentSurveyType type) { ... }

Now, this is how they called it:

QuestionCollection questions = occBL.GetStudentQuestions(int campusID, Definitions.Types.StudentSurveyType type);

Now, can anyone see the glaring problem?  They straight copied the declaration line!  No putting in their own variables or anything!  A senior graduating this semester wrote this!  I mean, anyone who took a basic programming class in C/C++ or Java should be able to know how to call a function.  It is a pretty core requirement.  How did this person get through their programming classes?

While I know that not everyone with a computer science degree is a programming, there are like 5-6 classes that are core requirements to graduate that require programming.  You should atleast pick up that much.

This is my gripe with the educational system... too many teachers in classes give these cookie cutter programming assignments where they basically spell out exactly what you need to do.  Very little creativity, very little actual programming effort is needed.  Even for someone who is not going to be a programmer, the programming classes are an insight into how and why a computer works the way it does, as well as basic problem solving.  If you can't even get that down, what good are you going to be?

Now I could laugh at this, since it is funny.  But it is also pathetic.  Here I am, still working on my degree, trying to actually learn and challenge myself, while this person is waltzing out, degree in hand, not knowing squat.  They can put on their resume that they know these languages and worked on these projects, since technically they did, and someone looking to hire them will see the resume and assume they do know these languages and that they can program.

If people like this are getting degrees and going out into the field, this industry is heading for hard times.

Guess I'm just a youngin'
23 today.
Phone Number Conversion Challenge

Even though the1 already posted the his solution to his programming challenge, I was already amidst my own solution to it when he posted his.

I didn't think I was going to take a stab at it, since I figured plenty of other people would.  But as I saw people posting their solutions, it seemed like one thing was consistent in that most of them were taking the number, looking at how many ways the number could be represented as a string and then check if it was a word.

Then I thought, “what if you did it the other way around?”  What if you take a word, get its numerical form, and see if it was in the phone number.  Main reason being, a sequence of numbers has many possible string forms, but a word only has one possible numerical form.  I should note though that this is rather inefficient.  I used the basic English word list the1 posted, which was about 850 words.  It was rather limited since in his challenge post, he had an example number with a possible output of “nice-window”, but the word list didn't even have “nice“.  As your dictionary grows, it gets slower and slower.  Example being I took some of the dictionary files from ispell (unix spell checker) so it had a little over 100,000 entries.  Yeah, slow.  Eventually, the number of possible strings the number can be is less than the amount of time you'll spend looping through the dictionary.  I did it just to see how it'd work though.  Here is my “solution” in C#:

using System;
using System.Collections;
using System.IO;

namespace PhoneWord
{
   /// <summary>
   /// Summary description for Class1.
   /// </summary>
   class Finder
   {
      static ArrayList dictionary = new ArrayList();

      /// <summary>
      /// The main entry point for the application.
      /// </summary>
      [STAThread]
      static void Main(string[] args)
      {
         string phoneNumber = "6423946369";   // The phone number
         // Load the dictionary
         LoadDictionary("dictionary.txt");
         FindWord(phoneNumber);            // Start finding them words!
      }

      /// <summary>
      /// This function loops through the dictionary, converts the words to their
      /// numerical form, and then looks for a match in the phone number. If there is a
      /// match, it replaces the numbers with the word, and then calls itself to find
      /// another word match.
      /// </summary>
      /// <param name="phoneNumber">The current phone number and any word matches already found.</param>
      static void FindWord(string phoneNumber)
      {
         foreach(string word in dictionary)
         {
            string tempNumber = phoneNumber;
            string metanumber = ConvertToMetanumber(word);
            if(tempNumber.IndexOf(metanumber) >= 0)
            {
               tempNumber = tempNumber.Replace(metanumber, word);
               Console.WriteLine("answer: " + tempNumber);
               FindWord(tempNumber);
            }
         }
      }

      /// <summary>
      /// This function loads all the entries from a dictionary file into an array list.
      /// The file is formatted with 1 word per line.
      /// </summary>
      /// <param name="filename">The filename of the dictionary file.</param>
      static void LoadDictionary(string filename)
      {
         StreamReader file = new StreamReader(filename);
         while(file.Peek() >= 0)
         {
            string word = file.ReadLine();
            if((word.Length < 10) || (word.IndexOf("'") == -1) || (word.Length != 0))
               dictionary.Add(word);
         }
         file.Close();
      }

      /// <summary>
      /// This function converts a word into its numerical representation.
      /// </summary>
      /// <param name="word">The word to be converted.</param>
      /// <returns>string</returns>
      static string ConvertToMetanumber(string word)
      {
         if(word == "")
            return "";
         else
         {
            // get a number referencing the char (A=0, B=1, C=2, ... Z=25)
            int asciiChar = Convert.ToInt32(Convert.ToChar(word.Substring(0, 1).ToUpper())) - 65;
            if(asciiChar > 17) asciiChar--;   // Trim it down by one after R, since 7 is PRQS
            if(asciiChar > 23) asciiChar--; // Trim it down by one more if Z, since 9 has WXYZ
            int asciiNumber = (asciiChar / 3) + 50;   // Divide by 3 to get which number it is on, add 50 for ASCII index
            return Convert.ToString(Convert.ToChar(asciiNumber)) + ConvertToMetanumber(word.Substring(1, word.Length-1));
         }
      }
   }
}

Posted: Apr 02 2004, 01:46 PM by qgyen | with 3 comment(s)
Filed under:
Using NSIS to install to a website other than the Default Website.

One of my biggest gripes with the default setup package that Visual Studio produces is that for projects, it only supports installing applications to the Default Website.  I don't want all my applications on the default website, and I find it a pain to have to manually move them and then have the uninstaller not work.

Since we're ready for a new release of nGallery, we've been wanting a better setup package than the default, one that lets you chose which website, setup the SQL database (if you want to use it), and setup all write permissions.  We've started writing an installation script using the Nullsoft Scriptable Install System (free software, yay!).  There are tons of functions available on their website to do stuff like detect the .NET framework, detect IIS, and more.  There wasn't one for allowing you to pick which website to use, so I thought I'd write one myself.  The end result is an installer screen that will look like this:

[Installer screen sample]

This script outputs a small Windows Hosting Script that queries the IIS metabase to get the list of website's names and their physical path, then display the names in a drop down and allow you get the path of the one they picked.

First, the script to put in your .nsi:

Function GetWebsites
 ; Output the VBScript
 FileOpen $4 "$PLUGINSDIR\GetWebsites.vbs" w
 FileWrite $4 'Set IISOBJ = getObject("IIS://localhost/" & "w3svc")$\n'
 FileWrite $4 'Set objFSO = CreateObject("Scripting.FileSystemObject")$\n'
 FileWrite $4 'Set objTextFile = objFSO.OpenTextFile("$PLUGINSDIR\websites.ini", 8, True)$\n'
 FileWrite $4 'AllSites = ""$\n'
 FileWrite $4 'for each Web in IISOBJ$\n'
 FileWrite $4 'if (Web.Class = "IIsWebServer") then$\n'
 FileWrite $4 'Set IISWebSite = getObject("IIS://localhost/" & "w3svc" & "/" & Web.Name)$\n'
 FileWrite $4 'Set IISWebSiteRoot = getObject("IIS://localhost/" & "w3svc" & "/" & Web.Name & "/root")$\n'
 FileWrite $4 'AllSites = AllSites & "|" & IISWebSite.ServerComment$\n'
 FileWrite $4 'objTextFile.WriteLine("[" & IISWebSite.ServerComment & "]")$\n'
 FileWrite $4 'objTextFile.WriteLine("Path=" & IISWebSiteRoot.Path)$\n'
 FileWrite $4 'objTextFile.WriteLine("")$\n'
 FileWrite $4 'Set IISWebSiteRoot = nothing$\n'
 FileWrite $4 'Set IISWebSite = Nothing$\n'
 FileWrite $4 'end if$\n'
 FileWrite $4 'next$\n'
 FileWrite $4 'objTextFile.WriteLine("[Websites]")$\n'
 FileWrite $4 'objTextFile.WriteLine("AllSites=" & Right(AllSites, Len(AllSites)-1))$\n'
 FileWrite $4 'objTextFile.Close$\n'
 FileWrite $4 'Set objFSO = Nothing$\n'
 FileWrite $4 'Set objTextFile = Nothing$\n'
 FileWrite $4 'Set IISOBj = Nothing$\n'
 FileClose $4

 ; Execute the script
 nsExec::Exec /TIMEOUT=20000 '"$SYSDIR\cscript.exe" "$PLUGINSDIR\GetWebsites.vbs"'

 ; Read the value and put it in the page's INI
 ReadINIStr $0 "$PLUGINSDIR\websites.ini" "Websites" "AllSites"
 WriteINIStr "$PLUGINSDIR\pick_website.ini" "Field 2" "ListItems" $0
FunctionEnd

You also might want a function to include a function to change an installation path of /myApp/ to \myApp\:

;--------------------------------
; StrSlash
;    By dirtydingus
;
; Usage:
;   Push $filenamestring (e.g. 'c:\this\and\that\filename.htm')
;   Push "\"
;   Call StrSlash
;   Pop $R0
; Now $R0 contains 'c:/this/and/that/filename.htm'

Function StrSlash
  Exch $R3 ; $R3 = needle ("\" or "/")
  Exch
  Exch $R1 ; $R1 = String to replacement in (haystack)
  Push $R2 ; Replaced haystack
  Push $R4 ; $R4 = not $R3 ("/" or "\")
  Push $R6
  Push $R7 ; Scratch reg
  StrCpy $R2 ""
  StrLen $R6 $R1
  StrCpy $R4 "\"
  StrCmp $R3 "/" loop
  StrCpy $R4 "/" 
loop:
  StrCpy $R7 $R1 1
  StrCpy $R1 $R1 $R6 1
  StrCmp $R7 $R3 found
  StrCpy $R2 "$R2$R7"
  StrCmp $R1 "" done loop
found:
  StrCpy $R2 "$R2$R4"
  StrCmp $R1 "" done loop
done:
  StrCpy $R3 $R2
  Pop $R7
  Pop $R6
  Pop $R4
  Pop $R2
  Pop $R1
  Exch $R3
FunctionEnd

Next, you'll need to use the InstallOptions plug in to show the custom page.  I created a basic pick_website.ini which lets you pick the website and specify the installation directory.

[Settings]
NumFields=4

[Field 1]
Type=label
Text=Please select which Web Site to set the application to use.
Left=0
Right=-1
Top=0
Bottom=10

[Field 2]
Type=Droplist
Text=droplist
ListItems=
Left=25
Right=150
Top=10
Bottom=22


[Field 3]
Type=label
Text=Path for the application:
Left=0
Right=-1
Top=30
Bottom=40

[Field 4]
Type=text
Text=webPath
State=/myApp
Left=25
Right=150
Top=40
Bottom=52

Next, have the .nsi display the custom page and call our little function to get the websites.  Along with my list of pages, I have a “Page custom PickWebsite”, which tells it to go to a page called PickWebsite instead of having them pick an installation directory (commented out the MUI_PAGE_DIRECTORY page).  You'll also need to do the usual calls for InstallOptions pages to reserve the files and extract the INI on runtime.

Now our custom page function looks like this:

Function PickWebsite
 Call GetWebsites
 !insertmacro MUI_HEADER_TEXT "Webserver Configuration" "Choose the website and path that your installation will reside at."
 !insertmacro MUI_INSTALLOPTIONS_DISPLAY "pick_website.ini"
FunctionEnd

And then in the installer section for your web application, use the following to set the $INSTDIR properly:

 ; Get the website path and the app path
 ReadINIStr $0 "$PLUGINSDIR\pick_website.ini" "Field 2" "State"
 ReadINIStr $1 "$PLUGINSDIR\websites.ini" "$0" "Path"
 ReadINIStr $2 "$PLUGINSDIR\pick_website.ini" "Field 4" "State"

 ; First, convert the / from the path to \, then setup the installation path
 Push $2
 Push "/"
 Call StrSlash
 Pop $R0
 StrCpy $INSTDIR "$1$R0"
 SetOutPath "$INSTDIR"

Coming next time, I'll include a function to mark the folder as an application, which can be done with a rendition of the sample to create a virtual directory.

To get a complete sample script, feel free to fetch my sample.  The sample includes the functions to check for the .NET Framework and if the user is has administrator priviledges.  I need to update it to check for IIS and to setup the folder as an application.  Will get to that this evening, hopefully.

Posted: Apr 01 2004, 05:05 PM by qgyen | with 19 comment(s)
Filed under: ,
Method of using Reflection to get current executing user? Or, find out which user an application pool runs as?

Is there some way to use reflection to get the name of the user who is currently executing as?  Or if that isn't possible, is there a way to find out which user the current application pool is running as?

Main reason I ask is as a part of the new nGallery release, we are working on a setup package that takes care of setting up all the necessary permissions for the user.  Sometimes though, people cannot use the installer, such as when they have webhosting.  So we'd also like to include a page in the admin section that checks to make sure all of the permissions are right, and if not, says which directory to give the user access to.  Part of the problem is we can't generalize and say “grant access to ASPNET for IIS5 and Network Service for IIS6”, since it is possible the webhost has the user in a special application pool that might be running as a different user.

Posted: Apr 01 2004, 03:24 PM by qgyen | with no comments
Filed under: ,
More Posts