parent
0e1f4ed4eb
commit
49f12b3e21
@ -0,0 +1,24 @@ |
||||
-----BEGIN PUBLIC KEY----- |
||||
MIIEFjANBgkqhkiG9w0BAQEFAAOCBAMAMIID/gKCA/UAquIZchoog2ffcr9J2KSl |
||||
mlum6sN3smTVNsp9JGd1q4fr/kUFGch6q1cFEX3x5BGDXx7wPPI4ppKzeQHaxWmx |
||||
wxqs3eevcTFUEF9A2MPX7p5Ia0TbH4d7e7D9YMWvDXoQLggrxMFdUHY3ppUnBPgB |
||||
+EJG1Pv0FlBAdxYX0em7kLwhcp9PBP/zXso/qkkKK/pncyKizOLC3zv3E0ixcQ7o |
||||
Nq0aolTJFMHcqEquKaQN1jicDdzU6ks+YKh7kByZvVChe/InlroVXKrUa34hAZDM |
||||
acEkURJma3meN0IyPFA7fHRe1AhiNYF2MatNKysPrbOffYLOjamlaqmHTeJAec6e |
||||
vMHd+LlIz4xXivR0lY2wDawqp0waSLJaW8lZetOf0iwbqQkzZhz4sWDZopyGiqAU |
||||
v9/zS4OjUBr7JQbVcV3LIkzGWwNysSvTMrlvzCesYVsCwpLjP6gFxdclYJuTwEeL |
||||
o+T+AgoNyuj6ixhwHTJxIVhuBpebX44/YTYyUGMgItekDCH2Dxvtv2DaCL7YIqNG |
||||
ibvCyzCyLak7Tz97CMvCUf1EIRVfolbGpphi2Zzpoeqhfheh+0LQ3gmMBJpuLnJU |
||||
VXEtrOPunTkOrdUqL5rD/+mfd8yufsJ16Uk5j7gNUcIJsCcGWZ3Nhfidi3tvmJPF |
||||
H8HgNZ6W6smj9k7+TdZbRsH1LZp1LL/stLch3ffFHHcJye7d2t75uKiOoz1/1JMY |
||||
fl/wfaEaKvTGBKr/NFKcVDSBXHgx6VMA3oWV2AyLnTaY98XVPY1zzbyNKlNQkSaD |
||||
p/VCl334+YMwR9/5aJYJg58lljw6aBu+cozNkydKjCmqEpZNdR1tFusAY7jd/M2G |
||||
yXPBTdUJD6GrLx0Hot/wImJ30gOMgpeoetNUfM9/FimySLy65DRzCtLm1hthAlWM |
||||
Is5vFwMmFFSb3ozTsnkj9W16jHk9HdodKJfezzcPqu3TW2EMMNbbtXa3OPaHkht2 |
||||
huYJGnGu0hNhAj+x+KCxkpLveS0Ajw322qmtAqnwJLvfwShnz//cptNX5kXtrNGy |
||||
+o6I4jHibIATfaMKMt8gHmCZ381zAwrAOU7c0FQna+IkU8dgZDx7T+Xo1Q/GXuaO |
||||
1b2aT6geT60A3VgF+OBnoHe5Ext7vfNL9v0wcN5NLR5KgjexwEhcBcA2FauCTrVt |
||||
FUNgir+5XALd/wBlvxkvPKTJnQld/aK7xF0ui3c3/ryPX5cKzpfm9APK/hOFzkJ7 |
||||
ieARHqrQGqOYdClIUJIH0b/92dFq49Eqn1cKpztVzsU9xzdI/4w5JUSw/kbguVf7 |
||||
Yd0Rdc9KF/9WMwjzrWSti4meNBUO6/18cAognx0Pf5qsrSzOewIDAQAB |
||||
-----END PUBLIC KEY----- |
After Width: | Height: | Size: 4.5 KiB |
@ -0,0 +1,13 @@ |
||||
.upsale-notification |
||||
max-width: 560px |
||||
margin-bottom: 20px |
||||
padding-bottom: 20px |
||||
|
||||
.widget-box--teaser-image |
||||
width: 30% |
||||
float: right |
||||
margin-top: -20px |
||||
|
||||
.token-form textarea |
||||
font-family: "Lucida Console", Monaco, monospace |
||||
max-width: 560px |
@ -0,0 +1,67 @@ |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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 token 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 token |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the token, 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 token for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public token |
||||
# 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.rdoc for more details. |
||||
#++ |
||||
class EnterprisesController < ApplicationController |
||||
layout 'admin' |
||||
menu_item :enterprise |
||||
|
||||
before_action :require_admin |
||||
|
||||
def show |
||||
@current_token = EnterpriseToken.current |
||||
@token = @current_token || EnterpriseToken.new |
||||
end |
||||
|
||||
def create |
||||
@token = EnterpriseToken.current || EnterpriseToken.new |
||||
@token.encoded_token = params[:enterprise_token][:encoded_token] |
||||
|
||||
if @token.save |
||||
flash[:notice] = t(:notice_successful_update) |
||||
redirect_to action: :show |
||||
else |
||||
render action: :show |
||||
end |
||||
end |
||||
|
||||
def destroy |
||||
token = EnterpriseToken.current |
||||
if token |
||||
token.destroy |
||||
flash[:notice] = t(:notice_successful_delete) |
||||
redirect_to action: :show |
||||
else |
||||
render_404 |
||||
end |
||||
end |
||||
|
||||
private |
||||
|
||||
def default_breadcrumb |
||||
t(:label_enterprise) |
||||
end |
||||
end |
@ -0,0 +1,107 @@ |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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. |
||||
# |
||||
# 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 doc/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
class EnterpriseToken < ActiveRecord::Base |
||||
class << self |
||||
def current |
||||
RequestStore.fetch(cache_key) do |
||||
set_current_token |
||||
end |
||||
end |
||||
|
||||
def cache_key |
||||
RequestStore.fetch(:current_ee_token_updated_at) { EnterpriseToken.maximum(:updated_at) } |
||||
most_recent_update = (RequestStore[:current_ee_token_updated_at] || Time.now.utc).to_i |
||||
"/openproject/ee/#{most_recent_update}" |
||||
end |
||||
|
||||
def clear_cache(key = cache_key) |
||||
Rails.cache.delete(key) |
||||
RequestStore.delete key |
||||
RequestStore.delete :current_ee_token_updated_at |
||||
end |
||||
|
||||
def allows_to?(action) |
||||
Authorization::EnterpriseService.new(current).call(action).result |
||||
end |
||||
|
||||
def show_banners |
||||
!current || current.expired? |
||||
end |
||||
|
||||
def set_current_token |
||||
token = EnterpriseToken.order('created_at DESC').first |
||||
|
||||
if token && token.token_object |
||||
token |
||||
end |
||||
end |
||||
end |
||||
|
||||
validates_presence_of :encoded_token |
||||
validate :valid_token_object |
||||
|
||||
before_save :unset_current_token |
||||
before_destroy :unset_current_token |
||||
|
||||
delegate :will_expire?, |
||||
:expired?, |
||||
:subscriber, |
||||
:mail, |
||||
:issued_at, |
||||
:starts_at, |
||||
:expires_at, |
||||
:restrictions, |
||||
to: :token_object |
||||
|
||||
def token_object |
||||
@token_object = load_token unless defined?(@token_object) |
||||
@token_object |
||||
end |
||||
|
||||
def allows_to?(action) |
||||
Authorization::EnterpriseService.new(self).call(action).result |
||||
end |
||||
|
||||
def unset_current_token |
||||
# Clear current cache |
||||
self.class.clear_cache |
||||
end |
||||
|
||||
private |
||||
|
||||
def load_token |
||||
OpenProject::Token.import(encoded_token) |
||||
rescue OpenProject::Token::ImportError => error |
||||
Rails.logger.error "Failed to load EE token: #{error}" |
||||
nil |
||||
end |
||||
|
||||
def valid_token_object |
||||
errors.add(:encoded_token, :unreadable) unless token_object |
||||
end |
||||
end |
@ -0,0 +1,64 @@ |
||||
#-- encoding: UTF-8 |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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. |
||||
# |
||||
# 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 doc/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
class Authorization::EnterpriseService |
||||
attr_accessor :token |
||||
|
||||
def initialize(token) |
||||
self.token = token |
||||
end |
||||
|
||||
# Return a true ServiceResult if the token contains this particular action. |
||||
def call(action) |
||||
|
||||
allowed = |
||||
if token.nil? || token.expired? |
||||
false |
||||
else |
||||
process(action) |
||||
end |
||||
|
||||
result(allowed) |
||||
end |
||||
|
||||
private |
||||
|
||||
def process(action) |
||||
case action |
||||
when :define_custom_style |
||||
true # Every non-expired token |
||||
else |
||||
false |
||||
end |
||||
end |
||||
|
||||
def result(bool) |
||||
ServiceResult.new(success: bool, result: bool) |
||||
end |
||||
end |
@ -0,0 +1,38 @@ |
||||
<div class="enterprise--active-token"> |
||||
<div class="attributes-group"> |
||||
<div class="attributes-key-value"> |
||||
<div class="attributes-key-value--key"><%= EnterpriseToken.human_attribute_name(:subscriber) %></div> |
||||
<div class="attributes-key-value--value-container"> |
||||
<div class="attributes-key-value--value -text"> |
||||
<span><%= @current_token.subscriber %></span> |
||||
</div> |
||||
</div> |
||||
<div class="attributes-key-value--key"><%= EnterpriseToken.human_attribute_name(:mail) %></div> |
||||
<div class="attributes-key-value--value-container"> |
||||
<div class="attributes-key-value--value -text"> |
||||
<span><%= @current_token.mail %></span> |
||||
</div> |
||||
</div> |
||||
<div class="attributes-key-value--key"><%= EnterpriseToken.human_attribute_name(:starts_at) %></div> |
||||
<div class="attributes-key-value--value-container"> |
||||
<div class="attributes-key-value--value -text"> |
||||
<span><%= format_date @current_token.starts_at %></span> |
||||
</div> |
||||
</div> |
||||
<% if @current_token.will_expire? %> |
||||
<div class="attributes-key-value--key"><%= EnterpriseToken.human_attribute_name(:expires_at) %></div> |
||||
<div class="attributes-key-value--value-container"> |
||||
<div class="attributes-key-value--value -text"> |
||||
<span><%= format_date @current_token.expires_at %></span> |
||||
</div> |
||||
</div> |
||||
<% end %> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<%= form_tag({ action: :destroy }, |
||||
method: :delete, |
||||
class: 'confirm-form-submit') do %> |
||||
<%= styled_button_tag l(:button_delete), type: 'submit', class: '-with-icon icon-delete' %> |
||||
<% end %> |
@ -0,0 +1,21 @@ |
||||
<% if @current_token.present? %> |
||||
<collapsible-section section-title="<%=h t('admin.enterprise.replace_token') %>"> |
||||
<% else %> |
||||
<collapsible-section initially-expanded="true" |
||||
section-title="<%=h t('admin.enterprise.add_token') %>"> |
||||
<% end %> |
||||
<%= labelled_tabular_form_for @token, |
||||
url: { action: :create }, |
||||
html: { class: 'token-form' }, |
||||
method: :post do |f|%> |
||||
|
||||
<div class="form--space"> |
||||
<%= f.text_area :encoded_token, :cols => 60, :rows => 15, placeholder: t('admin.enterprise.paste'), autocomplete: "off", autocorrect: "off", autocapitalize: "off", spellcheck: false %> |
||||
</div> |
||||
|
||||
<div class="form--space"> |
||||
<%= styled_button_tag t(:button_create), id: 'token-submit-button', class: '-highlight -with-icon icon-checkmark' %> |
||||
<%= link_to t(:button_cancel), { action: :show }, class: 'button' %> |
||||
</div> |
||||
<% end %> |
||||
</collapsible-section> |
@ -0,0 +1,48 @@ |
||||
<% html_title t(:label_administration), t(:label_enterprise_edition) %> |
||||
|
||||
<%= toolbar title: t(:label_enterprise_edition) do %> |
||||
<li class="toolbar-item"> |
||||
<%= link_to( "#{OpenProject::Static::Links.links[:upsale][:href]}?utm_source=ce-token-admin", |
||||
{ class: 'button -alt-highlight', |
||||
title: t('admin.enterprise.order')}) do %> |
||||
<i class="button--icon icon-add"></i> |
||||
<span class="button--text"><%= t('admin.enterprise.order') %></span> |
||||
<% end %> |
||||
</li> |
||||
<% end %> |
||||
|
||||
<%= error_messages_for 'token' %> |
||||
|
||||
<% if @current_token.present? %> |
||||
<%= render partial: "current" %> |
||||
<% else %> |
||||
<div class="notification-box upsale-notification"> |
||||
<div class="notification-box--content"> |
||||
<h3><%= t('admin.enterprise.upgrade_to_ee') %></h3> |
||||
<%= image_tag "enterprise_edition.png", class: "widget-box--teaser-image" %> |
||||
|
||||
<p><%= t('homescreen.blocks.upsale.description') %></p> |
||||
|
||||
<ul class=""> |
||||
<li> |
||||
<%= t('homescreen.blocks.upsale.additional_features') %> |
||||
</li> |
||||
<li> |
||||
<%= t('homescreen.blocks.upsale.professional_support') %> |
||||
</li> |
||||
</ul> |
||||
<p> |
||||
<b><%= t('homescreen.blocks.upsale.become_hero') %></b> <%= t('homescreen.blocks.upsale.you_contribute') %> |
||||
</p> |
||||
<%= link_to( "#{OpenProject::Static::Links.links[:upsale][:href]}?utm_source=ce-enterprise-admin", |
||||
{ class: 'button -alt-highlight', |
||||
aria: {label: t('admin.enterprise.order')}, |
||||
title: t('admin.enterprise.order')}) do %> |
||||
<i class="button--icon icon-add"></i> |
||||
<span class="button--text"><%= t('admin.enterprise.order') %></span> |
||||
<% end %> |
||||
</div> |
||||
</div> |
||||
<% end %> |
||||
|
||||
<%= render partial: "form" %> |
@ -0,0 +1,30 @@ |
||||
<h3 class="widget-box--header"> |
||||
<span class="icon-context icon-cart"></span> |
||||
<span class="widget-box--header-title"><%= t('homescreen.blocks.upsale.title') %></span> |
||||
</h3> |
||||
|
||||
<%= image_tag "enterprise_edition.png", class: "widget-box--teaser-image" %> |
||||
|
||||
<p><%= t('homescreen.blocks.upsale.description') %></p> |
||||
|
||||
<ul class="widget-box--feature-list"> |
||||
<li> |
||||
<%= t('homescreen.blocks.upsale.additional_features') %> |
||||
</li> |
||||
<li> |
||||
<%= t('homescreen.blocks.upsale.professional_support') %> |
||||
</li> |
||||
</ul> |
||||
<p> |
||||
<b><%= t('homescreen.blocks.upsale.become_hero') %></b> <%= t('homescreen.blocks.upsale.you_contribute') %> |
||||
</p> |
||||
|
||||
<div class="widget-box--blocks--buttons"> |
||||
<%= link_to "#{OpenProject::Static::Links.links[:upsale][:href]}?utm_source=ce-homescreen", |
||||
{ class: 'button -alt-highlight', |
||||
aria: {label: t('homescreen.blocks.upsale.more_info')}, |
||||
title: t('homescreen.blocks.upsale.more_info')} do %> |
||||
<i class="button--icon icon-info2"></i> |
||||
<span class="button--text"><%= t('homescreen.blocks.upsale.more_info') %></span> |
||||
<% end %> |
||||
</div> |
@ -0,0 +1,7 @@ |
||||
begin |
||||
data = File.read(Rails.root.join(".openproject-token.pub")) |
||||
key = OpenSSL::PKey::RSA.new(data) |
||||
OpenProject::Token.key = key |
||||
rescue |
||||
warn "WARNING: Missing .openproject-token.pub key" |
||||
end |
@ -0,0 +1,9 @@ |
||||
class CreateEnterpriseToken < ActiveRecord::Migration[5.0] |
||||
def change |
||||
create_table :enterprise_tokens do |t| |
||||
t.text :encoded_token |
||||
|
||||
t.timestamps |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,99 @@ |
||||
// -- copyright
|
||||
// OpenProject is a project management system.
|
||||
// Copyright (C) 2012-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.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 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 doc/COPYRIGHT.rdoc for more details.
|
||||
// ++
|
||||
|
||||
import IAugmentedJQuery = angular.IAugmentedJQuery; |
||||
import { IDialogOpenResult, IDialogService } from 'ng-dialog'; |
||||
import {IDialogScope} from 'ng-dialog'; |
||||
|
||||
export class ConfirmFormSubmitController { |
||||
|
||||
// Allow original form submission after dialog was closed
|
||||
private confirmed = false; |
||||
private dialog: IDialogOpenResult; |
||||
|
||||
constructor(protected $element:IAugmentedJQuery, |
||||
protected $scope:angular.IScope, |
||||
protected $http:angular.IHttpService, |
||||
protected $q:angular.IQService, |
||||
protected ngDialog:IDialogService, |
||||
protected I18n:op.I18n) { |
||||
|
||||
this.$scope['text'] = { |
||||
title: I18n.t('js.modals.form_submit.title'), |
||||
text: I18n.t('js.modals.form_submit.text'), |
||||
button_continue: I18n.t('js.button_continue'), |
||||
button_cancel: I18n.t('js.button_cancel') |
||||
}; |
||||
|
||||
this.$scope['confirmAndClose'] = () => { |
||||
this.confirmed = true; |
||||
this.dialog.close(); |
||||
}; |
||||
|
||||
$element.on('submit', (evt) => { |
||||
if (!this.confirmed) { |
||||
evt.preventDefault(); |
||||
this.openConfirmationDialog(); |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
}); |
||||
} |
||||
|
||||
public openConfirmationDialog() { |
||||
this.dialog = this.ngDialog.open({ |
||||
closeByEscape: true, |
||||
showClose: true, |
||||
closeByDocument: true, |
||||
scope: <IDialogScope> this.$scope, |
||||
template: '/components/modals/confirm-form-submit/confirm-form-submit.modal.html', |
||||
className: 'ngdialog-theme-openproject', |
||||
preCloseCallback: () => { |
||||
if (this.confirmed) { |
||||
this.$element.submit(); |
||||
} |
||||
return true; |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
function confirmFormSubmit() { |
||||
return { |
||||
restrict: 'AC', |
||||
scope: {}, |
||||
bindToController: true, |
||||
controller: ConfirmFormSubmitController, |
||||
controllerAs: '$ctrl', |
||||
}; |
||||
} |
||||
|
||||
angular |
||||
.module('openproject.uiComponents') |
||||
.directive('confirmFormSubmit', confirmFormSubmit); |
@ -0,0 +1,18 @@ |
||||
<div class="confirm-form-submit--modal"> |
||||
<div class="onboarding--start-modal"> |
||||
<div class="onboarding--top-menu"> |
||||
<span class="icon-context icon-attention"></span> |
||||
<h2 ng-bind="::text.title"></h2> |
||||
</div> |
||||
<div class="ngdialog-body onboarding--main"> |
||||
<p ng-bind="::text.text"></p> |
||||
</div> |
||||
<div class="onboarding--footer"> |
||||
<button class="confirm-form-submit--continue button -highlight" |
||||
ng-click="confirmAndClose()" |
||||
ng-bind="::text.button_continue" |
||||
ng-attr-title="::text.button_continue"> |
||||
</button> |
||||
</div> |
||||
</div> |
||||
</div> |
@ -0,0 +1,154 @@ |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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 EnterpriseToken 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 EnterpriseToken |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the EnterpriseToken, 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 EnterpriseToken for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public EnterpriseToken |
||||
# 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.rdoc for more details. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe EnterprisesController, type: :controller do |
||||
let(:a_token) { EnterpriseToken.new } |
||||
let(:token_object) do |
||||
token = OpenProject::Token.new |
||||
token.subscriber = 'Foobar' |
||||
token.mail = 'foo@example.org' |
||||
token.starts_at = Date.today |
||||
token.expires_at = nil |
||||
|
||||
token |
||||
end |
||||
|
||||
before do |
||||
login_as user |
||||
allow(a_token).to receive(:token_object).and_return(token_object) |
||||
end |
||||
|
||||
context 'with admin' do |
||||
let(:user) { FactoryGirl.build(:admin) } |
||||
|
||||
describe '#show' do |
||||
render_views |
||||
|
||||
context 'when token exists' do |
||||
before do |
||||
allow(EnterpriseToken).to receive(:current).and_return(a_token) |
||||
get :show |
||||
end |
||||
|
||||
it 'renders the overview' do |
||||
expect(response).to be_success |
||||
expect(response).to render_template 'show' |
||||
expect(response).to render_template partial: 'enterprises/_current' |
||||
expect(response).to render_template partial: 'enterprises/_form' |
||||
end |
||||
end |
||||
|
||||
context 'when no token exists' do |
||||
before do |
||||
allow(EnterpriseToken).to receive(:current).and_return(nil) |
||||
get :show |
||||
end |
||||
|
||||
it 'still renders #show with form' do |
||||
expect(response).not_to render_template partial: 'enterprises/_current' |
||||
expect(response.body).to have_selector '.upsale-notification' |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe '#create' do |
||||
let(:params) do |
||||
{ |
||||
enterprise_token: { encoded_token: 'foo' } |
||||
} |
||||
end |
||||
|
||||
before do |
||||
allow(EnterpriseToken).to receive(:new).and_return(a_token) |
||||
expect(a_token).to receive(:encoded_token=).with('foo') |
||||
expect(a_token).to receive(:save).and_return(valid) |
||||
|
||||
post :create, params: params |
||||
end |
||||
|
||||
context 'valid token input' do |
||||
let(:valid) { true } |
||||
|
||||
it 'redirects to index' do |
||||
expect(controller).to set_flash[:notice].to I18n.t(:notice_successful_update) |
||||
expect(response).to redirect_to action: :show |
||||
end |
||||
end |
||||
|
||||
context 'invalid token input' do |
||||
let(:valid) { false } |
||||
|
||||
it 'renders with error' do |
||||
expect(response).not_to be_redirect |
||||
expect(response).to render_template 'enterprises/show' |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe '#destroy' do |
||||
context 'when a token exists' do |
||||
before do |
||||
expect(EnterpriseToken).to receive(:current).and_return(a_token) |
||||
expect(a_token).to receive(:destroy) |
||||
|
||||
delete :destroy |
||||
end |
||||
|
||||
it 'redirects to show' do |
||||
expect(controller).to set_flash[:notice].to I18n.t(:notice_successful_delete) |
||||
expect(response).to redirect_to action: :show |
||||
end |
||||
end |
||||
|
||||
context 'when no token exists' do |
||||
before do |
||||
expect(EnterpriseToken).to receive(:current).and_return(nil) |
||||
delete :destroy |
||||
end |
||||
|
||||
it 'renders 404' do |
||||
expect(response.status).to eq(404) |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'regular user' do |
||||
let(:user) { FactoryGirl.build(:user) } |
||||
|
||||
before do |
||||
get :show |
||||
end |
||||
|
||||
it 'is forbidden' do |
||||
expect(response.status).to eq 403 |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,118 @@ |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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 EnterpriseToken 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 EnterpriseToken |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the EnterpriseToken, 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 EnterpriseToken for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public EnterpriseToken |
||||
# 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.rdoc for more details. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe 'Enterprise token', type: :feature do |
||||
include Redmine::I18n |
||||
|
||||
let(:admin) { FactoryGirl.create(:admin) } |
||||
let(:token_object) do |
||||
token = OpenProject::Token.new |
||||
token.subscriber = 'Foobar' |
||||
token.mail = 'foo@example.org' |
||||
token.starts_at = Date.today |
||||
token.expires_at = nil |
||||
|
||||
token |
||||
end |
||||
|
||||
let(:textarea) { find '#enterprise_token_encoded_token' } |
||||
let(:submit_button) { find '#token-submit-button' } |
||||
|
||||
describe 'EnterpriseToken management' do |
||||
before do |
||||
login_as(admin) |
||||
visit enterprise_path |
||||
end |
||||
|
||||
it 'shows a teaser and token form without a token' do |
||||
expect(page).to have_selector('.upsale-notification a', text: 'Order Enterprise Edition') |
||||
expect(textarea.value).to be_empty |
||||
|
||||
textarea.set 'foobar' |
||||
submit_button.click |
||||
|
||||
# Error output |
||||
expect(page).to have_selector('.errorExplanation', |
||||
text: "Enterprise support token can't be read. Are you sure it is a support token?") |
||||
expect(page).to have_selector('span.errorSpan #enterprise_token_encoded_token') |
||||
|
||||
# Keeps value |
||||
expect(textarea.value).to eq('foobar') |
||||
end |
||||
|
||||
context 'assuming valid input' do |
||||
before do |
||||
allow(OpenProject::Token).to receive(:import).and_return(token_object) |
||||
end |
||||
|
||||
it 'allows token import flow', js: true do |
||||
textarea.set 'foobar' |
||||
submit_button.click |
||||
|
||||
expect(page).to have_selector('.flash.notice', text: I18n.t(:notice_successful_update)) |
||||
expect(page).to have_selector('.enterprise--active-token') |
||||
|
||||
expect(page.all('.attributes-key-value--key').map(&:text)) |
||||
.to eq ['Subscriber', 'Email', 'Valid since'] |
||||
expect(page.all('.attributes-key-value--value').map(&:text)) |
||||
.to eq ['Foobar', 'foo@example.org', format_date(Date.today)] |
||||
|
||||
expect(page).to have_selector('.button.icon-delete', text: I18n.t(:button_delete)) |
||||
|
||||
# Expect section to be collapsed |
||||
expect(page).to have_no_selector('#token_encoded_token', visible: true) |
||||
|
||||
expect(EnterpriseToken.current.encoded_token).to eq('foobar') |
||||
|
||||
# Replace token |
||||
find('.collapsible-section--toggle-link').click |
||||
textarea.set 'blabla' |
||||
submit_button.click |
||||
expect(page).to have_selector('.flash.notice', text: I18n.t(:notice_successful_update)) |
||||
|
||||
# Assume next request |
||||
RequestStore.clear! |
||||
expect(EnterpriseToken.current.encoded_token).to eq('blabla') |
||||
|
||||
# Remove token |
||||
find('.button.icon-delete', text: I18n.t(:button_delete)).click |
||||
|
||||
# Expect modal |
||||
find('.confirm-form-submit--continue').click |
||||
expect(textarea.value).to be_empty |
||||
expect(page).to have_selector('.flash.notice', text: I18n.t(:notice_successful_delete)) |
||||
|
||||
# Assume next request |
||||
RequestStore.clear! |
||||
expect(EnterpriseToken.current).to be_nil |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,92 @@ |
||||
Grequire 'spec_helper' |
||||
|
||||
RSpec.describe EnterpriseToken, type: :model do |
||||
let(:object) { OpenProject::Token.new } |
||||
subject { EnterpriseToken.new(encoded_token: 'foo') } |
||||
|
||||
before do |
||||
EnterpriseToken.clear_cache |
||||
end |
||||
|
||||
describe 'existing token' do |
||||
before do |
||||
allow_any_instance_of(EnterpriseToken).to receive(:token_object).and_return(object) |
||||
subject.save!(validate: false) |
||||
end |
||||
|
||||
context 'when inner token is active' do |
||||
it 'has an active token' do |
||||
expect(object).to receive(:expired?).and_return(false) |
||||
expect(EnterpriseToken.count).to eq(1) |
||||
expect(EnterpriseToken.current).to eq(subject) |
||||
expect(EnterpriseToken.current.encoded_token).to eq('foo') |
||||
expect(EnterpriseToken.show_banners).to eq(false) |
||||
|
||||
# Deleting it updates the current token |
||||
EnterpriseToken.current.destroy! |
||||
|
||||
expect(EnterpriseToken.count).to eq(0) |
||||
expect(EnterpriseToken.current).to be_nil |
||||
end |
||||
|
||||
it 'delegates to the token object' do |
||||
allow(object).to receive_messages( |
||||
subscriber: 'foo', |
||||
mail: 'bar', |
||||
starts_at: Date.today, |
||||
issued_at: Date.today, |
||||
expires_at: 'never', |
||||
restrictions: { foo: :bar } |
||||
) |
||||
|
||||
expect(subject.subscriber).to eq('foo') |
||||
expect(subject.mail).to eq('bar') |
||||
expect(subject.starts_at).to eq(Date.today) |
||||
expect(subject.issued_at).to eq(Date.today) |
||||
expect(subject.expires_at).to eq('never') |
||||
expect(subject.restrictions).to eq(foo: :bar) |
||||
end |
||||
|
||||
describe '#allows_to?' do |
||||
let(:service_double) { ::Authorization::EnterpriseService.new(subject) } |
||||
|
||||
before do |
||||
expect(::Authorization::EnterpriseService).to receive(:new).twice.with(subject).and_return(service_double) |
||||
end |
||||
|
||||
it 'forwards to EnterpriseTokenService for checks' do |
||||
expect(service_double).to receive(:call).with(:forbidden_action).and_return double('ServiceResult', result: false) |
||||
expect(service_double).to receive(:call).with(:allowed_action).and_return double('ServiceResult', result: true) |
||||
|
||||
expect(EnterpriseToken.allows_to?(:forbidden_action)).to eq false |
||||
expect(EnterpriseToken.allows_to?(:allowed_action)).to eq true |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'when inner token is expired' do |
||||
before do |
||||
expect(object).to receive(:expired?).and_return(true) |
||||
end |
||||
|
||||
it 'has an expired token' do |
||||
expect(EnterpriseToken.current).to eq(subject) |
||||
expect(EnterpriseToken.show_banners).to eq(true) |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'no token' do |
||||
it do |
||||
expect(EnterpriseToken.current).to be_nil |
||||
expect(EnterpriseToken.show_banners).to eq(true) |
||||
end |
||||
end |
||||
|
||||
describe 'invalid token' do |
||||
it 'appears as if no token is shown' do |
||||
expect(EnterpriseToken.current).to be_nil |
||||
expect(EnterpriseToken.show_banners).to eq(true) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,87 @@ |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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. |
||||
# |
||||
# 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 doc/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe Authorization::EnterpriseService do |
||||
let(:token_object) do |
||||
token = OpenProject::Token.new |
||||
token.subscriber = 'Foobar' |
||||
token.mail = 'foo@example.org' |
||||
token.starts_at = Date.today |
||||
token.expires_at = nil |
||||
|
||||
token |
||||
end |
||||
let(:token) { mock_model(EnterpriseToken, token_object: token_object) } |
||||
let(:instance) { described_class.new(token) } |
||||
let(:result) { instance.call(action) } |
||||
let(:action) { :an_action } |
||||
|
||||
describe '#initialize' do |
||||
it 'has the token' do |
||||
expect(instance.token).to eql token |
||||
end |
||||
end |
||||
|
||||
describe 'expiry' do |
||||
before do |
||||
allow(token).to receive(:expired?).and_return(expired) |
||||
end |
||||
|
||||
context 'when expired' do |
||||
let(:expired) { true } |
||||
|
||||
it 'returns a false result' do |
||||
expect(result).to be_kind_of ServiceResult |
||||
expect(result.result).to be_falsey |
||||
expect(result.success?).to be_falsey |
||||
end |
||||
end |
||||
|
||||
context 'when active' do |
||||
let(:expired) { false } |
||||
|
||||
context 'invalid action' do |
||||
it 'returns false' do |
||||
expect(result.result).to be_falsey |
||||
end |
||||
end |
||||
|
||||
context 'valid action requires active token' do |
||||
let(:action) { :define_custom_style } |
||||
|
||||
it 'returns a true result' do |
||||
expect(result).to be_kind_of ServiceResult |
||||
expect(result.result).to be_truthy |
||||
expect(result.success?).to be_truthy |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue