parent
cd049721d5
commit
399e8c3ed9
@ -1,52 +0,0 @@ |
|||||||
#-- copyright |
|
||||||
# OpenProject is an open source project management software. |
|
||||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License version 3. |
|
||||||
# |
|
||||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
|
||||||
# Copyright (C) 2006-2017 Jean-Philippe Lang |
|
||||||
# Copyright (C) 2010-2013 the ChiliProject Team |
|
||||||
# |
|
||||||
# This program 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 2 |
|
||||||
# of the License, or (at your option) any later version. |
|
||||||
# |
|
||||||
# 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 docs/COPYRIGHT.rdoc for more details. |
|
||||||
#++ |
|
||||||
|
|
||||||
module ExtendedHTTP |
|
||||||
# Use this in response to an HTTP POST (or PUT), telling the client where the |
|
||||||
# new resource is. Works just like redirect_to, but sends back a 303 (See |
|
||||||
# Other) status code. Redirects should be used to tell the client to repeat |
|
||||||
# the same request on a different resource, and see_other when we want the |
|
||||||
# client to follow a POST (on this resource) with a GET (to the new resource). |
|
||||||
# |
|
||||||
# This is especially useful for successful create actions. |
|
||||||
def see_other(options = {}) |
|
||||||
if options.is_a?(Hash) |
|
||||||
redirect_to options.merge(status: :see_other) |
|
||||||
else |
|
||||||
redirect_to options, status: :see_other |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
# Use this in response to an HTTP PUT (or POST), telling the client that |
|
||||||
# everything went well and the desired change was performed successfully. |
|
||||||
# |
|
||||||
# This is especially useful for successful update actions. |
|
||||||
def no_content |
|
||||||
render body: '', status: :no_content |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,8 @@ |
|||||||
|
module OpenProject |
||||||
|
module Patches |
||||||
|
module Array |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
Array.send(:include, Redmine::Diff::Diffable) |
@ -1,30 +0,0 @@ |
|||||||
#-- encoding: UTF-8 |
|
||||||
#-- copyright |
|
||||||
# OpenProject is an open source project management software. |
|
||||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License version 3. |
|
||||||
# |
|
||||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
|
||||||
# Copyright (C) 2006-2017 Jean-Philippe Lang |
|
||||||
# Copyright (C) 2010-2013 the ChiliProject Team |
|
||||||
# |
|
||||||
# This program 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 2 |
|
||||||
# of the License, or (at your option) any later version. |
|
||||||
# |
|
||||||
# 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 docs/COPYRIGHT.rdoc for more details. |
|
||||||
#++ |
|
||||||
|
|
||||||
Dir[File.dirname(__FILE__) + '/core_ext/*.rb'].each { |file| require(file) } |
|
@ -1,36 +0,0 @@ |
|||||||
#-- encoding: UTF-8 |
|
||||||
#-- copyright |
|
||||||
# OpenProject is an open source project management software. |
|
||||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License version 3. |
|
||||||
# |
|
||||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
|
||||||
# Copyright (C) 2006-2017 Jean-Philippe Lang |
|
||||||
# Copyright (C) 2010-2013 the ChiliProject Team |
|
||||||
# |
|
||||||
# This program 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 2 |
|
||||||
# of the License, or (at your option) any later version. |
|
||||||
# |
|
||||||
# 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 docs/COPYRIGHT.rdoc for more details. |
|
||||||
#++ |
|
||||||
|
|
||||||
require File.dirname(__FILE__) + '/string/conversions' |
|
||||||
require File.dirname(__FILE__) + '/string/inflections' |
|
||||||
|
|
||||||
class String #:nodoc: |
|
||||||
include Redmine::CoreExtensions::String::Conversions |
|
||||||
include Redmine::CoreExtensions::String::Inflections |
|
||||||
end |
|
@ -0,0 +1,179 @@ |
|||||||
|
# = Diff |
||||||
|
# (({diff.rb})) - computes the differences between two arrays or |
||||||
|
# strings. Copyright (C) 2001 Lars Christensen |
||||||
|
# |
||||||
|
# == Synopsis |
||||||
|
# |
||||||
|
# diff = Diff.new(a, b) |
||||||
|
# b = a.patch(diff) |
||||||
|
# |
||||||
|
# == Class Diff |
||||||
|
# === Class Methods |
||||||
|
# --- Diff.new(a, b) |
||||||
|
# --- a.diff(b) |
||||||
|
# Creates a Diff object which represent the differences between |
||||||
|
# ((|a|)) and ((|b|)). ((|a|)) and ((|b|)) can be either be arrays |
||||||
|
# of any objects, strings, or object of any class that include |
||||||
|
# module ((|Diffable|)) |
||||||
|
# |
||||||
|
# == Module Diffable |
||||||
|
# The module ((|Diffable|)) is intended to be included in any class for |
||||||
|
# which differences are to be computed. Diffable is included into String |
||||||
|
# and Array when (({diff.rb})) is (({require}))'d. |
||||||
|
# |
||||||
|
# Classes including Diffable should implement (({[]})) to get element at |
||||||
|
# integer indices, (({<<})) to append elements to the object and |
||||||
|
# (({ClassName#new})) should accept 0 arguments to create a new empty |
||||||
|
# object. |
||||||
|
# |
||||||
|
# === Instance Methods |
||||||
|
# --- Diffable#patch(diff) |
||||||
|
# Applies the differences from ((|diff|)) to the object ((|obj|)) |
||||||
|
# and return the result. ((|obj|)) is not changed. ((|obj|)) and |
||||||
|
# can be either an array or a string, but must match the object |
||||||
|
# from which the ((|diff|)) was created. |
||||||
|
|
||||||
|
class Redmine::Diff::ArrayStringDiff |
||||||
|
VERSION = 0.3 |
||||||
|
|
||||||
|
def self.lcs(a, b) |
||||||
|
astart = 0 |
||||||
|
bstart = 0 |
||||||
|
afinish = a.length - 1 |
||||||
|
bfinish = b.length - 1 |
||||||
|
mvector = [] |
||||||
|
|
||||||
|
# First we prune off any common elements at the beginning |
||||||
|
while astart <= afinish && bstart <= afinish && a[astart] == b[bstart] |
||||||
|
mvector[astart] = bstart |
||||||
|
astart += 1 |
||||||
|
bstart += 1 |
||||||
|
end |
||||||
|
|
||||||
|
# now the end |
||||||
|
while astart <= afinish && bstart <= bfinish && a[afinish] == b[bfinish] |
||||||
|
mvector[afinish] = bfinish |
||||||
|
afinish -= 1 |
||||||
|
bfinish -= 1 |
||||||
|
end |
||||||
|
|
||||||
|
bmatches = b.reverse_hash(bstart..bfinish) |
||||||
|
thresh = [] |
||||||
|
links = [] |
||||||
|
|
||||||
|
(astart..afinish).each do |aindex| |
||||||
|
aelem = a[aindex] |
||||||
|
next unless bmatches.has_key? aelem |
||||||
|
k = nil |
||||||
|
bmatches[aelem].reverse_each { |bindex| |
||||||
|
if k && (thresh[k] > bindex) && (thresh[k - 1] < bindex) |
||||||
|
thresh[k] = bindex |
||||||
|
else |
||||||
|
k = thresh.replacenextlarger(bindex, k) |
||||||
|
end |
||||||
|
links[k] = [(k == 0) ? nil : links[k - 1], aindex, bindex] if k |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
if !thresh.empty? |
||||||
|
link = links[thresh.length - 1] |
||||||
|
while link |
||||||
|
mvector[link[1]] = link[2] |
||||||
|
link = link[0] |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
mvector |
||||||
|
end |
||||||
|
|
||||||
|
def makediff(a, b) |
||||||
|
mvector = self.class.lcs(a, b) |
||||||
|
ai = bi = 0 |
||||||
|
while ai < mvector.length |
||||||
|
bline = mvector[ai] |
||||||
|
if bline |
||||||
|
while bi < bline |
||||||
|
discardb(bi, b[bi]) |
||||||
|
bi += 1 |
||||||
|
end |
||||||
|
match(ai, bi) |
||||||
|
bi += 1 |
||||||
|
else |
||||||
|
discarda(ai, a[ai]) |
||||||
|
end |
||||||
|
ai += 1 |
||||||
|
end |
||||||
|
while ai < a.length |
||||||
|
discarda(ai, a[ai]) |
||||||
|
ai += 1 |
||||||
|
end |
||||||
|
while bi < b.length |
||||||
|
discardb(bi, b[bi]) |
||||||
|
bi += 1 |
||||||
|
end |
||||||
|
match(ai, bi) |
||||||
|
1 |
||||||
|
end |
||||||
|
|
||||||
|
def compactdiffs |
||||||
|
diffs = [] |
||||||
|
@diffs.each do |df| |
||||||
|
i = 0 |
||||||
|
curdiff = [] |
||||||
|
while i < df.length |
||||||
|
whot = df[i][0] |
||||||
|
s = @isstring ? df[i][2].chr : [df[i][2]] |
||||||
|
p = df[i][1] |
||||||
|
last = df[i][1] |
||||||
|
i += 1 |
||||||
|
while df[i] && df[i][0] == whot && df[i][1] == last + 1 |
||||||
|
s << df[i][2] |
||||||
|
last = df[i][1] |
||||||
|
i += 1 |
||||||
|
end |
||||||
|
curdiff.push [whot, p, s] |
||||||
|
end |
||||||
|
diffs.push curdiff |
||||||
|
end |
||||||
|
diffs |
||||||
|
end |
||||||
|
|
||||||
|
attr_reader :diffs, :difftype |
||||||
|
|
||||||
|
def initialize(diffs_or_a, b = nil, isstring = nil) |
||||||
|
if b.nil? |
||||||
|
@diffs = diffs_or_a |
||||||
|
@isstring = isstring |
||||||
|
else |
||||||
|
@diffs = [] |
||||||
|
@curdiffs = [] |
||||||
|
makediff(diffs_or_a, b) |
||||||
|
@difftype = diffs_or_a.class |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def match(_ai, _bi) |
||||||
|
@diffs.push @curdiffs unless @curdiffs.empty? |
||||||
|
@curdiffs = [] |
||||||
|
end |
||||||
|
|
||||||
|
def discarda(i, elem) |
||||||
|
@curdiffs.push ['-', i, elem] |
||||||
|
end |
||||||
|
|
||||||
|
def discardb(i, elem) |
||||||
|
@curdiffs.push ['+', i, elem] |
||||||
|
end |
||||||
|
|
||||||
|
def compact |
||||||
|
Diff.new(compactdiffs) |
||||||
|
end |
||||||
|
|
||||||
|
def compact! |
||||||
|
@diffs = compactdiffs |
||||||
|
end |
||||||
|
|
||||||
|
def inspect |
||||||
|
@diffs.inspect |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,123 @@ |
|||||||
|
# = Diff |
||||||
|
# (({diff.rb})) - computes the differences between two arrays or |
||||||
|
# strings. Copyright (C) 2001 Lars Christensen |
||||||
|
# |
||||||
|
# == Synopsis |
||||||
|
# |
||||||
|
# diff = Diff.new(a, b) |
||||||
|
# b = a.patch(diff) |
||||||
|
# |
||||||
|
# == Class Diff |
||||||
|
# === Class Methods |
||||||
|
# --- Diff.new(a, b) |
||||||
|
# --- a.diff(b) |
||||||
|
# Creates a Diff object which represent the differences between |
||||||
|
# ((|a|)) and ((|b|)). ((|a|)) and ((|b|)) can be either be arrays |
||||||
|
# of any objects, strings, or object of any class that include |
||||||
|
# module ((|Diffable|)) |
||||||
|
# |
||||||
|
# == Module Diffable |
||||||
|
# The module ((|Diffable|)) is intended to be included in any class for |
||||||
|
# which differences are to be computed. Diffable is included into String |
||||||
|
# and Array when (({diff.rb})) is (({require}))'d. |
||||||
|
# |
||||||
|
# Classes including Diffable should implement (({[]})) to get element at |
||||||
|
# integer indices, (({<<})) to append elements to the object and |
||||||
|
# (({ClassName#new})) should accept 0 arguments to create a new empty |
||||||
|
# object. |
||||||
|
# |
||||||
|
# === Instance Methods |
||||||
|
# --- Diffable#patch(diff) |
||||||
|
# Applies the differences from ((|diff|)) to the object ((|obj|)) |
||||||
|
# and return the result. ((|obj|)) is not changed. ((|obj|)) and |
||||||
|
# can be either an array or a string, but must match the object |
||||||
|
# from which the ((|diff|)) was created. |
||||||
|
|
||||||
|
module Redmine::Diff::Diffable |
||||||
|
def diff(b) |
||||||
|
Redmine::Diff::ArrayStringDiff.new(self, b) |
||||||
|
end |
||||||
|
|
||||||
|
# Create a hash that maps elements of the array to arrays of indices |
||||||
|
# where the elements are found. |
||||||
|
|
||||||
|
def reverse_hash(range = (0...length)) |
||||||
|
revmap = {} |
||||||
|
range.each do |i| |
||||||
|
elem = self[i] |
||||||
|
if revmap.has_key? elem |
||||||
|
revmap[elem].push i |
||||||
|
else |
||||||
|
revmap[elem] = [i] |
||||||
|
end |
||||||
|
end |
||||||
|
revmap |
||||||
|
end |
||||||
|
|
||||||
|
def replacenextlarger(value, high = nil) |
||||||
|
high ||= length |
||||||
|
if self.empty? || value > self[-1] |
||||||
|
push value |
||||||
|
return high |
||||||
|
end |
||||||
|
# binary search for replacement point |
||||||
|
low = 0 |
||||||
|
while low < high |
||||||
|
index = (high + low) / 2 |
||||||
|
found = self[index] |
||||||
|
return nil if value == found |
||||||
|
if value > found |
||||||
|
low = index + 1 |
||||||
|
else |
||||||
|
high = index |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
self[low] = value |
||||||
|
# $stderr << "replace #{value} : 0/#{low}/#{init_high} (#{steps} steps) (#{init_high-low} off )\n" |
||||||
|
# $stderr.puts self.inspect |
||||||
|
# gets |
||||||
|
# p length - low |
||||||
|
low |
||||||
|
end |
||||||
|
|
||||||
|
def patch(diff) |
||||||
|
newary = nil |
||||||
|
if diff.difftype == String |
||||||
|
newary = diff.difftype.new('') |
||||||
|
else |
||||||
|
newary = diff.difftype.new |
||||||
|
end |
||||||
|
ai = 0 |
||||||
|
bi = 0 |
||||||
|
diff.diffs.each do |d| |
||||||
|
d.each { |mod| |
||||||
|
case mod[0] |
||||||
|
when '-' |
||||||
|
while ai < mod[1] |
||||||
|
newary << self[ai] |
||||||
|
ai += 1 |
||||||
|
bi += 1 |
||||||
|
end |
||||||
|
ai += 1 |
||||||
|
when '+' |
||||||
|
while bi < mod[1] |
||||||
|
newary << self[ai] |
||||||
|
ai += 1 |
||||||
|
bi += 1 |
||||||
|
end |
||||||
|
newary << mod[2] |
||||||
|
bi += 1 |
||||||
|
else |
||||||
|
raise 'Unknown diff action' |
||||||
|
end |
||||||
|
} |
||||||
|
end |
||||||
|
while ai < length |
||||||
|
newary << self[ai] |
||||||
|
ai += 1 |
||||||
|
bi += 1 |
||||||
|
end |
||||||
|
newary |
||||||
|
end |
||||||
|
end |
@ -1,33 +0,0 @@ |
|||||||
#-- encoding: UTF-8 |
|
||||||
#-- copyright |
|
||||||
# OpenProject is an open source project management software. |
|
||||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License version 3. |
|
||||||
# |
|
||||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
|
||||||
# Copyright (C) 2006-2017 Jean-Philippe Lang |
|
||||||
# Copyright (C) 2010-2013 the ChiliProject Team |
|
||||||
# |
|
||||||
# This program 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 2 |
|
||||||
# of the License, or (at your option) any later version. |
|
||||||
# |
|
||||||
# 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 docs/COPYRIGHT.rdoc for more details. |
|
||||||
#++ |
|
||||||
|
|
||||||
module Redmine |
|
||||||
# THIS IS A REDMINE COMPATIBILITY INTERFACE |
|
||||||
VERSION = OpenProject::VERSION |
|
||||||
end |
|
@ -0,0 +1,23 @@ |
|||||||
|
module BasicData |
||||||
|
module Backlogs |
||||||
|
module TypeSeeder |
||||||
|
def coded_visibility_table |
||||||
|
super.merge backlogs_visibility_table |
||||||
|
end |
||||||
|
|
||||||
|
## |
||||||
|
# Relies on type names in the core TypeSeeder being (in this order) |
||||||
|
# task, milestone, phase, feature, epic, user_story, bug |
||||||
|
# and 0 to 2 being mapped to |
||||||
|
# hidden, default, visible |
||||||
|
def backlogs_visibility_table |
||||||
|
{ |
||||||
|
story_points: [0, 0, 0, 1, 2, 2, 1], |
||||||
|
remaining_time: [1, 0, 0, 1, 1, 1, 1] |
||||||
|
} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
BasicData::TypeSeeder.prepend BasicData::Backlogs::TypeSeeder |
||||||
|
end |
||||||
|
end |
@ -1,25 +0,0 @@ |
|||||||
module StandardSeeder |
|
||||||
module BasicData |
|
||||||
module Backlogs |
|
||||||
module TypeSeeder |
|
||||||
def coded_visibility_table |
|
||||||
super.merge backlogs_visibility_table |
|
||||||
end |
|
||||||
|
|
||||||
## |
|
||||||
# Relies on type names in the core TypeSeeder being (in this order) |
|
||||||
# task, milestone, phase, feature, epic, user_story, bug |
|
||||||
# and 0 to 2 being mapped to |
|
||||||
# hidden, default, visible |
|
||||||
def backlogs_visibility_table |
|
||||||
{ |
|
||||||
story_points: [0, 0, 0, 1, 2, 2, 1], |
|
||||||
remaining_time: [1, 0, 0, 1, 1, 1, 1] |
|
||||||
} |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
StandardSeeder::BasicData::TypeSeeder.prepend StandardSeeder::BasicData::Backlogs::TypeSeeder |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,105 @@ |
|||||||
|
module DemoData |
||||||
|
class OverviewSeeder < Seeder |
||||||
|
include ::DemoData::References |
||||||
|
|
||||||
|
def seed_data! |
||||||
|
puts "*** Seeding Overview" |
||||||
|
|
||||||
|
Array(demo_data_for('projects')).each do |(_key, project_config)| |
||||||
|
next unless overview_config(project_config) |
||||||
|
|
||||||
|
puts " -Creating overview for #{project_config[:name]}" |
||||||
|
|
||||||
|
overview = overview_from_config(project_config) |
||||||
|
|
||||||
|
overview_config(project_config)[:widgets].each do |widget_config| |
||||||
|
build_widget(overview, widget_config) |
||||||
|
end |
||||||
|
|
||||||
|
overview.save! |
||||||
|
end |
||||||
|
|
||||||
|
add_permission |
||||||
|
end |
||||||
|
|
||||||
|
def applicable? |
||||||
|
Grids::Overview.count.zero? && demo_projects_exist? |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def demo_projects_exist? |
||||||
|
identifiers = Array(demo_data_for('projects')) |
||||||
|
.map { |_key, project| project[:identifier] } |
||||||
|
|
||||||
|
identifiers |
||||||
|
.all? { |ident| Project.where(identifier: ident).exists? } |
||||||
|
end |
||||||
|
|
||||||
|
def build_widget(overview, widget_config) |
||||||
|
create_attachments!(overview, widget_config) |
||||||
|
|
||||||
|
widget_options = widget_config[:options] |
||||||
|
|
||||||
|
text_with_references(overview, widget_options) |
||||||
|
query_id_references(overview, widget_options) |
||||||
|
|
||||||
|
overview.widgets.build(widget_config.except(:attachments)) |
||||||
|
end |
||||||
|
|
||||||
|
def create_attachments!(overview, attributes) |
||||||
|
Array(attributes[:attachments]).each do |file_name| |
||||||
|
attachment = overview.attachments.build |
||||||
|
attachment.author = User.admin.first |
||||||
|
attachment.file = File.new attachment_path(file_name) |
||||||
|
|
||||||
|
attachment.save! |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def attachment_path(file_name) |
||||||
|
::Overviews::Engine.root.join( |
||||||
|
"config/locales/media/#{I18n.locale}/#{file_name}" |
||||||
|
) |
||||||
|
end |
||||||
|
|
||||||
|
def project_from_config(config) |
||||||
|
Project.find_by! identifier: config[:identifier] |
||||||
|
end |
||||||
|
|
||||||
|
def overview_from_config(project_config) |
||||||
|
params = overview_config(project_config) |
||||||
|
.slice(:row_count, :column_count) |
||||||
|
.merge(project: project_from_config(project_config)) |
||||||
|
|
||||||
|
Grids::Overview |
||||||
|
.create(params) |
||||||
|
end |
||||||
|
|
||||||
|
def overview_config(project_config) |
||||||
|
project_config[:"project-overview"] |
||||||
|
end |
||||||
|
|
||||||
|
def text_with_references(overview, widget_options) |
||||||
|
if widget_options && widget_options[:text] |
||||||
|
widget_options[:text] = with_references(widget_options[:text], overview.project) |
||||||
|
widget_options[:text] = link_attachments(widget_options[:text], overview.attachments) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def query_id_references(overview, widget_options) |
||||||
|
if widget_options && widget_options[:queryId] |
||||||
|
widget_options[:queryId] = with_references(widget_options[:queryId], overview.project) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def add_permission |
||||||
|
Role |
||||||
|
.includes(:role_permissions) |
||||||
|
.where(role_permissions: { permission: 'edit_project' }) |
||||||
|
.each do |role| |
||||||
|
role.add_permission!(:manage_overview) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -1,107 +0,0 @@ |
|||||||
module DemoData |
|
||||||
module Overviews |
|
||||||
class OverviewSeeder < Seeder |
|
||||||
include ::DemoData::References |
|
||||||
|
|
||||||
def seed_data! |
|
||||||
puts "*** Seeding Overview" |
|
||||||
|
|
||||||
Array(demo_data_for('projects')).each do |(_key, project_config)| |
|
||||||
next unless overview_config(project_config) |
|
||||||
|
|
||||||
puts " -Creating overview for #{project_config[:name]}" |
|
||||||
|
|
||||||
overview = overview_from_config(project_config) |
|
||||||
|
|
||||||
overview_config(project_config)[:widgets].each do |widget_config| |
|
||||||
build_widget(overview, widget_config) |
|
||||||
end |
|
||||||
|
|
||||||
overview.save! |
|
||||||
end |
|
||||||
|
|
||||||
add_permission |
|
||||||
end |
|
||||||
|
|
||||||
def applicable? |
|
||||||
Grids::Overview.count.zero? && demo_projects_exist? |
|
||||||
end |
|
||||||
|
|
||||||
private |
|
||||||
|
|
||||||
def demo_projects_exist? |
|
||||||
identifiers = Array(demo_data_for('projects')) |
|
||||||
.map { |_key, project| project[:identifier] } |
|
||||||
|
|
||||||
identifiers |
|
||||||
.all? { |ident| Project.where(identifier: ident).exists? } |
|
||||||
end |
|
||||||
|
|
||||||
def build_widget(overview, widget_config) |
|
||||||
create_attachments!(overview, widget_config) |
|
||||||
|
|
||||||
widget_options = widget_config[:options] |
|
||||||
|
|
||||||
text_with_references(overview, widget_options) |
|
||||||
query_id_references(overview, widget_options) |
|
||||||
|
|
||||||
overview.widgets.build(widget_config.except(:attachments)) |
|
||||||
end |
|
||||||
|
|
||||||
def create_attachments!(overview, attributes) |
|
||||||
Array(attributes[:attachments]).each do |file_name| |
|
||||||
attachment = overview.attachments.build |
|
||||||
attachment.author = User.admin.first |
|
||||||
attachment.file = File.new attachment_path(file_name) |
|
||||||
|
|
||||||
attachment.save! |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
def attachment_path(file_name) |
|
||||||
::Overviews::Engine.root.join( |
|
||||||
"config/locales/media/#{I18n.locale}/#{file_name}" |
|
||||||
) |
|
||||||
end |
|
||||||
|
|
||||||
def project_from_config(config) |
|
||||||
Project.find_by! identifier: config[:identifier] |
|
||||||
end |
|
||||||
|
|
||||||
def overview_from_config(project_config) |
|
||||||
params = overview_config(project_config) |
|
||||||
.slice(:row_count, :column_count) |
|
||||||
.merge(project: project_from_config(project_config)) |
|
||||||
|
|
||||||
Grids::Overview |
|
||||||
.create(params) |
|
||||||
end |
|
||||||
|
|
||||||
def overview_config(project_config) |
|
||||||
project_config[:"project-overview"] |
|
||||||
end |
|
||||||
|
|
||||||
def text_with_references(overview, widget_options) |
|
||||||
if widget_options && widget_options[:text] |
|
||||||
widget_options[:text] = with_references(widget_options[:text], overview.project) |
|
||||||
widget_options[:text] = link_attachments(widget_options[:text], overview.attachments) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
def query_id_references(overview, widget_options) |
|
||||||
if widget_options && widget_options[:queryId] |
|
||||||
widget_options[:queryId] = with_references(widget_options[:queryId], overview.project) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
def add_permission |
|
||||||
Role |
|
||||||
.includes(:role_permissions) |
|
||||||
.where(role_permissions: { permission: 'edit_project' }) |
|
||||||
.each do |role| |
|
||||||
role.add_permission!(:manage_overview) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -1,5 +1,7 @@ |
|||||||
module WorkPackageDisplay |
module OpenProject::PDFExport::ExportCard::ModelDisplay |
||||||
def display_id |
module WorkPackageDisplay |
||||||
"#{(kind.is_standard) ? "" : "#{kind.name}"} ##{id}" |
def display_id |
||||||
|
"#{kind.is_standard ? "" : "#{kind.name}"} ##{id}" |
||||||
|
end |
||||||
end |
end |
||||||
end |
end |
||||||
|
@ -1,5 +0,0 @@ |
|||||||
module OpenProject |
|
||||||
module Recaptcha |
|
||||||
require "open_project/recaptcha/engine" |
|
||||||
end |
|
||||||
end |
|
@ -1,95 +0,0 @@ |
|||||||
module TwoFactorAuthentication |
|
||||||
class TokenService |
|
||||||
attr_reader :user, :device, :strategy, :channel |
|
||||||
|
|
||||||
## |
|
||||||
# Create a token service for the given user. |
|
||||||
def initialize(user:, use_device: nil, use_channel: nil) |
|
||||||
@user = user |
|
||||||
@device = use_device || user.otp_devices.get_default |
|
||||||
@channel = use_channel || device.try(:channel) |
|
||||||
|
|
||||||
matching_strategy = get_matching_strategy |
|
||||||
if matching_strategy |
|
||||||
@strategy = matching_strategy.new(user: @user, device: @device) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
## |
|
||||||
# Determines whether a token should be entered by the user. |
|
||||||
def requires_token? |
|
||||||
# If 2FA is enforced, always required |
|
||||||
return true if manager.enforced? |
|
||||||
|
|
||||||
# Otherwise, only enabled if active and a device is present for the user |
|
||||||
return manager.enabled? && device.present? |
|
||||||
end |
|
||||||
|
|
||||||
## |
|
||||||
# Request a token through the active strategy |
|
||||||
# IF the instance is set up to have optional 2FA |
|
||||||
def request |
|
||||||
# Validate that we can request the token for this user |
|
||||||
# and get the matching strategy we will use |
|
||||||
verify_device_and_strategy |
|
||||||
|
|
||||||
# Produce the token with the given strategy (e.g., sending an sms) |
|
||||||
strategy.transmit |
|
||||||
|
|
||||||
ServiceResult.new(success: true, result: strategy.transmit_success_message) |
|
||||||
rescue => e |
|
||||||
Rails.logger.error "[2FA plugin] Error during token request to user##{user.id}: #{e}" |
|
||||||
|
|
||||||
result = ServiceResult.new(success: false) |
|
||||||
result.errors.add(:base, e.message) |
|
||||||
|
|
||||||
result |
|
||||||
end |
|
||||||
|
|
||||||
## |
|
||||||
# Validate a token that was input by the user |
|
||||||
def verify(input_token) |
|
||||||
# Validate that we can request the token for this user |
|
||||||
# and get the matching strategy we will use |
|
||||||
verify_device_and_strategy |
|
||||||
|
|
||||||
# Produce the token with the given strategy (e.g., sending an sms) |
|
||||||
result = strategy.verify input_token |
|
||||||
|
|
||||||
ServiceResult.new(success: result) |
|
||||||
rescue => e |
|
||||||
Rails.logger.error "[2FA plugin] Error during token validation for user##{user.id}: #{e}" |
|
||||||
|
|
||||||
result = ServiceResult.new(success: false) |
|
||||||
result.errors.add(:base, e.message) |
|
||||||
|
|
||||||
result |
|
||||||
end |
|
||||||
|
|
||||||
private |
|
||||||
|
|
||||||
## |
|
||||||
# Get the matching strategy from the desired channel, if set. |
|
||||||
def get_matching_strategy |
|
||||||
if @channel |
|
||||||
manager.find_matching_strategy(@channel) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
## |
|
||||||
# Perform service checks for the request and validate endpoints of this service |
|
||||||
def verify_device_and_strategy |
|
||||||
raise I18n.t('two_factor_authentication.error_2fa_disabled') unless manager.enabled? |
|
||||||
|
|
||||||
# Ensure the user's default device for OTP exists |
|
||||||
raise I18n.t('two_factor_authentication.error_no_device') if device.nil? |
|
||||||
|
|
||||||
# Ensure a matching registered strategy for the device's channel exists |
|
||||||
raise I18n.t('two_factor_authentication.error_no_matching_strategy') if strategy.nil? |
|
||||||
end |
|
||||||
|
|
||||||
def manager |
|
||||||
::OpenProject::TwoFactorAuthentication::TokenStrategyManager |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -1,5 +0,0 @@ |
|||||||
module OpenProject |
|
||||||
module TwoFactorAuthentication |
|
||||||
require "open_project/two_factor_authentication/engine" |
|
||||||
end |
|
||||||
end |
|
Loading…
Reference in new issue