Change class_key permission in Modx Revo - modx

I am looking for the permission to change the class_key of an resource but I can't find it. Any suggestions?
I need it so my client can change the resource-type of some resources by himself. He created some symlinks but they have to be weblinks.

Modx have permission "class_map" but it prevents any changes of this field. If you want to exclude certain types of resources you can use this plugin at event "OnManagerPageAfterRender".
<?php
// comma separated list of excluded from class list
$excluded = 'modSymLink,modStaticResource';
switch ($modx->event->name) {
case 'OnManagerPageAfterRender':
$controller_path = $controller->config['controller'];
if ($controller_path == 'resource/update' || $controller_path == 'resource/create') {
$my_js = "<script>Ext.onReady(function() {
Ext.getCmp('modx-resource-class-key').baseParams.skip = 'modXMLRPCResource,".$excluded."';
});</script>";
$controller->content = preg_replace('~(</head>)~i', $my_js . '\1', $controller->content);
}
break;
}

Related

Trying to hide Content Parts on Content Type

I am building Content Types and adding Content Parts specific to a Client and Attorney. All of these parts have fields and/or content pickers, etc.
I want to restrict the Client Role to see only Client Content Parts, while I just allow the Attorney Role to see any Content Parts, including it's own Attorney Content Part for a particular Content Type. Again, these are all on the same Content Type, so Content Permissions will not work (except on the Content Type in general).
I want to hide the Attorney Content Parts when a Client is logged on.
I have tried using this:
public override void Displaying(ShapeDisplayingContext context)
{
context.ShapeMetadata.OnDisplaying(displayedContext => {
var shape = context.Shape;
if (context.Shape.Part.Name == "Parts_AttorneyMatterPart")
{
var workContext = _workContextAccessor.GetContext();
var user = workContext.CurrentUser;
var roles = user.As<UserRolesPart>().Roles;
if (!roles.Contains("Spaces Attorney"))
{
shape = null;
}
}
});
}
Where I have a Content Part named "AttorneyMatterPart", and where the Attorney Role is "Spaces Attorney".
These Content Types and Parts were all created in the Orchard Admin. The only thing in my module is this class file.
But this won't hide the Content Part when the Client is logged in. I know that I have to work on the logic of what roles can see things (going to add || conditions for Admin, etc.). For now I am just testing this out.
Any help is appreciated.
EDIT (Bounty Added)
I am really stumped as to whether or not this is even possible. This Content Part is created through the Admin UI. Under shape tracing I can see under the "Content" zone Model > ContentItem > AttorneyMatterPart. I have tried ShapeTableBuilder and I have tried OnDisplaying and OnDisplayed from a ShapeDisplayingContext.
If someone could provide a working sample it would be much appreciated.
When a content part is created through the admin dashboard, there isn't really a shape to render it, only individual shapes for inner content fields...
So, try this
public override void Displaying(ShapeDisplayingContext context) {
context.ShapeMetadata.OnDisplaying(displayedContext => {
var shape = displayedContext.Shape;
if (shape.ContentPart != null
&& shape.ContentPart.PartDefinition.Name == "PartName") {
var workContext = _workContextAccessor.GetContext();
var user = workContext.CurrentUser;
if (user == null || !user.Has<UserRolesPart>()
|| !user.As<UserRolesPart>().Roles.Contains("RoleName")) {
displayedContext.ChildContent = new System.Web.HtmlString("");
}
}
});
}
See my answer on OrchardPros
http://orchardpros.net/tickets/6914
Best
Nulling the shape variable will just clear the local reference. Setting the following however should hide the shape:
displayedContext.ShapeMetadata.Position = "-";
Also FYI it's better not to check on roles the user have but rather create a custom permission, add that to the user role and then check for the permission through
IAuthorizationService.TryCheckAccess()

in a civicrm webform, create multiple 'groups' fields

In CiviCRM webform, you can 'enable tag and groups'. Configuring those allows you to create option elements in the webform.
This creates one 'widget', one dropdown or set of checkboxes. I have two field instances where I want the user to select a group - say for example
which mailing lists do you want to receive (a,b,c)
what food are you interested in (d,e,f)
a,b,c,d,e and f are all groups. I can not change that.
How could I do that ?
A technical suggestion below, but first, I'd suggest that your real solution is to not use groups for the second question. Groups are set up nicely to handle mailing lists, but if it's to track interests, you'd be better off setting those up as custom fields. It'll solve this immediate issue, and it'll make it easier to deal with tandem searches and so forth (on list b and likes food d).
Now if you must have them as groups, you can create a fake field and move checkboxes into it using jQuery. Create the fake field with one option that you don't care about, but label it "What food are you interested in", or equivalent. Then, edit the Groups field that CiviCRM generated: label it more specifically as "which mailing lists...", and choose Static Options so it doesn't start offering up just any group for someone to choose.
Now, add the following javascript:
// first remove the dummy checkboxes in your fake field
$('#yourdummyfield .form-item').each( function() { $(this).remove(); });
// now move things into the dummy field
$('#yourdummyfield').append( $('#groupsfield .form-item-d');
$('#yourdummyfield').append( $('#groupsfield .form-item-e');
$('#yourdummyfield').append( $('#groupsfield .form-item-f');
From the form processing perspective, they'll all be evaluated as the "groups" field. However, they'll look separate. For better or worse, this will have to be adjusted as you add new groups fields.
After using Andrew Hunts suggestion for a while, I finally solved this on the server side, in a custom module, using webform logic as described here
http://www.webomelette.com/drupal-webform-submission-presave-hook
Basicly, on presave, I look for 2 custom fields containing group ids (mailing and food in the example). Then I add these to the CiviCRM groups field.
I'll add the code below, which has some more logic:
to make it flexible, I use one hidden field to contain the fieldkey
of the civicrm groups selector to add the other fields in. that
field is called 'the_groups_element' (but its not the groups element, it contains the key of the groups element)
there is only one foods group allowed, so before it adds you to a food group, it removes all other food groups from the groups selector.
You could probably make it even more generic, but since I had different logic for the different groups, this was suitable for me.
function getFoodGroups() {
// return foodgroups
}
function getMailGroups() {
// return mailgroups
}
function MYMODULE_webform_submission_presave($node, &$submission) {
$groupselm = '';
$groups_cid = false;
$foods_cid = false;
$mailings_cid = false;
// http://www.webomelette.com/drupal-webform-submission-presave-hook
foreach($node->webform['components'] as $cid=>$comp) {
if ($comp['form_key']=='the_groups_element') {
$groupselm = $comp['value'];
break;
}
}
if ($groupselm) {
foreach($node->webform['components'] as $cid=>$comp) {
if ($comp['form_key']==$groupselm) $groups_cid = $comp['cid'];
if ($comp['form_key']=='the_foods') $foods_cid = $comp['cid'];
if ($comp['form_key']=='the_mailings') $mailings_cid = $comp['cid'];
}
$group_gids = $submission->data[$groups_cid];
if (!$group_gids) $group_gids=array();
if ($foods_cid!==false && $submission->data[$foods_cid]) {
// remove all current foods
foreach ($group_gids as $gidx=>$group_gid) {
foreach (getFoodGroups() as $foodgroup) {
if ($group_gid==$foodgroup['gid']) {
if ($debug) drupal_set_message('removing foodgroup '.$foodgroup['gid']);
unset($group_gids[$gidx]);
}
}
}
// validate and add submitted regions
$foodsgids = $submission->data[$foods_cid];
if (!is_array($foodsgids)) $foodsgids = array($foodsgids);
foreach ($foodsgids as $foodsgid) {
foreach (getFoodGroups() as $foodgroup) {
if ($foodsgid==$foodgroup['gid']) {
$group_gids[]=$foodsgid;
break; // only one food allowed
}
}
}
}
if ($mailings_cid!==false && $submission->data[$mailings_cid]) {
// just add submitted mailings, dont remove any
$mailinggids = $submission->data[$mailings_cid];
if (!is_array($mailinggids)) $mailinggids = array($mailinggids);
foreach ($mailinggids as $mailinggid) {
foreach (getMailGroups() as $mailing) {
if ($mailinggid==$mailing['gid']) {
if ($debug) drupal_set_message('adding mailing '.$mailing['gid']);
$group_gids[]=$mailinggid;
}
}
}
}
$submission->data[$groups_cid] = array_unique($group_gids);
}

Symfony 2.1 - $this->get('security.context')->isGranted('ROLE_ADMIN') returns false even if Profiler says I have that role

I have a Controller action (the Controller has $this->securityContext set to $this->get('security.context') via JMSDiExtraBundle):
$user = $this->securityContext->getToken()->getUser();
$groupRepo = $this->getDoctrine()->getRepository('KekRozsakFrontBundle:Group');
if ($this->securityContext->isGranted('ROLE_ADMIN') === false) {
$myGroups = $groupRepo->findByLeader($user);
} else {
$myGroups = $groupRepo->findAll();
}
When I log in to the dev environment and check the profiler, I can see that I have the ROLE_ADMIN role granted, but I still get the filtered list of Groups.
I have put some debugging code in my Controller, and Symfony's RoleVoter.php. The string representation of the Token in my Controller ($this->securityContext->getToken()) and the one in RoleVoter.php are the same, but when I use $token->getRoles(), I get two different arrays.
My Users and Roles are stored in the database via the User and Role entities. Is this a bug that I found or am I doing something wrong?
Finally got it. A dim idea hit my mind a minute ago. The problem was caused my own RoleHierarchyInterface implementation. My original idea was to copy Symfony's own, but load it from the ORM instead of security.yml. But because of this, I had to totally rewrite the buildRoleMap() function. The diff is as follows:
private function buildRoleMap()
{
$this->map = array();
$roles = $this->roleRepo->findAll();
foreach ($roles as $mainRole) {
$main = $mainRole->getRole();
- $this->map[$main] = array();
+ $this->map[$main] = array($main);
foreach ($mainRole->getInheritedRoles() as $childRole) {
$this->map[$main][] = $childRole->getRole();
// TODO: This is one-level only. Get as deep as possible.
// BEWARE OF RECURSIVE NESTING!
foreach ($childRole->getInheritedRoles() as $grandchildRole) {
$this->map[$main][] = $grandchildRole->getRole();
}
}
}
}
This case - roles are set and are displayed in Symfony's profiler but isGranted returns false - can be happened when the role names does not start with prefix ROLE_.
Bad role name: USER_TYPE_ADMIN
Correct role name: ROLE_USER_TYPE_ADMIN

How does one get Drupal's current view/page identifier?

What I am looking for is a page_id/view_id that I can use to identify and style specific pages. I would use the title or the url, but there is a chance that it could change if the a higher-up decides that the page should no longer be called Golf, but rather Tee-Time because he likes it better.
Presumably this identifier would not change if the current page were to be a paged view (page 1,2,3,4...).
One way of solving this is the following. It's depending on the url, so if it changes, so does the class-name.
In my themes template.php I implemented hook_preprocess_page:
function mytheme_preprocess_page(&$vars, $hook) {
$body_classes = array();
$body_classes[] = 'page-' . _get_page_name($_SERVER['REQUEST_URI']);
$vars['body_classes'] = implode(' ', $body_classes);
}
function _get_page_name($request_uri) {
static $numeric_subsection = array(
'/node/' => 'node',
);
$preAlias = $request_uri;
$alias = substr(strrchr($preAlias, "/"), 1);
if (strpos($alias, '?') > -1) {
$alias = substr($alias, 0, strpos($alias, '?'));
}
$page_name = $alias;
if (empty($alias)) {
$page_name = 'start';
}
else if (is_numeric($alias)) {
foreach ($numeric_subsection as $section => $pn) {
if (strpos($preAlias, $section) > -1) {
$page_name = $pn;
}
}
}
return $page_name;
}
Then in the main page-template:
<body class="<?php print $body_classes; ?>">
This isn't a generic solution. So you'll probably have to customize this for your specific needs. It will for example need som tweaking to play nicely with path auto.
This depends a little on how your site is put together (panel pages, view pages, "normal" pages). Essentially, you need to figure out what vars are in scope, and then determine which information in them can be used. To determine what is in scope, you can use print_r(array_keys(get_defined_vars())); and then poke around in the individual vars.
An option is to do something in theme_preprocess_page. One option is to get the page data via page_manager_get_current_page(), poke around in there, and then add body classes as needed. Without knowing what you are doing, you essentially need to print_r the results somewhere, look at what you have, and go from there.

Creating a custom Document Library in SharePoint

I have a document library in my SharePoint page and there are 10 documents in it.
If User A is logged in I want him to only see 5 of those documents in that document library.
How can I create some custom document library for this to work?
I have MOSS installed.
Thanks in advance!
You could configure different permissions on each document in the document library. Just select the "Manage Permissions" option on each item and break the permission inheritance from the document library level. Just note that having too many documents with item level permissions can create a maintenance nightmare for you. Another option could be to create two document libraries with different permissions.
Write an ItemEventReceiver that breaks the permissions based on a field in the library, i.e. a column that holds the different roles .
We have done this by creating a list that holds all roles coupled to sharepoint groups.
i.e.
Administrator -> Owners of website (SPGroup), Company Administrators (SPGroup)
Managers -> Managers (SPGroup)
then in our content type we have a lookup column to this list.
Here's the code for the ItemEventReceiver:
public override void ItemUpdated(SPItemEventProperties properties)
{
lock (_lock)
{
try
{
using (SPSite site = new SPSite(properties.SiteId,
properties.ListItem.ParentList.ParentWeb.Site.SystemAccount.UserToken))
using (SPWeb web = site.OpenWeb(properties.RelativeWebUrl))
{
web.AllowUnsafeUpdates = true;
var item = web.Lists[properties.ListId].GetItemById(properties.ListItemId);
var roles = item["Roles"] as SPFieldLookupValueCollection;
var rolesList = web.Site.RootWeb.Lists["Company Roles"];
var groupsToAdd = new List<SPFieldUserValue>();
if (item.HasUniqueRoleAssignments)
{
item.ResetRoleInheritance();
item = item.ParentList.GetItemById(item.ID);
}
if (roles != null && roles.Count > 0)
{
// Iterate over the roles and see if there is a group associated
foreach (var role in roles)
{
var roleItem = rolesList.GetItemById(rol.LookupId);
if (roleItem != null)
{
// This is the SPgroup field in the rolesList
var groups = roleItem["Groups"] as SPFieldUserValueCollection;
if (groups != null)
{
groupsToAdd.AddRange(from g in groups
where g.User == null
select g);
}
}
}
if (groupsToAdd.Count > 0)
{
item.BreakRoleInheritance(false);
foreach (var value in groupsToAdd)
{
var group = web.Groups[value.LookupValue];
var assignment = web.RoleAssignments.GetAssignmentByPrincipal(group);
item.RoleAssignments.Add(assignment);
}
}
}
DisableEventFiring();
item.SystemUpdate(false);
EnableEventFiring();
}
}
catch (Exception ex)
{
//LOG ERROR
}
}
}
If the coding doesn't work for you, and you'd rather not set permissions on each file, then there is a third option. We use folders with permissions set on them.
e.g.
Create a folder called "Managers", break permissions, and set rights to only the managers.
Create another folder called "Employee 1", break permissions, and set Contribute rights to the Employee and the Employe's manager.
Place the files in the appropriate folders and it will inherit rights from the folder.
This way, managers can see the manager files, and all files of their employees. Users can only see their own files.
Similar logic can be done for Headquarters, Region 1, Region 2, etc ... and creating different Groups for each region and then assigning the group to the folder's permissions.
Note, there's always concern in using this design on maintaining all the permissions and on performance, but we've been doing similar things for 750+ user populations and thousand of docs and it's been working fine for us so far.

Resources