Using an Openlayers map with multiple views with Drupal - drupal

I have a content type with geo data that I'd like to display on a map based on some contextual filters. There are multiple views on the site with different configs. I have defined my map, its behaviors, default layer, etc. and it works with an "all nodes of x type" view using an Openlayers data overlay and a map display.
A second view has it's own data overlay with a different set of points and a map display. I'd like to reuse the same Openlayers map so that any future changes to behaviors, zoom levels, etc. don't need to be made across multiple maps.
The problem I'm having is that whatever layer I define as activated in the Openlayers map is the default layer for all the views using that map. I need some way to define a default activated layer based on the view, url, or something else. Any ideas on how to achieve this?

First you have to create or clone and modify a view.
Second, you have to create another Openlayers map in /admin/structure/openlayers/maps/list/, and at the layers and styles/overlay layers you have to enable the layer of the newly created view.
Third, you have to edit the view and at the view format's settings you can select the new map.

Came up with a single view solution awhile back and forgot to post it. This allows you to create a single view using a single OpenLayers Map block with multiple data overlays within the same view, setting the active one based on URL. The benefits of this approach are that a change to one field populates across all of the displays and changing the map's styling, behavior, etc. only needs to be done once. This simplifies maintenance and changes for folks.
Here's what worked:
1. Create you OpenLayers map, layer definitions, etc. as you normally would.
2. Create a new view with the Format: OpenLayers Map. Configure it as you normally would.
3. To the OpenLayers Map view, add new OpenLayers Data Overlay displays for each source.
4. Edit the OpenLayers Map. Under Overlay Layers, check enabled and in switcher for each new Data Overlay. In my case, the Activated checkboxes are all unchecked.
5. Implement hook_openlayers_map_preprocess_alter(). Here's what mine looks like
function mymoduleortheme_openlayers_map_preprocess_alter(&$map = array()) {
//$displayLayer['URL param'] = "map_data_overlay_machine_name";
$displayLayer = array();
$displayLayer['first-page'] = "openlayers_maps_openlayers_first_page_data";
$displayLayer['another-page'] = "openlayers_maps_openlayers_another_page_data";
$currentURI = array();
$currentURI = explode("/", $_SERVER['REQUEST_URI']);
foreach ($map['layer_switcher'] as $idx => $val) {
//Map data found, activate the correct
if (isset($currentURI[2]) && isset($displayLayer[$currentURI[2]]) && $idx == $displayLayer[$currentURI[2]]) {
$map['layer_activated'][$displayLayer[$currentURI[2]]] = $displayLayer[$currentURI[2]];
}
else
$map['layer_switcher'][$idx] = 0;
}
}
Probably room for improvement, but it's working and does the trick. The switcher isn't used on this site so it's just CSS'd out.

Related

Dynamically changing a portion of a Drupal page based on clicking links from a view

Introduction
I'm a bit new to Drupal, so I'm still trying to wrap my head around how all the different ways of implementing dynamic content work. Let me explain my desired end state and explain some methods I have tried. Also, I'm using Drupal 7.
Desired end state
I want a page that pulls a context argument from the URL to display content (level 1) based on the Taxonomy Term ID in the URL. On this page, fields from the selected content node are displayed and a view displays associated sub-content (level 2) in a grid of icons. When one of the sub-content (level 2) icons are clicked, another region of the page updates with fields from the sub-content (level 2) node that match the icon selected. This pane should update with AJAX to prevent page reloads.
Current design
What I have so far is a PAGE that uses contextual filters to pass the Taxonomy Term ID as an argument in the URL to display the correct page content. This works.
To this page, I have added a panel for the content (level 1) I want to display. Additionally, I added a VIEW panel that displays only the sub-content (level 2) icons associated with the content (level 1). This works.
Attempted solutions
I am at a loss for how to display the NODE content for the selected sub-content (level 2) icon. I made a PAGE with the appropriate arguments to display exactly what I want. The intention being that I would add the PAGE as a panel to the existing PAGE and pass the arguments to this PAGE by rewriting the icon linking and setting the target parameter. I have not found a way to embed a page within a page.
My next thought was to create a VIEW that used contextual filters to display only the NODE content for the selected sub-content, but I haven't found a way to pass the contextual filters from a click of the icon to the VIEW.
Conclusion
I'm sure there are many ways to do this, but I am looking for the least invasive way. What I mean by that, is that I'm looking for a method using existing techniques within panels, panelizer, views, etc. if it exists. As a last resort, any custom code modifications would have to be contained within a module for ease of maintenance. I'm not exactly sure what I'm looking for because the terminology for everything in Drupal is still confusing to me, but some pointers in the right direction would be great.
Here's a mockup of what I'm describing:
I found a method of doing this that is working like a charm.
What I did:
I used Panelizer to modify the appearance of a node of content type 1, and added the fields I wanted displayed.
Next, I created a View for the sub-content icons. One of the fields for the sub-content is an Entity Reference to specific nodes of content type 1. I exploited this reference with a Views Relationship and a Views Contextual Filter to limit the output of my view to sub-content related to the specified node ID. I defined this node ID in the panel settings for the view in Panelizer Content by selecting "Content ID" as the context.
I added the "Nid" (Node ID) field to the view, but excluded the output. Next, I applied a rewrite rule to the Icon field and changed the link path to an absolute path of "ajax-viewer/nojs/[nid]".
Next, I created another view for the full display of the sub-content. I applied the same Views Relationship and Views Contextual Filter as the other view, however I didn't specify the context in the Panelizer Content settings, but I did add a CSS ID. I added a Content Pane to the view and set the name of the pane. Also I turned "Use AJAX" to "On".
I created one more view for the icons of sub-sub-content, but used the entity reference to sub-content instead of content. I also didn't define the context in Panelizer Content, but did define a CSS ID. I made the view a Content Pane and defined the name of the pane.
This set up all of the structure to the page, but I still had to make a module to power the AJAX call. I referenced this page for the idea. However, I made a second helper function for my second view. In Sean's example, the views call (which I used) has to be changed to match the view.
return views_embed_view ('machine-name-of-view','name-of-content-pane-in-view', $nid);
Since one function can only return one value, I modified the ajax_link_response() function a bit...
function ajax_link_response($type = 'ajax', $nid = 0) {
/* These next two lines are different */
$sub-content-output = _ajax_reader_load_noder_sub_content($nid);
$sub-sub-content-output = _ajax_reader_load_noder_sub_sub_content($nid);
if ($type == 'ajax') {
$commands = array();
/* These next two lines are different. The # and id= must match the CSS IDs set in Panelizer Content. */
$commands[] = ajax_command_replace('#sub-content', '<div id="sub-content">' . $sub-content-output . '</div>');
$commands[] = ajax_command_replace('#sub-sub-content', '<div id="sub-sub-content">' . $sub-sub-content-output . '</div>');
$page = array(
'#type' => 'ajax',
'#commands' => $commands
);
ajax_deliver($page);
}
else {
/* These next three lines are different. Same as above with the CSS IDs. */
$sub-content-output = '<div id="sub-content">' . $sub-content-output . '</div>';
$sub-sub-content-output = '<div id="sub-sub-content">' . $sub-sub-content-output . '</div>';
$output = $sub-content-output + $sub-sub-content-output;
return $output;
}
}
One last thing I did was to not include the _init() function in Sean's example as the view was activating AJAX already since I set it on. Including the _init() function caused AJAX on my forms to break.
This set-up works like a charm. One last thing I did was to specify a default Node ID in the Views Contextual Filter for the sub-content and sub-sub-content so that initial content is displayed.

How to structure Marionette with more than one CompositeView from the same kind?

We have to display the following view:
Categories (displayed as tables), each category table should have products (tr).
How can we structure it using Marionette itemView and CompositeView?
We need the product to be an itemView.
Should we make each category as CompositeView, and have more than one CompositeView? or is there any other alternative to include all compositeViews into a single item, like:
Collection(CompositeView???)
-> CompositeView-category1
-> itemView-product1-1
-> itemView-product1-2
-> CompositeView-category2
-> itemView-product2-1
-> itemView-product2-2
I would suggest you to use a Layout view, inside this layout you can have a region for each category and draw your composite views on these regions.
the benefit of the Layout view in marionette is that you can pass a template that should contain all the HTML that you need initially, for example a header with buttons to add and remove categories etc.
CategoriesLayout = Backbone.Marionette.Layout.extend({
template: "#layout-template",
regions: {
category1Region: "#category1",
category2Region: "#category2"
}
});;
and then you can render your composite views inside the regions
categoriesLayout = new CategoriesLayout();
categoriesLayout.category1Region.show(new CategoryView());
this way you can show/close your views using the Region functionality. If you need to add the regions on the fly that is also possible if you render the HTML with Marionette.renderer and then attach your regions to the Layout.
additionally you may want to take a look at the region manager of Marionette that helps you to manage a set of regions.
EDIT
I did a jsfiddle to demonstrate this, this is just an example but I think the idea is there...
http://jsfiddle.net/VS9hA/28/
MyLayout = Backbone.Marionette.Layout.extend({
template: "#layout-template",
regions: {
menu: "#menu",
}
});
var layout = new MyLayout({el:"#container"});
layout.render();
var dynamicRegion = sampleModel.get('categoryName');
layout.addRegion(dynamicRegion,"#dynamic"); // adding the region on the fly
layout[dynamicRegion].show(sampleView); // using it!
so the answer to your question in the comment
categoriesLayout[dynamicRegion].show(new CategoryView())
just make sure the DOM element is there (#dynamic) if its not, just append the HTML on your layout view or use the marionette.Renderer Object to pass a nice template for your region.
And there is no problem adding a region on the fly, marionette has a method for that scenario.
Hope that helps,
I would advise against using Layouts for your top level view because then you have to manually create a region for each new category you add. Using a CollectionView or CompositeView on the other hand allows for an unlimited number of categories (and even an emptyView when there are none) without extra work.
The decision of whether to use a CompositeView or CollectionView as the top view will depend on whether or not you need a template wrapper for it (for buttons, etc). If you don't then just go with a CollectionView.
I think you're on the right track with each category being a CompositeView so you can have the template with tbody as the 'itemViewContainer':
<table>
<tbody>
</tbody>
</table>
in the category view:
itemViewContainer: 'tbody'
And then the products (ItemViews) will use:
tagName: 'tr'

History.js implementation

I'm having trouble implementing history.js
I have a single page website that uses a jquery slider (royal slider) to slide full page divs that act as pages. The slider is operated using custom links. What I would like to do is have each link manipulate the browser history in order to 1. change the page URL. and 2. push states to the browser that are refreshable and can be navigated to and from via the browser's forward and back buttons.
What I'm not sure about is whether any pushstate will save the sliders current slide?
It's not essential if not, as the change in URL is the main thing.
Each link is controlled via a javascript click function that looks like this...
var workclick = document.getElementById('worklink');
workclick.onclick = function () {
thefooter.style.opacity = "1";
thefooter.style.filter = 'alpha(opacity=100)';
thefootdivide.style.opacity = "1";
thefootdivide.style.filter = 'alpha(opacity=100)';
$(".royalSlider").royalSlider('goTo', 1);
}
Can somebody help me with what I need to add to my page to get history.js to work with what I have here?
Thanks so much for any help
I am not an expert on History.js, but my understanding is that once it's installed, each time a slider value is changed you could call replaceState() with url parameters that reflect their values something like :
History.replaceState( null, 'My Title', '?slider1=' + $('#slider1').value );
And then, when your page loads have it read these request parameter values and (if present) set the sliders accordingly.
Again I'm not sure this is exactly what you need but I'm pretty sure it's on the right track.

Pre-Select Images when opening WordPress 3.5 media manager

I've been playing around with the new media manager in WordPress and had some fun with it, but have reached the point where I'm banging my head against a wall.
I have a custom meta box that I'd like to store some images in (well it's a hidden input and I'm currently storing their ID's, but could equally be the image objects), then making an AJAX call to show some thumbnails, which I have subsequently made draggable so users can reorder (not necessarily relevant just some background).
My problem is that when I open the media manager, no images are selected, so if a user wants to edit the pictures in their gallery they need to select them all again.
What I'm trying to figure out, is how do I open the media manager with the current images passed through so they are pre-selected.
So, broadly, my code looks like this
jQuery('#myButton').click(function(e) {
e.preventDefault();
frame = wp.media({
title : 'My Gallery Title',
multiple : true,
library : { type : 'image'},
button : { text : 'Insert' },
});
frame.on('close',function() {
// get selections and save to hidden input plus other AJAX stuff etc.
}
frame.open();
});
My thought is that there must be either a parameter to pass into the frame (probably a JSON object of the images, or I need to create an event for
frame.on('open', function() {
// Set selected images
}
But I have tried both ways round and am not getting anywhere.
It would appear possible, as changing the 'Featured Image' takes you to the library with the current one selected - I've just been unable to understand the core code sufficiently yet and hope someone else has !
After studying the core for a bit, the answer here is really quite straightforward.
Listen for the open event of the wp.media object, grab the state, create attachment objects with your id's and add them to the selection.
frame.on('open',function() {
var selection = frame.state().get('selection');
var ids_value = jQuery('#my_field_id').val();
if(ids_value.length > 0) {
var ids = ids_value.split(',');
ids.forEach(function(id) {
attachment = wp.media.attachment(id);
attachment.fetch();
selection.add(attachment ? [attachment] : []);
});
}
});
This works when selecting multiple images as well as single and assumes that using the code above you have stored the values in a single text/hidden field with comma separation.
not a real answer, but somethings that I have noticed
using your code the frame.open( console.log('open') ) does trigger the console.log.
The other frame.on('open', function() { console.log('on->open')}) does not.
When looking at the post edit page. (Where a featured image is already set).
If you open the featured img window a few things happen that are interesting.
WP does 3 ajax calls, the 1st and 3rst contain the featured img id. the 2nd is the same as with your code.
when the popup is loaded the featured image is visible / loaded before the rest of the images. When those show up the featured image is put in the right order.
When looking in firebug at the dom tab I discovered that the var wp.media.model.settings.post.featuredImageId holds (wait for it) the featured image value.
Hopes this helps you in some way.
I think those guy manage to do it :
https://wordpress.stackexchange.com/questions/76125/change-the-default-view-of-media-library-in-3-5/76213#76213
But this doesn't work for me.
I ve got the jquery in the footer of my post/edit, post/new but that just don't work for me :(

Drupal 6 Views: Toggling filters on/off via checkbox?

I have a view I'm using as an "Advanced Search" tool - it simply provides a list of relevant nodes which are limited by a number of exposed filters I have in place. In particular, I have a "keyword" exposed filter that's acting as the main search box, which filters on the node title. Now, I've had a request for the ability to toggle (I assume with a checkbox or similar) this filter to search on both the node title and body at the same time, as an option to provide users with more search results.
I currently have a view set up with two displays: one that filters on just the node title, and one that uses Views Or to limit the results to nodes that have the keywords in either the node title OR the node body. By themselves, both of these displays are working the way I'd like.
My question is how I'd go about toggling between these two displays using a checkbox or something similar. I don't want to use Views Display Tabs because both displays look visually identical (the only difference is how the filters are configured to include more/less results) which would be confusing to the user - plus it uses AJAX which breaks some (mostly small) modifications I've made via jQuery to the behavior of the view.
What I'd like to accomplish, in essence, is a conditional check when the view is submitted - depending on whether a checkbox is selected, the arguments are sent to one display or the other. Any suggestions on how to accomplish this?
One way you might be able to go about this could involve adding a custom validate handler on the exposed filter form that would in theory allow you to check the value and forward the request to a different page display appropriately. Exposed filters are exposed in the URL as $_GET variables, so, forwarding a user to a specific page with filters predefined should be easy enough.
<?php
function mymodule_form_alter(&$form, $form_state, $form_id) {
switch ($form_id) {
case 'views_exposed_form':
// Deterimine the ID so you only do this
// to a specific exposed filter form
// drupal_set_message($form['#id']);
if ($form['#id'] == 'views-exposed-form-api-search-page-1') {
// You might also want to add the checkbox FAPI item in this area
$form['#validate'][] = 'mymodule_api_search_validate'; // custom validate handler function name
}
break;
}
}
function mymodule_api_search_validate($form, &$form_state) {
// Check if the FAPI item has the specified checkbox value
if ($form_state['values']['options'] == 'title') {
// The get variables to pass to the views exposed filters
// You can configure what this $_GET variable should be while editing the filter
$query = array(
'query' => 'the search query',
);
drupal_goto('api/search', $query);
}
}
?>

Resources