changeset 2226:09a9bffa1581

Added Support for non-unique fields plus toggling b//w fqname and value on mouseover.
author Ashutosh Singla <ashu1461@gmail.com>
date Sat, 20 Jul 2013 02:40:21 +0530
parents 87c9fd50cec1
children 00debb5be61a
files MoinMoin/apps/frontend/views.py MoinMoin/items/__init__.py MoinMoin/templates/common.js MoinMoin/templates/layout.html MoinMoin/templates/link_list_no_item_panel.html MoinMoin/themes/__init__.py MoinMoin/themes/_tests/test_navi_bar.py
diffstat 7 files changed, 101 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/apps/frontend/views.py	Fri Jul 19 23:15:39 2013 +0530
+++ b/MoinMoin/apps/frontend/views.py	Sat Jul 20 02:40:21 2013 +0530
@@ -49,7 +49,7 @@
 from MoinMoin.forms import (OptionalText, RequiredText, URL, YourOpenID, YourEmail, RequiredPassword, Checkbox,
                             InlineCheckbox, Select, Names, Tags, Natural, Hidden, MultiSelect, Enum, validate_name,
                             NameNotValidError)
-from MoinMoin.items import BaseChangeForm, Item, NonExistent, NameNotUniqueError
+from MoinMoin.items import BaseChangeForm, Item, NonExistent, NameNotUniqueError, FieldNotUniqueError
 from MoinMoin.items.content import content_registry
 from MoinMoin import user, util
 from MoinMoin.constants.keys import *
@@ -359,14 +359,24 @@
 @frontend.route('/<itemname:item_name>', defaults=dict(rev=CURRENT), methods=['GET', 'POST'])
 @frontend.route('/+show/+<rev>/<itemname:item_name>', methods=['GET'])
 def show_item(item_name, rev):
-    flaskg.user.add_trail(item_name)
     item_displayed.send(app._get_current_object(),
                         item_name=item_name)
     try:
         item = Item.create(item_name, rev_id=rev)
+        flaskg.user.add_trail(item_name)
         result = item.do_show(rev)
     except AccessDenied:
         abort(403)
+    except FieldNotUniqueError:
+        fqname = split_fqname(item_name)
+        revs = flaskg.storage.documents(**fqname.query)
+        fq_names = []
+        for rev in revs:
+            fq_names.extend(rev.fqnames)
+        return render_template("link_list_no_item_panel.html",
+                               headline=_("Items with %(field)s %(value)s", field=fqname.field, value=fqname.value),
+                               item_name=fqname.fullname,
+                               fq_names=fq_names)
     return result
 
 
--- a/MoinMoin/items/__init__.py	Fri Jul 19 23:15:39 2013 +0530
+++ b/MoinMoin/items/__init__.py	Sat Jul 20 02:40:21 2013 +0530
@@ -49,7 +49,7 @@
     NAME, NAME_OLD, NAME_EXACT, WIKINAME, MTIME, ITEMTYPE,
     CONTENTTYPE, SIZE, ACTION, ADDRESS, HOSTNAME, USERID, COMMENT,
     HASH_ALGORITHM, ITEMID, REVID, DATAID, CURRENT, PARENTID, NAMESPACE,
-    UFIELDS_TYPELIST
+    UFIELDS_TYPELIST, UFIELDS
 )
 from MoinMoin.constants.contenttypes import CHARSET, CONTENTTYPE_NONEXISTENT
 from MoinMoin.constants.itemtypes import (
@@ -153,11 +153,10 @@
     :itemtype and :contenttype are used when creating a DummyRev, where
     metadata is not available from the storage.
     """
-    query = {fqname.field: fqname.value, NAMESPACE: fqname.namespace}
     rev_id = fqname.value if fqname.field == REVID else rev_id
     if 1:  # try:
         if item is None:
-            item = flaskg.storage.get_item(**query)
+            item = flaskg.storage.get_item(**fqname.query)
         else:
             if item.fqname:
                 fqname = item.fqname
@@ -255,6 +254,13 @@
     """
 
 
+class FieldNotUniqueError(ValueError):
+    """
+    The Field is not a UFIELD(unique Field).
+    Non unique fields can refer to more than one item.
+    """
+
+
 class Item(object):
     """ Highlevel (not storage) Item, wraps around a storage Revision"""
     # placeholder values for registry entry properties
@@ -288,6 +294,8 @@
         property.
         """
         fqname = split_fqname(name)
+        if fqname.field not in UFIELDS:  # Need a unique key to extract stored item.
+            raise FieldNotUniqueError("field {0} is not in UFIELDS".format(fqname.field))
         rev = get_storage_revision(fqname, itemtype, contenttype, rev_id, item)
         contenttype = rev.meta.get(CONTENTTYPE) or contenttype
         logging.debug("Item {0!r}, got contenttype {1!r} from revision meta".format(name, contenttype))
@@ -495,8 +503,7 @@
     def _save(self, meta, data=None, name=None, action=u'SAVE', contenttype_guessed=None, comment=None,
               overwrite=False, delete=False):
         backend = flaskg.storage
-        query = {self.fqname.field: self.fqname.value, NAMESPACE: self.fqname.namespace}
-        storage_item = backend.get_item(**query)
+        storage_item = backend.get_item(**self.fqname.query)
         try:
             currentrev = storage_item.get_revision(CURRENT)
             rev_id = currentrev.revid
--- a/MoinMoin/templates/common.js	Fri Jul 19 23:15:39 2013 +0530
+++ b/MoinMoin/templates/common.js	Sat Jul 20 02:40:21 2013 +0530
@@ -228,6 +228,21 @@
 
 
 
+// OnMouseOver show the fqname of the item else only show the value/id.
+function togglefqname(){
+    "use strict";
+    var fullname, value;
+    $(".moin-fqname").hover(function () {
+        fullname = $(this).attr('data-fqname');
+        value = $(this).html();
+        $(this).html(fullname);
+    },function () {
+        $(this).html(value);
+    });
+}
+$(document).ready(togglefqname);
+
+
 // Executed when user clicks insert-name button defined in modify.html.
 // When a page with subitems is modified, a subitems sidebar is present. User may
 // position caret in textarea and click button to insert name into textarea.
--- a/MoinMoin/templates/layout.html	Fri Jul 19 23:15:39 2013 +0530
+++ b/MoinMoin/templates/layout.html	Sat Jul 20 02:40:21 2013 +0530
@@ -74,7 +74,7 @@
                 {% for segment_name, segment_path, exists in theme_supp.location_breadcrumbs(item_name) -%}
                     {% if not loop.last -%}
                         <a href="{{ url_for('frontend.show_item', item_name=segment_path) }}" {% if not exists %}class="moin-nonexistent"{% endif %}>
-                            {{ segment_name|shorten_item_name }}
+                            {{ segment_name|shorten_fqname }}
                         </a>
                         <span class="sep">/</span>
                     {% else %}
@@ -82,7 +82,7 @@
                             {{ title_name }}
                         {% else %}
                         <a href="{{ url_for('frontend.show_item', item_name=segment_path) }}" {% if not exists %}class="moin-nonexistent"{% endif %}>
-                            {{ segment_name|shorten_item_name }}
+                            {{ segment_name|shorten_fqname }}
                         </a>
                         {%- endif %}
                     {%- endif %}
@@ -95,17 +95,17 @@
     {% set trail_items = theme_supp.path_breadcrumbs() %}
     {% if trail_items %}
         <div id="moin-pagetrail">
-        {% for wiki_name, item_name, item_href, exists, err in trail_items %}
+        {% for wiki_name, fqname, item_href, exists, err in trail_items %}
             {%- if wiki_name -%}
                 <a href="{{ item_href }}"{{ " " }}
                    title="{{ wiki_name }}"
                    class="{% if err %}moin-badinterwiki{% else %}moin-interwiki{% endif %}">
-                   {{ item_name|shorten_item_name }}
+                   {{ fqname|shorten_fqname }}
                 </a>
             {%- else -%}
-                <a href="{{ url_for('frontend.show_item', item_name=item_name) }}"{{ " " }}
+                <a href="{{ url_for('frontend.show_item', item_name=fqname) }}"{{ " " }}
                    {% if not exists -%}class="moin-nonexistent"{%- endif -%}>
-                   {{ item_name|shorten_item_name }}
+                   {{ fqname|shorten_fqname }}
                 </a>
             {%- endif %}
             {% if not loop.last %}<span class="sep"> &raquo; </span>{% endif %}
--- a/MoinMoin/templates/link_list_no_item_panel.html	Fri Jul 19 23:15:39 2013 +0530
+++ b/MoinMoin/templates/link_list_no_item_panel.html	Sat Jul 20 02:40:21 2013 +0530
@@ -3,11 +3,11 @@
 {% if headline %}
 <h1>{{ headline }}</h1>
 {% endif %}
-{% if item_names %}
-Total: {{ item_names|count }}
+{% if fq_names %}
+Total: {{ fq_names|count }}
 <ul>
-    {% for item_name in item_names|sort %}
-    <li><a href="{{ url_for('frontend.show_item', item_name=item_name) }}">{{ item_name }}</a></li>
+    {% for fq_name in fq_names|sort(attribute='value') %}
+    <li><a class="moin-fqname" href="{{ url_for('frontend.show_item', item_name=fq_name) }}" data-fqname="{{fq_name}}">{{ fq_name.value }}</a></li>
     {% endfor %}
 </ul>
 {% endif %}
--- a/MoinMoin/themes/__init__.py	Fri Jul 19 23:15:39 2013 +0530
+++ b/MoinMoin/themes/__init__.py	Sat Jul 20 02:40:21 2013 +0530
@@ -22,9 +22,9 @@
 
 from MoinMoin.i18n import _, L_, N_
 from MoinMoin import wikiutil, user
-from MoinMoin.constants.keys import USERID, ADDRESS, HOSTNAME
+from MoinMoin.constants.keys import USERID, ADDRESS, HOSTNAME, REVID, ITEMID, NAME_EXACT
 from MoinMoin.search import SearchForm
-from MoinMoin.util.interwiki import split_interwiki, getInterwikiHome, is_local_wiki, is_known_wiki, url_for_item
+from MoinMoin.util.interwiki import split_interwiki, getInterwikiHome, is_local_wiki, is_known_wiki, url_for_item, CompositeName, split_fqname
 from MoinMoin.util.crypto import cache_key
 from MoinMoin.util.forms import make_generator
 from MoinMoin.util.clock import timed
@@ -79,18 +79,31 @@
         self.content_dir = 'ltr'  # XXX
         self.meta_items = []  # list of (name, content) for html head <meta>
 
-    def location_breadcrumbs(self, item_name):
+    def location_breadcrumbs(self, fqname):
         """
         Assemble the location using breadcrumbs (was: title)
 
         :rtype: list
-        :returns: location breadcrumbs items in tuple (segment_name, item_name, exists)
+        :returns: location breadcrumbs items in tuple (segment_name, fq_name, exists)
         """
         breadcrumbs = []
         current_item = ''
+        if not isinstance(fqname, CompositeName):
+            fqname = split_fqname(fqname)
+        if fqname.field != NAME_EXACT:
+            return [(fqname, fqname, bool(self.storage.get_item(**fqname.query)))]
+        namespace = fqname.namespace
+        fq_current = CompositeName(u'', NAME_EXACT, namespace)
+        fq_segment = CompositeName(u'', NAME_EXACT, namespace or '~')
+        breadcrumbs.append((CompositeName(fq_segment, fq_current, False)))
+        item_name = fqname.value
+        if not item_name:
+            return breadcrumbs
         for segment in item_name.split('/'):
             current_item += segment
-            breadcrumbs.append((segment, current_item, self.storage.has_item(current_item)))
+            fq_current = CompositeName(namespace, NAME_EXACT, current_item)
+            fq_segment = CompositeName(namespace, NAME_EXACT, segment)
+            breadcrumbs.append((fq_segment, fq_current, bool(self.storage.get_item(**fq_current.query))))
             current_item += '/'
         return breadcrumbs
 
@@ -106,14 +119,16 @@
         trail = user.get_trail()
         for interwiki_item_name in trail:
             wiki_name, namespace, field, item_name = split_interwiki(interwiki_item_name)
+            fqname = CompositeName(namespace, field, item_name)
             err = not is_known_wiki(wiki_name)
-            href = url_for_item(item_name, namespace=namespace, wiki_name=wiki_name, field=field)
+            href = url_for_item(wiki_name=wiki_name, **fqname.split)
             if is_local_wiki(wiki_name):
-                exists = self.storage.has_item(item_name)
+                exists = bool(self.storage.get_item(**fqname.query))
                 wiki_name = ''  # means "this wiki" for the theme code
             else:
                 exists = True  # we can't detect existance of remote items
-            breadcrumbs.append((wiki_name, item_name, href, exists, err))
+            if item_name:
+                breadcrumbs.append((wiki_name, fqname, href, exists, err))
         return breadcrumbs
 
     def subitem_index(self, item_name):
@@ -333,6 +348,28 @@
     return result
 
 
+def shorten_fqname(fqname, length=25):
+    """
+    Shorten fqname
+
+    Shorten a given long fqname so that it looks good depending upon whether
+    the field is a UUID or not.
+
+    :param fqname: fqname, namedtuple
+    :param length maximum length for shortened fqnames in case the field
+    is not a UUID.
+    :rtype: unicode
+    :returns: shortened fqname.
+    """
+    name = fqname.value
+    if len(name) > length:
+        if fqname.field in [REVID, ITEMID]:
+            name = shorten_id(name)
+        else:
+            name = shorten_item_name(name, length)
+    return name
+
+
 def shorten_item_name(name, length=25):
     """
     Shorten item names
@@ -405,6 +442,7 @@
 
 
 def setup_jinja_env():
+    app.jinja_env.filters['shorten_fqname'] = shorten_fqname
     app.jinja_env.filters['shorten_item_name'] = shorten_item_name
     app.jinja_env.filters['shorten_id'] = shorten_id
     app.jinja_env.filters['contenttype_to_class'] = contenttype_to_class
--- a/MoinMoin/themes/_tests/test_navi_bar.py	Fri Jul 19 23:15:39 2013 +0530
+++ b/MoinMoin/themes/_tests/test_navi_bar.py	Sat Jul 20 02:40:21 2013 +0530
@@ -42,12 +42,12 @@
         test_segment_name_2, test_item_name_2, test_item_exists_2 = test_result[1]
         test_segment_name_3, test_item_name_3, test_item_exists_3 = test_result[2]
 
-        assert test_segment_name_1 == 'some'
-        assert test_item_name_1 == 'some'
-        assert test_segment_name_2 == 'place'
-        assert test_item_name_2 == 'some/place'
-        assert test_segment_name_3 == 'test_item'
-        assert test_item_name_3 == 'some/place/test_item'
+        assert test_segment_name_1.value == 'some'
+        assert test_item_name_1.value == 'some'
+        assert test_segment_name_2.value == 'place'
+        assert test_item_name_2.value == 'some/place'
+        assert test_segment_name_3.value == 'test_item'
+        assert test_item_name_3.value == 'some/place/test_item'
 
     def test_parent_item(self):
         test_result = ThemeSupport.parent_item(self.theme, 'moin/moin-2.0/Item')