Project

General

Profile

« Previous | Next » 

Revision 0df6346a

Added by Marc Dequènes almost 15 years ago

  • ID 0df6346aea93b5cf58ff4bc82053d57e6cafc608

[evol] improve config #8 (shadow selection support)

View differences:

.gitignore
lib/ldap_shadows/config.rb
data/locale
#
conf/ldap_shadows/shadow_private.conf
conf/ldap_shadows/shadows/MilkyPond/shadow_private.conf
var
bin/shadowwalker
opt.on("-d", "--debug", "Output debug info without being formated") {|t| $program_options[:debug] = true }
opt.on("-e", "--expert", "Output extra info for expert users") {|t| $program_options[:expert] = true }
opt.on("-H", "--handles", "Output with handles (objects/field/... keys used for manipulations)") {|t| $program_options[:handles] = true }
opt.on("-s SHADOW", "--shadow SHADOW", "Choose shadow to travel through") {|t| $program_options[:shadow_name] = t }
end
cmdparser.add_command(CmdParse::HelpCommand.new)
cmdparser.add_command(CmdParse::VersionCommand.new)
$ldapctl = Controller.new
begin
$ldapctl.load_shadow()
rescue ActiveLdap::Error => e
STDERR.puts _("LDAP connection error: %s") % e.to_s
exit 2
rescue
STDERR.puts _("Cannot load shadow configuration")
exit 2
end
class Command < CmdParse::Command
attr_accessor :usages_params
......
end
end
def execute(args)
$ldapctl = Controller.new
begin
$ldapctl.load_shadow($program_options[:shadow_name])
rescue ActiveLdap::Error => e
STDERR.puts _("LDAP connection error: %s") % e.to_s
exit 2
rescue
STDERR.puts _("Shadow initialization failed: %s") % $!
exit 2
end
end
protected
def params_shift_location(args)
......
]
end
def execute (args)
def execute(args)
super
raise SyntaxError, _("no object name given") if args.empty?
obj_hdl = args.shift.downcase.singularize
......
end
end
def execute (args)
def execute(args)
super
obj_klass = params_shift_object(args)
item = params_shift_item(obj_klass, args)
......
end
end
def execute (args)
def execute(args)
super
puts "Tree:"
search_base = nil
......
end
else
dn_list = []
gconfig = $ldapctl.get_global_config()
sconfig = $ldapctl.get_shadow_config()
(gconfig[:tree_objects] || []).each do |obj_hdl|
(sconfig[:tree_objects] || []).each do |obj_hdl|
obj_klass = $ldapctl.find_klass(obj_hdl.downcase)
unless obj_klass
raise PreProcessingError, _("Location object '%s' not defined") % obj_hdl
......
]
end
def execute (args)
def execute(args)
super
obj_klass = params_shift_object(args)
item = params_shift_item(obj_klass, args)
......
end
end
def execute (args)
def execute(args)
super
obj_klass = params_shift_object(args)
item = params_shift_item(obj_klass, args)
......
]
end
def execute (args)
def execute(args)
super
obj_klass = params_shift_object(args)
raise SyntaxError, _("no item name given") if args.empty?
......
]
end
def execute (args)
def execute(args)
super
raise SyntaxError, _("no search arguments") if args.empty?
res = LdapObject.items_find_from_strings(args)
conf/ldap_shadows/aspects/fs.conf
mapping:
classes: ['fsUser']
presentation:
associated_relations: ['primaryGroup', 'secondaryGroups']
relations:
primaryGroup:
type: :belongs_to
object: group
foreign_key: gidNumber
primary_key: gidNumber
secondaryGroups:
type: :belongs_to
object: group
many: uniqueMember
foreign_key: dn
conf/ldap_shadows/aspects/ftp.conf
mapping:
classes: ['ftpUser']
depend_aspects: ['primary', 'fs']
presentation:
relations:
conf/ldap_shadows/aspects/jabber.conf
mapping:
classes: ['jabberUser']
presentation:
relations:
conf/ldap_shadows/aspects/mail.conf
mapping:
classes: ['emailUser']
presentation:
relations:
conf/ldap_shadows/aspects/primary.conf
mapping:
classes: ['primaryAccount']
presentation:
associated_attributes: ['uid']
relations:
conf/ldap_shadows/aspects/shell.conf
mapping:
classes: ['shellUser']
depend_aspects: ['primary', 'fs']
presentation:
associated_attributes: ['loginShell']
relations:
conf/ldap_shadows/aspects/web.conf
mapping:
classes: ['webUser']
depend_aspects: ['primary']
presentation:
relations:
conf/ldap_shadows/global.conf
---
default_shadow: MilkyPond
conf/ldap_shadows/hooks/aspects/fs.rb
# should be in the configuration file
MIN_UID = 10000
MAX_UID = 65535
STEP_UID = 100
DEFAULT_GROUP = 'dc-users'
def self.hook_mod(mapper, item)
unless item.attribute_present?('gidNumber')
item.primaryGroup = mapper.find_klass(:group).find(:first, DEFAULT_GROUP)
end
unless item.attribute_present?('uidNumber')
groups = ActiveLdap::Base.search(:scope => :sub, :filter => "(uidNumber=*)", :attributes => ['uidNumber'])
uidnumbers = groups.collect {|group| group[1]['uidNumber'].first.to_i }
avail_uidnumber = nil
min_uidn = MIN_UID
max_uidn = [min_uidn + STEP_UID, MAX_UID].min
while avail_uidnumber.nil?
avail = (min_uidn..max_uidn).to_a - uidnumbers
unless avail.empty?
avail_uidnumber = avail.first
break
end
min_uidn = max_uidn
max_uidn = [min_uidn + STEP_UID, MAX_UID].min
end
if avail_uidnumber.nil?
STDERR.puts "Available UID range is depleted."
exit 4
else
item.uidNumber = avail_uidnumber
end
end
unless item.attribute_present?('homeDirectory')
item.homeDirectory = "/home/" + item.uid
end
end
conf/ldap_shadows/objects/bot.conf
mapping:
dn_attribute: uid
prefix: ''
classes: ['bot']
sort_by: uid
presentation:
allowed_aspects: ['primary', 'mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
hidden_attributes: ['uid']
expert_attributes: ['uidNumber', 'gidNumber', 'gecos']
conf/ldap_shadows/objects/domain.conf
mapping:
dn_attribute: cn
prefix: ''
classes: ['genericDomain']
sort_by: cn
presentation:
allowed_aspects: []
conf/ldap_shadows/objects/entity.conf
mapping:
dn_attribute: o
prefix: ''
classes: ['entity']
excluded_classes: ['dcObject']
sort_by: o
presentation:
name_attribute: o
hidden_attributes: ['founder']
associated_relations: ['foundersIndividuals', 'foundersEntities']
relations:
foundersIndividuals:
type: :has_many
object: individual
foreign_key: founder
primary_key: dn
foundersEntities:
type: :has_many
object: entity
foreign_key: founder
primary_key: dn
conf/ldap_shadows/objects/group.conf
mapping:
dn_attribute: cn
prefix: ''
classes: ['posixGroup', 'groupOfMembers']
sort_by: cn
presentation:
allowed_aspects: []
hidden_attributes: ['uniqueMember']
expert_attributes: ['gidNumber']
associated_relations: ['individualsPrimaryMembers', 'botsPrimaryMembers', 'individualsSecondaryMembers', 'botsSecondaryMembers']
relations:
individualsPrimaryMembers:
type: :belongs_to
object: individual
many: gidNumber
foreign_key: gidNumber
botsPrimaryMembers:
type: :belongs_to
object: bot
many: gidNumber
foreign_key: gidNumber
individualsSecondaryMembers:
type: :has_many
object: individual
foreign_key: uniqueMember
primary_key: dn
botsSecondaryMembers:
type: :has_many
object: bot
foreign_key: uniqueMember
primary_key: dn
conf/ldap_shadows/objects/individual.conf
mapping:
dn_attribute: uid
prefix: ''
classes: ['individual']
sort_by: uid
presentation:
allowed_aspects: ['primary', 'mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
hidden_attributes: ['uid']
expert_attributes: ['uidNumber', 'gidNumber', 'gecos']
conf/ldap_shadows/objects/secondary_account.conf
mapping:
dn_attribute: uid
prefix: ''
classes: ['secondaryAccount']
sort_by: uid
presentation:
allowed_aspects: ['mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
conf/ldap_shadows/objects/system_account.conf
mapping:
dn_attribute: uid
prefix: ''
classes: ['systemAccount']
sort_by: uid
presentation:
allowed_aspects: ['mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
conf/ldap_shadows/objects/unit.conf
mapping:
dn_attribute: ou
prefix: ''
classes: ['organizationalUnit']
# no sort_by means sort by DN
presentation:
name_attribute: dn
conf/ldap_shadows/shadow.conf
---
presentation:
hidden_attributes: ['objectClass', 'userPassword', 'hasSubordinates', 'entryUUID', 'entryDN', 'structuralObjectClass', 'subschemaSubentry']
tree_objects: ['unit', 'entity']
conf/ldap_shadows/shadows/MilkyPond/aspects/fs.conf
mapping:
classes: ['fsUser']
presentation:
associated_relations: ['primaryGroup', 'secondaryGroups']
relations:
primaryGroup:
type: :belongs_to
object: group
foreign_key: gidNumber
primary_key: gidNumber
secondaryGroups:
type: :belongs_to
object: group
many: uniqueMember
foreign_key: dn
conf/ldap_shadows/shadows/MilkyPond/aspects/ftp.conf
mapping:
classes: ['ftpUser']
depend_aspects: ['primary', 'fs']
presentation:
relations:
conf/ldap_shadows/shadows/MilkyPond/aspects/jabber.conf
mapping:
classes: ['jabberUser']
presentation:
relations:
conf/ldap_shadows/shadows/MilkyPond/aspects/mail.conf
mapping:
classes: ['emailUser']
presentation:
relations:
conf/ldap_shadows/shadows/MilkyPond/aspects/primary.conf
mapping:
classes: ['primaryAccount']
presentation:
associated_attributes: ['uid']
relations:
conf/ldap_shadows/shadows/MilkyPond/aspects/shell.conf
mapping:
classes: ['shellUser']
depend_aspects: ['primary', 'fs']
presentation:
associated_attributes: ['loginShell']
relations:
conf/ldap_shadows/shadows/MilkyPond/aspects/web.conf
mapping:
classes: ['webUser']
depend_aspects: ['primary']
presentation:
relations:
conf/ldap_shadows/shadows/MilkyPond/hooks/aspects/fs.rb
# should be in the configuration file
MIN_UID = 10000
MAX_UID = 65535
STEP_UID = 100
DEFAULT_GROUP = 'dc-users'
def self.hook_mod(mapper, item)
unless item.attribute_present?('gidNumber')
item.primaryGroup = mapper.find_klass(:group).find(:first, DEFAULT_GROUP)
end
unless item.attribute_present?('uidNumber')
groups = ActiveLdap::Base.search(:scope => :sub, :filter => "(uidNumber=*)", :attributes => ['uidNumber'])
uidnumbers = groups.collect {|group| group[1]['uidNumber'].first.to_i }
avail_uidnumber = nil
min_uidn = MIN_UID
max_uidn = [min_uidn + STEP_UID, MAX_UID].min
while avail_uidnumber.nil?
avail = (min_uidn..max_uidn).to_a - uidnumbers
unless avail.empty?
avail_uidnumber = avail.first
break
end
min_uidn = max_uidn
max_uidn = [min_uidn + STEP_UID, MAX_UID].min
end
if avail_uidnumber.nil?
STDERR.puts "Available UID range is depleted."
exit 4
else
item.uidNumber = avail_uidnumber
end
end
unless item.attribute_present?('homeDirectory')
item.homeDirectory = "/home/" + item.uid
end
end
conf/ldap_shadows/shadows/MilkyPond/objects/bot.conf
mapping:
dn_attribute: uid
prefix: ''
classes: ['bot']
sort_by: uid
presentation:
allowed_aspects: ['primary', 'mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
hidden_attributes: ['uid']
expert_attributes: ['uidNumber', 'gidNumber', 'gecos']
conf/ldap_shadows/shadows/MilkyPond/objects/domain.conf
mapping:
dn_attribute: cn
prefix: ''
classes: ['genericDomain']
sort_by: cn
presentation:
allowed_aspects: []
conf/ldap_shadows/shadows/MilkyPond/objects/entity.conf
mapping:
dn_attribute: o
prefix: ''
classes: ['entity']
excluded_classes: ['dcObject']
sort_by: o
presentation:
name_attribute: o
hidden_attributes: ['founder']
associated_relations: ['foundersIndividuals', 'foundersEntities']
relations:
foundersIndividuals:
type: :has_many
object: individual
foreign_key: founder
primary_key: dn
foundersEntities:
type: :has_many
object: entity
foreign_key: founder
primary_key: dn
conf/ldap_shadows/shadows/MilkyPond/objects/group.conf
mapping:
dn_attribute: cn
prefix: ''
classes: ['posixGroup', 'groupOfMembers']
sort_by: cn
presentation:
allowed_aspects: []
hidden_attributes: ['uniqueMember']
expert_attributes: ['gidNumber']
associated_relations: ['individualsPrimaryMembers', 'botsPrimaryMembers', 'individualsSecondaryMembers', 'botsSecondaryMembers']
relations:
individualsPrimaryMembers:
type: :belongs_to
object: individual
many: gidNumber
foreign_key: gidNumber
botsPrimaryMembers:
type: :belongs_to
object: bot
many: gidNumber
foreign_key: gidNumber
individualsSecondaryMembers:
type: :has_many
object: individual
foreign_key: uniqueMember
primary_key: dn
botsSecondaryMembers:
type: :has_many
object: bot
foreign_key: uniqueMember
primary_key: dn
conf/ldap_shadows/shadows/MilkyPond/objects/individual.conf
mapping:
dn_attribute: uid
prefix: ''
classes: ['individual']
sort_by: uid
presentation:
allowed_aspects: ['primary', 'mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
hidden_attributes: ['uid']
expert_attributes: ['uidNumber', 'gidNumber', 'gecos']
conf/ldap_shadows/shadows/MilkyPond/objects/secondary_account.conf
mapping:
dn_attribute: uid
prefix: ''
classes: ['secondaryAccount']
sort_by: uid
presentation:
allowed_aspects: ['mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
conf/ldap_shadows/shadows/MilkyPond/objects/system_account.conf
mapping:
dn_attribute: uid
prefix: ''
classes: ['systemAccount']
sort_by: uid
presentation:
allowed_aspects: ['mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
conf/ldap_shadows/shadows/MilkyPond/objects/unit.conf
mapping:
dn_attribute: ou
prefix: ''
classes: ['organizationalUnit']
# no sort_by means sort by DN
presentation:
name_attribute: dn
conf/ldap_shadows/shadows/MilkyPond/shadow.conf
---
presentation:
hidden_attributes: ['objectClass', 'userPassword', 'hasSubordinates', 'entryUUID', 'entryDN', 'structuralObjectClass', 'subschemaSubentry']
tree_objects: ['unit', 'entity']
conf/ldap_shadows/shadows/MilkyPond/translations/en.yml
---
en:
objects:
bot: "Bot"
domain: "Domain"
entity: "Entity"
group: "Group"
individual: "Individual"
secondary_account: "Secondary Account"
systemAccount: "System Account"
unit: "Unit"
attribute_types:
allowGlobalDirectory: "Disclose Own Contact Information in GLobal Directory"
cn: "Full Name"
createTimestamp: "Item creation time"
creatorsName: "Item Creator"
birthday: "Birthday"
birthlocation: "Birth Location"
description: "Description"
entryUUID: "Item UUID"
foundingDate: "Founding Date"
gecos: "GECOS"
gidNumber: "Primary Group (numeric)"
givenName: "FirstName"
homeDirectory: "Home Directory"
homePostalAddress: "Home Postal Address"
host: "Shell Allowed Hosts"
jid: "Jabber ID"
jpegPhoto: "Photo"
keyFingerPrint: "GPG/PGP Key Fingerprint"
labeledURI: "Web Site"
loginShell: "Shell Interpreter"
mail: "eMail address(es)"
mailForward: "eMail Forward Adress(es)"
mailQuota: "Maximum Mailbox Size"
manager: "Manager(s)"
mobile: "Mobile Phone"
modifiersName: "Item Last Modifier"
modifyTimestamp: "Item Last Modification Time"
occupation: "Job / Studies"
o: "Organization(s)"
structuralObjectClass: "Item Structural Classes"
owner: "Owner(s)"
preferredLanguage: "Language Preference"
sn: "Surname"
sshAuthKey: "SSH Public Key(s)"
uid: "Identifier (Login)"
uidNumber: "Identifier (numeric)"
uniqueAbbreviation: "Unique Abbreviation"
uniqueMember: "Group member"
userPassword: "Password"
webVirtualHost: "Hosted Web Sites"
relations:
primaryGroup: "Primary Group"
secondaryGroups: "Secondary Groups"
individualsPrimaryMembers: "Primary Member(s) Individual(s)"
botsPrimaryMembers: "Primary Member(s) Bot(s)"
individualsSecondaryMembers: "Secondary Member(s) Individual(s)"
botsSecondaryMembers: "Secondary Member(s) Bot(s)"
foundersIndividuals: "Founder(s) Individual(s)"
foundersEntities: "Founder(s) Entity(ies)"
aspects:
fs: "FileSystem Account"
ftp: "FTP Account"
jabber: "Jabber Account"
mail: "eMail Account"
primary: "Primary Account"
shell: "Shell Account"
web: "Web Account"
conf/ldap_shadows/translations/en.yml
---
en:
objects:
bot: "Bot"
domain: "Domain"
entity: "Entity"
group: "Group"
individual: "Individual"
secondary_account: "Secondary Account"
systemAccount: "System Account"
unit: "Unit"
attribute_types:
allowGlobalDirectory: "Disclose Own Contact Information in GLobal Directory"
cn: "Full Name"
createTimestamp: "Item creation time"
creatorsName: "Item Creator"
birthday: "Birthday"
birthlocation: "Birth Location"
description: "Description"
entryUUID: "Item UUID"
foundingDate: "Founding Date"
gecos: "GECOS"
gidNumber: "Primary Group (numeric)"
givenName: "FirstName"
homeDirectory: "Home Directory"
homePostalAddress: "Home Postal Address"
host: "Shell Allowed Hosts"
jid: "Jabber ID"
jpegPhoto: "Photo"
keyFingerPrint: "GPG/PGP Key Fingerprint"
labeledURI: "Web Site"
loginShell: "Shell Interpreter"
mail: "eMail address(es)"
mailForward: "eMail Forward Adress(es)"
mailQuota: "Maximum Mailbox Size"
manager: "Manager(s)"
mobile: "Mobile Phone"
modifiersName: "Item Last Modifier"
modifyTimestamp: "Item Last Modification Time"
occupation: "Job / Studies"
o: "Organization(s)"
structuralObjectClass: "Item Structural Classes"
owner: "Owner(s)"
preferredLanguage: "Language Preference"
sn: "Surname"
sshAuthKey: "SSH Public Key(s)"
uid: "Identifier (Login)"
uidNumber: "Identifier (numeric)"
uniqueAbbreviation: "Unique Abbreviation"
uniqueMember: "Group member"
userPassword: "Password"
webVirtualHost: "Hosted Web Sites"
relations:
primaryGroup: "Primary Group"
secondaryGroups: "Secondary Groups"
individualsPrimaryMembers: "Primary Member(s) Individual(s)"
botsPrimaryMembers: "Primary Member(s) Bot(s)"
individualsSecondaryMembers: "Secondary Member(s) Individual(s)"
botsSecondaryMembers: "Secondary Member(s) Bot(s)"
foundersIndividuals: "Founder(s) Individual(s)"
foundersEntities: "Founder(s) Entity(ies)"
aspects:
fs: "FileSystem Account"
ftp: "FTP Account"
jabber: "Jabber Account"
mail: "eMail Account"
primary: "Primary Account"
shell: "Shell Account"
web: "Web Account"
lib/ldap_shadows/controller.rb
module LdapShadows
class Controller
attr_reader :shadow_name, :shadow_config_path
def initialize(mod_container = LdapShadows::Objects)
@mod_container = mod_container
......
end
def clear_shadow
@shadow_name = nil
@shadow_config_path = nil
@global_config = nil
@shadow_config = nil
@object_definitions = {}
@aspects = {}
# TODO: should replace @aspects properly one day
@aspects2 = {}
@shadow_dir = nil
end
def set_global_config(global_config)
@global_config = global_config
def set_shadow_config(shadow_config)
@shadow_config = shadow_config
end
def get_global_config
@global_config
def get_shadow_config
@shadow_config
end
def set_aspect(aspect_name, aspect_def)
......
end
EOS
rescue
STDERR.puts "Could not load Aspect plugin '#{aspect_name}'"
exit 1
raise PreProcessingError, _("Could not load Aspect plugin '%s'") % aspect_name
end
@aspects2[aspect_name] = Aspects.const_get(klass_name)
......
obj_def[:presentation][:allowed_aspects].each do |aspect|
aspect_data = get_aspect(aspect)
if aspect_data.nil?
STDERR.puts "Aspect '%s' is missing" % aspect
exit 1
raise PreProcessingError, _("Aspect '%s' is missing") % aspect
end
obj_rel.merge!(aspect_data[:relations]) if aspect_data.has_key?(:relations) and aspect_data[:relations]
end
......
obj_rel.each_pair do |field_name, rel|
foreign_klass = find_klass(rel[:object])
if foreign_klass.nil?
STDERR.puts "Relation '%s' for object '%s' is impossible: foreign object '%s' is missing" % [field_name, obj_name, rel[:object]]
exit 1
raise PreProcessingError, _("Relation '%s' for object '%s' is impossible: foreign object '%s' is missing") % [field_name, obj_name, rel[:object]]
end
rel[:class_name] = foreign_klass.to_s
......
def load_shadow(shadow_name = nil)
clear_shadow
# TODO: local global config and use shadow name to load corresponding Shadow (or defautl Shadow if nil)
@shadow_dir = LdapShadows::Config::CFG_DIR
g_config_file = File.join(LdapShadows::Config::CFG_DIR, "global.conf")
unless File.exists? g_config_file
raise PreProcessingError, _("Global LdapShadows config file is missing")
end
g_default_config = {}
g_config = g_default_config.merge(YAML.load(IO.read(g_config_file)) || {})
g_config.recursive_symbolize_keys!
@global_config = g_config
@shadow_name = shadow_name || @global_config[:default_shadow]
if @shadow_name.nil?
raise PreProcessingError, _("Could not determine which Shadow to travel through")
end
@shadow_config_path = File.join(LdapShadows::Config::CFG_DIR, "shadows", @shadow_name)
unless File.exists? @shadow_config_path
raise PreProcessingError, _("Configuration directory for Shadow '%s' is missing") % @shadow_name
end
config_str = IO.read(File.join(@shadow_dir, "shadow.conf"))
config = YAML.load(config_str)
config_file = File.join(@shadow_config_path, "shadow.conf")
unless File.exists? config_file
raise PreProcessingError, _("General configuration file for Shadow '%s' is missing") % @shadow_name
end
config = YAML.load(IO.read(config_file))
config_str_prv_filelist = [
File.join(ENV['HOME'], ".shadowwalker"),
File.join(@shadow_dir, "shadow_private.conf")
File.join(@shadow_config_path, "shadow_private.conf")
]
config_str_prv_filelist.each do |file|
if File.exists?(file)
......
end
config.recursive_symbolize_keys!
$ldapctl.set_global_config(config[:presentation])
$ldapctl.set_shadow_config(config[:presentation])
load_config_components("aspects") do |c_name, c_config|
$ldapctl.set_aspect(c_name, c_config)
......
end
$ldapctl.load_relations
translation_path = File.join($ldapctl.shadow_config_path, "translations")
if File.exists? translation_path
# load interface translation
I18n.load_path += Dir[File.join(translation_path, "**", "*.yml")]
end
rescue
raise PreProcessingError, _("Could not load shadow configiguration: %s") % $!
end
protected
def load_config_components(type)
c_config_dir = File.join(@shadow_dir, type)
c_config_dir = File.join(@shadow_config_path, type)
c_config_pattern = File.join(c_config_dir, "**", "*.conf")
Dir.glob(c_config_pattern).each do |f|
lib/ldap_shadows/display_utils.rb
module LdapShadows
module Translator
I18n.load_path += Dir[File.join(Config::CFG_DIR, "translations", "**", "*.yml")]
I18n.default_locale = :en
def self.translate_object_name(obj_hdl)
lib/ldap_shadows/object.rb
end
def organized_data
ignored_attrs = self.mapper.get_global_config[:hidden_attributes] || []
ignored_attrs = self.mapper.get_shadow_config[:hidden_attributes] || []
ignored_attrs += self.class.presentation[:hidden_attributes] || []
attr_list = self.nonempty_attributes - ignored_attrs

Also available in: Unified diff