Schneimi’s Dev Weblog


Batch generated spritemap and css with ImageMagick on Windows

Posted in CSS,Eclipse by schneimi on February 3, 2013
Tags: , , , ,

I had the idea of one click generated spritemaps for quiet a while, but never had time to look into it and kept going on manually creating them by online service for my projects. When I read an article about command line css spriting, I was amazed, how easy the map can be built using ImageMagick and that it’s even possible to read width and height from the images. Nevertheless, the automated generation of the css code was still missing. Especially when the icon order changes, this is still annoying work.

This made me think…we know how to create the spritemap and can also read the width and height of each sprite with ImageMagick, so the only thing that is left, is to cycle through the sprites, read width or height from them and write the css code to a file. Then I dig out some of my Windows batch skills and came up with the following little batch script. It’s not that easy to understand, so I made some comments on the relevant lines.


spritemap.bat

@echo off
rem /* prepare environment */
setlocal EnableDelayedExpansion
del spritemap.png
del spritemap.css
set position=0
set sign=
rem /* execute imagick convert and create a horizontal (use -append for vertical) sprite map, using black as transparency color */
convert.exe *.png -transparent black +append spritemap.png
rem /* loop through all png files */
for /f %%f in ('dir /B *.png') do (
  rem /* for each file execute imagick identify */
  for /f "tokens=3 delims= " %%i in ('identify.exe %%f') do (
    rem /* parse the image width from the result (use tokens=2 for height in a vertical sprite map) */
    for /f "tokens=1 delims=x" %%j in ('echo %%i') DO (
      rem /* for each image append this css code to the spritemap.css file */
      echo .ui-%%~nf { background-image: url^("../img/spritemap.png"^)^; background-position: !sign!!position!px 0^; }>>spritemap.css
      rem /* calculate the next background position */
      set sign=-
      set /a position=%%j+position
    )
  )
)

In my case the css is for custom icons in jQuery Mobile. This example only runs with all files in one folder, but paths can be adapted easily, as well as the css output. Also further image optimization with tools like optipng, as described in the mentioned article, can be easily added.

I Hope this makes someone as much happy as I are, finally having found a fully automated solution. For me it’s a perfect solution, because I already use batch-scripts to merge and pack all css and js files with one click in eclipse and I just had to add this script to my existing batch file.

Advertisements

User specific caching in CakePHP

Posted in CakePHP by schneimi on February 2, 2013
Tags: , ,

CakePHP caching depends all on controllers and given arguments. But, what if you want different cache files for conditions that don’t come along with the arguments, like session variables?

In my case, I simply wanted separate cache files for each user of my app, because some actions deliver different views for different users, and I didn’t want to pass the User.id to each action in every controller that was concerned.

So I had a look into the CacheHelper and noticed that the file name for cache files is just build from the controller name and given arguments. First I tried to make a modified version of CacheHelper for my app. I don’t remember what problems I ran into, but it just didn’t work out very well.

Then I came up with the idea to internally manipulate the URL, just before the cache is read and written. Somehow this lead me to the lib/Cake/Routing/Filter/CacheDispatcher.php, where I needed just a few lines of additional code to put the User.id into place, which worked like a charm and had no side effects.

You have to copy the original lib/Cake/Routing/Filter/CacheDispatcher.php into your app app/Routing/Filter/UserCacheDispatcher.php and add the following lines of code. To put the dispatcher into action you finally have to register it in the bootstrap.php and replace the core CacheDispatcher.


app/Routing/Filter/UserCacheDispatcher.php

(...)
public function beforeDispatch($event) {
    // load SessionComponent
    App::uses('SessionComponent', 'Controller/Component');
    $session = new SessionComponent(new ComponentCollection());
    $userID = $session->read('User.userID');

    if (!empty($userID)) {
      // add user id to request url with 'u' prefix
      $event->data['request']->here = $event->data['request']->here.'/u'.$userID;

      // remove request parameters to solve issue with ajax request random number parameter
      $event->data['request']->query = '';
    } else {
      return;
    }
(...)


app/Config/bootstrap.php

Configure::write('Dispatcher.filters', array(
    'AssetDispatcher',
    'UserCacheDispatcher'
));

I wonder why this use case is not supported by CakePHP out of the box, or didn’t I just see it? Well, I am aware that this might not be the best solution, but it is at least a pretty simple and effective workaround. Any ideas on how to solve the problem in better ways are welcome.

CakePHP Base64 encoded caching file size

Posted in CakePHP by schneimi on February 2, 2013
Tags: , , ,

I stumbled upon the problem that some cached files in CakePHP took alot more space than expected, and I read about a similar problem on StackOverflow

Well, I took a deeper look into this and it looks like the serialized data in viewVars is used for the nocache parts of the view. So the solution should be to place the part where the base64 data is output between nocache tags.


<!--nocache--><?php echo $base64Data; ?><!--/nocache-->

But doing this, I had the effect, that on some views it worked, on others it didn’t. A closer look into CakePHP revealed, that preg_match_all() is used to look for the nocache parts, which cannot handle a very large amount of data (may depend on server settings) and only finds nocache parts up to a certain amount of data. It doesn’t even throw an error, wich I checked with preg_last_error(). I read about rising pcre memory limits, but it didn’t work for me, so I had to find another solution for that problem.

In my case image data was retrieved from database and placed base64 encoded within the view in an image src tag (src=”data:image/png;base64,…”). My solution was to replace the data in the image src tag with a link to an extra action that delivers just the image data and caches it seperately. This also has the advantage, that the image can now be cached by browsers apart from the view.

The first thing I did, was to care about the image data not coming with the find, in order not to get serialized in the viewVars. But I still needed the information about if there actually was an image in the data, which brought me to setup a virtual field in my model. The virtual field does the following, if there is image data present in the field it holds 1 (TRUE), otherwise 0 (FALSE).


app/Model/YourModel.php

public $virtualFields = array(
    'image' => 'IF(image IS NOT NULL, TRUE, FALSE)'
);

http://book.cakephp.org/2.0/en/models/virtual-fields.html

For the image to retrieve and use, I setup an extra action without view in my controller and cached the data manually. Unfortunately you also have to care about deleting it when your model gets deleted or updated, but you can easily do that in the model save and delete callbacks.

http://book.cakephp.org/2.0/en/models/callback-methods.html


app/Controller/YourController.php

public function image($id) {
  $this->autoRender = false;
  $this->cacheAction = false;

  // read data from cache
  $data = Cache::read('image_'.$id);

  if (empty(data)) {
    $data = $this->YourModel->find('first', array(
        'conditions' => array(
            'YourModel.id' => $id
        ),
        'fields' => array(
            'image_type'
            'image_data'
        )
    ));

    // write data into cache
    Cache::write('image_'.$id, $data);
  }

  // send cache header for browser caching
  header('Content-Type: '.$data['YourModel']['image_type']);
  header('Cache-Control: public, max-age=28800');

  // output the raw image data
  echo $data['YourModel']['image_data];
}

In the view the image src looks like this:

<img src="/your_controller/image/<? echo $id; ?>" />

Another idea would be to generally unset the viewVars if nocache parts aren’t used in a view. But I don’t know where to hook in for that.

Trac SubticketsPlugin with progress bar and report

Posted in Trac by schneimi on February 2, 2013
Tags: , , ,

I came across the problem, that I needed a sub-ticket plugin for Trac v1.0. I found the SubTicketPlugin for TRAC on trac-hacks.org but also the ChildTicketsPlugin, that comes along with a very neat progress meter for parent tickets, but unfortunately is not yet available for Trac v1.0.

Because I wanted it so much, I searched for a way of having such a progress meter with the SubTicketPlugin. Then finally I found a way of building a site template for Trac, where the progress meter is displayed conditionally. It’s not that easy to understand and also uses some inline python code to cover all conditions, but it should work without further explanation. Just place the site.html in the /templates folder of your trac project.

ticket_view


/templates/site.html

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:xi="http://www.w3.org/2001/XInclude"
      xmlns:py="http://genshi.edgewall.org/"
      py:strip="">
<div py:match="div[@class='description']" py:attrs="select('@*')">
  ${select('*')}
  <?python
    subticketCount = 0
    if 'ticket' in vars() and ticket.id != None:
      from trac.env import open_environment
      env = open_environment('D:\Development\TRAC\myapp')
      with env.db_query as db:
          cursor = db.cursor()
          cursor.execute(''.join(["SELECT count(*) FROM subtickets WHERE parent=", str(ticket.id)]))
          for row in cursor:
            subticketCount=row[0]
  ?>
  <py:if test="subticketCount > 0">
    <h3>Progress </h3>
    ${wiki_to_html(context, ''.join(["[[TicketQuery(format=progress,parents=", str(ticket.id), ')]]']))}
  </py:if>
</div>
</html>

I also developed a custom report, showing the tickets and their sub-tickets.

ticket_query

SELECT p.value AS __color__,
       id AS ticket,
       (SELECT GROUP_CONCAT(summary) FROM ticket WHERE id = parent) AS __group__,
       summary,
       component, version, milestone,
       t.type AS type,
       owner, status,
       CONCAT(ROUND(100 / (SELECT COUNT(*) FROM subtickets WHERE parent=t.id) * (SELECT COUNT(*) FROM subtickets INNER JOIN ticket ON (child=ticket.id ) WHERE parent=t.id AND status='closed')), "%") AS progress,
       CASE WHEN (SELECT COUNT(*) FROM subtickets WHERE parent=t.id) > 0 THEN 'border-bottom:solid 3px #DDD;border-top: solid 3px #DDD;background-color: #DDD' ELSE 'text-indent: 10px' END AS __style__,
       time AS created,
       changetime AS _changetime,
       description AS _description,
       reporter AS _reporter
  FROM subtickets s
  LEFT JOIN ticket AS t ON (parent =  t.id)
  LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority'
  WHERE status <> 'closed'
  GROUP BY ticket
UNION
  SELECT  p.value AS __color__,
       id AS ticket,
       (SELECT GROUP_CONCAT(summary) FROM ticket WHERE id = parent) AS __group__,
       summary,
       component, version, milestone,
       t.type AS type,
       owner, status,
       CONCAT(ROUND(100 / (SELECT COUNT(*) FROM subtickets WHERE parent=t.id) * (SELECT COUNT(*) FROM subtickets INNER JOIN ticket ON (child=ticket.id ) WHERE parent=t.id AND status='closed')), "%") AS progress,
       CASE WHEN (SELECT COUNT(*) FROM subtickets WHERE parent=t.id) > 0 THEN 'border-bottom:solid 3px #DDD;border-top:solid 3px #DDD;background-color: #DDD' ELSE 'text-indent: 10px' END AS __style__,
       time AS created,
       changetime AS _changetime,
       description AS _description,
       reporter AS _reporter
  FROM subtickets s
  LEFT JOIN ticket AS t ON (child = id)
  LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority'
  WHERE status <> 'closed'
  GROUP BY ticket
ORDER BY __group__ DESC

Behaviors on model associations

Posted in CakePHP by schneimi on September 6, 2009
Tags: , , , ,

I made a simple behavior that serializes (beforeSave) and deserializes (afterFind) data to and from the database.

The behavior works fine as long as I do a find directly over the model where my behavior is attached, but if I do a find through associated models using the containable behavior, my behavior is not triggered at all and I have to deserialize the data manually after the find, which is very uncomfortable.

Model behavior afterFind is triggered

$this->Model->find(...);

Model behavior afterFind isn’t triggered

$this->OtherModel->contain('Model');
$this->OtherModel->find(...);

I found a ticket and a thread concerning this topic, and it turns out that this is an existing problem with CakePHP for a longer time already.

Because I use the latest CakePHP 1.2.5 and couldn’t find any working patch for it, I adapted the patch found in the thread to this version. I am not very familiar with the core, so it’s still quick&dirty and I cannot tell if it works for every setup. Anyway I hope this still helps other people that run into the same problem.

Here are the changes that have to be done in the cake\libs\model\datasources\dbo_source.php, just place the code after the lines:

Line 877: $resultSet[$i][$association] = $linkModel->afterFind($resultSet[$i][$association]);

foreach ($linkModel->Behaviors->attached() as $behavior) {
  if ($behavior != 'Containable') {
    $data = array(array($association => $resultSet[$i][$association]));
    $filtered_data = $linkModel->Behaviors->{$behavior}->afterFind($linkModel, $data, false);
    
    if ($filtered_data) {
      $resultSet[$i][$association] = $filtered_data[0][$association];
    }
  }
}

Line 741: function queryAssociation(&$model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) {

foreach ($linkModel->Behaviors->attached() as $behavior) {
  if ($behavior != 'Containable') {
    $return = $linkModel->Behaviors->{$behavior}->beforeFind($linkModel, $assocData);
    $assocData = (is_array($return)) ? $return : $assocData;
  }
}

Line 716: $data = $model->{$className}->afterFind(array(array($className => $results[$i][$className])), false);

foreach ($model->{$className}->Behaviors->attached() as $behavior) {
  if ($behavior != 'Containable') {
    $filtered_data = $model->{$className}->Behaviors->{$behavior}->afterFind($model->{$className}, $data, false);
    
    if ($filtered_data) {
      $data = $filtered_data;
    }
  }
}

edit:
I added support for the beforeFind callback, because I needed this on a softdelete behavior to filter associated deleted data as well.

I also found out that you don’t have to really hack the core, instead just copy the cake\libs\model\datasources\dbo_source.php into your app app\models\datasources\dbo_source.php and change this file instead.

Fast random find with CakePHP

Posted in CakePHP by schneimi on August 28, 2009
Tags: , , ,

I want to share my experience with random finds in CakePHP because they can be very slow done the wrong way.

In this example we have the model Audioplaylist which hasAndBelongsToMany Audio and we want to get a random playlist of Audios. We assume the table for Audio is pretty large and we also need the data of other related models as result, let’s say the owner’s username and the title of the related album. All models are set to $recursive = -1 and the ContainableBehavior is used to contain the data.

  • The first approach would be to make one find and get all the data at once.
  • $audios = $this->Audioplaylist->Audio->find('all', array('contain' => array('User.name',
                                                                                'Album.title'),
                                                             'order' => 'RAND()',
                                                             'limit' => $count));
    

    Happy waiting!

    You have to know that DB queries ordered by RAND() get slower the more fields are selected, and in this case we even select all fields of the Audio model.

  • Knowing that, we can improve the find in using the fields parameter to also contain the fields of the Audio model.
  • $audios = $this->Audioplaylist->Audio->find('all', array('contain' => array('User.name',
                                                                                'Album.title'),
                                                             'fields' => 'id',
                                                             'order' => 'RAND()',
                                                             'limit' => $count));
    

    This is already a lot faster than before, but still not very applicable because it still slows down with the amount of related data.

  • So let’s try another approach that looks more circumstantial but avoids the problem seen before. The idea is to make two finds, the first one just finds some random Audio Ids and the second will then use the Ids to catch all the related data.
  • $randomAudioIds = $this->Audioplaylist->Audio->find('list', array('fields' => 'id',
                                                                      'order' => 'RAND()',
                                                                      'limit' => $count));
    
    $audios = $this->Audioplaylist->Audio->find('all', array('contain' => array('User.name',
                                                                                'Album.title'),
                                                             'conditions' => array('Audio.id' => $randomAudioIds),
                                                             'order' => 'RAND()'));
    

    Sometimes more is less, especially if you need alot of data from related models, you will appreciate the performance of these two queries.

    Star rating plugin for CakePHP

    Posted in Ajax,CakePHP by schneimi on May 25, 2009
    Tags: , , , , , ,

    In the google groups I recently noticed the lack of a star rating helper/plugin for CakePHP.

    As I just implemented an AJAX rating system for my current project and always wanted to learn the CakePHP plugin system, I thought this was a good opportunity to make my code into a plugin and contribute to the community.

    My rating system supports multiple users and multiple models, so you can let people rate any model you want.

    I tried to keep it simple and flexible, but I am sure there is still much room for improvements. So let me know what you think about it, any suggestions and comments are welcome.

    Here is the plugin v2.5 for CakePHP 1.3 to download. Older versions for CakePHP 1.2 can be found here.

    This article can also be found in the Bakery.

    Features

    • Multi user, multi model rating
    • Guest rating
    • Just one element to place in your views
    • Seamless integration with AJAX
    • Prototype and jQuery support
    • Cross browser compatibility
    • Fallback for disabled javascript
    • Various configurations

    Requirements

    • CakePHP 1.2, 1.3
    • Prototype or jQuery javascript framework
    • User id stored in session for secure rating

    Demonstration
    A demo can be tested at
    http://ratingdemo.schneimi.hostingsociety.com/

      Installation and Use

      • Make sure you meet the requirements above. Make sure you meet the requirements above. For the download and integration of a javascript framework, please visit the Prototype or jQuery website.
        • Extract the plugin, including the subfolder ‘rating’, to your app plugins folder ‘app/plugins‘.
          • Copy the ‘rating/config/plugin_rating.php’ to your app configs folder ‘app/config’ and change the settings to your desire. It is recommended to let ‘Rating.showHelp’ set to true until everything works.
            • Apply the ‘install.sql’ to your database to create the ratings table.
              CREATE TABLE `ratings` (
                `id` int(11) unsigned NOT NULL auto_increment,
                `user_id` char(36) NOT NULL default '',
                `model_id` char(36) NOT NULL default '',  
                `model` varchar(100) NOT NULL default '',
                `rating` tinyint(2) unsigned NOT NULL default '0',
                `name` varchar(100) default '',
                `created` datetime default NULL,
                `modified` datetime default NULL,
                PRIMARY KEY (`id`),
                KEY `rating` (`model_id`,`model`,`rating`,`name`)
              );
              
            • Load the plugin javascript and css files in your layout file. Replace [your_framework] with prototype_min or jquery_min depending on the framework you use.
                <?php echo $javascript->link('/rating/js/[your_framework]'); ?>
                <?php echo $html->css('/rating/css/rating'); ?> 
                
            • For full model integration in your app, apply the following relation to your models. (replace [name_of_your_model])
                var $hasMany = array('Rating' =>
                                     array('className'   => 'Rating',
                                           'foreignKey'  => 'model_id',
                                           'conditions' => array('model' => '[name_of_your_model]'),
                                           'dependent'   => true,
                                           'exclusive'   => true
                                     )
                               );  
            • If you set ‘Rating.saveToModel’ to true, then add the defined ‘Rating.modelAverageField’ and ‘Rating.modelVotesField’ to all models you want to rate. To do that you can use the following SQL statements (replace [your_table] and [Rating.modelAverageField]).
              ALTER TABLE [your_table] ADD (`[Rating.modelAverageField]` decimal(3,1) unsigned default '0.0');
              ALTER TABLE [your_table] ADD (`[Rating.modelVotesField]` int(11) unsigned default '0');

              If the plugin shows the fields are still missing, try to clear the model cache of your app at ‘app/tmp/cache/models’.

            • You can change the styles of the rating element in the css file ‘rating/vendors/css/rating.css’.
              • Finally you can place the rating element in your views as follows. (replace [name_of_your_model] and [id_of_your_model])

                Default rating element for one model id

                  echo $this->element('rating', array('plugin' => 'rating',
                                                      'model' => '[name_of_your_model]',
                                                      'id' => [id_of_your_model]));
                 

                More ratings for one model id
                If you want to have different ratings for one model id like sound and picture of a movie, you can use the additional name parameter.

                  echo $this->element('rating', array('plugin' => 'rating',
                                                      'model' => '[name_of_your_model]',
                                                      'id' => [id_of_your_model],
                                                      'name' => 'sound'));
                
                  echo $this->element('rating', array('plugin' => 'rating',
                                                      'model' => '[name_of_your_model]',
                                                      'id' => [id_of_your_model],
                                                      'name' => 'picture'));
                

                Individual configuration of a rating element
                Sometimes you want to use more than one style of rating elements in your app. That can be reached with the ‘config’ parameter and different config files in ‘app/config’. Just clone the original ‘plugin_rating.php’ and give it a different name, which you then pass to the element. There is also the possibility to overload the config file settings on the element.

                  // Uses 'plugin_rating.php' in 'app/config'
                  echo $this->element('rating', array('plugin' => 'rating', 
                                                      'model' => '[name_of_your_model]',
                                                      'id' => [id_of_your_model]));
                  
                  // Uses 'plugin_rating_style1.php' in 'app/config'
                  echo $this->element('rating', array('plugin' => 'rating',
                                                      'model' => '[name_of_your_model]',
                                                      'id' => [id_of_your_model],
                                                      'config' => 'plugin_rating_style1'));
                
                  // overload default settings
                  echo $this->element('rating', array('plugin' => 'rating',
                                                      'model' => [name_of_model],
                                                      'id' => [id_of_model],
                                                      'config' => array('Rating.[setting_1]' => true,
                                                                       'Rating.[setting_2]' => false)));
                                                    
                  // overload individual settings
                  echo $this->element('rating', array('plugin' => 'rating',
                                                      'model' => [name_of_model],
                                                      'id' => [id_of_model],
                                                      'config' => array('plugin_rating_style1', array(
                                                          'Rating.[setting_1]' => true,
                                                          'Rating.[setting_2]' => false));
                

              Speed-up your website with one click in eclipse

              Posted in CSS,Eclipse,JS by schneimi on April 30, 2009
              Tags: , , , , , ,

              The most important influence on the loading time of a website, apart from the amount and size of images, is the loading of JS and CSS files.

              This tutorial shows you three approaches to speed up the loading of JS and CSS and how to combine them in a batch script. Finally it shows how to integrate the script in eclipse and to execute it with one click.

              First of all, I will shortly describe the different approaches, before going into detail.

              Consolidation
              During development you need structure and overview, so it is necessary to split the JS and CSS code into different files. But the more files you include in your website, the more network connections have to be set up on loading, which costs unnecessary time and traffic.

              To avoid this problem, the idea is to consolidate all files (of one type) to one file, so that only one connection has to be set up.

              Shrinking
              Shrinking is the removal of all unneeded characters like whitespace and comments in your code. Further the replacement of long variable-names by short ones, which simultaneously obfuscates the code.

              Compression
              To reduce the data being transferred, the webserver can compress it before sending. A receiving webbrowser should be able to recognize the compression and decompress the data.

              The most popular compression for websites is the GZIP compression, which is supported by pretty much all browsers.

              The following steps are for Windows users, but I am sure you can transform them to Linux without much effort!

              1) Consolidation
              The first step is to consolidate your JS and CSS files, so you only have to shrink and compress one file of each type.

              Because JS and CSS includes are globally loaded into the browser, we are able to just glue all files together and include them as one file. A very simple method to achieve that, is the use of the copy command as follows:

              >copy *.css styles_consolidated.css /B
              >copy *.js scripts_consolidated.js /B
              

              The /B does a binary copy and avoids text encoding problems!

              2) Shrinking
              To get rid of unnecessary characters in your code, there are several tools available. As we want to automate the procedure later, we choose the YUI-Compressor which is a great java command line tool that supports JS as well as CSS.

              The only thing we have to do, is to set it on a JS or CSS file to create a shrinked version:

              >java -jar yuicompressor-2.4.1.jar scripts_consolidated.js -o scripts_shrinked.js
              >java -jar yuicompressor-2.4.1.jar styles_consolidated.css -o styles_shrinked.css
              

              If there are any errors in your code, you will get a notice here!

              3) Compression
              There are two ways compression can be applied. You can compress a file once and deposit it, or you can compress a file on-the-fly when transmitting it.

              I use the on-the-fly compression with PHP, because I use PHP anyway and the file is still readable. This is useful during development, where you may want to skip the shrinking for debugging and get useful line numbers in error messages.

              To activate the compression for a file, you just have to make it into a PHP file and add one line of code:

              scripts_shrinked.js.php

              <?php ob_start('ob_gzhandler');header("Content-type: text/javascript; charset: UTF-8"); ?>
              [content of scripts_shrinked.js]
              

              styles_shrinked.css.php

              <?php ob_start('ob_gzhandler');header("Content-type: text/css; charset: UTF-8"); ?>
              [content of styles_shrinked.css]
              

              You include these PHP files like normal JS and CSS files:

              <script type="text/javascript" src="scripts_shrinked.js.php"></script>
              <link rel="stylesheet" type="text/css" href="styles_shrinked.css.php" />
              

              4) Combination
              To combine all three approaches, we will create a little batch script.

              But first, we need to create two PHP files that will be used to apply the GZIP compression to our consolidated and shrinked JS and CSS file.

              gzip_js.php

              <?php ob_start('ob_gzhandler');header("Content-type: text/javascript; charset: UTF-8"); ?>
              

              gzip_css.php

              <?php ob_start('ob_gzhandler');header("Content-type: text/css; charset: UTF-8"); ?>
              

              The following batch script is just an example and is set up for CakePHP and the use in different environments. You have to adapt the paths for individual usage!

              combine.bat

              @echo on
              copy %1\app\vendors\js\protoaculous1.6.packed.js+%1\app\webroot\js\*.js %1\app\webroot\js\scripts_consolidated.js /B
              java -jar %1\app\vendors\yuicompressor-2.4.1.jar %1\app\webroot\js\scripts_consolidated.js -o %1\app\webroot\js\scripts_shrinked.js
              copy %1\app\gzip_js.php+%1\app\webroot\js\scripts_shrinked.js %1\app\webroot\js\scripts.js.php /B /Y
              @echo off
              del %1\app\webroot\js\scripts_consolidated.js
              del %1\app\webroot\js\scripts_shrinked.js
              
              @echo on
              copy %1\app\webroot\css\*.css %1\app\webroot\css\styles_consolidated.css /B
              java -jar %1\app\vendors\yuicompressor-2.4.1.jar %1\app\webroot\css\styles_consolidated.css -o %1\app\webroot\css\styles_shrinked.css
              copy %1\app\gzip_css.php+%1\app\webroot\css\styles_shrinked.css %1\app\webroot\css\styles.css.php /B /Y
              @echo off
              del %1\app\webroot\css\styles_consolidated.css
              del %1\app\webroot\css\styles_shrinked.css
              

              Note that additional files are added with a plus sign!

              Finally, we have to execute the script with the approot as first parameter and let the magic happen:

              >combine.bat [cakePHP approot]
              

              5) Eclipse integration
              Eclipse offers an easy way to integrate external programs and scripts. Here is what you have to do to be able to execute the script with one click:

              1. Open the External Tools Dialog (in submenu of the run-button with red toolbox)
              2. Create a new entry
              3. Enter some name
              4. Enter the location: ${workspace_loc:cake\app\combine.bat}
              5. Enter Arguments: ${workspace_loc:cake}
              6. Choose the “Common” tab
              7. Check “External Tools” under “Display in favorites menu”
              8. Check “Allocate console (necessary for input)” to see the output in the console view

              Now you should be able to select the created external tool and execute it with one click.

              AES 128Bit encryption between Java and PHP

              Posted in Java by schneimi on November 25, 2008

              Here is how AES encryption works between Java and PHP using the mcrypt module in PHP.

              This is mainly a quick summary of the 4-part tutorial at: http://propaso.com/blog/?cat=6

            • Generate Key in Java
            • String iv = "fedcba9876543210";
              IvParameterSpec ivspec;
              KeyGenerator keygen;
              Key key;
              
              ivspec = new IvParameterSpec(iv.getBytes());
              
              keygen = KeyGenerator.getInstance("AES");
              keygen.init(128);
              key = keygen.generateKey();
              
              keyspec = new SecretKeySpec(key.getEncoded(), "AES");
              
            • Encryption in Java
            • Cipher cipher;
              byte[] encrypted;
              
              cipher = Cipher.getInstance("AES/CBC/NoPadding");
              cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
              encrypted = cipher.doFinal(padString(text).getBytes());
              
            • Decryption in Java
            • Cipher cipher;
              byte[] decrypted;
              
              cipher = Cipher.getInstance("AES/CBC/NoPadding");
              cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
              decrypted = cipher.doFinal(hexToBytes(code));
              
            • Encryption in PHP
            • function encrypt($str, $key) {
                $key = $this->hex2bin($key);    
              
                $td = mcrypt_module_open("rijndael-128", "", "cbc", "fedcba9876543210");
              
                mcrypt_generic_init($td, $key, "fedcba9876543210");
                $encrypted = mcrypt_generic($td, $str);
              
                mcrypt_generic_deinit($td);
                mcrypt_module_close($td);
              
                return bin2hex($encrypted);
              }
              
            • Decryption in PHP
            • function decrypt($code, $key) {
                $key = $this->hex2bin($key);
                $code = $this->hex2bin($code);
              
                $td = mcrypt_module_open("rijndael-128", "", "cbc", "");
              
                mcrypt_generic_init($td, $key, "fedcba9876543210");
                $decrypted = mdecrypt_generic($td, $code);
              
                mcrypt_generic_deinit($td);
                mcrypt_module_close($td);
              
                return utf8_encode(trim($decrypted));
              }
              
            • Additional functions in Java
            • private byte[] hexToBytes(String hex) {
                String HEXINDEX = "0123456789abcdef";
                int l = hex.length() / 2;
                byte data[] = new byte[l];
                int j = 0;
              
                for (int i = 0; i < l; i++) {
                  char c = hex.charAt(j++);
                  int n, b;
              
                  n = HEXINDEX.indexOf(c);
                  b = (n & 0xf) << 4;
                  c = hex.charAt(j++);
                  n = HEXINDEX.indexOf(c);
                  b += (n & 0xf);
                  data[i] = (byte) b;
                }
              
                return data;
              }
              
              private String padString(String source) {
                char paddingChar = ' ';
                int size = 16;
                int padLength = size - source.length() % size;
              
                for (int i = 0; i < padLength; i++) {
                  source += paddingChar;
                }
              
                return source;
              }
              
            • Additional functions in PHP
            • function hex2bin($hexdata) {
                $bindata = "";
              
                for ($i = 0; $i < strlen($hexdata); $i += 2) {
                  $bindata .= chr(hexdec(substr($hexdata, $i, 2)));
                }
              
                return $bindata;
              }
              

              RSA encryption between Java and PHP

              Posted in Java by schneimi on November 25, 2008

              This article shows you one way to get RSA encryption working between Java and PHP without any extra libraries or classes, you only need the openssl module activated on PHP side.

              The goal is to encrypt a text with a public key in Java and send the code to PHP where it is decoded with the private key.

              It took me two days and a lot of googling to figure this out, and I hope this will help others not to spend so much time on this topic.

              1) Install and Configure PHP OpenSSL (Windows)

              • php.ini: extension=php_openssl.dll
              • Set environment variable   
                OPENSSL_CONF to C:\Programme\Apache2.2\php\extras\openssl\openssl.cnf
              • Set environment variable   
                PATH to C:\Programme\Apache2.2\php

              2) Generate a private keyfile with PHP

                $keys = openssl_pkey_new();
                $priv = openssl_pkey_get_private($keys);
                openssl_pkey_export_to_file($priv, 'private.pem');
              

              3) Generate a public .der-file from the private keyfile with OpenSSL

              • openssl rsa -in private.pem -pubout -outform DER -out public.der

              4) Import the public key in Java

                File pubKeyFile = new File("public.der");
                DataInputStream dis = new DataInputStream(new FileInputStream(pubKeyFile));
                byte[] keyBytes = new byte[(int) pubKeyFile.length()];
                
                dis.readFully(keyBytes);
                dis.close();
              
                X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                RSAPublicKey publicKey = (RSAPublicKey)keyFactory.generatePublic(keySpec);
              

              5) Encode the data in Java with the public key

                Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
                cipher.init(Cipher.ENCRYPT_MODE, publicKey);
                encrypted = cipher.doFinal(text.getBytes());
              

              6) Decode the data with the private key in PHP

              $fp = fopen(“private.pem”, “r”);
              $privateKey = fread($fp, 8192);
              fclose($fp);

              $res = openssl_get_privatekey($privateKey);
              openssl_private_decrypt($this->hex2bin($params[‘cipher’]), $decrypted, $res);

              // $decrypted is the result

              function hex2bin($hexdata) {
              $bindata = “”;

              for ($i = 0; $i < strlen($hexdata); $i += 2) { $bindata .= chr(hexdec(substr($hexdata, $i, 2))); } return $bindata; } [/sourcecode] 7) If you want to decrypt with the public key in PHP as well, you can generate a public.pem file with OpenSSL

              • openssl rsa -in private.pem -out public.pem -outform PEM -puboutr
              Next Page »