All about the development of eZ Publish by Graham Brookins, 7x (formerly Brookins Consulting) and our think tank kracker.org.
I bring news of a new solution 7x is working on for developers with an interest to syndicate information without an rss / atom / xml / etc feed available.
Today if you want to include your companies YouTube Playlist videos (say commercials or educational materials) you can embed this content manually within the administrator content editing features available by default.
What if you want to sync content from YouTube like your companies playlist video content (the meta data not the actual videos, playback is through YouTube's embed video URL APIs) into the eZ Content Tree Automatically?
Currently this is possible today without a feed via old fashioned web page scraping of specific account playlists video title and video URL URI v element data (video's id) using RSS-Bridge.
Normally RSS-Bridge (doc) is a standalone application well written in OOP PHP. Because documentation on using the library standalone is limited we created a solution to bootup the software layers and call the provided YouTube Playlist Data Collection APIs from RSS-Bridge within an eZ Publish Request Scope within a eZ Publish Cronjob Part Script.
Filename: extension/sevenx_rss_bridge/cronjobs/7xRssBridgeYoutubeFeedImport.php
<?php if ( !$isQuiet ) { $cli->output( "Creating youtube video object(s) ..." ); } $count = SevenxRssBridgeFeedImport::importFeedContentObjects(); if ( !$isQuiet ) { $cli->output( "Number of objects created: $count" ); $cli->output( "Done." ); } ?>
Got your attention now with a stub for calling the features we need within eZ (Remember to enable via cronjob settings and clear all caches).
Filename: extension/sevenx_rss_bridge/settings/cronjobs.ini.append.php
<?php /* #?ini charset="utf8"? [CronjobSettings] ExtensionDirectories[]=sevenx_rss_bridge [CronjobPart-youtube-feed-import] Scripts[]=7xRssBridgeYoutubeFeedImport.php */ ?>
Next we have the worker class that does the work to fetch the YouTube information, create the YouTube videos (Meta Data Based Embeds in a Playlist) underneath Youtube Playlist Nodes Containing the Playlist Meta Data.
With this we first create the playlist nodes (locations) with the required playlist YouTube embed URL + Parameters (like list which helps provide long form content within a playlist). With the following code in place (remember to regenerate eZ's Autoloads) we can call the cronjob and sync / import the data from YouTube into eZ Publish Content Tree.
Filename: extension/sevenx_rss_bridge/classes/SevenxRssBridgeFeedImport.php
<?php class SevenxRssBridgeFeedImport { public static function importFeedContentObjects() { // Settings $storageNodeID = eZINI::instance('sevenx_videos.ini')->variable('PlaylistSettings','PlaylistNodeID'); // Fetch Playlists Objects to store the videos underneath. $playlists = self::fetchNodeContent($storageNodeID ); // Initialize the count of imported items $importedCount = 0; // Include RSS-Bridge autoloader require_once('vendor/rss-bridge/rss-bridge/lib/bootstrap.php'); foreach ( $playlists as $playlist ) { if ( $playlist->attribute('children_count') <= 0 ) { $playlistsNodeID = $playlist->attribute('node_id'); $dm = $playlist->dataMap(); $playlistID = $dm[ 'youtube_url' ]->content(); if ( !str_contains($playlistID, 'videoseries' ) ) continue; $playlistID = explode( 'list=', $playlistID )[1]; $bridgeParams = [ 'p' => $playlistID ]; try { $main = new RssBridge(); $bridgeFactory = new BridgeFactory(); // Directly instantiate the YouTubeBridge class $bridge = $bridgeFactory->create( YoutubeBridge::class ); // Set parameters $bridge->setInput( $bridgeParams ); // Fetch data $data = $bridge->collectData(); $items = $bridge->getItems(); //var_dump( $items ); // Process each video entry foreach ($items as $item) { $importedCount += self::importYouTubeVideo($item, $playlistsNodeID, $playlistID); } } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; return 0; // Return 0 in case of error } } } echo "Successfully imported YouTube videos.\n"; return $importedCount; // Return the count of imported items } private static function importYouTubeVideo($item, $storageNodeID, $playlistID) { $adminUser = 'admin'; // no password used so safe and available by default. change if needed. $classID = 48; // Note: Please change to your video class. // Extract video data $title = $item['title']; $description = $item['content']; $url = $item['uri']; $video = explode( '?v=', $url )[1]; $url = "https://www.youtube.com/embed/$video?si=" . $video . '&list=' . $playlistID; // var_dump($item['title']); // var_dump($url); // die('fin'); // return 1; // Fetch admin user $user = eZUser::fetchByName( $adminUser ); $userCreatorID = $user->attribute( 'contentobject_id' ); // Create object $defaultSectionID = 1; $class = eZContentClass::fetch( $classID ); $contentObject = $class->instantiate( $userCreatorID, $defaultSectionID ); // Set remote_id content //$remoteID = "contentserver:incomingnode"; //$contentObject->setAttribute( 'remote_id', $remoteID ); $contentObject->store(); // Fetch related IDs $contentObjectID = $contentObject->attribute( 'id' ); //$userID = $contentObjectID; // Create node assignment $nodeAssignment = eZNodeAssignment::create( array( 'contentobject_id' => $contentObjectID, 'contentobject_version' => 1, 'parent_node' => $storageNodeID, 'is_main' => 1 ) ); $nodeAssignment->store(); // Set version modified and status content $version = $contentObject->version( 1 ); $version->setAttribute( 'modified', time() ); $version->setAttribute( 'status', eZContentObject::STATUS_DRAFT ); $version->store(); // Fetch contentObject IDs $contentObjectID = $contentObject->attribute( 'id' ); $contentObjectAttributes = $version->contentObjectAttributes(); // Set Name $contentObjectAttributes[0]->setAttribute( 'data_text', $title ); $contentObjectAttributes[0]->store(); // Set Name $contentObjectAttributes[2]->fromString( "$url|$title" ); $contentObjectAttributes[2]->store(); // Publish content object to top level root node $operationResult = eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $contentObjectID,'version' => 1 ) ); echo "Imported video: $title\n $url\n"; return 1; // Return 1 to indicate successful import } public static function fetchNodeContent( $nodeID = 2, $limit = false, $classIdentifierArray = array( 'playlist' ) ) { $node = eZContentObjectTreeNode::fetch($nodeID); if (!$node) { echo "Node with ID $nodeID not found.\n"; return []; } // Fetch only objects of 'playlist' class $params = [ 'ClassFilterType' => array( 'include' ), 'ClassFilterArray' => $classIdentifierArray, // Replace 'playlist' with your desired class identifier 'Limit' => $limit // Adjust the limit as needed ]; $result = eZContentObjectTreeNode::subTreeByNodeID($params, $nodeID); return $result; } } ?>
Here is an example of calling the cronjob script. We do this after the above is properly installed and configured.
cd /path/to/ezpublish/; clear; date;./runcronjobs.php youtube-feed-import; date
This solution works well by simulation of the RSS-Bridge Application which effortlessly (once you learn how to call it correctly) fetches Youtube / Instagram / Telegram / Other content for you to store and use within your own website powered by eZ Publish.
We are working to release a package of this software ready to install and customize to meet your own needs. Until then use this blog post as a project origin story / working example of a solution for content stored in web applications which do not provide feeds.
Hello eZ Community!
I write today to fulfil on the commitment made to describe the eZ Publish Symbolic Link Distribution I talked about previously in passing.
Here is how it works. No files are needed. The eZ Publish Symbolic Link Distribution is A Process to Turning an Empty sub domain virtual host folder ~/doc/ (main site) + ~/doc/admin.ezpublish.one (subdomain site) into a full fledged working eZ Publish Installation.
We do this by creating our base normal installation which gives us the eZ Files needed in ~/doc (main site).
If your like me and prefer full control via directory structure abstraction you'll quickly see that we need a way to use the main site files without var dir duplication (variable) so admin.ezpublish.one links to the same eZ Installation as the main site.
Here is how to do it:
cd /home/ezp/doc/admin.ezpublish.one; for filename in ../*; do ln -s $filename .; done
This takes care of the files an eZ request requires for operation and addresses the var directory variable with a dynamic link sharing the var dir for both virtual host eZ Installations.
It's really that simple of a process. The symbolic links are generated dynamically based on the installed files in the path of the command and is a quick solution in bash shell to do the heavy lifting.
Next and perhaps the most important on 7x cPanel Based Server Hosting is that fact that when using mod_rewrite in the main site ~/doc .htaccess file we require nothing special per say.
But with the admin.ezpublish.one subdomain we are using a subdomain virtualhost which requires the following added to the top of your .htaccess file to load the second virtualhost eZ Publish Installation Normally.
# Static Subdirectory Virtual Host Required Options RewriteEngine On RewriteOptions IgnoreInherit DirectoryIndex index.html index.php
These are just a few power querks to using these software platforms to quickly build multi-domain installations when changing the directory path in the virtual host is not an option.
Remember folks, there is always another way in all things eZ!
Announcing eZ Publish Basic: Revitalizing a Classic Open-Source CMS with Full PHP 8 Compatibility
We are thrilled to announce a new project to breathe new life into eZ Publish 2.x, the community-supported CMS that served users for nearly a decade. With PHP's evolution over the years, the original eZ Publish has faced compatibility issues. But now, 7x is leading an open-source initiative to bring eZ Publish 2 into 2025 with full PHP 8 support.
Introducing eZ Publish Basic (Version 2.4.0.0-preview1)
Launched on GitHub on November 1, 2024, "eZ Publish Basic" marks the first step in our mission to modernize this reliable content management system. Our goal is to retain its core functionality while updating the codebase to work seamlessly with PHP 8. By addressing major changes, such as replacing the outdated "register globals" setting, we aim to ensure that developers and community sites can continue to use and customize eZ Publish Basic with confidence.
The Power of Open Source and the Choice of the GNU GPL License
From the start, eZ Publish was built to be adaptable, transparent, and community-centered. In keeping with that mission, we’ve chosen to update eZ Publish Basic under the GNU GPLv2 (or later version). This choice is central to our commitment: the GPL not only allows everyone to access, use, and modify the software freely but also ensures that improvements remain open and available for all. The GNU license strengthens eZ Publish as a platform by safeguarding both the community’s and developers’ contributions, creating a CMS ecosystem where freedom, adaptability, and innovation flourish.
A Fresh Start for New Developers and PHP Enthusiasts
eZ Publish Basic is designed to be more than just a CMS upgrade—it’s an invitation for new end users or developers to experience the capabilities of a powerful, well-structured CMS that offers a breath of fresh air compared to writing vanilla PHP. This project provides a unique entry point for aspiring developers, where they can learn core CMS concepts that make eZ Publish worth using and adapting in 2025. By working with eZ Publish Basic, developers can gain foundational skills and insight into a robust CMS structure that emphasizes freedom, scalability, and practical solutions.
An Educational Tool for Students and New Developers
This project is intentionally educational, crafted with students and beginner developers in mind, even as it retains the power for production-grade tasks. eZ Publish Basic allows newer developers to learn the fundamentals of PHP and CMS design in an open-source, collaborative setting. While it’s accessible to those still learning PHP, it remains fully functional and ready for any production environment. This approach empowers emerging developers to build real-world skills while contributing to a larger, impactful project and experiencing the value of community-driven software.
Why Join the eZ Publish Basic Project?
For developers, historians of the open-source world, or anyone who used eZ Publish 2 or subsequent versions (eZ Publish 3, 4, 5, and the Netgen or Ibexa OSS adaptations), this project is a chance to support and preserve a piece of open-source history. The open-source community has always been a critical part of eZ Publish’s journey, and we are inviting contributors to help complete this PHP 8 migration and keep eZ Publish Basic as a useful, adaptable solution for years to come.
Project Details
- Repository: GitHub - eZ Publish Basic
- Current Version: 2.4.0.0.0
- Current preview release: First Preview Release Tag
- Planned Release for Full PHP 8 Compatibility: Q2 2025
- License: GNU GPLv2 (or later version)
Add comment