Index: includes/database.mysql.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.mysql.inc,v retrieving revision 1.52 diff -u -p -r1.52 database.mysql.inc --- includes/database.mysql.inc 27 Apr 2006 20:38:49 -0000 1.52 +++ includes/database.mysql.inc 29 Apr 2006 03:04:38 -0000 @@ -364,6 +364,58 @@ function db_unlock_tables() { } /** + * Constructs a query fragment to split a database field according to a + * delimiter, and to fetch the specified substring. + * + * @param $string + * The database field to be split. + * @param $delimiter + * The string value to use as a delimiter. + * @param $field + * The number of the field to return. Must be in the range 1 ... x, where x + * is the number of fields in the string. + * @return + * An SQL query fragment, suitable for forming part of a string that gets + * passed to db_query(). + */ +function db_sql_explode($string, $delimiter, $field) { + $sql = "SUBSTRING_INDEX($string, '$delimiter', $field)"; + if ($field > 1) { + $start_field = 1 - $field; + $sql = "SUBSTRING_INDEX($sql, '$delimiter', $start_field)"; + } + + return $sql; +} + +/** + * Constructs a query fragment to match a database field against a regular + * expression pattern. + * + * @param $field + * The database field on which to make the match. + * @param $pattern + * The pattern to use for matching. The pattern should conform to the + * regular expression syntax for both MySQL and PostgreSQL (this should not + * be a problem if the pattern adheres to PHP's PCRE syntax, but check the + * manuals for both database systems if in doubt). + * @param $check_match + * Boolean indicating whether to check for a positive or a negative match. + * Default is TRUE (i.e. positive match). + * @return + * An SQL query fragment, suitable for forming part of a string that gets + * passed to db_query(). + */ +function db_sql_preg_match($field, $pattern, $check_match = TRUE) { + $negate = ''; + if (!$check_match) { + $negate = ' NOT'; + } + + return $field . $negate . " RLIKE '$pattern'"; +} + +/** * @} End of "ingroup database". */ Index: includes/database.mysqli.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.mysqli.inc,v retrieving revision 1.17 diff -u -p -r1.17 database.mysqli.inc --- includes/database.mysqli.inc 27 Apr 2006 20:38:49 -0000 1.17 +++ includes/database.mysqli.inc 29 Apr 2006 03:04:38 -0000 @@ -366,6 +366,58 @@ function db_unlock_tables() { } /** + * Constructs a query fragment to split a database field according to a + * delimiter, and to fetch the specified substring. + * + * @param $string + * The database field to be split. + * @param $delimiter + * The string value to use as a delimiter. + * @param $field + * The number of the field to return. Must be in the range 1 ... x, where x + * is the number of fields in the string. + * @return + * An SQL query fragment, suitable for forming part of a string that gets + * passed to db_query(). + */ +function db_sql_explode($string, $delimiter, $field) { + $sql = "SUBSTRING_INDEX($string, '$delimiter', $field)"; + if ($field > 1) { + $start_field = 1 - $field; + $sql = "SUBSTRING_INDEX($sql, '$delimiter', $start_field)"; + } + + return $sql; +} + +/** + * Constructs a query fragment to match a database field against a regular + * expression pattern. + * + * @param $field + * The database field on which to make the match. + * @param $pattern + * The pattern to use for matching. The pattern should conform to the + * regular expression syntax for both MySQL and PostgreSQL (this should not + * be a problem if the pattern adheres to PHP's PCRE syntax, but check the + * manuals for both database systems if in doubt). + * @param $check_match + * Boolean indicating whether to check for a positive or a negative match. + * Default is TRUE (i.e. positive match). + * @return + * An SQL query fragment, suitable for forming part of a string that gets + * passed to db_query(). + */ +function db_sql_preg_match($field, $pattern, $check_match = TRUE) { + $negate = ''; + if (!$check_match) { + $negate = ' NOT'; + } + + return $field . $negate . " RLIKE '$pattern'"; +} + +/** * @} End of "ingroup database". */ Index: includes/database.pgsql.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.pgsql.inc,v retrieving revision 1.29 diff -u -p -r1.29 database.pgsql.inc --- includes/database.pgsql.inc 27 Apr 2006 20:38:49 -0000 1.29 +++ includes/database.pgsql.inc 29 Apr 2006 03:04:39 -0000 @@ -353,6 +353,52 @@ function db_check_setup() { } /** + * Constructs a query fragment to split a database field according to a + * delimiter, and to fetch the specified substring. + * + * @param $string + * The database field to be split. + * @param $delimiter + * The string value to use as a delimiter. + * @param $field + * The number of the field to return. Must be in the range 1 ... x, where x + * is the number of fields in the string. + * @return + * An SQL query fragment, suitable for forming part of a string that gets + * passed to db_query(). + */ +function db_sql_explode($string, $delimiter, $field) { + return "SPLIT_PART($string, '$delimiter', $field)"; +} + +/** + * Constructs a query fragment to match a database field against a regular + * expression pattern. + * + * @param $field + * The database field on which to make the match. + * @param $pattern + * The pattern to use for matching. The pattern should conform to the + * regular expression syntax for both MySQL and PostgreSQL (this should not + * be a problem if the pattern adheres to PHP's PCRE syntax, but check the + * manuals for both database systems if in doubt). + * @param $check_match + * Boolean indicating whether to check for a positive or a negative match. + * Default is TRUE (i.e. positive match). + * @return + * An SQL query fragment, suitable for forming part of a string that gets + * passed to db_query(). + */ +function db_sql_preg_match($field, $pattern, $check_match = TRUE) { + $negate = ' '; + if (!$check_match) { + $negate .= '!'; + } + + return $field . $negate . "~ '$pattern'"; +} + +/** * @} End of "ingroup database". */ Index: modules/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node.module,v retrieving revision 1.641 diff -u -p -r1.641 node.module --- modules/node.module 27 Apr 2006 22:20:51 -0000 1.641 +++ modules/node.module 29 Apr 2006 03:04:41 -0000 @@ -2408,14 +2408,74 @@ function node_access_view_all_nodes() { } /** + * Generate an SQL join clause for use in fetching a menu item. + * + * @param $menu_alias + * If the menu table has been given an SQL alias other than the default + * "m", that must be passed here. + * @param $node_alias + * If the node table has been given an SQL alias other than the default + * "n", that must be passed here. + * @param $node_access_alias + * If the node_access table has been given an SQL alias other than the default + * "na", that must be passed here. + * @return + * An SQL join clause. + */ +function _node_access_menu_join_sql($menu_alias = 'm', $node_alias = 'n', $node_access_alias = 'na') { + if (user_access('administer nodes')) { + return ''; + } + + $path_nid = db_sql_explode($menu_alias . '.path', '/', 2); + + return 'LEFT JOIN {node} '. $node_alias .' ON '. $path_nid .' = '. $node_alias .'.nid LEFT JOIN {node_access} '. $node_access_alias .' ON '. $node_alias .'.nid = '. $node_access_alias .'.nid'; +} + +/** + * Generate an SQL where clause for use in fetching a node listing. + * + * @param $op + * The operation that must be allowed to return a node. + * @param $menu_alias + * If the menu table has been given an SQL alias other than the default + * "m", that must be passed here. + * @param $node_alias + * If the node table has been given an SQL alias other than the default + * "n", that must be passed here. + * @param $node_access_alias + * If the node_access table has been given an SQL alias other than the default + * "na", that must be passed here. + * @return + * An SQL where clause. + */ +function _node_access_menu_where_sql($op = 'view', $menu_alias = 'm', $node_alias = 'n', $node_access_alias = 'na', $uid = NULL) { + if (user_access('administer nodes')) { + return; + } + + $not_node_path = db_sql_preg_match($menu_alias . '.path', '^node/[0-9]+$', FALSE); + + return $node_alias .'.nid IS NULL OR '. $not_node_path .' OR ('. _node_access_where_sql($op, $node_access_alias, $uid) .')'; +} + +/** * Implementation of hook_db_rewrite_sql */ function node_db_rewrite_sql($query, $primary_table, $primary_field) { - if ($primary_field == 'nid' && !node_access_view_all_nodes()) { - $return['join'] = _node_access_join_sql($primary_table); - $return['where'] = _node_access_where_sql(); - $return['distinct'] = 1; - return $return; + if (!node_access_view_all_nodes()) { + if ($primary_field == 'nid') { + $return['join'] = _node_access_join_sql($primary_table); + $return['where'] = _node_access_where_sql(); + $return['distinct'] = 1; + return $return; + } + else if ($primary_field == 'mid') { + $return['join'] = _node_access_menu_join_sql($primary_table); + $return['where'] = _node_access_menu_where_sql(); + $return['distinct'] = 1; + return $return; + } } }