Call a hook inside another hook function in MediaWiki - mediawiki

Hello guys I'm new to MediaWiki and trying to build my own extension. Using this extension I'm trying to show some content blow page heading but only to a page specific to a category.
For that, I'm using two hooks:
onArticleViewHeader ( To add my HTML content below the page heading)
onOutputPageMakeCategoryLinks (To get all the category of page being loaded)
From the first hook, I'm able to show my content using the following code:
public static function onArticleViewHeader( &$article, &$outputDone, &$pcache ) {
$article->getContext()->getOutput()->addHTML("Printed from a hook");
}
The above code prints the HTML below every page heading but I want to load HTML only to a specific page category. So for that, I'm trying to load the category and I'm just trying to call my first hook only if the category gets caught.
public static function onOutputPageMakeCategoryLinks( &$out, $categories, &$links ) {
foreach($categories as $category){
if($category=="my_page_category"){
MyExtentionClass::onArticleViewHeader();
}
}
}
I know I'm calling the hook in a bad manner which is not correct. But I just wanted to call my 1st hook 'onArticleViewHeader' from inside of my 2nd hook so that I can print my HTML only to a page with a specific category.

Just use $article->getPage()->getCategories() in the header hook.

Haven't really got the exact solution of the question I asked but has got the way out to solve the problem I have been facing.
I just tried getting the current categories in the "onArticleViewHeader" itself by using some of MediaWiki's global variables.
global $wgOut;
$title = Title::newFromText( $wgOut->getPageTitle() );
$categories = $title->getParentCategories();
if(isset($categories['Category:my_cat_name']){
//formed my logic here
}
This might help some other people facing this kind of issue.

Related

Using API to fetch data from one site to display on another

I have a site on Wordpress where I am trying to find the best way to create a dropdown which displays data from a custom taxonomy to eventually integrate it into a different site (also on Wordpress).
Where I have go to is trying to obtain the correct Routes/URL's to fetch this information.
I have a Post type called listings which has a taxonomy called listing_area which has different areas where posts are associated, e.g. Wales, East Anglia.
I have got so far that I have decided to use the Plugin WP-API (whether this is the right thing I don't know, I am aware that Wordpress now had an API in it's new update). I have managed to get this URL working and pulling in the terms of listing_area - http://scd.blaze.wpengine.com/wp-json/taxonomies/listing_area/terms/174
This is the test page I have going which is linking to these URL's in turn -
http://scd.blaze.wpengine.com/test/
I have no idea if I'm doing the right thing here and I have very basic knowledge on it and would hugely appreciate it if someone could point me in the right direction!
Thanks
You're going on the right path, but I suggest to work straight with the fresh Wordpress REST API if you can upgrade your websites to 4.4. Otherwise you can still use your REST plugin as it is pretty much the same. I'll try to explain how to go through what you want to achieve (navigate through terms of a distant Wordpress website and display posts related to this terms).
Get the terms from the other WP
Using the new WP REST API, here is a small function that you can use to get your taxonomy terms:
public function getDistantTerms($taxonomy) {
$response = wp_remote_get('http://www.yourwebsite.com/wp-json/wp/v2/terms/' . $taxonomy);
if(is_wp_error($response)) {
return array();
}
return json_decode(wp_remote_retrieve_body($response));
}
Here I make use of wp_remote_get function to get the JSON return from the REST function terms by passing it as parameter the taxonomy slug (ex:listing_area) - here is a demo of what it returns. Add this function to your functions.php then use it in your template to build up your select:
<select name="term_select">
<option value="">Please choose a term</option>
<?php foreach(getDistantTerms('listing_area') as $term): ?>
<option value="<?php echo $term->slug; ?>"><?php echo $term->name; ?></option>
<?php endforeach; ?>
</select>
It seems that's pretty much what you actually got.
Link your select to a custom template
So the next step is to redirect to a page that list the posts of the term you choose. First we handle the redirection in JS:
$('select[name="term_select"]').change(function() {
if($(this).val() != "") {
window.location = "/show-post-term/" + $(this).val();
}
});
We add a little rewrite rule to redirect this url (change it to whatever you want) to a template we'll name distant-posts.php (all of this take place in your theme functions.php):
1. Add the rewrite rule
add_action('init', 'distantposts_rewrite_rules');
function distantposts_rewrite_rules() {
add_rewrite_rule('show-post-term/([^/]+)/?$', 'index.php?term_slug=$matches[1]&distant_post=true', 'top');
}
2. Add two query vars
add_filter('query_vars', 'distantposts_query_vars' );
function distantposts_query_vars($vars) {
$vars[] = 'term_slug';
$vars[] = 'distant_post';
return $vars;
}
3. Redirect to the template if query vars are set
add_filter('template_include', 'yourpluginname_blah_template_include', 1, 1);
function yourpluginname_blah_template_include($template) {
global $wp_query;
$distant_post = $wp_query->query_vars['distant_post'];
$term_slug = $wp_query->query_vars['term_slug'];
if($distant_post && $term_slug) {
$tpl = locate_template(array('distant-posts.php'));
if(!empty($tpl)) {
return $tpl;
}
}
return $template;
}
So in short what we're doing here: we add a rule that handle the /show-post-term/term-slug URL by redirecting it to index with two query vars: one that tell we're in a "distant posts" mode and one that carry the term slug. Then we declare those query vars to Wordpress, and use them to change the template that Worpdress should display if they're set.
List the distants posts from the taxonomy term
Back to the REST API. We use the GET REST function posts by passing it as GET parameters the taxonomy name as key, and the term slug as value. Here is a demo of what kind of return you get.
An important note before going further: after you updated to WP 4.4, you need to change your taxonomy declaration in order to make this work. You need to add to your declaration the parameter show_in_rest set to true, and set query_var to true.
So we add this little function to functions.php to retrieve the posts from the template:
public function getDistantPosts($taxonomy, $term) {
$response = wp_remote_get('http://www.yourwebsite.com/wp-json/wp/v2/posts?' . $taxonomy . '=' . $term);
if(is_wp_error($response)) {
return array();
}
return json_decode(wp_remote_retrieve_body($response));
}
And then in your template, you call it this way:
global $wp_query;
$posts = getDistantPosts('listing_area', $wp_query->query_vars['term_slug']);
Then use the $posts array to display your posts (it contain regular post objects).
Going further
A few things that you may want to do now that you have the context established:
Add cache to the REST return
In order to avoid to overload your main website server, you should really consider caching your REST calls results. I will not detail this here as there is some work to do on it, but a good start could be this script.
Add pagination to your distant posts template
If you have a lot of posts associated to your terms, you might want to add a pagination. You can change a bit the distant posts REST function to add the page parameter for this - see the documentation.
Add a "single page" for your distant posts
You might want to have individual pages for your distant posts on your main website, as the text might be too long for the list mode. You can start on the distant-posts.php code and add a post_id query var, then use the REST posts function to get your post like this : /wp-json/wp/v2/posts/<post_id>
To understand the basics of the WP REST API I strongly suggest you to visit the wp-api.org website. There is a pretty good article on the REST API on wpmudev.org that you can read too. If you need to learn about the REST basics, I suggest you to read the Wikipedia post about it.
Hope you'll manage to get through this, have fun!
I found this this url got me the results I needed -
http://scd.blaze.wpengine.com/wp-json/posts?type=listings&filter[listing_area]=channel
my post type being listings and the slug of my term channel

how can i remove a index page from other pages in angularjs

I have a template html page(say Index page) containing a header and three other pages and i want that Header on first two pages but not on third page .Using angularjs routing I am able to have that header on all three pages but cant hide that header from the third page.The pages have different controllers as well .Can anybody help me how to achieve this.
This is not a good practice, not at all! But as your question lacks of code...
You say "The pages have different controllers", so let's say you have PageOneCtrl, PageTwoCtrl and PageThreeCtrl.
If you want to show the header on the page with controllers, let's say: PageOneCtrl and PageTwoCtrl, set a $scope (remember you have to define $scope on that controller first) variable just like:
$scope.showHeader = true;
And in PageThreeCtrl (where you want to HIDE the header element) write
$scope.showHeader = false;
Then in the html you should write:
<header ng-if="showHeader">This is your header content</header>
the ng-if will do the trick, check angularjs documentation for more information: https://docs.angularjs.org/api/ng/directive/ngIf
Doesn't work? Try $rootScope instead of $scope, but watch out! If you use $rootScope then you should declare that variable on every controller.
This is not a good practice, not at all! But as your question lacks of code...
A better practice, and one of the bests in my opinion, would be to use angular-ui-router and set a data attribute to the state (route) with something like
.state('myRoute', {
templateUrl: 'views/my-route-view.html',
controller: 'MyrouteCtrl',
data: {
hideHeader: true;
}
})
, in a .run() function set something like $rootScope.$state = $state (read more about it in the ui.router docs) and then simply: <header ng-if="!$state.current.data.hideHeader">. But I believe you're not an advanced developer to do it :) So keep learning.

How to edit category page layout?

How do I edit the layout for a category page, such as the they use on: http://gwpvx.gamepedia.com/Category:Meta_working_PvP_builds
(Image: http://i.gyazo.com/04337c415c7e67d766003bf02a598d1a.png)
I would like to add html code at the exact same where they have the ad but on my own wiki. This code should only be visible in categories.
There is no simple, built in way to alter the category pages. Adding the ad with Javascript from MediaWiki:Common.js would probably be easiest for you.
That said, if you really need to alter the HTML code of the page, the hook CategoryPageView is what you are looking for.
Something like this:
$wgHooks['CategoryPageView'][] = 'insertAdInCategoryPage';
public static function insertAdInCategoryPage( &$categoryArticle ) {
global $wgOut;
$wgOut->addHTML(/*some HTML*/);
return true;
}
$categoryArticle will be a article object.

Orchard - Add an additional shape name (i.e. an alternate) for the main List shape

Introduce the Problem
I would like to profoundly modify the layout of the Orchard CMS Tags list.
Here is an example page with Shape Tracing enabled.
The only alternate that it suggests for the List shape is ~/Themes/TheThemeMachine/Views/List.cshtml, because the page is rendering the default List shape. I would like to have other alternates that are specific to the page.
After reading Orchard list customization, I have been able to implement the default List.cshtml in razor. What I would like to do, though, is to add another alternate, such as ~/Themes/TheThemeMachine/Views/Parts.Tags.List.cshtml instead of implementing the default List.cshtml template.
The problem seems to be that the page is rendering the generic List shape.
In contrast, the blog post list page is rendering a Parts_Blogs_BlogPost_List shape, which means that a ~/Themes/TheThemeMachine/Views/Parts.Blogs.BlogPost.List.cshtml is available.
Search and Research
All quotes below are from the Orchard list customization blog post, which explains how to add a list item alternate (whereas I would like to add a list alternate).
What we really want is an alternate template... aptly called Shape
Alternates... [so] enable Shape Tracing... and select a post in the list...
[you will see that] we already have some possible alternates.
My example page also has some possible alternates for the List Content. Cool.
we need to somehow get into list rendering... [t]he default is defined
in code... [which] can be override by a new [cshtml] template in our
theme.
Okay. That makes sense. We can override the list rendering.
As Shape Tracing can show, we can override the list rendering for a
blog by creating a Parts.Blog.BlogPost.List.cshtml template.
This works for alog but not for the blog Tag page (example page). You see, the blog displays a **Parts_Blogs_BlogPost_List shape and suggests an appropriate alternate but the blog tags page displays the default List shape with no alternates other than List.cshtml.
Blog Page with alternates galore
Blog Tags Page with one alternate List.cshtml
So, I created a List.cshtml not a Parts.Blog.BlogPost.List.cshtml template, and save it in my theme's Views directory. (One problem here is that, once we get it working, we will b overriding the default List rendering.)
Then I add the Razor code (copy and pasted from Bertrand's post) to override the default rendering for Lists. When I refresh the site, the browser renders a blank page. It isn't working. Here's the code:
This Does NOT Work in List.cshtml
#using Orchard.DisplayManagement.Shapes;
#{
var list = Model.ContentItems;
var items = list.Items;
var count = items.Count;
var listTag = Tag(list, "ul");
listTag.AddCssClass("content-items");
listTag.AddCssClass("blog-posts");
var index = 0;
}
#listTag.StartElement
#foreach (var item in items) {
var itemTag = Tag(item, "li");
if (index == 0) {
itemTag.AddCssClass("first");
}
else if (index == count - 1) {
itemTag.AddCssClass("last");
}
#itemTag.StartElement
#Display(item)
#itemTag.EndElement
++index;
}
#listTag.EndElement
As a trouble shooting step, I replace the List.cshtml with <p>Hello world.</p>. Orchard renders the markup as expected. So, something is incompatible between the Razor code from Bertrand's blog and the Tags List.
To find out what exactly is incompatible, I try Betrand's code one line at time to see where it breaks (yup, VS would be better than WM here). At each change, I restart WebMatrix and view the results. This is the minimal code that breaks it.
The Culprit
#using Orchard.DisplayManagement.Shapes;
#{
var list = Model.ContentItems;
var items = list.Items;
}
list.Items isn't appropriate here. So I comment it out again and run the <p>Hello World</p> version again. Also, Shape Tracing reveals that on my Tags/tagname page, the Content Zone is now rendering the List twice. Is that normal?
As another step, I replace Model.ContentItems just with Model. It works. It seems that, to override the List.cshtml template, we cannot use the ContentItems property of Model. Here is the new, working code:
This Does Work in List.cshtml
#using Orchard.DisplayManagement.Shapes;
#{
//var list = Model.ContentItems;
//var items = list.Items;
var items = Model.Items;
var count = items.Count;
//var listTag = Tag(list, "ul");
var listTag = Tag(Model, "ul");
listTag.AddCssClass("content-items");
listTag.AddCssClass("blog-posts");
var index = 0;
}
#listTag.StartElement
#foreach (var item in items) {
var itemTag = Tag(item, "li");
if (index == 0) {
itemTag.AddCssClass("first");
}
else if (index == count - 1) {
itemTag.AddCssClass("last");
}
#itemTag.StartElement
#Display(item)
#itemTag.EndElement
++index;
}
#listTag.EndElement
Onward through the article.
So far so good, we have effectively taken over the rendering of the
list, but the actual HTML [will] be... identical to what we had before
[except for] the implementation.
Okay. I'm following. We want to modify the rendering not just re-implement it.
Alternates are a collection of strings that describe additional shape
names for the current shape... in the Metadata.Alternates property of any shape.
Gotcha. Now, why doesn't the Tags/tagname page show an alternate other than just List.cshtml for the rendering of the List shape?
All we need to do is add to this list [of alternates]... [and make sure] to respect the lifecycle...
Great. Maybe we can we add another alternate for the List shape on the Tags/tagname page. But, doing that is different from what Betrand is explaining. While Betrand's blog post is excellent, it is explaining how to add an alternate for an item, whereas I would like to add an alternate for the list.
The List.cshtml template is where I would add an alternate for a List Item as follows:
ShapeMetadata metadata = item.Metadata;
string alternate = metadata.Type + "_" +
metadata.DisplayType + "__" +
item.ContentItem.ContentType +
"_First";
metadata.OnDisplaying(ctx => {
metadata.Alternates.Add(alternate);
});
So that...
[t]he list of alternates from Shape Tracing now contains a new item.
Where and how, though, would I add an alternate for the List shape? Bertrand has recommended to check out the Shape Table Providers blog post for this. The quotes below are from that post.
But what if you want to change another shape template for specific
pages, for example the main Content shape on the home page?
This looks like a fit, because my example is the main List shape on the tags page. To do this we...
... handle an event that is triggered every time a shape named "Content"
[in our case "List"] is about to be displayed. [It] is implemented in a shape table provider which is where you do all shape related site-wide operations.
Great! Here is my implementation for adding another template for the main List shape.
TheThemeMachine > ListShapeProvider.cs
namespace Themes.TheThemeMachine
{
using Orchard.DisplayManagement.Descriptors;
public class ListShapeProvider : IShapeTableProvider
{
public void Discover(ShapeTableBuilder builder)
{
System.Diagnostics.Debugger.Break(); // break not hit
builder.Describe("List").OnDisplaying(displaying => {
// do stuff to the shape
displaying.ShapeMetadata.Alternates.Add("Tags__List");
});
}
}
}
The above builds and runs but does not hit the breakpoint nor add an alternate for the List shape on the /tags page. So I looked into the Orchard.Azure.MediaServices module and its CloudVideoPlayerShape which implements IShapeTableProvider. Its breakpoint does get hit. How is my code for ListShapeProvider fundamentally different than the code for the CloudVideoPlayerShape?
Also, I installed the Orchard.Themes.CustomLayoutMachine.1.0.nupkg as suggested in Bertrand's blog post. It unfortunately no longer contains an implementation of IShapeTableProvider.
I have also looked at this szmyd post, which does not explain where to put the IShapeTableProvider code.
Further, I installed the Contoso theme from the Orchard Gallery. It works and builds after adding a reference to Microsoft.CSharp. It also includes an implementation of the IShapeTableProvider. Hooray! Comparing its ContentShapeProvider with my ListShapeProvider reveals a subtle but important difference:
Contoso.csproj
<ItemGroup>
<Compile Include="Code\ContentShapeProvider.cs" />
</ItemGroup>
My implementation didn't include the .cs file in the compilation, because my theme has neither a .csproj nor a App_Code folder. So, I recreated my theme with the following code generation:
orchard.exe
feature enable Orchard.CodeGeneration
codegen theme My.FirstTheme /CreateProject:true
theme enable My.FirstTheme
feature enable Orchard.DesignerTools
When adding the ListShapeProvider.cs file, Visual Studio automatically added a ItemGroup/Compile entry for the file, which included the code in compilation. Hooray!
These two posts will help.
Shape Shifting
List Customization
Here are steps of my own minimum solution.
Download and unzip Orchard.Source.1.8.zip.
Open "\Downloads\Orchard.Source.1.8\src\Orchard.sln" in Visual Studio.
Build the solution to create orchard.exe.
Generate a new theme with orchard.exe. Use CreateProject:true because you will need a csproj to include your .cs file.
orchard.exe
setup /SiteName:SITE /AdminUsername:ME /AdminPassword:PWD /DatabaseProvider:SqlCe
feature enable Orchard.CodeGeneration
codegen theme My.FirstTheme /CreateProject:true
theme enable My.FirstTheme
In VS, add a ListShapeProvier.cs file to the root (or any folder) in your theme.
Add the following code to ListShapeProvider.cs.
namespace My.FirstTheme
{
using Orchard.DisplayManagement.Descriptors;
public class ListShapeProvider : IShapeTableProvider
{
public void Discover(ShapeTableBuilder builder)
{
System.Diagnostics.Debugger.Break();
// implementation here
}
}
}
Build the solution.
Run Orchard.Web.
Visual Studio will break at System.Diagnostics.Debugger.Break(). If it doesn't, go to the Orchard Dashboard and make My.FirstTheme the Current Theme.
Now read Shape Shifting to implement public void Discover(ShapeTableBuilder builder).
This post should give you a full response: http://weblogs.asp.net/bleroy/archive/2011/05/23/orchard-list-customization-first-item-template.aspx

code igniter dynamic routing

I am using code igniter.
What I want to do is,
if a page is visited that does not exist
example.com/idontexist
Then I want to first check a database to see if idontexist is in the database.
If it is then I want to route the user to
example.com/action/idontexist.
How can I do this?
I feel like this gets asked every week.
Open up your application/config/routes.php, then add something like this:
$route['^(:any)'] = "my_controller/get_article/$1";
Please note that it will route everything to a controller called action. If you have other controllers then you should add a route for them too (preferably placed before this one).
// EDIT: Using this you can goto http://your-site.com/secrets-of-internet-marketing and it will call the get_article function in the my_controller controller, and pass "secrets-of-internet-marketing" as the first argument. Which can then process with something like this:
public function get_article($article_name) {
// something like this:
$article = $this->article_model->get_model_by_name($article_name);
$this->load->view('article', $article);
}
One solution would be to extend the CI_Router class with your own in application/core/MY_Router.php and just copy the method _set_routing() in your class.
Your changes would go somewhere after routes.php file is included and set to $this->routes property, you can add your custom routes.
include(APPPATH.'config/routes'.EXT);
...
$this->routes = ( ! isset($route) OR ! is_array($route)) ? array() : $route;
unset($route);
//Get your custom routes:
$your_routes = $this->_get_custom_routes();
foreach($your_routes as $custom_route)
{
$this->routes[$custom_route['your_regex_match']] = $custom_route['controller'].'/'.$custom_route['action'];
}
Of course you might not need this, but I hope it gives you some idea.
This way you can add custom routes and since you will have to fetch them from database, consider caching them for better speed.