diff --git a/app/assets/images/arrow.png b/app/assets/images/arrow.png new file mode 100644 index 0000000..d5c4235 Binary files /dev/null and b/app/assets/images/arrow.png differ diff --git a/app/assets/images/bgd.jpg b/app/assets/images/bgd.jpg new file mode 100644 index 0000000..c5c446d Binary files /dev/null and b/app/assets/images/bgd.jpg differ diff --git a/app/assets/images/boxbar-background.png b/app/assets/images/boxbar-background.png new file mode 100644 index 0000000..7a9986a Binary files /dev/null and b/app/assets/images/boxbar-background.png differ diff --git a/app/assets/images/button-background-active.png b/app/assets/images/button-background-active.png new file mode 100644 index 0000000..10cda11 Binary files /dev/null and b/app/assets/images/button-background-active.png differ diff --git a/app/assets/images/button-background.png b/app/assets/images/button-background.png new file mode 100644 index 0000000..d19090c Binary files /dev/null and b/app/assets/images/button-background.png differ diff --git a/app/assets/images/messages/error.png b/app/assets/images/messages/error.png new file mode 100644 index 0000000..8f95270 Binary files /dev/null and b/app/assets/images/messages/error.png differ diff --git a/app/assets/images/messages/notice.png b/app/assets/images/messages/notice.png new file mode 100644 index 0000000..d8f1239 Binary files /dev/null and b/app/assets/images/messages/notice.png differ diff --git a/app/assets/images/messages/warning.png b/app/assets/images/messages/warning.png new file mode 100644 index 0000000..81ac5be Binary files /dev/null and b/app/assets/images/messages/warning.png differ diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 6827502..b128dcd 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -4,7 +4,7 @@ * the top of the compiled file, but it's generally better to create a new file per style scope. *= require normalize *= require theme/base -*= require web-app-theme/themes/default/style.css +*= require theme/default *= require marketing *= require active_scaffold *= require overrides diff --git a/app/assets/stylesheets/marketing.css.scss b/app/assets/stylesheets/marketing.css.scss index 40da202..b676ed6 100644 --- a/app/assets/stylesheets/marketing.css.scss +++ b/app/assets/stylesheets/marketing.css.scss @@ -19,6 +19,7 @@ padding: 0 30px 30px; h2 { font-size: 25px; + margin-top: 0; } p { font-size: 18px; diff --git a/app/assets/stylesheets/theme/default.css b/app/assets/stylesheets/theme/default.css new file mode 100644 index 0000000..35a1d06 --- /dev/null +++ b/app/assets/stylesheets/theme/default.css @@ -0,0 +1,438 @@ +body { + color: #111111; + background: #c4c4c4 url("images/bgd.jpg"); + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +h1, h2, h3 { + color: #111111; + font-weight: normal; + font-family: Cambria, Georgia, serif; +} + +a:link, a:visited, a:hover, a:active { + color: #111111; +} + +a { + -moz-outline: none; +} + +hr { + background: #dddddd; + color: #dddddd; +} + +#header { + height: 80px; + background-color: #333333; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); + box-shadow: inset 0 -5px 10px rgba(0, 0, 0, 0.25), 0 1px 1px rgba(255, 255, 255, 0.3); + -moz-box-shadow: inset 0 -5px 10px rgba(0, 0, 0, 0.25), 0 1px 1px rgba(255, 255, 255, 0.3); + -webkit-box-shadow: inset 0 -5px 10px rgba(0, 0, 0, 0.25), 0 1px 1px rgba(255, 255, 255, 0.3); +} +#header h1 { + padding: 15px 5px; + float: left; + font-size: 40px; + font-style: normal; + text-transform: normal; + letter-spacing: -1px; + line-height: 1.2em; + color: white; +} +#header h1 a:link, #header h1 a:active, #header h1 a:hover, #header h1 a:visited { + color: white; +} + +#main { + width: 77%; + float: left; +} +#main .block { + padding-top: 0px; + background-color: white; + text-shadow: 0 1px 0 white; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border-radius: 6px; + box-shadow: 0 0 1px rgba(0, 0, 0, 0.3), 0 1px 1px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 0 1px rgba(0, 0, 0, 0.3), 0 1px 1px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0 0 1px rgba(0, 0, 0, 0.3), 0 1px 1px rgba(0, 0, 0, 0.3); +} +#main .block .content { + padding-top: 1px; +} +#main .block .content .inner { + padding: 0 15px 15px; +} +#main .block .content h2 { + margin-left: 15px; + font-size: 22px; + text-transform: normal; + letter-spacing: -1px; + line-height: 1.2em; +} +#main .block .content p { + font-size: 13px; + font-style: normal; + font-weight: normal; + text-transform: normal; + letter-spacing: normal; + line-height: 1.45em; +} + +#main-navigation { + padding-top: 30px; + width: auto; +} +#main-navigation ul li { + padding-left: 20px; + margin-right: 0; +} +#main-navigation ul li a:link, #main-navigation ul li a:visited, #main-navigation ul li a:hover, #main-navigation ul li a:active { + padding: 8px 0; + text-decoration: none; + color: #eeeeee; +} +#main-navigation ul li.active a:link, #main-navigation ul li.active a:visited, #main-navigation ul li.active a:hover, #main-navigation ul li.active a:active { + color: #a4a4a4; +} + +.secondary-navigation { + background: #e0e0e0 url("images/boxbar-background.png") top; + border-bottom-width: 0px; + -moz-border-radius-topleft: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-topright: 6px; + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; +} +.secondary-navigation li.first a, .secondary-navigation ul li.first { + -moz-border-radius-topleft: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; +} +.secondary-navigation ul li a:link, .secondary-navigation ul li a:visited, .secondary-navigation ul li a:hover, .secondary-navigation ul li a:active { + text-decoration: none; + color: #111111; +} +.secondary-navigation ul li.text { + padding: 10px 15px; + color: #818171; +} +.secondary-navigation ul li.active { + background-color: white; +} +.secondary-navigation ul li.active a:hover { + background-color: white; +} + +#user-navigation { + top: 33px; + right: 10px; + color: #eeeeee; + font-size: 14px; +} + +#user-navigation ul li a:link, #user-navigation ul li a:visited, #user-navigation ul li a:hover, #user-navigation ul li a:active { + text-decoration: none; + color: #eeeeee; +} +#user-navigation ul li a:active img { + margin-top: 1px; +} + +#user-navigation ul li, .secondary-navigation ul li { + float: left; +} + +#footer { + color: #261f1f; + text-shadow: none; +} +#footer .block { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + background: none; +} +#footer p { + margin: 0; + padding: 0; + text-align: center; +} + +#sidebar { + width: 20%; + float: right; +} +#sidebar .block { + padding-top: 2px; + padding-bottom: 2px; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border-radius: 6px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6); +} +#sidebar .block h4 { + font-weight: normal; + font-family: helvetica, arial, sans-serif; +} +#sidebar .notice { + color: white; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); + background-color: rgba(0, 0, 0, 0.3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(rgba(0, 0, 0, 0.15)), to(rgba(0, 0, 0, 0))); + background-image: -moz-linear-gradient(rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0)); + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border-radius: 6px; + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.25), 0 1px 1px rgba(255, 255, 255, 0.3); + -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.25), 0 1px 1px rgba(255, 255, 255, 0.3); + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.25), 0 1px 1px rgba(255, 255, 255, 0.3); +} +#sidebar .warning { + padding-left: 10px; + padding-right: 10px; + color: #222222; + background: #ffef4e; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border-radius: 6px; + box-shadow: 0 0 1px rgba(0, 0, 0, 0.3), 0 1px 1px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 0 1px rgba(0, 0, 0, 0.3), 0 1px 1px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 0 0 1px rgba(0, 0, 0, 0.3), 0 1px 1px rgba(0, 0, 0, 0.1); + border: 1px solid rgba(0, 0, 0, 0.3); +} +#sidebar h3 { + padding-left: 25px; + color: #111111; + border-bottom: 1px solid #261f1f; +} +#sidebar ul li { + background-position: 0 11px; + background-repeat: no-repeat; + background-image: url("images/arrow.png"); + border-bottom: 1px dashed #777777; + list-style-type: none; +} +#sidebar ul li a { + margin-left: 10px; + text-decoration: none; + text-shadow: 0 1px 0 white; +} +#sidebar ul li.active a { + color: #a4a4a4; +} + +.control { + float: right; + margin-right: 9px; + margin-top: 11px; +} + +.pagination a, .pagination span { + border: 1px solid #c3c4ba; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + margin-right: 5px; + padding: 6px; + min-width: 15px; + text-align: center; + background: #dddddd; + background-image: url("images/button-background.png"); + color: #111111; +} +.pagination a:hover { + border: 1px solid #818171; + -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.3); + box-shadow: 0 0 4px rgba(0, 0, 0, 0.3); +} +.pagination span.current { + background: #261f1f; + color: white; + border: 1px solid #261f1f; +} + +.pagination a:active { + background-image: url("images/button-background-active.png"); + outline: none; +} + +.table th { + background: #eaeaea; + color: #222222; + font-weight: normal; +} +.table th.last { + min-width: 90px; +} +.table td { + border-bottom: 1px solid #eaeaea; +} +.table td.last { + padding-top: 0px; + padding-bottom: 0px; +} +.table tr.even { + background: #f8f8f8; +} + +.form label.label { + font-family: helvetica, arial, sans-serif; + font-weight: normal; + color: #666666; +} +.form input.text_field, .form textarea.text_area { + width: 100%; + border: 1px solid #dddddd; + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); +} +.form input.button { + background: #dddddd; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + border: 1px solid #c1c1c1; + padding: 2px 5px; + cursor: pointer; + color: #111111; + font-weight: bold; + font-size: 11px; +} +.form input.button:hover { + border: 1px solid #666666; +} +.form .description { + font-style: italic; + color: #8c8c8c; + font-size: 0.9em; +} +.form .navform a { + color: #cc0000; +} + +.flash .message { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + text-align: center; + margin: 0 auto 15px; + color: white; + text-shadow: 0 1px 0 rgba(0, 0, 0, 0.3); +} +.flash .message p { + margin: 8px; +} +.flash .error, .flash .error-list { + border: 1px solid #993624; + background: #cc4831 url("images/messages/error.png") no-repeat 10px center; +} +.flash .warning { + border: 1px solid #bb9004; + background: #f9c006 url("images/messages/warning.png") no-repeat 10px center; +} +.flash .notice { + color: #28485e; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); + border: 1px solid #8a9daa; + background: #b8d1e2 url("images/messages/notice.png") no-repeat 10px center; +} +.flash .error-list { + text-align: left; +} +.flash .error-list h2 { + font-size: 16px; + text-align: center; +} +.flash .error-list ul { + padding-left: 22px; + line-height: 18px; + list-style-type: square; + margin-bottom: 15px; +} + +ul.list li { + border-bottom-color: #dddddd; + border-bottom-width: 1px; + border-bottom-style: solid; +} +ul.list li .item .avatar { + border-color: #dddddd; + border-width: 1px; + border-style: solid; + padding: 2px; +} + +#box { + width: 480px; +} +#box .block { + background: white; + text-shadow: 0 1px 0 white; + box-shadow: 0 0 1px rgba(0, 0, 0, 0.3), 0 1px 1px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 0 1px rgba(0, 0, 0, 0.3), 0 1px 1px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0 0 1px rgba(0, 0, 0, 0.3), 0 1px 1px rgba(0, 0, 0, 0.3); + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border-radius: 6px; +} +#box .block h2 { + background: #261f1f; + color: white; + text-shadow: none; + -moz-border-radius-topleft: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-topright: 6px; + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; +} + +a.button, button.button { + background: #edeeed url("images/button-background.png") top; + border: 1px solid #c3c4ba; + font-family: helvetica, arial, sans-serif; + font-weight: normal; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} +a.button:link, a.button:visited, a.button:hover, a.button:active, button.button:link, button.button:visited, button.button:hover, button.button:active { + font-weight: normal; + background: #edeeed url("images/button-background.png") top; +} + +a.button:active, button.button:active { + background: #eaeaea url("images/button-background-active.png") top; + outline: none; + margin-top: 1px; + margin-bottom: -1px; +} + +.form div.left { + width: 10%; + float: left; +} +.form div.right { + width: 85%; + float: right; +} + +.login div.left, .signup div div.left { + width: 21%; + float: left; +} +.login div.right, .signup div div.right { + width: 74%; + float: right; +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6cd30c3..a47ff68 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -13,4 +13,9 @@ class ApplicationController < ActionController::Base render :template => 'errors/access_denied', :layout => 'errors' end + protected + + def ensure_nested_under_domain + raise CanCan::AccessDenied, "not found" unless nested? and nested_parent_record.is_a?(Domain) + end end diff --git a/app/controllers/domains_controller.rb b/app/controllers/domains_controller.rb index 262debc..2ec5238 100644 --- a/app/controllers/domains_controller.rb +++ b/app/controllers/domains_controller.rb @@ -5,8 +5,11 @@ class DomainsController < ApplicationController conf.list.columns = [:name, :soa_record, :ns_records, :records] conf.create.columns = [:name, :soa_record, :ns_records] conf.update.columns = [:name, :soa_record, :ns_records] + conf.columns[:name].description = 'yourdomain.com' + conf.columns[:ns_records].show_blank_record = false conf.actions.exclude :show conf.list.sorting = { :name => :asc } + conf.create.link.label = "Add Domain" conf.columns[:records].label = 'All Records' end @@ -25,6 +28,7 @@ class DomainsController < ApplicationController def after_create_save(record) session[:sample_ns] = nil + @record.reload end def sample_ns diff --git a/app/controllers/mxes_controller.rb b/app/controllers/mxes_controller.rb new file mode 100644 index 0000000..73e2d7a --- /dev/null +++ b/app/controllers/mxes_controller.rb @@ -0,0 +1,43 @@ +class MxesController < ApplicationController + active_scaffold :mx do |conf| + conf.list.columns = [:name, :type, :content, :ttl, :prio, :change_date] + conf.create.columns = [:name, :content, :ttl, :prio] + conf.update.columns = [:name, :content, :ttl, :prio] + conf.columns[:content].label = 'MX' + conf.columns[:name].description = 'Ex: mail or mx etc' + conf.columns[:change_date].list_ui = :timestamp + conf.actions.exclude :show + end + before_filter :ensure_nested_under_domain + + protected + + def do_new + super + @record.prio ||= begin + maximum = nested_parent_record.mx_records.maximum(:prio) + maximum.nil? ? Settings.default_prio : maximum + 10 + end + end + + # override to use :mx_records instead of :records assoc + def beginning_of_chain + if nested? && nested.association && nested.association.collection? && nested.association.name == :records + nested.parent_scope.mx_records + else + super + end + end + + # override, we make our own sti logic + def new_model + model = beginning_of_chain + model.new + end + + # override to close create form after success + def render_parent? + nested_singular_association? # || params[:parent_sti] + end + +end diff --git a/app/controllers/ns_controller.rb b/app/controllers/ns_controller.rb index 2ee841c..57e9911 100644 --- a/app/controllers/ns_controller.rb +++ b/app/controllers/ns_controller.rb @@ -1,15 +1,11 @@ class NsController < ApplicationController active_scaffold :ns do |conf| - conf.columns = [:name, :content, :ttl] + conf.list.columns = [:name, :type, :content, :ttl, :prio, :change_date] conf.create.columns = [:content, :ttl] conf.update.columns = [:content, :ttl] conf.columns[:content].label = 'NS' + conf.columns[:change_date].list_ui = :timestamp conf.actions.exclude :show end - - protected - - def beginning_of_chain - super.readonly(false) - end + before_filter :ensure_nested_under_domain end diff --git a/app/controllers/records_controller.rb b/app/controllers/records_controller.rb index 3ffd764..6347416 100644 --- a/app/controllers/records_controller.rb +++ b/app/controllers/records_controller.rb @@ -1,18 +1,25 @@ class RecordsController < ApplicationController + # override so SOA's cannot be created by themselves + def self._add_sti_create_links + new_action_link = active_scaffold_config.action_links.collection['new'] + unless new_action_link.nil? || active_scaffold_config.sti_children.empty? + active_scaffold_config.action_links.collection.delete('new') + sti_children = active_scaffold_config.sti_children - [:SOA] + sti_children.each do |child| + new_sti_link = Marshal.load(Marshal.dump(new_action_link)) # deep clone + new_sti_link.label = child.to_s.camelize.constantize.model_name.human + new_sti_link.parameters = {:parent_sti => controller_path} + new_sti_link.controller = Proc.new { active_scaffold_controller_for(child.to_s.camelize.constantize).controller_path } + active_scaffold_config.action_links.collection.create.add(new_sti_link) + end + end + end + active_scaffold :record do |conf| - conf.sti_children = [:SOA, :NS] + conf.sti_children = [:SOA, :NS, :MX] conf.columns = [:name, :type, :content, :ttl, :prio, :change_date] + conf.columns[:change_date].list_ui = :timestamp conf.actions.exclude :show end before_filter :ensure_nested_under_domain - - protected - - def beginning_of_chain - super.readonly(false) - end - - def ensure_nested_under_domain - raise CanCan::AccessDenied, "not found" unless nested? and nested_parent_record.is_a?(Domain) - end end diff --git a/app/controllers/soas_controller.rb b/app/controllers/soas_controller.rb index 36e3c47..d0c2a47 100644 --- a/app/controllers/soas_controller.rb +++ b/app/controllers/soas_controller.rb @@ -1,14 +1,9 @@ class SoasController < ApplicationController active_scaffold :soa do |conf| - conf.columns = [:name, :primary_ns, :contact, :ttl] + conf.list.columns = [:name, :type, :content, :ttl, :prio, :change_date] conf.create.columns = [:contact, :ttl] conf.update.columns = [:contact, :ttl] + conf.columns[:change_date].list_ui = :timestamp conf.actions.exclude :delete, :show end - - protected - - def beginning_of_chain - super.readonly(false) - end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index bd17d72..eb2f86a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -14,4 +14,9 @@ module ApplicationHelper end end + def active_scaffold_column_timestamp(column, record) + value = record.send(column.name) + value.nil? ? nil : Time.at(value) + end + end diff --git a/app/helpers/mxes_helper.rb b/app/helpers/mxes_helper.rb new file mode 100644 index 0000000..daefa67 --- /dev/null +++ b/app/helpers/mxes_helper.rb @@ -0,0 +1,2 @@ +module MxesHelper +end \ No newline at end of file diff --git a/app/models/domain.rb b/app/models/domain.rb index 24f4c4d..c3b7abf 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -30,19 +30,13 @@ class Domain < ActiveRecord::Base def setup(email, sample_ns) build_soa_record + soa = soa_record + soa.contact ||= email + ns_records.build - ns_records.build - + ns_records.build ns1, ns2 = ns_records - soa = soa_record - ns1.content = sample_ns.first - ns1.ttl ||= Settings.default_ttl - ns2.content = sample_ns.second - ns2.ttl ||= Settings.default_ttl - - soa.contact ||= email - soa.ttl ||= Settings.default_ttl end end diff --git a/app/models/mx.rb b/app/models/mx.rb index 611f562..7606bf4 100644 --- a/app/models/mx.rb +++ b/app/models/mx.rb @@ -7,9 +7,14 @@ # Obtained from http://www.zytrax.com/books/dns/ch8/mx.html # class MX < Record - validates :prio, - :numericality => {:greater_than_or_equal_to => 0, :less_than_or_equal_to => 65535, :only_integer => true} + validates :prio, :numericality => { + :greater_than_or_equal_to => 0, + :less_than_or_equal_to => 65535, + :only_integer => true + } validates :content, :presence => true, :hostname => true - - def supports_prio?; true end + + def supports_priority?; true end end + +Mx = MX \ No newline at end of file diff --git a/app/models/ns.rb b/app/models/ns.rb index 6cc684c..8b1f83f 100644 --- a/app/models/ns.rb +++ b/app/models/ns.rb @@ -22,6 +22,8 @@ class NS < Record validates :content, :presence => true, :hostname => true + def to_label; "#{content}" end + end Ns = NS diff --git a/app/models/record.rb b/app/models/record.rb index 5c80290..bea2c6a 100644 --- a/app/models/record.rb +++ b/app/models/record.rb @@ -16,6 +16,9 @@ class Record < ActiveRecord::Base before_validation :prepare_name! before_save :update_change_date after_save :update_soa_serial + after_initialize do + self.ttl ||= Settings.default_ttl + end # By default records don't support priorities. # Those who do can overwrite this in their own classes. diff --git a/app/models/soa.rb b/app/models/soa.rb index 50b55d5..06155cb 100644 --- a/app/models/soa.rb +++ b/app/models/soa.rb @@ -58,7 +58,7 @@ class SOA < Record save end - def to_label; "#{type} #{primary_ns} #{contact}" end + def to_label; "#{primary_ns} #{contact}" end private diff --git a/app/views/fragments/_top.html.erb b/app/views/fragments/_top.html.erb index 909bec8..308ad11 100644 --- a/app/views/fragments/_top.html.erb +++ b/app/views/fragments/_top.html.erb @@ -1,6 +1,6 @@