Hi,
We have several places where we need to display a document reference; for ex:
- rename page
- copy page
etc
One solution is to not display the reference and break it into discrete parts, for ex:
Source wiki:
<wiki name>
Source space:
<space name>
Source page:
<page name>
However there are drawbacks:
1) it takes space
2) when we introduced nested spaces it's going to be even more complex to do so...
I was wondering if instead we couldn't go another way which is to have a visual way to display a reference.
Two ideas:
http://tinycoke.com/_6rGYY9JXqpvD6/screen_shot_2011-02-10_at_12.29.24_pm.png
WDYT?
Any more ideas?
Thanks
-Vincent
Hi devs,
While designing the xwiki-action and xwiki-url modules I've realized that we need a general way to represent an xwiki resource and an action on that resource.
This will allow every resource in XWiki to be addressable and offer a generic action mechanism.
What is an xwiki resource?
=====================
Examples:
* an Entity (Document, XObject, XProperty, Attachment, etc)
* A Page (i.e. an Entity rendered with a skin)
* a resource file on the filesystem (image, css, js, icon, etc)
* something on which a REST call will operate (which may or may not be an Entity)
Note that a resource has a type, a reference and optional generic parameters. It's very much similar to a URI in the JDK but represented differently.
What is an action done on a resource?
=====================
Examples:
* For an Entity: view it in XHTML (or in any other syntax)
* For an Entity: view it with the skin applied, in XHTML (or in any other syntax)
* Generate a URL for it
* Create a link to it
* For an Entity: Create it, modify it, delete it
Where would it be used:
=====================
Examples:
* To transform any XWiki URL into a resource object. Any XWiki URL would be transformed internally into a resource. Note: The current XWikiURL class located in the xwiki-url module would disappear
* In the Rendering to represent the reference of a link or an image. Ex: [[label>>reference]]. We currently have a ResourceReference class that would be removed and replaced by the new generic resource class.
* More generically Resource and Resource Actions will be used at entry points of XWiki and internally XWiki will only manipulate Resources.
String representation:
=====================
<resource type>:<action>:<resource reference>
Examples:
=====================
* view a document with skin applied
page:view:wiki:space1.space2.page
<==> ViewAction
* view a document
entity:view:wiki:space1.space2.page
params: context=new
<==> No equiv or {{include document="wiki:space1.space2.page" context="new"/}}
ex: link to attachment
entity:link:wiki:space.page@my.png
<==> [[attach:wiki:space.page@my.png]]
ex: link to a document
entity:link:wiki:space1.space2.page
<==> [[doc:wiki:space1.space2.page]]
ex: view icon
icon:view:accept
<==> image:icon:accept
ex: view image stored in document
entity:view:wiki:space.page@my.png
<==> image:doc:wiki:space.page@my.png
ex: view image located at external URL
url:view:http://server/my.png
<==> image:url:http://server/my.png
API
===
* ResourceReference
* ResourceAction (extends ResourceReference by adding an action field)
* ResourceReferenceSerializer
* ResourceReferenceResolver
* ResourceExecutor(ResourceReference). Hint = <executor type>/<resource type>/<resource action>
Example: View "entity:view:wiki:space.page@my.png" in XHTML
Algorithm:
- lookup ResourceExecutor (hint = "xhtml/entity/view")
- cast ref = ResourceReference.getReference() to EntityReference
- if ref.type == ATTACHMENT
- get attachment URL
=> <img src="..../my.png"/>
Example: Generate URL for "entity:view:wiki:space.page@my.png"
Algorithm:
- lookup ResourceExecutor (hint = "url/entity/view")
- cast ref = ResourceReference.getReference() to EntityReference
=> http://wiki/xwiki/bin/view/space/page/my.png
OR simply:
ResourceExecutor(ResourceAction). Hint = <executor type>
ex: "xhtml", "url", etc.
And it's up to the executor to have sub components for the various actions, resource types....
Conclusion
===========
The API is not completely finished and I'd need to explore it (ie start implementing it) to refine it.
Right now I'd just like to discuss whether you see the idea as interesting and whether I should start spending some time working on it.
I'll post refinements as I progress.
Thanks
-Vincent
Hi devs,
I committed XWIKI-5938 but I think we should discuss the default source
of the dashboard macro, namely where does it pick the gadget objects to
display on the dashboard, e.g. in the case of a dashboard macro call in
an included document.
It is very easy to add a "source" parameter to the dashboard macro,
which would tell the dashboard where should it read the objects from
(and render in the context of the current document), but now the
question is what should {{dashboard /}} without parameters do?
1/ read the objects from the current document, which means, in the case
of an include, it will depend on the context of the include. Note that,
if an include is done in a new context, then all the gadgets on that
dashboard will be also relative to the included document (e.g. if they
use $doc, the $doc will be the included document, not the including doc).
2/ read the objects from the closest MetaData.SOURCE, which means the
document from which the content that contains the {{dashboard /}} call
comes from. E.g. in the case of an include, the included document.
WDYT?
There are a lot of possible situations to discuss, I will just present a
few usecases below, to be taken into account when analyzing:
A. The current Main.Dashboard is included in all the WebHomes by
default, but its gadgets refer to the current document (the _including_
document)
B. Users want to be able to easily customize their WebHomes, without
affecting other people (not change the wiki general dashboard, but only
their space dashboard)
C. There is a dashboard somewhere on the wiki, Main.CoolDashboard, and
users want to be able to include that dashboard as a copy, without
having to re-create the dashboard objects on their including page
D. User dashboards: at one point we will want each user to have their
own dashboard on their profile page, which is rendered by a sheet.
Ideally we should have the same user sheet for all users.
E. Also, the user dashboard should be include-able on the Main.WebHome
of a logged in user (a "Home").
F. Since the include doesn't make too much sense for a dashboard
document (since the dashboard document is only a call to a {{dashboard
/}} macro), users will replace its usage with {{dashboard /}} macro
calls, with a source parameter to pick gadget objects from another
document ('reuse' a dashboard definition).
If you can think of more cases that could involve include issues, please
fill them in.
Thanks,
Anca
Here is a proposal to reduce all get...Block to 2 methods which
support matching any Block.
interface Block
{
enum TraversalStrategy
{
/** Search in parents **/
PARENT,
/** Search in children **/
CHILD,
/** Search recursively in children (and children of children etc...) **/
CHILDRECURSE,
/** Search in previous siblings **/
PREVIOUS,
/** Search recursively in previous siblings (and parent previous
sibling etc...) **/
PREVIOUSRECURSE,
/** Search in next siblings**/
NEXT,
/** Search recursively in next siblings (and children etc...)**/
NEXTRECURSE
}
[...]
List<Block> getBlocks(BlockMatcher matcher, TraversalStrategy
traversalStrategy, boolean recurse);
Block getFirstBlock(BlockMatcher matcher, TraversalStrategy
traversalStrategy, boolean recurse);
}
interface BlockMatcher
{
boolean match(Block block);
}
et on refactor tous les autres get*Block basé sur ceux la avec des
BlockMatcher prédéfinis (ClassBlockMatcher, MetaDataBlockMatcher,
etc...).
Note: the main use case for this change is to support MetaData search.
WDYT (especially on the vocabulary like "TraversalStrategy") ?
--
Thomas Mortagne
On Tue, Feb 15, 2011 at 2:55 PM, lucaa <platform-notifications(a)xwiki.org>wrote:
> Author: lucaa
> Date: 2011-02-15 14:55:47 +0100 (Tue, 15 Feb 2011)
> New Revision: 34685
>
> Added:
>
> platform/web/trunk/standard/src/main/webapp/resources/uicomponents/dashboard/dashboard.js
> Modified:
>
> platform/core/trunk/xwiki-core/src/main/resources/ApplicationResources.properties
>
> platform/core/trunk/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-dashboard/src/main/java/org/xwiki/rendering/internal/macro/dashboard/ColumnsDashboardRenderer.java
>
> platform/core/trunk/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-dashboard/src/main/java/org/xwiki/rendering/internal/macro/dashboard/DashboardMacro.java
>
> platform/core/trunk/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-dashboard/src/test/java/org/xwiki/rendering/RenderingTests.java
>
> platform/core/trunk/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-dashboard/src/test/resources/macrodashboard1.test
>
> platform/web/trunk/standard/src/main/webapp/resources/uicomponents/container/columns.css
>
> platform/web/trunk/standard/src/main/webapp/resources/uicomponents/dashboard/dashboard.css
> Log:
> XWIKI-5939: Allow to edit the positions of gadgets on a dashboard with drag
> & drop
> XWIKI-5941: Allow to remove a gadget from a dashboard
> * Added dashboard visual editor in edit inline mode, with drag & drop for
> gadgets positions and remove button
> Fixed the columns layout to use columns spacing on both sides of columns
> (instead of only the right side), and to handle empty columns
>
>
>
>
[snip]
> Added:
> platform/web/trunk/standard/src/main/webapp/resources/uicomponents/dashboard/dashboard.js
> ===================================================================
> ---
> platform/web/trunk/standard/src/main/webapp/resources/uicomponents/dashboard/dashboard.js
> (rev 0)
> +++
> platform/web/trunk/standard/src/main/webapp/resources/uicomponents/dashboard/dashboard.js
> 2011-02-15 13:55:47 UTC (rev 34685)
> @@ -0,0 +1,280 @@
> +var XWiki = (function (XWiki) {
> +// Start XWiki augmentation.
> +XWiki.Dashboard = Class.create( {
> + initialize : function(element) {
> + //the class of the gadget objects
> + this.gadgetsClass = "XWiki.GadgetClass";
> + // the source of the dashboard, the document where the objects are
> stored and which needs to be updated
> + // TODO: put me here, but it's a bit tough because we don't know if as
> URL or as fullname or space, page, wiki vars
> + // flag to know if the dashboard was edited or not, to know if
> requests should be sent on save or not
> + this.edited = false;
> + // the list of removed gadgets, to really remove when the inline form
> is submitted
> + this.removed = new Array();
> + this.element = element;
> + // add an extra class to this element, to know that it's editing, for
> css that needs to be special on edit
> + this.element.addClassName("dashboard-edit");
> + // find out all the gadget-containers in element and add them ids
> + this.containers = element.select(".gadget-container");
> + this.createDragAndDrops();
> + this.addGadgetsHandlers();
> +
> + // add save listener, to save the dashboard before submit of the form
> + document.observe("xwiki:actions:save",
> this.saveChanges.bindAsEventListener(this));
> + },
> +
> + /**
> + * @param container the container to get the id of
> + * @return the original container id, parsed from the HTML id of this
> container.
> + * This is used to be able to have unique ids of containers in
> the HTML, and at the same time be able to match
> + * containers ids to the model.
> + * FIXME: this will cause issues with multiple dashboards, add the name
> of the dashboard to the ids
> + */
> + _getContainerId : function (container) {
> + // the gadget container id is of the form
> gadgetcontainer_<containerId>, so parse it back
> + return container.readAttribute('id').substring(16);
> + },
> +
> + /**
> + * @param gadget the gadget to get the id of
> + * @return the original gadget id, parsed from the HTML id of this
> gadget.
> + * This is used to be able to have unique ids of gadgets in the
> HTML, and at the same time be able to match
> + * gadgets ids to the model.
> + * FIXME: this will cause issues with multiple dashboards, add the name
> of the dashboard to the ids
> + */
> + _getGadgetId : function(gadget) {
> + // gadget ids are of the form gadget_<id>
> + return gadget.readAttribute('id').substring(7);
> + },
> +
> + /*
> + * Drag & drop decorators functions, to display nice placeholders when
> dragging & dropping.
> + */
> + _insertPlaceholder: function (container) {
> + if ( container.down('.gadget') ||
> container.down('.gadget-placeholder')) {
> + return;
> + }
> + var placeholder = new Element('div', {'class' : 'gadget-placeholder'})
> + .update('$msg.get("dashboard.gadget.actions.drop")');
> + container.insert(placeholder);
> + },
> +
> + _removePlaceholder: function (container) {
> + var placeholders = container.select('.gadget-placeholder');
> + placeholders.each(function (el) {
> + el.remove();
> + });
> + },
> +
> + _doOnStartDrag: function() {
> + this.containers.each(this._insertPlaceholder);
> + },
> + _doOnEndDrag: function() {
> + this.containers.each(this._removePlaceholder);
> + },
> +
> + /**
> + * Creates drag and drops for the gadgets inside the gadget containers.
> + */
> + createDragAndDrops : function() {
> + // put all the container ids in a list, to be able to pass it to the
> sortables
> + var containerIds = new Array();
> + this.containers.each(function(container) {
> + containerIds.push(container.readAttribute('id'));
> + });
> +
> + // create a sortable for each gadget container
> + containerIds.each(function(containerId) {
> + Sortable.create(containerId, {
> + tag:'div',
> + only:'gadget',
> + handle:'gadget-title',
> + overlap: 'vertical',
> + scroll: window,
> + containment: containerIds,
> + dropOnEmpty: true,
> + constraint: false,
> + ghosting:false,
> + hoverclass: 'gadget-container-hover-highlight',
> + onUpdate: this.onMoveGadget.bind(this)
> + });
> + }.bind(this));
> + },
> +
> + /**
> + * Adds handlers to the gadgets on the dashboard, the remove.
> + */
> + addGadgetsHandlers : function() {
> + // iterate through all the gadgets and add settings handlers
> + this.element.select('.gadget').each(function(gadget){
> + // create a settings menu button and add it to the gadget-title
> + var itemMenu = new Element('div', {'class' : 'settings', 'title' :
> '$msg.get("dashboard.gadget.actions.tooltip")'});
> + var gadgetTitle = gadget.down('.gadget-title');
> + if (!gadgetTitle) {
> + return;
> + }
> + // create a remove button in the settings menu
> + var removeLink = new Element('div', {'class' : 'remove', 'title' :
> '$msg.get("dashboard.gadget.action.delete.tooltip")'});
> + removeLink.observe('click',
> this.onRemoveGadget.bindAsEventListener(this));
> + var actionsContainer = new Element('div', {'class' :
> 'settings-menu'})
> + actionsContainer.hide();
> + actionsContainer.insert(removeLink);
> + itemMenu.hide();
> + gadgetTitle.insert(itemMenu);
> + gadgetTitle.insert(actionsContainer);
> + // and listen to the click to open the menu
> + itemMenu.observe('click', function(event){
> + // toggle actions container
> + actionsContainer.toggle();
> + // and add a class to the item menu, to be able to color it
> properly
> + itemMenu.toggleClassName('settings-open');
> + });
> +
> + // display the link remove link only when the gadget is hovered
> + gadget.observe('mouseover', function() {
> + itemMenu.show();
> + });
> + gadget.observe('mouseout', function(event) {
> + var relatedTarget = event.relatedTarget || event.toElement;
> + if ((event.element() == gadget || event.element().up('.gadget'))
> && (relatedTarget == null || relatedTarget.up('.gadget') == null)) {
> + // enough propagation
> + event.stop();
> + itemMenu.hide();
> + actionsContainer.hide();
> + itemMenu.removeClassName('settings-open');
> + }
> + });
> + }.bind(this));
> +
> + // add the decorators to the gadgets on drag & drop
> + var doOnStartDrag = this._doOnStartDrag.bind(this);
> + var doOnEndDrag = this._doOnEndDrag.bind(this);
> + Draggables.addObserver({
> + onStart: doOnStartDrag,
> + onEnd: doOnEndDrag
> + });
> + },
> +
> + /**
> + * Removes the gadget passed by its id.
> + *
> + * @param event the click event on the remove button for a gadget
> + */
> + onRemoveGadget : function(event) {
> + // get the clicked button
> + var item = event.element();
> + // get the gadget to remove
> + var gadget = item.up(".gadget");
> + if (!gadget) {
> + return;
> + }
> + var gadgetId = this._getGadgetId(gadget);
> + this.removed.push(gadgetId);
> + // send a request to the REST url to remove the object corresponding
> to this gadget
> + // TODO: this should be the URI to the dashboard source, not to the
> current page
> + var gadgetObjectURL = XWiki.contextPath + '/rest/wikis/' +
> encodeURIComponent(XWiki.currentWiki) + '/spaces/' +
> + encodeURIComponent(XWiki.currentSpace) +
> '/pages/' + encodeURIComponent(XWiki.currentPage) +
> + '/objects/' +
> encodeURIComponent(this.gadgetsClass) + '/' + gadgetId +
> '?media=json&method=DELETE';
>
Why not use XWiki.currentDocument.getRestUrl ?
> + new XWiki.widgets.ConfirmedAjaxRequest(
> + gadgetObjectURL,
> + {
> + onCreate : function() {
> + // Disable the button, to avoid a cascade of clicks from
> impatient users
> + item.disabled = true;
> + },
> + onSuccess : function(response) {
> + // remove the gadget from the page
> + gadget.remove();
> + }.bind(this)
> + },
> + /* Interaction parameters */
> + {
> + confirmationText:
> "$msg.get('dashboard.gadget.action.delete.confirm')",
> + progressMessageText :
> "$msg.get('dashboard.gadget.action.delete.inProgress')",
> + successMessageText :
> "$msg.get('dashboard.gadget.action.delete.done')",
> + failureMessageText :
> "$msg.get('dashboard.gadget.action.delete.failed')"
> + }
> + );
> + },
> +
> + /**
> + * Function called when a gadget has been moved from a container to
> another.
> + *
> + * @param container the source and target container for the move,
> depending on the particular call of this function
> + */
> + onMoveGadget : function(container) {
> + // just flag that the dashboard was edited, actual changes were
> performed when the save button will be clicked
> + this.edited = true;
> + },
> +
> + /**
> + * Saves the changes on the dashboard in this document: perform all
> removes and injects the object edit fields in the
> + * inline edit form.
> + */
> + saveChanges : function(event) {
> + if (!this.edited) {
> + return;
> + }
> +
> + var editFields = this.prepareEditParameters();
> + // get the edit form
> + var editForm = event.memo.form;
> + // put the fields in a dashboard layout fieldset, to be able to update
> them on subsequent saves
> + // (in case of save & continue)
> + // FIXME: add the name of the dashboard in here, will cause issues
> when there are multiple dashboards
> + var dashboardFieldSet = editForm.down('.dashboard-layout-fieldset');
> + if (dashboardFieldSet) {
> + // empty the dashboard field set, to fill in with the new fields
> after
> + dashboardFieldSet.update('');
> + } else {
> + // create the dashboard field set, to fill in with the new fields
> + dashboardFieldSet = new Element('fieldset', {'class' :
> 'dashboard-layout-fieldset'});
> + editForm.insert(dashboardFieldSet);
> + }
> + editFields.each(function(editField) {
> + // create a hidden input for the update of the position of the
> gadget object and append it to the form
> + var fieldInput = new Element('input', {'name' : editField.key,
> 'value' : editField.value, 'type' : 'hidden'});
> + dashboardFieldSet.insert(fieldInput);
> + }.bind(this));
> + },
> +
> + /**
> + * Prepares a hashmap of parameters that would update the positions of
> the gadget fields.
> + * @return the hash of parameters to submit to the object edit URL, to
> update the gadget object positions in this
> + * dashboard
> + */
> + prepareEditParameters : function() {
> + var parameters = new Hash();
> + // for each gadget in the containers, put it in the map, along with
> its new position
> + this.element.select('.gadget-container').each(function(container) {
> + // get the id of the container
> + var containerId = this._getContainerId(container);
> + // foreach of its gadget children, get the position and compose the
> position update field
> + container.select('.gadget').each(function(gadget, index) {
> + // get the id of the current gadget -> object number, actually
> + var gadgetId = this._getGadgetId(gadget);
> + // the position field name as in the inline form edit
> XWiki.GadgetClass_0_position
> + var positionFieldName = this.gadgetsClass + '_' + gadgetId + '_' +
> 'position';
> + // compose the position field value as container, index (1 based,
> though)
> + var positionFieldValue = containerId + ', ' + (index + 1);
> + // and put these in the prepared hash
> + parameters.set(positionFieldName, positionFieldValue);
> + }, this);
> + }, this);
> +
> + return parameters;
> + }
> +});
> +//End XWiki augmentation.
> +return XWiki;
> +}(XWiki || {}));
> +
> +document.observe('xwiki:dom:loaded', function(event) {
> + // editable dashboard only in inline mode
> + if (XWiki.contextaction == 'inline') {
> + // edit first dashboard FIXME: to create a dashboard editor for all
> dashboards
> + var dashboardRootElt = $$('.dashboard')[0];
>
You can use $('mainContentArea').down('.dashboard') which should be less
greedy
Jerome.
> + if (dashboardRootElt) {
> + var dashboard = new XWiki.Dashboard(dashboardRootElt);
> + }
> + }
> +});
> \ No newline at end of file
>
>
> Property changes on:
> platform/web/trunk/standard/src/main/webapp/resources/uicomponents/dashboard/dashboard.js
> ___________________________________________________________________
> Added: svn:keywords
> + Author Id Revision HeadURL
> Added: svn:eol-style
> + native
>
> _______________________________________________
> notifications mailing list
> notifications(a)xwiki.org
> http://lists.xwiki.org/mailman/listinfo/notifications
>
Hello devs,
I'm working on some project outside XWiki which uses your rendering API.
Managed to wire everything and to define my custom macro, wrote some unit
tests but when I use another wiki dialect except XWIKI_2_0 (for instance
Creole) in the Converter, my macro is not recognized.
See sample code below:
// Initialize Rendering components and allow getting instances
ecm = new EmbeddableComponentManager();
ecm.initialize(this.getClass().getClassLoader());
Converter converter = ecm.lookup(Converter.class);
//Register my special custom macros
ComponentAnnotationLoader loader = new ComponentAnnotationLoader();
List<ComponentDescriptor> compDescriptors =
loader.getComponentsDescriptors(MySpecialMacro.class);
ecm.registerComponent(compDescriptors.get(0));
Converter converter = ecm.lookup(Converter.class);
WikiPrinter printer = new DefaultWikiPrinter();
// HERE IS THE PROBLEM WITH MY CUSTOM MACRO
converter.convert(new StringReader("{{link path='foo'}}"),
Syntax.CREOLE_1_0, Syntax.XHTML_1_0, printer);
So my problem is that I'd like to instruct the Creole parser or any other
parser except XWIKI_2 to correctly parse my custom macro.
I might be wrong, but I guess I'll have to implement some new parser to suit
my needs. In respect to that I've dug into the Converter class, saw it's
instantiation strategy, then followed the story until I've encountered
IWikiParser and all its subclasses (XWikiParser, CreoleParser etc ) which
I'm unable to find on the repository.
Could you please point me in the right direction?
Thank you
--
ing. Bogdan Flueras
Hello devs,
I'm working on some project outside XWiki which uses your rendering API.
Managed to wire everything and to define my custom macro, wrote some unit
tests but when I use another wiki dialect except XWIKI_2_0 (for instance
Creole) in the Converter, my macro is not recognized.
See sample code below:
// Initialize Rendering components and allow getting instances
ecm = new EmbeddableComponentManager();
ecm.initialize(this.getClass().getClassLoader());
Converter converter = ecm.lookup(Converter.class);
//Register my special custom macros
ComponentAnnotationLoader loader = new ComponentAnnotationLoader();
List<ComponentDescriptor> compDescriptors =
loader.getComponentsDescriptors(MySpecialMacro.class);
ecm.registerComponent(compDescriptors.get(0));
Converter converter = ecm.lookup(Converter.class);
WikiPrinter printer = new DefaultWikiPrinter();
// HERE IS THE PROBLEM WITH MY CUSTOM MACRO
converter.convert(new StringReader("{{link path='foo'}}"),
Syntax.CREOLE_1_0, Syntax.XHTML_1_0, printer);
So my problem is that I'd like to instruct the Creole parser or any other
parser except XWIKI_2 to correctly parse my custom macro. How can I do that?
I might be wrong, but I guess I'll have to implement some new parser to suit
my needs. In respect to that I've dug into the Converter class, saw it's
instantiation strategy, then followed the story until I've encountered
IWikiParser and all its subclasses (XWikiParser, CreoleParser etc ) which
I'm unable to find thier sources on the repository.
Could you please point me in the right direction?
Thank you!
--
View this message in context: http://xwiki.475771.n2.nabble.com/How-to-instruct-a-wiki-parser-to-render-m…
Sent from the XWiki- Dev mailing list archive at Nabble.com.
Hello all,
I would like to know whether XWiki is using any HTML filter for purifying
generated HTML.
JTidy seems old and not maintained...
Best regards
YB DEV