changeset 273:d70a4adb61dd

themes: Add the `chef themes` command The `themes` command lets the user create or override a theme easily.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 01 Mar 2015 09:07:36 -0800
parents a561fbad0b7f
children 1163bd034dc5
files piecrust/__init__.py piecrust/commands/builtin/themes.py piecrust/plugins/builtin.py
diffstat 3 files changed, 133 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/piecrust/__init__.py	Sun Mar 01 09:07:02 2015 -0800
+++ b/piecrust/__init__.py	Sun Mar 01 09:07:36 2015 -0800
@@ -7,6 +7,7 @@
 
 CONFIG_PATH = 'config.yml'
 THEME_CONFIG_PATH = 'theme_config.yml'
+THEME_INFO_PATH = 'theme_info.yml'
 
 DEFAULT_FORMAT = 'markdown'
 DEFAULT_TEMPLATE_ENGINE = 'jinja2'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/piecrust/commands/builtin/themes.py	Sun Mar 01 09:07:36 2015 -0800
@@ -0,0 +1,130 @@
+import os
+import os.path
+import shutil
+import logging
+import yaml
+from piecrust import (
+        RESOURCES_DIR, THEME_DIR, THEME_CONFIG_PATH, THEME_INFO_PATH)
+from piecrust.commands.base import ChefCommand
+
+
+logger = logging.getLogger(__name__)
+
+
+class ThemesCommand(ChefCommand):
+    def __init__(self):
+        super(ThemesCommand, self).__init__()
+        self.name = 'themes'
+        self.description = "Manage the themes for the current website."
+
+    def setupParser(self, parser, app):
+        if app.root_dir is None:
+            return
+
+        subparsers = parser.add_subparsers()
+        p = subparsers.add_parser(
+                'create',
+                help="Create a new theme for the current website.")
+        p.add_argument(
+                '--from-default',
+                action='store_true',
+                help=("Create a new theme by copying the default PieCrust "
+                      "theme into the theme directory"))
+        p.add_argument(
+                'theme_name',
+                help=("The name of the theme"))
+        p.set_defaults(sub_func=self._createTheme)
+
+        p = subparsers.add_parser(
+                'override',
+                help="Copies a theme to the website for customization.")
+        p.set_defaults(sub_func=self._overrideTheme)
+
+    def checkedRun(self, ctx):
+        ctx.args.sub_func(ctx)
+
+    def _createTheme(self, ctx):
+        theme_dir = os.path.join(ctx.app.root_dir, THEME_DIR)
+        if os.path.exists(theme_dir):
+            logger.warning("A theme already exists, and will be overwritten. "
+                           "Are you sure? [Y/n]")
+            ans = input()
+            if len(ans) > 0 and ans.lower() not in ['y', 'yes']:
+                return 1
+
+            shutil.rmtree(theme_dir)
+
+        try:
+            if ctx.args.from_default:
+                def reporting_copy2(src, dst):
+                    rel_dst = os.path.relpath(dst, ctx.app.root_dir)
+                    logger.info(rel_dst)
+                    shutil.copy2(src, dst)
+
+                default_theme_dir = os.path.join(RESOURCES_DIR, 'theme')
+                shutil.copytree(default_theme_dir, theme_dir,
+                                copy_function=reporting_copy2)
+                return 0
+
+            logger.info("Creating theme directory.")
+            os.makedirs(theme_dir)
+
+            logger.info("Creating theme_config.yml")
+            config_path = os.path.join(theme_dir, THEME_CONFIG_PATH)
+            with open(config_path, 'w', encoding='utf8') as fp:
+                fp.write('')
+
+            logger.info("Creating theme_info.yml")
+            info_path = os.path.join(theme_dir, THEME_INFO_PATH)
+            with open(info_path, 'w', encoding='utf8') as fp:
+                yaml.dump(
+                        {
+                            'name': ctx.args.theme_name or 'My New Theme',
+                            'description': "A new PieCrust theme.",
+                            'authors': ['Your Name Here <email or twitter>'],
+                            'url': 'http://www.example.org'},
+                        fp,
+                        default_flow_style=False)
+            return 0
+        except:
+            logger.error("Error occured, deleting theme directory.")
+            shutil.rmtree(theme_dir)
+            raise
+
+    def _overrideTheme(self, ctx):
+        app_dir = ctx.app.root_dir
+        theme_dir = ctx.app.theme_dir
+        if not theme_dir:
+            logger.error("There is not theme currently applied to override.")
+            return 1
+
+        copies = []
+        for dirpath, dirnames, filenames in os.walk(theme_dir):
+            rel_dirpath = os.path.relpath(dirpath, theme_dir)
+            for name in filenames:
+                if (dirpath == theme_dir and
+                        name in [THEME_CONFIG_PATH, THEME_INFO_PATH]):
+                    continue
+                src_path = os.path.join(dirpath, name)
+                dst_path = os.path.join(app_dir, rel_dirpath, name)
+                copies.append((src_path, dst_path))
+
+        conflicts = []
+        for c in copies:
+            if os.path.exists(c[1]):
+                conflicts.append(c[1])
+        if conflicts:
+            logger.warning("Some website files will be overwritten:")
+            for c in conflicts:
+                logger.warning(os.path.relpath(c, app_dir))
+            logger.warning("Are you sure? [Y/n]")
+            ans = input()
+            if len(ans) > 0 and ans.lower() not in ['y', 'yes']:
+                return 1
+
+        for c in copies:
+            logger.info(os.path.relpath(c[1], app_dir))
+            if not os.path.exists(os.path.dirname(c[1])):
+                os.makedirs(os.path.dirname(c[1]))
+            shutil.copy2(c[0], c[1])
+
--- a/piecrust/plugins/builtin.py	Sun Mar 01 09:07:02 2015 -0800
+++ b/piecrust/plugins/builtin.py	Sun Mar 01 09:07:36 2015 -0800
@@ -10,6 +10,7 @@
         UserDefinedPrepareTemplatesCommandExtension,
         DefaultPrepareTemplatesHelpTopic)
 from piecrust.commands.builtin.serving import (ServeCommand)
+from piecrust.commands.builtin.themes import (ThemesCommand)
 from piecrust.commands.builtin.util import (
         InitCommand, PurgeCommand, ImportCommand)
 from piecrust.data.provider import (IteratorDataProvider, BlogDataProvider)
@@ -56,6 +57,7 @@
                 ShowSourcesCommand(),
                 ShowRoutesCommand(),
                 ShowPathsCommand(),
+                ThemesCommand(),
                 BakeCommand(),
                 ShowRecordCommand(),
                 ServeCommand()]