commit 5d28ad6e4da6d7eb3927b9e632bb9ac618d64d41
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Sun Jul 26 16:27:24 2009 +0200

    [evol] added a basic 'create' command, allowing to set fields with a single value to ensure required fields are not missing; btw ensure the objectClass attributetype is never considered as a field

diff --git a/TODO b/TODO
index 2fe17ef..5a583c2 100644
--- a/TODO
+++ b/TODO
@@ -1,4 +1,6 @@
-- create item (aspect-less, with at least MUST fields with a field=val syntax, and multiple occurences of the same field allowed to specify multiple values for multi-valued fields)
+- find a way to specify a parent location for new objects
 - remove/add aspect (removing obsolete fields/adding new MUST fields accordingly)
 - search by combination of objects/aspects/fields values
+- handle fields containing a DN (allowing either giving a DN string or something like <object>:<item>)
 - display familly information (preliminary work done for debug mode done, but object type should be recognized and displayed properly)
+- handle subtypes
diff --git a/bin/shadowwalker b/bin/shadowwalker
index c911546..050c01c 100755
--- a/bin/shadowwalker
+++ b/bin/shadowwalker
@@ -198,7 +198,7 @@ class ShowCommand < Command
       field_info = args.shift
       field_name, file_no = field_info.split("#")
 
-      unless item.has_attribute?(field_name)
+      unless item.has_field?(field_name)
         STDERR.puts "No such field '#{field_name}' in object '#{obj_hdl}'."
         exit 2
       end
@@ -207,8 +207,7 @@ class ShowCommand < Command
         exit 2
       end
 
-      field_data = item.send(field_name)
-      field_data = [field_data] unless field_data.is_a?(Array)
+      field_data = item.send(field_name, true)
 
       result_is_binary = false
       if file_no == "?"
@@ -366,7 +365,7 @@ class ModCommand < Command
 
       case type
       when nil
-        unless item.has_attribute?(field)
+        unless item.has_field?(field)
           STDERR.puts "No such field '#{field}' in object '#{obj_hdl}'." 
           exit 2
         end
@@ -391,8 +390,7 @@ class ModCommand < Command
           end
         end
 
-        old_val = item.send(field)
-        old_val = [old_val] unless old_val.is_a? Array
+        old_val = item.send(field, true)
       when 'rel'
         unless item.relations.include?(field)
           STDERR.puts "No such relation '#{field}' for object '#{obj_hdl}'." 
@@ -480,7 +478,7 @@ class DelCommand < Command
 
     self.short_desc = "Delete an item"
     self.usages_params = [
-                          ""
+                          "<object> <item>"
                          ]
   end
 
@@ -516,6 +514,81 @@ class DelCommand < Command
 end
 cmdparser.add_command(DelCommand.new)
 
+class CreateCommand < Command
+  def initialize
+    super('create', false)
+
+    self.short_desc = "Create an item"
+    self.usages_params = [
+                          "<object> <new_item> [<field>=<value>] ..."
+                         ]
+  end
+
+  def execute (args)
+    if args.size < 1
+      STDERR.puts "syntax error: no object name given"
+      exit 1
+    end
+    if args.size < 2
+      STDERR.puts "syntax error: no item name given"
+      exit 1
+    end
+
+    obj_hdl = args.shift
+    obj_klass = $ldapctl.find_klass(obj_hdl)
+    if obj_klass.nil?
+      STDERR.puts "No such object '#{obj_hdl}'."
+      exit 2
+    end
+
+    item_hdl = args.shift
+    item = obj_klass.new(item_hdl)
+
+    args.each do |param|
+      param =~ /^([a-zA-Z]+)=(.*)$/
+      key = $1
+      val = $2
+
+      if key.nil?
+        STDERR.puts "Syntax error in creation parameters: invalid field name"
+        exit 1
+      end
+
+      field = key
+
+      unless item.has_field?(field)
+        STDERR.puts "No such field '#{field}' in object '#{obj_hdl}'."
+        exit 2
+      end
+
+      attr_info = ActiveLdap::Base.schema.attribute(field)
+      if attr_info.read_only?
+        STDERR.puts "The field '#{field}' cannot be modified (read only), skipping."
+        next
+      end
+
+      if attr_info.binary?
+        unless File.exists?(val)
+          STDERR.puts "The field '#{field}' contains binary data, you must provide a filename instead of a direct value."
+          exit 2
+        end
+
+        begin
+          val = File.read(val)
+        rescue
+          STDERR.puts "The file for the binary field '#{field}' cannot be read: " + $!
+          exit 2
+        end
+      end
+
+      item.send(field + "=", val)
+    end
+
+    item.save!
+  end
+end
+cmdparser.add_command(CreateCommand.new)
+
 
 begin
   cmdparser.parse
diff --git a/lib/ldap_shadows/object.rb b/lib/ldap_shadows/object.rb
index 2669f4c..76caf6a 100755
--- a/lib/ldap_shadows/object.rb
+++ b/lib/ldap_shadows/object.rb
@@ -52,6 +52,11 @@ module LdapShadows
       name.strip
     end
 
+    def has_field?(field)
+      return false if field.downcase == "objectclass"
+      has_attribute?(field)
+    end
+
     def validate_required_ldap_values
       _schema = nil
       # Make sure all MUST attributes have a value
