1

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:

  • The user should see every product where the his/her data matches companySelectionIds or the companySelectionIds array is empty.
  • The user should not see the products where his/her data matches with the companySelectionNOTIds array or doesn't match with the companySelectionIds
KDSN
  • 13
  • 3
  • I'm not sure what the `12` and `1` numbers are doing in this scenario. It seems like the important thing is that a user with certain company-related configurations only get pieces back that match those configurations. Can you share some code related to how you are setting up that company option field? – alexbea Dec 07 '20 at 16:30
  • @alexbea thank you for your comment, I adjusted my post and added more informations to it. I hope it is now much more clearer, if not please don't hesitate ask for more and I will try to give more Informations. Best regards. – KDSN Dec 08 '20 at 10:28

2 Answers2

0

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.

alexbea
  • 1,311
  • 14
  • 25
  • thank you very much ! This was exactly what I was needing. I basically just made criteria dynamic with a mongoDB Query that already gets everything I want. Currently testing it carefully and I would add a new comment or solve this question if everything works perfectly. Two small questions for a better understanding on my behalf: criteria is already filled with ids and sometimes there are none. This happens kinda random for me. Is there a reason for that? Also how is the workflow on an apostrophe-pieces-pages? What is loaded at first the construct of the page or the widgets cursor? – KDSN Dec 14 '20 at 11:40
  • `criteria` should have IDs in it only when you are using the pieces widget in the mode where you use the "Select... Individually" option. The widget is in an area on the page, so it is part of the page loading process. `construct` is run only when the app starts up, but there are other functions like `beforeShow` that run when pages are requested. – alexbea Dec 16 '20 at 21:52
0

Just to build on alexbea's answer, in case anyone needs a little further help. My criteria were based on MongoDB query notation as hinted at by the op KDSN.

I was trying to filter certain theatre performances by date to only show those that were upcoming or in progress. Here's what I came up with in the end [in my widget's index.js]:

  construct: function(self, options) {
    const today = moment().startOf('day').format('YYYY-MM-DD');
    self.widgetCursor = function(req) {
      const performancesToShow = {
        $or: [
          { $and: [ { datelineStart: { $gte: today } }, { datelineEnd: null } ] },
          { $and: [ { datelineStart: { $lte: today } }, { datelineEnd: { $gte: today } } ] }
        ]
      };
      var filters = self.filters || (self.joinById && self.joinById.filters);
      return self.pieces.find(req, performancesToShow).applyFilters(filters);
    };
  }
Levomatix
  • 21
  • 3