I want to disable the "Advanced ..." (content_status_history) link in the workflow status menu for other roles except Managers and Site Administrators. Is there a permission that I can use to do this? Or is this link's permission coupled with the presence of a transition?
The link's presence is coupled to there being a workflow transition. The form it links to offers additional options to set for the transitions that are available on the current object. There is no permission that controls it's presence; the menu item is hardcoded.
From the plone.app.contentmenu.menu source:
if len(results) > 0:
results.append({ 'title' : _(u'label_advanced', default=u'Advanced...'),
'description' : '',
'action' : url + '/content_status_history',
'selected' : False,
'icon' : None,
'extra' : {'id': 'advanced', 'separator': 'actionSeparator', 'class': 'kssIgnore'},
'submenu' : None,
})
To provide your own implementation (perhaps using a subclass that removes the last option again if certain conditions are met), you'd have to use an override to redefine the browser:menu registration.
In your overrides.zcml you'd have to point to your own implementation using the following browser:menu declaration:
<browser:menu
id="plone_contentmenu_workflow"
title="The 'workflow' menu - allows the user to execute workflow transitions"
class=".yourmodule.YourWorkflowMenu"
/>
then in yourmodule.py create a YourWorkflowMenu class, something like:
from plone.app.contentmenu.menu import WorkflowMenu
class YourWorkflowMenu(WorkflowMenu):
def getMenuItems(self, context, request):
results = super(YourWorkflowMenu, self).getMenuItems(context, request)
if len(results) > 0 and someothercondition:
# Remove status history menu item ('Advanced...')
results = [r for r in results
if not r['action'].endswith('/content_status_history')]
return results
You should be able to hide the menu item by adding
a#advanced {
display: none;
}
to your styles.
That's a pragmatic solution compared the bloated former clean solution.
Related
I am looking for a way to hide this option in the gutenberg blocks. In this example you can see that the core/separator has the option to add "aditional CSS" and that is something I don't want to allow the client to do.
Any way to hide this?
The additional CSS classes section can be enabled/disabled by setting className in the Block Supports to true|false. Any property that is part of supports:{...} can be disabled in the same way as typography (see related answer to previous question), eg:
wp.hooks.addFilter(
'blocks.registerBlockType',
'jsforwpadvgb/extend-quote-block',
extendBlockQuoteBlock
);
function extendBlockQuoteBlock(settings, name) {
if (name !== 'core/quote') {
return settings;
}
return lodash.assign({}, settings, {
supports: lodash.assign({}, settings.supports, {
typography: false, // Previous question 71637137
className: false // Removes "Additional CSS classes" panel for blocks that support it
customClassName: false // **Updated** For blocks that don't have className
}),
});
}
Ref: Block Editor Handbook / Reference Guides / Filter Reference / Block Filters
If you want to remove different options for many blocks, the best way to find the name of the option is to view the block.json source (eg. quote) to find the properties it supports and then disabled/enable what you wish.
I have a client React app I'm instrumenting in appinsights, and I'm using both the React plugin and the new ClickAnalytics plugin. Everything works, telemetry is flowing, however I'm having trouble getting the data-*-id custom event feature working properly.
The docs say:
The id provided in the data-*-id will be used as the customEvent name. For example, if the clicked HTML element has the attribute "data-sample-id"="button1", then "button1" will be the customEvent name.
I instrument an element as follows (using Semantic UI React):
<Button
data-custom-id="AddDealButton"
as={Link}
color="blue"
icon
labelPosition="right"
size="huge"
>
Clicking that button causes the custom event to record but the name, "AddDealButton", doesn't flow through. I always get not_specified as the event name:
Reading the docs, there is this warning regarding the plugin configuration:
If useDefaultContentNameOrId is false, then the customEvent name will be "not_specified".
So I am initializing the plugin this way:
...
extensions: [reactPlugin, clickPlugin],
extensionConfig: {
[reactPlugin.identifier]: { history: browserHistory },
[clickPlugin.identifier]: { autoCapture: true, useDefaultContentNameOrId: true }
}
...yet the name does not pass. Am I misconfiguring? Any idea what I'm doing wrong?
It turns out the problem was in the initialization configuration I showed above. It should be set up as follows:
...
extensions: [reactPlugin, clickPlugin],
extensionConfig: {
[reactPlugin.identifier]: { history: browserHistory },
[clickPlugin.identifier]: { autoCapture: true, dataTags: { useDefaultContentNameOrId: true } }
}
The resulting event name is not being pulled from my data-custom-id but rather pulled from the content of the Icon element of the Button component, so the event name becomes "Create new deal", but I can figure that out.
Microsoft's docs show a different samples for the npm install method vs the "snippet" method, and so I missed the dataTags sample.
I have two components, DeleteCards and ConfirmModal. The DeleteCards component shows cards that can be deleted.
When the user clicks deleteCard an action updates the state like so:
{ showModal : true, title : 'Delete card', body : 'Are you sure?'}
So, this now causes my modal window to show up asking the user to confirm. The problem is that I need my deleteCard function to now wait and listen to the confirmation click inside the modal.
I can technically do this:
{ showModal : true, title : 'Delete card', body : 'Are you sure?', promise: promiseRef }
But, I know this is an anti-pattern. So, what are my options here? Or what would be the redux way of accomplishing this?
Technically that's not a Redux related issue, you could just pass a function as a prop:
<ConfirmModal onConfirm={this.onModalConfirm}/>
Or, if you're 100% decided into Redux, just make a flag, kind of, modalConfirmed, add it to mapStateToProps, in your componentDidUpdate, check for that prop, and then control the function you needed.
Recently I've made a component for recursive classifier with Relay.
I made it with two ways. One of which did not work and the second worked.
From the end-user side they both look the same with the same functionality.
In short the task is pretty much a general recursive data fetching with fetching
on next level expand.
The first way:
Fetch data after setting the relay variable expand. It is made via the
recursive fragment composition. The main part is this:
const CategoryItemContainer = Relay.createContainer(CategoryItem, {
initialVariables: {
expanded: false,
},
fragments: {
category: (variables) => Relay.QL`
fragment on Category {
id
name
hasChildren
${CategoryItemSubcategoryListContainer.getFragment('categories').if(variables.expanded)}
}
`,
},
});
const CategoryItemSubcategoryListContainer = Relay.createContainer(CategoryItemSubCategoryList, {
fragments: {
categories: () => Relay.QL`
fragment on Category {
categories(first: 10000) {
edges {
node {
${CategoryItemContainer.getFragment('category')}
}
}
}
}
`,
},
});
This way it is supposed that the Relay.RootContainer is called just once. It
did not work for me but I know that others implemented it and everything's OK (it's
a subject for another post).
The second way:
The second way worked for me easy. I don't use Relay variables but I call
Relay.Renderer for the next level of recursion depending on the UI component's
state. Like this:
{this.state.hasOwnProperty(category.id) ? // <- changing the state triggers the recursion
<Relay.Renderer
Container={CategoryItemSubCategoryListContainer}
queryConfig={new CategoryRoute({ id: category.id })}
environment={Relay.Store}
render={({ done, error, props, retry, stale }) => {
if (error) {
return 'Error';
} else if (props) {
return <CategoryItemSubCategoryListContainer {...props}
maxLevel={maxLevel}
currentLevel={currentLevel + 1}
activeCats={this.props.activeCats}
elementSelectedCallback={elementSelectedCallback}/>;
}
return (
<ul className="dropdown-menu" aria-labelledby="dLabel">
<li>
<div className="list-group-item"><p className="text-center"><img src="img/ajax-loader.gif"/></p></div>
</li>
</ul>
);
}}
/> : ''}
This works just as I expected to. Moreover I have a nice loader icon for the
next level everytime.
So, having this I've got three questions:
The first
Is any of these methods the preferrable one? Maybe there are drawbacks
somewhere that I don't know? I'd like to understand this since I'm introducing
Relay to my company and I'm willing to replace all the legacy frontend with
React and Relay.
The second
At some point in time a user will pick a category from this recursive
classifier and I will have to get the full path of data that led to the current
level.
I mean if a user picks a category at the level 3 of the hierarchy, I must give
back these data:
[root cat id, root cat name],
[level 1 cat id, level 1 cat name],
[level 2 cat id, level 2 cat name]
So when I catch the event of picking inside the component at the level 2 how can
I get the full path from the root category from Relay? I thought that I can
use some Relay API to get them from store? Or do I have to implement this
store myself?
The third
As I said in the second way of implementation there's a nice feature that I
get the loading spinner every time the Relay.Renderer is called. How can this
be done in the first way? I mean in the first way data fetching is hidden inside
fragment composition and and is triggered by the setVariables. Should I
trigger the spinner right before the setVariables method and then turn it off
in the onReadyStateChange of the only Relay.RootContainer? But then it seems
like I have to make a global store of spinners' state for rendered containers..
I'm confused here..
In a asp.net C# webapp I'm using the CKEditor 3.6.2 and I'm facing the following problem:
In my stylesheet I have a CSS class to use in tables and I'm trying to bring this class already filled in the "Table properties", "Advanced" tab and the "Stylesheet Classes" field.
I want to bring this field filled with the string "blue_table", which is the name of my CSS class. I'm working with the source of the "table" plugin. I have figured out how to change the value of fields like width and height, but the one I want is the "Stylesheet Classes" field.
Do any of you know to to set a default value for this field?
You don't have to edit the ckeditor.js file to customise the editor. You can add the following either to config.js and use it site wide or on any page where you're using CKEditor (inside a script tag as below, after the editor fields you're using).
<script type="text/javascript">
CKEDITOR.on( 'dialogDefinition', function( ev ) {
// Take the dialog name and its definition from the event data.
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
// Check if the definition is from the dialog we're
// interested on (the Table dialog).
if ( dialogName == 'table' ) {
// Set the tab
var advTab = dialogDefinition.getContents( 'advanced');
// Grab the field
var stylesField = advTab.get('advCSSClasses');
// Set the default value
stylesField['default'] = 'blue_table';
}
});
</script>
This is modified from the CKEditor documentation. The hardest part is working out the IDs and names for all the fields used in the dialogs.
Finally I found the answer. This property is in the dialogadvtab, in the property "advCSSClasses". The thing is that this plugin is inside the core js, I mean the ckeditor.js.
I had to do this :
children :
[
{
id : 'advCSSClasses',
att : 'class',
type : 'text',
label : lang.cssClasses,
'default' : 'blue_table',
setup : setupAdvParams,
commit : commitAdvParams
}
]
The "problem" now is that I had to do it in the ckeditor.js, which is not a good practice. The problem is solved, but not int the best way.