diff --git a/Gemfile b/Gemfile index c79352f2dd1..25613881246 100644 --- a/Gemfile +++ b/Gemfile @@ -10,6 +10,7 @@ gem 'simple_handlebars_rails', path: 'vendor/gems/simple_handlebars_rails' gem 'activerecord-postgres-hstore' gem 'acts_as_paranoid' +gem 'active_attr' # until we get ActiveModel::Model with Rails 4 gem 'airbrake', '3.1.2' # errbit is broken with 3.1.3 for now gem 'clockwork', require: false gem 'em-redis' diff --git a/Gemfile.lock b/Gemfile.lock index fde60ce7edf..393f96f4c62 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -76,6 +76,9 @@ GEM rack-cache (~> 1.2) rack-test (~> 0.6.1) sprockets (~> 2.2.1) + active_attr (0.7.0) + activemodel (>= 3.0.2, < 4.1) + activesupport (>= 3.0.2, < 4.1) activemodel (3.2.12) activesupport (= 3.2.12) builder (~> 3.0.0) @@ -440,6 +443,7 @@ PLATFORMS ruby DEPENDENCIES + active_attr active_model_serializers! activerecord-postgres-hstore acts_as_paranoid diff --git a/app/assets/javascripts/admin/controllers/admin_dashboard_controller.js.coffee b/app/assets/javascripts/admin/controllers/admin_dashboard_controller.js.coffee new file mode 100644 index 00000000000..63bb1e57c89 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin_dashboard_controller.js.coffee @@ -0,0 +1,26 @@ +window.Discourse.AdminDashboardController = Ember.Controller.extend + loading: true + versionCheck: null + + upToDate: (-> + if @versionCheck + @versionCheck.latest_version == @versionCheck.installed_version + else + true + ).property('versionCheck') + + updateIconClasses: (-> + classes = "icon icon-warning-sign " + if @get('versionCheck.critical_updates') + classes += "critical-updates-available" + else + classes += "updates-available" + classes + ).property('versionCheck') + + priorityClass: (-> + if @get('versionCheck.critical_updates') + 'critical' + else + 'normal' + ).property('versionCheck') \ No newline at end of file diff --git a/app/assets/javascripts/admin/models/version_check.js.coffee b/app/assets/javascripts/admin/models/version_check.js.coffee new file mode 100644 index 00000000000..13cb0af8557 --- /dev/null +++ b/app/assets/javascripts/admin/models/version_check.js.coffee @@ -0,0 +1,9 @@ +window.Discourse.VersionCheck = Discourse.Model.extend({}) + +Discourse.VersionCheck.reopenClass + find: -> + $.ajax + url: '/admin/version_check' + dataType: 'json' + success: (json) => + Discourse.VersionCheck.create(json) diff --git a/app/assets/javascripts/admin/routes/admin_dashboard_route.js.coffee b/app/assets/javascripts/admin/routes/admin_dashboard_route.js.coffee new file mode 100644 index 00000000000..8ed4b30e2ec --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin_dashboard_route.js.coffee @@ -0,0 +1,6 @@ +Discourse.AdminDashboardRoute = Discourse.Route.extend +  setupController: (c) -> +    Discourse.VersionCheck.find().then (vc) -> +      # Loading finished! +      c.set('versionCheck', vc) + c.set('loading', false) diff --git a/app/assets/javascripts/admin/templates/dashboard.js.handlebars b/app/assets/javascripts/admin/templates/dashboard.js.handlebars index fe5b381c738..bd9c6eb5190 100644 --- a/app/assets/javascripts/admin/templates/dashboard.js.handlebars +++ b/app/assets/javascripts/admin/templates/dashboard.js.handlebars @@ -1,4 +1,23 @@

Welcome to the admin section.

-

Not much to see here right now. Why not try the Site Settings?

+

+ {{#if loading }} + Loading... + {{else}} +

+ Discourse version: {{ versionCheck.installed_version }} + + {{#if upToDate }} + You are running the latest version of Discourse. + {{else}} + + A critical update is available. + Updates are available. + Please upgrade! + Latest version: {{ versionCheck.latest_version }} + {{/if}} + +

+ {{/if}} +

\ No newline at end of file diff --git a/app/assets/stylesheets/admin/admin_base.scss b/app/assets/stylesheets/admin/admin_base.scss index d1cc13009f0..018580af80c 100644 --- a/app/assets/stylesheets/admin/admin_base.scss +++ b/app/assets/stylesheets/admin/admin_base.scss @@ -77,6 +77,38 @@ } } +.version-check { + .version-number { + font-size: 18px; + font-weight: bold + } + + .version-notes { + margin-left: 24px; + } + + .critical .version-notes .normal-note { + display: none; + } + .normal .version-notes .critical-note { + display: none; + } + + i.icon { + font-size: 20px; + } + + i.update-to-date { + color: green; + } + i.updates-available { + color: orange; + } + i.critical-updates-available { + color: red; + } +} + .settings { .setting { padding-bottom: 20px; diff --git a/app/controllers/admin/versions_controller.rb b/app/controllers/admin/versions_controller.rb index 0723e9e9665..967c477e29a 100644 --- a/app/controllers/admin/versions_controller.rb +++ b/app/controllers/admin/versions_controller.rb @@ -1,15 +1,7 @@ -require_dependency 'discourse_hub' -require_dependency 'version' +require_dependency 'discourse_updates' class Admin::VersionsController < Admin::AdminController def show - if SiteSetting.discourse_org_access_key.present? - render json: success_json.merge( latest_version: DiscourseHub.current_discourse_version, installed_version: Discourse::VERSION::STRING ) - else - # Don't contact discourse.org - render json: success_json.merge( latest_version: Discourse::VERSION::STRING, installed_version: Discourse::VERSION::STRING ) - end - rescue RestClient::Forbidden - render json: {errors: [I18n.t("discourse_hub.access_token_problem")]} + render json: DiscourseUpdates.check_version end -end +end \ No newline at end of file diff --git a/app/models/discourse_version_check.rb b/app/models/discourse_version_check.rb new file mode 100644 index 00000000000..ac7a04807e6 --- /dev/null +++ b/app/models/discourse_version_check.rb @@ -0,0 +1,14 @@ +class DiscourseVersionCheck + + # include ActiveModel::Model <-- If we were using Rails 4, we could use this instead of active_attr + include ActiveAttr::Attributes + include ActiveAttr::MassAssignment + include ActiveModel::Serialization + + attr_accessor :latest_version, :installed_version, :critical_updates + + def active_model_serializer + DiscourseVersionCheckSerializer + end + +end \ No newline at end of file diff --git a/app/serializers/discourse_version_check_serializer.rb b/app/serializers/discourse_version_check_serializer.rb new file mode 100644 index 00000000000..42469652195 --- /dev/null +++ b/app/serializers/discourse_version_check_serializer.rb @@ -0,0 +1,5 @@ +class DiscourseVersionCheckSerializer < ApplicationSerializer + attributes :latest_version, :installed_version, :critical_updates + + self.root = false +end \ No newline at end of file diff --git a/config/clock.rb b/config/clock.rb index a1e74432b61..0b8ea7ff692 100644 --- a/config/clock.rb +++ b/config/clock.rb @@ -16,4 +16,5 @@ module Clockwork every(10.minutes, 'feature_topics') every(1.minute, 'calculate_score') every(20.minutes, 'calculate_view_counts') + every(1.day, 'version_check') end diff --git a/lib/discourse_hub.rb b/lib/discourse_hub.rb index c8b04002007..30c07a7399a 100644 --- a/lib/discourse_hub.rb +++ b/lib/discourse_hub.rb @@ -1,4 +1,5 @@ require_dependency 'rest_client' +require_dependency 'version' module DiscourseHub @@ -33,8 +34,8 @@ module DiscourseHub end - def self.current_discourse_version - get('/current_version')['version'] + def self.discourse_version_check + get('/version_check', {installed_version: Discourse::VERSION::STRING}) end diff --git a/lib/discourse_updates.rb b/lib/discourse_updates.rb new file mode 100644 index 00000000000..c4f1fe4182e --- /dev/null +++ b/lib/discourse_updates.rb @@ -0,0 +1,41 @@ +module DiscourseUpdates + + class << self + + def check_version + DiscourseVersionCheck.new( + latest_version: latest_version || Discourse::VERSION::STRING, + installed_version: Discourse::VERSION::STRING, + critical_updates: critical_update_available? + # TODO: more info, like links and release messages + ) + end + + def latest_version=(arg) + $redis.set latest_version_key, arg + end + + def latest_version + $redis.get latest_version_key + end + + def critical_update_available=(arg) + $redis.set critical_updates_available_key, arg + end + + def critical_update_available? + ($redis.get(critical_updates_available_key) || false) == 'true' + end + + + private + + def latest_version_key + 'discourse_latest_version' + end + + def critical_updates_available_key + 'critical_updates_available' + end + end +end \ No newline at end of file diff --git a/lib/jobs/version_check.rb b/lib/jobs/version_check.rb new file mode 100644 index 00000000000..7e008c69306 --- /dev/null +++ b/lib/jobs/version_check.rb @@ -0,0 +1,15 @@ +require_dependency 'discourse_hub' +require_dependency 'discourse_updates' + +module Jobs + class VersionCheck < Jobs::Base + + def execute(args) + json = DiscourseHub.discourse_version_check + DiscourseUpdates.latest_version = json['latestVersion'] + DiscourseUpdates.critical_update_available = json['criticalUpdates'] + true + end + + end +end \ No newline at end of file diff --git a/spec/components/discourse_hub_spec.rb b/spec/components/discourse_hub_spec.rb index 9941bf87b04..ae2c4183a25 100644 --- a/spec/components/discourse_hub_spec.rb +++ b/spec/components/discourse_hub_spec.rb @@ -55,10 +55,11 @@ describe DiscourseHub do end end - describe '#current_discourse_version' do - it 'should return the latest version of discourse' do - RestClient.stubs(:get).returns( {success: 'OK', version: 1.0}.to_json ) - DiscourseHub.current_discourse_version().should == 1.0 + describe '#discourse_version_check' do + it 'should return just return the json that the hub returns' do + hub_response = {'success' => 'OK', 'latest_version' => '0.8.1', 'critical_updates' => false} + RestClient.stubs(:get).returns( hub_response.to_json ) + DiscourseHub.discourse_version_check.should == hub_response end end diff --git a/spec/controllers/admin/versions_controller_spec.rb b/spec/controllers/admin/versions_controller_spec.rb index 8bf104cf3e5..cffe321aa84 100644 --- a/spec/controllers/admin/versions_controller_spec.rb +++ b/spec/controllers/admin/versions_controller_spec.rb @@ -4,7 +4,8 @@ require_dependency 'version' describe Admin::VersionsController do before do - RestClient.stubs(:get).returns( {success: 'OK', version: '1.2.33'}.to_json ) + DiscourseUpdates.stubs(:latest_version).returns('1.2.33') + DiscourseUpdates.stubs(:critical_update_available?).returns(false) end it "is a subclass of AdminController" do @@ -17,38 +18,17 @@ describe Admin::VersionsController do end describe 'show' do - context 'when discourse_org_access_key is set' do - before do - SiteSetting.stubs(:discourse_org_access_key).returns('asdf') - end + subject { xhr :get, :show } + it { should be_success } - subject { xhr :get, :show } - it { should be_success } - - it 'should return the currently available version' do - json = JSON.parse(subject.body) - json['latest_version'].should == '1.2.33' - end - - it "should return the installed version" do - json = JSON.parse(subject.body) - json['installed_version'].should == Discourse::VERSION::STRING - end + it 'should return the currently available version' do + json = JSON.parse(subject.body) + json['latest_version'].should == '1.2.33' end - context 'when discourse_org_access_key is blank' do - subject { xhr :get, :show } - it { should be_success } - - it 'should return the installed version as the currently available version' do - json = JSON.parse(subject.body) - json['latest_version'].should == Discourse::VERSION::STRING - end - - it "should return the installed version" do - json = JSON.parse(subject.body) - json['installed_version'].should == Discourse::VERSION::STRING - end + it "should return the installed version" do + json = JSON.parse(subject.body) + json['installed_version'].should == Discourse::VERSION::STRING end end end