Propel and I18N: fallback to another locale if first is not found - php

I am currently working on a API allowing users to get some event messages on their devices. Users call my API specifying me their preferred locale (for instance fr_FR).
On the back-office, administrators can choose the languages they want to support. Let's assume they choose English and Chinese. So, they do not support French language.
I am using the Propel I18N behavior to deal with all the translations. In my requests, I am currently doing:
SystemMessageQuery::create()
->joinWithI18N('fr_FR')
->findOne();
It works correctly if I specify an existing locale. Yet, if the translation is not available, it returns null. Sounds logical.
My question is: can I fallback the user on a default locale (in this case, en_US) if the provided locale is not planned by an administrator? If yes, how can I proceed?
I thought to send a prior request to check if the locale exists. But I do not think it is a good way for performances reasons (especially for an API).
Any idea?

According to the documentation, there is not such a fallback in Propel.
What you can try, is the combination of setLocale and joinWithI18N. Something like:
$default_locale = 'en_US';
$items = ItemQuery::create()
->joinWithI18n('fr_FR')
->find();
foreach ($items as $item)
{
echo $item->getPrice();
// test if name is translated
if (!$name = $item->getName())
{
// otherwise, try to get the translation with default locale
$item->setLocale($default_locale);
$name = $item->getName(); // one query to retrieve the English translation
}
}
I haven't tested this code but it seems to be a solution.
Also, have you tried to add two joinWithI18N (one for en_US & one for fr_FR) to see how the query handle it?

Finally, I used the following code:
public function setLocale($locale = 'en_US', $fallbackEnabled = false)
{
if($fallbackEnabled) {
$localeExists = SystemMessageI18nQuery::create()
->filterByLocale($locale)
->filterById($this->getId())
->count();
if(!$localeExists) {
$locale = SystemMessageI18nQuery::create()
->filterById($this->getId())
->select('locale')
->findOne();
}
}
parent::setLocale($locale);
}
It is not the best solution for performance reasons, but it is functionnal.

Try something like this
public function joinWithI18nFallback($locale=null, $joinType = Criteria::LEFT_JOIN, $default_locale=null)
{
$languages = sfConfig::get('sf_languages');
if(null == $locale)
{
$locale = sfPropel::getDefaultCulture();
}
if(null == $default_locale)
{
$default_locale = sfPropel::getDefaultCulture();
}
$languages[] = $locale;
$languages[] = $default_locale;
$languages = array_unique($languages);
$FallbackQuery = CategoryQuery::create()
->leftJoin('CategoryI18n')
->addJoinCondition('CategoryI18n', "CategoryI18n.Culture IN ('".implode("','", $languages)."')" )
->withColumn('CategoryI18n.Culture', 'CultureDefault')
->addAscendingOrderByColumn("FIELD(category_i18n.culture, '".$locale."', '".implode("','", $languages)."' )")
->where("CategoryI18n.name != ''");
$this->addSelectQuery($FallbackQuery, 'Category')
->join('CategoryI18n', $joinType)
->addJoinCondition('CategoryI18n', 'CategoryI18n.Culture = Category.CultureDefault' )
->with('CategoryI18n')
->groupById();
$this->with['CategoryI18n']->setIsWithOneToMany(false);
return $this;
}

Related

PrestaShop AdminController - is there better way to preserve backslashes and html entites?

I'm creatin a Prestashop module for my e-store; I've created module's own AdminController-inheritor class with standart List and Form views to manage module settings, and the ObjectModel-inheritor class to store setings in DB. All works good until I have to store text fields with folowing symbols: \ < >
The problem is that HelperForm spoiles the clean data retrieved from DB by using Tools::getValue() that applies stripslashes, without adding slashes to clean data.
I temporary solved problem with following:
to save < > symbols I just use self::TYPE_HTML definition in my ObjectModel; This is good declarative solution (I just define field options, and controller does everything the way I need);
but to save backslashes I have to use such interception:
.
public function getFieldValuePreserveSlashes($obj, $key, $id_lang = null) {
if ($id_lang)
$default_value = (isset($obj->id) && $obj->id && isset($obj->{$key}[$id_lang])) ? $obj->{$key}[$id_lang] : false;
else
$default_value = isset($obj->{$key}) ? $obj->{$key} : false;
$default_value = addslashes($default_value);
Tools::mapValue($key, 'addslashes');
return Tools::getValue($key.($id_lang ? '_'.$id_lang : ''), $default_value);
}
public function getFieldValue($obj, $key, $id_lang = null) {
if($key === 'export_params_text')
return $this->getFieldValuePreserveSlashes($obj, $key, $id_lang);
else
return parent::getFieldValue($obj, $key, $id_lang);
}
It works but I guess it is not the best way (I still hope there is some more native way, something like some options that I could set and not to duplicate core-functions code just to add 2 strings :)
PS I didnt forget about this after I solved a local task because I have such problem often enough, and if I dont want to use global override to place desired logic ther, I have to copy-paste this code from one controller to another, and this dont let me sleep calmly :)
Thanks in advance:)

Activate a Topic for a Page programmaticaly concrete5 5.7

The goal is to activate an existent topic in a Blog Entry page. Normally a user does this in the Pages Attributes section like so:
Now my goal is to do this programmaticaly. I won't post all my trials (since 2 days) here because it's just crap, but here's what I've done so far.
First I add a Blog Page to a chosen parent Page (ID 157):
use Concrete\Core\Page;
$parentPage = Page\Page::getByID(157);
$template = \PageTemplate::getByHandle('blog_entry');
$entry = $parentPage->add($type, array(
'cName' => 'My title',
'cDescription' => 'description',
'cHandle' => 'my_title',
'cvIsApproved' => true,
'cDatePublic' => $publishDate->format('Y-m-d H:i:s')
), $template);
As the newly created page is a blog_entry template the Blog Entry Topics is already assigned.
Then I create a Topic and add it to its Topic Tree (Blog Entry Topics) like so:
use \Concrete\Core\Tree\Type\Topic as TopicTree;
use \Concrete\Core\Tree\Node\Type\Topic as TopicTreeNode;
use \Concrete\Core\Tree\Node\Node as TreeNode;
$topicTree = TopicTree::getByName('Blog Entries');
$parentTopic = TreeNode::getByID($topicTree->getRootTreeNodeObject()->treeNodeID);
$item0 = TopicTreeNode::add('udland', $parentTopic);
How to activate/assign this Topic(Udland) to my page ($entry)? (As shown in the image)
I know it must be related to the DB-tables CollectionAttributeValues and atSelectedTopics. Also the Classes CollectionValue and CollectionKey must be involved.
I could add those entries manually in the DB but this isn't a good idea because I don't know what data is necessary to make this work correctly. The topics are used to filter Blog entries so I'm quite sure that there are other tables involved and as a Core developer said: "These are fragile little things" ;-).
As this version of concrete5 is a complete new launch, the developer docs aren't complete and after 2 days of digging inside the core code I'm just desperate.
Update (after a week of digging...)
I managed to do a hack taken out of a Controller method: (/concrete/controllers/panel/page/attributes.php -> submit()).
I know this isn't the way to go at all but it's my best trial so far:
(I just include the NameSpaces here to make clear what Classes I'm calling)
use Concrete\Core\Page;
use Concrete\Core\Page\Collection\Version\Version;
use Concrete\Core\Workflow\Request\ApprovePageRequest;
use CollectionAttributeKey;
use \Concrete\Core\Tree\Node\Type\Topic as TopicTreeNode;
Get the Attributes ID by handle:
$ak = CollectionAttributeKey::getByHandle('blog_entry_topics');
$attributekID = $ak->getAttributeKeyID();
get the topic
$item_one = TopicTreeNode::getNodeByName('Udland');
then simulate a posted form by:
$_POST = array(
'topics_' . $attributekID => array($item_one->treeNodeID)
);
I know this is so ugly and a big hack & not reliable at all but as said it's taken out of a Controller...
Then I do a slimmed version of the submit() method:
$c = Page\Page::getByID(157);
$published = new \DateTime();
$nvc = $c->getVersionToModify();
$nvcObj = $nvc->getVersionObject();
$data = array();
$data['cName'] = $nvcObj->cvName;
$data['cDescription'] = $nvcObj->cvDescription;
$data['cDatePublic'] = $published->format('Y-m-d H:i:s');
$data['uID'] = '1';
$nvc->update($data);
$setAttribs = $nvc->getSetCollectionAttributes();
$processedAttributes = array();
$selectedAKIDs = $attributekID;
if (!is_array($selectedAKIDs)) {
$selectedAKIDs = array();
}
$selected = is_array(array($attributekID)) ? array($attributekID) : array();
foreach ($setAttribs as $ak) {
if (in_array($ak->getAttributeKeyID(), $selected)) {
$ak->saveAttributeForm($nvc);
} else {
$nvc->clearAttribute($ak);
}
$processedAttributes[] = $ak->getAttributeKeyID();
}
$newAttributes = array_diff($selectedAKIDs, $processedAttributes);
foreach ($newAttributes as $akID) {
$ak = CollectionAttributeKey::getByID($akID);
$ak->saveAttributeForm($nvc);
}
So as said before this is really ugly but it's the best trial so far and somehow it works.
Then approve the Request by doing:
$pkr = new ApprovePageRequest();
$u = new User();
$pkr->setRequestedPage($c);
$v = Version::get($c, "RECENT");
$pkr->setRequestedVersionID($v->getVersionID());
$pkr->setRequesterUserID($u->getUserID());
$pkr->trigger();
$u->unloadCollectionEdit();
But what really makes me wonder is that method inside of /concrete/src/Attribute/Key/Key.php where finally the thing should happen (in my humble opinion):
/**
* Calls the functions necessary to save this attribute to the database. If no passed value is passed, then we save it via the stock form.
* NOTE: this code is screwy because all code ever written that EXTENDS this code creates an attribute value object and passes it in, like
* this code implies. But if you call this code directly it passes the object that you're messing with (Page, User, etc...) in as the $attributeValue
* object, which is obviously not right. So we're going to do a little procedural if/then checks in this to ensure we're passing the right
* stuff
*
* #param CollectionValue|mixed $mixed
* #param mixed $passedValue
*/
protected function saveAttribute($mixed, $passedValue = false)
{
/** #var \Concrete\Core\Attribute\Type $at */
$at = $this->getAttributeType();
$at->getController()->setAttributeKey($this);
if ($mixed instanceof AttributeValue) {
$attributeValue = $mixed;
} else {
// $mixed is ACTUALLY the object that we're setting the attribute against
//todo: figure out what $nvc should really be since it doesn't exist in this scope
$attributeValue = $nvc->getAttributeValueObject($mixed, true);
}
$at->getController()->setAttributeValue($attributeValue);
if ($passedValue) {
$at->getController()->saveValue($passedValue);
} else {
$at->getController()->saveForm($at->getController()->post());
}
$at->__destruct();
unset($at);
}
So I'm really curios to see what the reliable and system-suitable way is to resolve this.
Here's what I came up with that does work. You were pretty close.
use \Concrete\Core\Tree\Type\Topic as TopicTree;
use \Concrete\Core\Tree\Node\Type\Topic as TopicTreeNode;
use \Concrete\Core\Tree\Node\Node as TreeNode;
$parentPage = \Page::getbyPath('/blog');
$template = \PageTemplate::getByHandle('blog_entry');
$entry = $parentPage->add($type, array(
'cName' => 'ooops',
'cDescription' => 'hmmmm',
'cHandle' => 'yay',
'cvIsApproved' => true,
'cDatePublic' => '2015-12-21 00:00:00'
), $template);
$item0 = TopicTreeNode::getNodeByName('udland');
if (!$item0) {
$topicTree = TopicTree::getByName('Blog Entries');
$parentTopic = TreeNode::getByID($topicTree->getRootTreeNodeObject()->treeNodeID);
$item0 = TopicTreeNode::add('udland', $parentTopic);
}
$entry->setAttribute('blog_entry_topics', array($item0->getTreeNodeDisplayPath()));
It looks like the attribute takes in an array of node display paths and that is how it sets the selection. Additionally, you have to use the \Page alias, and not the fully qualified namespace as you were doing, otherwise you get an error about it being unable to clear the cache.

What's the best logic for switching language in Laravel?

I'm using Laravel localization to provide two different languages. I've got all the path stuff set up, and mydomain.com/en/bla delivers English and stores the 'en' session variable, and mydomain.com/he/bla delivers Hebrew and stores the 'he' session variable. However, I can't figure out a decent way to provide a language-switching link. How would this work?
I've solved my problem by adding this to the before filter in routes.php:
// Default language ($lang) & current uri language ($lang_uri)
$lang = 'he';
$lang_uri = URI::segment(1);
// Set default session language if none is set
if(!Session::has('language'))
{
Session::put('language', $lang);
}
// Route language path if needed
if($lang_uri !== 'en' && $lang_uri !== 'he')
{
return Redirect::to($lang.'/'.($lang_uri ? URI::current() : ''));
}
// Set session language to uri
elseif($lang_uri !== Session::get('language'))
{
Session::put('language', $lang_uri);
}
// Store the language switch links to the session
$he2en = preg_replace('/he\//', 'en/', URI::full(), 1);
$en2he = preg_replace('/en\//', 'he/', URI::full(), 1);
Session::put('he2en', $he2en);
Session::put('en2he', $en2he);
This is a post i posted originally on the laravel forums, but maybe it will help somebody else, so i post it here also.
I had some trouble with building a easy language switcher for my app, and the info on the forums where a little bit old (some posts), so i made this simple piece of code that makes it supereasy to change language on your app on the fly.
I have the language strings in my views as following:
{{ __('languagefile.the_language_string'); }}
And I get the languages with a URL, i think this is the best way, also its good for seo and for links that people share. Example:
www.myapp.com/fi/support (Finnish)
www.myapp.com/en/support (English)
www.myapp.com/sv/support (Swedish)
Ok, so the problem was that i wanted a easy way to change the language on the fly, without having to mess with sessions and cookies. Heres how i made it:
Make a library in your libraries folder called chooselang.php
Insert this code inside:
class Chooselang extends HTML {
/**
* Generate a Language changer link.
*
* <code>
* // Generate a link to the current location,
* // but still change the site langauge on the fly
* // Change $langcode to desired language, also change the Config::set('application.language', 'YOUR-LANG-HERE')); to desired language
* // Example
* echo Chooselang::langslug(URI::current() , $langcode = 'Finnish' . Config::set('application.language', 'fi'));
* </code>
*
* #param string $url
* #param string $langcode
* #param array $attributes
* #param bool $https
* #return string
*/
public static function langslug($url, $langcode = null, $attributes = array(), $https = null)
{
$url = URL::to($url, $https);
if (is_null($langcode)) $langcode = $url;
return '<a href="'.$url.'"'.static::attributes($attributes).'>'.static::entities($langcode).'</a>';
}
}
After this you are ready for getting your url switcher URL:s generated. Simply add them as you whould any other Blade links.
Example how to generate links for Finnish, Swedish and English (with Blade)
{{ Chooselang::langslug(URI::current() , $langcode = 'Fin' . Config::set('application.language', 'fi')); }}
{{ Chooselang::langslug(URI::current() , $langcode = 'Swe' . Config::set('application.language', 'sv')); }}
{{ Chooselang::langslug(URI::current() , $langcode = 'Eng' . Config::set('application.language', 'en')); }}
The above will generate URL:s that are always on the current page, and change the lang slug to the one you want. This way the language changes to the one you want, and the user naturally stays on the same page. The default language slug is never added to the url.
Generated urls look something like:
Fin
Swe
Eng
PS. The links are specially useful if you add them to your master template file.
You could have a Route to hand language change, for example:
Route::get('translate/(:any)', 'translator#set');
Then in the set action in the translator controller could alter the session, depending on the language code passed via the URL.
You could also alter the configuration setting by using
Config::set('application.language', $url_variable');
Controller Example - translate.php
public function action_set($url_variable)
{
/* Your code Here */
}
Just in case for future users if you want to use package for localization There is a great package at https://github.com/mcamara/laravel-localization. which is easy to install and has many helpers.
This question still comes in Google search, so here's the answer if you're using Laravel 4 or 5, and mcamara/laravellocalization.
<ul>
<li class="h5"><strong><span class="ee-text-dark">{{ trans('common.chooselanguage') }}:</span></strong> </li>
#foreach(LaravelLocalization::getSupportedLocales() as $localeCode => $properties)
<li>
<a rel="alternate" hreflang="{{$localeCode}}" href="{{LaravelLocalization::getLocalizedURL($localeCode) }}">
<img src="/img/flags/{{$localeCode}}.gif" /> {{{ $properties['native'] }}}
</a>
</li>
#endforeach
</ul>
NOTE that this example shows flags (in public/img/flags/{{locale}}.gif), and to use it you will need a bit of .css, but you can modify it to display the text if you want...
FYI. The mcamara/laravellocalization documentation has examples and a LOT of helpers, so look through the documentation on github. (https://github.com/mcamara/laravel-localization)
Try use Session's. Somthing like this:
Controller:
class Language_Controller extends Base_Controller {
function __construct(){
$this->action_set();
parent::__construct();
}
private function checkLang($lang = null){
if(isset($lang)){
foreach($this->_Langs as $k => $v){
if(strcmp($lang, $k) == 0) $Check = true;
}
}
return isset($Check) ? $Check : false;
}
public function action_set($lang = null){
if(isset($lang) && $this->checkLang($lang)){
Session::put('lang', $lang);
$this->_Langs['current'] = $lang;
Config::set('application.language', $lang);
} else {
if(Session::has('lang')){
Config::set('application.language', Session::get('lang'));
$this->_Langs['current'] = Session::get('lang');
} else {
$this->_Langs['current'] = $this->_Default;
}
}
return Redirect::to('/');
}
}
In Route.php:
Route::get('lang/(:any)', 'language#set');
I've been doing it like this:
$languages = Config::get('lang.languages'); //returns array('hrv', 'eng')
$locale = Request::segment(1); //fetches first URI segment
//for default language ('hrv') set $locale prefix to "", otherwise set it to lang prefix
if (in_array($locale, $languages) && $locale != 'hrv') {
App::setLocale($locale);
} else {
App::setLocale('hrv');
$locale = null;
}
// "/" routes will be default language routes, and "/$prefix" routes will be routes for all other languages
Route::group(array('prefix' => $locale), function() {
//my routes here
});
Source: http://forumsarchive.laravel.io/viewtopic.php?pid=35185#p35185
What I'm doing consists of two steps:
I'm creating a languages table which consists of these fields:
id | name | slug
which hold the data im gonna need for the languages for example
1 | greek | gr
2 | english | en
3 | deutch | de
The Language model I use in the code below refers to that table.
So, in my routes.php I have something like:
//get the first segment of the url
$slug = Request::segment(1);
$requested_slug = "";
//I retrieve the recordset from the languages table that has as a slug the first url segment of request
$lang = Language::where('slug', '=', $slug)->first();
//if it's null, the language I will retrieve a new recordset with my default language
$lang ? $requested_slug = $slug : $lang = Language::where('slug', '=', **mydefaultlanguage**')->first();
//I'm preparing the $routePrefix variable, which will help me with my forms
$requested_slug == ""? $routePrefix = "" : $routePrefix = $requested_slug.".";
//and I'm putting the data in the in the session
Session::put('lang_id', $lang->id);
Session::put('slug', $requested_slug);
Session::put('routePrefix', $routePrefix );
Session::put('lang', $lang->name);
And then I can write me routes using the requested slug as a prefix...
Route::group(array('prefix' => $requested_slug), function()
{
Route::get('/', function () {
return "the language here is gonna be: ".Session::get('lang');
});
Route::resource('posts', 'PostsController');
Route::resource('albums', 'AlbumsController');
});
This works but this code will ask the database for the languages everytime the route changes in my app.
I don't know how I could, and if I should, figure out a mechanism that detects if the route changes to another language.
Hope that helped.

Best practice to place meta tags, links and styles in zend framework?

I have project-range meta tags that are need to be set.
I've put them in protected method _initMeta in Bootstrap class.
Are there any better options? What if I would like different set of this data for another languages?
protected function _initMeta(){
$this->bootstrap('view');
$view = $this->getResource('view');
$view->doctype('XHTML1_STRICT');
$view->headTitle()->headTitle('Foo title');
$view->headMeta()->appendName('keywords','foo');
$view->headMeta()->appendHttpEquiv('Content-Type', 'text/html; charset=UTF-8')
->appendHttpEquiv('Content-Language', 'any');
$view->headLink()->appendStylesheet('/foo.css')->headLink(array('rel' => 'favicon',
'href' => '/favicon.ico'),
'PREPEND');
}
I use config for basic (bootstrap) data as:
application.ini
resources.view.meta.name.Viewport = "width=device-width, initial-scale=1.0"
resources.view.meta.name.MobileOptimized = "width"
resources.view.meta.name.HandheldFriendly = "true"
resources.view.meta.name.Keywords = "basic,keywords"
...
; format resources.view.headStyle.{MEDIA}.nfile =
resources.view.headStyle.all.1.href = "/css/basic.css"
resources.view.headStyle.all.1.conditionalStylesheet =
resources.view.headStyle.all.1.extras.title = "Basic style"
resources.view.headStyle.all.1.extras.charset = "utf-8"
resources.view.headStyle.all.2.href = "/css/ie.css"
resources.view.headStyle.all.2.conditionalStylesheet = "IE"
resources.view.headStyle.all.2.extras.title = "Internet Explorer style"
resources.view.headStyle.all.2.extras.charset = "utf-8"
; print media example
resources.view.headStyle.print.1.href = "/css/print.css"
...
; format resources.view.headLink.{REL} =
resources.view.headLink.humans.href = "/humans.txt"
resources.view.headLink.humans.type = "text/plain"
; ___ will be replaced by space, __ by point (or set another nest separator)
resources.view.headLink.shortcut___icon.href = "/favicon.png"
resources.view.headLink.shortcut___icon.type = "image/png"
...
At this point, maybe you have some special data. For example in:
project1.ini
project.headLink.author.href = "https://plus.google.com/XXXXX?rel=author"
project.headLink.image_src.href = "/author.jpg"
project.headLink.image_src.type = "image/jpg"
And finally, you mix all in your
Bootstrap.php
(example for *_initHeadLink()*):
// $options = your app options (basic)
// $projectOptions = your project options (special)
// $assets_url = your assets url
if ( is_array($headStyle = $options['headStyle']) ) {
foreach ( $headStyle as $media => $value ) {
foreach ( $value as $style ) {
extract($style);
$this->view->headLink()->appendStylesheet($assets_url . $href, $media,
$conditionalStylesheet, $extras);
}
}
}
$headLinks = array();
if ( isset($options['headLink']) )
$headLinks = $options['headLink'];
if ( isset($projectOptions['headLink']) )
$headLinks = array_merge($headLinks, (array) $projectOptions['headLink']);
// *array key, is the value for rel
foreach ( $headLinks as $rel => $value ) {
$rel = str_replace(array('___', '__'), array(' ', '.'), $rel);
$this->view->headLink()->headLink(array_merge(array('rel' => $rel), (array) $value));
}
Then, you can override these data from your Controller: setName, set...
I hope it helps ;)
You have several ways of achieving this. First and foremost, make sure the moment you define the metadata you already know what language will be loaded for the current request. Sometimes this may not be easy to determine at bootstrap time.
Having said this, besides the bootstrap, you can set the metadata on a:
Front Controller Plugin class (using for example the dispatchLoopStartup() or predispatch() methods).
Action Helper class (using for example the init() or preDispatch() methods).
At those points of execution you probably already determined the language to use, and can set the metadata accordingly. You can always change the metadata afterwards in your action controllers for specific cases in your application, so you're never really stuck if you previously specified metadata.
In my own work, I have this setup:
Front Controller Plugin, dispatchLoopStartup() method: determine language to load, giving priority to a "lang" GET parameter in the request object, then browser language, then default site language. I also use this to determine if the request is a normal request or an ajax request; if it's the former case, I register an action helper, see next...
Action Helper, preDispatch() method: load metadata depending on language and other stuff, load Layout and widgets, etc.
Action controller, someAction() method: if necessary, change some of the previously set metadata, for example headTitle(), which can depend on the effectively loaded content.
This arrangement makes sense to me, maybe it can help your approach?
The bootstrap is way to early for my projects. I add them in my controller/actions
$keywords = 'php,zend,framework';
$this->view->headMeta($keywords,'keywords','name',array(),'SET');
... etc.
Actually very late almost at the end. At this point I would also know about language and other things.

Hide Drupal nodes from search

I made a private section on a drupal site by writing a module that checks the RERQUEST_URI for the section as well as user role. The issue I am running into now is how to prevent those nodes/views from appearing in the search.
The content types used in the private section are used in other places in the site.
What's the best way to get Druapl search to ignore the content/not index/not display it in search results?
There is a wonderful article that explains just this on the lullabot site.
It's worth reading the comments to the post too, because people there suggested alternate ways of doing that, also by mean of contrib modules (rather than implementing some hooks in your own code). Code for D6 is in the comment as well.
HTH!
The lullabot article is a bit outdated and contains many blunt approaches. It also contains the answer in the comments - the Search Restrict module which works for DP6 and allows fine-grained and role-based control. Everything else either prevents content from being indexed, which may not be desirable if there are different access levels to content, or affects all search queries equally, which will also not work if there are different access levels.
If the content types used within the Private section are also used elsewhere how are you hoping to filter them out of the search results (note that I've not looked at the lullabot article by mac yet).
Basically, if you look at the details of two nodes, one private and one public, what differentiates them?
Note: I'm assuming that you want the nodes to appear to users with access to the Private area but not to 'anonymous' users.
For Drupal 7.
You can hide the node from search results by using custom field. In my case, I have created a custom field in the name of Archive to the desired content type and with the help of that custom field you can write the my_module_query_alter functionality.
Code
function my_module_query_alter(QueryAlterableInterface $query) {
$is_search = $is_node_search = FALSE;
$node_alias = FALSE;
foreach ( $query->getTables() as $table ) {
if ( $table['table'] == 'search_index' || $table['table'] == 'tracker_user') {
$is_search = TRUE;
}
if ( $table['table'] == 'node' || $table['table'] == 'tracker_user') {
$node_alias = $table['alias'];
$is_node_search = TRUE;
}
}
if ( $is_search && $is_node_search ) {
$nids = [];
// Run entity field query to get nodes that are 'suppressed from public'.
$efq = new EntityFieldQuery();
$efq->entityCondition('entity_type', 'node')
->fieldCondition('field_archive', 'value', 1, '=');
$result = $efq->execute();
if ( isset($result['node']) ) {
$nids = array_keys($result['node']);
}
if ( count($nids) > 0 ) {
$query->condition(sprintf('%s.nid', $node_alias), $nids, 'NOT IN');
}
}
}

Categories