Thursday, July 16, 2009

Adding to the Subversion global ignore pattern for Tortoise and Cygwin

I hate to say this, but I spent two hours trying to find out how to add global ignores to my Subversion clients. There is plenty of documentation and forum posts, but strangely nothing that told me exactly what I needed to do! *sigh* Flame me if you wish.

In particular, it took me ages to realise that svn propedit svn:ignore . only sets up ignores for the current directory only - these properties are not recursive.

Tortoise - add to the global ignore list

  1. Right click on any folder.
  2. Select Tortoise SVN
  3. Select Settings
  4. Navigate to General
  5. Add "*.bak " to the global ignore pattern (note the space at the end).

Cygwin's SVN command line client - add to the global ignore list

  1. Edit your subversion config file. E.g. vim ~/.subversion/config
  2. Uncomment this line (and add the *.bak) global-ignores = *.bak *.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo

Set up Cygwin Day 0

Day 0 instructions for setting up Cygwin on a fresh install.

Save this as a file, chmod 777 it and run it - or just c&p the code below into Cygwin. Important: make sure to take into account the two pre-requisites listed.

#!/bin/bash

# Pre-requisite 1. I have made a Windows HOME variable.
# right click on My Computer > select Properties
#   > select Advanced tab > click Environment Variables
#   > add new personal variable: HOME = "C:\rob\cygHome" for example

# Pre-requisite 2. Edit "mkpasswd options" to run one or the other.


# Make sure $USERNAME and $HOME are ok.
if [ ! -e "$HOME" ] ; then
    echo "Warning: HOME ($HOME) does not exist."
    exit 2
fi
if [ -z "$USERNAME" ] ; then
    echo "Warning: USERNAME ($USERNAME) is empty."
    exit 2
fi

# It is annoying always typing /cgydrive/
mount -s --change-cygdrive-prefix /

# Delete default home dir and replace as a link to HOME.
if [ -e /home/$USERNAME ] ; then
    rm -rf /home/$USERNAME
fi
ln -s $HOME /home/$USERNAME

# mkpasswd options
# Run this if in a corporate environment with domains.
# mkpasswd -l -c > /etc/passwd; mkgroup -l -d > /etc/group
# Otherwise..
# mkpasswd -l > /etc/passwd; mkgroup  -l > /etc/group

# Update passwd file with new home dir.
cat /etc/passwd | sed "s|:/home/$USERNAME:|:$HOME:|" > /etc/passwd

# Make links between Windows and Cygwin homes.
if [ -e $HOME/winHome ] ; then
    rm $HOME/winHome
fi
if [ -e /c/Documents\ and\ Settings/$USERNAME/cygHome ] ; then
    rm /c/Documents\ and\ Settings/$USERNAME/cygHome
fi
ln -s /c/Documents\ and\ Settings/$USERNAME $HOME/winHome
ln -s $HOME /c/Documents\ and\ Settings/$USERNAME/cygHome

Install PuttyCyg.

Thursday, July 02, 2009

Working with FatWire and Eclipse

The project I am currently working with uses FatWire 5.5. It has been very painful to work without Eclipse since the plugin doesn't work for FatWire 5.5. Here is a technique I introduced that allows me to use Eclipse in concert with Content Server Explorer. It is not as nice as the plugin would be, but for my money it is vastly superior to working with plain old text editors!

Eclipse Project for FatWire Code

FatWire 5.5 needs JDK 1.3, so I set up JDK1.3 as an installed JRE in my Eclipse workspace. Get JDK 1.3 from Sun's Archive Page.

  1. Within Eclipse, go to Windows > Preferences > Java > Installed JREs.
  2. Click "Add".
  3. Click "Standard VM" and then click Next.
  4. For "JRE Home", click Directory and navigate to the location of your JDK 1.3 install (for example C:\Java\jdk1.3.1_20).
  5. Click OK.
  6. For "JRE Name", enter something like "jdk1.3.1_20".
  7. Click Finish and then click OK.

Now I make a Java project for my FatWire based code that uses JDK 1.3. Unlike other web applications I have worked on, I don't need to make a Dynamic Web Project for the code I will deploy to FatWire Content Server; I just need a standard Java Project.

Eh?

Under the FatWire paradigm, all JSPs are created and maintained through FatWire tools - Content Server Advanced Interface and Content Server Explorer. I will deploy an EAR/WAR, but it will not contain any of my JSPs - which are CSElements. Instead, Content Server puts those files into the exploded EAR/WAR at runtime initialisation. This means that I never store JSPs in my own project (or static image or HTML files), so there is little advantage in using a Dynamic Web Project. I chose to use a standard Java Project for my project's FatWire code to keep the project structure simple. I use Ant build scripts to create the appropriate EAR/WAR file as needed.

Eclipse Project for Test Code

I keep unit tests in a separate project, which also means I can use a more recent JDK for unit tests. I set up another standard Java Project for the unit tests - and this project uses JDK 1.6. After I make the project (in the same workspace as the above project), I make the main project available to the unit test project. To do this, I select the unit test project and select Project > Properties > Java Build Path > Projects > click Add > check the box against the main FatWire code project. Now my unit test project will have all of the main FatWire project's code in its classpath.

Local JumpStart

In this section, I will explain the steps required to set up a local version of JumpStart that can be used for development and testing of both elements and Java backend code. I will integrate this JumpStart with my FatWire code project.

JumpStart 5.5 uses Resin underneath the hood.

  1. I get the JumpStart kit from the company repository (it's proprietary software and not available from the 'net) and unzip it somewhere such as C:\WorkSpaces\FatWireJsk_5.5.
  2. Click C:\WorkSpaces\FatWireJsk_5.5\CLICK ME FIRST.exe to start Content Server.
  3. I have a short-cut for this (for Windows).
    1. I right click on CLICK ME FIRST.exe and select "Create Shortcut"
    2. I rename the short-cut to something like "JumpStart - CS 5.5".
    3. I click and drag the shortcut to my Quick Launch bar.
    4. I do the same with Content Server Explorer (C:\WorkSpaces\FatWireJsk_5.5\JumpStart\tools\ContentServerExplorer\ContentServerExplorer.exe) and name it something like "CS-Explorer 5.5".
    5. It is important to keep track of the version numbers - CS-Explorer 7.02 is a bit flaky when connecting to Content Server 5.5 for example.
  4. I copy any JAR files I use in my FatWire code project to JumpStart's WEB-INF\lib folder, e.g. C:\WorkSpaces\FatWireJsk_5.5\resin\webapps\servlet\WEB-INF\lib. At least two exceptions are standard.jar and jstl.jar! Resin has its own jar files that cover this functionality.
  5. I next go to the WEB-INF directory that classes will be served from for JumpStart. For CS 5.5, this is C:\WorkSpaces\FatWireJsk_5.5\resin\webapps\servlet\WEB-INF.
  6. I create a classes directory within that folder.
  7. Within Eclipse I create an Ant build script (build.xml) in the main FatWire code project.
  8. I create an Ant task that will copy class files from the main FatWire code project to the WEB-INF\classes directory I just created. For example:
  9. <target name="deployClassesToLocalJumpstart">
      <fail unless="classes.dir"
            message="You must set classes.dir, such as -Dclasses.dir=C:\WorkSpaces\FatWireJsk_5.5\resin\webapps\servlet\WEB-INF\classes" />
      <copy todir="${classes.dir}">
         <fileset dir="bin">
            <include name="**/*.*" />
         </fileset>
      </copy>
    </target>
    
  10. In Eclipse, I create a run configuration to make it easy to run this task from within Eclipse.
    1. From within Eclipse, I select Run > External Tools > External Tools Configurations..
    2. I right click on Ant Build and select New and then select the new Ant build configuration just created.
    3. On the Main tab, I make sure the Buildfile points to my build.xml.
    4. Within the Arguments text area, I fill in the classes.dir argument. For example: -Dclasses.dir=C:\WorkSpaces\FatWireJsk_5.5\resin\webapps\servlet\WEB-INF\classes.
    5. On the Targets tab, I make sure the target I just wrote (deployClassesToLocalJumpstart) is ticked - and no others.
    6. On the Common tab, I select the Shared File radio button and click the Browse tab to select a directory in which to save the "run configuration". I do this so that I can check the "run configuration" file into source control for other programmers to use. Within my Eclipse project, I create a folder such as tools\eclipseIde for these sorts of files.
    7. Under "Display in favourites menu" I check the box for "External Tools". This makes it easier for me and other programmers to invoke the "run configuration".
    8. I click Apply.
    9. I click Close.
  11. Whenever I want to run this to deploy classes to my local JumpStart, I go Run > External Tools > DeployClassesToJumpstart. Personally, I use the keyboard shortcut for these run configurations: Alt, R (for Run), E (for External Tools), X (whatever number DeployClassesToJumpstart is assigned in the menu). For me, I currently type Alt,R,E,2 to deploy classes locally.
  12. After running this, I don't have to re-start JumpStart, but I do have to re-login to ContentServer and Content Server Explorer, because the web application gets restarted.

Now I have a local JumpStart running that I can run my own Java code within - and an Ant task to copy code over to Content Server. I use Content Server Explorer and the Advanced Interface as usual to interact with the other parts of Content Server.

Dynamic Web Project for CS Element Development

Now I need a way to edit CSElement files (primarily JSPs) in Eclipse. In this section I will go over how to create a Dynamic Web Project in Eclipse and set it up with all the ContentServer TLDs and Jar files. In the next section I will explore how to edit Content Server files in the Dynamic Web Project.

  1. In the same Eclipse workspace, I make a Dynamic Web Project. Since I am using FatWire 5.5, I made sure that "Dynamic Web Module Version" is at 2.3.
  2. Copy all of the files inside JumpStart's WEB-INF\lib folder to my dynamic web project's WEB-INF\lib folder, e.g. for FatWire 5.5 this was JumpStart5.5\resin\webapps\servlet\WEB-INF\lib.
  3. I copy the FatWire TLDs to my dynamic web project. For FatWire 5.5 this means copying the futuretense_cs folder (JumpStart5.5\resin\webapps\servlet\WEB-INF\futuretense_cs) to my dynamic web project's WEB-INF folder, i.e. I now have WEB-INF\futuretense_cs.
  4. Copy all of the TAGLIB elements from FatWire's web.xml (JumpStart5.5\resin\webapps\servlet\WEB-INF\web.xml) into my dynamic web project's web.xml.
    • Note the differences between web.xml in Servlet 2.3 (used in FatWire 5.5) and Servlet 2.4 (used in FatWire 7 and beyond).

      Excerpt from a Content Server web.xml using Servlet 2.3 - note that taglib is a child of web-app.

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
      <web-app id="WebApp_ID">
        <display-name>Fatwire5.5 Web</display-name>
        <welcome-file-list>
          <welcome-file>index.jsp</welcome-file>
          ....
        </welcome-file-list>
      
        <taglib>
          <taglib-uri>futuretense_cs/sessionobjects.tld</taglib-uri>
          <taglib-location>/WEB-INF/futuretense_cs/sessionobjects.tld</taglib-location>
        </taglib>
      </web-app>
      

      Excerpt from a Content Server web.xml using Servlet 2.4 - note that taglib is a child of jsp-config.

      <?xml version="1.0" encoding="UTF-8"?>
      <web-app id="WebApp_ID" version="2.4"
            xmlns="http://java.sun.com/xml/ns/j2ee"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
        <display-name>Fatwire7.0 Web</display-name>
        <welcome-file-list>
          <welcome-file>index.jsp</welcome-file>
          ....
        </welcome-file-list>
        <jsp-config>
          <taglib>
            <taglib-uri>futuretense_cs/csmac.tld</taglib-uri>
            <taglib-location>/WEB-INF/futuretense_cs/csmac.tld</taglib-location>
          </taglib>
          ....
        </jsp-config>
      </web-app>
      
  5. I give the dynamic web project access to my FatWire project code by selecting the dynamic web project and selecting Project > Properties > Java Build Path > Projects > click Add > check the box against the main FatWire code project. Now my dynamic web project project will have all of my main FatWire project's code in its classpath.

Editing CSElements

Now I have a dynamic web project with access to all the FatWire tag and Java libraries plus my own project work. I still need to hook this project up to my Content Server files so that I can edit them with Eclipse. Normally, I access Content Server files (CSElements such JSPs, XML files etc) through Content Server Explorer, which opens them up in a text editor of my choice. But this means I won't find out about compilation errors 'till I run the file - and I can't take advantage of the Eclipse tooling for JSP, Javascript, XML etc. I will fix this now.

Set up Eclipse to edit Content Server Explorer files in place. Whenever I edit or open some element through Content Server Explorer, it creates a copy of that file into temporary storage on my hard disk: these are the files I should edit with Eclipse.

  1. Find out what directory Content Server Explorer uses when creating local copies of the element files. UltraEdit tells me file locations when I use it to edit element files (I can also right click on a file tab and select "Open Folder" to open that directory in Windows Explorer). For me, running a FatWire 5.5 instance locally, it makes the files here: C:\Documents and Settings\rbram\Local Settings\Temp\localhost7001\ElementCatalog. Note the localhost7001 part: it is always a concatenation of the host-name/IP plus port number.
  2. I go to the WebContent/WEB-INF folder of my dynamic web project, right click and select New > Folder.
  3. Click Advanced.
  4. Check "Link to folder in the file system".
  5. Click Browse and navigate to the ElementCatalog directory Content Server Explorer is creating the local files within.
  6. I always change the "Folder name" of the folder to reflect the environment I am working within e.g. Local ElementCatalog or Dev ElementCatalog etc.
  7. Click OK.
  8. Click Finish.
  9. I can now directly modify any Content Server element from Eclipse, as long as I lock it and open it through Content Server Explorer first - then refresh my Navigator or Package Explorer view in Eclipse to pick up the file.
  10. Whenever I make an edit, I still have to go back to Content Server Explorer and select "save" there to have it upload the changes to the running application.
  11. Advantages to this approach:
    • I get to see all the files I am currently editing or just looking at - by that I mean all the files I have either opened or locked through Content Server Explorer.
  12. Disadvantages to this approach:
    • Every time I save in Content Server Explorer and go back to Eclipse, I get an annoying dialog that says "the file has been changed outside of Eclipse: do I want to reload the file?" I tried enabling the auto-refresh preference in Eclipse: Window > Preferences > General > Workspace > Refresh automatically. This had no effect so it seems I am stuck with that dialog every time.

Now I can do the following:

  1. My JSPs will compile and I will know about compilation errors etc immediately, without needing to run them.
  2. I have access to all the FatWire tags and classes because the dynamic web project includes the TLDs and APIs for them in the project classpath.
  3. I have access to all the classes in my main FatWire project because the dynamic web project includes the main FatWire project source in its project classpath.

Paste Unformatted Text into Word

Usually, when I want to copy and paste text into Word from a web page, I don't want the formatting. I just want the text and nothing else. In this entry I discuss various ways I have used to do just that.

Updates.
13/08/2009 9:59:07 AM. Corrected macro text thanks to the very helpful comment by Robert Kszan. Thank you Rob!
10/05/2010 3:46:58 PM. Added my preferred choice - using AutoHotkey!

Use Autohotkey Script

This is an amazing Windows scripting engine that is easy to write for and continually amazes me at the fantastic things it can do. First, install Autohotkey. Then create a plain text script file called utilities.ahk (or anything you want - as long as it has extension ahk) with the following contents.

; http://superuser.com/questions/7271/most-useful-autohotkey-scripts
; Paste as plain text
^+v::
    ClipSaved := ClipboardAll ; Save the entire clipboard to a variable of your choice.
    clipboard = %clipboard%  ; Convert any copied files, HTML, or other formatted text to plain text.
    ClipWait, 2
    Send ^v
    Sleep, 1000
    Clipboard := ClipSaved   ; Restore the original clipboard. Note the use of Clipboard (not ClipboardAll).
    ClipSaved =   ; Free the memory in case the clipboard was very large.
return

Save it and double click it - that's enough for AutoHotkey to pick up the script and have it ready. You should now see the AutoHotkey icon in your system tray. Test it out: copy something from a web-page; paste it into Word to see it pasted with formatting; then use control+shift+v to see it pasted as plain text.

Such an amazing tool, this is my preferred method for doing this now. Forget the rest of my post. :)

If you are as hooked as I am, feel free to try out the other scripts in the Superuser - Most useful AutoHotkey scripts? forum posting I got this one from. I have filled my utilities.ahk with others from that page; and since I want these shortcuts always available, I have put a shortcut to the file in my Startup folder (e.g. C:\Documents and Settings\Robert Bram\Start Menu\Programs\Startup.

Use a Text Editor

One way is to first paste the formatted text into a text editor which ignores the formatting (such as UltraEdit or NotePad) and then re-copy the plain text from the text editor, and paste it into Word.

Use a Firefox Add-On

Alternatively, if I am copying the formatted text from Firefox, I use the Copy Plain Text add-on. I select and right click the text I want and select "Copy as Plain Text". Then I can paste the copied plain text directly into Word.

Use Paste Special

If I have copied the text from another browser (or maybe another Word Document or PDF or something else that copies as formatted text), I can use Word 2007's "Paste Special" function.

Use the paste keyboard shortcut: CTRL+V. This pastes the formatted text, but the "Paste Special" icon appears (a little clipboard icon). Click on this icon and select "Keep Text Only".

Alternatively, access the "Paste Special" dialog through Word's menus.

  1. On the Ribbon's Home Tab, select Paste (select the word "Paste", not the clip-board icon) to bring up the Paste menu.
  2. Select "Paste Special".
  3. In the "Paste Special" dialog, select "As:" "Unformatted Text".
  4. Note there is a keyboard combination of ALT keys you can use to bring up the "Paste Special" dialog: ALT, H (Home tab), V (Paste), S (Paste Special).

Earlier version of Word (that don't have the supposedly super useful Ribbon) have the "Paste Special" dialog under Edit > Paste Special (ALT, E (Edit), S (Paste Special)).

Use a Macro

The Paste Special function is excruciatingly tedious for repeated uses. Luckily, we can make a macro for it and assign a keyboard shortcut to the macro.

Word 2007

  1. On the Ribbon's View tab, select Macros (select the word "Macros", not the icon) to bring up the Macros menu.
  2. Select "View Macros" to bring up the Macros dialog.
    • Or just press Alt+F8 to bring up the Macros dialog!
  3. In "Macro name", enter PasteUnformattedText.
  4. Click Create.
  5. This opens Microsoft Visual Basic and creates a new Sub (Sub Procedure), called PasteUnformattedText. Make sure the Sub Procedure is in Normal > Modules > General. Type the following line into the Sub Procedure.
    Selection.PasteSpecial DataType:=wdPasteText
  6. The code window should look something like this:

    (Click to open larger image.)
  7. Save your work and close Microsoft Visual Basic (ALT+Q).
  8. Back in Word, select Microsoft Office button in the upper left-hand corner.
  9. Select "Word Options".
  10. Select Customize on the left hand side.
  11. Click the Customize button down at the bottom of the "Word Options" dialog to bring up the "Customize Keyboard" dialog.

    (Click to open larger image.)
  12. In the "Customize Keyboard" dialog, select Macros on the left hand side.
  13. Select the macro you just made (PasteUnformattedText) on the right hand side.
  14. Click in "Press new shortcut key" and type the shortcut you want to use. I use "CTRL+SHIFT+V". It says this is currently assigned to something else, but I don't care - I override the old assignment because it isn't something I use.
  15. Click Assign and then Close to exit the "Customize Keyboard" dialog.
  16. Click OK to exit the "Word Options" dialog.
  17. You can now use that shortcut to access your macro.

Word 2003 and earlier

  1. Press Alt+F8 to bring up the Macros dialog.
  2. In "Macro name", enter PasteUnformattedText.
  3. Click Create.
  4. This opens Microsoft Visual Basic and creates a new Sub (Sub Procedure), called PasteUnformattedText. Make sure the Sub Procedure is in Normal > Modules > General. Type the following line into the Sub Procedure.
    Selection.PasteSpecial DataType:=wdPasteText
  5. Save your work and close Microsoft Visual Basic (ALT+Q).
  6. Back in Word, select Tools > Customize to bring up the Customize dialog.
  7. Select the Commands tab.
  8. On the left hand side, select Macros.
  9. On the right hand side, select Normal.NewMacros.PasteUnformattedText (the macro you just created).
  10. Click the Keyboard button down at the bottom of the "Customize" dialog to bring up the "Customize Keyboard" dialog.
  11. Click in "Press new shortcut key" and type the shortcut you want to use. I use "CTRL+SHIFT+V". It says this is currently assigned to something else, but I don't care - I override the old assignment because it isn't something I use.
  12. Click Assign and then Close to exit the "Customize Keyboard" dialog.
  13. Click OK to exit the "Word Options" dialog.
  14. You can now use that shortcut to access your macro.

Other Links

Other pages that helped me make this entry.