diff -urN family/EstimateDates.inc drupal/modules/family/EstimateDates.inc --- family/EstimateDates.inc 2006-04-30 19:59:21.000000000 -0500 +++ drupal/modules/family/EstimateDates.inc 2007-10-14 12:32:56.970018000 -0500 @@ -12,7 +12,7 @@ "If you have sensitive data, please check manually that the data is set as private. ". "You should be aware that any database may be hacked and there is no guarantee for your data security

". "

For large files this may take a few minutes.

". - "

Start auto privacy

"; + "

Start auto privacy

"; return $content; } diff -urN family/common.inc drupal/modules/family/common.inc --- family/common.inc 2006-04-30 19:59:21.000000000 -0500 +++ drupal/modules/family/common.inc 2007-10-01 17:09:27.492335750 -0500 @@ -15,7 +15,7 @@ */ function family_insert_fact($fact_code, $value) { $fid = db_next_id('{family_facts}_fid'); - db_query("INSERT INTO {family_facts} (fid, fact_code, fact_value) VALUES (%d, '%s', '%s')", $fid, $fact_code, $value); + $result = db_query("INSERT INTO {family_facts} (fid, fact_code, fact_value) VALUES (%d, '%s', '%s')", $fid, $fact_code, $value); return ($fid); } @@ -31,8 +31,8 @@ $removed = family_remove_fact($sub_fid, $fid); } //Remove both the fact & relationship to parent facts - db_query("DELETE FROM family_relations WHERE fid1 = %d", $fid); - db_query("DELETE FROM family_facts WHERE fid = %d", $fid); + db_query("DELETE FROM {family_relations} WHERE fid1 = %d", $fid); + db_query("DELETE FROM {family_facts} WHERE fid = %d", $fid); } function family_insert_relation($fid1, $fid2, $desc) { diff -urN family/family.info drupal/modules/family/family.info --- family/family.info 1969-12-31 18:00:00.000000000 -0600 +++ drupal/modules/family/family.info 2007-09-17 21:39:16.000000000 -0500 @@ -0,0 +1,3 @@ +; $Id: family.info,v 5.x-1.x-dev 2007/09/17 22:14:00 pyutaros $ +name = Family Tree +description = Adds a family tree to your Drupal website using Drupal nodes and relational database. diff -urN family/family.install drupal/modules/family/family.install --- family/family.install 1969-12-31 18:00:00.000000000 -0600 +++ drupal/modules/family/family.install 2007-10-01 10:15:41.621737500 -0500 @@ -0,0 +1,88 @@ +nid); + $n++; + } + drupal_set_message(t('Deleted @n family_individual nodes.', array('@n' => $n))); +} + + diff -urN family/family.module drupal/modules/family/family.module --- family/family.module 2006-04-30 20:04:29.000000000 -0500 +++ drupal/modules/family/family.module 2007-10-14 13:08:13.925862250 -0500 @@ -20,7 +20,8 @@ require_once "$path/common.inc"; require_once "$path/individual.inc"; // require_once "$path/location.inc"; - + + function family_menu($may_cache) { $items = array(); $items[] = array( @@ -36,32 +37,28 @@ 'access' => user_access('access family'), ); $items[] = array( - 'path' => 'admin/family', - 'title' => t('Family'), - 'type' => MENU_ITEM_GROUPING, - 'access' => user_access('administer family'), - ); - $items[] = array( - 'path' => 'admin/family/import', - 'title' => t('Import'), + 'path' => 'admin/content/family_import', + 'title' => t('Family Import'), + 'description' => 'Import your family tree from a standard GED file.', 'callback' => 'family_import', 'access' => user_access('administer family'), ); $items[] = array( - 'path' => 'admin/family/privacy', - 'title' => t('Set Auto Privacy'), + 'path' => 'admin/settings/family_privacy', + 'title' => t('Family Privacy Settings'), + 'description' => 'Protect living relatives with auto privacy.', 'callback' => 'family_auto_privacy', 'access' => user_access('administer family'), ); $items[] = array( - 'path' => 'admin/family/privacy_done', + 'path' => 'admin/settings/family_privacy_done', 'title' => t('Auto Privacy Done'), 'callback' => 'family_auto_privacy_done', 'type' => MENU_CALLBACK, 'access' => user_access('administer family'), ); $items[] = array( - 'path' => 'admin/family/import_done', + 'path' => 'admin/content/family_import_done', 'title' => t('Import Done'), 'callback' => 'family_import_done', 'type' => MENU_CALLBACK, @@ -83,11 +80,13 @@ return array( 'family_location' => array( 'name' => t('Family: location'), - 'base' => 'family_location' + 'module' => 'family_location', + 'description' => t('A geneological location.') ), 'family_individual' => array( 'name' => t('Family: individual'), - 'base' => 'family_individual' + 'module' => 'family_individual', + 'description' => t('A node representing a person.') ), ); } @@ -131,4 +130,66 @@ ); } // function family_perm() +/** + * Implementation of hook_user(). + */ +function family_user($op, &$edit, &$account, $category = NULL) { + + switch ($op) { + + case ('insert'): + case ('update'): + case ('submit'): + case ('login'): + case ('logout'): + case ('delete'): + break; + + case ('form'): + if ($category == 'account') { + $form['family'] = array( + '#type' => 'fieldset', + '#title' => t('Family association'), + '#collapsible' => TRUE, + '#weight' => 4); + $form['family']['family_link'] = array( + '#type' => 'textfield', + '#title' => t('My family link'), + '#default_value' => $edit['family_link'], + '#description' => t('The node number for the family_individual that represents you.')); + $form['family']['family_tree_root'] = array( + '#type' => 'textfield', + '#title' => t('Tree root individual'), + '#default_value' => $edit['family_tree_root'], + '#description' => t('The node number for the family_individual that your default tree is based on.')); + $form['family']['family_tree_type'] = array( + '#type' => 'select', + '#options' => array( + 'ANS' => 'Ancestry', + 'DESC' => 'Descendency', + 'PED' => 'Pedigree', + ), + '#default_value' => $edit['family_tree_type'], + '#description' => t('The type of tree you prefer to see by default.')); + $form['family']['family_tree_depth'] = array( + '#type' => 'textfield', + '#title' => t('Tree depth'), + '#default_value' => $edit['family_tree_depth'], + '#description' => t('The number of degrees to show on your default family tree.')); + return $form; + } + case ('view'): + return array( + t("Family Association") => array( + array('title' => t("My family link"), 'value' => l("Me","node/".$account->family_link)), + # description => t('The node number for the family_individual that represents you.')); + array('title' => t("Tree root individual"), 'value' => l("root","node/".$account->family_tree_root)), + # description => t('The node number for the family_individual that your default tree is based on.')); + array('title' => t("Tree type"), 'value' => $account->family_tree_type), + # description => t('The type of tree you prefer to see by default.')); + array('title' => t("Tree depth"), 'value' => $account->family_tree_depth), + # description => t('The number of degrees to show on your default family tree.')); + )); + } +} diff -urN family/family.mysql drupal/modules/family/family.mysql --- family/family.mysql 2006-04-30 19:59:21.000000000 -0500 +++ drupal/modules/family/family.mysql 1969-12-31 18:00:00.000000000 -0600 @@ -1,54 +0,0 @@ --- MySQL Schema for the Drupal module 'Family'. --- By Amnon Jonas 3-Dec-2005 --- This is a simple database format that uses only two tables. --- The facts table defines all the data, a record for each piece, e.g. individual, family, birth event, a date or a place. --- The relations ties between the facts, e.g. between a child and his family or between an event to its date. --- Every fact may be a node. It is done by including a non-null nid (node id). --- Most GEDCOM lines are represented by a single fact records that is tied to his parent using a relations record --- Some GEDCOM lines like CHIL, HUSB or WIFE define additional relations and don't add a fact record. --- Relations to external nodes will be defined using clipper module - -DROP TABLE IF EXISTS family_relations; -DROP TABLE IF EXISTS family_facts; - --- --------------------------------- --- FACTS Table --- fid: fact id (incremental index) --- nid: node id. NULL if it is not a node --- fact_code: a code of 3-4 letters that define the fact type. It uses the standard GEDCOM tag types --- (e.g. INDI, BIRT, NAME, SEX, PLAC). We might define more options for our needs. --- xref: XREF_ID record identifier from GEDCOM --- fact_value: The value of the fact, depend on fact_code. e.g. a name for NAME fact or a date for a date fact. --- gedcom_source: The GEDCOM source code that defines the fact, typically a single line. --- --------------------------------- -CREATE TABLE family_facts ( - fid INT(10) UNSIGNED NOT NULL PRIMARY KEY, - nid INT(10) UNSIGNED, - fact_code varchar(4), - xref varchar(20), - valuefact_value varchar(255), - gedcom_source TEXT -) TYPE = MyISAM; - --- -------------------------------------------------------------------------- -- --- --------------------------------- --- RELATIONS Table --- rid: relation id (incremental index) --- fid1, fid2: fact id's of the two facts --- relation_description: the relation of fid1 to fid2 --- gedcom_source: The GEDCOM source code that defines the relation, typically a single line. --- Used only for a code that only defines a relation, such as CHIL,WIFE,HUSB. --- Otherwise the source code is included in the fact record. --- --------------------------------- --- -CREATE TABLE family_relations ( - rid INT(10) UNSIGNED PRIMARY KEY, - fid1 INT(10) UNSIGNED NOT NULL, - fid2 INT(10) UNSIGNED NOT NULL, - relation_description TEXT, - gedcom_source TEXT, - FOREIGN KEY (fid1) REFERENCES facts(fid), - FOREIGN KEY (fid2) REFERENCES facts(fid) -) TYPE = MyISAM; --- -------------------------------------------------------------------------- -- - diff -urN family/import.inc drupal/modules/family/import.inc --- family/import.inc 2006-04-30 19:59:21.000000000 -0500 +++ drupal/modules/family/import.inc 2007-10-01 14:21:38.741709500 -0500 @@ -11,49 +11,142 @@ //Generate a form for uploading a GEDCOM file function family_import() { - $content = '

Caution: this will delete any current content from family '. - 'database

'. - ''. - 'Choose a file to upload:
'. - '

'; - return $content; + return drupal_get_form('family_import_form'); +} + +function family_import_form() { + $form['#attributes'] = array('enctype' => "multipart/form-data"); + $form['gedcom_file'] = array( + '#type' => 'file', + '#title' => t('GED file to upload'), + '#size' => 40, + ); + //$form['merge'] = array( + // '#type' => 'radios', + // '#title' => t('Merge options'), + // '#options' => array(t('replace existing data'), t('augment current data'), t('merge individuals by name')), + // '#default_value' => variable_get('family_import_replace', 1), + //); + + $form['range'] = array( + '#type' => 'fieldset', + '#title' => t('Import range'), + '#description' => t('Select a range of records (lines staring with 0) to import. This allows breaking very large files into multiple import sessions.'), + ); + $form['range']['start'] = array( + '#type' => 'textfield', + '#title' => t('First record to import'), + '#size' => 10, + '#maxlength' => 10, + '#description' => t('Enter the number of the first record in the GEDCOM file to include in this import session'), + ); + $form['range']['nrecords'] = array( + '#type' => 'textfield', + '#title' => t('Number of records to import'), + '#size' => 10, + '#maxlength' => 10, + '#description' => t('Enter the number of records to process in this import session'), + ); + $form['replace'] = array( + '#type' => 'checkbox', + '#title' => t('Replace existing GED data'), + '#default_value' => variable_get('family_import_replace', 1), + ); + $form['submit'] = array('#type' => 'submit', '#value' => t('Start Import')); + return $form; +} + +// Check the uploaded GEDCOM file +function family_import_form_validate($form_id, $form_values) { + $file = file_check_upload('gedcom_file'); + if (!$file) { + form_set_error('',t("Didn't get GED file")); + } + + $fp = fopen($file->filepath , "r" ); + if (!$fp) { + form_set_error('',t("Couldn't open get GED file")); + } + fclose($fp); } // Parse the uploaded GEDCOM file -function family_import_done() { - $content="

"; - $fp = fopen( $_FILES['gedcom_file']['tmp_name'] , "r" ); +function family_import_form_submit($form_id, $form_values) { + $file = file_check_upload('gedcom_file'); + if (!$file) { + form_set_error('',t("Didn't get GED file")); + } + + $fp = fopen($file->filepath , "r" ); if (!$fp) { - return "

Couldn't open the data file.

"; + form_set_error('',t("Couldn't open get GED file")); } - $gedcom_hier=array(); // References to GEDCOM parents on each level -//Empty current content. This is useful for debugging, but more caution should be -//done before deleting database in the working version - db_query("TRUNCATE family_facts"); - db_query("TRUNCATE family_relations"); + + // + // Empty current content. This is useful for debugging, but more caution should be + // done before deleting database in the working version + // + if ($form_values['replace']) + { + db_query("TRUNCATE {family_facts}"); + db_query("TRUNCATE {family_relations}"); + + $q = db_query("select nid from {node} where type = 'family_individual'"); + $n = 0; + while ($o = db_fetch_object($q)) { + node_delete($o->nid); + $n++; + } + drupal_set_message(t('Deleted @n family_individual nodes.', array('@n' => $n))); + } - while (!feof ($fp)) { + $rmin = $form_values['start']? $form_values['start']:0; + $rcount = $form_values['nrecords']? $form_values['nrecords']:99999999; + $rmax = $rmin + $rcount - 1; + $rnum = 0; + $rprocessed = 0; + $lprocessed = 0; + + $lnum = 0; + $gedcom_hier=array(); // References to GEDCOM parents on each level + + while (!feof ($fp)) + { $gedline = fgets( $fp, 1024 ); - if (preg_match("/^\s*(\d+)\s*(?:@([^@]+)@)?\s*(\S+)\s*(.*\S)?\s*$/i", $gedline , $matches)) { - + $lnum++; + + if (preg_match("/^\s*(\d+)\s*(?:@([^@]+)@)?\s*(\S+)\s*(.*\S)?\s*$/i", $gedline , $matches)) + { $level=$matches[1]; + if ($level == 0) ++$rnum; + if ($rnum < $rmin) continue; + if ($rnum > $rmax) break; + if ($level == 0) ++$rprocessed; + ++$lprocessed; $xref=$matches[2]; $fact_code=$matches[3]; $value=$matches[4]; $gedcom_source=$gedline; $parent=$gedcom_hier[$level-1]; - if (strpos(";HUSB;WIFE;CHIL",$fact_code)) { //Lines that define only a relation + if (strpos(";HUSB;WIFE;CHIL",$fact_code)) + { + // + // Lines that define only a relation + // $gedcom_hier[$level]=NULL; preg_match("/@\s*([^@\s]+)\s*@/i", $gedline , $matches); $target_xref=$matches[1]; $fid = db_result(db_query("SELECT fid FROM {family_facts} WHERE xref = '%s'", $target_xref)); $relation=$fact_code; } - else { //lines that define a fact -//Add fact, meanwhile every INDI fact gets a node + else + { + // + // Lines that define a fact + // Every INDI fact gets a node + // $fid = db_next_id('{family_facts}_fid'); $nid=NULL; $relation="FACT"; @@ -67,8 +160,6 @@ $node->moderate = 0; $node->comment = 2; $node->revision = 0; -// $node->created = mktime($date[0]->hour, $date[0]->minute, $date[0]->second, $date[0]->month, $date[0]->day, $date[0]->year); -// $node->changed = mktime($date[1]->hour, $date[1]->minute, $date[1]->second, $date[1]->month, $date[1]->day, $date[1]->year); $node->fid = $fid; $node->xref = $xref; $node->value = $value; @@ -78,9 +169,12 @@ $error['access'] = message_access(); } if ($error) { - print '
';
-            print_r($error);
-            print '
'; + drupal_set_message( + t( + 'Error at line @lnum of GED (@line): @error.', + array('@lnum' => $lnum, '@line' => $gedline, '@error' => print_r($error,true)) + ) + ); } else { node_save($node); @@ -88,15 +182,15 @@ } unset($node); } - else { - -// Add fact that is not a node - db_query("INSERT INTO {family_facts} (fid, nid, xref, fact_code, fact_value, - gedcom_source) VALUES (%d, %d, '%s', '%s', '%s', '%s')", $fid, - $nid, $xref,$fact_code,$value,$gedcom_source); - } + else + { + db_query("INSERT INTO {family_facts} (fid, nid, xref, fact_code, fact_value, gedcom_source) + VALUES (%d, %d, '%s', '%s', '%s', '%s')", + $fid, $nid, $xref,$fact_code,$value,$gedcom_source); + } $gedcom_hier[$level] = $fid; $gedcom_source=null; // If the source is in fact table, no need to put it again in relation table + $nchanged++; } if ($level>0) { @@ -108,28 +202,33 @@ } } fclose ($fp); - //Clean it all up - $fids = db_query("SELECT fid FROM family_facts WHERE fact_code = 'INDI'"); - while ($fid = db_fetch_array($fids)) { - family_import_cleanup_indinode($fid['fid']); - } - $content.="

"; -// print theme('page', $content); //uncomment for drupal 4.6 - return $content; //This supports drupal 4.7 -} - -function family_import_cleanup_indinode($fid) { - $nid = db_result(db_query("SELECT nid FROM {family_facts} WHERE fid= %d", $fid)); - $vid = db_result(db_query("SELECT vid FROM {node} WHERE nid= %d", $nid)); - //UPDATE NAME - $name = family_get_subvalue_by_code('NAME', $fid); - $name=str_replace("/","",$name); //Remove slashes arround surname - $node=node_load($nid); - $node->title = $name; - node_save(&$node); -// db_query("UPDATE {node} SET title = '%s' WHERE nid = %d", $name, $nid); - //Update node_revisions - title -// db_query( "UPDATE {node_revisions} SET title = '%s' WHERE vid = %d", -// $name, $vid ); + + drupal_set_message(t('Processed @r records (@n lines) of GED.', array('@r' => $rprocessed, '@n' => $lprocessed))); + if ($rnum > $rmax) drupal_set_message(t('Next start record: @r.', array('@r' => $rmax + 1))); + else drupal_set_message(t('No more records to process')); + + // + // Clean up: add appropriate Titles to nodes based on the NAME fact + // -- An alternative to this is to cache created nodes until we + // have added all the family_facts. That has the benefit of + // being easy to roll back if there are errors in the GED file + // but it uses more ram for a big import. I may try this if + // my big GED still takes too long to import... --pf + // + $fids = db_query("SELECT f1.fid as fid, f1.nid as nid, f2.fact_value as name ". + "FROM {family_facts} f1, {family_facts} f2, {family_relations} r ". + "WHERE f1.fact_code = 'INDI' AND f2.fact_code='NAME' AND f2.fid=r.fid1 AND r.fid2=f1.fid"); + while ($row = db_fetch_array($fids)) { + $name=str_replace("/","",$row['name']); //Remove slashes arround surname + + //UPDATE NAME + $node=node_load($row['nid']); + if ($node->title != $name) + { + $node->title = $name; + node_save($node); + } + } + return 'family'; } diff -urN family/individual.inc drupal/modules/family/individual.inc --- family/individual.inc 2006-04-30 19:59:21.000000000 -0500 +++ drupal/modules/family/individual.inc 2007-10-01 17:11:57.361702000 -0500 @@ -16,10 +16,13 @@ * Implementation of hook_form(). */ function family_individual_form(&$node) { - + $form['title'] = array( + '#type' => 'hidden', + '#default_value' => $node->NAME, + ); $form['GIVN'] = array( '#type' => 'textfield', - '#title' => t('Name'), + '#title' => t('Given Name'), '#default_value' => $node->GIVN, '#required' => FALSE, ); @@ -96,6 +99,7 @@ //INSERT NAME if (($node->SURN or $node->GIVN)) { $node->NAME = $node->GIVN . ' /' . $node->SURN . '/'; + $node->title = $node->GIVN . ' ' . $node->SURN; } if ($node->NAME) { $name_fid = family_insert_fact('NAME', $name); @@ -138,11 +142,6 @@ family_insert_relation($fid, $deat_fid, 'FACT'); } } - //Create a node title using full name formed above -// db_query("UPDATE {node} SET title = '%s' WHERE nid = %d", $name, $node->nid); - //Update node_revisions - title and format -// db_query( "UPDATE {node_revisions} SET title = '%s', format = 1 WHERE vid = %d", -// $name, $node->vid ); } /** @@ -219,13 +218,14 @@ //Create a node title using full name $node->title = str_replace("/","",$node->NAME); //UPDATE GENDER + drupal_set_message("gender '".$node->SEX."'"); if (($node->SEX=='F') or ($node->SEX=='M')) { if ($gender_fid = family_get_subfid_by_code("SEX", $node->fid)) { family_update_fact($gender_fid, $node->SEX); } else{ $fid = family_insert_fact('SEX', $node->SEX); - family_insert_relation($fid, $ind_fid, 'FACT'); + family_insert_relation($fid, $node->fid, 'FACT'); } } elseif ($gender_fid = family_get_subfid_by_code("SEX", $node->fid)) { @@ -306,10 +306,22 @@ function family_individual_view(&$node, $teaser = FALSE, $page = FALSE) { $node = node_prepare($node, $teaser); // $fid = db_result(db_query("SELECT fid FROM {family_facts} WHERE nid= %d", $node->nid)); - $node->body = family_view_indi($node->fid) . $node->body; - $node->teaser = family_view_indi($node->fid) . $node->teaser; +// $node->body = family_view_indi($node->fid) . $node->body; +// $node->teaser = family_view_indi($node->fid) . $node->teaser; + $node->content['family_individual_view'] = array( + '#value' => theme($teaser? 'family_individual_teaser':'family_individual_body', $node), + '#weight' => 0, + ); + + return $node; } +function theme_family_individual_teaser($node) { + return family_view_indi($node->fid); +} +function theme_family_individual_body($node) { + return family_view_indi($node->fid); +} /** * Implementation of hook_delete(). */ diff -urN family/view.inc drupal/modules/family/view.inc --- family/view.inc 2006-04-30 19:59:21.000000000 -0500 +++ drupal/modules/family/view.inc 2007-10-01 17:31:24.317769500 -0500 @@ -1,10 +1,8 @@ "; - $output .= $text; - $output .= ""; + $nid = db_result(db_query("SELECT nid FROM {family_facts} WHERE fid = %d", $fid)); + $output = l($text,"node/$nid"); return $output; } @@ -38,9 +36,9 @@ 'First Name', 'Sex', 'Birth Date', - 'Birth Place', + //'Birth Place', 'Death Date', - 'Death Place' + //'Death Place' ); $facts = array(); if ($indilist=='ALL') { @@ -74,9 +72,9 @@ 'GIVN' => family_make_link($fid, $firstname), 'SEX' => family_make_link($fid, $personfact['SEX']['value']), 'BIRTD' => family_make_link($fid, $birthfacts['DATE']['value']), - 'BIRTP' => family_make_link($fid, $birthfacts['PLAC']['value']), + //'BIRTP' => family_make_link($fid, $birthfacts['PLAC']['value']), 'DEATD' => family_make_link($fid, $deathfacts['DATE']['value']), - 'DEATP' => family_make_link($fid, $deathfacts['PLAC']['value']) + //'DEATP' => family_make_link($fid, $deathfacts['PLAC']['value']) ); } elseif ($indilist != 'ALL') { //Avoid listing all the private in the global listing @@ -85,15 +83,15 @@ 'GIVN' => '', 'SEX' => '', 'BIRTD' => '', - 'BIRTP' => '', + //'BIRTP' => '', 'DEATD' => '', - 'DEATP' => '' + //'DEATP' => '' ); } } } - $output .= theme('table', $header, $facts); + $output .= '
'.theme('table', $header, $facts)."
"; return $output; }