View Javadoc
1   package org.argeo.connect.e4.resources.parts;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   
6   import javax.annotation.PostConstruct;
7   import javax.annotation.PreDestroy;
8   import javax.inject.Inject;
9   import javax.jcr.Node;
10  import javax.jcr.Property;
11  import javax.jcr.Repository;
12  import javax.jcr.RepositoryException;
13  import javax.jcr.Session;
14  import javax.jcr.query.Query;
15  import javax.jcr.query.Row;
16  import javax.jcr.query.RowIterator;
17  
18  import org.apache.commons.logging.Log;
19  import org.apache.commons.logging.LogFactory;
20  import org.argeo.cms.auth.CurrentUser;
21  import org.argeo.cms.ui.eclipse.forms.FormToolkit;
22  import org.argeo.cms.util.CmsUtils;
23  import org.argeo.connect.ConnectConstants;
24  import org.argeo.connect.ConnectException;
25  import org.argeo.connect.ConnectTypes;
26  import org.argeo.connect.SystemAppService;
27  import org.argeo.connect.e4.ConnectE4Constants;
28  import org.argeo.connect.resources.ResourcesNames;
29  import org.argeo.connect.resources.ResourcesRole;
30  import org.argeo.connect.resources.ResourcesService;
31  import org.argeo.connect.resources.core.TagUtils;
32  import org.argeo.connect.ui.AppWorkbenchService;
33  import org.argeo.connect.ui.ConnectColumnDefinition;
34  import org.argeo.connect.ui.ConnectUiConstants;
35  import org.argeo.connect.ui.IJcrTableViewer;
36  import org.argeo.connect.ui.Refreshable;
37  import org.argeo.connect.ui.SystemWorkbenchService;
38  import org.argeo.connect.ui.util.HtmlListRwtAdapter;
39  import org.argeo.connect.ui.util.JcrRowLabelProvider;
40  import org.argeo.connect.ui.util.JcrViewerDClickListener;
41  import org.argeo.connect.ui.util.MainNodeTypeLabelProvider;
42  import org.argeo.connect.ui.util.TagLabelProvider;
43  import org.argeo.connect.ui.util.TitleIconRowLP;
44  import org.argeo.connect.ui.util.VirtualJcrTableViewer;
45  import org.argeo.connect.util.ConnectJcrUtils;
46  import org.argeo.connect.util.XPathUtils;
47  import org.argeo.eclipse.ui.EclipseUiUtils;
48  import org.argeo.jcr.JcrUtils;
49  import org.eclipse.e4.ui.di.Focus;
50  import org.eclipse.e4.ui.model.application.ui.basic.MPart;
51  import org.eclipse.jface.viewers.ColumnLabelProvider;
52  import org.eclipse.jface.viewers.ILazyContentProvider;
53  import org.eclipse.jface.viewers.TableViewer;
54  import org.eclipse.jface.viewers.Viewer;
55  import org.eclipse.jface.wizard.WizardDialog;
56  import org.eclipse.rap.rwt.RWT;
57  import org.eclipse.rap.rwt.client.service.BrowserNavigation;
58  import org.eclipse.swt.SWT;
59  import org.eclipse.swt.events.ModifyEvent;
60  import org.eclipse.swt.events.ModifyListener;
61  import org.eclipse.swt.events.SelectionAdapter;
62  import org.eclipse.swt.events.SelectionEvent;
63  import org.eclipse.swt.layout.GridData;
64  import org.eclipse.swt.layout.GridLayout;
65  import org.eclipse.swt.widgets.Composite;
66  import org.eclipse.swt.widgets.Label;
67  import org.eclipse.swt.widgets.Link;
68  import org.eclipse.swt.widgets.Text;
69  
70  /** display a filtered list of entities for a given Tag */
71  public class TagEditor implements Refreshable, IJcrTableViewer {
72  	private final static Log log = LogFactory.getLog(TagEditor.class);
73  	// public final static String ID = ConnectUiPlugin.PLUGIN_ID + ".tagEditor";
74  
75  	/* DEPENDENCY INJECTION */
76  	@Inject
77  	private Repository repository;
78  	@Inject
79  	private ResourcesService resourcesService;
80  	@Inject
81  	private SystemAppService systemAppService;
82  	@Inject
83  	private SystemWorkbenchService systemWorkbenchService;
84  	@Inject
85  	private MPart mPart;
86  
87  	// UI Objects
88  	private Row[] rows;
89  	protected FormToolkit toolkit;
90  	private List<ConnectColumnDefinition> colDefs;
91  	private TableViewer membersViewer;
92  	private Text filterTxt;
93  	private Label titleROLbl;
94  	private ColumnLabelProvider groupTitleLP;
95  
96  	// Context
97  	private Session session;
98  	private Node node;
99  	private String partName;
100 
101 	// RAP specific
102 	private BrowserNavigation browserNavigation;
103 
104 	// LIFE CYCLE
105 	public void init(String entityId) {
106 		// setSite(site);
107 		// setInput(input);
108 		// EntityEditorInput sei = (EntityEditorInput) getEditorInput();
109 
110 		session = ConnectJcrUtils.login(repository);
111 		node = ConnectJcrUtils.getNodeByIdentifier(session, entityId);
112 
113 		colDefs = new ArrayList<ConnectColumnDefinition>();
114 		colDefs.add(new ConnectColumnDefinition("Display Name",
115 				new TitleIconRowLP(systemWorkbenchService, null, Property.JCR_TITLE), 300));
116 		colDefs.add(new ConnectColumnDefinition("Other tags",
117 				new OtherTagsLabelProvider(resourcesService, systemWorkbenchService, node, null), 300));
118 	}
119 
120 	protected void afterNameUpdate(String name) {
121 		if (EclipseUiUtils.isEmpty(name))
122 			name = ConnectJcrUtils.get(node, Property.JCR_TITLE);
123 		if (EclipseUiUtils.notEmpty(name)) {
124 			setPartName(name);
125 			// ((EntityEditorInput) getEditorInput()).setTooltipText("List contacts tagged
126 			// as " + name);
127 		}
128 		if (titleROLbl != null && !titleROLbl.isDisposed())
129 			titleROLbl.setText(groupTitleLP.getText(node));
130 	}
131 
132 	protected void setPartName(String name) {
133 		this.partName = name;
134 		mPart.setLabel(name);
135 	}
136 
137 	/* SPECIFIC CONFIGURATION */
138 	/** Overwrite to add the batch update features */
139 	protected boolean enableBatchUpdate() {
140 		return false;
141 	}
142 
143 	/* CONTENT CREATION */
144 	@PostConstruct
145 	public void createPartControl(Composite parent) {
146 		String entityId = mPart.getPersistedState().get(ConnectE4Constants.ENTITY_ID);
147 		init(entityId);
148 		// Initialize main UI objects
149 		toolkit = new FormToolkit(parent.getDisplay());
150 		// Form form = toolkit.createForm(parent);
151 		// Composite main = form.getBody();
152 		// Composite main = toolkit.createComposite(parent);
153 		Composite main = new Composite(parent, SWT.NONE);
154 		createMainLayout(main);
155 		afterNameUpdate(null);
156 
157 		browserNavigation = RWT.getClient().getService(BrowserNavigation.class);
158 	}
159 
160 	protected void createMainLayout(Composite parent) {
161 		parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
162 		// The header
163 		Composite header = toolkit.createComposite(parent, SWT.NO_FOCUS);
164 		header.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
165 		populateHeader(header);
166 		// the body
167 		Composite body = toolkit.createComposite(parent, SWT.NO_FOCUS);
168 		body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
169 		createMembersList(body, getNode());
170 	}
171 
172 	protected void populateHeader(final Composite parent) {
173 		parent.setLayout(new GridLayout(2, false));
174 		titleROLbl = toolkit.createLabel(parent, "", SWT.WRAP);
175 		CmsUtils.markup(titleROLbl);
176 		groupTitleLP = new TagLabelProvider(resourcesService, ConnectUiConstants.LIST_TYPE_OVERVIEW_TITLE);
177 		titleROLbl.setText(groupTitleLP.getText(getNode()));
178 		titleROLbl.setLayoutData(EclipseUiUtils.fillWidth());
179 
180 		Link editTitleLink = null;
181 		if (CurrentUser.isInRole(ResourcesRole.editor.dn())) {
182 			editTitleLink = new Link(parent, SWT.NONE);
183 			editTitleLink.setText("<a>Edit Tag</a>");
184 		} else
185 			toolkit.createLabel(parent, "");
186 
187 		if (editTitleLink != null) {
188 			editTitleLink.addSelectionListener(new SelectionAdapter() {
189 				private static final long serialVersionUID = 1L;
190 
191 				@Override
192 				public void widgetSelected(final SelectionEvent event) {
193 					EditTagWizard wizard = new EditTagWizard(resourcesService, systemWorkbenchService, getNode(),
194 							ConnectConstants.RESOURCE_TAG, ResourcesNames.CONNECT_TAGS);
195 					WizardDialog dialog = new WizardDialog(titleROLbl.getShell(), wizard);
196 					// NoProgressBarWizardDialog dialog = new
197 					// NoProgressBarWizardDialog(titleROLbl.getShell(), wizard);
198 					dialog.open();
199 				}
200 			});
201 		}
202 	}
203 
204 	/* Provide extraction ability */
205 	@Override
206 	public Row[] getElements(String extractId) {
207 		return rows;
208 	}
209 
210 	@Override
211 	public List<ConnectColumnDefinition> getColumnDefinition(String extractId) {
212 		List<ConnectColumnDefinition> columns = new ArrayList<ConnectColumnDefinition>();
213 		columns.add(new ConnectColumnDefinition("Type", new MainNodeTypeLabelProvider(systemAppService)));
214 		columns.add(new ConnectColumnDefinition("Name", new JcrRowLabelProvider(Property.JCR_TITLE)));
215 		columns.add(new ConnectColumnDefinition("Tags", new JcrRowLabelProvider(ResourcesNames.CONNECT_TAGS)));
216 		columns.add(new ConnectColumnDefinition("Notes", new JcrRowLabelProvider(Property.JCR_DESCRIPTION)));
217 		return columns;
218 	}
219 
220 	public void createMembersList(Composite parent, final Node entity) {
221 		parent.setLayout(new GridLayout());
222 
223 		// First line: search Text and buttons
224 		filterTxt = createFilterText(parent);
225 		filterTxt.setLayoutData(EclipseUiUtils.fillWidth());
226 
227 		// Corresponding list
228 		Composite tableComp = toolkit.createComposite(parent);
229 		tableComp.setLayoutData(EclipseUiUtils.fillAll());
230 
231 		membersViewer = createTableViewer(tableComp);
232 		membersViewer.setContentProvider(new MyLazyContentProvider(membersViewer));
233 		refreshFilteredList();
234 
235 		// Double click
236 		JcrViewerDClickListener ndcl = new JcrViewerDClickListener();
237 		membersViewer.addDoubleClickListener(ndcl);
238 	}
239 
240 	private Text createFilterText(Composite parent) {
241 		Text filterTxt = toolkit.createText(parent, "", SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL);
242 		filterTxt.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
243 		filterTxt.addModifyListener(new ModifyListener() {
244 			private static final long serialVersionUID = 5003010530960334977L;
245 
246 			public void modifyText(ModifyEvent event) {
247 				refreshFilteredList();
248 			}
249 		});
250 		return filterTxt;
251 	}
252 
253 	/** Refresh the table viewer based on the free text search field */
254 	protected void refreshFilteredList() {
255 		long begin = System.currentTimeMillis();
256 		try {
257 
258 			Node tagParent = TagUtils.retrieveTagParentFromTag(node);
259 			String parentPath = tagParent.getProperty(ResourcesNames.RESOURCES_TAGGABLE_PARENT_PATH).getString();
260 			String xpathQueryStr = XPathUtils.descendantFrom(parentPath) + "//element(*, "
261 					+ ConnectTypes.CONNECT_TAGGABLE + ")";
262 
263 			String filter = filterTxt.getText();
264 			String currVal = ConnectJcrUtils.get(getNode(), Property.JCR_TITLE);
265 			String freeTxtCond = XPathUtils.getFreeTextConstraint(filter);
266 			String mlNamecond = XPathUtils.getPropertyEquals(ResourcesNames.CONNECT_TAGS, currVal);
267 			String conditions = XPathUtils.localAnd(freeTxtCond, mlNamecond);
268 			if (EclipseUiUtils.notEmpty(conditions))
269 				xpathQueryStr += "[" + conditions + "]";
270 			Query xpathQuery = XPathUtils.createQuery(session, xpathQueryStr);
271 			RowIterator xPathRit = xpathQuery.execute().getRows();
272 			Row[] rows = ConnectJcrUtils.rowIteratorToArray(xPathRit);
273 			setViewerInput(rows);
274 			if (log.isDebugEnabled()) {
275 				long end = System.currentTimeMillis();
276 				log.debug("Found: " + xPathRit.getSize() + " members for tag " + getNode() + " in " + (end - begin)
277 						+ " ms by executing XPath query (" + xpathQueryStr + ").");
278 			}
279 		} catch (RepositoryException e) {
280 			throw new ConnectException("Unable to list entities with static filter for tag " + getNode(), e);
281 		}
282 	}
283 
284 	private TableViewer createTableViewer(Composite parent) {
285 		parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
286 		VirtualJcrTableViewer tableCmp = new VirtualJcrTableViewer(parent, SWT.MULTI, colDefs, enableBatchUpdate());
287 		TableViewer tableViewer = tableCmp.getTableViewer();
288 		tableViewer.getTable().setHeaderVisible(false);
289 		tableCmp.setLayoutData(EclipseUiUtils.fillAll());
290 		tableViewer.getTable().addSelectionListener(new HtmlListRwtAdapter(systemWorkbenchService));
291 		return tableViewer;
292 	}
293 
294 	/** Use this method to update the result table */
295 	protected void setViewerInput(Row[] rows) {
296 		this.rows = rows;
297 		membersViewer.setInput(rows);
298 		// we must explicitly set the items count
299 		membersViewer.setItemCount(rows.length);
300 		membersViewer.refresh();
301 	}
302 
303 	private class MyLazyContentProvider implements ILazyContentProvider {
304 		private static final long serialVersionUID = 1L;
305 		private TableViewer viewer;
306 		private Row[] elements;
307 
308 		public MyLazyContentProvider(TableViewer viewer) {
309 			this.viewer = viewer;
310 		}
311 
312 		public void dispose() {
313 		}
314 
315 		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
316 			// IMPORTANT: don't forget this: an exception will be thrown if a
317 			// selected object is not part of the results anymore.
318 			viewer.setSelection(null);
319 			this.elements = (Row[]) newInput;
320 		}
321 
322 		public void updateElement(int index) {
323 			viewer.replace(elements[index], index);
324 		}
325 	}
326 
327 	@Override
328 	public void forceRefresh(Object object) {
329 		afterNameUpdate(null);
330 		refreshFilteredList();
331 	}
332 
333 	/* Expose to extending classes */
334 	protected Node getNode() {
335 		return node;
336 	}
337 
338 	protected ResourcesService getResourcesService() {
339 		return resourcesService;
340 	}
341 
342 	protected AppWorkbenchService getAppWorkbenchService() {
343 		return systemWorkbenchService;
344 	}
345 
346 	/* Life Cycle */
347 	protected boolean canSave() {
348 		return false;
349 	}
350 
351 	@PreDestroy
352 	public void dispose() {
353 		JcrUtils.logoutQuietly(session);
354 		browserNavigation.pushState("~", null);
355 	}
356 
357 	@Focus
358 	public void setFocus() {
359 		try {
360 			browserNavigation.pushState(node.getPath(), partName);
361 		} catch (RepositoryException e) {
362 			log.error("Cannot set client state", e);
363 		}
364 	}
365 
366 	/* DEPENDENCY INJECTION */
367 	// public void setRepository(Repository repository) {
368 	// this.repository = repository;
369 	// }
370 	//
371 	// public void setResourcesService(ResourcesService resourcesService) {
372 	// this.resourcesService = resourcesService;
373 	// }
374 	//
375 	// public void setSystemAppService(SystemAppService systemAppService) {
376 	// this.systemAppService = systemAppService;
377 	// }
378 	//
379 	// public void setSystemWorkbenchService(SystemWorkbenchService
380 	// systemWorkbenchService) {
381 	// this.systemWorkbenchService = systemWorkbenchService;
382 	// }
383 }