[gen] Added the possibility to navigate to tied object number X within a list of tied objects from a Ref that is numbered.
This commit is contained in:
parent
4577855d60
commit
7484fbca93
|
@ -38,9 +38,8 @@ class Ref(Field):
|
|||
# defined. If we are on a forward reference, the "nav" parameter is added to
|
||||
# the URL for allowing to navigate from one object to the next/previous one.
|
||||
pxObjectTitle = Px('''
|
||||
<x var="navInfo='ref.%s.%s:%s.%d.%d' % (zobj.id, field.name, \
|
||||
field.pageName, loop.tied.nb + 1 + startNumber, totalNumber);
|
||||
navInfo=(not field.isBack and not inPickList) and navInfo or '';
|
||||
<x var="navInfo=field.getNavInfo(zobj, loop.tied.nb + 1 + startNumber, \
|
||||
totalNumber, inPickList);
|
||||
pageName=field.isBack and field.back.pageName or 'main'">
|
||||
<x>::tied.o.getSupTitle(navInfo)</x>
|
||||
<x>::tied.o.getListTitle(nav=navInfo, target=target, page=pageName, \
|
||||
|
@ -105,8 +104,8 @@ class Ref(Field):
|
|||
</td>
|
||||
<!-- Edit -->
|
||||
<td if="not field.noForm and tied.o.mayEdit()">
|
||||
<a var="navInfo='ref.%s.%s:%s.%d.%d' % (zobj.id, field.name, \
|
||||
field.pageName, loop.tied.nb + 1 + startNumber, totalNumber);
|
||||
<a var="navInfo=field.getNavInfo(zobj, loop.tied.nb + 1 + startNumber, \
|
||||
totalNumber);
|
||||
linkInPopup=inPopup or (target.target != '_self')"
|
||||
href=":tied.o.getUrl(mode='edit', page='main', nav=navInfo, \
|
||||
inPopup=linkInPopup)"
|
||||
|
@ -149,8 +148,7 @@ class Ref(Field):
|
|||
<input type="hidden" name="action" value="Create"/>
|
||||
<input type="hidden" name="className" value=":tiedClassName"/>
|
||||
<input type="hidden" name="nav"
|
||||
value=":'ref.%s.%s:%s.%d.%d' % (zobj.id, field.name, \
|
||||
field.pageName, 0, totalNumber)"/>
|
||||
value=":field.getNavInfo(zobj, 0, totalNumber)"/>
|
||||
<input type="hidden" name="popup"
|
||||
value=":(inPopup or (target.target != '_self')) and '1' or '0'"/>
|
||||
<input
|
||||
|
@ -436,6 +434,7 @@ class Ref(Field):
|
|||
changeOrder=mayEdit and field.getAttribute(zobj, 'changeOrder');
|
||||
sortConfirm=changeOrder and _('sort_confirm');
|
||||
numbered=field.isNumbered(zobj);
|
||||
gotoNumber=numbered;
|
||||
changeNumber=not inPickList and numbered and changeOrder and \
|
||||
(totalNumber > 3);
|
||||
checkboxesEnabled=field.getAttribute(zobj, 'checkboxes') and \
|
||||
|
@ -1419,6 +1418,25 @@ class Ref(Field):
|
|||
appyObj.say(msg)
|
||||
tool.goto(urlBack)
|
||||
|
||||
def getNavInfo(self, obj, nb, total, inPickList=False):
|
||||
'''Gets the navigation info allowing to navigate from tied object number
|
||||
p_nb to its siblings.'''
|
||||
if self.isBack or inPickList: return ''
|
||||
# If p_nb is None, we want to produce a generic nav info into which we
|
||||
# will insert a specific number afterwards.
|
||||
if nb == None: return 'ref.%s.%s.%%d.%d' % (obj.id, self.name, total)
|
||||
return 'ref.%s.%s.%d.%d' % (obj.id, self.name, nb, total)
|
||||
|
||||
def onGotoTied(self, obj):
|
||||
'''Called when the user wants to go to a tied object whose number is in
|
||||
the request.'''
|
||||
number = int(obj.REQUEST['number']) - 1
|
||||
uids = getattr(obj.aq_base, self.name)
|
||||
tiedUid = uids[number]
|
||||
tied = obj.getTool().getObject(tiedUid)
|
||||
tiedUrl = tied.getUrl(nav=self.getNavInfo(obj, number+1, len(uids)))
|
||||
return obj.goto(tiedUrl)
|
||||
|
||||
def autoref(klass, field):
|
||||
'''klass.field is a Ref to p_klass. This kind of auto-reference can't be
|
||||
declared in the "normal" way, like this:
|
||||
|
|
|
@ -155,6 +155,15 @@ class Search:
|
|||
node['_appy_objs_cbs'] = {};
|
||||
node['_appy_objs_sem'] = '%s';''' % (hookId, default)
|
||||
|
||||
def getSessionKey(self, className, full=True):
|
||||
'''Returns the name of the key, in the session, where results for this
|
||||
search are stored when relevant. If p_full is False, only the suffix
|
||||
of the session key is returned (ie, without the leading
|
||||
"search_").'''
|
||||
res = (self.name == 'allSearch') and className or self.name
|
||||
if not full: return res
|
||||
return 'search_%s' % res
|
||||
|
||||
class UiSearch:
|
||||
'''Instances of this class are generated on-the-fly for manipulating a
|
||||
Search from the User Interface.'''
|
||||
|
|
|
@ -45,7 +45,7 @@ cellDelms = ''.join(cellDelimiters.keys())
|
|||
|
||||
pxDict = {
|
||||
# Page-related elements
|
||||
's': 'pxHeader', 'w': 'pxFields', 'n': 'pxNavigateSiblings', 'b': 'pxButtons',
|
||||
's': 'pxHeader', 'w': 'pxFields', 'n': 'pxNavigationStrip', 'b': 'pxButtons',
|
||||
# Field-related elements
|
||||
'l': 'pxLabel', 'd': 'pxDescription', 'h': 'pxHelp', 'v': 'pxValidation',
|
||||
'r': 'pxRequired', 'c': 'pxChanges'}
|
||||
|
|
|
@ -8,6 +8,7 @@ from appy.gen import utils as gutils
|
|||
from appy.gen.mixins import BaseMixin
|
||||
from appy.gen.wrappers import AbstractWrapper
|
||||
from appy.gen.descriptors import ClassDescriptor
|
||||
from appy.gen.navigate import Siblings
|
||||
from appy.shared import mimeTypes
|
||||
from appy.shared import utils as sutils
|
||||
from appy.shared.data import languages
|
||||
|
@ -375,15 +376,15 @@ class ToolMixin(BaseMixin):
|
|||
# time a page for an element is consulted.
|
||||
if remember:
|
||||
if not searchName:
|
||||
if not search or (search.name == 'allSearch'):
|
||||
if not search:
|
||||
searchName = className
|
||||
else:
|
||||
searchName = search.name
|
||||
searchName = search.getSessionKey(className, full=False)
|
||||
uids = {}
|
||||
i = -1
|
||||
for obj in res.objects:
|
||||
i += 1
|
||||
uids[startNumber+i] = obj.UID()
|
||||
uids[startNumber+i] = obj.id
|
||||
self.REQUEST.SESSION['search_%s' % searchName] = uids
|
||||
return res
|
||||
|
||||
|
@ -776,144 +777,9 @@ class ToolMixin(BaseMixin):
|
|||
if not res: return ''
|
||||
return res
|
||||
|
||||
def getQueryUrl(self, contentType, searchName, startNumber=None):
|
||||
'''This method creates the URL that allows to perform a (non-Ajax)
|
||||
request for getting queried objects from a search named p_searchName
|
||||
on p_contentType.'''
|
||||
baseUrl = self.absolute_url()
|
||||
baseParams = 'className=%s' % contentType
|
||||
rq = self.REQUEST
|
||||
if rq.get('ref'): baseParams += '&ref=%s' % rq.get('ref')
|
||||
# Manage start number
|
||||
if startNumber != None:
|
||||
baseParams += '&startNumber=%s' % startNumber
|
||||
elif rq.has_key('startNumber'):
|
||||
baseParams += '&startNumber=%s' % rq['startNumber']
|
||||
# Manage search name
|
||||
if searchName: baseParams += '&search=%s' % searchName
|
||||
return '%s/query?%s' % (baseUrl, baseParams)
|
||||
|
||||
def computeStartNumberFrom(self, currentNumber, totalNumber, batchSize):
|
||||
'''Returns the number (start at 0) of the first element in a list
|
||||
containing p_currentNumber (starts at 0) whose total number is
|
||||
p_totalNumber and whose batch size is p_batchSize.'''
|
||||
startNumber = 0
|
||||
res = startNumber
|
||||
while (startNumber < totalNumber):
|
||||
if (currentNumber < startNumber + batchSize):
|
||||
return startNumber
|
||||
else:
|
||||
startNumber += batchSize
|
||||
return startNumber
|
||||
|
||||
def getNavigationInfo(self, inPopup=False):
|
||||
'''Extracts navigation information from request/nav and returns an
|
||||
object with the info that a page can use for displaying object
|
||||
navigation.'''
|
||||
res = Object()
|
||||
rq = self.REQUEST
|
||||
t, d1, d2, currentNumber, totalNumber = rq.get('nav').split('.')
|
||||
res.currentNumber = int(currentNumber)
|
||||
res.totalNumber = int(totalNumber)
|
||||
# Compute the label of the search, or ref field
|
||||
if t == 'search':
|
||||
searchName = d2
|
||||
if not searchName:
|
||||
# We search all objects of a given type.
|
||||
label = '%s_plural' % d1.split(':')[0]
|
||||
elif searchName == 'customSearch':
|
||||
# This is an advanced, custom search.
|
||||
label = 'search_results'
|
||||
else:
|
||||
# This is a named, predefined search.
|
||||
label = '%s_search_%s' % (d1.split(':')[0], searchName)
|
||||
res.backText = self.translate(label)
|
||||
# If it is a dynamic search this label does not exist.
|
||||
if ('_' in res.backText): res.backText = ''
|
||||
else:
|
||||
fieldName, pageName = d2.split(':')
|
||||
sourceObj = self.getObject(d1)
|
||||
label = '%s_%s' % (sourceObj.meta_type, fieldName)
|
||||
res.backText = '%s - %s' % (sourceObj.Title(),self.translate(label))
|
||||
newNav = '%s.%s.%s.%%d.%s' % (t, d1, d2, totalNumber)
|
||||
# Among, first, previous, next and last, which one do I need?
|
||||
previousNeeded = False # Previous ?
|
||||
previousIndex = res.currentNumber - 2
|
||||
if (previousIndex > -1) and (res.totalNumber > previousIndex):
|
||||
previousNeeded = True
|
||||
nextNeeded = False # Next ?
|
||||
nextIndex = res.currentNumber
|
||||
if nextIndex < res.totalNumber: nextNeeded = True
|
||||
firstNeeded = False # First ?
|
||||
firstIndex = 0
|
||||
if previousIndex > 0: firstNeeded = True
|
||||
lastNeeded = False # Last ?
|
||||
lastIndex = res.totalNumber - 1
|
||||
if (nextIndex < lastIndex): lastNeeded = True
|
||||
# Get the list of available UIDs surrounding the current object
|
||||
if t == 'ref': # Manage navigation from a reference
|
||||
# In the case of a reference, we retrieve ALL surrounding objects.
|
||||
masterObj = self.getObject(d1)
|
||||
batchSize = masterObj.getAppyType(fieldName).maxPerPage
|
||||
uids = getattr(masterObj, fieldName)
|
||||
# Display the reference widget at the page where the current object
|
||||
# lies.
|
||||
startNumberKey = '%s%s_startNumber' % (masterObj.id, fieldName)
|
||||
startNumber = self.computeStartNumberFrom(res.currentNumber-1,
|
||||
res.totalNumber, batchSize)
|
||||
res.sourceUrl = masterObj.getUrl(**{startNumberKey:startNumber,
|
||||
'page':pageName, 'nav':''})
|
||||
else: # Manage navigation from a search
|
||||
contentType = d1
|
||||
searchName = keySuffix = d2
|
||||
batchSize = self.appy().numberOfResultsPerPage
|
||||
if not searchName: keySuffix = contentType
|
||||
s = rq.SESSION
|
||||
searchKey = 'search_%s' % keySuffix
|
||||
if s.has_key(searchKey): uids = s[searchKey]
|
||||
else: uids = {}
|
||||
# In the case of a search, we retrieve only a part of all
|
||||
# surrounding objects, those that are stored in the session.
|
||||
if (previousNeeded and not uids.has_key(previousIndex)) or \
|
||||
(nextNeeded and not uids.has_key(nextIndex)):
|
||||
# I do not have this UID in session. I will need to
|
||||
# retrigger the query by querying all objects surrounding
|
||||
# this one.
|
||||
newStartNumber = (res.currentNumber-1) - (batchSize / 2)
|
||||
if newStartNumber < 0: newStartNumber = 0
|
||||
self.executeQuery(contentType, searchName=searchName,
|
||||
startNumber=newStartNumber, remember=True)
|
||||
uids = s[searchKey]
|
||||
# For the moment, for first and last, we get them only if we have
|
||||
# them in session.
|
||||
if not uids.has_key(0): firstNeeded = False
|
||||
if not uids.has_key(lastIndex): lastNeeded = False
|
||||
# Compute URL of source object
|
||||
startNumber = self.computeStartNumberFrom(res.currentNumber-1,
|
||||
res.totalNumber, batchSize)
|
||||
res.sourceUrl = self.getQueryUrl(contentType, searchName,
|
||||
startNumber=startNumber)
|
||||
# Compute URLs
|
||||
for urlType in ('previous', 'next', 'first', 'last'):
|
||||
exec 'needIt = %sNeeded' % urlType
|
||||
urlKey = '%sUrl' % urlType
|
||||
setattr(res, urlKey, None)
|
||||
if needIt:
|
||||
exec 'index = %sIndex' % urlType
|
||||
uid = None
|
||||
try:
|
||||
uid = uids[index]
|
||||
# uids can be a list (ref) or a dict (search)
|
||||
except KeyError: pass
|
||||
except IndexError: pass
|
||||
if uid:
|
||||
brain = self.getObject(uid, brain=True)
|
||||
if brain:
|
||||
sibling = brain.getObject()
|
||||
setattr(res, urlKey, sibling.getUrl(\
|
||||
nav=newNav % (index + 1),
|
||||
page=rq.get('page', 'main'), inPopup=inPopup))
|
||||
return res
|
||||
def getNavigationInfo(self, nav, inPopup):
|
||||
'''Produces a Siblings instance from navigation info p_nav.'''
|
||||
return Siblings.get(nav, self, inPopup)
|
||||
|
||||
def getGroupedSearchFields(self, searchInfo):
|
||||
'''This method transforms p_searchInfo.fields, which is a "flat"
|
||||
|
|
|
@ -35,20 +35,17 @@ class BaseMixin:
|
|||
|
||||
def getInitiatorInfo(self, appy=False):
|
||||
'''Gets information about a potential initiator object from the request.
|
||||
Returns a 3-tuple (initiator, pageName, field):
|
||||
Returns a 2-tuple (initiator, field):
|
||||
* initiator is the initiator (Zope or Appy) object;
|
||||
* pageName is the page on the initiator where the origin of the Ref
|
||||
field lies;
|
||||
* field is the Ref instance.
|
||||
'''
|
||||
rq = self.REQUEST
|
||||
if not rq.get('nav', '').startswith('ref.'): return None, None, None
|
||||
if not rq.get('nav', '').startswith('ref.'): return None, None
|
||||
splitted = rq['nav'].split('.')
|
||||
initiator = self.getTool().getObject(splitted[1])
|
||||
fieldName, page = splitted[2].split(':')
|
||||
field = initiator.getAppyType(fieldName)
|
||||
field = initiator.getAppyType(splitted[2])
|
||||
if appy: initiator = initiator.appy()
|
||||
return initiator, page, field
|
||||
return initiator, field
|
||||
|
||||
def createOrUpdate(self, created, values,
|
||||
initiator=None, initiatorField=None):
|
||||
|
@ -203,7 +200,7 @@ class BaseMixin:
|
|||
# create the object.
|
||||
urlParams = {'mode':'edit', 'page':'main', 'nav':'',
|
||||
'inPopup':rq.get('popup') == '1'}
|
||||
initiator, initiatorPage, initiatorField = self.getInitiatorInfo()
|
||||
initiator, initiatorField = self.getInitiatorInfo()
|
||||
if initiator:
|
||||
# The object to create will be linked to an initiator object through
|
||||
# a Ref field. We create here a new navigation string with one more
|
||||
|
@ -387,7 +384,8 @@ class BaseMixin:
|
|||
isNew = self.isTemporary()
|
||||
inPopup = rq.get('popup') == '1'
|
||||
# If this object is created from an initiator, get info about him.
|
||||
initiator, initiatorPage, initiatorField = self.getInitiatorInfo()
|
||||
initiator, initiatorField = self.getInitiatorInfo()
|
||||
initiatorPage = initiatorField and initiatorField.pageName or None
|
||||
# If the user clicked on 'Cancel', go back to the previous page.
|
||||
buttonClicked = rq.get('button')
|
||||
if buttonClicked == 'cancel':
|
||||
|
@ -696,6 +694,10 @@ class BaseMixin:
|
|||
self.REQUEST.set(field.name, '')
|
||||
return self.edit()
|
||||
|
||||
def gotoTied(self):
|
||||
'''Redirects the user to an object tied to this one.'''
|
||||
return self.getAppyType(self.REQUEST['field']).onGotoTied(self)
|
||||
|
||||
def getCreateFolder(self):
|
||||
'''When an object must be created from this one through a Ref field, we
|
||||
must know where to put the newly create object: within this one if it
|
||||
|
|
220
gen/navigate.py
Normal file
220
gen/navigate.py
Normal file
|
@ -0,0 +1,220 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
from appy.px import Px
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class Siblings:
|
||||
'''Abstract class containing information for navigating from one object to
|
||||
its siblings.'''
|
||||
siblingTypes = ('previous', 'next', 'first', 'last')
|
||||
|
||||
# Buttons for going to siblings of the current object.
|
||||
pxNavigate = Px('''
|
||||
<!-- Go to the source URL (search or referred object) -->
|
||||
<a if="not inPopup" href=":self.sourceUrl"><img
|
||||
var="goBack='%s - %s' % (self.getBackText(), _('goto_source'))"
|
||||
src=":url('gotoSource')" title=":goBack"/></a>
|
||||
|
||||
<!-- Go to the first or previous page -->
|
||||
<a if="self.firstUrl" href=":self.firstUrl"><img title=":_('goto_first')"
|
||||
src=":url('arrowsLeft')"/></a><a
|
||||
if="self.previousUrl" href=":self.previousUrl"><img
|
||||
title=":_('goto_previous')" src=":url('arrowLeft')"/></a>
|
||||
|
||||
<!-- Explain which element is currently shown -->
|
||||
<span class="discreet">
|
||||
<x>:self.number</x> <b>//</b>
|
||||
<x>:self.total</x> </span>
|
||||
|
||||
<!-- Go to the next or last page -->
|
||||
<a if="self.nextUrl" href=":self.nextUrl"><img title=":_('goto_next')"
|
||||
src=":url('arrowRight')"/></a><a
|
||||
if="self.lastUrl" href=":self.lastUrl"><img title=":_('goto_last')"
|
||||
src=":url('arrowsRight')"/></a>
|
||||
|
||||
<!-- Go to the element number... -->
|
||||
<x if="self.showGotoNumber()"
|
||||
var2="field=self.field; sourceUrl=self.sourceObject.absolute_url();
|
||||
totalNumber=self.total"><br/><x>:obj.pxGotoNumber</x></x>''')
|
||||
|
||||
@staticmethod
|
||||
def get(nav, tool, inPopup):
|
||||
'''This method analyses the navigation info p_nav and returns the
|
||||
corresponding concrete Siblings instance.'''
|
||||
elems = nav.split('.')
|
||||
params = elems[1:]
|
||||
if elems[0] == 'ref': return RefSiblings(tool, inPopup, *params)
|
||||
elif elems[0] == 'search': return SearchSiblings(tool, inPopup, *params)
|
||||
|
||||
def computeStartNumber(self):
|
||||
'''Returns the start number of the batch where the current element
|
||||
lies.'''
|
||||
# First index starts at O, so we calibrate self.number
|
||||
number = self.number - 1
|
||||
batchSize = self.getBatchSize()
|
||||
res = 0
|
||||
while (res < self.total):
|
||||
if (number < res + batchSize): return res
|
||||
res += batchSize
|
||||
return res
|
||||
|
||||
def __init__(self, tool, inPopup, number, total):
|
||||
self.tool = tool
|
||||
self.request = tool.REQUEST
|
||||
# Are we in a popup window or not?
|
||||
self.inPopup = inPopup
|
||||
# The number of the current element
|
||||
self.number = int(number)
|
||||
# The total number of siblings
|
||||
self.total = int(total)
|
||||
# Do I need to navigate to first, previous, next and/or last sibling ?
|
||||
self.previousNeeded = False # Previous ?
|
||||
self.previousIndex = self.number - 2
|
||||
if (self.previousIndex > -1) and (self.total > self.previousIndex):
|
||||
self.previousNeeded = True
|
||||
self.nextNeeded = False # Next ?
|
||||
self.nextIndex = self.number
|
||||
if self.nextIndex < self.total: self.nextNeeded = True
|
||||
self.firstNeeded = False # First ?
|
||||
self.firstIndex = 0
|
||||
if self.previousIndex > 0: self.firstNeeded = True
|
||||
self.lastNeeded = False # Last ?
|
||||
self.lastIndex = self.total - 1
|
||||
if (self.nextIndex < self.lastIndex): self.lastNeeded = True
|
||||
# Compute the UIDs of the siblings of the current object
|
||||
self.siblings = self.getSiblings()
|
||||
# Compute back URL and URLs to siblings
|
||||
self.sourceUrl = self.getSourceUrl()
|
||||
siblingNav = self.getNavKey()
|
||||
siblingPage = self.request.get('page', 'main')
|
||||
for urlType in self.siblingTypes:
|
||||
exec 'needIt = self.%sNeeded' % urlType
|
||||
urlKey = '%sUrl' % urlType
|
||||
setattr(self, urlKey, None)
|
||||
if not needIt: continue
|
||||
exec 'index = self.%sIndex' % urlType
|
||||
uid = None
|
||||
try:
|
||||
# self.siblings can be a list (ref) or a dict (search)
|
||||
uid = self.siblings[index]
|
||||
except KeyError: continue
|
||||
except IndexError: continue
|
||||
if not uid: continue
|
||||
sibling = self.tool.getObject(uid)
|
||||
if not sibling: continue
|
||||
setattr(self, urlKey, sibling.getUrl(nav=siblingNav % (index + 1),
|
||||
page=siblingPage, inPopup=inPopup))
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class RefSiblings(Siblings):
|
||||
'''Class containing information for navigating from one object to another
|
||||
within tied objects from a Ref field.'''
|
||||
prefix = 'ref'
|
||||
|
||||
def __init__(self, tool, inPopup, sourceUid, fieldName, number, total):
|
||||
# The source object of the Ref field
|
||||
self.sourceObject = tool.getObject(sourceUid)
|
||||
# The Ref field in itself
|
||||
self.field = self.sourceObject.getAppyType(fieldName)
|
||||
# Call the base constructor
|
||||
Siblings.__init__(self, tool, inPopup, number, total)
|
||||
|
||||
def getNavKey(self):
|
||||
'''Returns the general navigation key for navigating to another
|
||||
sibling.'''
|
||||
return self.field.getNavInfo(self.sourceObject, None, self.total)
|
||||
|
||||
def getBackText(self):
|
||||
'''Computes the text to display when the user want to navigate back to
|
||||
the list of tied objects.'''
|
||||
_ = self.tool.translate
|
||||
return '%s - %s' % (self.sourceObject.Title(), _(self.field.labelId))
|
||||
|
||||
def getBatchSize(self):
|
||||
'''Returns the maximum number of shown objects at a time for this
|
||||
ref.'''
|
||||
return self.field.maxPerPage
|
||||
|
||||
def getSiblings(self):
|
||||
'''Returns the siblings of the current object.'''
|
||||
return getattr(self.sourceObject, self.field.name, ())
|
||||
|
||||
def getSourceUrl(self):
|
||||
'''Computes the URL allowing to go back to self.sourceObject's page
|
||||
where self.field lies and shows the list of tied objects, at the
|
||||
batch where the current object lies.'''
|
||||
# Allow to go back to the batch where the current object lies
|
||||
field = self.field
|
||||
startNumberKey = '%s_%s_objs_startNumber' % \
|
||||
(self.sourceObject.id,field.name)
|
||||
startNumber = str(self.computeStartNumber())
|
||||
return self.sourceObject.getUrl(**{startNumberKey:startNumber,
|
||||
'page':field.pageName, 'nav':''})
|
||||
|
||||
def showGotoNumber(self):
|
||||
'''Show "goto number" if the Ref field is numbered.'''
|
||||
return self.field.isNumbered(self.sourceObject)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class SearchSiblings(Siblings):
|
||||
'''Class containing information for navigating from one object to another
|
||||
within results of a search.'''
|
||||
prefix = 'search'
|
||||
|
||||
def __init__(self, tool, inPopup, className, searchName, number, total):
|
||||
# The class determining the type of searched objects
|
||||
self.className = className
|
||||
# Get the search object
|
||||
self.searchName = searchName
|
||||
self.uiSearch = tool.getSearch(className, searchName, ui=True)
|
||||
self.search = self.uiSearch.search
|
||||
Siblings.__init__(self, tool, inPopup, number, total)
|
||||
|
||||
def getNavKey(self):
|
||||
'''Returns the general navigation key for navigating to another
|
||||
sibling.'''
|
||||
return 'search.%s.%s.%%d.%d' % (self.className, self.searchName,
|
||||
self.total)
|
||||
|
||||
def getBackText(self):
|
||||
'''Computes the text to display when the user want to navigate back to
|
||||
the list of searched objects.'''
|
||||
return self.uiSearch.translated
|
||||
|
||||
def getBatchSize(self):
|
||||
'''Returns the maximum number of shown objects at a time for this
|
||||
search.'''
|
||||
return self.search.maxPerPage
|
||||
|
||||
def getSiblings(self):
|
||||
'''Returns the siblings of the current object. For performance reasons,
|
||||
only a part of the is stored, in the session object.'''
|
||||
session = self.request.SESSION
|
||||
searchKey = self.search.getSessionKey(self.className)
|
||||
if session.has_key(searchKey): res = session[searchKey]
|
||||
else: res = {}
|
||||
if (self.previousNeeded and not res.has_key(self.previousIndex)) or \
|
||||
(self.nextNeeded and not res.has_key(self.nextIndex)):
|
||||
# The needed sibling UID is not in session. We will need to
|
||||
# retrigger the query by querying all objects surrounding this one.
|
||||
newStartNumber = (self.number-1) - (self.search.maxPerPage / 2)
|
||||
if newStartNumber < 0: newStartNumber = 0
|
||||
self.tool.executeQuery(self.className, search=self.search,
|
||||
startNumber=newStartNumber, remember=True)
|
||||
res = session[searchKey]
|
||||
# For the moment, for first and last, we get them only if we have them
|
||||
# in session.
|
||||
if not res.has_key(0): self.firstNeeded = False
|
||||
if not res.has_key(self.lastIndex): self.lastNeeded = False
|
||||
return res
|
||||
|
||||
def getSourceUrl(self):
|
||||
'''Computes the (non-Ajax) URL allowing to go back to the search
|
||||
results, at the batch where the current object lies.'''
|
||||
params = 'className=%s&search=%s&startNumber=%d' % \
|
||||
(self.className, self.searchName, self.computeStartNumber())
|
||||
ref = self.request.get('ref', None)
|
||||
if ref: params += '&ref=%s' % ref
|
||||
return '%s/query?%s' % (self.tool.absolute_url(), params)
|
||||
|
||||
def showGotoNumber(self): return
|
||||
# ------------------------------------------------------------------------------
|
|
@ -275,6 +275,10 @@ msgstr ""
|
|||
msgid "goto_source"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Go to number"
|
||||
msgid "goto_number"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Whatever"
|
||||
msgid "whatever"
|
||||
msgstr ""
|
||||
|
|
|
@ -275,6 +275,10 @@ msgstr ""
|
|||
msgid "goto_source"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Go to number"
|
||||
msgid "goto_number"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Whatever"
|
||||
msgid "whatever"
|
||||
msgstr ""
|
||||
|
|
|
@ -275,6 +275,10 @@ msgstr "Gehen Sie zum Ende"
|
|||
msgid "goto_source"
|
||||
msgstr "Zurück"
|
||||
|
||||
#. Default: "Go to number"
|
||||
msgid "goto_number"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Whatever"
|
||||
msgid "whatever"
|
||||
msgstr "Ohne Bedeutung"
|
||||
|
|
|
@ -276,6 +276,10 @@ msgstr "Go to end"
|
|||
msgid "goto_source"
|
||||
msgstr "Go back"
|
||||
|
||||
#. Default: "Go to number"
|
||||
msgid "goto_number"
|
||||
msgstr "Go to number"
|
||||
|
||||
#. Default: "Whatever"
|
||||
msgid "whatever"
|
||||
msgstr "Whatever"
|
||||
|
|
|
@ -275,6 +275,10 @@ msgstr "Ir al final"
|
|||
msgid "goto_source"
|
||||
msgstr "Volver"
|
||||
|
||||
#. Default: "Go to number"
|
||||
msgid "goto_number"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Whatever"
|
||||
msgid "whatever"
|
||||
msgstr "No importa"
|
||||
|
|
|
@ -276,6 +276,10 @@ msgstr "Aller à la fin"
|
|||
msgid "goto_source"
|
||||
msgstr "Retour"
|
||||
|
||||
#. Default: "Go to number"
|
||||
msgid "goto_number"
|
||||
msgstr "Aller au numéro"
|
||||
|
||||
#. Default: "Whatever"
|
||||
msgid "whatever"
|
||||
msgstr "Peu importe"
|
||||
|
|
|
@ -275,6 +275,10 @@ msgstr "Andare alla fine"
|
|||
msgid "goto_source"
|
||||
msgstr "Andare indietro"
|
||||
|
||||
#. Default: "Go to number"
|
||||
msgid "goto_number"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Whatever"
|
||||
msgid "whatever"
|
||||
msgstr "Qualunque"
|
||||
|
|
|
@ -275,6 +275,10 @@ msgstr "Ga naar het einde"
|
|||
msgid "goto_source"
|
||||
msgstr "Terug"
|
||||
|
||||
#. Default: "Go to number"
|
||||
msgid "goto_number"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Whatever"
|
||||
msgid "whatever"
|
||||
msgstr "Maakt niets uit"
|
||||
|
|
|
@ -282,6 +282,19 @@ function askRefField(hookId, objectUrl, innerRef, startNumber, action,
|
|||
evalInnerScripts);
|
||||
}
|
||||
|
||||
function gotoTied(objectUrl, field, numberWidget, total) {
|
||||
// Check that the number is correct
|
||||
try {
|
||||
var number = parseInt(numberWidget.value);
|
||||
if (!isNaN(number)) {
|
||||
if ((number >= 1) && (number <= total)) {
|
||||
goto(objectUrl + '/gotoTied?field=' + field + '&number=' + number);
|
||||
}
|
||||
else numberWidget.style.background = wrongTextInput; }
|
||||
else numberWidget.style.background = wrongTextInput; }
|
||||
catch (err) { numberWidget.style.background = wrongTextInput; }
|
||||
}
|
||||
|
||||
function askField(hookId, objectUrl, layoutType, customParams, showChanges,
|
||||
masterValues, requestValue, error, className){
|
||||
// Sends an Ajax request for getting the content of any field.
|
||||
|
|
BIN
gen/ui/gotoNumber.png
Normal file
BIN
gen/ui/gotoNumber.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 235 B |
|
@ -47,51 +47,47 @@ class ToolWrapper(AbstractWrapper):
|
|||
# Buttons for navigating among a list of objects (from a Ref field or a
|
||||
# query): next,back,first,last...
|
||||
pxNavigate = Px('''
|
||||
<div if="totalNumber > batchSize" align=":dright">
|
||||
<table class="listNavigate"
|
||||
var="mustSortAndFilter=ajaxHookId == 'queryResult';
|
||||
<div if="totalNumber > batchSize" align=":dright"
|
||||
var2="mustSortAndFilter=ajaxHookId == 'queryResult';
|
||||
sortAndFilter=mustSortAndFilter and \
|
||||
',%s,%s,%s' % (q(sortKey),q(sortOrder),q(filterKey)) or ''">
|
||||
<tr valign="bottom">
|
||||
|
||||
<!-- Go to the first page -->
|
||||
<td if="(startNumber != 0) and (startNumber != batchSize)"><img
|
||||
class="clickable" src=":url('arrowsLeft')"
|
||||
title=":_('goto_first')"
|
||||
onClick=":navBaseCall.replace('**v**', '0'+sortAndFilter)"/></td>
|
||||
<img if="(startNumber != 0) and (startNumber != batchSize)"
|
||||
class="clickable" src=":url('arrowsLeft')" title=":_('goto_first')"
|
||||
onClick=":navBaseCall.replace('**v**', '0'+sortAndFilter)"/>
|
||||
|
||||
<!-- Go to the previous page -->
|
||||
<td var="sNumber=startNumber - batchSize" if="startNumber != 0"><img
|
||||
class="clickable" src=":url('arrowLeft')"
|
||||
title=":_('goto_previous')"
|
||||
onClick=":navBaseCall.replace('**v**', \
|
||||
str(sNumber)+sortAndFilter)"/></td>
|
||||
<img var="sNumber=startNumber - batchSize" if="startNumber != 0"
|
||||
class="clickable" src=":url('arrowLeft')" title=":_('goto_previous')"
|
||||
onClick=":navBaseCall.replace('**v**', str(sNumber)+sortAndFilter)"/>
|
||||
|
||||
<!-- Explain which elements are currently shown -->
|
||||
<td class="discreet">
|
||||
<span class="discreet">
|
||||
<x>:startNumber + 1</x> <img src=":url('to')"/>
|
||||
<x>:startNumber + batchNumber</x> <b>//</b>
|
||||
<x>:totalNumber</x> </td>
|
||||
<x>:totalNumber</x>
|
||||
</span>
|
||||
|
||||
<!-- Go to the next page -->
|
||||
<td var="sNumber=startNumber + batchSize"
|
||||
if="sNumber < totalNumber"><img class="clickable"
|
||||
src=":url('arrowRight')" title=":_('goto_next')"
|
||||
onClick=":navBaseCall.replace('**v**', \
|
||||
str(sNumber)+sortAndFilter)"/></td>
|
||||
<img var="sNumber=startNumber + batchSize" if="sNumber < totalNumber"
|
||||
class="clickable" src=":url('arrowRight')" title=":_('goto_next')"
|
||||
onClick=":navBaseCall.replace('**v**', str(sNumber)+sortAndFilter)"/>
|
||||
|
||||
<!-- Go to the last page -->
|
||||
<td var="lastPageIsIncomplete=totalNumber % batchSize;
|
||||
<img var="lastPageIsIncomplete=totalNumber % batchSize;
|
||||
nbOfCompletePages=totalNumber/batchSize;
|
||||
nbOfCountedPages=lastPageIsIncomplete and \
|
||||
nbOfCompletePages or nbOfCompletePages-1;
|
||||
sNumber= nbOfCountedPages * batchSize"
|
||||
if="(startNumber != sNumber) and \
|
||||
(startNumber != sNumber-batchSize)"><img class="clickable"
|
||||
(startNumber != sNumber-batchSize)" class="clickable"
|
||||
src=":url('arrowsRight')" title=":_('goto_last')"
|
||||
onClick=":navBaseCall.replace('**v**', \
|
||||
str(sNumber)+sortAndFilter)"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
onClick=":navBaseCall.replace('**v**', str(sNumber)+sortAndFilter)"/>
|
||||
|
||||
<!-- Go to the element number... -->
|
||||
<x var="gotoNumber=gotoNumber|False" if="gotoNumber"
|
||||
var2="sourceUrl=obj.url">:obj.pxGotoNumber</x>
|
||||
</div>''')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
@ -508,7 +504,7 @@ class ToolWrapper(AbstractWrapper):
|
|||
<span class="discreet">:uiSearch.translatedDescr</span><br/>
|
||||
</td>
|
||||
<!-- (Top) navigation -->
|
||||
<td align=":dright" width="150px"><x>:tool.pxNavigate</x></td>
|
||||
<td align=":dright" width="150px">:tool.pxNavigate</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
|
|
@ -19,34 +19,19 @@ class AbstractWrapper(object):
|
|||
'''Any real Appy-managed Zope object has a companion object that is an
|
||||
instance of this class.'''
|
||||
|
||||
# Buttons for going to next/previous objects if this one is among bunch of
|
||||
# referenced or searched objects. currentNumber starts with 1.
|
||||
pxNavigateSiblings = Px('''
|
||||
<div if="req.get('nav', None)" var2="ni=ztool.getNavigationInfo(inPopup)">
|
||||
<!-- Go to the source URL (search or referred object) -->
|
||||
<a if="not inPopup and ni.sourceUrl" href=":ni.sourceUrl"><img
|
||||
var="gotoSource=_('goto_source');
|
||||
goBack=ni.backText and ('%s - %s' % (ni.backText, gotoSource)) \
|
||||
or gotoSource"
|
||||
src=":url('gotoSource')" title=":goBack"/></a>
|
||||
|
||||
<!-- Go to the first or previous page -->
|
||||
<a if="ni.firstUrl" href=":ni.firstUrl"><img title=":_('goto_first')"
|
||||
src=":url('arrowsLeft')"/></a><a
|
||||
if="ni.previousUrl" href=":ni.previousUrl"><img
|
||||
title=":_('goto_previous')" src=":url('arrowLeft')"/></a>
|
||||
|
||||
<!-- Explain which element is currently shown -->
|
||||
<span class="discreet">
|
||||
<x>:ni.currentNumber</x> <b>//</b>
|
||||
<x>:ni.totalNumber</x> </span>
|
||||
|
||||
<!-- Go to the next or last page -->
|
||||
<a if="ni.nextUrl" href=":ni.nextUrl"><img title=":_('goto_next')"
|
||||
src=":url('arrowRight')"/></a><a
|
||||
if="ni.lastUrl" href=":ni.lastUrl"><img title=":_('goto_last')"
|
||||
src=":url('arrowsRight')"/></a>
|
||||
</div>''')
|
||||
# Input field for going to element number ...
|
||||
pxGotoNumber = Px('''
|
||||
<x var2="label=_('goto_number');
|
||||
gotoName='%s_%s_goto' % (obj.id, field.name)">
|
||||
<span class="discreet" style="padding-left: 5px">:label</span>
|
||||
<input type="text" size=":(len(str(totalNumber))-1) or 1"
|
||||
onclick="this.select()"
|
||||
onkeydown=":'if (event.keyCode==13) document.getElementById' \
|
||||
'(%s).click()' % q(gotoName)"/><img
|
||||
id=":gotoName" name=":gotoName"
|
||||
class="clickable" src=":url('gotoNumber')" title=":label"
|
||||
onClick=":'gotoTied(%s,%s,this.previousSibling,%s)' % \
|
||||
(q(sourceUrl), q(field.name), totalNumber)"/></x>''')
|
||||
|
||||
pxNavigationStrip = Px('''
|
||||
<table width="100%">
|
||||
|
@ -66,7 +51,9 @@ class AbstractWrapper(object):
|
|||
<x if="sub">::sub</x>
|
||||
</td>
|
||||
<!-- Object navigation -->
|
||||
<td align=":dright" width="150px">:obj.pxNavigateSiblings</td>
|
||||
<td var="nav=req.get('nav', None)" if="nav"
|
||||
var2="self=ztool.getNavigationInfo(nav, inPopup)" align=":dright"
|
||||
width="150px">:self.pxNavigate</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- Object phases and pages -->
|
||||
|
|
Loading…
Reference in a new issue