Skip to content
This repository has been archived by the owner on Aug 8, 2018. It is now read-only.

Commit

Permalink
Merge pull request #34 from systemseed/mainmenu
Browse files Browse the repository at this point in the history
[#155932196] Manage main menu.
  • Loading branch information
otarza authored Apr 18, 2018
2 parents d0ada47 + db7a50c commit 2a37568
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 18 deletions.
3 changes: 3 additions & 0 deletions backend-gifts/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@
]
},
"patches": {
"drupal/core": {
"Menu content is not accessible via jsonapi": "https://www.drupal.org/files/issues/2018-03-19/menu_link_content-view-permissions-2915792-16.patch"
},
"drupal/metatag": {
"Fix compatibility with jsonapi": "https://www.drupal.org/files/issues/make_metatag_fields-2636852-80.patch"
}
Expand Down
7 changes: 6 additions & 1 deletion backend-gifts/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend-gifts/config/sync/user.role.gifts_manager.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ permissions:
- 'access toolbar'
- 'access user profiles'
- 'add e-card item entities'
- 'administer menu'
- 'administer meta tags'
- 'administer nodes'
- 'administer taxonomy'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* Contains falcon_gifts_api.module.
*/

use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Routing\RouteMatchInterface;

/**
Expand All @@ -22,3 +24,18 @@ function falcon_gifts_api_help($route_name, RouteMatchInterface $route_match) {
default:
}
}

/**
* Implements hook_entity_base_field_info().
*/
function falcon_gifts_api_entity_base_field_info(EntityTypeInterface $entity_type) {
$fields = [];
if ($entity_type->id() == 'menu_link_content') {
$fields['url'] = BaseFieldDefinition::create('string')
->setLabel(t('URL'))
->setDescription(t('The URL of the menu link.'))
->setComputed(TRUE)
->setQueryable(FALSE);
}
return $fields;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
services:
serializer.normalizer.menu_link_content.falcon_gifts_api:
class: Drupal\falcon_gifts_api\Normalizer\MenuLinkContentEntityNormalizer
arguments: ['@jsonapi.link_manager', '@jsonapi.resource_type.repository', '@entity_type.manager']
tags:
- { name: normalizer, priority: 22 }
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Drupal\falcon_gifts_api\Normalizer;

use Drupal\jsonapi\Normalizer\ContentEntityNormalizer;
use Drupal\jsonapi\ResourceType\ResourceType;
use Drupal\menu_link_content\MenuLinkContentInterface;

class MenuLinkContentEntityNormalizer extends ContentEntityNormalizer {

/**
* The interface or class that this Normalizer supports.
*
* @var string
*/
protected $supportedInterfaceOrClass = MenuLinkContentInterface::class;

/**
* {@inheritdoc}
*/
protected function getFields($entity, $bundle, ResourceType $resource_type) {
/** @var \Drupal\menu_link_content\MenuLinkContentInterface $entity */
if ($bundle != 'menu_link_content') {
return parent::getFields($entity, $bundle, $resource_type);
}

// Check access to node if it is a link to node entity.
$url_object = $entity->getUrlObject();
$url = $url_object->toString();
if ($url_object->isRouted() && ($params = $url_object->getRouteParameters()) && isset($params['node'])) {
$node = $this->entityTypeManager->getStorage('node')->load($params['node']);
if (!$node->access('view')) {
$url = null;
}
}

$entity->set('url', $url);
return parent::getFields($entity, $bundle, $resource_type);
}

}
21 changes: 21 additions & 0 deletions frontend-gifts/src/actions/menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import request from 'superagent';
import jsonapify from 'superagent-jsonapify';
import config from '../config';

jsonapify(request);

export function loadAll() {
return {
type: 'GET_MENU',
payload: request
.get(`${config.backend}/v1/gifts/jsonapi/menu_link_content/menu_link_content`)
.query({
'fields[menu_link_content--menu_link_content]': 'uuid,title,url',
'filter[menu_name][condition][path]': 'menu_name',
'filter[menu_name][condition][value]': 'main',
'filter[enabled][condition][path]': 'enabled',
'filter[enabled][condition][value]': 1,
'sort': 'weight'
})
};
}
69 changes: 53 additions & 16 deletions frontend-gifts/src/components/GlobalHeader/MainMenu.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,66 @@
import React from 'react';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';
import * as menuActions from '../../actions/menu';

const MainMenu = ({ isMenuCollapsed, onMenuClick, location }) => (
<nav className={`main-navigation text-center ${isMenuCollapsed ? '' : 'open'}`}>
<ul className="menu">
<li><NavLink to="/" exact onClick={onMenuClick} location={location}>Browse Gifts</NavLink></li>
<li><NavLink to="/corporate" exact onClick={onMenuClick} location={location}>Corporate Gifts</NavLink></li>
<li><NavLink to="/how-gifts-work" exact onClick={onMenuClick} location={location}>How Gifts Work</NavLink></li>
<li><NavLink to="/faq" exact onClick={onMenuClick} location={location}>FAQs</NavLink></li>
<li><NavLink to="/contact" exact onClick={onMenuClick} location={location}>Contact</NavLink></li>
</ul>
</nav>
);
class MainMenu extends Component {

componentWillMount() {
// Load list of pages is they haven't been loaded yet.
const { menu, loadMenu, done } = this.props;
if (!menu.list.length) {
loadMenu().then(done, done);
}
}

render = () => {
const { isMenuCollapsed, onMenuClick, location, menu } = this.props;

return (<nav className={`main-navigation text-center ${isMenuCollapsed ? '' : 'open'}`}>
<ul className="menu">
{menu.list.map((item) => {
if (!item.url) {
return '';
}
return (<li key={item.uuid}>
<NavLink to={item.url} exact onClick={onMenuClick} location={location}>{item.title}</NavLink>
</li>);
})}
</ul>
</nav>);
}

}

MainMenu.propTypes = {
isMenuCollapsed: React.PropTypes.bool.isRequired,
onMenuClick: React.PropTypes.func.isRequired,
location: React.PropTypes.object.isRequired,
loadMenu: React.PropTypes.func,
done: React.PropTypes.func,
menu: React.PropTypes.shape({
isPending: React.PropTypes.bool,
isFulfilled: React.PropTypes.bool,
isError: React.PropTypes.bool,
list: React.PropTypes.arrayOf(
React.PropTypes.shape({
uuid: React.PropTypes.string,
title: React.PropTypes.string,
url: React.PropTypes.string
})
)
})
};

// Pass location prop to NavLink explicitly.
// See https://github.com/ReactTraining/react-router/issues/4638
const mapStateToProps = state => ({
location: state.router.location
// Pass location prop to NavLink explicitly.
// See https://github.com/ReactTraining/react-router/issues/4638
location: state.router.location,
menu: state.menu
});

export default connect(mapStateToProps)(MainMenu);
const mapDispatchToProps = {
loadMenu: menuActions.loadAll,
};

export default connect(mapStateToProps, mapDispatchToProps)(MainMenu);
2 changes: 2 additions & 0 deletions frontend-gifts/src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { giftsCorporate, giftCorporateCustomPrice } from './gifts.corporate';
import { giftsFree } from './gifts.free';
import { currentCurrency } from './currencies';
import { contactForm } from './contact';
import { menu } from './menu';
import { messageBar } from './messageBar';
import { featuredImages } from './featuredImages';
import { pages } from './pages';
Expand Down Expand Up @@ -45,6 +46,7 @@ export default combineReducers({
giftsFree,
giftCustomPrice,
giftCorporateCustomPrice,
menu,
messageBar,
siteContentSettings,
popup,
Expand Down
41 changes: 41 additions & 0 deletions frontend-gifts/src/reducers/menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export const menu = (state = {
isPending: false,
isFulfilled: false,
isError: false,
list: []
}, action) => {
switch (action.type) {

case 'GET_MENU_PENDING':
return {
...state,
isPending: true,
};

case 'GET_MENU_FULFILLED': {
const data = action.payload.body.data;
const list = [];

data.forEach((item) => {
list.push(item.attributes);
});

return {
...state,
isPending: false,
isFulfilled: true,
list
};
}

case 'GET_MENU_REJECTED':
return {
...state,
isPending: false,
isError: true,
};

default:
return state;
}
};
2 changes: 1 addition & 1 deletion frontend-gifts/src/views/BasicPageView/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ BasicPageView.propTypes = {
isPending: React.PropTypes.bool,
isFulfilled: React.PropTypes.bool,
isError: React.PropTypes.bool,
pages: React.PropTypes.arrayOf(
list: React.PropTypes.arrayOf(
React.PropTypes.shape({
uuid: React.PropTypes.string,
title: React.PropTypes.string,
Expand Down

0 comments on commit 2a37568

Please sign in to comment.