Umgang mit Uploads in RefineryCMS 0.9.8.5

There was a time, when I shared a blog. This post was written by Stephan. See all blogpost from him or stalk him on github.


Die neuste Version von RefineryCMS (0.9.8.5) nutzt die Rack-Anwendung "Dragonfly", um u.a. den Upload von Bildern zu realisieren. Diese Umstellung hat bei der Pluginerstellung zu einigen Änderungen geführt, die ich hier zusammenfassen möchte.

Als Grundlage soll uns folgendes Szenario dienen:

Mit dem neuen Refinery-Generator "refinery_engine" erzeugen wir ein neues Plugin (seit Umstieg auf Rails 3 auch "Engine" genannt). Gleichzeitig legen wir die für das Projekt-Plugin benötigten Felder an. Ich beschränke mich hier auf ein Titel- und Beschreibungsfeld.

 cd /my/path/to_rails/app
 rails generate refinery_engine project title:string description:text

Das Plugin sollte nun im Backend von RefineryCMS verfügbar sein.

Wollen wir nun einen Bildupload zur Verfügung stellen, müssen wir zunächst ein neues Feld in unserer Projekttabelle anlegen. Wir erstellen eine neue Migration,die das neue Feld "project_image" in die Projekttabelle hinzufügt:

Hier der Befehl für den Generator

rails generate migration AddProjectImageToProjects project_image:integer

und die erhaltene Migrationsdatei:

class AddProjectImageToProjects < ActiveRecord::Migration
 def self.up
  add_column :projects, :project_image, :integer
 end

 def self.down
  remove_column :projects, :project_image
 end
end

Es ist aber auch möglich, bei der Erzeugung des Plugins gleich das "Uploadfeld" mit anzugeben:

cd /my/path/to_rails/app
rails generate refinery_engine project title:string description:text project_image:integer

Im neu erstellten Feld wird später die ID gespeichert, die auf das gepsiecherte Bild in der Refinery Image Tabelle verweist. Damit dies funktioniert, muss in unser Projekt-Model noch folgendes eingetragen werden:

class Project < ActiveRecord::Base
 validates :title, :presence=> true

 #Verknüpfung mit Bildtabelle von RefineryCMS
 belongs_to :image, :foreign_key=> "project_image"
end

Merken: Der Foreign-Key kann nur weggelassen werden, wenn das Uploadfeld, in dem die ID zum Bild gespeichert wird, den Namen imageid hat (siehe Rails Konvention). Also bitte an die foreignkey-Option denken und die Namen stets überprüfen.

Die Verbindung in der Datenbank zwischen dem Bild und Projekt ist nun sichergestellt., aber im automatisch generierten Form muss noch das Uploadfeld für den RefineryCMS-File/Image-Manager eingebunden werden:

<%= form_for [:admin, @project] do |f| -%>
<%= render :partial =&gt; "/shared/admin/error_messages", :locals => {
:object => @project,
:include_object_name =&gt; true
} %>
<div class='field'>
 <%= f.label :title -%>
 <%= f.text_field :title -%>
</div>

<div class='field'>
 <%= f.label :description -%>
 <%= f.text_area :description, :rows => 20, :class => 'wymeditor widest' -%>
</div>

<div class='field'>
 <%= f.label :project_image -%>
 <%= render :partial => "/shared/admin/image_picker", :locals => {
  :f => f,
  :field => :project_image,
  :image => @project.image,
  :toggle_image_display => false
 } %>
</div>

<%= render :partial => "/shared/admin/form_actions", :locals => {
 :f => f,
 :continue_editing => false,
 :delete_title => t('admin.projects.project.delete'),
 :delete_confirmation => t('shared.admin.delete.message', :title => @project.title)
} %>
<% end -%>

Erklärungen zum Partial: :field beschreibt den Namen des Feldes, in dem die ID des Uploads gespeichert wird, in unserem Fall ist dies das Feld "projectimage" :image Bild aus der Image-Tabelle von RefineryCMS, @project.image steht durch die belongsto-Methode in unserem Modell zur Verfügung

Ich hoffe, ich konnte euch damit den RefineryCMS-Umstieg von Rails 2 auf Rails 3 erleichtern. Ich möchte noch anmerken, dass folgender Befehl:

rails generate refinery_engine product title:string description:text image:image brochure:resource

wie er hier beschrieben wird, nicht mehr funktioniert. Durch die Umstellung gibt es keinen Typ Image oder Resource mehr (bei mir erzeugt dieser Generatorbefehl ein Formular mit Integerfeldern für Image und Brochure).

So ist es nun möglich, das ImagePicker-Partial von RefineryCMS wieder zu verwenden. Möchte man jetzt aber ein Plugin schreiben, bei dem es verschiedene Arten von Bildern gibt, ist mir bis jetzt noch keine gute Lösung eingefallen. Bei einem aktuellen Projekt habe ich ein Panoramamodell, in dem 6 verschiedene Bilder geuploadet werden müssen, die dann zu einem großen Gesamtpanorama zusammengefügt werden. Durch den oben beschriebenen Weg ist es aber nur möglich EIN EINZIGES Bild zu verwenden.

Die ersten Gedanken führten dazu, eine hasmany-Assoziation mit dem Parameter :through zu verwenden, was eine extra Tabelle erfordert, die die Einträge zu der RefineryCMS Image-Tabelle herstellt. So arbeitet zum Beispiel auch die neue Rails 3 Variante vom Portfolio-Plugin (<a href="http://github.com/resolve/refinerycms-portfolio/blob/rails3/app/models/portfolioentry.rb">hier ein Blick auf das Model). Dort können zwar dann viele Bilder hochgeladen werden, aber ich habe noch keine Zuordnung, welches geuploadete Bild zur welcher Position im Panorama gehört (oben,unten,links,rechts usw.).

Natürlich kann ich mir auch in meinem Plugin eine neue Dragonfly-App registrieren, aber dann sind die damit gespeicherten Bilder nicht im Filemanager von Refinery (Iamge Plugin) verfügbar.

Ich muss das auf alle Fälle noch weiter überdenken und weiter schlau machen, aber im alten RefineryCMS ging das irgendwie etwas einfacher.

Aber vielleicht gibt es bald ein Tutorial-Update von den Entwicklern.

Bis zum nächsten mal