apostrophe-cms

Apostrophe CMS: filtering the Result of apostrophe-pieces-widgets


I have currently the two lines of Code in my Template

{{ apos.singleton(data.page, 'productsLimitedBy12', 'products',{...someoptions}) }}
...
{{ apos.singleton(data.page, 'singleproduct', 'products',{...someoptions}) }}

Both are the same widget that I declare with the help of 'apostrophe-pieces-widgets'.

I want to filter the loaded Pieces before apostrophe limits them to the number 12 and 1. If I filter the pieces on self.load, I will only have 12 - filtered_pieces. But the amount should be static and only less than 12 if there are less than 12 pieces in the DB that fit the blacklist and whitelist criteria.

Basically I want to have a black- and whitelist for pieces, with the options "visible for XYZ, not visible for XYZ, visible for all"

Example:

  1. User ABC, that has the following properties in an array "company1, company2" should see every piece that is either specified to be visible for company1 or company2 or pieces that are visible for everyone.
  2. User DEF, that has the following properties in an array "notCompany1, notCompany2", should see every piece that are not from company1 or company2.

How can I achieve that? Is it possible to include a filter that will be added to the query every time the pieces from a specific widget were requested?

EDIT-1:

For specifying what I exactly mean with the number 1 or 12. If I am logged in with access to the context menu and click on the widget, I get the following options: Select (which can be all, by tag or individually) and maximum displayed. For the first product widget I want 12 pieces for the other only 1.

If you meant the strings in the code, these were just for understanding that I have two lines where I have the same widgets but with different limits, I adjusted it for a better understanding.

To quickly summarize my Workflow:

  1. I give some products through the context menu some whitelisted and blacklisted companies
  2. These companies are added to the piece through a join
  3. I filter the pieces in the products-widget/index.js; But the problem is that the filtering happens after apostrophe chose the 12 or 1 piece(s). I want to filter and then apostrophe to limit the pieces.

Here is my product/index.js (trimmed of course):

module.exports = {
    extend: 'apostrophe-pieces',
    name: 'product',
    label: 'product',
    pluralLabel: 'products',
    contextual: true,
    addFields: [
        {
            name: 'title',
            label: 'Title',
            type: 'string',
            required: true
        },
        {
            name: '_companySelection',
            withType: 'company',
            label: 'Company-Selection',
            type: 'joinByArray',
            filters: {
                areas: false,
                joins: false,
                projection: {
                    _id: 1,
                    title: 1
                }
            }
        },
        {
            name: '_companySelectionNOT',
            withType: 'company',
            label: 'Company-Selection NOT',
            type: 'joinByArray',
            filters: {
                areas: false,
                joins: false,
                projection: {
                    _id: 1,
                    title: 1
                }
            }
        },

    ],

    arrangeFields: [
        {}
    ],
    construct: function (self, options) {
        self.beforeSave = function (req, piece, options, callback) {
            // foo()
            return callback();
        };
    }
};

Here is my database Object saved in aposDocs (trimmed of course):

{
    "_id": "ckfwbczu5002035ziaa8a2pzp",
    "priority": 100000,
    "validFrom": "2020-10-01",
    "validUntil": "2020-12-31",
    "trash": false,
    "title": "EXAMPLE",
    "createdAt": {
        "$date": "2020-10-05T09:10:16.685Z"
    },
    "updatedAt": {
        "$date": "2020-12-04T11:21:29.692Z"
    },
    "companySelectionIds": ['ck2q9raez01i04kous6fo59hj'],
    "companySelectionNOTIds": ["cjn0e31up02ydm8otk4h4uhwq"],

}

And here is my products-widgets/index.js (trimmed of course)

module.exports = {
    extend: 'apostrophe-pieces-widgets',
    filters: {
        projection: {
            ...projectobj
        }
    },
    label: 'Product Widget',
    alias: 'products',
    construct: function (self, options) {
        var superLoad = self.load;
        self.addHelpers({...helpers})
        self.load = function (req, widgets, callback) {
            var currentUser = req.data.user;
            return superLoad(req, widgets, function (err) {
                if (err) return callback(err);
                else {
                    widgets._pieces = filtered(user,widgets._pieces);
                    callback()
                }
            });
        };
    }
};

Also for a quick understanding regarding my cases:


Solution

  • I think using self.widgetCursor might be how I would try to achieve this. You wouldn't need to do anything with self.load at all since you need the filtering to be done before loading has begun. It needs to happen in the cursor.

    widgetCursor looks like:

    self.widgetCursor = function(req, criteria) {
      var filters = self.filters || (self.joinById && self.joinById.filters);
      return self.pieces.find(req, criteria).applyFilters(filters);
    };
    

    You should have req.user available to get the user info similar to:

    // Possibly could be `self.apos.users.find`
    var userCursor = self.apos.docs.getManager('user').find(req, { _id: req.user._id }, {
      _companySelection: 1,
      _companySelectionNOT: 1
    });
    
    const user = userCursor.toObject();
    

    I'm not 100% sure that's exactly right, but I'm pretty sure it's close enough to get started. Once you have the user data it can be applied to the criteria object in self.widgetCursor, either through the super pattern or overriding it completely.