[refs #576] Add flash notifies

This commit is contained in:
konstantin.grabar 2012-07-23 18:25:37 +04:00
parent d9755f295c
commit 15a86c2ecd
20 changed files with 528 additions and 0 deletions

View File

@ -0,0 +1,19 @@
function setCookie (name, value, expires, path, domain, secure) {
document.cookie = name + "=" + escape(value) +
((expires) ? "; expires=" + expires : "") +
((path) ? "; path=" + path : "") +
((domain) ? "; domain=" + domain : "") +
((secure) ? "; secure" : "");
}
$(document).ready(function() {
if ($(".alert").size()) {
$(".alert").alert()
}
$('#close-alert').click(function () {
setCookie("flash_notify_id", FLASH_NOTIFY_ID, FLASH_EXPIRES_AT);
setCookie("flash_notify_hash", FLASH_HASH_ID, FLASH_EXPIRES_AT);
});
});

View File

@ -1172,3 +1172,63 @@ table.tablesorter tr td.no_results {
text-align: center;
}
/* end */
/* Flash Notifies */
.flash_notify {
.alert-success {
color: #468847;
background-color: #DFF0D8;
border-color: #D6E9C6;
}
.alert {
padding: 8px 35px 8px 14px;
margin-bottom: 18px;
color: #C09853;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
background-color: #FCF8E3;
border: 1px solid #FBEED5;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.alert-danger, .alert-error {
color: #B94A48;
background-color: #F2DEDE;
border-color: #EED3D7;
}
.alert-info {
color: #3A87AD;
background-color: #D9EDF7;
border-color: #BCE8F1;
}
.alert .close {
position: relative;
top: -2px;
right: -21px;
line-height: 18px;
}
button.close {
padding: 0;
cursor: pointer;
background: transparent;
border: 0;
-webkit-appearance: none;
}
.close {
float: right;
font-size: 20px;
font-weight: bold;
line-height: 18px;
color: black;
text-shadow: 0 1px 0 white;
opacity: 0.2;
filter: alpha(opacity=20);
}
}

View File

@ -0,0 +1,46 @@
class FlashNotifiesController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource
def index
@flash_notifies = FlashNotify.paginate(:page => params[:page], :per_page => 20)
end
def new
@flash_notify = FlashNotify.new(:published => true)
end
def create
@flash_notify = FlashNotify.new(params[:flash_notify])
if @flash_notify.save
flash[:notice] = t("flash.flash_notify.saved")
redirect_to flash_notifies_path
else
flash[:error] = t("flash.flash_notify.save_error")
flash[:warning] = @flash_notify.errors.full_messages.join('. ')
render :new
end
end
def update
if @flash_notify.update_attributes(params[:flash_notify])
flash[:notice] = t("flash.flash_notify.saved")
redirect_to flash_notifies_path
else
flash[:error] = t("flash.flash_notify.save_error")
flash[:warning] = @flash_notify.errors.full_messages.join('. ')
render :edit
end
end
def destroy
if @flash_notify.destroy
flash[:notice] = t("flash.flash_notify.destroyed")
redirect_to flash_notifies_path
else
flash[:error] = t("flash.flash_notify.destroy_error")
redirect_to flash_notifies_path
end
end
end

View File

@ -0,0 +1,24 @@
require 'digest/md5'
class FlashNotify < ActiveRecord::Base
# attr_accessible :title, :body
STATUSES = %w[error success info]
validates :status, :inclusion => {:in => STATUSES}
validates :body_ru, :body_en, :status, :presence => true
scope :published, where(:published => true)
def hash_id
@digest ||= Digest::MD5.hexdigest("#{self.id}-#{self.updated_at}")
end
def body(language)
read_attribute("body_#{language}")
end
def should_show?(cookie_id, cookie_hash_id)
cookie_id.to_i == id && cookie_hash_id == hash_id ? false : true
end
end

View File

@ -0,0 +1,21 @@
.leftlist= f.label :body_ru, t("activerecord.attributes.flash_notify.body_ru"), :class => :label
.rightlist= f.text_area :body_ru, :class => 'text_field'
.both
.leftlist= f.label :body_en, t("activerecord.attributes.flash_notify.body_en"), :class => :label
.rightlist= f.text_area :body_en, :class => 'text_field'
.both
.leftlist= f.label :status, t("activerecord.attributes.flash_notify.status"), :class => :label
.rightlist= f.select :status, FlashNotify::STATUSES
.both
.leftlist= f.label :published, t("activerecord.attributes.flash_notify.published"), :class => :label
.rightlist= f.check_box :published
.both
.button_block
= submit_tag t("layout.save")
%span.text_button_padding= t("layout.or")
= link_to t("layout.cancel"), flash_notifies_path, :class => "button"

View File

@ -0,0 +1,4 @@
%h3= t("layout.flash_notifies.edit_header")
= form_for @flash_notify, :url => flash_notifies_path(@flash_notify), :html => { :class => :form } do |f|
= render "form", :f => f

View File

@ -0,0 +1,21 @@
= link_to t("layout.flash_notifies.new"), new_flash_notify_path, :class => 'button' if can? :create, FlashNotify
%table#myTable.tablesorter.flash_notifys{:cellspacing => "0", :cellpadding => "0"}
%thead
%tr
%th.th1= t("activerecord.attributes.flash_notify.body_en")
%th.th2= t("activerecord.attributes.flash_notify.body_ru")
%th.th3= t("activerecord.attributes.flash_notify.published")
%th.th3= t("layout.flash_notifies.actions")
%tbody
- @flash_notifies.each do |flash_notify|
%tr{:class => cycle("odd", "even")}
%td= flash_notify.body_en.slice(0..15) + "..."
%td= flash_notify.body_ru.slice(0..15) + "..."
%td= flash_notify.published
%td
= link_to t("layout.flash_notifies.edit"), edit_flash_notify_path(flash_notify)
= link_to t("layout.flash_notifies.delete"), flash_notify_path(flash_notify), :method => :delete, :confirm => t("layout.mass_builds.cancel_confirm") if can?(:delete, flash_notify)
= will_paginate @flash_notifies

View File

@ -0,0 +1,4 @@
%h3= t("layout.flash_notifies.new_header")
= form_for @flash_notify, :url => flash_notifies_path, :html => { :class => :form } do |f|
= render "form", :f => f

View File

@ -0,0 +1,12 @@
- if current_user || APP_CONFIG['anonymous_access']
.flash_notify
- flash_notify = FlashNotify.published.first
- if flash_notify && flash_notify.should_show?(cookies[:flash_notify_id], cookies[:flash_notify_hash])
.alert{:class => "alert-#{flash_notify.status}"}
= flash_notify.body current_user.language
%a{:class=>"close", :'data-dismiss'=>"alert", :href=>"#", :id => 'close-alert'} &times;
:javascript
var FLASH_NOTIFY_ID = "#{flash_notify.id}";
var FLASH_HASH_ID = "#{flash_notify.hash_id}";
var FLASH_EXPIRES_AT = "#{Date.today + 1.year}";

View File

@ -47,6 +47,7 @@
= yield :feed_tabs
.both
= render "layouts/flashes"
= render "layouts/notifies"
%article
- if content_for?(:sidebar)
%aside= yield :sidebar

View File

@ -0,0 +1,28 @@
en:
layout:
flash_notifies:
list_header: Notifies
new: New notify
new_header: New notify
actions: Actions
edit: Edit
edit_header: Edit notify
delete: Delete
flash:
flash_notify:
saved: Notify added
save_error: Unable to add notify
destroyed: Notify deleted
activerecord:
models:
flash_notify: Notify
attributes:
flash_notify:
body_ru: Body Ru
body_en: Body En
published: Published
status: Status
created_at: Created
updated_at: Updated

View File

@ -0,0 +1,28 @@
ru:
layout:
flash_notifies:
list_header: Оповещения
new: Новое оповещение
new_header: Новое оповещение
actions: Действия
edit: Редактирование
edit_header: Редактировать оповещение
delete: Удалить
flash:
flash_notify:
saved: Оповещение сохранено
save_error: Не получилось сохранить оповещение
destroyed: Оповещение удалено
activerecord:
models:
flash_notify: Оповещение
attributes:
flash_notify:
body_ru: Текст Ru
body_en: Текст En
published: Опубликовано
status: Статус
created_at: Создано
updated_at: Обновлено

View File

@ -21,6 +21,8 @@ Rosa::Application.routes.draw do
root :to => 'activity_feeds#index'
end
resources :flash_notifies
namespace :admin do
resources :users do
get :list, :on => :collection

View File

@ -0,0 +1,11 @@
class CreateFlashNotifies < ActiveRecord::Migration
def change
create_table :flash_notifies do |t|
t.text :body_ru, :null => false
t.text :body_en, :null => false
t.string :status, :null => false
t.boolean :published, :null => false, :default => true
t.timestamps
end
end
end

View File

@ -159,6 +159,15 @@ ActiveRecord::Schema.define(:version => 20120703101719) do
t.datetime "updated_at", :null => false
end
create_table "flash_notifies", :force => true do |t|
t.text "body_ru", :null => false
t.text "body_en", :null => false
t.string "status", :null => false
t.boolean "published", :default => true, :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "groups", :force => true do |t|
t.integer "owner_id"
t.datetime "created_at", :null => false

View File

@ -0,0 +1,132 @@
require 'spec_helper'
describe FlashNotifiesController do
before(:each) do
stub_symlink_methods
@user = FactoryGirl.create(:user)
@create_params = {
:flash_notify => {
:body_ru => "Hello! I`m ru body",
:body_en => "Hello! I`m en body",
:status => "error",
:published => true
}
}
@flash_notify = FactoryGirl.create(:flash_notify)
@flash_notify2 = FactoryGirl.create(:flash_notify)
@update_params = {
:id => @flash_notify,
:flash_notify => {
:body_ru => "updated!"
}
}
end
context 'for guest' do
[:index, :create, :update, :edit, :new, :destroy].each do |action|
it "should not be able to perform #{ action } action" do
get action, :id => @flash_notify
response.should redirect_to(new_user_session_path)
end
end
it 'should not change objects count on create' do
lambda { post :create, @create_params }.should change{ FlashNotify.count }.by(0)
end
it 'should not change objects count on destroy' do
lambda { delete :destroy, :id => @flash_notify }.should change{ FlashNotify.count }.by(0)
end
it 'should not change flash notify body on update' do
put :update, @update_params
@flash_notify.reload.body_ru.should_not == "updated!"
end
end
context 'for global admin' do
before(:each) do
@admin = FactoryGirl.create(:admin)
@user = FactoryGirl.create(:user)
set_session_for(@admin)
end
it 'should be able to perform index action' do
get :index
response.should render_template(:index)
end
it 'should load 2 flash notifies objects on index' do
get :index
assigns[:flash_notifies].count.should == 2
end
it 'should be able to perform new action' do
get :new
response.should render_template(:new)
end
it 'should be able to perform edit action' do
get :edit
response.should render_template(:edit)
end
it 'should be able to perform create action' do
post :create, @create_params
response.should redirect_to(flash_notifies_path)
end
it 'should change objects count on create' do
lambda { post :create, @create_params }.should change{ FlashNotify.count }.by(1)
end
it 'should be able to perform destroy action' do
delete :destroy, :id => @flash_notify
response.should redirect_to(flash_notifies_path)
end
it 'should change objects count on destroy' do
lambda { delete :destroy, :id => @flash_notify }.should change{ FlashNotify.count }.by(-1)
end
it 'should be able to perform update action' do
put :update, @update_params
response.should redirect_to(flash_notifies_path)
end
it 'should change flash notify body on update' do
put :update, @update_params
@flash_notify.reload.body_ru.should == "updated!"
end
end
context 'for simple user' do
before(:each) do
@user = FactoryGirl.create(:user)
set_session_for(@user)
end
[:index, :create, :update, :edit, :new, :destroy].each do |action|
it "should not be able to perform #{ action } action" do
get action, :id => @flash_notify
response.should redirect_to(forbidden_path)
end
end
it 'should not change objects count on create' do
lambda { post :create, @create_params }.should change{ FlashNotify.count }.by(0)
end
it 'should not change objects count on destroy' do
lambda { delete :destroy, :id => @flash_notify }.should change{ FlashNotify.count }.by(0)
end
it 'should not change flash notify body on update' do
put :update, @update_params
@flash_notify.reload.body_ru.should_not == "updated!"
end
end
end

View File

@ -0,0 +1,10 @@
# -*- encoding : utf-8 -*-
FactoryGirl.define do
factory :flash_notify do
body_ru { FactoryGirl.generate(:string) }
body_en { FactoryGirl.generate(:string) }
status "error"
published true
end
end

View File

@ -0,0 +1,5 @@
require 'spec_helper'
describe FlashNotify do
pending "add some examples to (or delete) #{__FILE__}"
end

View File

@ -0,0 +1,90 @@
/* ==========================================================
* bootstrap-alert.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#alerts
* ==========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* ALERT CLASS DEFINITION
* ====================== */
var dismiss = '[data-dismiss="alert"]'
, Alert = function (el) {
$(el).on('click', dismiss, this.close)
}
Alert.prototype.close = function (e) {
var $this = $(this)
, selector = $this.attr('data-target')
, $parent
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
e && e.preventDefault()
$parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
$parent.trigger(e = $.Event('close'))
if (e.isDefaultPrevented()) return
$parent.removeClass('in')
function removeElement() {
$parent
.trigger('closed')
.remove()
}
$.support.transition && $parent.hasClass('fade') ?
$parent.on($.support.transition.end, removeElement) :
removeElement()
}
/* ALERT PLUGIN DEFINITION
* ======================= */
$.fn.alert = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('alert')
if (!data) $this.data('alert', (data = new Alert(this)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.alert.Constructor = Alert
/* ALERT DATA-API
* ============== */
$(function () {
$('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
})
}(window.jQuery);

View File

@ -12,6 +12,7 @@
//= require bootstrap-dropdown
// require bootstrap-tooltip
// require bootstrap-popover
//= require bootstrap-alert
//= require chosen.jquery
// require html5shiv
// require_tree .