MODX Community

Articles - limit Author list to User Group?

For each article in Articles there is the [[*createdby]] placeholder to set the author of it. This option holds a dropdown of all users and I would like to limit this to a specific user group or a list of user IDs. I don’t want every author to need(/be able) to scroll through all registered users to find their name.

How would I achieve this?

I don’t think there’s an easy way to do it. It’s really a problem with the user permission system. Either the current user has view_user permission or they don’t. If they do, they can see all users (including the admin), if they don’t, I’m not sure what happens, either they can still see all the users, the drop-down would be empty, or they’d get “access denied.”

If there is a placeholder for the dropdown in an Articles Tpl chunk, you could replace it with a call to a fairly simple snippet that would create whatever content you’d like for the dropdown. Good luck finding it. :wink:

1 Like

Those words scared me like nothing else… :grimacing: But I was brave and I went deep down the rabbit hole. But I think, I might have actually found something:

// assets/components/articles/js/article/create.js (line 190+)

{
   xtype: MODx.config.publish_document ? 'modx-combo-user' : 'hidden' 
   //xtype: 'hidden'
   ,fieldLabel: _('resource_createdby')
   ,description: '<b>[[*createdby]]</b><br />'+_('resource_createdby_help')
   ,name: 'created_by'
   ,hiddenName: 'createdby'
   ,id: 'modx-resource-createdby'
   ,allowBlank: true
   ,width: 300
   ,value: config.record.createdby || MODx.user.
}



// assets/components/articles/js/article/upadate.js (line 403+)

{
   xtype: MODx.config.publish_document ? 'modx-combo-user' : 'hidden'
   ,fieldLabel: _('resource_createdby')
   ,description: '<b>[[*createdby]]</b><br />'+_('resource_createdby_help')
   ,name: 'created_by'
   ,hiddenName: 'createdby'
   ,id: 'modx-resource-createdby'
   ,allowBlank: true
   ,width: 300
   ,value: config.record.createdby
}

Is this an option now?

1 Like

I think what BobRay means is the tpl files that will be in components. This is what I did with Discuss when I was customizing it.

The entire output of articles should be through a series of tpl files, each quite small, chunks of html. Attacking from js side also may work…I am not sure I can’t do a thing with js.

So for example, I wanted to customize a similar thing, which was the author’s info under their name in the post itself, so I had to search for the appropriate tpl. In my case I believe I put a snippet in there to output the author’s number of comments and then a jpg based on that number (simple badge).

If you can find that tpl (I think it must exist) then its trivial to customize it

The reason I think BobRay says good luck searching is that its a slowish process, the pages are I guess made by articles snippet and so it can be hard to know which piece is going where, and there may be a bunch of these tpls.

BobRay have there been any improvements in developing these tools a little more smoothly? On the one hand I loved the tpls because I had real control over every inch of the page, on the other hand it seemed quite a mess, but that software is now like 10 years old, so…I guess keep the tpls but make a guide to them or something.

Under core/components/articles there’s no other .tpl files than the two example templates and a few default chunks, all dealing with the articles view on the front-end.

I’m quite sure that article’s create.js and update.js (see above) are responsible for building the back-end view of each article resource.

full create.js code
Articles.page.CreateArticle = function(config) {
    config = config || {record:{}};
    config.record = config.record || {};
    Ext.applyIf(config,{
        panelXType: 'modx-panel-article'
    });
    config.canDuplicate = false;
    config.canDelete = false;
    Articles.page.CreateArticle.superclass.constructor.call(this,config);
};
Ext.extend(Articles.page.CreateArticle,MODx.page.CreateResource,{

});
Ext.reg('articles-page-article-create',Articles.page.CreateArticle);

Articles.panel.Article = function(config) {
    config = config || {};
    Ext.applyIf(config,{
    });
    Articles.panel.Article.superclass.constructor.call(this,config);
};
Ext.extend(Articles.panel.Article,MODx.panel.Resource,{
    getFields: function(config) {
        var it = [];
        it.push({
            title: _('articles.article')
            ,id: 'modx-resource-settings'
            ,cls: 'modx-resource-tab'
            ,layout: 'form'
            ,labelAlign: 'top'
            ,labelSeparator: ''
            ,bodyCssClass: 'tab-panel-wrapper main-wrapper'
            ,autoHeight: true
            ,defaults: {
                border: false
                ,msgTarget: 'under'
                ,width: 400
            }
            ,items: this.getMainFields(config)
        });
        if (config.show_tvs && MODx.config.tvs_below_content != 1) {
            it.push(this.getTemplateVariablesPanel(config));
        }
        if (MODx.perm.resourcegroup_resource_list == 1) {
            it.push(this.getAccessPermissionsTab(config));
        }
        var its = [];
        its.push(this.getPageHeader(config),{
            id:'modx-resource-tabs'
            ,xtype: 'modx-tabs'
            ,forceLayout: true
            ,deferredRender: false
            ,collapsible: true
            ,itemId: 'tabs'
            ,items: it
        });

        if (MODx.config.tvs_below_content == 1) {
            var tvs = this.getTemplateVariablesPanel(config);
            tvs.style = 'margin-top: 10px;visibility: visible';
            its.push(tvs);
        }
        return its;
    }

    ,getMainLeftFields: function(config) {
        config = config || {record:{}};
        var mlf = [{
            xtype: 'textfield'
            ,fieldLabel: _('articles.article_title')+'<span class="required">*</span>'
            ,description: '<b>[[*pagetitle]]</b><br />'+_('articles.article_title_help')
            ,name: 'pagetitle'
            ,id: 'modx-resource-pagetitle'
            ,maxLength: 255
            ,anchor: '100%'
            ,allowBlank: false
            ,enableKeyEvents: true
            ,listeners: {
                'keyup': {scope:this,fn:function(f,e) {
                    var title = Ext.util.Format.stripTags(f.getValue());
                    Ext.getCmp('modx-resource-header').getEl().update('<h2>'+title+'</h2>');
                }}
            }
        }];

        if (MODx.config['articles.article_show_longtitle']) {
            mlf.push({
                xtype: 'textfield'
                ,fieldLabel: _('resource_longtitle')
                ,description: '<b>[[*longtitle]]</b><br />'+_('resource_longtitle')
                ,name: 'longtitle'
                ,id: 'modx-resource-longtitle'
                ,anchor: '100%'
                ,value: config.record.longtitle || ''
            });
        }

        mlf.push({
            xtype: 'textarea'
            ,fieldLabel: _('articles.article_summary')
            ,description: '<b>[[*introtext]]</b><br />'+_('articles.article_summary')
            ,name: 'introtext'
            ,id: 'modx-resource-introtext'
            ,anchor: '100%'
            ,value: config.record.introtext || ''
        });

        var ct = this.getContentField(config);
        if (ct) {
            mlf.push(ct);
        }
        return mlf;
    }

    ,getContentField: function(config) {
        return [{
            id: 'modx-content-above'
            ,border: false
        },{
            xtype: 'textarea'
            ,fieldLabel: _('articles.article_content')
            ,name: 'ta'
            ,id: 'ta'
            ,anchor: '100%'
            ,height: 400
            ,grow: false
            ,value: (config.record.content || config.record.ta) || ''
            ,itemCls: 'contentblocks_replacement'
        },{
            id: 'modx-content-below'
            ,border: false
        }];
    }


    ,getMainRightFields: function(config) {
        config = config || {};
		config.record.richtext = (config.record.richtext !== undefined ? parseInt(config.record.richtext) : 1); // don't ask me why
        return [{
            xtype: 'fieldset'
            ,title: _('articles.publishing_information')
            ,id: 'articles-box-publishing-information'
            ,defaults: {
                msgTarget: 'under'
            }
            ,items: [{
                xtype: 'articles-combo-publish-status'
                ,fieldLabel: _('articles.status')
                ,name: 'published'
                ,hiddenName: 'published'
                ,inputValue: 0
                ,value: 0
            },{
                xtype: 'xdatetime'
                ,fieldLabel: _('resource_publishedon')
                ,description: '<b>[[*publishedon]]</b><br />'+_('resource_publishedon_help')
                ,name: 'publishedon'
                ,id: 'modx-resource-publishedon'
                ,allowBlank: true
                ,dateFormat: MODx.config.manager_date_format
                ,timeFormat: MODx.config.manager_time_format
                ,dateWidth: 120
                ,timeWidth: 120
                ,value: config.record.publishedon
            },{
                xtype: MODx.config.publish_document ? 'xdatetime' : 'hidden'
                ,fieldLabel: _('resource_publishdate')
                ,description: '<b>[[*pub_date]]</b><br />'+_('resource_publishdate_help')
                ,name: 'pub_date'
                ,id: 'modx-resource-pub-date'
                ,allowBlank: true
                ,dateFormat: MODx.config.manager_date_format
                ,timeFormat: MODx.config.manager_time_format
                ,dateWidth: 120
                ,timeWidth: 120
                ,value: config.record.pub_date
            },{
                xtype: MODx.config.publish_document ? 'xdatetime' : 'hidden'
                ,fieldLabel: _('resource_unpublishdate')
                ,description: '<b>[[*unpub_date]]</b><br />'+_('resource_unpublishdate_help')
                ,name: 'unpub_date'
                ,id: 'modx-resource-unpub-date'
                ,allowBlank: true
                ,dateFormat: MODx.config.manager_date_format
                ,timeFormat: MODx.config.manager_time_format
                ,dateWidth: 120
                ,timeWidth: 120
                ,value: config.record.unpub_date
            },{
                xtype: MODx.config.publish_document ? 'modx-combo-user' : 'hidden' 
                 //xtype: 'hidden'
                ,fieldLabel: _('resource_createdby')
                ,description: '<b>[[*createdby]]</b><br />'+_('resource_createdby_help')
                ,name: 'created_by'
                ,hiddenName: 'createdby'
                ,id: 'modx-resource-createdby'
                ,allowBlank: true
                ,width: 300
                ,value: config.record.createdby || MODx.user.id
            },{
                xtype: 'xcheckbox'
                ,name: 'clearCache'  
                ,fieldLabel:_('resource_syncsite')
                ,description:_('resource_syncsite_help')
                ,id: 'modx-resource-clearcache'
                ,value: 1
                ,checked:true
            }]
        },{
            html: '<hr />'
            ,border: false
        },{
            xtype: 'fieldset'
            ,title: _('articles.article_options')
            ,id: 'articles-box-options'
            ,defaults: {
                msgTarget: 'under'
            }
            ,items: [{
                xtype: 'modx-combo-template'
                ,fieldLabel: _('resource_template')
                ,description: '<b>[[*template]]</b><br />'+_('resource_template_help')
                ,name: 'template'
                ,id: 'modx-resource-template'
                ,anchor: '100%'
                ,editable: false
            },{
                xtype: 'textfield'
                ,fieldLabel: _('articles.article_alias')
                ,description: '<b>[[*alias]]</b><br />'+_('articles.article_alias_help')
                ,name: 'alias'
                ,id: 'modx-resource-alias'
                ,maxLength: 100
                ,anchor: '100%'
                ,value: config.record.alias || ''

            },{
                xtype: 'bxr-field-tags'
                ,fieldLabel: _('articles.article_tags')
                ,description: _('articles.article_tags_help')
                ,name: 'fake_tags'
                ,id: 'modx-resource-tags'
                ,anchor: '100%'
            },{
                xtype: 'hidden'
                ,name: 'menutitle'
                ,id: 'modx-resource-menutitle'
                ,value: config.record.menutitle || ''

            },{
                xtype: 'hidden'
                ,name: 'link_attributes'
                ,id: 'modx-resource-link-attributes'
                ,value: config.record.link_attributes || ''

            },{
                xtype: 'hidden'
                ,name: 'hidemenu'
                ,id: 'modx-resource-hidemenu'
                ,value: config.record.hidemenu

            },{
                xtype: 'hidden'
                ,name: 'class_key'
                ,value: 'Article'
            }]
        },{
            xtype: 'fieldset'
            ,style:{'display':'none'}
            ,title: _('articles.article_edit_options')
            ,id: 'articles-edit-options'
            ,defaults: {
                msgTarget: 'under'
            }
            ,items: [{
                xtype: 'xcheckbox'
                ,name: 'richtext'
                ,fieldLabel:_('resource_richtext')
                ,description:_('resource_richtext_help')
                ,id: 'modx-resource-richtext'
                ,value:config.record.richtext !== undefined ? parseInt(config.record.richtext) : true
                ,checked:config.record.richtext  
            }]   
        }]
    }

    ,setup: function(){
        Articles.panel.Article.superclass.setup.call(this);

        var tagField = this.find('xtype', 'bxr-field-tags');

        if(tagField.length > 0){
            tagField = tagField[0];

            tagField.disable();
            tagField.setFieldValue(_('bxr.loading') + '...');
            tagField.setValue(this.config.record.tags);

            MODx.Ajax.request({
                url: Articles.connector_url
                ,params: {
                    action: 'extras/gettags'
                    ,container: this.config.record['parent']
                }
                ,listeners: {
                    'success':{fn:function(r){
                        tagField.store = new Ext.data.ArrayStore({
                            autoDestroy: true,
                            storeId: 'autoCompleteStore',
                            idIndex: 0,
                            fields: ['tag'],
                            data: r.object
                        });

                        tagField.setFieldValue();
                        tagField.enable();

                    },scope:this}
                }
            });
        }
    }

    ,beforeSubmit: function(o) {
        var d = {};

        var tags = this.find('xtype', 'bxr-field-tags')[0];
        if(tags) {d.tags = tags.getValue()}

        Ext.apply(o.form.baseParams,d);
    }
});
Ext.reg('modx-panel-article',Articles.panel.Article);

Any ideas how to tweak those files to get only a list of a specific user-group (or users by ID), anyone?

1 Like

Indeed, you may well be right. This looks promising, right after the halfway point of your code

        xtype: MODx.config.publish_document ? 'modx-combo-user' : 'hidden' 
                 //xtype: 'hidden'
                ,fieldLabel: _('resource_createdby')
                ,description: '<b>[[*createdby]]</b><br />'+_('resource_createdby_help')
                ,name: 'created_by'
                ,hiddenName: 'createdby'
                ,id: 'modx-resource-createdby'
                ,allowBlank: true
                ,width: 300
                ,value: config.record.createdby || MODx.user.id

HTH, but to be frank working from js side could be tough. Sure, you could cut it completely, but afaik you may have trouble to customize that output the way you want.

How does it work with these snippets, is the output controlled or styled by any chunk or anything? Just raw output?

Hmm maybe a chunk is used instead of backend tpl files, that would make a lot of sense, bringing the tpls into chunks makes them much more accessible

It also seems strange to output as a dropdown menu, why on earth really.

You are 100X the programmer I am, but personally I would probably just go around the js by removing the code above, and putting in a little bit of html to get rid of the dropdown. Or maybe make the js output here hidden.

The value

[[*createdby]]

should be available to you on the page, to output with simple html, no dropbox…idk just spitballing here

I don’t think that’s the code for the drop-down, but you may be close.

Many of the Articles TPLs are available in the various tabs when doing “Manage Articles.” I always have trouble finding them, though.

1 Like

Yes I agree, the style of the dropdown must be somewhere else, I myself have no idea where, actually that will be in the css, no? Sorry I am a bit out of my league here.

But it is, isn’t it, the place the js adds created by user X…

Replacing it with a custom snippet would mean finding the “code” for the drop-down (it could be a tag or even just an HTML identifier that’s replaced using JS code) and replacing it with a tag for your snippet. That means finding the Tpl chunk (which may not be a literal chunk – I think some are hard-coded in JS or PHP code, and some are in the Tpls available on the tabs for Manage Articles. I always have trouble finding them.

I would look for you, but I don’t have time right now. I’ve often planned do to a blog post on where they all are, but I never seem to get around to it.

1 Like

Right, so its back to the tpl files. They are always a little unwieldy at best.

This is interesting to me, and its html, the breaks will be below each level so they sit properly, can a snippet pass through this unparsed?

,fieldLabel: _('resource_createdby')
                ,description: '<b>[[*createdby]]</b><br />'+_('resource_createdby_help')
                ,name: 'created_by'

also, what’s the created by help? Could that possibly be the dropdown? Its so strange to have a list there

Thanks for all the help, I really appreciate the efforts. I did some more digging, but I couldn’t find anything useful so far. I started again by checking the manager output via the dev console and it seems, this user dropdown isn’t even a dropdown and most likely generated through .js or .php:

<div class="x-form-item x-tab-item" tabindex="-1" id="ext-gen215">	
  <label for="modx-resource-createdby" style="width:auto;" class="x-form-item-label" id="ext-gen216">Created By:</label>
	
  <div class="x-form-element" id="x-form-el-modx-resource-createdby" style="padding-left:0;">	  
    <div class="x-form-field-wrap x-form-field-trigger-wrap x-trigger-wrap-focus" id="ext-gen217" style="width: 298px;">
		
      <input type="hidden" name="createdby" id="ext-gen219" value="4">
		
      <input type="text" size="24" autocomplete="off" id="modx-resource-createdby" class="x-form-text x-form-field modx-combo x-form-focus" style="width: 260px;" title="">
		
      <div class="x-form-trigger x-form-arrow-trigger" id="ext-gen218"></div>
		
    </div>	  
  </div>
	
  <div class="x-form-clear-left"></div>
</div>


// following is only displayed after the (fake) dropdown field is clicked:

<div class="x-layer x-combo-list  x-resizable-pinned" id="ext-gen357" style="position: absolute; z-index: 12007; visibility: visible; left: 1824px; top: 545px; width: 298px; height: 308px; font-size: 13px;">
  <div class="x-combo-list-inner" id="ext-gen358" style="width: 298px; height: 298px;">
    <div class="x-combo-list-item">username1</div>
    <div class="x-combo-list-item">username2</div>
    <div class="x-combo-list-item">username3</div>
  </div>
</div>

Another thing I realized: Isn’t the [[*createdby]] tag a MODX default placeholder? So wouldn’t this be created somewhere by MODX itself? I narrowed my searches always to Articles specific folders, but could it be, that Articles simply grabs this placeholder from MODX itself (so to speak)?

@joshualuckers I know, you are currently working on Articles, do you happen to know how I could achieve a limited user list?

1 Like

The dropdown is provided by the xtype modx-combo-user and the values are populated by calling the processor security/user/getlist.

1 Like