[LON-CAPA-cvs] cvs: loncom /metadata_database searchcat.pl /metadata_database/LONCAPA lonmetadata.pm

raeburn lon-capa-cvs@mail.lon-capa.org
Tue, 26 Sep 2006 15:15:32 -0000


This is a MIME encoded message

--raeburn1159283732
Content-Type: text/plain

raeburn		Tue Sep 26 11:15:32 2006 EDT

  Modified files:              
    /loncom/metadata_database	searchcat.pl 
    /loncom/metadata_database/LONCAPA	lonmetadata.pm 
  Log:
  Support for searching for portfolio files using metadata associated with a file. Three new MySQL tables added - portfolio_access, portfolio_metadata and portfolio_addedfields.  Both personal portfolio and course group portfolio files with currently active access controls are included.   
  
  
--raeburn1159283732
Content-Type: text/plain
Content-Disposition: attachment; filename="raeburn-20060926111532.txt"

Index: loncom/metadata_database/searchcat.pl
diff -u loncom/metadata_database/searchcat.pl:1.68 loncom/metadata_database/searchcat.pl:1.69
--- loncom/metadata_database/searchcat.pl:1.68	Sat Apr  8 03:07:15 2006
+++ loncom/metadata_database/searchcat.pl	Tue Sep 26 11:15:19 2006
@@ -2,7 +2,7 @@
 # The LearningOnline Network
 # searchcat.pl "Search Catalog" batch script
 #
-# $Id: searchcat.pl,v 1.68 2006/04/08 07:07:15 albertel Exp $
+# $Id: searchcat.pl,v 1.69 2006/09/26 15:15:19 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -118,8 +118,18 @@
 
 ##
 ## Use variables for table names so we can test this routine a little easier
-my $oldname = 'metadata';
-my $newname = 'newmetadata'.$$; # append pid to have unique temporary table
+my %oldnames = (
+                 'metadata'    => 'metadata',
+                 'portfolio'   => 'portfolio_metadata',
+                 'access'      => 'portfolio_access',
+                 'addedfields' => 'portfolio_addedfields',
+               );
+
+my %newnames;
+# new table names -  append pid to have unique temporary tables
+foreach my $key (keys(%oldnames)) {
+    $newnames{$key} = 'new'.$oldnames{$key}.$$;
+}
 
 #
 # Only run if machine is a library server
@@ -155,16 +165,27 @@
 }
 # This can return an error and still be okay, so we do not bother checking.
 # (perhaps it should be more robust and check for specific errors)
-$dbh->do('DROP TABLE IF EXISTS '.$newname);
+foreach my $key (keys(%newnames)) {
+    if ($newnames{$key} ne '') {
+        $dbh->do('DROP TABLE IF EXISTS '.$newnames{$key});
+    }
+}
+
 #
-# Create the new table
-my $request = &LONCAPA::lonmetadata::create_metadata_storage($newname);
-$dbh->do($request);
-if ($dbh->err) {
-    $dbh->disconnect();
-    &log(0,"MySQL Error Create: ".$dbh->errstr);
-    die $dbh->errstr;
+# Create the new metadata and portfolio tables
+foreach my $key (keys(%newnames)) {
+    if ($newnames{$key} ne '') { 
+        my $request =
+             &LONCAPA::lonmetadata::create_metadata_storage($newnames{$key},$oldnames{$key});
+        $dbh->do($request);
+        if ($dbh->err) {
+            $dbh->disconnect();
+            &log(0,"MySQL Error Create: ".$dbh->errstr);
+            die $dbh->errstr;
+        }
+    }
 }
+
 #
 # find out which users we need to examine
 my @domains = sort(&Apache::lonnet::current_machine_domains());
@@ -200,16 +221,54 @@
               no_chdir   => 1,
              }, join('/',($Apache::lonnet::perlvar{'lonDocRoot'},'res',$dom,$user)) );
     }
+    # Search for public portfolio files
+    my %portusers;
+    if ($oneuser) {
+        %portusers = (
+                        $oneuser => '',
+                       );
+    } else {
+        my $dir = $Apache::lonnet::perlvar{lonUsersDir}.'/'.$dom;
+        &descend_tree($dir,0,\%portusers);
+    }
+    foreach my $uname (keys(%portusers)) {
+        my $urlstart = '/uploaded/'.$dom.'/'.$uname;
+        my $pathstart = &propath($dom,$uname).'/userfiles';
+        my $is_course = &check_for_course($dom,$uname);
+        my $curr_perm = &Apache::lonnet::get_portfile_permissions($dom,$uname);
+        my %access = &Apache::lonnet::get_access_controls($curr_perm);
+        foreach my $file (keys(%access)) { 
+            my ($group,$url,$fullpath);
+            if ($is_course) {
+                ($group, my ($path)) = ($file =~ /^(\w+)(\/.+)$/);
+                $fullpath = $pathstart.'/groups/'.$group.'/portfolio/'.$path;
+                $url = $urlstart.'/groups/'.$group.'/portfolio'.$path;
+            } else {
+                $fullpath = $pathstart.'/portfolio'.$file;
+                $url .= $urlstart.'/portfolio'.$file;
+            }
+            if (ref($access{$file}) eq 'HASH') {
+                &process_portfolio_access_data($url,$access{$file});
+            }
+            &process_portfolio_metadata($url,$fullpath,$is_course,$dom,
+                                        $uname,$group);
+        }
+    }
 }
+
 #
-# Rename the table
+# Rename the tables
 if (! $simulate) {
-    $dbh->do('DROP TABLE IF EXISTS '.$oldname);
-    if (! $dbh->do('RENAME TABLE '.$newname.' TO '.$oldname)) {
-        &log(0,"MySQL Error Rename: ".$dbh->errstr);
-        die $dbh->errstr;
-    } else {
-        &log(1,"MySQL table rename successful.");
+    foreach my $key (keys(%oldnames)) {
+        if (($oldnames{$key} ne '') && ($newnames{$key} ne '')) {
+            $dbh->do('DROP TABLE IF EXISTS '.$oldnames{$key});
+            if (! $dbh->do('RENAME TABLE '.$newnames{$key}.' TO '.$oldnames{$key})) {
+                &log(0,"MySQL Error Rename: ".$dbh->errstr);
+                die $dbh->errstr;
+            } else {
+                &log(1,"MySQL table rename successful for $key.");
+            }
+        }
     }
 }
 if (! $dbh->disconnect) {
@@ -243,6 +302,119 @@
     }
 }
 
+sub descend_tree {
+    my ($dir,$depth,$alldomusers) = @_;
+    if (-d $dir) {
+        opendir(DIR,$dir);
+        my @contents = grep(!/^\./,readdir(DIR));
+        closedir(DIR);
+        $depth ++;
+        foreach my $item (@contents) {
+            if ($depth < 4) {
+                &descend_tree($dir.'/'.$item,$depth,$alldomusers);
+            } else {
+                if (-e $dir.'/'.$item.'/file_permissions.db') {
+                 
+                    $$alldomusers{$item} = '';
+                }
+            }       
+        }
+    } 
+}
+
+sub check_for_course {
+    my ($cdom,$cnum) = @_;
+    my %courses = &Apache::lonnet::courseiddump($cdom,'.',1,'.','.',$cnum,undef,
+                                                undef,'.');
+    if (exists($courses{$cdom.'_'.$cnum})) {
+        return 1;
+    }
+    return 0;
+}
+
+
+sub process_portfolio_access_data {
+    my ($url,$access_hash) = @_;
+    foreach my $key (keys(%{$access_hash})) {
+        my $acc_data;
+        $acc_data->{url} = $url;
+        $acc_data->{keynum} = $key;
+        my ($num,$scope,$end,$start) =
+                        ($key =~ /^([^:]+):([a-z]+)_(\d*)_?(\d*)$/);
+        $acc_data->{scope} = $scope;
+        if ($end != 0) {
+            $acc_data->{end} = &sqltime($end);
+        }
+        $acc_data->{start} = &sqltime($start);
+        if (! $simulate) {
+            my ($count,$err) =
+              &LONCAPA::lonmetadata::store_metadata($dbh,
+                                                $newnames{'access'},
+                                                'portfolio_access',$acc_data);
+            if ($err) {
+                &log(0,"MySQL Error Insert: ".$err);
+            }
+            if ($count < 1) {
+                &log(0,"Unable to insert record into MySQL database for $url");
+            }
+        }
+    }
+}
+
+sub process_portfolio_metadata {
+    my ($url,$fullpath,$is_course,$dom,$uname,$group) = @_;
+    my ($ref,$crs,$addedfields) = &portfolio_metadata($fullpath,$dom,$uname,
+                                                      $group);
+    &getfiledates($ref,$fullpath);
+    if ($is_course) {
+        $ref->{'groupname'} = $group;
+    }
+    my %Data;
+    if (ref($ref) eq 'HASH') {
+        %Data = %{$ref};
+    }
+    %Data = (
+             %Data,
+             'url'=>$url,
+             'version'=>'current',
+    );
+    if (! $simulate) {
+        my ($count,$err) =
+         &LONCAPA::lonmetadata::store_metadata($dbh,
+                                               $newnames{'portfolio'},
+                                               'portfolio_metadata',\%Data);
+        if ($err) {
+            &log(0,"MySQL Error Insert: ".$err);
+        }
+        if ($count < 1) {
+            &log(0,"Unable to insert record into MySQL portfolio_metadata database table for $url");
+        }
+        if (ref($addedfields) eq 'HASH') {
+            if (keys(%{$addedfields}) > 0) {
+                foreach my $key (keys(%{$addedfields})) {
+                    my $added_data = {
+                                'url'   => $url,
+                                'field' => $key,
+                                'value' => $addedfields->{$key},
+                                'courserestricted' => $crs,
+                    };
+                    ($count,$err) = &LONCAPA::lonmetadata::store_metadata($dbh,
+                                            $newnames{'addedfields'},
+                                            'portfolio_addedfields',
+                                            $added_data);
+                    if ($err) {
+                        &log(0,"MySQL Error Insert: ".$err);
+                    }
+                    if ($count < 1) {
+                        &log(0,"Unable to insert record into MySQL portfolio_addedfields database table for url = $url and field = $key");
+                    }
+                }
+            }
+        }
+    }
+    return;
+}
+
 ########################################################
 ########################################################
 ###                                                  ###
@@ -293,7 +465,7 @@
     return if (-d $fullfilename); # No need to do anything here for directories
     if ($debug) {
         &log(6,$fullfilename);
-        my $ref=&metadata($fullfilename);
+        my $ref = &metadata($fullfilename);
         if (! defined($ref)) {
             &log(6,"    No data");
             return;
@@ -317,7 +489,7 @@
     #
     &log(3,$filename) if ($debug);
     #
-    my $ref=&metadata($filename);
+    my $ref = &metadata($filename);
     #
     # $url is the original file url, not the metadata file
     my $target = $filename;
@@ -346,25 +518,17 @@
         %dyn=&get_dynamic_metadata($url);
         &count_type($url);
     }
+    &getfiledates($ref,$target);
     #
-    if (! defined($ref->{'creationdate'}) ||
-        $ref->{'creationdate'} =~ /^\s*$/) {
-        $ref->{'creationdate'} = (stat($target))[9];
-    }
-    if (! defined($ref->{'lastrevisiondate'}) ||
-        $ref->{'lastrevisiondate'} =~ /^\s*$/) {
-        $ref->{'lastrevisiondate'} = (stat($target))[9];
-    }
-    $ref->{'creationdate'}     = &sqltime($ref->{'creationdate'});
-    $ref->{'lastrevisiondate'} = &sqltime($ref->{'lastrevisiondate'});
     my %Data = (
                 %$ref,
                 %dyn,
                 'url'=>$url,
                 'version'=>'current');
     if (! $simulate) {
-        my ($count,$err) = &LONCAPA::lonmetadata::store_metadata($dbh,$newname,
-                                                                 \%Data);
+        my ($count,$err) = 
+          &LONCAPA::lonmetadata::store_metadata($dbh,$newnames{'metadata'},
+                                                'metadata',\%Data);
         if ($err) {
             &log(0,"MySQL Error Insert: ".$err);
         }
@@ -386,7 +550,7 @@
 ########################################################
 ########################################################
 sub metadata {
-    my ($uri)=@_;
+    my ($uri) = @_;
     my %metacache=();
     $uri=&declutter($uri);
     my $filename=$uri;
@@ -416,7 +580,7 @@
             }
             foreach ( @{$token->[3]}) {
                 $metacache{$uri.''.$unikey.'.'.$_}=$token->[2]->{$_};
-            } 
+            }
             if (! ($metacache{$uri.''.$unikey}=$parser->get_text('/'.$entry))){
                 $metacache{$uri.''.$unikey} = 
                     $metacache{$uri.''.$unikey.'.default'};
@@ -426,6 +590,88 @@
     return \%metacache;
 }
 
+###############################################################
+###############################################################
+###                                                         ###
+###  &portfolio_metadata($filepath,$dom,$uname,$group) ###
+###   Retrieve metadata for the given file                  ###
+###   Returns array -                                       ###
+###      contains reference to metadatahash and             ###
+###         optional reference to addedfields hash          ###
+###                                                         ###
+###############################################################
+###############################################################
+sub portfolio_metadata {
+    my ($fullpath,$dom,$uname,$group)=@_;
+    my ($mime) = ( $fullpath=~/\.(\w+)$/ );
+    my %metacache=();
+    if ($fullpath !~ /\.meta$/) {
+        $fullpath .= '.meta';
+    }
+    my (@standard_fields,%addedfields);
+    my $colsref = 
+       $LONCAPA::lonmetadata::Portfolio_metadata_table_description;
+    if (ref($colsref) eq 'ARRAY') {
+        my @columns = @{$colsref};
+        foreach my $coldata (@columns) {
+            push(@standard_fields,$coldata->{'name'});
+        }
+    }
+    my $metastring=&getfile($fullpath);
+    if (! defined($metastring)) {
+        $metacache{'keys'}= 'owner,domain,mime';
+        $metacache{'owner'} = $uname.':'.$dom;
+        $metacache{'domain'} = $dom;
+        $metacache{'mime'} = $mime;
+        if (defined($group)) {
+            $metacache{'keys'} .= ',courserestricted';
+            $metacache{'courserestricted'} = 'course.'.$dom.'_'.$uname;
+        } 
+    } else {
+        my $parser=HTML::TokeParser->new(\$metastring);
+        my $token;
+        while ($token=$parser->get_token) {
+            if ($token->[0] eq 'S') {
+                my $entry=$token->[1];
+                if ($metacache{'keys'}) {
+                    $metacache{'keys'}.=','.$entry;
+                } else {
+                    $metacache{'keys'}=$entry;
+                }
+                my $value = $parser->get_text('/'.$entry);
+                if (!grep(/^\Q$entry\E$/,@standard_fields)) {
+                    my $clean_value = lc($value);
+                    $clean_value =~ s/\s/_/g;
+                    if ($clean_value ne $entry) {
+                        if (defined($addedfields{$entry})) {
+                            $addedfields{$entry} .=','.$value;
+                        } else {
+                            $addedfields{$entry} = $value;
+                        }
+                    }
+                } else {
+                    $metacache{$entry} = $value;
+                }
+            }
+        } # End of ($token->[0] eq 'S')
+    }
+    if (keys(%addedfields) > 0) {
+        foreach my $key (sort keys(%addedfields)) {
+            $metacache{'addedfieldnames'} .= $key.',';
+            $metacache{'addedfieldvalues'} .= $addedfields{$key}.'&&&';
+        }
+        $metacache{'addedfieldnames'} =~ s/,$//;
+        $metacache{'addedfieldvalues'} =~ s/\&\&\&$//;
+        if ($metacache{'keys'}) {
+            $metacache{'keys'}.=',addedfieldnames';
+        } else {
+            $metacache{'keys'}='addedfieldnames';
+        }
+        $metacache{'keys'}.=',addedfieldvalues';
+    }
+    return (\%metacache,$metacache{'courserestricted'},\%addedfields);
+}
+
 ##
 ## &getfile($filename)
 ##   Slurps up an entire file into a scalar.  
@@ -443,6 +689,24 @@
     return $contents;
 }
 
+##
+## &getfiledates() 
+## Converts creationdate and modifieddates to SQL format 
+## Applies stat() to file to retrieve dates if missing
+sub getfiledates {
+    my ($ref,$target) = @_;
+    if (! defined($ref->{'creationdate'}) ||
+        $ref->{'creationdate'} =~ /^\s*$/) {
+        $ref->{'creationdate'} = (stat($target))[9];
+    }
+    if (! defined($ref->{'lastrevisiondate'}) ||
+        $ref->{'lastrevisiondate'} =~ /^\s*$/) {
+        $ref->{'lastrevisiondate'} = (stat($target))[9];
+    }
+    $ref->{'creationdate'}     = &sqltime($ref->{'creationdate'});
+    $ref->{'lastrevisiondate'} = &sqltime($ref->{'lastrevisiondate'});
+}
+
 ########################################################
 ########################################################
 ###                                                  ###
Index: loncom/metadata_database/LONCAPA/lonmetadata.pm
diff -u loncom/metadata_database/LONCAPA/lonmetadata.pm:1.13 loncom/metadata_database/LONCAPA/lonmetadata.pm:1.14
--- loncom/metadata_database/LONCAPA/lonmetadata.pm:1.13	Tue Nov 29 14:56:42 2005
+++ loncom/metadata_database/LONCAPA/lonmetadata.pm	Tue Sep 26 11:15:31 2006
@@ -1,6 +1,6 @@
 # The LearningOnline Network with CAPA
 #
-# $Id: lonmetadata.pm,v 1.13 2005/11/29 19:56:42 www Exp $
+# $Id: lonmetadata.pm,v 1.14 2006/09/26 15:15:31 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -30,6 +30,8 @@
 
 use strict;
 use DBI;
+use vars qw($Metadata_Table_Description $Portfolio_metadata_table_description 
+$Portfolio_access_table_description $Fulltext_indicies $Portfolio_metadata_indices $Portfolio_access_indices $Portfolio_addedfields_table_description $Portfolio_addedfields_indices);
 
 ######################################################################
 ######################################################################
@@ -96,8 +98,8 @@
 
 ######################################################################
 ######################################################################
-my @Metadata_Table_Description = 
-    (
+$Metadata_Table_Description = 
+    [
      { name => 'title',     type=>'TEXT'},
      { name => 'author',    type=>'TEXT'},
      { name => 'subject',   type=>'TEXT'},
@@ -144,9 +146,9 @@
      { name => 'depth',          type=>'FLOAT'},
      { name => 'hostname',       type=> 'TEXT'},
      #--------------------------------------------------
-     );
+    ];
 
-my @Fulltext_indicies = qw/
+$Fulltext_indicies = [ qw/ 
     title
     author
     subject
@@ -158,11 +160,95 @@
     mime
     language
     owner
-    copyright/;
+    copyright/ ];
+
+######################################################################
+######################################################################
+$Portfolio_metadata_table_description =
+    [
+     { name => 'title',     type=>'TEXT'},
+     { name => 'author',    type=>'TEXT'},
+     { name => 'subject',   type=>'TEXT'},
+     { name => 'url',       type=>'TEXT', restrictions => 'NOT NULL' },
+     { name => 'keywords',  type=>'TEXT'},
+     { name => 'version',   type=>'TEXT'},
+     { name => 'notes',     type=>'TEXT'},
+     { name => 'abstract',  type=>'TEXT'},
+     { name => 'mime',      type=>'TEXT'},
+     { name => 'language',  type=>'TEXT'},
+     { name => 'creationdate',     type=>'DATETIME'},
+     { name => 'lastrevisiondate', type=>'DATETIME'},
+     { name => 'owner',     type=>'TEXT'},
+     { name => 'copyright',     type=>'TEXT'},
+     { name => 'domain',    type=>'TEXT'},
+     { name => 'groupname',     type=>'TEXT'},
+     { name => 'courserestricted', type=>'TEXT'},
+     { name => 'addedfieldnames',  type=>'TEXT'},
+     { name => 'addedfieldvalues', type=>'TEXT'},
+      #--------------------------------------------------
+     { name => 'dependencies',   type=>'TEXT'},
+     { name => 'modifyinguser',  type=>'TEXT'},
+     { name => 'authorspace',    type=>'TEXT'},
+     { name => 'lowestgradelevel',  type=>'INT'},
+     { name => 'highestgradelevel', type=>'INT'},
+     { name => 'standards',      type=>'TEXT'},
+     { name => 'hostname',       type=> 'TEXT'},
+     #--------------------------------------------------
+   ];
+
+$Portfolio_metadata_indices = [qw/
+    title
+    author
+    subject
+    url
+    keywords
+    version
+    notes
+    abstract
+    mime
+    language
+    owner/];
+
+######################################################################
+######################################################################
+
+$Portfolio_access_table_description =
+    [
+     { name => 'url',   type=>'TEXT', restrictions => 'NOT NULL' },
+     { name => 'keynum', type=>'TEXT', restrictions => 'NOT NULL' },
+     { name => 'scope', type=>'TEXT'},
+     { name => 'start', type=>'DATETIME'},
+     { name => 'end',   type=>'DATETIME'},
+   ];
+
+$Portfolio_access_indices = [qw/
+    url
+    keynum
+    scope
+    start
+    end/];
 
 ######################################################################
 ######################################################################
 
+$Portfolio_addedfields_table_description =
+    [
+     { name => 'url',   type=>'TEXT', restrictions => 'NOT NULL' },
+     { name => 'field', type=>'TEXT', restrictions => 'NOT NULL' },
+     { name => 'courserestricted', type=>'TEXT', restrictions => 'NOT NULL' },
+     { name => 'value', type=>'TEXT'},
+   ];
+
+$Portfolio_addedfields_indices = [qw/
+    url
+    field
+    value
+    courserestricted/];
+
+######################################################################
+######################################################################
+
+
 =pod
 
 =item &describe_metadata_storage
@@ -176,8 +262,36 @@
 
 ######################################################################
 ######################################################################
-sub describe_metadata_storage { 
-    return (\@Metadata_Table_Description,\@Fulltext_indicies);
+sub describe_metadata_storage {
+    my ($tabletype) = @_;
+    my %table_description = (
+        metadata              => $Metadata_Table_Description,
+        portfolio_metadata    => $Portfolio_metadata_table_description,
+        portfolio_access      => $Portfolio_access_table_description,
+        portfolio_addedfields => $Portfolio_addedfields_table_description, 
+    );
+    my %index_description = (
+        metadata              => $Fulltext_indicies,
+        portfolio_metadata    => $Portfolio_metadata_indices,
+        portfolio_access      => $Portfolio_access_indices,
+        portfolio_addedfields => $Portfolio_addedfields_indices,
+    );
+    if ($tabletype eq 'portfolio_search') {
+        my @portfolio_search_table = @{$table_description{portfolio_metadata}};
+        foreach my $item (@{$table_description{portfolio_access}}) {
+            if (ref($item) eq 'HASH') {
+                if ($item->{'name'} eq 'url') {
+                    next;
+                }
+            }
+            push(@portfolio_search_table,$item);
+        }
+        my @portfolio_search_indices = @{$index_description{portfolio_metadata}};
+        push(@portfolio_search_indices,('scope','keynum'));
+        return (\@portfolio_search_table,\@portfolio_search_indices);
+    } else {
+        return ($table_description{$tabletype},$index_description{$tabletype});
+    }
 }
 
 ######################################################################
@@ -197,15 +311,20 @@
 ######################################################################
 ######################################################################
 sub create_metadata_storage { 
-    my ($tablename) = @_;
+    my ($tablename,$tabletype) = @_;
     $tablename = 'metadata' if (! defined($tablename));
+    $tabletype = 'metadata' if (! defined($tabletype));
     my $request = "CREATE TABLE IF NOT EXISTS ".$tablename." ";
     #
     # Process the columns  (this code is stolen from lonmysql.pm)
     my @Columns;
     my $col_des; # mysql column description
-    foreach my $coldata (@Metadata_Table_Description) {
+    my ($table_columns,$table_indices) = 
+                          &describe_metadata_storage($tabletype);
+    my %coltype;
+    foreach my $coldata (@{$table_columns}) {
         my $column = $coldata->{'name'};
+        $coltype{$column} = $coldata->{'type'};
         $col_des = '';
         if (lc($coldata->{'type'}) =~ /(enum|set)/) { # 'enum' or 'set'
             $col_des.=$column." ".$coldata->{'type'}."('".
@@ -231,8 +350,14 @@
         # skip blank items.
         push (@Columns,$col_des) if ($col_des ne '');
     }
-    foreach my $colname (@Fulltext_indicies) {
-        my $text = 'FULLTEXT idx_'.$colname.' ('.$colname.')';
+    foreach my $colname (@{$table_indices}) {
+        my $text;
+        if ($coltype{$colname} eq 'TEXT') {
+            $text = 'FULLTEXT ';
+        } else {
+            $text = 'INDEX ';
+        }
+        $text .= 'idx_'.$colname.' ('.$colname.')';
         push (@Columns,$text);
     }
     $request .= "(".join(", ",@Columns).") TYPE=MyISAM";
@@ -246,8 +371,8 @@
 
 =item store_metadata()
 
-Inputs: database handle ($dbh), a table name, and a hash or hash reference 
-containing the metadata for a single resource.
+Inputs: database handle ($dbh), a table name, table type and a hash or hash 
+reference containing the metadata for a single resource.
 
 Returns: 1 on success, 0 on failure to store.
 
@@ -263,19 +388,21 @@
     ##
     ##  In most scripts, this will work fine.  If the dbi is going to be
     ##  dropped and (possibly) later recreated, call &clear_sth.  Yes it
-    ##  is annoying but $sth appearantly does not have a link back to the 
+    ##  is annoying but $sth apparently does not have a link back to the 
     ##  $dbh, so we can't check our validity.
     ##
     my $sth = undef;
     my $sth_table = undef;
 
 sub create_statement_handler {
-    my $dbh = shift();
-    my $tablename = shift();
+    my ($dbh,$tablename,$tabletype) = @_;
     $tablename = 'metadata' if (! defined($tablename));
+    $tabletype = 'metadata' if (! defined($tabletype));
+    my ($table_columns,$table_indices) = 
+          &describe_metadata_storage($tabletype);
     $sth_table = $tablename;
     my $request = 'INSERT INTO '.$tablename.' VALUES(';
-    foreach (@Metadata_Table_Description) {
+    foreach (@{$table_columns}) {
         $request .= '?,';
     }
     chop $request;
@@ -287,18 +414,23 @@
 sub clear_sth { $sth=undef; $sth_table=undef;}
 
 sub store_metadata {
-    my ($dbh,$tablename,@Metadata)=@_;
+    my ($dbh,$tablename,$tabletype,@Metadata)=@_;
     my $errors = '';
     if (! defined($sth) || 
         ( defined($tablename) && ($sth_table ne $tablename)) || 
         (! defined($tablename) && $sth_table ne 'metadata')) {
-        &create_statement_handler($dbh,$tablename);
+        &create_statement_handler($dbh,$tablename,$tabletype);
     }
     my $successcount = 0;
+    if (! defined($tabletype)) {
+        $tabletype = 'metadata';
+    }
+    my ($table_columns,$table_indices) = 
+                        &describe_metadata_storage($tabletype);
     foreach my $mdata (@Metadata) {
         next if (ref($mdata) ne "HASH");
         my @MData;
-        foreach my $field (@Metadata_Table_Description) {
+        foreach my $field (@{$table_columns}) {
             my $fname = $field->{'name'};
             if (exists($mdata->{$fname}) && 
                 defined($mdata->{$fname}) &&
@@ -422,6 +554,7 @@
 $dbh, database handle
 $newmetadata, hash reference containing the new metadata
 $tablename, metadata table name.  Defaults to 'metadata'.
+$tabletype, type of table (metadata, portfolio_metadata, portfolio_access)  
 
 Returns:
 $error on failure.  undef on success.
@@ -431,9 +564,10 @@
 ######################################################################
 ######################################################################
 sub update_metadata {
-    my ($dbh,$tablename,$newmetadata)=@_;
+    my ($dbh,$tablename,$tabletype,$newmetadata)=@_;
     my $error;
     $tablename = 'metadata' if (! defined($tablename));
+    $tabletype = 'metadata' if (! defined($tabletype));
     if (! exists($newmetadata->{'url'})) {
         $error = 'Unable to update: no url specified';
     }
@@ -445,7 +579,7 @@
                                    ' url='.$dbh->quote($newmetadata->{'url'}),
                                      undef,$tablename);
     return $error if ($error);
-    my %metadata = &LONCAPA::lonmetadata::metadata_col_to_hash(@{$row->[0]});
+    my %metadata = &LONCAPA::lonmetadata::metadata_col_to_hash($tabletype,@{$row->[0]});
     #
     # Update metadata values
     while (my ($key,$value) = each(%$newmetadata)) {
@@ -458,7 +592,7 @@
     #
     # Store updated metadata
     my $success;
-    ($success,$error) = &store_metadata($dbh,$tablename,\%metadata);
+    ($success,$error) = &store_metadata($dbh,$tablename,$tabletype,\%metadata);
     return $error;
 }
 
@@ -479,17 +613,18 @@
 ######################################################################
 ######################################################################
 sub metadata_col_to_hash {
-    my @cols=@_;
+    my ($tabletype,@cols)=@_;
     my %hash=();
-    for (my $i=0; $i<=$#Metadata_Table_Description;$i++) {
-        $hash{$Metadata_Table_Description[$i]->{'name'}}=$cols[$i];
-	unless ($hash{$Metadata_Table_Description[$i]->{'name'}}) {
-	    if ($Metadata_Table_Description[$i]->{'type'} eq 'TEXT') {
-		$hash{$Metadata_Table_Description[$i]->{'name'}}='';
-	    } elsif ($Metadata_Table_Description[$i]->{'type'} eq 'DATETIME') {
-		$hash{$Metadata_Table_Description[$i]->{'name'}}='0000-00-00 00:00:00';
+    my ($columns,$indices) = &describe_metadata_storage($tabletype);
+    for (my $i=0; $i<@{$columns};$i++) {
+        $hash{$columns->[$i]->{'name'}}=$cols[$i];
+	unless ($hash{$columns->[$i]->{'name'}}) {
+	    if ($columns->[$i]->{'type'} eq 'TEXT') {
+		$hash{$columns->[$i]->{'name'}}='';
+	    } elsif ($columns->[$i]->{'type'} eq 'DATETIME') {
+		$hash{$columns->[$i]->{'name'}}='0000-00-00 00:00:00';
 	    } else {
-		$hash{$Metadata_Table_Description[$i]->{'name'}}=0;
+		$hash{$columns->[$i]->{'name'}}=0;
 	    }
 	}
     }
@@ -795,6 +930,10 @@
 
 ######################################################################
 ######################################################################
+
+
+######################################################################
+######################################################################
 ##
 ## The usual suspects, repeated here to reduce dependency hell
 ##

--raeburn1159283732--