Making a whole site location aware - a design question
I have a question for the masters. If you are an impatient master, jump over the context and to the second to last sentence for the question. A friend and I are well into building up a Drupal 4.7 based site where the user's location is taken into account in nearly every display. The site, www.folkjam.org (or folkjam.org for some), is most simply a database of acoustic music jam sessions (Irish sessions, bluegrass, folk, fiddle, stuff like that). When a user visits the site, we determine their lat/lon from an IP to lat/lon database (a custom module we've added), or from user selection based on US zip code or finally from the zip code that the location module injects into the user's login record when a user registers and configures it. By the time we are done, every display in the site will show only nodes that a) have no locative data on them or b) Have locative data that is within a 90 mile radius of the user's configured location. We've come pretty far, but there is one key place where we need some direction. We intend to contribute much of our work back to Drupal (which is why I'm asking for help - I want the changes to be palatable). There are seven pieces of information that we need to store in a way that other modules can leverage them. Three are critical to a location-aware Drupal site. Four are important for display. They are: The site visitor's Latitude, Longitude, Radius (stored in miles), Country code, Postal code, Province, City. The Lat/Lon/Radius are the critical three. We are presently keeping these in session variables wrapped in accessor functions. The accessor functions are namespaced as part of the site specific glue module that brings most of our customizations together, and the session variables are too. Neither are appropriate for direct calls from contributed modules such as the event.module. In making event.module capable of only showing event-enabled nodes that are within $search_radius of a lat/lon, we started by adding GET style parameters and passing lat/lon/radius into the event.module. Because these three pieces of information needed to go all the way down the event module's call stack to the data retrieval routine, most functions had their signatures extended. Completing this work will require touching the entire event module theme as well, since the 'next month', etc navigation links all need the lat= lon= radius= parameters on the URL. We are not happy with this solution as something to contribute back. What we would rather do is store the seven location awareness information tidbits in a 'well recognized location' where other modules can know to use them. Then we want to add a setting to event.module that can be enabled to restrict events to within a search radius of the visitor's 'current location', and then modify the core data logic and display building logic in event.module to constrain the events it retrieves based on the lat/lon/radius in the 'well recognized location' when the location module is installed AND for nodes that have locative data AND when the active user has a lat/lon/radius configured. In reading the notes from the DrupalCon on Vancouver it seems that location awareness was a topic - I hope there is already some thought about this that I just didn't read. While I suspect that the $user object is probably the place to keep this, I am uncertain of how much we can rely on the $user object when the user is not logged in - we want to be able to customize displays even when users don't register with us or log in. Before fixing that little pocket of ignorance and becoming expert on the nuances of $user, I thought I'd seek advice. Hopefully that is enough context for you. Here's the question: What is an appropriate "well recognized location" within Drupal to keep these seven pieces of information? (latitide, longitude, search radius (storage in miles), country code, province, postal code and city. I'm looking forward to having an answer (or seven :) ) so that we can get closer to having patches in the patch queue. Scott
Consider... Write a module which stores this data however you see fit. (Could be a table you create that joins the users table on uid). Then, use hook_db_rewrite_sql to filter out any nodes which have location information which is not within the users radius. This way, your work applies to any node with location information, not just events. And the event (or any other) module does not have to change at all. If you take this approach, your module is essentially a location-based access control module. Read up on how node.module uses hook_db_rewrite_sql to filter nodes based on the node_access table and emulate that. -Dave On Thursday 17 August 2006 19:39, drupal@mclewin.com wrote:
I have a question for the masters. If you are an impatient master, jump over the context and to the second to last sentence for the question. [snip] Hopefully that is enough context for you. Here's the question: What is an appropriate "well recognized location" within Drupal to keep these seven pieces of information? (latitide, longitude, search radius (storage in miles), country code, province, postal code and city.
I'm looking forward to having an answer (or seven :) ) so that we can get closer to having patches in the patch queue.
Scott
Dave, I will seriously consider that route. I'm only vaguely aware that the db_rewrite_sql hook exists but don't know all of what it can do for me. I will head off to study it this evening. I will want a few exceptions to having displays filter based on location (admin interfaces and search engine friendly lists of everything in the db). If I use hook_db_rewrite_sql to filter as you suggest below, can I also selectivly disable the rewrite? (If RTFM is the most appropriate answer here I'll take it :) ) Scott
Consider...
Write a module which stores this data however you see fit. (Could be a table you create that joins the users table on uid).
Then, use hook_db_rewrite_sql to filter out any nodes which have location information which is not within the users radius. This way, your work applies to any node with location information, not just events. And the event (or any other) module does not have to change at all.
If you take this approach, your module is essentially a location-based access control module. Read up on how node.module uses hook_db_rewrite_sql to filter nodes based on the node_access table and emulate that.
-Dave
On Thursday 17 August 2006 19:39, drupal@mclewin.com wrote:
I have a question for the masters. If you are an impatient master, jump over the context and to the second to last sentence for the question. [snip] Hopefully that is enough context for you. Here's the question: What is an appropriate "well recognized location" within Drupal to keep these seven pieces of information? (latitide, longitude, search radius (storage in miles), country code, province, postal code and city.
I'm looking forward to having an answer (or seven :) ) so that we can get closer to having patches in the patch queue.
Scott
On Thursday 17 August 2006 22:00, drupal@mclewin.com wrote:
Dave,
I will want a few exceptions to having displays filter based on location (admin interfaces and search engine friendly lists of everything in the db). If I use hook_db_rewrite_sql to filter as you suggest below, can I also selectivly disable the rewrite? (If RTFM is the most appropriate answer here I'll take it :) )
This won't be difficult. For anonymous users (including search engines) you wont know a location, so you can simply allow them to see all locations. For other users, you can create a permission 'access all locations', then use user_access() to test that permission before rewriting the query. RTFM and experiment. It should not be too difficult. -Dave
On Fri, 2006-08-18 at 01:00 -0400, drupal@mclewin.com wrote:
Dave,
I will seriously consider that route. I'm only vaguely aware that the db_rewrite_sql hook exists but don't know all of what it can do for me. I will head off to study it this evening.
I will want a few exceptions to having displays filter based on location (admin interfaces and search engine friendly lists of everything in the db). If I use hook_db_rewrite_sql to filter as you suggest below, can I also selectivly disable the rewrite? (If RTFM is the most appropriate answer here I'll take it :) )
Scott
Consider...
Write a module which stores this data however you see fit. (Could be a table you create that joins the users table on uid).
Then, use hook_db_rewrite_sql to filter out any nodes which have location information which is not within the users radius. This way, your work applies to any node with location information, not just events. And the event (or any other) module does not have to change at all.
If you take this approach, your module is essentially a location-based access control module. Read up on how node.module uses hook_db_rewrite_sql to filter nodes based on the node_access table and emulate that.
-Dave
On Thursday 17 August 2006 19:39, drupal@mclewin.com wrote:
I have a question for the masters. If you are an impatient master, jump over the context and to the second to last sentence for the question. [snip] Hopefully that is enough context for you. Here's the question: What is an appropriate "well recognized location" within Drupal to keep these seven pieces of information? (latitide, longitude, search radius (storage in miles), country code, province, postal code and city.
I'm looking forward to having an answer (or seven :) ) so that we can get closer to having patches in the patch queue.
Scott
I did something like that for site that ever got out the door... also gave users a ui to manage 'search', but was using taxonomy terms to do it... btw, postgres has some nifty gis plugin's that make querying that kind of stuff easy...
Dave, I've run into a problem that may be easily solved if one knows the right SQL trick. How do I know when to apply my JOIN and WHERE clauses to a given node query? I've run into a condition where my nodes with locative data filter exactly as I want them to based on location, but nodes without locative data don't show up (I know why - it is because I INNER JOINED them with the location table, which yields no result). At the point where hook_db_rewrite_sql is called, how do I know that I'm dealing with a node that has location information? If this thread has become inappropriate for the overall developers list, I've placed a copy of it on http://mclewin.com/node/495. My hook_db_rewrite_sql implementation is posted there as well. Scott Dave Cohen wrote:
Consider...
Write a module which stores this data however you see fit. (Could be a table you create that joins the users table on uid).
Then, use hook_db_rewrite_sql to filter out any nodes which have location information which is not within the users radius. This way, your work applies to any node with location information, not just events. And the event (or any other) module does not have to change at all.
If you take this approach, your module is essentially a location-based access control module. Read up on how node.module uses hook_db_rewrite_sql to filter nodes based on the node_access table and emulate that.
-Dave
On Thursday 17 August 2006 19:39, drupal@mclewin.com wrote:
I have a question for the masters. If you are an impatient master, jump over the context and to the second to last sentence for the question.
[snip]
Hopefully that is enough context for you. Here's the question: What is an appropriate "well recognized location" within Drupal to keep these seven pieces of information? (latitide, longitude, search radius (storage in miles), country code, province, postal code and city.
I'm looking forward to having an answer (or seven :) ) so that we can get closer to having patches in the patch queue.
Scott
On Monday 21 August 2006 23:15, Scott McLewin wrote:
I've run into a condition where my nodes with locative data filter exactly as I want them to based on location, but nodes without locative data don't show up (I know why - it is because I INNER JOINED them with the location table, which yields no result).
Have you tried the following? $return['where'] = "a.longitude IS NULL OR your_where_clause_here"; If that doesn't work... If you can enforce that a certain node types always have location data, your where clause could like like $return['where'] = "$primary_table.type IN (list_of_node_types) OR your_where_clause"; You could try a nested query, but this may have problems with efficiency and portability $return['where'] = "$primary_table.nid NOT IN (SELECT oid FROM {location}) OR your_where_clause"; And you could write a hook_nodeapi that inserts a row in location for every node, inserting NULL for values. Then use the first query above. This may have a problem for nodes inserted before your module was installed, however.
If this thread has become inappropriate for the overall developers list, I've placed a copy of it on http://mclewin.com/node/495. My hook_db_rewrite_sql implementation is posted there as well.
I'm writing here in case someone has a better idea. Finally, I recommend you don't alias {location} to 'a'. It's really not a descriptive abbreviation, and likely to cause conflicts with some other hook_db_rewrite_sql eventually. Why not use 'location' and never have to worry about that? -Dave
Dave, Dave Cohen wrote:
On Monday 21 August 2006 23:15, Scott McLewin wrote:
I've run into a condition where my nodes with locative data filter exactly as I want them to based on location, but nodes without locative data don't show up (I know why - it is because I INNER JOINED them with the location table, which yields no result).
Have you tried the following?
$return['where'] = "a.longitude IS NULL OR your_where_clause_here";
Yes, but in looking back it didn't work because when I tried it I still had an inner join. Switching to a LEFT OUTER JOIN got me up and running. My bad for working with a bleary brain.
Finally, I recommend you don't alias {location} to 'a'. It's really not a descriptive abbreviation, and likely to cause conflicts with some other hook_db_rewrite_sql eventually. Why not use 'location' and never have to worry about that?
I picked "a" because that is what the location module uses for an alias to {location} and because I was unsure if I had to use "standard practice table names" within the hook_db_rewrite_sql. I've convinced myself that I don't and have changed to a far-less-likely-to-conflict alias name. Thanks a bunch for the suggestion on hook_db_rewrite_sql. The documentation and threads on it that I'd found never quite made it clear why I'd use it. A run at testing my site's logic with the location specific filtering in place looks good. Scott
Dave Cohen wrote:
Consider...
Write a module which stores this data however you see fit. (Could be a table you create that joins the users table on uid).
Then, use hook_db_rewrite_sql to filter out any nodes which have location information which is not within the users radius. This way, your work applies to any node with location information, not just events. And the event (or any other) module does not have to change at all.
If you take this approach, your module is essentially a location-based access control module. Read up on how node.module uses hook_db_rewrite_sql to filter nodes based on the node_access table and emulate that.
The use of hook_db_rewrite_sql did what I wanted for making displays only return nodes that are near the visitor's location. I use the term visitor here carefully to distinguish from $user - even anonymous users are given a lat/lon location based on their IP, a configured site default or self-selected. Every visitor has a valid lat/lon. Checking for anonymous users to control the hook_db_rewrite_sql behavior is not an option for me in this circumstance. There are a handful of side effects to using db_rewrite_sql that I am working through. Some are rather subtle (at least to me). They include the following cases: While I want displays such as event calendars and taxonomy lists to restrict display on location aware node type to nodes which are geographically close to the user, I do want search to search the entire site. Without exception logic, search results are restricted only to those nodes that are within the geographic search radius of the current visitor. For search to work, the indexer also needs to be able to see all nodes. Even hook_cron() triggered page loads end up with a location (the site's configured default). Thus the indexer never sees nodes that are outside of the default search radius from the site's default location. For a module that I don't intend to contribute back I'd twiddle the order of my module in the system table so that it loaded first, then use the module's hook_cron() and hook_search() functions to disable my hook_db_rewrite_sql() for that page load (I've got a global to control that). I intent to contribute the location based filter module. What I am looking for are early entry points in the call path where I can disable the location filter. For example, to handle data entered into the search form, I tried hook_form_alter to add a hidden element with a validation function that just disabled the location filter for that page load. That didn't do the trick as it appears that the page reloads after my validation function is called (and the page reload resets the global that disables location filtering for the page load). Are there more general purpose entry points that will allow me to disable the hook_db_rewrite_sql logic that filters by location in cases such as cron jobs and search form entry (I'm certain that there will be others), or are there status/state information tidbits within Drupal that I can peek at to know, for example, that cron is running? Scott
I'm very much looking forward to this module being contributed back to Drupal. I hope someone can help Scott out with the very last part-- disabling in specific cases the hook_db_rewrite_sql logic that filters by location. - ben melançon On 8/25/06, Scott McLewin <drupal@mclewin.com> wrote:
Dave Cohen wrote:
Consider...
Write a module which stores this data however you see fit. (Could be a table you create that joins the users table on uid).
Then, use hook_db_rewrite_sql to filter out any nodes which have location information which is not within the users radius. This way, your work applies to any node with location information, not just events. And the event (or any other) module does not have to change at all.
If you take this approach, your module is essentially a location-based access control module. Read up on how node.module uses hook_db_rewrite_sql to filter nodes based on the node_access table and emulate that.
The use of hook_db_rewrite_sql did what I wanted for making displays only return nodes that are near the visitor's location. I use the term visitor here carefully to distinguish from $user - even anonymous users are given a lat/lon location based on their IP, a configured site default or self-selected. Every visitor has a valid lat/lon. Checking for anonymous users to control the hook_db_rewrite_sql behavior is not an option for me in this circumstance.
There are a handful of side effects to using db_rewrite_sql that I am working through. Some are rather subtle (at least to me). They include the following cases:
While I want displays such as event calendars and taxonomy lists to restrict display on location aware node type to nodes which are geographically close to the user, I do want search to search the entire site. Without exception logic, search results are restricted only to those nodes that are within the geographic search radius of the current visitor.
For search to work, the indexer also needs to be able to see all nodes. Even hook_cron() triggered page loads end up with a location (the site's configured default). Thus the indexer never sees nodes that are outside of the default search radius from the site's default location.
For a module that I don't intend to contribute back I'd twiddle the order of my module in the system table so that it loaded first, then use the module's hook_cron() and hook_search() functions to disable my hook_db_rewrite_sql() for that page load (I've got a global to control that). I intent to contribute the location based filter module.
What I am looking for are early entry points in the call path where I can disable the location filter.
For example, to handle data entered into the search form, I tried hook_form_alter to add a hidden element with a validation function that just disabled the location filter for that page load. That didn't do the trick as it appears that the page reloads after my validation function is called (and the page reload resets the global that disables location filtering for the page load).
Are there more general purpose entry points that will allow me to disable the hook_db_rewrite_sql logic that filters by location in cases such as cron jobs and search form entry (I'm certain that there will be others), or are there status/state information tidbits within Drupal that I can peek at to know, for example, that cron is running?
Scott
Scott, I realize this thread is rather old. I've been away from my normal email client for a while and I lost track of it. Perhaps you've already addressed the issues. If not here's one suggestion. On Friday 25 August 2006 00:24, Scott McLewin wrote:
For search to work, the indexer also needs to be able to see all nodes. Even hook_cron() triggered page loads end up with a location (the site's configured default). Thus the indexer never sees nodes that are outside of the default search radius from the site's default location.
In your module settings, you could something similar to the block configuration. When configuring a block, you have a chance to show the block only on certain pages, or hide it on certain pages. In your case, you could configure your db_rewrite hook to be called in most cases, but not /cron.php or /search/*. Look at the code in block.module for an example. -Dave
On Aug 17, 2006, at 7:39 PM, drupal@mclewin.com wrote:
Here's the question: What is an appropriate "well recognized location" within Drupal to keep these seven pieces of information? (latitide, longitude, search radius (storage in miles), country code, province, postal code and city.
CivicSpace developed this location search module and sketched in some ideas for user location search as well. If you were interested in taking over this module and making a full project that would be appreciated. http://cvs.drupal.org/viewcvs/drupal/contributions/sandbox/nedjo/ modules/locationsearch/ Hope that helps, I didn't have time to ensure it met your requirements exactly. Kieran
participants (6)
-
Agaric Design -
Darrel O'Pry -
Dave Cohen -
drupal@mclewin.com -
Kieran Lal -
Scott McLewin