diff --git a/plugins/export_ui/site_verify.inc b/plugins/export_ui/site_verify.inc new file mode 100644 index 0000000..395c67a --- /dev/null +++ b/plugins/export_ui/site_verify.inc @@ -0,0 +1,179 @@ + 'site_verify', + 'access' => 'administer site configuration', + + 'menu' => array( + 'menu prefix' => 'admin/config/search', + 'menu item' => 'verifications', + 'menu title' => 'Verifications', + 'menu description' => 'Add, change or remove verifications for your site.', + ), + + 'title singular' => t('verification'), + 'title singular proper' => t('Verification'), + 'title plural' => t('verifications'), + 'title plural proper' => t('Verifications'), + + 'handler' => 'site_verify_ui', + + 'form' => array( + 'settings' => 'site_verify_export_ui_form', + 'submit' => 'site_verify_export_ui_form_submit', + ), + + 'export' => array( + 'admin_title' => 'label', + ), + ); +} + +/** + * Site Verify settings form. + */ +function site_verify_export_ui_form(&$form, &$form_state) { + $record = $form_state['item']; + + // Wrapper for AJAX callback. + $form['#prefix'] = '
'; + $form['#suffix'] = '
'; + + $engines = site_verify_get_engines(); + $options = array(); + foreach ($engines as $key => $engine) { + $options[$key] = $engine['name']; + } + asort($options); + + $form['engine'] = array( + '#type' => 'select', + '#title' => t('Search engine'), + '#options' => $options, + '#default_value' => $record->engine, + '#ajax' => array( + 'callback' => 'site_verify_export_ui_form_js', + 'wrapper' => 'verification-wrapper', + ), + ); + + $engine = isset($form_state['values']['engine']) ? $form_state['values']['engine'] : (!empty($record->engine) ? $record->engine : key($options)); + + $form['meta'] = array( + '#type' => 'textfield', + '#title' => t('Verification META tag'), + '#default_value' => $record->meta, + '#description' => t('This is the full meta tag provided for verification. Note that this meta tag will only be visible in the source code of your front page.', array('@frontpage' => url(''))), + '#element_validate' => $engines[$engine]['meta_validate'], + '#access' => $engines[$engine]['meta'], + '#maxlength' => NULL, + ); + + $form['file_upload'] = array( + '#type' => 'file', + '#title' => t('Upload an existing verification file'), + '#description' => t('If you have been provided with an actual file, you can simply upload the file.'), + '#access' => $engines[$engine]['file'], + ); + $form['file'] = array( + '#type' => 'textfield', + '#title' => t('Verification file'), + '#default_value' => $record->file, + '#description' => t('The name of the HTML verification file you were asked to upload.'), + '#element_validate' => $engines[$engine]['file_validate'], + '#access' => $engines[$engine]['file'], + ); + $form['file_contents'] = array( + '#type' => 'textarea', + '#title' => t('Verification file contents'), + '#default_value' => !empty($record->file_contents) ? $record->file_contents : t('This is a verification page.'), + '#element_validate' => $engines[$engine]['file_contents_validate'], + '#wysiwyg' => FALSE, + '#access' => $engines[$engine]['file_contents'], + ); + if (!variable_get('clean_url', 0)) { + drupal_set_message(t('Using verification files will not work if clean URLs are disabled.', array('@clean-urls' => url('admin/settings/clean-url'))), 'error', FALSE); + $form['file']['#disabled'] = TRUE; + $form['file_contents']['#disabled'] = TRUE; + $form['file_upload']['#disabled'] = TRUE; + } + + if ($engines[$engine]['file']) { + $form['#validate'][] = 'site_verify_validate_file'; + $form['#attributes'] = array('enctype' => 'multipart/form-data'); + } +} + +/** + * Ajax callback for Site Verify settings form. + */ +function site_verify_export_ui_form_js($form, $form_state) { + return $form; +} + +/** + * Validation callback; save the uploaded file and check file name uniqueness. + */ +function site_verify_validate_file($form, &$form_state) { + $values = &$form_state['values']; + + // Import the uploaded verification file. + $validators = array('file_validate_extensions' => array()); + if ($file = file_save_upload('file_upload', $validators, FALSE, FILE_EXISTS_REPLACE)) { + $contents = @file_get_contents($file->uri); + file_delete($file); + if ($contents === FALSE) { + drupal_set_message(t('The verification file import failed, because the file %filename could not be read.', array('%filename' => $file->filename)), 'error'); + } + else { + $values['file'] = $file->filename; + $values['file_contents'] = $contents; + //drupal_set_message(t('The verification file @filename was successfully imported.', array('@filename' => $file->filename))); + } + } + + if ($values['file']) { + $existing_file = db_query("SELECT svid FROM {site_verify} WHERE LOWER(file) = LOWER(:file) AND svid <> :svid", array(':file' => $values['file'], ':svid' => $values['svid']))->fetchField(); + if ($existing_file) { + form_set_error('file', t('The file %filename is already being used in another verification.', array('%filename' => $values['file']))); + } + } +} + +/** + * Submission callback; send form to the next step or save the verification. + */ +function site_verify_export_ui_form_submit(&$form, &$form_state) { + _site_verify_cache_clear(); +} + +/** + * Helper function; Clear front page caches and set the menu to be rebuilt. + */ +function _site_verify_cache_clear() { + cache_clear_all(url('', array('absolute' => TRUE)), 'cache_page'); + cache_clear_all(url(variable_get('site_frontpage', 'node'), array('absolute' => TRUE)), 'cache_page'); + variable_set('menu_rebuild_needed', TRUE); +} + +function site_verify_validate_meta_google($element, &$form_state) { + $value = strtolower(trim($element['#value'])); + if ($value != '' && !preg_match('%\A\Z%', $value)) { + form_error($element, t('Invalid verification meta tag.')); + } +} + +function site_verify_validate_page_google($element, &$form_state) { + $value = strtolower(trim($element['#value'])); + if ($value != '' && !preg_match('%\Agoogle[\da-f]+\.html\Z%', $value)) { + form_error($element, t('Invalid verification file.')); + } +} diff --git a/plugins/export_ui/site_verify_ui.class b/plugins/export_ui/site_verify_ui.class new file mode 100644 index 0000000..8543712 --- /dev/null +++ b/plugins/export_ui/site_verify_ui.class @@ -0,0 +1,82 @@ +{$this->plugin['export']['key']}; + + // Note: $item->type should have already been set up by export.inc so + // we can use it safely. + switch ($form_state['values']['order']) { + case 'disabled': + $this->sorts[$name] = empty($item->disabled) . $name; + break; + case 'title': + $this->sorts[$name] = $item->{$this->plugin['export']['admin_title']}; + break; + case 'name': + $this->sorts[$name] = $name; + break; + case 'storage': + $this->sorts[$name] = $item->type . $name; + break; + } + + $this->rows[$name]['data'] = array(); + $this->rows[$name]['class'] = !empty($item->disabled) ? array('ctools-export-ui-disabled') : array('ctools-export-ui-enabled'); + + $this->rows[$name]['data'][] = array('data' => check_plain("{$item->{$this->plugin['export']['admin_title']}} ({$name})"), 'class' => array('ctools-export-ui-title')); + $this->rows[$name]['data'][] = array('data' => $engines[$item->engine]['name'], 'class' => array('ctools-export-ui-engine')); + $this->rows[$name]['data'][] = array('data' => $item->meta ? '' . t('Yes') . '' : t('No'), 'class' => array('ctools-export-ui-meta')); + $this->rows[$name]['data'][] = array('data' => $item->file ? l($item->file, $item->file) : t('None'), 'class' => array('ctools-export-ui-file')); + $this->rows[$name]['data'][] = array('data' => check_plain($item->type), 'class' => array('ctools-export-ui-storage')); + $this->rows[$name]['data'][] = array('data' => theme('links', array('links' => $operations)), 'class' => array('ctools-export-ui-operations')); + } + + /** + * Provide the table header. + * + * If you've added columns via list_build_row() but are still using a + * table, override this method to set up the table header. + */ + function list_table_header() { + $header = array(); + + $header[] = array('data' => t('Title'), 'class' => array('ctools-export-ui-title')); + $header[] = array('data' => t('Engine'), 'class' => array('ctools-export-ui-engine')); + $header[] = array('data' => t('Meta tag'), 'class' => array('ctools-export-ui-meta')); + $header[] = array('data' => t('File'), 'class' => array('ctools-export-ui-file')); + $header[] = array('data' => t('Storage'), 'class' => array('ctools-export-ui-storage')); + $header[] = array('data' => t('Operations'), 'class' => array('ctools-export-ui-operations')); + + return $header; + } + + /** + * Callback to enable a page. + */ + function enable_page($js, $input, $item) { + _site_verify_cache_clear(); + return $this->set_item_state(FALSE, $js, $input, $item); + } + + /** + * Callback to disable a page. + */ + function disable_page($js, $input, $item) { + _site_verify_cache_clear(); + return $this->set_item_state(TRUE, $js, $input, $item); + } +} diff --git a/site_verify.info b/site_verify.info index 8f22950..ab7bc19 100644 --- a/site_verify.info +++ b/site_verify.info @@ -1,6 +1,7 @@ name = Site Verification description = "Verifies ownership of a site for use with search engines." core = 7.x +dependencies[] = ctools files[] = site_verify.module files[] = site_verify.admin.inc files[] = site_verify.install diff --git a/site_verify.install b/site_verify.install index a1177d5..58f7753 100644 --- a/site_verify.install +++ b/site_verify.install @@ -10,14 +10,33 @@ */ function site_verify_schema() { $schema['site_verify'] = array( + 'export' => array( + 'key' => 'svid', + 'key name' => 'Name', + 'primary key' => 'svid', + 'identifier' => 'verification', + 'default hook' => 'site_verify_defaults', + 'api' => array( + 'owner' => 'site_verify', + 'api' => 'site_verify', + 'minimum_version' => 1, + 'current_version' => 1, + ), + ), 'description' => '', 'fields' => array( 'svid' => array( - 'type' => 'serial', - 'unsigned' => TRUE, + 'type' => 'varchar', + 'length' => 128, 'not null' => TRUE, 'description' => 'Primary Key: Unique site verification ID.', ), + 'label' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), 'engine' => array( 'type' => 'varchar', 'length' => 32, @@ -89,6 +108,30 @@ function site_verify_update_3() { } /** + * Upgrade to CTools Exportable version of Site Verify. + */ +function site_verify_update_7000() { + // Update database schema. + db_drop_field('site_verify', 'svid'); + db_add_field('site_verify', 'svid', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => '', 'description' => 'Primary Key: Unique site verification ID.'), array('primary key' => array('svid'))); + db_add_field('site_verify', 'label', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => '')); + + $engines = site_verify_get_engines(); + $verifications = db_select('site_verify', 'sv') + ->fields('sv') + ->execute() + ->fetchAll(); + db_delete('site_verify')->execute(); + + // Re-create verification records. + foreach ($verifications as $delta => $verification) { + $verification->svid = "{$verification->engine}_{$delta}"; + $verification->label = "{$engines[$verification->engine]['name']}: {$delta}"; + drupal_write_record('site_verify', $verification); + } +} + +/** * Import data from XML sitemap engines. */ function site_verify_import_xmlsitemap() { diff --git a/site_verify.module b/site_verify.module index 3ae0ead..be134e8 100755 --- a/site_verify.module +++ b/site_verify.module @@ -1,4 +1,7 @@ 'Verifications', - 'description' => 'Add, change or remove verifications for your site.', - 'page callback' => 'site_verify_list', - 'access arguments' => array('administer site verify'), - 'file' => 'site_verify.admin.inc', - ); - // Add a verification record. - $items['admin/config/search/verifications/add'] = array( - 'title' => 'Add verification', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('site_verify_edit_form'), - 'access arguments' => array('administer site verify'), - 'file' => 'site_verify.admin.inc', - 'type' => MENU_LOCAL_ACTION, - ); - $items['admin/config/search/verifications/add/%site_verify_engine'] = array( - 'title' => 'Add', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('site_verify_edit_form', array(), 5), - 'access arguments' => array('administer site verify'), - 'file' => 'site_verify.admin.inc', - ); - $items['admin/config/search/verifications/%site_verify/edit'] = array( - 'page callback' => 'drupal_get_form', - 'page arguments' => array('site_verify_edit_form', 4), - 'access arguments' => array('administer site verify'), - 'file' => 'site_verify.admin.inc', - ); - $items['admin/config/search/verifications/%site_verify/delete'] = array( - 'page callback' => 'drupal_get_form', - 'page arguments' => array('site_verify_delete_form', 4), - 'access arguments' => array('administer site verify'), - 'file' => 'site_verify.admin.inc', - ); + $items = array(); // Add the verification paths. - $verifications = db_query("SELECT svid, file FROM {site_verify} WHERE file <> ''")->fetchAll(); - foreach ($verifications as $verification) { - $items[$verification->file] = array( - 'page callback' => 'site_verify_output', - 'page arguments' => array((string) $verification->svid), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); + ctools_include('export'); + foreach (ctools_export_crud_load_all('site_verify') as $verification) { + if (empty($verification->disabled) && !empty($verification->file)) { + $items[$verification->file] = array( + 'page callback' => 'site_verify_output', + 'page arguments' => array((string) $verification->svid), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + } } return $items; @@ -84,49 +54,20 @@ function site_verify_permission() { function site_verify_init() { // Add the verification meta tags to the front page. if (drupal_is_front_page()) { - $meta_tags = db_query("SELECT svid, meta FROM {site_verify} WHERE meta <> ''")->fetchAllKeyed(); - foreach ($meta_tags as $svid => $meta_tag) { - $data = array( - '#type' => 'markup', - '#markup' => $meta_tag . "\n", - ); - drupal_add_html_head($data, 'site_verify:' . $svid); + ctools_include('export'); + foreach (ctools_export_crud_load_all('site_verify') as $verification) { + if (empty($verification->disabled) && !empty($verification->meta)) { + $data = array( + '#type' => 'markup', + '#markup' => $verification->meta . "\n", + ); + drupal_add_html_head($data, 'site_verify:' . $verification->svid); + } } } } /** - * Menu load callback; loads a site verification record. - * - * This also loads the engine details if the record was found. - * - * @param $svid - * A site verification ID. - * @return - * An array of the site verification record, or FALSE if not found. - */ -function site_verify_load($svid) { - $record = db_query("SELECT svid, engine, file, file_contents, meta FROM {site_verify} WHERE svid = :svid", array(':svid' => $svid))->fetchAssoc(); - if ($record) { - $record['engine'] = site_verify_engine_load($record['engine']); - } - return $record; -} - -/** - * Menu load callback; loads engine details. - * - * @param $engine - * A string with the engine shortname. - * @return - * An arary of the engine details, or FALSE if not found. - */ -function site_verify_engine_load($engine) { - $engines = site_verify_get_engines(); - return isset($engines[$engine]) ? $engines[$engine] : FALSE; -} - -/** * Implements hook_site_verify_engine_info(). */ function site_verify_site_verify_engine_info() { @@ -202,12 +143,30 @@ function site_verify_get_engines() { * Output the page contents for a file-based custom verification page. */ function site_verify_output($svid) { - $verification = site_verify_load($svid); - if ($verification['file_contents'] && $verification['engine']['file_contents']) { - echo $verification['file_contents']; + ctools_include('export'); + $engines = site_verify_get_engines(); + $verification = ctools_export_crud_load('site_verify', $svid); + if (empty($verification->disabled)) { + if ($verification->file_contents && $engines[$verification->engine]['file_contents']) { + echo $verification->file_contents; + } + else { + drupal_set_title(t('Verification page')); + return t('This is a verification page for the @title search engine.', array( + '!title' => $engines[$verification->engine]['name'] + )); + } } else { - drupal_set_title(t('Verification page')); - return t('This is a verification page for the @title search engine.', array('!title' => $verification['engine']['name'])); + return page_not_found(); + } +} + +/** + * Implements hook_ctools_plugin_directory(). + */ +function site_verify_ctools_plugin_directory($module, $plugin) { + if ($module == 'ctools' && !empty($plugin)) { + return "plugins/{$plugin}"; } } diff --git a/site_verify.test b/site_verify.test index 6d891ea..3f2d68a 100644 --- a/site_verify.test +++ b/site_verify.test @@ -23,27 +23,29 @@ class SiteVerifyFunctionalTestCase extends DrupalWebTestCase { function testMetaTag() { $this->drupalGet('admin/config/search/verifications'); - $this->assertText('No verifications available.'); - $this->clickLink('Add verification'); + $this->assertText('There are no verifications to display.'); + $this->clickLink('Add'); // Add a dummy Google meta tag. - $edit = array('engine' => 'google'); - $this->drupalPost('admin/config/search/verifications/add', $edit, t('Next')); - $meta_tag = ''; - $edit = array('meta' => $meta_tag); + $edit = array( + 'label' => $this->randomName(), + 'svid' => drupal_strtolower($this->randomName()), + 'engine' => 'google', + 'meta' => '', + ); $this->drupalPost(NULL, $edit, t('Save')); - $this->assertText('Verification saved.'); + $this->assertText("{$edit['svid']} has been created."); $this->assertText('Google'); // Check if it displays on the front page. $this->drupalGet(''); - $this->assertRaw($meta_tag, 'Verification code displayed on front page.'); + $this->assertRaw($edit['meta'], 'Verification code displayed on front page.'); // Now try to delete it. - $this->drupalGet('admin/config/search/verifications'); - $this->clickLink('Delete'); - $this->drupalPost(NULL, array(), t('Delete')); - $this->assertText('Verification for Google has been deleted.'); - $this->assertText('No verifications available.'); + // $this->drupalGet('admin/config/search/verifications'); + // $this->clickLink('Delete'); + // $this->drupalPost(NULL, array(), t('Delete')); + // $this->assertText('Verification for Google has been deleted.'); + // $this->assertText('No verifications available.'); } }