-
-
-
{{ 'app.public.projects_show.step_N' | translate:{INDEX:step.step_nb} }} : {{step.title}}
-
-
-
-
-
-
+
+
+
+
+
+
{{ 'app.public.projects_show.project_description' }}
+
+
+
+
+
+
{{ 'app.public.projects_show.step_N' | translate:{INDEX:step.step_nb} }} : {{step.title}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
+
+
+
+
+
{{ 'app.public.projects_show.posted_on_' | translate }} {{project.created_at | amDateFormat: 'LL'}}
+
+
+
+ {{theme.name}}
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
diff --git a/app/models/project_step.rb b/app/models/project_step.rb
index 0324c54f2..b9b76a248 100644
--- a/app/models/project_step.rb
+++ b/app/models/project_step.rb
@@ -5,4 +5,6 @@ class ProjectStep < ApplicationRecord
belongs_to :project, touch: true
has_many :project_step_images, as: :viewable, dependent: :destroy
accepts_nested_attributes_for :project_step_images, allow_destroy: true, reject_if: :all_blank
+
+ default_scope -> { order(:step_nb) }
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 8daad8143..8da49c747 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -16,10 +16,14 @@ class ProjectPolicy < ApplicationPolicy
end
def update?
- user.admin? or record.author.user_id == user.id or record.users.include?(user)
+ user.admin? || record.author.user_id == user.id || record.users.include?(user)
+ end
+
+ def markdown?
+ user.admin? || user.manager? || record.author.user_id == user.id || record.users.include?(user)
end
def destroy?
- user.admin? or record.author.user_id == user.id
+ user.admin? || record.author.user_id == user.id
end
end
diff --git a/app/services/project_service.rb b/app/services/project_service.rb
index a0d0bdb8a..0e6abf32b 100644
--- a/app/services/project_service.rb
+++ b/app/services/project_service.rb
@@ -2,17 +2,17 @@
# Provides methods for Project
class ProjectService
- def search(params, current_user)
+ def search(params, current_user, paginate: true)
connection = ActiveRecord::Base.connection
return { error: 'invalid adapter' } unless connection.instance_values['config'][:adapter] == 'postgresql'
- search_from_postgre(params, current_user)
+ search_from_postgre(params, current_user, paginate: paginate)
end
private
- def search_from_postgre(params, current_user)
- query_params = JSON.parse(params[:search])
+ def search_from_postgre(params, current_user, paginate: true)
+ query_params = JSON.parse(params[:search] || "{}")
records = Project.published_or_drafts(current_user&.statistic_profile&.id)
records = Project.user_projects(current_user&.statistic_profile&.id) if query_params['from'] == 'mine'
@@ -44,6 +44,9 @@ class ProjectService
records.order(created_at: :desc)
end
- { total: records.count, projects: records.includes(:users, :project_image).page(params[:page]) }
+ records = records.includes(:users, :project_image)
+ records = records.page(params[:page]) if paginate
+
+ { total: records.count, projects: records }
end
end
diff --git a/app/services/project_to_markdown.rb b/app/services/project_to_markdown.rb
new file mode 100644
index 000000000..7f3521ff0
--- /dev/null
+++ b/app/services/project_to_markdown.rb
@@ -0,0 +1,88 @@
+class ProjectToMarkdown
+ attr_reader :project
+
+ def initialize(project)
+ @project = project
+ end
+
+ def call
+ md = []
+
+ md << "# #{project.name}"
+
+ md << "![#{I18n.t('app.shared.project.illustration')}](#{full_url(project.project_image.attachment.url)})" if project.project_image
+
+ md << ReverseMarkdown.convert(project.description.to_s)
+
+ project_steps = project.project_steps
+
+ if project_steps.present?
+ md << "## #{I18n.t('app.shared.project.steps')}"
+
+ project_steps.each do |project_step|
+ md << "### #{I18n.t('app.shared.project.step_N').gsub('{INDEX}', project_step.step_nb.to_s)} : #{project_step.title}"
+ md << ReverseMarkdown.convert(project_step.description.to_s)
+
+ project_step.project_step_images.each_with_index do |image, i|
+ md << "![#{I18n.t('app.shared.project.step_image')} #{i+1}](#{full_url(project.project_image.attachment.url)})"
+ end
+ end
+ end
+
+ md << "## #{I18n.t('app.shared.project.author')}"
+ md << project.author&.user&.profile&.full_name
+
+ if project.themes.present?
+ md << "## #{I18n.t('app.shared.project.themes')}"
+ md << project.themes.map(&:name).join(', ')
+ end
+
+ if project.project_caos.present?
+ md << "## #{I18n.t('app.shared.project.CAD_files')}"
+ project.project_caos.each do |cao|
+ md << "![#{cao.attachment_identifier}](#{full_url(cao.attachment_url)})"
+ end
+ end
+
+ if project.status
+ md << "## #{I18n.t('app.shared.project.status')}"
+ md << project.status.name
+ end
+
+ if project.machines.present?
+ md << "## #{I18n.t('app.shared.project.employed_machines')}"
+ md << project.machines.map(&:name).join(', ')
+ end
+
+ if project.components.present?
+ md << "## #{I18n.t('app.shared.project.employed_materials')}"
+ md << project.components.map(&:name).join(', ')
+ end
+
+ if project.users.present?
+ md << "## #{I18n.t('app.shared.project.collaborators')}"
+ md << project.users.map { |u| u.profile.full_name }.join(', ')
+ end
+
+ if project.licence.present?
+ md << "## #{I18n.t('app.shared.project.licence')}"
+ md << project.licence.name
+ end
+
+ if project.tags.present?
+ md << "## #{I18n.t('app.shared.project.tags')}"
+ md << project.tags
+ end
+
+
+ md = md.reject { |line| line.blank? }
+
+ md.join("\n\n")
+ end
+
+ private
+
+ def full_url(path)
+ "#{Rails.application.routes.url_helpers.root_url[...-1]}#{path}"
+ end
+end
\ No newline at end of file
diff --git a/app/services/projects_archive.rb b/app/services/projects_archive.rb
new file mode 100644
index 000000000..009ea661f
--- /dev/null
+++ b/app/services/projects_archive.rb
@@ -0,0 +1,22 @@
+class ProjectsArchive
+ attr_reader :projects
+
+ def initialize(projects)
+ @projects = projects
+ end
+
+ def call
+ stringio = Zip::OutputStream.write_buffer do |zio|
+ projects.includes(:project_image, :themes,
+ :project_caos, :status, :machines,
+ :components, :licence,
+ project_steps: :project_step_images,
+ author: { user: :profile },
+ users: :profile).find_each do |project|
+ zio.put_next_entry("#{project.name.parameterize}-#{project.id}.md")
+ zio.write ProjectToMarkdown.new(project).call
+ end
+ end
+ stringio.string
+ end
+end
\ No newline at end of file
diff --git a/config/locales/app.public.en.yml b/config/locales/app.public.en.yml
index 4afd8a93c..c90cbf0de 100644
--- a/config/locales/app.public.en.yml
+++ b/config/locales/app.public.en.yml
@@ -187,6 +187,7 @@ en:
filter_by_member: "Filter by member"
created_from: Created from
created_to: Created to
+ download_archive: Download
status_filter:
all_statuses: "All statuses"
select_status: "Select a status"
@@ -220,6 +221,7 @@ en:
report: "Report"
do_you_really_want_to_delete_this_project: "Do you really want to delete this project?"
status: "Status"
+ markdown_file: "Markdown file"
#list of machines
machines_list:
the_fablab_s_machines: "The machines"
diff --git a/config/locales/app.public.fr.yml b/config/locales/app.public.fr.yml
index 9fe6efb2e..bff82e15c 100644
--- a/config/locales/app.public.fr.yml
+++ b/config/locales/app.public.fr.yml
@@ -187,6 +187,7 @@ fr:
filter_by_member: "Filter par membre"
created_from: Créés à partir du
created_to: Créés jusqu'au
+ download_archive: Télécharger
status_filter:
all_statuses: "Tous les statuts"
select_status: "Sélectionnez un statut"
@@ -220,6 +221,7 @@ fr:
report: "Signaler"
do_you_really_want_to_delete_this_project: "Êtes-vous sur de vouloir supprimer ce projet ?"
status: "Statut"
+ markdown_file: "Fichier Markdown"
#list of machines
machines_list:
the_fablab_s_machines: "Les machines"
diff --git a/config/locales/app.shared.en.yml b/config/locales/app.shared.en.yml
index 423e3a6f9..bfe88a627 100644
--- a/config/locales/app.shared.en.yml
+++ b/config/locales/app.shared.en.yml
@@ -131,6 +131,7 @@ en:
illustration: "Visual"
add_an_illustration: "Add an illustration"
CAD_file: "CAD file"
+ CAD_files: "CAD files"
allowed_extensions: "Allowed extensions:"
add_a_new_file: "Add a new file"
description: "Description"
@@ -138,6 +139,7 @@ en:
steps: "Steps"
step_N: "Step {INDEX}"
step_title: "Step title"
+ step_image: "Image"
add_a_picture: "Add a picture"
change_the_picture: "Change the picture"
delete_the_step: "Delete the step"
@@ -149,7 +151,9 @@ en:
employed_materials: "Employed materials"
employed_machines: "Employed machines"
collaborators: "Collaborators"
+ author: Author
creative_commons_licences: "Creative Commons licences"
+ licence: "Licence"
themes: "Themes"
tags: "Tags"
save_as_draft: "Save as draft"
diff --git a/config/locales/app.shared.fr.yml b/config/locales/app.shared.fr.yml
index e03e762b3..2c655d255 100644
--- a/config/locales/app.shared.fr.yml
+++ b/config/locales/app.shared.fr.yml
@@ -131,6 +131,7 @@ fr:
illustration: "Illustration"
add_an_illustration: "Ajouter un visuel"
CAD_file: "Fichier CAO"
+ CAD_files: "Fichiers CAO"
allowed_extensions: "Extensions autorisées :"
add_a_new_file: "Ajouter un nouveau fichier"
description: "Description"
@@ -138,6 +139,7 @@ fr:
steps: "Étapes"
step_N: "Étape {INDEX}"
step_title: "Titre de l'étape"
+ step_image: "Image"
add_a_picture: "Ajouter une image"
change_the_picture: "Modifier l'image"
delete_the_step: "Supprimer l'étape"
@@ -149,7 +151,9 @@ fr:
employed_materials: "Matériaux utilisés"
employed_machines: "Machines utilisées"
collaborators: "Les collaborateurs"
+ author: Auteur
creative_commons_licences: "Licences Creative Commons"
+ licence: "Licence"
themes: "Thématiques"
tags: "Étiquettes"
save_as_draft: "Enregistrer comme brouillon"
diff --git a/config/routes.rb b/config/routes.rb
index a813bcc5f..f4ce0b5af 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -38,6 +38,7 @@ Rails.application.routes.draw do
get :last_published
get :search
end
+ get :markdown, on: :member
end
resources :openlab_projects, only: :index
resources :machines
diff --git a/test/fixtures/project_steps.yml b/test/fixtures/project_steps.yml
index cc2516cba..96d44b4fb 100644
--- a/test/fixtures/project_steps.yml
+++ b/test/fixtures/project_steps.yml
@@ -7,6 +7,7 @@ project_step_1:
created_at: 2016-04-04 15:39:08.259759000 Z
updated_at: 2016-04-04 15:39:08.259759000 Z
title: Le manche
+ step_nb: 1
project_step_2:
id: 2
@@ -16,3 +17,4 @@ project_step_2:
created_at: 2016-04-04 15:39:08.265840000 Z
updated_at: 2016-04-04 15:39:08.265840000 Z
title: La presse
+ step_nb: 2
\ No newline at end of file
diff --git a/test/fixtures/projects.yml b/test/fixtures/projects.yml
index 07e635e93..cec0d59b7 100644
--- a/test/fixtures/projects.yml
+++ b/test/fixtures/projects.yml
@@ -12,3 +12,4 @@ project_1:
state: published
slug: presse-puree
published_at: 2016-04-04 15:39:08.267614000 Z
+ status_id: 1
\ No newline at end of file
diff --git a/test/integration/projects_test.rb b/test/integration/projects_test.rb
new file mode 100644
index 000000000..60477000f
--- /dev/null
+++ b/test/integration/projects_test.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class ProjectsTest < ActionDispatch::IntegrationTest
+ def setup
+ @admin = User.find_by(username: 'admin')
+ login_as(@admin, scope: :user)
+ end
+
+ test 'download markdown file' do
+ get "/api/projects/1/markdown"
+
+ assert_response :success
+ assert_equal "text/markdown", response.content_type
+ end
+end
\ No newline at end of file
diff --git a/test/services/project_to_markdown_test.rb b/test/services/project_to_markdown_test.rb
new file mode 100644
index 000000000..388f615f1
--- /dev/null
+++ b/test/services/project_to_markdown_test.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class ProjectToMarkdownTest < ActiveSupport::TestCase
+ test "ProjectToMarkdown is working" do
+ project = projects(:project_1)
+ service = ProjectToMarkdown.new(project)
+
+ markdown_str = nil
+
+ assert_nothing_raised do
+ markdown_str = service.call
+ end
+
+ assert_includes markdown_str, project.name
+ project.project_steps.each do |project_step|
+ assert_includes markdown_str, project_step.title
+ end
+ end
+end