Repository: oppia/oppia

Issue:
Images in explorations should have descriptive alt attributes
The accessibility audit in issue #2862 identified that there were some essential images in explorations with a empty alt attributes.

To test, use the WebAIM WAVE accessibility tool on the Equivalent Fractions exploration.
<img width="983" alt="screen shot 2017-01-21 at 5 43 02 pm" src="https://cloud.githubusercontent.com/assets/10858542/22178606/1a57a5a6-e001-11e6-8406-363d3efa4897.png">

Because images in explorations will tend to convey essential information, the alt attributes should be descriptive. We need a feature of the editor that makes alt attributes compulsory for images added to explorations. A possible avenue is to prevent closure of image component modal until alt attribute is added.

<img width="1439" alt="screen shot 2017-01-21 at 5 45 04 pm" src="https://cloud.githubusercontent.com/assets/10858542/22178619/694e9930-e001-11e6-93bd-991d1b4fda4d.png">



Discussion:

Resolving patch:
diff --git a/extensions/rich_text_components/Image/Image.py b/extensions/rich_text_components/Image/Image.py
--- a/extensions/rich_text_components/Image/Image.py
+++ b/extensions/rich_text_components/Image/Image.py
@@ -47,9 +47,19 @@ class Image(base.BaseRichTextComponent):
         'default_value': '',
     }, {
         'name': 'alt',
-        'description': 'Alternative text (for screen readers)',
+        'description': (
+            'Briefly explain this image to a visually impaired '
+            'learner'),
         'schema': {
             'type': 'unicode',
+            'validators': [{
+                'id': 'is_nonempty',
+            }],
+            'ui_config': {
+                'placeholder': (
+                    'Description of Image (Example : George Handel, '
+                    '18th century baroque composer)'),
+            },
         },
         'default_value': '',
     }]
diff --git a/schema_utils.py b/schema_utils.py
--- a/schema_utils.py
+++ b/schema_utils.py
@@ -52,7 +52,7 @@
 SCHEMA_TYPE_UNICODE = 'unicode'
 
 
-def normalize_against_schema(obj, schema):
+def normalize_against_schema(obj, schema, apply_custom_validators=True):
     """Validate the given object using the schema, normalizing if necessary.
 
     Returns:
@@ -136,13 +136,15 @@ def normalize_against_schema(obj, schema):
                 normalized_obj, **kwargs)
 
     # Validate the normalized object.
-    if SCHEMA_KEY_VALIDATORS in schema:
-        for validator in schema[SCHEMA_KEY_VALIDATORS]:
-            kwargs = dict(validator)
-            del kwargs['id']
-            assert _Validators.get(validator['id'])(normalized_obj, **kwargs), (
-                'Validation failed: %s (%s) for object %s' % (
-                    validator['id'], kwargs, normalized_obj))
+    if apply_custom_validators:
+        if SCHEMA_KEY_VALIDATORS in schema:
+            for validator in schema[SCHEMA_KEY_VALIDATORS]:
+                kwargs = dict(validator)
+                del kwargs['id']
+                assert _Validators.get(
+                    validator['id'])(normalized_obj, **kwargs), (
+                        'Validation failed: %s (%s) for object %s' % (
+                            validator['id'], kwargs, normalized_obj))
 
     return normalized_obj
 


Test patch:
diff --git a/core/domain/rte_component_registry_test.py b/core/domain/rte_component_registry_test.py
--- a/core/domain/rte_component_registry_test.py
+++ b/core/domain/rte_component_registry_test.py
@@ -59,11 +59,16 @@ def _validate_customization_arg_specs(self, customization_arg_specs):
             self.assertTrue(isinstance(ca_spec['description'], basestring))
             self.assertGreater(len(ca_spec['description']), 0)
 
+            # The default value might not pass validation checks (e.g. the
+            # Image component has a required field whose default value is
+            # empty). Thus, when checking the default value schema, we don't
+            # apply the custom validators.
             schema_utils_test.validate_schema(ca_spec['schema'])
             self.assertEqual(
                 ca_spec['default_value'],
                 schema_utils.normalize_against_schema(
-                    ca_spec['default_value'], ca_spec['schema']))
+                    ca_spec['default_value'], ca_spec['schema'],
+                    apply_custom_validators=False))
 
             if ca_spec['schema']['type'] == 'custom':
                 obj_class = obj_services.Registry.get_object_class_by_type(
diff --git a/schema_utils_test.py b/schema_utils_test.py
--- a/schema_utils_test.py
+++ b/schema_utils_test.py
@@ -92,7 +92,10 @@
         'coding_mode': {
             'type': SCHEMA_TYPE_UNICODE,
             'choices': ['none', 'python', 'coffeescript'],
-        }
+        },
+        'placeholder': {
+            'type': SCHEMA_TYPE_UNICODE,
+        },
     },
 }