Repository: wagtail/wagtail

Issue:
Not specifying a filter for an Image tag
### Issue Summary

Adding an `{% image %}` tag without specifying a resize rule will cause a very confusing error:
`wagtail.wagtailimages.exceptions.InvalidFilterSpecError: Unrecognised operation: `

which is caused by:
```
        if op_spec_parts[0] not in self._registered_operations:
            raise InvalidFilterSpecError("Unrecognised operation: %s" % op_spec_parts[0])
```

**(wagtail\wagtailimages\models.py", line 360, in operations)**


### Steps to Reproduce

Inserting an `{% image %}` tag to a template without a resize rule like so:
`{% image page.cover_image  class="cover-image" %}`
or:
`{% image page.cover_image %}`

The exception thrown here must be much more explanatory, and also this must be documented in here:
http://docs.wagtail.io/en/v1.13/topics/images.html?highlight=wagtailimages_tags

### Technical details

Python version: 3.6
Django version: 1.11.7
Wagtail version: 1.13
Browser version: Irrelevant.


Discussion:
@lirlocker what are your thoughts on these suggestions?


## 1. Documentation Update

File: [/docs/topics/images.rst](https://github.com/wagtail/wagtail/blob/master/docs/topics/images.rst)

### Current
The syntax for the tag is thus:

`{% image [image] [resize-rule] %}`

For example:
...

### Suggested Update

The syntax for the tag is thus:

`{% image [image] [resize-rule] %}`

**Both the image and resize rule must be passed to the template tag.**

For example:
...

## 2. Clearer Error Message

### Current

```
            if op_spec_parts[0] not in self._registered_operations:
                raise InvalidFilterSpecError("Unrecognised operation: %s" % op_spec_parts[0])
```

### Update

Note: we use the terminology 'resize rule' and 'image tag', plus throw a more specific error if no resize rule is provided.

```
            if not op_spec_parts[0]:
                raise InvalidFilterSpecError("No resize rule in image tag")
            if op_spec_parts[0] not in self._registered_operations:
                raise InvalidFilterSpecError("Unrecognised resize rule in image tag: %s" % op_spec_parts[0])
```


@lb- 
Looks great.

Resolving patch:
diff --git a/wagtail/wagtailimages/templatetags/wagtailimages_tags.py b/wagtail/wagtailimages/templatetags/wagtailimages_tags.py
--- a/wagtail/wagtailimages/templatetags/wagtailimages_tags.py
+++ b/wagtail/wagtailimages/templatetags/wagtailimages_tags.py
@@ -56,6 +56,18 @@ def image(parser, token):
         # attributes are not valid when using the 'as img' form of the tag
         is_valid = False
 
+    if len(filter_specs) == 0:
+        # there must always be at least one filter spec provided
+        is_valid = False
+
+    if len(bits) == 0:
+        # no resize rule provided eg. {% image page.image %}
+        raise template.TemplateSyntaxError(
+            "no resize rule provided. "
+            "'image' tag should be of the form {% image self.photo max-320x200 [ custom-attr=\"value\" ... ] %} "
+            "or {% image self.photo max-320x200 as img %}"
+        )
+
     if is_valid:
         return ImageNode(image_expr, '|'.join(filter_specs), attrs=attrs, output_var_name=output_var_name)
     else:


Test patch:
diff --git a/wagtail/wagtailimages/tests/tests.py b/wagtail/wagtailimages/tests/tests.py
--- a/wagtail/wagtailimages/tests/tests.py
+++ b/wagtail/wagtailimages/tests/tests.py
@@ -123,6 +123,33 @@ def test_context_may_only_contain_one_argument(self):
             context = template.Context({'image_obj': self.image})
             temp.render(context)
 
+    def test_no_image_filter_provided(self):
+        # if image template gets the image but no filters
+        with self.assertRaises(template.TemplateSyntaxError):
+            temp = template.Template(
+                '{% load wagtailimages_tags %}{% image image_obj %}'
+            )
+            context = template.Context({'image_obj': self.image})
+            temp.render(context)
+
+    def test_no_image_filter_provided_when_using_as(self):
+        # if image template gets the image but no filters
+        with self.assertRaises(template.TemplateSyntaxError):
+            temp = template.Template(
+                '{% load wagtailimages_tags %}{% image image_obj as foo %}'
+            )
+            context = template.Context({'image_obj': self.image})
+            temp.render(context)
+
+    def test_no_image_filter_provided_but_attributes_provided(self):
+        # if image template gets the image but no filters
+        with self.assertRaises(template.TemplateSyntaxError):
+            temp = template.Template(
+                '{% load wagtailimages_tags %}{% image image_obj class="cover-image"%}'
+            )
+            context = template.Context({'image_obj': self.image})
+            temp.render(context)
+
 
 class TestMissingImage(TestCase):
     """