Repository: oppia/oppia

Issue:
Some skills cannot be created
**Describe the bug**
Some skills (example in screenshots) cannot be created and fire an error.

**To Reproduce**
Steps to reproduce the behavior:
 1. Go to 'Topics and Skills Dashboard'
 2. Click on 'CREATE SKILL'
 3. Create skill similar to the skill in the screenshot
 4. Click on 'Save'

**Observed behavior**
The skill is not added and error is shown in the console.

**Expected behavior**
The skill is added.

**Screenshots**
![image](https://user-images.githubusercontent.com/10142938/80873657-6be1fe80-8cba-11ea-9632-7f89cd466d31.png)
![image](https://user-images.githubusercontent.com/10142938/80873668-78feed80-8cba-11ea-9526-eb6c136e329c.png)


**Desktop (please complete the following information; delete this section if the issue does not arise on desktop):**
 - OS: Ubuntu 18.04.3
 - Browser: Firefox
 - Version: 75



Discussion:
@vojtechjelinek I was unable to reproduce this error.
@vojtechjelinek, Can you please reply to @nishantwrp comment?
 @nishantwrp Can you please add more detail like where you did the testing, what you found etc.?
Umm, I filled the fields in the form like shown in the image i.e. typed Skill repeatedly in every field. Found that the skill got created.
Btw, since this is a server error we should be able to find it in the Google Console.
This issue can be reproduced if you add any reserved character mention in [RFC1738](https://www.ietf.org/rfc/rfc1738.txt) (eg. ";", "/", "@") in any form field and hit submit. This seems to be a bug that affects all POST calls routed through `request-interceptor.service`. This happens because the content type header for the POST call is "application/x-www-form-urlencoded" which expects the request body to be form data but instead we use HttpParams which does not seem to encode reserved characters correctly (here's a related [bug](https://github.com/angular/angular/issues/18261) in Angular's HttpParams).

Discussed with @aks681 regarding this as he was facing a similar issue. @aks681 do you think you can add the fix in the PR you are working on or should this be done separately because it is a blocking bug?

Resolving patch:
diff --git a/core/domain/fs_services.py b/core/domain/fs_services.py
--- a/core/domain/fs_services.py
+++ b/core/domain/fs_services.py
@@ -69,6 +69,8 @@ def save_original_and_compressed_versions_of_image(
         compressed_image_content = original_image_content
         micro_image_content = original_image_content
 
+    mimetype = (
+        'image/svg+xml' if filetype == 'svg' else 'image/%s' % filetype)
     # Because in case of CreateVersionsOfImageJob, the original image is
     # already there. Also, even if the compressed, micro versions for some
     # image exists, then this would prevent from creating another copy of
@@ -76,17 +78,17 @@ def save_original_and_compressed_versions_of_image(
     if not fs.isfile(filepath.encode('utf-8')):
         fs.commit(
             filepath.encode('utf-8'), original_image_content,
-            mimetype='image/%s' % filetype)
+            mimetype=mimetype)
 
     if not fs.isfile(compressed_image_filepath.encode('utf-8')):
         fs.commit(
             compressed_image_filepath.encode('utf-8'),
-            compressed_image_content, mimetype='image/%s' % filetype)
+            compressed_image_content, mimetype=mimetype)
 
     if not fs.isfile(micro_image_filepath.encode('utf-8')):
         fs.commit(
             micro_image_filepath.encode('utf-8'),
-            micro_image_content, mimetype='image/%s' % filetype)
+            micro_image_content, mimetype=mimetype)
 
 
 def save_classifier_data(exp_id, job_id, classifier_data):


Test patch:
diff --git a/core/tests/protractor_desktop/topicAndStoryEditor.js b/core/tests/protractor_desktop/topicAndStoryEditor.js
--- a/core/tests/protractor_desktop/topicAndStoryEditor.js
+++ b/core/tests/protractor_desktop/topicAndStoryEditor.js
@@ -165,20 +165,6 @@ describe('Topic editor functionality', function() {
     storyEditorPage.expectNumberOfChaptersToBe(1);
   });
 
-  it('should publish and unpublish a story correctly', function() {
-    topicEditorPage.expectStoryPublicationStatusToBe('No', 0);
-    topicEditorPage.navigateToStoryWithIndex(0);
-    storyEditorPage.publishStory();
-    storyEditorPage.returnToTopic();
-
-    topicEditorPage.expectStoryPublicationStatusToBe('Yes', 0);
-    topicEditorPage.navigateToStoryWithIndex(0);
-    storyEditorPage.unpublishStory();
-    storyEditorPage.returnToTopic();
-
-    topicEditorPage.expectStoryPublicationStatusToBe('No', 0);
-  });
-
   it('should assign a skill to, between, and from subtopics', function() {
     topicsAndSkillsDashboardPage.get();
     topicsAndSkillsDashboardPage.createSkillWithDescriptionAndExplanation(
diff --git a/core/tests/protractor_desktop/topicAndStoryEditorFileUploadFeatures.js b/core/tests/protractor_desktop/topicAndStoryEditorFileUploadFeatures.js
--- a/core/tests/protractor_desktop/topicAndStoryEditorFileUploadFeatures.js
+++ b/core/tests/protractor_desktop/topicAndStoryEditorFileUploadFeatures.js
@@ -128,6 +128,43 @@ describe('Topic editor functionality', function() {
     topicEditorPage.expectSubtopicPageContentsToMatch('Subtopic Contents');
   });
 
+  it('should publish and unpublish a story correctly', function() {
+    var TOPIC_NAME = 'TASEFUF_3';
+    var defaultThumbnailImageSrc = null;
+    topicsAndSkillsDashboardPage.createTopic(TOPIC_NAME, false);
+
+    topicEditorPage.expectNumberOfStoriesToBe(0);
+    topicEditorPage.createStory('Story Title');
+    storyEditorPage.returnToTopic();
+
+    topicEditorPage.expectNumberOfStoriesToBe(1);
+    topicEditorPage.expectStoryPublicationStatusToBe('No', 0);
+    topicEditorPage.navigateToStoryWithIndex(0);
+    storyEditorPage.getStoryThumbnailSource().then(function(name) {
+      defaultThumbnailImageSrc = name;
+    });
+    expect(storyEditorPage.getStoryThumbnailSource())
+      .not
+      .toEqual(
+        storyEditorPage.submitStoryThumbnail('../data/test_svg.svg')
+          .then(function() {
+            return storyEditorPage.getStoryThumbnailSource();
+          })
+      );
+    storyEditorPage.saveStory('Added thumbnail.');
+    storyEditorPage.publishStory();
+    storyEditorPage.returnToTopic();
+
+    topicEditorPage.expectStoryPublicationStatusToBe('Yes', 0);
+    topicEditorPage.navigateToStoryWithIndex(0);
+    expect(storyEditorPage.getStoryThumbnailSource()).not.toEqual(
+      defaultThumbnailImageSrc);
+    storyEditorPage.unpublishStory();
+    storyEditorPage.returnToTopic();
+
+    topicEditorPage.expectStoryPublicationStatusToBe('No', 0);
+  });
+
   afterEach(function() {
     general.checkForConsoleErrors([]);
     users.logout();
@@ -142,7 +179,7 @@ describe('Chapter editor functionality', function() {
   var dummyExplorationIds = [];
   var dummyExplorationInfo = [
     'Dummy exploration', 'Algorithm', 'Learn more about oppia', 'English'];
-  var TOPIC_NAME = 'TASEFUF_3';
+  var TOPIC_NAME = 'TASEFUF_4';
   var USER_EMAIL = 'creator@chapterTest.com';
 
   var createDummyExplorations = function(numExplorations) {