Skip to content

Latest commit

 

History

History
372 lines (296 loc) · 18.4 KB

Redmine-Notlarim.md

File metadata and controls

372 lines (296 loc) · 18.4 KB

Rails'in varsayılan davranışları hakkında başlıklar ve açıklamalar

Tablo Adları

  • Başlık: Tekil Tablo Adları
  • Açıklama: Rails, ilişkili modellerin tablo adlarını oluştururken, genellikle tekil isimlere çoğul ek ekler. Örneğin, Issue modeli için ilişkili tablo adı issues olur.

Model Adları:

  • Başlık: Tekil Model Adları
  • Açıklama: Rails, model isimlerini tekil olarak kabul eder. Örneğin, Issue modeli için tekil model adı Issue olur.

İlişkili Tablo Adları:

  • Başlık: Çoğul İlişkili Tablo Adları
  • Açıklama: Rails, ilişkili tabloların çoğunlukla birçok örneğini içerdiği durumlarda çoğul tablo adlarını kullanır. Örneğin, Issue modeli için ilişkili tablo adı issues olur.

İlişkili Model Adları:

  • Başlık: Çoğul İlişkili Model Adları
  • Açıklama: Rails, ilişkili modellerin çoğunlukla birçok örneğini içeren ilişkili tablolar için çoğul model adlarını kullanır. Örneğin, :issues ifadesi, ilişkili tabloyu doğru şekilde temsil eder.

Bu davranışlar, Rails'in birçok projede kullanılan yaygın kabulleri ve sözleşmeleridir. Bunlar, Rails'in kendi varsayılanlarını takip eden ve geliştiriciler arasında tutarlılık sağlayan bir yapı sunmasını sağlar. Ancak, ihtiyaçlarınıza göre bu davranışları değiştirebilir ve özelleştirebilirsiniz.

Veritabanı Güncellemeleri ve Migration

  1. Dizin Yolu

Öncelikle veritabanı işelmleri için dizin yolu şöyle olacak: .../plugins/my_plugin/db/migrate

  1. Dosya Adları

Bu dizin içindeki .rb uzantılı modelin kod dosyaları (migration dosyanızın) adının standart formatta olması gerekiyor. Migration dosyalarının adı, genellikle tarih ve saat bilgisini içeren bir formatı takip etmelidir.

Örneğinizde migration dosyasının adı "CreateTestsAndIssueTestsTables" olarak belirtilmiş. Ancak, migration dosyalarının adı genellikle tarih ve saat bilgisini içeren bir formatı takip eder. Bu sayede sıralama ve takip kolaylaşır.

Yani, eklentinizin db/migrate dizininde bir migration dosyasının adı, genellikle YYYYMMDDHHMMSS_create_table_name.rb formatında olmalıdır.

Burada "20230627140000" tarih ve saat bilgisini temsil eder. Böylece migration dosyası, tarih ve saat bilgisine göre sıralanır ve daha öngörülebilir bir şekilde işlenir.

Aşağıdaki kodun içerisinde puts "...." komutuyla migration dosyanızın işlendiğini konsol çıktılarında takip edebileceksiniz.

# 20230627140000_create_tests_and_issue_tests_tables.rb

class CreateTestsAndIssueTestsTables < ActiveRecord::Migration[5.2]
  def change
    puts "Running migration: CreateTestsAndIssueTestsTables"

    create_table :tests do |t|
      t.string :test_name
      t.integer :product_id
      t.timestamp :create_date
      t.timestamp :last_retrieve_date
      t.timestamps
    end

    create_table :issue_tests do |t|
      t.references :issue, index: true
      t.references :test, index: true
      t.timestamps
    end
  end
end

Değişikliklerinizi aktif hale getirmeden önce veritabanına erişilebilir olması için /usr/src/redmine/config/database.yml dosyasının olduğundan ve RAILS_ENV parametresine gelebilecek production veya development değerlerini alacak şekilde veritabanı ayarlarının olduğundan emin olmalısınız.

production:
  adapter: mysql2
  database: redmine
  host: db
  username: root
  password: admin
  # Use "utf8" instead of "utfmb4" for MySQL prior to 5.7.7
  encoding: utf8mb4

development:
  adapter: mysql2
  database: redmine_development
  host: localhost
  username: root
  password: ""
  # Use "utf8" instead of "utfmb4" for MySQL prior to 5.7.7
  encoding: utf8mb4

Migration dosyasının adını yukarıdaki gibi güncelledikten sonra, bundle exec rake redmine:plugins:migrate komutunu çalıştırarak tüm migration dosyalarını yeniden çalıştırabilirsiniz.

Bu komutu daha özelleştirerek bundle exec rake redmine:plugins:migrate NAME=my_plugin RAILS_ENV=production doğrudan my_plugin eklentinizi ortamınız production olacak şekilde çalıştırabilirsiniz.

Konsol çıktınız şöyle olacak:

root@25794971d4b4:/usr/src/redmine# bundle exec rake redmine:plugins:migrate NAME=my_plugin RAILS_ENV=production
W, [2023-06-27T13:44:17.862965 #48710]  WARN -- : Creating scope :system. Overwriting existing method Enumeration.system.
W, [2023-06-27T13:44:17.966598 #48710]  WARN -- : Creating scope :sorted. Overwriting existing method User.sorted.
W, [2023-06-27T13:44:18.292549 #48710]  WARN -- : Creating scope :visible. Overwriting existing method Principal.visible.
I, [2023-06-27T13:44:19.367760 #48710]  INFO -- : Migrating to CreateTestsAndIssueTestsTables (20230627140000)
== 20230627140000 CreateTestsAndIssueTestsTables: migrating ===================
Running migration: CreateTestsAndIssueTestsTables
-- create_table(:tests)
   -> 0.0855s
-- create_table(:issue_tests)
   -> 0.0668s
== 20230627140000 CreateTestsAndIssueTestsTables: migrated (0.1526s) ==========

image

    create_table :issue_tests do |t|
      t.references :issue, index: true
      t.references :test, index: true
      t.timestamps
    end

image

    create_table :tests do |t|
      t.string :test_name
      t.integer :product_id
      t.timestamp :create_date
      t.timestamp :last_retrieve_date
      t.timestamps
    end

image

Anlaşılan create_date ve last_retrieve_date alanlarına t.timestamps sayesinde gerek kalmıyor. Çünkü otomatik olarak created_at ve updated_at alanlarını otomatik ekliyor.

O halde gereksiz alanları kaldırmak için bir migration dosyası daha ekleyelim.

# my_plugin/db/migrate/20230701090000_remove_columns_from_tests.rb

class RemoveColumnsFromTests < ActiveRecord::Migration[5.2]
    def up
      remove_column :tests, :create_date
      remove_column :tests, :last_retrieve_date
    end
  
    def down
      add_column :tests, :create_date, :timestamp
      add_column :tests, :last_retrieve_date, :timestamp
    end
  end

Ve çalıştıralım:

bundle exec rake redmine:plugins:migrate NAME=my_plugin RAILS_ENV=production

çıktısı:

root@25794971d4b4:/usr/src/redmine/plugins/my_plugin# bundle exec rake redmine:plugins:migrate NAME=my_plugin RAILS_ENV=production
(in /usr/src/redmine)
W, [2023-06-27T18:36:21.012906 #107304]  WARN -- : Creating scope :system. Overwriting existing method Enumeration.system.
W, [2023-06-27T18:36:21.115684 #107304]  WARN -- : Creating scope :sorted. Overwriting existing method User.sorted.
W, [2023-06-27T18:36:21.436836 #107304]  WARN -- : Creating scope :visible. Overwriting existing method Principal.visible.
I, [2023-06-27T18:36:22.470574 #107304]  INFO -- : Migrating to RemoveColumnsFromTests (20230701090000)
== 20230701090000 RemoveColumnsFromTests: migrating ===========================
-- remove_column(:tests, :create_date)
   -> 0.0773s
-- remove_column(:tests, :last_retrieve_date)
   -> 0.1296s
== 20230701090000 RemoveColumnsFromTests: migrated (0.2071s) ==================

Ve sonuç:

image

Kodlar

issue_id'nin ilişkili revizyonlarının repository bilgilerini çeker

Issue sayfasında ilişkili revizyonların içinde döner ve repository_id değerlerini tekrarsız hale getirir.

context[:issue].changesets&.map { |c| c.repository.id }.uniq.each { |repo_id| puts repo_id }

Repository bilgisi:

context[:issue].changesets&.map { |c| c.repository.id }.uniq.each { |repo_id| puts Repository.find_by_id(repo_id).inspect }
#<Repository::Git id: 1, project_id: 1, url: "/home/redmine/repos/my_plugin.git", login: "", password: [FILTERED], root_url: "/home/redmine/repos/my_plugin.git", type: "Repository::Git", path_encoding: "", log_encoding: nil, extra_info: {"extra_report_last_commit"=>"0", "heads"=>["0ad57a72ec80071b94d1cf55bcd86474a736d631", "bd0da8a8de5717dd9248fcb04d3906825f507405"], "db_consistent"=>{"ordering"=>1}}, identifier: "git_identifier", is_default: true, created_on: "2023-07-01 09:56:24.000000000 +0000">

Aktif issue içindeki revizyonlardan hangi dizinde olduklarını bulup, ilgili commit id değerini kapsayan etiketleri bulur:

context[:issue].changesets&.each { |cs| puts `git -C #{Repository.find_by_id(cs.repository_id).url} tag --contains #{cs.revision}` }
1.1.0-1-bd0da8a8
  • context[:issue].changesets&: & ile varsa changesets değerine bakar.
  • .each { |cs|: changesets içinde gez ve her birini cs değişkenine ata.
  • Repository.find_by_id(cs.repository_id).url: changeset'in repository_id değerinden repository bilgisine eriş.
  • git -C #{Repository.find_by_id(cs.repository_id).url} tag --contains #{cs.revision}: changeset'in commit id değerini içeren etiketleri getir.

db:migrate ve Veritabanı İşlemleri

Tüm "redmine" veritabanını önce siler (db:drop) sonra yaratır ve güncelleme dosyalarını çalıştırır.

bundle exec rake db:drop db:create db:migrate

Belirli bir eklenti için db:migrate çalıştırılır

root@c802f15a15b8:/usr/src/redmine/plugins/my_plugin# bundle exec rake redmine:plugins:migrate NAME=my_plugin RAILS_ENV=production --trace

Hook'lar

Redmine içinde hook için geliştirici bağlantısı.

root@c802f15a15b8:/usr/src/redmine# grep -roh  'call_hook([^)]*)' /usr/src/redmine | sort -u | grep '([^)]*)'
call_hook(:"view_custom_fields_form_#{@custom_field.type.to_s.underscore}", :custom_field => @custom_field, :form => f)
call_hook(:another_hook, :foo => 'bar')
call_hook(:controller_account_success_authentication_after, {:user => user})
call_hook(:controller_custom_fields_edit_after_save, :params => params, :custom_field => @custom_field)
call_hook(:controller_custom_fields_new_after_save, :params => params, :custom_field => @custom_field)
call_hook(:controller_issues_bulk_edit_before_save, {:params => params, :issue => issue})
call_hook(:controller_issues_new_after_save, {:params => params, :issue => @issue})
call_hook(:controller_issues_new_before_save, {:params => params, :issue => @issue})
call_hook(:controller_journals_edit_post, {:journal => @journal, :params => params})
call_hook(:controller_messages_new_after_save, {:params => params, :message => @message})
call_hook(:controller_messages_reply_after_save, {:params => params, :message => @reply})
call_hook(:controller_wiki_edit_after_save, {:params => params, :page => @page})
call_hook(:some_hook)
call_hook(:view_calendars_show_bottom, :year => @year, :month => @month, :project => @project, :query => @query)
call_hook(:view_custom_fields_form_upper_box, :custom_field => @custom_field, :form => f)
call_hook(:view_issue_statuses_form, :issue_status => @issue_status)
call_hook(:view_issues_bulk_edit_details_bottom, { :issues => @issues })
call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back })
call_hook(:view_issues_context_menu_start, {:issues => @issues, :can => @can, :back => @back })
call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f })
call_hook(:view_issues_form_details_bottom, { :issue => @issue, :form => f })
call_hook(:view_issues_form_details_top, { :issue => @issue, :form => f })
call_hook(:view_issues_history_changeset_bottom, { :changeset => changeset })
call_hook(:view_issues_history_journal_bottom, { :journal => journal })
call_hook(:view_issues_history_time_entry_bottom, { :time_entry => time_entry })
call_hook(:view_issues_index_bottom, { :issues => @issues, :project => @project, :query => @query })
call_hook(:view_issues_new_top, {:issue => @issue})
call_hook(:view_issues_show_description_bottom, :issue => @issue)
call_hook(:view_issues_show_details_bottom, :issue => @issue)
call_hook(:view_issues_sidebar_issues_bottom)
call_hook(:view_issues_sidebar_planning_bottom)
call_hook(:view_issues_sidebar_queries_bottom)
call_hook(:view_journals_notes_form_after_notes, { :journal => @journal})
call_hook(:view_journals_update_js_bottom, { :journal => @journal })
call_hook(:view_layouts_base_html_head)
call_hook(:view_layouts_base_html_head, :foo => 1, :bar => 'a')
call_hook(:view_layouts_base_sidebar)
call_hook(:view_my_account, :user => @user, :form => f)
call_hook(:view_my_account_contextual, :user => @user)
call_hook(:view_my_account_preferences, :user => @user, :form => f)
call_hook(:view_projects_form, :project => @project, :form => f)
call_hook(:view_projects_settings_members_table_header, :project => @project)
call_hook(:view_projects_settings_members_table_row, { :project => @project, :member => member})
call_hook(:view_projects_show_left, :project => @project)
call_hook(:view_projects_show_right, :project => @project)
call_hook(:view_projects_show_sidebar_bottom, :project => @project)
call_hook(:view_projects_sidebar_queries_bottom)
call_hook(:view_reports_issue_report_split_content_left, :project => @project)
call_hook(:view_reports_issue_report_split_content_right, :project => @project)
call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project })
call_hook(:view_search_index_options_content_bottom)
call_hook(:view_settings_general_form)
call_hook(:view_time_entries_bulk_edit_details_bottom, { :time_entries => @time_entries })
call_hook(:view_time_entries_context_menu_end, {:time_entries => @time_entries, :can => @can, :back => @back })
call_hook(:view_time_entries_context_menu_start, {:time_entries => @time_entries, :can => @can, :back => @back })
call_hook(:view_timelog_edit_form_bottom, { :time_entry => @time_entry, :form => f })
call_hook(:view_users_form, :user => @user, :form => f)
call_hook(:view_users_form_preferences, :user => @user, :form => f)
call_hook(:view_versions_show_contextual, { :version => @version, :project => @project })
call_hook(:view_welcome_index_left)
call_hook(:view_welcome_index_right)
call_hook(:view_wiki_show_sidebar_bottom, :wiki => @wiki, :page => @page)
call_hook(hook, context={})
call_hook(hook, default_context.merge(context)

Eklentilerin Proje Modüllerinde Faal Edilmesi

Proje modüllerini listele:

Project.find(1).enabled_module_names
["issue_tracking", "time_tracking", "news", "documents", "files", "wiki", "repository", "boards", "calendar", "gantt", "git_tag_artifacts_1_0_0"]
# eklentinin adı sembol olarak $NAME_CODE_ARTIFACTS global değişkeninde tutuluyor
# $NAME_CODE_ARTIFACTS = :git_tag_artifacts_1_0_0
project_id = context[:project][:id]
current_project = Project.find(project_id)
current_project.module_enabled?($NAME_CODE_ARTIFACTS)

Eklenti Yetkilendirme

Kaynaklar

Modül Temelli Yetkilendirme

Her Redmine eklentisi, eklentinin başlangıçta Redmine'e kaydedilmesi için bir başlatma dosyası (init.rb) eklenmesini gerektirir.

Aşağıda bir init.rb dosyasında eklentiye dair yer alabilecek özelliklerin açıklamalarını bulabilirsiniz:

name: Bu, eklentinin tam adıdır. description: Bu, eklentinin ne yaptığına dair kısa bir açıklama verir. url: Bu, eklentinin kendisinin web sitesidir. Bu genellikle çevrimiçi veri havuzu URL'si (GitHub, Bitbucket, Google Code vb.) veya eklenti web sitesidir (varsa veya uygulanabilirse). author: Bu, eklentinin yazarlarının adlarını tutar. author_url: Bu, genellikle yazar(lar)ın e-posta adreslerine veya bloglarına bağlantıdır. version: Bu, eklentinin dahili sürüm numarasıdır. Zorunlu olmamakla birlikte, Redmine benzer (resmi olmasa da) bir numaralandırma şeması izlediğinden Anlamsal Sürüm Oluşturma kullanmak iyi bir uygulamadır (daha fazla bilgi için http://semver.org adresine bakın). settings: Bu alan, dahili eklenti ayarlarının varsayılan değerlerini tanımlamak ve ayarlamak ve sistem yöneticilerinin eklenti yapılandırma değerlerini ayarlamak için kullanabileceği kısmi bir görünüme bağlantı vermek için kullanılır.

init.rb Dosyasında yetkilendirme ayarlarını eklentiyi modül haline getirip oluşturuyoruz:

  project_module $NAME_CODE_ARTIFACTS do
    # Code Artifacts sekme başlığını
    permission :view_issue_code_artifacts_tab, {}
    # Code Artifacts sekme içeriğini göster
    permission :view_issue_code_artifacts, { issue_code_artifacts: :view_issue_code_artifacts }
    # Code Artifacts etiketlerinin bilgilerini çek
    permission :get_tag_artifact_metadata, { issue_code_artifacts: :get_tag_artifact_metadata }
  end

before_action :authorize, only: [ :get_tag_artifact_metadata, :view_issue_code_artifacts ] bu kod şu demek: Sadece (only) get_tag_artifact_metadata ve view_issue_code_artifacts metotlarını çalıştırmadan önce (before_action) Redmine ile gelen authorize metodunu çalıştırıp yetkilendirme yapar. Bu yetkilendirme benim_yetkilendirmem fonksiyonuna benzer bir iş yapar. Bu işi kendimiz yapmak istersek before_action :benim_yetkilendirmem, only: [ :get_tag_artifact_metadata, :view_issue_code_artifacts ] kodunu inceleyebilirsiniz.

class IssueCodeArtifactsController < ApplicationController
  # before_action :authorize, only: [ :get_tag_artifact_metadata, :view_issue_code_artifacts ]
  before_action :benim_yetkilendirmem, only: [ :get_tag_artifact_metadata, :view_issue_code_artifacts ]

  def benim_yetkilendirmem
    current_project = Project.find(Issue.find(params[:issue_id])[:project_id])
    unless User.current.allowed_to?(:get_tag_artifact_metadata, current_project)
      @error_message = "Kullanıcının bu bilgiye erişme yetkisi yok!"
      html_content = render_to_string(
        template: "errors/401",
        layout: false,
      )
      render html: html_content
    end
  end

  def get_tag_artifact_metadata
    puts ">>>>>>>>>> get_tag_artifact_metadata.............."
  end
end
Redmine::AccessControl.permission(:view_code_artifacts)
#<Redmine::AccessControl::Permission:0x00007fb15ba99548 @name=:view_code_artifacts, @actions=["issue_code_artifacts/get_tag_artifact_metadata"], @public=false, @require=nil, @read=false, @project_module=:git_tag_artifacts_1_0_0>