models.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. # -*- coding: utf-8 -*-
  2. """
  3. flaskbb.plugins.models
  4. ~~~~~~~~~~~~~~~~~~~~~~~
  5. This module provides registration and a basic DB backed key-value
  6. store for plugins.
  7. :copyright: (c) 2017 by the FlaskBB Team.
  8. :license: BSD, see LICENSE for more details.
  9. """
  10. from flask import current_app
  11. from sqlalchemy import UniqueConstraint
  12. from sqlalchemy.orm.collections import attribute_mapped_collection
  13. from flaskbb.extensions import db
  14. from flaskbb.utils.database import CRUDMixin
  15. from flaskbb.utils.forms import generate_settings_form, SettingValueType
  16. class PluginStore(CRUDMixin, db.Model):
  17. id = db.Column(db.Integer, primary_key=True)
  18. key = db.Column(db.Unicode(255), nullable=False)
  19. value = db.Column(db.PickleType, nullable=False)
  20. # Available types: string, integer, float, boolean, select, selectmultiple
  21. value_type = db.Column(db.Enum(SettingValueType), nullable=False)
  22. # Extra attributes like, validation things (min, max length...)
  23. # For Select*Fields required: choices
  24. extra = db.Column(db.PickleType, nullable=True)
  25. plugin_id = db.Column(db.Integer,
  26. db.ForeignKey("plugin_registry.id",
  27. ondelete="CASCADE"))
  28. # Display stuff
  29. name = db.Column(db.Unicode(255), nullable=False)
  30. description = db.Column(db.Text, nullable=True)
  31. __table_args__ = (
  32. UniqueConstraint('key', 'plugin_id', name='plugin_kv_uniq'),
  33. )
  34. def __repr__(self):
  35. return '<PluginSetting plugin={} key={} value={}>'.format(
  36. self.plugin.name, self.key, self.value
  37. )
  38. @classmethod
  39. def get_or_create(cls, plugin_id, key):
  40. """Returns the PluginStore object or an empty one.
  41. The created object still needs to be added to the database session
  42. """
  43. obj = cls.query.filter_by(plugin_id=plugin_id, key=key).first()
  44. if obj is not None:
  45. return obj
  46. return PluginStore()
  47. class PluginRegistry(CRUDMixin, db.Model):
  48. id = db.Column(db.Integer, primary_key=True)
  49. name = db.Column(db.Unicode(255), unique=True, nullable=False)
  50. enabled = db.Column(db.Boolean, default=True)
  51. values = db.relationship(
  52. 'PluginStore',
  53. collection_class=attribute_mapped_collection('key'),
  54. backref='plugin',
  55. cascade="all, delete-orphan",
  56. )
  57. @property
  58. def settings(self):
  59. """Returns a dict with contains all the settings in a plugin."""
  60. return {kv.key: kv.value for kv in self.values.values()}
  61. @property
  62. def info(self):
  63. """Returns some information about the plugin."""
  64. return current_app.pluggy.list_plugin_metadata().get(self.name, {})
  65. @property
  66. def is_installable(self):
  67. """Returns True if the plugin has settings that can be installed."""
  68. plugin_module = current_app.pluggy.get_plugin(self.name)
  69. return True if plugin_module.SETTINGS else False
  70. @property
  71. def is_installed(self):
  72. """Returns True if the plugin is installed."""
  73. if self.settings:
  74. return True
  75. return False
  76. def get_settings_form(self):
  77. """Generates a settings form based on the settings."""
  78. return generate_settings_form(self.values.values())()
  79. def update_settings(self, settings):
  80. """Updates the given settings of the plugin.
  81. :param settings: A dictionary containing setting items.
  82. """
  83. pluginstore = PluginStore.query.filter(
  84. PluginStore.plugin_id == self.id,
  85. PluginStore.key.in_(settings.keys())
  86. ).all()
  87. setting_list = []
  88. for pluginsetting in pluginstore:
  89. pluginsetting.value = settings[pluginsetting.key]
  90. setting_list.append(pluginsetting)
  91. db.session.add_all(setting_list)
  92. db.session.commit()
  93. def add_settings(self, settings, force=False):
  94. """Adds the given settings to the plugin.
  95. :param settings: A dictionary containing setting items.
  96. :param force: Forcefully overwrite existing settings.
  97. """
  98. plugin_settings = []
  99. for key in settings:
  100. if force:
  101. with db.session.no_autoflush:
  102. pluginstore = PluginStore.get_or_create(self.id, key)
  103. else:
  104. # otherwise we assume that no such setting exist
  105. pluginstore = PluginStore()
  106. pluginstore.key = key
  107. pluginstore.plugin = self
  108. pluginstore.value = settings[key]['value']
  109. pluginstore.value_type = settings[key]['value_type']
  110. pluginstore.extra = settings[key]['extra']
  111. pluginstore.name = settings[key]['name']
  112. pluginstore.description = settings[key]['description']
  113. plugin_settings.append(pluginstore)
  114. db.session.add_all(plugin_settings)
  115. db.session.commit()
  116. def __repr__(self):
  117. return '<Plugin name={} enabled={}>'.format(self.name, self.enabled)