UNPKG

zotero-web-library

Version:

Web library from zotero.org

523 lines (479 loc) 16.3 kB
'use strict'; var log = require('libzotero/lib/Log').Logger('zotero-web-library:controlpanel'); var React = require('react'); var GroupsButton = React.createClass({ displayName: 'GroupsButton', render: function render() { var groupsUrl = '/groups'; return React.createElement( 'a', { className: 'btn btn-default navbar-btn navbar-left', href: groupsUrl, title: 'Groups' }, React.createElement('span', { className: 'glyphicons fonticon glyphicons-group' }) ); } }); var LibraryDropdown = React.createClass({ displayName: 'LibraryDropdown', getDefaultProps: function getDefaultProps() { return { library: null, user: false }; }, getInitialState: function getInitialState() { return { accessibleLibraries: [], loading: false, loaded: false }; }, populateDropdown: function populateDropdown() { log.debug('populateDropdown'); var reactInstance = this; if (this.state.loading || this.state.loaded) { return; } var library = this.props.library; if (library == null) { return; } if (!Zotero.config.loggedIn) { throw new Error('no logged in userID. Required for libraryDropdown widget'); } var user = Zotero.config.loggedInUser; var personalLibraryString = 'u' + user.userID; var personalLibraryUrl = Zotero.url.userWebLibrary(user.slug); var currentLibraryName = Zotero.config.librarySettings.name; this.setState({ loading: true }); var memberGroups = library.groups.fetchUserGroups(user.userID).then(function (response) { log.debug('got member groups', 3); var memberGroups = response.fetchedGroups; var accessibleLibraries = []; if (!(Zotero.config.librarySettings.libraryType == 'user' && Zotero.config.librarySettings.libraryID == user.userID)) { accessibleLibraries.push({ name: 'My Library', libraryString: personalLibraryString, webUrl: personalLibraryUrl }); } for (var i = 0; i < memberGroups.length; i++) { if (Zotero.config.librarySettings.libraryType == 'group' && memberGroups[i].get('id') == Zotero.config.librarySettings.libraryID) { continue; } var libraryString = 'g' + memberGroups[i].get('id'); accessibleLibraries.push({ name: memberGroups[i].get('name'), libraryString: libraryString, webUrl: Zotero.url.groupWebLibrary(memberGroups[i]) }); } reactInstance.setState({ accessibleLibraries: accessibleLibraries, loading: false, loaded: true }); }).catch(function (err) { log.error(err); log.error(err.message); }); }, render: function render() { if (this.props.user == false) { return null; } var currentLibraryName = Zotero.config.librarySettings.name; var accessibleLibraries = this.state.accessibleLibraries; var libraryEntries = accessibleLibraries.map(function (lib) { return React.createElement( 'li', { key: lib.libraryString }, React.createElement( 'a', { role: 'menuitem', href: lib.webUrl }, lib.name ) ); }); return React.createElement( 'div', { id: 'library-dropdown', className: 'btn-group', 'data-widget': 'libraryDropdown', 'data-library': this.props.library.libraryString }, React.createElement( 'button', { className: 'btn btn-default navbar-btn dropdown-toggle', onClick: this.populateDropdown, 'data-toggle': 'dropdown', href: '#', title: 'Libraries' }, React.createElement('span', { className: 'glyphicons fonticon glyphicons-inbox' }), React.createElement( 'span', { className: 'current-library-name' }, currentLibraryName ), React.createElement('span', { className: 'caret' }) ), React.createElement( 'ul', { className: 'library-dropdown-list dropdown-menu actions-menu' }, React.createElement( 'li', { hidden: !this.state.loading }, React.createElement( 'a', { role: 'menuitem', className: 'clickable' }, 'Loading...' ) ), libraryEntries ) ); } }); var ActionsDropdown = React.createClass({ displayName: 'ActionsDropdown', getDefaultProps: function getDefaultProps() { return { itemSelected: false, selectedCollection: null, library: null, editable: false }; }, trashOrDeleteItems: function trashOrDeleteItems(evt) { //move currently displayed item or list of selected items to trash //or permanently delete items if already in trash evt.preventDefault(); log.debug('move-to-trash clicked', 3); var library = this.props.library; var itemKeys = Zotero.state.getSelectedItemKeys(); var response; var trashingItems = library.items.getItems(itemKeys); var deletingItems = []; //potentially deleting instead of trashing //TODO: show items list loading? if (Zotero.state.getUrlVar('collectionKey') == 'trash') { //items already in trash. delete them var i; for (i = 0; i < trashingItems.length; i++) { var item = trashingItems[i]; if (item.get('deleted')) { //item is already in trash, schedule for actual deletion deletingItems.push(item); } } //make request to permanently delete items response = library.items.deleteItems(deletingItems); } else { //items are not in trash already so just add them to it response = library.items.trashItems(trashingItems); } library.dirty = true; response.catch(function () { log.error('Error trashing items'); }).then(function () { Zotero.state.clearUrlVars(['collectionKey', 'tag', 'q']); Zotero.state.pushState(true); library.trigger('displayedItemsChanged'); }).catch(Zotero.catchPromiseError); return false; //stop event bubbling }, removeFromTrash: function removeFromTrash(evt) { //Remove currently displayed single item or checked list of items from trash //when remove-from-trash link clicked log.debug('remove-from-trash clicked', 3); var library = this.props.library; var itemKeys = Zotero.state.getSelectedItemKeys(); var untrashingItems = library.items.getItems(itemKeys); //TODO: show items list loading? var response = library.items.untrashItems(untrashingItems); library.dirty = true; response.catch(function () {}).then(function () { log.debug('post-removeFromTrash always execute: clearUrlVars', 3); Zotero.state.clearUrlVars(['collectionKey', 'tag', 'q']); Zotero.state.pushState(); library.trigger('displayedItemsChanged'); }).catch(Zotero.catchPromiseError); return false; }, removeFromCollection: function removeFromCollection(evt) { //Remove currently displayed single item or checked list of items from //currently selected collection log.debug('remove-from-collection clicked', 3); var library = this.props.library; var itemKeys = Zotero.state.getSelectedItemKeys(); var collectionKey = Zotero.state.getUrlVar('collectionKey'); var modifiedItems = []; var responses = []; itemKeys.forEach(function (itemKey, index) { var item = library.items.getItem(itemKey); item.removeFromCollection(collectionKey); modifiedItems.push(item); }); library.dirty = true; library.items.writeItems(modifiedItems).then(function () { log.debug('removal responses finished. forcing reload', 3); Zotero.state.clearUrlVars(['collectionKey', 'tag']); Zotero.state.pushState(true); library.trigger('displayedItemsChanged'); }).catch(Zotero.catchPromiseError); return false; }, triggerLibraryEvent: function triggerLibraryEvent(evt) { var eventType = evt.currentTarget.getAttribute('data-triggers'); this.props.library.trigger(eventType); }, triggerSync: function triggerSync() { this.props.library.trigger('syncLibary'); }, triggerDeleteIdb: function triggerDeleteIdb() { this.props.library.trigger('deleteIdb'); }, render: function render() { var library = this.props.library; var editable = this.props.editable; var itemSelected = this.props.itemSelected; var selectedCollection = this.props.selectedCollection; var collectionSelected = selectedCollection != null; var showTrashActions = editable && itemSelected && selectedCollection == 'trash'; var showNonTrashActions = editable && itemSelected && selectedCollection != 'trash'; var showItemAction = editable && itemSelected; var showCollectionAction = editable && collectionSelected; return React.createElement( 'div', { className: 'btn-group' }, React.createElement( 'button', { className: 'btn btn-default navbar-btn dropdown-toggle', 'data-toggle': 'dropdown', href: '#', title: 'Actions' }, 'Actions', React.createElement('span', { className: 'caret' }) ), React.createElement( 'ul', { className: 'dropdown-menu actions-menu' }, React.createElement( 'li', { hidden: !showItemAction }, React.createElement( 'a', { href: '#', role: 'menuitem', className: 'add-to-collection-button', onClick: this.triggerLibraryEvent, 'data-triggers': 'addToCollectionDialog', title: 'Add to Collection' }, 'Add to Collection' ) ), React.createElement( 'li', { hidden: !(showItemAction && showCollectionAction) }, React.createElement( 'a', { onClick: this.removeFromCollection, href: '#', className: 'remove-from-collection-button', title: 'Remove from Collection' }, 'Remove from Collection' ) ), React.createElement( 'li', { hidden: !showNonTrashActions }, React.createElement( 'a', { onClick: this.trashOrDeleteItems, href: '#', className: 'move-to-trash-button', title: 'Move to Trash' }, 'Move to Trash' ) ), React.createElement( 'li', { hidden: !showTrashActions }, React.createElement( 'a', { onClick: this.trashOrDeleteItems, href: '#', className: 'permanently-delete-button', title: 'Move to Trash' }, 'Permanently Delete' ) ), React.createElement( 'li', { hidden: !showTrashActions }, React.createElement( 'a', { onClick: this.removeFromTrash, href: '#', className: 'remove-from-trash-button', title: 'Remove from Trash' }, 'Remove from Trash' ) ), React.createElement('li', { className: 'divider', hidden: !showItemAction }), React.createElement( 'li', null, React.createElement( 'a', { className: 'create-collection-button', href: '#', onClick: this.triggerLibraryEvent, 'data-triggers': 'createCollectionDialog', title: 'New Collection' }, 'Create Collection' ) ), React.createElement( 'li', { hidden: !showCollectionAction }, React.createElement( 'a', { href: '#', className: 'update-collection-button', onClick: this.triggerLibraryEvent, 'data-triggers': 'updateCollectionDialog', title: 'Change Collection' }, 'Rename Collection' ) ), React.createElement( 'li', { hidden: !showCollectionAction }, React.createElement( 'a', { href: '#', className: 'delete-collection-button', onClick: this.triggerLibraryEvent, 'data-triggers': 'deleteCollectionDialog', title: 'Delete Collection' }, 'Delete Collection' ) ), React.createElement('li', { className: 'divider' }), React.createElement( 'li', null, React.createElement( 'a', { href: '#', onClick: this.triggerLibraryEvent, 'data-triggers': 'librarySettingsDialog' }, 'Library Settings' ) ), React.createElement( 'li', null, React.createElement( 'a', { href: '#', className: 'cite-button', onClick: this.triggerLibraryEvent, 'data-triggers': 'citeItems' }, 'Cite' ) ), React.createElement( 'li', null, React.createElement( 'a', { href: '#', className: 'export-button', onClick: this.triggerLibraryEvent, 'data-triggers': 'exportItemsDialog' }, 'Export' ) ), React.createElement('li', { className: 'divider selected-item-action' }), React.createElement( 'li', { className: 'selected-item-action', hidden: !showItemAction }, React.createElement( 'a', { href: '#', className: 'send-to-library-button', onClick: this.triggerLibraryEvent, 'data-triggers': 'sendToLibraryDialog', title: 'Copy to Library' }, 'Copy to Library' ) ), React.createElement('li', { className: 'divider', hidden: !showItemAction }), React.createElement( 'li', null, React.createElement( 'a', { href: '#', 'data-triggers': 'syncLibrary', onClick: this.triggerLibraryEvent }, 'Sync' ) ), React.createElement( 'li', null, React.createElement( 'a', { href: '#', 'data-triggers': 'deleteIdb', onClick: this.triggerLibraryEvent }, 'Delete IDB' ) ) ) ); } }); var CreateItemDropdown = React.createClass({ displayName: 'CreateItemDropdown', getDefaultProps: function getDefaultProps() { return { editable: false }; }, createItem: function createItem(evt) { //clear path vars and send to new item page with current collection when create-item-link clicked log.debug('create-item-Link clicked', 3); evt.preventDefault(); var library = this.props.library; var itemType = evt.target.getAttribute('data-itemtype'); library.trigger('createItem', { itemType: itemType }); return false; }, render: function render() { var reactInstance = this; var itemTypes = Object.keys(Zotero.Item.prototype.typeMap); itemTypes = itemTypes.sort(); var nodes = itemTypes.map(function (itemType, ind) { return React.createElement( 'li', { key: itemType }, React.createElement( 'a', { onClick: reactInstance.createItem, href: '#', 'data-itemtype': itemType }, Zotero.Item.prototype.typeMap[itemType] ) ); }); var buttonClass = 'create-item-button btn btn-default navbar-btn dropdown-toggle'; if (Zotero.state.getUrlVar('collectionKey') == 'trash') { buttonClass += ' disabled'; } return React.createElement( 'div', { className: 'btn-group create-item-dropdown', hidden: !this.props.editable }, React.createElement( 'button', { type: 'button', className: buttonClass, 'data-toggle': 'dropdown', title: 'New Item' }, React.createElement('span', { className: 'glyphicons fonticon glyphicons-plus' }) ), React.createElement( 'ul', { className: 'dropdown-menu', role: 'menu', style: { maxHeight: '300px', overflow: 'auto' } }, nodes ) ); } }); var ControlPanel = React.createClass({ displayName: 'ControlPanel', componentWillMount: function componentWillMount() { var reactInstance = this; var library = this.props.library; reactInstance.setState({ user: Zotero.config.loggedInUser }); library.listen('selectedItemsChanged', function (evt) { log.debug('got selectedItemsChanged event in ControlPanel - setting selectedItems'); log.debug(evt); var selectedItemKeys = evt.data.selectedItemKeys; reactInstance.setState({ selectedItems: selectedItemKeys }); }, {}); library.listen('selectedCollectionChanged', function (evt) { var selectedCollection = Zotero.state.getUrlVar('collectionKey'); var selectedItemKeys = Zotero.state.getSelectedItemKeys(); reactInstance.setState({ selectedCollection: selectedCollection, selectedItems: selectedItemKeys }); }, {}); }, getDefaultProps: function getDefaultProps() { return { editable: false }; }, getInitialState: function getInitialState() { var selectedItems = Zotero.state.getSelectedItemKeys(); return { user: false, selectedItems: selectedItems, selectedCollection: null }; }, render: function render() { return React.createElement( 'div', { id: 'control-panel', className: 'nav navbar-nav', role: 'navigation' }, React.createElement( 'div', { className: 'btn-toolbar navbar-left' }, React.createElement(GroupsButton, { library: this.props.library }), React.createElement(LibraryDropdown, { user: this.state.user, library: this.props.library }), React.createElement(ActionsDropdown, { library: this.props.library, itemSelected: this.state.selectedItems.length > 0, selectedCollection: this.state.selectedCollection, editable: this.props.editable }), React.createElement(CreateItemDropdown, { library: this.props.library, editable: this.props.editable }) ) ); } }); module.exports = ControlPanel;