I just pushed out an update to the Linkselect jQuery Plugin.
I just pushed out an update to the mcDropdown jQuery Plugin.
Yesterday I finally had the incentive to download Git so I could fork a Github project (Mustache.cfc) and contribute some changes I'd made. Being that Eclipse is my main IDE, EGit seemed to be a logical client to install. The install process was painless and I was able to figure out how to clone a local copy, commit and push changes back to Github. Even the Github pull request process was painless. Much to my chagrin, my pull request was accepted almost immediately and merged back into the main branch.
Naturally the next thing I wanted to do was to sync my fork with the original repository. This is where I got stuck. Whether due to my lack of understanding of Git terminology or just a lack of my Google Search skills, I was not able to easily find instructions for how to sync the original repository back to my fork. After much searching, I was finally able to find some help.
However, I figured I'd clean up the instructions a bit and provide a little more detail on the steps for merging the main repository back into your fork.
The first thing you need to do, is set up a new remote repository linked to your fork:
Now that you have a linked the original repository to your fork, you can merge changes from the original repository to your fork:
These steps worked for me, so hopefully they'll help guide someone else!
We recently upgraded our version of Subversion from a very old version of Subversion to v1.7.1. We really wanted the newer merging capabilities that arrived in Subversion 1.5 and figured it was worth the energy to upgrade Subversion to the latest version. Fortunately upgrading our Subversion repositories went very smoothly. I even decided to just do a full dump/load to make sure the repositories were fresh 1.7.x repositories. While this process took a little extra time, the upgrade itself went very smoothly.
However, where things really fell apart for us was with our Trac/SVN integration. The problem is we were still running Python 2.4, which include old SVN bindings, so any attempt to do anything with our SVN ended up with an error that looked like:
TracError: Couldn't open Subversion repository c:/path/to/repository: SubversionException: ("Expected FS format '2'; found format '4'", 160043)
In order to get the SVN commit hooks working with Trac, I was going to need SVN bindings that worked with SVN v1.7.1, but also were compiled for my version of Python. While one might think this wouldn't be difficult, I couldn't find any SVN bindings for Python 2.4. This left my options at either trying to compile my own versions or upgrade Python. Since I don't have easy access to tools for compiling my own version of the Windows binaries, I thought the best option was just to upgrade Python—especially since Trac is soon dropping support for Python 2.4.
The problem is there's no easy way to just migrate a Trac install to a new version of Python, because you're going to need to recompile all your Python *.egg files for the version of Python you're using. After spending way too much time getting all this working, I thought it was wise that I compile a list of all the steps you may need to take in order to get Trac migrated to a new version of Python.
Here is the general install instructions. I don't break this down as a true step-by-step instructions, because so much varies based on your environment. However I do break down all the major steps you'll need to take and try to point you to step-by-step instructions for each bullet when possible.
If all goes well, your environment should now be running under your new Python installation.
This process ended up costing me way more time than expected, so hopefully this guide will prevent you from struggling the way I did.
I'm working on migrating our application to use the latest version of jQuery. In the process of migrating, I've been working on fixing compatibility issues with several of the plug-ins I've worked on to make sure they work with the latest version of jQuery. So, here's a list of updated plug-ins and their release notes:
A recent post on CF-Talk asked whether ColdFusion could use AJAX to do a database lookup. This is actually extremely easy to do in ColdFusion 8+, because it natively supports returning data in the JSON format.
To show how easy this is to do, I decided to throw together a little demo. This took me about 10 minutes to write—most of which was writing the markup. However, the bulk of the work is going to be handled automatically by ColdFusion, which will handle converting your data to JSON and by the jQuery Field Plug-in (which I wrote) which will handle populating your form from the data it receives from ColdFusion.
To show just how easy this all is, here's the jQuery code required to make an AJAX call to a CFC:
$.ajax({ // the location of the CFC to run url: "example.cfc" // send a GET HTTP operation , type: "get" // tell jQuery we're getting JSON back , dataType: "json" // send the data to the CFC , data: { // the method in the CFC to run method: "getUserById" /* send other arguments to CFC */ // send the ID entered by the user , userId: $("#userId").val() } // this gets the data returned on success , success: function (data){ // this uses the "jquery.field.min.js" library to easily populate your form with the data from the server $("#frmMain").formHash(data); } // this runs if an error , error: function (xhr, textStatus, errorThrown){ // show error alert(errorThrown); } });
Our CFC looks like this:
<cfcomponent output="false"> <cffunction name="getUserById" access="remote" returnType="struct" returnFormat="json" output="false"> <cfargument name="userId" type="numeric" required="false" /> <cfset var user = structNew() /> <!---// The only tricky part here is to use the bracketed notation to match the case in your HTML, JS is case sensentive. If you use the dot notation (user.name) then the keys will be returned in uppercase. //---> <cfset user["name"] = "User " & arguments.userId /> <cfset user["email"] = "user_" & arguments.userId & "@example.com" /> <cfif (arguments.userId mod 2) eq 0> <cfset user["gender"] = "f" /> <cfelse> <cfset user["gender"] = "m" /> </cfif> <cfreturn user /> </cffunction> </cfcomponent>
I've posted a working example so that you can see how this code looks. For the "User ID" just enter any number. If an error occurs, the error callback will handle displaying the error to the screen. I've also posted the source code as a zip file you can download.
NOTE:If you're still on ColdFusion MX & 7, there's a little more work because non-string data automatically gets converted to WDDX. While there are JS libraries for converting WDDX to native JS objects, they can be hard to find now that the OpenWDDX site has been shut down. You can find tools at RIAForge though that can convert your data into a JS string--which jQuery will automatically evaluate when it's received.
UPDATE:I've added some sample code to the download to show how you can use a proxy template in you're using ColdFusion 7 to get the same results. Just look at the example_cf7.cfm. The only difference is we call the example_cf7_proxy.cfm template instead of calling the example.cfc directly. The proxy template uses CFJSON to convert the results to JSON.
[UPDATED: Friday, March 04, 2011 at 9:59:11 AM]
I recently installed a copy of CFCDocs from RIAForge to give our developers better documentation for all of our various components. However, I quickly ran into a few problems related to the CFCParser component that it uses to analyze components.
The CFCParser doesn't create an instance of the object, but instead analyzes the component using a custom parser. The problem is the parser uses some overly simple logic for detecting attribute name/value pairs and for finding starting/ending tag values.
The problem really presents itself when you have a piece of code as follows:
<cfargument name="hightlightStart" type="string" required="false" default="<span style=""background-color:##ffff66;"">" />
In XML, it would be invalid to have an attribute that looked like: default="<span style=""background-color:##ffff66;"">". However, in ColdFusion this is perfectly legally.
After spending way too much time debugging the issue, I found there were two distinct issues:
I was able to fix both of these issues by re-writing portions of both the findTags() and getTagAttributes() functions.
Since I don't know who wrote this code or who manages it, I thought I'd at least post the code here in case anyone else has issues w/this component.
<cfcomponent hint="The CFCParser component provides methods to parse a ColdFusion Component."> <cffunction name="init" access="public" returntype="CFCParser" output="false" hint="The init method currently does nothing."> <cfreturn this> </cffunction> <cffunction name="findTags" access="private" returntype="array" output="true" hint="The findTags method searches the document for the given startTag and endTag. It returns an array of structures containing the locations in the document of the start and end position of each tag, and the full contents of the tag itself."> <cfargument name="document" type="string" required="yes"> <cfargument name="startTag" type="string" required="yes"> <cfargument name="endTag" type="string" required="yes"> <!--- Find and remove comments ---> <cfset var tagLocations = arrayNew(1)> <cfset var nestingLevel = 1> <cfset var searchMode = "start"> <cfset var position = 1> <cfset var i = 0> <cfset var j = 0> <cfset var tagBegin = 0> <cfset var tagEnd = 0> <cfset var tagBlock = ""> <cfset var tmpPosition = 0> <cfset var nestCount = 0> <cfset var padding = ""> <cfset var lastReturn = ""> <cfset var lastSpace = ""> <cfset var stTag = ""> <!----// captures quoted strings and the end tag //---> <cfset var regexFindEndTag = '(((("")|".*?[^"]"(?!"))|(('''')|''.*?[^'']''(?!''))))|(#arguments.endTag#)' /> <cfset var findEndTag = 0 /> <cfset var findEndTagMatch = "" /> <cfloop from="1" to="#len(document)#" index="i"> <cfif searchMode is "start"> <cfset tagBegin = findNoCase(startTag,document,position)> <cfif tagBegin> <cfset position = tagBegin + len(startTag)> <cfset searchMode = "end"> <!--- <cfoutput>Start Tag found at character #tagBegin#<br></cfoutput> ---> <cfelse> <cfbreak> </cfif> <cfelse> <cfset findEndTagMatch = "" /> <!---// if finding complex ending tokens, use original simple find //---> <cfif arguments.endTag neq ">"> <cfset tagEnd = find(endTag,document,position)> <!---// for other logic, we need to make sure we don't accidentally find the ending tag inside a quoted string //---> <cfelse> <!---// loop through quotes strings and end tag matches looking for the first end tag match //---> <cfloop condition="findEndTagMatch neq arguments.endTag"> <!---// we're going to loop through quoted strings and matching end tags looking for a match //---> <cfset findEndTag = reFindNoCase(regexFindEndTag, document, position, true) /> <!---// if a match was found, get it //---> <cfif findEndTag.pos[1]> <!---// get the match--which is either a quoted string or the matching end tag //---> <cfset findEndTagMatch = mid(document, findEndTag.pos[1], findEndTag.len[1]) /> <!---// update the position in the search //---> <cfset position = findEndTag.pos[1] + findEndTag.len[1] /> <!---// if no matches, stop //---> <cfelse> <cfbreak /> </cfif> </cfloop> <!---// return the ending position //---> <cfset tagEnd = findEndTag.pos[1] /> </cfif> <cfif tagEnd> <cfset tagEnd = tagEnd + len(endTag)> <cfset position = tagEnd> <!--- <cfoutput>End Tag found at character #tagEnd#<br></cfoutput> ---> <cfelse> <cfbreak> </cfif> <cfset tagBlock = mid(document,tagBegin,tagEnd-tagBegin)> <cfset tmpPosition = 1> <cfset nestCount = 0> <cfloop from="1" to="#len(tagBlock)#" index="j"> <cfif findNoCase(startTag,tagBlock,tmpPosition)> <cfset tmpPosition = findNoCase(startTag,tagBlock,tmpPosition) + len(startTag)> <cfset nestCount = nestCount + 1> <cfelse> <cfbreak> </cfif> <!--- <cfoutput>TmpPosition: #tmpPosition#(#htmlEditFormat(mid(tagBlock,tmpPosition,len(tagBlock)))#)<br></cfoutput> ---> </cfloop> <!--- <cfoutput>count - #nestCount# :: Level - #nestingLevel#<br></cfoutput> ---> <cfif nestCount EQ nestingLevel> <cfset lastSpace = reFindNoCase('[#chr(32)##chr(9)#][^#chr(32)##chr(9)#]+$',tagBlock)> <cfset lastReturn = reFindNoCase('[#chr(10)##chr(13)#][^#chr(10)##chr(13)#]+$',tagBlock)> <cfset padding = ""> <cfif lastReturn AND lastSpace AND lastReturn LT lastSpace> <cfset padding = mid(tagBlock,lastReturn+1,lastSpace-lastReturn)> </cfif> <cfset stTag = structNew()> <cfset stTag.start = tagBegin> <cfset stTag.end = tagEnd> <cfset stTag.tagBlock = padding & tagBlock> <cfset arrayAppend(tagLocations,stTag)> <cfset searchMode = "start"> <cfelse> <cfset nestingLevel = nestingLevel + 1> </cfif> </cfif> </cfloop> <cfreturn tagLocations> </cffunction> <cffunction name="removeComments" access="private" output="false" returntype="string" hint="Strips the comments from a document so that code inside comments gets ignored by the findTags method"> <cfargument name="document" type="string" required="yes"> <cfset var tagLocations = findTags(arguments.document,"<!---","--->")> <cfset var offset = 0> <cfset var i = 0> <cfset var start = 0> <cfset var count = 0> <cfloop from="1" to="#arrayLen(tagLocations)#" index="i"> <cfset start = tagLocations[i].start - offset> <cfset count = tagLocations[i].end - tagLocations[i].start> <cfset arguments.document = removeChars(arguments.document,start,count)> <cfset offset = offset + count> </cfloop> <cfreturn document> </cffunction> <cffunction name="getMethods" access="private" returntype="array" output="false" hint="Calls the findTags method to retrieve all cffunction tags in the given document."> <cfargument name="document" type="string" required="true"> <cfreturn findTags(document,"<cf"&"function ","</cf"&"function>") /> </cffunction> <cffunction name="getProperties" access="private" returntype="array" output="false" hint="Calls the findTags method to retrieve all cffunction tags in the given document."> <cfargument name="document" type="string" required="true"> <cfreturn findTags(document,"<cf"&"property ",">")> </cffunction> <cffunction name="getArguments" access="private" returntype="array" output="false" hint="Calls the findTags method to retrieve all cfarguments tags in the given document. This method should be passed the body of a cffunction tag as the document argument."> <cfargument name="document" type="string" required="true"> <cfreturn findTags(document,"<cf"&"argument ",">")> </cffunction> <cffunction name="getTagAttributes" access="private" returntype="struct" output="false" hint="Parses the attributes out of the given document for the first occurrence of the tag specified and returns a structure containing name value pairs for the tag attributes."> <cfargument name="document" type="string" required="true"> <cfargument name="tagname" type="string" required="true"> <cfset var startTag = ""> <cfset var stAttributes = structNew()> <!--- fails on escaped quotes inside the attribute <cfset var regex = '[[:space:]][^=]+="[^"]*"' > ---> <!---// get attributes, make sure we get escaped quotes inside quoted strings //---> <cfset var regex = '\s[^=]+=((("")|".*?[^"]"(?!"))|(('''')|''.*?[^'']''(?!'')))' /> <cfset var aTmp = reFindNoCase('<#arguments.tagname#(#regex#)*[^>]*>',document,1,true)> <cfset var i = 1> <cfset var position = 1> <cfset var attribute = ""> <cfset var attrName = ""> <cfset var attrValue = ""> <cfif NOT aTmp.pos[1]> <cfreturn stAttributes> </cfif> <!---// refactored code to use reMatch //---> <cfset startTag = mid(document,aTmp.pos[1],aTmp.len[1])> <cfset aTmp = reMatchNoCase(regex, startTag) /> <cfloop index="i" array="#aTmp#"> <cfset attribute = trim(i) /> <cfset attrName = listFirst(attribute, "=") /> <cfset attrValue = listRest(attribute, "=") /> <cfset stAttributes[attrName] = mid(attrValue, 2, len(attrValue)-2) /> </cfloop> <cfset stAttributes.fullTag = startTag> <cfreturn stAttributes> </cffunction> <cffunction name="parse" access="public" returntype="struct" output="false" hint="Provides the public interface to the CFC parser. This method should be passed the contents of a full ColdFusion component file."> <cfargument name="document" type="string" required="true"> <cfset var cleanDocument = ""> <cfset var stComponent = ""> <cfset var aMethods = ""> <cfset var i = ""> <cfset var j = ""> <cfset var stMethod = ""> <cfset var stArgument = ""> <cfset var aProperties = ""> <cfset var stProperty = ""> <cfset var aArguments = ""> <cfset var attribStruct = structNew()> <cfset cleanDocument = removeComments(document)> <cfset stComponent = structNew()> <cfset stComponent.isInterface = false> <cfset stComponent.attributes = structNew()> <cfset stComponent.attributes.hint = ""> <cfset stComponent.attributes.extends = "cfcomponent"> <cfset stComponent.attributes.implements = "cfinterface"> <cfset stComponent.attributes.displayname = ""> <cfset stComponent.attributes.output = ""> <!--- check to see if it is a component ---> <cfset attribStruct = getTagAttributes(cleanDocument,'cfcomponent')> <cfif structIsEmpty(attribStruct)> <!--- if no attribs found, it might be an interface ---> <cfset attribStruct = getTagAttributes(cleanDocument,'cfinterface')> <cfif NOT structIsEmpty(attribStruct)> <cfset stComponent.isInterface = true> </cfif> </cfif> <cfset structAppend(stComponent.attributes,attribStruct,true)> <cfset stComponent.properties = structNew()> <cfset aProperties = getProperties(cleanDocument)> <cfloop from="1" to="#arrayLen(aProperties)#" index="j"> <cfset stProperty = structNew()> <cfset stProperty.name = ""> <cfset stProperty.type = "any"> <cfset stProperty.required = "false"> <cfset stProperty.default = "_an_empty_string_"> <cfset stProperty.displayName = ""> <cfset stProperty.hint = ""> <cfset structAppend(stProperty,getTagAttributes(aProperties[j].tagBlock,'cfproperty'),true)> <cfset stComponent.properties[stProperty.name] = stProperty> </cfloop> <cfset stComponent.methods = structNew()> <cfset aMethods = getMethods(cleanDocument)> <cfloop from="1" to="#arrayLen(aMethods)#" index="i"> <cfset stMethod = structNew()> <cfset stMethod.name = ""> <cfset stMethod.access = "public"> <cfset stMethod.returnType = "any"> <cfset stMethod.roles = ""> <cfset stMethod.output = ""> <cfset stMethod.displayname = ""> <cfset stMethod.hint = ""> <cfset structAppend(stMethod,getTagAttributes(aMethods[i].tagblock,'cffunction'),true)> <cfset stMethod.fullTag = aMethods[i].tagBlock> <cfset stComponent.methods[stMethod.name] = stMethod> <cfset stMethod.arguments = arrayNew(1)> <cfset aArguments = getArguments(aMethods[i].tagBlock)> <cfloop from="1" to="#arrayLen(aArguments)#" index="j"> <cfset stArgument = structNew()> <cfset stArgument.name = ""> <cfset stArgument.type = "any"> <cfset stArgument.required = "false"> <cfset stArgument.displayName = ""> <cfset stArgument.hint = ""> <cfset stArgument.default = "_an_empty_string_"> <cfset structAppend(stArgument,getTagAttributes(aArguments[j].tagBlock,'cfargument'),true)> <cfset arrayAppend(stMethod.arguments,stArgument)> </cfloop> </cfloop> <cfreturn stComponent> </cffunction> </cfcomponent>
NOTE:I also updated all the unscoped variables!
[UPDATED: Thursday, February 24, 2011 at 2:21:00 PM]
For the longest time I've been stuck on Mylyn Eclipse Plug-in v3.0.5—which is very old. Every time I attempted to upgrade to a newer version, Mylyn could no longer authenticate against my Trac installation on Apache. It was a frustrating problem, because I really wanted to upgrade to take advantage of a number of enhancements and fixes.
Today I went upgrade another plug-in and figured I'd try the latest version of Mylyn to see if my issue was resolved. Unfortunately, I was still getting the error "Insufficient permissions for selected access type" anytime Mylyn would try to authenticate via the XML-RPC method. What was really strange, is when I tried connecting via the "Web" method, everything worked fine (which was a new behavior.) So, I decided to spend some time in Charles HTTP Proxy to see if I could debug the issue.
I spent some time monitoring the HTTP traffic, but really couldn't find any rhyme or reason why the authentication was failing. So, I decided to investigate my Apache modules to see if there were any updates to the authentication modules I was using.
One aspect of my Apache configuration that is a little different is that I use mod-auth-sspi module because I authenticate against our Active Directory server (see Configuring Windows Authentication with Apache 2.2.x and Subversion.) After much research and playing around with our configuration, I finally discovered the fix for my problem.
There's apparently an issue with the case that Mylyn sends the authentication information in, the trick was to change my Apache configuration and add the line: SSPIUsernameCase lower
Here's what my authentication configuration rules look like in my Apache httpd.conf file:
AuthType SSPI SSPIAuth On SSPIAuthoritative On # set the domain to authorize against SSPIDomain AD SSPIOfferBasic On # let non-IE clients authenticate SSPIOmitDomain On # keep domain name in userid string SSPIBasicPreferred Off # basic authentication should have higher priority SSPIPerRequestAuth On # required for IE POST operations SSPIUsernameCase lower
So, if you're having mystery issues with applications authenticating against your Apache server and your using SSPI, you might try implement the SSPIUsernameCase lower directive.
Paginating data is one of those commonly required tasks. In previous version of MS SQL Server it's been tricky to handle, but made easy via the use of stored procedures that handle the pagination logic for you. However, if you're using SQL Server 2005 there's a much easier way to handle pagination and that's to use a Common Expression Table (CTE.)
Microsoft created the CTE syntax to make solving complicated tasks easier (such as returning a query of hierarchical tree data.) When you create a CTE, you're essentially creating a virtual table you can query against. CTEs generally perform very well and can often replace the need for creating temp tables.
Let's look at an example of query out records 11-20 from a Employee database:
-- create the Common Table Expression, which is a table called "pagination" with pagination as ( -- your normal query goes here, but you put your ORDER BY clause in the rowNo declaration select row_number() over (order by department, employee) as rowNo, -- a list of the column you want to retrieve employeeId, employee, department from Employee where disabled = 0 ) -- we now query the CTE table select -- add an additional column which contains the total number of records in the query *, (select count(*) from pagination) as totalResults from pagination where RowNo between 11 and 20 order by rowNo
The first part of the query creates the table expression we'll actually query against when we grab the actual results from the database. In the CTE you're going to write the SQL you'd normally write to grab all the records with one exception—you use the row_number() function to generate a virtual column that orders your result rows for you. The actual "order by" clause you want to use will actually go in the row_number() declaration—this makes SQL Server assign the correct row number for each record.
It's important to remember that a CTE can be as complex as you need it to be. You can do joins, pivots, etc—whatever you need.
The next step is to actually query against the CTE you created. This query will virtually be the same for all pagination queries you write. You are simply grabbing all of the columns and then limiting the returned data to just the rows you want to display (in this case rows 11 - 20.)
In this example, I added an additional column that contains the total results that were returned from the CTE. This is not needed, but it's handy if you need to do something like "Results 11 - 20 of 4,567 records." There are many ways you could generate the total records, this is just one method.
If you've upgraded to SQL Server 2005 and haven't started using CTEs yet, you really need to start getting familiar with them as they really simplify many tasks in SQL Server that previously were very difficult to solve.
I was working on another SVN hook today and only wanted to run some code if a particular folder was modified, just to save some processing time from unnecessarily running an SVN update process. Here's the little snippet I came up with to run in my post-commit.bat file:
The idea behind this is simple. We check the log for the revision that was just checked in to see if it contains a specific string—which in this case is a folder path. If the log does contain the string, we update our local copy. If it doesn't exist, we skip that step and just output a message that says no updates found.
Make sure to check out my other posts on using SVN hooks in Windows for more tips and tricks.
Raymond Camden blogged about a question someone had about getting the folder path for the current template. While Raymond addressed how to get the OS path, how would you get the URL path? So, if you had your user was on the URL http://www.example.com/some/folder/and/file.cfm, how would you go about getting the "/some/folder/and/" path?
While there are always many ways to solve a problem, I've tried to come up with a solution that should work for any version of ColdFusion from 6.0 and above. I wanted to avoid using CGI variables (since those vary by webserver,) so I went with using getPageContext() instead.
Here's the solution I wiped up:
If you just call getWebPath() it will return the current web folder path for the current base template.
We use a regular expression to strip out additional path info information that can sometimes be present for people using SEO-friendly URLs. For example, the URLs on my site appear like: http://blog.pengoworks.com/index.cfm/2006/9/27/CFMX-UDF-Parsing-a-URI-into-a-struct which returns a path of /index.cfm/2006/9/27/CFMX-UDF-Parsing-a-URI-into-a-struct. We need the regex to find the last period in the string and assume everything else is additional path info. The default regex should work in the vast majority of cases, but you can adjust it for the corner cases.
You can also manually supply a path such as: #getWebPath('/index.cfm/2006/9/27/CFMX-UDF-Parsing-a-URI-into-a-struct')#. This would return "/" as the web path.
Anyway, hopefully some of you will find this little UDF useful.
I had the need to convert a ColdFusion date/time stamp to a JS Date Object. I thought serializeJSON() function would handle this, but it turns out it treats CF date/time variables as strings. The toScript() function will convert CF variables to JS Date Objects—provided that the date/time variable is in ODBC format (i.e. {ts '2008-05-02 13:32:16'}.)
However, I wanted something that would work for anything that ColdFusion saw as a Date object, so I just whipped out this little 4 line helper function:
If ColdFusion doesn't see the date as a date object, then it'll set the date/time to "null". To use this function you just do:
This would then generate the following:
Obviously this is pretty straightforward, but it's saved me a lot of repetitive typing today and simplified the readability of my code.
[UPDATED: Monday, November 21, 2011 at 8:33:18 AM]
I'm working on a project where I'm trying to create thumbnails for documents the user uploads. Since CF8 has introduced the <cfpdf /> tag, I thought it would be pretty straightforward to turn page 1 of a PDF into a thumbnail image—turns out I was wrong.
While the <cfpdf /> does work, it was causing me to jump through some various hoops some of which I could easily overcome. The issues I had were:
I've been working on a Java applet that can get images from the user's clipboard or take screenshots of the user's desktop and upload them to a server via HTTP. The last piece of the puzzle was to make sure I could access the task methods via JavaScript, as the interface will most likely be driven by HTML.
I've been using a self-signed cert to sign the Java Applet so that I can access the user's clipboard and take the screenshot. All of this was tested and working extremely well from if I used the Applet UI. However, as soon as I would try to invoke one of the sandboxed functions from JavaScript the applet started throwing the error:
AccessControlException is thrown: access denied (java.awt.AWTPermission accessClipboard)
I tried a number of things to get around this problem. I thought it was something I was doing wrong when I was signing the applet (since it was working fine from within the Applet's built-in UI controls.) After playing around with the cert signing process and getting no where, I finally came across the post JavaScripting in Applets - Getting Out of the Sandbox where a comment from Stéphane Bury pointed me to a solution.
public static void doSpecialWork(String param1){ final String fParam1 = param1; java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { public Object run() { // put the code of your method here ... ... return ""; } }); }
My final solution was to write a command() method which I can use to trigger off the internal privileged methods:
/** * The method to invoke from JS to perform the privileged methods--which throw * security errors if you try to access them directly. * * @param command - the command you want to perform (clipboard, screenshot, upload) */ public void command(String command){ final String cmd = command; java.security.AccessController.doPrivileged( new java.security.PrivilegedAction(){ public Object run() { // execute the privileged command executeCommand(cmd); // we must return an object, so we'll return an empty string return ""; } } ); }
As you can see the method is very basic, it just passes the command it received to a private method which actually executes the command. Now I can access any of the privileged methods in my applet with this little helper method.
[UPDATED: Saturday, March 08, 2008 at 10:26:56 AM]
I've been playing around with e - TextEditor this weekend to see if maybe it'll replace Textpad for me. I've always liked the speed of Textpad and it does a really good job on large files. I use Eclipse as my main IDE, so when it comes to a Notepad replacement my two main requirements are fast load times and the ability to handle large text files (for reading logs.)
However I keep seeing some of the really cool things that TextMate can do, so I've been keeping an eye out on e - TextEditor. It has a couple of features that would make quick editing of HTML files extremely easy. I really like the ability to selected a bunch of words or tags in a document and easily replace them.
One of the key features of e is it's TextMate bundle support—which really allows you to extend the functionality of the program. While surfing the e forums, I came across a Bundle command which allows you to open the file you have selected in the document (or the file where the caret is positioned.) This script makes it really easy to open files being loading from <script />, <style /> or any other tag you may use for loading files.
The original script, posted by tanguyr, I found either needed you to add e.exe to the Windows PATH environment or you needed to hard code the path to e.exe. I fixed the problem by using one of e's environmental variables. The $TM_SUPPORT_PATH variable points to a sub-directory above where the e.exe file exists, so I just use a relative path to get to the e.exe executable.
To add this command to e, go to the Bundles > Edit Bundles > Show Bundle Editor. I added this script to the Source bundle, as it seemed to make the most sense to me. Select the Source folder and click the + button and select the "New Command" option. I decided to call the command "Open Target Document", if you don't like the name choice something you like better.
Paste the following Bash script into the source area.