Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Redmine. Как писать плагины

Anna | 20.06.2014 | нет комментариев

В своем прошлом посте я постарался довольно подробно описать все тонкости установки Redmine на Linux Ubuntu. В этом, хочу рассказать о тонкостях написания плагинов под Redmine, об основных вероятностях метаморфозы функциональности стандартного Redmine, о подводных камнях, которые встречались моей команде на этом пути.

Думаю, эта статья будет пригодна тем, кто теснее знаком с основами фреймворка Ruby on Rails и хочет начать разрабатывать плагины для Redmine.

Раньше каждого, стоит поделить все плагины Redmine на две категории:

В первую попадают те плагины, которые реально не затрагивают функциональность стандартного Redmine. По сути это обыкновенные Rails-приложения внутри Redmine, с ними появляется немного трудностей, следственно они малоинтересны. На официальном сайте Redmine есть недурной туториал, детально описывающий как сделать плагин для голосования.

Все немножко труднее, когда плагин должен изменять встроенную функциональность!

Начнем с команды, которая создает конструкцию папок для плагина Redmine. Пускай наш плагин будет именоваться Luxury Buttons. Перейдем в корневую папку Redmine, запустим команду, создающую конструкцию папок:

$cd /usr/share/srv-redmine/redmine-2.3
$rails generate redmine_plugin LuxuryButtons

Позже выполнения команды в папке plugins должна возникнуть папка luxury_buttons со дальнейшей конструкцией:

В папку lib стоит сразу добавить, папку, совпадающую с наименованием плагина, т.е. папку luxury_buttons (дальше папка патчинга). В этой папке, в последующем, будут лежать файлы патчинга разных способов Redmine.

Отчего мы назвали эту папку также как назвали плагин? Это легко рекомендация, папку дозволено назвать и по-иному, но здесь появляется 1-й подводный камень: если в ином плагине наименование этой папки будет совпадать, и будет совпадать наименование файла патчинга, то один из файлов патчинга легко не примениться! Следственно, я рекомендую, называть папку патчинга одноименно с наименованием плагина. Такой метод минимизирует появление ошибок!

Когда плагин должен что-то добавить во вьюшке.

Возможен нам необходимо что-то добавить в стандартную вьюшку Redmine. Самый легкой и самый неверный метод сделать это – переписать вьюшку внутри плагина. Традиционно это делается путем копирования файла-вьюшки из ядра Redmine в соответствующую директорию плагина и последующим редактированием этого файла. Вот, скажем, в одном из наших плагинов, мы переписываем вьюшку с формой сохранения запроса.

Отчего так делать нехорошо:

  • Вы обрекаете себя на непрерывный мониторинг актуальности вашей вьюшки. Если в новой версии Redmine в данной вьюшке что-то поменяется, то вы утратите эту функциональность. Отслеживать востребованость вьюшки достаточно трудно.
  • Если появится иной плагин, тот, что перепишет эту же вьюшку, то применится либо ваша вьюшка, либо вьюшка иного плагина. Какая вьюшка применится, зависит от очередности плагинов.

Следственно, отменнее применять альтернативные способы.

Хуки

Хук во вьюшке – это такая строчка кода, которая разрешает встроить во вьюшку свое содержимое. Дабы обнаружить хук, необходимо легко исполнить поиск подстроки «hook» по каждому файлам Redmine либо дозволено воспользоваться вот этой табличкой.

Подключение хука

Мы усердствуем беречь все подключения хуков вьюшек в одном файле. Данный файл необходимо подключить в init.rb вот так:

require 'luxury_buttons/view_hooks'

Содержимое самого файла может быть таким:

module LuxuryButtons
  module LuxuryButtons
    class Hooks  < Redmine::Hook::ViewListener
      render_on( :view_issues_form_details_top, :partial => 'lu_buttons/view_issues_form_details_top')
render_on( :view_layouts_base_html_head, :partial => 'lu_buttons/page_header')
      render_on( :view_issues_show_description_bottom, :partial => "lu_buttons/button_bar" )
      render_on( :view_issues_history_journal_bottom, :partial => "lu_buttons/journal_detail")
    end
  end
end

Наименование первого модуля должно совпадать с наименованием плагина, второго – с наименованием папки патчинга.

Внутри класса находятся функции, которые показывают, в какой хук какой образец необходимо отрендерить.

Если два плагина будут применять один и тот же хук, то во вьюшки возникнуть содержимое и из первого и из второго плагина. Т.е. хуки не переписывают друг друга.

С хуками появляется две задачи:

  • Хука может не быть.
  • Изредка необходимо удалить что-то из вьюшки, а хук разрешает только добавить.

Исключительный метод, тот, что мы обнаружили, что бы решать данные задачи – это применение JQuery для модификации содержимого на странице, когда вьюшка теснее отрендерилась.

Для этого, проще каждого, применять хук «view_layouts_base_html_head», он разрешает вставить содержимое в шапку страницы. Нам нужно вставить ссылку на подключение js-файла с логикой вырезания либо добавления определенных DOM-элементов. Что бы данный js-файл не подгружался на страницах, на которых он не необходим, его загрузку отменнее загнать в условное выражение. Т.е. отсекать загрузку файла по экшину и контроллеру. Скажем:

  <% if controller_name == 'issues' && action_name == 'update' %>
    <%= javascript_include_tag :luxury_buttons_common, :plugin => :luxury_buttons %>
  <% end %>

В папке assets/javascript плагина должен находиться файл «luxury_buttons_common.js»:

jQuery(document).ready(function(){
//логика вырезания либо добавления элементов на страницу
});

Изредка, больше грамотно, встраивать строку подключения js-файла не через хук «view_layouts_base_html_head», а через определенный хук, тот, что встраивает содержимое на ограниченном, надобном нам числе страниц. Скажем, если нам необходимо, что-то добавить либо вырезать на странице задачи, то дозволено воспользоваться хуком «view_issues_form_details_bottom».

В таком случае, что бы файл подключался не в тело документа, а в шапку, необходимо применять конструкцию:

<% content_for :header_tags do %>
  <%= javascript_include_tag :luxury_buttons_common, :plugin => :luxury_buttons %>
<% end %>

Правда, с способом «content_for» в плагинах, от версии к версии появляются трудности.

Как изменять способы моделей, контроллеров и хелперов.

Метаморфоза (патчинг) способов во многом схоже на метаморфоза вьюшек и несет аналогичные задачи.

Хуки в контроллерах и моделях

В контроллерах и моделях тоже встречаются хуки. Подключаются они напротив. В init.rb должна быть строчка которая подключает определенный хук. Скажем, хук, тот, что вызывается перед сохранением новой задачи:

require 'luxury_buttons/controller_issues_new_before_save_hook'

В директории патчинга должен быть файл «controller_issues_new_before_save_hook.rb», скажем, с таким содержимым:

module LuxuryButtons
    class ControllerIssuesNewBeforeSaveHook < Redmine::Hook::ViewListener

      def controller_issues_new_before_save(context={})
        if context[:params] && context[:params][:issue]
          if (not context[:params][:issue][:assigned_to_id].nil?) and context[:params][:issue][:assigned_to_id].to_s==''
            context[:issue].assigned_to_id = context[:issue].author_id if context[:issue].new_record? and Setting.plugin_luxury_buttons['assign_to_author']
          end 
        end
        ''
      end
    end
end

Наименование модуля должно совпадать с наименованием плагина, наименование класса – с наименованием файла.

В данном случае, мы реализуем вероятность механического назначения новой задачи на автора.

Патчинг способов

Как и во вьюшках, надобные хуки в Redmine есть вдалеке не неизменно. И тогда необходимо патчить способы модели, хелпера либо контроллера.

Сперва, необходимо подключить файл патчинга в init.rb. К примеру, нам необходимопропатчить способ «read_only_attribute_names» модели «Issue».

Rails.application.config.to_prepare do
  Issue.send(:include, LuxuryButtons::IssuePatch)
end

В папке патчинга должен быть файл «issue_patch.rb», приблизительно дальнейшего оглавления:

module LuxuryButtons
  module IssuePatch
    def self.included(base)
      base.extend(ClassMethods)
      base.send(:include, InstanceMethods)  

      base.class_eval do  
        alias_method_chain :read_only_attribute_names, :luxury_buttons
      end
    end

    module ClassMethods   
    end

    module InstanceMethods
      def read_only_attribute_names_with_luxury_buttons(user)
        attribute = read_only_attribute_names_without_luxury_buttons(user)
        if Setting.plugin_luxury_buttons['hidden_fields_into_new_issue_form'] && new_record?
          hidden_fields = Setting.plugin_luxury_buttons['hidden_fields_into_new_issue_form']
          attribute  = hidden_fields
          attribute
        end
        attribute
      end
    end
  end
end

Конструкцией

alias_method_chain :read_only_attribute_names, :luxury_buttons

мы порождаем два способа «read_only_attribute_names_with_luxury_buttons» и «read_only_attribute_names_without_luxury_buttons».

1-й способ сейчас будет вызываться взамен стандартного способа модели «read_only_attribute_names», 2-й способ является алиасом для стандартного способа «read_only_attribute_names».

Сочетанием 2-х способов дозволено патчить типовой способ Redmine. В нашем примере, мы сперва вызываем типовой способ Redmine, тот, что возвращает массив значений, а после этого, добавляем значения в данный массив.

Если в стандартном способе Redmine в новой версии что-то поменяется, то шансов, что наш патчинг будет трудиться правильно значительно огромнее, чем если бы мы легко переписали типовой способ Redmine добавив в него свою логику.

Значимо! В Redmine отслеживаются какие-то задачи с патчингом модели User. Для правильного патчинга необходимо очевидно подключить следующие файлы:

require_dependency 'project'
require_dependency 'principal'
require_dependency 'user'

Статья не содержит каждого, что хотелось бы сказать о написании плагинов под Redmine. Я попытался собрать основные методики и подводные камни. Верю статья будет пригодна.

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB