'admin/settings/signup_status/cert', 'title' => t('Certificates'), 'callback' => 'drupal_get_form', 'callback arguments' => array('signup_status_cert_admin_form'), 'type' => MENU_LOCAL_TASK, ); $items[] = array( 'path' => 'signup_status_cert', 'title' => t('Print certificate'), 'callback' => 'drupal_get_form', 'callback arguments' => array('signup_status_cert_search_form'), 'type' => MENU_CALLBACK, 'access' => TRUE, ); } else { if (arg(0) == 'node' && is_numeric(arg(1)) && db_num_rows(db_query("SELECT nid FROM {signup} WHERE nid = %d", arg(1)))) { global $user; $nid = arg(1); $cid = variable_get('signup_status_cert_cid', 1); $status = signup_status_user_signup_status($user->uid, $nid); $access = $status == $cid ? TRUE : FALSE; $items[] = array( 'path' => 'node/'. arg(1) .'/signups/mycert', 'title' => t('My certificate'), 'callback' => 'signup_status_cert_print', 'callback arguments' => array($user->uid, $nid, NULL), 'access' => $access, 'type' => MENU_CALLBACK, ); } } return $items; } /** * Menu callback: provides module administration form. */ function signup_status_cert_admin_form() { $form = array(); $options = array(); $codes = signup_status_codes(); foreach ($codes as $cid => $code) { $options[$cid] = $code['name']; } $form['signup_status_cert_template_type'] = array( '#type' => 'select', '#title' => t('Certificate template node type'), '#options' => node_get_types('names'), '#default_value' => variable_get('signup_status_cert_template_type', NULL), '#description' => t('The body field from the chosen node type will be used as a template for certificates.'), ); $form['signup_status_cert_cid'] = array( '#type' => 'select', '#title' => t('Status code that grants certificate'), '#options' => $options, '#default_value' => variable_get('signup_status_cert_cid', 1), '#description' => t('When user signups are transitioned to this status code, the user will be granted a certificate of completion for the signup.'), ); if (module_exists('dompdf')) { $sizes = array("2a0" , "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10" , "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10" , "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10", "ra0", "ra1", "ra2", "ra3", "ra4", "sra0" , "sra1" , "sra2" , "sra3" , "sra4" , "letter", "legal", "ledger", "tabloid", "executive", "folio", "commerical #10 envelope", "catalog #10 1/2 envelope", "8.5x11", "8.5x14", "11x17"); $form['signup_status_cert_pdf_size'] = array( '#type' => 'select', '#title' => t('Default PDF Size'), '#options' => drupal_map_assoc($sizes), '#default_value' => variable_get('signup_status_cert_pdf_size', 'letter'), ); $form['signup_status_cert_pdf_orientation'] = array( '#type' => 'select', '#title' => t('Default PDF Orientation'), '#options' => array( 'landscape' => t('Landscape'), 'portrait' => t('Portrait'), ), '#default_value' => variable_get('signup_status_cert_pdf_orientation', 'landscape'), ); } return system_settings_form($form); } /** * Implementation of hook_form_alter */ function signup_status_cert_form_alter($form_id, &$form) { switch ($form_id) { case $form['type']['#value'] .'_node_form' && isset($form['signup']): signup_status_cert_alter_node_form($form_id, $form); break; case variable_get('signup_status_cert_template_type', NULL) .'_node_form': signup_status_cert_alter_template_form($form_id, $form); break; } } /** * Alter the node edit form */ function signup_status_cert_alter_node_form($form_id, &$form) { $options = signup_status_cert_template_options(); $node = $form['#node']; $form['signup_status_cert_nid'] = array( '#type' => 'select', '#title' => t('Certificate template'), '#options' => $options, '#default_value' => $node->signup_status_cert_nid, '#description' => t('The template to use when granting certificates to users.'), ); } /** * Implementation of hook_nodeapi() */ function signup_status_cert_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { // load if ($op == 'load' && $node->signup) { $node->signup_status_cert_nid = signup_status_cert_get_cert_nid($node); } // submit if ($op == 'submit' && $node->signup_status_cert_nid) { signup_status_cert_set_cert_nid($node); } // view if ($op == 'view' && $node->signup) { signup_status_cert_nodeapi_view($node); } } /** * Perform 'view' op of hook_nodeapi */ function signup_status_cert_nodeapi_view(&$node) { global $user; $cert_id = signup_status_cert_user_cert_id($user->uid, $node->nid); if ($cert_id) { $output = theme('signup_status_cert_user_node_view', $node); $node->content['signup_status_cert'] = array( '#value' => $output, '#weight' => 9, ); } } /** * Used to add a link to print the user's certificate to the given node. * * @param $node * The node object. * @return * A themed list of links. */ function theme_signup_status_cert_user_node_view($node) { $output = "

". t('Print your certificate') ."

"; $links = array(); $links['signup_status_cert_html'] = array( 'title' => t('HTML'), 'href' => 'node/'. $node->nid .'/signups/mycert', ); if (module_exists('dompdf')) { $links['signup_status_cert_pdf'] = array( 'title' => t('PDF'), 'href' => 'node/'. $node->nid .'/signups/mycert/pdf', ); } $output .= theme('links', $links, array('class' => 'links inline')); return $output; } /** * Set the certificate node id reference for the given node. * * @param $node * The node object to be updated, which must include the property * 'signup_status_cert_nid'. */ function signup_status_cert_set_cert_nid($node) { db_query("UPDATE {signup} SET signup_status_cert_nid = %d WHERE nid = %d", $node->signup_status_cert_nid, $node->nid); } /** * Get the certificate node id reference for the given node * * @param @node * The node for which to retrieve the certificate node id. * @return * A node id or NULL, if none is found. */ function signup_status_cert_get_cert_nid($node) { return db_result(db_query("SELECT signup_status_cert_nid FROM {signup} WHERE nid = %d", $node->nid)); } /** * Get an array of certificates in a format usable in a form select box. * * @return * An array of certificate node titles, keyed on the node id. */ function signup_status_cert_template_options() { $type = variable_get('signup_status_cert_template_type', NULL); $options = array(); if ($type) { $sql = "SELECT nid, title FROM {node} WHERE type = '%s' AND status = 1"; $result = db_query($sql, $type); while ($row = db_fetch_object($result)) { $options[$row->nid] = $row->title; } } return $options; } /** * Alter the node edit form for certificate template nodes. * * Adds a rollout of available tokens for use when creating the certificate * node. */ function signup_status_cert_alter_template_form($form_id, &$form) { $patterns = token_get_list('node'); $tags = signup_status_cert_tags('all'); $doc = ''; $form['body_filter']['body']['#weight'] = 0; $form['body_filter']['body']['#description'] = t('See the "Available tokens and directives" rollout for a complete list of available patterns that can be used in the template.'); if (count($tags)) { $doc .= "". t('Certificate-specific tags / directives') .""; $doc .= "
"; foreach ($tags as $tag => $info) { $doc .= "
[[". $tag ."]"; if (isset($info['args'])) { foreach ($info['args'] as $name => $description) { $doc .= "[". $name .":value]"; } } $doc .= "]
"; $doc .= "
". $info['description']; if (isset($info['args'])) { $doc .= "
". t('Arguments') ."
"; $doc .= "
"; foreach ($info['args'] as $name => $description) { $doc .= "
". $name ."
"; $doc .= "
". $description ."
"; } $doc .= "
"; } $doc .= "
"; } $doc .= "
"; } foreach ($patterns as $grouping => $tokens) { $doc .= "". $grouping .""; $doc .= "
\n"; foreach ($tokens as $name => $description) { $doc .= '
['. $name .']
'; $doc .= '
'. $description .'
'; } $doc .= "
\n"; } $form['body_filter']['help'] = array( '#type' => 'fieldset', '#title' => t('Available tokens and directives'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => 1, '#description' => $doc, ); $form['body_filter']['format']['#weight'] = 2; } /** * Returns a list of possible certificate-specific tags that users can enter * into templates. The tag key is converted to a string of the format: * [[tag-key][arg-key:value]] * * @return Array * An array of the format: * array( * 'tag-key' => array( * 'description' => t('Tag description / help'), * 'callback' => 'callback_name', * 'args' => array( * 'arg-key' => t('Description of argument), * ) * ), * ) */ function signup_status_cert_tags($op = 'generate') { $tags = array(); if ($op == 'all' || $op == 'after generate') { $tags['signup-status-cert-redirect'] = array( 'description' => t('Redirect the user to a URL instead of delivering a certificate. This is useful, for example, to redirect the user to a specific PDF file or web page'), 'args' => array( 'url' => t('The URL to redirect the user to.'), ), 'callback' => 'signup_status_cert_redirect', ); } if ($op == 'all' || $op == 'generate') { $tags['signup-status-cert-set_paper'] = array( 'description' => t('The page orientation of the printed certificate.'), 'args' => array( 'size' => t('The paper size. i.e. letter'), 'orientation' => t('The orientation of the printed page. i.e. portrait, landscape.'), ), 'callback' => 'signup_status_cert_set_paper', ); } return $tags; } /** * Implementation of hook_update_signup_status. * * Grants the user a certificate, if they have been transferred to the state * that grants it. */ function signup_status_cert_update_signup_status($uid, $nid, $curr_cid, $new_cid, $anon_mail = NULL) { if ($new_cid == variable_get('signup_status_cert_cid', 1)) { $curr_cert_id = signup_status_cert_user_cert_id($uid, $nid, $anon_mail); if (!$curr_cert_id) { signup_status_cert_grant_cert($uid, $nid, $anon_mail); } } } /** * Provide a form for locating and outputting a certificate, based on the * certificate id. * * @param $cert_id * The certificate id. If not NULL, and a certificate is found matching * the given id, the certificate is sent to the user's browser * @param $format * The output format. Can be 'html' or, if dompdf is installed, 'pdf' * @return * A certificate, if the form was submitted, or a form array for submitting * and locating a certificate. */ function signup_status_cert_search_form($cert_id = NULL, $format = 'html') { if (user_access('print any signup certificate')) { if ($cert_id) { $result = db_query("SELECT * FROM {signup_log} WHERE cert_id = %d", $cert_id); if (db_num_rows($result)) { $signup = db_fetch_object($result); signup_status_cert_print($signup->uid, $signup->nid, $signup->anon_mail, $format); exit(); } else { drupal_set_message(t('No certificate found with that certificate number.')); drupal_goto('signup_status_cert'); } } else { $form = array(); $form['help'] = array('#value' => t('If you would like to print or verify a certificate, please enter the certificate number below and it will be retrieved.')); $form['cert_id'] = array( '#type' => 'textfield', '#title' => t('Certificate number'), '#description' => t('The certificate number. This number is located on the certificate.'), ); if (module_exists('dompdf')) { $form['output'] = array( '#type' => 'select', '#title' => t('Output'), '#options' => array( 'html' => t('HTML'), 'pdf' => t('PDF'), ), ); } $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), ); return $form; } } } /** * Submit handler for signup_status_cert_search_form */ function signup_status_cert_search_form_submit($form_id, $form_values) { drupal_goto('signup_status_cert/'. $form_values['cert_id'] .'/'. $form_values['output']); } /** * Grant a course certificate to a user. * * @param $uid * The uid of the user * @param $nid * The node id to grant the certificate for. * @param $anon_mail * The anon_mail attribute of the signup, if it was anonymous. */ function signup_status_cert_grant_cert($uid, $nid, $anon_mail = NULL) { // We fudge the use of db_next_id, which should refer to an actual table, // but here we use it to handle tracking the certificate IDs. $cert_id = db_next_id('{signup_status}_cert_id'); $sql = "UPDATE {signup_log} SET completion_time = %d, cert_id = %d WHERE uid = %d AND nid = %d AND anon_mail = '%s'"; db_query($sql, time(), $cert_id, $uid, $nid, $anon_mail); } /** * Get the certificate id for a user's signup to a course. * * @param $uid * The uid for the user. * @param $nid * The nid for the node. * @param $anon_mail * The email address provided by the user when she registered, if applicable. */ function signup_status_cert_user_cert_id($uid, $nid, $anon_mail = NULL) { return db_result(db_query("SELECT cert_id FROM {signup_log} WHERE uid = %d AND nid = %d AND anon_mail = '%s'", $uid, $nid, $anon_mail)); } /** * Implementation of hook_signup_status_operations * * Provides operations for printing certificates as HTML or PDF */ function signup_status_cert_signup_status_operations() { $operations['signup_status_cert'] = array( 'label' => t('Print Certificate(s)'), 'callback' => 'signup_status_cert_bulk_print', ); if (module_exists('dompdf')) { $operations['signup_status_cert_pdf'] = array( 'label' => t('Print Certificate(s) to PDF'), 'callback' => 'signup_status_cert_bulk_print', 'callback arguments' => array('pdf'), ); } return $operations; } /** * Print certificates for a bulk set of users for a node * * @param $users * An array of arrays of the format: * array('uid' => $uid, 'anon_mail' => $mail) * @param $nid * The node id to print the certificates for. * @param $format * The format to output the certificates in. Can be either 'html' (the * default) or 'pdf', if the dompdf module is installed. */ function signup_status_cert_bulk_print($users, $nid, $format = 'html') { global $_signup_status_cert_pdf_size, $_signup_status_cert_pdf_orientation; $node = node_load($nid); $cid = variable_get('signup_status_cert_cid', 1); $accounts = signup_status_users_to_accounts($users); foreach ($accounts as $account) { $status = signup_status_user_signup_status($account['uid'], $nid, $account['anon_mail']); if ($status == $cid) { $output .= signup_status_cert_generate_cert($nid, $account['uid'], $account['anon_mail']); } } $output = theme('signup_status_cert_page', $output, $node); if ($format == 'html') { print $output; exit(); } if ($format == 'pdf' && module_exists('dompdf')) { $directives = array( 'set_paper' => array($_signup_status_cert_pdf_size, $_signup_status_cert_pdf_orientation), ); dompdf_stream_pdf($output, $filename, $directives); exit(); } } /** * Print the certificate for an individual user. * * @param $uid * The uid of the user for whom to print the certificate. * @param $nid * The node id to print the certificates for. * @param $format * The format to output the certificates in. Can be either 'html' (the * default) or 'pdf', if the dompdf module is installed. */ function signup_status_cert_print($uid, $nid, $anon_mail = NULL, $format = 'html') { global $_signup_status_cert_pdf_size, $_signup_status_cert_pdf_orientation; $node = node_load($nid); $tag_results_before = signup_status_process_tags($original, 'before generate'); $output = signup_status_cert_generate_cert($nid, $uid, $anon_mail); $tag_results_after = signup_status_process_tags($original, 'after generate'); $output = theme('signup_status_cert_page', $output, $node); if ($format == 'html') { print $output; exit(); } if ($format == 'pdf' && module_exists('dompdf')) { $filename = substr(check_url($node->title), 0, 16) .'.'. t('certificate') .'.'. format_date(time(), 'custom', "Ymd") .'.pdf'; $directives = array( 'set_paper' => array($_signup_status_cert_pdf_size, $_signup_status_cert_pdf_orientation), ); dompdf_stream_pdf($output, $filename, $directives); exit(); } } /** * Render a page of HTML containing the certificate. * * @param $content * The HTML-formatted content of the certificate. * @return * HTML */ function theme_signup_status_cert_page($content) { global $base_url; $module_path = $base_url .'/'. drupal_get_path('module', 'signup_status_cert'); $header = ''; $body = ''; $header_contents = module_invoke_all('signup_status_cert_content', $node); foreach ($header_contents as $header_content) { $header .= $header_content; } $title = t('Certificate for !title', array('!title' => check_plain($node->title))); if ($header) { $body .= '
'. $header ."
\n\n"; } $body .= $content; $html = "\n"; $html .= ''; $html .= "\n". $title ."\n"; $html .= ''; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n\n". $body ."\n\n\n"; return $html; } /** * Generate a certificate for the given user's signup for the given node. * * @param $nid * The nid of the node. * @param $uid * The uid of the user. * @param $anon_mail * The anon_mail attribute of the signup, if it was anonymous. */ function signup_status_cert_generate_cert($nid, $uid, $anon_mail = NULL) { $node = node_load($nid); $template_node = node_load($node->signup_status_cert_nid); $original = $template_node->body; $tag_results = signup_status_process_tags($original, 'generate'); $options = array( 'signup-status-uid' => $uid, ); $full = token_get_values('node', $node, TRUE, $options); $modified = _token_replace_tokens($original, $full->tokens, $full->values, '[', ']'); return theme('signup_status_cert', $modified); } /** * $op can be 'before generate', 'generate', 'after generate' */ function signup_status_process_tags(&$text, $op = 'generate') { // Check for and process directives - i.e. redirect to a url, etc. $results = array(); $tags = signup_status_cert_tags($op); foreach ($tags as $tag => $info) { if (strstr($text, "[[". $tag ."]")) { // Parse the tag down to its pieces $fulltag = substr($text, strpos($text, $tag)); $fulltag = substr($fulltag, 0, strpos($text, "]]") -2); // remove the tag from the string, so it doesn't render $text = str_replace("[[". $fulltag ."]]", '', $text); $params = array(array_pop(explode("][", $fulltag))); $args = array(); foreach ($params as $param) { $keyval = explode(":", $param, 2); $args[$keyval[0]] = $keyval[1]; } $function = $info['callback']; if (function_exists($function)) { $results[$tag] = $function($args, $text); } } } return $results; } function signup_status_cert_set_paper($args) { global $_signup_status_cert_pdf_size, $_signup_status_cert_pdf_orientation; if (isset($args['orientation'])) { $_signup_status_cert_pdf_orientation = $args['orientation']; } if (isset($args['size'])) { $_signup_status_cert_pdf_size = $args['size']; } } /** * Redirect to a url. Basically just a wrapper for drupal_goto that lets us * use a convenient data format. */ function signup_status_cert_redirect($args) { $url = $args['url']; drupal_goto($url); } /** * Wrapper for the certificate (for page breaks, etc.) */ function theme_signup_status_cert($content) { $output = '
'; $output .= $content; $output .= '
'; return $output; } /** * Implementation of hook_token_values */ function signup_status_cert_token_values($type, $object = NULL, $options = array()) { $values = array(); switch ($type) { case 'node': $node = $object; // Are we dealing with a signup status token replacement? if ($options['signup-status-uid'] && $node->signup) { // Get the user's signup status info $account = user_load(array('uid' => $options['signup-status-uid'])); $signup = db_fetch_object(db_query("SELECT * FROM {signup_log} WHERE nid = %d AND uid = %d", $node->nid, $account->uid)); $data = unserialize($signup->form_data); $codes = signup_status_codes(); $values = array( 'signup-status-cert-user-id' => $account->uid, 'signup-status-cert-user-name' => $account->name, 'signup-status-cert-extra-name' => $data['Name'] ? $data['Name'] : $account->name, 'signup-status-cert-status' => $codes[$signup->status]['name'], 'signup-status-cert-id' => $signup->cert_id, 'signup-status-cert-completed-yyyy' => date('Y', $signup->completion_time), 'signup-status-cert-completed-yy' => date('y', $signup->completion_time), 'signup-status-cert-completed-month' => date('F', $signup->completion_time), 'signup-status-cert-completed-mon' => date('M', $signup->completion_time), 'signup-status-cert-completed-mm' => date('m', $signup->completion_time), 'signup-status-cert-completed-m' => date('n', $signup->completion_time), 'signup-status-cert-completed-ww' => date('W', $signup->completion_time), 'signup-status-cert-completed-date' => date('N', $signup->completion_time), 'signup-status-cert-completed-day' => date('l', $signup->completion_time), 'signup-status-cert-completed-ddd' => date('D', $signup->completion_time), 'signup-status-cert-completed-dd' => date('d', $signup->completion_time), 'signup-status-cert-completed-d' => date('j', $signup->completion_time), ); // Process profile fields $fields = signup_status_cert_get_profile_fields(); foreach ($fields as $fid => $field) { $property = $field['name']; $values['signup-status-cert-user-profile-'. $fid] = $account->$property; } } break; } return $values; } /** * Implementation of hook_token_list() */ function signup_status_cert_token_list($type = 'all') { if ($type == 'node') { $key = t('Signup Status Certificates'); $tokens[$key] = array( 'signup-status-cert-user-id' => t('The user ID of the user for which the signup certificate is being generated'), 'signup-status-cert-user-name' => t('The username of the user'), 'signup-status-cert-extra-name' => t('The "Name" field from the signup form for the user'), 'signup-status-cert-status' => t('The user\'s current signup status'), 'signup-status-cert-id' => t('The user\'s certificate ID'), 'signup-status-cert-completed-yyyy' => t('User signup completion year (four digit)'), 'signup-status-cert-completed-yy' => t('User signup completion year (two digit)'), 'signup-status-cert-completed-month' => t('User signup completion month (full word)'), 'signup-status-cert-completed-mon' => t('User signup completion month (abbreviated)'), 'signup-status-cert-completed-mm' => t('User signup completion month (two digit, zero padded)'), 'signup-status-cert-completed-m' => t('User signup completion month (one or two digit)'), 'signup-status-cert-completed-ww' => t('User signup completion week (two digit)'), 'signup-status-cert-completed-date' => t('User signup completion date (day of month)'), 'signup-status-cert-completed-day' => t('User signup completion day (full word)'), 'signup-status-cert-completed-ddd' => t('User signup completion day (abbreviation)'), 'signup-status-cert-completed-dd' => t('User signup completion day (two digit, zero padded)'), 'signup-status-cert-completed-d' => t('User signup completion day (one or two digit)'), ); // Add profile fields $fields = signup_status_cert_get_profile_fields(); foreach ($fields as $fid => $field) { $tokens[$key]['signup-status-cert-user-profile-'. $fid] = t('The value of the user\'s %category: %title profile field', array('%category' => $field['category'], '%title' => $field['title'])); } return $tokens; } } function signup_status_cert_get_profile_fields() { $fields = array(); $result = db_query("SELECT fid, title, name, category FROM {profile_fields} ORDER BY fid"); while ($row = db_fetch_object($result)) { $fields[$row->fid] = array( 'name' => $row->name, 'title' => $row->title, 'category' => $row->category, ); } return $fields; } /** * Implementation of hook_views_tables_alter() */ function signup_status_cert_views_tables_alter(&$tables) { $tables['signup_log']['fields']['completion_time'] = array( 'name' => t('Signup: User: Completion time'), 'sortable' => true, 'handler' => views_handler_field_dates(), 'option' => 'string', ); $tables['signup_log']['fields']['cert_id'] = array( 'name' => t('Signup: User: Print certificate link'), 'option' => array( '#type' => 'select', '#options' => array( 'html' => t('As HTML'), 'pdf' => t('As PDF'), ), ), 'handler' => 'views_handler_field_signup_status_cert', ); $tables['signup_log']['filters']['cert_id'] = array( 'name' => t('Signup: User: Has certificate'), 'field' => 'cert_id', 'operator' => array('IS' => t('has certificate')), 'value' => array( '#type' => 'select', '#options' => array(FALSE => t('No'), TRUE => t('Yes')), ), 'handler' => 'views_handler_filter_signup_status_cert_id', ); $tables['signup_log']['filters']['completion_time'] = array( 'name' => t('Signup: User: Completion time'), 'operator' => 'views_handler_operator_gtlt', 'value' => views_handler_filter_date_value_form(), 'handler' => 'views_handler_filter_timestamp', 'option' => 'string', 'help' => t('Enter dates in the format: CCYY-MM-DD HH:MM:SS. Enter \'now\' to use the current time. You may enter a delta (in seconds) to the option that will be added to the time; this is most useful when combined with now. If you have the jscalendar module from jstools installed, you can use a popup date picker here.'), ); } /** * Handle display of the signup status certificate field */ function views_handler_field_signup_status_cert($fieldinfo, $fielddata, $value, $data) { if ($value) { return l(t('Print'), 'node/'. $data->nid .'/signups/mycert/'. $fielddata['options']); } else { return t('Incomplete'); } } /** * Handle filtering based on presence of signup status certificate id */ function views_handler_filter_signup_status_cert_id($op, $filter, $filterinfo, &$query) { $table = $filterinfo['table']; $column = $filterinfo['field']; $field = "$table.$column"; $value = $filter['value']; if ($value) { $query->ensure_table($table); $query->add_where("%s > 0", $field); } else { $query->ensure_table($table); $query->add_where("%s = $value", $field); } }