I'm using the feeds module to pull in a set of publications into a Drupal content type. They are set to run at regular intervals using cron. I have two separate feeds, which should work as follows:
Feed 1 (pure_feed) - pulls in the bulk of the fields
Feed 2 (harvard_format) - accesses a separate url source and updates one field on the content type.
The problem I have is that feed 2 always creates a new set of nodes rather than updating the existing nodes (that were created using feed 1). I have used the debug options at /import and can see that the GUIDs for feed 2 match the GUIDs for feed 1, but it still creates 2 sets of nodes rather than updating the 1 set of nodes.
Here is an excerpt from the feeds_items database table:
As you can see they both have the same GUID but they are mapped to separate nodes. Is there any way to have the second feed map to the same nodes as the first feed?
I knocked something together that allows my second feed to update the nodes from my first feed. Not sure if this is the right way of doing things but it works. Here's what I did in case it helps someone else in future:
Created a custom processor that extends FeedsNodeProcessor
Copied across all of FeedsNodeProcessor's functions to the new class.
Overrided the existingEntityId function as follows (harvard_format is my secondary feed and pure_feed is my primary feed):
protected function existingEntityId(FeedsSource $source, FeedsParserResult $result) {
if($source->id == 'harvard_format') {
$query = db_select('feeds_item')
->fields('feeds_item', array('entity_id'))
->condition('feed_nid', $source->feed_nid)
->condition('entity_type', $this->entityType())
->condition('id', 'pure_feed');
// Iterate through all unique targets and test whether they do already
// exist in the database.
foreach ($this->uniqueTargets($source, $result) as $target => $value) {
switch ($target) {
case 'url':
$entity_id = $query->condition('url', $value)->execute()->fetchField();
break;
case 'guid':
$entity_id = $query->condition('guid', $value)->execute()->fetchField();
break;
}
if (isset($entity_id)) {
// Return with the content id found.
return $entity_id;
}
}
return 0;
}
elseif ($nid = parent::existingEntityId($source, $result)) {
return $nid;
} else {
// Iterate through all unique targets and test whether they do already
// exist in the database.
foreach ($this->uniqueTargets($source, $result) as $target => $value) {
switch ($target) {
case 'nid':
$nid = db_query("SELECT nid FROM {node} WHERE nid = :nid", array(':nid' => $value))->fetchField();
break;
case 'title':
$nid = db_query("SELECT nid FROM {node} WHERE title = :title AND type = :type", array(':title' => $value, ':type' => $this->config['content_type']))->fetchField();
break;
case 'feeds_source':
if ($id = feeds_get_importer_id($this->config['content_type'])) {
$nid = db_query("SELECT fs.feed_nid FROM {node} n JOIN {feeds_source} fs ON n.nid = fs.feed_nid WHERE fs.id = :id AND fs.source = :source", array(':id' => $id, ':source' => $value))->fetchField();
}
break;
}
if ($nid) {
// Return with the first nid found.
return $nid;
}
}
return 0;
}
}
Copied across the the Process and newItemInfo functions from FeedsProcessor.
Overrided newItemInfo as follows:
protected function newItemInfo($entity, $feed_nid, $hash = '') {
$entity->feeds_item = new stdClass();
$entity->feeds_item->entity_id = 0;
$entity->feeds_item->entity_type = $this->entityType();
// Specify the feed id, otherwise pure_feed's entries in the feeds_item table will be changed to harvard_format
$entity->feeds_item->id = ($this->id == "harvard_format") ? "pure_feed" : $this->id;
$entity->feeds_item->feed_nid = $feed_nid;
$entity->feeds_item->imported = REQUEST_TIME;
$entity->feeds_item->hash = $hash;
$entity->feeds_item->url = '';
$entity->feeds_item->guid = '';
}
Related
I simply use _t() to translate CMS Fields in a DataObject: TextField::create('Title', _t('cms.TitleField', 'Title'));. I thought translating $summary_fields was just as simple, but it's not.
Instead of trying to translate Fields and their accompanying summary_fields seperately, I believe I noticed a better way how these fields are translated using the function FieldLabels as used in SiteTree.
Is there way I can translate these both fields in one place (DRY principle) and apply to both easily by calling the var?
Yes I would certainly say the use of FieldLabels is for localisation / translation because of the comment "Localize fields (if possible)" here in the DataObject code...
public function summaryFields() {
$fields = $this->stat('summary_fields');
// if fields were passed in numeric array,
// convert to an associative array
if($fields && array_key_exists(0, $fields)) {
$fields = array_combine(array_values($fields), array_values($fields));
}
if (!$fields) {
$fields = array();
// try to scaffold a couple of usual suspects
if ($this->hasField('Name')) $fields['Name'] = 'Name';
if ($this->hasDatabaseField('Title')) $fields['Title'] = 'Title';
if ($this->hasField('Description')) $fields['Description'] = 'Description';
if ($this->hasField('FirstName')) $fields['FirstName'] = 'First Name';
}
$this->extend("updateSummaryFields", $fields);
// Final fail-over, just list ID field
if(!$fields) $fields['ID'] = 'ID';
// Localize fields (if possible)
foreach($this->fieldLabels(false) as $name => $label) {
// only attempt to localize if the label definition is the same as the field name.
// this will preserve any custom labels set in the summary_fields configuration
if(isset($fields[$name]) && $name === $fields[$name]) {
$fields[$name] = $label;
}
}
return $fields;
}
If you are familiar with WordPress Gravity Forms then you know that can add a "Phone" field from the advanced fields options. That's great but I am working on a site that offers a service to mobile users so I need make sure that the person filling out the form does so twice (in two fields) to ensure that there isn't a typo in their mobile number entered. I've been looking everywhere and can't figure out how to do this.
This code from http://gravitywiz.com/custom-field-confirmation/ worked perfectly for me. There are more detailed instructions on their site.
Paste this into your functions.php file and change register_confirmation_fields(8, array(1, 2)); to suit your form.
/**
* Double Confirmation Fields
* http://gravitywiz.com/2012/05/01/custom-field-confirmation/
*/
register_confirmation_fields(8, array(1, 2));
add_filter('gform_validation', 'gfcf_validation');
function gfcf_validation($validation_result) {
global $gfcf_fields;
$form = $validation_result['form'];
$confirm_error = false;
if(!isset($gfcf_fields[$form['id']]))
return $validation_result;
foreach($gfcf_fields[$form['id']] as $confirm_fields) {
$values = array();
// loop through form fields and gather all field values for current set of confirm fields
foreach($form['fields'] as $field) {
if(!in_array($field['id'], $confirm_fields))
continue;
$values[] = rgpost("input_{$field['id']}");
}
// filter out unique values, if greater than 1, a value was different
if(count(array_unique($values)) <= 1)
continue;
$confirm_error = true;
foreach($form['fields'] as &$field) {
if(!in_array($field['id'], $confirm_fields))
continue;
// fix to remove phone format instruction
if(RGFormsModel::get_input_type($field) == 'phone')
$field['phoneFormat'] = '';
$field['failed_validation'] = true;
$field['validation_message'] = 'Your values do not match.';
}
}
$validation_result['form'] = $form;
$validation_result['is_valid'] = !$validation_result['is_valid'] ? false : !$confirm_error;
return $validation_result;
}
function register_confirmation_fields($form_id, $fields) {
global $gfcf_fields;
if(!$gfcf_fields)
$gfcf_fields = array();
if(!isset($gfcf_fields[$form_id]))
$gfcf_fields[$form_id] = array();
$gfcf_fields[$form_id][] = $fields;
}
// register field IDs 1 and 2 on form ID 8
register_confirmation_fields(8, array(1, 2));
I have a vocab category and four terms within it. what i want to do is if content is tagged with a termin in particular say "term1" to have the url generated as word1/[node:title] and for all the other tags just the standard url formatting.
If i wanted the term in the url obviously id use pattern replacement but i want another word to be used if a particular tag is used
I can't think of an easy plug-and-play way of achieving this. You may have to create your own token for the "Default path pattern" in Pathauto's URL alias settings:
/**
* Implementation of hook_token_info().
*/
function MODULE_token_info() {
$info['tokens']['node']['node-term-path'] = array(
'name' => t('Node path by term'),
'description' => t('The path to a node based on its taxonomy terms.'),
);
return $info;
}
/**
* Implementation of hook_tokens().
*/
function MODULE_tokens($type, $tokens, array $data = array(), array $options = array()) {
$replacements = array();
if ($type == 'node' && !empty($data['node'])) {
$node = $data['node'];
foreach ($tokens as $name => $original) {
switch ($name) {
case 'node-term-path':
$items = field_get_items('node', $node, 'TAXONOMY_FIELD_NAME');
foreach ($items as $item) {
$tids[] = $item['tid'];
}
if (in_array(TID_OF_TERM1, $tids)) {
// Path for nodes with term1
$replacements[$original] = 'word1/'. pathauto_cleanstring($node->title);
}
else {
// Path for other nodes
$replacements[$original] = 'content/'. pathauto_cleanstring($node->title);
}
break;
}
}
}
return $replacements;
}
Found a simple way actually to anyone who need a similar solution use the module Entity Reference.
http://drupal.org/project/entityreference
I just created a new field for the user account select entity reference then you can choose any entity within drupal to reference.
(ie so you can select a term/content/anything)
I have a D7 website where users can make content (obviously...). So every node has it's own author. Every author is a member of an organization. But he can be a member of more then one organization. So far the facts.
I would like to create a view where the content is filtered on Author. Very easy, set the relation of the view on "Content's Author" and select the current user as filter.
But what I would like is to filter on the author's organization. So in fact it's a nested relation. Filter the nodes on the current logged in user (that's easy), but how can I filter on the current logged in user's organization?
Ok, the panels didn't work out, so I wrote my own hook :-)
function mymodule_views_pre_view (&$view, &$display_id, &$args) {
// Only execute this script when the view 'fiches_my_thema' is called
if ('fiches_my_thema' == $view->name) {
// Get users thema
global $user;
$userRoles = $user->roles;
$user_themas = array();
// Filter roles so you end up with the "Thema's".
foreach ($userRoles as $key) {
if(strpos($key,'edacteur')) {
$key = str_replace('Redacteur - ','', $key);
$key = str_replace('Eindredacteur - ','', $key);
$user_themas[] = $key;
}
}
// Resolve tid
$terms = taxonomy_get_tree(5);
$allRoles = array();
$arguments = array();
// Assign the 'tid' to a variable
foreach ($terms as $key) {
$singleRoles = $key->name;
$allRoles[] = $singleRoles;
if(in_array($singleRoles, $user_themas)) {
$arguments[] = $key->tid;
}
}
// Only when the arguments are NOT empty, set the arguments
if(!empty($arguments)) {
$finalArguments = implode("+", $arguments);
$args[] = "$finalArguments";
$view->set_arguments($args);
}
}
}
I've got the following:
function view_sorter_views_pre_view(&$view) { // don't need $items
if ($view->name == 'MOST_RECENT') {
$insert = array();
$insert[order] = 'DESC'; //SORT ORDER
$insert[id] = 'title';
$insert[table] = 'node';
$insert[field] = 'title';
$insert[override] = array();
$insert[override][button] = 'Override';
$insert[relationship] = 'none';
unset ($view->display['default']->display_options['sorts']['title']);
$view->display['default']->display_options['sorts']['title'] = $insert;
}
}
Basically, I'm just changing the sort order... but this does not appear on the view when opening it. Any idea why?
I believe what you want is
/**
* Implementation of hook_views_pre_view().
*/
function view_sorter_views_pre_view(&$view) {
if ($view->name == 'MOST_RECENT') {
$view->display['default']->handler->options['sorts']['title']['order'] = 'DESC';
}
}
Views uses the handler object to build the query instead of the display_options. The display_options contain all the options for every display type that the view contains (eg. default, page_1, block_1, etc...). The 'handler' object holds the options that will be used to actually build the current display.
Note: I simplified the code to only change the sort order. The rest of your code should work, just change the last two lines to
unset($view->display['default']->handler->options['sorts']['title']);
$view->display['default']->handler->options['sorts']['title'] = $insert;