diff --git a/VERSION.txt b/VERSION.txt
index 7be101b..e69fb78 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1 +1 @@
-6.8.8
\ No newline at end of file
+6.8.13
\ No newline at end of file
diff --git a/scripttease/cli/__init__.py b/scripttease/cli/__init__.py
index c131441..8a596cb 100644
--- a/scripttease/cli/__init__.py
+++ b/scripttease/cli/__init__.py
@@ -2,7 +2,7 @@
from argparse import ArgumentParser, RawDescriptionHelpFormatter
from commonkit.logging import LoggingHelper
-from ..constants import LOGGER_NAME
+from ..variables import LOGGER_NAME
from ..version import DATE as VERSION_DATE, VERSION
from . import initialize
from . import subcommands
diff --git a/scripttease/lib/commands/base.py b/scripttease/lib/commands/base.py
index 0c3fdef..a9816d4 100644
--- a/scripttease/lib/commands/base.py
+++ b/scripttease/lib/commands/base.py
@@ -1,17 +1,20 @@
# Imports
-from commonkit import parse_jinja_template, read_file
+from commonkit import indent, parse_jinja_template, read_file, split_csv
from jinja2 import TemplateNotFound, TemplateError
import logging
import os
+from ...variables import LOGGER_NAME
-log = logging.getLogger(__name__)
+log = logging.getLogger(LOGGER_NAME)
# Exports
__all__ = (
"Command",
+ "Content",
"ItemizedCommand",
+ "Prompt",
"Sudo",
"Template",
)
@@ -118,6 +121,135 @@ class Command(object):
return self.statement
+class Content(object):
+
+ def __init__(self, content_type, caption=None, css=None, heading=None, height=None, image=None, message=None,
+ width=None, **kwargs):
+ self.caption = caption
+ self.css = css
+ self.heading = heading
+ self.height = height
+ self.image = image
+ self.message = message
+ self.width = width
+ self.name = "content"
+ self.type = content_type
+
+ self.kwargs = kwargs
+
+ def __getattr__(self, item):
+ return self.kwargs.get(item)
+
+ def __repr__(self):
+ return "<%s %s>" % (self.__class__.__name__, self.content_type)
+
+ @property
+ def is_itemized(self):
+ """Always returns ``False``. Content cannot be itemized."""
+ return False
+
+ def get_output(self, output_format):
+ if self.content_type == "explain":
+ return self._get_message_output(output_format)
+ elif self.content_type == "screenshot":
+ return self._get_image_output(output_format)
+ else:
+ log.warning("Invalid content type: %s" % self.content_type)
+ return None
+
+ # noinspection PyUnusedLocal
+ def get_statement(self, cd=True, include_comment=True, include_register=True, include_stop=True):
+ """Override to return only bash output format."""
+ return self.get_output("bash")
+
+ def _get_image_output(self, output_format):
+ """Get the output of the content when the content type is an image."""
+ a = list()
+
+ if output_format == "bash":
+ if self.caption:
+ a.append("# %s" % self.caption)
+
+ a.append("# %s" % self.image)
+ elif output_format == "html":
+ b = list()
+ b.append('")
+ elif output_format == "md":
+ a.append("" % (self.caption or self.comment, self.image))
+ a.append("")
+ elif output_format == "rst":
+ a.append(".. figure:: %s" % self.image)
+
+ if self.caption is not None:
+ a.append(indent(":alt: %s" % self.caption, 8))
+
+ if self.height is not None:
+ a.append(indent(":height: %s" % self.height, 8))
+
+ if self.height is not None:
+ a.append(indent(":height: %s" % self.height, 8))
+ else:
+ if self.caption:
+ a.append("%s: %s" % (self.caption, self.image))
+ else:
+ a.append(self.image)
+
+ return "\n".join(a)
+
+ def _get_message_output(self, output_format):
+ """Get the output of the content when the content type is a message."""
+ a = list()
+
+ if output_format == "bash":
+ if self.heading:
+ a.append("# %s: %s" % (self.heading, self.message))
+ else:
+ a.append("# %s" % self.message)
+
+ a.append("")
+ elif output_format == "html":
+ if self.heading:
+ a.append("
%s
" % self.message) + elif output_format == "md": + if self.heading: + a.append("## %s" % self.heading) + a.append("") + + a.append(self.message) + a.append("") + elif output_format == "rst": + if self.heading: + a.append(self.heading) + a.append("=" * len(self.heading)) + a.append("") + + a.append(self.message) + a.append("") + else: + if self.heading: + a.append("***** %s *****" % self.heading) + a.append("") + + a.append(self.message) + a.append("") + + return "\n".join(a) + + class ItemizedCommand(object): """An itemized command represents multiple commands of with the same statement but different parameters.""" @@ -194,6 +326,133 @@ class ItemizedCommand(object): return True +class Prompt(Command): + """Prompt the user for input.""" + + def __init__(self, name, back_title="Input", choices=None, default=None, dialog=False, help_text=None, label=None, + **kwargs): + """Initialize a prompt for user input. + + :param name: The variable name. + :type name: str + + :param back_title: The back title of the input. Used only when ``dialog`` is enabled. + :type back_title: str + + :param choices: Valid choices for the variable. May be given as a list of strings or a comma separated string. + :type choices: list[str] | str + + :param default: The default value of the variable. + + :param dialog: Indicates the dialog command should be used. + :type dialog: bool + + :param help_text: Additional text to display. Only use when ``fancy`` is ``True``. + :type help_text: str + + :param label: The label of the prompt. + + """ + self.back_title = back_title + self.default = default + self.dialog_enabled = dialog + self.help_text = help_text + self.label = label or name.replace("_", " ").title() + self.variable_name = name + + if type(choices) in (list, tuple): + self.choices = choices + elif type(choices) is str: + self.choices = split_csv(choices, smart=False) + else: + self.choices = None + + kwargs.setdefault("comment", "prompt user for %s input" % name) + + super().__init__(name, **kwargs) + + def get_statement(self, cd=True, include_comment=True, include_register=True, include_stop=True): + """Get the statement using dialog or read.""" + if self.dialog_enabled: + return self._get_dialog_statement() + + return self._get_read_statement() + + def _get_dialog_statement(self): + """Get the dialog statement.""" + a = list() + + a.append('dialog --clear --backtitle "%s" --title "%s"' % (self.back_title, self.label)) + + if self.choices is not None: + a.append('--menu "%s" 15 40 %s' % (self.help_text or "Select", len(self.choices))) + count = 1 + for choice in self.choices: + a.append('"%s" %s' % (choice, count)) + count += 1 + + a.append('2>/tmp/input.txt') + else: + if self.help_text is not None: + a.append('--inputbox "%s"' % self.help_text) + else: + a.append('--inputbox ""') + + a.append('8 60 2>/tmp/input.txt') + + b = list() + + b.append('touch /tmp/input.txt') + b.append(" ".join(a)) + + b.append('%s=$(