initial checkn of redmine_cms tarball from RedmineUP

supposedly this is version 1.2.1.
This commit is contained in:
Harald Welte 2022-03-29 17:58:36 +02:00
commit 1d839e9bc2
775 changed files with 158754 additions and 0 deletions

26
.drone.jsonnet Normal file
View File

@ -0,0 +1,26 @@
local Pipeline(rubyVer, license, db, redmine) = {
kind: "pipeline",
name: rubyVer + "-" + db + "-" + redmine + "-" + license,
steps: [
{
name: "tests",
image: "redmineup/redmineup_ci",
commands: [
"service postgresql start && service mysql start && sleep 5",
"export PATH=~/.rbenv/shims:$PATH",
"export CODEPATH=`pwd`",
"/root/run_for.sh redmine_cms ruby-" + rubyVer + " " + db + " redmine-" + redmine
]
}
]
};
[
Pipeline("2.7.3", "pro", "mysql", "trunk"),
Pipeline("2.7.3", "pro", "pg", "trunk"),
Pipeline("2.4.1", "pro", "mysql", "4.1"),
Pipeline("2.4.1", "pro", "mysql", "4.0"),
Pipeline("2.4.1", "pro", "mysql", "3.4"),
Pipeline("2.2.6", "pro", "mysql", "3.3"),
Pipeline("2.2.6", "pro", "pg", "3.3")
]

2
Gemfile Normal file
View File

@ -0,0 +1,2 @@
gem "sass", "~> 3.4.22"
gem "redmine_crm"

View File

@ -0,0 +1,181 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsAssetsController < ApplicationController
unloadable
before_action :authorize_edit, :except => [:download, :thumbnail, :timelink]
before_action :find_attachment, :only => [:download, :thumbnail, :timelink, :destroy, :show]
before_action :find_attachments, :only => [:edit, :update]
helper :attachments
helper :cms
helper :sort
include SortHelper
def index
sort_init 'filename', 'asc'
sort_update 'filename' => "#{Attachment.table_name}.filename",
'description' => "#{Attachment.table_name}.description",
'created_on' => "#{Attachment.table_name}.created_on",
'size' => "#{Attachment.table_name}.filesize"
@attachments = CmsSite.instance.attachments.reorder(sort_clause)
end
def new
end
def show
if @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte
@content = File.new(@attachment.diskfile, "rb").read
render :template => 'attachments/file'
elsif @attachment.is_image?
render :template => 'attachments/image'
else
render :template => 'attachments/other'
end
end
def create
container = CmsSite.instance
attachments = params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash['attachments'] : params['attachments']
attachments.each { |a| a[1][:container_type] = CmsSite.name; a[1][:container_id] = 1 } if attachments
saved_attachments = CmsSite.instance.save_attachments(attachments, User.current)
saved_attachments[:files].each { |a| a.update(:container_id => 1, :container_type => CmsSite.name) } if saved_attachments
render_attachment_warning_if_needed(container)
if saved_attachments[:files].present?
flash[:notice] = l(:label_file_added)
redirect_to cms_assets_path
else
flash.now[:error] = l(:label_attachment) + ' ' + l('activerecord.errors.messages.invalid')
index
render :action => 'index'
end
end
def destroy
@attachment.destroy
respond_to do |format|
format.html { redirect_to_referer_or cms_assets_path }
format.js
end
end
def download
if (@attachment.container_type == CmsSite.name) || @attachment.visible?
# fresh_when(:etag => @attachment.digest, :last_modified => @attachment.update_on.utc, :public => true)
expires_in RedmineCms.cache_expires_in.minutes, :public => true, :private => false
if stale?(:etag => @attachment.digest, :public => true, :template => false)
# images are sent inline
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
:type => detect_content_type(@attachment),
:disposition => (@attachment.image? ? 'inline' : 'attachment')
end
else
deny_access
end
end
def edit
end
def update
if params[:attachments].is_a?(Hash)
if Attachment.update_attachments(@attachments, params[:attachments])
redirect_back_or_default home_path
return
end
end
render :action => 'edit'
end
def thumbnail
if (@attachment.token_auth(params[:token]) || @attachment.container_type == CmsSite.name || @attachment.visible?) && @attachment.thumbnailable? && tbnail = RedmineCms::Thumbnail.generate(@attachment, params)
if stale?(:etag => tbnail, :public => true, :template => false)
send_file tbnail,
:filename => filename_for_content_disposition(@attachment.filename),
:type => detect_content_type(@attachment),
:disposition => 'inline'
else
expires_in RedmineCms.cache_expires_in.minutes, :public => true, :private => false
end
else
# No thumbnail for the attachment or thumbnail could not be created
head :not_found
end
end
def timelink
if RedmineCms::Cryptor.check_timestamp(@attachment.id, params[:timestamp], params[:key])
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
:type => detect_content_type(@attachment),
:disposition => (@attachment.image? ? 'inline' : 'attachment')
else
deny_access
end
end
private
def find_layout
@cms_layout = CmsLayout.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
def authorize_edit
deny_access unless RedmineCms.allow_edit?
end
def find_attachment
@attachment = Attachment.find(params[:id])
# Show 404 if the filename in the url is wrong
raise ActiveRecord::RecordNotFound if params[:filename] && params[:filename] != @attachment.filename
rescue ActiveRecord::RecordNotFound
render_404
end
def find_attachments
@attachments = Attachment.where(:container_type => CmsSite.name).where(:id => params[:id] || params[:ids]).to_a
raise ActiveRecord::RecordNotFound if @attachments.empty?
rescue ActiveRecord::RecordNotFound
render_404
end
def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
if unsaved_time_entry_ids.empty?
flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
else
flash[:error] = l(:notice_failed_to_save_time_entries,
:count => unsaved_time_entry_ids.size,
:total => time_entries.size,
:ids => '#' + unsaved_time_entry_ids.join(', #'))
end
end
def detect_content_type(attachment)
content_type = attachment.content_type
if content_type.blank? || content_type == "application/octet-stream"
content_type = Redmine::MimeType.of(attachment.filename)
end
content_type.to_s
end
end

View File

@ -0,0 +1,56 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsExportsController < ApplicationController
unloadable
before_action :authorize_edit
before_action :find_cms_object, except: [:website]
before_action :build_export_object, :only => [:new, :create]
def create
@cms_export.configure(params[:cms_export])
send_data(@cms_export.export, :type => 'text/yaml; header=present', :filename => [@cms_object.name, 'yaml'].join('.'))
end
def website
if request.post?
@cms_export = CmsExport.new(params[:object_type])
@cms_export.configure(params)
send_data(@cms_export.export_website, :type => 'text/yaml; header=present', :filename => ['website', 'yaml'].join('.'))
end
end
private
def find_cms_object
raise ActiveRecord::RecordNotFound unless params[:object_type]
klass = params[:object_type].to_s.singularize.classify.constantize
@cms_object = klass.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
def build_export_object
@cms_export = CmsExport.new(@cms_object)
end
def authorize_edit
deny_access unless RedmineCms.allow_edit?
end
end

View File

@ -0,0 +1,66 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsHistoryController < ApplicationController
unloadable
before_action :authorize_edit
before_action :find_cms_object
helper :cms
def history
@versions = @cms_object.versions
@version_count = @versions.count
end
def diff
@diff = @cms_object.diff(params[:version], params[:version_from])
render_404 unless @diff
end
def annotate
@annotate = @cms_object.annotate(params[:version])
render_404 unless @annotate
end
private
def find_cms_object
klass = params[:object_type].to_s.singularize.classify.constantize rescue nil
@cms_object = klass.find_by_id(params[:id])
if @cms_object.blank? || !@cms_object.respond_to?(:versions)
render_404
end
end
def authorize_edit
deny_access unless RedmineCms.allow_edit?
end
def set_content_from_version
return if !@page
@version = @page.versions.where(:version => params[:version]).first
if @version
@current_version = @page.version
@page.content = @version.content
@page.version = @version.version
@page.is_cached = false
end
end
end

View File

@ -0,0 +1,50 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsImportsController < ApplicationController
unloadable
before_action :authorize_edit
before_action :build_import_object, :only => [:new, :create]
def create
@cms_import.configure(params[:cms_import])
@cms_object = @cms_import.import
end
def website
if request.post?
@cms_import = CmsImport.new(params[:object_type])
@cms_import.author = User.current
@cms_import.configure(params)
@cms_import.import_website
return redirect_to cms_settings_path
end
end
private
def build_import_object
@cms_import = CmsImport.new(params[:object_type].to_s.singularize.classify.constantize.new)
@cms_import.author = User.current
end
def authorize_edit
deny_access unless RedmineCms.allow_edit?
end
end

View File

@ -0,0 +1,104 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsLayoutsController < ApplicationController
unloadable
before_action :authorize_edit
before_action :find_layout, :except => [:index, :new, :create]
helper :attachments
helper :cms
def index
@cms_layouts = CmsLayout.order(:name)
end
def new
@cms_layout = CmsLayout.new
@cms_layout.copy_from(params[:copy_from]) if params[:copy_from]
end
def edit
@current_version = @cms_layout.set_content_from_version(params[:version]) if params[:version]
end
def show
@current_version = @cms_layout.set_content_from_version(params[:version]) if params[:version]
@page = CmsPage.new(:content => 'Empty content', :layout => @cms_layout)
render((Rails.version < '5.1' ? :text : :plain) => @page.process(self), :layout => false)
end
def preview
@current_version = @cms_layout.set_content_from_version(params[:version]) if params[:version]
@cms_object = @cms_layout
render :template => 'cms_pages/preview', :layout => 'cms_preview'
end
def update
@cms_layout.safe_attributes = params[:cms_layout]
@cms_layout.save_attachments(params[:attachments])
if @cms_layout.save
render_attachment_warning_if_needed(@cms_layout)
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'edit', :id => @cms_layout
else
render :action => 'edit'
end
end
def create
@cms_layout = CmsLayout.new
@cms_layout.safe_attributes = params[:cms_layout]
@cms_layout.save_attachments(params[:attachments])
if @cms_layout.save
render_attachment_warning_if_needed(@cms_layout)
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'edit', :id => @cms_layout
else
render :action => 'new'
end
end
def destroy
if params[:version]
version = @cms_layout.versions.where(:version => params[:version]).first
if version.current_version?
flash[:warning] = l(:label_cms_version_cannot_destroy_current)
else
version.destroy
end
redirect_to cms_object_history_path(@cms_layout, :object_type => @cms_layout.class.name.underscore)
else
@cms_layout.destroy
redirect_to :controller => 'cms_layouts', :action => 'index'
end
end
private
def find_layout
@cms_layout = CmsLayout.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
def authorize_edit
deny_access unless RedmineCms.allow_edit?
end
end

View File

@ -0,0 +1,87 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsMenusController < ApplicationController
unloadable
before_action :require_edit_permission
before_action :find_menu, :except => [:index, :new, :create, :parent_menu_options]
helper :cms
def index
@cms_menus = CmsMenu.all
end
def edit
end
def new
@cms_menu = CmsMenu.new(:menu_type => 'top_menu')
end
def update
@cms_menu.safe_attributes = params[:menu]
@cms_menu.insert_at(@cms_menu.position - 1) if @cms_menu.position_changed?
if @cms_menu.save
flash[:notice] = l(:notice_successful_update)
@cms_menus = CmsMenu.all
respond_to do |format|
format.html { render :action => 'edit', :id => @cms_menus }
format.js { render :action => 'change' }
end
else
render :action => 'edit'
end
end
def create
@cms_menu = CmsMenu.new
@cms_menu.safe_attributes = params[:menu]
if @cms_menu.save
flash[:notice] = l(:notice_successful_create)
render :action => 'edit', :id => @cms_menu
else
render :action => 'new'
end
end
def destroy
@cms_menu.destroy
redirect_to :controller => 'cms_pages', :action => 'index', :tab => 'cms_menus'
end
def parent_menu_options
unless params[:id].blank?
find_menu
else
@cms_menu = CmsMenu.new(:menu_type => params[:menu_type])
end
end
private
def find_menu
@cms_menu = CmsMenu.find(params[:id])
end
def require_edit_permission
deny_access unless RedmineCms.allow_edit?
end
end

View File

@ -0,0 +1,96 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsPageQueriesController < ApplicationController
before_action :find_query, :except => [:new, :create, :index]
helper :queries
include QueriesHelper
def index
case params[:format]
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
@limit = per_page_option
end
@query_count = CmsPageQuery.visible.count
@query_pages = Paginator.new @query_count, @limit, params['page']
@queries = CmsPageQuery.visible.
order("#{Query.table_name}.name").
limit(@limit).
offset(@offset).
all
respond_to do |format|
format.html
format.api
end
end
def new
@query = CmsPageQuery.new
@query.user = User.current
@query.build_from_params(params)
@query.visibility = PeopleQuery::VISIBILITY_PRIVATE unless User.current.admin?
end
def create
@query = CmsPageQuery.new(params[:query])
@query.user = User.current
@query.build_from_params(params)
@query.visibility = PeopleQuery::VISIBILITY_PRIVATE unless User.current.admin?
if @query.save
flash[:notice] = l(:notice_successful_create)
redirect_to url_for(:controller => 'cms_pages', :action => 'index', :query_id => @query.id)
else
render :action => 'new', :layout => !request.xhr?
end
end
def edit
end
def update
@query.attributes = params[:query]
@query.build_from_params(params)
@query.visibility = PeopleQuery::VISIBILITY_PRIVATE unless User.current.admin?
if @query.save
flash[:notice] = l(:notice_successful_update)
redirect_to url_for(:controller => 'cms_pages', :action => 'index', :query_id => @query.id)
else
render :action => 'edit'
end
end
def destroy
@query.destroy
redirect_to url_for(:controller => 'cms_page_queries', :action => 'index', :set_filter => 1)
end
private
def find_query
@query = CmsPageQuery.find(params[:id])
render_403 unless @query.editable_by?(User.current)
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@ -0,0 +1,239 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsPagesController < ApplicationController
include Redmine::I18n
include CmsPagesHelper
helper :queries
include QueriesHelper
unloadable
before_action :authorize_page_edit, :except => [:show, :search]
before_action :find_page, :only => [:show, :destroy, :preview, :edit, :update, :expire_cache]
before_action :authorize_page, :only => [:show]
before_action :set_locale, :only => [:show]
before_action :require_admin, :only => :destroy
accept_api_auth :index, :show, :create, :update, :destroy
helper :attachments
helper :cms_menus
helper :cms_parts
helper :cms
protect_from_forgery :except => :show
def index
retrieve_pages_query
conditions = {}
if @query.filters && @query.filters.empty? && params[:search].nil?
parent = CmsPage.where(:id => params[:parent_id].delete('page-')).first if params[:parent_id]
conditions[:parent_id] = parent.try(:id)
end
@pages = @query.results_scope(:search => params[:search], :conditions => conditions).preload(:children_pages)
if request.xhr?
@pages.any? ? render(:partial => 'pages_list_content') : render(:nothing => true)
else
render :action => 'index'
end
end
def show
if params[:version]
return false unless authorize_page_edit
@current_version = @page.set_content_from_version(params[:version])
end
# if @page.is_cached?
# expires_in RedmineCms.cache_expires_in.minutes, :public => true, :private => false
# else
# expires_in nil, :private => true, "no-cache" => true
# headers['ETag'] = ''
# end
unless @page.layout.blank?
render((Rails.version < '5.1' ? :text : :plain) => @page.process(self), :layout => false)
end
end
def search
q = (params[:q] || params[:term]).to_s.strip
if params[:page] && page = CmsPage.find_by_name(params[:page])
scope = case params[:children]
when 'leaves'
page.leaves
when 'descendants'
page.descendants
else
page.children
end
else
scope = CmsPage.visible
end
scope = scope.includes(:tags)
scope = scope.limit(params[:limit] || 10)
q.split(' ').collect { |search_string| scope = scope.like(search_string) } if q.present?
scope = scope.order(:title)
if params[:tag]
tags = params[:tag].split(',')
tag_options = case params[:match]
when 'any'
{ :match_all => false }
when 'exclude'
{ :exclude => true }
else
{ :match_all => true }
end
scope = scope.tagged_with(tags, tag_options)
end
@pages = scope
render :json => @pages.map { |page| { 'name' => page.name,
'slug' => page.slug,
'path' => page.path,
'title' => page.title,
'tags' => page.tag_list } }
end
def preview
@current_version = @page.set_content_from_version(params[:version]) if params[:version]
@cms_object = @page
render :action => 'preview', :layout => 'cms_preview'
end
def edit
@current_version = @page.set_content_from_version(params[:version]) if params[:version]
respond_to do |format|
format.html { render :action => 'edit', :layout => use_layout }
end
end
def new
@page = CmsPage.new
@page.page_date = Time.now
@page.safe_attributes = params[:page]
@page.layout_id ||= params[:layout_id] || RedmineCms.default_layout
RedmineCms.default_page_fields.each { |f| @page.fields.build(:name => f) }
@page.copy_from(params[:copy_from]) if params[:copy_from]
respond_to do |format|
format.html { render :action => 'new', :layout => use_layout }
end
end
def update
cms_page_attributes = params[:page]
if cms_page_attributes && params[:conflict_resolution]
case params[:conflict_resolution]
when 'overwrite'
cms_page_attributes = cms_page_attributes.dup
cms_page_attributes.delete(:lock_version)
when 'cancel'
redirect_to edit_cms_page_path(@page, :tab => params[:tab])
return false
end
end
cms_page_attributes[:tag_list] = [] if cms_page_attributes[:tag_list].blank?
@page.safe_attributes = cms_page_attributes
attachments_chanched = @page.save_attachments(params[:attachments])
begin
saved = @page.save
rescue ActiveRecord::StaleObjectError
@conflict = true
@conflict_versions = @page.versions_after(params[:last_version]).to_a unless params[:last_version].blank?
end
if saved
@page.reload.touch if attachments_chanched
render_attachment_warning_if_needed(@page)
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default edit_cms_page_path(@page, :tab => params[:tab])
}
format.js do
# @pages = CmsPage.includes([:layout, :author])
render :action => 'change'
end
end
else
respond_to do |format|
format.html { render :action => 'edit', :tab => params[:tab] }
format.js { render :js => "alert('#{l(:label_cms_save_page_error)}');" }
end
end
end
def create
@page = CmsPage.new
@page.safe_attributes = params[:page]
@page.author = User.current
@page.save_attachments(params[:attachments])
if @page.save
render_attachment_warning_if_needed(@page)
flash[:notice] = l(:notice_successful_create)
redirect_to edit_cms_page_path(@page, :tab => params[:tab])
else
render :action => 'new', :tab => params[:tab]
end
end
def destroy
if params[:version]
version = @page.versions.where(:version => params[:version]).first
if version.current_version?
flash[:warning] = l(:label_cms_version_cannot_destroy_current)
else
version.destroy
end
redirect_to cms_object_history_path(@page)
else
@page.destroy
redirect_to :controller => 'cms_pages', :action => 'index', :tab => 'pages'
end
end
def expire_cache
@page.expire_cache
redirect_to :back
end
private
def authorize_page
deny_access unless @page.visible?
rescue ActiveRecord::RecordNotFound
render_404
end
def find_page
page_scope = CmsPage.includes([:attachments, :parts, :layout])
@page = params[:id] ? page_scope.find_by_name(params[:id]) : page_scope.find_by_path(params[:path])
@parts = @page.parts.order(:position) if @page
render_404 unless @page
end
def authorize_page_edit
unless RedmineCms.allow_edit?
deny_access
return false
end
true
end
end

View File

@ -0,0 +1,146 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsPartsController < ApplicationController
unloadable
before_action :require_edit_permission, :except => [:show]
before_action :find_part, :except => [:index, :new, :create, :show]
before_action :find_page_part, :only => [:show]
before_action :authorize_part, :only => [:show]
before_action :require_admin, :only => :destroy
helper :attachments
helper :cms
helper :cms_pages
protect_from_forgery :except => :show
def index
redirect_to :controller => 'cms_pages', :action => 'index', :tab => 'parts'
end
def preview
@current_version = @part.set_content_from_version(params[:version]) if params[:version]
@page.listener = self
@preview_content = @page.render_part(@part)
end
def show
@page.listener = self
render((Rails.version < '5.1' ? :text : :plain) => @page.render_part(@part), :layout => false)
end
def edit
@current_version = @part.set_content_from_version(params[:version]) if params[:version]
end
def expire_cache
Rails.cache.delete(@part)
redirect_to :back
end
def new
@part = CmsPart.new(:filter_id => 'textile', :page_id => params[:page_id])
@part.copy_from(params[:copy_from]) if params[:copy_from]
end
def refresh
expire_fragment(@part)
end
def update
@part.safe_attributes = params[:part]
@part.save_attachments(params[:attachments])
if @part.save
render_attachment_warning_if_needed(@part)
flash[:notice] = l(:notice_successful_update)
respond_to do |format|
format.html do
redirect_back_or_default edit_cms_part_path(@part)
end
format.js do
find_parts
render :action => 'change'
end
end
else
render :action => 'edit'
end
end
def create
@part = CmsPart.new
@part.safe_attributes = params[:part]
@part.save_attachments(params[:attachments])
if @part.save
render_attachment_warning_if_needed(@part)
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'edit', :id => @part
else
render :action => 'new'
end
end
def destroy
if params[:version]
version = @part.versions.where(:version => params[:version]).first
if version.current_version?
flash[:warning] = l(:label_cms_version_cannot_destroy_current)
else
version.destroy
end
redirect_to history_cms_part_path(@page)
else
@part.destroy
redirect_to :controller => 'cms_pages', :action => 'edit', :tab => 'page_parts', :id => @page
end
end
private
def authorize_part
deny_access unless @part.page.visible?
rescue ActiveRecord::RecordNotFound
render_404
end
def find_parts
@page = @part.page
@parts = @page.parts.order(:position)
end
def find_page_part
@part = CmsPart.active.joins(:page).includes(:page => :attachments).where(:page_id => params[:page_id]).find_part(params[:id] || params[:name])
raise ActiveRecord::RecordNotFound if @part.blank?
@page = @part.page
rescue ActiveRecord::RecordNotFound
render_404
end
def find_part
@part = CmsPart.includes(:page).find(params[:id])
@page = @part.page
rescue ActiveRecord::RecordNotFound
render_404
end
def require_edit_permission
deny_access unless RedmineCms.allow_edit?
end
end

View File

@ -0,0 +1,76 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsRedirectsController < ApplicationController
unloadable
before_action :require_admin
before_action :find_redirect, :except => [:index, :new, :create]
helper :cms
def index
@cms_redirects = CmsRedirect.all
end
def new
@cms_redirect = CmsRedirect.new
end
def edit
end
def update
new_cms_redirect = CmsRedirect.new(params[:cms_redirect])
if new_cms_redirect.save
@cms_redirect.destroy unless new_cms_redirect.source_path == @cms_redirect.source_path
flash[:notice] = l(:notice_successful_update)
redirect_to cms_redirects_path
else
source_path = @cms_redirect.source_path
@cms_redirect = new_cms_redirect
@cms_redirect.source_path = source_path
render :action => 'edit'
end
end
def create
@cms_redirect = CmsRedirect.new(params[:cms_redirect])
if @cms_redirect.save
flash[:notice] = l(:notice_successful_create)
redirect_to cms_redirects_path
else
render :action => 'new'
end
end
def destroy
@cms_redirect.destroy
redirect_to cms_redirects_path
end
private
def find_redirect
parametrized_redirects = RedmineCms.redirects.map { |k, v| [k, v] }.inject({}) { |memo, (key, value)| memo[key.parameterize] = { :s => key, :d => value }; memo }
redirect = parametrized_redirects[params[:id].gsub(/^_$/, '')]
render_404 unless redirect
@cms_redirect = CmsRedirect.new(:source_path => redirect.try(:[], :s), :destination_path => redirect.try(:[], :d))
end
end

View File

@ -0,0 +1,75 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsRedmineLayoutsController < ApplicationController
unloadable
before_action :require_admin
before_action :find_redmine_layout, :except => [:index, :new, :create]
helper :cms
def index
@cms_redmine_layouts = CmsRedmineLayout.all
end
def new
@cms_redmine_layout = CmsRedmineLayout.new
end
def edit
end
def update
new_cms_redmine_layout = CmsRedmineLayout.new(params[:cms_redmine_layout])
if new_cms_redmine_layout.save
@cms_redmine_layout.destroy unless new_cms_redmine_layout.redmine_action == @cms_redmine_layout.redmine_action
flash[:notice] = l(:notice_successful_update)
redirect_to cms_redmine_layouts_path
else
redmine_action = @cms_redmine_layout.redmine_action
@cms_redmine_layout = new_cms_redmine_layout
@cms_redmine_layout.redmine_action = redmine_action
render :action => 'edit'
end
end
def create
@cms_redmine_layout = CmsRedmineLayout.new(params[:cms_redmine_layout])
if @cms_redmine_layout.save
flash[:notice] = l(:notice_successful_create)
redirect_to cms_redmine_layouts_path
else
render :action => 'new'
end
end
def destroy
@cms_redmine_layout.destroy
redirect_to cms_redmine_layouts_path
end
private
def find_redmine_layout
# parametrized_redmine_layouts = CmsRedmineLayout.all.inject({}){|memo, (key, value)| memo[key.parameterize] = {:a => key, :l => value}; memo}
@cms_redmine_layout = CmsRedmineLayout[params[:id]]
render_404 unless @cms_redmine_layout
end
end

View File

@ -0,0 +1,65 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsSettingsController < ApplicationController
unloadable
menu_item :cms_settings
before_action :require_admin
before_action :find_settings
helper :cms
def index
end
def redmine_hooks
end
def update
params[:settings].each do |key, value|
@settings[key] = value
end
Setting.plugin_redmine_cms = @settings
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :tab => params[:tab]
end
def edit
end
def save
find_project_by_project_id
params_hash = params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params
if params_hash[:cms_settings] && params_hash[:cms_settings].is_a?(Hash)
settings = params_hash[:cms_settings]
settings.map do |k, v|
RedmineCms.set_project_settings(k, @project.id, v)
end
end
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => params[:tab]
end
private
def find_settings
@settings = Setting.plugin_redmine_cms
@settings = {} unless @settings.is_a?(Hash)
end
end

View File

@ -0,0 +1,193 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsSiteController < ApplicationController
# caches_action :sitemap, :expires_in => 1.hour
before_action :redirect_urls, :only => [:login, :lost_password, :register]
def sitemap
@projects = Project.all_public.active
@pages = CmsPage.where(:project_id => nil).visible.all
end
def login
if request.get?
if User.current.logged?
redirect_to @on_success_url || home_url
elsif params[:key] && user = User.find_by_api_key(params[:key])
successful_authentication(user)
return
else
redirect_back_or_default home_url
end
else
authenticate_user
end
end
# Lets user choose a new password
def lost_password
(redirect_to(home_url); return) unless Setting.lost_password?
if prt = (params[:token] || session[:password_recovery_token])
@token = Token.find_token('recovery', prt.to_s)
if @token.nil? || @token.expired?
redirect_to home_url
return
elsif @token.expired?
# remove expired token from session and let user try again
session[:password_recovery_token] = nil
flash[:error] = l(:error_token_expired)
redirect_to lost_password_url
return
end
@user = @token.user
unless @user && @user.active?
redirect_to home_url
return
end
if request.post?
@user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
if @user.save
@token.destroy
Mailer.password_updated(@user)
flash[:notice] = l(:notice_account_password_updated)
redirect_to @on_success_url || home_url
return
end
end
render :template => 'account/password_recovery'
return
else
if request.post?
email = params[:mail].to_s
user = User.find_by_mail(email)
# user not found
unless user
flash[:error] = l(:notice_account_unknown_email)
redirect_to @on_falure_url || :back
return
end
unless user.active?
handle_inactive_user(user, lost_password_path)
return
end
# user cannot change its password
unless user.change_password_allowed?
flash[:error] = l(:notice_can_t_change_password)
redirect_to @on_falure_url || :back
return
end
# create a new token for password recovery
token = Token.new(:user => user, :action => 'recovery')
if token.save
# Don't use the param to send the email
recipent = user.mails.detect { |e| email.casecmp(e) == 0 } || user.mail
if Redmine::VERSION.to_s > '4'
Mailer.lost_password(user, token, recipent).deliver
else
Mailer.lost_password(token, recipent).deliver
end
flash[:notice] = l(:notice_account_lost_email_sent)
redirect_to @on_success_url || home_url
return
end
end
end
end
def logout
end
def register
end
def expire_cache
Rails.cache.clear
redirect_back_or_default home_url
end
private
def invalid_credentials
logger.warn "[CMS] Failed login for '#{params[:email]}' from #{request.remote_ip} at #{Time.now.utc}"
flash[:error] = l(:notice_account_invalid_credentials)
redirect_to @on_falure_url || :back
end
def authenticate_user
password_authentication
end
def password_authentication
login = User.find_by_mail(params[:email]).try(:login)
user = User.try_to_login(login, params[:password], false)
if user.nil?
invalid_credentials
else
# Valid user
if user.active?
successful_authentication(user)
update_sudo_timestamp! # activate Sudo Mode
else
handle_inactive_user(user, @on_falure_url)
end
end
rescue
invalid_credentials
end
def successful_authentication(user)
logger.info "[CMS] Successful authentication for '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}"
# Valid user
self.logged_user = user
redirect_back_or_default @on_success_url || home_url
end
def handle_inactive_user(user, redirect_path=signin_path)
if user.registered?
account_pending(user, redirect_path)
else
account_locked(user, redirect_path)
end
end
def account_pending(user, redirect_path=signin_path)
if Setting.self_registration == '1'
flash[:error] = l(:notice_account_not_activated_yet, :url => activation_email_path)
session[:registered_user_id] = user.id
else
flash[:error] = l(:notice_account_pending)
end
redirect_to redirect_path
end
def account_locked(user, redirect_path=signin_path)
flash[:error] = l(:notice_account_locked)
redirect_to redirect_path
end
def redirect_urls
@on_success_url = params[:on_success_url]
@on_falure_url = params[:on_falure_url]
end
end

View File

@ -0,0 +1,105 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsSnippetsController < ApplicationController
unloadable
before_action :require_edit_permission
before_action :find_snippet, :except => [:index, :new, :create]
helper :attachments
helper :cms
helper :cms_pages
def index
@snippets = CmsSnippet.order(:name)
end
def preview
@current_version = @cms_snippet.set_content_from_version(params[:version]) if params[:version]
page = CmsPage.new(:listener => self)
@preview_content = page.render_part(@cms_snippet)
end
def edit
@current_version = @cms_snippet.set_content_from_version(params[:version]) if params[:version]
end
def new
@cms_snippet = CmsSnippet.new(:filter_id => 'textile')
@cms_snippet.copy_from(params[:copy_from]) if params[:copy_from]
end
def update
@cms_snippet.safe_attributes = params[:cms_snippet]
@cms_snippet.save_attachments(params[:attachments])
if @cms_snippet.save
render_attachment_warning_if_needed(@cms_snippet)
flash[:notice] = l(:notice_successful_update)
respond_to do |format|
format.html { redirect_back_or_default edit_cms_snippet_path(@cms_snippet) }
end
else
render :action => 'edit'
end
end
def create
@cms_snippet = CmsSnippet.new
@cms_snippet.safe_attributes = params[:cms_snippet]
@cms_snippet.save_attachments(params[:attachments])
if @cms_snippet.save
render_attachment_warning_if_needed(@cms_snippet)
flash[:notice] = l(:notice_successful_create)
redirect_to :action => 'edit', :id => @cms_snippet
else
render :action => 'new'
end
end
def destroy
if params[:version]
version = @cms_snippet.versions.where(:version => params[:version]).first
if version.current_version?
flash[:warning] = l(:label_cms_version_cannot_destroy_current)
else
version.destroy
end
redirect_to history_cms_snippet_path(@page)
else
@cms_snippet.destroy
redirect_to :controller => 'cms_snippets', :action => 'index'
end
end
private
def find_snippets
@snippets = CmsSnippet.order(:name)
end
def find_snippet
@cms_snippet = CmsSnippet.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
def require_edit_permission
deny_access unless RedmineCms.allow_edit?
end
end

View File

@ -0,0 +1,75 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsVariablesController < ApplicationController
unloadable
before_action :require_admin
before_action :find_variable, :except => [:index, :new, :create]
helper :cms
def index
@cms_variables = CmsVariable.all
end
def new
@cms_variable = CmsVariable.new
end
def edit
end
def update
new_cms_variable = CmsVariable.new(params[:cms_variable])
if new_cms_variable.save
@cms_variable.destroy unless new_cms_variable.name == @cms_variable.name
flash[:notice] = l(:notice_successful_update)
redirect_to cms_variables_path
else
name = @cms_variable.name
@cms_variable = new_cms_variable
@cms_variable.name = name
render :action => 'edit'
end
end
def create
@cms_variable = CmsVariable.new(params[:cms_variable])
if @cms_variable.save
flash[:notice] = l(:notice_successful_create)
redirect_to cms_variables_path
else
render :action => 'new'
end
end
def destroy
@cms_variable.destroy
redirect_to cms_variables_path
end
private
def find_variable
# parametrized_variables = CmsVariable.all.inject({}){|memo, (key, value)| memo[key.parameterize] = {:a => key, :l => value}; memo}
@cms_variable = CmsVariable[params[:id]]
render_404 unless @cms_variable
end
end

View File

@ -0,0 +1,60 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsVotesController < ApplicationController
unloadable
before_action :check_params
before_action :find_cms_page
helper :cms
def vote
case params[:vote]
when 'up'
@cms_page.vote_up(User.current, vote_options)
when 'down'
@cms_page.vote_down(User.current, vote_options)
when 'unvote'
@cms_page.unvote_by(User.current, vote_options)
end
redirect_to params[:back_url] || :back
rescue ActionController::RedirectBackError
redirect_to cms_page_path(CmsPage.find(RedmineCms.landing_page))
end
private
def find_cms_page
@cms_page = CmsPage.find_by(:name => params[:id]) || CmsPage.find_by(:id => params[:id])
render_404 if @cms_page.blank? || !@cms_page.respond_to?(:versions)
end
def check_params
render_404 unless %w(up down unvote).include?(params[:vote])
end
def vote_options
options = {}
options[:vote_scope] = params[:vote_scope] if params[:vote_scope]
options[:vote_weight] = params[:vote_weight].to_i if params[:vote_weight]
options[:vote_ip] = request.remote_ip
options[:vote_by_ip] = User.current.anonymous?
options
end
end

View File

@ -0,0 +1,43 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class ProjectTabsController < ApplicationController
unloadable
before_action :find_project_by_project_id, :authorize
before_action :find_page
helper :cms_pages
helper :cms
def show
unless @page.layout.blank?
render((Rails.version < '5.1' ? :text : :plain) => @page.process(self), :layout => false)
end
end
private
def find_page
tab_name = "project_tab_#{params[:tab]}".to_sym
menu_items[:project_tabs][:actions][:show] = tab_name
@page = CmsPage.find_by_name(RedmineCms.get_project_settings("project_tab_#{params[:tab]}_page", @project.id))
render_404 if @page.blank?
end
end

176
app/helpers/cms_helper.rb Normal file
View File

@ -0,0 +1,176 @@
# encoding: utf-8
#
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
module CmsHelper
def cms_change_status_link(obj_name, obj)
return unless obj.respond_to?(:status_id)
url = {:controller => "cms_#{obj_name}s", :action => 'update', :id => obj, obj_name.to_sym => params[obj_name.to_sym], :status => params[:status], :back_url => request.path}
if obj.active?
link_to l(:button_lock), url.merge(obj_name.to_sym => {:status_id => RedmineCms::STATUS_LOCKED}, :unlock => true), :method => :put, :remote => :true, :class => 'icon icon-lock'
else
link_to l(:button_unlock), url.merge(obj_name.to_sym => {:status_id => RedmineCms::STATUS_ACTIVE}, :unlock => true), :method => :put, :remote => :true, :class => 'icon icon-unlock'
end
end
def cms_visibilities_for_select(selected = nil, options={})
grouped_options = {}
grouped_options[l(:label_user_plural)] = [[l(:field_admin), '']] if options[:admin]
grouped_options[l(:label_role_plural)] = [["Public", 'public'], ["Logged", 'logged']] unless options[:only_groups]
grouped_options[l(:label_group_plural)] = Group.where(:type => 'Group').map{|g| [g.name, g.id]}
grouped_options_for_select(grouped_options, selected)
end
def code_mirror_tags
s = ''
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/lib/codemirror')
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/lib/emmet')
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/addon/mode/overlay')
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/addon/search/search')
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/addon/search/searchcursor')
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/addon/dialog/dialog')
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/addon/comment/comment')
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/mode/htmlmixed/htmlmixed')
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/mode/css/css')
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/mode/liquid/liquid')
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/mode/xml/xml')
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/mode/javascript/javascript')
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/mode/textile/textile')
s << javascript_include_tag('/plugin_assets/redmine_cms/codemirror/keymap/sublime')
s << stylesheet_link_tag('/plugin_assets/redmine_cms/codemirror/lib/codemirror')
s << stylesheet_link_tag('/plugin_assets/redmine_cms/codemirror/theme/ambiance')
s << stylesheet_link_tag('/plugin_assets/redmine_cms/codemirror/mode/liquid/liquid')
s.html_safe
end
def cms_layouts_for_select(options = {})
cms_layouts = []
cms_layouts << [l(:label_cms_redmine_layout), ''] unless options[:only_cms]
cms_layouts += CmsLayout.order(:name).map{|l| [l.name, l.id]} if CmsLayout.any?
cms_layouts
end
def cms_statuses_for_select(options = {})
[[l(:label_cms_status_locked), RedmineCms::STATUS_LOCKED], [l(:label_cms_status_active), RedmineCms::STATUS_ACTIVE]]
end
def link_to_cms_attachments(container, options = {})
options.assert_valid_keys(:author, :thumbnails)
attachments = container.attachments.preload(:author).to_a
if attachments.any?
options = {
:editable => RedmineCms.allow_edit?,
:deletable => RedmineCms.allow_edit?,
:author => true
}.merge(options)
render :partial => 'cms_attachments/links',
:locals => {
:container => container,
:attachments => attachments,
:options => options,
:thumbnails => (options[:thumbnails] && Setting.thumbnails_enabled?)
}
end
end
def cms_thumbnail_tag(attachment)
link_to image_tag(cms_thumbnail_path(attachment, 100, attachment.filename)),
download_named_asset_path(attachment, attachment.filename),
:title => attachment.filename
end
# Renders the project quick-jump box
def render_page_jump_box
return unless User.current.logged?
pages = CmsPage.all.to_a
if pages.any?
options =
("<option value=''>#{ l(:label_cms_jump_to_a_page) }</option>" +
'<option value="" disabled="disabled">---</option>').html_safe
pages_name_options_for_select(pages, :label => 'name').each do |o|
tag_options = {:value => edit_cms_page_path(:id => o[1])}
tag_options[:selected] = @page && o[1] == @page.name ? 'selected' : nil
options << content_tag('option', o[0], tag_options)
end
content_tag( :span, nil, :class => 'jump-box-arrow') +
select_tag('page_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
end
end
def pages_name_options_for_select(pages, options = {})
pages_name_options = []
CmsPage.page_tree(pages) do |page, level|
label = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
label << (options[:label] == 'name' ? page.name : page.title)
pages_name_options << [label, page.name]
end
pages_name_options
end
def cms_title(*args)
strings = args.map do |arg|
if arg.is_a?(Array) && arg.size >= 2
link_to(*arg)
else
h(arg.to_s)
end
end
content_tag('h2', strings.join(' &#187; ').html_safe)
end
def render_cms_tag_link(tag, options = {})
filters = [[:cms_page_tags, '=', tag.id]]
content = link_to_cms_filter tag.name, filters, :project_id => @project
if options[:show_count]
content << content_tag('span', "(#{tag.count})", :class => 'tag-count')
end
style = {:class => "tag-label"}
content_tag('span', content, style)
end
def link_to_cms_filter(title, filters, options = {})
options.merge! link_to_cms_filter_options(filters)
link_to title, options
end
def link_to_cms_filter_options(filters)
options = {
:controller => 'cms_pages',
:action => 'index',
:set_filter => 1,
:fields => [],
:values => {},
:operators => {}
}
filters.each do |f|
name, operator, value = f
options[:fields].push(name)
options[:operators][name] = operator
options[:values][name] = [value]
end
options
end
end

View File

@ -0,0 +1,48 @@
# encoding: utf-8
#
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
module CmsMenusHelper
def change_menu_status_link(cms_menu)
url = {:controller => 'cms_menus', :action => 'update', :id => cms_menu, :cms_menu => params[:cms_menu], :status => params[:status], :tab => nil}
if cms_menu.active?
link_to l(:button_lock), url.merge(:cms_menu => {:status_id => RedmineCms::STATUS_LOCKED}), :method => :put, :class => 'icon icon-lock'
else
link_to l(:button_unlock), url.merge(:cms_menu => {:status_id => RedmineCms::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
end
end
def menus_options_for_select(menus)
options = []
CmsMenu.menu_tree(menus) do |menu, level|
label = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
label << menu.caption
options << [label, menu.id]
end
options
end
def cms_menus_tree(menus, &block)
CmsMenu.menu_tree(menus, &block)
end
end

View File

@ -0,0 +1,100 @@
# encoding: utf-8
#
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
module CmsPagesHelper
def page_breadcrumb(page)
return unless page.parent
pages = page.ancestors.reverse
pages << page
links = pages.map {|ancestor| link_to(h(ancestor.title), cms_page_path(ancestor))}
breadcrumb links
end
def pages_options_for_select(pages)
options = []
CmsPage.page_tree(pages) do |page, level|
label = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
label << page.name
options << [label, page.id]
end
options
end
def set_locale
if RedmineCms.use_localization?
if lang = params[:path].to_s[/^\/?(#{CmsSite.locales.join('|')})+(\/|$)/, 1]
CmsSite.language = lang
elsif params[:locale]
CmsSite.language = params[:locale]
elsif session[:cms_locale]
CmsSite.language = session[:cms_locale]
else
CmsSite.language = Setting.default_language
end
session[:cms_locale] = CmsSite.language
end
end
def change_page_status_link(page)
url = {:controller => 'cms_pages', :action => 'update', :id => page, :page => params[:page], :status => params[:status], :tab => nil}
if page.active?
link_to l(:button_lock), url.merge(:page => {:status_id => RedmineCms::STATUS_LOCKED}), :method => :put, :class => 'icon icon-lock'
else
link_to l(:button_unlock), url.merge(:page => {:status_id => RedmineCms::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
end
end
def link_to_add_page_fields(name, f, association, options={})
new_object = CmsPageField.new
fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
render "page_field_form", :f => builder
end
link_to_function(name, "add_page_fields(this, '#{association}', '#{escape_javascript(fields)}')", options)
end
def link_to_remove_page_fields(name, f, options={})
f.hidden_field(:_destroy) + link_to_function(name, "remove_page_fields(this)", options)
end
def page_tree(pages, &block)
CmsPage.page_tree(pages, &block)
end
def retrieve_pages_query
if params[:query_id].present?
@query = CmsPageQuery.find(params[:query_id])
raise ::Unauthorized unless @query.visible?
session[:cms_pages_query] = { :id => @query.id }
elsif api_request? || params[:set_filter] || session[:cms_pages_query].nil?
# Give it a name, required to be valid
@query = CmsPageQuery.new(:name => '_')
@query.build_from_params(params)
session[:cms_pages_query] = { :filters => @query.filters }
else
# retrieve from session
@query = CmsPageQuery.where(:id => session[:cms_pages_query][:id]).first if session[:cms_pages_query][:id]
@query ||= CmsPageQuery.new(:name => '_', :filters => session[:cms_pages_query][:filters])
end
end
end

View File

@ -0,0 +1,63 @@
# encoding: utf-8
#
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
module CmsPartsHelper
def change_part_status_link(part)
url = {:controller => 'cms_parts', :action => 'update', :id => part, :part => params[:part], :status => params[:status], :tab => nil}
if part.active?
link_to l(:button_lock), url.merge(:part => {:status_id => RedmineCms::STATUS_LOCKED}, :unlock => true), :method => :put, :class => 'icon icon-lock'
else
link_to l(:button_unlock), url.merge(:part => {:status_id => RedmineCms::STATUS_ACTIVE}, :unlock => true), :method => :put, :class => 'icon icon-unlock'
end
end
def parts_type_collection
[["Pages", [["Content", "content"],
["Sections", "sections"],
["Sidebar", "sidebar"],
["Header", "header"],
["Footer", "footer"],
["Header tags", "header_tags"]]],
["Layout", [["Layout html head", "layout_html_head_part"],
["Layout sidebar top", "layout_base_sidebar"],
["Layout body top", "layout_body_top_part"],
["Layout body bottom", "layout_body_bottom_part"]]]]
end
def parts_option_for_select
parts = CmsPart.order(:part_type).where("#{CmsPart.table_name}.part_type NOT LIKE 'layout_%'").order(:content_type)
return "" unless parts.any?
previous_group = parts.first.part_type
s = "<optgroup label=\"#{ERB::Util.html_escape(parts.first.part_type)}\">".html_safe
parts.each do |part|
if part.part_type != previous_group
reset_cycle
s << '</optgroup>'.html_safe
s << "<optgroup label=\"#{ERB::Util.html_escape(part.part_type)}\">".html_safe
previous_group = part.part_type
end
s << %Q(<option value="#{ERB::Util.html_escape(part.id)}">#{part.to_s}</option>).html_safe
end
s << '</optgroup>'.html_safe
s.html_safe
end
end

View File

@ -0,0 +1,56 @@
class CmsContentVersion < ActiveRecord::Base
unloadable
include Redmine::SafeAttributes
belongs_to :versionable, polymorphic: true
belongs_to :author, class_name: 'User'
attr_protected :id if ActiveRecord::VERSION::MAJOR <= 4
safe_attributes 'author', 'content', 'version', 'comments'
acts_as_event :title => Proc.new { |o|
s = ""
s = s + l("label_#{o.versionable.class.name.underscore}")
s = s + " (#{o.versionable.respond_to?(:page) ? "#{o.versionable.page.name}:#{o.versionable.name}": o.versionable.name})"
if o.versionable.respond_to?(:title) && o.versionable.title.present?
s = s + ": #{o.versionable.title}"
elsif o.versionable.respond_to?(:description) && o.versionable.description.present?
s = s + ": #{o.versionable.description}"
end
s
},
:description => :comments,
:group => :versionable,
:datetime => :created_at,
:url => Proc.new {|o| {:controller => 'cms_history', :action => 'history', :id => o.versionable.id, :object_type => o.versionable.class.name.underscore}},
:type => Proc.new {|o| o.versionable.class.name.underscore.gsub('cms_', '') }
acts_as_activity_provider :timestamp => "#{table_name}.created_at",
:author_key => :author_id,
:scope => preload(:author)
scope :visible, lambda {|*args|
if RedmineCms.allow_edit?
where('1=1')
else
where('1=0')
end
}
def current_version?
versionable.version == version
end
def project
nil
end
# Returns the previous version or nil
def previous
@previous ||= CmsContentVersion.
reorder('version DESC').
includes(:author).
where('versionable_type = ? AND versionable_id = ? AND version < ?',
versionable.class, versionable.id, version).first
end
end

166
app/models/cms_export.rb Normal file
View File

@ -0,0 +1,166 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsExport
attr_accessor :attachment_content
attr_reader :errors
def initialize(export_object)
@export_object = export_object
@errors = ActiveModel::Errors.new(self)
end
def configure(options = {})
return unless options
options.slice(:attachment_content).each { |opt, val| send("#{opt}=", val) }
end
def id
@export_object.id
end
def object_type
@export_object.class.name.underscore
end
def export
@export_yaml ||= export_to_yaml
end
def export_website
content = { :layouts => [], :pages => [], :snippets => [], :assets => [], :global_variables => [] }
CmsLayout.find_each do |layout|
@export_object = layout
content[:layouts] << export_object_to_yaml(true)
end
CmsPage.order(:lft).each do |page|
@export_object = page
content[:pages] << export_page_to_yaml(true)
end
CmsSnippet.find_each do |snippet|
@export_object = snippet
content[:snippets] << export_object_to_yaml(true)
end
CmsSite.instance.attachments.find_each do |attachment|
content[:assets] << attachment.attributes.slice('container_type', 'filename', 'content_type', 'description').
merge(file_data(attachment))
end
CmsVariable.all.each do |variable|
@export_object = variable
content[:global_variables] << export_variable_to_yaml(true)
end
content.to_yaml
end
private
def export_to_yaml
case @export_object.class.name
when 'CmsPage'
return export_page_to_yaml
when 'CmsLayout'
return export_object_to_yaml
when 'CmsSnippet'
return export_object_to_yaml
end
end
def export_page_to_yaml(original = false)
attrs = @export_object.attributes.except!('id', 'created_at', 'updated_at', 'version', 'parent_id', 'project_id', 'layout_id', 'lft', 'rgt')
attrs.merge!(page_parent)
attrs.merge!(page_layout)
attrs.merge!(page_parts)
attrs.merge!(page_fields)
attrs.merge!(page_tags)
attrs.merge!(shared_attributes)
original ? attrs : attrs.to_yaml
end
def export_object_to_yaml(original = false)
attrs = @export_object.attributes.except!('id', 'created_at', 'updated_at', 'version')
attrs.merge!(shared_attributes)
original ? attrs : attrs.to_yaml
end
def export_variable_to_yaml(original = false)
attrs = @export_object.attributes
original ? attrs : attrs.to_yaml
end
def page_parent
{ 'parent_name' => @export_object.parent.try(:name) }
end
def page_layout
{ 'layout_name' => @export_object.layout.try(:name) }
end
def page_parts
return {} if @export_object.parts.empty?
parts_attibutes = @export_object.parts.map do |part|
part.attributes.except!('id', 'created_at', 'updated_at', 'version', 'page_id').
merge('page_name' => @export_object.name)
end
{ 'parts' => parts_attibutes }
end
def page_fields
return {} if @export_object.fields.empty?
fields_attibutes = @export_object.fields.map do |field|
field.attributes.except!('id', 'created_at', 'updated_at', 'page_id').
merge('page_name' => @export_object.name)
end
{ 'fields' => fields_attibutes }
end
def page_tags
{ 'tags' => @export_object.tag_list.join(',') }
end
def shared_attributes
shared_attributes = {}
shared_attributes.merge(attachments_to_yaml)
end
def attachments_to_yaml
return {} if @export_object.attachments.empty?
attachments_attibutes = @export_object.attachments.map do |attachment|
attachment.attributes.slice('container_type', 'filename', 'content_type', 'description').
merge('page_name' => @export_object.name).
merge(file_data(attachment))
end
{ 'attachments' => attachments_attibutes }
end
def file_data(file)
@attachment_content.to_i > 0 ? file_content(file) : file_url(file)
end
def file_content(file)
{ 'file_content' => File.open(file.diskfile, 'rb') { |f| Base64.encode64(f.read) } }
end
def file_url(file)
{ 'file_url' => Rails.application.routes.url_helpers.
download_named_attachment_url(file,
:filename => file.filename,
:host => Setting.host_name,
:protocol => Setting.protocol) }
end
end

184
app/models/cms_import.rb Normal file
View File

@ -0,0 +1,184 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsImport
require 'open-uri'
attr_accessor :file, :rewrite, :author
attr_reader :errors
def initialize(import_object)
@import_object = import_object
@errors = ActiveModel::Errors.new(self)
end
def configure(options = {})
return unless options
options.slice(:file, :rewrite).each { |opt, val| send("#{opt}=", val) }
end
def object_type
@import_object.class.name.underscore
end
def import
data = YAML.load_file(RUBY_VERSION >= '1.9.3' ? file.tempfile : file.path)
import_object(data)
end
def import_website(file_path = nil)
file_path = RUBY_VERSION >= '1.9.3' ? file.tempfile : file.path unless file_path
data = YAML.load_file(file_path)
lock_optimistically_setting = ActiveRecord::Base.lock_optimistically
ActiveRecord::Base.lock_optimistically = false
CmsPage.transaction do
data[:layouts].each { |layout| import_layout(layout) }
data[:pages].each { |page| import_page(page) }
data[:snippets].each { |snippet| import_snippet(snippet) }
data[:assets].each { |asset| import_assets(asset) }
data[:global_variables].each { |global_variable| import_global_variable(global_variable) }
CmsPage.rebuild_tree!
end
ActiveRecord::Base.lock_optimistically = lock_optimistically_setting
end
private
def import_object(data)
case @import_object.class.name
when 'CmsPage'
return import_page(data)
when 'CmsLayout'
return import_layout(data)
when 'CmsSnippet'
return import_snippet(data)
end
end
def import_page(data)
page = CmsPage.where(:name => data['name']).first || CmsPage.new
page.errors.add(:base, I18n.t(:label_crm_import_page_exist)) if rewrite.to_i.zero? && page.persisted?
return page unless page.errors.empty?
CmsPage.transaction do
page.assign_attributes(data.except('fields', 'attachments', 'parts', 'parent_name', 'layout_name', 'tags', 'lft', 'rgt', 'lock_version'))
page.author = @author
page.tag_list = data['tags'].split(',')
page.parent = CmsPage.where(:name => data['parent_name']).first if data['parent_name'].present?
page.layout = CmsLayout.where(:name => data['layout_name']).first if data['layout_name'].present?
return page unless page.save
import_parts(page, data['parts'])
import_fields(page, data['fields'])
import_attachments(page, data['attachments'])
end
page
end
def import_layout(data)
layout = CmsLayout.where(:name => data['name']).first || CmsLayout.new
return layout.errors.add(:base, I18n.t(:label_crm_import_layout_exist)) if rewrite.to_i.zero? && layout.persisted?
CmsLayout.transaction do
layout.assign_attributes(data.except('attachments'))
return layout unless layout.save
import_attachments(layout, data['attachments'])
end
layout
end
def import_snippet(data)
snippet = CmsSnippet.where(:name => data['name']).first || CmsSnippet.new
return snippet.errors.add(:base, I18n.t(:label_crm_import_snippet_exist)) if rewrite.to_i.zero? && snippet.persisted?
CmsSnippet.transaction do
snippet.assign_attributes(data.except('attachments'))
return snippet unless snippet.save
import_attachments(snippet, data['attachments'])
end
snippet
end
def import_assets(data)
asset = CmsSite.instance.attachments.where(:filename => data['filename']).first || CmsSite.instance.attachments.new
return asset.errors.add(:base, I18n.t(:label_crm_import_asset_exist)) if asset.persisted?
Attachment.transaction do
asset = create_from_content(data) if data['file_content'].present?
asset = create_from_url(data) if data['file_url'].present?
asset.update(:container_id => 1, :container_type => CmsSite.name) if asset
end
asset
end
def import_global_variable(data)
variable = CmsVariable[data[:name]] || CmsVariable.new
return variable.errors.add(:base, I18n.t(:label_crm_import_variable_exist)) if rewrite.to_i.zero? && variable.persisted?
variable.name = data[:name]
variable.value = data[:value]
variable.save
variable
end
def import_parts(page, parts)
return if parts.nil? || parts.empty?
parts.each do |part|
existed_part = page.parts.where(:name => part['name']).first
existed_part ? existed_part.update(part.except('page_name')) : page.parts.create!(part.except('page_name'))
end
end
def import_fields(page, fields)
return if fields.nil? || fields.empty?
fields.each do |field|
existed_field = page.fields.where(:name => field['name']).first
existed_field ? existed_field.update(field.except('page_name')) : page.fields.create!(field.except('page_name'))
end
end
def import_attachments(object, attachments)
return if attachments.nil? || attachments.empty?
attachments.each do |attachment|
new_attachment = if attachment['file_content'].present?
create_from_content(attachment)
elsif attachment['file_url'].present?
create_from_url(attachment)
end
if new_attachment.present? && object.attachments.where(:filename => attachment['filename'], :filesize => new_attachment.filesize).empty?
object.attachments << new_attachment
end
end
end
def create_from_content(attachment)
temp_file = Tempfile.new(attachment['filename'])
temp_file.binmode
temp_file.write(Base64.decode64(attachment['file_content']))
temp_file.rewind
Attachment.new(:file => temp_file.read,
:author => @author,
:filename => attachment['filename'],
:content_type => attachment['content_type'],
:description => attachment['description'])
end
def create_from_url(attachment)
Attachment.new(:file => open(attachment['file_url']),
:author => @author,
:filename => attachment['filename'],
:content_type => attachment['content_type'],
:description => attachment['description'])
end
end

43
app/models/cms_layout.rb Normal file
View File

@ -0,0 +1,43 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsLayout < ActiveRecord::Base
unloadable
include Redmine::SafeAttributes
include RedmineCms::Filterable
has_many :pages, class_name: 'CmsPage', foreign_key: 'layout_id', dependent: :nullify
after_commit :expire_pages_cache
acts_as_attachable_cms
acts_as_versionable_cms
validates_presence_of :name, :content
attr_protected :id if ActiveRecord::VERSION::MAJOR <= 4
safe_attributes 'name',
'content',
'content_type',
'filter_id'
def expire_pages_cache
pages.each(&:expire_cache)
end
end

191
app/models/cms_menu.rb Normal file
View File

@ -0,0 +1,191 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsMenu < ActiveRecord::Base
unloadable
include Redmine::SafeAttributes
belongs_to :source, :polymorphic => true
rcrm_acts_as_list :scope => 'menu_type = \'#{menu_type}\' AND parent_id #{parent_id ? \'=\' + parent_id.to_s : \'IS NULL\'}'
acts_as_tree :dependent => :nullify
default_scope { order(:menu_type).order(:position) }
scope :active, lambda { where(:status_id => RedmineCms::STATUS_ACTIVE) }
scope :visible, lambda { where(CmsMenu.visible_condition) }
scope :top_menu, lambda { where(:menu_type => 'top_menu') }
scope :account_menu, lambda { where(:menu_type => 'account_menu') }
before_update :remove_from_menu
before_destroy :remove_from_menu
after_commit :rebuild_menu, :on => [:create, :update, :destroy]
validates_presence_of :name, :caption
validates_uniqueness_of :name, :scope => :menu_type
validates_length_of :name, :maximum => 30
validates_length_of :caption, :maximum => 255
validate :validate_menu
validate :uniqueness_in_menu_manager, :on => :create
validates_format_of :name, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/
@cached_cleared_on = Time.now
attr_protected :id if ActiveRecord::VERSION::MAJOR <= 4
safe_attributes 'name',
'caption',
'path',
'position',
'status_id',
'parent_id',
'visibility',
'menu_type'
def self.visible_condition(user = User.current)
user_ids = [user.id] + user.groups.map(&:id)
return '(1=1)' if user.admin?
cond = ''
cond << " ((#{table_name}.visibility = 'public')"
cond << " OR (#{table_name}.visibility = 'logged')" if user.logged?
cond << " OR (#{table_name}.visibility IN (#{user_ids.join(',')})))" if user.logged?
end
def visible?(user = User.current)
return true if user.admin?
return true if visibility == 'public'
return true if visibility == 'logged' && user.logged?
user_ids = [user.id] + user.groups.map(&:id)
return true if user_ids.include?(visibility.to_i) && user.logged?
false
end
def active?
status_id == RedmineCms::STATUS_ACTIVE
end
def rebuild_menu
# CmsMenu.rebuild
CmsMenu.clear_cache
end
def remove_from_menu
Redmine::MenuManager.map(:top_menu) do |menu|
menu.delete(name_was.to_sym)
menu.delete(name.to_sym)
end
Redmine::MenuManager.map(:account_menu) do |menu|
menu.delete(name_was.to_sym)
menu.delete(name.to_sym)
end
end
def reload(*args)
@valid_parents = nil
super
end
def self.menu_tree(menus, parent_id = nil, level = 0)
tree = []
menus.select { |menu| menu.parent_id == parent_id }.sort_by(&:position).sort_by(&:menu_type).each do |menu|
tree << [menu, level]
tree += menu_tree(menus, menu.id, level + 1)
end
if block_given?
tree.each do |menu, _level|
yield menu, level
end
end
tree
end
def self.check_cache
menu_updated_on = CmsMenu.maximum(:updated_at)
clear_cache if menu_updated_on && @cached_cleared_on <= menu_updated_on
end
# Clears the settings cache
def self.clear_cache
CmsMenu.rebuild
@cached_cleared_on = Time.now
logger.info 'Menu cache cleared.' if logger
end
def self.rebuild
Redmine::MenuManager.map :top_menu do |menu|
CmsMenu.top_menu.each{|m| menu.delete(m.name.to_sym) }
CmsMenu.active.top_menu.where(:parent_id => nil).each do |cms_menu|
menu.push(
cms_menu.name,
cms_menu.path,
:caption => cms_menu.caption,
:first => cms_menu.first?,
:if => Proc.new { |p| cms_menu.visible? }
) unless menu.exists?(cms_menu.name.to_sym)
end
CmsMenu.active.top_menu.where("#{CmsMenu.table_name}.parent_id IS NOT NULL").each do |cms_menu|
menu.push(
cms_menu.name.to_sym,
cms_menu.path,
:parent => cms_menu.parent.name.to_sym,
:caption => cms_menu.caption, :if => Proc.new { |p| cms_menu.visible? && cms_menu.parent.visible? }
) if cms_menu.parent.active? && !menu.exists?(cms_menu.name.to_sym)
end
end
Redmine::MenuManager.map :account_menu do |menu|
CmsMenu.account_menu.each { |m| menu.delete(m.name.to_sym) }
CmsMenu.active.account_menu.where(:parent_id => nil).each do |cms_menu|
menu.push(
cms_menu.name,
cms_menu.path,
:caption => cms_menu.caption,
:first => cms_menu.first?
) unless menu.exists?(cms_menu.name.to_sym)
end
CmsMenu.active.account_menu.where("#{CmsMenu.table_name}.parent_id IS NOT NULL").each do |cms_menu|
menu.push(
cms_menu.name.to_sym,
cms_menu.path,
:parent => cms_menu.parent.name.to_sym,
:caption => cms_menu.caption) if cms_menu.parent.active? && cms_menu.parent.visible? && !menu.exists?(cms_menu.name.to_sym)
end
end
end
def valid_parents
@valid_parents ||= (children.any? ? [] : CmsMenu.where(:menu_type => menu_type, :parent_id => nil) - self_and_descendants)
end
protected
def validate_menu
if parent_id && parent_id_changed?
errors.add(:parent_id, :invalid) unless valid_parents.include?(parent)
end
end
def uniqueness_in_menu_manager
if Redmine::MenuManager.items(menu_type.to_sym).detect { |i| i.name.to_s == name }
errors.add(:name, :taken)
end
end
end

381
app/models/cms_page.rb Normal file
View File

@ -0,0 +1,381 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsPage < ActiveRecord::Base
unloadable
include Redmine::SafeAttributes
include RedmineCms::Filterable
include RedmineCms::PageNestedSet
attr_accessor :page_params
attr_accessor :deleted_attachment_ids
attr_accessor :listener, :context
belongs_to :layout, class_name: 'CmsLayout', foreign_key: 'layout_id'
belongs_to :author, class_name: 'User', foreign_key: 'author_id'
has_many :parts, class_name: 'CmsPart', foreign_key: 'page_id', dependent: :destroy
has_many :children_pages, class_name: 'CmsPage', foreign_key: 'parent_id', dependent: :nullify
if ActiveRecord::VERSION::MAJOR >= 4
has_many :fields, lambda { order(:name) }, class_name: 'CmsPageField', foreign_key: 'page_id', dependent: :destroy
else
has_many :fields, class_name: 'CmsPageField', foreign_key: 'page_id', dependent: :destroy, order: "#{CmsPageField.table_name}.name"
end
acts_as_attachable_cms
acts_as_versionable_cms
rcrm_acts_as_taggable
rcrm_acts_as_votable
scope :active, lambda { where(:status_id => RedmineCms::STATUS_ACTIVE) }
scope :status, lambda { |arg| where(arg.blank? ? nil : { :status_id => arg.to_i }) }
scope :visible, lambda { where(CmsPage.visible_condition) }
scope :like, lambda { |arg|
if arg.blank?
where(nil)
else
pattern = "%#{arg.to_s.strip}%"
where("(LOWER(#{CmsPage.table_name}.content) LIKE LOWER(:p)) OR (LOWER(#{CmsPage.table_name}.name) LIKE LOWER(:p)) OR (LOWER(#{CmsPage.table_name}.title) LIKE LOWER(:p))", :p => pattern)
end
}
scope :live_search, lambda {|search| where("(LOWER(#{CmsPage.table_name}.name) LIKE LOWER(:p) OR LOWER(#{CmsPage.table_name}.slug) LIKE LOWER(:p))",
:p => '%' + search.downcase + '%')}
validates_presence_of :name, :slug
validates_uniqueness_of :name
validates_uniqueness_of :slug, :scope => :parent_id
validates_length_of :name, :maximum => 255
validates_length_of :title, :maximum => 255
validate :validate_page
validates_format_of :name, :slug, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/i
accepts_nested_attributes_for :fields, :allow_destroy => true
after_save :delete_selected_attachments
[:content, :header, :sidebar].each do |name, _params|
src = <<-END_SRC
def #{name}_parts
pages_parts.includes(:part).where(:parts => {:part_type => "#{name}"})
end
END_SRC
class_eval src, __FILE__, __LINE__
end
attr_protected :id if ActiveRecord::VERSION::MAJOR <= 4
safe_attributes 'name',
'slug',
'title',
'visibility',
'filter_id',
'is_cached',
'content',
'page_date',
'layout_id',
'status_id',
'parent_id',
'lock_version',
'tag_list',
'fields_attributes'
safe_attributes 'deleted_attachment_ids',
:if => lambda {|page, user| page.attachments_deletable?(user)}
def self.visible_condition(user = User.current)
user_ids = ([user.id] + user.groups.map(&:id)).map { |id| "'#{id}'" }
cond = '(1=1) '
cond << " AND (#{table_name}.status_id = #{RedmineCms::STATUS_ACTIVE})" unless RedmineCms.allow_edit?(user)
cond << " AND ((#{table_name}.visibility = 'public')"
cond << " OR (#{table_name}.visibility = 'logged')" if user.logged?
cond << " OR (#{table_name}.visibility IN (#{user_ids.join(',')})))"
end
def visible?(user = User.current)
if active?
return true if visibility == 'public'
return true if visibility == 'logged' && user.logged?
user_ids = [user.id] + user.groups.map(&:id)
return true if user_ids.include?(visibility.to_i) && user.logged?
end
RedmineCms.allow_edit?(user)
end
def active?
status_id == RedmineCms::STATUS_ACTIVE
end
def to_param
name.parameterize
end
def reload(*args)
@valid_parents = nil
super
end
def to_s
name
end
def page_fields
@page_fields ||= fields.inject({}) { |mem, var| mem[var.name] = var.content; mem }
end
def valid_parents
@valid_parents ||= CmsPage.all - self_and_descendants
end
def self.tags_cloud(options = {})
scope = RedmineCrm::Tag.where({})
join = []
join << "JOIN #{RedmineCrm::Tagging.table_name} ON #{RedmineCrm::Tagging.table_name}.tag_id = #{RedmineCrm::Tag.table_name}.id "
join << "JOIN #{CmsPage.table_name} ON #{CmsPage.table_name}.id = #{RedmineCrm::Tagging.table_name}.taggable_id AND #{RedmineCrm::Tagging.table_name}.taggable_type = '#{CmsPage.name}' "
group_fields = ""
group_fields << ", #{RedmineCrm::Tag.table_name}.created_on" if RedmineCrm::Tag.respond_to?(:created_on)
group_fields << ", #{RedmineCrm::Tag.table_name}.updated_on" if RedmineCrm::Tag.respond_to?(:updated_on)
scope = scope.joins(join.join(' '))
scope = scope.where("LOWER(#{RedmineCrm::Tag.table_name}.name) LIKE LOWER(?)", "%#{options[:name_like]}%") if options[:name_like]
scope = scope.select("#{RedmineCrm::Tag.table_name}.*, COUNT(DISTINCT #{RedmineCrm::Tagging.table_name}.taggable_id) AS count")
scope = scope.group("#{RedmineCrm::Tag.table_name}.id, #{RedmineCrm::Tag.table_name}.name #{group_fields} HAVING COUNT(*) > 0")
scope = scope.order("#{RedmineCrm::Tag.table_name}.name")
scope = scope.limit(options[:limit]) if options[:limit]
scope
end
def deleted_attachment_ids
Array(@deleted_attachment_ids).map(&:to_i)
end
# Yields the given block for each project with its level in the tree
def self.page_tree(pages, &block)
ancestors = []
pages.sort_by(&:lft).each do |page|
while (ancestors.any? && !page.is_descendant_of?(ancestors.last))
ancestors.pop
end
yield page, ancestors.size
ancestors << page
end
end
def copy_from(arg)
page = arg.is_a?(CmsPage) ? arg : CmsPage.where(:name => arg).first
self.attributes = page.attributes.dup.except('id', 'created_at', 'updated_at')
self.tags << page.tags
self.fields = page.fields.map { |f| CmsPageField.new f.attributes.dup.except('id', 'page_id') }
self.name = name.to_s + '_copy'
self
end
def process(listener, page_params = {})
@page_params = page_params.is_a?(Hash) ? page_params : {}
@listener = listener
set_response_headers(@listener.response)
@listener.response.status = response_code
@listener.response.body = render
end
def headers
# Return a blank hash that child classes can override or merge
{}
end
def set_response_headers(response)
set_content_type(response)
headers.each { |k,v| response.headers[k] = v }
end
private :set_response_headers
def set_content_type(response)
if layout
content_type = layout.content_type.to_s.strip
response.headers['Content-Type'] = content_type.present? ? content_type : 'text/html'
end
end
private :set_content_type
def response_code
200
end
def render
if layout
render_object(layout)
else
render_page
end
end
def render_page
if is_cached?
Rails.cache.fetch(self, :expires_in => RedmineCms.cache_expires_in.minutes) { render_page_with_content_parts(self) }
else
render_page_with_content_parts(self)
end
end
def render_part(part)
part.set_content_type(@listener) if part.respond_to?(:set_content_type)
if part.respond_to?(:is_cached) && part.is_cached?
Rails.cache.fetch(part, :expires_in => RedmineCms.cache_expires_in.minutes) { render_object(part) }
else
render_object(part)
end
end
def digest
@generated_digest ||= digest!
end
def expire_cache
if Rails.cache.respond_to?(:delete_matched)
Rails.cache.delete_matched(cache_key)
else
Rails.cache.delete(self)
end
end
def digest!
# Digest::MD5.hexdigest(self.render)
updated_at.to_formatted_s(:number)
end
def initialize_context(page_listener)
assigns = {}
assigns['users'] = RedmineCrm::Liquid::UsersDrop.new(User.visible.sorted)
assigns['projects'] = RedmineCrm::Liquid::ProjectsDrop.new(Project.visible.order(:name))
assigns['newss'] = RedmineCrm::Liquid::NewssDrop.new(News.visible.order("#{News.table_name}.created_on"))
assigns['current_user'] = RedmineCrm::Liquid::UserDrop.new(User.current)
assigns['page'] = RedmineCrm::PageDrop.new(self)
assigns['pages'] = RedmineCrm::PagesDrop.new(CmsPage.visible)
assigns['current_page'] = page_listener.request.params[:page] || 1 if page_listener
assigns['params'] = page_listener.request.params if page_listener
assigns['flash'] = page_listener.flash.to_hash if page_listener
assigns['action_variables'] = page_listener.instance_variable_names.inject({}) { |memo, value| memo.merge(value => page_listener.instance_variable_get(value)) } if page_listener
assigns['now'] = Time.now
assigns['today'] = Date.today
assigns['site'] = RedmineCrm::SiteDrop.new
assigns['page_params'] = @page_params
registers = {}
registers[:page] = self
registers[:listener] = page_listener
::Liquid::Context.new({}, assigns, registers)
end
def self.find_by_path(path)
if path == '/' && RedmineCms.landing_page
CmsPage.find(RedmineCms.landing_page)
else
RedmineCms::Pages::Finder.find(path)
end
end
def delete_selected_attachments
if deleted_attachment_ids.present?
objects = attachments.where(:id => deleted_attachment_ids.map(&:to_i))
attachments.delete(objects)
end
end
def path
@path ||= self_and_ancestors.pluck(:slug).join('/')
end
alias_method :url, :path
def in_locale(page_locale = CmsSite.language)
return self if locale == page_locale
return false unless CmsSite.locales.include?(page_locale)
return CmsPage.find_by_path(page_locale) if id == RedmineCms.landing_page
default_path = path
if path =~ /^\/?(#{CmsSite.locales.join('|')})+(\/|$)/
self_locale = $1
default_path = path.gsub(/^\/?(#{CmsSite.locales.join('|')})+(\/|$)/, '')
default_path = '/' if default_path.blank?
end
if page_locale == Setting.default_language
localized_path = default_path
else
localized_path = [page_locale, default_path].join('/')
end
CmsPage.find_by_path(localized_path)
end
def locale
path.to_s[/^\/?(#{CmsSite.locales.join('|')})+(\/|$)/, 1] || Setting.default_language.to_s
end
def tree_kind
children_pages.any? ? 'dir' : 'file'
end
def page_icon
return 'locked' unless active?
return 'parent' if children_pages.any?
'single'
end
protected
def validate_page
if parent_id && parent_id_changed?
errors.add(:parent_id, :invalid) unless valid_parents.include?(parent)
end
end
private
def render_page_with_content_parts(page)
s = render_part(page)
page.parts.where(:name => 'content').active.order(:position).each do |page_part|
s << render_part(page_part)
end
s
end
def render_liquid(cms_object)
@context ||= initialize_context(@listener)
begin
old_object = @context.registers[:cms_object]
@context.registers[:cms_object] = cms_object
::Liquid::Template.parse(cms_object.content).render(@context).html_safe
rescue => e
e.message
ensure
@context.registers[:cms_object] = old_object
end
end
def render_object(cms_object)
text = render_liquid(cms_object)
text = cms_object.filter.filter(text, cms_object) if cms_object.respond_to? :filter_id
text = RedmineCms::HtmlCompressor::Compressor.new.compress(text) if false
text.html_safe
end
end

View File

@ -0,0 +1,44 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsPageField < ActiveRecord::Base
unloadable
include Redmine::SafeAttributes
belongs_to :page, class_name: 'CmsPage', foreign_key: 'page_id'
validates_presence_of :name
validates_uniqueness_of :name, :scope => 'page_id'
validates_format_of :name, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/i
attr_protected :id if ActiveRecord::VERSION::MAJOR <= 4
safe_attributes 'name',
'content',
'page_id'
after_commit :touch_page
private
def touch_page
return unless page
page.touch
page.expire_cache
end
end

View File

@ -0,0 +1,159 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsPageQuery < Query
VISIBILITY_PRIVATE = 0
VISIBILITY_ROLES = 1
VISIBILITY_PUBLIC = 2
self.queried_class = CmsPage
self.available_columns = []
def initialize(attributes = nil, *args)
super attributes
self.filters ||= {}
end
def initialize_available_filters
add_available_filter('name', :type => :string, :name => l(:label_cms_name))
add_available_filter('slug', :type => :string, :name => l(:label_cms_slug))
add_available_filter('title', :type => :string, :name => l(:label_cms_title))
add_available_filter('content', :type => :string, :name => l(:label_cms_content))
add_available_filter('field_name', :type => :string, :name => l(:label_cms_page_field_name))
add_available_filter('part_name', :type => :string, :name => l(:label_cms_page_part_name))
add_available_filter('part_description', :type => :string, :name => l(:label_cms_page_part_description))
add_available_filter('created_at', :type => :date_past, :name => l(:label_cms_created_at))
add_available_filter('updated_at', :type => :date_past, :name => l(:label_cms_updated_at))
add_available_filter('part_content', :type => :string, :name => l(:label_cms_page_part_content))
status_values = [[l(:label_cms_status_locked), RedmineCms::STATUS_LOCKED.to_s], [l(:label_cms_status_active), RedmineCms::STATUS_ACTIVE.to_s]]
add_available_filter('status_id', :type => :list_optional,
:values => status_values,
:name => l(:label_cms_status))
visibility_values = [[l(:field_admin), ''], ['Public', 'public'], ['Logged', 'logged']] + Group.where(:type => 'Group').map { |g| [g.name, g.id.to_s] }
add_available_filter('visibility', :type => :list_optional,
:values => visibility_values,
:name => l(:label_cms_visibility))
layout_values = CmsLayout.order(:name).map { |l| [l.name, l.id.to_s] }
add_available_filter('layout_id', :type => :list_optional,
:values => layout_values,
:name => l(:label_cms_layout))
tags_values = CmsPage.tags_cloud.map { |l| [l.name, l.id.to_s] }
add_available_filter('cms_page_tags', :type => :list_optional, :name => l(:label_cms_page_tags), :values => tags_values)
end
def self.visible(*args)
user = args.shift || User.current
scope = CmsPageQuery.all
if user.admin?
scope.where("#{table_name}.visibility <> ? OR #{table_name}.user_id = ?", VISIBILITY_PRIVATE, user.id)
elsif user.memberships.any?
scope.where("#{table_name}.visibility = ?" +
" OR (#{table_name}.visibility = ? AND #{table_name}.id IN (" +
"SELECT DISTINCT q.id FROM #{table_name} q" +
" INNER JOIN #{table_name_prefix}queries_roles#{table_name_suffix} qr on qr.query_id = q.id" +
" INNER JOIN #{MemberRole.table_name} mr ON mr.role_id = qr.role_id" +
" INNER JOIN #{Member.table_name} m ON m.id = mr.member_id AND m.user_id = ?" +
" WHERE q.project_id IS NULL OR q.project_id = m.project_id))" +
" OR #{table_name}.user_id = ?",
VISIBILITY_PUBLIC, VISIBILITY_ROLES, user.id, user.id)
elsif user.logged?
scope.where("#{table_name}.visibility = ? OR #{table_name}.user_id = ?", VISIBILITY_PUBLIC, user.id)
else
scope.where("#{table_name}.visibility = ?", VISIBILITY_PUBLIC)
end
end
def visible?(user=User.current)
return true if user.admin?
case visibility
when VISIBILITY_PUBLIC
true
when VISIBILITY_ROLES
Member.where(:user_id => user.id).joins(:roles).where(:member_roles => { :role_id => roles.map(&:id) }).any?
else
user == self.user
end
end
def objects_scope(options={})
scope = CmsPage.order(:name)
options[:search].split(' ').collect{ |search_string| scope = scope.live_search(search_string) } unless options[:search].blank?
scope = scope.includes((query_includes + (options[:include] || [])).uniq).
where(statement).
where(options[:conditions])
scope
end
def query_includes
[:layout, :author, :fields, :parts]
end
def results_scope(options = {})
objects_scope(options)
rescue ::ActiveRecord::StatementInvalid => e
raise StatementInvalid.new(e.message)
end
def sql_for_field_name_field(_field, operator, value)
string_filter_builder(operator, value, 'name', CmsPageField)
end
def sql_for_part_name_field(_field, operator, value)
string_filter_builder(operator, value, 'name', CmsPart)
end
def sql_for_part_description_field(_field, operator, value)
string_filter_builder(operator, value, 'description', CmsPart)
end
def sql_for_part_content_field(_field, operator, value)
string_filter_builder(operator, value, 'content', CmsPart)
end
def sql_for_cms_page_tags_field(_field, operator, value)
sw = ['!', '!~', '!*'].include?(operator) ? 'NOT' : ''
page_ids = RedmineCrm::Tagging.where(:taggable_type => CmsPage.name)
page_ids = page_ids.where(:tag_id => value) unless ['*', '!*'].include?(operator)
page_ids = page_ids.uniq.pluck(:taggable_id).push(0)
"(#{CmsPage.table_name}.id #{sw} IN (#{page_ids.join(',')}))"
end
def string_filter_builder(operator, value, filter_attr, filter_class)
sw = ['!', '!~', '!*'].include?(operator) ? 'NOT' : ''
case operator
when '=', '!'
like_value = "LIKE '#{value.first.to_s.downcase}'"
when '!*'
like_value = "IS NOT NULL OR #{filter_class.table_name}.#{filter_attr} = ''"
when '*'
like_value = "IS NOT NULL OR #{filter_class.table_name}.#{filter_attr} <> ''"
when '~', '!~'
like_value = "LIKE '%#{self.class.connection.quote_string(value.first.to_s.downcase)}%'"
end
fields_select = "SELECT #{filter_class.table_name}.page_id FROM #{filter_class.table_name} WHERE LOWER(#{filter_class.table_name}.#{filter_attr}) #{like_value}"
"(#{CmsPage.table_name}.id #{sw} IN (#{fields_select}))"
end
end

106
app/models/cms_part.rb Normal file
View File

@ -0,0 +1,106 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsPart < ActiveRecord::Base
unloadable
include Redmine::SafeAttributes
include RedmineCms::Filterable
belongs_to :page, class_name: 'CmsPage', foreign_key: 'page_id'
acts_as_attachable_cms
acts_as_versionable_cms
acts_as_positioned scope: :page_id
scope :active, lambda { where(:status_id => RedmineCms::STATUS_ACTIVE) }
after_commit :touch_page
validates_presence_of :name, :content, :page
validates_format_of :name, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/
[:content, :header, :footer, :sidebar].each do |name, _params|
src = <<-END_SRC
def is_#{name}_type?
self.name.strip.downcase == "#{name}"
end
END_SRC
class_eval src, __FILE__, __LINE__
end
attr_protected :id if ActiveRecord::VERSION::MAJOR <= 4
safe_attributes 'name',
'description',
'filter_id',
'status_id',
'page_id',
'position',
'is_cached',
'content'
def copy_from(arg)
part = arg.is_a?(CmsPart) ? arg : CmsPart.where(:id => arg).first
self.attributes = part.attributes.dup.except('id', 'created_at', 'updated_at') if part
self
end
def cache_key
"#{ page.cache_key + '/' if page.present?}#{super}"
end
def active?
self.status_id == RedmineCms::STATUS_ACTIVE
end
def to_s
ERB::Util.html_escape(name)
end
def digest
@generated_digest ||= digest!
end
def digest!
Digest::MD5.hexdigest(content)
end
def title
description.to_s.strip.blank? ? name : "#{description} (#{name})"
end
def self.find_part(*args)
if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/)
find_by_name(*args)
else
find(*args)
end
end
def set_content_type(response)
response.headers['Content-Type'] ||= filter.content_type.present? ? filter.content_type : 'text/html'
end
private
def touch_page
return unless page
page.reload.touch
page.expire_cache
end
end

View File

@ -0,0 +1,84 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsRedirect
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
include Redmine::SafeAttributes
attr_accessor :source_path, :destination_path
validates_presence_of :source_path, :destination_path
validates_format_of :source_path, :with => /\A(?!\d+$)\/[a-z0-9\-_\/]*\z/i, :message => 'Invalide format'
validates_length_of :source_path, :destination_path, :maximum => 200
validate :validate_redirect
def self.all
RedmineCms.redirects.map { |k, v| CmsRedirect.new(:source_path => k, :destination_path => v) }
end
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def save
if !valid?
errors.messages.each { |k, msg| Rails.logger.info "attribute #{k} #{msg.first}('#{send(k)}')" }
return false
end
redirects = Setting.plugin_redmine_cms['redirects'].is_a?(Hash) ? Setting.plugin_redmine_cms['redirects'] : {}
redirects.merge!(source_path => destination_path)
Setting.plugin_redmine_cms = Setting.plugin_redmine_cms.merge('redirects' => redirects)
end
def destroy
redirects = Setting.plugin_redmine_cms['redirects'].is_a?(Hash) ? Setting.plugin_redmine_cms['redirects'] : {}
redirects.delete(source_path)
Setting.plugin_redmine_cms = Setting.plugin_redmine_cms.merge('redirects' => redirects)
true
end
def to_param
if source_path == '/'
'_'
else
source_path.parameterize
end
end
def persisted?
false
end
private
def validate_redirect
if source_path.to_s.start_with?('/admin', '/settings', '/users', '/groups', '/plugins', '/cms')
errors.add :source_path, 'Invalid source path'
end
begin
Rails.application.routes.recognize_path(source_path.to_s)
rescue => e
errors.add :source_path, e.message
end
end
end

View File

@ -0,0 +1,93 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsRedmineLayout
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
include Redmine::SafeAttributes
attr_accessor :redmine_action, :cms_layout_id
validates_presence_of :redmine_action, :cms_layout_id
validates_length_of :redmine_action, :maximum => 200
validate :validate_redmine_layout
def self.all
redmine_layouts = Setting.plugin_redmine_cms['redmine_layouts'].is_a?(Hash) ? Setting.plugin_redmine_cms['redmine_layouts'] : {}
redmine_layouts.map { |k, v| CmsRedmineLayout.new(:redmine_action => k, :cms_layout_id => v) }
end
def self.[](key)
all.detect { |l| l.redmine_action.to_s.downcase.strip == key.to_s.downcase.strip }
end
def self.find_by_action(ctrl, actn)
all.select { |l| l.controller == ctrl.to_s.downcase.strip }.detect { |l| l.action == actn.to_s.downcase.strip || l.action.blank? }
end
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def layout
CmsLayout.where(:id => cms_layout_id).first
end
def controller
redmine_action.to_s.downcase.gsub(' ', '').split('#')[0]
end
def action
redmine_action.to_s.downcase.gsub(' ', '').split('#')[1]
end
def save
if !valid?
errors.messages.each { |k, msg| Rails.logger.info "attribute #{k} #{msg.first}('#{send(k)}')" }
return false
end
redmine_layouts = Setting.plugin_redmine_cms["redmine_layouts"].is_a?(Hash) ? Setting.plugin_redmine_cms['redmine_layouts'] : {}
redmine_layouts.merge!(redmine_action => cms_layout_id)
Setting.plugin_redmine_cms = Setting.plugin_redmine_cms.merge('redmine_layouts' => redmine_layouts)
end
def destroy
redmine_layouts = Setting.plugin_redmine_cms['redmine_layouts'].is_a?(Hash) ? Setting.plugin_redmine_cms['redmine_layouts'] : {}
redmine_layouts.delete(redmine_action)
Setting.plugin_redmine_cms = Setting.plugin_redmine_cms.merge('redmine_layouts' => redmine_layouts)
true
end
def to_param
redmine_action
end
def persisted?
false
end
private
def validate_redmine_layout
errors.add :cms_layout, 'Invalid layout' unless CmsLayout.where(:id => cms_layout_id).first.present?
end
end

53
app/models/cms_site.rb Normal file
View File

@ -0,0 +1,53 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
require 'singleton'
class CmsSite
unloadable
include RedmineCms::ActsAsAttachableCms
include Singleton
include Redmine::I18n
@@language = Setting.default_language || 'en'
acts_as_attachable_cms
class << self
def locales
@locales ||= valid_languages.map { |l| l.to_s.downcase }
end
def language
if RedmineCms.use_localization?
@@language || Setting.default_language || 'en'
else
Setting.default_language || 'en'
end
end
def language=(lang)
@@language = CmsSite.locales.include?(lang) && lang
end
end
def attachments
Attachment.where(:container_type => self.class.name)
end
end

61
app/models/cms_snippet.rb Normal file
View File

@ -0,0 +1,61 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsSnippet < ActiveRecord::Base
unloadable
include Redmine::SafeAttributes
include RedmineCms::Filterable
acts_as_attachable_cms
acts_as_versionable_cms
validates_presence_of :name, :content
validates_format_of :name, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/
after_commit :expire_cache
attr_protected :id if ActiveRecord::VERSION::MAJOR <= 4
safe_attributes 'name',
'filter_id',
'description',
'content'
def used_in_pages
CmsPage.where("#{CmsPage.table_name}.content LIKE '%{% render_snippet '?' %}%'", name).order(:name)
end
def copy_from(arg)
snippet = arg.is_a?(CmsSnippet) ? arg : CmsSnippet.where(:id => arg).first
self.attributes = snippet.attributes.dup.except("id", "created_at", "updated_at") if snippet
self
end
def digest
@generated_digest ||= digest!
end
def digest!
Digest::MD5.hexdigest(content)
end
def expire_cache
used_in_pages.includes(:parts).map(&:expire_cache)
CmsPage.joins(:layout).where("#{CmsLayout.table_name}.content LIKE '%{% render_snippet '?' %}%'", name).map(&:expire_cache)
end
end

View File

@ -0,0 +1,86 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class CmsVariable
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
include Redmine::SafeAttributes
attr_accessor :name, :value
validates_presence_of :name, :value
validates_length_of :name, :maximum => 30
validates_format_of :name, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/
validate :validate_cms_variable
def self.all
cms_variables = Setting.plugin_redmine_cms["cms_variables"].is_a?(Hash) ? Setting.plugin_redmine_cms['cms_variables'] : {}
cms_variables.map { |k, v| CmsVariable.new(:name => k, :value => v) }
end
def self.[](key)
all.detect { |l| l.name.to_s.downcase.strip == key.to_s.downcase.strip }
end
def self.find_by_action(ctrl, actn)
all.select { |l| l.controller == ctrl.to_s.downcase.strip }.detect { |l| l.action == actn.to_s.downcase.strip || l.action.blank? }
end
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def save
if !valid?
errors.messages.each { |k, msg| Rails.logger.info "attribute #{k} #{msg.first}('#{send(k)}')" }
return false
end
cms_variables = Setting.plugin_redmine_cms['cms_variables'].is_a?(Hash) ? Setting.plugin_redmine_cms['cms_variables'] : {}
cms_variables.merge!(name => value)
Setting.plugin_redmine_cms = Setting.plugin_redmine_cms.merge('cms_variables' => cms_variables)
end
def destroy
cms_variables = Setting.plugin_redmine_cms['cms_variables'].is_a?(Hash) ? Setting.plugin_redmine_cms['cms_variables'] : {}
cms_variables.delete(name)
Setting.plugin_redmine_cms = Setting.plugin_redmine_cms.merge('cms_variables' => cms_variables)
true
end
def to_param
name
end
def persisted?
false
end
def attributes
{ :name => name, :value => value }
end
private
def validate_cms_variable
end
end

50
app/models/pages_part.rb Normal file
View File

@ -0,0 +1,50 @@
# This file is a part of Redmin CMS (redmine_cms) plugin,
# CMS plugin for redmine
#
# Copyright (C) 2011-2021 RedmineUP
# http://www.redmineup.com/
#
# redmine_cms is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# redmine_cms is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with redmine_cms. If not, see <http://www.gnu.org/licenses/>.
class PagesPart < ActiveRecord::Base
unloadable
include Redmine::SafeAttributes
belongs_to :page, class_name: 'CmsPage', foreign_key: 'page_id'
belongs_to :part, class_name: 'CmsPart', foreign_key: 'part_id'
rcrm_acts_as_list scope: 'page_id = \'#{page_id}\''
scope :active, lambda { where(:status_id => RedmineCms::STATUS_ACTIVE) }
scope :order_by_type, lambda { includes(:part).order("#{CmsPart.table_name}.part_type").order(:position) }
before_destroy :touch_page
after_save :touch_page
validates_presence_of :page, :part
def active?
status_id == RedmineCms::STATUS_ACTIVE
end
attr_protected :id if ActiveRecord::VERSION::MAJOR <= 4
safe_attributes 'page',
'part'
private
def touch_page
page.touch
end
end

View File

@ -0,0 +1,48 @@
<%= call_hook :view_account_login_top %>
<div id="login-form">
<%= form_tag(signin_path) do %>
<%= back_url_hidden_field_tag %>
<table>
<% if Setting.self_registration? %>
<tr>
<td align="right"></td>
<td align="left"><%= link_to l(:label_register), register_path %></td>
</tr>
<% end %>
<tr>
<td align="right"><label for="username"><%=l(:field_login)%>:</label></td>
<td align="left"><%= text_field_tag 'username', nil, :tabindex => '1' %></td>
</tr>
<tr>
<td align="right"><label for="password"><%=l(:field_password)%>:</label></td>
<td align="left"><%= password_field_tag 'password', nil, :tabindex => '2' %></td>
</tr>
<% if Setting.openid? %>
<tr>
<td align="right"><label for="openid_url"><%=l(:field_identity_url)%></label></td>
<td align="left"><%= text_field_tag "openid_url", nil, :tabindex => '3' %></td>
</tr>
<% end %>
<tr>
<td></td>
<td align="left">
<% if Setting.autologin? %>
<label for="autologin"><%= check_box_tag 'autologin', 1, false, :tabindex => 4 %> <%= l(:label_stay_logged_in) %></label>
<% end %>
</td>
</tr>
<tr>
<td align="left">
<% if Setting.lost_password? %>
<%= link_to l(:label_password_lost), lost_password_path %>
<% end %>
</td>
<td align="right">
<input type="submit" name="login" value="<%=l(:button_login)%> &#187;" tabindex="6"/>
</td>
</tr>
</table>
<%= javascript_tag "$('#username').focus();" %>
<% end %>
</div>
<%= call_hook :view_account_login_bottom %>

View File

@ -0,0 +1,6 @@
<%= raw @cms_page_tags.map { |cms_page_tag| {
'id' => @names_only ? cms_page_tag.name : cms_page_tag.id,
'text' => cms_page_tag.name
}
}.to_json
%>

View File

@ -0,0 +1,33 @@
<% html_title(l(:label_cms_edit_assets)) -%>
<h2><%= l(:label_cms_edit_assets) %></h2>
<%= error_messages_for *@attachments %>
<%= form_tag(cms_assets_update_path, :method => 'patch') do %>
<%= back_url_hidden_field_tag %>
<%= hidden_field_tag :ids, @attachments.map(&:id) %>
<div class="box attachments">
<table>
<% @attachments.each do |attachment| %>
<tr>
<td colspan="2">
<span class="icon icon-attachment"><%= attachment.filename_was %></span>
<span class="size">(<%= number_to_human_size attachment.filesize %>)</span>
<span class="author"><%= attachment.author %>, <%= format_time(attachment.created_on) %></span>
</td>
</tr>
<tr id="attachment-<%= attachment.id %>">
<td><%= text_field_tag "attachments[#{attachment.id}][filename]", attachment.filename, :size => 40 %></td>
<td>
<%= text_field_tag "attachments[#{attachment.id}][description]", attachment.description, :size => 80, :placeholder => l(:label_optional_description) %>
</td>
</tr>
<% end %>
</table>
</div>
<p>
<%= submit_tag l(:button_save) %>
<%= link_to l(:button_cancel), back_url if back_url.present? %>
</p>
<% end %>

View File

@ -0,0 +1,52 @@
<div class="contextual">
<%= link_to(l(:label_attachment_new), new_cms_asset_path, :class => 'icon icon-add') %>
</div>
<h2><%=l(:label_cms_asset_plural)%></h2>
<%= form_tag(cms_assets_path, :multipart => true, :class => "tabular") do %>
<div class="box">
<table class="list files">
<thead><tr>
<th><%= l(:label_cms_thumbnail) %></th>
<%= sort_header_tag('filename', :caption => l(:field_filename)) %>
<%= sort_header_tag('description', :caption => l(:field_description)) %>
<%= sort_header_tag('created_on', :caption => l(:label_date), :default_order => 'desc') %>
<%= sort_header_tag('size', :caption => l(:field_filesize), :default_order => 'desc') %>
<th></th>
</tr></thead>
<tbody>
<% @attachments.each do |file| %>
<tr class="file <%= cycle("odd", "even") %>">
<td class="thumbnail"><%= file.thumbnailable? ? cms_thumbnail_tag(file) : link_to('', download_named_asset_path(file, file.filename),:class => 'icon icon-file') %></td>
<td class="filename"><%= link_to file.filename, named_asset_path(file, file.filename), :title => file.description %></td>
<td class="name description"><%= file.description %></td>
<td class="created_on"><%= format_time(file.created_on) %></td>
<td class="filesize"><%= number_to_human_size(file.filesize) %></td>
<td class="buttons">
<%= link_to(l(:label_edit_attachments),
cms_assets_edit_path(:id => file.id),
:title => l(:label_edit_attachments),
:class => 'icon-only icon-edit'
) %>
<%= link_to(image_tag('delete.png'), cms_asset_path(file),
:data => {:confirm => l(:text_are_you_sure)}, :method => :delete) if User.current.admin? %>
</td>
</tr>
<% end %>
</tbody>
</table>
<p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form' %></p>
</div>
<%= submit_tag l(:button_add) %>
<% end %>
<% content_for :sidebar do %>
<% render :partial => 'cms_settings/menu' %>
<% end %>
<% html_title(l(:label_cms_asset_plural)) -%>

View File

@ -0,0 +1,11 @@
<% html_title(l(:label_attachment_new)) -%>
<h2><%=l(:label_attachment_new)%></h2>
<%= error_messages_for 'attachment' %>
<%= form_tag(cms_assets_path, :multipart => true, :class => "tabular") do %>
<div class="box">
<p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form' %></p>
</div>
<%= submit_tag l(:button_add) %>
<% end %>

View File

@ -0,0 +1,38 @@
<div class="attachments">
<div class="contextual">
<%= link_to image_tag('edit.png'),
container_attachments_edit_path(container),
:title => l(:label_edit_attachments) if options[:editable] %>
</div>
<% for attachment in attachments %>
<p><%= link_to_attachment attachment, :class => 'icon icon-attachment', :download => true -%>
<% if attachment.is_text? %>
<%= link_to image_tag('magnifier.png'),
:controller => 'attachments', :action => 'show',
:id => attachment, :filename => attachment.filename %>
<% end %>
<%= h(" - #{attachment.description}") unless attachment.description.blank? %>
<span class="size">(<%= number_to_human_size attachment.filesize %>)</span>
<% if options[:deletable] %>
<%= link_to image_tag('delete.png'), cms_asset_path(attachment),
:data => {:confirm => l(:text_are_you_sure)},
:method => :delete,
:class => 'delete',
:title => l(:button_delete) %>
<% end %>
<% if options[:author] %>
<span class="author"><%= h(attachment.author) %>, <%= format_time(attachment.created_on) %></span>
<% end %>
</p>
<% end %>
<% if defined?(thumbnails) && thumbnails %>
<% images = attachments.select(&:thumbnailable?) %>
<% if images.any? %>
<div class="thumbnails">
<% images.each do |attachment| %>
<div><%= thumbnail_tag(attachment) %></div>
<% end %>
</div>
<% end %>
<% end %>
</div>

View File

@ -0,0 +1,10 @@
<div class="contextual">
<p><%= link_to l(:button_save), cms_export_path(@cms_object.id, :object_type => @cms_object.class.name.underscore, :attachment_content => @cms_export.attachment_content, :format => 'yaml'), :class => 'icon icon-save' %></p>
</div>
<h2><%=l(:label_cms_exported_object)%></h2>
<div class="wiki">
<pre>
<%= @export_content %>
</pre>
</div>

View File

@ -0,0 +1,10 @@
<h2><%=l(:label_cms_exports_new)%></h2>
<%= labelled_form_for :cms_export, @cms_export, :url => { :action => 'create', :object_type => @cms_export.object_type } do |f| %>
<div class="box tabular">
<%= f.hidden_field :id %>
<%= f.hidden_field :object_type %>
<p><%= f.check_box :attachment_content, :label => l(:label_cms_exports_attachment_content) %></p>
</div>
<%= submit_tag l(:button_export) %>
<% end %>

View File

@ -0,0 +1,12 @@
<h2><%=l(:label_cms_website_exports_new)%></h2>
<%= form_tag cms_export_website_path do %>
<div class="box tabular">
<%= hidden_field_tag :object_type, 'website' %>
<p>
<%= label_tag l(:label_cms_exports_attachment_content)%>
<%= check_box_tag :attachment_content %>
</p>
</div>
<%= submit_tag l(:button_export) %>
<% end %>

View File

@ -0,0 +1,9 @@
<div class="history-link">
<%= link_to(("\xc2\xab " + l(:label_previous)), send("preview_#{history_context_menu.class.name.underscore}_path", history_context_menu, :version => history_context_menu.previous_version.version)) + " - " if history_context_menu.previous_version %>
<%= "#{l(:label_version)} #{history_context_menu.version}/#{link_to @current_version, send("preview_#{history_context_menu.class.name.underscore}_path", history_context_menu)}".html_safe %>
<%= '('.html_safe + link_to(l(:label_diff), cms_object_diff_path(history_context_menu.id, :object_type => history_context_menu.class.name.underscore,
:version => history_context_menu.version)) + ')'.html_safe if history_context_menu.previous_version %>
<%= (" - " + link_to((l(:label_next) + " \xc2\xbb"), send("preview_#{history_context_menu.class.name.underscore}_path", history_context_menu, :version => history_context_menu.next_version.version))).html_safe if history_context_menu.next_version %>
</div>
<hr>
<%= link_to l(:button_rollback), send("edit_#{history_context_menu.class.name.underscore}_path", history_context_menu, :version => params[:version]), :class => 'icon icon-cancel' %>

View File

@ -0,0 +1,35 @@
<div class="contextual">
<%= link_to(l(:button_edit), send("edit_#{@cms_object.class.name.underscore}_path", @cms_object), :class => 'icon icon-edit') %>
<%= link_to(l(:label_history),
{:action => 'history', :id => @cms_object}, :class => 'icon icon-history') %>
</div>
<%= cms_title [@cms_object.name, send("#{@cms_object.class.name.underscore}_path", @cms_object, :version => nil)], l(:label_history) %>
<p>
<em><%= @annotate.content.author ? link_to_user(@annotate.content.author) : l(:label_user_anonymous)
%>, <%= format_time(@annotate.content.updated_at) %> </em><br />
<%= @annotate.content.comments %>
</p>
<% colors = Hash.new {|k,v| k[v] = (k.size % 12) } %>
<table class="filecontent annotate">
<tbody>
<% line_num = 1 %>
<% @annotate.lines.each do |line| -%>
<tr class="bloc-<%= colors[line[0]] %>">
<th class="line-num"><%= line_num %></th>
<td class="revision"><%= link_to line[0], send("#{@cms_object.class.name.underscore}_path", @cms_object, :version => line[0]) %></td>
<td class="author"><%= line[1] %></td>
<td class="line-code"><pre><%= line[2] %></pre></td>
</tr>
<% line_num += 1 %>
<% end -%>
</tbody>
</table>
<% content_for :header_tags do %>
<%= stylesheet_link_tag 'scm' %>
<% end %>

View File

@ -0,0 +1,24 @@
<div class="contextual">
<%= link_to l(:label_history), cms_object_history_path(@cms_object, :object_type => @cms_object.class.name.underscore),
:class => 'icon icon-history' %>
</div>
<%= cms_title [@cms_object.name, send("preview_#{@cms_object.class.name.underscore}_path", @cms_object, :version => nil)],
[l(:label_history), cms_object_history_path(@cms_object.id, :object_type => @cms_object.class.name.underscore)],
"#{l(:label_version)} #{@diff.version_to.version}" %>
<p>
<%= l(:label_version) %> <%= link_to @diff.version_from.version, send("preview_#{@cms_object.class.name.underscore}_path", @cms_object, :version => @diff.version_from.version) %>
<em>(<%= @diff.version_from.author ?
@diff.version_from.author.name : l(:label_user_anonymous)
%>, <%= format_time(@diff.version_from.updated_at) %>)</em>
&#8594;
<%= l(:label_version) %> <%= link_to @diff.version_to.version, send("preview_#{@cms_object.class.name.underscore}_path", @cms_object, :version => @diff.version_to.version)
%>/<%= @cms_object.version %>
<em>(<%= @diff.version_to.author ?
link_to_user(@diff.version_to.author.name) : l(:label_user_anonymous)
%>, <%= format_time(@diff.version_to.updated_at) %>)</em>
</p>
<div class="text-diff">
<%= simple_format_without_paragraph @diff.to_html %>
</div>

View File

@ -0,0 +1,38 @@
<%= cms_title [@cms_object.name, send("edit_#{@cms_object.class.name.underscore}_path", @cms_object, :version => nil)], l(:label_history) %>
<%= form_tag(cms_object_diff_path(@cms_object.id, :object_type => @cms_object.class.name.underscore),
:method => :get) do %>
<table class="list wiki-page-versions">
<thead>
<tr>
<th>#</th>
<th></th>
<th></th>
<th><%= l(:field_updated_on) %></th>
<th><%= l(:field_author) %></th>
<th><%= l(:field_comments) %></th>
<th></th>
</tr>
</thead>
<tbody>
<% show_diff = @versions.size > 1 %>
<% line_num = 1 %>
<% @versions.reverse.each do |ver| %>
<tr class="wiki-page-version <%= cycle("odd", "even") %>">
<td class="id"><%= link_to ver.version, send("preview_#{@cms_object.class.name.underscore}_path", @cms_object, :version => ver.version) %></td>
<td class="checkbox"><%= radio_button_tag('version', ver.version, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('#cbto-#{line_num+1}').prop('checked', true);") if show_diff && (line_num < @versions.size) %></td>
<td class="checkbox"><%= radio_button_tag('version_from', ver.version, (line_num==2), :id => "cbto-#{line_num}") if show_diff && (line_num > 1) %></td>
<td class="updated_on"><%= format_time(ver.updated_at) %></td>
<td class="author"><%= link_to_user ver.author %></td>
<td class="comments"><%= ver.comments %></td>
<td class="buttons">
<%= link_to l(:button_annotate), cms_object_annotate_path(@cms_object.id, :object_type => @cms_object.class.name.underscore, :version => ver.version) %>
<%= delete_link send("#{@cms_object.class.name.underscore}_path", @cms_object, :version => ver.version) if @version_count > 1 %>
</td>
</tr>
<% line_num += 1 %>
<% end %>
</tbody>
</table>
<%= submit_tag l(:label_view_diff), :class => 'small' if show_diff %>
<% end %>

View File

@ -0,0 +1,18 @@
<h2><%=l(:label_crm_import_result)%></h2>
<div class="wiki">
<% if @cms_object.errors.empty? %>
<%= t(:label_crm_import_success) %>
<br>
<%= link_to @cms_object.name, edit_cms_page_path(@cms_object) if @cms_object.class.name == 'CmsPage' %>
<%= link_to @cms_object.name, edit_cms_snippet_path(@cms_object) if @cms_object.class.name == 'CmsSnippet' %>
<%= link_to @cms_object.name, edit_cms_layout_path(@cms_object) if @cms_object.class.name == 'CmsLayout' %>
<% else %>
<%= t(:label_crm_import_failed) %>
<ul>
<% @cms_object.errors.full_messages.each do |error| %>
<li><%= error %></li>
<% end %>
</ul>
<% end %>
</div>

View File

@ -0,0 +1,10 @@
<h2><%=l(:label_cms_imports_new)%></h2>
<%= labelled_form_for :cms_import, @cms_import, :url => { :action => 'create', :object_type => @cms_import.object_type }, :multipart => true do |f| %>
<div class="box tabular">
<%= f.hidden_field :object_type %>
<p><%= f.file_field :file, :label => l(:label_crm_import_file), :accept => "text/yaml" %></p></p>
<p><%= f.check_box :rewrite, :label => l(:label_cms_import_rewrite_existed) %></p>
</div>
<%= submit_tag l(:button_import) %>
<% end %>

View File

@ -0,0 +1,16 @@
<h2><%=l(:label_cms_website_imports_new)%></h2>
<%= form_tag cms_import_website_path, :multipart => true do %>
<div class="box tabular">
<%= hidden_field_tag :object_type, 'website' %>
<p>
<%= label_tag l(:label_crm_import_file)%>
<%= file_field_tag :file, :accept => "text/yaml" %>
</p>
<p>
<%= label_tag l(:label_cms_import_rewrite_existed)%>
<%= check_box_tag :rewrite %>
</p>
</div>
<%= submit_tag l(:button_import) %>
<% end %>

View File

@ -0,0 +1,29 @@
<%= error_messages_for 'cms_layout' %>
<div class="box tabular">
<div class="splitcontent">
<div class="splitcontentleft">
<p><%= f.text_field :name, :size => 30, :required => true, :label => l(:label_cms_name) %></p>
</div>
<div class="splitcontentright">
<p><%= f.text_field :content_type, :label => l(:label_cms_content_type) %></p>
</div>
</div>
<p id="attachments_form"><label><%= l(:label_attachment_plural) %></label><%= render :partial => 'attachments/form', :locals => {:container => @cms_layout} %></p>
<%= text_area_tag 'cms_layout[content]', @cms_layout.content, :cols => 100, :rows => 30, :class => 'wiki-edit' %>
<%= javascript_tag "editor = activateCodeMirror('cms_layout_content');" %>
<%= javascript_tag "$('.contextual.page-edit').draggable();" %>
</div>
<% content_for :header_tags do %>
<%= code_mirror_tags %>
<%= javascript_include_tag 'redmine_cms', :plugin => 'redmine_cms' %>
<% end %>

View File

@ -0,0 +1,29 @@
<div class="contextual">
<%= link_to l(:label_preview), preview_cms_layout_path(@cms_layout), :class => 'icon icon-preview' %>
<%= link_to l(:label_history), cms_object_history_path(@cms_layout, :object_type => @cms_layout.class.name.underscore), :class => 'icon icon-history' if @cms_layout.version > 1 %>
<%= link_to l(:button_export), cms_export_path(@cms_layout.id, :object_type => @cms_layout.class.name.underscore), :class => 'icon icon-save' %>
<%= link_to l(:button_delete), cms_layout_path(@cms_layout), :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %>
</div>
<h2><%= link_to l(:label_cms_layout_plural), cms_layouts_path %> &#187; <%= @cms_layout.name %></h2>
<%= error_messages_for 'cms_layout' %>
<%= labelled_form_for :cms_layout, @cms_layout, :url => { :action => 'update', :id => @cms_layout},
:html => { :id => 'cms_layout_form', :multi_part => true, :method => :put } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<% if @cms_layout.attachments.any? %>
<fieldset><legend><%= l(:label_attachment_plural) %></legend>
<%= link_to_cms_attachments @cms_layout, :thumbnails => true %>
</fieldset><br/>
<% end %>
<%= submit_tag l(:button_save) %>
<% end %>
<% content_for :sidebar do %>
<% render :partial => 'cms_settings/menu' %>
<% end %>
<% html_title(@cms_layout.name) -%>

View File

@ -0,0 +1,38 @@
<div class="contextual">
<%= link_to l(:label_cms_layout_new), new_cms_layout_path, :class => 'icon icon-add' %>
<%= link_to l(:button_import), cms_import_path(:object_type => CmsLayout.name.underscore), :class => 'icon icon-duplicate' %>
</div>
<h2><%=l(:label_cms_layout_plural)%></h2>
<div class="autoscroll">
<% if @cms_layouts.any? %>
<table class="list">
<thead>
<tr>
<th><%= l(:label_cms_name) %></th>
<th><%= l(:label_cms_content_type) %></th>
<th style="width:10%;"> </th>
</tr>
</thead>
<tbody>
<% @cms_layouts.each do |cms_layout| -%>
<tr class="cms_layout <%= cycle("odd", "even") %>" id="layout_<%= cms_layout.id %>">
<td class="name"><%= link_to h(cms_layout.name), edit_cms_layout_path(cms_layout) %></td>
<td class="type"><%= cms_layout.content_type.blank? ? "text/html" : cms_layout.content_type %></td>
<td class="buttons">
<%= link_to l(:label_history), cms_object_history_path(cms_layout, :object_type => cms_layout.class.name.underscore), :class => 'icon icon-history' if cms_layout.version > 1 %>
<%= delete_link cms_layout_path(cms_layout) %>
</td>
</tr>
<% end -%>
</tbody>
</table>
<% end %>
</div>
<% content_for :sidebar do %>
<% render :partial => 'cms_settings/menu' %>
<% end %>
<% html_title(l(:label_cms_layout_plural)) -%>

View File

@ -0,0 +1,9 @@
<h2><%= link_to l(:label_cms_layout_plural), cms_layouts_path %> &#187; <%=l(:label_cms_layout_new)%></h2>
<%= labelled_form_for :cms_layout, @cms_layout, :url => { :action => 'create', :project_id => @project },
:html => { :id => 'layout-form', :multipart => true } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_create) %>
<% end %>
<% html_title(l(:label_cms_layout_new)) -%>

View File

@ -0,0 +1,42 @@
<table class="list">
<thead><tr>
<th><%= l(:label_cms_menu_name) %></th>
<th><%= l(:label_cms_menu_caption) %></th>
<th></th>
<th></th>
</tr></thead>
<% previous_group = false %>
<tbody>
<% cms_menus_tree(@cms_menus) do |menu, level| -%>
<% if menu.menu_type != previous_group %>
<% reset_cycle %>
</tbody>
<tbody>
<tr class="group open">
<td colspan="5">
<span class="expander" onclick="toggleRowGroup(this);">&nbsp;</span>
<%= menu.menu_type.blank? ? 'None' : menu.menu_type %>
<%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}", "toggleAllRowGroups(this)", :class => 'toggle-all') %>
</td>
</tr>
<% previous_group = menu.menu_type %>
<% end %>
<tr class="menu project <%= cycle("odd", "even") %> <%= %w(disabled active)[menu.status_id] %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>" id="cms_menu_<%= menu.id %>">
<td class="name"><span><%= link_to menu.name, edit_cms_menu_path(menu), :title => menu.path %></span></td>
<td class="name"><%= link_to menu.caption.html_safe, edit_cms_menu_path(menu), :title => menu.path %></td>
<td>
<%= stocked_reorder_link(menu, 'menu', {:controller => 'cms_menus', :action => 'update', :id => menu}, :put) %>
</td>
<td class="buttons">
<%= cms_change_status_link('menu', menu) %>
<%= delete_link cms_menu_path(menu, :back_url => cms_menus_path) %>
</td>
</tr>
<% end -%>
</tbody>
</table>
<%= javascript_tag do %>
$(function() { $("table.list tbody").positionedItems(); });
<% end %>

View File

@ -0,0 +1,44 @@
<%= error_messages_for 'cms_menu' %>
<div class="box tabular attributes splitcontent">
<div class="splitcontentleft">
<p><%= f.text_field :name, :size => 30, :required => true, :label => l(:label_cms_menu_name) %></p>
<p><%= f.text_field :caption, :required => true, :label => l(:label_cms_menu_caption), :style => "width: 99%;" %></p>
<p><%= f.text_field :path, :required => true, :label => l(:label_cms_menu_path) %></p>
<p><%= f.text_field :position, :size => 5, :required => true, :label => l(:label_cms_position) %></p>
</div>
<div class="splitcontentright">
<p><%= f.select :menu_type, options_for_select([["top_menu", "top_menu"], ["account_menu", "account_menu"]], @cms_menu.menu_type), :label => l(:label_cms_menu_type) %></p>
<p><%= f.select :parent_id, menus_options_for_select(@cms_menu.valid_parents), :include_blank => true, :label => :label_cms_menu_parent, :disabled => @cms_menu.menu_type == 'top_menu' %></p>
<p><%= f.select :status_id, options_for_select([["Disabled", 0], ["Active", 1]], @cms_menu.status_id), :label => l(:label_cms_menu_status) %></p>
<p><%= f.select :visibility, cms_visibilities_for_select(@cms_menu.visibility || "public"), :label => l(:label_cms_visibility) %></p>
</div>
</div>
<script type="text/javascript">
function disableParentSelect(node){
if ($(node).val() != 'top_menu'){
$("#cms_menu_parent_id").val('');
$("#cms_menu_parent_id").parent('p').hide();
}
else{
$("#cms_menu_parent_id").parent('p').show();
$.ajax({
url: '<%= cms_menus_parent_menu_options_path(:id => @cms_menu) %>',
type: 'get',
data: {
menu_type: $(node).val()
}
});
}
}
if ($("#cms_menu_parent_id").length == 1){
// disableParentSelect($("#cms_menu_menu_type"));
$("#cms_menu_menu_type").change(function(){
disableParentSelect(this);
});
}
</script>

View File

@ -0,0 +1,2 @@
$('#cms_menus').html('<%= escape_javascript(render :partial => 'cms_menus/cms_menus') %>');
$('#cms_menu_<%= @cms_menu.id %>').effect("highlight");

View File

@ -0,0 +1,9 @@
<h2><%= link_to l(:label_cms_menu_plural), cms_menus_path %> &#187; <%=l(:label_cms_menu)%> <%= @cms_menu.name %></h2>
<%= labelled_form_for :menu, @cms_menu, :url => { :action => 'update', :id => @cms_menu},
:html => { :id => 'cms_menu-form', :method => :put } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_save) %>
<% end %>
<% html_title(l(:label_cms_menu_edit)) -%>

View File

@ -0,0 +1,17 @@
<div class="contextual">
<%= link_to l(:label_cms_menu_new), new_cms_menu_path, :class => 'icon icon-add' %>
</div>
<h2><%=l(:label_cms_menu_plural)%></h2>
<div class="autoscroll" id="cms_menus">
<%= render :partial => 'cms_menus/cms_menus' %>
</div>
<% content_for :sidebar do %>
<% render :partial => 'cms_settings/menu' %>
<% end %>
<% html_title(l(:label_cms_menu_plural)) -%>

View File

@ -0,0 +1,9 @@
<h2><%=l(:label_cms_menu_new)%></h2>
<%= labelled_form_for :menu, @cms_menu, :url => { :action => 'create', :id => @cms_menu},
:html => { :id => 'cms_menu-form', :method => :post } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_create) %>
<% end %>
<% html_title(l(:label_cms_menu_new)) -%>

View File

@ -0,0 +1,2 @@
<% select = content_tag('select', content_tag('option') + options_for_select(menus_options_for_select(@cms_menu.valid_parents)), :id => 'cms_menu_parent_id', :name => 'cms_menu[parent_id]') %>
$('#cms_menu_parent_id').replaceWith('<%= escape_javascript(select) %>');

View File

@ -0,0 +1,24 @@
<%= javascript_tag do %>
var operatorLabels = <%= raw_json Query.operators_labels %>;
var operatorByType = <%= raw_json Query.operators_by_filter_type %>;
var availableFilters = <%= raw_json query.available_filters_as_json %>;
var labelDayPlural = <%= raw_json l(:label_day_plural) %>;
var allProjects = <%= raw_json query.all_projects_values %>;
$(document).ready(function(){
initFilters();
<% query.filters.each do |field, options| %>
addFilter("<%= field %>", <%= raw_json query.operator_for(field) %>, <%= raw_json query.values_for(field) %>);
<% end %>
});
<% end %>
<table id="filters-table">
</table>
<div class="add-filter">
<%= label_tag('add_filter_select', l(:label_filter_add)) %>
<%= select_tag 'add_filter_select', filters_options_for_select(query), :name => nil %>
</div>
<%= hidden_field_tag 'f[]', '' %>
<% include_calendar_headers_tags %>

View File

@ -0,0 +1,38 @@
<%= error_messages_for 'query' %>
<div class="box">
<div class="tabular">
<p><label for="query_name"><%=l(:field_name)%></label>
<%= text_field 'query', 'name', :size => 80 %></p>
<% if User.current.admin? || User.current.allowed_to?(:manage_public_queries, @query.project) %>
<p><label><%=l(:field_visible)%></label>
<label class="block"><%= radio_button 'query', 'visibility', Query::VISIBILITY_PRIVATE %> <%= l(:label_visibility_private) %></label>
<label class="block"><%= radio_button 'query', 'visibility', Query::VISIBILITY_PUBLIC %> <%= l(:label_visibility_public) %></label>
<label class="block"><%= radio_button 'query', 'visibility', Query::VISIBILITY_ROLES %> <%= l(:label_visibility_roles) %>:</label>
<% Role.givable.sorted.each do |role| %>
<label class="block role-visibility"><%= check_box_tag 'query[role_ids][]', role.id, @query.roles.include?(role), :id => nil %> <%= role.name %></label>
<% end %>
<%= hidden_field_tag 'query[role_ids][]', '' %>
</p>
<% end %>
</div>
<fieldset id="filters"><legend><%= l(:label_filter_plural) %></legend>
<%= render :partial => 'queries/filters', :locals => {:query => query}%>
</fieldset>
</div>
<%= javascript_tag do %>
$(document).ready(function(){
$("input[name='query[visibility]']").change(function(){
var roles_checked = $('#query_visibility_1').is(':checked');
var private_checked = $('#query_visibility_0').is(':checked');
$("input[name='query[role_ids][]'][type=checkbox]").attr('disabled', !roles_checked);
if (!private_checked) $("input.disable-unless-private").attr('checked', false);
$("input.disable-unless-private").attr('disabled', !private_checked);
}).trigger('change');
});
<% end %>

View File

@ -0,0 +1,6 @@
<h2><%= l(:label_query) %></h2>
<%= form_tag(cms_page_query_path(@query), :method => :put) do %>
<%= render :partial => 'form', :locals => {:query => @query} %>
<%= submit_tag l(:button_save) %>
<% end %>

View File

@ -0,0 +1,25 @@
<div class="contextual">
<%= link_to_if_authorized l(:label_query_new), {:controller => 'cms_page_queries', :action => 'new'}, :class => 'icon icon-add' %>
</div>
<h2><%= l(:label_query_plural) %></h2>
<% if @queries.empty? %>
<p><i><%=l(:label_no_data)%></i></p>
<% else %>
<table class="list">
<% @queries.each do |query| %>
<tr class="<%= cycle('odd', 'even') %>">
<td class="name">
<%= link_to query.name, :controller => 'cms_pages', :action => 'index', :query_id => query %>
</td>
<td class="buttons">
<% if query.editable_by?(User.current) %>
<%= link_to l(:button_edit), edit_cms_page_query_path(query), :class => 'icon icon-edit' %>
<%= delete_link cms_page_query_path(query) %>
<% end %>
</td>
</tr>
<% end %>
</table>
<% end %>

View File

@ -0,0 +1,6 @@
<h2><%= l(:label_query_new) %></h2>
<%= form_tag(cms_page_queries_path) do %>
<%= render :partial => 'form', :locals => {:query => @query} %>
<%= submit_tag l(:button_save) %>
<% end %>

View File

@ -0,0 +1,20 @@
<div class="conflict">
<%= l(:label_cms_update_conflict) %>
<% if @conflict_versions.present? %>
<div class="conflict-versions">
<% @conflict_versions.sort_by(&:version).reverse.each do |version| %>
<div class="conflict-version">
<p><%= authoring version.created_at, version.author, :label => :label_updated_time_by %>, <%= link_to("##{version.version}", cms_object_diff_path(@page.id, :object_type => @page.class.name.underscore, :version => version.version, :version_from => params[:last_version])) %></p>
<div class="wiki">
<%= textilizable(version.comments) unless version.comments.blank? %>
</div>
</div>
<% end %>
</div>
<% end %>
</div>
<p>
<label><%= radio_button_tag 'conflict_resolution', 'overwrite' %> <%= l(:label_cms_conflict_resolution_overwrite) %></label><br />
<label><%= radio_button_tag 'conflict_resolution', 'cancel' %> <%= l(:label_cms_conflict_resolution_cancel) %></label>
</p>
<p><%= submit_tag l(:button_submit) %></p>

View File

@ -0,0 +1,9 @@
<%= text_area_tag 'page[content]', @page.content, :cols => 100, :rows => 30, :class => 'wiki-edit' %>
<% unless @page.new_record? %>
<p>
<label for="page_version_comment"><%= l(:label_cms_version_comment) %></label>
<%= text_field_tag 'page[version_comment]', '', :style => "width: 100%;" %>
</p>
<% end %>
<%= javascript_tag "editor = activateCodeMirror('page_content', '#{@page.filter.class.mine_type}');" %>

View File

@ -0,0 +1,23 @@
<% if RedmineCms.allow_edit? %>
<div class="contextual page-edit">
<% if params[:version] && @page.version != @current_version %>
<%= render :partial => 'cms_history/history_context_menu', :object => @page %>
<% else %>
<%= link_to l(:button_edit), edit_cms_page_path(@page), :class => 'icon icon-edit' %>
<% if @page.parts.active.any? %>
<ul class="parts-edit">
<% @page.parts.active.each do |part| %>
<li><%= link_to part.description.blank? ? part.name : part.description, edit_cms_part_path(part), :class => 'icon icon-bullet-arrow-right' %>
</li>
<% end %>
</ul>
<% end %>
<hr>
<%= link_to l(:label_history), cms_object_history_path(@page.id, :object_type => @page.class.name.underscore), :class => 'icon icon-history'%>
<hr>
<%= link_to l(:label_cms_refresh_cache), expire_cache_cms_page_path(@page), :class => 'icon icon-reload' %>
<% end %>
</div>
<%= javascript_tag "$('.contextual.page-edit').draggable();" %>
<% end %>

View File

@ -0,0 +1,65 @@
<% @f = f %>
<div class="box tabular">
<div class="splitcontent">
<div class="splitcontentleft">
<p><%= f.text_field :title, :size => '100%', :label => l(:label_cms_title) %></p>
<p><%= f.text_field :name, :size => 30, :required => true, :label => l(:label_cms_name) %></p>
<p><%= f.text_field :slug, :size => 30, :required => true, :label => l(:label_cms_slug) %></p>
<% if @page.valid_parents.any? %>
<p>
<%= f.select :parent_id, pages_options_for_select(@page.valid_parents), :include_blank => true, :label => :label_cms_page_parent %>
<%= javascript_tag do %>
$(document).ready(function(){
$('#page_parent_id').select2({ containerCssClass : "page-select icon icon-page" });
});
<% end %>
</p>
<% end %>
<p><%= f.select :status_id, cms_statuses_for_select, :label => l(:label_cms_status) %></p>
<p>
<%= render :partial => 'cms_pages/form_tags' %>
</p>
</div>
<div class="splitcontentright">
<p><%= f.select :visibility, cms_visibilities_for_select(@page.visibility || "public"), :label => l(:label_cms_visibility) %></p>
<p>
<%= f.select :layout_id, cms_layouts_for_select %>
</p>
<p>
<%= f.check_box :is_cached, :label => l(:label_cms_is_cached) %>
</p>
<p>
<%= f.select :filter_id, filter_options_for_select(@page.filter_id), {:label => l(:label_cms_filter)}, {:id => "text_filter_select", :onchange => "editor.setOption('mode', $('option:selected', this).data('mode'));return false"} %>
</p>
<p>
<%= f.date_field :page_date, :size => 10, :label => l(:label_cms_page_date) %>
<%= calendar_for('page_page_date') %>
</p>
</div>
</div>
<p id="attachments_form">
<label><%= l(:label_attachment_plural) %></label>
<%= render :partial => 'attachments/form', :locals => {:container => @page} %>
</p>
<% edit_page_tabs = [{:name => 'content', :partial => 'content_tab', :label => :label_cms_content}] %>
<% edit_page_tabs << {:name => 'page_parts', :partial => 'page_parts_tab', :label => :label_cms_part_plural} if @page.parts.any? %>
<% edit_page_tabs << {:name => 'page_fields', :partial => 'page_fields_tab', :label => :label_cms_field_plural} %>
<% @pages = @page.children_pages.includes(:layout, :author, :children_pages) %>
<% edit_page_tabs << {:name => 'sub_pages', :partial => 'sub_pages_tab', :label => :label_cms_sub_pages} unless @pages.blank? %>
<%= render_tabs edit_page_tabs %>
</div>
<% content_for :header_tags do %>
<%= code_mirror_tags %>
<%= javascript_include_tag 'redmine_cms', :plugin => 'redmine_cms' %>
<% end %>

View File

@ -0,0 +1,11 @@
<label for="page_tag_list"><%= l(:label_cms_tags) %></label>
<span class="cms-page-tags-edit">
<%= select2_tag 'page[tag_list]',
options_from_collection_for_select(@page.tags, 'name', 'name', @page.tags.map(&:name)),
:multiple => true,
:style => 'width: 100%;',
:url => auto_complete_cms_page_tags_path(names: true),
:placeholder => '+ add tag',
:include_blank => true,
:tags => true %>
</span>

View File

@ -0,0 +1,7 @@
<%= page_breadcrumb(@page) unless @project %>
<%= @page.process(self) %>
<%= render 'cms_pages/context_menu' %>
<% html_title(@page.title) -%>

View File

@ -0,0 +1,5 @@
<tr class="page-field">
<td class="name"><%= f.text_field :name, :no_label => true, :size => 8, :placeholder => l(:label_cms_name) %></td>
<td class="content"><%= f.text_field :content, :no_label => true, :size => 8, :placeholder => l(:label_cms_field_value) %></td>
<td class="buttons"><%= link_to_remove_page_fields "", f, :class => "icon icon-del" %></td>
</tr>

View File

@ -0,0 +1,18 @@
<div class="add-to-table">
<%= link_to_add_page_fields l(:label_cms_add_field), @f, :fields, :class => 'icon icon-add' %>
</div>
<table class="list page-fields" id="commit-keywords">
<thead>
<tr>
<th><%= l(:label_cms_name) %></th>
<th><%= l(:label_cms_field_value) %></th>
<th class="buttons"></th>
</tr>
</thead>
<tbody>
<tr style="display: none;"/>
<%= @f.fields_for :fields do |builder| %>
<%= render :partial => 'page_field_form', :locals => {:f => builder, :page => @page} %>
<% end %>
</table>

View File

@ -0,0 +1,8 @@
<div class="contextual add-to-table">
<%= link_to l(:label_cms_part_new), new_cms_part_path(:page_id => @page.id), :class => 'icon icon-add' %>
</div>
<div id="cms_page_parts">
<%= render :partial => "cms_parts/parts" %>
</div>

View File

@ -0,0 +1,18 @@
<div class="autoscroll">
<table class="list pages" id="browser">
<thead>
<tr>
<th><%= l(:label_cms_slug) %></th>
<th><%= l(:label_cms_name) %></th>
<th><%= l(:label_cms_layout) %></th>
<th><%= l(:label_cms_version) %></th>
<th><%= l(:field_updated_on) %></th>
<th><%= l(:label_cms_author) %></th>
<th></th>
</tr>
</thead>
<tbody>
<%= render :partial => 'pages_list_content' %>
</tbody>
</table>
</div>

View File

@ -0,0 +1,33 @@
<% @pages.each do |page| %>
<% tr_id = "page-#{page.id}" %>
<% depth = params[:depth].to_i %>
<% is_page_tree = @query.blank? || @query.filters.blank? %>
<tr id="<%= tr_id %>" class="<%= params[:parent_id] %> entry <%= is_page_tree ? page.tree_kind : 'file' %>">
<td style="padding-left: <%= 18 * depth %>px;" class="<%= "filename_no_report" %>">
<% if page.tree_kind == 'dir' && is_page_tree %>
<span class="expander" onclick="scmEntryClick('<%= tr_id %>', '<%= escape_javascript(cms_pages_path(
:status_id => @status,
:visibility => @visibility,
:layout_id => @layout_id,
:tag => @tag,
:name => @name,
:depth => (depth + 1),
:parent_id => tr_id
)) %>');">&nbsp;</span>
<% end %>
<%= link_to(show_site_page_path(:path => page.path)) do %>
<span class="page-icon <%= page.page_icon %>"></span>
<%= h(page.slug) %>
<% end %>
</td>
<td class="name page_name_field"><%= link_to h(page.name), edit_cms_page_path(page), :title => page.path %></td>
<td><%= page.layout ? page.layout.name : l(:label_cms_redmine_layout) %></td>
<td><%= link_to page.version, cms_object_diff_path(page.id, :object_type => page.class) %></td>
<td><%= format_date(page.updated_at) %></td>
<td><%= page.author %></td>
<td class="buttons">
<%= cms_change_status_link('page', page) %>
<%= delete_link cms_page_path(page) if User.current.admin? %>
</td>
</tr>
<% end %>

View File

@ -0,0 +1,7 @@
<div class="add-to-table">
<%= link_to l(:label_cms_page_new), new_cms_page_path(:page => {:parent_id => @page.id, :layout_id => @page.layout_id, :filter_id => @page.filter_id}), :class => 'icon icon-add' %>
</div>
<div id="cms_page_parts">
<%= render :partial => "pages" %>
</div>

View File

@ -0,0 +1,2 @@
$('tr#page-<%= @page.id %> .buttons').html('<%= escape_javascript(cms_change_status_link('page', @page) + (User.current.admin? ? delete_link(cms_page_path(@page)) : '')) %>');
$('#page_lock_version').val('<%= @page.lock_version %>')

View File

@ -0,0 +1,79 @@
<div class="contextual">
<%= link_to l(:label_preview), preview_cms_page_path(@page), :class => 'icon icon-preview' %>
<%= link_to l(:label_cms_show_site), show_site_page_path(:path => @page.path), :class => 'icon icon-root-page' %>
<%= link_to l(:label_history), cms_object_history_path(@page.id, :object_type => @page.class.name.underscore), :class => 'icon icon-history' if @page.version > 1 %>
<%= link_to l(:button_duplicate), new_cms_page_path(:copy_from => @page), :class => 'icon icon-duplicate' %>
<%= link_to l(:button_export), cms_export_path(@page.id, :object_type => @page.class.name.underscore), :class => 'icon icon-save' %>
<%= delete_link cms_page_path(@page) if User.current.admin? %>
</div>
<h2><%= link_to l(:label_cms_page_plural), cms_pages_path %>
<% @page.ancestors.each do |page| %>
&#187; <%= link_to page.slug, edit_cms_page_url(page) %>
<% end %>
&#187; <%= @page.slug %>
</h2>
<%= labelled_form_for :page, @page, :url => { :action => 'update', :id => @page},
:html => { :id => 'page_form', :multipart => true, :method => :put } do |f| %>
<%= error_messages_for 'page' %>
<%= render :partial => 'conflict' if @conflict %>
<div class="tabs part-tabs">
<ul>
<li><%= link_to(l(:label_cms_page), edit_cms_page_path(@page), :class => "page selected", :id => "cms_page_tab") %></li>
<% @page.parts.active.order(:position).each do |part| %>
<li><%= link_to(part.title, edit_cms_part_path(part), :class => " #{'locked' unless part.active?}") %></li>
<% end %>
<li><%= link_to "+", new_cms_part_path(:page_id => @page.id) %></li>
</ul>
<div class="tabs-buttons" style="display:none;">
<button class="tab-left" type="button" onclick="moveTabLeft(this);"></button>
<button class="tab-right" type="button" onclick="moveTabRight(this);"></button>
</div>
</div>
<%= render :partial => 'form', :locals => { :f => f } %>
<% if @page.attachments.any? %>
<fieldset><legend><%= l(:label_attachment_plural) %></legend>
<%= link_to_cms_attachments @page, :thumbnails => true %>
<% if @page.attachments.any? && @page.safe_attribute?('deleted_attachment_ids') %>
<%= link_to l(:label_cms_delete_attachments),
'#', :onclick => "$('#existing-attachments').toggle(); return false;", :class => 'icon icon-del' %>
<div id="existing-attachments" style="<%= @page.deleted_attachment_ids.blank? ? 'display:none;' : '' %>">
<% @page.attachments.each do |attachment| %>
<span class="existing-attachment">
<%= text_field_tag '', attachment.filename, :class => "icon icon-attachment filename", :disabled => true %>
<label>
<%= check_box_tag 'page[deleted_attachment_ids][]',
attachment.id,
@page.deleted_attachment_ids.include?(attachment.id),
:id => nil, :class => "deleted_attachment" %> <%= l(:button_delete) %>
</label>
</span>
<% end %>
<hr />
</div>
<% end %>
</fieldset>
<br/>
<% end %>
<%= f.hidden_field :lock_version %>
<%= hidden_field_tag 'last_version', params[:last_version] || @page.version %>
<%= submit_tag l(:button_save) %>
<%= link_to l(:label_cms_apply), "#", :style => "margin-left:10px;", :remote => true, :onclick => "editor.save(); $.ajax({url: '#{escape_javascript cms_page_path(@page)}', type: \'post\', data: $(\'#page_form\').serialize() + '&format=js'}); $('textarea').removeData('changed'); $('#last_version').val(''); return false;"%>
<% end %>
<% content_for :sidebar do %>
<% render :partial => 'cms_settings/menu' %>
<% end %>
<% content_for :header_tags do %>
<div class="contextual page-edit" id="float_buttons">
<%= link_to l(:label_cms_apply), "#", :remote => true, :onclick => "editor.save(); $.ajax({url: '#{escape_javascript cms_page_path(@page)}', type: \'post\', data: $(\'#page_form\').serialize() + '&format=js'}); $('textarea').removeData('changed'); $('#last_version').val(''); return false;"%>
</div>
<% end %>
<%= javascript_tag "$('.contextual.page-edit').draggable();" %>
<% html_title(@page.name) -%>

View File

@ -0,0 +1,52 @@
api.array :cms_pages, api_meta(:total_count => @pages.count) do
@pages.each do |cms_page|
api.cms_page do
api.id cms_page.id
api.name cms_page.name
api.title cms_page.title
api.content cms_page.content
api.filter_id cms_page.filter_id
api.status_id cms_page.status_id
api.visibility cms_page.visibility
api.is_cached cms_page.is_cached
api.parent(:id => cms_page.parent_id, :name => cms_page.parent.name) unless cms_page.parent.nil?
api.layout(:id => cms_page.layout_id, :name => cms_page.layout.name) unless cms_page.layout.nil?
api.version cms_page.version
api.created_at cms_page.created_at
api.updated_at cms_page.updated_at
api.array :parts do
cms_page.parts.each do |cms_part|
api.cms_part do
api.id cms_part.id
api.name cms_part.name
api.description cms_part.description
api.content cms_page.content
api.filter_id cms_page.filter_id
api.status_id cms_page.status_id
api.visibility cms_page.visibility
api.is_cached cms_page.is_cached
api.created_at cms_part.created_at
api.updated_at cms_part.updated_at
end
end
end if include_in_api_response?('parts') && cms_page.parts.any?
api.array :fields do
cms_page.fields.each do |page_field|
api.field do
api.id page_field.id
api.name page_field.name
api.content page_field.content
api.created_at page_field.created_at
api.updated_at page_field.updated_at
end
end
end if include_in_api_response?('fields') && cms_page.fields.any?
end
end
end

View File

@ -0,0 +1,80 @@
<div class="contextual">
<%= link_to l(:label_cms_page_new), new_cms_page_path, :class => 'icon icon-add' %>
<%= link_to l(:button_import), cms_import_path(:object_type => CmsPage.name.underscore), :class => 'icon icon-duplicate' %>
<% if !@query.new_record? && @query.editable_by?(User.current) %>
<%= link_to l(:button_edit), edit_cms_page_query_path(@query), :class => 'icon icon-edit' %>
<%= delete_link cms_page_query_path(@query) %>
<% end %>
</div>
<%= form_tag({ :controller => 'cms_pages', :action => 'index', :project_id => @project }, :method => :get, :id => 'query_form') do %>
<script type="text/javascript">
jQuery(function($) {
$("#search").observe_field(2, function() {
var form = $("#query_form");
var url = form.attr("action");
form.find('[name="c[]"] option').each(function(i, elem){
$(elem).attr('selected', true)
})
var formData = form.serialize();
form.find('[name="c[]"] option').each(function(i, elem){
$(elem).attr('selected', false)
})
$.get(url, formData, function(data) {
$("#cms_pages table.pages tbody").html(data);
});
});
});
</script>
<h2 class="cms_pages_header">
<span id='scope_header' class="scope_title">
<%= @query.new_record? ? l(:label_cms_page_plural) : h(@query.name) %>
</span>
<span class="live_search">
<%= text_field_tag(:search, params[:search], :autocomplete => "off", :class => "live_search_field", :placeholder => l(:label_cms_page_search) ) %>
</span>
</h2>
<%= hidden_field_tag 'set_filter', '1' %>
<%= hidden_field_tag 'object_type', 'cms_page' %>
<div id="query_form_content" class="hide-when-print">
<fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
<legend class="icon icon-<%= @query.new_record? ? 'expended' : 'collapsed' %>" onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
<div style="<%= @query.new_record? ? "" : "display: none;" %>">
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
</div>
</fieldset>
</div>
<p class="buttons hide-when-print">
<%= link_to_function l(:button_apply), 'submit_query_form("query_form")', :class => 'icon icon-checked' %>
<%= link_to l(:button_clear), { :set_filter => 1 }, :class => 'icon icon-reload' %>
<% if @query.new_record? && User.current.logged? %>
<%= link_to_function l(:button_save),
"$('#query_form').attr('action', '#{ new_cms_page_query_path }'); submit_query_form('query_form')",
:class => 'icon icon-save' %>
<% end %>
</p>
<% end %>
<%= error_messages_for 'query' %>
<% if @query.valid? %>
<% end %>
<div class="autoscroll" id="cms_pages">
<%= render :partial => 'pages' %>
</div>
<% content_for :sidebar do %>
<% render :partial => 'cms_settings/menu' %>
<% end %>
<% content_for :header_tags do %>
<%= javascript_include_tag :redmine_cms_pages, :plugin => 'redmine_cms' %>
<%= select2_assets %>
<% end %>
<% html_title(l(:label_cms_page_plural)) -%>

View File

@ -0,0 +1,16 @@
<h2><%=l(:label_cms_page_new)%></h2>
<%= labelled_form_for :page, @page, :url => { :action => 'create', :project_id => @project },
:html => { :id => 'page-form', :multipart => true } do |f| %>
<%= error_messages_for 'page' %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_create) %>
<% end %>
<% content_for :header do %>
<div class="contextual page-edit" id="float_buttons" style="display:none;">
<%= link_to l(:label_cms_apply), "#", :class => "icon icon-checked", :remote => true, :onclick => "editor.save(); $.ajax({url: '#{escape_javascript cms_pages_path}', type: \'post\', data: $(\'#page_form\').serialize()});return false;"%>
</div>
<% end %>
<% html_title(l(:label_cms_page_new)) -%>

View File

@ -0,0 +1,56 @@
<div id="cms-top-menu">
<ul>
<li><%= link_to l(:button_edit), send("edit_#{@cms_object.class.name.underscore}_path", @cms_object, :reset_preview => true) %></li>
<% if @cms_object.respond_to?(:parts) && @cms_object.parts.active.any? %>
<li>
<%= link_to l(:label_cms_part_edit_plural), "" %>
<ul class="menu-children">
<% @cms_object.parts.active.each do |part| %>
<li><%= link_to part.description.blank? ? part.name : part.description, edit_cms_part_path(part, :reset_preview => true) %>
</li>
<% end %>
</ul>
</li>
<% end %>
<li><%= link_to l(:label_history), cms_object_history_path(@cms_object.id, :object_type => @cms_object.class.name.underscore) %>
<ul class="menu-children">
<% if @cms_object.versions %>
<li>
<%= link_to(("\xc2\xab " + l(:label_previous)), send("preview_#{@cms_object.class.name.underscore}_path", @cms_object, :version => @cms_object.previous_version.version)) if @cms_object.previous_version %>
<%= " #{@cms_object.version}#{'/' + link_to(@current_version, send("preview_#{@cms_object.class.name.underscore}_path", @cms_object)) if @cms_object.next_version } ".html_safe %>
<%= link_to((l(:label_next) + " \xc2\xbb"), send("preview_#{@cms_object.class.name.underscore}_path", @cms_object, :version => @cms_object.next_version.version)) if @cms_object.next_version %>
</li>
<% end %>
<% if @current_version && @cms_object.version != @current_version %>
<li><%= link_to(l(:label_cms_diff_with_current), cms_object_diff_path(@cms_object.id, :object_type => @cms_object.class.name.underscore, :version => @cms_object.version, :version_from => @current_version)) %></li>
<li><%= link_to l(:button_rollback), send("edit_#{@cms_object.class.name.underscore}_path", @cms_object, :version => params[:version]), :class => 'icon icon-cancel' %></li>
<% end %>
</ul>
</li>
<% if @cms_object.respond_to?(:expire_cache) %>
<li><%= link_to l(:label_cms_refresh_cache), expire_cache_cms_page_path(@cms_object) %></li>
<% end %>
<li>|</li>
<li><%= link_to l(:label_cms), cms_settings_path %>
<ul class="menu-children">
<li><%= link_to l(:label_cms_page_plural), cms_pages_path, :class => "icon icon-page #{'selected' if params[:controller] == 'cms_pages'}" %></li>
<li><%= link_to l(:label_cms_snippet_plural), cms_snippets_path, :class => "icon icon-snippet #{'selected' if params[:controller] == 'cms_snipets'}" %></li>
<li><%= link_to l(:label_cms_menu_plural), cms_menus_path, :class => "icon icon-menu #{'selected' if params[:controller] == 'cms_menus'}" %></li>
<li><%= link_to l(:label_cms_layout_plural), cms_layouts_path, :class => "icon icon-layout #{'selected' if params[:controller] == 'cms_layouts'}" %></li>
<li><%= link_to l(:label_cms_redmine_layout_plural), cms_redmine_layouts_path, :class => "icon icon-redmine-layout #{'selected' if params[:controller] == 'cms_redmine_layouts'}" %></li>
<li><%= link_to l(:label_cms_redmine_hook_plural), redmine_hooks_cms_settings_path, :class => "icon icon-redmine-hooks #{'selected' if params[:controller] == 'cms_settings' && params[:action] == 'redmine_hooks'}" %></li>
<li><%= link_to l(:label_cms_redirect_plural), cms_redirects_path, :class => "icon icon-redirect #{'selected' if params[:controller] == 'cms_redirects'}" %></li>
<li><%= link_to l(:label_cms_asset_plural), cms_assets_path, :class => "icon icon-package #{'selected' if params[:controller] == 'cms_assets'}" %></li>
<li><%= link_to l(:label_cms_variable_plural), cms_variables_path, :class => "icon icon-vars #{'selected' if params[:controller] == 'cms_variables'}" %></li>
</ul>
</li>
</ul>
</div>
<iframe src="<%= url_for (params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params).merge(:action => 'show') %>" scrollbars="auto" name="preview" height="100%" width="100%">
</iframe>
<% html_title("#{l(:label_cms_preview)} - #{@cms_object.name}") -%>

View File

@ -0,0 +1,16 @@
api.cms_page do
api.id @cms_page.id
api.name @cms_page.name
api.title @cms_page.title
api.content @cms_page.content
api.filter_id @cms_page.filter_id
api.status_id @cms_page.status_id
api.visibility @cms_page.visibility
api.is_cached @cms_page.is_cached
api.parent(:id => @cms_page.parent_id, :name => @cms_page.parent.name) unless @cms_page.parent.nil?
api.layout(:id => @cms_page.layout_id, :name => @cms_page.layout.name) unless @cms_page.layout.nil?
api.version @cms_page.version
api.created_at @cms_page.created_at
api.updated_at @cms_page.updated_at
end

View File

@ -0,0 +1,5 @@
<%= render :partial => 'cms_pages/page' %>
<% @page.parts.active.order(:position).each do |page_part| %>
<%= content_for(page_part.name.to_sym, @page.render_part(page_part)) unless page_part.name.strip == 'content' %>
<% end %>

View File

@ -0,0 +1,47 @@
<%= error_messages_for 'part' %>
<div class="box tabular">
<div class="splitcontent">
<div class="splitcontentleft">
<p><%= f.text_field :name, :size => 30, :required => true, :label => l(:label_cms_section) %></p>
<p><%= f.text_field :description, :size => "98%", :label => l(:label_cms_description) %></p>
<% if @part.new_record? || @part.page.blank? %>
<p>
<%= f.select :page_id, pages_options_for_select(CmsPage.all), :required => true, :label => l(:label_cms_page) %>
</p>
<% end %>
<p><%= f.select :status_id, cms_statuses_for_select, :label => l(:label_cms_status) %></p>
</div>
<div class="splitcontentright">
<p><%= f.check_box :is_cached, :label => l(:label_cms_is_cached) %></p>
<p><%= f.select :filter_id, filter_options_for_select(@part.filter_id), {:label => l(:label_cms_filter)}, {:id => "text_filter_select", :onchange => "editor.setOption('mode', $('option:selected', this).data('mode'));return false"} %></p>
</div>
</div>
<!--
<p id="attachments_form"><label><%= l(:label_attachment_plural) %></label><%= render :partial => 'attachments/form', :locals => {:container => @part} %></p> -->
<%= text_area_tag 'part[content]', @part.content, :cols => 100, :rows => 30, :class => 'wiki-edit' %>
<%= javascript_tag "editor = activateCodeMirror('part_content', '#{@part.filter.class.mine_type}');" %>
<%= javascript_tag "$('.contextual.page-edit').draggable();" %>
</div>
<%= javascript_tag do %>
$(document).ready(function(){
$('#part_page_id').select2({containerCssClass: "page-select icon icon-page" });
});
<% end %>
<% content_for :header_tags do %>
<%= code_mirror_tags %>
<%= javascript_include_tag 'redmine_cms', :plugin => 'redmine_cms' %>
<%= select2_assets %>
<% end %>

View File

@ -0,0 +1,29 @@
<% if @parts.any? %>
<table class="list page-parts">
<thead>
<tr>
<th><%= l(:label_cms_name) %></th>
<th><%= l(:label_cms_description) %></th>
<th style="width:15%;"> </th>
</tr>
</thead>
<tbody>
<% @parts.each do |page_part| -%>
<tr class="part <%= cycle("odd", "even") %>" id="page_part_<%= page_part.id %>">
<td class="name"><%= link_to h(page_part.name), edit_cms_part_path(page_part) %></td>
<td class="name"><%= link_to h(page_part.description), edit_cms_part_path(page_part) %></td>
<td class="buttons">
<%= reorder_handle(page_part, url: cms_part_path(page_part), param: 'part') %>
<%= cms_change_status_link('part', page_part) %>
<%= link_to l(:label_history), cms_object_history_path(page_part, :object_type => page_part.class.name.underscore), :class => 'icon icon-history' if page_part.version > 1 %>
<%= delete_link cms_part_path(page_part) if User.current.admin? %>
</td>
</tr>
<% end -%>
</tbody>
</table>
<%= javascript_tag do %>
$(function() { $("table.page-parts tbody").positionedItems(); });
<% end %>
<% end %>

View File

@ -0,0 +1 @@
$('#cms_page_parts').html('<%= escape_javascript(render :partial => 'cms_parts/parts') %>');

View File

@ -0,0 +1,61 @@
<div class="contextual">
<%= link_to l(:label_cms_preview), preview_cms_part_path(@part), :class => 'icon icon-preview' %>
<%= link_to l(:label_cms_preview_page), preview_cms_page_path(@part.page), :class => 'icon icon-preview' %>
<%= link_to l(:label_history), cms_object_history_path(@part, :object_type => @part.class.name.underscore), :class => 'icon icon-history' %>
<%= link_to l(:button_duplicate), new_cms_part_path(:copy_from => @part), :class => 'icon icon-duplicate' %>
<%= delete_link cms_part_path(@part) if User.current.admin? %>
</div>
<h2><%= link_to l(:label_cms_page_plural), cms_pages_path %>
<% @page.ancestors.each do |page| %>
&#187; <%= link_to page.slug, edit_cms_page_url(page) %>
<% end %>
&#187; <%= @page.slug %>
</h2>
<div class="tabs part-tabs">
<ul>
<li><%= link_to(l(:label_cms_page), edit_cms_page_path(@part.page), :class => "page") %></li>
<% @part.page.parts.active.order(:position).each do |part| %>
<li><%= link_to(part.title, edit_cms_part_path(part), :class => "#{(@part.id == part.id ? 'selected' : nil)} #{'locked' unless part.active?}") %></li>
<% end %>
<li><%= link_to "+", new_cms_part_path(:page_id => @part.page.id) %></li>
</ul>
<div class="tabs-buttons" style="display:none;">
<button class="tab-left" type="button" onclick="moveTabLeft(this);"></button>
<button class="tab-right" type="button" onclick="moveTabRight(this);"></button>
</div>
</div>
<%= labelled_form_for :part, @part, :url => { :action => 'update', :id => @part},
:html => { :id => 'part_form', :multipart => true, :method => :put } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<% if @page.attachments.any? %>
<fieldset><legend><%= l(:label_attachment_plural) %></legend>
<%= link_to_cms_attachments @page, :thumbnails => true %>
</fieldset>
<br/>
<% end %>
<!-- <fieldset><legend><%= l(:label_attachment_plural) %></legend>
<%= link_to_cms_attachments @part, :thumbnails => true %>
</fieldset><br/> -->
<% content_for :sidebar do %>
<% render :partial => 'cms_settings/menu' %>
<% end %>
<%= submit_tag l(:button_save) %>
<%= link_to l(:label_cms_apply), "#", :style => "margin-left:10px;", :remote => true, :onclick => "editor.save(); $.ajax({url: '#{escape_javascript cms_part_path(@part)}', type: \'post\', dataType: \'script\', data: $(\'#part_form\').serialize()}); $('textarea').removeData('changed'); return false;"%>
<% end %>
<% content_for :header_tags do %>
<div class="contextual page-edit" id="float_buttons">
<%= link_to l(:label_cms_apply), "#", :remote => true, :onclick => "editor.save(); $.ajax({url: '#{escape_javascript cms_part_path(@part)}', type: \'post\', dataType: \'script\', data: $(\'#part_form\').serialize()}); $('textarea').removeData('changed'); return false;"%>
</div>
<% end %>
<%= javascript_tag "$('.contextual.page-edit').draggable();" %>
<% html_title(@part.title + " " + @part.page.name) -%>

View File

@ -0,0 +1,15 @@
<h2><%=l(:label_cms_part_new)%></h2>
<%= labelled_form_for :part, @part, :url => { :action => 'create', :project_id => @project },
:html => { :id => 'part-form', :multipart => true } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_create) %>
<% end %>
<% content_for :header do %>
<div class="contextual page-edit" id="float_buttons">
<%= link_to l(:label_cms_apply), "#", :class => "icon icon-checked", :remote => true, :onclick => "editor.save(); $.ajax({url: '#{escape_javascript cms_parts_path(@part)}', type: \'post\', data: $(\'#part_form\').serialize()});return false;"%>
</div>
<% end %>
<% html_title(l(:label_cms_part_new)) -%>

View File

@ -0,0 +1,20 @@
<h2><%= link_to cms_page_path(@part.page), edit_cms_page_url(@part.page, :tab => "page_parts") %> &#187; <%=l(:label_cms_part)%>: <%= @part.name + (@part.description.blank? ? '' : " (#{@part.description})") %></h2>
<div class="wiki page part <%= @part.name %>">
<%= @preview_content %>
</div>
<% content_for :header_tags do %>
<div class="contextual page-edit">
<% if params[:version] && @part.version != @current_version %>
<%= render :partial => 'cms_history/history_context_menu', :object => @part %>
<% else %>
<%= link_to l(:button_edit), edit_cms_part_path(@part), :class => 'icon icon-edit' %>
<hr>
<%= link_to l(:label_history), cms_object_history_path(@part, :object_type => @part.class.name.underscore), :class => 'icon icon-history'%>
<hr>
<%= link_to l(:label_cms_refresh_cache), expire_cache_cms_part_path(@part), :class => 'icon icon-reload' %>
<% end %>
</div>
<%= javascript_tag "$('.contextual.page-edit').draggable();" %>
<% end if RedmineCms.allow_edit? %>

View File

@ -0,0 +1,6 @@
<%= error_messages_for 'cms_redirect' %>
<div class="box tabular">
<p><%= f.text_field :source_path, :size => 100, :required => true, :label => l(:label_cms_source_path) %></p>
<p><%= f.text_field :destination_path, :size => 100, :required => true, :label => l(:label_cms_destination_path) %></p>
</div>

View File

@ -0,0 +1,13 @@
<h2><%= link_to l(:label_cms_redirect_plural), cms_redirects_path %> &#187; <%=l(:label_cms_redirect)%></h2>
<%= labelled_form_for :cms_redirect, @cms_redirect, :url => { :action => 'update', :id => @cms_redirect},
:html => { :id => 'cms_redirect-form', :method => :put } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_save) %>
<% end %>
<% content_for :sidebar do %>
<% render :partial => 'cms_settings/menu' %>
<% end %>
<% html_title(l(:label_cms_redirect_edit)) -%>

View File

@ -0,0 +1,33 @@
<div class="contextual">
<%= link_to l(:label_cms_redirect_new), new_cms_redirect_path, :class => 'icon icon-add' %>
</div>
<h2><%=l(:label_cms_redirect_plural)%></h2>
<div class="autoscroll">
<table class="list">
<thead><tr>
<th><%= l(:label_cms_source_path) %></th>
<th><%= l(:label_cms_destination_path) %></th>
<th></th>
</tr></thead>
<tbody>
<% @cms_redirects.each do |cms_redirect| -%>
<tr class="cms_redirect project <%= cycle("odd", "even") %>">
<td class="name"><%= link_to cms_redirect.source_path, edit_cms_redirect_path(cms_redirect) %></td>
<td class="name"><%= cms_redirect.destination_path %></td>
<td class="buttons">
<%= delete_link cms_redirect_path(cms_redirect, :back_url => cms_redirects_path(params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params)) %>
</td>
</tr>
<% end -%>
</tbody>
</table>
</div>
<% content_for :sidebar do %>
<% render :partial => 'cms_settings/menu' %>
<% end %>
<% html_title(l(:label_cms_redirect_plural)) -%>

View File

@ -0,0 +1,9 @@
<h2><%=l(:label_cms_redirect_new)%></h2>
<%= labelled_form_for :cms_redirect, @cms_redirect, :url => { :action => 'create'},
:html => { :id => 'cms_redirect-form', :method => :post } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_create) %>
<% end %>
<% html_title(l(:label_cms_redirect_new)) -%>

Some files were not shown because too many files have changed in this diff Show More