Wordpress: programmatically create sub menu for frontend - mysql

I'd like to write a wordpress-plugin, where you can add a page.
If you submit the page it should also create a submenu under the menu "Teams".
Until now I can create a page through my code, but not the submenu.
I tried different functions I found on google, but nothing will work.
Does anyone know a function or a script that will help?

Yes sure, use the following as a sample to get you going. The clause to check if you are in the right menu may need altering or deleting if you don't have multiple menu objects defined.
menu_item_parent is vital and that is the parent item uid. find that by viewing your front end source code. You should find that each menu item inserted via WP menu creating functions inserts the unique items id.
// add a sub menu dynamically via code!
function aj_add_menu_item( $items, $args ) {
// check we are in the right menu
if( $args -> theme_location =="primary" ) {
$new_links = array();
// Create a nav_menu_item object
$newItem = array(
'title' => "Offers",
'menu_item_parent' => 71,
'ID' => 'loginout',
'db_id' => '12312332', // something random
'url' => "offers",
'classes' => array( 'menu-item' )
);
$items[] = (object) $newItem; // add to end of existing object.
menu_item_parent value will ensure it goes in right place
return $items;
}else{
return $items;
}
}
add_filter( 'wp_nav_menu_objects', 'aj_add_menu_item', 10, 2 );

Related

WordPress: Calling a shortcode for a value in a different shortcode

I'm very new to this, so please go easy on me if this is a dumb question.
I am building a site with events manager and geo my WordPress plugins. I want a user to be able to input their autofill location (via GMW) and have the calendar in EM output only events a certain distance from that location. I have already gotten (with handholding) to a point where I have a shortcode that spits out the coordinates of the location entered. The EM full calendar shortcode takes an attribute called 'near' which takes coordinates and subsequently outputs the desired calendar.
Code at the moment:
[fullcalendar near=[gmw_current_latlng] near_distance=20]
with [gmw_current_latlng] normally returning the lat and long separated by a comma. Normally, the near att takes say 50.111123,-45.234, etc.
My problem is that it seems that I cannot get what I want with this hardheaded approach. Again, I'm very new to coding and don't know much, but I've been working on this problem for weeks and have not found an answer. I've tried many different routes, but this way has brought me oh so close to where I want to be.
The GMW dev said this about the problem:
"The thing is that I am not even sure if you can pass a value to a
shortcode using another shortcode. I’ve never tried this myself. The
best way would be to use filters and a custom function to “inject” the
coords directly into the calendar function."
If he is right and it's not possible, I have no idea how to carry out his second suggestion. Hopefully, I can get this sorted out because frankly, my site depends on it working.
As #Jeppe mentioned, you can do Nested Shortcodes:
[shortcode_1][shortcode_2][/shortcode_1]
But the parser does not like shortcode values as attributes in other shortcodes.
It sounds like you're reliant on a few plugins and their shortcodes, so I don't suggest editing those shortcodes - but if you look at the Shortcode API it's pretty easy to add you own. For simplicity's sake, this example won't contain the "proper" methods of making sure the shortcodes exist/plugins are installed etc, and will just assume they are.
// Register a new shortcode called [calendar_with_latlng]
add_shortcode( 'calendar_with_latlng', 'calendar_with_latlng_function' );
// Create the function that handles that shortcode
function calendar_with_latlng_function( $atts ){
// Handle default values and extract them to variables
extract( shortcode_atts( array(
'near_distance' => 20
), $atts, 'calendar_with_latlng' ) );
// Get the current latlng from the gmw shortcode
$gmw_current_latlng = do_shortcode( '[gmw_current_latlng]' );
// Drop that value into the fullcalendar shortcode, and add the default (or passed) distance as well.
return do_shortcode( '[fullcalendar near='. $gmw_current_latlng .' near_distance='. $near_distance .']' );
}
Provided [gmw_current_latlng] returns a useable format for your [fullcalendar] shortcode, you should now be able to use your new shortcode that combines the two: [calendar_with_latlng] or you can also add the near_distance attribute: [calendar_with_latlng near_distance=44].
You would just need to put the above functions into your functions.php, create a Simple Plugin, or save them to a file and add it in your Must-Use Plugins directory.
Of course you can pass a shortcode as an attribute of another shortcode. The only problem is, attributes doesn't pass [ or ]. So you have replace convert those bracket them with their html entry.
Replace [ with [ and ] with ] and you should be fine. Here is an example.
function foo_shortcode( $atts ) {
$a = shortcode_atts( array(
'foo' => "Something",
'bar' => '',
), $atts );
$barContent = html_entity_decode( $atts['bar'] );
$barShortCodeOutput = do_shortcode($barContent);
return sprintf("Foo = %s and bar = %s", $a['foo'], $barShortCodeOutput);
}
add_shortcode( 'foo', 'foo_shortcode' );
function bar_shortcode( $atts ) {
return "Output from bar shortcode";
}
add_shortcode( 'bar', 'bar_shortcode' );
Then put this on your editor
[foo bar=[bar] ]
See we are passing a shortcode [bar] as an attribute of [foo]. So the output should be -
Foo = Something and bar = Output from bar shortcode
I know it looks a bit nasty but it will do the trick.

How can we add a custom dropdown in tiny mce using yii2

I want to add a custom drop down in tiny mce editor, I am using yii framework and using a yii plugin to integrate the editor
You haven't added any details in your question but since you are a new
bee here and SO Code of Conduct
has been revised to be more nice and humble towards newcomers, so I am
adding the answer for you, do go through the How to Ask a
Question? before posting a
question next time.
You can add the dropdown in the TinyMCE using setup option which takes a callback function with a parameter editor which holds the editor instance, and then you need to call the editor.addButton(label, options) with the options to create the custom dropdown button.
As you have not added any details in the question like what are the options that you are going to display in the dropdown so, I will assume here as usernames from the database in the variable $users.
Steps to Implement
First, we will convert $users array to js array by using yii\helpers\Json::encode().
Iterate that array to create the drop-down options with onclick event to insert the content to the editor.
Use editor.addButton('users',options) to create a button of type dropdown with label users to be later used when initializing the editor toolbar buttons.
Add the following code on top of the view
$usersList = \yii\helpers\Json::encode($users);
$tinyMCECallback = <<< JS
function (editor) {
let usersList = $usersList;
let options = [];
//iterate the user array and create the options with text and
//onclick event to insert the content on click to the editor
$.each(usersList, function(label, mapping) {
options.push({
text: label,
onclick: function() { tinymce.activeEditor.insertContent(label); }
});
});
//add the dropdown button to the editor
editor.addButton('users', {
type: 'menubutton',
text: 'Users',
icon: false,
menu: options
});
}
JS;
Now all you need to do is to pass the $tinyMCECallback to the setup option of the tinyMCE widget, if you are using the active form you code should be like below.
Note: Don't forget to add the users label of the button to the toolbar options, or if you change it in the javascript code change it accordingly in the editor toolbar options otherwise it won't show up
<?php
echo $form->field(
$model, 'body'
)->widget(
TinyMce::class, [
'options' => ['rows' => 10],
'language' => 'en',
'clientOptions' => [
'menubar' => false,
'statusbar' => false,
'toolbar' => "undo redo | users",
'setup' => new \yii\web\JsExpression($tinyMCECallback),
],
]
);
?>

Laravel endless scrolling

I want to build a endless-scrolling functionality in my Laravel app. When a user visits my Page I load the first 10 Items with PHP.
MyModal::skip(0)->take(10)->get()
For sure I could get the next Items with
MyModal::skip(10)->take(10)->get()
But the problem is, when there are new entrys added while I scroll to the bottom. How can I tell Laravel "give me the next 10 items starting from the item with the ID X"
I hope you understand my question.
You could use an approach like this. You probably have a route that is inserted at the end of the view as an anchor link and calls the next 10 results, using AJAX.
So, the route should be something like this:
Route::get('/mymodal/{skip?}', 'MyModalController#nextItems');
This would call the Controller with the possibility to skip some items. And then the next skip value would be sent to the view with the results.
public function nextItems(Request $request, $skip = 0) {
$results = MyModal::skip($skip)->take(10)->get();
return view('mymodals.theview', [
'results' => $results,
'skip' => $skip + 10
]);
}
And then, in the view, you add the skip value to the anchor:
#foreach ($results as $r)
(...)
#endforeach
Next page...
Hope you get the idea. I've used the same idea in a project, but with the timestamp as parameter, and it works flawlessly.

Kendo Grid Fluent API DataBound event for a Child Grid to show No Items text?

It feels like i've lost a lot of time looking for this and still haven't found anything that works (well, that works properly). I have a set of nested grids for a user requirement, each one drills down into the next one etc and all that is working fine. It's handled using client templates which do an ajax call when they get expanded, and then displays the data.
The problem I have is that if there are no results for one of the expansions, Kendo just shows the child grid header and nothing else. When I connect to the DataBound event (on the grid, .Events(e => e.DataBound("myJavaScriptFunctionName")) the this is not the Kendo Grid, and if I pass the name of the child kendo grid (which is unique by using the key in #=#) it gives me 0 items in my data source.
I'm not sure if this is because it does an ajax post back, and then OnDataBound fires before it comes back?
I just need to show a "No Items Found" message to make the user experience better when there is no data (this really only happens at the lowest level)
Enough with words, here is some example code:
<script id="SecondToLastTemplate" type="text/kendo-tmpl">
#(Html.Kendo().Grid<CustomerViewModel>()
.Name("SumGrid_#=ResultYear#_#=ResultQuarter#_#=ResultMonth#_#=ResultWeekStart#_#=ResultDate#_#=Region#")
.Columns(column =>
{
column.Bound(x => x.CustomerName).Width("23%");
column.Bound(x => x.CustomerSummaryItem1).Width("14%");
column.Bound(x => x.CustomerSummaryItem2).Width("14%");
column.Bound(x => x.CustomerSummaryItem3).Width("14%");
})
.DataSource(dataBinding => dataBinding
.Ajax()
.PageSize(500)
.Read(read => read.Action("GetCustomerSummaryItems", Constants.Controller_ReportController, new
{
ResultYear = "#=ResultYear#"
,ResultQuarter = "#=ResultQuarter#"
,ResultMonth = "#=ResultMonth#"
,ResultWeekStart = "#=ResultWeekStart#"
,ResultDate = "#=ResultDate#"
,Region = "#=Region#"
}))
)
.Scrollable(scrolling => scrolling.Enabled(false))
.Sortable()
.Filterable(filtering => filtering.Enabled(true))
.ClientDetailTemplateId("LastTemplate")
.Pageable(paging => paging.Enabled(false))
.ToClientTemplate()
)
</script>
<script id="LastTemplate" type="text/kendo-tmpl">
#(Html.Kendo().Grid<CustomerDetailsViewModel>()
.Name("SumGrid_#=ResultYear#_#=ResultQuarter#_#=ResultMonth#_#=ResultWeekStart#_#=ResultDate#_#=Region#_#=CustomerName#")
.Columns(column =>
{
column.Bound(x => x.CustomerDetails1).Width("23%");
column.Bound(x => x.CustomerDetails2).Width("20%");
column.Bound(x => x.CustomerDetails3).Width("35%");
column.Bound(x => x.CustomerDetails4).Width("14%");
})
.DataSource(dataBinding => dataBinding
.Ajax()
.PageSize(500)
.Read(read => read.Action("GetCustomerDetails", Constants.Controller_ReportController, new
{
ResultYear = "#=ResultYear#"
,ResultQuarter = "#=ResultQuarter#"
,ResultMonth = "#=ResultMonth#"
,ResultWeekStart = "#=ResultWeekStart#"
,ResultDate = "#=ResultDate#"
,Region = "#=Region#"
,CustomerName = "#=CustomerName#"
}))
)
.Events(e => e.DataBound("onDataBound"))
.Scrollable(scrolling => scrolling.Enabled(false))
.Sortable()
.Filterable(filtering => filtering.Enabled(true))
.Pageable(paging => paging.Enabled(false))
.ToClientTemplate()
)
</script>
OnDataBound i've tried a few things, including the answer from this thread (Display a message within the Kendo grid when it's empty) with no luck. That one specifically always tells me I have 0 items in my data source (originally it was undefined, then I passed the grid name and still no luck).
Does anyone have a nice way to just say "No Items to display" while using the Fluent API with nested grids? I feel like i'm missing something simple here.
Thank you!
I figured these out:
Since I was using an ajax post back, the grid items weren't always available when the DataBound event was being called for some reason (it feels like they should be, since it's DataBound, but it wasn't)
I wrapped my no-results query in a setTimeout of 500ms so as to catch it, anything less and I would go back to the original error. I also modified the .find(... call to remove k-grid-header since that class wasn't outputting on my grid, and the colgroup was directly under the k-grid on the table.
function DisplayNoResultsFound(e) {
var self = e;
setTimeout(function (item) {
var grid = self;
var dataSource = grid.data('kendoGrid').dataSource;
var colCount = grid.find('colgroup > col').length;
var noResultsMessage = '#Resources.Global.NoResultsFound';
// If there are no results place an indicator row
if (dataSource._view.length == 0) {
grid.find('tbody')
.append('<tr class="kendo-data-row"><td colspan="' + colCount + '" style="text-align:center"><b>' + noResultsMessage + '</b></td></tr>');
}
}, 500); //Need to delay for ajax postback
}
This code is called by passing the jQuery grid item from the Databound Event:
.Events(e => e.DataBound(DisplayNoResultsFound($('\\#SumGrid_#=ResultYear#_#=ResultQuarter#_#=ResultMonth#_#=ResultWeekStart#_#=ResultDate#_#=Region#_#=CustomerName#'))")
Hope this helps someone else in the future!

MediaWiki Extension question / suggestion

Complete beginner here. I want to create a new tab on each page that has a custom action. When clicked, it takes you to a new page which has custom HTML on it along with the text or the original article.
So far I could create a new Tab and could give a custom action mycustomaction to it. I am pasting what I did so far here. Please let me know if I am using the correct hooks etc. and what is a better way to achieve this basic functionality.
So far with their docs I have done this:
#Hook for Tab
$wgHooks['SkinTemplateContentActions'][] = 'myTab';
#Callback
function myTab( $content_actions) {
global $wgTitle;
$content_actions['0'] = array(
'text' => 'my custom label',
'href' => $wgTitle->getFullURL( 'action=mycustomaction' ),
);
return true;
}
#new action hook
$wgHooks['UnknownAction'][] = 'mycustomaction';
#callback
function mycustomaction($action, $article) {
echo $action;
return true;
}
This gives me error:
No such action
The action specified by the URL is invalid. You might have mistyped the URL, or followed an incorrect link. This might also indicate a bug in the software used by yourplugin
What I was doing wrong:
$content_actions[‘0’] should simply be $content_actions[] (minor nitpick)
$content_actions is passed-by-reference, it should be function myTab( &$content_actions ) {}
mycustomaction() should do something along the lines of
if ( $action == ‘mycustomaction’ ) {
do stuff; return false;
}
else {
return true;
}
It should use $wgOut->addHTML() instead of echo
Thanks a lot everyone for your help!