# HG changeset patch # User Ludovic Chabant # Date 1425229656 28800 # Node ID d70a4adb61ddae92c7e14b28a74e088fc530b901 # Parent a561fbad0b7ffe7aaf5a524d34499790b823db17 themes: Add the `chef themes` command The `themes` command lets the user create or override a theme easily. diff -r a561fbad0b7f -r d70a4adb61dd piecrust/__init__.py --- 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' diff -r a561fbad0b7f -r d70a4adb61dd piecrust/commands/builtin/themes.py --- /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 '], + '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]) + diff -r a561fbad0b7f -r d70a4adb61dd piecrust/plugins/builtin.py --- 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()]