After trying quite a few solutions, I have settled for the most popular WordPress plugin, NextGen Gallery (Pro version) and never looked back.
The migration process has been quite straightforward (more on this in another post maybe later) and some technical issues have been quickly answered by the very efficient support team.
Some customization was however required due to the complex set up
For those using NextGen already, I have created my albums as main container per year (eg. 2019, 2018) and then attached my galleries into each album. This allowed me to have to build only 12 pages to display the galleries from 2007 to 2019 with the following simple shortcode in each page.
[ngg src=”albums” ids=”xx” display=”basic_extended_album” order_direction=”ASC” template=”basic-album”]
The first problem I have faced was, because I didn’t have a post attached to each gallery there was no easy way for me to display the title the way I wanted in each gallery page.
Breadcumbs template to the rescue
I have however noticed that by enabling the breadcrumbs in the basic extended album setting the gallery title was retrieved by the plugin. I have thus decided to override this template and quickly came up with the following code to display the gallery title in the page.
<?php $end = end($breadcrumbs); ?> <?php if(array_key_exists('name', $end)): ?> <h2><?php echo $end['name']; ?></h2> <?php endif; ?>
The second problem I noticed was the breadcrumb (included with Yoast SEO) which was not retrieving the gallery title either since there was no dedicated page created.
This time I have used a more complex filter to overcome my issue which is looking for a nggallery
string in the request URI (default slug used by NextGen) and retrieve the matching album to append the correct breadcrumb to the existing array.
add_filter('wpseo_breadcrumb_links', 'yoast_seo_breadcrumb_append_link'); function yoast_seo_breadcrumb_append_link($links) { $request_parts = explode('/', $_SERVER['REQUEST_URI']); if (in_array('nggallery', $request_parts)) { $gallery_slug = end($request_parts); global $wpdb; $gallery = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}ngg_gallery WHERE `slug` = '$gallery_slug'", ARRAY_A); if ($gallery) { $links[] = array( 'text' => $gallery[0]['title'], ); } } return $links; }
I hope this would help someone with the same set up one day.
Do not hesitate to comment if you have any question.
]]>For some unexplicable reason Prestashop has decided to have a single "Meta title" field in CMS page edition. However you most often need to have a meta title different from your page title (h1), which could also be different from the label used in a menu.
In order to add those additional fields in our CMS admin we are going to create a module and register a hook on the AdminCmsFormModifier. Find below the full module code
Once your module is installed and database updated you should be able to edit the 2 new created fields.
Now to use the "real_meta_title" field override the Meta class by creating a Meta.php file in "override/classes" such as
Finally, in your CMS page template you can update the "page_title" block such as
{block name='page_title'}
{$cms.page_title}
{/block}
I hope this will be useful to you. Please comment if you have come over the same issue and solved it differently.
Let's assume the following scenario where you have a Product entity linked to a Category entity by a ManyToOne relationship.
In your twig template you want to loop on all categories and display a list of products for each of them. The {{ category.products }} will give you that ... no problem. Now imagine you want to retrieve only active products ordered by position. Since it is not good practice to call the Doctrine Entity Manager in your entity, here comes Doctrine Criteria Filters to the rescue.
In your Category entity create the following method
public function getActiveProducts()
{
$criteria = Criteria::create()
->where(Criteria::expr()->eq("enabled", 1))
->andWhere(Criteria::expr()->eq("category", $this))
->orderBy(array("position" => Criteria::ASC));
return $this->getProducts()->matching($criteria);
}
To loop on your active and ordered products you can now call the method like this in your twig template
{% for product in category.activeProducts %}
[...]
{% endfor %}
More information on the Doctrine documentation.
We assume that you have SonataMediaBundle up and running in your project and have extended the bundle with SonataEasyExtendsBundle in your Application folder.
Next, add the following class in your extended bundle in a Resizer folder
namespace Application\Sonata\MediaBundle\Resizer;
use Imagine\Image\ImagineInterface;
use Imagine\Image\Box;
use Gaufrette\File;
use Sonata\MediaBundle\Model\MediaInterface;
use Imagine\Image\ImageInterface;
use Imagine\Exception\InvalidArgumentException;
use Sonata\MediaBundle\Metadata\MetadataBuilderInterface;
use Sonata\MediaBundle\Resizer\ResizerInterface;
class FixedDimensionsResizer implements ResizerInterface
{
protected $adapter;
protected $mode;
protected $metadata;
/**
* @param ImagineInterface $adapter
* @param string $mode
*/
public function __construct(ImagineInterface $adapter, $mode, MetadataBuilderInterface $metadata)
{
$this->adapter = $adapter;
$this->mode = $mode;
$this->metadata = $metadata;
}
/**
* {@inheritdoc}
*/
public function resize(MediaInterface $media, File $in, File $out, $format, array $settings)
{
if (!isset($settings['width'])) {
throw new \RuntimeException(sprintf('Width parameter is missing in context "%s" for provider "%s"', $media->getContext(), $media->getProviderName()));
}
$image = $this->adapter->load($in->getContent());
$content = $image
->thumbnail($this->getBox($media, $settings), $this->mode)
->get($format, array('quality' => $settings['quality']));
$out->setContent($content, $this->metadata->get($media, $out->getName()));
}
/**
* {@inheritdoc}
*/
public function getBox(MediaInterface $media, array $settings)
{
$size = $media->getBox();
if ($settings['width'] == null && $settings['height'] == null) {
throw new \RuntimeException(sprintf('Width/Height parameter is missing in context "%s" for provider "%s". Please add at least one parameter.', $media->getContext(), $media->getProviderName()));
}
if($settings['constraint'] === false){
return new Box($settings['width'], $settings['height']);
}
if ($settings['height'] == null) {
$settings['height'] = (int) ($settings['width'] * $size->getHeight() / $size->getWidth());
}
if ($settings['width'] == null) {
$settings['width'] = (int) ($settings['height'] * $size->getWidth() / $size->getHeight());
}
return $this->computeBox($media, $settings);
}
/**
* @throws InvalidArgumentException
*
* @param MediaInterface $media
* @param array $settings
*
* @return Box
*/
private function computeBox(MediaInterface $media, array $settings)
{
if ($this->mode !== ImageInterface::THUMBNAIL_INSET && $this->mode !== ImageInterface::THUMBNAIL_OUTBOUND) {
throw new InvalidArgumentException('Invalid mode specified');
}
$size = $media->getBox();
$ratios = array(
$settings['width'] / $size->getWidth(),
$settings['height'] / $size->getHeight()
);
if ($this->mode === ImageInterface::THUMBNAIL_INSET) {
$ratio = min($ratios);
} else {
$ratio = max($ratios);
}
return $size->scale($ratio);
}
}
Then, add the following service in your config.yml
services:
sonata.media.resizer.fixedDimensions:
class: Application\Sonata\MediaBundle\Resizer\FixedDimensionsResizer
arguments: [@sonata.media.adapter.image.gd, 'outbound', @sonata.media.metadata.proxy]
Finally enabled the provider in the sonata_media config
sonata_media:
[...]
providers:
image:
resizer: sonata.media.resizer.fixedDimensions
When you add a new context for your image you can now add the following parameter ton constraint your image dimensions to the width and height specified.
sonata_media:
[...]
contexts:
my_context:
providers:
- sonata.media.provider.image
formats:
small: { width: 400, height: 300, constraint: false}
First of all you will need to configure the database connection. We will assumme you have followed the previous tutorials and have an Oracle XE database.
parameters: database_driver: oci8 database_host: your_VM_IP database_port: '1521' database_name: XE database_user: your_username database_password: your_password database_charset: AL32UTF8
Database charset is by default AL32UTF8 in Oracle XE but if you have to move your database to another Oracle version I would recommend to specify it explicitely in the configuration.
You need to make sure you have the following parameter in your app/config/config.yml
doctrine: dbal: charset: %database_charset%
You need then to add a custom listener to handle the session properly.
services: oracle.listener: class: Doctrine\DBAL\Event\Listeners\OracleSessionInit tags: - { name: doctrine.event_listener, event: postConnect }
Doctrine does not support where conditions on CLOB. You need to update your schema using the following PR https://github.com/sonata-project/SonataPageBundle/pull/104
Finally there are a few known issues between Oracle and Doctrine
]]>Open a terminal and start the configuration process with the following command
> /etc/init.d/oracle-xe configure
Go to Applications > Oracle Database 11G … > Get Started
Click on Application Express and login with the admin user you have previously configured (default user is sys)
Follow the process to create the environment and keep a note of your username and password.
Go to Applications > Oracle Database 11G … > Run SQL command line
CREATE USER DUMMYUSER IDENTIFIED BY password DEFAULT TABLESPACE "YOURTABLESPACE" TEMPORARY TABLESPACE "TEMP"; ALTER USER DUMMYUSER QUOTA UNLIMITED ON FFFCNF; GRANT create procedure, create session, create table, create type, create view, create synonym, create trigger, resource TO DUMMYUSER;
Add to /etc/profile the following configuration
ORACLE_HOME=/u01/app/oracle/product/11.2.0/xe PATH=$PATH:$ORACLE_HOME/bin export ORACLE_HOME export ORACLE_SID=XE export PATH
Then reload your profile with
> source /etc/profile
Finally restart Oracle with the following command
> /etc/init.d/oracle-xe restart
You can test the connection with the following PHP script
$conn = oci_connect('username', 'password', 'IP:1521/XE'); if (!$conn) { $e = oci_error(); trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR); }
If you have a listener error message read the next paragraph
We are going to register the listener dynamically so it can start with our Oracle instance.
Stop the listener
> lsnrctl stop
Remove the listener.ora (just move it to keep a copy)
> mv /u01/app/oracle/product/11.2.0/xe/network/admin/listener.ora /u01/app/oracle/product/11.2.0/xe/network/admin/listener.ora.old
Restart the service
> lsnrctl start
Connect to the database
> sqlplus Enter user-name: sys as sysdba
(Re)start an Oracle instance (shutdown first if there is one running)
SQL> shutdown Database closed. Database dismounted. ORACLE instance shut down. SQL> startup ORACLE instance started. Total System Global Area 204591104 bytes Fixed Size 2225032 bytes Variable Size 171969656 bytes Database Buffers 25165824 bytes Redo Buffers 5230592 bytes Database mounted. Database opened. SQL> exit Disconnected from Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
Check the status listener
> lsnrctl status
The following line should appear
Service "XE" has 1 instance(s). Instance "XE", status READY, has 1 handler(s) for this service...
If you get the following error
ORA-04031: unable to allocate 3896 bytes of shared memory ("shared pool","update seq$ set increment$=:...","sga heap(1,0)","kglsim object batch")
Connect with sqlplus and run the following queries
SQL> alter system set SHARED_POOL_RESERVED_SIZE = '64M' scope=spfile; SQL> alter system set SHARED_POOL_SIZE = '200M' scope=spfile;
If you have messed up with your Oracle configuration and cannot start the server anymore you can access your configuration file with the following procedure
SQL> create pfile='/tmp/pfile' from spfile; Editer le fichier /tmp/pfile en modifiant les valeurs concernées SQL> create spfile from pfile='/tmp/pfile'; SQL> startup
Tip to know all the size parameters
SQL> show parameter size;
http://jamessmith73.wordpress.com/james-smiths-java/just-like-that/oracle-xe-database-on-ubuntu/
http://edstevensdba.wordpress.com/2011/07/30/exploring-the-local_listener-parameter/
http://www.nielskrijger.com/2012/06/creating-tablespace-and-user-in-oracle.html
https://forums.oracle.com/forums/thread.jspa?threadID=2421429
http://oraclequirks.blogspot.co.uk/2008/05/ora-02095-specified-initialization.html
First of all you need to get Virtualbox for your operating system
https://www.virtualbox.org/wiki/Downloads
Then you need to create an account on the Oracle website (if you don’t have one already) and get the Enterprise PHP Development VM
http://www.oracle.com/technetwork/community/developer-vm/index.html
Import the VM in Virtualbox (File > Import Appliance) and follow instructions.
Click on the appliance created > Settings > Network and change the adapter to “Bridged Adapter”. In the advanced tab, update the “Promiscuous mode” to “Allow all”.
Start the VM and login using the root account provided. Then go in System > Administration > Network. Open the network adapter (eth0) and configure a static IP address according to your local network. Leave 0.0.0.0 for the default gateway.
Save and restart the network adapter for your configuration to be reset.
Start a terminal on your computer and execute the following commands to make sure you can reach the VM.
> ping [STATIC_IP] > telnet [STATIC_IP] 1521
If this does not work, try to disable the firewall in System > Administration > Firewall and security
http://www.oracle.com/technetwork/articles/servers-storage-admin/evaluating-linux-vb-1934676.html
]]>