Class: Plugin::Instance

Inherits:
Object
  • Object
show all
Defined in:
lib/plugin/instance.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(metadata = nil, path = nil) ⇒ Instance

Returns a new instance of Instance.



89
90
91
92
93
# File 'lib/plugin/instance.rb', line 89

def initialize( = nil, path = nil)
  @metadata = 
  @path = path
  @idx = 0
end

Instance Attribute Details

#admin_routeObject (readonly)

Returns the value of attribute admin_route.



47
48
49
# File 'lib/plugin/instance.rb', line 47

def admin_route
  @admin_route
end

#metadataObject

Returns the value of attribute metadata.



46
47
48
# File 'lib/plugin/instance.rb', line 46

def 
  @metadata
end

#pathObject

Returns the value of attribute path.



46
47
48
# File 'lib/plugin/instance.rb', line 46

def path
  @path
end

Class Method Details

.find_all(parent_path) ⇒ Object



78
79
80
81
82
83
84
85
86
87
# File 'lib/plugin/instance.rb', line 78

def self.find_all(parent_path)
  [].tap do |plugins|
    # also follows symlinks - http://stackoverflow.com/q/357754
    Dir["#{parent_path}/*/plugin.rb"].sort.each do |path|
      source = File.read(path)
       = Plugin::Metadata.parse(source)
      plugins << self.new(, path)
    end
  end
end

Instance Method Details

#activate!Object

note, we need to be able to parse separately to activation. this allows us to present information about a plugin in the UI prior to activations



689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
# File 'lib/plugin/instance.rb', line 689

def activate!
  if @path
    root_dir_name = File.dirname(@path)

    # Automatically include all ES6 JS and hbs files
    root_path = "#{root_dir_name}/assets/javascripts"
    DiscoursePluginRegistry.register_glob(root_path, "js")
    DiscoursePluginRegistry.register_glob(root_path, "js.es6")
    DiscoursePluginRegistry.register_glob(root_path, "hbs")
    DiscoursePluginRegistry.register_glob(root_path, "hbr")

    admin_path = "#{root_dir_name}/admin/assets/javascripts"
    DiscoursePluginRegistry.register_glob(admin_path, "js", admin: true)
    DiscoursePluginRegistry.register_glob(admin_path, "js.es6", admin: true)
    DiscoursePluginRegistry.register_glob(admin_path, "hbs", admin: true)
    DiscoursePluginRegistry.register_glob(admin_path, "hbr", admin: true)

    DiscourseJsProcessor.plugin_transpile_paths << root_path.sub(Rails.root.to_s, "").sub(
      %r{\A/*},
      "",
    )
    DiscourseJsProcessor.plugin_transpile_paths << admin_path.sub(Rails.root.to_s, "").sub(
      %r{\A/*},
      "",
    )

    test_path = "#{root_dir_name}/test/javascripts"
    DiscourseJsProcessor.plugin_transpile_paths << test_path.sub(Rails.root.to_s, "").sub(
      %r{\A/*},
      "",
    )
  end

  self.instance_eval File.read(path), path
  if auto_assets = generate_automatic_assets!
    assets.concat(auto_assets)
  end

  register_assets! unless assets.blank?
  register_locales!
  register_service_workers!

  seed_data.each { |key, value| DiscoursePluginRegistry.register_seed_data(key, value) }

  # TODO: possibly amend this to a rails engine

  # Automatically include assets
  Rails.configuration.assets.paths << auto_generated_path
  Rails.configuration.assets.paths << File.dirname(path) + "/assets"
  Rails.configuration.assets.paths << File.dirname(path) + "/admin/assets"
  Rails.configuration.assets.paths << File.dirname(path) + "/test/javascripts"

  # Automatically include rake tasks
  Rake.add_rakelib(File.dirname(path) + "/lib/tasks")

  # Automatically include migrations
  migration_paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
  migration_paths << File.dirname(path) + "/db/migrate"

  unless Discourse.skip_post_deployment_migrations?
    migration_paths << "#{File.dirname(path)}/#{Discourse::DB_POST_MIGRATE_PATH}"
  end

  public_data = File.dirname(path) + "/public"
  if Dir.exist?(public_data)
    target = Rails.root.to_s + "/public/plugins/"

    Discourse::Utils.execute_command("mkdir", "-p", target)
    target << name.gsub(/\s/, "_")

    Discourse::Utils.atomic_ln_s(public_data, target)
  end

  ensure_directory(js_file_path)

  contents = []
  handlebars_includes.each { |hb| contents << "require_asset('#{hb}')" }
  javascript_includes.each { |js| contents << "require_asset('#{js}')" }

  if !contents.present?
    [js_file_path, extra_js_file_path].each do |f|
      File.delete(f)
    rescue Errno::ENOENT
    end
    return
  end

  contents.insert(0, "<%")
  contents << "%>"

  Discourse::Utils.atomic_write_file(extra_js_file_path, contents.join("\n"))

  begin
    File.delete(js_file_path)
  rescue Errno::ENOENT
  end
end

#add_admin_route(label, location) ⇒ Object



102
103
104
# File 'lib/plugin/instance.rb', line 102

def add_admin_route(label, location)
  @admin_route = { label: label, location: location }
end

#add_api_key_scope(resource, action) ⇒ Object

Register a new API key scope.

Example: add_api_key_scope(:groups, { delete: { actions: %w, params: %i } })

This scope lets you add members to a group. Additionally, you can specify which group ids are allowed. The delete action is added to the groups resource.



930
931
932
# File 'lib/plugin/instance.rb', line 930

def add_api_key_scope(resource, action)
  DiscoursePluginRegistry.register_api_key_scope_mapping({ resource => action }, self)
end

#add_api_parameter_route(methods: nil, actions: nil, formats: nil) ⇒ Object

Register a route which can be authenticated using an api key or user api key in a query parameter rather than a header. For example:

add_api_parameter_route(

methods: :get,
actions: "users#bookmarks",
formats: :ics

)

See Auth::DefaultCurrentUserProvider::PARAMETER_API_PATTERNS for more examples and Auth::DefaultCurrentUserProvider#api_parameter_allowed? for implementation



974
975
976
977
978
979
# File 'lib/plugin/instance.rb', line 974

def add_api_parameter_route(methods: nil, actions: nil, formats: nil)
  DiscoursePluginRegistry.register_api_parameter_route(
    RouteMatcher.new(methods: methods, actions: actions, formats: formats),
    self,
  )
end

#add_body_class(class_name) ⇒ Object

Applies to all sites in a multisite environment. Ignores plugin.enabled?



294
295
296
# File 'lib/plugin/instance.rb', line 294

def add_body_class(class_name)
  reloadable_patch { |plugin| ::ApplicationHelper.extra_body_classes << class_name }
end

#add_class_method(klass_name, attr, &block) ⇒ Object

Adds a class method to a class, respecting if plugin is enabled



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'lib/plugin/instance.rb', line 322

def add_class_method(klass_name, attr, &block)
  reloadable_patch do |plugin|
    klass =
      begin
        klass_name.to_s.classify.constantize
      rescue StandardError
        klass_name.to_s.constantize
      end

    hidden_method_name = :"#{attr}_without_enable_check"
    klass.public_send(:define_singleton_method, hidden_method_name, &block)

    klass.public_send(:define_singleton_method, attr) do |*args, **kwargs|
      public_send(hidden_method_name, *args, **kwargs) if plugin.enabled?
    end
  end
end

#add_custom_reviewable_filter(filter) ⇒ Object

Receives an array with two elements:

  1. A symbol that represents the name of the value to filter.

  2. A Proc that takes the existing ActiveRecord::Relation and the value received from the front-end.



919
920
921
# File 'lib/plugin/instance.rb', line 919

def add_custom_reviewable_filter(filter)
  reloadable_patch { Reviewable.add_custom_filter(filter) }
end

#add_directory_column(column_name, query:, icon: nil) ⇒ Object



451
452
453
454
455
456
457
458
459
460
461
# File 'lib/plugin/instance.rb', line 451

def add_directory_column(column_name, query:, icon: nil)
  validate_directory_column_name(column_name)

  DiscourseEvent.on("before_directory_refresh") do
    DirectoryColumn.find_or_create_plugin_directory_column(
      column_name: column_name,
      icon: icon,
      query: query,
    )
  end
end

#add_model_callback(klass_name, callback, options = {}, &block) ⇒ Object



340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/plugin/instance.rb', line 340

def add_model_callback(klass_name, callback, options = {}, &block)
  reloadable_patch do |plugin|
    klass =
      begin
        klass_name.to_s.classify.constantize
      rescue StandardError
        klass_name.to_s.constantize
      end

    # generate a unique method name
    method_name = "#{plugin.name}_#{klass.name}_#{callback}#{@idx}".underscore
    @idx += 1
    hidden_method_name = :"#{method_name}_without_enable_check"
    klass.public_send(:define_method, hidden_method_name, &block)

    klass.public_send(callback, **options) do |*args, **kwargs|
      public_send(hidden_method_name, *args, **kwargs) if plugin.enabled?
    end

    hidden_method_name
  end
end

#add_permitted_post_create_param(name, type = :string) ⇒ Object

Add a permitted_create_param to Post, respecting if the plugin is enabled



392
393
394
395
396
# File 'lib/plugin/instance.rb', line 392

def add_permitted_post_create_param(name, type = :string)
  reloadable_patch do |plugin|
    ::Post.plugin_permitted_create_params[name] = { plugin: plugin, type: type }
  end
end

#add_permitted_post_update_param(attribute, &block) ⇒ Object

Add a permitted_update_param to Post, respecting if the plugin is enabled



399
400
401
402
403
# File 'lib/plugin/instance.rb', line 399

def add_permitted_post_update_param(attribute, &block)
  reloadable_patch do |plugin|
    ::Post.plugin_permitted_update_params[attribute] = { plugin: plugin, handler: block }
  end
end

#add_permitted_reviewable_param(type, param) ⇒ Object



990
991
992
# File 'lib/plugin/instance.rb', line 990

def add_permitted_reviewable_param(type, param)
  DiscoursePluginRegistry.register_reviewable_param({ type: type, param: param }, self)
end

#add_post_revision_notifier_recipients(&block) ⇒ Object

Allows to add additional user_ids to the list of people notified when doing a post revision



373
374
375
376
377
378
379
# File 'lib/plugin/instance.rb', line 373

def add_post_revision_notifier_recipients(&block)
  reloadable_patch do |plugin|
    ::PostActionNotifier.add_post_revision_notifier_recipients do |post_revision|
      plugin.enabled? ? block.call(post_revision) : []
    end
  end
end

#add_preloaded_group_custom_field(field) ⇒ Object

Applies to all sites in a multisite environment. Ignores plugin.enabled?



382
383
384
# File 'lib/plugin/instance.rb', line 382

def add_preloaded_group_custom_field(field)
  reloadable_patch { |plugin| ::Group.preloaded_custom_field_names << field }
end

#add_preloaded_topic_list_custom_field(field) ⇒ Object

Applies to all sites in a multisite environment. Ignores plugin.enabled?



387
388
389
# File 'lib/plugin/instance.rb', line 387

def add_preloaded_topic_list_custom_field(field)
  reloadable_patch { |plugin| ::TopicList.preloaded_custom_fields << field }
end

#add_report(name, &block) ⇒ Object

Applies to all sites in a multisite environment. Ignores plugin.enabled?



175
176
177
# File 'lib/plugin/instance.rb', line 175

def add_report(name, &block)
  reloadable_patch { |plugin| Report.add_report(name, &block) }
end

Register a ReviewableScore setting_name associated with a reason. We’ll use this to build a site setting link and add it to the reason’s translation.

If your plugin has a reason translation looking like this:

my_plugin_reason: "This is the reason this post was flagged. See %{link}."

And you associate the reason with a setting:

add_reviewable_score_link(:my_plugin_reason, 'a_plugin_setting')

We’ll generate the following link and attach it to the translation:

<a href="/admin/site_settings/category/all_results?filter=a_plugin_setting">
  a plugin setting
</a>


1022
1023
1024
1025
1026
1027
# File 'lib/plugin/instance.rb', line 1022

def add_reviewable_score_link(reason, setting_name)
  DiscoursePluginRegistry.register_reviewable_score_link(
    { reason: reason.to_sym, setting: setting_name },
    self,
  )
end

#add_to_class(class_name, attr, &block) ⇒ Object

Extend a class but check that the plugin is enabled for class methods use ‘add_class_method`



304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/plugin/instance.rb', line 304

def add_to_class(class_name, attr, &block)
  reloadable_patch do |plugin|
    klass =
      begin
        class_name.to_s.classify.constantize
      rescue StandardError
        class_name.to_s.constantize
      end
    hidden_method_name = :"#{attr}_without_enable_check"
    klass.public_send(:define_method, hidden_method_name, &block)

    klass.public_send(:define_method, attr) do |*args, **kwargs|
      public_send(hidden_method_name, *args, **kwargs) if plugin.enabled?
    end
  end
end

#add_to_serializer(serializer, attr, deprecated_respect_plugin_enabled = nil, respect_plugin_enabled: true, include_condition: nil, &block) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/plugin/instance.rb', line 121

def add_to_serializer(
  serializer,
  attr,
  deprecated_respect_plugin_enabled = nil,
  respect_plugin_enabled: true,
  include_condition: nil,
  &block
)
  if !deprecated_respect_plugin_enabled.nil?
    Discourse.deprecate(
      "add_to_serializer's respect_plugin_enabled argument should be passed as a keyword argument",
    )
    respect_plugin_enabled = deprecated_respect_plugin_enabled
  end

  if attr.to_s.starts_with?("include_")
    Discourse.deprecate(
      "add_to_serializer should not be used to directly override include_*? methods. Use the include_condition keyword argument instead",
    )
  end

  reloadable_patch do |plugin|
    base =
      begin
        "#{serializer.to_s.classify}Serializer".constantize
      rescue StandardError
        "#{serializer.to_s}Serializer".constantize
      end

    # we have to work through descendants cause serializers may already be baked and cached
    ([base] + base.descendants).each do |klass|
      unless attr.to_s.start_with?("include_")
        klass.attributes(attr)

        if respect_plugin_enabled || include_condition
          # Don't include serialized methods if the plugin is disabled
          klass.public_send(:define_method, "include_#{attr}?") do
            next false if respect_plugin_enabled && !plugin.enabled?
            next instance_exec(&include_condition) if include_condition
            true
          end
        end
      end

      klass.public_send(:define_method, attr, &block)
    end
  end
end

#add_topic_static_page(page, options = {}, &blk) ⇒ Object

Allows customizing existing topic-backed static pages, like: faq, tos, privacy (see: StaticController) The block passed to this method has to return a SiteSetting name that contains a topic id.

add_topic_static_page("faq") do |controller|
  current_user&.locale == "pl" ? "polish_faq_topic_id" : "faq_topic_id"
end

You can also add new pages in a plugin, but remember to add a route, for example:

get "contact" => "static#show", id: "contact"


1061
1062
1063
# File 'lib/plugin/instance.rb', line 1061

def add_topic_static_page(page, options = {}, &blk)
  StaticController::CUSTOM_PAGES[page] = blk ? { topic_id: blk } : options
end

#add_user_api_key_scope(scope_name, matcher_parameters) ⇒ Object

Register a new UserApiKey scope, and its allowed routes. Scope will be prefixed with the (parameterized) plugin name followed by a colon.

For example, if discourse-awesome-plugin registered this:

add_user_api_key_scope(:read_my_route,

methods: :get,
actions: "mycontroller#myaction",
formats: :ics,
params: :testparam

)

The scope registered would be ‘discourse-awesome-plugin:read_my_route`

Multiple matchers can be attached by supplying an array of parameter hashes

See UserApiKeyScope::SCOPES for more examples And lib/route_matcher.rb for the route matching logic

Raises:

  • (ArgumentError)


952
953
954
955
956
957
958
959
960
961
# File 'lib/plugin/instance.rb', line 952

def add_user_api_key_scope(scope_name, matcher_parameters)
  raise ArgumentError.new("scope_name must be a symbol") if !scope_name.is_a?(Symbol)
  matcher_parameters = [matcher_parameters] if !matcher_parameters.is_a?(Array)

  prefixed_scope_name = :"#{(name || directory_name).parameterize}:#{scope_name}"
  DiscoursePluginRegistry.register_user_api_key_scope_mapping(
    { prefixed_scope_name => matcher_parameters&.map { |m| RouteMatcher.new(**m) } },
    self,
  )
end

#admin_js_asset_exists?Boolean

Returns:

  • (Boolean)


911
912
913
914
# File 'lib/plugin/instance.rb', line 911

def admin_js_asset_exists?
  # If this directory exists, ember-cli will output a .js file
  File.exist?("#{File.dirname(@path)}/admin/assets/javascripts")
end

#after_initialize(&block) ⇒ Object



488
489
490
# File 'lib/plugin/instance.rb', line 488

def after_initialize(&block)
  initializers << block
end

#allow_public_user_custom_field(field) ⇒ Object



196
197
198
# File 'lib/plugin/instance.rb', line 196

def allow_public_user_custom_field(field)
  DiscoursePluginRegistry.register_public_user_custom_field(field, self)
end

#allow_staff_user_custom_field(field) ⇒ Object



192
193
194
# File 'lib/plugin/instance.rb', line 192

def allow_staff_user_custom_field(field)
  DiscoursePluginRegistry.register_staff_user_custom_field(field, self)
end

#auth_provider(opts) ⇒ Object



787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
# File 'lib/plugin/instance.rb', line 787

def auth_provider(opts)
  before_auth do
    provider = Auth::AuthProvider.new

    Auth::AuthProvider.auth_attributes.each do |sym|
      provider.public_send("#{sym}=", opts.delete(sym)) if opts.has_key?(sym)
    end

    begin
      provider.authenticator.enabled?
    rescue NotImplementedError
      provider
        .authenticator
        .define_singleton_method(:enabled?) do
          Discourse.deprecate(
            "#{provider.authenticator.class.name} should define an `enabled?` function. Patching for now.",
            drop_from: "2.9.0",
          )
          return SiteSetting.get(provider.enabled_setting) if provider.enabled_setting
          Discourse.deprecate(
            "#{provider.authenticator.class.name} has not defined an enabled_setting. Defaulting to true.",
            drop_from: "2.9.0",
          )
          true
        end
    end

    DiscoursePluginRegistry.register_auth_provider(provider)
  end
end

#auto_generated_pathObject



484
485
486
# File 'lib/plugin/instance.rb', line 484

def auto_generated_path
  File.dirname(path) << "/auto_generated"
end

#automatic_assetsObject



669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
# File 'lib/plugin/instance.rb', line 669

def automatic_assets
  css = styles.join("\n")
  js = javascripts.join("\n")

  # Generate an IIFE for the JS
  js = "(function(){#{js}})();" if js.present?

  result = []
  result << [css, "css"] if css.present?
  result << [js, "js"] if js.present?

  result.map do |asset, extension|
    hash = Digest::SHA1.hexdigest asset
    ["#{auto_generated_path}/plugin_#{hash}.#{extension}", asset]
  end
end

#before_auth(&block) ⇒ Object



505
506
507
508
509
510
# File 'lib/plugin/instance.rb', line 505

def before_auth(&block)
  if @before_auth_complete
    raise "Auth providers must be registered before omniauth middleware. after_initialize is too late!"
  end
  before_auth_initializers << block
end

#commit_hashObject



492
493
494
# File 'lib/plugin/instance.rb', line 492

def commit_hash
  git_repo.latest_local_commit
end

#commit_urlObject



496
497
498
499
# File 'lib/plugin/instance.rb', line 496

def commit_url
  return if commit_hash.blank?
  "#{git_repo.url}/commit/#{commit_hash}"
end

#configurable?Boolean

Returns:

  • (Boolean)


106
107
108
# File 'lib/plugin/instance.rb', line 106

def configurable?
  true
end

#css_asset_exists?(target = nil) ⇒ Boolean

Returns:

  • (Boolean)


898
899
900
# File 'lib/plugin/instance.rb', line 898

def css_asset_exists?(target = nil)
  DiscoursePluginRegistry.stylesheets_exists?(directory_name, target)
end

#custom_avatar_column(column) ⇒ Object



286
287
288
289
290
291
# File 'lib/plugin/instance.rb', line 286

def custom_avatar_column(column)
  reloadable_patch do |plugin|
    UserLookup.lookup_columns << column
    UserLookup.lookup_columns.uniq!
  end
end

#delete_extra_automatic_assets(good_paths) ⇒ Object



463
464
465
466
467
468
469
470
471
472
473
# File 'lib/plugin/instance.rb', line 463

def delete_extra_automatic_assets(good_paths)
  return unless Dir.exist? auto_generated_path

  filenames = good_paths.map { |f| File.basename(f) }
  # nuke old files
  Dir.foreach(auto_generated_path) do |p|
    next if %w[. ..].include?(p)
    next if filenames.include?(p)
    File.delete(auto_generated_path + "/#{p}")
  end
end

#directoryObject



480
481
482
# File 'lib/plugin/instance.rb', line 480

def directory
  File.dirname(path)
end

#directory_nameObject



894
895
896
# File 'lib/plugin/instance.rb', line 894

def directory_name
  @directory_name ||= File.dirname(path).split("/").last
end

#each_globbed_assetObject



861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
# File 'lib/plugin/instance.rb', line 861

def each_globbed_asset
  if @path
    # Automatically include all ES6 JS and hbs files
    root_path = "#{File.dirname(@path)}/assets/javascripts"
    admin_path = "#{File.dirname(@path)}/admin/assets/javascripts"

    Dir
      .glob(["#{root_path}/**/*", "#{admin_path}/**/*"])
      .sort
      .each do |f|
        f_str = f.to_s
        if File.directory?(f)
          yield [f, true]
        elsif f_str.end_with?(".js.es6") || f_str.end_with?(".hbs") || f_str.end_with?(".hbr")
          yield [f, false]
        elsif f_str.end_with?(".js")
          yield [f, false]
        end
      end
  end
end

#enabled?Boolean

Returns:

  • (Boolean)


114
115
116
117
# File 'lib/plugin/instance.rb', line 114

def enabled?
  return false if !configurable?
  @enabled_site_setting ? SiteSetting.get(@enabled_site_setting) : true
end

#enabled_site_setting(setting = nil) ⇒ Object



832
833
834
835
836
837
838
# File 'lib/plugin/instance.rb', line 832

def enabled_site_setting(setting = nil)
  if setting
    @enabled_site_setting = setting
  else
    @enabled_site_setting
  end
end

#ensure_directory(path) ⇒ Object



475
476
477
478
# File 'lib/plugin/instance.rb', line 475

def ensure_directory(path)
  dirname = File.dirname(path)
  FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
end

#extend_content_security_policy(extension) ⇒ Object



592
593
594
# File 'lib/plugin/instance.rb', line 592

def extend_content_security_policy(extension)
  csp_extensions << extension
end

#extend_list_method(klass, method, new_attributes) ⇒ Object



887
888
889
890
891
892
# File 'lib/plugin/instance.rb', line 887

def extend_list_method(klass, method, new_attributes)
  current_list = klass.public_send(method)
  current_list.concat(new_attributes)

  reloadable_patch { klass.public_send(:define_singleton_method, method) { current_list } }
end

#extra_js_asset_exists?Boolean

Returns:

  • (Boolean)


907
908
909
# File 'lib/plugin/instance.rb', line 907

def extra_js_asset_exists?
  File.exist?(extra_js_file_path)
end

#gem(name, version, opts = {}) ⇒ Object

shotgun approach to gem loading, in future we need to hack bundler

to at least determine dependencies do not clash before loading

Additionally we want to support multiple ruby versions correctly and so on

This is a very rough initial implementation



824
825
826
# File 'lib/plugin/instance.rb', line 824

def gem(name, version, opts = {})
  PluginGem.load(path, name, version, opts)
end

#generate_automatic_assets!Object

will make sure all the assets this plugin needs are registered



436
437
438
439
440
441
442
443
444
445
446
447
448
449
# File 'lib/plugin/instance.rb', line 436

def generate_automatic_assets!
  paths = []
  assets = []

  automatic_assets.each do |path, contents|
    write_asset(path, contents)
    paths << path
    assets << [path, nil, directory_name]
  end

  delete_extra_automatic_assets(paths)

  assets
end

#git_repoObject



501
502
503
# File 'lib/plugin/instance.rb', line 501

def git_repo
  @git_repo ||= GitRepo.new(directory, name)
end

#handlebars_includesObject



840
841
842
843
844
845
846
847
848
# File 'lib/plugin/instance.rb', line 840

def handlebars_includes
  assets
    .map do |asset, opts|
      next if opts == :admin
      next unless asset =~ DiscoursePluginRegistry::HANDLEBARS_REGEX
      asset
    end
    .compact
end

#hide_pluginObject



828
829
830
# File 'lib/plugin/instance.rb', line 828

def hide_plugin
  @hidden = true
end

#javascript_includesObject



850
851
852
853
854
855
856
857
858
859
# File 'lib/plugin/instance.rb', line 850

def javascript_includes
  assets
    .map do |asset, opts|
      next if opts == :vendored_core_pretty_text
      next if opts == :admin
      next unless asset =~ DiscoursePluginRegistry::JS_REGEX
      asset
    end
    .compact
end

#js_asset_exists?Boolean

Returns:

  • (Boolean)


902
903
904
905
# File 'lib/plugin/instance.rb', line 902

def js_asset_exists?
  # If assets/javascripts exists, ember-cli will output a .js file
  File.exist?("#{File.dirname(@path)}/assets/javascripts")
end

#listen_for(event_name) ⇒ Object



575
576
577
578
# File 'lib/plugin/instance.rb', line 575

def listen_for(event_name)
  return unless self.respond_to?(event_name)
  DiscourseEvent.on(event_name, &self.method(event_name))
end

#notify_after_initializeObject



517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
# File 'lib/plugin/instance.rb', line 517

def notify_after_initialize
  color_schemes.each do |c|
    unless ColorScheme.where(name: c[:name]).exists?
      ColorScheme.create_from_base(name: c[:name], colors: c[:colors])
    end
  end

  initializers.each do |callback|
    begin
      callback.call(self)
    rescue ActiveRecord::StatementInvalid => e
      # When running `db:migrate` for the first time on a new database,
      # plugin initializers might try to use models.
      # Tolerate it.
      raise e unless e.message.try(:include?, "PG::UndefinedTable")
    end
  end
end

#notify_before_authObject



536
537
538
539
# File 'lib/plugin/instance.rb', line 536

def notify_before_auth
  before_auth_initializers.each { |callback| callback.call(self) }
  @before_auth_complete = true
end

#on(event_name, &block) ⇒ Object

A proxy to ‘DiscourseEvent.on` which does nothing if the plugin is disabled



513
514
515
# File 'lib/plugin/instance.rb', line 513

def on(event_name, &block)
  DiscourseEvent.on(event_name) { |*args, **kwargs| block.call(*args, **kwargs) if enabled? }
end

#register_about_stat_group(plugin_stat_group_name, show_in_ui: false, &block) ⇒ Object

Allows the plugin to export additional site stats via the About class which will be shown on the /about route. The stats returned by the block should be in the following format (these four keys are required):

last_day: 1,
7_days: 10,
30_days: 100,
count: 1000

Only keys above will be shown on the /about page in the UI, but all stats will be shown on the /about.json route. For example take this usage:

register_about_stat_group(“chat_messages”) do

{ last_day: 1, "7_days" => 10, "30_days" => 100, count: 1000, previous_30_days: 150 }

end

In the UI we will show a table like this:

| 24h | 7 days | 30 days | all time|

Chat Messages | 1 | 10 | 100 | 1000 |

But the JSON will be like this:

"chat_messages_last_day": 1,
"chat_messages_7_days": 10,
"chat_messages_30_days": 100,
"chat_messages_count": 1000,

The show_in_ui option (default false) is used to determine whether the group of stats is shown on the site About page in the Site Statistics table. Some stats may be needed purely for reporting purposes and thus do not need to be shown in the UI to admins/users.



1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
# File 'lib/plugin/instance.rb', line 1127

def register_about_stat_group(plugin_stat_group_name, show_in_ui: false, &block)
  # We do not want to register and display the same group multiple times.
  if DiscoursePluginRegistry.about_stat_groups.any? { |stat_group|
       stat_group[:name] == plugin_stat_group_name
     }
    return
  end

  DiscoursePluginRegistry.register_about_stat_group(
    { name: plugin_stat_group_name, show_in_ui: show_in_ui, block: block },
    self,
  )
end

#register_anonymous_cache_key(key, &block) ⇒ Object



95
96
97
98
99
100
# File 'lib/plugin/instance.rb', line 95

def register_anonymous_cache_key(key, &block)
  key_method = "key_#{key}"
  add_to_class(Middleware::AnonymousCache::Helper, key_method, &block)
  Middleware::AnonymousCache.cache_key_segments[key] = key_method
  Middleware::AnonymousCache.compile_key_builder
end

#register_asset(file, opts = nil) ⇒ Object



628
629
630
631
632
633
634
635
636
637
638
639
640
641
# File 'lib/plugin/instance.rb', line 628

def register_asset(file, opts = nil)
  raise <<~ERROR if file.end_with?(".hbs", ".handlebars")
      [#{name}] Handlebars templates can no longer be included via `register_asset`.
      Any hbs files under `assets/javascripts` will be automatically compiled and included."
    ERROR

  if opts && opts == :vendored_core_pretty_text
    full_path = DiscoursePluginRegistry.core_asset_for_name(file)
  else
    full_path = File.dirname(path) << "/assets/" << file
  end

  assets << [full_path, opts, directory_name]
end

#register_asset_filter(&blk) ⇒ Object

Register a block to run when adding css and js assets Two arguments will be passed: (type, request) Type is :css or :js. ‘request` is an instance of Rack::Request When using this, make sure to consider the effect on AnonymousCache



600
601
602
# File 'lib/plugin/instance.rb', line 600

def register_asset_filter(&blk)
  asset_filters << blk
end

#register_bookmarkable(klass) ⇒ Object

Register a class that implements [BaseBookmarkable], which represents another

ActiveRecord::Model

that may be bookmarked via the [Bookmark] model’s

polymorphic association. The class handles create and destroy hooks, querying, and reminders among other things.



1225
1226
1227
1228
# File 'lib/plugin/instance.rb', line 1225

def register_bookmarkable(klass)
  return if Bookmark.registered_bookmarkable_from_type(klass.model.name).present?
  DiscoursePluginRegistry.register_bookmarkable(RegisteredBookmarkable.new(klass), self)
end

#register_category_custom_field_type(name, type) ⇒ Object

Applies to all sites in a multisite environment. Ignores plugin.enabled?



542
543
544
# File 'lib/plugin/instance.rb', line 542

def register_category_custom_field_type(name, type)
  reloadable_patch { |plugin| Category.register_custom_field_type(name, type) }
end

#register_color_scheme(name, colors) ⇒ Object



647
648
649
# File 'lib/plugin/instance.rb', line 647

def register_color_scheme(name, colors)
  color_schemes << { name: name, colors: colors }
end

#register_css(style) ⇒ Object



580
581
582
# File 'lib/plugin/instance.rb', line 580

def register_css(style)
  styles << style
end

#register_custom_html(hash) ⇒ Object



612
613
614
# File 'lib/plugin/instance.rb', line 612

def register_custom_html(hash)
  DiscoursePluginRegistry.custom_html.merge!(hash)
end

#register_demon_process(demon_class) ⇒ Object

Register a new demon process to be forked by the Unicorn master. The demon_class should inherit from Demon::Base. With great power comes great responsibility - this method should be used with extreme caution. See ‘config/unicorn.conf.rb`.



985
986
987
988
# File 'lib/plugin/instance.rb', line 985

def register_demon_process(demon_class)
  raise "Not a demon class" if !demon_class.ancestors.include?(Demon::Base)
  DiscoursePluginRegistry.demon_processes << demon_class
end

#register_editable_group_custom_field(field) ⇒ Object



208
209
210
# File 'lib/plugin/instance.rb', line 208

def register_editable_group_custom_field(field)
  DiscoursePluginRegistry.register_editable_group_custom_field(field, self)
end

#register_editable_user_custom_field(field, staff_only: false) ⇒ Object



200
201
202
203
204
205
206
# File 'lib/plugin/instance.rb', line 200

def register_editable_user_custom_field(field, staff_only: false)
  if staff_only
    DiscoursePluginRegistry.register_staff_editable_user_custom_field(field, self)
  else
    DiscoursePluginRegistry.register_self_editable_user_custom_field(field, self)
  end
end

#register_email_poller(poller) ⇒ Object



623
624
625
626
# File 'lib/plugin/instance.rb', line 623

def register_email_poller(poller)
  plugin = self
  DiscoursePluginRegistry.register_mail_poller(poller) if plugin.enabled?
end

#register_email_unsubscriber(type, unsubscriber) ⇒ Object

Let plugin define custom unsubscribe keys, set custom instance variables on the ‘EmailController#unsubscribe` action, and describe what unsubscribing for that key does.

The method receives a class that inherits from ‘Email::BaseEmailUnsubscriber`. Take a look at it to know how to implement your child class.

In conjunction with this, you’ll have to:

- Register a new connector under app/views/connectors/unsubscribe_options.
We'll include the HTML inside the unsubscribe form, so you can add your fields using the
instance variables you set in the controller previously. When the form is submitted,
it sends the updated preferences to `EmailController#perform_unsubscribe`.

- Your code is responsible for creating the custom key by calling `UnsubscribeKey#create_key_for`.

Raises:

  • (ArgumentError)


1080
1081
1082
1083
1084
1085
1086
1087
1088
# File 'lib/plugin/instance.rb', line 1080

def register_email_unsubscriber(type, unsubscriber)
  core_types = [UnsubscribeKey::ALL_TYPE, UnsubscribeKey::DIGEST_TYPE, UnsubscribeKey::TOPIC_TYPE]
  raise ArgumentError.new("Type already exists") if core_types.include?(type)
  if !unsubscriber.ancestors.include?(EmailControllerHelper::BaseEmailUnsubscriber)
    raise ArgumentError.new("Not an email unsubscriber")
  end

  DiscoursePluginRegistry.register_email_unsubscriber({ type => unsubscriber }, self)
end

#register_emoji(name, url, group = Emoji::DEFAULT_GROUP) ⇒ Object



659
660
661
662
663
# File 'lib/plugin/instance.rb', line 659

def register_emoji(name, url, group = Emoji::DEFAULT_GROUP)
  name = name.gsub(/[^a-z0-9]+/i, "_").gsub(/_{2,}/, "_").downcase
  Plugin::CustomEmoji.register(name, url, group)
  Emoji.clear_cache
end

#register_group_custom_field_type(name, type) ⇒ Object

Applies to all sites in a multisite environment. Ignores plugin.enabled?



557
558
559
# File 'lib/plugin/instance.rb', line 557

def register_group_custom_field_type(name, type)
  reloadable_patch { |plugin| ::Group.register_custom_field_type(name, type) }
end

#register_group_param(param) ⇒ Object

Add a permitted_param to Group, respecting if the plugin is enabled Used in GroupsController#update and Admin::GroupsController#create



407
408
409
# File 'lib/plugin/instance.rb', line 407

def register_group_param(param)
  DiscoursePluginRegistry.register_group_param(param, self)
end

#register_groups_callback_for_users_search_controller_action(callback, &block) ⇒ Object

Add a custom callback for search to Group Callback is called in UsersController#search_users Block takes groups and optional current_user For example: plugin.register_groups_callback_for_users_search_controller_action(:admins_filter) do |groups, user|

groups.where(name: "admins")

end



418
419
420
421
422
423
424
# File 'lib/plugin/instance.rb', line 418

def register_groups_callback_for_users_search_controller_action(callback, &block)
  if DiscoursePluginRegistry.groups_callback_for_users_search_controller_action.key?(callback)
    raise "groups_callback_for_users_search_controller_action callback already registered"
  end

  DiscoursePluginRegistry.groups_callback_for_users_search_controller_action[callback] = block
end

#register_hashtag_data_source(klass) ⇒ Object

Used to register data sources for HashtagAutocompleteService to look up results based on a #hashtag string.

signatures:

Roughly corresponding to a model, this is used as a unique
key for the datasource and is also used when allowing different
contexts to search for and lookup these types. The `category`
and `tag` types are registered by default.
def self.type
end

The FontAwesome icon to use for the data source in the search results
and cooked markdown.
def self.icon
end

@param {Guardian} guardian - Current user's guardian, used for permission-based filtering
@param {Array} slugs - An array of strings that represent slugs to search this type for,
                       e.g. category slugs.
@returns {Hash} A hash with the slug as the key and the URL of the record as the value.
def self.lookup(guardian, slugs)
end

@param {Guardian} guardian - Current user's guardian, used for permission-based filtering
@param {String} term - The search term used to filter results
@param {Integer} limit - The number of search results that should be returned by the query
@returns {Array} An Array of HashtagAutocompleteService::HashtagItem
def self.search(guardian, term, limit)
end

@param {Array} search_results - An array of HashtagAutocompleteService::HashtagItem to sort
@param {String} term - The search term which was used, which may help with sorting.
@returns {Array} An Array of HashtagAutocompleteService::HashtagItem
def self.search_sort(search_results, term)
end

@param {Guardian} guardian - Current user's guardian, used for permission-based filtering
@param {Integer} limit - The number of search results that should be returned by the query
@returns {Array} An Array of HashtagAutocompleteService::HashtagItem
def self.search_without_term(guardian, limit)
end

Parameters:

  • klass (Class)
    • Must be a class that implements methods with the following



1185
1186
1187
# File 'lib/plugin/instance.rb', line 1185

def register_hashtag_data_source(klass)
  DiscoursePluginRegistry.register_hashtag_autocomplete_data_source(klass, self)
end

#register_hashtag_type_priority_for_context(type, context, priority) ⇒ Object

Used to set up the priority ordering of hashtag autocomplete results by type using HashtagAutocompleteService.

Parameters:

  • type (String)
    • Roughly corresponding to a model, can only be registered once

    per context. The ‘category` and `tag` types are registered for the `topic-composer` context by default in that priority order.

  • context (String)
    • The context in which the hashtag lookup or search is happening

    in. For example, the Discourse composer context is ‘topic-composer`. Different contexts may want to have different priority orderings for certain types of hashtag result.

  • priority (Integer)
    • A number value for ordering type results when hashtag searches

    or lookups occur. Priority is ordered by DESCENDING order.



1202
1203
1204
1205
1206
1207
# File 'lib/plugin/instance.rb', line 1202

def register_hashtag_type_priority_for_context(type, context, priority)
  DiscoursePluginRegistry.register_hashtag_autocomplete_contextual_type_priority(
    { type: type, context: context, priority: priority },
    self,
  )
end

#register_html_builder(name, &block) ⇒ Object



616
617
618
619
620
621
# File 'lib/plugin/instance.rb', line 616

def register_html_builder(name, &block)
  plugin = self
  DiscoursePluginRegistry.register_html_builder(name) do |*args, **kwargs|
    block.call(*args, **kwargs) if plugin.enabled?
  end
end

#register_javascript(js) ⇒ Object



584
585
586
# File 'lib/plugin/instance.rb', line 584

def register_javascript(js)
  javascripts << js
end

#register_locale(locale, opts = {}) ⇒ Object

Parameters:

  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (opts):

  • :name (String)
  • :nativeName (String)
  • :fallbackLocale (String)
  • :plural (Hash)


608
609
610
# File 'lib/plugin/instance.rb', line 608

def register_locale(locale, opts = {})
  locales << [locale, opts]
end

#register_modifier(modifier_name, &blk) ⇒ Object



170
171
172
# File 'lib/plugin/instance.rb', line 170

def register_modifier(modifier_name, &blk)
  DiscoursePluginRegistry.register_modifier(self, modifier_name, &blk)
end

#register_notification_consolidation_plan(plan) ⇒ Object

If your plugin creates notifications, and you’d like to consolidate/collapse similar ones, you’re in the right place. This method receives a plan object, which must be an instance of ‘Notifications::ConsolidateNotifications`.

Instead of using ‘Notification#create!`, you should use `Notification#consolidate_or_save!`, which will automatically pick your plan and apply it, updating an already consolidated notification, consolidating multiple ones, or creating a regular one.

The rule object is quite complex. We strongly recommend you write tests to ensure your plugin consolidates notifications correctly.



1042
1043
1044
1045
1046
1047
# File 'lib/plugin/instance.rb', line 1042

def register_notification_consolidation_plan(plan)
  if !plan.class.ancestors.include?(Notifications::ConsolidationPlan)
    raise ArgumentError.new("Not a consolidation plan")
  end
  DiscoursePluginRegistry.register_notification_consolidation_plan(plan, self)
end

#register_post_custom_field_type(name, type) ⇒ Object

Applies to all sites in a multisite environment. Ignores plugin.enabled?



552
553
554
# File 'lib/plugin/instance.rb', line 552

def register_post_custom_field_type(name, type)
  reloadable_patch { |plugin| ::Post.register_custom_field_type(name, type) }
end

#register_presence_channel_prefix(prefix, &block) ⇒ Object

Register a new PresenceChannel prefix. See PresenceChannel.register_prefix for usage instructions



996
997
998
# File 'lib/plugin/instance.rb', line 996

def register_presence_channel_prefix(prefix, &block)
  DiscoursePluginRegistry.register_presence_channel_prefix([prefix, block], self)
end

#register_push_notification_filter(&block) ⇒ Object

Registers a new push notification filter. User and notification payload are passed into block, and if all filters return ‘true`, the push notification will be sent.



1002
1003
1004
# File 'lib/plugin/instance.rb', line 1002

def register_push_notification_filter(&block)
  DiscoursePluginRegistry.register_push_notification_filter(block, self)
end

#register_reviewable_type(reviewable_type_class) ⇒ Object



883
884
885
# File 'lib/plugin/instance.rb', line 883

def register_reviewable_type(reviewable_type_class)
  extend_list_method Reviewable, :types, [reviewable_type_class.name]
end

#register_search_advanced_filter(trigger, &block) ⇒ Object

Allows to define custom search filters. Example usage:

Search.advanced_filter(/^min_chars:(\d+)$/) do |posts, match|
  posts.where("(SELECT LENGTH(p2.raw) FROM posts p2 WHERE p2.id = posts.id) >= ?", match.to_i)
end


224
225
226
# File 'lib/plugin/instance.rb', line 224

def register_search_advanced_filter(trigger, &block)
  Search.advanced_filter(trigger, &block)
end

#register_search_advanced_order(trigger, &block) ⇒ Object

Allows to define custom search order. Example usage:

Search.advanced_order(:chars) do |posts|
  posts.reorder("(SELECT LENGTH(raw) FROM posts WHERE posts.topic_id = subquery.topic_id) DESC")
end


216
217
218
# File 'lib/plugin/instance.rb', line 216

def register_search_advanced_order(trigger, &block)
  Search.advanced_order(trigger, &block)
end

#register_search_topic_eager_load(tables = nil, &block) ⇒ Object

Allow to eager load additional tables in Search. Useful to avoid N+1 performance problems. Example usage:

register_search_topic_eager_load do |opts|
  %i(example_table)
end

OR

register_search_topic_eager_load(%i(example_table))


253
254
255
# File 'lib/plugin/instance.rb', line 253

def register_search_topic_eager_load(tables = nil, &block)
  Search.custom_topic_eager_load(tables, &block)
end

#register_seed_data(key, value) ⇒ Object



651
652
653
# File 'lib/plugin/instance.rb', line 651

def register_seed_data(key, value)
  seed_data[key] = value
end

#register_seed_path_builder(&block) ⇒ Object



655
656
657
# File 'lib/plugin/instance.rb', line 655

def register_seed_path_builder(&block)
  DiscoursePluginRegistry.register_seed_path_builder(&block)
end

#register_seedfu_filter(filter = nil) ⇒ Object



571
572
573
# File 'lib/plugin/instance.rb', line 571

def register_seedfu_filter(filter = nil)
  DiscoursePluginRegistry.register_seedfu_filter(filter)
end

#register_seedfu_fixtures(paths) ⇒ Object



566
567
568
569
# File 'lib/plugin/instance.rb', line 566

def register_seedfu_fixtures(paths)
  paths = [paths] if !paths.kind_of?(Array)
  SeedFu.fixture_paths.concat(paths)
end

#register_service_worker(file, opts = nil) ⇒ Object



643
644
645
# File 'lib/plugin/instance.rb', line 643

def register_service_worker(file, opts = nil)
  service_workers << [File.join(File.dirname(path), "assets", file), opts]
end

#register_site_categories_callback(&block) ⇒ Object

Register a callback to add custom payload to Site#categories Example usage:

register_site_categories_callback do |categories|
  categories.each do |category|
    category[:some_field] = 'test'
  end
end


274
275
276
# File 'lib/plugin/instance.rb', line 274

def register_site_categories_callback(&block)
  Site.add_categories_callbacks(&block)
end

#register_summarization_strategy(strategy) ⇒ Object

Register an object that inherits from [Summarization::Base], which provides a way to summarize content. Staff can select which strategy to use through the ‘summarization_strategy` setting.



1234
1235
1236
1237
1238
1239
# File 'lib/plugin/instance.rb', line 1234

def register_summarization_strategy(strategy)
  if !strategy.class.ancestors.include?(Summarization::Base)
    raise ArgumentError.new("Not a valid summarization strategy")
  end
  DiscoursePluginRegistry.register_summarization_strategy(strategy, self)
end

#register_svg_icon(icon) ⇒ Object



588
589
590
# File 'lib/plugin/instance.rb', line 588

def register_svg_icon(icon)
  DiscoursePluginRegistry.register_svg_icon(icon)
end

#register_topic_custom_field_type(name, type) ⇒ Object

Applies to all sites in a multisite environment. Ignores plugin.enabled?



547
548
549
# File 'lib/plugin/instance.rb', line 547

def register_topic_custom_field_type(name, type)
  reloadable_patch { |plugin| ::Topic.register_custom_field_type(name, type) }
end

#register_topic_list_preload_user_ids(&block) ⇒ Object

Allows to add more user IDs to the list of preloaded users. This can be useful to efficiently change the list of posters or participants. Example usage:

register_topic_list_preload_user_ids do |topics, user_ids, topic_list|
  user_ids << Discourse::SYSTEM_USER_ID
end


242
243
244
# File 'lib/plugin/instance.rb', line 242

def register_topic_list_preload_user_ids(&block)
  TopicList.on_preload_user_ids(&block)
end

#register_topic_thumbnail_size(size) ⇒ Object

Request a new size for topic thumbnails Will respect plugin enabled setting is enabled Size should be an array with two elements [max_width, max_height]



260
261
262
263
264
265
# File 'lib/plugin/instance.rb', line 260

def register_topic_thumbnail_size(size)
  if !(size.kind_of?(Array) && size.length == 2)
    raise ArgumentError.new("Topic thumbnail dimension is not valid")
  end
  DiscoursePluginRegistry.register_topic_thumbnail_size(size, self)
end

#register_topic_view_posts_filter(trigger, &block) ⇒ Object

Allows to define TopicView posts filters. Example usage:

TopicView.advanced_filter do |posts, opts|
  posts.where(wiki: true)
end


232
233
234
# File 'lib/plugin/instance.rb', line 232

def register_topic_view_posts_filter(trigger, &block)
  TopicView.add_custom_filter(trigger, &block)
end

#register_upload_in_use(&block) ⇒ Object



282
283
284
# File 'lib/plugin/instance.rb', line 282

def register_upload_in_use(&block)
  Upload.add_in_use_callback(&block)
end

#register_upload_unused(&block) ⇒ Object



278
279
280
# File 'lib/plugin/instance.rb', line 278

def register_upload_unused(&block)
  Upload.add_unused_callback(&block)
end

#register_user_custom_field_type(name, type) ⇒ Object

Applies to all sites in a multisite environment. Ignores plugin.enabled?



562
563
564
# File 'lib/plugin/instance.rb', line 562

def register_user_custom_field_type(name, type)
  reloadable_patch { |plugin| ::User.register_custom_field_type(name, type) }
end

#register_user_destroyer_on_content_deletion_callback(callback) ⇒ Object

Register a block that will be called when the UserDestroyer runs with the :delete_posts opt set to true. It’s important to note that the block will execute before any other :delete_posts actions, it allows us to manipulate flags before agreeing with them. For example, discourse-akismet makes use of this

Parameters:

  • callback (Block)

    to be called with the user, guardian, and the destroyer opts as arguments



1216
1217
1218
# File 'lib/plugin/instance.rb', line 1216

def register_user_destroyer_on_content_deletion_callback(callback)
  DiscoursePluginRegistry.register_user_destroyer_on_content_deletion_callback(callback, self)
end

#replace_flags(settings: ::FlagSettings.new, score_type_names: []) {|settings, next_flag_id| ... } ⇒ Object

Applies to all sites in a multisite environment. Ignores plugin.enabled?

Yields:

  • (settings, next_flag_id)


180
181
182
183
184
185
186
187
188
189
190
# File 'lib/plugin/instance.rb', line 180

def replace_flags(settings: ::FlagSettings.new, score_type_names: [])
  next_flag_id = ReviewableScore.types.values.max + 1

  yield(settings, next_flag_id) if block_given?

  reloadable_patch do |plugin|
    ::PostActionType.replace_flag_settings(settings)
    ::ReviewableScore.reload_types
    ::ReviewableScore.add_new_types(score_type_names)
  end
end

#rescue_from(exception, &block) ⇒ Object



298
299
300
# File 'lib/plugin/instance.rb', line 298

def rescue_from(exception, &block)
  reloadable_patch { |plugin| ::ApplicationController.rescue_from(exception, &block) }
end

#seed_dataObject



70
71
72
# File 'lib/plugin/instance.rb', line 70

def seed_data
  @seed_data ||= HashWithIndifferentAccess.new({})
end

#seed_fu_filter(filter = nil) ⇒ Object



74
75
76
# File 'lib/plugin/instance.rb', line 74

def seed_fu_filter(filter = nil)
  @seed_fu_filter = filter
end

#topic_view_post_custom_fields_allowlister(&block) ⇒ Object

Add a post_custom_fields_allowlister block to the TopicView, respecting if the plugin is enabled



364
365
366
367
368
369
370
# File 'lib/plugin/instance.rb', line 364

def topic_view_post_custom_fields_allowlister(&block)
  reloadable_patch do |plugin|
    ::TopicView.add_post_custom_fields_allowlister do |user, topic|
      plugin.enabled? ? block.call(user, topic) : []
    end
  end
end

#translate_emoji(from, to) ⇒ Object



665
666
667
# File 'lib/plugin/instance.rb', line 665

def translate_emoji(from, to)
  Plugin::CustomEmoji.translate(from, to)
end

#validate(klass, name, &block) ⇒ Object

Add validation method but check that the plugin is enabled



427
428
429
430
431
432
433
# File 'lib/plugin/instance.rb', line 427

def validate(klass, name, &block)
  klass = klass.to_s.classify.constantize
  klass.public_send(:define_method, name, &block)

  plugin = self
  klass.validate(name, if: -> { plugin.enabled? })
end

#visible?Boolean

Returns:

  • (Boolean)


110
111
112
# File 'lib/plugin/instance.rb', line 110

def visible?
  configurable? && !@hidden
end