models.py 4.9 KB

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