diff --git a/.classpath b/.classpath index d86c837f55..8f5c7f0be8 100644 --- a/.classpath +++ b/.classpath @@ -1,10 +1,19 @@ - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/extension/web-client-config-custom.xml.sample b/config/alfresco/extension/web-client-config-custom.xml.sample index 9ce908883d..5a4f99358b 100644 --- a/config/alfresco/extension/web-client-config-custom.xml.sample +++ b/config/alfresco/extension/web-client-config-custom.xml.sample @@ -22,9 +22,9 @@ Italian Japanese Dutch - Portugese (Brazillian) + Portuguese (Brazilian) Russian - Finish + Finnish Turkish Simplified Chinese diff --git a/config/alfresco/messages/jbpm.properties b/config/alfresco/messages/jbpm.properties new file mode 100644 index 0000000000..e994165d7f --- /dev/null +++ b/config/alfresco/messages/jbpm.properties @@ -0,0 +1,13 @@ +processDefinitionsList=Process Definitions List +searchProcessInstances=Search Process Instances +processDefinitions=Process Definitions +variableName=Variable Name +variableValue=Variable Value +search=Search +id=Id +name=Name +version=Version +instances=Instances +start=Start +end=End +value=Value \ No newline at end of file diff --git a/config/alfresco/messages/webclient.properties b/config/alfresco/messages/webclient.properties index 5125b5f0c3..55f375ace1 100644 --- a/config/alfresco/messages/webclient.properties +++ b/config/alfresco/messages/webclient.properties @@ -158,6 +158,7 @@ system_error=System Error login=Login templates=Templates template=Template +rss_template=RSS Template select_button=Select... select_items=Select items select_an_item=Select an item @@ -181,6 +182,9 @@ lock=Lock unlock=Unlock items_per_page=Items Per Page raise_issue=Raise an Issue +click_to_set_date=Click to set a date +today=Today +reset=Reset # Properties username=User Name @@ -235,6 +239,9 @@ advanced_space_wizard=Advanced Space Wizard create_rule=Create Rule reapply_rules=Reapply Rules reapply_rules_success=The rules have been successfully reapplied. +ignore_inherited_rules=Ignore Inherited Rules +include_inherited_rules=Include Inherited Rules +inherited_rules_being_ignored=Inherited rules are being ignored. manage_rules=Manage Content Rules manage_users=Manage System Users manage_groups=Manage User Groups @@ -508,8 +515,7 @@ modify_props_of=Modify Properties of modify_space_properties=Modify Space Properties modify_content_properties=Modify Content Properties preview=Preview in Template -dashboard_view=Dashboard View -dashboard=Dashboard +custom_view=Custom View view_links=Links not_inline_editable=This document is not inline editable. allow_inline_editing=Allow Inline Editing @@ -540,9 +546,15 @@ success_unlock=Successfully unlocked the document. inherit_permissions=Inherit Parent Space Permissions success_inherit_permissions=Successfully changed Inherit Parent Permissions to 'Yes' success_not_inherit_permissions=Successfully changed Inherit Parent Permissions to 'No' -apply_dashboard=Apply Dashboard -apply_dashboard_info=Select a template to be applied to the Space as a Dashboard view. -apply_dashboard_doc_info=Select a template to be applied to the Document as a Dashboard view. +apply_template=Apply Template +apply_dashboard_info=Select a template to be applied to the Space as a Custom view. +apply_dashboard_doc_info=Select a template to be applied to the Document as a Custom view. +apply_rss_feed=Apply RSS Feed Template +apply_rss_feed_info=Select a template to be applied to the Space as an RSS feed. +apply_rss_feed_warning1=This Space must be visible to the Guest user for the RSS feed to be publically viewable, you can Invite the Guest user using the +apply_rss_feed_warning2=view. +rss_feed=RSS Feed +rss_feed_link=RSS Feed Link # Export messages export_info=Exports metadata and content from this or all Spaces. @@ -596,6 +608,7 @@ system_props=System Properties hide_details=Hide Details show_details=Show Details user_search_info=To find a user search for them using their first name, last name and/or user name. Alternatively to see all users click 'Show All', however, this may take some time if there are a lot of users in the system. +user_change_homespace_info=Selecting a new home space for a user will not remove the existing permissions on the original home space. You may wish to use the Manage Space Users dialog to modify permissions if they are no longer required on the original home space. # Content Wizard messages add_content_dialog_title=Add Content Dialog @@ -687,6 +700,7 @@ condition_contains_hints=Hints condition_contains_hints_desc=Use zz* to match any name that begins with zz; use *.txt to match any text file; use *zz* to match any file name that contains zz anywhere including the beginning or end. apply_to_sub_spaces=Apply rule to sub spaces run_in_background=Run rule in background +rule_disabled=Disable rule not=Not click_set_and_add=Click to set values and add to list click_add_to_list=Click to add to list @@ -864,6 +878,7 @@ title_my_alfresco=My Alfresco dashboard_info=My Alfresco Dashboard dashboard_description=Configure this view and build your personal Alfresco dashboard configure=Configure +error_dashboard=An error occurred in one of the dashlets. # My Alfresco Layout Manager wizard messages configure_dashboard_title=Configure Dashboard Wizard @@ -900,6 +915,7 @@ dashlet_gettingstarted_desc=Displays helpful information for getting started wit # User Console and Settings messages title_user_console=User Options +title_edit_user_details=Edit User Details user_console=User Options user_console_info=User Options user_console_description=Use this page to change your options and settings @@ -909,6 +925,60 @@ change_my_password_description=Use this view to change your password change_my_password_instructions=Enter your new password. old_password=Old Password new_password=New Password +edit_user_details=Edit User Details +edit_user_details_description=Use this view to change your user details and email address + +# Delete Space Dialog messages +select_delete_operation=What do you want to delete? +delete_op_all=This space and all its contents. Note: Rules will also be deleted. +delete_op_files=Only the files within this space. +delete_op_folders=Only the folders within this space. +delete_op_contents=Files and folders within this space. + +# Email users dialog +email_space_users=Email Space users +email_space_users_desc=Send an email to the users and groups assigned to this space. + +# Workflow messages +start_workflow=Start Workflow +start_workflow_wizard=Start New Workflow Wizard +start_workflow_desc=This wizard helps you start a workflow for an item in the repository. +available_workflows=Available workflows +step_choose_workflow=Choose Workflow +start_workflow_choose_title=Choose Workflow +start_workflow_choose_desc=Choose the workflow you want to start +step_workflow_options=Workflow Options +start_workflow_options_title=Workflow Options +start_workflow_options_desc=Select options for the workflow +start_workflow_finish_instruction=To start the workflow press Finish. To review or change your selections click Back. +start_workflow_no_metadata=There is no metadata to collect for this particular workflow. +users_and_roles=Users and their Roles +resources=Resources +manage_workitem=Manage WorkItem +manage_workitem_title=Manage WorkItem +manage_workitem_desc=This dialog allows the work item to be managed +workitem_properties=Work Item Properties +id=Id +status=Status +completed=Completed +source=Source +priority=Priority +my_workitems_todo_title=My Work Items To Do +my_workitems_todo_desc=List of your workflow items still to complete +my_workitems_completed_title=My Completed Work Items +my_workitems_completed_desc=List of your completed workflow items +due_date=Due Date +completed_on=Completed on +outcome=Outcome +reassign=Reassign +cancel_workflow=Cancel Workflow +cancel_workflow_info=To cancel this workflow, click OK. +cancel_workflow_confirm=Are you sure you want to cancel the \"{0}\" workflow? +error_cancel_workflow=Unable to cancel the workflow due to system error: +reassign_workitem_title=Reassign Work Item +reassign_workitem_desc=This dialog allows you to reassign a work item. +reassign_select_user=Select the user to assign the work item to, then press OK. +error_reassign_workitem=Unable to reassign the work item due to system error: # Admin Console messages title_admin_console=Administration Console @@ -1067,19 +1137,19 @@ error_shortcut_permissions=Unable to navigate to the item as it cannot be read b # Confirmations return_to_application=Return to application -delete_space_info=To remove this space and all of its contents, click Yes. +delete_space_info=To remove this space and all of its contents, click OK. delete_space_confirm=Are you sure you want to delete \"{0}\" and all its contents? -delete_forums_info=To remove this forum space and its contents, click Yes. -delete_forum_info=To remove this forum and its topics, click Yes. +delete_forums_info=To remove this forum space and its contents, click OK. +delete_forum_info=To remove this forum and its topics, click OK. delete_forum_confirm=Are you sure you want to delete \"{0}\" and all its topics? -delete_topic_info=To remove this topic and its posts, click Yes. +delete_topic_info=To remove this topic and its posts, click OK. delete_topic_confirm=Are you sure you want to delete \"{0}\" and all its posts? -delete_post_info=To remove this post, click Yes. +delete_post_info=To remove this post from the topic, click OK. delete_post_confirm=Are you sure you want to delete the post from \"{0}\"? -delete_file_info=To remove this file and any previous versions, click Yes. +delete_file_info=To remove this file and any previous versions, click OK. delete_file_confirm=Are you sure you want to delete \"{0}\" and all previous versions? -delete_rule_info=To remove this rule, click Yes. -delete_user_info=To delete this user, click Yes. +delete_rule_info=To remove this rule from the space, click Yes. +delete_user_info=To delete this user from the system, click Yes. delete_rule_confirm=Are you sure you want to delete \"{0}\"? delete_user_confirm=The User will no longer be able to access the system. Are you sure you want to delete user \"{0}\"? remove_invited_user_confirm=The User will no longer be able to access the documents and folders in this space. Are you sure you want to remove user \"{0}\"? diff --git a/config/alfresco/web-client-application-context.xml b/config/alfresco/web-client-application-context.xml index ed6062261a..818bf38e94 100644 --- a/config/alfresco/web-client-application-context.xml +++ b/config/alfresco/web-client-application-context.xml @@ -12,6 +12,7 @@ classpath:alfresco/web-client-config-properties.xml classpath:alfresco/web-client-config-navigation.xml classpath:alfresco/web-client-config-actions.xml + classpath:alfresco/web-client-config-workflow.xml classpath:alfresco/web-client-config-forum-actions.xml classpath:alfresco/extension/web-client-config-custom.xml diff --git a/config/alfresco/web-client-config-actions.xml b/config/alfresco/web-client-config-actions.xml index bce47a5ad3..37b7a158e3 100644 --- a/config/alfresco/web-client-config-actions.xml +++ b/config/alfresco/web-client-config-actions.xml @@ -27,13 +27,16 @@ inlineAction /images/icons/edit_icon.gif - + #{CheckinCheckoutBean.editFile} editDocument http://... new javascript:myhandler - + + + + #{actionContext.id} @@ -379,8 +382,20 @@ + + + email_space_users + /images/icons/email_users.gif + dialog:emailSpaceUsers + #{BrowseBean.setupSpaceAction} + + #{actionContext.id} + + + + org.alfresco.web.action.evaluator.ShortcutNodeEvaluator manage_deleted_items /images/icons/trashcan.gif dialog:manageDeletedItems @@ -486,6 +501,12 @@ #{RulesBean.reapplyRules} + + + + /images/icons/reapply_rules.gif + #{RulesBean.ignoreInheritedRules} + @@ -602,6 +623,7 @@ + @@ -629,6 +651,7 @@ + diff --git a/config/alfresco/web-client-config-dialogs.xml b/config/alfresco/web-client-config-dialogs.xml index fa5eea4ae6..2768dd5126 100644 --- a/config/alfresco/web-client-config-dialogs.xml +++ b/config/alfresco/web-client-config-dialogs.xml @@ -5,6 +5,9 @@ + + + - @@ -77,7 +80,16 @@ + description-id="create_topic_description" error-message-id="error_create_topic_dialog" /> + + + + + + + diff --git a/config/alfresco/web-client-config-forum-actions.xml b/config/alfresco/web-client-config-forum-actions.xml index 8037d12083..d50e31ff8d 100644 --- a/config/alfresco/web-client-config-forum-actions.xml +++ b/config/alfresco/web-client-config-forum-actions.xml @@ -60,7 +60,7 @@ - CreateChildren + Collaborator org.alfresco.web.action.evaluator.CreateForumNodeEvaluator start_discussion diff --git a/config/alfresco/web-client-config-workflow.xml b/config/alfresco/web-client-config-workflow.xml new file mode 100644 index 0000000000..43538372a4 --- /dev/null +++ b/config/alfresco/web-client-config-workflow.xml @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + start_workflow + /images/icons/new_workflow.gif + wizard:startWorkflow + #{WizardManager.setupParameters} + + #{actionContext.id} + + + + + reassign + /images/icons/reassign_workflow_item.gif + dialog:reassignWorkItem + #{DialogManager.setupParameters} + + #{actionContext.id} + + + + + cancel_workflow + /images/icons/cancel_workflow.gif + dialog:cancelWorkflow + #{DialogManager.setupParameters} + + #{actionContext.workflowInstanceId} + #{actionContext.workflowInstanceName} + + + + + add + /images/icons/add_item.gif + null + + + + remove + /images/icons/remove_item.gif + #{DialogManager.bean.removePackageItem} + + #{actionContext.id} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/web-client-config.xml b/config/alfresco/web-client-config.xml index 56486602f4..d10ba6ab65 100644 --- a/config/alfresco/web-client-config.xml +++ b/config/alfresco/web-client-config.xml @@ -52,6 +52,10 @@ Consumer + + + userhome + http://www.alfresco.org/help/webclient @@ -136,6 +140,7 @@ + @@ -184,14 +189,16 @@ + + + - - + @@ -248,6 +256,16 @@ + + + + + + + + + + diff --git a/source/java/org/alfresco/web/action/evaluator/ApproveDocEvaluator.java b/source/java/org/alfresco/web/action/evaluator/ApproveDocEvaluator.java index 714c35f4f5..caf3a15cd6 100644 --- a/source/java/org/alfresco/web/action/evaluator/ApproveDocEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/ApproveDocEvaluator.java @@ -26,7 +26,7 @@ import org.alfresco.web.bean.repository.Node; * * @author Kevin Roast */ -public final class ApproveDocEvaluator implements ActionEvaluator +public class ApproveDocEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -37,6 +37,3 @@ public final class ApproveDocEvaluator implements ActionEvaluator node.isLocked() == false); } } -/* - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/ApproveNonDraftDocEvaluator.java b/source/java/org/alfresco/web/action/evaluator/ApproveNonDraftDocEvaluator.java index 01ba1aed31..726eba4319 100644 --- a/source/java/org/alfresco/web/action/evaluator/ApproveNonDraftDocEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/ApproveNonDraftDocEvaluator.java @@ -25,7 +25,7 @@ import org.alfresco.web.bean.repository.Node; * * @author Kevin Roast */ -public final class ApproveNonDraftDocEvaluator implements ActionEvaluator +public class ApproveNonDraftDocEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -37,6 +37,3 @@ public final class ApproveNonDraftDocEvaluator implements ActionEvaluator node.hasAspect(ContentModel.ASPECT_WORKING_COPY) == false); } } -/* - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/CancelCheckoutDocEvaluator.java b/source/java/org/alfresco/web/action/evaluator/CancelCheckoutDocEvaluator.java index b8a37db14d..6999484c47 100644 --- a/source/java/org/alfresco/web/action/evaluator/CancelCheckoutDocEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/CancelCheckoutDocEvaluator.java @@ -26,7 +26,7 @@ import org.alfresco.web.bean.repository.Node; * * @author Kevin Roast */ -public final class CancelCheckoutDocEvaluator implements ActionEvaluator +public class CancelCheckoutDocEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -37,6 +37,3 @@ public final class CancelCheckoutDocEvaluator implements ActionEvaluator node.hasAspect(ContentModel.ASPECT_WORKING_COPY)); } } -/* - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/CheckinDocEvaluator.java b/source/java/org/alfresco/web/action/evaluator/CheckinDocEvaluator.java index 2c7c05434a..9c2f16ad48 100644 --- a/source/java/org/alfresco/web/action/evaluator/CheckinDocEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/CheckinDocEvaluator.java @@ -26,7 +26,7 @@ import org.alfresco.web.bean.repository.Node; * * @author Kevin Roast */ -public final class CheckinDocEvaluator implements ActionEvaluator +public class CheckinDocEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -37,6 +37,3 @@ public final class CheckinDocEvaluator implements ActionEvaluator node.hasAspect(ContentModel.ASPECT_WORKING_COPY) == true); } } -/* - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/CheckoutDocEvaluator.java b/source/java/org/alfresco/web/action/evaluator/CheckoutDocEvaluator.java index adb9346b4a..2550736623 100644 --- a/source/java/org/alfresco/web/action/evaluator/CheckoutDocEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/CheckoutDocEvaluator.java @@ -30,7 +30,7 @@ import org.alfresco.web.bean.repository.Repository; * * @author Kevin Roast */ -public final class CheckoutDocEvaluator implements ActionEvaluator +public class CheckoutDocEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -46,6 +46,3 @@ public final class CheckoutDocEvaluator implements ActionEvaluator node.hasAspect(ContentModel.ASPECT_WORKING_COPY) == false))); } } -/* - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/CreateForumNodeEvaluator.java b/source/java/org/alfresco/web/action/evaluator/CreateForumNodeEvaluator.java index 135de5addf..9c1f1b708c 100644 --- a/source/java/org/alfresco/web/action/evaluator/CreateForumNodeEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/CreateForumNodeEvaluator.java @@ -25,7 +25,7 @@ import org.alfresco.web.bean.repository.Node; * * @author Kevin Roast */ -public final class CreateForumNodeEvaluator implements ActionEvaluator +public class CreateForumNodeEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -36,6 +36,3 @@ public final class CreateForumNodeEvaluator implements ActionEvaluator node.isLocked() == false); } } -/* - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/DeleteDocEvaluator.java b/source/java/org/alfresco/web/action/evaluator/DeleteDocEvaluator.java index 6d39cf10ae..5f816fe88e 100644 --- a/source/java/org/alfresco/web/action/evaluator/DeleteDocEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/DeleteDocEvaluator.java @@ -25,7 +25,7 @@ import org.alfresco.web.bean.repository.Node; * * @author Kevin Roast */ -public final class DeleteDocEvaluator implements ActionEvaluator +public class DeleteDocEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -36,6 +36,3 @@ public final class DeleteDocEvaluator implements ActionEvaluator node.hasAspect(ContentModel.ASPECT_WORKING_COPY) == false); } } -/* - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/DiscussNodeEvaluator.java b/source/java/org/alfresco/web/action/evaluator/DiscussNodeEvaluator.java index 062d279cd9..748ff3fca9 100644 --- a/source/java/org/alfresco/web/action/evaluator/DiscussNodeEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/DiscussNodeEvaluator.java @@ -33,7 +33,7 @@ import org.alfresco.web.bean.repository.Repository; * * @author Kevin Roast */ -public final class DiscussNodeEvaluator implements ActionEvaluator +public class DiscussNodeEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -60,6 +60,3 @@ public final class DiscussNodeEvaluator implements ActionEvaluator return result; } } -/* - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/EditDocCIFSEvaluator.java b/source/java/org/alfresco/web/action/evaluator/EditDocCIFSEvaluator.java index cdfcb6d16f..8f6a10352c 100644 --- a/source/java/org/alfresco/web/action/evaluator/EditDocCIFSEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/EditDocCIFSEvaluator.java @@ -30,7 +30,7 @@ import org.alfresco.web.bean.repository.Repository; * * @author Kevin Roast */ -public final class EditDocCIFSEvaluator implements ActionEvaluator +public class EditDocCIFSEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -59,7 +59,3 @@ public final class EditDocCIFSEvaluator implements ActionEvaluator return result; } } -/* - - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/EditDocHttpEvaluator.java b/source/java/org/alfresco/web/action/evaluator/EditDocHttpEvaluator.java index 2ded8854fa..3ed3d935e6 100644 --- a/source/java/org/alfresco/web/action/evaluator/EditDocHttpEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/EditDocHttpEvaluator.java @@ -30,7 +30,7 @@ import org.alfresco.web.bean.repository.Repository; * * @author Kevin Roast */ -public final class EditDocHttpEvaluator implements ActionEvaluator +public class EditDocHttpEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -60,7 +60,3 @@ public final class EditDocHttpEvaluator implements ActionEvaluator return result; } } -/* - - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/EditDocWebDavEvaluator.java b/source/java/org/alfresco/web/action/evaluator/EditDocWebDavEvaluator.java index f75c19f619..82ffa55282 100644 --- a/source/java/org/alfresco/web/action/evaluator/EditDocWebDavEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/EditDocWebDavEvaluator.java @@ -30,7 +30,7 @@ import org.alfresco.web.bean.repository.Repository; * * @author Kevin Roast */ -public final class EditDocWebDavEvaluator implements ActionEvaluator +public class EditDocWebDavEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -59,7 +59,3 @@ public final class EditDocWebDavEvaluator implements ActionEvaluator return result; } } -/* - - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/ForumsCheckinDocEvaluator.java b/source/java/org/alfresco/web/action/evaluator/ForumsCheckinDocEvaluator.java index 2d510fe9d7..3559fdb864 100644 --- a/source/java/org/alfresco/web/action/evaluator/ForumsCheckinDocEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/ForumsCheckinDocEvaluator.java @@ -32,7 +32,7 @@ import org.alfresco.web.bean.repository.Repository; * * @author Kevin Roast */ -public final class ForumsCheckinDocEvaluator implements ActionEvaluator +public class ForumsCheckinDocEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -67,6 +67,3 @@ public final class ForumsCheckinDocEvaluator implements ActionEvaluator return allow; } } -/* - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/RejectDocEvaluator.java b/source/java/org/alfresco/web/action/evaluator/RejectDocEvaluator.java index 68a7e3df14..17489475ff 100644 --- a/source/java/org/alfresco/web/action/evaluator/RejectDocEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/RejectDocEvaluator.java @@ -26,7 +26,7 @@ import org.alfresco.web.bean.repository.Node; * * @author Kevin Roast */ -public final class RejectDocEvaluator implements ActionEvaluator +public class RejectDocEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -37,6 +37,3 @@ public final class RejectDocEvaluator implements ActionEvaluator node.isLocked() == false); } } -/* - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/RejectNonDraftDocEvaluator.java b/source/java/org/alfresco/web/action/evaluator/RejectNonDraftDocEvaluator.java index 29eb1a64a1..03c0550c90 100644 --- a/source/java/org/alfresco/web/action/evaluator/RejectNonDraftDocEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/RejectNonDraftDocEvaluator.java @@ -25,7 +25,7 @@ import org.alfresco.web.bean.repository.Node; * * @author Kevin Roast */ -public final class RejectNonDraftDocEvaluator implements ActionEvaluator +public class RejectNonDraftDocEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -37,6 +37,3 @@ public final class RejectNonDraftDocEvaluator implements ActionEvaluator node.hasAspect(ContentModel.ASPECT_WORKING_COPY) == false); } } -/* - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/ShortcutNodeEvaluator.java b/source/java/org/alfresco/web/action/evaluator/ShortcutNodeEvaluator.java index 61f076953e..8e66afe0dc 100644 --- a/source/java/org/alfresco/web/action/evaluator/ShortcutNodeEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/ShortcutNodeEvaluator.java @@ -28,7 +28,7 @@ import org.alfresco.web.bean.repository.Node; * * @author Kevin Roast */ -public final class ShortcutNodeEvaluator implements ActionEvaluator +public class ShortcutNodeEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -40,6 +40,3 @@ public final class ShortcutNodeEvaluator implements ActionEvaluator return (nav.getIsGuest() == false); } } -/* -rendered="#{NavigationBean.isGuest == false}" -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/TakeOwnershipDocEvaluator.java b/source/java/org/alfresco/web/action/evaluator/TakeOwnershipDocEvaluator.java index 091302ebe5..aa4a255d2e 100644 --- a/source/java/org/alfresco/web/action/evaluator/TakeOwnershipDocEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/TakeOwnershipDocEvaluator.java @@ -24,7 +24,7 @@ import org.alfresco.web.bean.repository.Node; * * @author Kevin Roast */ -public final class TakeOwnershipDocEvaluator implements ActionEvaluator +public class TakeOwnershipDocEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) diff --git a/source/java/org/alfresco/web/action/evaluator/UnlockDocEvaluator.java b/source/java/org/alfresco/web/action/evaluator/UnlockDocEvaluator.java index 11020467c2..4fba5ee296 100644 --- a/source/java/org/alfresco/web/action/evaluator/UnlockDocEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/UnlockDocEvaluator.java @@ -24,7 +24,7 @@ import org.alfresco.web.bean.repository.Node; * * @author Kevin Roast */ -public final class UnlockDocEvaluator implements ActionEvaluator +public class UnlockDocEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) diff --git a/source/java/org/alfresco/web/action/evaluator/UpdateDocEvaluator.java b/source/java/org/alfresco/web/action/evaluator/UpdateDocEvaluator.java index ffb9c74885..0511fee858 100644 --- a/source/java/org/alfresco/web/action/evaluator/UpdateDocEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/UpdateDocEvaluator.java @@ -29,7 +29,7 @@ import org.alfresco.web.bean.repository.Repository; * * @author Kevin Roast */ -public final class UpdateDocEvaluator implements ActionEvaluator +public class UpdateDocEvaluator implements ActionEvaluator { /** * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) @@ -45,6 +45,3 @@ public final class UpdateDocEvaluator implements ActionEvaluator node.hasAspect(ContentModel.ASPECT_WORKING_COPY) == false))); } } -/* - -*/ \ No newline at end of file diff --git a/source/java/org/alfresco/web/app/AlfrescoNavigationHandler.java b/source/java/org/alfresco/web/app/AlfrescoNavigationHandler.java index 978bf86817..9a0fabb997 100644 --- a/source/java/org/alfresco/web/app/AlfrescoNavigationHandler.java +++ b/source/java/org/alfresco/web/app/AlfrescoNavigationHandler.java @@ -26,6 +26,7 @@ import javax.faces.context.FacesContext; import org.alfresco.config.Config; import org.alfresco.config.ConfigService; +import org.alfresco.web.app.servlet.ExternalAccessServlet; import org.alfresco.web.bean.NavigationBean; import org.alfresco.web.bean.dialog.DialogManager; import org.alfresco.web.bean.repository.Node; @@ -623,14 +624,28 @@ public class AlfrescoNavigationHandler extends NavigationHandler } else { - // we are trying to close a dialog when one hasn't been opened! - // log a warning and return a null outcome to stay on the same page - if (logger.isWarnEnabled()) + // the details pages can be loaded via the external access servlet, + // if this is the case the details page would not have been loaded as + // a dialog, in this scenario just use the global "browse" outcome. + String referer = (String)context.getExternalContext(). + getRequestHeaderMap().get("referer"); + if ((referer != null) && + ((referer.indexOf(ExternalAccessServlet.OUTCOME_DOCDETAILS) != -1) || + (referer.indexOf(ExternalAccessServlet.OUTCOME_SPACEDETAILS) != -1))) { - logger.warn("Attempting to close a " + closingItem + " with an empty view stack, returning null outcome"); + navigate(context, fromAction, "browse"); + } + else + { + // we are trying to close a dialog when one hasn't been opened! + // log a warning and return a null outcome to stay on the same page + if (logger.isWarnEnabled()) + { + logger.warn("Attempting to close a " + closingItem + " with an empty view stack, returning null outcome"); + } + + navigate(context, fromAction, null); } - - navigate(context, fromAction, null); } } diff --git a/source/java/org/alfresco/web/app/Application.java b/source/java/org/alfresco/web/app/Application.java index 4d1bd2fee6..969f935d56 100644 --- a/source/java/org/alfresco/web/app/Application.java +++ b/source/java/org/alfresco/web/app/Application.java @@ -71,6 +71,7 @@ public class Application private static String spaceTemplatesFolderName; private static String contentTemplatesFolderName; private static String emailTemplatesFolderName; + private static String rssTemplatesFolderName; private static String savedSearchesFolderName; private static String scriptsFolderName; private static String guestHomeFolderName; @@ -376,6 +377,22 @@ public class Application return getEmailTemplatesFolderName(FacesContextUtils.getRequiredWebApplicationContext(context)); } + /** + * @return Returns the RSS templates folder name + */ + public static String getRSSTemplatesFolderName(ServletContext context) + { + return getRSSTemplatesFolderName(WebApplicationContextUtils.getRequiredWebApplicationContext(context)); + } + + /** + * @return Returns the RSS templates folder name + */ + public static String getRSSTemplatesFolderName(FacesContext context) + { + return getRSSTemplatesFolderName(FacesContextUtils.getRequiredWebApplicationContext(context)); + } + /** * @return Return the Saved Searches folder name */ @@ -750,6 +767,24 @@ public class Application return emailTemplatesFolderName; } + /** + * Returns the RSS Templates folder name + * + * @param context The spring context + * @return The RSS folder name + */ + private static String getRSSTemplatesFolderName(WebApplicationContext context) + { + if (rssTemplatesFolderName == null) + { + ImporterBootstrap bootstrap = (ImporterBootstrap)context.getBean(BEAN_IMPORTER_BOOTSTRAP); + Properties configuration = bootstrap.getConfiguration(); + rssTemplatesFolderName = configuration.getProperty("spaces.templates.rss.childname"); + } + + return rssTemplatesFolderName; + } + /** * Returns the Saved Searches folder name * diff --git a/source/java/org/alfresco/web/app/ContextListener.java b/source/java/org/alfresco/web/app/ContextListener.java index 948f3d9305..cd36ba6598 100644 --- a/source/java/org/alfresco/web/app/ContextListener.java +++ b/source/java/org/alfresco/web/app/ContextListener.java @@ -146,17 +146,7 @@ public class ContextListener implements ServletContextListener, HttpSessionListe */ public void contextDestroyed(ServletContextEvent event) { - WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); - Scheduler quartz = (Scheduler) ctx.getBean("schedulerFactory"); - try - { - quartz.shutdown(true); - } - catch (SchedulerException e) - { - // TODO Auto-generated catch block - e.printStackTrace(); - } + // nothing to do } /** diff --git a/source/java/org/alfresco/web/app/servlet/AlfrescoFacesServlet.java b/source/java/org/alfresco/web/app/servlet/AlfrescoFacesServlet.java deleted file mode 100644 index a0cfba6b3c..0000000000 --- a/source/java/org/alfresco/web/app/servlet/AlfrescoFacesServlet.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2005 Alfresco, Inc. - * - * Licensed under the Mozilla Public License version 1.1 - * with a permitted attribution clause. You may obtain a - * copy of the License at - * - * http://www.alfresco.org/legal/license.txt - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - * either express or implied. See the License for the specific - * language governing permissions and limitations under the - * License. - */ -package org.alfresco.web.app.servlet; - -import java.io.IOException; - -import javax.faces.webapp.FacesServlet; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.alfresco.web.app.Application; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Wrapper around standard faces servlet to provide error handling - * - * @author gavinc - */ -public class AlfrescoFacesServlet extends FacesServlet -{ - private static Log logger = LogFactory.getLog(AlfrescoFacesServlet.class); - - /** - * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) - */ - public void service(ServletRequest request, ServletResponse response) - throws IOException, ServletException - { - try - { - super.service(request, response); - } - catch (Throwable error) - { - String returnPage = ((HttpServletRequest)request).getRequestURI(); - - Application.handleServletError(getServletConfig().getServletContext(), (HttpServletRequest)request, - (HttpServletResponse)response, error, logger, returnPage); - } - } -} diff --git a/source/java/org/alfresco/web/app/servlet/DownloadContentServlet.java b/source/java/org/alfresco/web/app/servlet/DownloadContentServlet.java index ea9835c25b..951f1c2b3f 100644 --- a/source/java/org/alfresco/web/app/servlet/DownloadContentServlet.java +++ b/source/java/org/alfresco/web/app/servlet/DownloadContentServlet.java @@ -21,6 +21,7 @@ import java.io.UnsupportedEncodingException; import java.net.SocketException; import java.net.URLEncoder; import java.text.MessageFormat; +import java.util.Date; import java.util.StringTokenizer; import javax.servlet.ServletException; @@ -35,6 +36,7 @@ import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; @@ -170,6 +172,7 @@ public class DownloadContentServlet extends BaseServlet // get the services we need to retrieve the content ServiceRegistry serviceRegistry = getServiceRegistry(getServletContext()); + NodeService nodeService = serviceRegistry.getNodeService(); ContentService contentService = serviceRegistry.getContentService(); PermissionService permissionService = serviceRegistry.getPermissionService(); @@ -184,6 +187,21 @@ public class DownloadContentServlet extends BaseServlet return; } + // check If-Modified-Since header and set Last-Modified header as appropriate + Date modified = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED); + long modifiedSince = req.getDateHeader("If-Modified-Since"); + if (modifiedSince > 0L) + { + // round the date to the ignore millisecond value which is not supplied by header + long modDate = (modified.getTime() / 1000L) * 1000L; + if (modDate <= modifiedSince) + { + res.setStatus(304); + return; + } + } + res.setDateHeader("Last-Modified", modified.getTime()); + if (attachment == true) { // set header based on filename - will force a Save As from the browse if it doesn't recognise it diff --git a/source/java/org/alfresco/web/app/servlet/ExternalAccessServlet.java b/source/java/org/alfresco/web/app/servlet/ExternalAccessServlet.java index d58f0caf6c..5bdc7383d9 100644 --- a/source/java/org/alfresco/web/app/servlet/ExternalAccessServlet.java +++ b/source/java/org/alfresco/web/app/servlet/ExternalAccessServlet.java @@ -34,6 +34,7 @@ import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.web.app.Application; import org.alfresco.web.bean.BrowseBean; +import org.alfresco.web.bean.dashboard.DashboardManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -61,12 +62,14 @@ public class ExternalAccessServlet extends BaseServlet private static Log logger = LogFactory.getLog(ExternalAccessServlet.class); - private final static String OUTCOME_DOCDETAILS = "showDocDetails"; - private final static String OUTCOME_SPACEDETAILS = "showSpaceDetails"; - private final static String OUTCOME_BROWSE = "browse"; - private final static String OUTCOME_LOGOUT = "logout"; + public final static String OUTCOME_DOCDETAILS = "showDocDetails"; + public final static String OUTCOME_SPACEDETAILS = "showSpaceDetails"; + public final static String OUTCOME_BROWSE = "browse"; + public final static String OUTCOME_MYALFRESCO = "myalfresco"; + public final static String OUTCOME_LOGOUT = "logout"; private static final String ARG_TEMPLATE = "template"; + private static final String ARG_PAGE = "page"; /** * @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) @@ -224,6 +227,19 @@ public class ExternalAccessServlet extends BaseServlet navigationHandler.handleNavigation(fc, null, outcome); } } + else if (OUTCOME_MYALFRESCO.equals(outcome)) + { + // setup the Dashboard Manager ready for the page we want to display + if (req.getParameter(ARG_PAGE) != null) + { + DashboardManager manager = (DashboardManager)FacesHelper.getManagedBean(fc, "DashboardManager"); + manager.getPageConfig().setCurrentPage(req.getParameter(ARG_PAGE)); + } + + // perform the appropriate JSF navigation outcome + NavigationHandler navigationHandler = fc.getApplication().getNavigationHandler(); + navigationHandler.handleNavigation(fc, null, outcome); + } else if (OUTCOME_LOGOUT.equals(outcome)) { // special case for logout diff --git a/source/java/org/alfresco/web/app/servlet/JBPMDeployProcessServlet.java b/source/java/org/alfresco/web/app/servlet/JBPMDeployProcessServlet.java new file mode 100644 index 0000000000..937af24ad0 --- /dev/null +++ b/source/java/org/alfresco/web/app/servlet/JBPMDeployProcessServlet.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.web.app.servlet; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.List; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.workflow.WorkflowComponent; +import org.alfresco.service.cmr.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowDeployment; +import org.alfresco.service.cmr.workflow.WorkflowException; +import org.apache.commons.fileupload.DiskFileUpload; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileUpload; +import org.apache.commons.fileupload.FileUploadException; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + + +/** + * Servlet for handling process deployments from jBPM process designer. + * + * @author davidc + */ +public class JBPMDeployProcessServlet extends HttpServlet +{ + private static final long serialVersionUID = 8002539291245090187L; + + + /* (non-Javadoc) + * @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + */ + public void service(HttpServletRequest request, HttpServletResponse response) + throws IOException + { + try + { + response.setContentType("text/html"); + InputStream deploymentArchive = getDeploymentArchive(request); + WorkflowDefinition workflowDef = deployArchive(deploymentArchive); + response.getWriter().println("Deployed archive " + workflowDef.title + " successfully"); + } + catch(IOException e) + { + // NOTE: according to original jBPM deployment servlet + response.getWriter().println("IOException"); + } + catch(FileUploadException e) + { + // NOTE: according to original jBPM deployment servlet + response.getWriter().println("FileUploadException"); + } + } + + + /** + * Retrieve the JBPM Process Designer deployment archive from the request + * + * @param request the request + * @return the input stream onto the deployment archive + * @throws WorkflowException + * @throws FileUploadException + * @throws IOException + */ + private InputStream getDeploymentArchive(HttpServletRequest request) + throws FileUploadException, IOException + { + if (!FileUpload.isMultipartContent(request)) + { + throw new FileUploadException("Not a multipart request"); + } + + GPDUpload fileUpload = new GPDUpload(); + List list = fileUpload.parseRequest(request); + Iterator iterator = list.iterator(); + if (!iterator.hasNext()) + { + throw new FileUploadException("No process file in the request"); + } + + FileItem fileItem = (FileItem) iterator.next(); + if (fileItem.getContentType().indexOf("application/x-zip-compressed") == -1) + { + throw new FileUploadException("Not a process archive"); + } + + return fileItem.getInputStream(); + } + + + /** + * Deploy the jBPM process archive to the Alfresco Repository + * + * @param deploymentArchive the archive to deploy + * @return the deployed workflow definition + */ + private WorkflowDefinition deployArchive(InputStream deploymentArchive) + { + // NOTE: retrieve jbpm engine directly as this servlet only serves JBPM process designer deployments + WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); + WorkflowComponent jbpmEngine = (WorkflowComponent)wc.getBean("jbpm_engine"); + WorkflowDeployment deployment = jbpmEngine.deployDefinition(deploymentArchive, MimetypeMap.MIMETYPE_ZIP); + return deployment.definition; + } + + + /** + * NOTE: Workaround... + * + * The JBPM process designer (as of 3.1.2) issues a request with a multi-part line + * delimited by ",". It should be ":" according to the HTTP specification which + * the commons file-upload is adhering to. + * + * @author davidc + */ + @SuppressWarnings("deprecation") + private class GPDUpload extends DiskFileUpload + { + @Override + protected byte[] getBoundary(String contentType) + { + return super.getBoundary(contentType.replace(",", ";")); + } + } + +} \ No newline at end of file diff --git a/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java b/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java index 11cc65633d..99fdce8af5 100644 --- a/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java +++ b/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java @@ -142,7 +142,7 @@ public class TemplateContentServlet extends BaseServlet templateRef = pathInfo.NodeRef; } - else if (tokenCount == 7) + else if (tokenCount >= 7) { StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken()); templateRef = new NodeRef(storeRef, t.nextToken()); @@ -157,7 +157,7 @@ public class TemplateContentServlet extends BaseServlet if (nodeRef == null) { - throw new TemplateException("Not enough arguments supplied in URL."); + throw new TemplateException("Not enough elements supplied in URL or no 'path' argument specified."); } // get the services we need to retrieve the content @@ -268,7 +268,6 @@ public class TemplateContentServlet extends BaseServlet TemplateNode node = new TemplateNode(nodeRef, services, this.imageResolver); root.put("space", node); root.put("document", node); - root.put("template", new TemplateNode(templateRef, services, this.imageResolver)); // add URL arguments as a map called 'args' to the root of the model Map args = new HashMap(8, 1.0f); diff --git a/source/java/org/alfresco/web/app/servlet/ajax/AjaxCommand.java b/source/java/org/alfresco/web/app/servlet/ajax/AjaxCommand.java index 5675677a11..17eba223be 100644 --- a/source/java/org/alfresco/web/app/servlet/ajax/AjaxCommand.java +++ b/source/java/org/alfresco/web/app/servlet/ajax/AjaxCommand.java @@ -28,14 +28,10 @@ public interface AjaxCommand * * @param facesContext FacesContext * @param expression The binding expression - * * @param request The request * @param response The response */ - public abstract void execute(FacesContext facesContext, - String expression, - // String responseMimeType, - HttpServletRequest request, - HttpServletResponse response) - throws ServletException, IOException; + public abstract void execute(FacesContext facesContext, String expression, + HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException; } diff --git a/source/java/org/alfresco/web/app/servlet/command/EndTaskCommand.java b/source/java/org/alfresco/web/app/servlet/command/EndTaskCommand.java new file mode 100644 index 0000000000..b92b0534fd --- /dev/null +++ b/source/java/org/alfresco/web/app/servlet/command/EndTaskCommand.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.web.app.servlet.command; + +import java.util.Map; + +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.workflow.WorkflowService; + +/** + * End Task command implementation + * + * @author David Caruana + */ +public final class EndTaskCommand implements Command +{ + public static final String PROP_TASK_ID = "taskId"; + public static final String PROP_TRANSITION = "transition"; + + private static final String[] PROPERTIES = new String[] {PROP_TASK_ID, PROP_TRANSITION}; + + /** + * @see org.alfresco.web.app.servlet.command.Command#getPropertyNames() + */ + public String[] getPropertyNames() + { + return PROPERTIES; + } + + /** + * @see org.alfresco.web.app.servlet.command.Command#execute(org.alfresco.service.ServiceRegistry, java.util.Map) + */ + public Object execute(ServiceRegistry serviceRegistry, Map properties) + { + String taskId = (String)properties.get(PROP_TASK_ID); + if (taskId == null) + { + throw new IllegalArgumentException("Unable to execute EndTaskCommand - mandatory parameter not supplied: " + PROP_TASK_ID); + } + String transition = (String)properties.get(PROP_TRANSITION); + + // end task + WorkflowService workflowService = serviceRegistry.getWorkflowService(); + return workflowService.endTask(taskId, transition); + } +} diff --git a/source/java/org/alfresco/web/app/servlet/command/TaskCommandProcessor.java b/source/java/org/alfresco/web/app/servlet/command/TaskCommandProcessor.java new file mode 100644 index 0000000000..4c0323a6fa --- /dev/null +++ b/source/java/org/alfresco/web/app/servlet/command/TaskCommandProcessor.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.web.app.servlet.command; + +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.ServiceRegistry; + +/** + * Task specific command processor implementation. + *

+ * Responsible for executing workflow task operations. + * + * @author David Caruana + */ +public final class TaskCommandProcessor implements CommandProcessor +{ + private String taskId; + private String transition = null; + private String command; + + static + { + // add our commands to the command registry + CommandFactory.getInstance().registerCommand("end", EndTaskCommand.class); + } + + + /* (non-Javadoc) + * @see org.alfresco.web.app.servlet.command.CommandProcessor#validateArguments(javax.servlet.ServletContext, java.lang.String, java.util.Map, java.lang.String[]) + */ + public boolean validateArguments(ServletContext sc, String command, Map args, String[] urlElements) + { + if (urlElements.length == 0) + { + throw new IllegalArgumentException("Not enough URL arguments passed to command servlet."); + } + taskId = urlElements[0]; + if (urlElements.length == 2) + { + transition = urlElements[1]; + } + return true; + } + + /** + * @see org.alfresco.web.app.servlet.command.CommandProcessor#process(org.alfresco.service.ServiceRegistry, javax.servlet.http.HttpServletRequest, java.lang.String) + */ + public void process(ServiceRegistry serviceRegistry, HttpServletRequest request, String command) + { + Map properties = new HashMap(1, 1.0f); + // all workflow commands use a "target" Node property as an argument + properties.put(EndTaskCommand.PROP_TASK_ID, taskId); + if (transition != null) + { + properties.put(EndTaskCommand.PROP_TRANSITION, transition); + } + Command cmd = CommandFactory.getInstance().createCommand(command); + if (cmd == null) + { + throw new AlfrescoRuntimeException("Unregistered workflow command specified: " + command); + } + cmd.execute(serviceRegistry, properties); + this.command = command; + } + + /** + * @see org.alfresco.web.app.servlet.command.CommandProcessor#outputStatus(java.io.PrintWriter) + */ + public void outputStatus(PrintWriter out) + { + out.print("Task command: '"); + out.print(command); + out.print("' executed against task: "); + out.println(taskId); + } + +} diff --git a/source/java/org/alfresco/web/bean/AboutBean.java b/source/java/org/alfresco/web/bean/AboutBean.java new file mode 100644 index 0000000000..83f9d26916 --- /dev/null +++ b/source/java/org/alfresco/web/bean/AboutBean.java @@ -0,0 +1,43 @@ +package org.alfresco.web.bean; + +import org.alfresco.service.descriptor.DescriptorService; + +/** + * Simple backing bean used by the about page to display the version. + * + * @author gavinc + */ +public class AboutBean +{ + DescriptorService descriptorService; + + /** + * Retrieves the version of the repository. + * + * @return The version string + */ + public String getVersion() + { + return this.descriptorService.getServerDescriptor().getVersion(); + } + + /** + * Retrieves the edition of the repository. + * + * @return The edition + */ + public String getEdition() + { + return this.descriptorService.getServerDescriptor().getEdition(); + } + + /** + * Sets the DescriptorService. + * + * @param descriptorService The DescriptorService + */ + public void setDescriptorService(DescriptorService descriptorService) + { + this.descriptorService = descriptorService; + } +} diff --git a/source/java/org/alfresco/web/bean/AdvancedSearchBean.java b/source/java/org/alfresco/web/bean/AdvancedSearchBean.java index 05f2451530..9ee2208ae1 100644 --- a/source/java/org/alfresco/web/bean/AdvancedSearchBean.java +++ b/source/java/org/alfresco/web/bean/AdvancedSearchBean.java @@ -57,6 +57,7 @@ import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.CachingDateFormat; +import org.alfresco.util.ISO9075; import org.alfresco.web.app.Application; import org.alfresco.web.bean.SearchContext.RangeProperties; import org.alfresco.web.bean.repository.MapNode; @@ -620,10 +621,6 @@ public class AdvancedSearchBean } } } - - // make sure the list is sorted by the label - QuickSort sorter = new QuickSort(this.contentTypes, "label", true, IDataContainer.SORT_CASEINSENSITIVE); - sorter.sort(); } } @@ -672,10 +669,6 @@ public class AdvancedSearchBean } } } - - // make sure the list is sorted by the label - QuickSort sorter = new QuickSort(this.folderTypes, "label", true, IDataContainer.SORT_CASEINSENSITIVE); - sorter.sort(); } } @@ -1375,7 +1368,8 @@ public class AdvancedSearchBean { FacesContext fc = FacesContext.getCurrentInstance(); User user = Application.getCurrentUser(fc); - String xpath = NamespaceService.APP_MODEL_PREFIX + ":" + QName.createValidLocalName(user.getUserName()); + String userName = ISO9075.encode(user.getUserName()); + String xpath = NamespaceService.APP_MODEL_PREFIX + ":" + QName.createValidLocalName(userName); List results = null; try diff --git a/source/java/org/alfresco/web/bean/BaseDetailsBean.java b/source/java/org/alfresco/web/bean/BaseDetailsBean.java index 65795a5cce..763b1915a3 100644 --- a/source/java/org/alfresco/web/bean/BaseDetailsBean.java +++ b/source/java/org/alfresco/web/bean/BaseDetailsBean.java @@ -256,7 +256,7 @@ public abstract class BaseDetailsBean /** * Action handler to apply the selected Template and Templatable aspect to the current Space */ - public String applyTemplate() + public void applyTemplate(ActionEvent event) { if (this.template != null && this.template.equals(TemplateSupportBean.NO_SELECTION) == false) { @@ -283,13 +283,12 @@ public abstract class BaseDetailsBean FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e); } } - return getReturnOutcome(); } /** * Action handler to remove a dashboard template from the current Space */ - public String removeTemplate() + public void removeTemplate(ActionEvent event) { try { @@ -305,14 +304,8 @@ public abstract class BaseDetailsBean Utils.addErrorMessage(MessageFormat.format(Application.getMessage( FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e); } - return getReturnOutcome(); } - /** - * @return return to details page JSF navigation outcome - */ - protected abstract String getReturnOutcome(); - /** * Action Handler to take Ownership of the current Space */ diff --git a/source/java/org/alfresco/web/bean/CheckinCheckoutBean.java b/source/java/org/alfresco/web/bean/CheckinCheckoutBean.java index 7db8022d9d..b130c3d788 100644 --- a/source/java/org/alfresco/web/bean/CheckinCheckoutBean.java +++ b/source/java/org/alfresco/web/bean/CheckinCheckoutBean.java @@ -759,7 +759,8 @@ public class CheckinCheckoutBean { try { - tx = Repository.getUserTransaction(FacesContext.getCurrentInstance()); + FacesContext context = FacesContext.getCurrentInstance(); + tx = Repository.getUserTransaction(context); tx.begin(); if (LOGGER.isDebugEnabled()) @@ -778,8 +779,10 @@ public class CheckinCheckoutBean { // add the content to an anonymous but permanent writer location // we can then retrieve the URL to the content to to be set on the node during checkin - ContentWriter writer = this.contentService.getWriter(node.getNodeRef(), ContentModel.PROP_CONTENT, false); - // TODO: Adjust the mimetype + ContentWriter writer = this.contentService.getWriter(node.getNodeRef(), ContentModel.PROP_CONTENT, true); + // also update the mime type in case a different type of file is uploaded + String mimeType = Repository.getMimeTypeForFileName(context, this.fileName); + writer.setMimetype(mimeType); writer.putContent(this.file); contentUrl = writer.getContentUrl(); } @@ -846,7 +849,8 @@ public class CheckinCheckoutBean { try { - tx = Repository.getUserTransaction(FacesContext.getCurrentInstance()); + FacesContext context = FacesContext.getCurrentInstance(); + tx = Repository.getUserTransaction(context); tx.begin(); if (LOGGER.isDebugEnabled()) @@ -854,7 +858,12 @@ public class CheckinCheckoutBean // get an updating writer that we can use to modify the content on the current node ContentWriter writer = this.contentService.getWriter(node.getNodeRef(), ContentModel.PROP_CONTENT, true); - writer.putContent(this.file); + + // also update the mime type in case a different type of file is uploaded + String mimeType = Repository.getMimeTypeForFileName(context, this.fileName); + writer.setMimetype(mimeType); + + writer.putContent(this.file); // commit the transaction tx.commit(); diff --git a/source/java/org/alfresco/web/bean/DocumentDetailsBean.java b/source/java/org/alfresco/web/bean/DocumentDetailsBean.java index f09af0d416..6d8c5eba3a 100644 --- a/source/java/org/alfresco/web/bean/DocumentDetailsBean.java +++ b/source/java/org/alfresco/web/bean/DocumentDetailsBean.java @@ -243,8 +243,8 @@ public class DocumentDetailsBean extends BaseDetailsBean { // we know for now that the general classifiable aspect only will be // applied so we can retrive the categories property direclty - Collection categories = (Collection)this.nodeService.getProperty(getDocument().getNodeRef(), - ContentModel.PROP_CATEGORIES); + Collection categories = (Collection)this.nodeService.getProperty( + getDocument().getNodeRef(), ContentModel.PROP_CATEGORIES); if (categories == null || categories.size() == 0) { @@ -256,16 +256,13 @@ public class DocumentDetailsBean extends BaseDetailsBean MSG_HAS_FOLLOWING_CATEGORIES)); builder.append("

    "); - for (Object obj : categories) + for (NodeRef ref : categories) { - if (obj instanceof NodeRef) + if (this.nodeService.exists(ref)) { - if (this.nodeService.exists((NodeRef)obj)) - { - builder.append("
  • "); - builder.append(Repository.getNameForNode(this.nodeService, (NodeRef)obj)); - builder.append("
  • "); - } + builder.append("
  • "); + builder.append(Repository.getNameForNode(this.nodeService, ref)); + builder.append("
  • "); } } builder.append("
"); @@ -992,14 +989,6 @@ public class DocumentDetailsBean extends BaseDetailsBean { return "document-props"; } - - /** - * @see org.alfresco.web.bean.BaseDetailsBean#getReturnOutcome() - */ - protected String getReturnOutcome() - { - return OUTCOME_RETURN; - } /** * Returns a model for use by a template on the Document Details page. diff --git a/source/java/org/alfresco/web/bean/DocumentPropertiesBean.java b/source/java/org/alfresco/web/bean/DocumentPropertiesBean.java index 27c6cd3ffc..eedbc2560b 100644 --- a/source/java/org/alfresco/web/bean/DocumentPropertiesBean.java +++ b/source/java/org/alfresco/web/bean/DocumentPropertiesBean.java @@ -262,7 +262,7 @@ public class DocumentPropertiesBean String statusMsg = MessageFormat.format( Application.getMessage( FacesContext.getCurrentInstance(), "error_exists"), - e.getExisting().getName()); + e.getName()); Utils.addErrorMessage(statusMsg); // no outcome outcome = null; diff --git a/source/java/org/alfresco/web/bean/ErrorBean.java b/source/java/org/alfresco/web/bean/ErrorBean.java index 6a387f2c36..8cf9b29244 100644 --- a/source/java/org/alfresco/web/bean/ErrorBean.java +++ b/source/java/org/alfresco/web/bean/ErrorBean.java @@ -58,11 +58,22 @@ public class ErrorBean } /** - * @param lastError The lastError to set. + * @param error The lastError to set. */ - public void setLastError(Throwable lastError) + public void setLastError(Throwable error) { - this.lastError = lastError; + // servlet exceptions hide the actual error within the rootCause + // variable, set the base error to that and throw away the + // ServletException wrapper + if (error instanceof ServletException && + ((ServletException)error).getRootCause() != null) + { + this.lastError = ((ServletException)error).getRootCause(); + } + else + { + this.lastError = error; + } } /** @@ -74,29 +85,17 @@ public class ErrorBean if (this.lastError != null) { - StringBuilder builder = null; - Throwable cause = null; - if (this.lastError instanceof ServletException && - ((ServletException)this.lastError).getRootCause() != null) - { - // servlet exception puts the actual error in root cause!! - Throwable actualError = ((ServletException)this.lastError).getRootCause(); - builder = new StringBuilder(actualError.toString()); - cause = actualError.getCause(); - } - else - { - builder = new StringBuilder(this.lastError.toString()); - cause = this.lastError.getCause(); - } + StringBuilder builder = new StringBuilder(this.lastError.toString());; + Throwable cause = this.lastError.getCause(); + // build up stack trace of all causes while (cause != null) { - builder.append("

caused by:
"); + builder.append("\ncaused by:\n"); builder.append(cause.toString()); if (cause instanceof ServletException && - ((ServletException)cause).getRootCause() != null) + ((ServletException)cause).getRootCause() != null) { cause = ((ServletException)cause).getRootCause(); } @@ -107,6 +106,11 @@ public class ErrorBean } message = builder.toString(); + + // format the message for HTML display + message = message.replaceAll("<", "<"); + message = message.replaceAll(">", ">"); + message = message.replaceAll("\n", "
"); } return message; @@ -119,18 +123,14 @@ public class ErrorBean { StringWriter stringWriter = new StringWriter(); PrintWriter writer = new PrintWriter(stringWriter); + this.lastError.printStackTrace(writer); - if (this.lastError instanceof ServletException && - ((ServletException)this.lastError).getRootCause() != null) - { - Throwable actualError = ((ServletException)this.lastError).getRootCause(); - actualError.printStackTrace(writer); - } - else - { - this.lastError.printStackTrace(writer); - } + // format the message for HTML display + String trace = stringWriter.toString(); + trace = trace.replaceAll("<", "<"); + trace = trace.replaceAll(">", ">"); + trace = trace.replaceAll("\n", "
"); - return stringWriter.toString().replaceAll("\r\n", "
"); + return trace; } } diff --git a/source/java/org/alfresco/web/bean/LoginBean.java b/source/java/org/alfresco/web/bean/LoginBean.java index 896d5c117d..17274e42b9 100644 --- a/source/java/org/alfresco/web/bean/LoginBean.java +++ b/source/java/org/alfresco/web/bean/LoginBean.java @@ -279,12 +279,15 @@ public class LoginBean // in the session - this is used by the servlet filter etc. on each page to check for login this.authenticationService.authenticate(this.username, this.password.toCharArray()); + // Set the user name as stored by the back end + this.username = this.authenticationService.getCurrentUserName(); + // remove the session invalidated flag (used to remove last username cookie by AuthenticationFilter) session.remove(AuthenticationHelper.SESSION_INVALIDATED); // setup User object and Home space ID User user = new User( - this.authenticationService.getCurrentUserName(), + this.username, this.authenticationService.getCurrentTicket(), personService.getPerson(this.username)); @@ -325,7 +328,17 @@ public class LoginBean } else { - return "success"; + // special case to handle jump to My Alfresco page initially + String location = Application.getClientConfig(FacesContext.getCurrentInstance()).getInitialLocation(); + if (NavigationBean.LOCATION_MYALFRESCO.equals(location)) + { + return "myalfresco"; + } + else + { + // generally this will navigate to the generic browse screen + return "success"; + } } } catch (AuthenticationException aerr) diff --git a/source/java/org/alfresco/web/bean/NavigationBean.java b/source/java/org/alfresco/web/bean/NavigationBean.java index 57a7f07158..d426a07732 100644 --- a/source/java/org/alfresco/web/bean/NavigationBean.java +++ b/source/java/org/alfresco/web/bean/NavigationBean.java @@ -187,11 +187,93 @@ public class NavigationBean } /** - * @param toolbarLocation The toolbar Location to set. + * @param location The toolbar Location to set. */ - public void setToolbarLocation(String toolbarLocation) + public void setToolbarLocation(String location) { - this.toolbarLocation = toolbarLocation; + processToolbarLocation(location, true); + } + + /** + * Process the selected toolbar location. Setup the breadcrumb with initial value and + * setup the current node ID. This method can also perform the navigatin setup if requested. + * + * @param location Toolbar location constant + * @param navigate True to perform navigation, false otherwise + */ + private void processToolbarLocation(String location, boolean navigate) + { + this.toolbarLocation = location; + + FacesContext context = FacesContext.getCurrentInstance(); + if (LOCATION_COMPANY.equals(location)) + { + List elements = new ArrayList(1); + Node companyHome = getCompanyHomeNode(); + elements.add(new NavigationBreadcrumbHandler(companyHome.getNodeRef(), companyHome.getName())); + setLocation(elements); + setCurrentNodeId(companyHome.getId()); + + // we need to force a navigation to refresh the browse screen breadcrumb + if (navigate) + { + context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_BROWSE); + } + } + else if (LOCATION_HOME.equals(location)) + { + List elements = new ArrayList(1); + String homeSpaceId = Application.getCurrentUser(context).getHomeSpaceId(); + NodeRef homeSpaceRef = new NodeRef(Repository.getStoreRef(), homeSpaceId); + String homeSpaceName = Repository.getNameForNode(this.nodeService, homeSpaceRef); + elements.add(new NavigationBreadcrumbHandler(homeSpaceRef, homeSpaceName)); + setLocation(elements); + setCurrentNodeId(homeSpaceRef.getId()); + + // we need to force a navigation to refresh the browse screen breadcrumb + if (navigate) + { + context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_BROWSE); + } + } + else if (LOCATION_GUEST.equals(location)) + { + List elements = new ArrayList(1); + Node guestHome = getGuestHomeNode(); + elements.add(new NavigationBreadcrumbHandler(guestHome.getNodeRef(), guestHome.getName())); + setLocation(elements); + setCurrentNodeId(guestHome.getId()); + + // we need to force a navigation to refresh the browse screen breadcrumb + if (navigate) + { + context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_BROWSE); + } + } + else if (LOCATION_MYALFRESCO.equals(location)) + { + List elements = new ArrayList(1); + elements.add(new IBreadcrumbHandler() + { + public String navigationOutcome(UIBreadcrumb breadcrumb) + { + setLocation( (List)breadcrumb.getValue() ); + return OUTCOME_MYALFRESCO; + }; + + public String toString() + { + return Application.getMessage(FacesContext.getCurrentInstance(), MSG_MYALFRESCO); + }; + }); + setLocation(elements); + + // we need to force a navigation to refresh the browse screen breadcrumb + if (navigate) + { + context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_MYALFRESCO); + } + } } /** @@ -396,20 +478,20 @@ public class NavigationBean if (diskShare != null) { - ContentContext contentCtx = (ContentContext) diskShare.getContext(); - NodeRef rootNode = contentCtx.getRootNode(); - try - { - String cifsPath = Repository.getNamePath(this.nodeService, path, rootNode, "\\", "file:///" + getCIFSServerPath(diskShare)); - - node.getProperties().put("cifsPath", cifsPath); - node.getProperties().put("cifsPathLabel", cifsPath.substring(8)); // strip file:/// part - } - catch(AccessDeniedException ade) - { - node.getProperties().put("cifsPath", ""); - node.getProperties().put("cifsPathLabel",""); // strip file:/// part - } + ContentContext contentCtx = (ContentContext) diskShare.getContext(); + NodeRef rootNode = contentCtx.getRootNode(); + try + { + String cifsPath = Repository.getNamePath(this.nodeService, path, rootNode, "\\", "file:///" + getCIFSServerPath(diskShare)); + + node.getProperties().put("cifsPath", cifsPath); + node.getProperties().put("cifsPathLabel", cifsPath.substring(8)); // strip file:/// part + } + catch(AccessDeniedException ade) + { + node.getProperties().put("cifsPath", ""); + node.getProperties().put("cifsPathLabel",""); // strip file:/// part + } } this.currentNode = node; @@ -425,26 +507,13 @@ public class NavigationBean { if (this.location == null) { - // set the current node to the users Home Space Id if one has not already been set - NodeRef homeSpaceRef; - List elements = new ArrayList(1); - if (getCurrentNodeId() == null) + // get the initial location from the client config + String initialLocation = clientConfig.getInitialLocation(); + if (initialLocation == null || initialLocation.length() == 0) { - User user = Application.getCurrentUser(FacesContext.getCurrentInstance()); - homeSpaceRef = new NodeRef(Repository.getStoreRef(), user.getHomeSpaceId()); + initialLocation = LOCATION_HOME; } - else - { - homeSpaceRef = new NodeRef(Repository.getStoreRef(), getCurrentNodeId()); - } - - // set initial node ID - setCurrentNodeId(homeSpaceRef.getId()); - - // setup the breadcrumb with the same initial location - String homeSpaceName = Repository.getNameForNode(this.nodeService, homeSpaceRef); - elements.add(new NavigationBreadcrumbHandler(homeSpaceRef, homeSpaceName)); - setLocation(elements); + processToolbarLocation(initialLocation, false); } return this.location; @@ -591,63 +660,6 @@ public class NavigationBean UIModeList locationList = (UIModeList)event.getComponent(); String location = locationList.getValue().toString(); setToolbarLocation(location); - - if (LOCATION_COMPANY.equals(location)) - { - List elements = new ArrayList(1); - Node companyHome = getCompanyHomeNode(); - elements.add(new NavigationBreadcrumbHandler(companyHome.getNodeRef(), companyHome.getName())); - setLocation(elements); - setCurrentNodeId(companyHome.getId()); - - // we need to force a navigation to refresh the browse screen breadcrumb - context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_BROWSE); - } - else if (LOCATION_HOME.equals(location)) - { - List elements = new ArrayList(1); - String homeSpaceId = Application.getCurrentUser(context).getHomeSpaceId(); - NodeRef homeSpaceRef = new NodeRef(Repository.getStoreRef(), homeSpaceId); - String homeSpaceName = Repository.getNameForNode(this.nodeService, homeSpaceRef); - elements.add(new NavigationBreadcrumbHandler(homeSpaceRef, homeSpaceName)); - setLocation(elements); - setCurrentNodeId(homeSpaceRef.getId()); - - // we need to force a navigation to refresh the browse screen breadcrumb - context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_BROWSE); - } - else if (LOCATION_GUEST.equals(location)) - { - List elements = new ArrayList(1); - Node guestHome = getGuestHomeNode(); - elements.add(new NavigationBreadcrumbHandler(guestHome.getNodeRef(), guestHome.getName())); - setLocation(elements); - setCurrentNodeId(guestHome.getId()); - - // we need to force a navigation to refresh the browse screen breadcrumb - context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_BROWSE); - } - else if (LOCATION_DASHBOARD.equals(location)) - { - List elements = new ArrayList(1); - elements.add(new IBreadcrumbHandler() - { - public String navigationOutcome(UIBreadcrumb breadcrumb) - { - setLocation( (List)breadcrumb.getValue() ); - return OUTCOME_MYALFRESCO; - }; - - public String toString() - { - return Application.getMessage(FacesContext.getCurrentInstance(), MSG_MYALFRESCO); - }; - }); - setLocation(elements); - - // we need to force a navigation to refresh the browse screen breadcrumb - context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_MYALFRESCO); - } } catch (InvalidNodeRefException refErr) { @@ -686,6 +698,22 @@ public class NavigationBean return this.cifsServerPath; } + /** + * @return true if the current space has an RSS feed applied + */ + public boolean isRSSFeed() + { + return SpaceDetailsBean.hasRSSFeed(getCurrentNode()); + } + + /** + * @return RSS Feed URL for the current space + */ + public String getRSSFeedURL() + { + return SpaceDetailsBean.buildRSSFeedURL(getCurrentNode()); + } + // ------------------------------------------------------------------------------ // Private helpers @@ -770,10 +798,10 @@ public class NavigationBean private static Logger s_logger = Logger.getLogger(NavigationBean.class); /** constant values used by the toolbar location modelist control */ - private static final String LOCATION_COMPANY = "company"; - private static final String LOCATION_HOME = "home"; - private static final String LOCATION_GUEST = "guest"; - private static final String LOCATION_DASHBOARD = "dashboard"; + static final String LOCATION_COMPANY = "companyhome"; + static final String LOCATION_HOME = "userhome"; + static final String LOCATION_GUEST = "guesthome"; + static final String LOCATION_MYALFRESCO = "myalfresco"; private static final String MSG_MYALFRESCO = "my_alfresco"; diff --git a/source/java/org/alfresco/web/bean/SpaceDetailsBean.java b/source/java/org/alfresco/web/bean/SpaceDetailsBean.java index d55818b869..234a37ecdb 100644 --- a/source/java/org/alfresco/web/bean/SpaceDetailsBean.java +++ b/source/java/org/alfresco/web/bean/SpaceDetailsBean.java @@ -34,6 +34,7 @@ import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; import org.alfresco.web.app.AlfrescoNavigationHandler; import org.alfresco.web.app.Application; +import org.alfresco.web.app.servlet.TemplateContentServlet; import org.alfresco.web.bean.repository.Node; import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.ui.common.Utils; @@ -46,8 +47,6 @@ import org.alfresco.web.ui.common.component.UIActionLink; */ public class SpaceDetailsBean extends BaseDetailsBean { - private static final String OUTCOME_RETURN = "showSpaceDetails"; - private static final String MSG_HAS_FOLLOWING_CATEGORIES = "has_following_categories_space"; private static final String MSG_NO_CATEGORIES_APPLIED = "no_categories_applied_space"; private static final String MSG_ERROR_UPDATE_CATEGORY = "error_update_category"; @@ -60,6 +59,9 @@ public class SpaceDetailsBean extends BaseDetailsBean private NodeRef addedCategory; private List categories; + /** RSS Template ID */ + private String rssTemplate; + // ------------------------------------------------------------------------------ // Construction @@ -149,14 +151,6 @@ public class SpaceDetailsBean extends BaseDetailsBean { return "space-props"; } - - /** - * @see org.alfresco.web.bean.BaseDetailsBean#getReturnOutcome() - */ - protected String getReturnOutcome() - { - return OUTCOME_RETURN; - } // ------------------------------------------------------------------------------ @@ -306,8 +300,8 @@ public class SpaceDetailsBean extends BaseDetailsBean { // we know for now that the general classifiable aspect only will be // applied so we can retrive the categories property direclty - Collection categories = (Collection)this.nodeService.getProperty(getSpace().getNodeRef(), - ContentModel.PROP_CATEGORIES); + Collection categories = (Collection)this.nodeService.getProperty( + getSpace().getNodeRef(), ContentModel.PROP_CATEGORIES); if (categories == null || categories.size() == 0) { @@ -319,16 +313,13 @@ public class SpaceDetailsBean extends BaseDetailsBean MSG_HAS_FOLLOWING_CATEGORIES)); builder.append("
    "); - for (Object obj : categories) + for (NodeRef ref : categories) { - if (obj instanceof NodeRef) + if (this.nodeService.exists(ref)) { - if (this.nodeService.exists((NodeRef)obj)) - { - builder.append("
  • "); - builder.append(Repository.getNameForNode(this.nodeService, (NodeRef)obj)); - builder.append("
  • "); - } + builder.append("
  • "); + builder.append(Repository.getNameForNode(this.nodeService, ref)); + builder.append("
  • "); } } builder.append("
"); @@ -474,4 +465,117 @@ public class SpaceDetailsBean extends BaseDetailsBean { return getSpace().isLocked(); } + + /** + * @return true if the current space has an RSS feed applied + */ + public boolean isRSSFeed() + { + return hasRSSFeed(getSpace()); + } + + /** + * @return true if the current space has an RSS feed applied + */ + public static boolean hasRSSFeed(Node space) + { + return (space.hasAspect(ContentModel.ASPECT_FEEDSOURCE) && + space.getProperties().get(ContentModel.PROP_FEEDTEMPLATE) != null); + } + + /** + * @return RSS Feed URL for the current space + */ + public String getRSSFeedURL() + { + return buildRSSFeedURL(getSpace()); + } + + /** + * Build URL for an RSS space based on the 'feedsource' aspect property. + * + * @param space Node to build RSS template URL for + * + * @return URL for the RSS feed for a space + */ + public static String buildRSSFeedURL(Node space) + { + // build RSS feed template URL from selected template and the space NodeRef and + // add the guest=true URL parameter - this is required for no login access and + // add the mimetype=text/xml URL parameter - required to return correct stream type + return TemplateContentServlet.generateURL(space.getNodeRef(), + (NodeRef)space.getProperties().get(ContentModel.PROP_FEEDTEMPLATE)) + + "/rss.xml?guest=true" + "&mimetype=text%2Fxml"; + } + + /** + * @return Returns the current RSS Template ID. + */ + public String getRSSTemplate() + { + // return current template if it exists + NodeRef ref = (NodeRef)getNode().getProperties().get(ContentModel.PROP_FEEDTEMPLATE); + return ref != null ? ref.getId() : this.rssTemplate; + } + + /** + * @param rssTemplate The RSS Template Id to set. + */ + public void setRSSTemplate(String rssTemplate) + { + this.rssTemplate = rssTemplate; + } + + /** + * Action handler to apply the selected RSS Template and FeedSource aspect to the current Space + */ + public void applyRSSTemplate(ActionEvent event) + { + if (this.rssTemplate != null && this.rssTemplate.equals(TemplateSupportBean.NO_SELECTION) == false) + { + try + { + // apply the feedsource aspect if required + if (getNode().hasAspect(ContentModel.ASPECT_FEEDSOURCE) == false) + { + this.nodeService.addAspect(getNode().getNodeRef(), ContentModel.ASPECT_FEEDSOURCE, null); + } + + // get the selected template Id from the Template Picker + NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.rssTemplate); + + // set the template NodeRef into the templatable aspect property + this.nodeService.setProperty(getNode().getNodeRef(), ContentModel.PROP_FEEDTEMPLATE, templateRef); + + // reset node details for next refresh of details page + getNode().reset(); + } + catch (Exception e) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e); + } + } + } + + /** + * Action handler to remove a RSS template from the current Space + */ + public void removeRSSTemplate(ActionEvent event) + { + try + { + // clear template property + this.nodeService.setProperty(getNode().getNodeRef(), ContentModel.PROP_FEEDTEMPLATE, null); + this.nodeService.removeAspect(getNode().getNodeRef(), ContentModel.ASPECT_FEEDSOURCE); + + // reset node details for next refresh of details page + getNode().reset(); + } + catch (Exception e) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e); + } + } } diff --git a/source/java/org/alfresco/web/bean/TemplateMailHelperBean.java b/source/java/org/alfresco/web/bean/TemplateMailHelperBean.java new file mode 100644 index 0000000000..6bc9834053 --- /dev/null +++ b/source/java/org/alfresco/web/bean/TemplateMailHelperBean.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.web.bean; + +import java.text.MessageFormat; +import java.util.Map; + +import javax.faces.context.FacesContext; +import javax.faces.event.ActionEvent; +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.TemplateNode; +import org.alfresco.web.app.Application; +import org.alfresco.web.bean.repository.Repository; +import org.alfresco.web.ui.common.Utils; +import org.alfresco.web.ui.repo.component.template.DefaultModelHelper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.mail.javamail.MimeMessagePreparator; + +/** + * @author Kevin Roast + */ +public class TemplateMailHelperBean +{ + private static Log logger = LogFactory.getLog(TemplateMailHelperBean.class); + + /** JavaMailSender bean reference */ + protected JavaMailSender mailSender; + + /** NodeService bean reference */ + protected NodeService nodeService; + + /** dialog state */ + private String subject = null; + private String body = null; + private String automaticText = null; + private String template = null; + private String usingTemplate = null; + private String finalBody; + + /** + * @param mailSender The JavaMailSender to set. + */ + public void setMailSender(JavaMailSender mailSender) + { + this.mailSender = mailSender; + } + + /** + * @param nodeService The nodeService to set. + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Initialises the bean + */ + public TemplateMailHelperBean() + { + subject = ""; + body = ""; + automaticText = ""; + template = null; + usingTemplate = null; + } + + /** + * Send an email notification to the specified User authority + * + * @param person Person node representing the user + * @param node Node they are invited too + * @param from From text message + * @param roleText The role display label for the user invite notification + */ + public void notifyUser(NodeRef person, NodeRef node, final String from, String roleText) + { + final String to = (String)this.nodeService.getProperty(person, ContentModel.PROP_EMAIL); + + if (to != null && to.length() != 0) + { + String body = this.body; + if (this.usingTemplate != null) + { + FacesContext fc = FacesContext.getCurrentInstance(); + + // use template service to format the email + NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.usingTemplate); + ServiceRegistry services = Repository.getServiceRegistry(fc); + Map model = DefaultModelHelper.buildDefaultModel( + services, Application.getCurrentUser(fc), templateRef); + model.put("role", roleText); + model.put("space", new TemplateNode(node, Repository.getServiceRegistry(fc), null)); + + body = services.getTemplateService().processTemplate("freemarker", templateRef.toString(), model); + } + this.finalBody = body; + + MimeMessagePreparator mailPreparer = new MimeMessagePreparator() + { + public void prepare(MimeMessage mimeMessage) throws MessagingException + { + MimeMessageHelper message = new MimeMessageHelper(mimeMessage); + message.setTo(to); + message.setSubject(subject); + message.setText(finalBody); + message.setFrom(from); + } + }; + + if (logger.isDebugEnabled()) + logger.debug("Sending notification email to: " + to + "\n...with subject:\n" + subject + "\n...with body:\n" + body); + + try + { + // Send the message + this.mailSender.send(mailPreparer); + } + catch (Throwable e) + { + // don't stop the action but let admins know email is not getting sent + logger.error("Failed to send email to " + to, e); + } + } + } + + /** + * Action handler called to insert a template as the email body + */ + public void insertTemplate(ActionEvent event) + { + if (this.template != null && this.template.equals(TemplateSupportBean.NO_SELECTION) == false) + { + // get the content of the template so the user can get a basic preview of it + try + { + NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.template); + ContentService cs = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getContentService(); + ContentReader reader = cs.getReader(templateRef, ContentModel.PROP_CONTENT); + if (reader != null && reader.exists()) + { + this.body = reader.getContentString(); + + this.usingTemplate = this.template; + } + } + catch (Throwable err) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); + } + } + } + + /** + * Action handler called to discard the template from the email body + */ + public void discardTemplate(ActionEvent event) + { + this.body = this.automaticText; + usingTemplate = null; + } + + /** + * @return Returns the email body text. + */ + public String getBody() + { + return this.body; + } + + /** + * @param body The email body text to set. + */ + public void setBody(String body) + { + this.body = body; + } + + /** + * @return Returns the email subject text. + */ + public String getSubject() + { + return this.subject; + } + + /** + * @param subject The email subject text to set. + */ + public void setSubject(String subject) + { + this.subject = subject; + } + + /** + * @return Returns the automatic text. + */ + public String getAutomaticText() + { + return this.automaticText; + } + + /** + * @param automaticText The automatic text to set. + */ + public void setAutomaticText(String automaticText) + { + this.automaticText = automaticText; + } + + /** + * @return Returns the email template Id + */ + public String getTemplate() + { + return this.template; + } + + /** + * @param template The email template to set. + */ + public void setTemplate(String template) + { + this.template = template; + } + + /** + * @return Returns if a template has been inserted by a user for email body. + */ + public String getUsingTemplate() + { + return this.usingTemplate; + } + + /** + * @param usingTemplate Template that has been inserted by a user for the email body. + */ + public void setUsingTemplate(String usingTemplate) + { + this.usingTemplate = usingTemplate; + } +} diff --git a/source/java/org/alfresco/web/bean/TemplateSupportBean.java b/source/java/org/alfresco/web/bean/TemplateSupportBean.java index 01f5e59519..29aed02a2a 100644 --- a/source/java/org/alfresco/web/bean/TemplateSupportBean.java +++ b/source/java/org/alfresco/web/bean/TemplateSupportBean.java @@ -64,6 +64,9 @@ public class TemplateSupportBean /** cache of email templates that lasts 30 seconds - enough for a few page refreshes */ private ExpiringValueCache> emailTemplates = new ExpiringValueCache>(1000*30); + /** cache of RSS templates that lasts 30 seconds - enough for a few page refreshes */ + private ExpiringValueCache> rssTemplates = new ExpiringValueCache>(1000*30); + /** cache of JavaScript files that lasts 30 seconds - enough for a few page refreshes */ private ExpiringValueCache> scriptFiles = new ExpiringValueCache>(1000*30); @@ -128,6 +131,28 @@ public class TemplateSupportBean return templates; } + /** + * @return the list of available RSS Templates. + */ + public List getRSSTemplates() + { + List templates = rssTemplates.get(); + if (templates == null) + { + // get the template from the special Email Templates folder + FacesContext fc = FacesContext.getCurrentInstance(); + String xpath = Application.getRootPath(fc) + "/" + + Application.getGlossaryFolderName(fc) + "/" + + Application.getRSSTemplatesFolderName(fc) + "//*"; + + templates = selectDictionaryNodes(fc, xpath, MSG_SELECT_TEMPLATE); + + rssTemplates.put(templates); + } + + return templates; + } + /** * @return the list of available JavaScript files that can be applied to the current document. */ diff --git a/source/java/org/alfresco/web/bean/actions/BaseActionWizard.java b/source/java/org/alfresco/web/bean/actions/BaseActionWizard.java index 0549966b2c..219018ca69 100644 --- a/source/java/org/alfresco/web/bean/actions/BaseActionWizard.java +++ b/source/java/org/alfresco/web/bean/actions/BaseActionWizard.java @@ -63,7 +63,10 @@ public abstract class BaseActionWizard extends BaseWizardBean protected List actions; protected List transformers; protected List imageTransformers; - protected List aspects; + protected List commonAspects; + protected List removableAspects; + protected List addableAspects; + protected List testableAspects; protected List users; protected List encodings; protected List objectTypes; @@ -193,7 +196,12 @@ public abstract class BaseActionWizard extends BaseWizardBean this.actions = new ArrayList(); for (ActionDefinition ruleActionDef : ruleActions) { - this.actions.add(new SelectItem(ruleActionDef.getName(), ruleActionDef.getTitle())); + String title = ruleActionDef.getTitle(); + if (title == null || title.length() == 0) + { + title = ruleActionDef.getName(); + } + this.actions.add(new SelectItem(ruleActionDef.getName(), title)); } // make sure the list is sorted by the label @@ -209,70 +217,129 @@ public abstract class BaseActionWizard extends BaseWizardBean } /** - * Returns the aspects that are available + * Returns a list of aspects that can be removed * - * @return List of SelectItem objects representing the available aspects + * @return List of SelectItem objects representing the aspects that can be removed */ - public List getAspects() + public List getRemovableAspects() { - if (this.aspects == null) + if (this.removableAspects == null) { + // get the list of common aspects + this.removableAspects = new ArrayList(); + this.removableAspects.addAll(getCommonAspects()); + + // get those aspects configured to appear only in the remove aspect action ConfigService svc = Application.getConfigService(FacesContext.getCurrentInstance()); Config wizardCfg = svc.getConfig("Action Wizards"); if (wizardCfg != null) { - ConfigElement aspectsCfg = wizardCfg.getConfigElement("aspects"); + ConfigElement aspectsCfg = wizardCfg.getConfigElement("aspects-remove"); if (aspectsCfg != null) { - FacesContext context = FacesContext.getCurrentInstance(); - this.aspects = new ArrayList(); - for (ConfigElement child : aspectsCfg.getChildren()) - { - QName idQName = Repository.resolveToQName(child.getAttribute("name")); - - if (idQName != null) - { - // try and get the display label from config - String label = Utils.getDisplayLabel(context, child); - - // if there wasn't a client based label try and get it from the dictionary - if (label == null) - { - AspectDefinition aspectDef = this.dictionaryService.getAspect(idQName); - if (aspectDef != null) - { - label = aspectDef.getTitle(); - } - else - { - label = idQName.getLocalName(); - } - } - - this.aspects.add(new SelectItem(idQName.toString(), label)); - } - else - { - logger.warn("Failed to resolve aspect '" + child.getAttribute("name") + "'"); - } - } - - // make sure the list is sorted by the label - QuickSort sorter = new QuickSort(this.aspects, "label", true, IDataContainer.SORT_CASEINSENSITIVE); - sorter.sort(); + List aspects = readAspectsConfig(FacesContext.getCurrentInstance(), aspectsCfg); + this.removableAspects.addAll(aspects); } else { - logger.warn("Could not find 'aspects' configuration element"); + logger.warn("Could not find 'aspects-remove' configuration element"); } } else { logger.warn("Could not find 'Action Wizards' configuration section"); } + + // make sure the list is sorted by the label + QuickSort sorter = new QuickSort(this.removableAspects, "label", true, IDataContainer.SORT_CASEINSENSITIVE); + sorter.sort(); } - return this.aspects; + return this.removableAspects; + } + + /** + * Returns a list of aspects that can be added + * + * @return List of SelectItem objects representing the aspects that can be added + */ + public List getAddableAspects() + { + if (this.addableAspects == null) + { + // get the list of common aspects + this.addableAspects = new ArrayList(); + this.addableAspects.addAll(getCommonAspects()); + + // get those aspects configured to appear only in the remove aspect action + ConfigService svc = Application.getConfigService(FacesContext.getCurrentInstance()); + Config wizardCfg = svc.getConfig("Action Wizards"); + if (wizardCfg != null) + { + ConfigElement aspectsCfg = wizardCfg.getConfigElement("aspects-add"); + if (aspectsCfg != null) + { + List aspects = readAspectsConfig(FacesContext.getCurrentInstance(), aspectsCfg); + this.addableAspects.addAll(aspects); + } + else + { + logger.warn("Could not find 'aspects-add' configuration element"); + } + } + else + { + logger.warn("Could not find 'Action Wizards' configuration section"); + } + + // make sure the list is sorted by the label + QuickSort sorter = new QuickSort(this.addableAspects, "label", true, IDataContainer.SORT_CASEINSENSITIVE); + sorter.sort(); + } + + return this.addableAspects; + } + + /** + * Returns a list of aspects that can be tested i.e. hasAspect + * + * @return List of SelectItem objects representing the aspects that can be tested for + */ + public List getTestableAspects() + { + if (this.testableAspects == null) + { + // get the list of common aspects + this.testableAspects = new ArrayList(); + this.testableAspects.addAll(getCommonAspects()); + + // get those aspects configured to appear only in the remove aspect action + ConfigService svc = Application.getConfigService(FacesContext.getCurrentInstance()); + Config wizardCfg = svc.getConfig("Action Wizards"); + if (wizardCfg != null) + { + ConfigElement aspectsCfg = wizardCfg.getConfigElement("aspects-test"); + if (aspectsCfg != null) + { + List aspects = readAspectsConfig(FacesContext.getCurrentInstance(), aspectsCfg); + this.testableAspects.addAll(aspects); + } + else + { + logger.warn("Could not find 'aspects-test' configuration element"); + } + } + else + { + logger.warn("Could not find 'Action Wizards' configuration section"); + } + + // make sure the list is sorted by the label + QuickSort sorter = new QuickSort(this.testableAspects, "label", true, IDataContainer.SORT_CASEINSENSITIVE); + sorter.sort(); + } + + return this.testableAspects; } /** @@ -846,6 +913,77 @@ public abstract class BaseActionWizard extends BaseWizardBean } } + /** + * Returns the aspects that are available in all scenarios i.e. add, remove and test + * + * @return List of SelectItem objects representing the available aspects + */ + protected List getCommonAspects() + { + if (this.commonAspects == null) + { + ConfigService svc = Application.getConfigService(FacesContext.getCurrentInstance()); + Config wizardCfg = svc.getConfig("Action Wizards"); + if (wizardCfg != null) + { + ConfigElement aspectsCfg = wizardCfg.getConfigElement("aspects"); + if (aspectsCfg != null) + { + this.commonAspects = readAspectsConfig(FacesContext.getCurrentInstance(), aspectsCfg); + } + else + { + logger.warn("Could not find 'aspects' configuration element"); + } + } + else + { + logger.warn("Could not find 'Action Wizards' configuration section"); + } + } + + return this.commonAspects; + } + + + protected List readAspectsConfig(FacesContext context, ConfigElement aspectsCfg) + { + List aspects = new ArrayList(); + + for (ConfigElement child : aspectsCfg.getChildren()) + { + QName idQName = Repository.resolveToQName(child.getAttribute("name")); + + if (idQName != null) + { + // try and get the display label from config + String label = Utils.getDisplayLabel(context, child); + + // if there wasn't a client based label try and get it from the dictionary + if (label == null) + { + AspectDefinition aspectDef = this.dictionaryService.getAspect(idQName); + if (aspectDef != null) + { + label = aspectDef.getTitle(); + } + else + { + label = idQName.getLocalName(); + } + } + + aspects.add(new SelectItem(idQName.toString(), label)); + } + else + { + logger.warn("Failed to resolve aspect '" + child.getAttribute("name") + "'"); + } + } + + return aspects; + } + // ------------------------------------------------------------------------------ // Inner classes diff --git a/source/java/org/alfresco/web/bean/actions/handlers/AddFeaturesHandler.java b/source/java/org/alfresco/web/bean/actions/handlers/AddFeaturesHandler.java index baf02081b6..edd553e04e 100644 --- a/source/java/org/alfresco/web/bean/actions/handlers/AddFeaturesHandler.java +++ b/source/java/org/alfresco/web/bean/actions/handlers/AddFeaturesHandler.java @@ -49,7 +49,7 @@ public class AddFeaturesHandler extends BaseActionHandler String aspect = (String)actionProps.get(PROP_ASPECT); // find the label used by looking through the SelectItem list - for (SelectItem item : ((BaseActionWizard)wizard).getAspects()) + for (SelectItem item : ((BaseActionWizard)wizard).getAddableAspects()) { if (item.getValue().equals(aspect)) { diff --git a/source/java/org/alfresco/web/bean/actions/handlers/RemoveFeaturesHandler.java b/source/java/org/alfresco/web/bean/actions/handlers/RemoveFeaturesHandler.java index ed0f329c37..9029000ced 100644 --- a/source/java/org/alfresco/web/bean/actions/handlers/RemoveFeaturesHandler.java +++ b/source/java/org/alfresco/web/bean/actions/handlers/RemoveFeaturesHandler.java @@ -49,7 +49,7 @@ public class RemoveFeaturesHandler extends BaseActionHandler String aspect = (String)actionProps.get(PROP_ASPECT); // find the label used by looking through the SelectItem list - for (SelectItem item : ((BaseActionWizard)wizard).getAspects()) + for (SelectItem item : ((BaseActionWizard)wizard).getRemovableAspects()) { if (item.getValue().equals(aspect)) { diff --git a/source/java/org/alfresco/web/bean/actions/handlers/ScriptHandler.java b/source/java/org/alfresco/web/bean/actions/handlers/ScriptHandler.java index d7b2c1f785..41321fba6e 100644 --- a/source/java/org/alfresco/web/bean/actions/handlers/ScriptHandler.java +++ b/source/java/org/alfresco/web/bean/actions/handlers/ScriptHandler.java @@ -35,11 +35,6 @@ public class ScriptHandler extends BaseActionHandler String id = (String)actionProps.get(PROP_SCRIPT); NodeRef scriptRef = new NodeRef(Repository.getStoreRef(), id); repoProps.put(ScriptActionExecutor.PARAM_SCRIPTREF, scriptRef); - - NavigationBean navBean = (NavigationBean)FacesHelper.getManagedBean( - FacesContext.getCurrentInstance(), "NavigationBean"); - repoProps.put(ScriptActionExecutor.PARAM_SPACEREF, - navBean.getCurrentNode().getNodeRef()); } public void prepareForEdit(Map actionProps, diff --git a/source/java/org/alfresco/web/bean/ajax/XFormsBean.java b/source/java/org/alfresco/web/bean/ajax/XFormsBean.java index 81d3ea3cc4..d596cb69b0 100644 --- a/source/java/org/alfresco/web/bean/ajax/XFormsBean.java +++ b/source/java/org/alfresco/web/bean/ajax/XFormsBean.java @@ -196,6 +196,7 @@ public class XFormsBean * HTTPConnectors. Instance loading and submission then uses these cookies. Important for * applications using auth. */ + @SuppressWarnings("unchecked") private static void storeCookies(final javax.servlet.http.Cookie[] cookiesIn, final ChibaBean chibaBean){ if (cookiesIn != null) { diff --git a/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java b/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java index 7471bd3ba2..89c9b0c81f 100644 --- a/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java +++ b/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java @@ -28,6 +28,9 @@ import javax.faces.event.ActionEvent; import javax.transaction.UserTransaction; import org.alfresco.model.ContentModel; +import org.alfresco.repo.search.QueryParameterDefImpl; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.model.FileExistsException; import org.alfresco.service.cmr.model.FileFolderService; @@ -38,6 +41,8 @@ import org.alfresco.service.cmr.repository.CopyService; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.search.QueryParameterDefinition; +import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.QName; import org.alfresco.web.app.Application; import org.alfresco.web.app.context.UIContextService; @@ -81,6 +86,14 @@ public class ClipboardBean this.copyService = copyService; } + /** + * @param searchService The SearchService to set. + */ + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + /** * @param navigator The NavigationBean to set. */ @@ -244,6 +257,12 @@ public class ClipboardBean // initial name to attempt the copy of the item with String name = item.Node.getName(); + if (action == UIClipboardShelfItem.ACTION_PASTE_LINK) + { + // copy as link was specifically requested by the user + String linkTo = Application.getMessage(FacesContext.getCurrentInstance(), MSG_LINK_TO); + name = linkTo + ' ' + name; + } boolean operationComplete = false; while (operationComplete == false) @@ -254,56 +273,58 @@ public class ClipboardBean { if (action == UIClipboardShelfItem.ACTION_PASTE_LINK) { + // LINK operation if (logger.isDebugEnabled()) logger.debug("Attempting to link node ID: " + item.Node.getId() + " into node ID: " + destRef.getId()); - // copy as link was specifically requested by the user - // we create a special Link Object node that has a property to reference the original - // use FileFolderService to check if already exists as using nodeService directly here - String linkTo = Application.getMessage(FacesContext.getCurrentInstance(), MSG_LINK_TO); - // create the node using the nodeService (can only use FileFolderService for content) - Map props = new HashMap(4, 1.0f); - String linkName = linkTo + ' ' + name; - props.put(ContentModel.PROP_NAME, linkName + ".lnk"); - props.put(ContentModel.PROP_LINK_DESTINATION, item.Node.getNodeRef()); - if (dd.isSubClass(item.Node.getType(), ContentModel.TYPE_CONTENT)) + if (checkExists(name + ".lnk", destRef) == false) { - // create File Link node - ChildAssociationRef childRef = this.nodeService.createNode( - destRef, - ContentModel.ASSOC_CONTAINS, - assocRef.getQName(), - ContentModel.TYPE_FILELINK, - props); + Map props = new HashMap(2, 1.0f); + props.put(ContentModel.PROP_NAME, name + ".lnk"); + props.put(ContentModel.PROP_LINK_DESTINATION, item.Node.getNodeRef()); + if (dd.isSubClass(item.Node.getType(), ContentModel.TYPE_CONTENT)) + { + // create File Link node + ChildAssociationRef childRef = this.nodeService.createNode( + destRef, + ContentModel.ASSOC_CONTAINS, + assocRef.getQName(), + ContentModel.TYPE_FILELINK, + props); + + // apply the titled aspect - title and description + Map titledProps = new HashMap(2, 1.0f); + titledProps.put(ContentModel.PROP_TITLE, name); + titledProps.put(ContentModel.PROP_DESCRIPTION, name); + this.nodeService.addAspect(childRef.getChildRef(), ContentModel.ASPECT_TITLED, titledProps); + } + else + { + // create Folder link node + ChildAssociationRef childRef = this.nodeService.createNode( + destRef, + ContentModel.ASSOC_CONTAINS, + assocRef.getQName(), + ContentModel.TYPE_FOLDERLINK, + props); + + // apply the uifacets aspect - icon, title and description props + Map uiFacetsProps = new HashMap(4, 1.0f); + uiFacetsProps.put(ContentModel.PROP_ICON, "space-icon-link"); + uiFacetsProps.put(ContentModel.PROP_TITLE, name); + uiFacetsProps.put(ContentModel.PROP_DESCRIPTION, name); + this.nodeService.addAspect(childRef.getChildRef(), ContentModel.ASPECT_UIFACETS, uiFacetsProps); + } - // apply the titled aspect - title and description - Map titledProps = new HashMap(2, 1.0f); - titledProps.put(ContentModel.PROP_TITLE, linkName); - titledProps.put(ContentModel.PROP_DESCRIPTION, linkName); - this.nodeService.addAspect(childRef.getChildRef(), ContentModel.ASPECT_TITLED, titledProps); - } - else - { - // create Folder link node - ChildAssociationRef childRef = this.nodeService.createNode( - destRef, - ContentModel.ASSOC_CONTAINS, - assocRef.getQName(), - ContentModel.TYPE_FOLDERLINK, - props); - - // apply the uifacets aspect - icon, title and description props - Map uiFacetsProps = new HashMap(3, 1.0f); - uiFacetsProps.put(ContentModel.PROP_ICON, "space-icon-link"); - uiFacetsProps.put(ContentModel.PROP_TITLE, linkName); - uiFacetsProps.put(ContentModel.PROP_DESCRIPTION, linkName); - this.nodeService.addAspect(childRef.getChildRef(), ContentModel.ASPECT_UIFACETS, uiFacetsProps); + // if we get here without an exception, the clipboard link operation was successful + operationComplete = true; } } else { + // COPY operation if (logger.isDebugEnabled()) logger.debug("Attempting to copy node ID: " + item.Node.getId() + " into node ID: " + destRef.getId()); @@ -326,17 +347,24 @@ public class ClipboardBean else { // copy the node - this.copyService.copy( - item.Node.getNodeRef(), - destRef, - ContentModel.ASSOC_CONTAINS, - assocRef.getQName(), - true); + if (checkExists(name, destRef) == false) + { + this.copyService.copy( + item.Node.getNodeRef(), + destRef, + ContentModel.ASSOC_CONTAINS, + assocRef.getQName(), + true); + } } + + // if we get here without an exception, the clipboard copy operation was successful + operationComplete = true; } } else { + // MOVE operation if (logger.isDebugEnabled()) logger.debug("Attempting to move node ID: " + item.Node.getId() + " into node ID: " + destRef.getId()); @@ -358,27 +386,57 @@ public class ClipboardBean ContentModel.ASSOC_CONTAINS, assocRef.getQName()); } + + // if we get here without an exception, the clipboard move operation was successful + operationComplete = true; } - - // if we get here without an exception, the clipboard operation was successful - operationComplete = true; } catch (FileExistsException fileExistsErr) { - if (item.Mode == ClipboardStatus.COPY) + if (item.Mode != ClipboardStatus.COPY) { - String copyOf = Application.getMessage(FacesContext.getCurrentInstance(), MSG_COPY_OF); - name = copyOf + ' ' + name; - } - else - { - // we should not rename an item when it is being moved + // we should not rename an item when it is being moved - so exit throw fileExistsErr; } } + if (operationComplete == false) + { + String copyOf = Application.getMessage(FacesContext.getCurrentInstance(), MSG_COPY_OF); + name = copyOf + ' ' + name; + } } } + private boolean checkExists(String name, NodeRef parent) + { + ServiceRegistry services = Repository.getServiceRegistry(FacesContext.getCurrentInstance()); + + QueryParameterDefinition[] params = new QueryParameterDefinition[1]; + params[0] = new QueryParameterDefImpl( + ContentModel.PROP_NAME, + services.getDictionaryService().getDataType( + DataTypeDefinition.TEXT), + true, + name); + + // execute the query + List nodeRefs = searchService.selectNodes( + parent, + XPATH_QUERY_NODE_MATCH, + params, + services.getNamespaceService(), + false); + + return (nodeRefs.size() != 0); + } + + /** Shallow search for nodes with a name pattern */ + private static final String XPATH_QUERY_NODE_MATCH = + "./*" + + "[like(@cm:name, $cm:name, false)]";// + + //" and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" + + //" and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "'))]"; + /** * Add a clipboard node for an operation to the clipboard * @@ -437,6 +495,9 @@ public class ClipboardBean /** The CopyService to be used by the bean */ protected CopyService copyService; + /** The SearchService to be used by the bean */ + protected SearchService searchService; + /** The NavigationBean reference */ protected NavigationBean navigator; diff --git a/source/java/org/alfresco/web/bean/content/EditContentPropertiesDialog.java b/source/java/org/alfresco/web/bean/content/EditContentPropertiesDialog.java index 04cc367d9b..edaa8f7dea 100644 --- a/source/java/org/alfresco/web/bean/content/EditContentPropertiesDialog.java +++ b/source/java/org/alfresco/web/bean/content/EditContentPropertiesDialog.java @@ -198,7 +198,7 @@ public class EditContentPropertiesDialog extends BaseDialogBean { return MessageFormat.format(Application.getMessage( FacesContext.getCurrentInstance(), Repository.ERROR_EXISTS), - ((FileExistsException)exception).getExisting().getName()); + ((FileExistsException)exception).getName()); } else if (exception instanceof InvalidNodeRefException) { diff --git a/source/java/org/alfresco/web/bean/dashboard/PageConfig.java b/source/java/org/alfresco/web/bean/dashboard/PageConfig.java index f59b271a76..65d8dbed76 100644 --- a/source/java/org/alfresco/web/bean/dashboard/PageConfig.java +++ b/source/java/org/alfresco/web/bean/dashboard/PageConfig.java @@ -42,7 +42,7 @@ import org.dom4j.io.XMLWriter; * * @author Kevin Roast */ -final class PageConfig +public final class PageConfig { private static Log logger = LogFactory.getLog(DashboardManager.class); @@ -55,7 +55,7 @@ final class PageConfig private static final String ATTR_REFID = "idref"; private List pages = new ArrayList(4); - private int currentPageIndex = 0; + private Page currentPage = null; /** @@ -86,13 +86,30 @@ final class PageConfig */ public Page getCurrentPage() { - if (currentPageIndex < pages.size()) + if (this.currentPage == null) { - return pages.get(currentPageIndex); + if (this.pages.size() != 0) + { + this.currentPage = pages.get(0); + } } - else + return this.currentPage; + } + + /** + * Set the current Page for the cnfig + * + * @param pageId ID of the page to set as current + */ + public void setCurrentPage(String pageId) + { + for (Page page : pages) { - return null; + if (page.getId().equals(pageId)) + { + this.currentPage = page; + break; + } } } diff --git a/source/java/org/alfresco/web/bean/dialog/BaseDialogBean.java b/source/java/org/alfresco/web/bean/dialog/BaseDialogBean.java index b0c4918589..586eb6bd83 100644 --- a/source/java/org/alfresco/web/bean/dialog/BaseDialogBean.java +++ b/source/java/org/alfresco/web/bean/dialog/BaseDialogBean.java @@ -2,6 +2,7 @@ package org.alfresco.web.bean.dialog; import java.text.MessageFormat; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.faces.context.FacesContext; @@ -19,6 +20,7 @@ import org.alfresco.web.app.context.UIContextService; import org.alfresco.web.bean.BrowseBean; import org.alfresco.web.bean.NavigationBean; import org.alfresco.web.bean.repository.Repository; +import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig; import org.alfresco.web.ui.common.Utils; /** @@ -96,13 +98,18 @@ public abstract class BaseDialogBean implements IDialogBean // rollback the transaction try { if (tx != null) {tx.rollback();} } catch (Exception ex) {} - Utils.addErrorMessage(formatErrorMessage(e)); + Utils.addErrorMessage(formatErrorMessage(e), e); outcome = getErrorOutcome(e); } } return outcome; } + + public List getAdditionalButtons() + { + return null; + } public String getCancelButtonLabel() { diff --git a/source/java/org/alfresco/web/bean/dialog/DialogManager.java b/source/java/org/alfresco/web/bean/dialog/DialogManager.java index a588eb5c77..c47c6d8c62 100644 --- a/source/java/org/alfresco/web/bean/dialog/DialogManager.java +++ b/source/java/org/alfresco/web/bean/dialog/DialogManager.java @@ -1,5 +1,7 @@ package org.alfresco.web.bean.dialog; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import javax.faces.component.UIComponent; @@ -9,6 +11,7 @@ import javax.faces.event.ActionEvent; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.web.app.Application; import org.alfresco.web.app.servlet.FacesHelper; +import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig; import org.alfresco.web.config.DialogsConfigElement.DialogConfig; import org.alfresco.web.ui.common.component.UIActionLink; @@ -147,6 +150,17 @@ public class DialogManager return desc; } + /** + * Returns the id of a configured action group representing the actions to + * display for the dialog. + * + * @return The action group id + */ + public String getActions() + { + return this.currentDialogConfig.getActionsConfigId(); + } + /** * Returns the page the dialog will use * @@ -157,6 +171,51 @@ public class DialogManager return this.currentDialogConfig.getPage(); } + /** + * Determines whether the current dialog's OK button is visible + * + * @return true if the OK button is visible, false if it's not + */ + public boolean isOKButtonVisible() + { + return this.currentDialogConfig.isOKButtonVisible(); + } + + /** + * Returns a list of additional buttons to display in the dialog + * + * @return List of button configurations + */ + public List getAdditionalButtons() + { + List buttons = null; + + // get a list of buttons to display from the configuration + List cfgButtons = this.currentDialogConfig.getButtons(); + + // get a list of buttons added dynamically by the dialog + List dynButtons = this.currentDialog.getAdditionalButtons(); + + if (cfgButtons != null && dynButtons != null) + { + // combine the two lists + buttons = new ArrayList( + cfgButtons.size() + dynButtons.size()); + buttons.addAll(cfgButtons); + buttons.addAll(dynButtons); + } + else if (cfgButtons != null && dynButtons == null) + { + buttons = cfgButtons; + } + else if (cfgButtons == null && dynButtons != null) + { + buttons = dynButtons; + } + + return buttons; + } + /** * Returns the label to use for the cancel button * diff --git a/source/java/org/alfresco/web/bean/dialog/IDialogBean.java b/source/java/org/alfresco/web/bean/dialog/IDialogBean.java index 354129db01..fe2e9b59dc 100644 --- a/source/java/org/alfresco/web/bean/dialog/IDialogBean.java +++ b/source/java/org/alfresco/web/bean/dialog/IDialogBean.java @@ -1,7 +1,10 @@ package org.alfresco.web.bean.dialog; +import java.util.List; import java.util.Map; +import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig; + /** * Interface that defines the contract for a dialog backing bean * @@ -30,6 +33,13 @@ public interface IDialogBean */ public String finish(); + /** + * Returns a list of additional buttons to display in the dialog. + * + * @return List of button configurations, null if there are no buttons + */ + public List getAdditionalButtons(); + /** * Returns the label to use for the cancel button * diff --git a/source/java/org/alfresco/web/bean/generator/BaseComponentGenerator.java b/source/java/org/alfresco/web/bean/generator/BaseComponentGenerator.java index 8137ec929b..3766266bea 100644 --- a/source/java/org/alfresco/web/bean/generator/BaseComponentGenerator.java +++ b/source/java/org/alfresco/web/bean/generator/BaseComponentGenerator.java @@ -32,6 +32,7 @@ import org.alfresco.web.ui.repo.component.property.BaseAssociationEditor; import org.alfresco.web.ui.repo.component.property.PropertySheetItem; import org.alfresco.web.ui.repo.component.property.UIProperty; import org.alfresco.web.ui.repo.component.property.UIPropertySheet; +import org.alfresco.web.ui.repo.component.property.UISeparator; import org.alfresco.web.ui.repo.component.property.UIPropertySheet.ClientValidation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -83,6 +84,12 @@ public abstract class BaseComponentGenerator implements IComponentGenerator // setup any converter the property needs setupConverter(context, propertySheet, item, propertyDef, component); } + else if (item instanceof UISeparator) + { + // just create the component and add it + component = createComponent(context, propertySheet, item); + item.getChildren().add(component); + } else { // get the association definition diff --git a/source/java/org/alfresco/web/bean/generator/DatePickerGenerator.java b/source/java/org/alfresco/web/bean/generator/DatePickerGenerator.java index f0068f32e5..a7f0463ddb 100644 --- a/source/java/org/alfresco/web/bean/generator/DatePickerGenerator.java +++ b/source/java/org/alfresco/web/bean/generator/DatePickerGenerator.java @@ -1,7 +1,10 @@ package org.alfresco.web.bean.generator; +import java.util.Date; + import javax.faces.component.UIComponent; import javax.faces.component.UIOutput; +import javax.faces.component.UISelectOne; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; @@ -11,6 +14,7 @@ import org.alfresco.web.app.servlet.FacesHelper; import org.alfresco.web.ui.common.ComponentConstants; import org.alfresco.web.ui.common.converter.XMLDateConverter; import org.alfresco.web.ui.repo.RepoConstants; +import org.alfresco.web.ui.repo.component.UIMultiValueEditor; import org.alfresco.web.ui.repo.component.property.PropertySheetItem; import org.alfresco.web.ui.repo.component.property.UIPropertySheet; @@ -21,8 +25,43 @@ import org.alfresco.web.ui.repo.component.property.UIPropertySheet; */ public class DatePickerGenerator extends BaseComponentGenerator { + private int yearCount = 30; + private int startYear = new Date().getYear() + 1900 + 2; + private static final String MSG_DATE = "date_pattern"; + /** + * @return Returns the year to start counting back from + */ + public int getStartYear() + { + return startYear; + } + + /** + * @param startYear Sets the year to start counting back from + */ + public void setStartYear(int startYear) + { + this.startYear = startYear; + } + + /** + * @return Returns the number of years to show + */ + public int getYearCount() + { + return yearCount; + } + + /** + * @param yearCount Sets the number of years to show + */ + public void setYearCount(int yearCount) + { + this.yearCount = yearCount; + } + @SuppressWarnings("unchecked") public UIComponent generate(FacesContext context, String id) { @@ -30,7 +69,8 @@ public class DatePickerGenerator extends BaseComponentGenerator createComponent(ComponentConstants.JAVAX_FACES_INPUT); component.setRendererType(RepoConstants.ALFRESCO_FACES_DATE_PICKER_RENDERER); FacesHelper.setupComponentId(context, component, id); - component.getAttributes().put("yearCount", new Integer(30)); + component.getAttributes().put("startYear", this.startYear); + component.getAttributes().put("yearCount", this.yearCount); component.getAttributes().put("style", "margin-right: 7px;"); return component; @@ -59,8 +99,26 @@ public class DatePickerGenerator extends BaseComponentGenerator UIPropertySheet propertySheet, PropertySheetItem item, UIComponent component, boolean realTimeChecking, String idSuffix) { - // a date picker will always have a date value so there - // is no need to create a mandatory validation rule + if (component instanceof UIMultiValueEditor) + { + // Override the setup of the mandatory validation + // so we can send the _current_value id suffix. + // We also enable real time so the page load + // check disables the ok button if necessary, as the user + // adds or removes items from the multi value list the + // page will be refreshed and therefore re-check the status. + + super.setupMandatoryValidation(context, propertySheet, item, + component, true, "_current_value"); + } + else + { + // setup the client validation rule with real time validation enabled + // so that the initial page load checks the state of the date + super.setupMandatoryValidation(context, propertySheet, item, + component, true, idSuffix); + } + } /** diff --git a/source/java/org/alfresco/web/bean/generator/HeaderSeparatorGenerator.java b/source/java/org/alfresco/web/bean/generator/HeaderSeparatorGenerator.java new file mode 100644 index 0000000000..2cb6e31f9b --- /dev/null +++ b/source/java/org/alfresco/web/bean/generator/HeaderSeparatorGenerator.java @@ -0,0 +1,23 @@ +package org.alfresco.web.bean.generator; + +import javax.faces.component.UIComponent; + +import org.alfresco.web.ui.repo.component.property.PropertySheetItem; + +/** + * Generates a component to represent a separator that gets rendered + * as a header. + * + * @author gavinc + */ +public class HeaderSeparatorGenerator extends SeparatorGenerator +{ + @Override + protected String getHtml(UIComponent component, PropertySheetItem item) + { + String html = "
 " + + item.getDisplayLabel() + "
"; + + return html; + } +} diff --git a/source/java/org/alfresco/web/bean/generator/SeparatorGenerator.java b/source/java/org/alfresco/web/bean/generator/SeparatorGenerator.java new file mode 100644 index 0000000000..49076c1fa1 --- /dev/null +++ b/source/java/org/alfresco/web/bean/generator/SeparatorGenerator.java @@ -0,0 +1,49 @@ +package org.alfresco.web.bean.generator; + +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; + +import org.alfresco.web.ui.repo.component.property.PropertySheetItem; +import org.alfresco.web.ui.repo.component.property.UIPropertySheet; + +/** + * Generates a component to represent a separator. + * + * @author gavinc + */ +public class SeparatorGenerator extends BaseComponentGenerator +{ + @SuppressWarnings("unchecked") + public UIComponent generate(FacesContext context, String id) + { + UIComponent component = this.createOutputTextComponent(context, id); + component.getAttributes().put("escape", Boolean.FALSE); + + return component; + } + + @Override + @SuppressWarnings("unchecked") + protected UIComponent createComponent(FacesContext context, UIPropertySheet propertySheet, + PropertySheetItem item) + { + UIComponent component = this.generate(context, item.getName()); + + // set the HTML to use + component.getAttributes().put("value", getHtml(component, item)); + + return component; + } + + /** + * Returns the HTML to display for the separator + * + * @param component The JSF component representing the separator + * @param item The separator item + * @return The HTML + */ + protected String getHtml(UIComponent component, PropertySheetItem item) + { + return "

"; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/web/bean/generator/TextAreaGenerator.java b/source/java/org/alfresco/web/bean/generator/TextAreaGenerator.java new file mode 100644 index 0000000000..7a2af9bc09 --- /dev/null +++ b/source/java/org/alfresco/web/bean/generator/TextAreaGenerator.java @@ -0,0 +1,64 @@ +package org.alfresco.web.bean.generator; + +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; + +import org.alfresco.web.app.servlet.FacesHelper; +import org.alfresco.web.ui.common.ComponentConstants; + +/** + * Generates a text field component. + * + * @author gavinc + */ +public class TextAreaGenerator extends TextFieldGenerator +{ + private int rows = 3; + private int columns = 32; + + /** + * @return Returns the number of columns + */ + public int getColumns() + { + return columns; + } + + /** + * @param columns Sets the number of columns + */ + public void setColumns(int columns) + { + this.columns = columns; + } + + /** + * @return Returns the number of rows + */ + public int getRows() + { + return rows; + } + + /** + * @param rows Sets the number of rows + */ + public void setRows(int rows) + { + this.rows = rows; + } + + @SuppressWarnings("unchecked") + public UIComponent generate(FacesContext context, String id) + { + UIComponent component = context.getApplication(). + createComponent(ComponentConstants.JAVAX_FACES_INPUT); + component.setRendererType(ComponentConstants.JAVAX_FACES_TEXTAREA); + FacesHelper.setupComponentId(context, component, id); + + component.getAttributes().put("rows", this.rows); + component.getAttributes().put("cols", this.columns); + + return component; + } +} diff --git a/source/java/org/alfresco/web/bean/generator/TextFieldGenerator.java b/source/java/org/alfresco/web/bean/generator/TextFieldGenerator.java index cef4447498..cdbe96d52e 100644 --- a/source/java/org/alfresco/web/bean/generator/TextFieldGenerator.java +++ b/source/java/org/alfresco/web/bean/generator/TextFieldGenerator.java @@ -26,6 +26,41 @@ import org.alfresco.web.ui.repo.component.property.UIPropertySheet; */ public class TextFieldGenerator extends BaseComponentGenerator { + private int size = 35; + private int maxLength = 1024; + + /** + * @return Returns the default size for a text field + */ + public int getSize() + { + return size; + } + + /** + * @param size Sets the size of a text field + */ + public void setSize(int size) + { + this.size = size; + } + + /** + * @return Returns the max length for the text field + */ + public int getMaxLength() + { + return maxLength; + } + + /** + * @param maxLength Sets the max length of the text field + */ + public void setMaxLength(int maxLength) + { + this.maxLength = maxLength; + } + @SuppressWarnings("unchecked") public UIComponent generate(FacesContext context, String id) { @@ -33,8 +68,9 @@ public class TextFieldGenerator extends BaseComponentGenerator createComponent(ComponentConstants.JAVAX_FACES_INPUT); component.setRendererType(ComponentConstants.JAVAX_FACES_TEXT); FacesHelper.setupComponentId(context, component, id); - component.getAttributes().put("size", "35"); - component.getAttributes().put("maxlength", "1024"); + + component.getAttributes().put("size", this.size); + component.getAttributes().put("maxlength", this.maxLength); return component; } @@ -48,12 +84,17 @@ public class TextFieldGenerator extends BaseComponentGenerator if (propertySheet.inEditMode()) { - // if the field has the list of values constraint a - // SelectOne component is required otherwise create - // the standard edit component + // if the field has the list of values constraint + // and it is editable a SelectOne component is + // required otherwise create the standard edit component ListOfValuesConstraint constraint = getListOfValuesConstraint( context, propertySheet, item); - if (constraint != null) + + PropertyDefinition propDef = this.getPropertyDefinition(context, + propertySheet.getNode(), item.getName()); + + if (constraint != null && item.isReadOnly() == false && + propDef != null && propDef.isProtected() == false) { component = context.getApplication().createComponent( UISelectOne.COMPONENT_TYPE); diff --git a/source/java/org/alfresco/web/bean/repository/Node.java b/source/java/org/alfresco/web/bean/repository/Node.java index f0a6e49e12..cfab7794b1 100644 --- a/source/java/org/alfresco/web/bean/repository/Node.java +++ b/source/java/org/alfresco/web/bean/repository/Node.java @@ -36,8 +36,6 @@ import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.web.app.Application; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * Lighweight client side representation of a node held in the repository. @@ -48,28 +46,25 @@ public class Node implements Serializable { private static final long serialVersionUID = 3544390322739034169L; - protected static final Log logger = LogFactory.getLog(Node.class); - protected NodeRef nodeRef; - private String name; - private QName type; - private String path; - private String id; - private Set aspects = null; - private Map permissions; - private Boolean locked = null; - private Boolean workingCopyOwner = null; + protected String name; + protected QName type; + protected String path; + protected String id; + protected Set aspects = null; + protected Map permissions; + protected Boolean locked = null; + protected Boolean workingCopyOwner = null; protected QNameNodeMap properties; protected boolean propsRetrieved = false; protected ServiceRegistry services = null; + protected boolean childAssocsRetrieved = false; + protected QNameNodeMap childAssociations; + protected boolean assocsRetrieved = false; + protected QNameNodeMap associations; - private boolean childAssocsRetrieved = false; - private QNameNodeMap childAssociations; private Map> childAssociationsAdded; private Map> childAssociationsRemoved; - - private boolean assocsRetrieved = false; - private QNameNodeMap associations; private Map> associationsAdded; private Map> associationsRemoved; @@ -94,7 +89,7 @@ public class Node implements Serializable /** * @return All the properties known about this node. */ - public Map getProperties() + public final Map getProperties() { if (this.propsRetrieved == false) { @@ -120,7 +115,7 @@ public class Node implements Serializable { if (this.assocsRetrieved == false) { - associations = new QNameNodeMap(getServiceRegistry().getNamespaceService(), this); + this.associations = new QNameNodeMap(getServiceRegistry().getNamespaceService(), this); List assocs = getServiceRegistry().getNodeService().getTargetAssocs(this.nodeRef, RegexQNamePattern.MATCH_ALL); @@ -341,7 +336,7 @@ public class Node implements Serializable * * @return true if the permission is applied to the node for this user, false otherwise */ - public final boolean hasPermission(String permission) + public boolean hasPermission(String permission) { Boolean valid = null; if (permissions != null) diff --git a/source/java/org/alfresco/web/bean/repository/QNameNodeMap.java b/source/java/org/alfresco/web/bean/repository/QNameNodeMap.java index 92c0ecdc18..d12f40f28f 100644 --- a/source/java/org/alfresco/web/bean/repository/QNameNodeMap.java +++ b/source/java/org/alfresco/web/bean/repository/QNameNodeMap.java @@ -16,15 +16,11 @@ */ package org.alfresco.web.bean.repository; -import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.Set; import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.QNameMap; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * A extension of the repo QNameMap to provide custom property resolving support for Node wrappers. @@ -86,6 +82,7 @@ public final class QNameNodeMap extends QNameMap implements Map, Cloneable /** * @see java.util.Map#get(java.lang.Object) */ + @SuppressWarnings("unchecked") public Object get(Object key) { String qnameKey = Repository.resolveToQNameString(key.toString()); @@ -120,6 +117,7 @@ public final class QNameNodeMap extends QNameMap implements Map, Cloneable /** * Shallow copy the map by copying keys and values into a new QNameNodeMap */ + @SuppressWarnings("unchecked") public Object clone() { QNameNodeMap map = new QNameNodeMap(this.resolver, this.parent); diff --git a/source/java/org/alfresco/web/bean/repository/TransientMapNode.java b/source/java/org/alfresco/web/bean/repository/TransientMapNode.java new file mode 100644 index 0000000000..301d987ac8 --- /dev/null +++ b/source/java/org/alfresco/web/bean/repository/TransientMapNode.java @@ -0,0 +1,149 @@ +package org.alfresco.web.bean.repository; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.namespace.QName; + +/** + * Represents a transient node i.e. it is not and will not be present in the repository. + *

+ * This type of node is typically used to drive rich lists where the Map implementation + * is required for sorting columns. + *

+ * + * @author gavinc + */ +public class TransientMapNode extends TransientNode implements Map +{ + private static final long serialVersionUID = 1120307465342597322L; + + /** + * Constructor. + *

+ * NOTE: The name is NOT automatically added to the map of properties, + * if you need the name of this node to be in the map then add it to + * the map passed in to this constructor. + *

+ * + * @param type The type this node will represent + * @param name The name of the node + * @param data The properties and associations this node will have + */ + public TransientMapNode(QName type, String name, Map data) + { + super(type, name, data); + } + + @Override + public String toString() + { + return "Transient map node of type: " + getType() + + "\nProperties: " + this.getProperties().toString(); + } + + // ------------------------------------------------------------------------------ + // Map implementation - allows the Node bean to be accessed using JSF expression syntax + + /** + * @see java.util.Map#clear() + */ + public void clear() + { + getProperties().clear(); + } + + /** + * @see java.util.Map#containsKey(java.lang.Object) + */ + public boolean containsKey(Object key) + { + return getProperties().containsKey(key); + } + + /** + * @see java.util.Map#containsValue(java.lang.Object) + */ + public boolean containsValue(Object value) + { + return getProperties().containsKey(value); + } + + /** + * @see java.util.Map#entrySet() + */ + @SuppressWarnings("unchecked") + public Set entrySet() + { + return getProperties().entrySet(); + } + + /** + * @see java.util.Map#get(java.lang.Object) + */ + public Object get(Object key) + { + return getProperties().get(key); + } + + /** + * @see java.util.Map#isEmpty() + */ + public boolean isEmpty() + { + return getProperties().isEmpty(); + } + + /** + * @see java.util.Map#keySet() + */ + @SuppressWarnings("unchecked") + public Set keySet() + { + return getProperties().keySet(); + } + + /** + * @see java.util.Map#put(K, V) + */ + public Object put(String key, Object value) + { + return getProperties().put(key, value); + } + + /** + * @see java.util.Map#putAll(java.util.Map) + */ + @SuppressWarnings("unchecked") + public void putAll(Map t) + { + getProperties().putAll(t); + } + + /** + * @see java.util.Map#remove(java.lang.Object) + */ + public Object remove(Object key) + { + return getProperties().remove(key); + } + + /** + * @see java.util.Map#size() + */ + public int size() + { + return getProperties().size(); + } + + /** + * @see java.util.Map#values() + */ + @SuppressWarnings("unchecked") + public Collection values() + { + return getProperties().values(); + } +} diff --git a/source/java/org/alfresco/web/bean/repository/TransientNode.java b/source/java/org/alfresco/web/bean/repository/TransientNode.java new file mode 100644 index 0000000000..50a2c11e32 --- /dev/null +++ b/source/java/org/alfresco/web/bean/repository/TransientNode.java @@ -0,0 +1,150 @@ +package org.alfresco.web.bean.repository; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Represents a transient node i.e. it is not and will not be present in the repository. + *

+ * This type of node is typically used to drive the property sheet when data collection + * is required for a type but the node does not need to be stored in the repository. An + * example use is the workflow, transient nodes are used to collect workitem metadata. + *

+ * + * @author gavinc + */ +public class TransientNode extends Node +{ + private static final long serialVersionUID = 2140554155948154106L; + + private static final Log logger = LogFactory.getLog(TransientNode.class); + + /** + * Constructor. + *

+ * NOTE: The name is NOT automatically added to the map of properties, + * if you need the name of this node to be in the map then add it to + * the map passed in to this constructor. + *

+ * + * @param type The type this node will represent + * @param name The name of the node + * @param data The properties and associations this node will have + */ + public TransientNode(QName type, String name, Map data) + { + // create a dummy NodeRef to pass to the constructor + super(new NodeRef(Repository.getStoreRef(), GUID.generate())); + + this.type = type; + this.name = name; + + // initialise the node + initNode(data); + + if (logger.isDebugEnabled()) + logger.debug("Constructed transient node: " + this); + } + + /** + * Initialises the node. + * + * @param data The properties and associations to initialise the node with + */ + protected void initNode(Map data) + { + // setup the transient node so that the super class methods work + // and do not need to go back to the repository + + DictionaryService ddService = this.getServiceRegistry().getDictionaryService(); + + // marshall the given properties and associations into the internal maps + this.associations = new QNameNodeMap(getServiceRegistry().getNamespaceService(), this); + this.childAssociations = new QNameNodeMap(getServiceRegistry().getNamespaceService(), this); + + if (data != null) + { + // go through all data items and allocate to the correct internal list + for (QName item : data.keySet()) + { + PropertyDefinition propDef = ddService.getProperty(item); + if (propDef != null) + { + this.properties.put(item, data.get(item)); + } + else + { + // see if the item is either type of association + AssociationDefinition assocDef = ddService.getAssociation(item); + if (assocDef != null) + { + if (assocDef.isChild()) + { + this.childAssociations.put(item, data.get(item)); + } + else + { + this.associations.put(item, data.get(item)); + } + } + } + } + } + + // show that the maps have been initialised + this.propsRetrieved = true; + this.assocsRetrieved = true; + this.childAssocsRetrieved = true; + + // setup the list of aspects the node would have + TypeDefinition typeDef = ddService.getType(this.type); + if (typeDef == null) + { + throw new AlfrescoRuntimeException("Failed to find type definition for start task: " + this.type); + } + + this.aspects = new HashSet(); + for (AspectDefinition aspectDef : typeDef.getDefaultAspects()) + { + this.aspects.add(aspectDef.getName()); + } + + // setup remaining variables + this.path = ""; + this.locked = Boolean.FALSE; + this.workingCopyOwner = Boolean.FALSE; + } + + @Override + public boolean hasPermission(String permission) + { + return true; + } + + @Override + public void reset() + { + // don't reset anything otherwise we'll lose our data + // with no way of getting it back!! + } + + @Override + public String toString() + { + return "Transient node of type: " + getType() + + "\nProperties: " + this.getProperties().toString(); + } +} diff --git a/source/java/org/alfresco/web/bean/repository/User.java b/source/java/org/alfresco/web/bean/repository/User.java index cfcb58a2bb..99bbd61bc3 100644 --- a/source/java/org/alfresco/web/bean/repository/User.java +++ b/source/java/org/alfresco/web/bean/repository/User.java @@ -64,6 +64,16 @@ public final class User this.person = person; } + /** + * Forces a clear of any cached or calcluated values + */ + public void reset() + { + this.fullName = null; + this.administrator = null; + this.preferences = null; + } + /** * @return The user name */ @@ -114,7 +124,6 @@ public final class User { return this.ticket; } - /** * @return Returns the person NodeRef diff --git a/source/java/org/alfresco/web/bean/rules/CreateRuleWizard.java b/source/java/org/alfresco/web/bean/rules/CreateRuleWizard.java index eb59e93ff3..1b8be56344 100644 --- a/source/java/org/alfresco/web/bean/rules/CreateRuleWizard.java +++ b/source/java/org/alfresco/web/bean/rules/CreateRuleWizard.java @@ -19,6 +19,7 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionCondition; import org.alfresco.service.cmr.action.ActionConditionDefinition; +import org.alfresco.service.cmr.action.CompositeAction; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.rule.Rule; import org.alfresco.service.cmr.rule.RuleService; @@ -67,6 +68,7 @@ public class CreateRuleWizard extends BaseActionWizard protected boolean runInBackground; protected boolean applyToSubSpaces; protected boolean editingCondition; + protected boolean ruleDisabled; private static final Log logger = LogFactory.getLog(CreateRuleWizard.class); @@ -84,6 +86,7 @@ public class CreateRuleWizard extends BaseActionWizard this.condition = null; this.applyToSubSpaces = false; this.runInBackground = false; + this.ruleDisabled = false; this.conditions = null; this.allConditionsProperties = new ArrayList>(); @@ -100,7 +103,9 @@ public class CreateRuleWizard extends BaseActionWizard Node currentSpace = this.browseBean.getActionSpace(); // create the new rule - Rule rule = this.ruleService.createRule(this.getType()); + //Rule rule = this.ruleService.createRule(this.getType()); + Rule rule = new Rule(); + rule.setRuleType(this.getType()); // setup the rule outcome = setupRule(context, rule, outcome); @@ -189,12 +194,13 @@ public class CreateRuleWizard extends BaseActionWizard String backgroundYesNo = this.runInBackground ? bundle.getString("yes") : bundle.getString("no"); String subSpacesYesNo = this.applyToSubSpaces ? bundle.getString("yes") : bundle.getString("no"); + String ruleDisabledYesNo = this.ruleDisabled ? bundle.getString("yes") : bundle.getString("no"); return buildSummary( new String[] {bundle.getString("rule_type"), bundle.getString("name"), bundle.getString("description"), - bundle.getString("apply_to_sub_spaces"), bundle.getString("run_in_background"), + bundle.getString("apply_to_sub_spaces"), bundle.getString("run_in_background"), bundle.getString("rule_disabled"), bundle.getString("conditions"), bundle.getString("actions")}, - new String[] {this.type, this.title, this.description, subSpacesYesNo, backgroundYesNo, + new String[] {this.type, this.title, this.description, subSpacesYesNo, backgroundYesNo, ruleDisabledYesNo, conditionsSummary.toString(), actionsSummary.toString()}); } @@ -203,6 +209,21 @@ public class CreateRuleWizard extends BaseActionWizard { return "error_rule"; } + + protected CompositeAction getCompositeAction(Rule rule) + { + // Get the composite action + Action ruleAction = rule.getAction(); + if (ruleAction == null) + { + throw new AlfrescoRuntimeException("Rule does not have associated action."); + } + else if ((ruleAction instanceof CompositeAction) == false) + { + throw new AlfrescoRuntimeException("Rules with non-composite actions are not currently supported by the UI"); + } + return (CompositeAction)ruleAction; + } // ------------------------------------------------------------------------------ // Bean Getters and Setters @@ -437,6 +458,22 @@ public class CreateRuleWizard extends BaseActionWizard this.applyToSubSpaces = applyToSubSpaces; } + /** + * @return Returns whether the rule is disabled or not. + */ + public boolean getRuleDisabled() + { + return this.ruleDisabled; + } + + /** + * @param ruleDisabled Sets whether the rule is disabled or not + */ + public void setRuleDisabled(boolean ruleDisabled) + { + this.ruleDisabled = ruleDisabled; + } + /** * @return Returns the type. */ @@ -650,6 +687,10 @@ public class CreateRuleWizard extends BaseActionWizard rule.setDescription(this.description); rule.applyToChildren(this.applyToSubSpaces); rule.setExecuteAsynchronously(this.runInBackground); + rule.setRuleDisabled(this.ruleDisabled); + + CompositeAction compositeAction = this.actionService.createCompositeAction(); + rule.setAction(compositeAction); // add all the conditions to the rule for (Map condParams : this.allConditionsProperties) @@ -674,7 +715,7 @@ public class CreateRuleWizard extends BaseActionWizard Boolean not = (Boolean)condParams.get(BaseConditionHandler.PROP_CONDITION_NOT); condition.setInvertCondition(((Boolean)not).booleanValue()); - rule.addActionCondition(condition); + compositeAction.addActionCondition(condition); } // add all the actions to the rule @@ -696,7 +737,7 @@ public class CreateRuleWizard extends BaseActionWizard // add the action to the rule Action action = this.actionService.createAction(actionName); action.setParameterValues(repoActionParams); - rule.addAction(action); + compositeAction.addAction(action); } return outcome; diff --git a/source/java/org/alfresco/web/bean/rules/EditRuleWizard.java b/source/java/org/alfresco/web/bean/rules/EditRuleWizard.java index 437d44063b..bd4e13059e 100644 --- a/source/java/org/alfresco/web/bean/rules/EditRuleWizard.java +++ b/source/java/org/alfresco/web/bean/rules/EditRuleWizard.java @@ -12,6 +12,7 @@ import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionCondition; import org.alfresco.service.cmr.action.ActionConditionDefinition; import org.alfresco.service.cmr.action.ActionDefinition; +import org.alfresco.service.cmr.action.CompositeAction; import org.alfresco.service.cmr.rule.Rule; import org.alfresco.web.bean.actions.IHandler; import org.alfresco.web.bean.repository.Node; @@ -46,16 +47,20 @@ public class EditRuleWizard extends CreateRuleWizard } // populate the bean with current values - this.type = rule.getRuleTypeName(); + this.type = rule.getRuleTypes().get(0); this.title = rule.getTitle(); this.description = rule.getDescription(); this.applyToSubSpaces = rule.isAppliedToChildren(); - this.runInBackground = rule.getExecuteAsychronously(); + this.runInBackground = rule.getExecuteAsynchronously(); + this.ruleDisabled = rule.getRuleDisabled(); FacesContext context = FacesContext.getCurrentInstance(); + // Get the composite action + CompositeAction compositeAction = getCompositeAction(rule); + // populate the conditions list with maps of properties representing each condition - List conditions = rule.getActionConditions(); + List conditions = compositeAction.getActionConditions(); for (ActionCondition condition : conditions) { this.currentConditionProperties = new HashMap(3); @@ -90,7 +95,7 @@ public class EditRuleWizard extends CreateRuleWizard } // populate the actions list with maps of properties representing each action - List actions = rule.getActions(); + List actions = compositeAction.getActions(); for (Action action : actions) { this.currentActionProperties = new HashMap(3); @@ -135,10 +140,13 @@ public class EditRuleWizard extends CreateRuleWizard // get the existing rule Rule rule = this.rulesBean.getCurrentRule(); + + // Get the composite action + CompositeAction compositeAction = getCompositeAction(rule); // remove all the conditions and actions from the current rule - rule.removeAllActionConditions(); - rule.removeAllActions(); + compositeAction.removeAllActionConditions(); + compositeAction.removeAllActions(); // re-setup the rule outcome = setupRule(context, rule, outcome); diff --git a/source/java/org/alfresco/web/bean/rules/RulesBean.java b/source/java/org/alfresco/web/bean/rules/RulesBean.java index e3dc802b17..2a45f58c61 100644 --- a/source/java/org/alfresco/web/bean/rules/RulesBean.java +++ b/source/java/org/alfresco/web/bean/rules/RulesBean.java @@ -27,10 +27,13 @@ import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; import javax.transaction.UserTransaction; +import org.alfresco.model.ContentModel; import org.alfresco.repo.action.executer.ExecuteAllRulesActionExecuter; +import org.alfresco.repo.rule.RuleModel; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.rule.Rule; import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.web.app.Application; @@ -55,6 +58,8 @@ public class RulesBean implements IContextListener { private static final String MSG_ERROR_DELETE_RULE = "error_delete_rule"; private static final String MSG_REAPPLY_RULES_SUCCESS = "reapply_rules_success"; + private static final String MSG_IGNORE_INHERTIED_RULES = "ignore_inherited_rules"; + private static final String MSG_INCLUDE_INHERITED_RULES = "include_inherited_rules"; private static final String LOCAL = "local"; private static final String INHERITED = "inherited"; @@ -67,6 +72,7 @@ public class RulesBean implements IContextListener private Rule currentRule; private UIRichList richList; private ActionService actionService; + private NodeService nodeService; /** @@ -116,7 +122,11 @@ public class RulesBean implements IContextListener // wrap them all passing the current space for (Rule rule : repoRules) { - WrappedRule wrapped = new WrappedRule(rule, getSpace().getNodeRef()); + Date createdDate = (Date)this.nodeService.getProperty(rule.getNodeRef(), ContentModel.PROP_CREATED); + Date modifiedDate = (Date)this.nodeService.getProperty(rule.getNodeRef(), ContentModel.PROP_MODIFIED); + boolean isLocal = getSpace().getNodeRef().equals(this.ruleService.getOwningNodeRef(rule)); + + WrappedRule wrapped = new WrappedRule(rule, isLocal, createdDate, modifiedDate); this.rules.add(wrapped); } @@ -138,13 +148,13 @@ public class RulesBean implements IContextListener if (logger.isDebugEnabled()) logger.debug("Rule clicked, it's id is: " + id); - this.currentRule = this.ruleService.getRule( - getSpace().getNodeRef(), id); + this.currentRule = this.ruleService.getRule(new NodeRef(id)); + //getSpace().getNodeRef(), id); // refresh list contextUpdated(); } - } + } /** * Reapply the currently defines rules to the @@ -193,6 +203,52 @@ public class RulesBean implements IContextListener } } + /** + * Gets the label id from the ignore inhertied action + * + * @return the message id + */ + public String getIgnoreInheritedRulesLabelId() + { + FacesContext fc = FacesContext.getCurrentInstance(); + String result = Application.getMessage(fc, MSG_IGNORE_INHERTIED_RULES); + + if (this.nodeService.hasAspect(this.getSpace().getNodeRef(), RuleModel.ASPECT_IGNORE_INHERITED_RULES) == true) + { + result = Application.getMessage(fc, MSG_INCLUDE_INHERITED_RULES); + } + return result; + } + + public boolean getIgnoreInheritedRules() + { + return this.nodeService.hasAspect(this.getSpace().getNodeRef(), RuleModel.ASPECT_IGNORE_INHERITED_RULES); + } + + /** + * Action listener to ignore (or include) inherited rules. + * + * @param event the action event object + */ + public void ignoreInheritedRules(ActionEvent event) + { + NodeRef nodeRef = this.getSpace().getNodeRef(); + if (this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES) == true) + { + this.nodeService.removeAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES); + } + else + { + this.nodeService.addAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES, null); + } + + // force the list to be re-queried when the page is refreshed + if (this.richList != null) + { + this.richList.setValue(null); + } + } + /** * Returns the current rule * @@ -306,6 +362,16 @@ public class RulesBean implements IContextListener { this.actionService = actionService; } + + /** + * Set the node service to use + * + * @param nodeService the node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } // ------------------------------------------------------------------------------ @@ -330,7 +396,9 @@ public class RulesBean implements IContextListener public static class WrappedRule { private Rule rule; - private NodeRef ruleNode; + private boolean isLocal; + private Date createdDate; + private Date modifiedDate; /** * Constructs a RuleWrapper object @@ -338,10 +406,12 @@ public class RulesBean implements IContextListener * @param rule The rule we are wrapping * @param ruleNode The node the rules belong to */ - public WrappedRule(Rule rule, NodeRef ruleNode) + public WrappedRule(Rule rule, boolean isLocal, Date createdDate, Date modifiedDate) { this.rule = rule; - this.ruleNode = ruleNode; + this.isLocal = isLocal; + this.createdDate = createdDate; + this.modifiedDate = modifiedDate; } /** @@ -362,7 +432,7 @@ public class RulesBean implements IContextListener */ public boolean getLocal() { - return ruleNode.equals(this.rule.getOwningNodeRef()); + return this.isLocal; } /** Methods to support sorting of the rules list in a table */ @@ -374,7 +444,7 @@ public class RulesBean implements IContextListener */ public String getId() { - return this.rule.getId(); + return this.rule.getNodeRef().toString(); } /** @@ -404,7 +474,7 @@ public class RulesBean implements IContextListener */ public Date getCreatedDate() { - return this.rule.getCreatedDate(); + return this.createdDate; } /** @@ -414,7 +484,7 @@ public class RulesBean implements IContextListener */ public Date getModifiedDate() { - return this.rule.getModifiedDate(); + return this.modifiedDate; } } } diff --git a/source/java/org/alfresco/web/bean/rules/handlers/HasAspectHandler.java b/source/java/org/alfresco/web/bean/rules/handlers/HasAspectHandler.java index 5510ec6316..e04025845c 100644 --- a/source/java/org/alfresco/web/bean/rules/handlers/HasAspectHandler.java +++ b/source/java/org/alfresco/web/bean/rules/handlers/HasAspectHandler.java @@ -49,7 +49,7 @@ public class HasAspectHandler extends BaseConditionHandler String label = null; String aspectName = (String)conditionProps.get(PROP_ASPECT); - for (SelectItem item : ((CreateRuleWizard)wizard).getAspects()) + for (SelectItem item : ((CreateRuleWizard)wizard).getTestableAspects()) { if (item.getValue().equals(aspectName)) { diff --git a/source/java/org/alfresco/web/bean/spaces/CreateSpaceWizard.java b/source/java/org/alfresco/web/bean/spaces/CreateSpaceWizard.java index 6fffd00e31..70d9ecae90 100644 --- a/source/java/org/alfresco/web/bean/spaces/CreateSpaceWizard.java +++ b/source/java/org/alfresco/web/bean/spaces/CreateSpaceWizard.java @@ -712,7 +712,7 @@ public class CreateSpaceWizard extends BaseWizardBean { return MessageFormat.format(Application.getMessage( FacesContext.getCurrentInstance(), Repository.ERROR_EXISTS), - ((FileExistsException)exception).getExisting().getName()); + ((FileExistsException)exception).getName()); } else { diff --git a/source/java/org/alfresco/web/bean/spaces/DeleteSpaceDialog.java b/source/java/org/alfresco/web/bean/spaces/DeleteSpaceDialog.java index 5001db6ca6..2409f1ae78 100644 --- a/source/java/org/alfresco/web/bean/spaces/DeleteSpaceDialog.java +++ b/source/java/org/alfresco/web/bean/spaces/DeleteSpaceDialog.java @@ -1,15 +1,27 @@ package org.alfresco.web.bean.spaces; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; +import javax.transaction.UserTransaction; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.service.transaction.TransactionService; import org.alfresco.web.app.AlfrescoNavigationHandler; import org.alfresco.web.app.Application; import org.alfresco.web.bean.content.DeleteContentDialog; import org.alfresco.web.bean.dialog.BaseDialogBean; +import org.alfresco.web.bean.repository.MapNode; import org.alfresco.web.bean.repository.Node; +import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.ui.common.Utils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -22,6 +34,14 @@ import org.apache.commons.logging.LogFactory; public class DeleteSpaceDialog extends BaseDialogBean { private static final Log logger = LogFactory.getLog(DeleteContentDialog.class); + + private static final String DELETE_ALL = "all"; + private static final String DELETE_FILES = "files"; + private static final String DELETE_FOLDERS = "folders"; + private static final String DELETE_CONTENTS = "contents"; + + private String deleteMode = DELETE_ALL; + // ------------------------------------------------------------------------------ // Dialog implementation @@ -34,10 +54,84 @@ public class DeleteSpaceDialog extends BaseDialogBean Node node = this.browseBean.getActionSpace(); if (node != null) { + // force cache of name property so we can use it after the delete + node.getName(); + if (logger.isDebugEnabled()) - logger.debug("Trying to delete space: " + node.getId()); + logger.debug("Trying to delete space: " + node.getId() + " using delete mode: " + this.deleteMode); + + if (DELETE_ALL.equals(this.deleteMode)) + { + this.nodeService.deleteNode(node.getNodeRef()); + } + else + { + List childRefs = this.nodeService.getChildAssocs(node.getNodeRef(), + ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + List deleteRefs = new ArrayList(childRefs.size()); + for (ChildAssociationRef ref : childRefs) + { + NodeRef nodeRef = ref.getChildRef(); + + if (this.nodeService.exists(nodeRef)) + { + if (DELETE_CONTENTS.equals(this.deleteMode)) + { + deleteRefs.add(nodeRef); + } + else + { + // find it's type so we can see if it's a node we are interested in + QName type = this.nodeService.getType(nodeRef); + + // make sure the type is defined in the data dictionary + TypeDefinition typeDef = this.dictionaryService.getType(type); + + if (typeDef != null) + { + if (DELETE_FOLDERS.equals(this.deleteMode)) + { + // look for folder type + if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) == true && + this.dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false) + { + deleteRefs.add(nodeRef); + } + } + else if (DELETE_FILES.equals(this.deleteMode)) + { + // look for content file type + if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT)) + { + deleteRefs.add(nodeRef); + } + } + } + } + } + } - this.nodeService.deleteNode(node.getNodeRef()); + // delete the list of refs + TransactionService txService = Repository.getServiceRegistry(context).getTransactionService(); + for (NodeRef nodeRef : deleteRefs) + { + UserTransaction tx = null; + + try + { + tx = txService.getNonPropagatingUserTransaction(); + tx.begin(); + + this.nodeService.deleteNode(nodeRef); + + tx.commit(); + } + catch (Throwable err) + { + try { if (tx != null) {tx.rollback();} } catch (Exception ex) {} + } + } + } } else { @@ -52,21 +146,28 @@ public class DeleteSpaceDialog extends BaseDialogBean { Node node = this.browseBean.getActionSpace(); - // remove this node from the breadcrumb if required - this.browseBean.removeSpaceFromBreadcrumb(node); - - // add a message to inform the user that the delete was OK - String statusMsg = MessageFormat.format( - Application.getMessage(FacesContext.getCurrentInstance(), "status_space_deleted"), - new Object[]{node.getName()}); - Utils.addStatusMessage(FacesMessage.SEVERITY_INFO, statusMsg); - - // clear action context - this.browseBean.setActionSpace(null); - - // setting the outcome will show the browse view again - return AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME + - AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "browse"; + if (node != null && this.nodeService.exists(node.getNodeRef()) == false) + { + // remove this node from the breadcrumb if required + this.browseBean.removeSpaceFromBreadcrumb(node); + + // add a message to inform the user that the delete was OK + String statusMsg = MessageFormat.format( + Application.getMessage(FacesContext.getCurrentInstance(), "status_space_deleted"), + new Object[]{node.getName()}); + Utils.addStatusMessage(FacesMessage.SEVERITY_INFO, statusMsg); + + // clear action context + this.browseBean.setActionSpace(null); + + // setting the outcome will show the browse view again + return AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME + + AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "browse"; + } + else + { + return outcome; + } } @Override @@ -81,6 +182,7 @@ public class DeleteSpaceDialog extends BaseDialogBean return false; } + // ------------------------------------------------------------------------------ // Bean Getters and Setters @@ -97,4 +199,20 @@ public class DeleteSpaceDialog extends BaseDialogBean return MessageFormat.format(fileConfirmMsg, new Object[] {this.browseBean.getActionSpace().getName()}); } + + /** + * @return Returns the delete operation mode. + */ + public String getDeleteMode() + { + return this.deleteMode; + } + + /** + * @param deleteMode The delete operation mode to set. + */ + public void setDeleteMode(String deleteMode) + { + this.deleteMode = deleteMode; + } } diff --git a/source/java/org/alfresco/web/bean/users/EmailSpaceUsersDialog.java b/source/java/org/alfresco/web/bean/users/EmailSpaceUsersDialog.java new file mode 100644 index 0000000000..53eb16465f --- /dev/null +++ b/source/java/org/alfresco/web/bean/users/EmailSpaceUsersDialog.java @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.web.bean.users; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.faces.context.FacesContext; +import javax.faces.event.ActionEvent; +import javax.transaction.UserTransaction; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.web.app.Application; +import org.alfresco.web.app.context.IContextListener; +import org.alfresco.web.app.context.UIContextService; +import org.alfresco.web.bean.TemplateMailHelperBean; +import org.alfresco.web.bean.dialog.BaseDialogBean; +import org.alfresco.web.bean.repository.MapNode; +import org.alfresco.web.bean.repository.Node; +import org.alfresco.web.bean.repository.Repository; +import org.alfresco.web.bean.repository.User; +import org.alfresco.web.ui.common.Utils; +import org.alfresco.web.ui.repo.WebResources; +import org.alfresco.web.ui.repo.component.UIUserGroupPicker; +import org.alfresco.web.ui.repo.component.UIUserGroupPicker.PickerEvent; +import org.springframework.mail.javamail.JavaMailSender; + +/** + * Dialog bean managing the state for the Email Space Users page. Calculates the user/groups + * that are invited to a space and builds the data structures needed to display and modify + * the list in the web-client UI. Notifies the selected user/groups with a templatable email. + * + * @author Kevin Roast + */ +public class EmailSpaceUsersDialog extends BaseDialogBean implements IContextListener +{ + private static final String PROP_DUPLICATE = "duplicate"; + private static final String PROP_PARENT = "parent"; + private static final String PROP_ID = "id"; + private static final String PROP_ISGROUP = "isGroup"; + private static final String PROP_ICON = "icon"; + private static final String PROP_FULLNAME = "fullName"; + private static final String PROP_ROLES = "roles"; + private static final String PROP_EXPANDED = "expanded"; + private static final String PROP_SELECTED = "selected"; + private static final String PROP_USERNAME = "userName"; + + /** Injected Bean references */ + protected PermissionService permissionService; + protected PersonService personService; + protected AuthorityService authorityService; + protected JavaMailSender mailSender; + + /** Helper providing template based mailing facilities */ + protected TemplateMailHelperBean mailHelper; + + /** List of user/group property map/node instances */ + private List usersGroups = null; + + /** Quick lookup table of authority to user/group instance */ + private Map userGroupLookup = new HashMap(); + + + /** + * Default constructor + */ + public EmailSpaceUsersDialog() + { + UIContextService.getInstance(FacesContext.getCurrentInstance()).registerBean(this); + } + + /** + * Setup the dialog + */ + public void init(Map parameters) + { + super.init(parameters); + + mailHelper = new TemplateMailHelperBean(); + mailHelper.setMailSender(mailSender); + mailHelper.setNodeService(nodeService); + } + + /** + * @see org.alfresco.web.bean.dialog.BaseDialogBean#finishImpl(javax.faces.context.FacesContext, java.lang.String) + */ + @Override + protected String finishImpl(FacesContext context, String outcome) throws Exception + { + // get the space ref this mail applies to + NodeRef spaceRef = getSpace().getNodeRef(); + + // calculate the 'from' email address + User user = Application.getCurrentUser(context); + String from = (String)this.nodeService.getProperty(user.getPerson(), ContentModel.PROP_EMAIL); + if (from == null || from.length() == 0) + { + // if the user does not have an email address get the default one from the config service + from = Application.getClientConfig(context).getFromEmailAddress(); + } + + Set mailedAuthorities = new HashSet(usersGroups.size()); + + // walk the list of users/groups to notify + for (Map node : usersGroups) + { + String authority = (String)node.get(PROP_USERNAME); + boolean selected = (Boolean)node.get(PROP_SELECTED); + + // if User, email then, else if Group get all members and email them + AuthorityType authType = AuthorityType.getAuthorityType(authority); + if (authType.equals(AuthorityType.USER)) + { + if (selected == true && this.personService.personExists(authority)) + { + if (mailedAuthorities.contains(authority) == false) + { + this.mailHelper.notifyUser( + this.personService.getPerson(authority), spaceRef, from, (String)node.get(PROP_ROLES)); + mailedAuthorities.add(authority); + } + } + } + else if (authType.equals(AuthorityType.GROUP)) + { + // is the group expanded? if so we'll deal with the child authorities instead + boolean expanded = (Boolean)node.get(PROP_EXPANDED); + if (expanded == false && selected == true) + { + // notify all members of the group + Set users = this.authorityService.getContainedAuthorities(AuthorityType.USER, authority, false); + for (String userAuth : users) + { + if (this.personService.personExists(userAuth) == true) + { + if (mailedAuthorities.contains(userAuth) == false) + { + this.mailHelper.notifyUser( + this.personService.getPerson(userAuth), spaceRef, from, (String)node.get(PROP_ROLES)); + mailedAuthorities.add(userAuth); + } + } + } + } + } + } + + return outcome; + } + + + // ------------------------------------------------------------------------------ + // IContextListener implementation + + /** + * @see org.alfresco.web.app.context.IContextListener#contextUpdated() + */ + public void contextUpdated() + { + this.usersGroups = null; + this.userGroupLookup = new HashMap(); + } + + + // ------------------------------------------------------------------------------ + // Bean Getters and Setters + + /** + * @param permissionService The PermissionService to set + */ + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + /** + * @param permissionService The PersonService to set + */ + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + /** + * @param mailSender The JavaMailSender to set. + */ + public void setMailSender(JavaMailSender mailSender) + { + this.mailSender = mailSender; + } + + /** + * @param authorityService The AuthorityService to set. + */ + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + /** + * @return The space to email users for + */ + public Node getSpace() + { + return this.browseBean.getActionSpace(); + } + + /** + * Return the List of objects representing the Users and Groups invited to this space. + * The picker is then responsible for rendering a view to represent those users and groups + * which allows the users to select and deselect users and groups, also to expand groups + * to show sub-groups and users. + * + * @return List of Map objects representing the users/groups assigned to the current space + */ + public List getUsersGroups() + { + if (this.usersGroups == null) + { + FacesContext context = FacesContext.getCurrentInstance(); + + UserTransaction tx = null; + try + { + tx = Repository.getUserTransaction(context, true); + tx.begin(); + + // Return all the permissions set against the current node + // for any authentication instance (user/group). + // Then combine them into a single list for each authentication found. + Map> permissionMap = new HashMap>(8, 1.0f); + Set permissions = permissionService.getAllSetPermissions(getSpace().getNodeRef()); + for (AccessPermission permission : permissions) + { + // we are only interested in Allow and not groups/owner etc. + if (permission.getAccessStatus() == AccessStatus.ALLOWED && + (permission.getAuthorityType() == AuthorityType.USER || + permission.getAuthorityType() == AuthorityType.GROUP || + permission.getAuthorityType() == AuthorityType.GUEST || + permission.getAuthorityType() == AuthorityType.EVERYONE)) + { + String authority = permission.getAuthority(); + + List userPermissions = permissionMap.get(authority); + if (userPermissions == null) + { + // create for first time + userPermissions = new ArrayList(4); + permissionMap.put(authority, userPermissions); + } + // add the permission name for this authority + userPermissions.add(permission.getPermission()); + } + } + + // create the structure as a linked list for fast insert/removal of items + this.usersGroups = new LinkedList(); + + // for each authentication (username/group key) found we get the Person + // node represented by it and use that for our list databinding object + for (String authority : permissionMap.keySet()) + { + Map node = buildAuthorityMap(authority, UserMembersBean.roleListToString(context, permissionMap.get(authority))); + if (node != null) + { + this.usersGroups.add(node); + } + } + + // commit the transaction + tx.commit(); + } + catch (InvalidNodeRefException refErr) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + context, Repository.ERROR_NODEREF), new Object[] {refErr.getNodeRef()}) ); + this.usersGroups = Collections.emptyList(); + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + } + catch (Throwable err) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + context, Repository.ERROR_GENERIC), err.getMessage()), err ); + this.usersGroups = Collections.emptyList(); + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + } + } + return this.usersGroups; + } + + /** + * Build a Map representing a user/group with a set of useful property values required + * by the UIUserGroupPicker UI component. + * + * @param authority User/Group authority + * @param roles Role text for the authority + * + * @return Map + */ + private Map buildAuthorityMap(String authority, String roles) + { + Map node = null; + + if (AuthorityType.getAuthorityType(authority) == AuthorityType.GUEST || + this.personService.personExists(authority)) + { + NodeRef nodeRef = this.personService.getPerson(authority); + if (nodeRef != null) + { + // create our Node representation + node = new MapNode(nodeRef); + + // set data binding properties + // this will also force initialisation of the props now during the UserTransaction + // it is much better for performance to do this now rather than during page bind + Map props = ((MapNode)node).getProperties(); + props.put(PROP_FULLNAME, ((String)props.get("firstName")) + ' ' + ((String)props.get("lastName"))); + props.put(PROP_ICON, WebResources.IMAGE_PERSON); + props.put(PROP_ISGROUP, false); + } + } + else if (AuthorityType.getAuthorityType(authority) == AuthorityType.GROUP) + { + // need a map (dummy node) to represent props for this Group Authority + node = new HashMap(8, 1.0f); + if (authority.startsWith(PermissionService.GROUP_PREFIX) == true) + { + node.put(PROP_FULLNAME, authority.substring(PermissionService.GROUP_PREFIX.length())); + } + else + { + node.put(PROP_FULLNAME, authority); + } + node.put(PROP_USERNAME, authority); + node.put(PROP_ID, authority); + node.put(PROP_ICON, WebResources.IMAGE_GROUP); + node.put(PROP_ISGROUP, true); + node.put(PROP_EXPANDED, false); + } + if (node != null) + { + // add the common properties + node.put(PROP_ROLES, roles); + node.put(PROP_PARENT, null); + + if (this.userGroupLookup.get(authority) != null) + { + // this authority already exists in the list somewhere else - mark as duplicate + node.put(PROP_DUPLICATE, true); + node.put(PROP_SELECTED, false); + } + else + { + // add to table for the first time, not a duplicate + this.userGroupLookup.put(authority, node); + node.put(PROP_DUPLICATE, false); + node.put(PROP_SELECTED, true); + } + } + + return node; + } + + /** + * @return TemplateMailHelperBean instance for this wizard + */ + public TemplateMailHelperBean getMailHelper() + { + return this.mailHelper; + } + + + // ------------------------------------------------------------------------------ + // Action Event Listeners + + /** + * Action handler for a user/group selector event + */ + public void userGroupSelectorAction(ActionEvent event) + { + if (event instanceof PickerEvent) + { + PickerEvent pickerEvent = (PickerEvent)event; + + // find the user/group this event represents + Map userGroup = null; + int index = 0; + for (; index authorities = authorityService.getContainedAuthorities( + null, pickerEvent.Authority, true); + for (String authority : authorities) + { + Map node = buildAuthorityMap(authority, (String)userGroup.get(PROP_ROLES)); + if (node != null) + { + node.put(PROP_PARENT, userGroup); + node.put(PROP_SELECTED, selected); + this.usersGroups.add(++index, node); + } + } + } + else + { + // remove the children for the group + for (index++; index> permissionMap = new HashMap>(13, 1.0f); + Map> permissionMap = new HashMap>(8, 1.0f); Set permissions = permissionService.getAllSetPermissions(getNode().getNodeRef()); - if (permissions != null) + for (AccessPermission permission : permissions) { - for (AccessPermission permission : permissions) + // we are only interested in Allow and not groups/owner etc. + if (permission.getAccessStatus() == AccessStatus.ALLOWED && + (permission.getAuthorityType() == AuthorityType.USER || + permission.getAuthorityType() == AuthorityType.GROUP || + permission.getAuthorityType() == AuthorityType.GUEST || + permission.getAuthorityType() == AuthorityType.EVERYONE)) { - // we are only interested in Allow and not groups/owner etc. - if (permission.getAccessStatus() == AccessStatus.ALLOWED && - (permission.getAuthorityType() == AuthorityType.USER || - permission.getAuthorityType() == AuthorityType.GROUP || - permission.getAuthorityType() == AuthorityType.GUEST || - permission.getAuthorityType() == AuthorityType.EVERYONE)) + String authority = permission.getAuthority(); + + List userPermissions = permissionMap.get(authority); + if (userPermissions == null) { - String authority = permission.getAuthority(); - - List userPermissions = permissionMap.get(authority); - if (userPermissions == null) - { - // create for first time - userPermissions = new ArrayList(4); - permissionMap.put(authority, userPermissions); - } - // add the permission name for this authority - userPermissions.add(permission.getPermission()); + // create for first time + userPermissions = new ArrayList(4); + permissionMap.put(authority, userPermissions); } + // add the permission name for this authority + userPermissions.add(permission.getPermission()); } } - // for each authentication (username key) found we get the Person + // for each authentication (username/group key) found we get the Person // node represented by it and use that for our list databinding object personNodes = new ArrayList(permissionMap.size()); for (String authority : permissionMap.keySet()) @@ -337,7 +334,7 @@ public abstract class UserMembersBean implements IContextListener // it is much better for performance to do this now rather than during page bind Map props = node.getProperties(); props.put("fullName", ((String)props.get("firstName")) + ' ' + ((String)props.get("lastName"))); - props.put("roles", listToString(context, permissionMap.get(authority))); + props.put("roles", roleListToString(context, permissionMap.get(authority))); props.put("icon", WebResources.IMAGE_PERSON); personNodes.add(node); @@ -357,7 +354,7 @@ public abstract class UserMembersBean implements IContextListener } node.put("userName", authority); node.put("id", authority); - node.put("roles", listToString(context, permissionMap.get(authority))); + node.put("roles", roleListToString(context, permissionMap.get(authority))); node.put("icon", WebResources.IMAGE_GROUP); personNodes.add(node); } @@ -369,7 +366,7 @@ public abstract class UserMembersBean implements IContextListener catch (InvalidNodeRefException refErr) { Utils.addErrorMessage(MessageFormat.format(Application.getMessage( - context, Repository.ERROR_NODEREF), new Object[] {"root"}) ); + context, Repository.ERROR_NODEREF), new Object[] {refErr.getNodeRef()}) ); personNodes = Collections.emptyList(); try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} } @@ -384,7 +381,16 @@ public abstract class UserMembersBean implements IContextListener return personNodes; } - private static String listToString(FacesContext context, List list) + /** + * Convert a list of user Roles to a comma separated string list. Each individual role + * will be looked up in message bundle to convert to a human readable string value. + * + * @param context FacesContext + * @param list List of Role names + * + * @return Comma separated string of human readable roles + */ + public static String roleListToString(FacesContext context, List list) { StringBuilder buf = new StringBuilder(); diff --git a/source/java/org/alfresco/web/bean/users/UsersBean.java b/source/java/org/alfresco/web/bean/users/UsersBean.java index a03197a32d..5653d090f0 100644 --- a/source/java/org/alfresco/web/bean/users/UsersBean.java +++ b/source/java/org/alfresco/web/bean/users/UsersBean.java @@ -16,6 +16,7 @@ */ package org.alfresco.web.bean.users; +import java.io.Serializable; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; @@ -35,6 +36,8 @@ import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO9075; import org.alfresco.web.app.Application; import org.alfresco.web.app.context.IContextListener; import org.alfresco.web.app.context.UIContextService; @@ -264,10 +267,10 @@ public class UsersBean implements IContextListener // create the node ref, then our node representation NodeRef ref = new NodeRef(Repository.getStoreRef(), id); Node node = new Node(ref); - + // remember the Person node setPerson(node); - + // clear the UI state in preparation for finishing the action // and returning to the main page contextUpdated(); @@ -387,7 +390,39 @@ public class UsersBean implements IContextListener return outcome; } - + + /** + * Action handler called for the OK button press + */ + public String changeUserDetails() + { + String outcome = DIALOG_CLOSE; + + FacesContext context = FacesContext.getCurrentInstance(); + try + { + Map props = this.nodeService.getProperties(getPerson().getNodeRef()); + props.put(ContentModel.PROP_FIRSTNAME, + (String)getPerson().getProperties().get(ContentModel.PROP_FIRSTNAME)); + props.put(ContentModel.PROP_LASTNAME, + (String)getPerson().getProperties().get(ContentModel.PROP_LASTNAME)); + props.put(ContentModel.PROP_EMAIL, + (String)getPerson().getProperties().get(ContentModel.PROP_EMAIL)); + + // persist changes + this.nodeService.setProperties(getPerson().getNodeRef(), props); + + // if the above call was successful, then reset Person Node in the session + Application.getCurrentUser(context).reset(); + } + catch (Throwable err) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + context, Repository.ERROR_GENERIC), err.getMessage()), err ); + } + + return outcome; + } /** * Event handler called when the user wishes to search for a user @@ -413,10 +448,11 @@ public class UsersBean implements IContextListener tx.begin(); // define the query to find people by their first or last name + String search = ISO9075.encode(this.searchCriteria); String query = "( TYPE:\"{http://www.alfresco.org/model/content/1.0}person\") AND " + - "((@\\{http\\://www.alfresco.org/model/content/1.0\\}firstName:" + this.searchCriteria + - "*) OR (@\\{http\\://www.alfresco.org/model/content/1.0\\}lastName:" + this.searchCriteria + - "*) OR (@\\{http\\://www.alfresco.org/model/content/1.0\\}userName:" + this.searchCriteria + + "((@\\{http\\://www.alfresco.org/model/content/1.0\\}firstName:" + search + + "*) OR (@\\{http\\://www.alfresco.org/model/content/1.0\\}lastName:" + search + + "*) OR (@\\{http\\://www.alfresco.org/model/content/1.0\\}userName:" + search + "*)))"; if (logger.isDebugEnabled()) diff --git a/source/java/org/alfresco/web/bean/wizard/InviteUsersWizard.java b/source/java/org/alfresco/web/bean/wizard/InviteUsersWizard.java index df0afdfb48..2a150fc77a 100644 --- a/source/java/org/alfresco/web/bean/wizard/InviteUsersWizard.java +++ b/source/java/org/alfresco/web/bean/wizard/InviteUsersWizard.java @@ -20,7 +20,6 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.ResourceBundle; import java.util.Set; @@ -30,16 +29,10 @@ import javax.faces.event.ActionEvent; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; import javax.faces.model.SelectItem; -import javax.mail.MessagingException; -import javax.mail.internet.MimeMessage; import javax.transaction.UserTransaction; import org.alfresco.model.ContentModel; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.TemplateNode; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PermissionService; @@ -47,20 +40,16 @@ import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.web.app.Application; import org.alfresco.web.app.context.UIContextService; -import org.alfresco.web.bean.TemplateSupportBean; +import org.alfresco.web.bean.TemplateMailHelperBean; import org.alfresco.web.bean.repository.Node; import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.bean.repository.User; import org.alfresco.web.ui.common.SortableSelectItem; import org.alfresco.web.ui.common.Utils; import org.alfresco.web.ui.common.component.UIGenericPicker; -import org.alfresco.web.ui.repo.component.template.DefaultModelHelper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.mail.javamail.MimeMessageHelper; -import org.springframework.mail.javamail.MimeMessagePreparator; /** * @author Kevin Roast @@ -96,6 +85,9 @@ public abstract class InviteUsersWizard extends AbstractWizardBean /** personService bean reference */ protected PersonService personService; + /** Helper providing template based mailing facilities */ + protected TemplateMailHelperBean mailHelper; + /** datamodel for table of roles for users */ private DataModel userRolesDataModel = null; @@ -104,12 +96,6 @@ public abstract class InviteUsersWizard extends AbstractWizardBean /** dialog state */ private String notify = NOTIFY_YES; - private String subject = null; - private String body = null; - private String automaticText = null; - private String template = null; - private String usingTemplate = null; - private String finalBody; /** * @return a cached list of available permissions for the type being dealt with @@ -175,11 +161,9 @@ public abstract class InviteUsersWizard extends AbstractWizardBean notify = NOTIFY_YES; userGroupRoles = new ArrayList(8); - subject = ""; - body = ""; - automaticText = ""; - template = null; - usingTemplate = null; + mailHelper = new TemplateMailHelperBean(); + mailHelper.setMailSender(mailSender); + mailHelper.setNodeService(nodeService); } /** @@ -239,7 +223,8 @@ public abstract class InviteUsersWizard extends AbstractWizardBean { if (this.personService.personExists(authority) == true) { - notifyUser(this.personService.getPerson(authority), nodeRef, from, userGroupRole.getRole()); + this.mailHelper.notifyUser( + this.personService.getPerson(authority), nodeRef, from, userGroupRole.getRole()); } } else if (authType.equals(AuthorityType.GROUP)) @@ -250,7 +235,8 @@ public abstract class InviteUsersWizard extends AbstractWizardBean { if (this.personService.personExists(userAuth) == true) { - notifyUser(this.personService.getPerson(userAuth), nodeRef, from, userGroupRole.getRole()); + this.mailHelper.notifyUser( + this.personService.getPerson(userAuth), nodeRef, from, userGroupRole.getRole()); } } } @@ -274,65 +260,6 @@ public abstract class InviteUsersWizard extends AbstractWizardBean return outcome; } - /** - * Send an email notification to the specified User authority - * - * @param person Person node representing the user - * @param node Node they are invited too - * @param from From text message - * @param roleText The role display label for the user invite notification - */ - private void notifyUser(NodeRef person, NodeRef node, final String from, String roleText) - { - final String to = (String)this.nodeService.getProperty(person, ContentModel.PROP_EMAIL); - - if (to != null && to.length() != 0) - { - String body = this.body; - if (this.usingTemplate != null) - { - FacesContext fc = FacesContext.getCurrentInstance(); - - // use template service to format the email - NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.usingTemplate); - ServiceRegistry services = Repository.getServiceRegistry(fc); - Map model = DefaultModelHelper.buildDefaultModel( - services, Application.getCurrentUser(fc), templateRef); - model.put("role", roleText); - model.put("space", new TemplateNode(node, Repository.getServiceRegistry(fc), null)); - - body = services.getTemplateService().processTemplate("freemarker", templateRef.toString(), model); - } - this.finalBody = body; - - MimeMessagePreparator mailPreparer = new MimeMessagePreparator() - { - public void prepare(MimeMessage mimeMessage) throws MessagingException - { - MimeMessageHelper message = new MimeMessageHelper(mimeMessage); - message.setTo(to); - message.setSubject(subject); - message.setText(finalBody); - message.setFrom(from); - } - }; - - if (logger.isDebugEnabled()) - logger.debug("Sending notification email to: " + to + "\n...with subject:\n" + subject + "\n...with body:\n" + body); - - try - { - // Send the message - this.mailSender.send(mailPreparer); - } - catch (Throwable e) - { - // don't stop the action but let admins know email is not getting sent - logger.error("Failed to send email to " + to, e); - } - } - } - /** * Returns the properties for current user-roles JSF DataModel * @@ -552,43 +479,6 @@ public abstract class InviteUsersWizard extends AbstractWizardBean return roles; } - /** - * Action handler called to insert a template as the email body - */ - public void insertTemplate(ActionEvent event) - { - if (this.template != null && this.template.equals(TemplateSupportBean.NO_SELECTION) == false) - { - // get the content of the template so the user can get a basic preview of it - try - { - NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.template); - ContentService cs = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getContentService(); - ContentReader reader = cs.getReader(templateRef, ContentModel.PROP_CONTENT); - if (reader != null && reader.exists()) - { - this.body = reader.getContentString(); - - this.usingTemplate = this.template; - } - } - catch (Throwable err) - { - Utils.addErrorMessage(MessageFormat.format(Application.getMessage( - FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); - } - } - } - - /** - * Action handler called to discard the template from the email body - */ - public void discardTemplate(ActionEvent event) - { - this.body = this.automaticText; - usingTemplate = null; - } - /** * @return Returns the notify listbox selection. */ @@ -605,70 +495,6 @@ public abstract class InviteUsersWizard extends AbstractWizardBean this.notify = notify; } - /** - * @return Returns the email body text. - */ - public String getBody() - { - return this.body; - } - - /** - * @param body The email body text to set. - */ - public void setBody(String body) - { - this.body = body; - } - - /** - * @return Returns the email subject text. - */ - public String getSubject() - { - return this.subject; - } - - /** - * @param subject The email subject text to set. - */ - public void setSubject(String subject) - { - this.subject = subject; - } - - /** - * @return Returns the email template Id - */ - public String getTemplate() - { - return this.template; - } - - /** - * @param template The email template to set. - */ - public void setTemplate(String template) - { - this.template = template; - } - - /** - * @return Returns if a template has been inserted by a user for email body. - */ - public String getUsingTemplate() - { - return this.usingTemplate; - } - - /** - * @param usingTemplate Template that has been inserted by a user for the email body. - */ - public void setUsingTemplate(String usingTemplate) - { - this.usingTemplate = usingTemplate; - } - /** * @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepDescription() */ @@ -771,7 +597,7 @@ public abstract class InviteUsersWizard extends AbstractWizardBean personName}) ); // default the subject line to an informative message - this.subject = buf.toString(); + this.mailHelper.setSubject(buf.toString()); // add the rest of the automatic body text buf.append("\r\n\r\n"); @@ -790,10 +616,9 @@ public abstract class InviteUsersWizard extends AbstractWizardBean buf.append(roleText); - this.automaticText = buf.toString(); - - // default the body content to this text - this.body = this.automaticText; + // set the body content and default text to this text + this.mailHelper.setAutomaticText(buf.toString()); + this.mailHelper.setBody(this.mailHelper.getAutomaticText()); } return outcome; @@ -825,7 +650,15 @@ public abstract class InviteUsersWizard extends AbstractWizardBean } return outcome; - } + } + + /** + * @return TemplateMailHelperBean instance for this wizard + */ + public TemplateMailHelperBean getMailHelper() + { + return this.mailHelper; + } /** * Simple wrapper class to represent a user/group and a role combination diff --git a/source/java/org/alfresco/web/bean/wizard/NewUserWizard.java b/source/java/org/alfresco/web/bean/wizard/NewUserWizard.java index 68228babe6..0100a7fff6 100644 --- a/source/java/org/alfresco/web/bean/wizard/NewUserWizard.java +++ b/source/java/org/alfresco/web/bean/wizard/NewUserWizard.java @@ -462,12 +462,7 @@ public class NewUserWizard extends AbstractWizardBean else { if (this.password.equals(this.confirm)) - { - if (!this.personService.getUserNamesAreCaseSensitive()) - { - this.userName = this.userName.toLowerCase(); - } - + { // create properties for Person type from submitted Form data Map props = new HashMap(7, 1.0f); props.put(ContentModel.PROP_USERNAME, this.userName); diff --git a/source/java/org/alfresco/web/bean/wizard/WizardManager.java b/source/java/org/alfresco/web/bean/wizard/WizardManager.java index f708413da4..a967648627 100644 --- a/source/java/org/alfresco/web/bean/wizard/WizardManager.java +++ b/source/java/org/alfresco/web/bean/wizard/WizardManager.java @@ -187,6 +187,18 @@ public class WizardManager { return Integer.toString(this.currentStep); } + + /** + * Returns the name of the current step, wizards should use + * the name of the step rather than the step number to discover + * the position as extra steps can be added via configuration. + * + * @return The name of the current step + */ + public String getCurrentStepName() + { + return ((StepConfig)this.steps.get(this.currentStep-1)).getName(); + } /** * Returns a list of UIListItems representing the steps of the wizard diff --git a/source/java/org/alfresco/web/bean/workflow/CancelWorkflowDialog.java b/source/java/org/alfresco/web/bean/workflow/CancelWorkflowDialog.java new file mode 100644 index 0000000000..c9c0b78964 --- /dev/null +++ b/source/java/org/alfresco/web/bean/workflow/CancelWorkflowDialog.java @@ -0,0 +1,106 @@ +package org.alfresco.web.bean.workflow; + +import java.text.MessageFormat; +import java.util.Map; + +import javax.faces.context.FacesContext; + +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.web.app.Application; +import org.alfresco.web.bean.dialog.BaseDialogBean; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Bean implementation for the "Cancel Workflow" dialog + * + * @author gavinc + */ +public class CancelWorkflowDialog extends BaseDialogBean +{ + protected String workflowInstanceId; + protected WorkflowService workflowService; + + private static final Log logger = LogFactory.getLog(CancelWorkflowDialog.class); + + // ------------------------------------------------------------------------------ + // Dialog implementation + + @Override + public void init(Map parameters) + { + super.init(parameters); + + // make sure the workflow instance id has been passed + this.workflowInstanceId = this.parameters.get("workflow-instance-id"); + if (this.workflowInstanceId == null || this.workflowInstanceId.length() == 0) + { + throw new IllegalArgumentException("Cancel workflow dialog called without workflow instance id"); + } + } + + @Override + protected String finishImpl(FacesContext context, String outcome) + throws Exception + { + if (logger.isDebugEnabled()) + logger.debug("Cancelling workflow with id: " + this.workflowInstanceId); + + // cancel the workflow + this.workflowService.cancelWorkflow(this.workflowInstanceId); + + if (logger.isDebugEnabled()) + logger.debug("Cancelled workflow with id: " + this.workflowInstanceId); + + return outcome; + } + + @Override + protected String getErrorMessageId() + { + return "error_cancel_workflow"; + } + + @Override + public boolean getFinishButtonDisabled() + { + return false; + } + + // ------------------------------------------------------------------------------ + // Bean Getters and Setters + + /** + * Returns the confirmation to display to the user before deleting the content. + * + * @return The formatted message to display + */ + public String getConfirmMessage() + { + String confirmMsg = Application.getMessage(FacesContext.getCurrentInstance(), + "cancel_workflow_confirm"); + + return MessageFormat.format(confirmMsg, + new Object[] {this.parameters.get("workflow-instance-name")}); + } + + /** + * Returns the workflow service instance + * + * @return WorkflowService instance + */ + public WorkflowService getWorkflowService() + { + return workflowService; + } + + /** + * Sets the workflow service to use + * + * @param workflowService The WorkflowService instance + */ + public void setWorkflowService(WorkflowService workflowService) + { + this.workflowService = workflowService; + } +} diff --git a/source/java/org/alfresco/web/bean/workflow/ManageWorkItemDialog.java b/source/java/org/alfresco/web/bean/workflow/ManageWorkItemDialog.java new file mode 100644 index 0000000000..05842b6c22 --- /dev/null +++ b/source/java/org/alfresco/web/bean/workflow/ManageWorkItemDialog.java @@ -0,0 +1,476 @@ +package org.alfresco.web.bean.workflow; + +import java.io.Serializable; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import javax.faces.context.FacesContext; +import javax.faces.event.ActionEvent; +import javax.transaction.UserTransaction; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.workflow.WorkflowModel; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.cmr.workflow.WorkflowTask; +import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition; +import org.alfresco.service.cmr.workflow.WorkflowTransition; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.web.app.Application; +import org.alfresco.web.bean.dialog.BaseDialogBean; +import org.alfresco.web.bean.repository.MapNode; +import org.alfresco.web.bean.repository.Node; +import org.alfresco.web.bean.repository.NodePropertyResolver; +import org.alfresco.web.bean.repository.Repository; +import org.alfresco.web.bean.repository.TransientNode; +import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig; +import org.alfresco.web.ui.common.Utils; +import org.alfresco.web.ui.common.component.UIActionLink; +import org.alfresco.web.ui.common.component.data.UIRichList; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Bean implementation for the "Manage WorkItem" dialog. + * + * @author gavinc + */ +public class ManageWorkItemDialog extends BaseDialogBean +{ + protected WorkflowService workflowService; + protected Node workItemNode; + protected WorkflowTask workItem; + protected WorkflowTransition[] transitions; + protected List resources; + protected WorkItemCompleteResolver completeResolver = new WorkItemCompleteResolver(); + protected UIRichList packageItemsRichList; + + protected static final String ID_PREFIX = "transition_"; + protected static final String CLIENT_ID_PREFIX = "dialog:" + ID_PREFIX; + + private static final Log logger = LogFactory.getLog(ManageWorkItemDialog.class); + + // ------------------------------------------------------------------------------ + // Dialog implementation + + @Override + public void init(Map parameters) + { + super.init(parameters); + + String taskId = this.parameters.get("id"); + this.workItem = this.workflowService.getTaskById(taskId); + + if (this.workItem != null) + { + // setup a transient node to represent the work item we're managing + WorkflowTaskDefinition taskDef = this.workItem.definition; + this.workItemNode = new TransientNode(taskDef.metadata.getName(), + "task_" + System.currentTimeMillis(), this.workItem.properties); + } + } + + @Override + protected String finishImpl(FacesContext context, String outcome) + throws Exception + { + if (logger.isDebugEnabled()) + logger.debug("Saving work item: " + this.workItemNode.getId()); + + // prepare the edited parameters for saving + Map params = WorkflowBean.prepareWorkItemParams(this.workItemNode); + + // update the task with the updated parameters + this.workflowService.updateTask(this.workItem.id, params, null, null); + + return outcome; + } + + @Override + public List getAdditionalButtons() + { + List buttons = null; + + if (this.workItem != null) + { + // get the transitions available from this work item and + // show them in the dialog as additional buttons + this.transitions = this.workItem.path.node.transitions; + + if (this.transitions != null) + { + buttons = new ArrayList(this.transitions.length); + + for (WorkflowTransition trans : this.transitions) + { + buttons.add(new DialogButtonConfig(ID_PREFIX + trans.title, trans.title, null, + "#{DialogManager.bean.transition}", "false", null)); + } + } + } + + return buttons; + } + + @Override + public String getFinishButtonLabel() + { + return Application.getMessage(FacesContext.getCurrentInstance(), "save"); + } + + @Override + public boolean getFinishButtonDisabled() + { + return false; + } + + // ------------------------------------------------------------------------------ + // Event handlers + + @SuppressWarnings("unused") + public String transition() + { + String outcome = getDefaultFinishOutcome(); + + if (logger.isDebugEnabled()) + logger.debug("Transitioning work item: " + this.workItemNode.getId()); + + // to find out which transition button was pressed we need + // to look for the button's id in the request parameters, + // the first non-null result is the button that was pressed. + FacesContext context = FacesContext.getCurrentInstance(); + Map reqParams = context.getExternalContext().getRequestParameterMap(); + + String selectedTransition = null; + for (WorkflowTransition trans : this.transitions) + { + Object result = reqParams.get(CLIENT_ID_PREFIX + trans.title); + if (result != null) + { + // this was the button that was pressed + selectedTransition = trans.id; + break; + } + } + + if (selectedTransition != null) + { + UserTransaction tx = null; + + try + { + tx = Repository.getUserTransaction(context); + tx.begin(); + + // prepare the edited parameters for saving + Map params = WorkflowBean.prepareWorkItemParams(this.workItemNode); + + // update the task with the updated parameters + this.workflowService.updateTask(this.workItem.id, params, null, null); + + // signal the selected transition to the workflow task + this.workflowService.endTask(this.workItem.id, selectedTransition); + + // commit the changes + tx.commit(); + + if (logger.isDebugEnabled()) + logger.debug("Ended work item with transition: " + selectedTransition); + } + catch (Throwable e) + { + // rollback the transaction + try { if (tx != null) {tx.rollback();} } catch (Exception ex) {} + Utils.addErrorMessage(formatErrorMessage(e), e); + outcome = this.getErrorOutcome(e); + } + } + + return outcome; + } + + /** + * Removes an item from the workflow package + * + * @param event The event containing a reference to the item to remove + */ + public void removePackageItem(ActionEvent event) + { + logger.info("remove package item: " + event); + } + + /** + * Toggles the complete flag for a workflow package item + * + * @param event The event containing a reference to the item to toggle the status for + */ + public void togglePackageItemComplete(ActionEvent event) + { + UserTransaction tx = null; + try + { + FacesContext context = FacesContext.getCurrentInstance(); + tx = Repository.getUserTransaction(context); + tx.begin(); + + UIActionLink link = (UIActionLink)event.getComponent(); + Map params = link.getParameterMap(); + + // create the node ref for the item we are toggling + NodeRef nodeRef = new NodeRef(Repository.getStoreRef(), + (String)params.get("id")); + + // get the existing list of completed items + List completedItems = (List)this.workItem.properties.get( + WorkflowModel.PROP_COMPLETED_ITEMS); + + if (completedItems == null) + { + // if it doesn't exist yet create the list and add the noderef + completedItems = new ArrayList(1); + completedItems.add(nodeRef); + this.workItem.properties.put(WorkflowModel.PROP_COMPLETED_ITEMS, + (Serializable)completedItems); + } + else + { + if (completedItems.contains(nodeRef)) + { + // the item is already in the list remove it + completedItems.remove(nodeRef); + + // NOTE: There is a bug somwehere which causes the list to be + // returned as a byte array instead of a list if an empty + // list is persisted, therefore if the list is now empty + // set the completed items back to null + if (completedItems.size() == 0) + { + this.workItem.properties.put(WorkflowModel.PROP_COMPLETED_ITEMS, null); + } + } + else + { + // the noderef is not in the list yet so just add it + completedItems.add(nodeRef); + } + } + + // update the task with the updated parameters + this.workflowService.updateTask(this.workItem.id, this.workItem.properties, + null, null); + + // commit the transaction + tx.commit(); + + // reset the rich list if the change was successful + this.packageItemsRichList.setValue(null); + } + catch (Throwable err) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); + this.resources = Collections.emptyList(); + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + } + } + + // ------------------------------------------------------------------------------ + // Bean Getters and Setters + + /** + * Sets the rich list being used for the workflow package items + * + * @param richList The rich list instance + */ + public void setPackageItemsRichList(UIRichList richList) + { + this.packageItemsRichList = richList; + } + + /** + * Returns the rich list being used for the workflow package items + * + * @return The rich list instance + */ + public UIRichList getPackageItemsRichList() + { + return this.packageItemsRichList; + } + + /** + * Returns the Node representing the work item + * + * @return The node + */ + public Node getWorkItemNode() + { + return this.workItemNode; + } + + /** + * Returns the action group the current task uses for the workflow package + * + * @return action group id + */ + public String getPackageActionGroup() + { + return (String)this.workItem.properties.get( + WorkflowModel.PROP_PACKAGE_ACTION_GROUP); + } + + /** + * Returns the action group the current task uses for each workflow package item + * + * @return action group id + */ + public String getPackageItemActionGroup() + { + return (String)this.workItem.properties.get( + WorkflowModel.PROP_PACKAGE_ITEM_ACTION_GROUP); + } + + /** + * Returns a list of resources associated with this work item + * i.e. the children of the workflow package + * + * @return The list of nodes + */ + public List getResources() + { + NodeRef workflowPackage = null; + Serializable obj = this.workItem.properties.get(WorkflowModel.ASSOC_PACKAGE); + // TODO: remove this workaroud where JBPM may return a String and not the NodeRef + if (obj instanceof NodeRef) + { + workflowPackage = (NodeRef)obj; + } + else if (obj instanceof String) + { + workflowPackage = new NodeRef((String)obj); + } + + this.resources = new ArrayList(4); + + if (workflowPackage != null) + { + UserTransaction tx = null; + try + { + FacesContext context = FacesContext.getCurrentInstance(); + tx = Repository.getUserTransaction(context, true); + tx.begin(); + + if (logger.isDebugEnabled()) + logger.debug("Found workflow package for work item '" + + this.workItem.id + "': " + workflowPackage ); + + List childRefs = this.nodeService.getChildAssocs(workflowPackage, + ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + + for (ChildAssociationRef ref: childRefs) + { + // create our Node representation from the NodeRef + NodeRef nodeRef = ref.getChildRef(); + + if (this.nodeService.exists(nodeRef)) + { + // find it's type so we can see if it's a node we are interested in + QName type = this.nodeService.getType(nodeRef); + + // make sure the type is defined in the data dictionary + TypeDefinition typeDef = this.dictionaryService.getType(type); + + if (typeDef != null) + { + // look for content nodes or links to content + // NOTE: folders within workflow packages are ignored for now + if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) || + ContentModel.TYPE_FILELINK.equals(type)) + { + // create our Node representation + MapNode node = new MapNode(nodeRef, this.nodeService, true); + this.browseBean.setupCommonBindingProperties(node); + + // add property resolvers to show path information + node.addPropertyResolver("path", this.browseBean.resolverPath); + node.addPropertyResolver("displayPath", this.browseBean.resolverDisplayPath); + + // add a property resolver to indicate whether the item has been completed or not + node.addPropertyResolver("completed", this.completeResolver); + + this.resources.add(node); + } + } + else + { + if (logger.isWarnEnabled()) + logger.warn("Found invalid object in database: id = " + nodeRef + ", type = " + type); + } + } + } + + // commit the transaction + tx.commit(); + } + catch (Throwable err) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); + this.resources = Collections.emptyList(); + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + } + } + else if (logger.isDebugEnabled()) + { + logger.debug("Failed to find workflow package for work item: " + this.workItem.id); + } + + return this.resources; + } + + /** + * Sets the workflow service to use + * + * @param workflowService + * WorkflowService instance + */ + public void setWorkflowService(WorkflowService workflowService) + { + this.workflowService = workflowService; + } + + // ------------------------------------------------------------------------------ + // Helper methods + + + // ------------------------------------------------------------------------------ + // Inner classes + + /** + * Property resolver to determine if the given node has been flagged as complete + */ + protected class WorkItemCompleteResolver implements NodePropertyResolver + { + public Object get(Node node) + { + String result = Application.getMessage(FacesContext.getCurrentInstance(), "no"); + + List completedItems = (List)workItem.properties.get( + WorkflowModel.PROP_COMPLETED_ITEMS); + + if (completedItems != null && completedItems.size() > 0 && + completedItems.contains(node.getNodeRef())) + { + result = Application.getMessage(FacesContext.getCurrentInstance(), "yes"); + } + + return result; + } + } +} diff --git a/source/java/org/alfresco/web/bean/workflow/ReassignWorkItemDialog.java b/source/java/org/alfresco/web/bean/workflow/ReassignWorkItemDialog.java new file mode 100644 index 0000000000..67f682e244 --- /dev/null +++ b/source/java/org/alfresco/web/bean/workflow/ReassignWorkItemDialog.java @@ -0,0 +1,199 @@ +package org.alfresco.web.bean.workflow; + +import java.io.Serializable; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; + +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; +import javax.faces.model.SelectItem; +import javax.transaction.UserTransaction; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.web.app.Application; +import org.alfresco.web.bean.dialog.BaseDialogBean; +import org.alfresco.web.bean.repository.Repository; +import org.alfresco.web.ui.common.SortableSelectItem; +import org.alfresco.web.ui.common.Utils; +import org.alfresco.web.ui.common.component.UIGenericPicker; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Bean implementation for the "Reassign Work Item" dialog + * + * @author gavinc + */ +public class ReassignWorkItemDialog extends BaseDialogBean +{ + protected String workItemId; + + protected WorkflowService workflowService; + protected PersonService personService; + + private static final Log logger = LogFactory.getLog(ReassignWorkItemDialog.class); + + // ------------------------------------------------------------------------------ + // Dialog implementation + + @Override + public void init(Map parameters) + { + super.init(parameters); + + this.workItemId = this.parameters.get("workitem-id"); + if (this.workItemId == null || this.workItemId.length() == 0) + { + throw new IllegalArgumentException("Reassign workitem dialog called without task id"); + } + } + + @Override + protected String finishImpl(FacesContext context, String outcome) + throws Exception + { + if (logger.isDebugEnabled()) + logger.debug("Reassigning work item with id: " + this.workItemId); + + UIComponent picker = context.getViewRoot().findComponent("dialog:dialog-body:user-picker"); + + if (picker != null && picker instanceof UIGenericPicker) + { + UIGenericPicker userPicker = (UIGenericPicker)picker; + String[] user = userPicker.getSelectedResults(); + if (user != null && user.length > 0) + { + // create a map to hold the new owner property then update the task + String userName = user[0]; + Map params = new HashMap(1); + params.put(ContentModel.PROP_OWNER, userName); + this.workflowService.updateTask(this.workItemId, params, null, null); + } + else + { + if (logger.isWarnEnabled()) + logger.warn("Failed to find selected user, reassign was unsuccessful"); + } + } + else + { + if (logger.isWarnEnabled()) + logger.warn("Failed to find user-picker component, reassign was unsuccessful"); + } + + if (logger.isDebugEnabled()) + logger.debug("Reassigning work item with id: " + this.workItemId); + + return outcome; + } + + @Override + protected String getErrorMessageId() + { + return "error_reassign_workitem"; + } + + // ------------------------------------------------------------------------------ + // Bean Getters and Setters + + /** + * Property accessed by the Generic Picker component. + * + * @return the array of filter options to show in the users/groups picker + */ + public SelectItem[] getFilters() + { + ResourceBundle bundle = Application.getBundle(FacesContext.getCurrentInstance()); + return new SelectItem[] {new SelectItem("0", bundle.getString("users"))}; + } + + /** + * Query callback method executed by the Generic Picker component. + * This method is part of the contract to the Generic Picker, it is up to the backing bean + * to execute whatever query is appropriate and return the results. + * + * @param filterIndex Index of the filter drop-down selection + * @param contains Text from the contains textbox + * + * @return An array of SelectItem objects containing the results to display in the picker. + */ + public SelectItem[] pickerCallback(int filterIndex, String contains) + { + FacesContext context = FacesContext.getCurrentInstance(); + + SelectItem[] items; + + UserTransaction tx = null; + try + { + tx = Repository.getUserTransaction(context, true); + tx.begin(); + + // build xpath to match available User/Person objects + NodeRef peopleRef = personService.getPeopleContainer(); + // NOTE: see SearcherComponentTest + String xpath = "*[like(@" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + "firstName, '%" + contains + "%', false)" + + " or " + "like(@" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + "lastName, '%" + contains + "%', false)]"; + + List nodes = searchService.selectNodes( + peopleRef, + xpath, + null, + this.namespaceService, + false); + + items = new SelectItem[nodes.size()]; + for (int index=0; index availableWorkflows; + protected Map workflows; + protected WorkflowService workflowService; + protected Node startTaskNode; + protected boolean nextButtonDisabled = false; + + private static final Log logger = LogFactory.getLog(StartWorkflowWizard.class); + + // ------------------------------------------------------------------------------ + // Wizard implementation + + @Override + public void init(Map parameters) + { + super.init(parameters); + + // reset the selected workflow + if (this.availableWorkflows != null && this.availableWorkflows.size() > 0) + { + this.selectedWorkflow = (String)this.availableWorkflows.get(0).getValue(); + } + else + { + this.selectedWorkflow = null; + } + + this.startTaskNode = null; + } + + @Override + protected String finishImpl(FacesContext context, String outcome) + throws Exception + { + // TODO: Deal with workflows that don't require any data + + if (logger.isDebugEnabled()) + logger.debug("Starting workflow: " + this.selectedWorkflow); + + // prepare the parameters from the current state of the property sheet + Map params = WorkflowBean.prepareWorkItemParams(this.startTaskNode); + + // create a workflow package for the attached items and add them + String itemToWorkflowId = this.parameters.get("item-to-workflow"); + if (itemToWorkflowId != null && itemToWorkflowId.length() > 0) + { + // create the node ref for the item and determine its type + NodeRef itemToWorkflow = new NodeRef(Repository.getStoreRef(), itemToWorkflowId); + QName type = this.nodeService.getType(itemToWorkflow); + + NodeRef workflowPackage = null; + if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) || + this.dictionaryService.isSubClass(type, ContentModel.TYPE_FILELINK)) + { + // create a workflow package and add the given item to workflow as a child + workflowPackage = this.workflowService.createPackage(null); + this.nodeService.addChild(workflowPackage, itemToWorkflow, + ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + QName.createValidLocalName((String)this.nodeService.getProperty( + itemToWorkflow, ContentModel.PROP_NAME)))); + } + + // add the workflow package to the parameter map + params.put(WorkflowModel.ASSOC_PACKAGE, workflowPackage); + } + + // setup the context for the workflow (this is the space the workflow was launched from) + Node workflowContext = this.navigator.getCurrentNode(); + if (workflowContext != null) + { + params.put(WorkflowModel.PROP_CONTEXT, (Serializable)workflowContext.getNodeRef()); + } + + // start the workflow to get access to the start task + WorkflowPath path = this.workflowService.startWorkflow(this.selectedWorkflow, params); + if (path != null) + { + // extract the start task + List tasks = this.workflowService.getTasksForWorkflowPath(path.id); + if (tasks.size() == 1) + { + WorkflowTask startTask = tasks.get(0); + + if (logger.isDebugEnabled()) + logger.debug("Found start task:" + startTask); + + if (startTask.state == WorkflowTaskState.IN_PROGRESS) + { + // end the start task to trigger the first 'proper' + // task in the workflow + this.workflowService.endTask(startTask.id, null); + } + } + + if (logger.isDebugEnabled()) + logger.debug("Started workflow: " + this.selectedWorkflow); + } + + return outcome; + } + + @Override + public String next() + { + String stepName = Application.getWizardManager().getCurrentStepName(); + + if ("options".equals(stepName) && this.startTaskNode == null) + { + // retrieve the start task for the selected workflow, get the task + // definition and create a transient node to allow the property + // sheet to collect the required data. + + WorkflowDefinition flowDef = this.workflows.get(this.selectedWorkflow); + + if (logger.isDebugEnabled()) + logger.debug("Selected workflow: "+ flowDef); + + WorkflowTaskDefinition taskDef = flowDef.startTaskDefinition; + if (taskDef != null) + { + if (logger.isDebugEnabled()) + logger.debug("Start task definition: " + taskDef); + + // create an instance of a task from the data dictionary + this.startTaskNode = new TransientNode(taskDef.metadata.getName(), + "task_" + System.currentTimeMillis(), null); + } + } + + return null; + } + + @Override + public boolean getNextButtonDisabled() + { + return this.nextButtonDisabled; + } + + // ------------------------------------------------------------------------------ + // Bean Getters and Setters + + /** + * Returns the workflow selected by the user + * + * @return The selected workflow + */ + public String getSelectedWorkflow() + { + return selectedWorkflow; + } + + /** + * Sets the selected workflow + * + * @param selectedWorkflow The workflow selected + */ + public void setSelectedWorkflow(String selectedWorkflow) + { + this.selectedWorkflow = selectedWorkflow; + } + + /** + * Returns the Node representing the start task metadata required + * + * @return The Node for the start task + */ + public Node getTaskMetadataNode() + { + return this.startTaskNode; + } + + /** + * Returns the action group the current task uses for the workflow package + * + * @return action group id + */ + public String getPackageActionGroup() + { + String actionGroup = null; + + WorkflowDefinition flowDef = this.workflows.get(this.selectedWorkflow); + WorkflowTaskDefinition taskDef = flowDef.startTaskDefinition; + if (taskDef != null) + { + PropertyDefinition propDef = taskDef.metadata.getProperties().get( + WorkflowModel.PROP_PACKAGE_ACTION_GROUP); + if (propDef != null) + { + actionGroup = propDef.getDefaultValue(); + } + } + + return actionGroup; + } + + /** + * Returns the action group the current task uses for each workflow package item + * + * @return action group id + */ + public String getPackageItemActionGroup() + { + String actionGroup = null; + + WorkflowDefinition flowDef = this.workflows.get(this.selectedWorkflow); + WorkflowTaskDefinition taskDef = flowDef.startTaskDefinition; + if (taskDef != null) + { + PropertyDefinition propDef = taskDef.metadata.getProperties().get( + WorkflowModel.PROP_PACKAGE_ITEM_ACTION_GROUP); + if (propDef != null) + { + actionGroup = propDef.getDefaultValue(); + } + } + + return actionGroup; + } + + /** + * @return Returns the summary data for the wizard. + */ + public String getSummary() + { + ResourceBundle bundle = Application.getBundle(FacesContext.getCurrentInstance()); + + String workflowName = null; + for (SelectItem item : this.availableWorkflows) + { + if (item.getValue().equals(this.selectedWorkflow)) + { + workflowName = item.getLabel(); + break; + } + } + + return buildSummary( + new String[] {bundle.getString("start_workflow")}, + new String[] {workflowName}); + } + + /** + * Returns a list of workflows that can be started. + * + * @return List of SelectItem objects representing the workflows + */ + public List getStartableWorkflows() + { + if (this.availableWorkflows == null) + { + this.availableWorkflows = new ArrayList(4); + this.workflows = new HashMap(4); + + List workflowDefs = this.workflowService.getDefinitions(); + for (WorkflowDefinition workflowDef : workflowDefs) + { + String label = workflowDef.title; + if (workflowDef.description != null && workflowDef.description.length() > 0) + { + label = label + " (" + workflowDef.description + ")"; + } + this.availableWorkflows.add(new SelectItem(workflowDef.id, label)); + this.workflows.put(workflowDef.id, workflowDef); + } + + // set the initial selected workflow to the first in the list, unless there are no + // workflows, in which disable the next button + if (this.availableWorkflows.size() > 0) + { + this.selectedWorkflow = (String)this.availableWorkflows.get(0).getValue(); + } + else + { + this.nextButtonDisabled = true; + } + } + + return availableWorkflows; + } + + /** + * Sets the workflow service to use + * + * @param workflowService WorkflowService instance + */ + public void setWorkflowService(WorkflowService workflowService) + { + this.workflowService = workflowService; + } +} diff --git a/source/java/org/alfresco/web/bean/workflow/WorkflowBean.java b/source/java/org/alfresco/web/bean/workflow/WorkflowBean.java new file mode 100644 index 0000000000..5368ca9be6 --- /dev/null +++ b/source/java/org/alfresco/web/bean/workflow/WorkflowBean.java @@ -0,0 +1,279 @@ +package org.alfresco.web.bean.workflow; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.faces.context.FacesContext; +import javax.transaction.UserTransaction; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.workflow.WorkflowModel; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.cmr.workflow.WorkflowTask; +import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition; +import org.alfresco.service.cmr.workflow.WorkflowTaskState; +import org.alfresco.service.cmr.workflow.WorkflowTransition; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO9075; +import org.alfresco.web.app.Application; +import org.alfresco.web.bean.repository.Node; +import org.alfresco.web.bean.repository.Repository; +import org.alfresco.web.bean.repository.TransientMapNode; +import org.alfresco.web.bean.repository.User; +import org.alfresco.web.ui.common.Utils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Managed bean used for handling workflow related features + * + * @author gavinc + */ +public class WorkflowBean +{ + protected NodeService nodeService; + protected WorkflowService workflowService; + protected List workItems; + protected List completedWorkItems; + + private static final Log logger = LogFactory.getLog(WorkflowBean.class); + + // ------------------------------------------------------------------------------ + // Bean Getters and Setters + + /** + * Returns a list of nodes representing the to do work items the + * current user has. + * + * @return List of to do work items + */ + public List getWorkItemsToDo() + { + // get the current username + FacesContext context = FacesContext.getCurrentInstance(); + User user = Application.getCurrentUser(context); + String userName = ISO9075.encode(user.getUserName()); + + UserTransaction tx = null; + try + { + tx = Repository.getUserTransaction(context, true); + tx.begin(); + + // get the current in progress tasks for the current user + List tasks = this.workflowService.getAssignedTasks( + userName, WorkflowTaskState.IN_PROGRESS); + + // create a list of transient nodes to represent + this.workItems = new ArrayList(tasks.size()); + for (WorkflowTask task : tasks) + { + Node node = createWorkItem(task); + this.workItems.add(node); + + if (logger.isDebugEnabled()) + logger.debug("Added to do work item: " + node); + } + + // commit the changes + tx.commit(); + } + catch (Throwable e) + { + // rollback the transaction + try { if (tx != null) {tx.rollback();} } catch (Exception ex) {} + Utils.addErrorMessage("Failed to get to do work items: " + e.toString(), e); + } + + return this.workItems; + } + + /** + * Returns a list of nodes representing the completed work items the + * current user has. + * + * @return List of completed work items + */ + public List getWorkItemsCompleted() + { + // get the current username + FacesContext context = FacesContext.getCurrentInstance(); + User user = Application.getCurrentUser(context); + String userName = ISO9075.encode(user.getUserName()); + + UserTransaction tx = null; + try + { + tx = Repository.getUserTransaction(context, true); + tx.begin(); + + // get the current in progress tasks for the current user + List tasks = this.workflowService.getAssignedTasks( + userName, WorkflowTaskState.COMPLETED); + + // create a list of transient nodes to represent + this.completedWorkItems = new ArrayList(tasks.size()); + for (WorkflowTask task : tasks) + { + Node node = createWorkItem(task); + this.completedWorkItems.add(node); + + if (logger.isDebugEnabled()) + logger.debug("Added completed work item: " + node); + } + + // commit the changes + tx.commit(); + } + catch (Throwable e) + { + // rollback the transaction + try { if (tx != null) {tx.rollback();} } catch (Exception ex) {} + Utils.addErrorMessage("Failed to get completed work items: " + e.toString(), e); + } + + return this.completedWorkItems; + } + + /** + * Sets the workflow service to use + * + * @param workflowService WorkflowService instance + */ + public void setWorkflowService(WorkflowService workflowService) + { + this.workflowService = workflowService; + } + + /** + * Sets the node service to use + * + * @param nodeService NodeService instance + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + // ------------------------------------------------------------------------------ + // Helper methods + + public static Map prepareWorkItemParams(Node node) + { + Map params = new HashMap(); + + // marshal the properties and associations captured by the property sheet + // back into a Map to pass to the workflow service + + // go through all the properties in the transient node and add them to + // params map + Map props = node.getProperties(); + for (String propName : props.keySet()) + { + QName propQName = Repository.resolveToQName(propName); + params.put(propQName, (Serializable)props.get(propName)); + } + + // go through any associations that have been added to the start task + // and build a list of NodeRefs representing the targets + Map> assocs = node.getAddedAssociations(); + for (String assocName : assocs.keySet()) + { + QName assocQName = Repository.resolveToQName(assocName); + + // get the associations added and create list of targets + Map addedAssocs = assocs.get(assocName); + List targets = new ArrayList(addedAssocs.size()); + for (AssociationRef assoc : addedAssocs.values()) + { + targets.add(assoc.getTargetRef()); + } + + // add the targets for this particular association + params.put(assocQName, (Serializable)targets); + } + + if (logger.isDebugEnabled()) + logger.debug("Prepared parameters: " + params); + + return params; + } + + /** + * Creates and populates a TransientNode to represent the given + * workflow task from the repository workflow engine + * + * @param task The task to create a representation of + */ + protected TransientMapNode createWorkItem(WorkflowTask task) + { + // get the type of the task + WorkflowTaskDefinition taskDef = task.definition; + + // create the basic transient node + TransientMapNode node = new TransientMapNode(taskDef.metadata.getName(), + task.title, task.properties); + + // add properties for the other useful metadata + node.getProperties().put(ContentModel.PROP_NAME.toString(), task.title); + node.getProperties().put("type", taskDef.metadata.getTitle()); + node.getProperties().put("id", task.id); + + // add the name of the source space (if there is one) + // TODO: remove this workaroud where JBPM may return a String and not the NodeRef + Serializable obj = task.properties.get(WorkflowModel.PROP_CONTEXT); + NodeRef context = null; + if (obj instanceof NodeRef) + { + context = (NodeRef)obj; + } + else if (obj instanceof String) + { + context = new NodeRef((String)obj); + } + + if (context != null) + { + String name = Repository.getNameForNode(this.nodeService, context); + node.getProperties().put("sourceSpaceName", name); + node.getProperties().put("sourceSpaceId", context.getId()); + } + + // add extra properties for completed tasks + if (task.state.equals(WorkflowTaskState.COMPLETED)) + { + // add the outcome label for any completed task + String outcome = null; + String transition = (String)task.properties.get(WorkflowModel.PROP_OUTCOME); + if (transition != null) + { + WorkflowTransition[] transitions = task.definition.node.transitions; + for (WorkflowTransition trans : transitions) + { + if (trans.id.equals(transition)) + { + outcome = trans.title; + break; + } + } + + if (outcome != null) + { + node.getProperties().put("outcome", outcome); + } + } + + // add the workflow instance id and name this taks belongs to + node.getProperties().put("workflowInstanceId", task.path.instance.id); + node.getProperties().put("workflowInstanceName", task.path.instance.definition.title); + } + + return node; + } +} diff --git a/source/java/org/alfresco/web/config/ActionsConfigElement.java b/source/java/org/alfresco/web/config/ActionsConfigElement.java index b57336ff36..e4c835f6b4 100644 --- a/source/java/org/alfresco/web/config/ActionsConfigElement.java +++ b/source/java/org/alfresco/web/config/ActionsConfigElement.java @@ -229,6 +229,7 @@ public class ActionsConfigElement extends ConfigElementAdapter public String Action; public String Href; public String Target; + public String Script; public String Onclick; } diff --git a/source/java/org/alfresco/web/config/ActionsElementReader.java b/source/java/org/alfresco/web/config/ActionsElementReader.java index 6c1fce52d1..7ebd0ce8cc 100644 --- a/source/java/org/alfresco/web/config/ActionsElementReader.java +++ b/source/java/org/alfresco/web/config/ActionsElementReader.java @@ -50,6 +50,7 @@ public class ActionsElementReader implements ConfigElementReader public static final String ELEMENT_ONCLICK = "onclick"; public static final String ELEMENT_HREF = "href"; public static final String ELEMENT_TARGET = "target"; + public static final String ELEMENT_SCRIPT = "script"; public static final String ELEMENT_PARAMS = "params"; public static final String ELEMENT_PARAM = "param"; public static final String ATTRIBUTE_ID = "id"; @@ -252,6 +253,7 @@ public class ActionsElementReader implements ConfigElementReader actionDef.TooltipMsg = actionElement.elementTextTrim(ELEMENT_TOOLTIPMSG); actionDef.Href = actionElement.elementTextTrim(ELEMENT_HREF); actionDef.Target = actionElement.elementTextTrim(ELEMENT_TARGET); + actionDef.Script = actionElement.elementTextTrim(ELEMENT_SCRIPT); actionDef.Action = actionElement.elementTextTrim(ELEMENT_ACTION); actionDef.ActionListener = actionElement.elementTextTrim(ELEMENT_ACTIONLISTENER); actionDef.Onclick = actionElement.elementTextTrim(ELEMENT_ONCLICK); diff --git a/source/java/org/alfresco/web/config/ClientConfigElement.java b/source/java/org/alfresco/web/config/ClientConfigElement.java index 4c75bfcc8d..0f6a3e216e 100644 --- a/source/java/org/alfresco/web/config/ClientConfigElement.java +++ b/source/java/org/alfresco/web/config/ClientConfigElement.java @@ -40,6 +40,7 @@ public class ClientConfigElement extends ConfigElementAdapter private String editLinkType = "http"; private String homeSpacePermission = null; private boolean ajaxEnabled = false; + private String initialLocation = null; /** * Default Constructor @@ -145,6 +146,17 @@ public class ClientConfigElement extends ConfigElementAdapter combinedElement.setFromEmailAddress(newElement.getFromEmailAddress()); } + if (newElement.isAjaxEnabled() != combinedElement.isAjaxEnabled()) + { + combinedElement.setAjaxEnabled(newElement.isAjaxEnabled()); + } + + if (newElement.getInitialLocation() != null && + newElement.getInitialLocation().equals(combinedElement.getInitialLocation()) == false) + { + combinedElement.setInitialLocation(newElement.getInitialLocation()); + } + return combinedElement; } @@ -348,4 +360,20 @@ public class ClientConfigElement extends ConfigElementAdapter { this.ajaxEnabled = ajaxEnabled; } + + /** + * @return Returns the default initial location for the user. + */ + public String getInitialLocation() + { + return this.initialLocation; + } + + /** + * @param initialLocation The initial location to set. + */ + /*package*/ void setInitialLocation(String initialLocation) + { + this.initialLocation = initialLocation; + } } diff --git a/source/java/org/alfresco/web/config/ClientElementReader.java b/source/java/org/alfresco/web/config/ClientElementReader.java index 00feb8377f..43d25005ee 100644 --- a/source/java/org/alfresco/web/config/ClientElementReader.java +++ b/source/java/org/alfresco/web/config/ClientElementReader.java @@ -40,6 +40,7 @@ public class ClientElementReader implements ConfigElementReader public static final String ELEMENT_FROMEMAILADDRESS = "from-email-address"; public static final String ELEMENT_SHELFVISIBLE = "shelf-visible"; public static final String ELEMENT_AJAX_ENABLED = "ajax-enabled"; + public static final String ELEMENT_INITIALLOCATION = "initial-location"; /** * @see org.alfresco.config.xml.elementreader.ConfigElementReader#parse(org.dom4j.Element) @@ -144,6 +145,13 @@ public class ClientElementReader implements ConfigElementReader { configElement.setAjaxEnabled(Boolean.parseBoolean(ajaxEnabled.getTextTrim())); } + + // get the initial location + Element initialLocation = element.element(ELEMENT_INITIALLOCATION); + if (initialLocation != null) + { + configElement.setInitialLocation(initialLocation.getTextTrim()); + } } return configElement; diff --git a/source/java/org/alfresco/web/config/DashboardsConfigElement.java b/source/java/org/alfresco/web/config/DashboardsConfigElement.java index d35e0256e5..83ed1a8039 100644 --- a/source/java/org/alfresco/web/config/DashboardsConfigElement.java +++ b/source/java/org/alfresco/web/config/DashboardsConfigElement.java @@ -148,5 +148,6 @@ public class DashboardsConfigElement extends ConfigElementAdapter public String Description; public String DescriptionId; public String JSPPage; + public String ConfigJSPPage; } } diff --git a/source/java/org/alfresco/web/config/DashboardsElementReader.java b/source/java/org/alfresco/web/config/DashboardsElementReader.java index b9e3e08079..e532ec8486 100644 --- a/source/java/org/alfresco/web/config/DashboardsElementReader.java +++ b/source/java/org/alfresco/web/config/DashboardsElementReader.java @@ -46,6 +46,7 @@ public class DashboardsElementReader implements ConfigElementReader public static final String ATTR_LABELID = "label-id"; public static final String ATTR_DESCRIPTIONID = "description-id"; public static final String ATTR_JSP = "jsp"; + public static final String ATTR_CONFIGJSP = "config-jsp"; public static final String ATTR_ALLOWNARROW = "allow-narrow"; /** @@ -165,6 +166,7 @@ public class DashboardsElementReader implements ConfigElementReader def.AllowNarrow = Boolean.parseBoolean(allowNarrow); } def.JSPPage = getMandatoryDashletAttributeValue(config, ATTR_JSP); + def.ConfigJSPPage = config.attributeValue(ATTR_CONFIGJSP); String label = config.attributeValue(ATTR_LABEL); String labelId = config.attributeValue(ATTR_LABELID); if ((label == null || label.length() == 0) && (labelId == null || labelId.length() == 0)) diff --git a/source/java/org/alfresco/web/config/DialogsConfigElement.java b/source/java/org/alfresco/web/config/DialogsConfigElement.java index e9298ce86c..677440b564 100644 --- a/source/java/org/alfresco/web/config/DialogsConfigElement.java +++ b/source/java/org/alfresco/web/config/DialogsConfigElement.java @@ -131,12 +131,15 @@ public class DialogsConfigElement extends ConfigElementAdapter protected String description; protected String descriptionId; protected String errorMsgId = "error_dialog"; + protected boolean isOKButtonVisible = true; + protected List buttons; public DialogConfig(String name, String page, String bean, String actionsConfigId, String icon, String title, String titleId, String description, String descriptionId, - String errorMsgId) + String errorMsgId, boolean isOKButtonVisible, + List buttons) { // check the mandatory parameters are present ParameterCheck.mandatoryString("name", name); @@ -152,6 +155,8 @@ public class DialogsConfigElement extends ConfigElementAdapter this.titleId = titleId; this.description = description; this.descriptionId = descriptionId; + this.isOKButtonVisible = isOKButtonVisible; + this.buttons = buttons; if (errorMsgId != null && errorMsgId.length() > 0) { @@ -209,6 +214,16 @@ public class DialogsConfigElement extends ConfigElementAdapter return this.errorMsgId; } + public boolean isOKButtonVisible() + { + return this.isOKButtonVisible; + } + + public List getButtons() + { + return this.buttons; + } + /** * @see java.lang.Object#toString() */ @@ -225,7 +240,98 @@ public class DialogsConfigElement extends ConfigElementAdapter buffer.append(" titleId=").append(this.titleId); buffer.append(" description=").append(this.description); buffer.append(" descriptionId=").append(this.descriptionId); - buffer.append(" errorMsgId=").append(this.errorMsgId).append(")"); + buffer.append(" errorMsgId=").append(this.errorMsgId); + buffer.append(" isOKButtonVisible=").append(this.isOKButtonVisible); + buffer.append(" buttons=").append(this.buttons).append(")"); + return buffer.toString(); + } + } + + /** + * Inner class representing the configuration for an additional + * dialog button. + * + * @author gavinc + */ + public static class DialogButtonConfig + { + private String id; + private String label; + private String labelId; + private String action; + private String disabled; + private String onclick; + + public DialogButtonConfig(String id, String label, String labelId, + String action, String disabled, String onclick) + { + this.id = id; + this.label = label; + this.labelId = labelId; + this.action = action; + this.disabled = disabled; + this.onclick = onclick; + + if ((this.label == null || this.label.length() == 0) && + (this.labelId == null || this.labelId.length() == 0)) + { + throw new ConfigException("A dialog button needs to have a label or a label-id"); + } + + if (this.action == null || this.action.length() == 0) + { + throw new ConfigException("A dialog button requires an action"); + } + else if (this.action.startsWith("#{") == false) + { + throw new ConfigException("The action for a dialog button must be a method binding expression, '" + + this.action + "' is not!"); + } + } + + public String getAction() + { + return action; + } + + public String getDisabled() + { + return disabled; + } + + public String getId() + { + return id; + } + + public String getLabel() + { + return label; + } + + public String getLabelId() + { + return labelId; + } + + public String getOnclick() + { + return onclick; + } + + /** + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + StringBuilder buffer = new StringBuilder(super.toString()); + buffer.append(" (id=").append(this.id); + buffer.append(" label=").append(this.label); + buffer.append(" label-id=").append(this.labelId); + buffer.append(" action=").append(this.action); + buffer.append(" disabled=").append(this.disabled); + buffer.append(" onclick=").append(this.onclick).append(")"); return buffer.toString(); } } diff --git a/source/java/org/alfresco/web/config/DialogsElementReader.java b/source/java/org/alfresco/web/config/DialogsElementReader.java index f0fc6f4949..7df5472757 100644 --- a/source/java/org/alfresco/web/config/DialogsElementReader.java +++ b/source/java/org/alfresco/web/config/DialogsElementReader.java @@ -16,11 +16,14 @@ */ package org.alfresco.web.config; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import org.alfresco.config.ConfigElement; import org.alfresco.config.ConfigException; import org.alfresco.config.xml.elementreader.ConfigElementReader; +import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig; import org.dom4j.Element; /** @@ -32,6 +35,8 @@ public class DialogsElementReader implements ConfigElementReader { public static final String ELEMENT_DIALOGS = "dialogs"; public static final String ELEMENT_DIALOG = "dialog"; + public static final String ELEMENT_BUTTONS = "buttons"; + public static final String ELEMENT_BUTTON = "button"; public static final String ATTR_NAME = "name"; public static final String ATTR_PAGE = "page"; public static final String ATTR_MANAGED_BEAN = "managed-bean"; @@ -42,10 +47,18 @@ public class DialogsElementReader implements ConfigElementReader public static final String ATTR_DESCRIPTION = "description"; public static final String ATTR_DESCRIPTION_ID = "description-id"; public static final String ATTR_ERROR_MSG_ID = "error-message-id"; + public static final String ATTR_SHOW_OK_BUTTON = "show-ok-button"; + public static final String ATTR_ID = "id"; + public static final String ATTR_LABEL = "label"; + public static final String ATTR_LABEL_ID = "label-id"; + public static final String ATTR_ACTION = "action"; + public static final String ATTR_DISABLED = "disabled"; + public static final String ATTR_ONCLICK = "onclick"; /** * @see org.alfresco.config.xml.elementreader.ConfigElementReader#parse(org.dom4j.Element) */ + @SuppressWarnings("unchecked") public ConfigElement parse(Element element) { DialogsConfigElement configElement = null; @@ -62,7 +75,7 @@ public class DialogsElementReader implements ConfigElementReader configElement = new DialogsConfigElement(); - // go through the items to show + // go through the dialogs Iterator items = element.elementIterator(ELEMENT_DIALOG); while (items.hasNext()) { @@ -78,10 +91,20 @@ public class DialogsElementReader implements ConfigElementReader String description = item.attributeValue(ATTR_DESCRIPTION); String descriptionId = item.attributeValue(ATTR_DESCRIPTION_ID); String errorMsgId = item.attributeValue(ATTR_ERROR_MSG_ID); + String showOK = item.attributeValue(ATTR_SHOW_OK_BUTTON); + + boolean isOKButtonVisible = true; + if (showOK != null) + { + isOKButtonVisible = Boolean.parseBoolean(showOK); + } + + // parse any buttons that may be present + List buttons = parseButtons(item); DialogsConfigElement.DialogConfig cfg = new DialogsConfigElement.DialogConfig( name, page, bean, actions, icon, title, titleId, description, - descriptionId, errorMsgId); + descriptionId, errorMsgId, isOKButtonVisible, buttons); configElement.addDialog(cfg); } @@ -90,4 +113,44 @@ public class DialogsElementReader implements ConfigElementReader return configElement; } + /** + * Retrieve the configuration for additional buttons. + * + * @param dialog The dialog XML element + * @return List of configured buttons + */ + @SuppressWarnings("unchecked") + protected List parseButtons(Element dialog) + { + List buttons = null; + + // iterate over any configured buttons + Element buttonsConfig = dialog.element(ELEMENT_BUTTONS); + if (buttonsConfig != null) + { + buttons = new ArrayList(4); + + Iterator children = buttonsConfig.elementIterator(ELEMENT_BUTTON); + while (children.hasNext()) + { + Element button = children.next(); + + String id = button.attributeValue(ATTR_ID); + String label = button.attributeValue(ATTR_LABEL); + String labelId = button.attributeValue(ATTR_LABEL_ID); + String action = button.attributeValue(ATTR_ACTION); + String disabled = button.attributeValue(ATTR_DISABLED); + String onclick = button.attributeValue(ATTR_ONCLICK); + + // create the button config object + DialogButtonConfig btnCfg = new DialogButtonConfig(id, label, + labelId, action, disabled, onclick); + + // add the button to the list + buttons.add(btnCfg); + } + } + + return buttons; + } } diff --git a/source/java/org/alfresco/web/config/PropertySheetConfigElement.java b/source/java/org/alfresco/web/config/PropertySheetConfigElement.java index 5b556071fe..bd0fca32c9 100644 --- a/source/java/org/alfresco/web/config/PropertySheetConfigElement.java +++ b/source/java/org/alfresco/web/config/PropertySheetConfigElement.java @@ -32,9 +32,6 @@ import org.alfresco.config.element.ConfigElementAdapter; */ public class PropertySheetConfigElement extends ConfigElementAdapter { - // TODO: Currently this object just deals with properties and associations to show, - // in the future it will also deal with properties and associations to hide. - public static final String CONFIG_ELEMENT_ID = "property-sheet"; protected Map items = new LinkedHashMap(8, 10f); @@ -193,6 +190,24 @@ public class PropertySheetConfigElement extends ConfigElementAdapter converter, inView, inEdit, compGenerator)); } + /** + * Adds a separator + * + * @param name The name of the separator + * @param displayLabel Display label to use for the separator + * @param displayLabelId Display label message id to use for the separator + * @param inView Sets whether the separator should be shown when the property + * sheet is in view mode + * @param inEdit Sets whether the separator should be shown when the property + * sheet is in edit mode + * @param compGenerator The name of a bean that can be used as a component generator + */ + /*package*/ void addSeparator(String name, String displayLabel, String displayLabelId, + String inView, String inEdit, String compGenerator) + { + addItem(new SeparatorConfig(name, displayLabel, displayLabelId, inView, inEdit, compGenerator)); + } + /** * @return Returns a map of the all the items */ @@ -266,6 +281,12 @@ public class PropertySheetConfigElement extends ConfigElementAdapter boolean readOnly, String converter, String inView, String inEdit, String compGenerator, String ignoreIfMissing) { + // check we have a name + if (name == null || name.length() == 0) + { + throw new ConfigException("You must specify a name for a proprty sheet item"); + } + this.name = name; this.displayLabel = displayLabel; this.displayLabelId = displayLabelId; @@ -420,4 +441,17 @@ public class PropertySheetConfigElement extends ConfigElementAdapter inView, inEdit, compGenerator, null); } } + + /** + * Inner class to represent a configured separator + */ + public class SeparatorConfig extends ItemConfig + { + public SeparatorConfig(String name, String displayLabel, String displayLabelId, + String inView, String inEdit, String compGenerator) + { + super(name, displayLabel, displayLabelId, false, null, + inView, inEdit, compGenerator, null); + } + } } diff --git a/source/java/org/alfresco/web/config/PropertySheetElementReader.java b/source/java/org/alfresco/web/config/PropertySheetElementReader.java index bdbf413ded..e97bd6669c 100644 --- a/source/java/org/alfresco/web/config/PropertySheetElementReader.java +++ b/source/java/org/alfresco/web/config/PropertySheetElementReader.java @@ -34,6 +34,7 @@ public class PropertySheetElementReader implements ConfigElementReader public static final String ELEMENT_SHOW_PROPERTY = "show-property"; public static final String ELEMENT_SHOW_ASSOC = "show-association"; public static final String ELEMENT_SHOW_CHILD_ASSOC = "show-child-association"; + public static final String ELEMENT_SEPARATOR = "separator"; public static final String ATTR_NAME = "name"; public static final String ATTR_DISPLAY_LABEL = "display-label"; public static final String ATTR_DISPLAY_LABEL_ID = "display-label-id"; @@ -96,6 +97,10 @@ public class PropertySheetElementReader implements ConfigElementReader configElement.addChildAssociation(propName, label, labelId, readOnly, converter, inView, inEdit, compGenerator); } + else if (ELEMENT_SEPARATOR.equals(item.getName())) + { + configElement.addSeparator(propName, label, labelId, inView, inEdit, compGenerator); + } } } diff --git a/source/java/org/alfresco/web/ui/common/ComponentConstants.java b/source/java/org/alfresco/web/ui/common/ComponentConstants.java index 062a8afc89..4f7e6d3d1b 100644 --- a/source/java/org/alfresco/web/ui/common/ComponentConstants.java +++ b/source/java/org/alfresco/web/ui/common/ComponentConstants.java @@ -23,6 +23,7 @@ public final class ComponentConstants { public static final String JAVAX_FACES_INPUT = "javax.faces.Input"; public static final String JAVAX_FACES_TEXT = "javax.faces.Text"; + public static final String JAVAX_FACES_TEXTAREA = "javax.faces.Textarea"; public static final String JAVAX_FACES_OUTPUT = "javax.faces.Output"; public static final String JAVAX_FACES_GRID = "javax.faces.Grid"; public static final String JAVAX_FACES_PANEL = "javax.faces.Panel"; @@ -31,6 +32,7 @@ public final class ComponentConstants public static final String JAVAX_FACES_GRAPHIC = "javax.faces.Graphic"; public static final String JAVAX_FACES_PARAMETER = "javax.faces.Parameter"; public static final String JAVAX_FACES_MENU = "javax.faces.Menu"; + public static final String JAVAX_FACES_BUTTON = "javax.faces.Button"; /** * Private constructor diff --git a/source/java/org/alfresco/web/ui/common/component/UIGenericPicker.java b/source/java/org/alfresco/web/ui/common/component/UIGenericPicker.java index b8c74892f9..07a5ad9bb5 100644 --- a/source/java/org/alfresco/web/ui/common/component/UIGenericPicker.java +++ b/source/java/org/alfresco/web/ui/common/component/UIGenericPicker.java @@ -67,6 +67,7 @@ public class UIGenericPicker extends UICommand private Boolean showContains = null; private Boolean showAddButton = null; private Boolean filterRefresh = null; + private Boolean multiSelect = null; private String addButtonLabel; private Integer width = null; private Integer height = null; @@ -118,6 +119,7 @@ public class UIGenericPicker extends UICommand currentResults = (SelectItem[])values[11]; filters = (SelectItem[])values[12]; filterRefresh = (Boolean)values[13]; + multiSelect = (Boolean)values[14]; } /** @@ -125,7 +127,7 @@ public class UIGenericPicker extends UICommand */ public Object saveState(FacesContext context) { - Object values[] = new Object[14]; + Object values[] = new Object[15]; // standard component attributes are saved by the super class values[0] = super.saveState(context); values[1] = showFilter; @@ -141,6 +143,7 @@ public class UIGenericPicker extends UICommand values[11] = currentResults; values[12] = filters; values[13] = filterRefresh; + values[14] = multiSelect; return (values); } @@ -221,7 +224,7 @@ public class UIGenericPicker extends UICommand { // use reflection to execute the query callback method and retrieve results Object result = callback.invoke(getFacesContext(), new Object[] { - this.filterIndex, this.contains}); + this.filterIndex, this.contains.trim()}); if (result instanceof SelectItem[]) { @@ -354,7 +357,14 @@ public class UIGenericPicker extends UICommand out.write(Integer.toString(getHeight())); out.write("px' name='"); out.write(clientId + FIELD_RESULTS); - out.write("' multiple>"); + out.write("' id='"); + out.write(clientId + FIELD_RESULTS); + out.write("'"); + if (getMultiSelect() == true) + { + out.write(" multiple"); + } + out.write(">"); // results if (currentResults != null) @@ -530,6 +540,28 @@ public class UIGenericPicker extends UICommand this.filterRefresh = Boolean.valueOf(filterRefresh); } + /** + * @return true if multi select should be enabled. + */ + public boolean getMultiSelect() + { + ValueBinding vb = getValueBinding("multiSelect"); + if (vb != null) + { + this.multiSelect = (Boolean)vb.getValue(getFacesContext()); + } + + return multiSelect != null ? multiSelect.booleanValue() : true; + } + + /** + * @param multiSelect Flag to determine whether multi select is enabled + */ + public void setMultiSelect(boolean multiSelect) + { + this.multiSelect = Boolean.valueOf(multiSelect); + } + /** * @return Returns the width. */ @@ -633,6 +665,7 @@ public class UIGenericPicker extends UICommand /** * Class representing the an action relevant to the Generic Selector component. */ + @SuppressWarnings("serial") public static class PickerEvent extends ActionEvent { public PickerEvent(UIComponent component, int action, int filterIndex, String contains, String[] results) diff --git a/source/java/org/alfresco/web/ui/common/component/UIPanel.java b/source/java/org/alfresco/web/ui/common/component/UIPanel.java index 6d12cafcd2..7a5842fc68 100644 --- a/source/java/org/alfresco/web/ui/common/component/UIPanel.java +++ b/source/java/org/alfresco/web/ui/common/component/UIPanel.java @@ -101,6 +101,10 @@ public class UIPanel extends UICommand // determine whether we have any adornments String label = getLabel(); + if (label != null) + { + label = Utils.encode(label); + } if (label != null || isProgressive() == true || titleComponent != null) { this.hasAdornments = true; @@ -156,11 +160,11 @@ public class UIPanel extends UICommand if (isExpanded() == true) { - out.write(Utils.buildImageTag(context, WebResources.IMAGE_EXPANDED, 11, 11, "")); + out.write(Utils.buildImageTag(context, WebResources.IMAGE_EXPANDED, 11, 11, label)); } else { - out.write(Utils.buildImageTag(context, WebResources.IMAGE_COLLAPSED, 11, 11, "")); + out.write(Utils.buildImageTag(context, WebResources.IMAGE_COLLAPSED, 11, 11, label)); } out.write("  "); @@ -174,11 +178,11 @@ public class UIPanel extends UICommand Utils.outputAttribute(out, getAttributes().get("styleClass"), "class"); out.write('>'); - out.write(Utils.encode(label)); + out.write(label); // already encoded above out.write(""); } - + if (this.hasAdornments) { out.write(""); diff --git a/source/java/org/alfresco/web/ui/common/renderer/ActionLinkRenderer.java b/source/java/org/alfresco/web/ui/common/renderer/ActionLinkRenderer.java index 85cdb404b5..0d0acb2fc2 100644 --- a/source/java/org/alfresco/web/ui/common/renderer/ActionLinkRenderer.java +++ b/source/java/org/alfresco/web/ui/common/renderer/ActionLinkRenderer.java @@ -17,7 +17,9 @@ package org.alfresco.web.ui.common.renderer; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.net.URLEncoder; import java.util.Map; import javax.faces.component.UIComponent; @@ -133,13 +135,44 @@ public class ActionLinkRenderer extends BaseRenderer else { String href = link.getHref(); - if (href.startsWith("http") == false && href.startsWith("file") == false) + + // prefix the web context path if required + linkBuf.append(" actionParams = getParameterComponents(link); + if (actionParams != null) + { + boolean first = (href.indexOf('?') == -1); + for (String name : actionParams.keySet()) + { + String paramValue = actionParams.get(name); + if (first) + { + linkBuf.append('?'); + first = false; + } + else + { + linkBuf.append('&'); + } + try + { + linkBuf.append(name).append("=").append(URLEncoder.encode(paramValue, "UTF-8")); + } + catch (UnsupportedEncodingException err) + { + // if this happens we have bigger problems than a missing URL parameter...! + } + } + } + + linkBuf.append('"'); // output href 'target' attribute if supplied if (link.getTarget() != null) diff --git a/source/java/org/alfresco/web/ui/common/renderer/DatePickerRenderer.java b/source/java/org/alfresco/web/ui/common/renderer/DatePickerRenderer.java index 4f85d1404f..6181461f69 100644 --- a/source/java/org/alfresco/web/ui/common/renderer/DatePickerRenderer.java +++ b/source/java/org/alfresco/web/ui/common/renderer/DatePickerRenderer.java @@ -36,6 +36,9 @@ import javax.faces.convert.ConverterException; import javax.faces.model.SelectItem; import org.alfresco.web.app.Application; +import org.alfresco.web.ui.common.Utils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * @author kevinr @@ -51,6 +54,12 @@ public class DatePickerRenderer extends BaseRenderer private static final String FIELD_DAY = "_day"; private static final String FIELD_HOUR = "_hour"; private static final String FIELD_MINUTE = "_minute"; + private static final String FIELD_CMD = "_cmd"; + private static final int CMD_SET = 1; + private static final int CMD_RESET = 2; + private static final int CMD_TODAY = 3; + + private static final Log logger = LogFactory.getLog(DatePickerRenderer.class); /** * @see javax.faces.render.Renderer#decode(javax.faces.context.FacesContext, javax.faces.component.UIComponent) @@ -66,26 +75,68 @@ public class DatePickerRenderer extends BaseRenderer // TODO: should check for disabled/readonly here - no need to decode String clientId = component.getClientId(context); Map params = context.getExternalContext().getRequestParameterMap(); - String year = (String)params.get(clientId + FIELD_YEAR); - if (year != null) + + // see if a command was invoked + String cmd = (String)params.get(clientId + FIELD_CMD); + if (cmd != null && cmd.length() > 0) { - // found data for our component - String month = (String)params.get(clientId + FIELD_MONTH); - String day = (String)params.get(clientId + FIELD_DAY); - String hour = (String)params.get(clientId + FIELD_HOUR); - String minute = (String)params.get(clientId + FIELD_MINUTE); + int action = Integer.parseInt(cmd); - // we encode the values needed for the component as we see fit - int[] parts = new int[5]; - parts[0] = Integer.parseInt(year); - parts[1] = Integer.parseInt(month); - parts[2] = Integer.parseInt(day); - parts[3] = Integer.parseInt(hour); - parts[4] = Integer.parseInt(minute); - - // save the data in an object for our component as the "EditableValueHolder" - // all UI Input Components support this interface for the submitted value - ((EditableValueHolder)component).setSubmittedValue(parts); + switch (action) + { + case CMD_RESET: + { + // set the submitted value to be null + ((EditableValueHolder)component).setSubmittedValue(null); + + // set the component value to be null too + ((EditableValueHolder)component).setValue(null); + + break; + } + + default: + { + // the user is either trying to set the date for the first + // time or set it back to today's date, create the parts array + // to represent this and set as the submitted value + int[] parts = new int[5]; + + Calendar date = Calendar.getInstance(); + parts[0] = date.get(Calendar.YEAR); + parts[1] = date.get(Calendar.MONTH); + parts[2] = date.get(Calendar.DAY_OF_MONTH); + parts[3] = date.get(Calendar.HOUR_OF_DAY); + parts[4] = date.get(Calendar.MINUTE); + + ((EditableValueHolder)component).setSubmittedValue(parts); + } + } + } + else + { + // a command was not invoked so decode the date the user set (if present) + String year = (String)params.get(clientId + FIELD_YEAR); + if (year != null) + { + // found data for our component + String month = (String)params.get(clientId + FIELD_MONTH); + String day = (String)params.get(clientId + FIELD_DAY); + String hour = (String)params.get(clientId + FIELD_HOUR); + String minute = (String)params.get(clientId + FIELD_MINUTE); + + // we encode the values needed for the component as we see fit + int[] parts = new int[5]; + parts[0] = Integer.parseInt(year); + parts[1] = Integer.parseInt(month); + parts[2] = Integer.parseInt(day); + parts[3] = Integer.parseInt(hour); + parts[4] = Integer.parseInt(minute); + + // save the data in an object for our component as the "EditableValueHolder" + // all UI Input Components support this interface for the submitted value + ((EditableValueHolder)component).setSubmittedValue(parts); + } } } catch (NumberFormatException nfe) @@ -124,6 +175,9 @@ public class DatePickerRenderer extends BaseRenderer if (component.isRendered() == true) { Date date = null; + String clientId = component.getClientId(context); + ResponseWriter out = context.getResponseWriter(); + String cmdFieldName = clientId + FIELD_CMD; // this is part of the spec: // first you attempt to build the date from the submitted value @@ -136,57 +190,119 @@ public class DatePickerRenderer extends BaseRenderer { // second if no submitted value is found, default to the current value Object value = ((ValueHolder)component).getValue(); - // finally check for null value and create default if needed - date = value instanceof Date ? (Date)value : new Date(); + if (value instanceof Date) + { + date = (Date)value; + } } - // get the attributes from the component we need for rendering - int nStartYear; - Integer startYear = (Integer)component.getAttributes().get("startYear"); - if (startYear != null) + // create a flag to show if the component is disabled + Boolean disabled = (Boolean)component.getAttributes().get("disabled"); + if (disabled == null) { - nStartYear = startYear.intValue(); + disabled = Boolean.FALSE; + } + + if (date != null) + { + // get the attributes from the component we need for rendering + int nStartYear; + Integer startYear = (Integer)component.getAttributes().get("startYear"); + if (startYear != null) + { + nStartYear = startYear.intValue(); + } + else + { + nStartYear = new Date().getYear() + 1900 + 2; // for "effectivity date" searches + } + + int nYearCount = 25; + Integer yearCount = (Integer)component.getAttributes().get("yearCount"); + if (yearCount != null) + { + nYearCount = yearCount.intValue(); + } + + // now we render the output for our component + // we create 3 drop-down menus for day, month and year and + // two text fields for the hour and minute + + // note that we build a client id for our form elements that we are then + // able to decode() as above. + Calendar calendar = new GregorianCalendar(); + calendar.setTime(date); + renderMenu(out, component, getDays(), calendar.get(Calendar.DAY_OF_MONTH), clientId + FIELD_DAY); + renderMenu(out, component, getMonths(), calendar.get(Calendar.MONTH), clientId + FIELD_MONTH); + renderMenu(out, component, getYears(nStartYear, nYearCount), calendar.get(Calendar.YEAR), clientId + FIELD_YEAR); + + // make sure we have a flag to determine whether to show the time + Boolean showTime = (Boolean)component.getAttributes().get("showTime"); + if (showTime == null) + { + showTime = Boolean.FALSE; + } + + out.write(" "); + renderTimeElement(out, component, calendar.get(Calendar.HOUR_OF_DAY), clientId + FIELD_HOUR, showTime.booleanValue()); + if (showTime.booleanValue()) + { + out.write(" : "); + } + renderTimeElement(out, component, calendar.get(Calendar.MINUTE), clientId + FIELD_MINUTE, showTime.booleanValue()); + out.write(" "); + + // render 2 links (if the component is not disabled) to allow the user to reset the + // date back to null or to select today's date + if (disabled.booleanValue() == false) + { + out.write(" "); + } } else { - nStartYear = new Date().getYear() + 1900 + 2; // for "effectivity date" searches + // Render a link indicating there isn't a date set (unless the property is disabled) + out.write(""); } - int nYearCount = 25; - Integer yearCount = (Integer)component.getAttributes().get("yearCount"); - if (yearCount != null) + // also output a hidden field containing the current value of the date, this will + // allow JavaScript to determine if a value is set for validation purposes. + out.write(""); } } diff --git a/source/java/org/alfresco/web/ui/common/renderer/ImagePickerRadioRenderer.java b/source/java/org/alfresco/web/ui/common/renderer/ImagePickerRadioRenderer.java index dc7a8b1ebe..e3212a7212 100644 --- a/source/java/org/alfresco/web/ui/common/renderer/ImagePickerRadioRenderer.java +++ b/source/java/org/alfresco/web/ui/common/renderer/ImagePickerRadioRenderer.java @@ -50,6 +50,7 @@ public class ImagePickerRadioRenderer extends BaseRenderer private int columns; private int position; private boolean open; + private boolean imageSelected = false; // ------------------------------------------------------------------------------ // Renderer implemenation @@ -89,6 +90,7 @@ public class ImagePickerRadioRenderer extends BaseRenderer this.columns = 1; this.position = 0; this.open = false; + this.imageSelected = false; ResponseWriter out = context.getResponseWriter(); @@ -210,6 +212,14 @@ public class ImagePickerRadioRenderer extends BaseRenderer ResponseWriter out = context.getResponseWriter(); out.write(""); + + // if we didn't select any image, default to the first one + if (this.imageSelected == false) + { + out.write("\n\n"); + } } /** @@ -275,6 +285,7 @@ public class ImagePickerRadioRenderer extends BaseRenderer if (itemValue != null && itemValue.equals(currentValue)) { out.write(" checked='true'"); + this.imageSelected = true; } if (tooltip != null) diff --git a/source/java/org/alfresco/web/ui/common/tag/GenericPickerTag.java b/source/java/org/alfresco/web/ui/common/tag/GenericPickerTag.java index 24c3151c86..f88c181ae9 100644 --- a/source/java/org/alfresco/web/ui/common/tag/GenericPickerTag.java +++ b/source/java/org/alfresco/web/ui/common/tag/GenericPickerTag.java @@ -20,7 +20,6 @@ import javax.faces.FacesException; import javax.faces.component.UICommand; import javax.faces.component.UIComponent; import javax.faces.el.MethodBinding; -import javax.faces.el.ValueBinding; import org.alfresco.web.ui.common.component.UIGenericPicker; @@ -57,6 +56,7 @@ public class GenericPickerTag extends BaseComponentTag setBooleanProperty(component, "showContains", this.showContains); setBooleanProperty(component, "showAddButton", this.showAddButton); setBooleanProperty(component, "filterRefresh", this.filterRefresh); + setBooleanProperty(component, "multiSelect", this.multiSelect); setStringProperty(component, "addButtonLabel", this.addButtonLabel); setActionProperty((UICommand)component, this.action); setActionListenerProperty((UICommand)component, this.actionListener); @@ -94,6 +94,7 @@ public class GenericPickerTag extends BaseComponentTag this.queryCallback = null; this.filters = null; this.filterRefresh = null; + this.multiSelect = null; } /** @@ -205,8 +206,20 @@ public class GenericPickerTag extends BaseComponentTag { this.filterRefresh = filterRefresh; } + + /** + * Set the multiSelect + * + * @param mutliSelect the multiSelect + */ + public void setMultiSelect(String multiSelect) + { + this.multiSelect = multiSelect; + } - + /** the multiSelect */ + private String multiSelect; + /** the filterRefresh */ private String filterRefresh; diff --git a/source/java/org/alfresco/web/ui/repo/RepoConstants.java b/source/java/org/alfresco/web/ui/repo/RepoConstants.java index c2f2597d0d..079c26176d 100644 --- a/source/java/org/alfresco/web/ui/repo/RepoConstants.java +++ b/source/java/org/alfresco/web/ui/repo/RepoConstants.java @@ -26,6 +26,7 @@ public final class RepoConstants public static final String ALFRESCO_FACES_ASSOCIATION = "org.alfresco.faces.Association"; public static final String ALFRESCO_FACES_CHILD_ASSOCIATION = "org.alfresco.faces.ChildAssociation"; public static final String ALFRESCO_FACES_PROPERTY = "org.alfresco.faces.Property"; + public static final String ALFRESCO_FACES_SEPARATOR = "org.alfresco.faces.Separator"; public static final String ALFRESCO_FACES_SPACE_SELECTOR = "org.alfresco.faces.SpaceSelector"; public static final String ALFRESCO_FACES_ASSOC_EDITOR = "org.alfresco.faces.AssociationEditor"; public static final String ALFRESCO_FACES_CHILD_ASSOC_EDITOR = "org.alfresco.faces.ChildAssociationEditor"; @@ -43,12 +44,15 @@ public final class RepoConstants public static final String GENERATOR_LABEL = "LabelGenerator"; public static final String GENERATOR_TEXT_FIELD = "TextFieldGenerator"; + public static final String GENERATOR_TEXT_AREA = "TextAreaGenerator"; public static final String GENERATOR_CHECKBOX = "CheckboxGenerator"; public static final String GENERATOR_DATE_PICKER = "DatePickerGenerator"; public static final String GENERATOR_DATETIME_PICKER = "DateTimePickerGenerator"; public static final String GENERATOR_CATEGORY_SELECTOR = "CategorySelectorGenerator"; public static final String GENERATOR_ASSOCIATION = "AssociationGenerator"; public static final String GENERATOR_CHILD_ASSOCIATION = "ChildAssociationGenerator"; + public static final String GENERATOR_SEPARATOR = "SeparatorGenerator"; + public static final String GENERATOR_HEADER_SEPARATOR = "HeaderSeparatorGenerator"; /** * Private constructor diff --git a/source/java/org/alfresco/web/ui/repo/component/UIActions.java b/source/java/org/alfresco/web/ui/repo/component/UIActions.java index bdbbce52c8..dcebb9290c 100644 --- a/source/java/org/alfresco/web/ui/repo/component/UIActions.java +++ b/source/java/org/alfresco/web/ui/repo/component/UIActions.java @@ -17,6 +17,7 @@ package org.alfresco.web.ui.repo.component; import java.io.IOException; +import java.net.URLEncoder; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -30,6 +31,7 @@ import javax.faces.el.ValueBinding; import org.alfresco.config.Config; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.web.app.Application; import org.alfresco.web.bean.repository.Node; import org.alfresco.web.config.ActionsConfigElement; @@ -369,6 +371,7 @@ public class UIActions extends SelfRenderingComponent control.setOnclick(actionDef.Onclick); } } + if (actionDef.Href != null) { if (UIComponentTagUtils.isValueReference(actionDef.Href)) @@ -380,6 +383,29 @@ public class UIActions extends SelfRenderingComponent control.setHref(actionDef.Href); } } + else if (actionDef.Script != null && actionDef.Script.length() != 0) + { + // found a script reference - may be a Path or a NodeRef + StringBuilder scriptHref = new StringBuilder(100); + scriptHref.append("/command/script/execute"); + if (actionDef.Script.charAt(0) == '/') + { + // found a Path - encode it as a URL argument + scriptHref.append("?scriptPath="); + scriptHref.append(Utils.replace(URLEncoder.encode(actionDef.Script, "UTF-8"), "+", "%20")); + } + else + { + // found a NodeRef string, encode as URL elements + NodeRef ref = new NodeRef(actionDef.Script); + scriptHref.append('/').append(ref.getStoreRef().getProtocol()) + .append('/').append(ref.getStoreRef().getIdentifier()) + .append('/').append(ref.getId()); + } + // set the full script execution URL as the href for the control + control.setHref(scriptHref.toString()); + } + control.setTarget(actionDef.Target); control.setImage(actionDef.Image); diff --git a/source/java/org/alfresco/web/ui/repo/component/UIDialogButtons.java b/source/java/org/alfresco/web/ui/repo/component/UIDialogButtons.java new file mode 100644 index 0000000000..e9f19c9927 --- /dev/null +++ b/source/java/org/alfresco/web/ui/repo/component/UIDialogButtons.java @@ -0,0 +1,292 @@ +package org.alfresco.web.ui.repo.component; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; + +import javax.faces.component.UICommand; +import javax.faces.component.UIComponent; +import javax.faces.component.UIOutput; +import javax.faces.component.html.HtmlCommandButton; +import javax.faces.context.FacesContext; +import javax.faces.context.ResponseWriter; +import javax.faces.el.MethodBinding; +import javax.faces.el.ValueBinding; + +import org.alfresco.web.app.Application; +import org.alfresco.web.app.servlet.FacesHelper; +import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig; +import org.alfresco.web.ui.common.ComponentConstants; +import org.alfresco.web.ui.common.Utils; +import org.alfresco.web.ui.common.component.SelfRenderingComponent; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Component that displays the buttons for a dialog. + *

+ * The standard OK and Cancel buttons + * are always generated. Any additional buttons, either configured + * or generated dynamically by the dialog, are generated in between + * the standard buttons. + * + * @author gavinc + */ +public class UIDialogButtons extends SelfRenderingComponent +{ + protected static final String BINDING_EXPRESSION_START = "#{"; + + private static final Log logger = LogFactory.getLog(UIDialogButtons.class); + + @Override + public String getFamily() + { + return "org.alfresco.faces.DialogButtons"; + } + + @Override + public void encodeBegin(FacesContext context) throws IOException + { + if (!isRendered()) return; + + if (this.getChildCount() == 0) + { + // generate all the required buttons the first time + generateButtons(context); + } + + ResponseWriter out = context.getResponseWriter(); + out.write(""); + } + + @Override + public void encodeChildren(FacesContext context) throws IOException + { + if (!isRendered()) return; + + ResponseWriter out = context.getResponseWriter(); + + // render the buttons + for (Iterator i = getChildren().iterator(); i.hasNext(); /**/) + { + out.write(""); + } + } + + @Override + public void encodeEnd(FacesContext context) throws IOException + { + if (!isRendered()) return; + + ResponseWriter out = context.getResponseWriter(); + out.write("
"); + + UIComponent child = (UIComponent)i.next(); + Utils.encodeRecursive(context, child); + + out.write("
"); + } + + @Override + public boolean getRendersChildren() + { + return true; + } + + /** + * Generates the buttons for the dialog currently being shown. + * + * @param context Faces context + */ + @SuppressWarnings("unchecked") + protected void generateButtons(FacesContext context) + { + // generate the OK button, if necessary + if (Application.getDialogManager().isOKButtonVisible()) + { + UICommand okButton = (UICommand)context.getApplication(). + createComponent(HtmlCommandButton.COMPONENT_TYPE); + okButton.setRendererType(ComponentConstants.JAVAX_FACES_BUTTON); + FacesHelper.setupComponentId(context, okButton, "finish-button"); + + // create the binding for the finish button label + ValueBinding valueBinding = context.getApplication().createValueBinding( + "#{DialogManager.finishButtonLabel}"); + okButton.setValueBinding("value", valueBinding); + + // create the action binding + MethodBinding methodBinding = context.getApplication().createMethodBinding( + "#{DialogManager.finish}", null); + okButton.setAction(methodBinding); + + // create the binding for whether the button is disabled + valueBinding = context.getApplication().createValueBinding( + "#{DialogManager.finishButtonDisabled}"); + okButton.setValueBinding("disabled", valueBinding); + + // setup CSS class for button + String styleClass = (String)this.getAttributes().get("styleClass"); + if (styleClass != null) + { + okButton.getAttributes().put("styleClass", styleClass); + } + + // add the OK button + this.getChildren().add(okButton); + } + + // generate the additional buttons + generateAdditionalButtons(context); + + // generate the OK button + UICommand cancelButton = (UICommand)context.getApplication(). + createComponent(HtmlCommandButton.COMPONENT_TYPE); + cancelButton.setRendererType(ComponentConstants.JAVAX_FACES_BUTTON); + FacesHelper.setupComponentId(context, cancelButton, "cancel-button"); + + // create the binding for the cancel button label + ValueBinding valueBinding = context.getApplication().createValueBinding( + "#{DialogManager.cancelButtonLabel}"); + cancelButton.setValueBinding("value", valueBinding); + + // create the action binding + MethodBinding methodBinding = context.getApplication().createMethodBinding( + "#{DialogManager.cancel}", null); + cancelButton.setAction(methodBinding); + + // setup CSS class for button + String styleClass = (String)this.getAttributes().get("styleClass"); + if (styleClass != null) + { + cancelButton.getAttributes().put("styleClass", styleClass); + } + + // set the immediate flag to true + cancelButton.getAttributes().put("immediate", Boolean.TRUE); + + // add the Cancel button + this.getChildren().add(cancelButton); + } + + /** + * If there are any additional buttons to add as defined by the dialog + * configuration and the dialog at runtime they are generated in this + * method. + * + * @param context Faces context + */ + @SuppressWarnings("unchecked") + protected void generateAdditionalButtons(FacesContext context) + { + // get potential list of additional buttons + List buttons = Application.getDialogManager().getAdditionalButtons(); + + if (buttons != null && buttons.size() > 0) + { + if (logger.isDebugEnabled()) + logger.debug("Adding " + buttons.size() + " additional buttons: " + buttons); + + for (DialogButtonConfig buttonCfg : buttons) + { + UICommand button = (UICommand)context.getApplication(). + createComponent(HtmlCommandButton.COMPONENT_TYPE); + button.setRendererType(ComponentConstants.JAVAX_FACES_BUTTON); + FacesHelper.setupComponentId(context, button, buttonCfg.getId()); + + // setup the value of the button (the label) + String label = buttonCfg.getLabel(); + if (label != null) + { + // see if the label represents a value binding + if (label.startsWith(BINDING_EXPRESSION_START)) + { + ValueBinding binding = context.getApplication().createValueBinding(label); + button.setValueBinding("value", binding); + } + else + { + button.setValue(label); + } + } + else + { + // NOTE: the config checks that a label or a label id + // is present so we can assume there is an id + // if there isn't a label + String labelId = buttonCfg.getLabelId(); + label = Application.getMessage(context, labelId); + button.setValue(label); + } + + // setup the action binding, the config checks that an action + // is present so no need to check for NullPointer. It also checks + // it represents a method binding expression. + String action = buttonCfg.getAction(); + MethodBinding methodBinding = context.getApplication(). + createMethodBinding(action, null); + button.setAction(methodBinding); + + // setup the disabled attribute, check for null and + // binding expressions + String disabled = buttonCfg.getDisabled(); + if (disabled != null && disabled.length() > 0) + { + if (disabled.startsWith(BINDING_EXPRESSION_START)) + { + ValueBinding binding = context.getApplication(). + createValueBinding(disabled); + button.setValueBinding("disabled", binding); + } + else + { + button.getAttributes().put("disabled", + Boolean.parseBoolean(disabled)); + } + } + + // setup CSS class for the button + String styleClass = (String)this.getAttributes().get("styleClass"); + if (styleClass != null) + { + button.getAttributes().put("styleClass", styleClass); + } + + // setup the onclick handler for the button + String onclick = buttonCfg.getOnclick(); + if (onclick != null && onclick.length() > 0) + { + button.getAttributes().put("onclick", onclick); + } + + // add the button + this.getChildren().add(button); + + if (logger.isDebugEnabled()) + logger.debug("Added button with id of: " + button.getId()); + } + + // add a spacing row to separate the additional buttons from the Cancel button + addSpacingRow(context); + } + } + + /** + * Creates an output text component to represent a spacing row. + * + * @param context Faces context + */ + @SuppressWarnings("unchecked") + protected void addSpacingRow(FacesContext context) + { + UIOutput spacingRow = (UIOutput)context.getApplication().createComponent( + ComponentConstants.JAVAX_FACES_OUTPUT); + spacingRow.setRendererType(ComponentConstants.JAVAX_FACES_TEXT); + FacesHelper.setupComponentId(context, spacingRow, null); + spacingRow.setValue("

"); + spacingRow.getAttributes().put("escape", Boolean.FALSE); + this.getChildren().add(spacingRow); + } +} + + + diff --git a/source/java/org/alfresco/web/ui/repo/component/UISearchCustomProperties.java b/source/java/org/alfresco/web/ui/repo/component/UISearchCustomProperties.java index b45916ab67..fc085b9029 100644 --- a/source/java/org/alfresco/web/ui/repo/component/UISearchCustomProperties.java +++ b/source/java/org/alfresco/web/ui/repo/component/UISearchCustomProperties.java @@ -78,6 +78,7 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements /** * @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext) */ + @SuppressWarnings("unchecked") public void encodeBegin(FacesContext context) throws IOException { if (isRendered() == false) @@ -133,6 +134,7 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements * * @param context FacesContext */ + @SuppressWarnings("unchecked") private void createComponentsFromConfig(FacesContext context) { DictionaryService dd = Repository.getServiceRegistry(context).getDictionaryService(); @@ -236,6 +238,7 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements * * @return UIComponent */ + @SuppressWarnings("unchecked") private UIComponent generateControl(FacesContext context, PropertyDefinition propDef, String displayLabel, String beanBinding) { UIComponent control = null; @@ -265,6 +268,22 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements { Boolean showTime = Boolean.valueOf(typeName.equals(DataTypeDefinition.DATETIME)); + // create value bindings for the start year and year count attributes + ValueBinding startYearBind = null; + ValueBinding yearCountBind = null; + + if (showTime) + { + startYearBind = facesApp.createValueBinding("#{DateTimePickerGenerator.startYear}"); + yearCountBind = facesApp.createValueBinding("#{DateTimePickerGenerator.yearCount}"); + } + else + { + startYearBind = facesApp.createValueBinding("#{DatePickerGenerator.startYear}"); + yearCountBind = facesApp.createValueBinding("#{DatePickerGenerator.yearCount}"); + } + + // Need to output component for From and To date selectors and labels // also neeed checkbox for enable/disable state - requires an outer wrapper component control = (UIPanel)facesApp.createComponent(ComponentConstants.JAVAX_FACES_PANEL); @@ -298,7 +317,8 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements UIInput inputFromDate = (UIInput)facesApp.createComponent(ComponentConstants.JAVAX_FACES_INPUT); inputFromDate.setId(context.getViewRoot().createUniqueId()); inputFromDate.setRendererType(RepoConstants.ALFRESCO_FACES_DATE_PICKER_RENDERER); - inputFromDate.getAttributes().put("yearCount", new Integer(30)); + inputFromDate.setValueBinding("startYear", startYearBind); + inputFromDate.setValueBinding("yearCount", yearCountBind); inputFromDate.getAttributes().put("showTime", showTime); ValueBinding vbFromDate = facesApp.createValueBinding( "#{" + beanBinding + "[\"" + PREFIX_DATE_FROM + propDef.getName().toString() + "\"]}"); @@ -316,7 +336,8 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements UIInput inputToDate = (UIInput)facesApp.createComponent(ComponentConstants.JAVAX_FACES_INPUT); inputToDate.setId(context.getViewRoot().createUniqueId()); inputToDate.setRendererType(RepoConstants.ALFRESCO_FACES_DATE_PICKER_RENDERER); - inputToDate.getAttributes().put("yearCount", new Integer(30)); + inputToDate.setValueBinding("startYear", startYearBind); + inputToDate.setValueBinding("yearCount", yearCountBind); inputToDate.getAttributes().put("showTime", showTime); ValueBinding vbToDate = facesApp.createValueBinding( "#{" + beanBinding + "[\"" + PREFIX_DATE_TO + propDef.getName().toString() + "\"]}"); @@ -333,8 +354,8 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements // any other type is represented as an input text field control = (UIInput)facesApp.createComponent(ComponentConstants.JAVAX_FACES_INPUT); control.setRendererType(ComponentConstants.JAVAX_FACES_TEXT); - control.getAttributes().put("size", "28"); - control.getAttributes().put("maxlength", "1024"); + control.setValueBinding("size", facesApp.createValueBinding("#{TextFieldGenerator.size}")); + control.setValueBinding("maxlength", facesApp.createValueBinding("#{TextFieldGenerator.maxLength}")); control.setValueBinding(VALUE, vb); } diff --git a/source/java/org/alfresco/web/ui/repo/component/UIUserGroupPicker.java b/source/java/org/alfresco/web/ui/repo/component/UIUserGroupPicker.java new file mode 100644 index 0000000000..fd99017deb --- /dev/null +++ b/source/java/org/alfresco/web/ui/repo/component/UIUserGroupPicker.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.web.ui.repo.component; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; + +import javax.faces.component.NamingContainer; +import javax.faces.component.UICommand; +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; +import javax.faces.context.ResponseWriter; +import javax.faces.event.ActionEvent; + +import org.alfresco.web.app.Application; +import org.alfresco.web.ui.common.Utils; +import org.alfresco.web.ui.common.WebResources; + +/** + * Seld rendering component tied to the EmailSpaceUsersDialog bean. Renders a hierarchy of + * user/group authorities. Each authority can be (de)selected and groups can be expanded/collapsed + * to display and select from the child authorities in the group. Nested groups are supported. + * + * @author Kevin Roast + */ +public class UIUserGroupPicker extends UICommand +{ + /** action ids */ + public final static int ACTION_NONE = -1; + public final static int ACTION_EXPANDCOLLAPSE = 0; + public final static int ACTION_SELECT = 1; + + private static String SELECTED_AUTHORITY = "_check"; + + + // ------------------------------------------------------------------------------ + // Component implementation + + /** + * Default constructor + */ + public UIUserGroupPicker() + { + setRendererType(null); + } + + /** + * @see javax.faces.component.UIComponent#getFamily() + */ + public String getFamily() + { + return "org.alfresco.faces.UserGroupPicker"; + } + + /** + * @see javax.faces.component.UIComponentBase#decode(javax.faces.context.FacesContext) + */ + public void decode(FacesContext context) + { + Map requestMap = context.getExternalContext().getRequestParameterMap(); + Map valuesMap = context.getExternalContext().getRequestParameterValuesMap(); + String fieldId = getHiddenFieldName(context); + String value = (String)requestMap.get(fieldId); + + if (value != null && value.length() != 0) + { + // decode the values - we are expecting an action identifier and an authority name + int sepIndex = value.indexOf(NamingContainer.SEPARATOR_CHAR); + int action = Integer.parseInt(value.substring(0, sepIndex)); + String authority = value.substring(sepIndex + 1); + + // queue an event + PickerEvent event = new PickerEvent(this, action, authority); + queueEvent(event); + } + } + + /** + * @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext) + */ + public void encodeBegin(FacesContext context) throws IOException + { + if (isRendered() == false) + { + return; + } + + ResponseWriter out = context.getResponseWriter(); + + ResourceBundle bundle = Application.getBundle(context); + + String clientId = getClientId(context); + + // start outer table + out.write(""); + + // get the data that represents the users/groups to display + List userGroups = (List)getValue(); + if (userGroups != null) + { + for (Map authority : userGroups) + { + String authorityId = (String)authority.get("id"); + + out.write(""); + } + } + + out.write("
"); + + // walk parent hierarchy to calculate width of this cell + int width = 16; + Map parent = (Map)authority.get("parent"); + while (parent != null) + { + width += 16; + parent = (Map)parent.get("parent"); + } + out.write(""); + + out.write("
"); + + // output expanded/collapsed icon if authority is a group + boolean expanded = false; + boolean isGroup = (Boolean)authority.get("isGroup"); + if (isGroup) + { + // either output the expanded or collapsed selectable widget + expanded = (Boolean)authority.get("expanded"); + String image = expanded ? WebResources.IMAGE_EXPANDED : WebResources.IMAGE_COLLAPSED; + out.write(Utils.buildImageTag(context, image, 11, 11, "", + generateFormSubmit(context, ACTION_EXPANDCOLLAPSE, authorityId))); + } + out.write(""); + + // output selected checkbox if not expanded and not a duplicate + boolean duplicate = (Boolean)authority.get("duplicate"); + if (duplicate == false && (isGroup == false || expanded == false)) + { + boolean selected = (Boolean)authority.get("selected"); + out.write("'); + } + out.write(""); + + // output icon + out.write(Utils.buildImageTag(context, (String)authority.get("icon"), 16, 16, "")); + out.write(""); + + // output textual information + if (duplicate) + { + out.write(""); + } + out.write((String)authority.get("fullName")); + out.write(" ("); + out.write((String)authority.get("roles")); + out.write(")"); + if (duplicate) + { + out.write(""); + } + out.write("
"); + } + + + // ------------------------------------------------------------------------------ + // Private helpers + + /** + * We use a hidden field per picker instance on the page. + * + * @return hidden field name + */ + private String getHiddenFieldName(FacesContext context) + { + return getClientId(context); + } + + /** + * Generate FORM submit JavaScript for the specified action + * + * @param context FacesContext + * @param action Action index + * @param authority Authority Id of the action source + * + * @return FORM submit JavaScript + */ + private String generateFormSubmit(FacesContext context, int action, String authority) + { + return Utils.generateFormSubmit(context, this, getHiddenFieldName(context), + Integer.toString(action) + NamingContainer.SEPARATOR_CHAR + authority); + } + + + // ------------------------------------------------------------------------------ + // Inner classes + + /** + * Class representing the an action relevant to the User Group picker component. + */ + public static class PickerEvent extends ActionEvent + { + public PickerEvent(UIComponent component, int action, String authority) + { + super(component); + Action = action; + Authority = authority; + } + + public String Authority; + public int Action; + } +} diff --git a/source/java/org/alfresco/web/ui/repo/component/property/BaseAssociationEditor.java b/source/java/org/alfresco/web/ui/repo/component/property/BaseAssociationEditor.java index 1bf6901a1f..b79bceb239 100644 --- a/source/java/org/alfresco/web/ui/repo/component/property/BaseAssociationEditor.java +++ b/source/java/org/alfresco/web/ui/repo/component/property/BaseAssociationEditor.java @@ -32,6 +32,7 @@ import javax.faces.event.AbortProcessingException; import javax.faces.event.ActionEvent; import javax.faces.event.FacesEvent; +import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -698,9 +699,17 @@ public abstract class BaseAssociationEditor extends UIInput } out.write("'>"); - out.write(Repository.getDisplayPath(nodeService.getPath(targetRef))); - out.write("/"); - out.write(Repository.getNameForNode(nodeService, targetRef)); + if (ContentModel.TYPE_PERSON.equals(nodeService.getType(targetRef))) + { + out.write((String)nodeService.getProperty(targetRef, ContentModel.PROP_USERNAME)); + } + else + { + out.write(Repository.getDisplayPath(nodeService.getPath(targetRef))); + out.write("/"); + out.write(Repository.getNameForNode(nodeService, targetRef)); + } + out.write(""); - out.write(Repository.getDisplayPath(nodeService.getPath(item))); - out.write("/"); - out.write(Repository.getNameForNode(nodeService, item)); + // if the node represents a person, show the username instead of the name + if (ContentModel.TYPE_PERSON.equals(nodeService.getType(item))) + { + out.write((String)nodeService.getProperty(item, ContentModel.PROP_USERNAME)); + } + else + { + out.write(Repository.getDisplayPath(nodeService.getPath(item))); + out.write("/"); + out.write(Repository.getNameForNode(nodeService, item)); + } out.write(""); } } @@ -852,17 +869,31 @@ public abstract class BaseAssociationEditor extends UIInput if (assocDef != null) { // find and show all the available options for the current association + String type = assocDef.getTargetClass().getName().toString(); StringBuilder query = new StringBuilder("+TYPE:\""); - query.append(assocDef.getTargetClass().getName().toString()); + query.append(type); query.append("\""); if (contains != null && contains.length() > 0) { String safeContains = Utils.remove(contains.trim(), "\""); - String nameAttr = Repository.escapeQName(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "name")); - query.append(" AND +@"); - query.append(nameAttr); + + // if the association's target is the person type search on the + // username instead of the name property + if (type.equals(ContentModel.TYPE_PERSON.toString())) + { + String userName = Repository.escapeQName(QName.createQName( + NamespaceService.CONTENT_MODEL_1_0_URI, "userName")); + query.append(userName); + } + else + { + String nameAttr = Repository.escapeQName(QName.createQName( + NamespaceService.CONTENT_MODEL_1_0_URI, "name")); + query.append(nameAttr); + } + query.append(":*" + safeContains + "*"); } diff --git a/source/java/org/alfresco/web/ui/repo/component/property/UIAssociationEditor.java b/source/java/org/alfresco/web/ui/repo/component/property/UIAssociationEditor.java index 3f9a10a2bc..dbd65eaf0d 100644 --- a/source/java/org/alfresco/web/ui/repo/component/property/UIAssociationEditor.java +++ b/source/java/org/alfresco/web/ui/repo/component/property/UIAssociationEditor.java @@ -26,6 +26,7 @@ import java.util.Map; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; +import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -152,9 +153,18 @@ public class UIAssociationEditor extends BaseAssociationEditor { out.write(""); AssociationRef assoc = (AssociationRef)iter.next(); - out.write(Repository.getDisplayPath(nodeService.getPath(assoc.getTargetRef()))); - out.write("/"); - out.write(Repository.getNameForNode(nodeService, assoc.getTargetRef())); + NodeRef targetNode = assoc.getTargetRef(); + // if the node represents a person, show the username instead of the name + if (ContentModel.TYPE_PERSON.equals(nodeService.getType(targetNode))) + { + out.write((String)nodeService.getProperty(targetNode, ContentModel.PROP_USERNAME)); + } + else + { + out.write(Repository.getDisplayPath(nodeService.getPath(targetNode))); + out.write("/"); + out.write(Repository.getNameForNode(nodeService, targetNode)); + } out.write(""); } diff --git a/source/java/org/alfresco/web/ui/repo/component/property/UIChildAssociationEditor.java b/source/java/org/alfresco/web/ui/repo/component/property/UIChildAssociationEditor.java index 3c0d510a41..9d693ffc36 100644 --- a/source/java/org/alfresco/web/ui/repo/component/property/UIChildAssociationEditor.java +++ b/source/java/org/alfresco/web/ui/repo/component/property/UIChildAssociationEditor.java @@ -26,6 +26,7 @@ import java.util.Map; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; +import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -152,9 +153,19 @@ public class UIChildAssociationEditor extends BaseAssociationEditor { out.write(""); ChildAssociationRef assoc = (ChildAssociationRef)iter.next(); - out.write(Repository.getDisplayPath(nodeService.getPath(assoc.getChildRef()))); - out.write("/"); - out.write(Repository.getNameForNode(nodeService, assoc.getChildRef())); + NodeRef targetNode = assoc.getChildRef(); + // if the node represents a person, show the username instead of the name + if (ContentModel.TYPE_PERSON.equals(nodeService.getType(targetNode))) + { + out.write((String)nodeService.getProperty(targetNode, ContentModel.PROP_USERNAME)); + } + else + { + out.write(Repository.getDisplayPath(nodeService.getPath(targetNode))); + out.write("/"); + out.write(Repository.getNameForNode(nodeService, targetNode)); + } + out.write(""); } diff --git a/source/java/org/alfresco/web/ui/repo/component/property/UIProperty.java b/source/java/org/alfresco/web/ui/repo/component/property/UIProperty.java index 9dea96b0af..d6b4b7c09c 100644 --- a/source/java/org/alfresco/web/ui/repo/component/property/UIProperty.java +++ b/source/java/org/alfresco/web/ui/repo/component/property/UIProperty.java @@ -178,7 +178,8 @@ public class UIProperty extends PropertySheetItem { // if we are trying to edit a NodeRef or Path property type set it to read-only as // these are internal properties that shouldn't be edited. - if (typeName.equals(DataTypeDefinition.NODE_REF) || typeName.equals(DataTypeDefinition.PATH)) + if (typeName.equals(DataTypeDefinition.NODE_REF) || typeName.equals(DataTypeDefinition.PATH) || + typeName.equals(DataTypeDefinition.CONTENT)) { logger.warn("Setting property " + propDef.getName().toString() + " to read-only as it can not be edited"); control.getAttributes().put("disabled", Boolean.TRUE); diff --git a/source/java/org/alfresco/web/ui/repo/component/property/UIPropertySheet.java b/source/java/org/alfresco/web/ui/repo/component/property/UIPropertySheet.java index d7d4b75bef..d60b492400 100644 --- a/source/java/org/alfresco/web/ui/repo/component/property/UIPropertySheet.java +++ b/source/java/org/alfresco/web/ui/repo/component/property/UIPropertySheet.java @@ -35,6 +35,7 @@ import org.alfresco.config.Config; import org.alfresco.config.ConfigLookupContext; import org.alfresco.config.ConfigService; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; import org.alfresco.web.app.Application; import org.alfresco.web.app.servlet.FacesHelper; import org.alfresco.web.bean.repository.Node; @@ -43,6 +44,7 @@ import org.alfresco.web.config.PropertySheetConfigElement.AssociationConfig; import org.alfresco.web.config.PropertySheetConfigElement.ChildAssociationConfig; import org.alfresco.web.config.PropertySheetConfigElement.ItemConfig; import org.alfresco.web.config.PropertySheetConfigElement.PropertyConfig; +import org.alfresco.web.config.PropertySheetConfigElement.SeparatorConfig; import org.alfresco.web.ui.common.ComponentConstants; import org.alfresco.web.ui.common.Utils; import org.alfresco.web.ui.repo.RepoConstants; @@ -63,6 +65,7 @@ public class UIPropertySheet extends UIPanel implements NamingContainer private static String DEFAULT_VAR_NAME = "node"; private static String PROP_ID_PREFIX = "prop_"; private static String ASSOC_ID_PREFIX = "assoc_"; + private static String SEP_ID_PREFIX = "sep_"; private List validations = new ArrayList(); private String variable; @@ -624,8 +627,13 @@ public class UIPropertySheet extends UIPanel implements NamingContainer // create the property component UIProperty propComp = (UIProperty)context.getApplication(). createComponent(RepoConstants.ALFRESCO_FACES_PROPERTY); - FacesHelper.setupComponentId(context, propComp, PROP_ID_PREFIX + propertyName); - propComp.setName(propertyName); + + // get the property name in it's prefix form + QName qname = QName.createQName(propertyName); + String prefixPropName = qname.toPrefixString(); + + FacesHelper.setupComponentId(context, propComp, PROP_ID_PREFIX + prefixPropName); + propComp.setName(prefixPropName); // if this property sheet is set as read only, set all properties to read only if (isReadOnly()) @@ -640,7 +648,7 @@ public class UIPropertySheet extends UIPanel implements NamingContainer if (logger.isDebugEnabled()) logger.debug("Created property component " + propComp + "(" + propComp.getClientId(context) + - ") for '" + propertyName + + ") for '" + prefixPropName + "' and added it to property sheet " + this); } @@ -652,8 +660,13 @@ public class UIPropertySheet extends UIPanel implements NamingContainer String assocName = (String)iter.next(); UIAssociation assocComp = (UIAssociation)context.getApplication(). createComponent(RepoConstants.ALFRESCO_FACES_ASSOCIATION); - FacesHelper.setupComponentId(context, assocComp, ASSOC_ID_PREFIX + assocName); - assocComp.setName(assocName); + + // get the association name in it's prefix form + QName qname = QName.createQName(assocName); + String prefixAssocName = qname.toPrefixString(); + + FacesHelper.setupComponentId(context, assocComp, ASSOC_ID_PREFIX + prefixAssocName); + assocComp.setName(prefixAssocName); // if this property sheet is set as read only, set all properties to read only if (isReadOnly()) @@ -668,7 +681,7 @@ public class UIPropertySheet extends UIPanel implements NamingContainer if (logger.isDebugEnabled()) logger.debug("Created association component " + assocComp + "(" + assocComp.getClientId(context) + - ") for '" + assocName + + ") for '" + prefixAssocName + "' and added it to property sheet " + this); } @@ -737,6 +750,12 @@ public class UIPropertySheet extends UIPanel implements NamingContainer propSheetItem = (PropertySheetItem)context.getApplication(). createComponent(RepoConstants.ALFRESCO_FACES_CHILD_ASSOCIATION); } + else if (item instanceof SeparatorConfig) + { + id = SEP_ID_PREFIX + item.getName(); + propSheetItem = (PropertySheetItem)context.getApplication(). + createComponent(RepoConstants.ALFRESCO_FACES_SEPARATOR); + } // now setup the common stuff across all component types if (propSheetItem != null) diff --git a/source/java/org/alfresco/web/ui/repo/component/property/UISeparator.java b/source/java/org/alfresco/web/ui/repo/component/property/UISeparator.java new file mode 100644 index 0000000000..5059ae26c3 --- /dev/null +++ b/source/java/org/alfresco/web/ui/repo/component/property/UISeparator.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.web.ui.repo.component.property; + +import java.io.IOException; + +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; + +import org.alfresco.web.app.servlet.FacesHelper; +import org.alfresco.web.ui.repo.RepoConstants; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Component to represent a separator within a property sheet + * + * @author gavinc + */ +public class UISeparator extends PropertySheetItem +{ + public static final String COMPONENT_FAMILY = "org.alfresco.faces.Separator"; + + private static Log logger = LogFactory.getLog(UISeparator.class); + + /** + * Default constructor + */ + public UISeparator() + { + // set the default renderer + setRendererType("org.alfresco.faces.SeparatorRenderer"); + } + + /** + * @see javax.faces.component.UIComponent#getFamily() + */ + public String getFamily() + { + return COMPONENT_FAMILY; + } + + protected String getIncorrectParentMsg() + { + return "The separator component must be nested within a property sheet component"; + } + + protected void generateItem(FacesContext context, UIPropertySheet propSheet) throws IOException + { + String componentGeneratorName = this.getComponentGenerator(); + + if (componentGeneratorName == null) + { + componentGeneratorName = RepoConstants.GENERATOR_SEPARATOR; + } + + UIComponent separator = FacesHelper.getComponentGenerator(context, componentGeneratorName). + generateAndAdd(context, propSheet, this); + + if (logger.isDebugEnabled()) + logger.debug("Created separator " + separator + "(" + + separator.getClientId(context) + + ") for '" + this.getName() + + "' and added it to component " + this); + } +} diff --git a/source/java/org/alfresco/web/ui/repo/component/template/DefaultModelHelper.java b/source/java/org/alfresco/web/ui/repo/component/template/DefaultModelHelper.java index f7676bbcc7..a24798c76e 100644 --- a/source/java/org/alfresco/web/ui/repo/component/template/DefaultModelHelper.java +++ b/source/java/org/alfresco/web/ui/repo/component/template/DefaultModelHelper.java @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.Map; import org.alfresco.repo.template.DateCompareMethod; +import org.alfresco.repo.template.FreeMarkerProcessor; import org.alfresco.repo.template.HasAspectMethod; import org.alfresco.repo.template.I18NMessageMethod; import org.alfresco.service.ServiceRegistry; @@ -69,37 +70,11 @@ public class DefaultModelHelper throw new IllegalArgumentException("Current User is mandatory."); } - // create FreeMarker default model and merge - Map root = new HashMap(16, 1.0f); - - // supply the CompanyHome space as "companyhome" NodeRef companyRootRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId()); - TemplateNode companyRootNode = new TemplateNode(companyRootRef, services, imageResolver); - root.put("companyhome", companyRootNode); - - // supply the users Home Space as "userhome" NodeRef userRootRef = new NodeRef(Repository.getStoreRef(), user.getHomeSpaceId()); - TemplateNode userRootNode = new TemplateNode(userRootRef, services, imageResolver); - root.put("userhome", userRootNode); - // supply the current user Node as "person" - root.put("person", new TemplateNode(user.getPerson(), services, imageResolver)); - - // add the template itself as "template" if it comes from content on a node - if (template != null) - { - root.put("template", new TemplateNode(template, services, imageResolver)); - } - - // current date/time is useful to have and isn't supplied by FreeMarker by default - root.put("date", new Date()); - - // add custom method objects - root.put("hasAspect", new HasAspectMethod()); - root.put("message", new I18NMessageMethod()); - root.put("dateCompare", new DateCompareMethod()); - - return root; + return FreeMarkerProcessor.buildDefaultModel( + services, user.getPerson(), companyRootRef, userRootRef, template, imageResolver); } /** Template Image resolver helper */ diff --git a/source/java/org/alfresco/web/ui/repo/renderer/property/PropertySheetItemRenderer.java b/source/java/org/alfresco/web/ui/repo/renderer/property/PropertySheetItemRenderer.java index 82913887a7..bdc291f124 100644 --- a/source/java/org/alfresco/web/ui/repo/renderer/property/PropertySheetItemRenderer.java +++ b/source/java/org/alfresco/web/ui/repo/renderer/property/PropertySheetItemRenderer.java @@ -70,8 +70,6 @@ public class PropertySheetItemRenderer extends BaseRenderer UIComponent label = children.get(0); UIComponent control = children.get(1); - out.write(""); - // encode the mandatory marker component if present if (count == 3) { diff --git a/source/java/org/alfresco/web/ui/repo/renderer/property/SeparatorRenderer.java b/source/java/org/alfresco/web/ui/repo/renderer/property/SeparatorRenderer.java new file mode 100644 index 0000000000..24ab159804 --- /dev/null +++ b/source/java/org/alfresco/web/ui/repo/renderer/property/SeparatorRenderer.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.web.ui.repo.renderer.property; + +import java.io.IOException; + +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; +import javax.faces.context.ResponseWriter; + +import org.alfresco.web.ui.common.Utils; +import org.alfresco.web.ui.common.renderer.BaseRenderer; + +/** + * Renderer for a Separator component + * + * @author gavinc + */ +public class SeparatorRenderer extends BaseRenderer +{ + /** + * @see javax.faces.render.Renderer#encodeBegin(javax.faces.context.FacesContext, javax.faces.component.UIComponent) + */ + public void encodeBegin(FacesContext context, UIComponent component) throws IOException + { + if (component.isRendered() == false) + { + return; + } + + // NOTE: we close off the first generated by the property sheet's grid renderer + context.getResponseWriter().write(""); + } + + /** + * @see javax.faces.render.Renderer#encodeChildren(javax.faces.context.FacesContext, javax.faces.component.UIComponent) + */ + @SuppressWarnings("unchecked") + public void encodeChildren(FacesContext context, UIComponent component) throws IOException + { + if (component.isRendered() == false) + { + return; + } + + ResponseWriter out = context.getResponseWriter(); + + int count = component.getChildCount(); + + if (count == 1) + { + // there should be 3 columns so write out a td with colspan of 3 + // then render the child component + out.write(""); + Utils.encodeRecursive(context, (UIComponent)component.getChildren().get(0)); + + // NOTE: we'll allow the property sheet's grid renderer close off the last + } + } + + /** + * @see javax.faces.render.Renderer#encodeEnd(javax.faces.context.FacesContext, javax.faces.component.UIComponent) + */ + public void encodeEnd(FacesContext context, UIComponent component) throws IOException + { + // we don't need to do anything in here + } + + /** + * @see javax.faces.render.Renderer#getRendersChildren() + */ + public boolean getRendersChildren() + { + return true; + } +} diff --git a/source/java/org/alfresco/web/ui/repo/tag/DialogButtonsTag.java b/source/java/org/alfresco/web/ui/repo/tag/DialogButtonsTag.java new file mode 100644 index 0000000000..f499fd7c1f --- /dev/null +++ b/source/java/org/alfresco/web/ui/repo/tag/DialogButtonsTag.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.web.ui.repo.tag; + +import org.alfresco.web.ui.common.tag.HtmlComponentTag; + +/** + * Tag class that allows the UIDialogButtons component to be placed on a JSP. + * + * @author gavinc + */ +public class DialogButtonsTag extends HtmlComponentTag +{ + /** + * @see javax.faces.webapp.UIComponentTag#getComponentType() + */ + public String getComponentType() + { + return "org.alfresco.faces.DialogButtons"; + } + + /** + * @see javax.faces.webapp.UIComponentTag#getRendererType() + */ + public String getRendererType() + { + return null; + } +} diff --git a/source/java/org/alfresco/web/ui/repo/tag/SystemErrorTag.java b/source/java/org/alfresco/web/ui/repo/tag/SystemErrorTag.java index 0efbc92ed8..b46d25f65f 100644 --- a/source/java/org/alfresco/web/ui/repo/tag/SystemErrorTag.java +++ b/source/java/org/alfresco/web/ui/repo/tag/SystemErrorTag.java @@ -39,6 +39,8 @@ import org.alfresco.web.bean.ErrorBean; */ public class SystemErrorTag extends TagSupport { + private static final long serialVersionUID = -7336055169875448199L; + private static final String MSG_RETURN_TO_APP = "return_to_application"; private static final String MSG_HIDE_DETAILS = "hide_details"; private static final String MSG_SHOW_DETAILS = "show_details"; @@ -126,6 +128,21 @@ public class SystemErrorTag extends TagSupport errorMessage = errorBean.getLastErrorMessage(); errorDetails = errorBean.getStackTrace(); } + else + { + // if we reach here the error was caught by the declaration in web.xml so + // pull all the information from the request and create the error bean + Throwable error = (Throwable)pageContext.getRequest().getAttribute("javax.servlet.error.exception"); + String uri = (String)pageContext.getRequest().getAttribute("javax.servlet.error.request_uri"); + + // create and store the ErrorBean + errorBean = new ErrorBean(); + pageContext.getSession().setAttribute(ErrorBean.ERROR_BEAN_NAME, errorBean); + errorBean.setLastError(error); + errorBean.setReturnPage(uri); + errorMessage = errorBean.getLastErrorMessage(); + errorDetails = errorBean.getStackTrace(); + } try { @@ -254,6 +271,11 @@ public class SystemErrorTag extends TagSupport { throw new JspException(ioe); } + finally + { + // clear out the error bean otherwise the next error could be hidden + pageContext.getSession().removeAttribute(ErrorBean.ERROR_BEAN_NAME); + } return SKIP_BODY; } diff --git a/source/java/org/alfresco/web/ui/repo/tag/UserGroupPickerTag.java b/source/java/org/alfresco/web/ui/repo/tag/UserGroupPickerTag.java new file mode 100644 index 0000000000..011ba48a96 --- /dev/null +++ b/source/java/org/alfresco/web/ui/repo/tag/UserGroupPickerTag.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.web.ui.repo.tag; + +import javax.faces.component.UICommand; +import javax.faces.component.UIComponent; + +import org.alfresco.web.ui.common.tag.HtmlComponentTag; + +/** + * @author Kevin Roast + */ +public class UserGroupPickerTag extends HtmlComponentTag +{ + /** + * @see javax.faces.webapp.UIComponentTag#getComponentType() + */ + public String getComponentType() + { + return "org.alfresco.faces.UserGroupPicker"; + } + + /** + * @see javax.faces.webapp.UIComponentTag#getRendererType() + */ + public String getRendererType() + { + return null; + } + + /** + * @see javax.faces.webapp.UIComponentTag#setProperties(javax.faces.component.UIComponent) + */ + protected void setProperties(UIComponent component) + { + super.setProperties(component); + + setStringProperty(component, "value", this.value); + setActionListenerProperty((UICommand)component, this.actionListener); + } + + /** + * @see org.alfresco.web.ui.common.tag.HtmlComponentTag#release() + */ + public void release() + { + super.release(); + this.value = null; + } + + /** + * Set the value (binding to the list of user/group data) + * + * @param value the value + */ + public void setValue(String value) + { + this.value = value; + } + + /** + * Set the actionListener + * + * @param actionListener the actionListener + */ + public void setActionListener(String actionListener) + { + this.actionListener = actionListener; + } + + /** the value (binding to the list of user/group data) */ + private String value; + + /** the actionListener */ + private String actionListener; +} diff --git a/source/java/org/jbpm/webapp/bean/AdminBean.java b/source/java/org/jbpm/webapp/bean/AdminBean.java new file mode 100644 index 0000000000..e35540f3b7 --- /dev/null +++ b/source/java/org/jbpm/webapp/bean/AdminBean.java @@ -0,0 +1,64 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.bean; + +import java.util.List; + +import javax.faces.context.FacesContext; + +import org.jbpm.scheduler.impl.Scheduler; + +public class AdminBean { + + String deployUrl; + + public void deployProcess() { + } + + public void createSchema() { + } + + public void dropSchema() { + } + + public boolean isSchedulerRunning() { + return getScheduler().isRunning(); + } + + public List getSchedulerHistoryLogs() { + return getScheduler().getSchedulerHistoryLogs(); + } + + private Scheduler getScheduler() { + return (Scheduler) FacesContext.getCurrentInstance() + .getExternalContext() + .getApplicationMap() + .get("scheduler"); + } + + public String getDeployUrl() { + return deployUrl; + } + public void setDeployUrl(String deployUrl) { + this.deployUrl = deployUrl; + } +} diff --git a/source/java/org/jbpm/webapp/bean/AlfrescoUserBean.java b/source/java/org/jbpm/webapp/bean/AlfrescoUserBean.java new file mode 100644 index 0000000000..12b682a8ca --- /dev/null +++ b/source/java/org/jbpm/webapp/bean/AlfrescoUserBean.java @@ -0,0 +1,22 @@ +package org.jbpm.webapp.bean; + +import org.alfresco.service.cmr.security.AuthenticationService; + +public class AlfrescoUserBean extends UserBean +{ + AuthenticationService authService; + + + public void setAuthenticationService(AuthenticationService authService) + { + this.authService = authService; + } + + + @Override + public String getUserName() + { + return authService.getCurrentUserName(); + } + +} diff --git a/source/java/org/jbpm/webapp/bean/FormParameter.java b/source/java/org/jbpm/webapp/bean/FormParameter.java new file mode 100644 index 0000000000..61ee3eb221 --- /dev/null +++ b/source/java/org/jbpm/webapp/bean/FormParameter.java @@ -0,0 +1,42 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.bean; + +import org.jbpm.context.def.VariableAccess; + +public class FormParameter { + + private String value = null; + private VariableAccess variableAccess = null; + + public FormParameter(String value, VariableAccess variableAccess) { + this.value = value; + this.variableAccess = variableAccess; + } + + public String getValue() { + return value; + } + public VariableAccess getVariableAccess() { + return variableAccess; + } +} diff --git a/source/java/org/jbpm/webapp/bean/HomeBean.java b/source/java/org/jbpm/webapp/bean/HomeBean.java new file mode 100644 index 0000000000..36fedc939c --- /dev/null +++ b/source/java/org/jbpm/webapp/bean/HomeBean.java @@ -0,0 +1,205 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.bean; + +import java.util.List; + +import javax.faces.event.ActionEvent; +import javax.faces.model.DataModel; +import javax.faces.model.ListDataModel; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.jbpm.JbpmConfiguration; +import org.jbpm.JbpmContext; +import org.jbpm.db.GraphSession; +import org.jbpm.db.TaskMgmtSession; +import org.jbpm.graph.def.ProcessDefinition; +import org.jbpm.graph.def.Transition; +import org.jbpm.graph.exe.ProcessInstance; +import org.jbpm.taskmgmt.def.Task; +import org.jbpm.taskmgmt.exe.TaskInstance; + +public class HomeBean { + + UserBean userBean; + TaskBean taskBean; + JbpmConfiguration config; + DataModel taskInstances; + DataModel processDefs; + + + public HomeBean() + { + } + + public void setJbpmConfiguration(JbpmConfiguration config) + { + this.config = config; + } + + public List getTaskInstances() + { + JbpmContext xjbpmContext = config.getCurrentJbpmContext(); + JbpmContext jbpmContext = (xjbpmContext == null) ? config.createJbpmContext() : xjbpmContext; + + try + { + TaskMgmtSession taskMgmtSession = jbpmContext.getTaskMgmtSession(); + List taskInstances = taskMgmtSession.findTaskInstances(userBean.getUserName()); + for (TaskInstance taskInstance : taskInstances) + { + taskInstance.getName(); + taskInstance.getTaskMgmtInstance().getTaskMgmtDefinition().getProcessDefinition().getName(); + } + return taskInstances; + } + finally + { + if (xjbpmContext == null) jbpmContext.close(); + } + } + + public DataModel getTaskInstancesModel() + { + if (taskInstances == null) + { + taskInstances = new ListDataModel(getTaskInstances()); + } + return taskInstances; + } + + public List getLatestProcessDefinitions() + { + JbpmContext xjbpmContext = config.getCurrentJbpmContext(); + JbpmContext jbpmContext = (xjbpmContext == null) ? config.createJbpmContext() : xjbpmContext; + try + { + GraphSession graphSession = jbpmContext.getGraphSession(); + List procDefs = graphSession.findLatestProcessDefinitions(); + for (ProcessDefinition procDef : procDefs) + { + procDef.getName(); + Task startTask = procDef.getTaskMgmtDefinition().getStartTask(); + if (startTask != null) + { + startTask.getName(); + } + } + return procDefs; + } + finally + { + if (xjbpmContext == null) jbpmContext.close(); + } + } + + public DataModel getLatestProcessDefinitionsModel() + { + if (processDefs == null) + { + processDefs = new ListDataModel(getLatestProcessDefinitions()); + } + return processDefs; + } + + /** + * selects a task. + */ + public String selectTaskInstance() + { + JbpmContext xjbpmContext = config.getCurrentJbpmContext(); + JbpmContext jbpmContext = (xjbpmContext == null) ? config.createJbpmContext() : xjbpmContext; + + try + { + // Get the task instance id from request parameter + TaskInstance selectedTask = (TaskInstance)taskInstances.getRowData(); + long taskInstanceId = selectedTask.getId(); + TaskMgmtSession taskMgmtSession = jbpmContext.getTaskMgmtSession(); + TaskInstance taskInstance = taskMgmtSession.loadTaskInstance(taskInstanceId); + taskBean.initialize(taskInstance); + + return "task"; + } + finally + { + taskInstances = null; + processDefs = null; + if (xjbpmContext == null ) jbpmContext.close(); + } + } + + /** + * prepares a task form for starting a new process instance. + */ + public String startProcessInstance() + { + JbpmContext xjbpmContext = config.getCurrentJbpmContext(); + JbpmContext jbpmContext = (xjbpmContext == null) ? config.createJbpmContext() : xjbpmContext; + try + { + jbpmContext.setActorId(AuthenticationUtil.getCurrentUserName()); + + // Get the task instance id from request parameter + ProcessDefinition selectedProc = (ProcessDefinition)processDefs.getRowData(); + long processDefinitionId = selectedProc.getId(); + GraphSession graphSession = jbpmContext.getGraphSession(); + ProcessDefinition processDefinition = graphSession.loadProcessDefinition(processDefinitionId); + + // create a new process instance to run + ProcessInstance processInstance = new ProcessInstance(processDefinition); + + // create a new taskinstance for the start task + Task startTask = processInstance.getTaskMgmtInstance().getTaskMgmtDefinition().getStartTask(); + if (startTask != null) + { + TaskInstance taskInstance = processInstance.getTaskMgmtInstance().createStartTaskInstance(); + taskBean.initialize(taskInstance); + } + + // Save the process instance along with the task instance + jbpmContext.save(processInstance); + + // Fill the task backing bean with useful information + return (startTask == null) ? "home" : "task"; + } + finally + { + if (xjbpmContext == null) jbpmContext.close(); + taskInstances = null; + processDefs = null; + } + } + + public UserBean getUserBean() { + return userBean; + } + public void setUserBean(UserBean userBean) { + this.userBean = userBean; + } + public TaskBean getTaskBean() { + return taskBean; + } + public void setTaskBean(TaskBean taskBean) { + this.taskBean = taskBean; + } +} diff --git a/source/java/org/jbpm/webapp/bean/JsfHelper.java b/source/java/org/jbpm/webapp/bean/JsfHelper.java new file mode 100644 index 0000000000..1d287d1b2b --- /dev/null +++ b/source/java/org/jbpm/webapp/bean/JsfHelper.java @@ -0,0 +1,61 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.bean; + +import javax.faces.application.FacesMessage; +import javax.faces.context.FacesContext; + +public class JsfHelper { + + public static long getId(String parameterName) { + long value = -1; + String valueText = (String) FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(parameterName); + try { + Long id = new Long(valueText); + value = id.longValue(); + } catch (NumberFormatException e) { + throw new RuntimeException("couldn't parse '"+parameterName+"'='"+valueText+"' as a long"); + } + return value; + } + + public static void addMessage(String msg) { + FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(msg)); + } + + public static void setSessionAttribute(String key, Object value) { + FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(key, value); + } + + public static Object getSessionAttribute(String key) { + return FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(key); + } + + public static void removeSessionAttribute(String key) { + FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(key); + } + + public static String getParameter(String name) { + return (String) FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(name); + } + // private static final Log log = LogFactory.getLog(JsfHelper.class); +} diff --git a/source/java/org/jbpm/webapp/bean/MonitoringBean.java b/source/java/org/jbpm/webapp/bean/MonitoringBean.java new file mode 100644 index 0000000000..2d1d254883 --- /dev/null +++ b/source/java/org/jbpm/webapp/bean/MonitoringBean.java @@ -0,0 +1,228 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.bean; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +import javax.faces.context.FacesContext; +import javax.faces.model.SelectItem; + +import org.jbpm.JbpmContext; +import org.jbpm.graph.def.ProcessDefinition; + + +/** + * Monitoring Bean Implementation. + * + * @author David Loiseau + */ + +public class MonitoringBean { + + long processInstanceId; + String message; + String variableName; + String variableValue; + String variableNameOperator; + String variableValueOperator; + ArrayList processInstances; + + public String showProcessDefinitions() { + return "processDefinitions"; + } + + public List getProcessDefinitions() { + + ArrayList processDefinitionsList = new ArrayList(); + + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + List processDefinitions = jbpmContext.getGraphSession().findAllProcessDefinitions(); + + if (processDefinitions.isEmpty() == false) { + ListIterator listProcessDefinitions = processDefinitions.listIterator(); + while (listProcessDefinitions.hasNext() ) { + ProcessDefinition processDefinition = (ProcessDefinition)listProcessDefinitions.next(); + + int instancesCount = 0; + try { + Connection connection = jbpmContext.getConnection(); + Statement statement = connection.createStatement(); + + String request = "SELECT COUNT(*) AS instancesCount " + + "FROM jbpm_processinstance " + + "WHERE processdefinition_='" + + processDefinition.getId() + "'"; + ResultSet resultSet = statement.executeQuery(request); + resultSet.next(); + instancesCount = resultSet.getInt("instancesCount"); + } + catch (Exception e) {} + + processDefinitionsList.add( + new ProcessDefinitionBean( + processDefinition.getId(), + processDefinition.getName(), + processDefinition.getVersion(), + instancesCount + )); + } + } + + return(processDefinitionsList); + } + + public String inspectInstance() { + try { + ProcessInstanceBean processInstanceBean = new ProcessInstanceBean(this.processInstanceId); + FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("processInstanceBean", processInstanceBean); + this.message = ""; + return "inspectInstance"; + } + catch (Exception exception) { + this.message = "Error for process instance " + this.processInstanceId; + return ""; + } + } + + public String showSearchInstances() { + return("showSearchInstances"); + } + + public String searchInstances() { + + long count = 0; + + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + + try { + Connection connection = jbpmContext.getConnection(); + Statement statement = connection.createStatement(); + statement.setMaxRows(100); + + String request = "SELECT DISTINCT processinstance_, name_, stringvalue_ FROM jbpm_variableinstance " + + "WHERE name_ " + + this.variableNameOperator + " '" + + variableName + "' AND stringvalue_ " + + this.variableValueOperator + " '" + variableValue + "'"; + + ResultSet resultSet = statement.executeQuery(request); + + processInstances = new ArrayList(); + + while (resultSet.next()) { + processInstances.add(new ProcessInstanceBean( + resultSet.getLong("processinstance_"), + resultSet.getString("name_"), + resultSet.getString("stringvalue_"))); + count++; + } + statement.close(); + } + catch (Exception e) { + this.message = "Search error " + e.getMessage(); + } + + if (count == 1) { + ProcessInstanceBean processInstanceBean = (ProcessInstanceBean)processInstances.iterator().next(); + FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("processInstanceBean", processInstanceBean); + return("inspectInstance"); + } + return ""; + } + + public List getOperatorsList (){ + + ArrayList operatorsList = new ArrayList(); + + SelectItem item = new SelectItem("=", "is equal to"); + operatorsList.add(item); + item = new SelectItem("like", "is like"); + operatorsList.add(item); + return operatorsList; + + } + + public long getProcessInstanceId() { + return processInstanceId; + } + + public void setProcessInstanceId(long processInstanceId) { + this.processInstanceId = processInstanceId; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isShowProcessInstances() { + if (processInstances == null) return false; + if (processInstances.size() == 0) return false; + return true; + } + + public ArrayList getProcessInstances() { + return processInstances; + } + + public String getVariableName() { + return variableName; + } + + public void setVariableName(String variableName) { + this.variableName = variableName; + } + + public String getVariableValue() { + return variableValue; + } + + public void setVariableValue(String variableValue) { + this.variableValue = variableValue; + } + + public String getVariableNameOperator() { + return variableNameOperator; + } + + public void setVariableNameOperator(String variableNameOperator) { + this.variableNameOperator = variableNameOperator; + } + + public String getVariableValueOperator() { + return variableValueOperator; + } + + public void setVariableValueOperator(String variableValueOperator) { + this.variableValueOperator = variableValueOperator; + } + + +} diff --git a/source/java/org/jbpm/webapp/bean/ProcessDefinitionBean.java b/source/java/org/jbpm/webapp/bean/ProcessDefinitionBean.java new file mode 100644 index 0000000000..e78fa6fe09 --- /dev/null +++ b/source/java/org/jbpm/webapp/bean/ProcessDefinitionBean.java @@ -0,0 +1,141 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.bean; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +import javax.faces.context.FacesContext; + +import org.jbpm.JbpmContext; +import org.jbpm.db.GraphSession; +import org.jbpm.graph.def.ProcessDefinition; +import org.jbpm.graph.exe.ProcessInstance; + +/** + * Process Definition Bean Implementation. + * + * @author David Loiseau + */ + +public class ProcessDefinitionBean { + + String name; + int version; + long id; + int instancesCount; + + public ProcessDefinitionBean() { + } + + public ProcessDefinitionBean(long id) { + this.id = id; + initialize(); + } + + public ProcessDefinitionBean(long id, String name, int version, int instancesCount) { + this.id = id; + this.name = name; + this.version = version; + this.instancesCount = instancesCount; + } + + private void initialize() { + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + GraphSession graphSession = jbpmContext.getGraphSession(); + ProcessDefinition processDefinition = graphSession.loadProcessDefinition(id); + this.name = processDefinition.getName(); + this.version = processDefinition.getVersion(); + this.instancesCount = graphSession.findProcessInstances(this.id).size(); + } + + public List getProcessInstances() { + + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + GraphSession graphSession = jbpmContext.getGraphSession(); + + ArrayList processInstancesList = new ArrayList(); + + List listProcessInstance = graphSession.findProcessInstances(this.id); + + if (listProcessInstance.isEmpty() == false) { + ListIterator listProcessInstances = listProcessInstance.listIterator(); + while (listProcessInstances.hasNext()) { + ProcessInstance processInstance = (ProcessInstance) listProcessInstances.next(); + + processInstancesList.add(new ProcessInstanceBean(processInstance.getId(), processInstance.getStart(), processInstance.getEnd())); + } + } + + return processInstancesList; + } + + public String showProcessInstances() { + ProcessDefinitionBean processDefinitionBean = new ProcessDefinitionBean(); + processDefinitionBean.setId(this.id); + FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("processDefinitionBean", processDefinitionBean); + return ("processInstances"); + } + + public String startProcessInstance() { + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + GraphSession graphSession = jbpmContext.getGraphSession(); + ProcessDefinition processDefinition = graphSession.loadProcessDefinition(getId()); + processDefinition.createInstance(); + return showProcessInstances(); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + this.initialize(); + } + + public int getInstancesCount() { + return instancesCount; + } + + public void setInstancesCount(int instancesCount) { + this.instancesCount = instancesCount; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + +} diff --git a/source/java/org/jbpm/webapp/bean/ProcessInstanceBean.java b/source/java/org/jbpm/webapp/bean/ProcessInstanceBean.java new file mode 100644 index 0000000000..334eeb64c7 --- /dev/null +++ b/source/java/org/jbpm/webapp/bean/ProcessInstanceBean.java @@ -0,0 +1,420 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.bean; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.Map.Entry; + +import javax.faces.context.FacesContext; + +import org.jbpm.JbpmContext; +import org.jbpm.db.GraphSession; +import org.jbpm.db.TaskMgmtSession; +import org.jbpm.graph.def.Transition; +import org.jbpm.graph.exe.ProcessInstance; +import org.jbpm.graph.exe.Token; +import org.jbpm.taskmgmt.exe.TaskInstance; + +/** + * Process Instance Bean Implementation. + * + * @author David Loiseau + */ + +public class ProcessInstanceBean { + + long id; + String processDefinitionLabel; + long processDefinitionId; + Date start; + Date end; + + ArrayList tokens; + ArrayList variables; + ArrayList tasks; + ArrayList transitions; + + String variableName; + String variableValue; + + long tokenInstanceId; + long taskInstanceId; + + public ProcessInstanceBean(long id, Date start, Date end) { + this.id = id; + this.start = start; + this.end = end; + } + + public ProcessInstanceBean(long id) { + this.id = id; + this.initialize(); + } + + public ProcessInstanceBean(long id, String variableName, String variableValue) { + this.id = id; + this.variableName = variableName; + this.variableValue = variableValue; + this.initialize(); + } + + public String inspectProcessInstance() { + ProcessInstanceBean processInstanceBean = new ProcessInstanceBean(this.id); + FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("processInstanceBean", processInstanceBean); + return ("inspectInstance"); + } + + public String deleteProcessInstance() { + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + GraphSession graphSession = jbpmContext.getGraphSession(); + graphSession.deleteProcessInstance(this.id); + return ("deleteInstance"); + } + + private void initialize() { + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + GraphSession graphSession = jbpmContext.getGraphSession(); + ProcessInstance processInstance = graphSession.loadProcessInstance(this.id); + this.start = processInstance.getStart(); + this.end = processInstance.getEnd(); + this.processDefinitionId = processInstance.getProcessDefinition().getId(); + this.processDefinitionLabel = processInstance.getProcessDefinition().getName() + " (version " + processInstance.getProcessDefinition().getVersion() + ")"; + + initializeVariablesList(processInstance); + initializeTokensList(processInstance); + initializeTasksList(processInstance); + } + + private void initializeAvailableTransitions(TaskInstance taskInstance) { + + transitions = new ArrayList(); + + if (taskInstance.getAvailableTransitions().isEmpty() == false) { + Iterator availableTransitionsIterator = taskInstance.getAvailableTransitions().iterator(); + while (availableTransitionsIterator.hasNext()) { + Transition transition = (Transition) availableTransitionsIterator.next(); + transitions.add(transition); + + } + } + } + + private void initializeAvailableTransitions(Token token) { + + transitions = new ArrayList(); + + if (token.getNode().getLeavingTransitions().isEmpty() == false) { + Iterator availableTransitionsIterator = token.getNode().getLeavingTransitions().iterator(); + while (availableTransitionsIterator.hasNext()) { + Transition transition = (Transition) availableTransitionsIterator.next(); + transitions.add(transition); + + } + } + } + + private void initializeVariablesList(ProcessInstance processInstance) { + + // Variables list + variables = new ArrayList(); + + if (processInstance.getContextInstance().getVariables() != null && !processInstance.getContextInstance().getVariables().values().isEmpty()) { + int mapsize = processInstance.getContextInstance().getVariables().size(); + Iterator variablesIterator = processInstance.getContextInstance().getVariables().entrySet().iterator(); + for (int i = 0; i < mapsize; i++) { + Entry entry = (Entry) variablesIterator.next(); + variables.add(new VariableBean((String) entry.getKey(), entry.getValue())); + } + } + + } + + private void initializeTasksList(ProcessInstance processInstance) { + + // Tasks list + tasks = new ArrayList(); + if (processInstance.getTaskMgmtInstance().getTaskInstances().isEmpty() == false) { + Iterator tasksIterator = processInstance.getTaskMgmtInstance().getTaskInstances().iterator(); + while (tasksIterator.hasNext()) { + TaskInstance taskInstance = (TaskInstance) tasksIterator.next(); + tasks.add(new TaskBean(taskInstance.getId(), taskInstance.getName(), taskInstance.getActorId(), taskInstance.getEnd())); + } + } + + } + + private void initializeTokensList(ProcessInstance processInstance) { + + // Tokens list + Token rootToken = processInstance.getRootToken(); + + tokens = new ArrayList(); + this.tokenInstanceId = rootToken.getId(); + this.taskInstanceId = 0; + tokens.add(new TokenBean(rootToken.getId(), "Root", rootToken.getNode().getName(), rootToken.getNode().getClass().getName(), rootToken.getStart(), + rootToken.getEnd(), 1)); + try { + if (rootToken.getChildren().isEmpty() == false) { + AddChildrenTokensToTokensList(this.tokens, rootToken, 2); + } + } catch (Exception exception) { + } + + } + + /** + * + * Add token childs to the current token beans list + * + * @param tokensList + * Current token list to update + * @param token + * Token where are the token childs + * @param level + * Level where is the token: 1 for the root token, 2 for the childs + * of the root token, ... + */ + private void AddChildrenTokensToTokensList(ArrayList tokensList, Token token, long level) { + + Iterator childrenIterator = token.getChildren().values().iterator(); + while (childrenIterator.hasNext()) { + Token childToken = (Token) childrenIterator.next(); + tokensList.add(new TokenBean(childToken.getId(), childToken.getName(), childToken.getNode().getName(), childToken.getNode().getClass().getName(), + childToken.getStart(), childToken.getEnd(), level)); + try { + if (childToken.getChildren().isEmpty() == false) { + AddChildrenTokensToTokensList(tokensList, childToken, level + 1); + } + } catch (Exception exception) { + } + } + } + + public String updateVariable() { + + if (this.variableName != null) { + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + GraphSession graphSession = jbpmContext.getGraphSession(); + ProcessInstance processInstance = graphSession.loadProcessInstance(this.id); + if (this.variableValue != null) { + processInstance.getContextInstance().setVariable(this.variableName, this.variableValue); + } else { + processInstance.getContextInstance().deleteVariable(this.variableName); + } + initializeVariablesList(processInstance); + } + return "inspectInstance"; + } + + public String selectToken() { + this.taskInstanceId = 0; + this.tokenInstanceId = JsfHelper.getId("tokenInstanceId"); + return ""; + } + + public String selectTask() { + this.tokenInstanceId = 0; + this.taskInstanceId = JsfHelper.getId("taskInstanceId"); + return ""; + } + + public String signal() { + + selectToken(); + + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + GraphSession graphSession = jbpmContext.getGraphSession(); + + Token token = graphSession.loadToken(this.tokenInstanceId); + + if (token.getNode().getLeavingTransitions().size() > 1) { + initializeAvailableTransitions(token); + return "showTransitions"; + } + + token.signal(); + + this.initializeTokensList(token.getProcessInstance()); + + return "inspectInstance"; + } + + public String selectTransition() { + String transitionName; + + transitionName = JsfHelper.getParameter("transitionName"); + ProcessInstance processInstance = null; + + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + if (this.taskInstanceId > 0) { + TaskMgmtSession taskMgmtSession = jbpmContext.getTaskMgmtSession(); + TaskInstance taskInstance = taskMgmtSession.loadTaskInstance(this.taskInstanceId); + if (transitionName.equals("")) { + taskInstance.end(); + } else { + taskInstance.end(transitionName); + } + processInstance = taskInstance.getToken().getProcessInstance(); + } else if (this.tokenInstanceId > 0) { + GraphSession graphSession = jbpmContext.getGraphSession(); + Token token = graphSession.loadToken(this.tokenInstanceId); + if (transitionName.equals("")) { + token.signal(); + } else { + token.signal(transitionName); + } + processInstance = token.getProcessInstance(); + } + + jbpmContext.save(processInstance); + + this.initializeTasksList(processInstance); + this.initializeTokensList(processInstance); + + return "inspectInstance"; + } + + public String endTask() { + + selectTask(); + + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + TaskMgmtSession taskMgmtSession = jbpmContext.getTaskMgmtSession(); + + TaskInstance taskInstance = taskMgmtSession.loadTaskInstance(this.taskInstanceId); + + if (taskInstance.getAvailableTransitions().size() > 1) { + initializeAvailableTransitions(taskInstance); + return "showTransitions"; + } + + taskInstance.end(); + + ProcessInstance processInstance = taskInstance.getToken().getProcessInstance(); + jbpmContext.save(processInstance); + + this.initializeTasksList(processInstance); + this.initializeTokensList(processInstance); + + return "inspectInstance"; + } + + // Show all the process instances for a given process definition ID + public String showProcessInstances() { + ProcessDefinitionBean processDefinitionBean = new ProcessDefinitionBean(this.processDefinitionId); + FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("processDefinitionBean", processDefinitionBean); + return ("processInstances"); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Date getStart() { + return start; + } + + public void setStart(Date start) { + this.start = start; + } + + public Date getEnd() { + return end; + } + + public void setEnd(Date end) { + this.end = end; + } + + public ArrayList getTokens() { + return tokens; + } + + public void setTokens(ArrayList tokens) { + this.tokens = tokens; + } + + public String getProcessDefinitionLabel() { + return processDefinitionLabel; + } + + public void setProcessDefinitionLabel(String processDefinitionLabel) { + this.processDefinitionLabel = processDefinitionLabel; + } + + public ArrayList getVariables() { + return variables; + } + + public ArrayList getTasks() { + return tasks; + } + + public ArrayList getTransitions() { + return transitions; + } + + public void setVariables(ArrayList variables) { + this.variables = variables; + } + + public String getVariableName() { + return variableName; + } + + public void setVariableName(String variableName) { + this.variableName = variableName; + } + + public String getVariableValue() { + return variableValue; + } + + public void setVariableValue(String variableValue) { + this.variableValue = variableValue; + } + + public long getTokenInstanceId() { + return tokenInstanceId; + } + + public void setTokenInstanceId(long tokenInstanceId) { + this.taskInstanceId = 0; + this.tokenInstanceId = tokenInstanceId; + } + + public long getTaskInstanceId() { + return taskInstanceId; + } + + public void setTaskInstanceId(long taskInstanceId) { + this.tokenInstanceId = 0; + this.taskInstanceId = taskInstanceId; + } + +} diff --git a/source/java/org/jbpm/webapp/bean/TaskBean.java b/source/java/org/jbpm/webapp/bean/TaskBean.java new file mode 100644 index 0000000000..dba4ffbe5f --- /dev/null +++ b/source/java/org/jbpm/webapp/bean/TaskBean.java @@ -0,0 +1,269 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.bean; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import javax.faces.model.DataModel; +import javax.faces.model.ListDataModel; +import javax.faces.model.SelectItem; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jbpm.JbpmContext; +import org.jbpm.context.def.VariableAccess; +import org.jbpm.graph.def.Transition; +import org.jbpm.graph.exe.ProcessInstance; +import org.jbpm.logging.exe.LoggingInstance; +import org.jbpm.taskmgmt.def.TaskController; +import org.jbpm.taskmgmt.exe.TaskInstance; +import org.jbpm.taskmgmt.log.TaskAssignLog; + +public class TaskBean { + + UserBean userBean = null; + List taskFormParameters; + List availableTransitions; + List availableTransitionItems; + TaskInstance taskInstance; + long taskInstanceId; + DataModel transitions; + +// JbpmContext jbpmContext; +// GraphSession graphSession; +// TaskMgmtSession taskMgmtSession; + + // For monitoring purposes + String name; + String actorId; + Date end; + + public TaskBean() { +// this.jbpmContext = JbpmContext.getCurrentJbpmContext(); +// this.graphSession = jbpmContext.getGraphSession(); +// this.taskMgmtSession = jbpmContext.getTaskMgmtSession(); + + // get the parameters from the session +// this.taskFormParameters = (List) JsfHelper.getSessionAttribute("taskFormParameters"); + } + + public TaskBean(long taskInstanceId, String name, String actorId, Date end) { + this.taskInstanceId = taskInstanceId; + this.name = name; + this.actorId = actorId; + this.end = end; + } + + public void initialize(TaskInstance taskInstance) { + this.taskInstance = taskInstance; + this.taskInstanceId = taskInstance.getId(); + + // set the parameters + this.taskFormParameters = new ArrayList(); + TaskController taskController = taskInstance.getTask().getTaskController(); + if (taskController!=null) { + List variableAccesses = taskController.getVariableAccesses(); + Iterator iter = variableAccesses.iterator(); + while (iter.hasNext()) { + VariableAccess variableAccess = (VariableAccess) iter.next(); + String mappedName = variableAccess.getMappedName(); + Object value = taskInstance.getVariable(mappedName); + TaskFormParameter tfp = new TaskFormParameter(variableAccess, value); + taskFormParameters.add(tfp); + } + } + + // store the parameters in the session + //JsfHelper.setSessionAttribute("taskFormParameters", taskFormParameters); + + // get the available transitions + availableTransitions = null; + + availableTransitions = taskInstance.getAvailableTransitions(); + if ((availableTransitions != null) && (availableTransitions.size() <= 1)) { + transitions = null; + availableTransitions = null; + availableTransitionItems = null; + } else { + transitions = new ListDataModel(availableTransitions); + availableTransitionItems = new ArrayList(); + Iterator iter = availableTransitions.iterator(); + while (iter.hasNext()) { + Transition transition = (Transition) iter.next(); + SelectItem transitionItem = new SelectItem(); + transitionItem.setValue(transition.getName()); + transitionItem.setLabel(transition.getName()); + transitionItem.setDisabled(false); + availableTransitionItems.add(transitionItem); + } + } + + log.debug("initialized availableTransitions " + availableTransitions); + } + + public String save() { + log.debug("saving the task parameters " + taskFormParameters); + + // submit the parameters in the jbpm task controller + TaskInstance taskInstance = JbpmContext.getCurrentJbpmContext().getTaskMgmtSession().loadTaskInstance(taskInstanceId); + + // collect the parameter values from the values that were updated in the + // parameters by jsf. + Iterator iter = taskFormParameters.iterator(); + while (iter.hasNext()) { + TaskFormParameter taskFormParameter = (TaskFormParameter) iter.next(); + + if ((taskFormParameter.isWritable()) && (taskFormParameter.getValue() != null)) { + log.debug("submitting [" + taskFormParameter.getLabel() + "]=" + taskFormParameter.getValue()); + taskInstance.setVariable(taskFormParameter.getLabel(), taskFormParameter.getValue()); + } else { + log.debug("ignoring unwritable [" + taskFormParameter.getLabel() + "]"); + } + } + + // save the process instance and hence the updated task instance variables + JbpmContext.getCurrentJbpmContext().save(taskInstance); + + // remove the parameters from the session + //JsfHelper.removeSessionAttribute("taskFormParameters"); + + return "home"; + } + + public String saveAndClose() { + // save + save(); + + TaskInstance taskInstance = JbpmContext.getCurrentJbpmContext().getTaskMgmtSession().loadTaskInstance(taskInstanceId); + + // close the task instance + if (transitions == null) + { + taskInstance.end(); + } + else + { + Transition selectedTransition = (Transition)transitions.getRowData(); + taskInstance.end(selectedTransition.getName()); + } + + ProcessInstance processInstance = taskInstance.getTaskMgmtInstance().getProcessInstance(); + if (processInstance.hasEnded()) { + JsfHelper.addMessage("The process has finished."); + } + + LoggingInstance loggingInstance = processInstance.getLoggingInstance(); + List assignmentLogs = loggingInstance.getLogs(TaskAssignLog.class); + + log.debug("assignmentlogs: " + assignmentLogs); + + if (assignmentLogs.size() == 1) { + TaskAssignLog taskAssignLog = (TaskAssignLog) assignmentLogs.get(0); + JsfHelper.addMessage("A new task has been assigned to '" + taskAssignLog.getTaskNewActorId() + "'"); + + } else if (assignmentLogs.size() > 1) { + String msg = "New tasks have been assigned to: "; + Iterator iter = assignmentLogs.iterator(); + while (iter.hasNext()) { + TaskAssignLog taskAssignLog = (TaskAssignLog) iter.next(); + msg += taskAssignLog.getActorId(); + if (iter.hasNext()) + msg += ", "; + } + msg += "."; + JsfHelper.addMessage(msg); + } + + JbpmContext.getCurrentJbpmContext().save(taskInstance); + + return "home"; + } + + public long getTaskInstanceId() { + return taskInstanceId; + } + public void setTaskInstanceId(long taskInstanceId) { + this.taskInstanceId = taskInstanceId; + } + public UserBean getUserBean() { + return userBean; + } + public void setUserBean(UserBean userBean) { + this.userBean = userBean; + } + public List getTaskFormParameters() { + return taskFormParameters; + } + + public DataModel getTransitions() + { + return transitions; + } + + public List getAvailableTransitions() { + return availableTransitions; + } + public void setAvailableTransitions(List availableTransitions) { + this.availableTransitions = availableTransitions; + } + public List getAvailableTransitionItems() { + return availableTransitionItems; + } + public TaskInstance getTaskInstance() { + return taskInstance; + } + + private static final Log log = LogFactory.getLog(TaskBean.class); + + public String getActorId() { + return actorId; + } + + public void setActorId(String actorId) { + this.actorId = actorId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Date getEnd() { + return end; + } + + public void setEnd(Date end) { + this.end = end; + } + + public boolean isEnded() { + if (end == null) + return true; + return false; + } +} diff --git a/source/java/org/jbpm/webapp/bean/TaskFormParameter.java b/source/java/org/jbpm/webapp/bean/TaskFormParameter.java new file mode 100644 index 0000000000..c3de976756 --- /dev/null +++ b/source/java/org/jbpm/webapp/bean/TaskFormParameter.java @@ -0,0 +1,109 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.bean; + +import java.io.Serializable; + +import org.hibernate.Session; +import org.jbpm.context.def.VariableAccess; +import org.jbpm.taskmgmt.exe.TaskInstance; + +public class TaskFormParameter implements Serializable { + + private static final long serialVersionUID = 1L; + + protected String label = null; + protected String description = null; + protected Object value = null; + protected boolean isReadable = true; + protected boolean isWritable = true; + protected boolean isRequired = true; + + public TaskFormParameter() { + } + + public TaskFormParameter(VariableAccess variableAccess, Object value) { + this.label = variableAccess.getMappedName(); + this.value = value; + this.isReadable = variableAccess.isReadable(); + this.isWritable = variableAccess.isWritable(); + this.isRequired = variableAccess.isRequired(); + } + + public TaskFormParameter(TaskFormParameter other) { + this.label = other.label; + this.description = other.description; + this.value = other.value; + this.isReadable = other.isReadable; + this.isWritable = other.isWritable; + this.isRequired = other.isRequired; + } + + public static TaskFormParameter create(TaskInstance instance, String name, Object value, Session session) { + TaskFormParameter taskFormParameter = null; + return taskFormParameter; + } + + public String toString() { + return "("+label+","+value+")"; + } + + public String getDescription() { + return description; + } + public void setDescription(String description) { + this.description = description; + } + public boolean isReadable() { + return isReadable; + } + public void setReadable(boolean isReadable) { + this.isReadable = isReadable; + } + public boolean isRequired() { + return isRequired; + } + public void setRequired(boolean isRequired) { + this.isRequired = isRequired; + } + public boolean isWritable() { + return isWritable; + } + public boolean isReadOnly() { + return !isWritable; + } + public void setWritable(boolean isWritable) { + this.isWritable = isWritable; + } + public String getLabel() { + return label; + } + public void setLabel(String label) { + this.label = label; + } + public Object getValue() { + return value; + } + public void setValue(Object value) { + this.value = value; + } +} diff --git a/source/java/org/jbpm/webapp/bean/TokenBean.java b/source/java/org/jbpm/webapp/bean/TokenBean.java new file mode 100644 index 0000000000..ba85f8c1a0 --- /dev/null +++ b/source/java/org/jbpm/webapp/bean/TokenBean.java @@ -0,0 +1,125 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.bean; + +import java.util.Date; + +/** + * Token Bean Implementation. + * + * @author David Loiseau + */ + +public class TokenBean { + + long id; + String name; + String nodeName; + String nodeClassName; + Date start; + Date end; + long level; + + public TokenBean(long id, String name, String nodeName, String nodeClassName, Date start, Date end, long level) { + + this.id = id; + this.name = name; + this.nodeName = nodeName; + this.nodeClassName = nodeClassName; + this.start = start; + this.end = end; + this.level = level; + } + + private String getTypeNameFromClassName(String className) { + String typeName = ""; + if (className.indexOf(".") > 0) { + typeName = className.substring(className.lastIndexOf(".") + 1); + } + return typeName; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLabel() { + String label = ""; + int i = 1; + while (i < this.level) { + label = label + "---"; + i++; + } + if (i > 1) + label = label + " "; + label = label + this.name; + + return label; + } + + public String getNodeName() { + return nodeName; + } + + public void setNodeName(String nodeName) { + this.nodeName = nodeName; + } + + public Date getEnd() { + return end; + } + + public void setEnd(Date end) { + this.end = end; + } + + public Date getStart() { + return start; + } + + public void setStart(Date start) { + this.start = start; + } + + public String getNodeType() { + return getTypeNameFromClassName(this.nodeClassName); + } + + public boolean isSignal() { + if (this.end == null) + return true; + return false; + } + +} diff --git a/source/java/org/jbpm/webapp/bean/UserBean.java b/source/java/org/jbpm/webapp/bean/UserBean.java new file mode 100644 index 0000000000..f5ecd333e7 --- /dev/null +++ b/source/java/org/jbpm/webapp/bean/UserBean.java @@ -0,0 +1,77 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.bean; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.faces.model.SelectItem; + +import org.hibernate.Session; +import org.jbpm.JbpmContext; +import org.jbpm.identity.User; +import org.jbpm.identity.hibernate.IdentitySession; + +public class UserBean { + + String userName; + + public String getUserName() { + return userName; + } + + public void setUserName(String name) { + this.userName = name; + } + + public String login() { + JbpmContext.getCurrentJbpmContext().setActorId(userName); + return "home"; + } + + public List getUsers() { + Session session = JbpmContext.getCurrentJbpmContext().getSession(); + IdentitySession identitySession = new IdentitySession(session); + return identitySession.getUsers(); + } + + public List getUserSelectItems() { + List userSelectItems = new ArrayList(); + + Iterator iter = getUsers().iterator(); + while (iter.hasNext()) { + User user = (User) iter.next(); + userSelectItems.add(new UserSelectItem(user)); + } + + return userSelectItems; + } + + public static class UserSelectItem extends SelectItem { + private static final long serialVersionUID = 1L; + public UserSelectItem(User user) { + setValue(user.getName()); + setLabel(user.getName()); + } + } +} diff --git a/source/java/org/jbpm/webapp/bean/VariableBean.java b/source/java/org/jbpm/webapp/bean/VariableBean.java new file mode 100644 index 0000000000..eefd7b2500 --- /dev/null +++ b/source/java/org/jbpm/webapp/bean/VariableBean.java @@ -0,0 +1,55 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.bean; + +/** + * Variable Bean Implementation. + * + * @author David Loiseau + */ + +public class VariableBean { + + String name; + Object value; + + public VariableBean(String name, Object value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } +} diff --git a/source/java/org/jbpm/webapp/context/BpmContext.java b/source/java/org/jbpm/webapp/context/BpmContext.java new file mode 100644 index 0000000000..c8c18babdf --- /dev/null +++ b/source/java/org/jbpm/webapp/context/BpmContext.java @@ -0,0 +1,44 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.context; + +import org.jbpm.graph.def.ProcessDefinition; +import org.jbpm.taskmgmt.exe.TaskInstance; + +public class BpmContext { + + ProcessDefinition processDefinition; + TaskInstance taskInstance; + + public ProcessDefinition getProcessDefinition() { + return processDefinition; + } + public void setProcessDefinition(ProcessDefinition processDefinition) { + this.processDefinition = processDefinition; + } + public TaskInstance getTaskInstance() { + return taskInstance; + } + public void setTaskInstance(TaskInstance taskInstance) { + this.taskInstance = taskInstance; + } +} diff --git a/source/java/org/jbpm/webapp/context/Context.java b/source/java/org/jbpm/webapp/context/Context.java new file mode 100644 index 0000000000..89b54a1840 --- /dev/null +++ b/source/java/org/jbpm/webapp/context/Context.java @@ -0,0 +1,60 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.context; + +import java.util.HashMap; +import java.util.Map; + +public class Context { + + static ThreadLocal contextsThreadLocal = new ThreadLocal(); + + public static void create() { + contextsThreadLocal.set(new HashMap()); + } + + public static void destroy() { + contextsThreadLocal.set(null); + } + + public static Object getContext(Class clazz) { + Map contexts = (Map) contextsThreadLocal.get(); + Object context = contexts.get(clazz); + if (context==null) { + try { + context = clazz.newInstance(); + contexts.put(clazz, context); + } catch (Exception e) { + throw new RuntimeException("couldn't instantiate context '"+clazz.getName()+"'"); + } + } + return context; + } + + public static PersistenceContext getPersistenceContext() { + return (PersistenceContext) getContext(PersistenceContext.class); + } + + public static BpmContext getBpmContext() { + return (BpmContext) getContext(BpmContext.class); + } +} diff --git a/source/java/org/jbpm/webapp/context/PersistenceContext.java b/source/java/org/jbpm/webapp/context/PersistenceContext.java new file mode 100644 index 0000000000..0cb090c8d6 --- /dev/null +++ b/source/java/org/jbpm/webapp/context/PersistenceContext.java @@ -0,0 +1,83 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.context; + +import javax.naming.InitialContext; +import javax.rmi.PortableRemoteObject; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.SessionFactory; +import org.jbpm.db.JbpmSession; +import org.jbpm.db.JbpmSessionFactory; +import org.jbpm.identity.hibernate.IdentitySession; + +public class PersistenceContext { + + static String jndiName = "java:/jbpm/SessionFactory"; + static JbpmSessionFactory jbpmSessionFactory = null; + static { + try { + InitialContext initialContext = new InitialContext(); + Object o = initialContext.lookup(jndiName); + SessionFactory sessionFactory = (SessionFactory) PortableRemoteObject.narrow(o, SessionFactory.class); + jbpmSessionFactory = new JbpmSessionFactory(null, sessionFactory); + } catch (Exception e) { + throw new RuntimeException("couldn't get the hibernate session factory from jndi entry '"+jndiName+"'", e); + } + } + + boolean isRollbackOnly; + JbpmSession jbpmSession; + IdentitySession identitySession; + + public void beginTransaction() { + isRollbackOnly = false; + log.debug("beginning transaction"); + jbpmSession = jbpmSessionFactory.openJbpmSessionAndBeginTransaction(); + identitySession = new IdentitySession(jbpmSession.getSession()); + } + + public void endTransaction() { + if (isRollbackOnly) { + log.debug("rolling back transaction"); + jbpmSession.rollbackTransactionAndClose(); + } else { + log.debug("committing transaction"); + jbpmSession.commitTransactionAndClose(); + } + } + + public void setRollbackOnly() { + isRollbackOnly = true; + } + + public IdentitySession getIdentitySession() { + return identitySession; + } + + public JbpmSession getJbpmSession() { + return jbpmSession; + } + + private static final Log log = LogFactory.getLog(PersistenceContext.class); +} diff --git a/source/java/org/jbpm/webapp/filter/AuthenticationFilter.java b/source/java/org/jbpm/webapp/filter/AuthenticationFilter.java new file mode 100644 index 0000000000..4f93dac993 --- /dev/null +++ b/source/java/org/jbpm/webapp/filter/AuthenticationFilter.java @@ -0,0 +1,60 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.filter; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.jbpm.JbpmContext; +import org.jbpm.webapp.bean.UserBean; + +public class AuthenticationFilter implements Filter { + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpSession session = request.getSession(); + UserBean userBean = null; + if (session!=null) { + userBean = (UserBean) session.getAttribute("userBean"); + } + if (userBean!=null) { + String actorId = userBean.getUserName(); + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + jbpmContext.setActorId(actorId); + } + filterChain.doFilter(servletRequest, servletResponse); + } + + public void destroy() { + } +} diff --git a/source/java/org/jbpm/webapp/filter/JbpmContextFilter.java b/source/java/org/jbpm/webapp/filter/JbpmContextFilter.java new file mode 100644 index 0000000000..b2ab089369 --- /dev/null +++ b/source/java/org/jbpm/webapp/filter/JbpmContextFilter.java @@ -0,0 +1,88 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.filter; + +import java.io.IOException; +import java.io.Serializable; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.TransactionUtil; +import org.alfresco.service.transaction.TransactionService; +import org.jbpm.JbpmConfiguration; +import org.jbpm.JbpmContext; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +public class JbpmContextFilter implements Filter, Serializable +{ + + private static final long serialVersionUID = 1L; + + private ServletContext context; + + public void init(FilterConfig filterConfig) throws ServletException + { + this.context = filterConfig.getServletContext(); + } + + public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) + throws IOException, ServletException + { + WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(context); + final JbpmConfiguration jbpmConfig = (JbpmConfiguration) wc.getBean("jbpm_configuration"); + TransactionService trx = (TransactionService) wc.getBean("TransactionService"); + + TransactionUtil.executeInUserTransaction(trx, new TransactionUtil.TransactionWork() + { + public Object doWork() throws Exception + { + JbpmContext jbpmContext = jbpmConfig.createJbpmContext(); + try + { + String actorId = AuthenticationUtil.getCurrentUserName(); + if (actorId != null) + { + jbpmContext.setActorId(actorId); + } + filterChain.doFilter(servletRequest, servletResponse); + } + finally + { + jbpmContext.close(); + } + return null; + } + }); + } + + public void destroy() + { + } +} diff --git a/source/java/org/jbpm/webapp/filter/LogFilter.java b/source/java/org/jbpm/webapp/filter/LogFilter.java new file mode 100644 index 0000000000..4cfa8db033 --- /dev/null +++ b/source/java/org/jbpm/webapp/filter/LogFilter.java @@ -0,0 +1,69 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.filter; + +import java.io.IOException; +import java.util.Enumeration; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class LogFilter implements Filter { + + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + + log.debug("request "+request.getRequestURL()); + + Enumeration enumeration = request.getParameterNames(); + while (enumeration.hasMoreElements()) { + String paramName = (String) enumeration.nextElement(); + log.debug("request parameter ["+paramName+"]="+request.getParameter(paramName)); + } + + HttpSession session = request.getSession(); + enumeration = session.getAttributeNames(); + while (enumeration.hasMoreElements()) { + String attributeName = (String) enumeration.nextElement(); + log.debug("session parameter ["+attributeName+"]="+session.getAttribute(attributeName)); + } + + filterChain.doFilter(servletRequest, servletResponse); + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void destroy() { + } + + private static final Log log = LogFactory.getLog(LogFilter.class); +} diff --git a/source/java/org/jbpm/webapp/servlet/ProcessImageServlet.java b/source/java/org/jbpm/webapp/servlet/ProcessImageServlet.java new file mode 100644 index 0000000000..85146b8d8d --- /dev/null +++ b/source/java/org/jbpm/webapp/servlet/ProcessImageServlet.java @@ -0,0 +1,55 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.servlet; + +import java.io.IOException; +import java.io.OutputStream; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.jbpm.JbpmContext; +import org.jbpm.graph.def.ProcessDefinition; + +public class ProcessImageServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + long processDefinitionId = Long.parseLong( request.getParameter( "definitionId" ) ); + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + ProcessDefinition processDefinition = jbpmContext.getGraphSession().loadProcessDefinition(processDefinitionId); + byte[] bytes = processDefinition.getFileDefinition().getBytes("processimage.jpg"); + OutputStream out = response.getOutputStream(); + out.write(bytes); + out.flush(); + + // leave this in. it is in case we want to set the mime type later. + // get the mime type + // String contentType = URLConnection.getFileNameMap().getContentTypeFor( fileName ); + // set the content type (=mime type) + // response.setContentType( contentType ); + } +} diff --git a/source/java/org/jbpm/webapp/tag/ProcessImageTag.java b/source/java/org/jbpm/webapp/tag/ProcessImageTag.java new file mode 100644 index 0000000000..5eaf9ffd63 --- /dev/null +++ b/source/java/org/jbpm/webapp/tag/ProcessImageTag.java @@ -0,0 +1,244 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jbpm.webapp.tag; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; +import javax.servlet.jsp.tagext.TagSupport; + +import org.dom4j.DocumentException; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.dom4j.XPath; +import org.dom4j.xpath.DefaultXPath; +import org.jbpm.JbpmContext; +import org.jbpm.file.def.FileDefinition; +import org.jbpm.graph.def.ProcessDefinition; +import org.jbpm.graph.exe.Token; +import org.jbpm.taskmgmt.exe.TaskInstance; + +public class ProcessImageTag extends TagSupport { + + private static final long serialVersionUID = 1L; + private long taskInstanceId = -1; + private long tokenInstanceId = -1; + + private byte[] gpdBytes = null; + private byte[] imageBytes = null; + private Token currentToken = null; + private ProcessDefinition processDefinition = null; + + static String currentTokenColor = "red"; + static String childTokenColor = "blue"; + static String tokenNameColor = "blue"; + + + public void release() { + taskInstanceId = -1; + gpdBytes = null; + imageBytes = null; + currentToken = null; + } + + public int doEndTag() throws JspException { + try { + initialize(); + retrieveByteArrays(); + if (gpdBytes != null && imageBytes != null) { + writeTable(); + } + } catch (IOException e) { + e.printStackTrace(); + throw new JspException("table couldn't be displayed", e); + } catch (DocumentException e) { + e.printStackTrace(); + throw new JspException("table couldn't be displayed", e); + } + release(); + return EVAL_PAGE; + } + + private void retrieveByteArrays() { + try { + FileDefinition fileDefinition = processDefinition.getFileDefinition(); + gpdBytes = fileDefinition.getBytes("gpd.xml"); + imageBytes = fileDefinition.getBytes("processimage.jpg"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void writeTable() throws IOException, DocumentException { + + int borderWidth = 4; + Element rootDiagramElement = DocumentHelper.parseText(new String(gpdBytes)).getRootElement(); + int[] boxConstraint; + int[] imageDimension = extractImageDimension(rootDiagramElement); + String imageLink = "/alfresco/processimage?definitionId=" + processDefinition.getId(); + JspWriter jspOut = pageContext.getOut(); + + if (tokenInstanceId > 0) { + + List allTokens = new ArrayList(); + walkTokens(currentToken, allTokens); + + jspOut.println("
"); + + for (int i = 0; i < allTokens.size(); i++) + { + Token token = (Token) allTokens.get(i); + + //check how many tokens are on teh same level (= having the same parent) + int offset = i; + if(i > 0) { + while(offset > 0 && ((Token) allTokens.get(offset - 1)).getParent().equals(token.getParent())) { + offset--; + } + } + boxConstraint = extractBoxConstraint(rootDiagramElement, token); + + //Adjust for borders + //boxConstraint[2]-=borderWidth*2; + //boxConstraint[3]-=borderWidth*2; + + jspOut.println("
"); + + if(token.getName()!=null) + { + jspOut.println(" " + token.getName() +""); + } + + jspOut.println("
"); + } + jspOut.println("
"); + } + else + { + boxConstraint = extractBoxConstraint(rootDiagramElement); + + jspOut.println(""); + jspOut.println(" "); + jspOut.println(" "); + jspOut.println(" "); + jspOut.println("
"); + jspOut.println(" "); + jspOut.println(" "); + jspOut.println(" "); + jspOut.println(" "); + jspOut.println(" "); + jspOut.println(" "); + jspOut.println(" "); + jspOut.println(" "); + jspOut.println("
 
"); + jspOut.println("
"); + } + } + + private int[] extractBoxConstraint(Element root) { + int[] result = new int[4]; + String nodeName = currentToken.getNode().getName(); + XPath xPath = new DefaultXPath("//node[@name='" + nodeName + "']"); + Element node = (Element) xPath.selectSingleNode(root); + result[0] = Integer.valueOf(node.attribute("x").getValue()).intValue(); + result[1] = Integer.valueOf(node.attribute("y").getValue()).intValue(); + result[2] = Integer.valueOf(node.attribute("width").getValue()).intValue(); + result[3] = Integer.valueOf(node.attribute("height").getValue()).intValue(); + return result; + } + + private int[] extractBoxConstraint(Element root, Token token) { + int[] result = new int[4]; + String nodeName = token.getNode().getName(); + XPath xPath = new DefaultXPath("//node[@name='" + nodeName + "']"); + Element node = (Element) xPath.selectSingleNode(root); + result[0] = Integer.valueOf(node.attribute("x").getValue()).intValue(); + result[1] = Integer.valueOf(node.attribute("y").getValue()).intValue(); + result[2] = Integer.valueOf(node.attribute("width").getValue()).intValue(); + result[3] = Integer.valueOf(node.attribute("height").getValue()).intValue(); + return result; + } + + private int[] extractImageDimension(Element root) { + int[] result = new int[2]; + result[0] = Integer.valueOf(root.attribute("width").getValue()).intValue(); + result[1] = Integer.valueOf(root.attribute("height").getValue()).intValue(); + return result; + } + + private void initialize() { + JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); + if (this.taskInstanceId > 0) { + TaskInstance taskInstance = jbpmContext.getTaskMgmtSession().loadTaskInstance(taskInstanceId); + currentToken = taskInstance.getToken(); + } + else + { + if (this.tokenInstanceId > 0) + currentToken = jbpmContext.getGraphSession().loadToken(this.tokenInstanceId); + } + processDefinition = currentToken.getProcessInstance().getProcessDefinition(); + } + + private void walkTokens(Token parent, List allTokens) + { + Map children = parent.getChildren(); + if(children != null && children.size() > 0) + { + Collection childTokens = children.values(); + for (Iterator iterator = childTokens.iterator(); iterator.hasNext();) + { + Token child = (Token) iterator.next(); + walkTokens(child, allTokens); + } + } + + allTokens.add(parent); + } + + public void setTask(long id) { + this.taskInstanceId = id; + } + + public void setToken(long id) { + this.tokenInstanceId = id; + } + +} diff --git a/source/web/WEB-INF/alfresco.tld b/source/web/WEB-INF/alfresco.tld index cf592f757b..cee3690833 100644 --- a/source/web/WEB-INF/alfresco.tld +++ b/source/web/WEB-INF/alfresco.tld @@ -1693,6 +1693,12 @@ false true + + + multiSelect + false + true + addButtonLabel diff --git a/source/web/WEB-INF/c.tld b/source/web/WEB-INF/c.tld new file mode 100644 index 0000000000..22698c97dc --- /dev/null +++ b/source/web/WEB-INF/c.tld @@ -0,0 +1,563 @@ + + + + + JSTL 1.1 core library + JSTL core + 1.1 + c + http://java.sun.com/jsp/jstl/core + + + + Provides core validation features for JSTL tags. + + + org.apache.taglibs.standard.tlv.JstlCoreTLV + + + + + + Catches any Throwable that occurs in its body and optionally + exposes it. + + catch + org.apache.taglibs.standard.tag.common.core.CatchTag + JSP + + +Name of the exported scoped variable for the +exception thrown from a nested action. The type of the +scoped variable is the type of the exception thrown. + + var + false + false + + + + + + Simple conditional tag that establishes a context for + mutually exclusive conditional operations, marked by + <when> and <otherwise> + + choose + org.apache.taglibs.standard.tag.common.core.ChooseTag + JSP + + + + + Simple conditional tag, which evalutes its body if the + supplied condition is true and optionally exposes a Boolean + scripting variable representing the evaluation of this condition + + if + org.apache.taglibs.standard.tag.rt.core.IfTag + JSP + + +The test condition that determines whether or +not the body content should be processed. + + test + true + true + boolean + + + +Name of the exported scoped variable for the +resulting value of the test condition. The type +of the scoped variable is Boolean. + + var + false + false + + + +Scope for var. + + scope + false + false + + + + + + Retrieves an absolute or relative URL and exposes its contents + to either the page, a String in 'var', or a Reader in 'varReader'. + + import + org.apache.taglibs.standard.tag.rt.core.ImportTag + org.apache.taglibs.standard.tei.ImportTEI + JSP + + +The URL of the resource to import. + + url + true + true + + + +Name of the exported scoped variable for the +resource's content. The type of the scoped +variable is String. + + var + false + false + + + +Scope for var. + + scope + false + false + + + +Name of the exported scoped variable for the +resource's content. The type of the scoped +variable is Reader. + + varReader + false + false + + + +Name of the context when accessing a relative +URL resource that belongs to a foreign +context. + + context + false + true + + + +Character encoding of the content at the input +resource. + + charEncoding + false + true + + + + + + The basic iteration tag, accepting many different + collection types and supporting subsetting and other + functionality + + forEach + org.apache.taglibs.standard.tag.rt.core.ForEachTag + org.apache.taglibs.standard.tei.ForEachTEI + JSP + + +Collection of items to iterate over. + + items + false + true + java.lang.Object + + + +If items specified: +Iteration begins at the item located at the +specified index. First item of the collection has +index 0. +If items not specified: +Iteration begins with index set at the value +specified. + + begin + false + true + int + + + +If items specified: +Iteration ends at the item located at the +specified index (inclusive). +If items not specified: +Iteration ends when index reaches the value +specified. + + end + false + true + int + + + +Iteration will only process every step items of +the collection, starting with the first one. + + step + false + true + int + + + +Name of the exported scoped variable for the +current item of the iteration. This scoped +variable has nested visibility. Its type depends +on the object of the underlying collection. + + var + false + false + + + +Name of the exported scoped variable for the +status of the iteration. Object exported is of type +javax.servlet.jsp.jstl.core.LoopTagStatus. This scoped variable has nested +visibility. + + varStatus + false + false + + + + + + Iterates over tokens, separated by the supplied delimeters + + forTokens + org.apache.taglibs.standard.tag.rt.core.ForTokensTag + JSP + + +String of tokens to iterate over. + + items + true + true + java.lang.String + + + +The set of delimiters (the characters that +separate the tokens in the string). + + delims + true + true + java.lang.String + + + +Iteration begins at the token located at the +specified index. First token has index 0. + + begin + false + true + int + + + +Iteration ends at the token located at the +specified index (inclusive). + + end + false + true + int + + + +Iteration will only process every step tokens +of the string, starting with the first one. + + step + false + true + int + + + +Name of the exported scoped variable for the +current item of the iteration. This scoped +variable has nested visibility. + + var + false + false + + + +Name of the exported scoped variable for the +status of the iteration. Object exported is of +type +javax.servlet.jsp.jstl.core.LoopTag +Status. This scoped variable has nested +visibility. + + varStatus + false + false + + + + + + Like <%= ... >, but for expressions. + + out + org.apache.taglibs.standard.tag.rt.core.OutTag + JSP + + +Expression to be evaluated. + + value + true + true + + + +Default value if the resulting value is null. + + default + false + true + + + +Determines whether characters <,>,&,'," in the +resulting string should be converted to their +corresponding character entity codes. Default value is +true. + + escapeXml + false + true + + + + + + + Subtag of <choose> that follows <when> tags + and runs only if all of the prior conditions evaluated to + 'false' + + otherwise + org.apache.taglibs.standard.tag.common.core.OtherwiseTag + JSP + + + + + Adds a parameter to a containing 'import' tag's URL. + + param + org.apache.taglibs.standard.tag.rt.core.ParamTag + JSP + + +Name of the query string parameter. + + name + true + true + + + +Value of the parameter. + + value + false + true + + + + + + Redirects to a new URL. + + redirect + org.apache.taglibs.standard.tag.rt.core.RedirectTag + JSP + + +The URL of the resource to redirect to. + + url + false + true + + + +Name of the context when redirecting to a relative URL +resource that belongs to a foreign context. + + context + false + true + + + + + + Removes a scoped variable (from a particular scope, if specified). + + remove + org.apache.taglibs.standard.tag.common.core.RemoveTag + empty + + +Name of the scoped variable to be removed. + + var + true + false + + + +Scope for var. + + scope + false + false + + + + + + Sets the result of an expression evaluation in a 'scope' + + set + org.apache.taglibs.standard.tag.rt.core.SetTag + JSP + + +Name of the exported scoped variable to hold the value +specified in the action. The type of the scoped variable is +whatever type the value expression evaluates to. + + var + false + false + + + +Expression to be evaluated. + + value + false + true + + + +Target object whose property will be set. Must evaluate to +a JavaBeans object with setter property property, or to a +java.util.Map object. + + target + false + true + + + +Name of the property to be set in the target object. + + property + false + true + + + +Scope for var. + + scope + false + false + + + + + + Creates a URL with optional query parameters. + + url + org.apache.taglibs.standard.tag.rt.core.UrlTag + JSP + + +Name of the exported scoped variable for the +processed url. The type of the scoped variable is +String. + + var + false + false + + + +Scope for var. + + scope + false + false + + + +URL to be processed. + + value + false + true + + + +Name of the context when specifying a relative URL +resource that belongs to a foreign context. + + context + false + true + + + + + + Subtag of <choose> that includes its body if its + condition evalutes to 'true' + + when + org.apache.taglibs.standard.tag.rt.core.WhenTag + JSP + + +The test condition that determines whether or not the +body content should be processed. + + test + true + true + boolean + + + + diff --git a/source/web/WEB-INF/faces-config-beans.xml b/source/web/WEB-INF/faces-config-beans.xml index f1be25f9a3..fb26e4ff66 100644 --- a/source/web/WEB-INF/faces-config-beans.xml +++ b/source/web/WEB-INF/faces-config-beans.xml @@ -95,6 +95,17 @@ + + Bean that provides information for the About page + AboutBean + org.alfresco.web.bean.AboutBean + session + + descriptorService + #{DescriptorService} + + + Bean that manages the dialog framework DialogManager @@ -239,6 +250,10 @@ copyService #{CopyService} + + searchService + #{SearchService} + @@ -543,6 +558,31 @@ + + + The bean that backs up the Set Content Properties Dialog + + SetContentPropertiesDialog + org.alfresco.web.bean.content.SetContentPropertiesDialog + session + + nodeService + #{NodeService} + + + fileFolderService + #{FileFolderService} + + + dictionaryService + #{DictionaryService} + + + browseBean + #{BrowseBean} + + + The bean that backs up the Edit Content Properties Dialog @@ -1030,6 +1070,10 @@ actionService #{ActionService} + + nodeService + #{NodeService} + @@ -1507,8 +1551,8 @@ #{ContentService} - namespaceService - #{NamespaceService} + dictionaryService + #{DictionaryService} @@ -1614,8 +1658,8 @@ #{BrowseBean} - dictionaryService - #{DictionaryService} + namespaceService + #{NamespaceService} namespaceService @@ -1680,8 +1724,8 @@ #{SearchService} - dictionaryService - #{DictionaryService} + namespaceService + #{NamespaceService} @@ -1719,6 +1763,242 @@ + + + The bean that backs up the Manage WorkItem Dialog + + ManageWorkItemDialog + org.alfresco.web.bean.workflow.ManageWorkItemDialog + session + + nodeService + #{NodeService} + + + fileFolderService + #{FileFolderService} + + + searchService + #{SearchService} + + + navigator + #{NavigationBean} + + + browseBean + #{BrowseBean} + + + dictionaryService + #{DictionaryService} + + + namespaceService + #{NamespaceService} + + + workflowService + #{WorkflowService} + + + + + StartWorkflowWizard + org.alfresco.web.bean.workflow.StartWorkflowWizard + session + + nodeService + #{NodeService} + + + fileFolderService + #{FileFolderService} + + + searchService + #{SearchService} + + + navigator + #{NavigationBean} + + + browseBean + #{BrowseBean} + + + namespaceService + #{NamespaceService} + + + namespaceService + #{NamespaceService} + + + workflowService + #{WorkflowService} + + + + + + The bean that backs up the Cancel Workflow Dialog + + CancelWorkflowDialog + org.alfresco.web.bean.workflow.CancelWorkflowDialog + session + + nodeService + #{NodeService} + + + fileFolderService + #{FileFolderService} + + + searchService + #{SearchService} + + + navigator + #{NavigationBean} + + + browseBean + #{BrowseBean} + + + dictionaryService + #{DictionaryService} + + + namespaceService + #{NamespaceService} + + + workflowService + #{WorkflowService} + + + + + + The bean that backs up the Reassign Work Item Dialog + + ReassignWorkItemDialog + org.alfresco.web.bean.workflow.ReassignWorkItemDialog + session + + nodeService + #{NodeService} + + + fileFolderService + #{FileFolderService} + + + searchService + #{SearchService} + + + navigator + #{NavigationBean} + + + browseBean + #{BrowseBean} + + + dictionaryService + #{DictionaryService} + + + namespaceService + #{NamespaceService} + + + workflowService + #{WorkflowService} + + + personService + #{PersonService} + + + + + WorkflowBean + org.alfresco.web.bean.workflow.WorkflowBean + session + + nodeService + #{NodeService} + + + workflowService + #{WorkflowService} + + + + + + The bean that backs up the Email Space Users Dialog + + EmailSpaceUsersDialog + org.alfresco.web.bean.users.EmailSpaceUsersDialog + session + + browseBean + #{BrowseBean} + + + nodeService + #{NodeService} + + + permissionService + #{PermissionService} + + + personService + #{PersonService} + + + authorityService + #{AuthorityService} + + + mailSender + #{mailService} + + + + + Bean that manages the Dashboard framework + DashboardManager + org.alfresco.web.bean.dashboard.DashboardManager + session + + + + + The bean that backs up the Dashboard Config Wizard + + DashboardWizard + org.alfresco.web.bean.dashboard.DashboardWizard + session + + nodeService + #{NodeService} + + + dashboardManager + #{DashboardManager} + + + @@ -1728,6 +2008,31 @@ TextFieldGenerator org.alfresco.web.bean.generator.TextFieldGenerator request + + + + + + Bean that generates a text area component + + TextAreaGenerator + org.alfresco.web.bean.generator.TextAreaGenerator + request + @@ -1746,6 +2051,16 @@ DatePickerGenerator org.alfresco.web.bean.generator.DatePickerGenerator request + @@ -1755,6 +2070,16 @@ DateTimePickerGenerator org.alfresco.web.bean.generator.DateTimePickerGenerator request + @@ -1809,6 +2134,44 @@ ChildAssociationGenerator org.alfresco.web.bean.generator.ChildAssociationGenerator request + + + + + Bean that generates a separator component + + SeparatorGenerator + org.alfresco.web.bean.generator.SeparatorGenerator + request + + + + + Bean that generates a header separator component + + HeaderSeparatorGenerator + org.alfresco.web.bean.generator.HeaderSeparatorGenerator + request + + + + + + + + Bean that returns information on a node + + NodeInfoBean + org.alfresco.web.bean.ajax.NodeInfoBean + request + + nodeService + #{NodeService} + + + contentService + #{ContentService} + diff --git a/source/web/WEB-INF/faces-config-jbpm.xml b/source/web/WEB-INF/faces-config-jbpm.xml new file mode 100644 index 0000000000..e31eed4112 --- /dev/null +++ b/source/web/WEB-INF/faces-config-jbpm.xml @@ -0,0 +1,147 @@ + + + + + + userBean + org.jbpm.webapp.bean.AlfrescoUserBean + session + + authenticationService + #{AuthenticationService} + + + + + homeBean + org.jbpm.webapp.bean.HomeBean + session + + taskBean + #{taskBean} + + + userBean + #{userBean} + + + jbpmConfiguration + #{jbpm_configuration} + + + + + taskBean + org.jbpm.webapp.bean.TaskBean + session + + userBean + #{userBean} + + + + + monitoringBean + org.jbpm.webapp.bean.MonitoringBean + session + + + + /jbpm/login.jsp + + home + /jbpm/home.jsp + + + + + /jbpm/home.jsp + + login + /jbpm/login.jsp + + + + + /jbpm/home.jsp + + task + /jbpm/task.jsp + + + + + /jbpm/task.jsp + + login + /jbpm/login.jsp + + + + + /jbpm/task.jsp + + home + /jbpm/home.jsp + + + + + /jsp/dashboards/container.jsp + + task + /jbpm/task_view.jsp + + + + + /jbpm/task_view.jsp + + home + /jsp/dashboards/container.jsp + + + + + /jbpm/monitor.jsp + + processDefinitions + /jbpm/process_definitions.jsp + + + showSearchInstances + /jbpm/search_instances.jsp + + + + + /jbpm/process_definitions.jsp + + processInstances + /jbpm/process_instances.jsp + + + + + /jbpm/inspect_instance.jsp + + processInstances + /jbpm/process_instances.jsp + + + showTransitions + /jbpm/inspect_instance_transitions.jsp + + + + + * + + inspectInstance + /jbpm/inspect_instance.jsp + + + + diff --git a/source/web/WEB-INF/faces-config-navigation.xml b/source/web/WEB-INF/faces-config-navigation.xml index cf8683f9f5..44da3c35b6 100644 --- a/source/web/WEB-INF/faces-config-navigation.xml +++ b/source/web/WEB-INF/faces-config-navigation.xml @@ -283,6 +283,10 @@ applyTemplate /jsp/dialog/apply-space-template.jsp + + applyRSSTemplate + /jsp/dialog/apply-rss-template.jsp + previewSpace /jsp/dialog/preview-space.jsp @@ -297,6 +301,14 @@ + + /jsp/dialog/apply-rss-template.jsp + + manageInvitedUsers + /jsp/roles/manage-invited-users.jsp + + + /jsp/dialog/document-details.jsp @@ -619,6 +631,10 @@ changePassword /jsp/users/change-my-password.jsp + + editUserDetails + /jsp/users/edit-user-details.jsp + diff --git a/source/web/WEB-INF/faces-config-repo.xml b/source/web/WEB-INF/faces-config-repo.xml index e4d3bd38ab..9b779c54f9 100644 --- a/source/web/WEB-INF/faces-config-repo.xml +++ b/source/web/WEB-INF/faces-config-repo.xml @@ -12,6 +12,11 @@ org.alfresco.faces.Property org.alfresco.web.ui.repo.component.property.UIProperty + + + + org.alfresco.faces.Separator + org.alfresco.web.ui.repo.component.property.UISeparator @@ -129,6 +134,16 @@ org.alfresco.web.ui.repo.component.UINodeInfo + + org.alfresco.faces.DialogButtons + org.alfresco.web.ui.repo.component.UIDialogButtons + + + + org.alfresco.faces.UserGroupPicker + org.alfresco.web.ui.repo.component.UIUserGroupPicker + + @@ -165,6 +180,12 @@ org.alfresco.faces.Property org.alfresco.faces.PropertyRenderer org.alfresco.web.ui.repo.renderer.property.PropertyRenderer + + + + org.alfresco.faces.Separator + org.alfresco.faces.SeparatorRenderer + org.alfresco.web.ui.repo.renderer.property.SeparatorRenderer diff --git a/source/web/WEB-INF/jbpm.tld b/source/web/WEB-INF/jbpm.tld new file mode 100644 index 0000000000..03dd5ce333 --- /dev/null +++ b/source/web/WEB-INF/jbpm.tld @@ -0,0 +1,36 @@ + + + + + + 1.0 + 1.1 + jBPM tags + jBPM tags + + + + processimage + org.jbpm.webapp.tag.ProcessImageTag + empty + + task + true + true + + + + + processimageToken + org.jbpm.webapp.tag.ProcessImageTag + empty + + token + true + true + + + + diff --git a/source/web/WEB-INF/repo.tld b/source/web/WEB-INF/repo.tld index 02c03607b6..55b9930f2e 100644 --- a/source/web/WEB-INF/repo.tld +++ b/source/web/WEB-INF/repo.tld @@ -1538,4 +1538,80 @@ + + dialogButtons + org.alfresco.web.ui.repo.tag.DialogButtonsTag + JSP + + + The dialogButtons component displays the buttons for a dialog. + + + + id + false + true + + + + styleClass + false + true + + + + + userGroupPicker + org.alfresco.web.ui.repo.tag.UserGroupPickerTag + JSP + + + The userGroupPicker component renders a multi-select hierarchical list of groups + and users. The groups and be expanded to show the child users and groups for individual + selection and deselection. + + + + id + false + true + + + + value + true + true + + + + binding + false + true + + + + rendered + false + true + + + + actionListener + false + true + + + + style + false + true + + + + styleClass + false + true + + + diff --git a/source/web/WEB-INF/web.xml b/source/web/WEB-INF/web.xml index 45e03798a7..6ae45b32a1 100644 --- a/source/web/WEB-INF/web.xml +++ b/source/web/WEB-INF/web.xml @@ -16,7 +16,7 @@ javax.faces.CONFIG_FILES - /WEB-INF/faces-config-app.xml,/WEB-INF/faces-config-beans.xml,/WEB-INF/faces-config-navigation.xml,/WEB-INF/faces-config-common.xml,/WEB-INF/faces-config-repo.xml,WEB-INF/faces-config-custom.xml,/WEB-INF/faces-config-enterprise.xml + /WEB-INF/faces-config-app.xml,/WEB-INF/faces-config-beans.xml,/WEB-INF/faces-config-navigation.xml,/WEB-INF/faces-config-common.xml,/WEB-INF/faces-config-repo.xml,WEB-INF/faces-config-custom.xml,/WEB-INF/faces-config-enterprise.xml,/WEB-INF/faces-config-jbpm.xml @@ -28,7 +28,7 @@ org.apache.myfaces.DETECT_JAVASCRIPT false This is an EXPERIMENTAL feature, so leave it off for now! - + @@ -92,6 +92,31 @@ --> + + + + + + + LogFilter + org.jbpm.webapp.filter.LogFilter + + + JbpmContextFilter + org.jbpm.webapp.filter.JbpmContextFilter + + + + + + + + Authentication Filter /faces/* @@ -111,6 +136,38 @@ /webdav/* + + + + + + + LogFilter + /faces/jbpm/* + + + JbpmContextFilter + /faces/jbpm/* + + + JbpmContextFilter + /processimage + + + JbpmContextFilter + /faces/jsp/dashboards/* + + + + + + + org.apache.myfaces.webapp.StartupServletContextListener @@ -126,7 +183,7 @@ Faces Servlet - org.alfresco.web.app.servlet.AlfrescoFacesServlet + javax.faces.webapp.FacesServlet 1 @@ -179,7 +236,36 @@ 5 - + + + JBPMDeployProcessServlet + org.alfresco.web.app.servlet.JBPMDeployProcessServlet + + + + + + + + + + + + + ProcessImageServlet + org.jbpm.webapp.servlet.ProcessImageServlet + + + + + + Faces Servlet /faces/* @@ -224,7 +310,34 @@ WebDAV /webdav/* + + + JBPMDeployProcessServlet + /jbpm/deployprocess + + + + + + + + + + + ProcessImageServlet + /processimage + + + + + + 60 @@ -233,4 +346,9 @@ index.jsp + + 500 + /jsp/error.jsp + + diff --git a/source/web/css/main.css b/source/web/css/main.css index 7f2e713ba6..dc8bd137bd 100644 --- a/source/web/css/main.css +++ b/source/web/css/main.css @@ -346,7 +346,7 @@ input,textarea,select .topToolbarTitle { - background-color:#5D747F; + background-color: #465F7D; color: #ffffff; font-weight: bold; padding-left: 5px; @@ -519,3 +519,12 @@ a.topToolbarLinkHighlight, a.topToolbarLinkHighlight:link, a.topToolbarLinkHighl padding: 4px; -moz-border-radius: 4px; } + +.userGroupPickerList +{ + padding: 2px; + background-color: #EEEEEE; + border-width: 1px; + border-style: solid; + border-color: #AAAAAA; +} diff --git a/source/web/images/icons/add_item.gif b/source/web/images/icons/add_item.gif new file mode 100644 index 0000000000..977e4ec117 Binary files /dev/null and b/source/web/images/icons/add_item.gif differ diff --git a/source/web/images/icons/add_item_large.gif b/source/web/images/icons/add_item_large.gif new file mode 100644 index 0000000000..d67e2b04b3 Binary files /dev/null and b/source/web/images/icons/add_item_large.gif differ diff --git a/source/web/images/icons/cancel_workflow.gif b/source/web/images/icons/cancel_workflow.gif new file mode 100644 index 0000000000..8728358b1f Binary files /dev/null and b/source/web/images/icons/cancel_workflow.gif differ diff --git a/source/web/images/icons/cancel_workflow_large.gif b/source/web/images/icons/cancel_workflow_large.gif new file mode 100644 index 0000000000..b8bbfb398e Binary files /dev/null and b/source/web/images/icons/cancel_workflow_large.gif differ diff --git a/source/web/images/icons/completed_workflow_item.gif b/source/web/images/icons/completed_workflow_item.gif new file mode 100644 index 0000000000..65970ff789 Binary files /dev/null and b/source/web/images/icons/completed_workflow_item.gif differ diff --git a/source/web/images/icons/completed_workflow_item_large.gif b/source/web/images/icons/completed_workflow_item_large.gif new file mode 100644 index 0000000000..d3014a8d8c Binary files /dev/null and b/source/web/images/icons/completed_workflow_item_large.gif differ diff --git a/source/web/images/icons/configure_dashboard.gif b/source/web/images/icons/configure_dashboard.gif index d79e1a233b..56c1a7f64b 100644 Binary files a/source/web/images/icons/configure_dashboard.gif and b/source/web/images/icons/configure_dashboard.gif differ diff --git a/source/web/images/icons/configure_dashboard_large.gif b/source/web/images/icons/configure_dashboard_large.gif index 336f7967dd..9656c03235 100644 Binary files a/source/web/images/icons/configure_dashboard_large.gif and b/source/web/images/icons/configure_dashboard_large.gif differ diff --git a/source/web/images/icons/dashboard.gif b/source/web/images/icons/dashboard.gif index d79e1a233b..bbaef9e2fc 100644 Binary files a/source/web/images/icons/dashboard.gif and b/source/web/images/icons/dashboard.gif differ diff --git a/source/web/images/icons/dashboard_large.gif b/source/web/images/icons/dashboard_large.gif index 336f7967dd..cf542588bd 100644 Binary files a/source/web/images/icons/dashboard_large.gif and b/source/web/images/icons/dashboard_large.gif differ diff --git a/source/web/images/icons/email_users.gif b/source/web/images/icons/email_users.gif new file mode 100644 index 0000000000..31ddcd03a0 Binary files /dev/null and b/source/web/images/icons/email_users.gif differ diff --git a/source/web/images/icons/email_users_large.gif b/source/web/images/icons/email_users_large.gif new file mode 100644 index 0000000000..ac879afcdb Binary files /dev/null and b/source/web/images/icons/email_users_large.gif differ diff --git a/source/web/images/icons/manage_workflow_item_large.gif b/source/web/images/icons/manage_workflow_item_large.gif new file mode 100644 index 0000000000..442c772fb6 Binary files /dev/null and b/source/web/images/icons/manage_workflow_item_large.gif differ diff --git a/source/web/images/icons/new_workflow.gif b/source/web/images/icons/new_workflow.gif new file mode 100644 index 0000000000..d37fe0bc94 Binary files /dev/null and b/source/web/images/icons/new_workflow.gif differ diff --git a/source/web/images/icons/new_workflow_large.gif b/source/web/images/icons/new_workflow_large.gif new file mode 100644 index 0000000000..9c7e869856 Binary files /dev/null and b/source/web/images/icons/new_workflow_large.gif differ diff --git a/source/web/images/icons/reassign_workflow_item.gif b/source/web/images/icons/reassign_workflow_item.gif new file mode 100644 index 0000000000..95f2962dc8 Binary files /dev/null and b/source/web/images/icons/reassign_workflow_item.gif differ diff --git a/source/web/images/icons/reassign_workflow_item_large.gif b/source/web/images/icons/reassign_workflow_item_large.gif new file mode 100644 index 0000000000..09c30fb95e Binary files /dev/null and b/source/web/images/icons/reassign_workflow_item_large.gif differ diff --git a/source/web/images/icons/remove_item.gif b/source/web/images/icons/remove_item.gif new file mode 100644 index 0000000000..b1b114165f Binary files /dev/null and b/source/web/images/icons/remove_item.gif differ diff --git a/source/web/images/icons/remove_item_large.gif b/source/web/images/icons/remove_item_large.gif new file mode 100644 index 0000000000..8417c4405b Binary files /dev/null and b/source/web/images/icons/remove_item_large.gif differ diff --git a/source/web/images/icons/rss.gif b/source/web/images/icons/rss.gif new file mode 100644 index 0000000000..0e9be56673 Binary files /dev/null and b/source/web/images/icons/rss.gif differ diff --git a/source/web/images/icons/rss_large.gif b/source/web/images/icons/rss_large.gif new file mode 100644 index 0000000000..5b3e2273d7 Binary files /dev/null and b/source/web/images/icons/rss_large.gif differ diff --git a/source/web/images/icons/user_console.gif b/source/web/images/icons/user_console.gif index 39a7969d3c..1537eab355 100644 Binary files a/source/web/images/icons/user_console.gif and b/source/web/images/icons/user_console.gif differ diff --git a/source/web/images/icons/user_console_large.gif b/source/web/images/icons/user_console_large.gif index 0f0cd28945..50a9a1cf3c 100644 Binary files a/source/web/images/icons/user_console_large.gif and b/source/web/images/icons/user_console_large.gif differ diff --git a/source/web/images/icons/workflow.gif b/source/web/images/icons/workflow.gif new file mode 100644 index 0000000000..000df7c04c Binary files /dev/null and b/source/web/images/icons/workflow.gif differ diff --git a/source/web/images/icons/workflow_item.gif b/source/web/images/icons/workflow_item.gif new file mode 100644 index 0000000000..6cee9ec039 Binary files /dev/null and b/source/web/images/icons/workflow_item.gif differ diff --git a/source/web/images/icons/workflow_item_large.gif b/source/web/images/icons/workflow_item_large.gif new file mode 100644 index 0000000000..bf5bd91084 Binary files /dev/null and b/source/web/images/icons/workflow_item_large.gif differ diff --git a/source/web/images/icons/workflow_large.gif b/source/web/images/icons/workflow_large.gif new file mode 100644 index 0000000000..183d71c2ca Binary files /dev/null and b/source/web/images/icons/workflow_large.gif differ diff --git a/source/web/jbpm/admin.jsp b/source/web/jbpm/admin.jsp new file mode 100644 index 0000000000..5f614a7d8e --- /dev/null +++ b/source/web/jbpm/admin.jsp @@ -0,0 +1,47 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> + + + +Administration + + + + +TODO + +<%-- +(this page is not yet implemented) + +

Deploy process

+ +    + + + +

Database Schema

+ + + + + + + +

Scheduler

+ + + + + + + + + + + + + + +--%> + +
diff --git a/source/web/jbpm/css/jbpm.css b/source/web/jbpm/css/jbpm.css new file mode 100644 index 0000000000..f835ebc084 --- /dev/null +++ b/source/web/jbpm/css/jbpm.css @@ -0,0 +1,106 @@ +body, td, p { + font-family:verdana; + font-size:10pt; +} + +a { + color: rgb(110, 110, 110); + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +div.nav2 { + background-color:#5c5c4f; + color:#ffffff; + font-size: 12px; + font-weight: bold; + text-decoration: none; + line-height: 12px; + border: 0px; + margin-left: 0px; + margin-right: 1px; + margin-top: 1px; + margin-bottom: 0px; + padding-top:5px; + padding-bottom:5px; + width:174px; + cursor: pointer; +} + +div.innerNav { + position:relative; + left:10px; +} + +a.nav { + font-size: 12px; + font-weight: bold; + text-decoration: none; + line-height: 12px; + width:100%; + background-color:#5c5c4f; + color:#ffffff; + border: 0px; + margin-left: 0px; + margin-right: 1px; + margin-top: 1px; + margin-bottom: 0px; + padding-left:10px; + padding-right:10px; + padding-top:5px; + padding-bottom:5px; +} + +div.nav { + font-size: 12px; + font-weight: bold; + text-decoration: none; + line-height: 12px; + width:174; + background-color:#5c5c4f; + color:#ffffff; + border: 0px; + height: 20px; + margin-left: 0px; + margin-right: 1px; + margin-top: 1px; + margin-bottom: 0px; + vertical-align:middle; + padding-left:10px; + padding-top:5px; + padding-bottom:5px; +} + +a.ref { + padding: 5px; + color: rgb(110, 110, 110); + text-decoration: none; + font-size: 11px; + line-height: 18px; +} + +h1, h2, h3, h4, h5, h6 { + font-family:arial; + color:purple; + border-bottom:0px; + margin-bottom:0px; + padding-bottom:0px; +} + +td.tablecell { + padding-left:10px; + padding-right:10px; + background-color:#eeeeee; +} + +th.tableheader { + text-align:center; + text-weight:bold; + padding-right:10px; + padding-left:10px; + color:#ffffff; + background-color:#999999; +} diff --git a/source/web/jbpm/footer.jsp b/source/web/jbpm/footer.jsp new file mode 100644 index 0000000000..91b78efbf3 --- /dev/null +++ b/source/web/jbpm/footer.jsp @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/source/web/jbpm/header1.jsp b/source/web/jbpm/header1.jsp new file mode 100644 index 0000000000..a16e14b038 --- /dev/null +++ b/source/web/jbpm/header1.jsp @@ -0,0 +1,35 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ page import="org.jbpm.webapp.bean.*" %> + + + +JBoss jBPM + + + + + + + + + + + +
JBoss Inc. + + + + + + + + + + +
+
+ Alfresco Web Client    + Docs    + Forums    + Wiki    + Download    + Contact       +
+
+

diff --git a/source/web/jbpm/header2.jsp b/source/web/jbpm/header2.jsp new file mode 100644 index 0000000000..cb865990f7 --- /dev/null +++ b/source/web/jbpm/header2.jsp @@ -0,0 +1,31 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> + +

+
+
+ + + + + diff --git a/source/web/jsp/actions/remove-features.jsp b/source/web/jsp/actions/remove-features.jsp index e88f3ebfaa..ca53707df2 100644 --- a/source/web/jsp/actions/remove-features.jsp +++ b/source/web/jsp/actions/remove-features.jsp @@ -106,7 +106,7 @@ diff --git a/source/web/jsp/browse/browse.jsp b/source/web/jsp/browse/browse.jsp index 2bc57ab8d3..4314fc5948 100644 --- a/source/web/jsp/browse/browse.jsp +++ b/source/web/jsp/browse/browse.jsp @@ -108,7 +108,11 @@ @@ -181,7 +185,7 @@ - + diff --git a/source/web/jsp/browse/dashboard.jsp b/source/web/jsp/browse/dashboard.jsp index d6d8327d1d..e19d044612 100644 --- a/source/web/jsp/browse/dashboard.jsp +++ b/source/web/jsp/browse/dashboard.jsp @@ -87,7 +87,7 @@ - + @@ -110,7 +110,7 @@
+ + + + + + + +
green side
+ + + +
+
+ + diff --git a/source/web/jbpm/home.jsp b/source/web/jbpm/home.jsp new file mode 100644 index 0000000000..1304beb2f7 --- /dev/null +++ b/source/web/jbpm/home.jsp @@ -0,0 +1,64 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> + + + + +Home + + + + +

Tasklist

+ + + + + + + + + + + + + + + + + + + + + + + +

Start New Process Execution

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/source/web/jbpm/images/hdr_green_side.gif b/source/web/jbpm/images/hdr_green_side.gif new file mode 100644 index 0000000000..ec5d33d88b Binary files /dev/null and b/source/web/jbpm/images/hdr_green_side.gif differ diff --git a/source/web/jbpm/images/logo_green.gif b/source/web/jbpm/images/logo_green.gif new file mode 100644 index 0000000000..3367b78e24 Binary files /dev/null and b/source/web/jbpm/images/logo_green.gif differ diff --git a/source/web/jbpm/images/logo_red.gif b/source/web/jbpm/images/logo_red.gif new file mode 100644 index 0000000000..be6952d16d Binary files /dev/null and b/source/web/jbpm/images/logo_red.gif differ diff --git a/source/web/jbpm/images/logo_yellow.gif b/source/web/jbpm/images/logo_yellow.gif new file mode 100644 index 0000000000..3e4fb26698 Binary files /dev/null and b/source/web/jbpm/images/logo_yellow.gif differ diff --git a/source/web/jbpm/images/side_nav_green_btm.gif b/source/web/jbpm/images/side_nav_green_btm.gif new file mode 100644 index 0000000000..a3db50a6dc Binary files /dev/null and b/source/web/jbpm/images/side_nav_green_btm.gif differ diff --git a/source/web/jbpm/images/spacer.gif b/source/web/jbpm/images/spacer.gif new file mode 100644 index 0000000000..fc2560981e Binary files /dev/null and b/source/web/jbpm/images/spacer.gif differ diff --git a/source/web/jbpm/images/swoosh_green.gif b/source/web/jbpm/images/swoosh_green.gif new file mode 100644 index 0000000000..253b13d6ac Binary files /dev/null and b/source/web/jbpm/images/swoosh_green.gif differ diff --git a/source/web/jbpm/inspect_instance.jsp b/source/web/jbpm/inspect_instance.jsp new file mode 100644 index 0000000000..b159f79943 --- /dev/null +++ b/source/web/jbpm/inspect_instance.jsp @@ -0,0 +1,190 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="/WEB-INF/jbpm.tld" prefix="jbpm" %> + +<%@ page isELIgnored="false" %> + + + + + +Inspect Instance + + + + +
+ +: + +
+: + +
+: + + + + + +
+: + + + + + +
+ +
+ +

Tasks

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Variables

+ + + + + + + + + + + + + + + + + + + +
+ + Variable Name: + +
+ Variable Value: + +
+ +
+ +
+ +

Tokens

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ + + + + +
diff --git a/source/web/jbpm/inspect_instance_transitions.jsp b/source/web/jbpm/inspect_instance_transitions.jsp new file mode 100644 index 0000000000..9a5c01f2a9 --- /dev/null +++ b/source/web/jbpm/inspect_instance_transitions.jsp @@ -0,0 +1,69 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="/WEB-INF/jbpm.tld" prefix="jbpm" %> + + + + + +Transitions + + + + +
+: + +
+: + +
+
+ + +
+ +

Available Transitions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ + + + + + + + +
diff --git a/source/web/jbpm/layout.jsp b/source/web/jbpm/layout.jsp new file mode 100644 index 0000000000..156f99e3b9 --- /dev/null +++ b/source/web/jbpm/layout.jsp @@ -0,0 +1,105 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" %> + +<%@ page import="org.jbpm.webapp.bean.*" %> + + + +JBoss jBPM + + + + + + + + + + + + + +
JBoss Inc. + + + + + + + + + + +
+
+ Docs    + Forums    + Wiki    + Download    + Contact       +
+
+

+ + + +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> + +

+
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
green side
green side
+   +
+ swoosh +
+
+ + + + + + +
+
+ + + + diff --git a/source/web/jbpm/monitor.jsp b/source/web/jbpm/monitor.jsp new file mode 100644 index 0000000000..b11d1fa7ee --- /dev/null +++ b/source/web/jbpm/monitor.jsp @@ -0,0 +1,33 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> + + + + + +Monitoring + + + + +
+ +

+ + + + +

+ + + +

+ + Instance ID: + + + + + + +
diff --git a/source/web/jbpm/process_definitions.jsp b/source/web/jbpm/process_definitions.jsp new file mode 100644 index 0000000000..12343e1105 --- /dev/null +++ b/source/web/jbpm/process_definitions.jsp @@ -0,0 +1,54 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> + + + + + +Process Definitions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/web/jbpm/process_instances.jsp b/source/web/jbpm/process_instances.jsp new file mode 100644 index 0000000000..8f84fdbf82 --- /dev/null +++ b/source/web/jbpm/process_instances.jsp @@ -0,0 +1,71 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> + + + + + +Process Instances + + + + +
+ +: + +
+ +: + +
+ +: + +
+ +
+ +

Instances

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/source/web/jbpm/process_list.jsp b/source/web/jbpm/process_list.jsp new file mode 100644 index 0000000000..89a3db560b --- /dev/null +++ b/source/web/jbpm/process_list.jsp @@ -0,0 +1,28 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> + + +  + +

+ + + + + + + + + + + + + + + + + + + + + diff --git a/source/web/jbpm/process_view.jsp b/source/web/jbpm/process_view.jsp new file mode 100644 index 0000000000..4ee6dc6978 --- /dev/null +++ b/source/web/jbpm/process_view.jsp @@ -0,0 +1,26 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/jbpm.tld" prefix="jbpm" %> + +<%@ page isELIgnored="false" %> + +<% System.out.println(request.getParameter("taskInstanceId")); %> + + + default value + + +Setting the value: "Hello World!" + +

+ + +<%-- --%> + +Process View +<%-- --%> + + + +<%-- --%> diff --git a/source/web/jbpm/search_instances.jsp b/source/web/jbpm/search_instances.jsp new file mode 100644 index 0000000000..dd653f8724 --- /dev/null +++ b/source/web/jbpm/search_instances.jsp @@ -0,0 +1,80 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> + + + + + +Search Instances + + + + + + + + + + +
+ + + + + +
+ +

+ +
+ +


+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/web/jbpm/task.jsp b/source/web/jbpm/task.jsp new file mode 100644 index 0000000000..55ed500db0 --- /dev/null +++ b/source/web/jbpm/task.jsp @@ -0,0 +1,74 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/jbpm.tld" prefix="jbpm" %> +<%@ page import="org.jbpm.webapp.bean.*" %> +<%@ page import="org.jbpm.taskmgmt.exe.*" %> + +<%@ page isELIgnored="false" %> + + + + +Task + + + + + + + +
+ + + + +

+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + +
 
+ +
+ + +
+ + + + +
+ +
+    + + +
+ + +
diff --git a/source/web/jbpm/task_list.jsp b/source/web/jbpm/task_list.jsp new file mode 100644 index 0000000000..7d3334c8f6 --- /dev/null +++ b/source/web/jbpm/task_list.jsp @@ -0,0 +1,20 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> + + + + + + + + + + + + + + + + + + diff --git a/source/web/jbpm/task_view.jsp b/source/web/jbpm/task_view.jsp new file mode 100644 index 0000000000..ec28f879a8 --- /dev/null +++ b/source/web/jbpm/task_view.jsp @@ -0,0 +1,70 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/jbpm.tld" prefix="jbpm" %> +<%@ page import="org.jbpm.webapp.bean.*" %> +<%@ page import="org.jbpm.taskmgmt.exe.*" %> + +<%@ page isELIgnored="false" %> + + + + + + + + + +
+ + + + +

+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + +
 
+ +
+ + +
+ + + + +
+ +
+    + + +
+ +
diff --git a/source/web/jsp/actions/add-features.jsp b/source/web/jsp/actions/add-features.jsp index 5e263a6675..7a0bd45826 100644 --- a/source/web/jsp/actions/add-features.jsp +++ b/source/web/jsp/actions/add-features.jsp @@ -106,7 +106,7 @@
- +
- +
<%-- Summary --%> -
 
+
+   +   + +
- + diff --git a/source/web/jsp/content/add-content-dialog.jsp b/source/web/jsp/content/add-content-dialog.jsp index b4d66e0ed9..70eaba69ce 100644 --- a/source/web/jsp/content/add-content-dialog.jsp +++ b/source/web/jsp/content/add-content-dialog.jsp @@ -111,7 +111,7 @@ if (dialog != null && dialog.getFileName() != null)
- + <% if (fileUploaded) diff --git a/source/web/jsp/dashboards/container.jsp b/source/web/jsp/dashboards/container.jsp index f5b8745a55..adc53b82af 100644 --- a/source/web/jsp/dashboards/container.jsp +++ b/source/web/jsp/dashboards/container.jsp @@ -100,6 +100,7 @@ +
diff --git a/source/web/jsp/dialog/about.jsp b/source/web/jsp/dialog/about.jsp index 34669e0241..dfcf4bb32a 100644 --- a/source/web/jsp/dialog/about.jsp +++ b/source/web/jsp/dialog/about.jsp @@ -71,7 +71,7 @@
-
:
+
 - v
diff --git a/source/web/jsp/dialog/advanced-search.jsp b/source/web/jsp/dialog/advanced-search.jsp index 0e45054bbb..ff72a049a9 100644 --- a/source/web/jsp/dialog/advanced-search.jsp +++ b/source/web/jsp/dialog/advanced-search.jsp @@ -286,35 +286,35 @@ - : + : - : + : - : + : - + - + - + - - + - + -
::
::
: + :
::
::
: + :
diff --git a/source/web/jsp/dialog/apply-doc-template.jsp b/source/web/jsp/dialog/apply-doc-template.jsp index 89c6ccd763..f328f07a00 100644 --- a/source/web/jsp/dialog/apply-doc-template.jsp +++ b/source/web/jsp/dialog/apply-doc-template.jsp @@ -69,7 +69,7 @@ -
''
+
''
@@ -113,12 +113,12 @@
- +
- +
diff --git a/source/web/jsp/dialog/apply-rss-template.jsp b/source/web/jsp/dialog/apply-rss-template.jsp new file mode 100644 index 0000000000..1de5e2b9cc --- /dev/null +++ b/source/web/jsp/dialog/apply-rss-template.jsp @@ -0,0 +1,167 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Mozilla Public License version 1.1 + with a permitted attribution clause. You may obtain a + copy of the License at + + http://www.alfresco.org/legal/license.txt + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + either express or implied. See the License for the specific + language governing permissions and limitations under the + License. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + +<%@ page buffer="64kb" contentType="text/html;charset=UTF-8" %> +<%@ page isELIgnored="false" %> +<%@ page import="org.alfresco.web.ui.common.PanelGenerator" %> + + + + + + <%-- load a bundle of properties with I18N strings --%> + + + + + <%-- Main outer table --%> + + + <%-- Title bar --%> + + + + + <%-- Main area --%> + + <%-- Shelf --%> + + + <%-- Work Area --%> + + +
+ <%@ include file="../parts/titlebar.jsp" %> +
+ <%@ include file="../parts/shelf.jsp" %> + + + <%-- Breadcrumb --%> + <%@ include file="../parts/breadcrumb.jsp" %> + + <%-- Status and Actions --%> + + + + + + + <%-- separator row with gradient shadow --%> + + + + + + + <%-- Details --%> + + + + + + + <%-- separator row with bottom panel graphics --%> + + + + + + +
+ + <%-- Status and Actions inner contents table --%> + <%-- Generally this consists of an icon, textual summary and actions for the current object --%> + + + + + +
+ + +
''
+
+
+ +
+ + + <%-- TODO: check for Guest user access and hide panel? --%> + + + + +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "yellowInner", "#ffffcc"); %> + + + + + +
+ + + + + +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "yellowInner"); %> + +
+ + <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "white", "white"); %> + + + + + +
: + <%-- Templates drop-down selector --%> + + + +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "white"); %> +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "blue", "#D3E6FE"); %> + + + + + + + +
+ +
+ +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "blue"); %> +
+
+
+ +
+ +
+ +
\ No newline at end of file diff --git a/source/web/jsp/dialog/apply-space-template.jsp b/source/web/jsp/dialog/apply-space-template.jsp index c2b79cc254..0f195400c8 100644 --- a/source/web/jsp/dialog/apply-space-template.jsp +++ b/source/web/jsp/dialog/apply-space-template.jsp @@ -69,7 +69,7 @@ -
''
+
''
@@ -113,12 +113,12 @@
- +
- +
diff --git a/source/web/jsp/dialog/container.jsp b/source/web/jsp/dialog/container.jsp index a5dcd4cd25..0d9067e811 100644 --- a/source/web/jsp/dialog/container.jsp +++ b/source/web/jsp/dialog/container.jsp @@ -73,6 +73,11 @@
+ + + +   @@ -106,23 +111,7 @@ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "blue", "#D3E6FE"); %> - - - - - - - -
- -
- -
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "blue"); %> diff --git a/source/web/jsp/dialog/delete-space.jsp b/source/web/jsp/dialog/delete-space.jsp new file mode 100644 index 0000000000..58013306c5 --- /dev/null +++ b/source/web/jsp/dialog/delete-space.jsp @@ -0,0 +1,54 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Mozilla Public License version 1.1 + with a permitted attribution clause. You may obtain a + copy of the License at + + http://www.alfresco.org/legal/license.txt + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + either express or implied. See the License for the specific + language governing permissions and limitations under the + License. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + +<%@ page buffer="8kb" contentType="text/html;charset=UTF-8" %> +<%@ page isELIgnored="false" %> + +<%@ page import="org.alfresco.web.ui.common.PanelGenerator" %> + + + + <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "yellowInner", "#ffffcc"); %> + + + + + +
+ + + +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "yellowInner"); %> +
+
+
+ + + + + + + + + + diff --git a/source/web/jsp/dialog/document-details.jsp b/source/web/jsp/dialog/document-details.jsp index 4436c21d32..4e575061ac 100644 --- a/source/web/jsp/dialog/document-details.jsp +++ b/source/web/jsp/dialog/document-details.jsp @@ -120,20 +120,19 @@ - - + + - - + + + + <%-- infomation panel --%> + + + + + + + + + + + <%-- Details --%> diff --git a/source/web/jsp/dialog/space-details.jsp b/source/web/jsp/dialog/space-details.jsp index 3efc490c35..32dc64a698 100644 --- a/source/web/jsp/dialog/space-details.jsp +++ b/source/web/jsp/dialog/space-details.jsp @@ -108,20 +108,19 @@ - - + + -
- +
diff --git a/source/web/jsp/dialog/rules.jsp b/source/web/jsp/dialog/rules.jsp index 4cc77466e7..e6ed79ef71 100644 --- a/source/web/jsp/dialog/rules.jsp +++ b/source/web/jsp/dialog/rules.jsp @@ -102,7 +102,38 @@
+ + +
+ + <%PanelGenerator.generatePanelStart(out, request.getContextPath(), "yellowInner", "#ffffcc");%> +
+ + + + + + + +
+ <%PanelGenerator.generatePanelEnd(out, request.getContextPath(), "yellowInner");%> +
+
diff --git a/source/web/jsp/rules/details.jsp b/source/web/jsp/rules/details.jsp index 235d878755..1f03e9e4e1 100644 --- a/source/web/jsp/rules/details.jsp +++ b/source/web/jsp/rules/details.jsp @@ -104,6 +104,16 @@ + + + diff --git a/source/web/jsp/users/edit-user-details.jsp b/source/web/jsp/users/edit-user-details.jsp new file mode 100644 index 0000000000..788e08fcd0 --- /dev/null +++ b/source/web/jsp/users/edit-user-details.jsp @@ -0,0 +1,196 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Mozilla Public License version 1.1 + with a permitted attribution clause. You may obtain a + copy of the License at + + http://www.alfresco.org/legal/license.txt + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + either express or implied. See the License for the specific + language governing permissions and limitations under the + License. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + +<%@ page buffer="32kb" contentType="text/html;charset=UTF-8" %> +<%@ page isELIgnored="false" %> +<%@ page import="org.alfresco.web.ui.common.PanelGenerator" %> + + + + + + + + <%-- load a bundle of properties with I18N strings --%> + + + <%-- set the form name here --%> + + + <%-- Main outer table --%> +
- +
@@ -296,6 +295,31 @@ +
+ + + + + + + + + + + + + + +
+ + + + +
+
+ <%-- TODO: implement this - but READONLY details only! Manage Space Users for edits... need support for panel with facets - so can hide edit link unless edit permissions also need to wrap this panel with an permissions check: ReadPermissions diff --git a/source/web/jsp/forums/create-post-dialog.jsp b/source/web/jsp/forums/create-post-dialog.jsp index 5c5c8a0b85..2bac31c29a 100644 --- a/source/web/jsp/forums/create-post-dialog.jsp +++ b/source/web/jsp/forums/create-post-dialog.jsp @@ -19,6 +19,18 @@ <%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> <%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + + + + + + + + + - - - - - - - - - - + \ No newline at end of file diff --git a/source/web/jsp/forums/create-reply-dialog.jsp b/source/web/jsp/forums/create-reply-dialog.jsp index 08b120880d..df9af3573f 100644 --- a/source/web/jsp/forums/create-reply-dialog.jsp +++ b/source/web/jsp/forums/create-reply-dialog.jsp @@ -19,7 +19,20 @@ <%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> <%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> - + + + + + + + + + + + - - - - - - - - - - - - - diff --git a/source/web/jsp/parts/titlebar.jsp b/source/web/jsp/parts/titlebar.jsp index cc014227a7..4397ec40ea 100644 --- a/source/web/jsp/parts/titlebar.jsp +++ b/source/web/jsp/parts/titlebar.jsp @@ -28,10 +28,10 @@ - - - - + + + +
+ + + + + + +
diff --git a/source/web/jsp/rules/has-aspect.jsp b/source/web/jsp/rules/has-aspect.jsp index 9d6b76c2b7..91578d515c 100644 --- a/source/web/jsp/rules/has-aspect.jsp +++ b/source/web/jsp/rules/has-aspect.jsp @@ -106,7 +106,7 @@
: - +
+ + <%-- Title bar --%> + + + + + <%-- Main area --%> + + <%-- Shelf --%> + + + <%-- Work Area --%> + + +
+ <%@ include file="../parts/titlebar.jsp" %> +
+ <%@ include file="../parts/shelf.jsp" %> + + + <%-- Breadcrumb --%> + <%@ include file="../parts/breadcrumb.jsp" %> + + <%-- Status and Actions --%> + + + + + + + <%-- separator row with gradient shadow --%> + + + + + + + <%-- Details --%> + + + + + + + <%-- separator row with bottom panel graphics --%> + + + + + + +
+ + <%-- Status and Actions inner contents table --%> + <%-- Generally this consists of an icon, textual summary and actions for the current object --%> + + + + + +
+ +
+
+
+ +
+ + + + + + +
+ + + + <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "white", "white"); %> + + + + + + + + + + + + + + + + + +
: +  * +
: +  * +
: +  * +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "white"); %> +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "blue", "#D3E6FE"); %> + + + + + + + + +
+ +
+ +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "blue"); %> +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/source/web/jsp/users/email-space-users.jsp b/source/web/jsp/users/email-space-users.jsp new file mode 100644 index 0000000000..75994aa0b6 --- /dev/null +++ b/source/web/jsp/users/email-space-users.jsp @@ -0,0 +1,74 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Mozilla Public License version 1.1 + with a permitted attribution clause. You may obtain a + copy of the License at + + http://www.alfresco.org/legal/license.txt + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + either express or implied. See the License for the specific + language governing permissions and limitations under the + License. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + + + + + + +
+ + +
+ + + + + + +  * + + + + + + + + + + + + + + + diff --git a/source/web/jsp/users/user-console.jsp b/source/web/jsp/users/user-console.jsp index 877c84bf6d..008991349e 100644 --- a/source/web/jsp/users/user-console.jsp +++ b/source/web/jsp/users/user-console.jsp @@ -97,12 +97,10 @@ <%-- wrapper comment used by the panel to add additional component facets --%> - <%----%> - - <%----%> + - @@ -132,18 +130,16 @@
<%-- context for current user is setup on entry to user console --%> - +
- <%----%> - - <%----%> + -
diff --git a/source/web/jsp/wizard/invite-content-users/notify.jsp b/source/web/jsp/wizard/invite-content-users/notify.jsp index daf88c4a7f..dca4970d27 100644 --- a/source/web/jsp/wizard/invite-content-users/notify.jsp +++ b/source/web/jsp/wizard/invite-content-users/notify.jsp @@ -134,7 +134,7 @@ : -  * +  * @@ -146,12 +146,12 @@ : <%-- Templates drop-down selector --%> - + - - + + @@ -160,8 +160,8 @@ : - + diff --git a/source/web/jsp/wizard/invite-users/notify.jsp b/source/web/jsp/wizard/invite-users/notify.jsp index 02046b0fa8..740aa959cd 100644 --- a/source/web/jsp/wizard/invite-users/notify.jsp +++ b/source/web/jsp/wizard/invite-users/notify.jsp @@ -134,24 +134,24 @@ : -  * +  * - - - + +
: <%-- Templates drop-down selector --%> - +
@@ -160,8 +160,8 @@ : - + diff --git a/source/web/jsp/wizard/new-user/user-properties.jsp b/source/web/jsp/wizard/new-user/user-properties.jsp index 096caa1a15..b26bdef311 100644 --- a/source/web/jsp/wizard/new-user/user-properties.jsp +++ b/source/web/jsp/wizard/new-user/user-properties.jsp @@ -205,6 +205,23 @@ + + + + <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "yellowInner", "#ffffcc"); %> + + + + + +
+ +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "yellowInner"); %> + + +
+ diff --git a/source/web/jsp/workflow/cancel-workflow-dialog.jsp b/source/web/jsp/workflow/cancel-workflow-dialog.jsp new file mode 100644 index 0000000000..d14067c888 --- /dev/null +++ b/source/web/jsp/workflow/cancel-workflow-dialog.jsp @@ -0,0 +1,26 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Mozilla Public License version 1.1 + with a permitted attribution clause. You may obtain a + copy of the License at + + http://www.alfresco.org/legal/license.txt + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + either express or implied. See the License for the specific + language governing permissions and limitations under the + License. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + +<%@ page buffer="32kb" contentType="text/html;charset=UTF-8" %> +<%@ page isELIgnored="false" %> + + \ No newline at end of file diff --git a/source/web/jsp/workflow/manage-workitem-dialog.jsp b/source/web/jsp/workflow/manage-workitem-dialog.jsp new file mode 100644 index 0000000000..048aa785e4 --- /dev/null +++ b/source/web/jsp/workflow/manage-workitem-dialog.jsp @@ -0,0 +1,112 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Mozilla Public License version 1.1 + with a permitted attribution clause. You may obtain a + copy of the License at + + http://www.alfresco.org/legal/license.txt + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + either express or implied. See the License for the specific + language governing permissions and limitations under the + License. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + + + + + + + + + + + + + <%-- Name column --%> + + + + + + + + + + + <%-- Description column --%> + + + + + + + + <%-- Path column --%> + + + + + + + + <%-- Created Date column --%> + + + + + + + + + + <%-- Modified Date column --%> + + + + + + + + + + <%-- Actions column --%> + + + + + + + + <%-- Completed column --%> + <%-- + + + + + + + + + --%> + + + <%-- Put the package actions here --%> + + diff --git a/source/web/jsp/workflow/reassign-workitem-dialog.jsp b/source/web/jsp/workflow/reassign-workitem-dialog.jsp new file mode 100644 index 0000000000..0e14e13887 --- /dev/null +++ b/source/web/jsp/workflow/reassign-workitem-dialog.jsp @@ -0,0 +1,40 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Mozilla Public License version 1.1 + with a permitted attribution clause. You may obtain a + copy of the License at + + http://www.alfresco.org/legal/license.txt + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + either express or implied. See the License for the specific + language governing permissions and limitations under the + License. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + +<%@ page buffer="32kb" contentType="text/html;charset=UTF-8" %> +<%@ page isELIgnored="false" %> + + + + + + \ No newline at end of file diff --git a/source/web/jsp/workflow/start-workflow-wizard/choose-workflow.jsp b/source/web/jsp/workflow/start-workflow-wizard/choose-workflow.jsp new file mode 100644 index 0000000000..a3799bbefb --- /dev/null +++ b/source/web/jsp/workflow/start-workflow-wizard/choose-workflow.jsp @@ -0,0 +1,28 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Mozilla Public License version 1.1 + with a permitted attribution clause. You may obtain a + copy of the License at + + http://www.alfresco.org/legal/license.txt + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + either express or implied. See the License for the specific + language governing permissions and limitations under the + License. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + + + + + + + \ No newline at end of file diff --git a/source/web/jsp/workflow/start-workflow-wizard/workflow-options.jsp b/source/web/jsp/workflow/start-workflow-wizard/workflow-options.jsp new file mode 100644 index 0000000000..969d4ef1bb --- /dev/null +++ b/source/web/jsp/workflow/start-workflow-wizard/workflow-options.jsp @@ -0,0 +1,50 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Mozilla Public License version 1.1 + with a permitted attribution clause. You may obtain a + copy of the License at + + http://www.alfresco.org/legal/license.txt + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + either express or implied. See the License for the specific + language governing permissions and limitations under the + License. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + +<%@ page import="org.alfresco.web.ui.common.PanelGenerator" %> + + + + <%PanelGenerator.generatePanelStart(out, request.getContextPath(), "yellowInner", "#ffffcc");%> +
+ + + + + + + +
+ <%PanelGenerator.generatePanelEnd(out, request.getContextPath(), "yellowInner");%> +
+
+ + + + + + + + + + \ No newline at end of file diff --git a/source/web/jsp/workflow/workitems-completed-dashlet.jsp b/source/web/jsp/workflow/workitems-completed-dashlet.jsp new file mode 100644 index 0000000000..969aeb8bc8 --- /dev/null +++ b/source/web/jsp/workflow/workitems-completed-dashlet.jsp @@ -0,0 +1,76 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + + + + <%-- Primary column for details view mode --%> + + + + + + + + + + + <%-- Task id column --%> + + + + + + + + <%-- Type column --%> + + + + + + + + <%-- Source column --%> + + + + + + + + <%-- Completed date column --%> + + + + + + + + + + <%-- Outcome column --%> + + + + + + + + <%-- Actions column --%> + + + + + + + + + + + \ No newline at end of file diff --git a/source/web/jsp/workflow/workitems-todo-dashlet.jsp b/source/web/jsp/workflow/workitems-todo-dashlet.jsp new file mode 100644 index 0000000000..1bae631b80 --- /dev/null +++ b/source/web/jsp/workflow/workitems-todo-dashlet.jsp @@ -0,0 +1,92 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + + + + <%-- Primary column for details view mode --%> + + + + + + + + + + + + + + + + + <%-- Task id column --%> + + + + + + + + <%-- Type column --%> + + + + + + + + <%-- Source column --%> + + + + + + + + <%-- Due date column --%> + + + + + + + + + + <%-- Status column --%> + + + + + + + + <%-- Priority column --%> + + + + + + + + <%-- Actions column --%> + + + + + + + + + + + \ No newline at end of file