<CharlieDigital/> Programming, Politics, and uhh…pineapples

9Oct/13Off

Is Social for the Enterprise Useless?

Posted by Charles Chen

There is a key difference when we look at social networks in our personal lives (Facebook , Twitter, etc.) and professional lives (LinkedIn) and social networks in an enterprise and that key difference is that enterprises are still primarily concerned with getting work done.

I've already given my thoughts on it before: I think enterprise social is more or less useless for most employees in a corporation.

Wired has a good opinion piece on this:

If we sat down and read every email in everyone’s inboxes, took notes in every meeting, lurked in every chat room, and then carefully kept everything up to date, we’d be able to answer questions like, What are all the steps left between now and the next project? Who’s responsible for each step? Which tasks are high priority, and which can wait until later? Where are all the files and conversations needed to do this particular activity?

Or, Why did we decide that thing six months ago that’s affecting what I’m trying to do right now? Heck, What should I be working on right now?

But no one is able to read/note/track/lurk on everything. Even if they could, it’s soul-sucking (at least, it was for me). And this work about work — and the resulting confusion around not knowing what’s expected of us — contributes to disengagement, resulting in billions in lost productivity. Every year.

It seems crazy that 99% of companies lack a single place to track all of this, a definitive source of “truth” about everything they’re working on. Crazier still given that $304 billion will be spent on enterprise software this year, much of it — like enterprise social networks — purporting to solve these problems. The problem with many of these approaches is that they’re just ports of earlier technologies designed for connecting people, not for coordinating work.

Yet there’s a way to work together with less effort, and it requires harnessing the work graph. Whereas a social graph maps people and their relationships, a work graph centers around the work.

I think the bolded is the key; what enterprise needs today isn't necessarily tools to connect people -- there are plenty of those already -- but better tools to coordinate work.  What enterprises need is a way to streamline how work gets done and -- sure -- part of that is connecting the right people, but part of that is being able to then coordinate the effort without introducing too much process friction and overhead.

By and large, most of the teams I work with from Fortune 500 customers and clients as well as tier 1 consulting companies still rely on positively antiquated processes and software to manage coordination of work that are heavily wrought with impedance.  Microsoft Project plans and Excel spreadsheets are some of the absolute worst tools that one could possibly choose for planning, managing, and coordinating modern software projects and yet they are still the bread and butter of many teams (maybe that's why "On average, large IT projects run 45 percent over budget and 7 percent over time, while delivering 56 percent less value than predicted").

I find that most of that is a result of the simple fact that project managers in your typical enterprise project are likely to be more senior folks who moved on up from their prior positions that are somewhat set in their ways and their tools.  Software and how we interact with it has evolved dramatically over the last decade (think about it, Facebook didn't exist 10 years ago) and yet many project managers have not evolved their thinking and how to utilize more fit-for-purpose software for coordinating work; they have stuck to their guns and carry over legacy Excel spreadsheets and Microsoft Project plans that are simply too unwieldy for fast-paced, day-to-day collaborative execution of work.

Just as cloud infrastructure is beginning to shift how enterprises rethink IT infrastructure, will we see a similar shift in how teams coordinate work?  If it comes, I think it will look far less "social".

Thinktastic TeamPoint is one crack at solving this problem of coordinating work by integrating chat with real-time notifications linked to project artifacts (tasks, documents, milestones) to create a real-time, contextual, workstream that allows teams to view progress, broadcast status and in-progress work, as well as provide greater visibility into the overall health of the project.

20Sep/13Off

Step by Step Android Development with Cordova and IntelliJ

Posted by Charles Chen

There are other guides that cover this, but I found them lacking a bit in the step-by-step department for a beginner like myself.

Why IntelliJ?  Well first, Google has chosen it as the basis of their Android Studio IDE.  And second, Eclipse sucks :-D

Okay, so let's get started.

Download Pre-Requisites

  1. Download the Java SDK from here: http://www.oracle.com/technetwork/java/javase/downloads/index.html
  2. Download IntelliJ IDEA Community Edition from here: http://www.jetbrains.com/idea/download/
  3. Download and unzip the Android SDK or ADT Bundle from here: http://developer.android.com/sdk/index.html
  4. Download and unzip the PhoneGap package from here: http://phonegap.com/install/

If you download the Cordova package directly from the Apache site, you'll have to deal with the build process since it comes as source.  It's just all around easier to get started with the PhoneGap binaries.

Install Java

Follow the standard installation for the SDK, but after you finish, you will want to update your environment settings.

First, add the path to the Java binaries to your Path variable (right click on Computer -> Properties -> Advanced system settings -> Environment Variables).

Find the Path variable and add the path to the bin directory under where you installed the SDK:

path-var

Then click on New and add a variable called JAVA_HOME pointing to the directory where the SDK is installed:

java-home

If you open a command window and run javac, you should see the following:

javac-command

If not, you need to double check your settings.

Install IntelliJ

Not much to see here as the install is straight forward.  The installer will prompt you to run IntelliJ after it completes.

Create a New Project

Now we can create a new project and select Application Module under Android and then enter a project name and location:

new-project

Next, we need to select the Project SDK.  Click on the New button to create a new SDK mapping.  On your first run, it will prompt you to configure a Java SDK:

new-sdk

Click OK and select the location where the Java SDK was installed (C:\Program Files\Java\jdk1.7.0_04 for me).

Once you select that, you will be prompted to select the location of the Android SDK (E:\Experimental\android\adt-bundle-windows-x86_64-20130911\sdk for me).

It's the same dialog, so pay attention to the title.

Finally, you will see a dialog prompting you to Created New Android SDK:

create-android-sdk

When you are all done, it should look like so:

selected-android-sdk

Click Next and click Finish on the final screen:

finish-project-creation

Set Up Cordova

Now that we have our project set up, we need to hook Cordova into it.

Find the location where you unzipped the PhoneGap binaries and go to lib\android; you will find cordova.js and cordova-2.9.0.jar here.

Under the assets directory in the project, create a directory called www and paste the cordova.js file here.

Under the libs directory, paste the cordova-2.9.0.jar file here.

You need to right click on this file and select Add as Library.

Finally, under the res directory, paste the xml directory from the PhoneGap SDK and your directory should look like this:

project-setup

Create Content Files

Now we're ready to create our content files!  In the assets\www directory, add an HTML file called index.html:

create-html-file

Now update the HTML file with the following:

<!DOCTYPE html>
<html>
<head>
    <title>Demo Phonegap</title>
    <script type="text/javascript" charset="utf-8" src="cordova.js">
    </script>
</head>
<body>
<h2>Hello Android</h2>
</body>
</html>

Locate the file MyActivity.java and paste the following:

package com.example.CordovaExample;

import android.os.Bundle;
import org.apache.cordova.DroidGap;

public class MyActivity extends DroidGap {
    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.loadUrl("file:///android_asset/www/index.html");
    }
}

You'll noticed that even though the directory is assets\www, it must be androd_asset/www for the code to work.

According to the official Cordova docs, you need to update the file AndroidManifest.xml file (however, I didn't need to add it to run it at this stage).  You need to paste the following into your AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.CordovaExample"
          android:versionCode="1"
          android:versionName="1.0">
    <uses-sdk android:minSdkVersion="18"/>
    <supports-screens
            android:largeScreens="true"
            android:normalScreens="true"
            android:smallScreens="true"
            android:resizeable="true"
            android:anyDensity="true"/>
    <uses-permission android:name="android.permission.VIBRATE"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
    <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
        <activity android:name="MyActivity"
                  android:label="@string/app_name"
                  android:screenOrientation="sensor"
                  android:configChanges="orientation|keyboardHidden|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>

Set Up Virtual Device

Now you're ready to run!  You'll need to set up an Android Virtual Device (AVD) by selecting Tools -> Android -> AVD Manager:

create-avd

In this dialog, click on the Device Definitions tab and select Nexus S by Google and then click Create AVD:

selected-avd

Accept the defaults and enter 10 in the SD Card Size.  Now you're ready to build and run.

Build and Run

From the Build menu, select Make Project.  Once this completes successfully, click on Run -> Run 'CordovaExample'.

You should see your virtual device boot up now :-D (it could take a while...)

The cool thing is that as you update your code and build, IntelliJ will automatically push the changes to the virtual device.

Filed under: Android, Dev, DevTools No Comments
8Aug/11Off

TFS – Does It Suck?

Posted by Charles Chen

I'm not sure, but I don't want to find out either.

I'm currently tasked with recommending a new source control platform and a new defect tracking platform as well.

I'm late to this post from March 2010, but Martin Fowler posted an internal ThoughtWorks survey of version control tools:

I conducted the survey from February 23 2010 until March 3 2010 on the ThoughtWorks software development mailing list. I got 99 replies. In the survey I asked everyone to rate a number of version control tools...

...there's a clear cluster around Subversion, git, and Mercurial with high approval and a large amount of responses. It's also clear that there's a big divide in approval between those three, together with Bazaar and Perforce, versus the rest.

The biggest offender?  TFS with a 0% (yes, z-e-r-o) approval from the ThoughtWorks staff.  Scary.

James McKay provides an interesting take on it:

Team Foundation Server advocates claim it’s unfair to compare TFS to other source control tools, since it’s not just source control, but an integrated end-to-end application lifecycle management solution. Comparing TFS to, say, Subversion, is like comparing Microsoft Office to Notepad, so they say.

Now where have I heard something like that before? Oh yes, Lotus Notes:

The main focus for frustration is Notes’s odd way with email, and its unintuitive interface. But to complain about that is to miss the point, says Ben Rose, founder and leader of the UK Notes User Group (www.lnug.org.uk). He’s a Notes administrator, for “a large automotive group”.

It’s regarded by many as an email program, but it’s actually groupware,” Rose explains. “It does do email, and calendaring, but can host discussion forums, and the collaboration can extend to long-distance reporting. It will integrate at the back end with huge systems. It’s extremely powerful.”

The thing is, it wasn’t the detractors who were missing the point. It was the Lotus Notes guys. You see, e-mail is right at the heart of any groupware application. It’s the part of the application that users interact with the most. It’s where usability matters the most. And it’s what Notes got wrong the most.

Is TFS really that bad?  I haven't used it or recommended it (mostly out of concern for cost), but 0% approval?

On a related note, I've been digging into Redmine the last few days to try to examine its suitability for a project that I'm taking over and new products that I'll be bringing online.  I've been really impressed with it, even compared to the excellent Trac.  Compared to Trac, Redmine just feels more well put thought out (i.e. native support for multiple types of source control systems, native sub-projects, so on) and the UI is a bit cleaner and easier to use.  I expect to be blogging about it frequently in the coming months.

13Jun/11Off

FFMPEG and Flowplayer

Posted by Charles Chen

Flowplayer is a great little bit of Flash and JavaScript for incorporating streaming video on your site (until we get a fully viable, widely supported HTML5 solution).

I've used it in various capacities for putting together apps to stream recorded webcasts using CamStudio and splicing video and audio clips together in Windows Live Movie Maker (plenty adequate for my simple usage).  You can see an example of this on the home page for zaanglabs.

The input video was a 720p .wmv file output from Movie Maker that comes in at 13.7MB.  The output is a 3MB h.264 encoded .flv that is almost as good as the original.

For anyone doing webcasts using open source tools (or you just need to encode whatever you output from Movie Maker), this is what you'll need to pull it all together:

FFMPEG -i input.wmv -ar 44100 -qscale 9 -vcodec libx264 output.flv

The input is the file saved from Movie Maker and the output is a file that can be streamed using Flowplayer.  That seems to be the secret sauce after playing around with various settings and different configuration options.  This yields an output video that should be nearly indistinguishable from the original for webcasting purposes.  I'm sure you'll have to adjust the value if you have more action in your frames.

Happy webcasting!

1Oct/10Off

Irony .NET Language Implementation Kit

Posted by Charles Chen

I came across Irony (http://irony.codeplex.com/) today while contemplating whether to use antlr or not for a project I'm working on where the requirements call for allowing users to write small conditional instructions.

From the project site description:

Irony is a development kit for implementing languages on .NET platform. It uses the flexibility and power of c# language and .NET Framework 3.5 to implement a completely new and streamlined technology of compiler construction.

Unlike most existing yacc/lex-style solutions Irony does not employ any scanner or parser code generation from grammar specifications written in a specialized meta-language. In Irony the target language grammar is coded directly in c# using operator overloading to express grammar constructs. Irony's scanner and parser modules use the grammar encoded as c# class to control the parsing process. See the expression grammar sample for an example of grammar definition in c# class, and using it in a working parser.

Compared to antlr, it seemed much simpler from the samples.

In the past, I've usually used Spring.NET's expression evaluation functionality (built on antlr); however, the entirety of the Sprint.NET library seemed too heavy for the simple scenario I had to implement and I'd still have to do some string parsing anyways if I used it.  So I set out to try out Irony for myself instead.

The basic gist of the solution is that the interface needs to allow users to specify meta-instructions as strings in the form of:

if ("property1"="value1") action("param1","param2")

Once the user has configured the instructions for a given template document, the meta-instructions are executed when an instance of the template is created and metadata properties are set on the document (in SharePoint).  So the goal is to define a set of meta-instructions and actions which allow users to build dynamic document templates.  For example:

if ("status"="draft") delete()
if ("status"="published") lock()
if ("status"="pending") insert("22ad25d6-3bbd-45f3-bc63-e0e1b931e247")

The first step is to define the grammar (I'm sure this isn't very well constructed BNF, but I need to brush up on that :-D):

using Irony.Parsing;

namespace IronySample
{
    public class AssemblyDirectiveGrammar : Grammar
    {
        public AssemblyDirectiveGrammar() : base(false)
        {
            // Terminals
            StringLiteral property = new StringLiteral("property", "\"");
            StringLiteral value = new StringLiteral("value", "\"");
            StringLiteral param = new StringLiteral("param", "\"");
            IdentifierTerminal action = new IdentifierTerminal("action");

            // Non-terminals
            NonTerminal command = new NonTerminal("command");
            NonTerminal ifStatement = new NonTerminal("ifStatement");
            NonTerminal comparisonStatement = new NonTerminal("comparisonStatement");
            NonTerminal actionStatement = new NonTerminal("actionStatement");
            NonTerminal argumentsStatement = new NonTerminal("argumentsStatement");
            NonTerminal parametersStatement = new NonTerminal("paremeters");
            NonTerminal parameterStatement = new NonTerminal("parameter");

            // BNF
            command.Rule = ifStatement + NewLine;
            ifStatement.Rule = ToTerm("if") + comparisonStatement + actionStatement;
            comparisonStatement.Rule = "(" + property + "=" + value + ")";
            actionStatement.Rule = action + argumentsStatement;
            argumentsStatement.Rule = "(" + parametersStatement + ")";
            parametersStatement.Rule = MakePlusRule(parametersStatement, ToTerm(","),
                        parameterStatement) | Empty;
            parameterStatement.Rule = param;

            MarkPunctuation("if","(", ")", ",", "=");

            LanguageFlags = LanguageFlags.NewLineBeforeEOF;

            Root = command;
        }
    }
}

This defines the elements of the "language" (see the Irony wikibook for a better explanation).  (You'll note that the "if" is entirely superfluous; I decided to leave it in there just so that it would make more sense to the expression authors as they create the meta-instruction.)

As you're writing your grammar, it'll be useful to test the grammar using the provided grammar explorer tool to check for errors:

Irony grammar explorer tool

Once the grammar is complete, the next step is to make use of it.

I wrote a simple console program that mocks up some data input:

private static void Main(string[] args)
{
    // Mock up the input.
    Dictionary<string, string> inputs = new Dictionary<string, string>
                                        {
                                          {"status", "ready"}
                                        };

    // Mock up the instructions.
    string instructions = "if (\"status\"=\"ready\") Echo(\"Hello, World!\")";

    Program program = new Program();
    program.Run(inputs, instructions);
}

The idea is to simulate a scenario where the metadata on a document stored in SharePoint is mapped to a dictionary which is then passed to a processor.  The processor will iterate through the instructions embedded in the document and perform actions.

In this example, I've mapped the statement directly to a method on the Program class for simplicity.  The action is a method called "Echo" which will be fed on parameter: "Hello, World".  The Run() method contains most of the logic:

private void Run(Dictionary<string, string> inputs, string instructions)
{
    // Run the parser
    AssemblyDirectiveGrammar grammar = new AssemblyDirectiveGrammar();

    LanguageData language = new LanguageData(grammar);

    Parser parser = new Parser(language);

    ParseTree tree = parser.Parse(instructions);

    List<ParseTreeNode> nodes = new List<ParseTreeNode>();

    // Flatten the nodes for easier processing with LINQ
    Flatten(tree.Root, nodes);

    var property = nodes.Where(n => n.Term.Name == "property").FirstOrDefault().Token.Value.ToString();
    var value = nodes.Where(n => n.Term.Name == "value").FirstOrDefault().Token.Value.ToString();
    var action = nodes.Where(n => n.Term.Name == "action").FirstOrDefault().Token.Value.ToString();
    string[] parameters = (from n in nodes
                           where n.Term.Name == "param"
                           select Convert.ToString(n.Token.Value)).ToArray();

    // Execute logic
    string inputValue = inputs[property];

    if(inputValue != value)
    {
        return;
    }

    MethodInfo method = GetType().GetMethod(action);

    if(method == null)
    {
        return;
    }

    method.Invoke(this, parameters);
}

You can see that in this case, the evaluation is very simple; it's a basic string equality comparison.  The action execution is basic as well.  It simply executes a method of the same name on the current object instance.  Try running the code and changing the "ready" value in the dictionary and see what happens.

A helper method is included to flatten the resultant abstract syntax tree for querying with LINQ (could possibly be done with a recursive LINQ query?):

public void Flatten(ParseTreeNode node, List<ParseTreeNode> nodes)
{
    nodes.Add(node);

    foreach (ParseTreeNode child in node.ChildNodes)
    {
        Flatten(child, nodes);
    }
}

And finally, the actual method that gets invoked (the action):

public void Echo(string message)
{
    System.Console.Out.WriteLine("Echoed: {0}", message);
}

This sample is fairly basic, but it was pretty easy to get up and running (far easier than antlr) and there's lots of potential for other use cases.  One thing I've found lacking so far is documentation.  It's fairly sparse so there's going to be a lot of trial and error, but the good news is that the source code includes a lot of examples (some of them fairly complex including C#, SQL, and Scheme grammars).

Filed under: .Net, DevTools No Comments
5Apr/10Off

jsTree and Nested XML Data Stores

Posted by Charles Chen

I happened upon jsTree a few months back while searching for a solid jQuery based tree.

Without a doubt, it is one of the most well implemented and functional Javascript trees I've used with perhaps the most powerful feature being the built-in support for client-side XML representations of the tree and the ability to add arbitrary metadata to the tree using the Sarissa library.

While the tree itself is extremely powerful, some of the documentation is actually out of date and made my implementation of the tree a bit more tasking than it should have been.

For example, the option for initializing the tree with a static XML string (as demonstrated here) actually requires using staticData instead of static in the latest version.  Adding metadata to the tree is also made far more complicated by the documentation and examples I found online.

In reality, the datastore implementation for the nested XML support is powerful enough to extract arbitrary DOM attributes (online posts seem to indicate that you need to use the custom metadata plugin and/or the jQuery metadata plugin).  You can see in the sample below, that I set the attribute "md" to an encoded JSON string (you'll want to do this for when you reload the tree as a Javascript string) and then retrieve the value as a part of the XML by specifying the attributes to collect:

/*--- test adding data ---*/
$("#test").click(function() {
    var t = $.tree.focused();

    if (!t.selected) {
        return;
    }

    /*--- sets a person on the node ---*/
    var person = {
        "FirstName": $("#firstName").val(),
        "LastName": $("#lastName").val(),
        "Age": $("#age").val() 
    };

    var serialized = JSON.stringify(person);

    /*--- sample of adding an arbitrary attribute at the DOM level ---*/
    t.selected.attr("md", encodeURI(serialized));

    /*--- ...and how to retrieve it in XML ---*/
    var opts = {};
    opts.outer_attrib = ["id", "rel", "class", "md"];

    var xml = t.get(null, "xml_nested", opts)

    $("#xml-d").text(xml);
    $("#treeXml").val(encodeURI(xml));              
});

In real usage, you'd assign some more meaningful values to the metadata and save it.  I find that with a library like this, it's probably easier to just save the whole XML string and that's simple enough by just pushing the XML to a hidden input before submitting the form (as I've done in the last line).

Mahr Mohyuddin has a much more complex and more generic implementation of ASP.NET integration here, but I think that might be more complexity than is needed.  In practice, it makes more sense to use full JSON objects on the client side (as I've used above) and embed them into the attribute and then, using the JavaScriptSerializer class, extract the objects into domain objects on the server side.  Here's an example:

string xmlString = Uri.UnescapeDataString(treeXml.Value);

XDocument xml = XDocument.Parse(xmlString);

// Get all the <items/>.
var items = from x in xml.Descendants()
            where x.Name == "item"
            select x;

List<Person> people = new List<Person>();

JavaScriptSerializer serializer = new JavaScriptSerializer();

// Resolve the paths and Person instances.
foreach(var item in items) {
    string[] parts = item.AncestorsAndSelf()
        .Select(a => a.Descendants("name").First().Value)
        .Reverse().Skip(1).ToArray();

    string path = string.Join("/", parts);

    if(item.Attribute("md") == null) {
        continue; // Next iteration.
    }

    string serializedPerson = 
        Uri.UnescapeDataString(item.Attribute("md").Value);

    Person p = serializer.Deserialize<Person>(serializedPerson);
    p.OrgPath = path;

    people.Add(p);
}

_people.DataSource = people;
_people.DataBind();

Nothing fancy here; the only thing of note is the little LINQ query to resolve the node path (may or may not be useful).

The Person class is also very simple and barebones:

using System;

namespace JsTreeSample {
    /// <summary>
    /// Models a person.
    /// </summary>
    /// <remarks>
    /// Serializable to support deserialization from JSON.
    /// </remarks>
    [Serializable]
    public class Person {
        private string _orgPath;
        private int _age;
        private string _firstName;
        private string _lastName;

        public string FirstName {
            get { return _firstName; }
            set { _firstName = value; }
        }

        public string LastName {
            get { return _lastName; }
            set { _lastName = value; }
        }

        public int Age {
            get { return _age; }
            set { _age = value; }
        }

        public string OrgPath {
            get { return _orgPath; }
            set { _orgPath = value; }
        }
    }
}

The full project is included.  Some usage notes: select a node first and then enter a first name, last name, and age.  Then click "Set Data" to create a Person object at the node (this will also show the full XML of the tree).  Then click Submit to send the data (displays in a repeater).

JsTreeSample.7z (144.07 KB)

Definitely check out jsTree for your next project; it's amazingingly versatile and rich in functionality.

Filed under: Dev, DevTools 1 Comment
2Apr/10Off

SharpZipLib and ASP.NET

Posted by Charles Chen

I recently had to write a search-driven component to extract and export documents from a SharePoint repository. It presented a challenge since many examples on the web start from the premise of a file system and not binary streams.

I settled upon SharpZipLib, an excellent and fairly easy to use library, but found the documentation quite lacking, particularly around creating a zip file to a stream (like an ASP.NET output response stream).

I put together a little sample just to test it out and finally got it working after struggling with it for a good 30 minutes. This sample creates a zip package using an embedded resource (text file) for simplicity. In practice, you can just modify the implementation of the GetBinaryContent helper method. Note that when adding multiple files, you do a "put" first and then a "write".

using System;
using System.IO;
using System.Reflection;
using System.Web;
using System.Web.UI;
using ICSharpCode.SharpZipLib.Zip;
 

namespace SharpZipLibTest {
    public partial class _Default : Page {
        protected void Page_Load(object sender, EventArgs e) {
            string fileName = "package.zip";

            // Clear the response.
            Response.Clear();
            Response.Buffer = true;
            Response.ContentType = "application/octet-stream";
            Response.Charset = "";
            Response.AddHeader("Content-Disposition",
                string.Format("attachment; filename={0}", fileName));

            using (var ms = new MemoryStream()) 
            using (var zip = new ZipOutputStream(ms)) {
                byte[] fileBuffer = GetBinaryContent();

                // Write to the zip package.
                var entry = new ZipEntry("helloworld.txt");
                entry.DateTime = DateTime.Now;
                entry.Size = fileBuffer.Length;
                zip.PutNextEntry(entry);
                zip.Write(fileBuffer, 0, fileBuffer.Length);

                // Repeat for each file
                /*
                for(...) {
                    var entry = new ZipEntry("...");

                    zip.PutNextEntry(entry);
                    zip.Write(...);
                }
                */

                zip.Flush();
                zip.Finish();

                byte[] output = ms.ToArray();

                // Write the final output to the response stream.
                Response.OutputStream.Write(output, 0, output.Length);                
            }

            // End the response.
            Response.Flush();
            Response.End();

            HttpContext.Current.ApplicationInstance.CompleteRequest();
        }

        /// <summary>
        /// Gets the binary content to send.
        /// </summary>
        /// <returns>The byte array containing the binary content.</returns>
        private byte[] GetBinaryContent() {
            string resourceName = "SharpZipLibTest.helloworld.txt";

            byte[] fileBuffer;

            using (Stream s = Assembly.GetExecutingAssembly()
                .GetManifestResourceStream(resourceName)) {
                // Read the file stream into a buffer.
                fileBuffer = new byte[s.Length];

                s.Read(fileBuffer, 0, fileBuffer.Length);
            }

            return fileBuffer;
        }
    }
}

The full project file is attached and runnable.

SharpZipLibTest.7z (104.3 KB)

Filed under: .Net, DevTools No Comments
26Feb/10Off

SharePoint 2010 Mobile Rendering (And Device Emulator Fun!)

Posted by Charles Chen

I was recently tasked with taking an exploratory look at the mobile rendering capabilities of SharePoint 2010 to get a feel for it.  The short of it is that it's sad to say that in 2010 (the year, that is), we are still doing not much better than 3-4 years ago.  Of course, this raises the question of whether it's an issue with the browser or an issue with the software (SharePoint 2010).  Based on my early experience, I have to say it's an issue with 2010 - it seems like the SharePoint team simply took the easy way out.

If you've ever used the Gmail web page from a mobile browser (like Opera), you'll see that there is a "mobile" version and a "basic HTML" version available (links on the bottom).  For all intents and purposes, the "basic HTML" version looks and works pretty much like the full web version (minus the fancy drag/drop stuff, chat, etc.) and loads fine on my nearly two year old HTC Touch Pro.  This is what we should expect from a company as big as Microsoft with vast resources at their disposal.  This is what we should expect from them given that it's 2010 and the mobile space is an increasingly bigger piece of the pie. 

What's worse is that there's no apparent way from the browser client to access the "full view".  What happens when more capable mobile browsers are deployed that can match the capabilities of the current desktop browsers?  Seems like you'll have to make configuration changes on the server to enable proper browser detection but it would be much easier to simply have the option, like on mobile Amazon.com, to load the full view in lieu of Microsoft actually offering a more usable "basic HTML" view.

Alas, instead, we get neutered functionality and weak rendering to mobile browsers for a product that's supposed to carry us to 2013 or 2014.

The following screenshots are taken using my desktop IE8, Microsoft's Windows Mobile 6.1.4 emulator, and RIM's Blackberry 9630 emulator:

Landing Page

List View

Calendar View

New Event

In analysis, I think what makes matters worse is that they've dramatically increased the capabilities of the desktop browser version, but neglected to carry forward some of the basic functionality that seems like it wouldn't be too had to implement like the ratings or tagging functionality.  This discrepancy makes the shortcomings of the mobile offering all the more apparent.

Certainly, I'm not expecting that the desktop experience to be fully emulated on a mobile device, knowing the limitations of the small screens and limited resources and capabilities of mobile browsers.  That said, I would expect a more worthy effort than this.  I would expect something similar to Gmail's "basic HTML" option and a link to the full version.  It's an embarassment, especially the new event screen; at the least, the developers could have aligned the fields (it's pretty offensive).

It's just sloppy and lazy all around and I suggest Microsoft spend some time with the mobile browsers from their competitors.  People are accustomed to a certain level of mobile browser capabilities these days and it's nowhere near as low as Microsoft would like to believe.

For comparison's sake, here's Gmail's rendering in "mobile" and "basic HTML" mode:

Gmail desktop browser, "mobile", and "basic HTML" rendering

This is much more usable and a much more congruous experience across platforms.  Come on Microsoft, stop being lazy!  As Mark Jackson would say "You're better than that!"

On a side note, working with the emulators was a bit challenging.  If you'd like to try it out for yourself, here are some resources:

Mobile IE 6

Blackberry Devices

My experience is that working with the Blackberry emulator is extremely frustrating because you'll want to click on the screen with your mouse but then you remember that the devices don't support touch so it would only be logical that the emulator doesn't either.  However, this logic leads to a hair-pulling experience when working with the emulator.

4Dec/09Off

More WebSequenceDiagrams.com Awesomeness

Posted by Charles Chen

As I've been working with a client which has demanded rigorous sequence diagrams as deliverables for the design phase of the project, I've started to use WebSequenceDiagrams.com more and more.

I've blogged about it previously, but I've only come to truly appreciate it after having to use Visio for a few days before I convinced the client that I could deliver the content faster and in an easier to maintain format (well, text) using WSD.

What Visio Gets Wrong


I ran out of connection points. Yes, it's possible; for a long activation sequence, you can actually run out of connection points. One could argue that this calls for a refactoring of the diagram in the first place, but then I would say that you've never tried to actually refactor a Visio diagram...I still find it hard to believe: the activation box ran out of connection points; I wouldn't have believed it if I didn't see it myself:

I kept having to resize the page. By default, you can't really fit much on the page. But as I started to build my sequence up, I found that I had to keep toggling around with the paper size just so that I would have a grid. This was annoying since it also involved then zooming out so that I could select everything and reposition it to the top left corner of the page then zooming back in. You'd think that Visio would be smart enough to do this, but it isn't...

I kept having to move elements around. Want to add a new step? What about introducing a new actor in between two existing actors? Prepare for some carpal tunnel my friend. There's simply no easy way to do it aside from zooming out, grabbing everything to the right and shifting it around while counting gridlines and getting your result some 10-20 clicks later. What's worse is that you end up having to scroll around horizontally (reordering actors) and vertically (reordering steps) while dragging a bunch of stuff around.

I kept having to fix connection lines. This was absolutely mindboggling: if I extended an activation, it would cause the first connection on the activation to jump, which would then require me to manually drag the connection back to where it belonged. I probably spent a good 10% of my time simply fixing these connection points as a adjusted activations:

This is an incomprehensible design flaw; I have no idea how people work around this in Visio since I adjust activations multiple times as I'm working through a diagram.

There was no representation of alternate paths or optional steps. I ended up having to draw a rectangle and manually managing the size of it as I changed steps and added more steps. What made this even more annoying was that having the rectangle, even though I sent it to the back, then made it difficult to select elements that were enclosed by the rectangle like a message line or a connection point or a note; I'd end up selecting this stupid rectangle instead.

I had to keep managing the location of notes. The notes don't seem to anchor to anything and it's not clear to me how that's supposed to work. That meant that I had to keep moving notes around as I changed activations and modified connection points.

The lifelines didn't synchronize. For the life of me, I couldn't figure out how to get the lifelines to synchronize in length so I didn't have to manually go back and drag each one down to the same length. You can't actually CTRL+click two lifelines, as one would think you'd be able to do, and drag them both to extend them simultaneously. I mean, this seamed like pretty basic stuff to me.

So yeah, all in all, I'm not sure why anyone would want to subject themselves to the pain of creating sequence diagrams in Visio. Maybe if it's a final product and you don't plan on touching it ever again and you've already done most of the work on paper or something, but it's a terrible tool if you're just trying to think an idea out and see it visually.

What WebSequenceDiagrams.com Gets Right


I can't speak to the console program or the DLL (yet), but I decided that the only way that I could do this right was to do it in WSD first and then just use Visio to render the final output. While doing it in the browser is fine, I found it much easier to do it in EditPlus, my text editor of choice.

The first step was to create a syntax and auto-complete file so that it was a little more user-friendly.

Here's my .stx syntax file:

#TITLE=WSD
 

#DELIMITER=,(){}[]+*%/="'~!&|<?:;.
#QUOTATION1="
#LINECOMMENT=note
#CASE=y

#KEYWORD=Activate
activate

#KEYWORD=Deactivate
deactivate
destroy

#KEYWORD=Alias
as
participant

#KEYWORD=Notes
note
over
right
left

#KEYWORD=Control
alt
else
end
opt

#KEYWORD=Newline
\n

#KEYWORD=Transition
->
-->
#

And my .acp auto-complete file:

#TITLE=WSD
 

#CASE=y

#T=act
activate ^!
#T=de
deactivate ^!
#T=pa
participant ^!
#T=alt
alt ^!

else

end
#T=opt
opt ^!

end
#

What I get from this is:

I know what you're thinking: Chuck, that looks like code! By golly, it does! And -- at least to me -- that's the beauty; all of a sudden, a frustrating, time consuming, mouse-centric activity becomes a keyboard-centric, coding-like activity. Moving objects around becomes a matter of moving lines of text. Reordering actors involves moving your participant declarations around. Notes stay in their context if you add a step since everything gets pushed down. There's no manual resizing of anything. There's no fixing connection points. There's no stupid. It actually makes working with sequence diagrams, beyond just whiteboarding, much more useful and much more productive as it lets you kind of think out the code by actually writing pseudocode.

I highly recommend downloading EditPlus (you can keep using it for free, perpetually, if you're a cheap bastard or pony up the $20 for such an awesome editor). For me, EditPlus is a perfect pairing for WSD due to the easy to create language syntax/autocomplete files and the handy split-document feature so you can easily reference your participants at the top.

Simply create a new file type and add the .stx and .acp files I defined:

Now the downside of this whole affair is that you have an extra step of having to copy out the text and pasting it into the browser, but even with that extra little bit of annoyance, the time and frustration saved over working with Visio is more than worth it.

The next step, once I get my hands on the command line tool, is to hook it up to the external tools feature of EditPlus for a quick hit. I'm also considering writing an integration for Visual Studio for custom rendering within VS or at least something quick-and-dirty like an add-in.

One additional note is that WSD has an HTML/Javascript API whereby you can render a diagram inline with your HTML by simply using a set of <pre></pre> tags and a reference to a Javascript file.

What's cool about this is that now you can use the standard CTRL+B/CTRL+E shortcut keys to preview without a copy/paste step! For free! That's pretty awesome.

Of course, the downside is that using this method, there is a limit to the size of the sequence that you can send up as well as the fact that you need some additional hijinks to make the syntax highlighting work (I gave up on that part :-D) . But if were doing it in the browser to begin with or using notepad and you don't care for the syntax highlighting, then this is a huge upgrade.

Conclusion: stop using Visio :-D Now I'm just looking forward to WebERDiagrams.com, WebStateMachineDiagram.com, and....well, you get the idea.

My EditPlus .stx and .acp files for anyone that wants 'em: wsd-files.zip (.52 KB)

Filed under: Awesome, DevTools No Comments
22Jul/09Off

Mercurial vs. SVN

Posted by Charles Chen

As I've been starting on a new project recently, I've been delving into Mercurial (hg) as an alternative to Subversion (svn).

I've been using svn for about 3 years now, and - for the most part - it has been way better than Visual Source Safe (whatever last version I used...it sucked), it clearly has some pain points that make daily workflows difficult.  The most important of these are the speed of commits, the fact that a commit is global, reverting to a version, and branching/merging (painful...so painful).

hg addresses each of these pain points:

  • Speed of commits and local commits: Mercurial has two separate concepts of commit and push.  A commit in hg creates a local commit as opposed to a global commit in svn.  This is useful because you can work->work->commit->work->commit->work->revert->commit without affecting anyone else's work stream.  In other words, you don't have to wait to commit until you have flawless, working, compiling code.  You can commit as much as you want and keep your work intact; this is a big win as it encourages experimentation.  When you're ready to share your code, a push operation (the equivalent of a svn commit) pushes it to a shared location for others to pull.  Of course, to some degree, you can accomplish this with svn as well using a branch, but...
  • Branching/merging: OMG, so much easier and more intuitive than svn.  I can't believe it.  In comparison, svn is a giant charlie foxtrot.  Branching and merging in svn pre-1.5 was an exercise in futility.  It was extremely difficult to remember the right procedure (always requiring a lookup to the docs) and very much error prone.  So difficult, in fact, that it discouraged branching for fear of wasting a good half day trying to merge it back in later.  Maybe it's better with 1.5?
  • Reverting to a version: ever try it in svn?  'Nuff said.  It's counterintuitive and always confuses the heck out of junior devs or devs new to svn.

I've only been using it for a few days now (primarily TortoiseHg), so perhaps hg has just as many warts as svn, but I'm going to stick with it and find out.

Some good resources on hg/git vs. svn (or DCVS vs CCVS):

I'll post new entries as I learn more about hg in daily use :)

P.S. WebFaction is a pretty awesome webhost.  For the low, low price of some $10 a month, I can host my own hg repository, svn repository, Trac, and (note: not or) my web app.  Hot damn, that's awesome!  Although svn was significantly easier to set up with WebFaction than hg, I've read that they are close to officially supporting hg and it should be just as easy in the future.

Filed under: Dev, DevTools No Comments