Create a Joomla! Article Programmatically - php

I have created my own component. When I add a new record to my component, I also want it to create a new article in joomla (i.e. using com_content).
I found this on stack overflow Programmatically adding an article to Joomla which explains how to do it. The code makes sense, and looks like it will work. The problem is that once methods start being called that are contained in com_content, all the relative URLs in com_content break down and joomla throws an error.
Does anyone know a way to overcome this? A comment from the link above suggests that changing the current working directory to the com_content one before including it will work, but I'm not 100% sure on how to do this.

It's not possible to change the working directory because its a constant. To work around this issue you could choose not to use ContentModelArticle at all and instead use the table class only:
$table = JTable::getInstance('Content', 'JTable', array());
$data = array(
'catid' => 1,
'title' => 'SOME TITLE',
'introtext' => 'SOME TEXT',
'fulltext' => 'SOME TEXT',
'state' => 1,
);
// Bind data
if (!$table->bind($data))
{
$this->setError($table->getError());
return false;
}
// Check the data.
if (!$table->check())
{
$this->setError($table->getError());
return false;
}
// Store the data.
if (!$table->store())
{
$this->setError($table->getError());
return false;
}
Note that the code above does not trigger the before/after save events. If that is needed however, it should not be a problem to trigger those events. Also worth noticing is that the field published_up will not be automatically set and the articles within the category will not be reordered.
To reorder the category:
$table->reorder('catid = '.(int) $table->catid.' AND state >= 0');

The error I get says:
File not found /var/www/administrator/com_mynewcomponent/helpers/content.php
I got around the problem by creating an empty file at this location to suppress the error message and manually including /var/www/administrator/com_content/helpers/content.php with a require_once statement.

Support Joomla 2.5 and Joomla 3.0
JTableContent is not autoloaded prior to Joomla! version 3.0, so it needs to included:
if (version_compare(JVERSION, '3.0', 'lt')) {
JTable::addIncludePath(JPATH_PLATFORM . 'joomla/database/table');
}
$article = JTable::getInstance('content');
$article->title = 'This is my super cool title!';
$article->alias = JFilterOutput::stringURLSafe('This is my super cool title!');
$article->introtext = '<p>This is my super cool article!</p>';
$article->catid = 9;
$article->created = JFactory::getDate()->toSQL();
$article->created_by_alias = 'Super User';
$article->state = 1;
$article->access = 1;
$article->metadata = '{"page_title":"","author":"","robots":""}';
$article->language = '*';
// Check to make sure our data is valid, raise notice if it's not.
if (!$article->check()) {
JError::raiseNotice(500, $article->getError());
return FALSE;
}
// Now store the article, raise notice if it doesn't get stored.
if (!$article->store(TRUE)) {
JError::raiseNotice(500, $article->getError());
return FALSE;
}

Related

creating a variable shortcode based on user input that returns (prechosen) variables Wordpress

I need to update values in all posts of a wordpress installation on a regular base. I was gooing to use shortcodes to insert the request into the wordpress post. Then use a custom functions.php that holds all the variables that need to be updated from time to time.
I got it working. Somehow but not the way I intended to use it. I'm a total beginner. Please consider this when answering my questions.
I want to have a function that reads what comes after honda_ and displays the correct value in wordpress without having to create a separate shortcode for each variable.
When entering [honda_link] wordpress should display the value from honda_link. When entering [honda_longlink] the value from honda_longlink variable should get displayed. I don't want to create a shortcode for each value.
I came up with this as a working solution...
// Honda
function honda() {
$honda_link = 'www.honda.com';
$honda_longlink = 'http://www.honda.com';
$honda_free = 'Free';
$honda_new = '23.688 $';
$honda_mileage = '00';
return $honda_link;
}
add_shortcode('neu', 'honda_link');
I tried some approaches by using an array but it ultimately failed all the time. I also tried it with if statements but wasn't able to get the right value displayed.
Someone willing to help a noob? I think I need to see a working example in order to understand it. The code snippets I have been looking at (that do something similiar but not the same I want to achieve) did confuse me more than they helped me.
I came up with this / Which works in a way but... This isn't very comfortable to use.
add_shortcode('HONDA','HONDA_TEST');
function HONDA_TEST($atts = array(), $content = null, $tag){
shortcode_atts(array(
'var1' => 'default var1',
'var2' => false,
'var3' => false,
'var4' => false
), $atts);
if ($atts['var2'])
return 'honda2';
else if ($atts['var3'])
return 'honda3';
else if ($atts['var4'])
return 'honda4';
else
return 'honda1';
}
So now when using:
[HONDA var1="novalue"][/HONDA]
[HONDA var2="novalue"][/HONDA]
[HONDA var3="novalue"][/HONDA]
[HONDA var4="novalue"][/HONDA]
it shows:
honda1
honda2
honda3
honda4
and so on.
Is there a better way to achieve the intended goal from post #1 ? Any way I could import the $variables from 1st post in bulk for example?
I don't have a working WP setup right now to test, but could you try this:
add_shortcode('HONDA','HONDA_TEST');
function HONDA_TEST($atts = array(), $content = null, $tag){
$atts = shortcode_atts(array(
'model' => 1,
), $atts);
$myHondas = [1 => 'honda1', 'honda2', 'honda3'];
return isset($myHondas[$atts['model']]) ? $myHondas[$atts['model']] : 'unknown model id';
}
And use it with [HONDA model="1"], [HONDA model="2"]

PHP file exist & Wordpress Shortcode

I'm trying to retrieve the number of parking lots from a .txt file, its working on the static site iframe but I want to make a shortcode and place it on wordpress theme function file.
For some reason it's not reading the data...
function GenerateLOT($atts = array()) {
// Default Parameters
extract(shortcode_atts(array(
'id' => 'id'
), $atts));
// Create Default Park / Help
if ($id == '') {
$id = 'PARK IDs: bahnhofgarage;';
}
// Create Park Bahnhofgarage
if ($id == 'bahnhofgarage') {
$completeBahnhof = "//xxx.de/bahnhof.txt";
if(file_exists($completeBahnhof )) {
$fp=file($completeBahnhof );
$Garage = $fp[0];
$valmpl=explode(" ",$Garage);
$Bahnhof_Platz = $valmpl[0];
$Bahnhof_Tendenz = $valmpl[1];
}
$id = $Bahnhof_Platz;
}
return $id;
}
add_shortcode('parking', 'GenerateLOT');
[parking id='bahnhofgarage']
PS: The .txt is working properly retrieving like this: 000 - //bahnhof 27.12.15 12:46:59
For some reason its only displaying the $park == '' text and not the parking lots according shortcode param.
I've used this tutorial: sitepoint.com/wordpress-shortcodes-tutorial/
EDIT: There are 6 parking lots.
EDIT2: Changed park to id on all instances
The problem is that you can't meaningfully use file_exists on remote path. See SO answer to "file_exists() returns false even if file exist (remote URL)" question for details.
You should probably just call file() on that path. It will return FALSE if it encounter an error.
if ($id == 'bahnhofgarage') {
$completeBahnhof = "//xxx.de/bahnhof.txt";
$fp=file($completeBahnhof );
if ($fp !== false) {
$Garage = $fp[0];
// rest of code
On a side note, shortcode_atts() is used to provide default values for shortcode attributes, while you seem to be using it as some sort of mapping between shortcode attributes and internal variable names.
Accessing file on remote server inside shortcode is asking for trouble. Just think what will happen if this server is overloaded, slow to respond or not available anymore. You should really access that file asynchronously. If it is located on your server, access it through file-system path.

Activate a Topic for a Page programmaticaly concrete5 5.7

The goal is to activate an existent topic in a Blog Entry page. Normally a user does this in the Pages Attributes section like so:
Now my goal is to do this programmaticaly. I won't post all my trials (since 2 days) here because it's just crap, but here's what I've done so far.
First I add a Blog Page to a chosen parent Page (ID 157):
use Concrete\Core\Page;
$parentPage = Page\Page::getByID(157);
$template = \PageTemplate::getByHandle('blog_entry');
$entry = $parentPage->add($type, array(
'cName' => 'My title',
'cDescription' => 'description',
'cHandle' => 'my_title',
'cvIsApproved' => true,
'cDatePublic' => $publishDate->format('Y-m-d H:i:s')
), $template);
As the newly created page is a blog_entry template the Blog Entry Topics is already assigned.
Then I create a Topic and add it to its Topic Tree (Blog Entry Topics) like so:
use \Concrete\Core\Tree\Type\Topic as TopicTree;
use \Concrete\Core\Tree\Node\Type\Topic as TopicTreeNode;
use \Concrete\Core\Tree\Node\Node as TreeNode;
$topicTree = TopicTree::getByName('Blog Entries');
$parentTopic = TreeNode::getByID($topicTree->getRootTreeNodeObject()->treeNodeID);
$item0 = TopicTreeNode::add('udland', $parentTopic);
How to activate/assign this Topic(Udland) to my page ($entry)? (As shown in the image)
I know it must be related to the DB-tables CollectionAttributeValues and atSelectedTopics. Also the Classes CollectionValue and CollectionKey must be involved.
I could add those entries manually in the DB but this isn't a good idea because I don't know what data is necessary to make this work correctly. The topics are used to filter Blog entries so I'm quite sure that there are other tables involved and as a Core developer said: "These are fragile little things" ;-).
As this version of concrete5 is a complete new launch, the developer docs aren't complete and after 2 days of digging inside the core code I'm just desperate.
Update (after a week of digging...)
I managed to do a hack taken out of a Controller method: (/concrete/controllers/panel/page/attributes.php -> submit()).
I know this isn't the way to go at all but it's my best trial so far:
(I just include the NameSpaces here to make clear what Classes I'm calling)
use Concrete\Core\Page;
use Concrete\Core\Page\Collection\Version\Version;
use Concrete\Core\Workflow\Request\ApprovePageRequest;
use CollectionAttributeKey;
use \Concrete\Core\Tree\Node\Type\Topic as TopicTreeNode;
Get the Attributes ID by handle:
$ak = CollectionAttributeKey::getByHandle('blog_entry_topics');
$attributekID = $ak->getAttributeKeyID();
get the topic
$item_one = TopicTreeNode::getNodeByName('Udland');
then simulate a posted form by:
$_POST = array(
'topics_' . $attributekID => array($item_one->treeNodeID)
);
I know this is so ugly and a big hack & not reliable at all but as said it's taken out of a Controller...
Then I do a slimmed version of the submit() method:
$c = Page\Page::getByID(157);
$published = new \DateTime();
$nvc = $c->getVersionToModify();
$nvcObj = $nvc->getVersionObject();
$data = array();
$data['cName'] = $nvcObj->cvName;
$data['cDescription'] = $nvcObj->cvDescription;
$data['cDatePublic'] = $published->format('Y-m-d H:i:s');
$data['uID'] = '1';
$nvc->update($data);
$setAttribs = $nvc->getSetCollectionAttributes();
$processedAttributes = array();
$selectedAKIDs = $attributekID;
if (!is_array($selectedAKIDs)) {
$selectedAKIDs = array();
}
$selected = is_array(array($attributekID)) ? array($attributekID) : array();
foreach ($setAttribs as $ak) {
if (in_array($ak->getAttributeKeyID(), $selected)) {
$ak->saveAttributeForm($nvc);
} else {
$nvc->clearAttribute($ak);
}
$processedAttributes[] = $ak->getAttributeKeyID();
}
$newAttributes = array_diff($selectedAKIDs, $processedAttributes);
foreach ($newAttributes as $akID) {
$ak = CollectionAttributeKey::getByID($akID);
$ak->saveAttributeForm($nvc);
}
So as said before this is really ugly but it's the best trial so far and somehow it works.
Then approve the Request by doing:
$pkr = new ApprovePageRequest();
$u = new User();
$pkr->setRequestedPage($c);
$v = Version::get($c, "RECENT");
$pkr->setRequestedVersionID($v->getVersionID());
$pkr->setRequesterUserID($u->getUserID());
$pkr->trigger();
$u->unloadCollectionEdit();
But what really makes me wonder is that method inside of /concrete/src/Attribute/Key/Key.php where finally the thing should happen (in my humble opinion):
/**
* Calls the functions necessary to save this attribute to the database. If no passed value is passed, then we save it via the stock form.
* NOTE: this code is screwy because all code ever written that EXTENDS this code creates an attribute value object and passes it in, like
* this code implies. But if you call this code directly it passes the object that you're messing with (Page, User, etc...) in as the $attributeValue
* object, which is obviously not right. So we're going to do a little procedural if/then checks in this to ensure we're passing the right
* stuff
*
* #param CollectionValue|mixed $mixed
* #param mixed $passedValue
*/
protected function saveAttribute($mixed, $passedValue = false)
{
/** #var \Concrete\Core\Attribute\Type $at */
$at = $this->getAttributeType();
$at->getController()->setAttributeKey($this);
if ($mixed instanceof AttributeValue) {
$attributeValue = $mixed;
} else {
// $mixed is ACTUALLY the object that we're setting the attribute against
//todo: figure out what $nvc should really be since it doesn't exist in this scope
$attributeValue = $nvc->getAttributeValueObject($mixed, true);
}
$at->getController()->setAttributeValue($attributeValue);
if ($passedValue) {
$at->getController()->saveValue($passedValue);
} else {
$at->getController()->saveForm($at->getController()->post());
}
$at->__destruct();
unset($at);
}
So I'm really curios to see what the reliable and system-suitable way is to resolve this.
Here's what I came up with that does work. You were pretty close.
use \Concrete\Core\Tree\Type\Topic as TopicTree;
use \Concrete\Core\Tree\Node\Type\Topic as TopicTreeNode;
use \Concrete\Core\Tree\Node\Node as TreeNode;
$parentPage = \Page::getbyPath('/blog');
$template = \PageTemplate::getByHandle('blog_entry');
$entry = $parentPage->add($type, array(
'cName' => 'ooops',
'cDescription' => 'hmmmm',
'cHandle' => 'yay',
'cvIsApproved' => true,
'cDatePublic' => '2015-12-21 00:00:00'
), $template);
$item0 = TopicTreeNode::getNodeByName('udland');
if (!$item0) {
$topicTree = TopicTree::getByName('Blog Entries');
$parentTopic = TreeNode::getByID($topicTree->getRootTreeNodeObject()->treeNodeID);
$item0 = TopicTreeNode::add('udland', $parentTopic);
}
$entry->setAttribute('blog_entry_topics', array($item0->getTreeNodeDisplayPath()));
It looks like the attribute takes in an array of node display paths and that is how it sets the selection. Additionally, you have to use the \Page alias, and not the fully qualified namespace as you were doing, otherwise you get an error about it being unable to clear the cache.

Trying to highlight current menu item in Drupal 8 using hook_preprocess_menu

I have the below function to create active trail functionality. So if I were to have /blog as a "parent" and a post of /blog/mypost, when on mypost the blog link would show as highlighted. I don't want to have to make menu items for all the blog posts. The problem is when caching is turned on (not using settings.local.php and debug turned off) the getRequestUri isn't changing on some pages. It seems to be cached depending on the page. It works fine with page caching turned off but I'd like to get this working with caching. Is there a better way to check for the current path and apply the active class?
function mytheme_preprocess_menu(&$variables, $hook) {
if($variables['theme_hook_original'] == 'menu__main'){
$node = \Drupal::routeMatch()->getParameter('node');
if($node){
$current_path = \Drupal::request()->getRequestUri();
$items = $variables['items'];
foreach ($items as $key => $item) {
// If current path starts with a part of another path i.e. a parent, set active to li.
if (0 === strpos($current_path, $item['url']->toString())) {
// Add active link.
$variables['items'][$key]['attributes']['class'] .= ' menu-item--active-trail';
}
}
}
}
}
I've also tried putting this into a module to try and see if I can get the current path to then do the twig logic in the menu--main.twig.html template but I have the same problem.
function highlight_menu_sections_template_preprocess_default_variables_alter(&$variables) {
$variables['current_path'] = $_SERVER['REQUEST_URI'];
}
After a very long time trying all sorts of things, I found an excellent module which addresses exactly this problem. Install and go, not configuration, it just works:
https://www.drupal.org/project/menu_trail_by_path
Stable versions for D7 and D8.
I tried declaring an active path as part of a custom menu block, and even then my declared trail gets cached. Assuming it's related to the "There is no way to set the active link - override the service if you need more control." statement in this changelog, though why MenuTreeParameters->setActiveTrail() exists is anybody's guess.
For the curious (and for me when I search for this later!), here's my block's build() function:
public function build() {
$menu_tree = \Drupal::menuTree();
$parameters = new MenuTreeParameters();
$parameters->setRoot('menu_link_content:700c69e6-785b-4db7-be49-73188b47b5a3')->setMinDepth(1)->setMaxDepth(1)->onlyEnabledLinks();
// An array of routes and menu_link_content ids to set as active
$define_active_mlid = array(
'view.press_releases.page_1' => 385
);
$route_name = \Drupal::request()->get(RouteObjectInterface::ROUTE_NAME);
if (array_key_exists($route_name, $define_active_mlid)) {
$menu_link = \Drupal::entityTypeManager()->getStorage('menu_link_content')->loadByProperties(array('id' => $define_active_mlid[$route_name]));
$link = array_shift($menu_link);
$parameters->setActiveTrail(array('menu_link_content:' . $link->uuid()));
}
$footer_tree = $menu_tree->load('footer', $parameters);
$manipulators = array(
array('callable' => 'menu.default_tree_manipulators:checkAccess'),
array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
);
$tree = $menu_tree->transform($footer_tree, $manipulators);
$menu = $menu_tree->build($tree);
return array(
'menu' => $menu,
);
}
[adding a new answer since this is a completely different approach than my earlier one]
If a CSS-based solution is acceptable, this seems to work okay:
.page-node-type-press-release {
a[data-drupal-link-system-path="press-room/press-releases"] {
// active CSS styles here
}
}

Joomla render full article by id with "JModelLegacy getInstance article"

I'm stuck trying to get joomla full article to render in a tab. The tab is working. I just canĀ“t render the article content. This is where I am now.
This is helper.php
public static function getArticle($articleId)
{
JModelLegacy::addIncludePath(JPATH_SITE.'/components/com_content/models', 'ContentModel');
$model = JModelLegacy::getInstance('Article', 'ContentModel', array('ignore_request' => true));
$article = $model->getItem((int) $articleId);
$fullarticle = $item->fulltext;
$itemsHtml = '<div>'. $fullarticle .'</div>';
return $itemsHtml;
}
And this is in default.php
...code...
else if ($list_of_tabs['use'][$i][0] == 'article'){
echo '<div class="tab-pane '.$active.'" id="'.$i.$rid.'">'.
modJpTabsHelper::getArticle($list_of_tabs['article'][$i], $params) .
'</div>';
}
...code...
If you need more info. Don't hesitate to ask.
What are you trying to achieve: to write your own Joomla! extension which displays articles in a tab or you just need to display your J! articles in a tab?
If it's a latter, then there are already some nice and free (as in a "free bear") add-ons written just for that.
You are trying to use the model part of an MVC as a thing to render.
You should use the MVC system - using a controller to gathering the model and the view, and then you can render the model with the attached view, via the controller.
So you use something like (I've not tested this - you will need to correct it).
$filter=array('id' => $i->query['id']);
$options=array('filter_fields' => $filter,'ignore_request' => true);
$ctl = new ContentModelController();
$view = $ctl->getView( 'Article');
$model = $ctl->getModel( 'Article','',$options);
you may need to set params from application, eg..
$model->setState('params', JApplication::getInstance('site')->getParams());
then continue
$view->setModel( $model, true );
$result = $view->display();
Make sure that you have JLoader::import'ed any classes/classpaths - j. tends to fail silently if they aren't found, which can be difficult to trace.
Sorry, it's only a partial solution - but hopefully it may put you on the right track.
Here was the problem:
$fullarticle = $item->fulltext;
Article object from model was in variable $article not $item:
$article = $model->getItem((int) $articleId);
So getting property fulltext from article object should be:
$fullarticle = $article->fulltext;

Categories