I am developing a new module and in my hook_menu_alter() I need to detect the node currently being viewed.
Instead of using arg(1) to fetch the the node id from the url, I discovered I can use
menu_get_object().
The following code works in my hook_init() but does not in hook_menu_alter():
$node = menu_get_object();
dpm($node);
Can anyone offer some insight into why that does not work and how to get the current node infomation in hook_menu_alter()?
Thanks.
The output from hook_menu, hook_menu_alter etc. is cached so those functions will only be called when the caches are cleared, not for every page load. If you think about, if the menus were rebuilt on every page load the performance of the site would suffer considerably.
As such, when hook_menu_alter is called (which won't be from a node page), there's no node for menu_get_object() to give you. The way to handle these things is in the page/access callback for the menu item:
function mymodule_menu_alter(&$items) {
$items['some/path']['page callback'] = 'mymodule_page_callback';
}
function mymodule_page_callback() {
// This is a live page so menu_get_object() is now available
$node = menu_get_object();
}
From your comment I think you're trying to deny access to particular nodes based on some criteria. For this you'll want to implement your own access callback for the already existing node/% menu path. Something like this:
function mymodule_menu_alter(&$items) {
$items['node/%node']['access callback'] = 'mymodule_access_callback';
}
function mymodule_access_callback($node) {
if ($node->type == 'group') {
if (some_function_that_determines_access($node)) {
return TRUE;
}
return FALSE;
}
return node_access('view', $node);
}
Related
I am Working on making the menu for our content management software using php and we are having this small issue. Since we want everything to eventually be called in chunks, were breaking certain page items into chunks and loading them via functions through an included file. Since this is hard to explain, I will post some example code of what i mean below.
This is the file page.php (removed needless html code).
This is the page the user is on:
<?php
define("CURRENT_PAGE", "page.php");
include_once("data/main.inc.php");
?><html>
Content loads here.
<? desktopMenu(); ?>
</html>
Okay and here's the function for desktopMenu() from main.inc.php:
function desktopMenu() {
// Query to get the top level navigation links with no parents
$query = mysql_query("SELECT * FROM menu WHERE p_id = '0'");
if(mysql_num_rows($query) > 0) {
while($result = mysql_fetch_array($query)) {
extract($result);
if($isparent == "1") {
// Just check if they have children items
$sub_menu_query = mysql_query("SELECT * FROM menu WHERE p_id = '$id'");
if(mysql_num_rows($sub_menu_query) > 0) {
// CODE TO SHOW THE MENU ITEM AND ITS SUBS
}
} else {
// CODE TO SHOW REGULAR MENU ITEMS
// WANT TO INSERT class='active' if the CURRENT_PAGE is this value..
echo "<li><a href='#'>link</a></li>";
}
} else {
echo "<li><a href='javascript:void(0);'>Error Loading Menu</a></li>";
}
}
I am wondering how I can get the CURRENT_PAGE on the included script so I can load the class="active" onto the correct page. I am already using the following:
$config = include('config.inc.php');
$GLOBALS = $config;
on the top of main.inc.php, above this menu function so I could set global variables and include my $config['database'] variables for calling the SQL database within a function (doesn't work otherwise).
How can I check the current_page variable so I can set it active in the menu? I have tried a few different things but nothing is showing the way we expect it to. Thanks guy.
First of all I would recommend looking at MVC architecture when building your apps. I believe the use of GLOBALS is frowned upon.
To answer your question:
Since you are defining a constant define("CURRENT_PAGE", "page.php"); then this will be globally available within the scope of the function desktopMenu()
so you may use something like:
$className = (isset(CURRENT_PAGE) && CURRENT_PAGE=='xxxxx')?'class="active"':'';
echo "<li>link</li>";
xxxx string is most likely a field output from you database as the page name which will match the defined constant.
$className = (isset(CURRENT_PAGE) && CURRENT_PAGE==$result['page_name'])?'class="active"':'';
This is the basic form and you will most likely need additional conditions for the 'active' menu switch mapping to different pages.
I've tried to answer your question with an example although the structure you have used run the app is not the recommended way to develop.
I would look at the way modern frameworks are structured (Laravel, Zend, Symphony...) and utilise these.
I would also try and automate the page mapping (e.g. look at the URL and pull out the page from a rewrite which matches to the menu in your database)
best of luck
There are multiple options. Including static functions, global variables and passing the variable or object into the function.
The consensus for various reasons is to pass the variable into the function
$myVar = new Object\Or\Data();
function myFunction($myVar) {
//do stuff with $myVar
}
//then call the function
myFunction($myVar);
There are lots of answers to this question on stackOverflow, so have a deeper search. Here is an example
I found the solution to my problem and thought I would share here. I first set the call on the page.php to use desktopMenu(CURRENT_PAGE); and then on my main.inc.php I added this line
$thispage = CURRENT_PAGE;
function desktopMenu($thispage) {
//REST OF FUNCTION
}
And I set a table variable on each menu item called menu-group, so I can define the current menu group for a menu item and have the appropriate menu item highlighted when you're on that page or one of it's sub pages.
Thanks so much for the answers guys!
URL : http://www.sayuri.co.jp/used-cars
Example : http://www.sayuri.co.jp/used-cars/B37753-Toyota-Wish-japanese-used-cars
Hey guys , need some help with one of my personal projects , I've already wrote the code to fetch data from each single car url (example) and post on my site
Now i need to go through the main url : sayuri.co.jp/used-cars , and :
1) Make an array / list / nodes of all the urls for all the single cars in it , then run my internal code for each one to fetch data , then move on to the next one
I already have the code to save each url into a log file when completed (don't think it will be necessary if it goes link by link without starting from the top but will ensure no repetition.
2) When all links are done for the page , it should move to the next page and do the same thing until the end ( there are 5-6 pages max )
I've been stuck on this part since last night and would really appreciate any help . Thanks
My code to get data from the main url :
$content = file_get_contents('http://www.sayuri.co.jp/used-cars/');
// echo $content;
and
$dom = new DOMDocument;
$dom->loadHTML($content);
//echo $dom;
I'm guessing you already know this since you say you've gotten data from the car entries themselves, but a good point to start is by dissecting the page's DOM and seeing if there are any elements you can use to jump around quickly. Most browsers have page inspection tools to help with this.
In this case, <div id="content"> serves nicely. You'll note it contains a collection of tables with the required links and a <div> that contains the text telling us how many pages there are.
Disclaimer, but it's been years since I've done PHP and I have not tested this, so it is probably neither correct or optimal, but it should get you started. You'll need to tie the functions together (what's the fun in me doing it?) to achieve what you want, but these should grab the data required.
You'll be working with the DOM on each page, so a convenience to grab the DOMDocument:
function get_page_document($index) {
$content = file_get_contents("http://www.sayuri.co.jp/used-cars/page:{$index}");
$document = new DOMDocument;
$document->loadHTML($content);
return $document;
}
You need to know how many pages there are in total in order to iterate over them, so grab it:
function get_page_count($document) {
$content = $document->getElementById('content');
$count_div = $content->childNodes->item($content->childNodes->length - 4);
$count_text = $count_div->firstChild->textContent;
if (preg_match('/Page \d+ of (\d+)/', $count_text, $matches) === 1) {
return $matches[1];
}
return -1;
}
It's a bit ugly, but the links are available inside each <table> in the contents container. Rip 'em out and push them in an array. If you use the link itself as the key, there is no concern for duplicates as they'll just rewrite over the same key-value.
function get_page_links($document) {
$content = $document->getElementById('content');
$tables = $content->getElementsByTagName('table');
$links = array();
foreach ($tables as $table) {
if ($table->getAttribute('class') === 'itemlist-table') {
// table > tbody > tr > td > a
$link = $table->firstChild->firstChild->firstChild->firstChild->getAttribute('href');
// No duplicates because they just overwrite the same entry.
$links[$link] = "http://www.sayuri.co.jp{$link}";
}
}
return $links;
}
Perhaps also obvious, but these will break if this site changes their formatting. You'd be better off asking if they have a REST API or some such available for long term use, though I'm guessing you don't care as much if it's just a personal project for tinkering.
Hope it helps prod you in the right direction.
I'm using this a particular code to dynamically load content on a webpage.
Problem is, I want to use it to load multiple things on the same page. How do I go about making this possible? I've already isolated the problem to the fact that the second instance of the code that runs replaces the entire URL instead of appending an additional ?="pageurl" but I'm stuck right there.
I've got a second script that uses the variable p instead of b so the browser knows to load p's content in a different location.
This is what the code does:
http://www.youtube.com/watch?v=Qg-EBdNaUbo
[edit]
Not sure why you guys can't see the link, it explains everything. I'm calling the script via a link Here is the link again youtube.com/watch?v=Qg-EBdNaUbo
CODE:
<?php
$pages_dir = 'pages';
if(!empty($_GET['b']))
{
$pages = scandir($pages_dir, 0);
unset($pages [0], $pages [1]);
$b = $_GET['b'];
if(in_array($b, $pages))
{
include($pages_dir.'/'.$b);
}
else
{
echo 'sorry page not found';
}
}
else
{
}
?>
I have a page on my site that fetches and displays news items from the database of another (legacy) site on the same server. Some of the items contain relative links that should be fixed so that they direct to the external site instead of causing 404 errors on the main site.
I first considered using the <base> tag on the fetched news items, but this changes the base URL of the whole page, breaking the relative links in the main navigation - and it feels pretty hackish too.
I'm currently thinking of creating a regex to find the relative URLs (they all start with /index.php?) and prepending them with the desired base URL. Are there any more elegant solutions to this? The site is built on Symfony 2 and uses jQuery.
Here is how I would tackle the problem:
function prepend_url ($prefix, $path) {
// Prepend $prefix to $path if $path is not a full URL
$parts = parse_url($path);
return empty($parts['scheme']) ? rtrim($prefix, '/').'/'.ltrim($path, '/') : $path;
}
// The URL scheme and domain name of the other site
$otherDomain = 'http://othersite.tld';
// Create a DOM object
$dom = new DOMDocument('1.0');
$dom->loadHTML($inHtml); // $inHtml is an HTML string obtained from the database
// Create an XPath object
$xpath = new DOMXPath($dom);
// Find candidate nodes
$nodesToInspect = $xpath->query('//*[#src or #href]');
// Loop candidate nodes and update attributes
foreach ($nodesToInspect as $node) {
if ($node->hasAttribute('src')) {
$node->setAttribute('src', prepend_url($otherDomain, $node->getAttribute('src')));
}
if ($node->hasAttribute('href')) {
$node->setAttribute('href', prepend_url($otherDomain, $node->getAttribute('href')));
}
}
// Find all nodes to export
$nodesToExport = $xpath->query('/html/body/*');
// Iterate and stringify them
$outHtml = '';
foreach ($nodesToExport as $node) {
$outHtml .= $node->C14N();
}
// $outHtml now contains the "fixed" HTML as a string
See it working
You can override the base tag by putting http:\\ in front of the link. That is, give a full url, not a relative URL.
Well, not actually a solution, but mostly a tip...
You could start playing aroung with ExceptionController.
There, just for example, you could seek for 404 error and check query string appended to request:
$request = $this->container->get('request');
....
if (404 === $exception->getStatusCode()) {
$query = $request->server->get('QUERY_STRING');
//...handle your logic
}
The other solution would be to define special route with its controller for such purposes, which would catch requests to index.php and do redirects and so on. Just define index.php in requirements of route and move this route on the top of your routing.
Not a clearest answer ever, but at least I hope I gave you a direction...
Cheers ;)
I am making quite a large online points/purchasing system in PHP and just have a fundamental question.
All the relevant stuff is on a single PHP page within the site, with "includes" from other parts of the site such as shopping cart, points review, products etc..., but throughout the page there are stages where the user clicks a form submit button to pass values via $_POST.
As there is a main page for all this stuff, I have a part at the top of the page where it takes all the POST values and makes decisions based upon them, like so:
if($_POST['add']) {
$product_id = $_POST['add'];
}
if($_POST['remove']) {
$rid = $_POST['id'];
$cart->del_item($rid);
}
if($_POST['empty']){
$cart->empty_cart();
}
if($_POST['purchase']) {
foreach($cart->get_contents() as $item) {
$sql="INSERT INTO wp_scloyalty_orders VALUES (".$user_id.", ".$item['id'].")";
$result=mysql_query($sql);
}
$cart->empty_cart();
unset($_SESSION['cart']);
}
if($_POST['add']) {
query_posts('post_type=prizes&showposts=-1&p='.$product_id.'');
while (have_posts()) : the_post();
$my_meta = get_post_meta($post->ID,'_my_meta',TRUE);
if($calctotalnew > $my_meta['pointsvalue']){
$cart->add_item(get_the_id(), 1, $my_meta['pointsvalue'], get_the_title());
} else {
echo 'You do not have sufficient points to redeem this product...';
}
endwhile;
wp_reset_query();
}
So my question is... is this really a good way to organize a system, having the form actions go to the same page that the form is on, and have a load of IF statements to decide what to do with the POST values?
Thanks! :)
it's generally best to capture separate POST calls (grouped by type) in separate actions. I usually go as follows:
page 1 has a form, which will submit to eg. product.php?action=add. In product.php you can route the 'add' action to the function add_product() (or whatever). Then when the product is added, just header the user back to the main page (or whatever page you'd like). This immediately tackles the problem with refresh-posts (user refreshing the page which will send the same data again).
following mvc imagine you have a controller Product which handles all the product actions. The skeleton could look like this (assuming function action_x will be executed when yoursite.com/product/x is requested):
class Product_Controller {
function action_show() {
}
function action_update() {
}
function action_delete() {
}
}
if your framework supports a default action of some sort you could route your actions:
function action_default() {
if(method_exists(array($this, 'action_'. $_POST['action']))) {
return call_user_method('action_'. $_POST['action'], $this);
}
}
ofcourse the same can be achieved without controller classes;
if(function_exists('action_'. $_POST['action'])) {
call_user_func('action_'. $_POST['action']);
}
function action_show() { }
...
and to illustrate the discussion in the comments;
function action_update() {
// do some update logic, query an UPDATE to mysql etc.
if($result) {
// optionally save a success message
Message::add('Your record has been updated');
header('Location: main_page.php'); // or another intelligent redirect function
} else {
Message::add('Sorry, something went wrong');
header('Location: error_page.php'); // or also main_page
}
}
This will also keep your code cleaner, as updating/adding/deleting stuff is radically different from showing stuff, this will prevent you from mixing up stuff. You could even call the show function from within the update function if you want to skip the redirect.
But in the end it's a matter of choice, led by pragmatism or your framework ;)
I hope this'll explain everything a bit, don't hesitate to ask for clarification
if you want to separate the logic from the interface then you can simple create new file and put the all logical and database related code in that file and include OR require that file in the view file
like
view.php interface file and
logic.php is your logic file then
first line in view.php is
require_once(logic.php');
and all the logic is in this files
simple MVC
Well, it seems I have to explain.
It absolutely does not matter how much IF statements you have in the POST handler. Your current design is okay, and there is no reason to ask nor change it.
The only thing that you may wish to add to your design is a front controller, which will take both entity (cart) and action("add") and call add() method of $cart class. these methods you may store one under another in the class source.
Though it is quite huge improvement, requiring great rethinking of the whole site architecture. So, you may stick with your current one.
As for your other question, how to display errors, here is an answer: php redirection not working