How to add translations to a custom extension? - refinerycms

I am trying to add translations to a custom extension named Post, but when I try to save a post I get an error.
I have enabled three locales (:es, :en, :pt). I have noticed when I open a a form there is not a default locale selected.
What am I missing?
Migration:
class PostTranslations < ActiveRecord::Migration
def up
::Refinery::Posts::Post.create_translation_table!({
:title => :string,
:description => :text },
:migrate_data => true)
remove_column :refinery_posts, :title
remove_column :refinery_posts, :description
end
def self.down
add_column :refinery_posts, :title
add_column :refinery_posts, :description
::Refinery::Posts::Post.drop_translation_table! :migrate_data => true
end
end
_locale_picker.html.erb
<input type="hidden" name="switch_locale" id="switch_locale" value="<%= local_assigns[:current_locale] %>" />
<% if Refinery::I18n.frontend_locales.many? %>
<label><%= t('.language') %></label>
<ul id="switch_locale_picker" class="clearfix">
<% locales = Refinery::I18n.locales.clone %>
<% Refinery::I18n.frontend_locales.each do |locale| %>
<% locale_name = locales.delete(locale) %>
<li<%= %Q{ class=selected} if locale.to_s == local_assigns[:current_locale].to_s %>>
<%= link_to refinery.url_for(:switch_locale => locale, :parent_id => params[:parent_id]), id: locale do %>
<div class="<%=locale %> locale_marker">
<%= locale_text_icon(locale.upcase) %>
</div>
<%= locale_name %>
<% end %>
</li>
<% end %>
</ul>
<% end %>
in Post form:
...
<%= render :partial => "locale_picker",
:locals => {
:current_locale => Thread.current[:globalize_locale]
} if defined?(::Refinery::I18n) %>
...
Schema:
...
create_table "refinery_post_translations", force: :cascade do |t|
t.integer "refinery_post_id", null: false
t.string "locale", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "title"
t.text "description"
end
add_index "refinery_post_translations", ["locale"], name: "index_refinery_post_translations_on_locale", using: :btree
add_index "refinery_post_translations", ["refinery_post_id"], name: "index_refinery_post_translations_on_refinery_post_id", using: :btree
create_table "refinery_posts", force: :cascade do |t|
t.integer "post_image_id"
t.date "post_date"
t.string "author"
t.integer "position"
t.datetime "created_at"
t.datetime "updated_at"
end
...
Error:
Started POST "/refinery/posts" for ::1 at 2016-06-04 13:22:08 -0400
Processing by Refinery::Posts::Admin::PostsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"MIHqOv9yCQWkIdS2dKInFbJQtX62+21iqqTDzBtSCOHfBr7TejNIOgQX6iJgI4TWVqa+DVjsemz1DgkhtIZFOw==", "switch_locale"=>"", "post"=>{"post_image_id"=>"", "title"=>"Juan", "post_date(3i)"=>"4", "post_date(2i)"=>"6", "post_date(1i)"=>"2016", "author"=>"", "description"=>""}, "locale"=>:pt}
(0.4ms) SELECT MAX("refinery_posts"."position") FROM "refinery_posts"
Refinery::Authentication::Devise::User Load (0.2ms) SELECT "refinery_authentication_devise_users".* FROM "refinery_authentication_devise_users" WHERE "refinery_authentication_devise_users"."id" = $1 ORDER BY "refinery_authentication_devise_users"."id" ASC LIMIT 1 [["id", 1]]
Refinery::Authentication::Devise::Role Load (0.2ms) SELECT "refinery_authentication_devise_roles".* FROM "refinery_authentication_devise_roles" INNER JOIN "refinery_authentication_devise_roles_users" ON "refinery_authentication_devise_roles"."id" = "refinery_authentication_devise_roles_users"."role_id" WHERE "refinery_authentication_devise_roles_users"."user_id" = $1 [["user_id", 1]]
(0.2ms) BEGIN
(0.3ms) ROLLBACK
Rendered /Users/juan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/refinerycms-9e79b276891f/core/app/views/refinery/admin/_error_messages.html.erb (0.6ms)
Rendered vendor/extensions/posts/app/views/refinery/posts/admin/posts/_locale_picker.html.erb (2.5ms)
Rendered /Users/juan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/refinerycms-9e79b276891f/core/app/views/refinery/admin/_image_picker.html.erb (1.1ms)
Rendered /Users/juan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/refinerycms-9e79b276891f/core/app/views/refinery/admin/_wysiwyg.html.erb (0.4ms)
Rendered /Users/juan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/refinerycms-9e79b276891f/core/app/views/refinery/admin/_form_actions.html.erb (1.2ms)
Rendered vendor/extensions/posts/app/views/refinery/posts/admin/posts/_form.html.erb (142.5ms)
Rendered vendor/extensions/posts/app/views/refinery/posts/admin/posts/new.html.erb within layouts/refinery/admin (151.0ms)
Rendered refinery/_html_tag.html.erb (1.0ms)
Rendered /Users/juan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/refinerycms-9e79b276891f/core/app/views/refinery/admin/_javascripts.html.erb (139.9ms)
Rendered /Users/juan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/refinerycms-9e79b276891f/core/app/views/refinery/admin/_head.html.erb (207.1ms)
Rendered refinery/_site_bar.html.erb (0.7ms)
Rendered /Users/juan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/refinerycms-9e79b276891f/core/app/views/refinery/admin/_menu_item.html.erb (59.5ms)
Rendered /Users/juan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/refinerycms-9e79b276891f/core/app/views/refinery/admin/_menu.html.erb (89.9ms)
Rendered refinery/_no_script.html.erb (0.3ms)
Rendered refinery/_message.html.erb (0.1ms)

I am not an expert in translations but I think there is a quicker way to do it.
In your model you could have
translates :title, :description
and that would be enough. The whole locale picker is, I think, generated automatically once model fields are being translated.
There may be generators too, but I am not sure. Check the gitter responses.

I assume you don't get this error when you are on some default tab.
Check if in your vendor/extensions/posts/config/locales folder are all languages you want. Maybe it's because it's trying to get the translation for a language that is not set (or just special keyword for translation you added only for one language?).
Look inside your en.yml and check if that content is set in all the others language files you want to use (es.yml and pt.yml).
Edit: Oh wait... And what about that error inside of the image field? What if it's trying to get the translation for image title or something but it fails because an image is not set?

Related

Filtering content in Text::Xslate

In Mason i could define a filter:
<%filter Div($class)>
<div class="<% $class %>">
<% $yield->() %>
</div>
</%filter>
And later i could use it
% $.Div("row") {{
1 The "$yield->()" method returns everything from here (free text)
% $.Div("col") {{
2 even could have another nested filter, etc...
% }}
% }}
with result
<div class="row">
1 The "$yield->()" method returns everything from here (free text)
<div class="col">
2 even could have another nested filter, etc...
</div>
</div>
E.g. the $yield->() method returns everything what is inside of the enclosed filter.
Want achieve same functionality using Text::Xslate, but don't know how.
The closest thing what I found ,are the macro blocks where i could write:
: macro div -> ($cls,$str) {
<div class="<: $cls :>">
<: $str :>
</div>
: }
and use it as
: div("row",
: div("col",
: "my string"
: )
: )
but the my string must be quoted and : marked, so it is impossible to use any free html text. E.g. the following dies.
: div("row",
some free text here
: div("col",
nested
here
: )
: )
After the long introduction, the question is simple:
Exists in the Text::Xslate something as the Mason's $yield->() call, which returns the content of the enclosed filter call? (or if not exists, how i could mimic it?)

Shrine with Rails multiple polymorphic image uploads

I've been struggling for about 5 hours trying to understand why Shrine is blocking my uploads. I either get errors like "Shrine: Invalid file", or "Expected Array but got string" in strong params. If there aren't errors, the images aren't actually saved.
require "image_processing/mini_magick"
class ImageUploader < Shrine
include ImageProcessing::MiniMagick
plugin :activerecord
plugin :backgrounding
plugin :cached_attachment_data
plugin :determine_mime_type
plugin :delete_raw
plugin :direct_upload
plugin :logging, logger: Rails.logger
plugin :processing
plugin :remove_attachment
plugin :store_dimensions
plugin :validation_helpers
plugin :versions
Attacher.validate do
validate_max_size 2.megabytes, message: 'is too large (max is 2 MB)'
validate_mime_type_inclusion ['image/jpg', 'image/jpeg', 'image/png', 'image/gif']
end
def process(io, context)
case context[:phase]
when :store
thumb = resize_to_limit!(io.download, 200, 200)
{ original: io, thumb: thumb }
end
end
end
class Image < ActiveRecord::Base
include ImageUploader[:image]
belongs_to :imageable, polymorphic: true
end
class Product < ApplicationRecord
has_many :images, as: :imageable, dependent: :destroy
accepts_nested_attributes_for :images, allow_destroy: true
...
# Strong Params:
def product_params
params.require(:product).permit(
:name, :brand_id, :category_id, :price, :compare_price, :description,
images_attributes: { image: [] },
product_properties_attributes: [:id, :property_id, :value]
)
...
And my view:
<%= f.fields_for :images do |image_form| %>
<%= image_form.file_field :image, multiple: true %>
<% end %>
According to everything I've read on the docs or from gorails, this should work. Do I need to restructure the images_attributes hash? I also tried using direct_uploads, but struggled to get the presigned_url to work with S3.
Refile makes this really easy, so I'll probably run crying back to that.
Is there something I'm obviously doing wrong?
According to the fields_for documentation, the provided block will be called for each image in the project.images collection. So if your product currently doesn't have any images, the block won't be called (according to the docs).
For nested attributes to work, you need to forward the following parameters when creating the Product:
product[images_attributes][0][image] = <file object or data hash>
product[images_attributes][1][image] = <file object or data hash>
product[images_attributes][2][image] = <file object or data hash>
...
If you look at the "Multiple Files" Shrine guide, it's recommended that you just have a single file field which accepts multiple files:
<input type="file" name="file" multiple>
And then setup direct uploads for this field using Uppy, dynamically generating the image field for each uploaded file populated with the uploaded file data hash:
<input type="hidden" name="product[images_attributes][0][image]" value='{"id":"...","storage":"cache","metadata":{...}}'>
<input type="hidden" name="product[images_attributes][1][image]" value='{"id":"...","storage":"cache","metadata":{...}}'>
....
Alternatively you can just let users attach multiple files, which are all submitted to the app, and then destructure them in the controller:
class ProductsController < ApplicationController
def create
images_attributes = params["files"].map { |file| {image: file} }
Product.create(product_params.merge(images_attributes: images_attributes))
end
end
In that case you have to make sure your HTML form has the enctype="multipart/form-data" attribute set (otherwise only the files' filenames will get submitted, not files themselves).

Why would my Edit Form be view-able but not my New form of the same controller?

If I click on my edit link the link works and takes me to the edit form for the particular gallery I'd like to edit. If I click on the link to the new for the same controller I get the following error:
NoMethodError at /users/2/galleries/new
undefined method `galleries_path' for #<#<Class:0x007f4714fbac68>:0x000000060f32e0>
Did you mean? gallery_path
I've done extensive searching for the solution to this, but I'm not sure what I am doing wrong. This was working fine until I updated the routes to have galleries a nested resource to the user. I was in the process of updating/correcting my links and forms when I couldn't figure out how to get past this issue.
Below are my routes, the galleries/_form, galleries/edit, galleries/_new, parts of the gallery controller.
routes.rb
resources :users, shallow: true do
resources :galleries
resources :images
resources :albums
end
galleries.html.erb
<h1>New Gallery</h1>
<%= render 'form' %>
<%= link_to 'Dashboard', user_path(current_user) %>
galleries/edit.html.erb
<h1>Update Gallery</h1>
<%= render 'form' %>
<%= link_to 'Dashboard', user_path(current_user) %>
galleries_controller.rb
def new
#gallery = current_user.galleries.new
end
def create
#gallery = current_user.galleries.build(gallery_params)
respond_to do |format|
if #gallery.save
format.html { redirect_to #gallery, notice: 'Gallery was successfully created.' }
format.json { render :show, status: :created, location: #gallery }
else
format.html { render :new }
format.json { render json: #gallery.errors, status: :unprocessable_entity }
end
end
end
def show
end
def update
if #gallery.update_attributes(gallery_params)
flash[:success] = "Gallery Updated"
redirect_to #gallery
else
render 'edit'
end
end
private
def set_gallery
#gallery = Gallery.find(params[:id])
end
def gallery_params
params.require(:gallery).permit(:title, images_files: [])
end
If you need more from me let me know. Thanks in advance.
I ended up not using 1 form for edit and new. I made two separate forms, violating DRY principles, but getting my app to work so I could move on.

Rails 4 form_tag submitting to wrong controller action

This feels dumb, but so far everything I've read makes me feel like I'm doing it right but it's still not working.
I'm using a form_tag to submit params to a custom controller action. Instead of the action I indicate, it seems intents on submitting to the show action in my controller, which I need to reserve for profiles. Anyways here's the code (please excuse it's un-refactored state):
Doctors controller:
class DoctorsController < ApplicationController
def new
end
def show
#doctor_list = Doctor.find(params[:id])
end
def index
#doctor_list = Doctor.all
end
def search
end
def results
if params[:zip] && params[:zip].length === 5 && params[:zip]
#doctor_list = Doctor.where("zip = ?", params[:zip])
elsif params[:id]
begin
#doctor_list = []
#doctor_list<<Doctor.find(params[:id])
rescue
flash.now[:errors] = "That doctor does not exist!"
render 'search'
end
else
flash.now[:errors] = "That is not a valid zipcode!"
render 'search'
end
end
end
Routes:
resources :users
resources :doctors
root 'doctors#search'
get 'doctors/results' => 'doctors#results'
search.html.erb:
<% provide(:title, "Home") %>
<div class="hero">
<h1>Find an M.D.</h1>
<%= form_tag(doctors_results_path, method: "get") do %>
<%= label_tag("Zipcode: ") %>
<%= text_field_tag(:zip) %><br>
<%= submit_tag "FIND", class: "button"%>
<% end %>
</div>
Again, the issue is that I'm getting an error (Couldn't find Doctor with 'id'=results) because the form is using my show action vs. my results action. The application trace says the error is located at app/controllers/doctors_controller.rb:6:in 'show'. As an added confusion, I don't really understand why it's sending "id"=>"results" as part of the params hash on submit, but it seems like that might be a non-issue if it would use the correct controller action to begin with.
Thanks for any thoughts.
Yes its a priority issue. Since the resources :doctors is first, the GET show will be given the priority than get 'doctors/results' => 'doctors#results'
Moving the get 'doctors/results' => 'doctors#results' above resources :doctors should solve your problem
#routes.rb
root 'doctors#search'
get 'doctors/results' => 'doctors#results'
resources :users
resources :doctors

opa : strange behaviour of dom element update

I have a strange behaviour with the code below:
function update(txt, _)
{
#text = <>{txt}</>
#data = <>{txt}</>
}
command = <a onclick={update("test1", _)}> change text1 </a> <+>
<a onclick={update("test2", _)}> change text2 </a>
content = <textarea style="width:30%;" rows=1 id=#text > filename </textarea>
<textarea style="width:100%;" rows=30 id=#data > This is a text area </textarea>
Server.start(
Server.http,
[
{page: function() {command <+> content}, title: "test" }
]
)
When I clik on the links "change text1" or "change text2", the text is updated in the two textareas, but as soon as I edit the value of one of these textareas, the update failed when I clik on the links.
Why ?
i think this is because once you have edited a textarea, the browser considers the "value" attribute of the textarea, and not the HTML content inside the textarea.
So in order to work, you should :
function update(txt, _)
{
Dom.set_value(#text, txt)
Dom.set_value(#data, txt)
}