Skip to content

Commit

Permalink
[Closes #4249] Contribute back upstream menu management from IGAD
Browse files Browse the repository at this point in the history
  • Loading branch information
afabiani committed Feb 22, 2019
1 parent d9e8d7a commit b60e26b
Show file tree
Hide file tree
Showing 21 changed files with 814 additions and 12 deletions.
108 changes: 108 additions & 0 deletions docs/tutorials/admin/customize_lookfeel/customize/theme_admin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,114 @@ From here you can continue to customize your :file:`site_index.html` template to
Other theming options
---------------------

Additional menus
~~~~~~~~~~~~~~~~

An interesting customization could be to insert `additional menus` in the navbar.
It can be done very easily through the **render_nav_menu** inclusion tags which allow to have ready-to-use dropdown menus without any further front-end implementation needed.
This template tag takes the `placeholder name` as argument and renders all the menus which belong to it through ``<li>`` tags.

GeoNode provides a **TOPBAR_MENU** named placeholder by default inside the `extra_tab` block of the ``base.html``:

.. code-block:: html

{% block extra_tab %}

{% render_nav_menu 'TOPBAR_MENU' %}

{% endblock %}

A placeholder is a logical container for menus, to make it work you should load the `intial_data.json` (see :ref:`geonode_install_initialization` for references) or create the placeholder from the `Admin` panel (pay attention to the `Name`, it must be 'TOPBAR_MENU'):

.. figure:: ../img/admin_menu_placeholder_initial_data.png

Once the placeholder has been created you can add your menus from the 'Admin' panel.
The following is a basic example.

Open the `Admin panel` http://localhost:8000/admin/, go to the BASE section and take a look at the models inside the red rectangle in the picture below:

.. figure:: ../img/admin_base_section.png

Follow these steps:

#. Check if **TOPBAR_MENU** exists in the **Menu placeholder** section (if not create it as described in the section above):

.. figure:: ../img/admin_menu_placeholders.png

#. Create **Menu**\s. They are links containers, give them a `title` and choose the placeholder they belong to (the **TOPBAR_MENU** placeholder in this case). A menu will be shown before or after another menu based on the `order` field:

.. figure:: ../img/admin_menu.png

.. figure:: ../img/admin_menu_2.png

#. Create **Menu Item**\s. They can be internal or external links. For each link a `menu` must be selected. The `title` is the link visible label and the `order` field determines the mutual position between all the links in a menu:

.. figure:: ../img/admin_menu_item_1_1.png

.. figure:: ../img/admin_menu_item_1_2.png

.. figure:: ../img/admin_menu_item_2_1.png

#. See the result:

.. figure:: ../img/custom_menu_1.png

.. figure:: ../img/custom_menu_2.png

You could want your menu on the right side of the top navbar, near the `search` function for example. It can be accomplished in few simple steps:

#. Add a block (`my_extra_right_tab` in the example below) in the :file:`base.html` template, inside the ``navbar-right`` <ul> element:

.. code-block:: html

<ul class="nav navbar-nav navbar-right">

{% block my_extra_right_tab %}

{% endblock my_extra_right_tab %}

<li>
<div class="search">
...

#. Move the **render_nav_menu** tag into the block:

.. code-block:: html

<ul class="nav navbar-nav navbar-right">

{% block my_extra_right_tab %}

{% render_nav_menu 'TOPBAR_MENU' %}

{% endblock my_extra_right_tab %}

<li>
<div class="search">
...

Take a look at the right side of the navbar:

.. figure:: ../img/custom_right_menu.png

The custom menus seen so far are based on a fixed html template, the :file:`base/templates/menu.html`, hooked to the **render_nav_menu** tag. Feel free to create new placeholders and menus through this powerful template tag, it makes the menus creation very fast in the top navbar but it's not flexible enough to allow to choose a different position.
So more generic tags has been implemented in GeoNode to have more customization power.

The **get_menu** assignment tag, included in the `base_tags`, lets you decide how to render the menus returning only their data in a dictionary like this:

.. code-block:: html

{
<Menu: my_menu_1>: <QuerySet [<MenuItem: my_item_1_1>, <MenuItem: my_item_1_2>]>,
<Menu: my_menu_2>: <QuerySet [<MenuItem: my_item_2_1>, <MenuItem: my_item_2_2>]>,
...
}

You can easily use those data to build `Bootstrap <https://getbootstrap.com/docs/3.3>`_ dropdowns or an accordion panels and put them wherever you want.

Styling options
---------------

You are able to change any specific piece of your GeoNode project's style by adding CSS rules to :file:`site_base.css`, but since GeoNode is based on Bootstrap, there are many pre-defined themes that you can simply drop into your project to get a whole new look. This is very similar to `WordPress <http://wordpress.com>`_ themes and is a powerful and easy way to change the look of your site without much effort.

Bootswatch
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 23 additions & 1 deletion geonode/base/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@
Link,
Backup,
License,
HierarchicalKeyword)
HierarchicalKeyword,
MenuPlaceholder,
Menu,
MenuItem
)
from django.http import HttpResponseRedirect


Expand Down Expand Up @@ -249,6 +253,21 @@ class HierarchicalKeywordAdmin(TreeAdmin):
form = movenodeform_factory(HierarchicalKeyword)


class MenuPlaceholderAdmin(admin.ModelAdmin):
model = MenuPlaceholder
list_display = ('name', )


class MenuAdmin(admin.ModelAdmin):
model = Menu
list_display = ('title', 'placeholder', 'order')


class MenuItemAdmin(admin.ModelAdmin):
model = MenuItem
list_display = ('title', 'menu', 'order', 'blank_target', 'url')


admin.site.register(TopicCategory, TopicCategoryAdmin)
admin.site.register(Region, RegionAdmin)
admin.site.register(SpatialRepresentationType, SpatialRepresentationTypeAdmin)
Expand All @@ -258,6 +277,9 @@ class HierarchicalKeywordAdmin(TreeAdmin):
admin.site.register(Backup, BackupAdmin)
admin.site.register(License, LicenseAdmin)
admin.site.register(HierarchicalKeyword, HierarchicalKeywordAdmin)
admin.site.register(MenuPlaceholder, MenuPlaceholderAdmin)
admin.site.register(Menu, MenuAdmin)
admin.site.register(MenuItem, MenuItemAdmin)


class ResourceBaseAdminForm(ModelForm):
Expand Down
7 changes: 7 additions & 0 deletions geonode/base/fixtures/initial_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -4845,5 +4845,12 @@
"bbox_y0": 3.48639,
"bbox_y1": 12.21558
}
},
{
"model": "base.menuplaceholder",
"pk": 1,
"fields": {
"name": "TOPBAR_MENU"
}
}
]
61 changes: 61 additions & 0 deletions geonode/base/migrations/0036_auto_20190129_1433.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.16 on 2019-01-29 14:33
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('base', '0035_resourcebase_dirty_state'),
]

operations = [
migrations.CreateModel(
name='Menu',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255)),
('order', models.IntegerField()),
],
options={
'ordering': ['order'],
},
),
migrations.CreateModel(
name='MenuItem',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255)),
('order', models.IntegerField()),
('blank_target', models.BooleanField()),
('url', models.CharField(max_length=2000)),
('menu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.Menu')),
],
options={
'ordering': ['order'],
},
),
migrations.CreateModel(
name='MenuPlaceholder',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, unique=True)),
],
),
migrations.AddField(
model_name='menu',
name='placeholder',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.MenuPlaceholder'),
),
migrations.AlterUniqueTogether(
name='menuitem',
unique_together=set([('menu', 'order'), ('menu', 'title')]),
),
migrations.AlterUniqueTogether(
name='menu',
unique_together=set([('placeholder', 'order'), ('placeholder', 'title')]),
),
]
20 changes: 20 additions & 0 deletions geonode/base/migrations/0037_auto_20190222_1347.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-02-22 13:47
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('base', '0036_auto_20190129_1433'),
]

operations = [
migrations.AlterField(
model_name='resourcebase',
name='language',
field=models.CharField(choices=[(b'abk', b'Abkhazian'), (b'aar', b'Afar'), (b'afr', b'Afrikaans'), (b'amh', b'Amharic'), (b'ara', b'Arabic'), (b'asm', b'Assamese'), (b'aym', b'Aymara'), (b'aze', b'Azerbaijani'), (b'bak', b'Bashkir'), (b'ben', b'Bengali'), (b'bih', b'Bihari'), (b'bis', b'Bislama'), (b'bre', b'Breton'), (b'bul', b'Bulgarian'), (b'bel', b'Byelorussian'), (b'cat', b'Catalan'), (b'chi', b'Chinese'), (b'cos', b'Corsican'), (b'dan', b'Danish'), (b'dzo', b'Dzongkha'), (b'eng', b'English'), (b'fra', b'French'), (b'epo', b'Esperanto'), (b'est', b'Estonian'), (b'fao', b'Faroese'), (b'fij', b'Fijian'), (b'fin', b'Finnish'), (b'fry', b'Frisian'), (b'glg', b'Gallegan'), (b'ger', b'German'), (b'gre', b'Greek'), (b'kal', b'Greenlandic'), (b'grn', b'Guarani'), (b'guj', b'Gujarati'), (b'hau', b'Hausa'), (b'heb', b'Hebrew'), (b'hin', b'Hindi'), (b'hun', b'Hungarian'), (b'ind', b'Indonesian'), (b'ina', b'Interlingua (International Auxiliary language Association)'), (b'iku', b'Inuktitut'), (b'ipk', b'Inupiak'), (b'ita', b'Italian'), (b'jpn', b'Japanese'), (b'kan', b'Kannada'), (b'kas', b'Kashmiri'), (b'kaz', b'Kazakh'), (b'khm', b'Khmer'), (b'kin', b'Kinyarwanda'), (b'kir', b'Kirghiz'), (b'kor', b'Korean'), (b'kur', b'Kurdish'), (b'oci', b"Langue d 'Oc (post 1500)"), (b'lao', b'Lao'), (b'lat', b'Latin'), (b'lav', b'Latvian'), (b'lin', b'Lingala'), (b'lit', b'Lithuanian'), (b'mlg', b'Malagasy'), (b'mlt', b'Maltese'), (b'mar', b'Marathi'), (b'mol', b'Moldavian'), (b'mon', b'Mongolian'), (b'nau', b'Nauru'), (b'nep', b'Nepali'), (b'nor', b'Norwegian'), (b'ori', b'Oriya'), (b'orm', b'Oromo'), (b'pan', b'Panjabi'), (b'pol', b'Polish'), (b'por', b'Portuguese'), (b'pus', b'Pushto'), (b'que', b'Quechua'), (b'roh', b'Rhaeto-Romance'), (b'run', b'Rundi'), (b'rus', b'Russian'), (b'smo', b'Samoan'), (b'sag', b'Sango'), (b'san', b'Sanskrit'), (b'scr', b'Serbo-Croatian'), (b'sna', b'Shona'), (b'snd', b'Sindhi'), (b'sin', b'Singhalese'), (b'ssw', b'Siswant'), (b'slv', b'Slovenian'), (b'som', b'Somali'), (b'sot', b'Sotho'), (b'spa', b'Spanish'), (b'sun', b'Sudanese'), (b'swa', b'Swahili'), (b'tgl', b'Tagalog'), (b'tgk', b'Tajik'), (b'tam', b'Tamil'), (b'tat', b'Tatar'), (b'tel', b'Telugu'), (b'tha', b'Thai'), (b'tir', b'Tigrinya'), (b'tog', b'Tonga (Nyasa)'), (b'tso', b'Tsonga'), (b'tsn', b'Tswana'), (b'tur', b'Turkish'), (b'tuk', b'Turkmen'), (b'twi', b'Twi'), (b'uig', b'Uighur'), (b'ukr', b'Ukrainian'), (b'urd', b'Urdu'), (b'uzb', b'Uzbek'), (b'vie', b'Vietnamese'), (b'vol', b'Volap\xc3\xbck'), (b'wol', b'Wolof'), (b'xho', b'Xhosa'), (b'yid', b'Yiddish'), (b'yor', b'Yoruba'), (b'zha', b'Zhuang'), (b'zul', b'Zulu')], default=b'eng', help_text='language used within the dataset', max_length=3, verbose_name='language'),
),
]
73 changes: 73 additions & 0 deletions geonode/base/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,79 @@ def __unicode__(self):
return u"{0} link".format(self.link_type)


class MenuPlaceholder(models.Model):

name = models.CharField(
max_length=255,
null=False,
blank=False,
unique=True
)

def __str__(self):
return self.name


class Menu(models.Model):

title = models.CharField(
max_length=255,
null=False,
blank=False
)
placeholder = models.ForeignKey(
to='MenuPlaceholder',
on_delete=models.CASCADE,
null=False
)
order = models.IntegerField(
null=False,
)

def __str__(self):
return self.title

class Meta:
unique_together = (
('placeholder', 'order'),
('placeholder', 'title'),
)
ordering = ['order']


class MenuItem(models.Model):

title = models.CharField(
max_length=255,
null=False,
blank=False
)
menu = models.ForeignKey(
to='Menu',
on_delete=models.CASCADE,
null=False
)
order = models.IntegerField(
null=False
)
blank_target = models.BooleanField()
url = models.CharField(
max_length=2000,
null=False,
blank=False
)

def __str__(self):
return self.title

class Meta:
unique_together = (
('menu', 'order'),
('menu', 'title'),
)
ordering = ['order']


def resourcebase_post_save(instance, *args, **kwargs):
"""
Used to fill any additional fields after the save.
Expand Down
50 changes: 50 additions & 0 deletions geonode/base/templates/base/menu.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!-- -----------------------------------------------------------------
-
- Copyright (C) 2019 OSGeo
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
---------------------------------------------------------------------- -->

{% for menu, menu_items in menus.items %}

{% if menu_items|length > 0 %}

<li>

<a href="" class="dropdown-toggle" data-toggle="dropdown"
role="button" aria-haspopup="true">
{{ menu.title }}
{% if menu_items|length > 0 %}
<i class="fa fa-angle-down fa-lg"></i>
{% endif %}
</a>
<ul class="dropdown-menu">
{% for menu_item in menu_items %}
<li>
<a href="{{ menu_item.url }}"
{% if menu_item.blank_target %}target="_blank"{% endif %}>
{{ menu_item.title }}
</a>
</li>
{% endfor %}
</ul>

</li>

{% endif %}

{% endfor %}


0 comments on commit b60e26b

Please sign in to comment.