File system path for images - cakephp

I'm writing a custom helper that extends HtmlHelper and overriding the \HtmlHelper::image() method to calculate the image dimensions and add them as HTML attributes. What I have so far works fine for regular pictures:
public function image($path, $options = array()) {
if (!array_key_exists('width', $options) && !array_key_exists('height', $options)) {
$stamp = Configure::read('Asset.timestamp');
Configure::write('Asset.timestamp', false);
$path = $this->assetUrl($path, $options + array('pathPrefix' => Configure::read('App.imageBaseUrl')));
list($width, $height) = #getimagesize(rtrim(WWW_ROOT, '\\/') . $path);
if (!is_null($width)) {
$options['width'] = $width;
}
if (!is_null($height)) {
$options['height'] = $height;
}
Configure::write('Asset.timestamp', $stamp);
}
return parent::image($path, $options);
}
… but has these flaws:
Pictures from plug-ins can't be located on disk (and they should), e.g.:
echo $this->Html->image('/debug_kit/img/cake.icon.png', array('alt' => 'CakePHP'));
… produces this file system path:
…\src\webroot/debug_kit/img/cake.icon.png
… thus getimagesize() fails because actual location is:
…\src\Plugin\DebugKit\webroot\img\cake.icon.png"
External pictures (which should be ignored) go through the full process:
echo $this->Html->image('http://placekitten.com/200/300');
…\src\webroothttp://placekitten.com/200/300
I've been looking for a builtin method to convert a CakePHP picture URL (in any format accepted by \HtmlHelper::image() into a file system path (o something like null when doesn't apply) but I couldn't find any. Native features that need a disk path, such as \Helper::assetTimestamp() are wrapped in tons of non-reusable code.
Is there an elegant solution?

I'd say that there are pretty much only 3 options:
Submit a patch to add asset filesystem path retrieval functionality to the core.
Copy a lot of code from the helper (assetUrl(), webroot(), and assetTimestamp()).
Use web requests for local URLs (ideally combined with caching).

Try using DS rather than using \ or /, they sometime can cause problems with the OS.
DS is directory separator provided by cakephp Short for PHP’s DIRECTORY_SEPARATOR, which is / on Linux and \ on Windows.
Check the doc

Related

file path is not retrieved in codeigniter

I am creating an invoice on users desired package selection. The pdf file is being created (but it takes some time), while the code checks for the file. The file exists. Here is the address of the file;
C:/wamp/www/proposal/file/invoice/Basic_52_60.pdf
This is the correct path to the file. I am passing this path to the function in another controller as;
redirect('email/email_invoice/'.$file);
When I tested the file path in email_invoice function, it displayed only c:
c:
The slashes in the path are not transferred. I don't know exactly what is the problem.
CodeIgniter considers each segment of the URL a parameter after the controller and method. So you are essentially passing 7 variables to the Email::email_invoice() method.
You could use some sort of encoding to pass it as one variable and then decode it on the other side such as:
$file = base64_encode($file);
redirect('email/email_invoice/' . $file);
Then in Email.php:
public function email_invoice($file) {
$file = base64_decode($file);
}
Or you could pass it as a get parameter:
redirect('email/email_invoice/?file=' . $file);
public function email_invoice() {
$file = $this->input->get('file');
}
The latter requires the $_GET array to be enabled which it is not by default.
UPDATE - Using Flashdata
Based on some of the comments I thought I would update this answer. base64_encode() can result in characters that will break the URL so you would need to use:
$file = urlencode(base64_encode($file));
redirect('email/email_invoice/' . $file);
And on the other side:
public function email_invoice($file) {
$file = urldecode(base64_decode($file));
}
As the OP pointed out $_GET variables can be manipulated leaving you open to directory traversal attacks or other vulnerabilities. Even if done right you would need extra code for security. Encoding can easily be spotted and altered.
File paths probably shouldn't be carried around in the URL. POST data can be manipulated also even if it is less obvious. Security through obscurity is not security at all. A better approach would be to use flashdata.
$this->session->set_flashdata('email_invoice_pdf', $file);
redirect('email/email_invoice/');
Then in your controller:
public function email_invoice() {
$file = $this->session->flashdata('email_invoice_pdf');
}
That's it. The session was used to carry the file path to the next page request, but after that it is gone.

drupal 7 file field formatter on custom node for javascript (to set up jQuizMe)

I have set up a module with custom node type (I called jquizme, after the javascript jQuizMe that I really like using). I set up two fields for the javascript files I need to supply to make it work (after the general jQuizMe-2.2.js file you need to add another two javascript files - one for settings and one for the quiz content).
Drupal saves the files as myjavascriptfile.js.txt - I tested them and they still work to make the jQuizMe interface - ok. the problem is, I want to add these files on the node page... the files will be different for each node. how can I access the files for the drupal_add_js() function so they will load the files for the node in question?
I tried setting up custom field formatters, but I don't know how to access the uri for the files of a given node automatically to put in the drupal_add_js() function (I can add a static file and it loads fine ... I did this with hook_node_view ( jquizme_node_view ).
So I just need a way to access the info for the files... how are they linked to each node? I can't find the connection.
As you probably noticed, I am a module writing newbie, and I probably won't understand much related to object oriented programming sorry, haven;t progressed to that level yet), but I am open to any answer. I am sure I left out important info, but this it already getting too long.
I also set up a special page earlier on to just see if I could get jQuizMe to work in Drupal so that is still in the code.
I have tried many answers (last six hours or so... too much to say here), the latest of which is using tokens, but that is not working. Here is what I have so far:
function jquizme_node_view($node, $view_mode, $langcode) {
switch ($node->type) {
case 'jquizme':
$items = field_get_items('node', $node, 'field_myfield', $node->language);
drupal_add_css(drupal_get_path('module', 'jquizme') . '/jQuizMe.css', >array('scope' => 'header'));
drupal_add_js(drupal_get_path('module', 'jquizme') . '/alert.js');
drupal_add_js(drupal_get_path('module', 'jquizme') . '/jQuizMe-2.2.js', >array('scope' => 'header'));
//drupal_add_js($tokens['node']['jquizme_js1_field'], array('scope' => >'header'));
//drupal_add_js($tokens['node']['jquizme_js2'], array('scope' => 'header'));
break;
}
}
Thanks in advance!
Can you try this?
// Let me assume that the field names of your two file fields
// are jquizme_js1_field and jquizme_js2_field for convenience..
function jquizme_node_view($node, $view_mode, $language) {
$items = field_get_items('node', $node, 'jquizme_js1_field');
_jquizme_add_js_from_filefield($items);
$items = field_get_items('node', $node, 'jquizme_js2_field');
_jquizme_add_js_from_filefield($items);
}
// Given the values of a filefield attached to some entity,
// adds them as JS files to the page.
function _jquizme_add_js_from_filefield($items = array()) {
foreach ($items as $item) {
$fid = &$item['fid'];
$file = file_load($fid);
if (!$file) {
continue; // Maybe the file got deleted..
}
$wrapper = file_stream_wrapper_get_instance_by_uri($file->uri);
$path = $wrapper->realpath();
// Ensure that the path exists and that it is a Javascript file..
if (file_exists($path) && preg_match('\.js$', $path)) {
drupal_add_js($path, array('type' => 'file'));
}
}
}

CakePHP Media Plugin - Upload Folder

I am using the Media Plugin (http://www.ohloh.net/p/cakephp-media).
I wanna define custom folder for all the uplaods. i am a little confused where it has to be done.
This is the folder structure i want to achieve
webroot/media/image/original (for the original file storage)
webroot/media/image/large (for the large image filter)
webroot/media/image/medium (for the medium image filter)
webroot/media/image/small (for the small image filter)
also i want to use a random name that i want to generate using the following sript.
//UUID generator
function _imgName() {
return time() . substr(md5(microtime()), 0, 12);
}
Firstly, having used this plugin, I would recommend considering the default directory structure to keep your app simpler. You don't want to edit the plugin directly as it will make upgrading more painful...
# /app/webroot/media/transfer/img/slug.ext (for the original file storage)
# /app/webroot/media/filter/l/img/slug.ext (for the large image filter)
# /app/webroot/media/filter/m/img/slug.ext (for the medium image filter)
# /app/webroot/media/filter/s/img/slug.ext (for the small image filter)
However, the Media plugin configuration file is located at /app/plugins/media/config/core.php and contains some constants that you can override application-wide by defining them in /app/config/bootstrap.php first. To achieve a format somewhat similar to what you want, you could define the following variables:
define('MEDIA_TRANSFER', WWW_ROOT . 'media' . DS . 'original' . DS);
define('MEDIA_FILTER', WWW_ROOT . 'media' . DS);
define('MEDIA_TRANSFER_URL', 'media/original/');
define('MEDIA_FILTER_URL', 'media/');
# /app/webroot/media/original/img/slug.ext (for the original file storage)
# /app/webroot/media/l/img/slug.ext (for the large image filter)
# /app/webroot/media/m/img/slug.ext (for the medium image filter)
# /app/webroot/media/s/img/slug.ext (for the small image filter)
(Note: you can also set the above paths on a per-model basis by passing the correct configuration options when adding the behavior to your models.)
You can also redefine the names of the image filters being used to get a step closer to your goal. You need to do this in /app/config/bootstrap.php again, but after you have loaded the Media plugin configuration:
require APP . 'plugins' . DS . 'media' . DS . 'config' . DS . 'core.php';
Configure::write('Media.filter.image', array(
'small' => array('convert' => 'image/png', 'zoomCrop' => array(100, 100)),
'medium' => array('convert' => 'image/png', 'fitCrop' => array(300, 300)),
'large' => array('convert' => 'image/png', 'fit' => array(600, 440)),
));
# /app/webroot/media/original/img/slug.ext (for the original file storage)
# /app/webroot/media/large/img/slug.ext (for the large image filter)
# /app/webroot/media/medium/img/slug.ext (for the medium image filter)
# /app/webroot/media/small/img/slug.ext (for the small image filter)
If you read the documentation for the Media.TransferBehavior::transferTo() method, you will see you can customize the latter part of the path (ie. img/slug.ext) by reimplementing this method in your model (eg. MyModel::transferTo()). Something like this would get you even closer:
class MyModel extends AppModel {
public function transferTo($via, $from) {
extract($from);
$mime = Mime_Type::guessName($mimeType ? $mimeType : $file);
$name = $this->_imgName();
$path = $mime . DS . $name
$path .= !empty($extension) ? '.' . strtolower($extension) : null;
return $path;
}
}
# /app/webroot/media/original/image/129916996632c787226a0b.ext (for the original file storage)
# /app/webroot/media/large/image/129916998392a3570a1828.ext (for the large image filter)
# /app/webroot/media/medium/image/12991699891c7625bebedb.ext (for the medium image filter)
# /app/webroot/media/small/image/12991699938ab22d80cfc6.ext (for the small image filter)
While not exactly what you were looking for (/large/image/ vs /image/large/), this is about as far as you can go without reimplementing larger portions of the plugin. This is because the path is treated as two parts (eg. /media/large/ and image/129916998392a3570a1828.ext) that get appended together later. You can see the append operation happening in the Media.TransferBehavior::_prepare() method and in the Media.GeneratorBehavior::make() method. You would need to either extend the plugin and duplicate those methods (with alterations) in your application code, or hack those two lines directly to get the desired output!

how to force drupal function to not use DB cache?

i have a module and i am using node_load(array('nid' => arg(1)));
now the problem is that this function keep getting its data for node_load from DB cache.
how can i force this function to not use DB cache?
Example
my link is http://mydomain.com/node/344983
now:
$node=node_load(array('nid'=>arg(1)),null,true);
echo $node->nid . " -- " arg(1);
output
435632 -- 435632
which is a randomly node id (available on the system)
and everytime i ctrl+F5 my browser i get new nid!!
Thanks for your help
Where are you calling this? For example, are you using it as part of your template.php file, as part of a page, or as an external module?
Unless you have this wrapped in a function with its own namespace, try naming the variable differently than $node -- for example, name it $my_node. Depending on the context, the 'node' name is very likely to be accessed and modified by Drupal core and other modules.
If this is happening inside of a function, try the following and let me know what the output is:
$test_node_1 = node_load(344983); // Any hard-coded $nid that actually exists
echo $test_node_1->nid;
$test_node_2 = node_load(arg(1)); // Consider using hook_menu loaders instead of arg() in the future, but that's another discussion
echo $test_node_2->nid;
$test_node_3 = menu_get_object(); // Another method that is better than arg()
echo $test_node_3->nid;
Edit:
Since you're using hook_block, I think I see your problem -- the block itself is being cached, not the node.
Try setting BLOCK_NO_CACHE or BLOCK_CACHE_PER_PAGE in hook_block, per the documentation at http://api.drupal.org/api/drupal/developer--hooks--core.php/function/hook_block/6
You should also try to avoid arg() whenever possible -- it's a little bit of a security risk, and there are better ways to accomplish just about anything arg() would do in a module environment.
Edit:*
Some sample code that shows what I'm referring to:
function foo_block ($op = 'list', $delta = 0, $edit = array()) {
switch ($op) {
case 'list':
$blocks[0] = array(
'info' => 'I am a block!',
'status' => 1,
'cache' => BLOCK_NO_CACHE // Add this line
);
return $block;
case 'view':
.....
}
}
node_load uses db_query, which uses mysql_query -- so there's no way to easily change the database's cache through that function.
But, node_load does use Drupal's static $nodes cache -- It's possible that this is your problem instead of the database's cache. You can have node_load clear that cache by calling node_load with $reset = TRUE (node_load($nid, NULL, TRUE).
Full documentation is on the node_load manual page at http://api.drupal.org/api/drupal/modules--node--node.module/function/node_load/6
I have had luck passing in the node id to node_load not in an array.
node_load(1);
According to Druapl's api this is acceptable and it looks like if you pass in an array as the first variable it's loaded as an array of conditions to match against in the database query.
The issue is not with arg(), your issue is that you have caching enabled for anonymous users.
You can switch off caching, or you can exclude your module's menu items from the cache with the cache exclude module.
edit: As you've now explained that this is a block, you can use BLOCK_NO_CACHE in hook_block to exclude your block from the block cache.

the best way to make codeigniter website multi-language. calling from lang arrays depends on lang session?

I'm researching hours and hours, but I could not find any clear, efficient way to make it :/
I have a codeigniter base website in English and I have to add a Polish language now. What is the best way to make my site in 2 language depending visitor selection?
is there any way to create array files for each language and call them in view files depends on Session from lang selection? I don't wanna use database.
Appreciate helps! I'm running out of deadline :/ thanks!!
Have you seen CodeIgniter's Language library?
The Language Class provides functions
to retrieve language files and lines
of text for purposes of internationalization.
In your CodeIgniter system folder you'll
find one called language containing sets
of language files. You can create your
own language files as needed in order
to display error and other messages in
other languages.
Language files are typically stored in
your system/language directory. Alternately
you can create a folder called language
inside your application folder and store
them there. CodeIgniter will look first
in your application/language directory.
If the directory does not exist or the
specified language is not located there
CI will instead look in your global
system/language folder.
In your case...
you need to create a polish_lang.php and english_lang.php inside application/language/polish
then create your keys inside that file (e.g. $lang['hello'] = "Witaj";
then load it in your controller like $this->lang->load('polish_lang', 'polish');
then fetch the line like $this->lang->line('hello'); Just store the return value of this function in a variable so you can use it in your view.
Repeat the steps for the english language and all other languages you need.
Also to add the language to the session, I would define some constants for each language, then make sure you have the session library autoloaded in config/autoload.php, or you load it whenever you need it. Add the users desired language to the session:
$this->session->set_userdata('language', ENGLISH);
Then you can grab it anytime like this:
$language = $this->session->userdata('language');
In the controller add following lines when you make the cunstructor
i.e, after
parent::Controller();
add below lines
$this->load->helper('lang_translate');
$this->lang->load('nl_site', 'nl'); // ('filename', 'directory')
create helper file lang_translate_helper.php with following function and put it in directory system\application\helpers
function label($label, $obj)
{
$return = $obj->lang->line($label);
if($return)
echo $return;
else
echo $label;
}
for each of the language, create a directory with language abbrevation like en, nl, fr, etc., under
system\application\languages
create language file in above (respective) directory which will contain $lang array holding pairs label=>language_value as given below
nl_site_lang.php
$lang['welcome'] = 'Welkom';
$lang['hello word'] = 'worde Witaj';
en_site_lang.php
$lang['welcome'] = 'Welcome';
$lang['hello word'] = 'Hello Word';
you can store multiple files for same language with differently as per the requirement
e.g, if you want separate language file for managing backend (administrator section) you can use it in controller as $this->lang->load('nl_admin', 'nl');
nl_admin_lang.php
$lang['welcome'] = 'Welkom';
$lang['hello word'] = 'worde Witaj';
and finally
to print the label in desired language, access labels as below in view
label('welcome', $this);
OR
label('hello word', $this);
note the space in hello & word you can use it like this way as well :)
whene there is no lable defined in the language file, it will simply print it what you passed to the function label.
I second Randell's answer.
However, one could always integrate a GeoIP such as http://www.maxmind.com/app/php
or http://www.ipinfodb.com/. Then you can save the results with the codeigniter session class.
If you want to use the ipinfodb.com api You can add the ip2locationlite.class.php file to your codeigniter application library folder and then create a model function to do whatever geoip logic you need for your application, such as:
function geolocate()
{
$ipinfodb = new ipinfodb;
$ipinfodb->setKey('API KEY');
//Get errors and locations
$locations = $ipinfodb->getGeoLocation($this->input->ip_address());
$errors = $ipinfodb->getError();
//Set geolocation cookie
if(empty($errors))
{
foreach ($locations as $field => $val):
if($field === 'CountryCode')
{
$place = $val;
}
endforeach;
}
return $place;
}
For easier use CI have updated this so you can just use
$this->load->helper('language');
and to translate text
lang('language line');
and if you want to warp it inside label then use optional parameter
lang('language line', 'element id');
This will output
// becomes <label for="form_item_id">language_key</label>
For good reading
http://ellislab.com/codeigniter/user-guide/helpers/language_helper.html
I've used Wiredesignz's MY_Language class with great success.
I've just published it on github, as I can't seem to find a trace of it anywhere.
https://github.com/meigwilym/CI_Language
My only changes are to rename the class to CI_Lang, in accordance with the new v2 changes.
When managing the actual files, things can get out of sync pretty easily unless you're really vigilant. So we've launched a (beta) free service called String which allows you to keep track of your language files easily, and collaborate with translators.
You can either import existing language files (in PHP array, PHP Define, ini, po or .strings formats) or create your own sections from scratch and add content directly through the system.
String is totally free so please check it out and tell us what you think.
It's actually built on Codeigniter too! Check out the beta at http://mygengo.com/string
Follow this https://github.com/EllisLab/CodeIgniter/wiki/CodeIgniter-2.1-internationalization-i18n
its simple and clear, also check out the document # http://ellislab.com/codeigniter/user-guide/libraries/language.html
its way simpler than
I am using such code in config.php:
$lang = 'ru'; // this language will be used if there is no any lang information from useragent (for example, from command line, wget, etc...
if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'],0,2);
$tmp_value = $_COOKIE['language'];
if (!empty($tmp_value)) $lang = $tmp_value;
switch ($lang)
{
case 'ru':
$config['language'] = 'russian';
setlocale(LC_ALL,'ru_RU.UTF-8');
break;
case 'uk':
$config['language'] = 'ukrainian';
setlocale(LC_ALL,'uk_UA.UTF-8');
break;
case 'foo':
$config['language'] = 'foo';
setlocale(LC_ALL,'foo_FOO.UTF-8');
break;
default:
$config['language'] = 'english';
setlocale(LC_ALL,'en_US.UTF-8');
break;
}
.... and then i'm using usualy internal mechanizm of CI
o, almost forget! in views i using buttons, which seting cookie 'language' with language, prefered by user.
So, first this code try to detect "preffered language" setted in user`s useragent (browser). Then code try to read cookie 'language'. And finaly - switch sets language for CI-application
you can make a function like this
function translateTo($language, $word) {
define('defaultLang','english');
if (isset($lang[$language][$word]) == FALSE)
return $lang[$language][$word];
else
return $lang[defaultLang][$word];
}
Friend, don't worry, if you have any application installed built in codeigniter and you wanna add some language pack just follow these steps:
1. Add language files in folder application/language/arabic (i add arabic lang in sma2 built in ci)
2. Go to the file named setting.php in application/modules/settings/views/setting.php. Here you find the array
<?php /*
$lang = array (
'english' => 'English',
'arabic' => 'Arabic', // i add this here
'spanish' => 'Español'
Now save and run the application. It's worked fine.

Resources