Repository: oppia/oppia

Issue:
Stories with multiple chapters having the same name are publishable
<!--
  - Thanks for taking the time to report a bug in the Oppia project.
  - Before filing a new issue, please do a quick search to check that it hasn't
  - already been filed on the [issue tracker](https://github.com/oppia/oppia/issues)._
  -->

**Describe the bug**
A story that contains multiple chapters with the exact same name should not bu publishable but is. While creating the chapter, if you repeat the chapter name, a warning is shown but if you edit chapters and rename them with the same names, no warnings are shown and the story is publishable.

**To Reproduce**
Steps to reproduce the behavior:
 1. Go to Topics and Skills Dashboard.
 2. Create a new topic and a new story in that topic.
 3. Create two chapters and assign a valid exploration to each of them. (Notice that a warning will get shown if you repeat names here).
 4. Now edit the second chapter name and make it equal to the first one.
 5. Try to publish the story. 

**Observed behavior**
No warnings are shown and the story is publishable.

**Expected behavior**
A warning should be displayed and the publish button should be disabled.

**Screenshots**
![chapterNames](https://user-images.githubusercontent.com/35144226/78707744-a613e680-792e-11ea-96ed-0413158cfa15.gif)


**Desktop (please complete the following information; delete this section if the issue does not arise on desktop):**
 - OS: Ubuntu 19.10
 - Browser Chrome
 - Version 80

Downloading Unpublished Exploration Yields Hidden YAML File
**Describe the bug**
Downloading unpublished explorations yields a hidden YAML file.

**To Reproduce**
Steps to reproduce the behavior:
 1. Create an exploration and save it, but do not publish
 2. In the history tab, choose to download the latest version
 3. Unzip the downloaded `oppia--v2.zip` file
 4. Notice that no unzipped file is visible
 5. In a terminal, view hidden files with `ls -a`

**Observed behavior**
From the terminal output, we can see that the file name is `.yaml`, which is hidden because of the leading `.`. This makes it hard for users to find the YAML files.

**Expected behavior**
The file should not be hidden.

**Desktop (please complete the following information; delete this section if the issue does not arise on desktop):**
 - OS: macOS
 - Browser: Firefox
 - Version: 74
Downloading Unpublished Exploration Yields Hidden YAML File
**Describe the bug**
Downloading unpublished explorations yields a hidden YAML file.

**To Reproduce**
Steps to reproduce the behavior:
 1. Create an exploration and save it, but do not publish
 2. In the history tab, choose to download the latest version
 3. Unzip the downloaded `oppia--v2.zip` file
 4. Notice that no unzipped file is visible
 5. In a terminal, view hidden files with `ls -a`

**Observed behavior**
From the terminal output, we can see that the file name is `.yaml`, which is hidden because of the leading `.`. This makes it hard for users to find the YAML files.

**Expected behavior**
The file should not be hidden.

**Desktop (please complete the following information; delete this section if the issue does not arise on desktop):**
 - OS: macOS
 - Browser: Firefox
 - Version: 74


Discussion:
@aks681 Can you confirm that you or someone from your team will fix this issue for this release (by Apr 13)? If yes, please update the status of this issue at least daily.
Yeah, I'll take this up. Will create a PR by today.
This also happens on Chrome 80
@aks681 Can you confirm that you or someone from your team will fix this issue for this release (by Apr 13)? If yes, please update the status of this issue at least daily.
This also happens on Chrome 80
@aks681 Can you confirm that you or someone from your team will fix this issue for this release (by Apr 13)? If yes, please update the status of this issue at least daily.

Resolving patch:
diff --git a/core/controllers/editor.py b/core/controllers/editor.py
--- a/core/controllers/editor.py
+++ b/core/controllers/editor.py
@@ -388,9 +388,12 @@ def get(self, exploration_id):
             version = exploration.version
 
         # If the title of the exploration has changed, we use the new title.
-        filename = utils.to_ascii(
-            'oppia-%s-v%s.zip'
-            % (exploration.title.replace(' ', ''), version)).decode('utf-8')
+        if not exploration.title:
+            init_filename = 'oppia-unpublished_exploration-v%s.zip' % version
+        else:
+            init_filename = 'oppia-%s-v%s.zip' % (
+                exploration.title.replace(' ', ''), version)
+        filename = utils.to_ascii(init_filename).decode('utf-8')
 
         if output_format == feconf.OUTPUT_FORMAT_ZIP:
             self.render_downloadable_file(
diff --git a/core/domain/exp_services.py b/core/domain/exp_services.py
--- a/core/domain/exp_services.py
+++ b/core/domain/exp_services.py
@@ -273,8 +273,10 @@ def export_to_zip_file(exploration_id, version=None):
     memfile = python_utils.string_io()
     with zipfile.ZipFile(
         memfile, mode='w', compression=zipfile.ZIP_DEFLATED) as zfile:
-
-        zfile.writestr('%s.yaml' % exploration.title, yaml_repr)
+        if not exploration.title:
+            zfile.writestr('Unpublished_exploration.yaml', yaml_repr)
+        else:
+            zfile.writestr('%s.yaml' % exploration.title, yaml_repr)
 
         fs = fs_domain.AbstractFileSystem(
             fs_domain.GcsFileSystem(


Test patch:
diff --git a/core/controllers/editor_test.py b/core/controllers/editor_test.py
--- a/core/controllers/editor_test.py
+++ b/core/controllers/editor_test.py
@@ -616,6 +616,34 @@ def test_exploration_download_handler_with_unicode_title(self):
 
         self.logout()
 
+    def test_exploration_download_handler_with_no_title(self):
+        # This is the case for most unpublished explorations.
+        self.login(self.EDITOR_EMAIL)
+        owner_id = self.get_user_id_from_email(self.EDITOR_EMAIL)
+
+        # Create a simple exploration.
+        exp_id = 'eid'
+        self.save_new_valid_exploration(
+            exp_id, owner_id,
+            title='',
+            category='This is just a test category',
+            objective='')
+
+        # Download to zip file using download handler.
+        download_url = '/createhandler/download/%s' % exp_id
+        response = self.get_custom_response(download_url, 'text/plain')
+
+        # Check downloaded zip file.
+        filename = 'oppia-unpublished_exploration-v1.zip'
+        self.assertEqual(response.headers['Content-Disposition'],
+                         'attachment; filename=%s' % filename)
+
+        zf_saved = zipfile.ZipFile(
+            python_utils.string_io(buffer_value=response.body))
+        self.assertEqual(zf_saved.namelist(), ['Unpublished_exploration.yaml'])
+
+        self.logout()
+
     def test_state_yaml_handler(self):
         self.login(self.EDITOR_EMAIL)
         owner_id = self.get_user_id_from_email(self.EDITOR_EMAIL)
diff --git a/core/domain/exp_services_test.py b/core/domain/exp_services_test.py
--- a/core/domain/exp_services_test.py
+++ b/core/domain/exp_services_test.py
@@ -1825,6 +1825,17 @@ def test_export_to_zip_file(self):
         self.assertEqual(
             zf.open('A title.yaml').read(), self.SAMPLE_YAML_CONTENT)
 
+    def test_export_to_zip_file_with_unpublished_exploration(self):
+        """Test the export_to_zip_file() method."""
+        self.save_new_default_exploration(
+            self.EXP_0_ID, self.owner_id, title='')
+
+        zip_file_output = exp_services.export_to_zip_file(self.EXP_0_ID)
+        zf = zipfile.ZipFile(python_utils.string_io(
+            buffer_value=zip_file_output))
+
+        self.assertEqual(zf.namelist(), ['Unpublished_exploration.yaml'])
+
     def test_export_to_zip_file_with_assets(self):
         """Test exporting an exploration with assets to a zip file."""
         exploration = self.save_new_valid_exploration(