diff --git a/js/optimizely-admin.js b/js/optimizely-admin.js index 881d33fcccb5c9cb08f4f657dbadcd345b589dfa..541ef1d77d0ed1089f8c51dfbe14054c914ed638 100644 --- a/js/optimizely-admin.js +++ b/js/optimizely-admin.js @@ -38,13 +38,13 @@ // Toggle enable / disabled class if ($(target_this).attr('checked')) { $(target_this).parents('tr').find('td').addClass('enabled').removeClass('disabled'); - $(target_this).parents('tr').find('div.status-' + data.oid).text("Project enabled successfully."); + $(target_this).parents('tr').find('div.status-' + data.oid).text("Project enabled successfully." + data.message); $(target_this).parents('tr').find('div.status-' + data.oid).fadeOut(6000, function() { $(this).text('').css('display', '') }); } else { $(target_this).parents('tr').find('td').addClass('disabled').removeClass('enabled'); - $(target_this).parents('tr').find('div.status-' + data.oid).attr("innerHTML","Project disabled successfully."); + $(target_this).parents('tr').find('div.status-' + data.oid).attr("innerHTML","Project disabled successfully." + data.message); $(target_this).parents('tr').find('div.status-' + data.oid).fadeOut(6000, function() { $(this).text('').css('display', ''); }); } @@ -59,7 +59,7 @@ } // Display status message - $(target_this).parents('tr').find('div.status-' + data.oid).text('Project was not enabled due to path setting resulting in a duplicate path.'); + $(target_this).parents('tr').find('div.status-' + data.oid).text(data.message); $(target_this).parents('tr').find('div.status-' + data.oid).fadeOut(6000, function() { $(this).text('').css('display', ''); }); } diff --git a/optimizely.admin.inc b/optimizely.admin.inc index 4070825d8d92f2b6e119e4c0cf0d53ffa5e8074b..a3f4b227a377d649ee0e860e67031fed419f0936 100644 --- a/optimizely.admin.inc +++ b/optimizely.admin.inc @@ -55,10 +55,9 @@ function optimizely_add_update_form($form, &$form_submit, $target_oid = NULL) { $form_action = 'Update'; - $query = db_select('optimizely', 'o') + $query = db_select('optimizely', 'o', array('target' => 'slave')) ->fields('o') ->condition('o.oid', $target_oid, '='); - $record = $query->execute() ->fetchObject(); @@ -96,7 +95,7 @@ function optimizely_add_update_form($form, &$form_submit, $target_oid = NULL) { '#title' => t('Optimizely Project Code'), '#default_value' => $project_code == FALSE ? '' : check_plain($project_code), '#description' => check_plain($target_oid) == 1 ? - t('The Optimizely account value has not been set under <a href="/admin/config/system/optimizely/settings">ACCOUNT INFO</a>.') : + t('The Optimizely account value has not been set in the <a href="/admin/config/system/optimizely/settings">Account Info</a> settings form. The Optimizely account value is used as the project ID for this "default" project entry.') : t('The Optimizely javascript file name used in the snippet as provided by the Optimizely website for the project.'), '#size' => 30, '#maxlength' => 100, @@ -104,18 +103,6 @@ function optimizely_add_update_form($form, &$form_submit, $target_oid = NULL) { '#weight' => 20, ); - $form['optimizely_include'] = array( - '#type' => 'radios', - '#title' => t('Include/Exclude Optimizely Snippet on specific pages or paths.'), - '#description' => t('Include/Exclude Optimizely snippet / javascript on specific pages or paths. The use of wildcards ("*") and "<front>" is supported.'), - '#default_value' => $target_oid ? $record->include : 1, - '#options' => array( - 1 => 'Only the listed pages', - 0 => 'All pages except those listed', - ), - '#weight' => 30, - ); - $form['optimizely_path'] = array( '#type' => 'textarea', '#title' => t('Set Path Where Optimizely Code Snippet Appears'), @@ -176,9 +163,12 @@ function optimizely_add_update_update($target_project) { * to allow for custom settings. */ function optimizely_add_update_form_validate($form, &$form_state) { - - // Validate that the project code entered is a number - if (!ctype_digit($form_state['values']['optimizely_project_code'])) { + + // Watch for "Undefined" value in Project Code, Account ID needed in Settings page + if ($form_state['values']['optimizely_project_code'] == "Undefined") { + form_set_error('optimizely_project_code', t('The Optimizely Account ID must be set in the <a href="/admin/config/system/optimizely/settings">Account Info</a> page. The account ID is used as the default Optimizely Project Code.')); + } // Validate that the project code entered is a number + elseif (!ctype_digit($form_state['values']['optimizely_project_code'])) { form_set_error('optimizely_project_code', t('The project code !code must only contain digits.', array('!code' => $form_state['values']['optimizely_project_code']))); } elseif ($form_state['values']['op'] == 'Add') { @@ -208,13 +198,13 @@ function optimizely_add_update_form_validate($form, &$form_state) { $target_paths = preg_split('/[\r\n]+/', $form_state['values']['optimizely_path'], -1, PREG_SPLIT_NO_EMPTY); $valid_path = _optimizely_valid_paths($target_paths); if (!is_bool($valid_path)) { - form_set_error('optimizely_path', t('The project path "!project_path" is not a valid path. The path or alias could not be resolved as a valid URL resolving to content.', + form_set_error('optimizely_path', t('The project path "!project_path" is not a valid path. The path or alias could not be resolved as a valid URL that will result in content on the site.', array('!project_path' => $valid_path))); } // There must be only one Optimizely javascript call on a page. Check paths to ensure there are no duplicates // http://support.optimizely.com/customer/portal/questions/893051-multiple-code-snippets-on-same-page-ok- - list($error_title, $error_path) = _optimizely_unique_paths($target_paths, $form_state['values']['optimizely_include'], $form_state['values']['optimizely_oid']); + list($error_title, $error_path) = _optimizely_unique_paths($target_paths, $form_state['values']['optimizely_oid']); if (!is_bool($error_title)) { form_set_error('optimizely_path', t('The path "!error_path" will result in a duplicate entry based on the other project path settings. Optimizely does not allow more than one project to be run on a page.', array('!error_path' => $error_path))); @@ -240,7 +230,6 @@ function optimizely_add_update_form_submit($form, &$form_state) { // @totdo - Add support for "<front>" to allow use of check_plain() on ['optimizely_path'] $path_array = preg_split('/[\r\n]+/', $form_state['values']['optimizely_path'], -1, PREG_SPLIT_NO_EMPTY); - $include = check_plain($form_state['values']['optimizely_include']); $enabled = check_plain($form_state['values']['optimizely_enabled']); // Process add or edit submission @@ -250,7 +239,6 @@ function optimizely_add_update_form_submit($form, &$form_state) { db_insert('optimizely') ->fields(array( 'project_title' => $project_title, - 'include' => $include, 'path' => serialize($path_array), 'project_code' => $project_code, 'enabled' => $enabled, @@ -261,7 +249,7 @@ function optimizely_add_update_form_submit($form, &$form_state) { // Rebuild the provided paths to ensure Optimizely javascript is now included on paths if ($enabled) { - optimizely_refresh_cache($path_array, $include); + optimizely_refresh_cache($path_array); } } // $oid is set, update exsisting entry @@ -270,7 +258,6 @@ function optimizely_add_update_form_submit($form, &$form_state) { db_update('optimizely') ->fields(array( 'project_title' => $project_title, - 'include' => $include, 'path' => serialize($path_array), 'project_code' => $project_code, 'enabled' => $enabled, @@ -283,7 +270,7 @@ function optimizely_add_update_form_submit($form, &$form_state) { // Path originally set for project - to be compaired to the updated value to determine what cache paths needs to be refreshed $original_path_array = preg_split('/[\r\n]+/', $form_state['values']['optimizely_original_path'], -1, PREG_SPLIT_NO_EMPTY); - optimizely_refresh_cache($path_array, $include, $original_path_array); + optimizely_refresh_cache($path_array, $original_path_array); } @@ -309,8 +296,7 @@ function optimizely_account_settings_form($form_state) { '#type' => 'textfield', '#title' => t('Optimizely ID Number'), '#default_value' => variable_get('optimizely_id', ''), - '#description' => t('Your Optimizely account ID. This is the number after <q>/js/</q> in the Optimizely Tracking Code that\'s applied to the web - pages of the site.'), + '#description' => t('Your Optimizely account ID. This is the number after "/js/" in the Optimizely Tracking Code found in your account on the Optimizely website.'), '#size' => 60, '#maxlength' => 256, '#required' => TRUE, @@ -356,7 +342,7 @@ function optimizely_account_settings_form_submit($form, &$form_state) { drupal_set_message(t('The default project entry is now ready to be enabled. This will apply the default Optimizely project tests site wide.'), 'status'); // Redirect back - drupal_goto('/admin/config/system/optimizely'); + $form_state['redirect'] = 'admin/config/system/optimizely'; } @@ -391,29 +377,30 @@ function optimizely_project_list_form() { $account_id = variable_get('optimizely_id', 0); // Begin building the query. - $query = db_select('optimizely', 'o') + $query = db_select('optimizely', 'o', array('target' => 'slave')) ->orderBy('oid') ->fields('o'); - - // Fetch the result set. $result = $query->execute(); // Build each row of the table foreach ($result as $project_count => $row) { - // Deal with "<front>" as one of the paths + // Listing of target paths for the project entry $paths = unserialize($row->path); $project_paths = '<ul>'; foreach ($paths as $path) { + // Deal with "<front>" as one of the paths if ($path == '<front>') { - $path = htmlspecialchars('<front>'); + $front_path = variable_get('site_frontpage'); + $front_path .= ' <-> ' . drupal_lookup_path('alias', $front_path); + $path = htmlspecialchars('<front>') . ' (' . $front_path . ')'; } $project_paths .= '<li>' . $path . '</li>'; } $project_paths .= '</ul>'; - // Build Delete link + // Build Edit / Delete links if ($row->oid != 1) { $edit_link = l(t('Update'), 'admin/config/system/optimizely/add_update/' . $row->oid); $delete_link = ' / ' . l(t('Delete'), 'admin/config/system/optimizely/delete/' . $row->oid); @@ -425,36 +412,30 @@ function optimizely_project_list_form() { $default_entry_class = array('default-entry'); } - $row->enabled ? $checked = "'#checked' => 'checked'" : $checked = ''; - // Build form elements in cluding enable checkbox and data columns $form['projects'][$project_count]['enable'] = array( '#type' => 'checkbox', '#attributes' => array( 'id' => 'project-enable-' . $row->oid, - 'name' => 'project-' . $row->oid, - $row->enabled ? "'#checked' => 'checked'" : '', + 'name' => 'project-' . $row->oid ), '#default_value' => $row->enabled, '#extra_data' => array('field_name' => 'project_enabled'), '#suffix' => '<div class="status-container status-' . $row->oid . '"></div>' ); + if ($row->enabled) { + $form['projects'][$project_count]['enable']['#attributes']['checked'] = 'checked'; + } + $form['projects'][$project_count]['#project_title'] = $row->project_title; $form['projects'][$project_count]['#admin_links'] = $edit_link . $delete_link; - - if ($row->include) { - $include = t('Include:') . ' ' . $project_paths; - } - else { - $include = t('Exclude:') . ' ' . $project_paths; - } - $form['projects'][$project_count]['#paths'] = $include; + $form['projects'][$project_count]['#paths'] = $project_paths; if ($account_id == 0 && $row->oid == 1) { - $project_code = t('Set <strong>') . - l(t('Optimizely account ID'), 'admin/config/system/optimizely/settings') . - t('</strong> to enable default project site wide.'); + $project_code = t('Set Optimizely ID in <strong>') . + l(t('Account Info'), 'admin/config/system/optimizely/settings') . + t('</strong> page to enable default project site wide.'); } else { $project_code = $row->project_code; @@ -554,12 +535,11 @@ function optimizely_delete_page_confirm_submit($form, &$form_state) { // target $oid $oid = $form_state['values']['oid']; - + // Lookup entry details before delete - $query = db_select('optimizely', 'o') - ->fields('o', array('path', 'include', 'enabled')) + $query = db_select('optimizely', 'o', array('target' => 'slave')) + ->fields('o', array('path', 'enabled')) ->condition('o.oid', $oid, '='); - $record = $query->execute() ->fetchObject(); @@ -571,11 +551,9 @@ function optimizely_delete_page_confirm_submit($form, &$form_state) { // Only clear page cache for entries that are active when deleted if ($record->enabled) { - // Always serialize when saved + // Always serialized when saved $path_array = unserialize($record->path); - $include = $record->include; - - optimizely_refresh_cache($path_array, $include); + optimizely_refresh_cache($path_array); } @@ -596,6 +574,8 @@ function optimizely_ajax_enable() { // Default $unique_path = FALSE; + $default_project = FALSE; + $message = ''; // Retrieve the json POST values $target_oid = $_POST['target_oid']; @@ -606,29 +586,41 @@ function optimizely_ajax_enable() { if ($target_enable == TRUE) { // Lookup the current project settings - $query = db_select('optimizely', 'o') - ->fields('o', array('path', 'include')) + $query = db_select('optimizely', 'o', array('target' => 'slave')) + ->fields('o', array('path', 'project_code')) ->condition('o.oid', $target_oid, '='); - $result = $query->execute()->fetchObject(); - - $target_paths = unserialize($result->path); - $target_include = $result->include; - // Check that the paths are valid for the newly enabled project - $valid_paths = _optimizely_valid_paths($target_paths); + // Prevent the Default project from being enabled when the project code is not set + if (!($target_oid == 1 && $result->project_code == 0)) { - // Check to see if the enable project has path entries that will result in - // duplicates with other enable projects - if ($valid_paths) { - list($unique_path, $null) = _optimizely_unique_paths($target_paths, $target_include, $target_oid); + // Check that the paths are valid for the newly enabled project + $target_paths = unserialize($result->path); + $valid_paths = _optimizely_valid_paths($target_paths); + + // Check to see if the enable project has path entries that will result in + // duplicates with other enable projects + if ($valid_paths) { + list($unique_path, $target_path) = _optimizely_unique_paths($target_paths, $target_oid); + if ($unique_path !== TRUE) { + $message = t('Project was not enabled due to path setting resulting in duplicate path entries between enabled projects.'); + } + } + else { + $message = t('Project was not enabled due to path setting resulting in invalid path.'); + } + + } + else { + $default_project = TRUE; + $message = t('Default project not enabled. Enter Optimizely ID in Account Info page.'); } } // The newly enabled project has unique paths OR the target project is // currently enabled (TRUE) and will now be disbaled - if ($target_enable == FALSE || $unique_path === TRUE) { + if (($target_enable == FALSE || $unique_path === TRUE) && ($default_project == FALSE)) { // Toggle $target_enable $target_enable ? $target_enable = 1 : $target_enable = 0; @@ -643,14 +635,14 @@ function optimizely_ajax_enable() { // Refresh cache on project paths, this includes both enable and disabled // projects as there will be a need to clear the js calls in both cases - optimizely_refresh_cache($target_paths, $target_include); + optimizely_refresh_cache($target_paths); // Tell AJAX request of status to trigger jquery - drupal_json_output(array('status' => 'updated', 'oid' => $target_oid, 'target_enable' => $target_enable)); + drupal_json_output(array('status' => 'updated', 'oid' => $target_oid, 'target_enable' => $target_enable, 'message' => $message)); } else { - drupal_json_output(array('status' => 'rejected', 'oid' => $target_oid)); + drupal_json_output(array('status' => 'rejected', 'oid' => $target_oid, 'issue_path' => $target_path, 'message' => $message)); } } @@ -661,11 +653,8 @@ function optimizely_ajax_enable() { * @parm * $path_array - An array of the target paths entries that the cache needs to * be cleared. Each entry can also contain wildcards /* or variables "<front>". - * - * @parm - * $include - Flag if the paths are include or exclude */ -function optimizely_refresh_cache($path_array, $include = TRUE, $original_path_array = NULL) { +function optimizely_refresh_cache($path_array, $original_path_array = NULL) { // Determine protocol $protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http'; @@ -685,7 +674,7 @@ function optimizely_refresh_cache($path_array, $include = TRUE, $original_path_a // Apply to all paths when there's a '*' path entry (default project entry // for example) or it's an exclude path entry (don't even try to figure out // the paths, just flush all page cache - if ((strpos($path, '*') !== 0) || (!$include)) { + if (strpos($path, '*') !== 0) { if (strpos($path, '<front>') === 0) { $cid = $cid_base . '/' . variable_get('site_frontpage', 'node'); @@ -726,108 +715,186 @@ function optimizely_refresh_cache($path_array, $include = TRUE, $original_path_a * @parm * $target_paths - the paths entered for a new project entry OR * the paths of an exsisting project entry that has been enabled. - * - * @parm - * $target_include = TRUE : the include setting for if the paths are include - * or excluded - * * @parm * $target_paths = NULL : the oid of the project entry that has been enabled * * @return - * $project_title can be TRUE if the $target_paths are unique OR the name of - * the project_title - * which has a duplicate path entry. - * - * @return - * $target_path the path that is a duplicate that must be addressed to - * enable or create the new project entry. + * $target_path: the path that is a duplicate that must be addressed to + * enable or create the new project entry or TRUE if unique paths. */ -function _optimizely_unique_paths($target_paths, $target_include = TRUE, $target_oid = NULL) { +function _optimizely_unique_paths($target_paths, $target_oid = NULL) { - // Collect all of the exsisting project paths grouped by include or exclude - // and enabled, - $query = db_select('optimizely', 'o') - ->fields('o', array('oid', 'project_title', 'path', 'include')) - ->condition('o.enabled', 1, '='); - - // Add target_oid to query when it's an update, $target_oid is will be defined - if ($target_oid != NULL) { - $query = $query->condition('o.oid', $target_oid, '<>'); - } - - $result = $query->execute(); + // Look up alternative paths + $target_paths = _optimizely_collect_alias($target_paths); - $all_project_include_paths = array(); - $all_project_exclude_paths = array(); - - // Build array of all the project entry paths - foreach ($result as $row) { + // Look for duplicate paths in submitted $target_paths + $duplicate_target_path = _optimizely_duplicate_check($target_paths); + + // Look for duplicate paths within target paths + if (!$duplicate_target_path) { - // Collect all of the path values and merge into collective array, one each - // for include and exclude - $project_paths = unserialize($row->path); - - if ($row->include) { - $all_project_include_paths = array_merge($all_project_include_paths, $project_paths); + // Collect all of the exsisting project paths that are enabled, + $query = db_select('optimizely', 'o', array('target' => 'slave')) + ->fields('o', array('oid', 'project_title', 'path')) + ->condition('o.enabled', 1, '='); + + // Add target_oid to query when it's an update, $target_oid is will be defined + if ($target_oid != NULL) { + $query = $query->condition('o.oid', $target_oid, '<>'); } - else { - $all_project_exclude_paths = array_merge($all_project_exclude_paths, $project_paths); + + $projects = $query->execute(); + + // No other enabled projects + if ($query->countQuery()->execute()->fetchField() == 0) { + return array(TRUE, NULL); } - - } + + $all_project_paths = array(); - // Include or not include matching path values - if ($target_include) { + // Build array of all the project entry paths + foreach ($projects as $project) { + + // Collect all of the path values and merge into collective array + $project_paths = unserialize($project->path); + $all_project_paths = array_merge($all_project_paths, $project_paths); + + } + // Add any additional aliases to catch all match possiblities + $all_project_paths = _optimizely_collect_alias($all_project_paths); + + // Convert array into string for drupal_match_path() + $all_project_paths_string = implode("\n", $all_project_paths); + // Check all of the paths for all of the active project entries to make sure // the paths are unique foreach ($target_paths as $target_path) { - // Remove wildcard notation + // "*" found in path if (strpos($target_path, '*') !== FALSE) { - $target_path = substr($target_path, 0, strpos($target_path, '*')); - - // Compare base $target_path with all of the project paths. With a - // wildcard removed the base $target_path will match duplicate entries - // in other projects as a potential duplicate - foreach ($all_project_include_paths as $project_oid => $project_path) { - if (strpos($project_path, $target_path) !== FALSE) { - return array($project_oid, $project_path); + + // Look for wild card match if not site wide + if (strpos($target_path, '*') !== 0) { + + $target_path = substr($target_path, 0, -2); + + // Look for duplicate path due to wild card + foreach ($all_project_paths as $all_project_path) { + + // + if (strpos($all_project_path, $target_path) === 0 && $all_project_path != $target_path) { + return array($project->project_title, $target_path); + } + } + + } // If site wide wild card then it must be the only enabled path to be unique + elseif (strpos($target_path, '*') === 0 && (count($target_paths) > 1 || count($all_project_paths) > 0)) { + return array($project->project_title, $target_path); } - + + // Look for site wide wild card in target project paths + if (in_array('*', $all_project_paths)) { + return array($project->project_title, $target_path); + } + } - - // Search for target path in all other target project paths - if (in_array($target_path, $all_project_include_paths)) { - return array($row->project_title, $target_path); + elseif (drupal_match_path($target_path, $all_project_paths_string)) { + return array($project->project_title, $target_path); } } - + + return array(TRUE, NULL); + } else { + return array(NULL, $duplicate_target_path); + } + +} + +/* + * Compare paths within passed array to ensure each item resolves to a unique entry + * + * @parm + * $paths - a set of paths to be reviewed for uniqueness + * + * @return + * FALSE if no duplicates found otherwaise the dusplicate path is returned. + */ +function _optimizely_duplicate_check($paths) { + + $unreviewed_paths = $paths; + + // Check all of the paths + foreach ($paths as $path) { - foreach ($target_paths as $target_path) { - - if (in_array($target_path, $all_project_exclude_paths)) { - return array($row->project_title, $target_path); + // Remove path that's being processed from the front of the list + array_shift($unreviewed_paths); + + // "*" found in path + if (strpos($path, '*') !== FALSE) { + + // Look for wild card match that's not site wide (posiiton not zero (0)) + if (strpos($path, '*') !== 0) { + + $path = substr($path, 0, -2); + + foreach ($unreviewed_paths as $unreviewed_path) { + if (strpos($unreviewed_path, $path) !== FALSE) { + return $path . '/*'; + } + } + + } // If site wide wild card then it must be the only path in path set + elseif (strpos($path, '*') === 0 && count($paths) > 1) { + return $path; } - + + } + elseif (in_array($path, $unreviewed_paths)) { + return $path; } - - // Exclude path - $common_paths = array_intersect($target_paths, $all_project_include_paths); - if (count($common_paths) !== 0) { - return array($row->project_title, $common_paths[0]); + } + + return FALSE; + +} + +/* + * Lookup all alternatives to the group of paths - alias, <front> + * + * @parm + * $paths - a set of paths to be reviewed for alternatives + * + * @return + * $paths - an updated list of paths that include the additional source and alias values. + */ +function _optimizely_collect_alias($paths) { + + // Add alternative values - alias, source, <front> to ensure matches also check different possiblities + foreach ($paths as $path) { + !drupal_lookup_path('alias', $path) ?: $paths[] = drupal_lookup_path('alias', $path); + !drupal_lookup_path('source', $path) ?: $paths[] = drupal_lookup_path('source', $path); + + // Collect all the possible values to match <front> + if ($path == '<front>') { + + $frontpage = variable_get('site_frontpage', FALSE); + if ($frontpage) { + $paths[] = variable_get('site_frontpage'); + $paths[] = drupal_lookup_path('alias', $frontpage); + } + } } - - return array(TRUE, NULL); - + + return $paths; + } /** @@ -835,11 +902,11 @@ function _optimizely_unique_paths($target_paths, $target_include = TRUE, $target * * Validate the target paths with drupal_lookup_path. * - * @parm - * $target_paths: An array of the paths to validate. + * @parm $target_paths + * An array of the paths to validate. * * @return - * boolean of TRUE if the paths are valid or a string of the path that failed. + * Boolean of TRUE if the paths are valid or a string of the path that failed. */ function _optimizely_valid_paths($project_paths) { @@ -848,41 +915,61 @@ function _optimizely_valid_paths($project_paths) { // Check for site wide wildcard if (strpos($project_path, '*') === 0) { + + if (count($project_paths) == 1) { return TRUE; + } + else { + return $project_path; + } + } // Path wildcards elseif (strpos($project_path, '*') !== FALSE) { - $project_wildpath = substr($project_path, 0, strpos($project_path, '*') - 1); - - // select * from url_alias where source like 'article%' or alias like 'article%'; + $project_wildpath = substr($project_path, 0, -2); + if (!drupal_valid_path($project_wildpath, TRUE)) { + + // Look for entries in url_aias + $query = db_query("SELECT * FROM {url_alias} WHERE + source LIKE :project_wildpath OR alias LIKE :project_wildpath", + array(':project_wildpath' => $project_wildpath . '%')); + $project_wildpath_match = $query->rowCount(); + + // No matches found for wildcard path + if (!$project_wildpath_match) { + return $project_path; + } + + } - // Look for entries in url_aias - $query = db_query("SELECT * FROM {url_alias} WHERE - source LIKE :project_wildpath OR alias LIKE :project_wildpath", - array(':project_wildpath' => $project_wildpath . '%')); - $project_wildpath_match = $query->rowCount(); + } // Parameters + elseif (strpos($project_path, '?') !== FALSE) { + + // Look for entries in menu_router + $project_parmpath = substr($project_path, 0, strpos($project_path, '?')); + // Collect all of the exsisting project paths that are enabled, + $query = db_select('menu_router', 'mr', array('target' => 'slave')) + ->fields('mr', array('path')) + ->condition('path', db_like($project_parmpath) . '%', 'LIKE'); + $query->execute(); + // No matches found for wildcard path - if (!$project_wildpath_match) { + if ($query->countQuery()->execute()->fetchField() == 0) { return $project_path; } - - } // Specific path - else { - - // Validation if path valid menu router entry, includes support for <front> - if (drupal_valid_path($project_path, TRUE) === FALSE) { - - // Look for entry in url_alias table - if (drupal_lookup_path('alias', $project_path) === FALSE && - drupal_lookup_path('source', $project_path) === FALSE) { - return $project_path; - } - + + } // Validation if path valid menu router entry, includes support for <front> + elseif (drupal_valid_path($project_path, TRUE) === FALSE) { + + // Look for entry in url_alias table + if (drupal_lookup_path('alias', $project_path) === FALSE && + drupal_lookup_path('source', $project_path) === FALSE) { + return $project_path; } - + } - + } return TRUE; diff --git a/optimizely.info b/optimizely.info index 5afe5d1df1de7ac78d7bc718d4dbf145a01d2ffe..3ed8e7fbad19b4e6e4b280231beef62e97c3234f 100644 --- a/optimizely.info +++ b/optimizely.info @@ -2,7 +2,7 @@ name = Optimizely description = Manages loading Optimizely generated project javascript file(s) to enable A/B testing. core = 7.x php = 5.2 -version = "7.x-2.x" +version = "7.x-2.11" project = "optimizely" configure = admin/config/system/optimizely/settings files[] = optimizely.test \ No newline at end of file diff --git a/optimizely.install b/optimizely.install index b40578e3e4a28f14091c0b7dee7631a576dac23e..ee3e6b9bc8d392e619e9acda49f3d73c76136ae4 100644 --- a/optimizely.install +++ b/optimizely.install @@ -111,8 +111,6 @@ function optimizely_install() { else { // Create default entry - // Load Optimizely account code - used for default project snippet ID for javascript file name - $account_id = variable_get('optimizely_id', 0); $default_entry_created = (bool) db_insert('optimizely') ->fields( @@ -121,7 +119,7 @@ function optimizely_install() { 'include' => 1, 'enabled' => 0, 'path' => serialize( array('*') ), - 'project_code' => $account_id, + 'project_code' => 0, ) ) ->execute(); @@ -242,15 +240,31 @@ function optimizely_update_7210(&$sandbox) { )) ->execute(); - // Inform the administrator that all projects have been disabled - if ($disabled_projects) { - drupal_set_message(st('All Optimizely project entries have been disabled. Reenable each project on the') . ' ' . - l(st('project listing Adminstration page'), '/admin/config/system/optimizely') . ' ' . - st('to ensure that the settings pass new validation functionality. Each entry must have valid paths that point to functional paths that are unique to the entry.'), - 'status'); - } - else { - drupal_set_message(st('An error was encountred resetting all of the project entries to disabled for the Optimizely module.'), 'error'); - } + drupal_set_message(st('All Optimizely project entries have been disabled. Reenable each project on the') . ' ' . + l(st('project listing Adminstration page'), '/admin/config/system/optimizely') . ' ' . + st('to ensure that the settings pass new validation functionality. Each entry must have valid paths that point to functional paths that are unique to the entry.'), + 'status'); + +} + +/** + * Disable all project entries to ensure that validatation tests are run on + * each entry when enabled. Validation includes valid and unique paths. + * + * Again... + */ +function optimizely_update_7211(&$sandbox) { + + // Update all database project entries to disabled + $disabled_projects = (bool) db_update('optimizely') + ->fields(array( + 'enabled' => 0, + )) + ->execute(); + + drupal_set_message(st('All Optimizely project entries have been disabled. Reenable each project on the') . ' ' . + l(st('project listing Adminstration page'), '/admin/config/system/optimizely') . ' ' . + st('to ensure that the settings pass new validation functionality. Each entry must have valid paths that point to functional paths that are unique to the entry.'), + 'status'); } \ No newline at end of file diff --git a/optimizely.module b/optimizely.module index 421c20d716249b758db97349ad4430b89d459d90..07a6362af364a68a1a2bff97adfcc3b5758bd002 100644 --- a/optimizely.module +++ b/optimizely.module @@ -34,8 +34,8 @@ function optimizely_help($path, $arg) { entry to apply the project settings while not needing to remove the actual entry. The default entry can be disabled when additional project entries are made with more specific settings. This can include using the the orginal Project Code.'; case 'admin/config/system/optimizely': - return t('<p>A listing of the Optimizely projects. Each entry can be enabled / disable and included / excluded for specific or wildcard paths. Enabled entries are highlighted in green while disabled entries are in red. The top, "Default" entry can not be deleted but its settings can be adjusted or completely disabled.</p> - <p>The goal of having multiple projects is to minimized the size of the Optimizely hosted javascript file. If all experiments are contained in a single file and processed on every page load there may be an issue with decreased page load time. Having multiple projects and loading them on specific paths that apply to the experiments helps to minimize the size of the file and eliminate processing unused javascript on the users browser.</p>'); + return t('<p>A listing of the Optimizely projects. Each entry can be enabled / disable for specific or wildcard paths. Enabled entries are highlighted in green while disabled entries are in red. The top, "Default" entry can not be deleted but its settings can be adjusted or completely disabled.</p> + <p>The goal of having multiple projects is to minimized the size of the Optimizely hosted javascript file. If all experiments are contained in a single file and processed on every page load there may be an issue with decreased page load time. Having multiple projects and loading them on specific paths that apply to the experiments helps to minimize the size of the file and eliminate processing unused javascript on the user\'s browser.</p>'); break; case 'admin/config/system/optimizely/add_update': @@ -208,7 +208,7 @@ function optimizely_init() { $add_snippet = FALSE; // Load all entries in the optimizely table - $query = db_select('optimizely', 'o') + $query = db_select('optimizely', 'o', array('target' => 'slave')) ->fields('o') ->condition('o.enabled', 1, '=') ->orderBy('oid'); @@ -229,34 +229,42 @@ function optimizely_init() { $project_paths = unserialize($project['path']); $front_index = array_search('<front>', $project_paths); !is_int($front_index) ?: $project_paths[$front_index] = variable_get('site_frontpage'); + + // Make copy of $project_paths for exclude loop + $exclude_project_paths = $project_paths; // Check all paths for alias or sytem URL values, foreach loop based on // orginal array value, addiitonal values added will not effect the array foreach ($project_paths as $project_path) { - - if (strpos($project_path, '*') !== FALSE) { - - // Sitewide wild card - if ($project_path == '*') { - $wildcard_match = TRUE; - break; - } - else { - - // Remove wildcard, get base path - $project_path = substr($project_path, 0, strlen($project_path) - 2); - - // Look for wildcard match - if ( stristr($current_path, $project_path) || - stristr(drupal_lookup_path('alias', $current_path), $project_path) || - stristr(drupal_lookup_path('source', $current_path), $project_path) ) { - $wildcard_match = TRUE; - break; + + if (strpos($project_path, '*') !== FALSE) { + + // Different conditions for include and exclued projects + if ($project['include']) { + + // Sitewide wild card + if ($project_path == '*') { + $add_snippet = TRUE; + break 2; } - - } - - } + else { + + // Remove wildcard, get base path(s) + $project_path = substr($project_path, 0, -2); + + // Look for wildcard match + if ( stristr($current_path, $project_path) || + stristr(drupal_lookup_path('alias', $current_path), $project_path) || + stristr(drupal_lookup_path('source', $current_path), $project_path) ) { + $add_snippet = TRUE; + break 2; + } + + } + + } + + } // Build out $project_paths with possible source and alias values to // test with drupal_match_path() @@ -267,40 +275,33 @@ function optimizely_init() { !$project_path_alias ?: $project_paths[] = $project_path_alias; } - + // Prep for drupal_match_path() $project_paths = implode("\n", array_unique($project_paths)); - - if ($project['include']) { - - if ( drupal_match_path($current_path, $project_paths) || - drupal_match_path($current_path_alias, $project_paths) || - $wildcard_match ) { - - $add_snippet = TRUE; - - } - - } - else { // Exclude - - if ( !drupal_match_path($current_path, $project_paths) && - !drupal_match_path($current_path_alias, $project_paths) && - !$wildcard_match ) { - - $add_snippet = TRUE; - - } - + + if (drupal_match_path($current_path, $project_paths) || + drupal_match_path($current_path_alias, $project_paths)) { + $add_snippet = TRUE; + break; } } - - if ($add_snippet) { - - // Adjust path to support secure pages - $protocol = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ? 'https' : 'http'; - drupal_add_js($protocol . '://cdn.optimizely.com/js/' . $project['project_code'] . '.js', 'external'); + + } + + if ($add_snippet) { + + // Adjust path to support secure pages + $protocol = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ? 'https' : 'http'; + + // Add javascript call to page markup + drupal_add_js($protocol . '://cdn.optimizely.com/js/' . $project['project_code'] . '.js', 'external'); + + } + + } + +}://cdn.optimizely.com/js/' . $project['project_code'] . '.js', 'external'); // No need to keep processing, only one snippet pre page allowed by Optimizely break; diff --git a/optimizely.test b/optimizely.test index 3496ebd85b890c3bddec1218bbc12a4f2ae3872b..d053aa9977bf09649fdbb0b09de770857647418b 100644 --- a/optimizely.test +++ b/optimizely.test @@ -5,459 +5,408 @@ * Optimizely Tests */ - /** +/** * Web Tests + * + * OptimizelyTestModuleSetupCase: Check default database sets up correctly. */ -class OptimizelyTestAddUpdateCase extends DrupalWebTestCase { - protected $privileged_user; + + /** - * OptimizelyTestAddUpdateCase getInfo(). - */ - public static function getInfo() { - return array( - 'name' => 'Add / Update Projects Test', - 'description' => 'Ensure that the add / update features function properly.', - 'group' => 'Optimizely', - ); - } - -/** - * OptimizelyTestAddUpdateCase setUp(). - */ - public function setUp() { - parent::setUp('optimizely'); // Enable any modules required for the test - // Create and log in our user. The user has the arbitrary privilege - // 'extra special edit any Optimizely project' which the code uses - // to grant access. - $this->privileged_user = $this->drupalCreateUser(array('administer optimizely')); - $this->drupalLogin($this->privileged_user); - } - - public function testOptimizelyAddUpdateProject() - { - $edit = array( - 'optimizely_project_title' => $this->randomName(8), - 'optimizely_project_code' => rand(0,10000), - 'optimizely_path' => $this->randomName(8), - 'optimizely_include' => rand(0, 1), - 'optimizely_enabled' => rand(0, 1), - ); - $this->drupalPost('admin/config/system/optimizely/add_update', $edit, t('Add')); - - $project_title = db_query('SELECT project_title FROM {optimizely} WHERE project_title = :optimizely_project_title', - array(':optimizely_project_title' => $edit['optimizely_project_title']))->fetchField(); - $this->assertEqual($project_title, $edit['optimizely_project_title'], t('The project was added to the database.')); - - $edit = array( - 'optimizely_project_title' => $this->randomName(8), - 'optimizely_project_code' => rand(0,10000), - 'optimizely_path' => $this->randomName(8), - 'optimizely_include' => rand(0, 1), - 'optimizely_enabled' => rand(0, 1), - ); - $this->drupalPost('admin/config/system/optimizely/add_update/2', $edit, t('Update')); - - // test if database was updated - $project_title = db_query('SELECT project_title FROM {optimizely} WHERE project_title = :optimizely_project_title', - array(':optimizely_project_title' => $edit['optimizely_project_title']))->fetchField(); - $this->assertEqual($project_title, $edit['optimizely_project_title'], t('The project was updated in the database.')); - } -} - - /** - * Web Tests + * OptimizelyTestUserRoleTestCase: Create anonymous, authenticated and privileged user to test access to module related pages. */ -class OptimizelyTestUserPermissionsCase extends DrupalWebTestCase { +class OptimizelyTestUserRoleTestCase extends DrupalWebTestCase { + + //Public, Private, Protected: http://stackoverflow.com/questions/4361553/php-public-private-protected + protected $anonymous_user; + protected $authenticated_user; protected $privileged_user; /** - * OptimizelyTestUserPermissionsCase getInfo(). + * OptimizelyTestAdminRoleCase getInfo(). */ public static function getInfo() { return array( - 'name' => 'User Permissions Test', - 'description' => 'Ensure user permissons function properly.', + 'name' => 'Access Test', + 'description' => 'Test that no part of the Optimizely module administration interface can be accessed without the necessary permissions.', 'group' => 'Optimizely', ); } /** - * OptimizelyTestUserPermissionsCase setUp(). + * OptimizelyTestAdminRoleCase setUp(). */ public function setUp() { - parent::setUp('optimizely'); // Enable any modules required for the test - // Create and log in our user. The user has the arbitrary privilege - // 'extra special edit any Optimizely project' which the code uses - // to grant access. - $this->privileged_user = $this->drupalCreateUser(array('administer optimizely')); - $this->drupalLogin($this->privileged_user); - } - - public function testUserPermissions() - { - $permissions = array( - 'name' => 'administer optimizely', - ); - - $valid = $this->checkPermissions($permissions); - $this->assertTrue($valid, $permissions['name'] . t(' is a valid permission.')); - - $this->drupalGet('admin/config/system/optimizely'); - $this->assertResponse(200, t('access allowed')); - - $this->drupalLogout(); - $this->drupalGet('admin/config/system/optimizely'); - $this->assertResponse(403, t('access denied')); - } -} - - /** - * Test schema creation. - */ -class OptimizelyTestSchemaCase extends DrupalWebTestCase { - protected $privileged_user; - + + // Enable any modules required for the test + parent::setUp('optimizely'); + + $this->anonymous_user = $this->drupalCreateUser(array()); + + $this->authenticated_user = $this->drupalCreateUser(array( + 'access content')); + + // Create and log in an admin user. The user will have the privilege + // 'administer optimizely'. This privaged is need to access all administration + // functionality of the module. + $this->privileged_user = $this->drupalCreateUser(array( + 'administer optimizely', + 'create page content', + 'edit own page content', + 'administer url aliases', + 'create url aliases')); + + } + /** - * OptimizelyTestSchemaCase getInfo(). + * OptimizelyTestAdminRoleCase testOptimizelyTestUserRolePublicAccess() */ - public static function getInfo() { - return array( - 'name' => 'Schema Creation Test', - 'description' => 'Ensure schema creation.', - 'group' => 'Optimizely', - ); - } + public function testOptimizelyTestUserRolePublicAccess() { + + for ($i = 1; $i <= 2; $i++) { + + if ($i == 1) { + + $target = $i . '. <strong>Anonymous</strong>'; + $this->drupalLogin($this->anonymous_user); + + } + else { + + $target = $i . '. <strong>Authenticated</strong>'; + $this->drupalLogin($this->authenticated_user); + + } + + // Tests + $this->drupalGet('admin/config/system/optimizely'); + $this->assertNoRaw('<h1 class="page-title">Optimizely</h1>', $target . ' user *<strong>can not</strong>* access project listing page -> admin/config/system/optimizely'); + + $this->drupalGet('admin/config/system/optimizely/default'); + $this->assertNoRaw('<h1 class="page-title">Optimizely</h1>', $target . ' user *<strong>can not</strong>* access project listing page -> admin/config/system/optimizely/default'); + + $this->drupalGet('admin/config/system/optimizely/add_update'); + $this->assertNoRaw('<h1 class="page-title">Optimizely</h1>', $target . ' user *<strong>can not</strong>* access project add form page -> admin/config/system/optimizely/add_update'); + + $this->drupalGet('admin/config/system/optimizely/settings'); + $this->assertNoRaw('<h1 class="page-title">Optimizely</h1>', $target . ' user *<strong>can not</strong>* access settings page -> admin/config/system/optimizely/settings'); + + $this->drupalGet('ajax/optimizely'); + $this->assertNoRaw('<h1 class="page-title">Optimizely</h1>', $target . ' user *<strong>can not</strong>* access AJAX callback URL -> ajax/optimizely'); + + $this->drupalLogout(); + + } + } + /** - * OptimizelyTestSchemaCase setUp(). + * OptimizelyTestAdminRoleCase testOptimizelyTestUserRoleAdminAccess() */ - public function setUp() { - parent::setUp('optimizely'); // Enable any modules required for the test - // Create and log in our user. The user has the arbitrary privilege - // 'extra special edit any Optimizely project' which the code uses - // to grant access. - $this->privileged_user = $this->drupalCreateUser(array('administer optimizely')); - $this->drupalLogin($this->privileged_user); - } - - public function testOptimizelySchema() - { - $schema = module_invoke('optimizely', 'schema'); - $this->assertNotNull($schema, t('Optimizely table was created.')); - } -} + public function testOptimizelyTestUserRoleAdminAccess() { + + $this->drupalLogin($this->privileged_user); + + $this->drupalGet('admin/config/system/optimizely'); + $this->assertNoRaw('Access denied', '** <strong>Admin user can access</strong> project listing page -> admin/config/system/optimizely'); + + $this->drupalGet('admin/config/system/optimizely/default'); + $this->assertNoRaw('Access denied', '** <strong>Admin user can access</strong> project listing page -> admin/config/system/optimizely/default'); + + $this->drupalGet('admin/config/system/optimizely/add_update'); + $this->assertNoRaw('Access denied', '** <strong>Admin user can access</strong> add project form page -> admin/config/system/optimizely/add_update'); + + $this->drupalGet('admin/config/system/optimizely/settings'); + $this->assertNoRaw('Access denied', '** <strong>Admin user can access</strong> settings page -> admin/config/system/optimizely/settings'); + + // $this->drupalGet('ajax/optimizely'); + // $this->assertNoRaw('Access denied', 'Admin user can access AJAX callback URL -> ajax/optimizely'); + + $this->drupalLogout(); + - /** - * Test enabling / disabling non-default project from update page - */ -class OptimizelyTestEnableDisableCase extends DrupalWebTestCase { - protected $privileged_user; +// admin/config/system/optimizely/add_update/% +// admin/config/system/optimizely/delete/% -/** - * OptimizelyTestEnableDisableCase getInfo(). - */ - public static function getInfo() { - return array( - 'name' => 'Enable / Disable Project Test', - 'description' => 'Test enabling / disabling non-default projects.', - 'group' => 'Optimizely', - ); } -/** - * OptimizelyTestEnableDisableCase setUp(). - */ - public function setUp() { - parent::setUp('optimizely'); // Enable any modules required for the test - // Create and log in our user. The user has the arbitrary privilege - // 'extra special edit any Optimizely project' which the code uses - // to grant access. - $this->privileged_user = $this->drupalCreateUser(array('administer optimizely')); - $this->drupalLogin($this->privileged_user); - } - - public function testOptimizelyUpdateEnableDisable() - { - // create a disabled project - $edit = array( - 'optimizely_project_title' => $this->randomName(8), - 'optimizely_project_code' => rand(0,10000), - 'optimizely_path' => $this->randomName(8), - 'optimizely_include' => rand(0, 1), - 'optimizely_enabled' => 0, - ); - $this->drupalPost('admin/config/system/optimizely/add_update', $edit, t('Add')); - - $edit = array( - 'optimizely_enabled' => 1, - ); - $this->drupalPost('admin/config/system/optimizely/add_update/2', $edit, t('Update')); - - // test if project was enabled - $enabled = db_query('SELECT enabled FROM {optimizely} WHERE oid = 2')->fetchField(); - $this->assertEqual($enabled, $edit['optimizely_enabled'], t('The project was enabled from update page.')); - - $edit = array( - 'optimizely_enabled' => 0, - ); - $this->drupalPost('admin/config/system/optimizely/add_update/2', $edit, t('Update')); - - // test if project was disabled - $enabled = db_query('SELECT enabled FROM {optimizely} WHERE oid = 2')->fetchField(); - $this->assertEqual($enabled, $edit['optimizely_enabled'], t('The project was disabled from update page.')); - - } } - /** - * Test if the default project settings work correctly. - */ -class OptimizelyTestDefaultSettingsCase extends DrupalWebTestCase { - protected $privileged_user; /** - * OptimizelyTestDefaultSettingsCase getInfo(). + * OptimizelyTestDefaultProjectTestCase: Test that: + * + * 1. The default project is available but disabled in the project listing page after the module has been enabled. + * 2. A message in the project listing page directs the administrator to go to the module settings page to enter the Optimizely account value. + * 3. Accessing the account setting page should be blank by default with a message informing the user that the account setting will be used + * for the default project number. + * 4. Test adding the account setting redirects to the project listing page with the account number listed as the disabled project dumber for the + * default project entry. + * 5. The default project can not be enabled until the account number is entered on the settings page. + * 6. Enabling the default project with the default path setting of side wide "*" should result in the snippet being displayed on the sites front page. */ - public static function getInfo() { - return array( - 'name' => 'Default Settings Test', - 'description' => 'Ensure ensure the project settings work correctly.', - 'group' => 'Optimizely', - ); - } - -/** - * OptimizelyTestDefaultSettingsCase setUp(). - */ - public function setUp() { - parent::setUp('optimizely'); // Enable any modules required for the test - // Create and log in our user. The user has the arbitrary privilege - // 'extra special edit any Optimizely project' which the code uses - // to grant access. - $this->privileged_user = $this->drupalCreateUser(array('administer optimizely')); - $this->drupalLogin($this->privileged_user); - } - - // Test add the optimizely account ID to the Default project and enable/disable the Default project - public function testOptimizelyDefaultSettings() - { - // add the Optimizely account ID - $edit = array( - 'optimizely_id' => rand(0, 10000), - ); - $this->drupalPost('admin/config/system/optimizely/settings', $edit, t('Submit')); - - $optimizely_id = db_query('SELECT project_code FROM {optimizely} WHERE oid = 1')->fetchField(); - $this->assertEqual($optimizely_id, $edit['optimizely_id'], t('Optimizely ID number added to Default project.')); - - // enable the default project - $edit = array( - 'optimizely_enabled' => 1, - ); - $this->drupalPost('admin/config/system/optimizely/add_update/1', $edit, t('Update')); - - $enabled = db_query('SELECT enabled FROM {optimizely} WHERE oid = 1')->fetchField(); - $this->assertEqual($enabled, $edit['optimizely_enabled'], t('The Default project was enabled.')); - - // disable the default project - $edit = array( - 'optimizely_enabled' => 0, - ); - $this->drupalPost('admin/config/system/optimizely/add_update/1', $edit, t('Update')); - - $enabled = db_query('SELECT enabled FROM {optimizely} WHERE oid = 1')->fetchField(); - $this->assertEqual($enabled, $edit['optimizely_enabled'], t('The Default project was disabled.')); - } -} - - - /** - * Test if snippet is excluded in path. - */ -class OptimizelyTestExcludeSnippetCase extends DrupalWebTestCase { +class OptimizelyTestDefaultProjectTestCase extends DrupalWebTestCase { + + protected $anonymous_user; + protected $authenticated_user; protected $privileged_user; + + protected $optimizely_account_id; /** - * OptimizelyTestExcludeSnippetCase getInfo(). + * OptimizelyTestDefaultProjectTestCase getInfo(). */ public static function getInfo() { return array( - 'name' => 'Exclude Snippet Test', - 'description' => 'Ensure that the Optimizely snippet excluded from project path.', + 'name' => 'Default Project Test', + 'description' => 'Test the exsistence of a disabled default project entry that when enabled after adding the Optimizely account ID results in the default snippeting being added to the front page (default) of the site.', 'group' => 'Optimizely', ); } /** - * OptimizelyTestExcludeSnippetCase setUp(). + * OptimizelyTestDefaultProjectTestCase setUp(). */ public function setUp() { - parent::setUp('optimizely'); // Enable any modules required for the test - // Create and log in our user. The user has the arbitrary privilege - // 'extra special edit any Optimizely project' which the code uses - // to grant access. - $this->privileged_user = $this->drupalCreateUser(array('administer optimizely', 'create page content', 'edit own page content', 'administer url aliases', 'create url aliases')); - $this->drupalLogin($this->privileged_user); - } - - public function testSnippet() { - //$return = $this->drupalGetTestFiles('html',1024); - - $edit = array( - 'optimizely_project_title' => $this->randomName(8), - 'optimizely_project_code' => rand(0,10000), - 'optimizely_path' => $this->randomName(8), - 'optimizely_include' => 0, //snippet should appear on pages other than the project path - 'optimizely_enabled' => 1, - ); - $snippet = 'http://cdn.optimizely.com/js/' . $edit['optimizely_project_code'] . '.js'; - $this->drupalPost('admin/config/system/optimizely/add_update', $edit, t('Add')); - - // create first page - $settings = array( - 'type' => 'page', - 'title' => $this->randomName(32), - 'body' => array(LANGUAGE_NONE => array(array($this->randomName(64)))), - ); - $node1 = $this->drupalCreateNode($settings); - - // Create the url alias - $edit_node1 = array(); - $edit_node1['source'] = 'node/' . $node1->nid; - $edit_node1['alias'] = $edit['optimizely_path']; - $this->drupalPost('admin/config/search/path/add', $edit_node1, t('Save')); - - // create second page - $settings = array( - 'type' => 'page', - 'title' => $this->randomName(32), - 'body' => array(LANGUAGE_NONE => array(array($this->randomName(64)))), + + // Enable any modules required for the test + parent::setUp('optimizely'); + + $this->anonymous_user = $this->drupalCreateUser(array()); + + $this->authenticated_user = $this->drupalCreateUser(array( + 'access content')); + + // Create and log in an admin user. The user will have the privilege + // 'administer optimizely'. This privaged is need to access all administration + // functionality of the module. + $this->privileged_user = $this->drupalCreateUser(array( + 'administer optimizely', + 'create page content', + 'edit own page content', + 'administer url aliases', + 'create url aliases')); + + } + + /* + * 1. The default project is available but disabled in the project listing page + * after the module has been enabled. + * 2. A message in the project listing page directs the administrator to go to + * the module settings page to enter the Optimizely account value. + * 5. The default project can not be enabled until the account number is entered on the settings page. + */ + public function testOptimizelyTestDefaultProjectEnable() { + + // Access with privileged user + $this->drupalLogin($this->privileged_user); + + // Look for entry in project listing page + $this->drupalGet('admin/config/system/optimizely'); + $this->assertRaw('<td class="project-title-column disabled">Default</td>', '** <strong>Default project entry fround on project listing page.</strong>'); + + // Confirm default project is not enabled + $this->assertRaw('<input id="project-enable-1" name="project-1" type="checkbox" value="1" class="form-checkbox" />', '** <strong>Default project is not enabled.</strong>'); + + // Link to complete default project setup available + $this->assertRaw('<strong><a href="/admin/config/system/optimizely/settings">Account Info</a></strong>', '** <strong>Link from default project to module settings page available.</strong>'); + + // Navigate to Edit form for Default project + $this->drupalGet('admin/config/system/optimizely/add_update/1'); + + // Title field set to Default, not accessable + $this->assertRaw('<input disabled="disabled" type="text" id="edit-optimizely-project-title" name="optimizely_project_title" value="Default"', '** <strong>Project title field is not editable and set to "Default"</strong>.'); + + // Project Code field not set (Undefined), not accessable + $this->assertRaw('<input disabled="disabled" type="text" id="edit-optimizely-project-code" name="optimizely_project_code" value="Undefined"', '** <strong>Project code field is not editable and set to "Undefined".</strong>'); + + // Link to settings page to set account / Default project code + $this->assertRaw('<a href="/admin/config/system/optimizely/settings">', '** <strong>Link to settings page found to set Default project code.</strong>'); - ); - $node2 = $this->drupalCreateNode($settings); + // Check default Default project path is set to site wide wild card + $this->assertRaw('name="optimizely_path" cols="100" rows="6" class="form-textarea">*</textarea>', '** <strong>Default project path set to site wide wild card "*".</strong>'); + + // * 5. The default project can not be enabled until the account number is entered on the settings page. - // Create the url alias - $edit_node2 = array(); - $edit_node2['source'] = 'node/' . $node2->nid; - $edit_node2['alias'] = $this->randomName(9); - $this->drupalPost('admin/config/search/path/add', $edit_node2, t('Save')); + $this->drupalLogout(); + + } + + /* + * 3. Accessing the account setting page should be blank by default with a message informing the user that the account setting will be used + * for the default project number. + * 4. Test adding the account setting redirects to the project listing page with the account number listed as the disabled project number for the + * default project entry. + */ + + public function testOptimizelyTestDefaultProjectSettings() { + + // Access with privileged user + $this->drupalLogin($this->privileged_user); + + // Access generate module settings page + $this->drupalGet('admin/config/system/optimizely/settings'); + + // Check for blank setting (default) + $this->assertFieldByName('optimizely_id', NULL, '** <strong>The Optimizely ID field is blank.</strong>'); + + // Add Optimizely account setting + $this->optimizely_account_id = rand(1000000,9999999); + $edit = array( + 'optimizely_id' => $this->optimizely_account_id, + ); + $this->drupalPost('admin/config/system/optimizely/settings', $edit, t('Submit')); + + // Check that redirect to project page worked after entering Optimizely account ID in setting page + // $this->assertUrl('/admin/config/system/optimizely', $options = array(), 'Redirected to project listing page -> /admin/config/system/optimizely after submitting Optimizely account ID on setting page.'); + + $this->drupalGet('/admin/config/system/optimizely'); + + // Check that the newly entered Optimizely ID is now listed as the project ID for the Default project + $this->assertRaw('<td class="project-code-column disabled">' . $this->optimizely_account_id . '</td>', '** <strong>Default project is using the Optimizely account setting for project ID -> ' . $this->optimizely_account_id . '.</strong>'); + + // Access add / edit project page for default project + $this->drupalGet('/admin/config/system/optimizely/add_update/1'); + + // Check the project ID setting matches the Optimizely Account ID setting. + $this->assertFieldByName('optimizely_project_code', $this->optimizely_account_id, '** <strong>The Optimizely Project Code matches the Optimizely account ID setting.</strong>'); - // create third page - $settings = array( - 'type' => 'page', - 'title' => $this->randomName(32), - 'body' => array(LANGUAGE_NONE => array(array($this->randomName(64)))), + // Enable the Default project + $edit = array( + 'optimizely_enabled' => 1, + ); + $this->drupalPost('/admin/config/system/optimizely/add_update/1', $edit, t('Update')); + + // Go to project listings page + $this->drupalGet('admin/config/system/optimizely'); + + // Confirm default project *is* enabled + $this->assertRaw('<input id="project-enable-1" name="project-1" checked="checked" type="checkbox" value="1" class="form-checkbox" />', '** <strong>Default project *is* enabled on project listing page.</strong>'); + + $this->drupalLogout(); + + } + + public function testOptimizelyTestDefaultProjectListing() { + + // Access with privileged user + $this->drupalLogin($this->privileged_user); + + // Go to project listings page + $this->drupalGet('admin/config/system/optimizely'); - ); - $node3 = $this->drupalCreateNode($settings); + // Confirm default project is *enabled* + $this->assertRaw('<td class="enable-column enabled">', '** <strong>Default project is enabled on project listing page.</strong>'); - // Create the url alias - $edit_node3 = array(); - $edit_node3['source'] = 'node/' . $node3->nid; - $edit_node3['alias'] = $this->randomName(10); - $this->drupalPost('admin/config/search/path/add', $edit_node3, t('Save')); - - //log out to make sure optimizely_refresh_cache() works - $this->drupalLogout(); - - // check if snippet does not appear on project path page - $this->drupalGet($edit_node1['alias']); - $this->assertNoRaw($snippet, 'snippet not found in markup of project page'); - - // check if snippet appears on other project path pages - $this->drupalGet($edit_node2['alias']); - $this->drupalGetContent(); - $this->assertRaw($snippet, 'snippet found in markup of other page'); - - $this->drupalGet($edit_node3['alias']); - $this->drupalGetContent(); - $this->assertRaw($snippet, 'snippet found in markup of other page'); - } + $edit = array( + 'target_oid' => 1, + 'target_enable' => 0, + ); + $this->drupalPostAJAX('ajax/optimizely', $edit, 'project-1'); + + // Confirm default project is *disabled* + $this->assertRaw('<td class="enable-column disabled">', '** <strong>Default project is disabled on project listing page.</strong>'); + + + $this->drupalLogout(); + + } + } - /** - * Test if snippet is included in path. - */ -class OptimizelyTestIncludeSnippetCase extends DrupalWebTestCase { + +class OptimizelyTestPageSnippetTestCase extends DrupalWebTestCase { + + protected $anonymous_user; + protected $authenticated_user; protected $privileged_user; /** - * OptimizelyTestIncludeSnippetCase getInfo(). + * OptimizelyTestPageSnippetTestCase getInfo(). */ public static function getInfo() { return array( - 'name' => 'Include Snippet Test', - 'description' => 'Ensure that the Optimizely snippet included in project path only.', + 'name' => 'Presence of Optimizely Javascript Snippet Test', + 'description' => 'Test the presence of the Optimizely snippet (Javascript call) on pages (paths) defined in project entries.', 'group' => 'Optimizely', ); } /** - * OptimizelyTestIncludeSnippetCase setUp(). + * OptimizelyTestPageSnippetTestCase setUp(). */ public function setUp() { - parent::setUp('optimizely'); // Enable any modules required for the test - // Create and log in our user. The user has the arbitrary privilege - // 'extra special edit any Optimizely project' which the code uses - // to grant access. - $this->privileged_user = $this->drupalCreateUser(array('administer optimizely', 'create page content', 'edit own page content', 'administer url aliases', 'create url aliases')); - $this->drupalLogin($this->privileged_user); - } - - public function testSnippet() { - //$return = $this->drupalGetTestFiles('html',1024); - + + // Enable any modules required for the test + parent::setUp('optimizely'); + + $this->anonymous_user = $this->drupalCreateUser(array()); + + $this->authenticated_user = $this->drupalCreateUser(array( + 'access content')); + + // Create and log in an admin user. The user will have the privilege + // 'administer optimizely'. This privaged is need to access all administration + // functionality of the module. + $this->privileged_user = $this->drupalCreateUser(array( + 'administer optimizely', + 'create page content', + 'edit own page content', + 'administer url aliases', + 'create url aliases')); + + /* + * Pages + * 1. 1 x page (node/x), no alias + * 2. 1 x page, article + * 2. 3 x page (node/x), 2 x alias - "article/one, article/two" + * 3. 2 x sub page (node/x), 2 x alias - "article/one/sub, article/two/sub" + * 4. <front>, node/x, article/three + * + * Projects + * 1. node/x, + * 2. article/one + * 3. node/x, article/one, node/x + * 4. article/one, node/x, article/two + * 5. node/* + * 6. article/* <-- Multi matches: article, article/one, article/two, article/one/sub, article/two/sub + * 7. <front> (article/one) + * 8. <front>, article/one <-- non unique path + * 9. node/*, article/* <-- non unique path + * 10. article/one/* <-- Multi matches: article/one/sub + * 11. article, article/one, article/one/* + * 12. article, node/x, article/one, article/two/* + * 13. node/x, article/one, article/two/*, user, user/* + * 14. article/one?param=xx&parm2=xxx + * 15. node/x, article/one, article/two/*, user/*, article?param=xx&parm2=xxx + * + * ++ multi projects enabled + */ + + // Test page creation + + // Test Project creation $edit = array( 'optimizely_project_title' => $this->randomName(8), 'optimizely_project_code' => rand(0,10000), 'optimizely_path' => $this->randomName(8), - 'optimizely_include' => 1, //snippet should appear only on the project path page - 'optimizely_enabled' => 1, + 'optimizely_include' => rand(0, 1), + 'optimizely_enabled' => rand(0, 1), ); - $snippet = 'http://cdn.optimizely.com/js/' . $edit['optimizely_project_code'] . '.js'; $this->drupalPost('admin/config/system/optimizely/add_update', $edit, t('Add')); - - // create first page - $settings = array( - 'type' => 'page', - 'title' => $this->randomName(32), - 'body' => array(LANGUAGE_NONE => array(array($this->randomName(64)))), - ); - $node1 = $this->drupalCreateNode($settings); - - // Create the url alias - $edit_node1 = array(); - $edit_node1['source'] = 'node/' . $node1->nid; - $edit_node1['alias'] = $edit['optimizely_path']; - $this->drupalPost('admin/config/search/path/add', $edit_node1, t('Save')); - - // create second page - $settings = array( - 'type' => 'page', - 'title' => $this->randomName(32), - 'body' => array(LANGUAGE_NONE => array(array($this->randomName(64)))), - - ); - $node2 = $this->drupalCreateNode($settings); - - // Create the url alias - $edit_node2 = array(); - $edit_node2['source'] = 'node/' . $node2->nid; - $edit_node2['alias'] = $this->randomName(9); - $this->drupalPost('admin/config/search/path/add', $edit_node2, t('Save')); - - // create third page - $settings = array( - 'type' => 'page', - 'title' => $this->randomName(32), - 'body' => array(LANGUAGE_NONE => array(array($this->randomName(64)))), - ); - $node3 = $this->drupalCreateNode($settings); + } + + /* + * 1. + */ + public function testOptimizelyTestPageSnippetTestForPresence() { + + } - // Create the url alias - $edit_node3 = array(); +} +y(); $edit_node3['source'] = 'node/' . $node3->nid; $edit_node3['alias'] = $this->randomName(10); $this->drupalPost('admin/config/search/path/add', $edit_node3, t('Save')); diff --git a/templates/optimizely-account-settings-form.tpl.php b/templates/optimizely-account-settings-form.tpl.php index 46067aef56b4ea30b8a85a1ac1f614fbe54f68dc..6376c731e742c3f63e0ba2d3b255c0af7c51eaf0 100644 --- a/templates/optimizely-account-settings-form.tpl.php +++ b/templates/optimizely-account-settings-form.tpl.php @@ -2,7 +2,4 @@ <p>Most of the configuration and designing of the A/B tests is done by logging into your account on the <a href="http://optimize.ly/OZRdc0/">Optimizely website</a>.</p> -<p>The default Project javascript (js) file (snippet) uses the Optimizely account ID for it's file name. The "Default" / first project entry in the - <a href="/admin/config/system/optimizely">Project Listing</a> page uses the account ID value for the Project Code setting. The Default project entry targets all pages site wide with it's initial settings. Enabling this entry will result in the initial Optimizely experiments running site wide.</p> - <?php echo drupal_render_children($form) ?> \ No newline at end of file diff --git a/templates/optimizely-add-update-form.tpl.php b/templates/optimizely-add-update-form.tpl.php index f4c5feb05c23f34243b614a5ca27b57dd8b1e107..8a95f39767ad29d1c4d0189f8dcb70e6ffe7eba6 100644 --- a/templates/optimizely-add-update-form.tpl.php +++ b/templates/optimizely-add-update-form.tpl.php @@ -1,14 +1,17 @@ <p>In order to use this module, you'll need an <a href="http://optimize.ly/OZRdc0">Optimizely account</a>. A Free 30 day trial account is available.</p> + +<p>The basic configuration and design of the A/B tests is done by logging into your account on the <a href="http://optimize.ly/OZRdc0" target="_NEW">Optimizely website.</a></p> -<?php if ($variables['form']['optimizely_project_code']['#default_value'] == 0): ?> +<?php if (($variables['form']['optimizely_project_code']['#default_value'] == 0) && ($variables['form']['optimizely_oid']['#value'] == 1)): ?> <ul> <li><strong>Add the account ID to the <a href="/admin/config/system/optimizely/settings">Account Info</a> settings page to be able to enable this entry</strong>.</li> </ul> + + <p>The default Project javascript (js) file (snippet) uses the Optimizely account ID for it's file name. The "Default" / first project entry in the + <a href="/admin/config/system/optimizely">Project Listing</a> page uses the account ID value for the Project Code setting. The Default project entry targets all pages site wide with it's initial settings. Enabling this entry will result in the initial Optimizely experiments running site wide.</p> <?php endif; ?> - -<p>The basic configuration and design of the A/B tests is done by logging into your account on the <a href="http://optimize.ly/OZRdc0">Optimizely website.</a></p> <?php echo drupal_render_children($form) ?> \ No newline at end of file