Validating multiple files in Symfony 2.5

Symfony 2.5 implemented handy multiple file upload support, e.g. input fields that look like this:

<input id="form_resumes" multiple="multiple" name="form[documents][]" required="required" type="file" />

Unfortunately there seem to be some problems in the Validator component. For example, it’s difficult to require at least one file. Likewise the All constraint doesn’t play nicely with validation groups. Here are a few workarounds.

To require at least one file but not more than three, use the Count constraint in combination with All and NotNull:

This works around a bug where 'min'=>1 is ignored.

If you’re using validation groups, you may notice that the All constraint is not being applied. Here’s how to fix it:

Note line 11: 'groups' => 'Test',

Technically we shouldn’t have to specify validation groups for the All constraint; in fact there seems to be no way to do this in YAML. Hacking it in as an option to All on the PHP side however seems to be working for me. YMMV. And as far as I can tell this last one is fixed in 2.6.

End User Help Documentation: Tricks of the Trade

Some internal rumination that I thought would make a nice blog post.  Stuff we’ve learned after a dozen years of dealing with startup software documentation.

Because most software is a moving target, the vast majority of end user documentation is out-of-date soon after it is created.  Out-of-date documentation is worse than no documentation because it adds to, rather than detracts from, confusion.  This problem becomes exponentially worse when maintaining documentation across multiple languages.  It can become a nightmare when resources are limited.

To help prevent the documentation from turning into a problem, consider the following heuristics:

  • Only create a piece of documentation when there is a real need.  Never create documentation for features that are intuitive.  When in doubt, leave it out.
  • A picture is worth a thousand words.  Use screenshots and a minimal amount of text.
  • Avoid full-screen screenshots.  “Narrow” screenshots are easier to understand and much easier to maintain as the software evolves.  That said, leave enough surrounding space so that the user can recognize context.  (A screenshot of, for example, a single widget is like trying to ask someone to make an identification from a picture of an eyeball..)
  • When possible, describe a feature in general terms rather than specific steps.  This approach will have more “staying power”, meaning that the documentation is less likely to change even if there are small changes to the software.
  • Create a style guide to help keep the documentation consistent.  Especially important when multiple people are working on the documentation.
  • If resources are limited, only translate “proven” documentation or documentation that is demand.  Services like Freshdesk have “Was this answer helpful?” features.  I recommend only translating documentation that gets at least one up vote, or issues for which we get at least two support requests.

And of course:

  • Whenever possible refactor for usability rather than write even a line of documentation!

If you’re working on documentation and have come across this post, I feel for ya..  Keeping the scope as small as possible will bear you in good stead.

 

Postmaster Abuse

Here’s an interesting feature of Google Apps that got me into trouble recently:

https://support.google.com/a/answer/33389

The “abuse” and “postmaster” accounts are monitored by Google and always active. Even if explicitly configured in your Google Apps interface, emails to these accounts will be accepted into a Google black hole rather than bounce.

To make sure you get copies of emails going to these, be sure to create “abuse” and “postmaster” groups, otherwise you *will* miss an important email or two. Note that these need to be created as actual group addresses.. the abuse and postmaster accounts cannot be added to other addresses as an alias.

Git branch from a specific commit

Yet more git syntax I keep forgetting. To branch and create a new line of development from a specific commit, do the following:

git branch newbranchname a9c146a09505837ec03b

Add the -b switch to immediately checkout after branching.

Since the commit will likley be behind, use --force when pushing to tell git to ignore future commits from the original branch and start a new fork.

Know your CSRF timeouts

Web development frameworks such as Symfony these days bake-in a number of security features, CSRF protection being a fairly common one. I recently made the mistake of not scrutinizing Symfony’s CSRF protection defaults, which led to occasional confusing CSRF errors after a web app had been deployed.

CSRF protection embeds a code into both the generated form and some client-side storage mechanism (cookies, though I suppose this could work with an HTML5 client-side database or whatnot). On submit, if the value embedded into the form and the cookie value match, then we know the form is submitted by the user who originally requested it.

In Symfony, the CSRF cookie lifetime can be configured, defaulting to null, which falls back to session.cookie_lifetime value from php.ini. I had originally set this to be one hour, probably based on configuration examples from a year or so back..

Anyway, app users lately have been leaving forms open for more than an hour. This is a good thing: It means they are treating our web app more like a client side app, which indeed it is meant to replace. Took some sleuthing, however, to figure out why I was seeing the occasional CSRF error when everything else seemed hunky dory. I’ve now set this to

which will retain the cookie for the length of the browser session.
Lesson learned. Know your timeouts. And for that matter, plan some time to review old assumptions.

Use “the big E” for quick and clean monitoring

Despite all the great tools out there these days for monitoring, it’s difficult to get away without writing a custom monitoring script or two. Spools, various types of cache, multi-server indexing. So many things that can go wrong.

Once upon a time I used to write quick and dirty complex scripts that would email in one case, otherwise do some-such-thing, etc. They were brittle with questionable reliability. Talk about a false sense of security.

These days I like to use an untouted feature of mail to keep things simpler. The idea is to only make noise when there is a problem. If all is well, keep silent.

Take this cron job for example:


*/5 * * * * root /somedir/check-spool.sh | mail -E -s 'Spool Monitor' problem@somedomain.com

check-spool.sh will do just that: Check a spool each time it is run. If everything is fine, no output. If it finds a problem then it outputs what it finds. The “big E” (-E) switch tells mail to ignore any body-less input, and will therefore only spam us when there is some panic-worthy output.

Thanks big E.

JMS\Serializer\Exception\RuntimeException: “Resources are not supported in serialized data…” Yadda yadda

Been bit by this a couple of times now. It’s really a problem with a bad route. In my case I had some ajax using the FOSJsRoutingBundle to generate a route along the lines of:

Routing.generate('some_titles', {id: $(this).val()});

where val() was actually returning an empty string. The resulting URL (“some//titles”) was invalid, looks to JMSSerializer like a resource thanks to the double slash, and badda bing badda boom: completely irrelevant error.

I think this may also be the source of “Cannot redeclare class Doctrine\ORM\Mapping\Annotation” errors to boot. Double whammy.

Couldn’t reserve space for cygwin’s heap, Win32 error 0

Cygwin and Windows git stopped playing nicely together after a recent Windows update. There’s a variety of recommendations for how to fix this this on StackOverflow and elsewhere, but this post actually makes the most sense. In a nutshell, msys-1.0.dll (installed into your Program Files\Git\bin directory) is not built to be position independent. Use the dll rebaser to get it to load at a new address, like so:

$ rebase.exe -b 0x50000000 msys-1.0.dll

And voila, git goodness restored.