Hi,
Is it possible to have the following:
* In the profile, I've added a field that user selects his working units CM or Inch.
* I'd like to have a filter that can handle something like [cm=1|inch=2.54]. The filter will need to check the settings from the profile, and act accordingly (i.e. show 1cm, or 2.54'').

CommentFileSizeAuthor
#23 cache.png6.24 KBarhip
#16 Snap1_20.png13.2 KBamitaibu
#13 workingunits.txt3.89 KBarhip
#5 Untitled-1_5.jpg53.44 KBarhip

Comments

arhip’s picture

Yes, it is possible. You can get the unit being used from table {profile_values}. We need two parameters, fid and uid. uid can be retrieved using global variable $user->uid. fid can be retrieved from table {profile_fields}.

Below is the pattern & replacement code. First you must create a filterset, and create a new filter under that filterset. At line 13, $fieldname should be assigned to field name.

Pattern:

/([0-9]+(\.[0-9]*)?)(mm|cm|inch)/i

Replacement (with PHP code):

if (! isset($vars->units)) {  // following code will execute once
  global $user;

  $vars->units = array();

  // define the conversion table
  $vars->units['conv'] = array(
    'mm' => 0.1,
    'cm' => 1,
    'inch' => 2.54);

  // get the field name
  $fieldname = 'profile_unit';      // <--------- change this according to your field name

  // get field ID from table {profile_fields}
  $unitfield = db_fetch_array(
    db_query("SELECT fid FROM {profile_fields} WHERE name='%s'",
       $fieldname));

  // get user preferred unit
  $unitsel = db_fetch_array(
    db_query("SELECT value FROM {profile_values} WHERE fid=%d and uid=%d",
       $unitfield['fid'], $user->uid));

  // and assign to $vars->units['unit']
  $vars->units['unit'] = $unitsel['value'];
 }

// the value is in the first bracket
$value = $matches[1];
// the unit is in the third bracket
$unit = $matches[3];

// Converts all values to cm
$valuecm = $value * $vars->units['conv'][$unit];
// Converts cm to user's preferred unit
$value2 = $valuecm / $vars->units['conv'][$vars->units['unit']];

return $value2 . $vars->units['unit'];
amitaibu’s picture

Thanks a lot! I'll check it and report back :)

amitaibu’s picture

Silly question, but what is the syntax that I should write?

amitaibu’s picture

Ok, seems to be a problem there:
When I type "This is 90cm", I get only "This is" and an error:

warning: Division by zero in C:\wamp\www\drupal\modules\customfilter\customfilter.module(1004) : runtime-created function on line 37.

Can't it be because in my profile working units appear as CM and Inch (i.e. case sensitive)?

arhip’s picture

StatusFileSize
new53.44 KB

I think so.

Maybe you should change the line 34-37 into:

// Converts all values to cm
$valuecm = $value * $vars->units['conv'][strtolower($unit)];
// Converts cm to user's preferred unit
$value2 = $valuecm / $vars->units['conv'][strtolower($vars->units['unit'])];

to convert all units processed into lowercase.

amitaibu’s picture

Status: Active » Fixed

Dude, what a great module!

Here's my final version, for everybody to enjoy...

Name:

Working units

Pattern (that will except 1inch, 1 inch, 1.5 inch):

/([0-9]+(\.[0-9]*)?)[ ]*(mm|cm|meter|inch|yard)/i

Replacement text:

if (! isset($vars->units)) {  // following code will execute once
  global $user;

  $vars->units = array();

  // define the conversion table
  $vars->units['conv'] = array(
    'mm' => 0.1,
    'cm' => 1,
    'centimeter' => 1,
    'meter' => 100,
  // We'll make inch 2.5 instead of 2.54, and Yard 91.5 instead of 91.44
    'inch' => 2.5,
    'yard' => 91.4);

  // get the field name
  $fieldname = 'profile_working_units';      // <--------- change this according to your field name

  // get field ID from table {profile_fields}
  $unitfield = db_fetch_array(
    db_query("SELECT fid FROM {profile_fields} WHERE name='%s'",
       $fieldname));

  // get user preferred unit
  $unitsel = db_fetch_array(
    db_query("SELECT value FROM {profile_values} WHERE fid=%d and uid=%d",
       $unitfield['fid'], $user->uid));
	
  switch ($unitsel['value']) {
	case 'Centimeter':
	    $unitsel['value']='cm';
	    break;
	case 'Inch':
	    $unitsel['value']='inch';
	    break;
	}
	
  // and assign to $vars->units['unit']
  $vars->units['unit'] = $unitsel['value'];
}

// the value is in the first bracket
$value = $matches[1];
// the unit is in the third bracket
$unit = $matches[3];

// Converts all values to cm
$valuecm = $value * $vars->units['conv'][strtolower($unit)];
// Converts cm to user's preferred unit
$value2 = $valuecm / $vars->units['conv'][strtolower($vars->units['unit'])];

return $value2 . $vars->units['unit'];

And the PHP code enabled.

Cheers!

amitaibu’s picture

Status: Fixed » Active

One problem, though...

Some users have CM in their profile and some Inch. I'd like the created content to show the values for every user according to their preference.

Is this module supposed/ can do this, or should I hack the theme?

arhip’s picture

If I don't misunderstand, you want the content displayed like this:

Some title

User's working unit: CM

Some text some text some text some text some text some text some text some text some text some text some text some text some text some text some text some text some text some text some text some text some text some text some text some text

We can use ^ to insert the text we want at the top. ^ matches the beginning of the string. So you can create a new filter with this configuration:

Pattern:

/(^)/

Replacement:

return "<div class=\"workingunit\">User's working unit: {$vars->units['unit']}</div>";

with PHP Code checked.

Create this new filter under the same filterset you're working on, and make sure to set it's weight more than Working units filter, since $vars->units won't be recognized before Working units filter executed.

But I think it would be nicer if you put this working unit information in a block.

amitaibu’s picture

I actaully mean something a bit different.

Title: hello world

body:
Some text some text some text 1 Inch some text some text

Now, I'd like the users who have CM defined in their profile to see the text like this:

body:
Some text some text some text 2.54 cm some text some text.

arhip’s picture

Oh, I'm sorry to misunderstand.

I'm sorry again to say that it is not possible (as long as I know) to do that only using this module. Since a filter will be executed once after we submit the content, we cannot change the content every time the content displayed. And since the current user's settings could affect a content only if the filter executed again, then there is no possible way to do that.

But I think we can get this effect by 2 way:

  • Combining with PHP filter, or
  • Inserts javascript

I'm trying each of this ways. I'll report you if any of these works.

amitaibu’s picture

Oh man, thank you so much for your help! waiting to see your results..

amitaibu’s picture

Not a developer, but I'm thinking maybe it's possible to use your filter to add for example <div class="working units>, that later will be replaced with to correct working units.

arhip’s picture

StatusFileSize
new3.89 KB

I think we can't use <div>s nor javascripts, because we need to get $user->uid to know which user is being logged, and we can only do this on the server. The PHP filter solution does work, because it will be executed on the server. I attach the sample working units filter (as plain text file).

For this filter to work, you need to turn on PHP Filter in the input format, and make sure it weights more than working units filter, so the PHP script produced could be executed.

The filter works like this:

  • First, it declares all variables needed
  • Next, it replaces all occurence of values in your content with some PHP code to do the conversion. These values are temporarily converted into what I called universal factor.
  • Next, this writes a PHP script on the top of the content, to get the current user's information, and define the conversion table.
  • The PHP Filter will execute all PHP codes produced by previous filter.

The customfilter module lacks the function to turn off cache, so that filters will run only when a content submitted. If only this module could turn cache off for particular filterset, then the code you submit on #6 will work as you wish. I'm working on the patch version. Thanks for your question, it really helps me to improve this module.

amitaibu’s picture

Sound interseting. One problem, though, is probably security - allowing user to use PHP. You think any posibble way not to use PHP filter for that?

arhip’s picture

Yes, it is very vulnerable, unless we can create a filter that strips any <?php ... ?> before our filter executed. I have added option 'cache' in the filterset edit page. The development release should be available soon (within 12 hours - the tarball created every 12 hours). You can try it when available. Using this fixed module, you don't need PHP filter. I've tried your code #6 with cache turned off, and it worked. Note that you should refresh the Input Format for the filter work properly (by saving the configuration again).

amitaibu’s picture

StatusFileSize
new13.2 KB

Ari,
I've imported configuration from #13, but something seems to be written wrong there. Checkout the snapshot - all text is replaced with

1, 'mm' => 0.1, 'cm' => 1, 'm' => 100, 'inch' => 2.5, 'yard' => 91.4, );$unit_aliases = array('cm' => 'centimeter', 'mm' => 'milimeter', 'm' => 'meter', );$unitsel = db_fetch_array( db_query("SELECT value FROM {profile_values} WHERE fid=%d and uid=%d", 1, $user->uid)); $unit_unit = strtolower($unitsel['value']);$aliaskey = array_search($unit_unit, $unit_aliases); if ($aliaskey) $unit_unit = $aliaskey;$unit_precision = 2; ?>
arhip’s picture

Oh, it seems that the PHP evaluator wasn't executed. Please check the:

  • Input Format configuration, have you check PHP evaluator?
  • Rearrange tab, make sure PHP evaluator weights more than Working Units
amitaibu’s picture

It works great! but the fact that I've enabled PHP evaluator doesn't mean that there is a security threat? How can I make sure users doesn't use PHP on my site?

arhip’s picture

You can define a new filter that will flush all <?php ?>. This filter must be executed first, because we don't want any php code get into PHP evaluator.

Filterset: UnPHP

Filter: Strip PHP code

Pattern: /\<\?php(.*?)\?\>/s

Replacement: <code type="php">$1</code>

And you should rearrange the input format in this order:

  1. UnPHP
  2. Working Units
  3. PHP Evaluator

So that any PHP from users cannot reach PHP evaluator, while PHP produced by Working Units can.

amitaibu’s picture

What will happen if user enters <? php <?php print ('hello'); ?> ?> - does this replace it properly?

arhip’s picture

No, that would be converted into

<code><?php print ('hello');< /code> ?>

But I think that's ok, that broken code wouldn't be executed. I think you'd better use the cache option (#15) instead of PHP filter.

amitaibu’s picture

use the cache option (#15) instead of PHP filter.

Didn't understand this part

p.s. welcome back :)

arhip’s picture

StatusFileSize
new6.24 KB

CustomFilter 5.x-1.2 supports cache option in filterset edit operation. When Drupal saves a content, it can be cached, so that any filter applied to the input only calculated once and then the result cached. Or, we can turn off cache, so that any filter applied to the input will be calculated every time the content viewed.

For working unit case, we can turn off this option, because every unit written should be calculated every time the content viewed. If you're going to use this method, then you can just use your filter in #6, and turn off cache option in the filterset settings.

avpaderno’s picture

Status: Active » Fixed

i am setting the report as fixed because it got an answer.

avpaderno’s picture

Title: filter according to profile » Filter according to profile

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.