Merge pull request #37 from floriank/fix/19325-two-columns

[19325] Personalize Overview does not use two column layout
pull/6827/head
Alex Coles 10 years ago
commit 8bb44c7c89
  1. 176
      app/assets/javascripts/my_project_page/my_project_page.js
  2. 26
      app/assets/stylesheets/my_project_page/my_projects_overview.css
  3. 34
      app/assets/stylesheets/my_project_page/my_projects_overview.sass
  4. 7
      app/controllers/my_projects_overviews_controller.rb
  5. 67
      app/helpers/my_projects_overviews_helper.rb
  6. 4
      app/views/my_projects_overviews/_block.html.erb
  7. 11
      app/views/my_projects_overviews/_block_textilizable.html.erb
  8. 2
      app/views/my_projects_overviews/order_blocks.js.erb
  9. 169
      app/views/my_projects_overviews/page_layout.html.erb
  10. 7
      app/views/my_projects_overviews/remove_block.js.erb

@ -1,7 +1,7 @@
//-- copyright
// OpenProject My Project Page Plugin
//
// Copyright (C) 2011-2014 the OpenProject Foundation (OPF)
// Copyright (C) 2011-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
@ -18,25 +18,165 @@
// See doc/COPYRIGHT.md for more details.
//++
/*globals jQuery, I18n*/
/* globals jQuery, $$, $, Sortable, Effect, Form, Ajax, I18n, _ */
/* jshint camelcase: false */
/* jshint nonew: false */
var MyProjectPage = (function ($) {
var init;
(function($) {
// $ is prototype
// @see app/views/my_projects_overviews/page_layout.html.erb
init = function () {
$('#users_per_role .all').click(function () {
$('#users_per_role').html('');
});
function recreateSortables() {
var lists = $$('.list-position'),
containedPositions = (function() {
var positions = _.map(lists, function(list) {
return list.readAttribute('id');
});
return _.uniq(positions);
}()),
destroy = function destroy(list) {
var id = list.readAttribute('id');
Sortable.destroy(id);
},
create = function create(list) {
var id = list.readAttribute('id'),
url = list.readAttribute('data-ajax-url');
Sortable.create(id, {
constraint: false,
dropOnEmpty: true,
handle: 'handle',
onUpdate: function updatePosition() {
new Ajax.Request(url, {
asynchronous: true,
evalScripts: true,
parameters: Sortable.serialize(id)
});
},
containment: containedPositions,
only: 'mypage-box',
tag: 'div'
});
};
$.ajaxAppend({
trigger: '.all',
indicator_class: 'ajax-indicator',
load_target: '#users_per_role',
loading_text: I18n.t("js.ajax.loading"),
loading_class: 'box loading'
});
};
lists.each(destroy);
lists.each(create);
}
function updateSelect() {
var s = $('block-select');
for (var i = 0; i < s.options.length; i++) {
if ($('block_' + s.options[i].value)) {
s.options[i].disabled = true;
} else {
s.options[i].disabled = false;
}
}
s.options[0].selected = true;
}
function afterAddBlock(response) {
recreateSortables();
updateSelect();
editTextilizable(extractBlockName(response));
new Effect.ScrollTo('list-hidden');
}
function extractBlockName(response) {
return response.responseText.match(/id="block_(.*?)"/)[1];
}
function resetTextilizable(name) {
$('textile_' + name).setValue(window['page_layout-textile' + name] + '');
toggleTextilizableVisibility(name);
return false;
}
function editTextilizable(name) {
var textile_name = $('textile_' + name);
if (textile_name !== null) {
window['page_layout-textile' + name] = textile_name.getValue();
toggleTextilizableVisibility(name);
}
return false;
}
function toggleTextilizableVisibility(name) {
$(name + '-form-div').toggle();
$(name + '-preview-div').toggle();
$(name + '-text').toggle();
}
function addBlock() {
new Ajax.Updater('list-hidden',
$('block-form').action,
{ insertion: 'top',
onComplete: afterAddBlock,
parameters: Form.serialize('block-form'),
evalScripts:true
});
return false;
}
// prototype end
$('document').ready(init);
(function($) {
// from here on, '$' is jQuery
}(jQuery));
$(function() {
$('#users_per_role .all').click(function () {
$('#users_per_role').html('');
});
$.ajaxAppend({
trigger: '.all',
indicator_class: 'ajax-indicator',
load_target: '#users_per_role',
loading_text: I18n.t('js.ajax.loading'),
loading_class: 'box loading'
});
// this was previously bound in the template directly
$('#block-select').on('change', addBlock);
// we need to rebind some of the links constantly, as the content is generated
// on the page
function updateBlockLinks() {
function getBlockName(element) {
var blockName = element.data('block-name');
if (!blockName) {
throw new Error('no block name found for element');
}
return blockName;
}
// bind textilizable block links
$('a.reset-textilizable').on('click', function(e) {
e.preventDefault();
resetTextilizable(getBlockName($(this)));
});
$('a.edit-textilizable').on('click', function(e) {
e.preventDefault();
editTextilizable(getBlockName($(this)));
});
}
// initialize the fun! (prototype)
recreateSortables();
updateSelect();
// moar fun
updateBlockLinks();
//these are generated blocks, so we have to watch the links inside them
// TODO: this is exceptionally _not_ fun
// this attaches the update method to the window in order for it
// being callable after removal of a block
window.myPage = window.myPage || {
updateSelect: updateSelect,
updateBlockLinks: updateBlockLinks
};
});
}(jQuery));
}($));

@ -1,26 +0,0 @@
/*-- copyright
OpenProject My Project Page Plugin
Copyright (C) 2011-2014 the OpenProject Foundation (OPF)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See doc/COPYRIGHT.md for more details.
++*/
div.overview {
padding:6px;
margin-bottom: 10px;
line-height:1.5em;
}

@ -0,0 +1,34 @@
// -- copyright
// OpenProject My Project Page Plugin
//
// Copyright (C) 2011-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.md for more details.
@import foundation
div.overview
padding: 6px
margin-bottom: 10px
line-height: 1.5em
#visible-grid
.block-receiver
@extend .grid-content
min-height: 32px
// TODO: this fixes an issue which currently breaks the layout
// It has to be removed once it is fixed upstream
// @see https://github.com/zurb/foundation-apps/issues/575
&.left, &.right
@extend .medium-6

@ -37,7 +37,6 @@ class MyProjectsOverviewsController < ApplicationController
end
def index
render
end
# User's page layout configuration
@ -92,10 +91,9 @@ class MyProjectsOverviewsController < ApplicationController
# Remove a block to user's page
# params[:block] : id of the block to remove
def remove_block
block = param_to_block(params[:block])
%w(top left right hidden).each {|f| overview.send(f).delete block }
@block = param_to_block(params[:block])
%w(top left right hidden).each {|f| overview.send(f).delete @block }
overview.save!
render :nothing => true
end
# Change blocks order on user's page
@ -115,7 +113,6 @@ class MyProjectsOverviewsController < ApplicationController
overview.update_attribute(group, group_items)
end
end
render :nothing => true
end
def param_to_block(param)

@ -1,7 +1,7 @@
#-- copyright
# OpenProject My Project Page Plugin
#
# Copyright (C) 2011-2014 the OpenProject Foundation (OPF)
# Copyright (C) 2011-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
@ -20,4 +20,69 @@
module MyProjectsOverviewsHelper
include WorkPackagesFilterHelper
TOP = %w(top)
MIDDLE = %w(left right)
HIDDEN = %w(hidden)
def field_list
TOP + MIDDLE + HIDDEN
end
def visible_fields
TOP + MIDDLE
end
# TODO: potentially dangerous, is there a better way? (via define_method?)
def method_missing(name)
constant_name = name.to_s.gsub('_fields', '').upcase
if MyProjectsOverviewsHelper.const_defined? constant_name
return MyProjectsOverviewsHelper.const_get constant_name
end
raise NoMethodError.new("tried to call method #{name}, but was not found!")
end
def grid_field(name)
css_classes = %w(block-receiver list-position) + [name]
data = {
:'ajax-url' => ajax_url(name),
position: name
}
content_tag :div, id: "list-#{name}", class: css_classes, data: data do
ActiveSupport::SafeBuffer.new(blocks[name].map { |b| construct b }.join)
end
end
protected
def block_available?(block)
controller.class.available_blocks.keys.include? block
end
def construct(block)
if block.is_a? Array
return render_textilized block
end
if block_available? block
return render_normal block
end
end
def ajax_url(name)
url_for controller: '/my_projects_overviews',
action: 'order_blocks',
group: name
end
def render_textilized(block)
render partial: 'block_textilizable', locals: {
block_name: block.first,
block_title: block[1],
textile: block.last
}
end
def render_normal(block)
render partial: 'block', locals: { block_name: block }
end
end

@ -27,8 +27,8 @@ See doc/COPYRIGHT.md for more details.
<%= link_to_remote l(:button_delete), {
:confirm => l(:label_confirm_delete),
:url => { :action => "remove_block", :block => block_name },
:complete => "removeBlock('block_#{block_name.dasherize}')" },
:class => "icon icon-delete"
:class => "icon icon-delete"
}
%>
</div>
<% end %>

@ -24,8 +24,8 @@ See doc/COPYRIGHT.md for more details.
<% content_for block_name do %>
<div id="<%= block_name %>-form-div" style="display: none;" ng-non-bindable>
<div class="box-actions">
<a href="#" onclick="return resetTextilizable('<%= block_name %>');"
class="icon icon-cancel"><%= l(:button_cancel) %></a>
<a href="#" data-block-name="<%= block_name %>"
class="icon icon-cancel reset-textilizable"><%= l(:button_cancel) %></a>
</div>
<div style="clear: right;"></div>
<%= form_for :overview,
@ -49,14 +49,13 @@ See doc/COPYRIGHT.md for more details.
</div>
<div id="<%= block_name %>-preview-div" class="wiki">
<div class="box-actions">
<a href="#" onclick="return editTextilizable('<%= block_name %>');"
class="icon icon-edit"><%= l(:button_edit) %></a>
<a href="#" class="icon icon-edit edit-textilizable" data-block-name="<%= block_name %>"><%= l(:button_edit) %></a>
<%= link_to_remote l(:button_delete), {
:confirm => l(:label_confirm_delete),
:url => { :action => "remove_block", :block => block_name },
:complete => "removeBlock('block_#{block_name.dasherize}')" },
:class => "icon icon-delete"
:class => "icon icon-delete"
}
%>
</div>
</div>

@ -0,0 +1,2 @@
// rebind some of the links if the block is textilizable
myPage.updateBlockLinks();

@ -18,171 +18,54 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See doc/COPYRIGHT.md for more details.
++#%>
<% html_title(l(:label_overview)) -%>
<!-- page_layout.html -->
<% field_list = ['top', 'left', 'right', 'hidden'] %>
<% content_for :header_tags do %>
<%= javascript_include_tag "my_project_page/my_project_page" %>
<%= stylesheet_link_tag "my_project_page/my_projects_overview", :media => 'all' %>
<% heads_for_wiki_formatter %>
<% end %>
<script language="JavaScript">
//<![CDATA[
function recreateSortables() {
<% field_list.each do |f| %>
Sortable.destroy('list-<%= f %>');
<% end %>
<% field_list.each do |f| %>
Sortable.create("list-<%= f %>", {
constraint: false,
containment: <%= field_list.collect {|x| "list-#{x}" }.inspect.html_safe %>,
dropOnEmpty: true,
handle: 'handle',
onUpdate: function () {
new Ajax.Request('<%= url_for(:controller => '/my_projects_overviews',
:action => 'order_blocks',
:group => f) %>',
{ asynchronous: true,
evalScripts: true,
parameters: Sortable.serialize("list-<%= f %>")
});
},
only: 'mypage-box',
tag: 'div' });
<% end %>
}
function updateSelect() {
s = $('block-select')
for (var i = 0; i < s.options.length; i++) {
if ($('block_' + s.options[i].value)) {
s.options[i].disabled = true;
} else {
s.options[i].disabled = false;
}
}
s.options[0].selected = true;
}
function afterAddBlock(response) {
recreateSortables();
updateSelect();
editTextilizable(extractBlockName(response));
new Effect.ScrollTo('list-hidden');
}
function extractBlockName(response) {
return response.responseText.match(/id="block_(.*?)"/)[1];
}
function removeBlock(block) {
Effect.DropOut(block);
$(block).remove();
updateSelect();
}
function resetTextilizable(name) {
$("textile_" + name).setValue(window["page_layout-textile" + name] + "");
toggleTextilizableVisibility(name);
return false;
}
function editTextilizable(name) {
var textile_name = $("textile_" + name);
if (textile_name != null) {
window["page_layout-textile" + name] = textile_name.getValue();
toggleTextilizableVisibility(name);
}
return false;
}
function toggleTextilizableVisibility(name) {
$(name + '-form-div').toggle();
$(name + '-preview-div').toggle();
$(name + '-text').toggle();
}
function addBlock() {
new Ajax.Updater('list-hidden',
$('block-form').action,
{ insertion: 'top',
onComplete: afterAddBlock,
parameters: Form.serialize('block-form'),
evalScripts:true
});
return false;
}
//]]>
</script>
<% content_for :action_menu_specific do %>
<li>
<%= form_tag({:action => "add_block"}, :id => "block-form") do %>
<%= select_tag 'block',
<% content_for :toolbar do %>
<li class="toolbar-item">
<%= styled_form_tag({:action => "add_block"}, :id => "block-form") do %>
<%= styled_select_tag 'block',
("<option>--#{t(:button_add)}--</option>" + options_for_select(block_options)).html_safe,
:id => "block-select",
:onChange => "addBlock();",
class: 'form--select -small'
class: 'form--select'
%>
<% end %>
</li>
<li><%= link_to l(:button_back), {:action => 'index'}, :class => 'icon icon-cancel' %></li>
<li class="toolbar-item"><%= link_to l(:button_back), {:action => 'index'}, class: 'button' %></li>
<% end %>
<h2><%=l(:label_overview)%></h2>
<%= render :partial => 'layouts/action_menu_specific' %>
<%= render :partial => 'layouts/toolbar' %>
<h4><%=l(:label_visible_elements) %></h4>
<% (field_list - ['hidden']).each do |f| %>
<div id="list-<%= f %>" class="splitcontent<%= f %> block-receiver">
<% blocks[f].each do |b| %>
<% if MyProjectsOverviewsController.available_blocks.keys.include? b %>
<%= render(:partial => 'block', :locals => { :block_name => b }) %>
<% elsif b.respond_to? :to_ary %>
<%= render(:partial => 'block_textilizable',
:locals => {:block_name => b.first,
:block_title => b[1],
:textile => b.last}) %>
<% end %>
<div id="visible-grid">
<div class="grid-block">
<% top_fields.each do |f| %>
<%= grid_field f %>
<% end %>
</div>
<% end %>
<div style="clear: both"></div>
<h4><%=l(:label_hidden_elements) %></h4>
<div id="list-hidden" class="block-receiver">
<% blocks['hidden'].each do |b| %>
<% if MyProjectsOverviewsController.available_blocks.keys.include? b %>
<%= render(:partial => 'block', :locals => {:block_name => b}) %>
<% elsif b.respond_to? :to_ary %>
<%= render(:partial => 'block_textilizable',
:locals => {:block_name => b.first,
:block_title => b[1],
:textile => b.last}) %>
<div class="grid-block">
<% middle_fields.each do |f| %>
<%= grid_field f %>
<% end %>
<% end %>
</div>
</div>
<h4><%=l(:label_hidden_elements) %></h4>
<% hidden_fields.each do |f| %>
<%= grid_field f %>
<% end %>
<h4><%= l(:label_file_plural) %></h4>
<div class="attachments" id="page_layout_attachments">
<%= render(:partial => "page_layout_attachments") %>
</div>
<% field_list.each do |f| %>
<%= sortable_element "list-#{f}",
:tag => 'div',
:only => 'mypage-box',
:handle => "handle",
:dropOnEmpty => true,
:containment => field_list.collect {|x| "list-#{x}" },
:constraint => false,
:url => { :action => "order_blocks", :group => f }
%>
<% end %>
<%= javascript_tag "updateSelect()" %>
<% content_for :header_tags do %>
<%= javascript_include_tag "my_project_page/my_project_page" %>
<%= stylesheet_link_tag "my_project_page/my_projects_overview", :media => 'all' %>
<% heads_for_wiki_formatter %>
<% end %>
<% html_title(l(:label_overview)) -%>

@ -0,0 +1,7 @@
(function($) {
var block = $('#block_' + '<%= @block.is_a?(Array) ? @block.first.dasherize : @block.dasherize %>');
block.fadeOut('fast', function() {
block.remove();
myPage.updateSelect();
});
}(jQuery))
Loading…
Cancel
Save