utils.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. # -*- coding: utf-8 -*-
  2. """
  3. flaskbb.cli.utils
  4. ~~~~~~~~~~~~~~~~~
  5. This module contains some utility helpers that are used across
  6. commands.
  7. :copyright: (c) 2016 by the FlaskBB Team.
  8. :license: BSD, see LICENSE for more details.
  9. """
  10. import sys
  11. import os
  12. import re
  13. import click
  14. from flask import __version__ as flask_version
  15. from flask_themes2 import get_theme
  16. from flaskbb import __version__
  17. from flaskbb.extensions import plugin_manager
  18. from flaskbb.utils.populate import create_user, update_user
  19. cookiecutter_available = False
  20. try:
  21. from cookiecutter.main import cookiecutter
  22. cookiecutter_available = True
  23. except ImportError:
  24. pass
  25. _email_regex = r"[^@]+@[^@]+\.[^@]+"
  26. class FlaskBBCLIError(click.ClickException):
  27. """An exception that signals a usage error including color support.
  28. This aborts any further handling.
  29. :param styles: The style kwargs which should be forwarded to click.secho.
  30. """
  31. def __init__(self, message, **styles):
  32. click.ClickException.__init__(self, message)
  33. self.styles = styles
  34. def show(self, file=None):
  35. if file is None:
  36. file = click._compat.get_text_stderr()
  37. click.secho("[-] Error: %s" % self.format_message(), file=file,
  38. **self.styles)
  39. class EmailType(click.ParamType):
  40. """The choice type allows a value to be checked against a fixed set of
  41. supported values. All of these values have to be strings.
  42. See :ref:`choice-opts` for an example.
  43. """
  44. name = "email"
  45. def convert(self, value, param, ctx):
  46. # Exact match
  47. if re.match(_email_regex, value):
  48. return value
  49. else:
  50. self.fail(("invalid email: %s" % value), param, ctx)
  51. def __repr__(self):
  52. return "email"
  53. def save_user_prompt(username, email, password, group, only_update=False):
  54. if not username:
  55. username = click.prompt(
  56. click.style("Username", fg="magenta"), type=str,
  57. default=os.environ.get("USER", "")
  58. )
  59. if not email:
  60. email = click.prompt(
  61. click.style("Email address", fg="magenta"), type=EmailType()
  62. )
  63. if not password:
  64. password = click.prompt(
  65. click.style("Password", fg="magenta"), hide_input=True,
  66. confirmation_prompt=True
  67. )
  68. if not group:
  69. group = click.prompt(
  70. click.style("Group", fg="magenta"),
  71. type=click.Choice(["admin", "super_mod", "mod", "member"]),
  72. default="admin"
  73. )
  74. if only_update:
  75. return update_user(username, password, email, group)
  76. return create_user(username, password, email, group)
  77. def validate_plugin(plugin):
  78. """Checks if a plugin is installed.
  79. TODO: Figure out how to use this in a callback. Doesn't work because
  80. the appcontext can't be found and using with_appcontext doesn't
  81. help either.
  82. """
  83. if plugin not in plugin_manager.all_plugins.keys():
  84. raise FlaskBBCLIError("Plugin {} not found.".format(plugin), fg="red")
  85. return True
  86. def validate_theme(theme):
  87. """Checks if a theme is installed."""
  88. try:
  89. get_theme(theme)
  90. except KeyError:
  91. raise FlaskBBCLIError("Theme {} not found.".format(theme), fg="red")
  92. def check_cookiecutter(ctx, param, value):
  93. if not cookiecutter_available:
  94. raise FlaskBBCLIError(
  95. "Can't create {} because cookiecutter is not installed. "
  96. "You can install it with 'pip install cookiecutter'.".
  97. format(value), fg="red"
  98. )
  99. return value
  100. def get_version(ctx, param, value):
  101. if not value or ctx.resilient_parsing:
  102. return
  103. message = ("FlaskBB %(version)s using Flask %(flask_version)s on "
  104. "Python %(python_version)s")
  105. click.echo(message % {
  106. 'version': __version__,
  107. 'flask_version': flask_version,
  108. 'python_version': sys.version.split("\n")[0]
  109. }, color=ctx.color)
  110. ctx.exit()