Repository: translate/pootle

Issue:
Disabled project cannot use update_stores
If a project is 'disabled' then you cannot run `update_stores` against it.

The use case is setting up a project that you only want to make available to translators once all files are correctly synced.  Currently you have to enable to project before you can run `update_stores` which means that users can potentially work on it when you might not yet want them to.



Discussion:
@ta2-1, I was under impression that we have addressed this already? See https://github.com/translate/pootle/issues/3530#issuecomment-132747404 that explicitly states that the disabled projects should properly be updated.

@iafan your comment is about stats updating (as I understood it). But I wonder how can we change project and update stats if `update_stores` doesn't work. I'll check it.

Let me quote the relevant bit:

> [disabled project] should be hidden from anybody expect admins, but otherwise it should be a regular project

Related question: is the stats ticker displayed while processing disabled projects? Regular users don't wanna be bothered by this while the project remains disabled, it's interesting for site-admins though.

@julen, I think you already answered your question. Users shouldn't see anything related to projects they don't have an access to (be it disabled projects or projects with custom permissions). Dirty stats status if on of such properties.

There is one confusing thing here.
`update_stores` behaviour: if there is no project directory we disable the entire project (Note: we don't make anything (TPs) obsolete).

How can we update a TP within a disabled project?
- if the directory for the disabled project exists then we update the TP as usual and even make it obsolete if there is no directory for this TP.
- if the directory for the disabled project doesn't exist then we can do nothing or make the TP obsolete.

I'm about to add extra option to `update_stores` command if we need to touch internal structure for disabled projects. Any thoughts?

@ta2-1 can you explain what the option would be?  This doesn't feel like it needs an option (unless I misunderstood something). In my mind `update_stores` simply works as expected on a disabled TP.


Resolving patch:
diff --git a/pootle/apps/pootle_app/management/commands/update_stores.py b/pootle/apps/pootle_app/management/commands/update_stores.py
--- a/pootle/apps/pootle_app/management/commands/update_stores.py
+++ b/pootle/apps/pootle_app/management/commands/update_stores.py
@@ -37,17 +37,24 @@ class Command(PootleCommand):
                  "appear unchanged)."),
     )
     help = "Update database stores from files."
+    process_disabled_projects = True
 
     def handle_translation_project(self, translation_project, **options):
         """
         :return: flag if child stores should be updated
         """
-        if translation_project.directory_exists():
+        if translation_project.directory_exists_on_disk():
             logging.info(u"Scanning for new files in %s", translation_project)
             translation_project.scan_files()
             return True
 
-        translation_project.directory.makeobsolete()
+        # Skip if project directory was ceased to exist on disk.
+        if translation_project.project.directory_exists_on_disk():
+            translation_project.directory.makeobsolete()
+        else:
+            logging.warning(u"Missing project directory for %s. Skipping %s.",
+                            translation_project.project, translation_project)
+
         return False
 
     def handle_store(self, store, **options):
diff --git a/pootle/apps/pootle_project/models.py b/pootle/apps/pootle_project/models.py
--- a/pootle/apps/pootle_project/models.py
+++ b/pootle/apps/pootle_project/models.py
@@ -361,9 +361,8 @@ def __unicode__(self):
 
     def save(self, *args, **kwargs):
         # Create file system directory if needed
-        project_path = self.get_real_path()
-        if not os.path.exists(project_path) and not self.disabled:
-            os.makedirs(project_path)
+        if not self.directory_exists_on_disk() and not self.disabled:
+            os.makedirs(self.get_real_path())
 
         from pootle_app.models.directory import Directory
         self.directory = Directory.objects.projects \
@@ -396,6 +395,10 @@ def clean(self):
                 _('"%s" cannot be used as a project code', self.code)
             )
 
+    def directory_exists_on_disk(self):
+        """Checks if the actual directory for the project exists on disk."""
+        return os.path.exists(self.get_real_path())
+
     # # # TreeItem
 
     def get_children(self):
diff --git a/pootle/apps/pootle_translationproject/models.py b/pootle/apps/pootle_translationproject/models.py
--- a/pootle/apps/pootle_translationproject/models.py
+++ b/pootle/apps/pootle_translationproject/models.py
@@ -66,7 +66,7 @@ def create_translation_project(language, project):
 
 
 def scan_translation_projects(languages=None, projects=None):
-    project_query = Project.objects.enabled()
+    project_query = Project.objects.all()
 
     if projects is not None:
         project_query = project_query.filter(code__in=projects)
@@ -383,9 +383,9 @@ def clear_all_cache(self, children=True, parents=True):
 
     # # # /TreeItem
 
-    def directory_exists(self):
+    def directory_exists_on_disk(self):
         """Checks if the actual directory for the translation project
-        exists on-disk.
+        exists on disk.
         """
         return not does_not_exist(self.abs_real_path)
 


Test patch: