diff --git a/app/assets/javascripts/locomotive/models/page.js.coffee b/app/assets/javascripts/locomotive/models/page.js.coffee index 98197a8c..4ff2cde1 100644 --- a/app/assets/javascripts/locomotive/models/page.js.coffee +++ b/app/assets/javascripts/locomotive/models/page.js.coffee @@ -16,7 +16,7 @@ class Locomotive.Models.Page extends Backbone.Model toJSON: -> _.tap super, (hash) => - _.each ['content_type_id_text', 'edit_url', 'parent_id_text'], (key) => delete hash[key] + _.each ['content_type_id_text', 'edit_url', 'parent_id_text', 'response_type_text'], (key) => delete hash[key] delete hash['editable_elements'] hash.editable_elements = @get('editable_elements').toJSONForSave() if @get('editable_elements')? && @get('editable_elements').length > 0 diff --git a/app/assets/javascripts/locomotive/views/pages/_form_view.js.coffee b/app/assets/javascripts/locomotive/views/pages/_form_view.js.coffee index 87a9063a..241250e4 100644 --- a/app/assets/javascripts/locomotive/views/pages/_form_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/pages/_form_view.js.coffee @@ -37,6 +37,9 @@ class Locomotive.Views.Pages.FormView extends Locomotive.Views.Shared.FormView # the url gets modified by different ways so reflect the changes in the UI @listen_for_url_changes() + # enable response type + @enable_response_type_select() + # enable check boxes @enable_templatized_checkbox() @@ -110,6 +113,14 @@ class Locomotive.Views.Pages.FormView extends Locomotive.Views.Shared.FormView success: (data) => @$('#page_slug_input .inline-hints').html(data.url).effect('highlight') + enable_response_type_select: -> + @$('li#page_response_type_input').change (event) => + if $(event.target).val() == 'text/html' + @$('li#page_redirect_input, li#page_redirect_url_input').show() + else + @model.set redirect: false + @$('li#page_redirect_input, li#page_redirect_url_input').hide() + enable_templatized_checkbox: -> @_enable_checkbox 'templatized', features: ['slug', 'redirect', 'listed'] diff --git a/app/assets/stylesheets/locomotive/backoffice/application.css.scss b/app/assets/stylesheets/locomotive/backoffice/application.css.scss index 9ade96b2..d449f550 100644 --- a/app/assets/stylesheets/locomotive/backoffice/application.css.scss +++ b/app/assets/stylesheets/locomotive/backoffice/application.css.scss @@ -80,6 +80,7 @@ ul.list { span.untranslated { @include label; + background-color: #F89406; top: -1px; left: 5px; } @@ -219,6 +220,13 @@ ul.list { &.hidden a { font-style: italic; font-weight: normal; } span.untranslated { + @include label; + background-color: #F89406; + top: 3px; + left: 5px; + } + + span.response-type { @include label; top: 3px; left: 5px; diff --git a/app/helpers/locomotive/pages_helper.rb b/app/helpers/locomotive/pages_helper.rb index fa9c6f81..c9f7b63a 100644 --- a/app/helpers/locomotive/pages_helper.rb +++ b/app/helpers/locomotive/pages_helper.rb @@ -50,5 +50,18 @@ module Locomotive ] end + def options_for_page_response_type + [ + ['HTML', 'text/html'], + ['RSS', 'application/rss+xml'], + ['XML', 'text/xml'], + ['JSON', 'application/json'] + ] + end + + def page_response_type_to_string(page) + options_for_page_response_type.detect { |t| t.last == page.response_type }.try(:first) || '—' + end + end end \ No newline at end of file diff --git a/app/models/locomotive/page.rb b/app/models/locomotive/page.rb index ca06fada..50dc06e4 100644 --- a/app/models/locomotive/page.rb +++ b/app/models/locomotive/page.rb @@ -22,6 +22,7 @@ module Locomotive field :locales, :type => Array field :published, :type => Boolean, :default => false field :cache_strategy, :default => 'none' + field :response_type, :default => 'text/html' ## associations ## belongs_to :site, :class_name => 'Locomotive::Site' @@ -51,7 +52,7 @@ module Locomotive scope :published, :where => { :published => true } scope :fullpath, lambda { |fullpath| { :where => { :fullpath => fullpath } } } scope :handle, lambda { |handle| { :where => { :handle => handle } } } - scope :minimal_attributes, lambda { |attrs = []| { :only => (attrs || []) + %w(title slug fullpath position depth published templatized redirect listed parent_id site_id created_at updated_at) } } + scope :minimal_attributes, lambda { |attrs = []| { :only => (attrs || []) + %w(title slug fullpath position depth published templatized redirect listed response_type parent_id site_id created_at updated_at) } } scope :dependent_from, lambda { |id| { :where => { :template_dependencies.in => [id] } } } ## methods ## @@ -72,6 +73,10 @@ module Locomotive self.cache_strategy != 'none' end + def default_response_type? + self.response_type == 'text/html' + end + def translated? self.title_translations.key?(::Mongoid::Fields::I18n.locale.to_s) rescue false end diff --git a/app/presenters/locomotive/page_presenter.rb b/app/presenters/locomotive/page_presenter.rb index b865f97e..62a7798b 100644 --- a/app/presenters/locomotive/page_presenter.rb +++ b/app/presenters/locomotive/page_presenter.rb @@ -1,7 +1,7 @@ module Locomotive class PagePresenter < BasePresenter - delegate :title, :slug, :fullpath, :handle, :raw_template, :published, :listed, :templatized, :redirect, :redirect_url, :template_changed, :cache_strategy, :to => :source + delegate :title, :slug, :fullpath, :handle, :raw_template, :published, :listed, :templatized, :redirect, :redirect_url, :template_changed, :cache_strategy, :response_type, :to => :source def escaped_raw_template h(self.source.raw_template) @@ -12,7 +12,7 @@ module Locomotive end def included_methods - super + %w(title slug fullpath handle raw_template published listed templatized redirect redirect_url cache_strategy template_changed editable_elements localized_fullpaths) + super + %w(title slug fullpath handle raw_template published listed templatized redirect redirect_url cache_strategy response_type template_changed editable_elements localized_fullpaths) end def localized_fullpaths diff --git a/app/views/locomotive/pages/_form.html.haml b/app/views/locomotive/pages/_form.html.haml index dda9c99e..69800585 100644 --- a/app/views/locomotive/pages/_form.html.haml +++ b/app/views/locomotive/pages/_form.html.haml @@ -32,6 +32,8 @@ = f.input :handle + = f.input :response_type, :as => :select, :collection => options_for_page_response_type, :include_blank => false + = f.input :templatized, :as => :'Locomotive::Toggle', :style => "#{'display: none' if @page.redirect?}" = f.input :target_klass_name, :as => :select, :collection => options_for_target_klass_name, :include_blank => false, :wrapper_html => { :style => "#{'display: none' unless @page.templatized?}" } @@ -40,7 +42,7 @@ = f.input :listed, :as => :'Locomotive::Toggle' - = f.input :redirect, :as => :'Locomotive::Toggle', :style => "#{'display: none' if @page.templatized?}" + = f.input :redirect, :as => :'Locomotive::Toggle', :wrapper_html => { :style => "#{'display: none' if @page.templatized? || !@page.default_response_type?}" } = f.input :cache_strategy, :as => :select, :collection => options_for_page_cache_strategy, :include_blank => false, :wrapper_html => { :style => "#{'display: none' if @page.redirect?}" } diff --git a/app/views/locomotive/pages/_page.html.haml b/app/views/locomotive/pages/_page.html.haml index ba21b3d7..f82219c3 100644 --- a/app/views/locomotive/pages/_page.html.haml +++ b/app/views/locomotive/pages/_page.html.haml @@ -17,6 +17,9 @@ = link_to truncate(page.title, :length => 80), edit_page_url(page) + - unless page.default_response_type? + %span.response-type= page_response_type_to_string(page) + - unless page.translated? %span.untranslated= t('locomotive.shared.list.untranslated') diff --git a/config/locales/admin_ui.fr.yml b/config/locales/admin_ui.fr.yml index b4473a7b..7f10846a 100644 --- a/config/locales/admin_ui.fr.yml +++ b/config/locales/admin_ui.fr.yml @@ -77,10 +77,17 @@ fr: file: delete_file: Supprimer fichier has_many: - empty: Vide + empty: La liste est vide + new_entry: + Ajouter une nouvelle entrée + many_to_many: + empty: La liste est vide. Ajouter une entrée depuis la liste ci-dessous. + form: - is_required: est obligatoire - default_label: Nom du champ + required: Required + optional: Optional + default_label: Field name + select_options: + ask_name: "Type the label of the option" sessions: new: @@ -149,6 +156,8 @@ fr: new: title: "Nouveau site" help: "Remplissez le formulaire ci-dessous pour créer votre nouveau site." + domains: + empty: Aucun domaine current_site: edit: @@ -187,6 +196,7 @@ fr: media: Média fonts: Polices no_items: "Il n'existe pas de fichiers." + quick_upload: "Upload rapide" asset: updated_at: Mis à jour le new: @@ -216,6 +226,7 @@ fr: content_types: index: new: nouveau modèle + edit: éditer new: title: Nouveau modèle help: "Créer votre propre modèle de données (Projets, Personnes, ...etc). Votre modèle doit au moins comporter un champ. Le premier champ sera obligatoire lorsque vous ajouterez un élément de ce type-là." diff --git a/config/locales/default.fr.yml b/config/locales/default.fr.yml index f8b0d257..45d043ae 100644 --- a/config/locales/default.fr.yml +++ b/config/locales/default.fr.yml @@ -52,7 +52,7 @@ fr: mongoid: attributes: - page: + locomotive/page: title: Titre parent: Dossier parent parent_id: Dossier parent @@ -63,42 +63,49 @@ fr: redirect: Redirection redirect_url: Url de redirection cache_strategy: Cache + response_type: Type de réponse seo_title: Titre SEO - content_type: + locomotive/content_type: name: Nom description: Description slug: Raccourci order_by: Ordonner - highlighted_field_name: Champ mis en avant + order_direction: Direction + label_field_name: Champ mis en avant + label_field_id: Champ mis en avant group_by_field_name: Champ pour grouper - api_enabled: Activation API - asset: + group_by_field_id: Champ pour grouper + public_submission_enabled: Activation API + public_submission_accounts: Comptes à notifier + locomotive/asset: source: Fichier - account: + locomotive/account: email: E-mail name: Nom language: Langue password: Mot de passe password_confirmation: Confirmation mot de passe - snippet: + locomotive/snippet: body: Code slug: Raccourci name: Nom - theme_asset: + locomotive/theme_asset: content_type: Type du fichier - site: + locomotive/site: name: Nom du site + locales: Langues domain_name: Domaine subdomain: Sous-domaine restricted_access: Activer ? access_login: Identifiant access_password: "Mot de passe" - custom_fields: - field: - name: Alias - hint: Aide - required: Requis ? - text_formatting: Formattage + custom_fields/field: + name: Alias + hint: Aide + required: Requis ? + text_formatting: Formattage + localized: Localisé ? + select_options: Options pagination: previous: "« Précédent" diff --git a/config/locales/formtastic.en.yml b/config/locales/formtastic.en.yml index 3fa4c3c1..c76da942 100644 --- a/config/locales/formtastic.en.yml +++ b/config/locales/formtastic.en.yml @@ -33,12 +33,6 @@ en: custom_fields: field: name: Alias - import: - new: - source: File - samples: Copy samples - reset: Reset site - default_site_template: "Use the default site template. Click here to upload a site template as a zip file instead." content_type: raw_item_template: Item template public_submission_enabled: Public submission diff --git a/config/locales/formtastic.fr.yml b/config/locales/formtastic.fr.yml index eb76028a..2a872895 100644 --- a/config/locales/formtastic.fr.yml +++ b/config/locales/formtastic.fr.yml @@ -35,13 +35,8 @@ fr: custom_fields: field: name: Alias - import: - new: - source: Fichier - samples: Copier contenu - reset: Remettre à zéro content_type: - item_template: Template d'affichage + raw_item_template: Template d'affichage api_accounts: Comptes à notifier content_entry: _slug: Permalink @@ -51,6 +46,7 @@ fr: password: Nouveau mot de passe password_confirmation: Confirmation nouveau mot de passe page: + target_klass_name: Modèle seo_title: Titre hints: @@ -59,13 +55,14 @@ fr: cache_strategy: "Cache la page pour de meilleure performance. L'option 'Simple' est le meilleur compromis." templatized: "Utilise la page comme un template pour un modèle défini." listed: "Controle si la page doit être visible depuis les menus automatiquement générés." - content_type_id: "Le type du contenu pour lequel cette page est un template." + target_klass_name: "Le type du contenu pour lequel cette page est un template." seo_title: "Définit un titre de page à mettre dans la balise TITLE de la page. Laissez le blanc pour utiliser la valeur par défaut (voir configuration du site)." meta_keywords: "Redéfinit les mots-clés du site. Utilisés à l'intérieur de la balise HEAD. Ils sont séparés par une virgule." meta_description: "Redéfinit la description du site. Utilisée à l'intérieur de la balise HEAD." snippet: slug: "Utilisé pour insérer le snippet dans une page." site: + seo_title: "Valeur globale qui sera utilisée par défaut dans la balise TITLE de la section HEAD. Requis pour un meilleur référencement." meta_keywords: "Mots-clés utilisés à l'intérieur de la balise HEAD. Ils sont séparés par une virgule. Requis pour un meilleur référencement." meta_description: "Description utilisée à l'intérieur de la balise HEAD. Requis pour un meilleur référencement." domain_name: "ex: locomotiveapp.org" @@ -88,12 +85,8 @@ fr: seo_title: "La valeur que vous rentrez sera utilisée comme titre SEO pour la page faisant office de template pour ce modèle." meta_keywords: "Redéfinit les mots-clés du site. Utilisés à l'intérieur de la balise HEAD. Ils sont séparés par une virgule." meta_description: "Redéfinit la description du site. Utilisée à l'intérieur de la balise HEAD." - import: - source: "Un fichier zip contenant database.yml, les fichiers du thème et les templates de page" - samples: "Si activé, les contenus et les média seront aussi copiés lors de l'import" - reset: "Si activé, toutes les données de votre site seront détruites avant l'import du nouveau site" content_type: slug: Nom utilisé dans les templates liquid afin d'accéder aux enregistrements de ce modèle - item_template: "Personnaliser le texte affiché pour chaque élément de la liste. Utilisez simplement du code Liquid. Ex: {{ entry.name }}" - api_enabled: "Utilisé pour autoriser la création de nouvelles instances de l'extérieur (ex.: les messages dans un formulaire de contact)" - api_accounts: "Un email de notification sera envoyé à chaque compte listé ci-dessus lors de la création d'une nouvelle instance" + raw_item_template: "Personnaliser le texte affiché pour chaque élément de la liste. Utilisez simplement du code Liquid. Ex: {{ entry.name }}" + public_submission_enabled: "Utilisé pour autoriser la création de nouvelles instances de l'extérieur (ex.: les messages dans un formulaire de contact)" + public_submission_accounts: "Un email de notification sera envoyé à chaque compte listé ci-dessus lors de la création d'une nouvelle instance" diff --git a/lib/locomotive/render.rb b/lib/locomotive/render.rb index b9a42b42..04134cc6 100644 --- a/lib/locomotive/render.rb +++ b/lib/locomotive/render.rb @@ -26,20 +26,25 @@ module Locomotive end def locomotive_page + page = nil path = self.locomotive_page_path - if page = current_site.pages.any_in(:fullpath => [*path]).first - if not page.published? and current_locomotive_account.nil? - page = nil + current_site.pages.any_in(:fullpath => [*path]).each do |_page| + if not _page.published? and current_locomotive_account.nil? + next else - if page.templatized? - @content_entry = page.fetch_target_entry(File.basename(path.first)) + if _page.templatized? + @content_entry = _page.fetch_target_entry(File.basename(path.first)) if @content_entry.nil? || (!@content_entry.visible? && current_locomotive_account.nil?) # content instance not found or not visible - page = nil + next end end end + + page = _page + + break end page || not_found_page @@ -101,7 +106,7 @@ module Locomotive def prepare_and_set_response(output) flash.discard - response.headers['Content-Type'] = 'text/html; charset=utf-8' + response.headers['Content-Type'] = "#{@page.response_type}; charset=utf-8" response.headers['Editable'] = 'true' unless self.editing_page? || current_locomotive_account.nil? if @page.with_cache? diff --git a/spec/lib/locomotive/render_spec.rb b/spec/lib/locomotive/render_spec.rb index 1fd4b924..099700d8 100644 --- a/spec/lib/locomotive/render_spec.rb +++ b/spec/lib/locomotive/render_spec.rb @@ -37,6 +37,12 @@ describe 'Locomotive rendering system' do @controller.response.headers['Content-Type'].should == 'text/html; charset=utf-8' end + it 'sets the content type to the one specified by the page' do + @page.response_type = 'application/json' + @controller.send(:prepare_and_set_response, 'Hello world !') + @controller.response.headers['Content-Type'].should == 'application/json; charset=utf-8' + end + it 'sets the status to 200 OK' do @controller.status.should == :ok end diff --git a/spec/models/locomotive/page_spec.rb b/spec/models/locomotive/page_spec.rb index 61b74544..b62be49b 100644 --- a/spec/models/locomotive/page_spec.rb +++ b/spec/models/locomotive/page_spec.rb @@ -307,6 +307,24 @@ describe Locomotive::Page do end end + describe 'response type' do + + before(:each) do + @page = FactoryGirl.build(:page, :site => nil) + end + + it 'is a HTML document by default' do + @page.response_type.should == 'text/html' + @page.default_response_type?.should be_true + end + + it 'can also be a JSON document' do + @page.response_type = 'application/json' + @page.default_response_type?.should be_false + end + + end + class Foo end