Plugin Directory

Changeset 2670662


Ignore:
Timestamp:
02/01/2022 03:05:45 PM (4 years ago)
Author:
shoppingfeed
Message:

Update to version 6.1.0 from GitHub

Location:
shopping-feed
Files:
2 added
52 edited
1 copied

Legend:

Unmodified
Added
Removed
  • shopping-feed/tags/6.1.0/assets/css/app.css

    r2384546 r2670662  
    7171  cursor: inherit; }
    7272
     73.sf__inline {
     74  display: inline-block;
     75  margin-right: 0; }
     76
     77.sf__table tr:first-child button {
     78  display: none; }
     79
     80.sf__table td {
     81  padding-left: 0; }
     82
    7383.sf__logo {
    7484  overflow: hidden;
     
    95105    background-color: #19a088; }
    96106
    97 .sf__button__logout.button {
     107.sf__button__secondary.button, .sf__button__logout.button {
    98108  color: #fff;
    99109  text-transform: uppercase;
    100110  border-color: #632F8E;
    101111  background-color: #632F8E; }
    102   .sf__button__logout.button:hover, .sf__button__logout.button:active, .sf__button__logout.button:focus {
     112  .sf__button__secondary.button:hover, .sf__button__secondary.button:active, .sf__button__secondary.button:focus, .sf__button__logout.button:hover, .sf__button__logout.button:active, .sf__button__logout.button:focus {
    103113    color: #fff;
    104114    border-color: #56297b;
     
    222232      flex-basis: 33%; } }
    223233
    224 /*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLmNzcyIsInNvdXJjZXMiOlsiYXBwLnNjc3MiLCJtYWluL19taXhpbnMuc2NzcyIsIm1haW4vX2NvbG9ycy5zY3NzIiwibXVsdGkvX211bHRpLnNjc3MiLCJtYWluL19zZi5zY3NzIl0sInNvdXJjZXNDb250ZW50IjpbIkBpbXBvcnQgXCJtYWluL21peGluc1wiO1xuQGltcG9ydCBcIm1haW4vY29sb3JzXCI7XG5AaW1wb3J0IFwibXVsdGkvbXVsdGlcIjtcbkBpbXBvcnQgXCJtYWluL3NmXCI7IiwiLy8gQm91cmJvbidzIGVzc2VudGlhbHMgbWl4aW5zXG4kZW0tYmFzZTogMTZweCAhZGVmYXVsdDtcbi8vIFN0cmlwIHVuaXRzXG5AZnVuY3Rpb24gc3RyaXAtdW5pdHMoJHZhbHVlKSB7XG5cdEByZXR1cm4gKCR2YWx1ZSAvICgkdmFsdWUgKiAwICsgMSkpO1xufVxuLy8gUFggdG8gRU1cbkBmdW5jdGlvbiBlbSgkcHh2YWwsICRiYXNlOiAkZW0tYmFzZSkge1xuXHRAaWYgbm90IHVuaXRsZXNzKCRweHZhbCkge1xuXHQgICRweHZhbDogc3RyaXAtdW5pdHMoJHB4dmFsKTtcblx0fVxuXHRAaWYgbm90IHVuaXRsZXNzKCRiYXNlKSB7XG5cdCAgJGJhc2U6IHN0cmlwLXVuaXRzKCRiYXNlKTtcblx0fVxuXHRAcmV0dXJuICgkcHh2YWwgLyAkYmFzZSkgKiAxZW07XG59XG4vL0JyZWFrcG9pbnRzXG4kYnJlYWtwb2ludHM6IChcbiAgICB4c206IGVtKDQ4MCksXG4gICAgc206IGVtKDc2OCksXG4gICAgbWQ6IGVtKDEwMjQpLFxuICAgIGxnOiBlbSgxMjYwKSxcbiAgICB4bGc6IGVtKDE0NDApXG4pO1xuXG5AbWl4aW4gYnJlYWtwb2ludHMoJGJyZWFrcG9pbnQpIHtcbiAgICBAbWVkaWEgc2NyZWVuIGFuZCAobWluLXdpZHRoOiBtYXAtZ2V0KCRtYXA6ICRicmVha3BvaW50cywgJGtleTogJGJyZWFrcG9pbnQpKSB7XG4gICAgICAgIEBjb250ZW50O1xuICAgIH1cbn1cbi8vIEhvdmVyIEFjdGl2ZSBGb2N1cyBwc2V1ZG8gc2VsZWN0b3IgbWl4aW5cbkBtaXhpbiBob3ZlciB7XG5cdCY6aG92ZXIsICY6YWN0aXZlLCAmOmZvY3VzIHtcblx0XHRAY29udGVudDtcblx0fVxufVxuLy8gU21vb3RoIHRleHRcbkBtaXhpbiBzbW9vdGhUZXh0IHtcblx0LXdlYmtpdC1mb250LXNtb290aGluZzogYW50aWFsaWFzZWQ7XG4gICAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbn0iLCIkd2hpdGU6ICNmZmY7XG4kYmxhY2s6ICMwMDA7XG4kcHJpbWFyeTogIzYzMkY4RTtcbiRzZWNvbmRhcnk6ICMxQ0I2OUI7XG4kdGV4dGNvbG9yOiAjMjMyODJEO1xuJGdyYXk6ICM5RTlFOUU7XG4kYmx1ZTogIzA0Mjc0RDtcbiRwaW5rOiAjRTkxRTYzO1xuIiwiLm11bHRpLXdyYXBwZXIge1xuICAgIGJvcmRlcjogMXB4IHNvbGlkICNjY2M7XG4gICAgYm9yZGVyLXJhZGl1czogM3B4O1xuICAgIHdpZHRoOiAxMDAlO1xufVxuXG4ubXVsdGktd3JhcHBlciAubm9uLXNlbGVjdGVkLXdyYXBwZXIsXG4ubXVsdGktd3JhcHBlciAuc2VsZWN0ZWQtd3JhcHBlciB7XG4gICAgYm94LXNpemluZzogYm9yZGVyLWJveDtcbiAgICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG4gICAgaGVpZ2h0OiAyMDBweDtcbiAgICBvdmVyZmxvdy15OiBzY3JvbGw7XG4gICAgcGFkZGluZzogMTBweDtcbiAgICB2ZXJ0aWNhbC1hbGlnbjogdG9wO1xuICAgIHdpZHRoOiA1MCU7XG59XG5cbi5tdWx0aS13cmFwcGVyIC5ub24tc2VsZWN0ZWQtd3JhcHBlciB7XG4gICAgYmFja2dyb3VuZDogI2ZhZmFmYTtcbiAgICBib3JkZXItcmlnaHQ6IDFweCBzb2xpZCAjY2NjO1xufVxuXG4ubXVsdGktd3JhcHBlciAuc2VsZWN0ZWQtd3JhcHBlciB7XG4gICAgYmFja2dyb3VuZDogI2ZmZjtcbn1cblxuLm11bHRpLXdyYXBwZXIgLmhlYWRlciB7XG4gICAgY29sb3I6ICM0ZjRmNGY7XG4gICAgY3Vyc29yOiBkZWZhdWx0O1xuICAgIGZvbnQtd2VpZ2h0OiBib2xkO1xuICAgIG1hcmdpbi1ib3R0b206IDVweDtcbiAgICBwYWRkaW5nOiA1cHggMTBweDtcbn1cblxuLm11bHRpLXdyYXBwZXIgLml0ZW0ge1xuICAgIGN1cnNvcjogcG9pbnRlcjtcbiAgICBkaXNwbGF5OiBibG9jaztcbiAgICBwYWRkaW5nOiA1cHggMTBweDtcbn1cblxuLm11bHRpLXdyYXBwZXIgLml0ZW06aG92ZXIge1xuICAgIGJhY2tncm91bmQ6ICNlY2VjZWM7XG4gICAgYm9yZGVyLXJhZGl1czogMnB4O1xufVxuXG4ubXVsdGktd3JhcHBlciAuaXRlbS1ncm91cCB7XG4gICAgcGFkZGluZzogNXB4IDEwcHg7XG59XG5cbi5tdWx0aS13cmFwcGVyIC5pdGVtLWdyb3VwIC5ncm91cC1sYWJlbCB7XG4gICAgZGlzcGxheTogYmxvY2s7XG4gICAgZm9udC1zaXplOiAwLjg3NXJlbTtcbiAgICBvcGFjaXR5OiAwLjU7XG4gICAgcGFkZGluZzogNXB4IDA7XG59XG5cbi5tdWx0aS13cmFwcGVyIC5zZWFyY2gtaW5wdXQge1xuICAgIGJvcmRlcjogMDtcbiAgICBib3JkZXItYm90dG9tOiAxcHggc29saWQgI2NjYztcbiAgICBib3JkZXItcmFkaXVzOiAwO1xuICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgIGZvbnQtc2l6ZTogMWVtO1xuICAgIG1hcmdpbjogMDtcbiAgICBvdXRsaW5lOiAwO1xuICAgIHBhZGRpbmc6IDEwcHggMjBweDtcbiAgICB3aWR0aDogMTAwJTtcbiAgICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xufVxuXG4ubXVsdGktd3JhcHBlciAubm9uLXNlbGVjdGVkLXdyYXBwZXIgLml0ZW0uc2VsZWN0ZWQge1xuICAgIG9wYWNpdHk6IDAuNTtcbn1cblxuLm11bHRpLXdyYXBwZXIgLm5vbi1zZWxlY3RlZC13cmFwcGVyIC5pdGVtLmRpc2FibGVkLFxuLm11bHRpLXdyYXBwZXIgLnNlbGVjdGVkLXdyYXBwZXIgLml0ZW0uZGlzYWJsZWQge1xuICAgIG9wYWNpdHk6IDAuNTtcbiAgICB0ZXh0LWRlY29yYXRpb246IGxpbmUtdGhyb3VnaDtcbn1cblxuLm11bHRpLXdyYXBwZXIgLm5vbi1zZWxlY3RlZC13cmFwcGVyIC5pdGVtLmRpc2FibGVkOmhvdmVyLFxuLm11bHRpLXdyYXBwZXIgLnNlbGVjdGVkLXdyYXBwZXIgLml0ZW0uZGlzYWJsZWQ6aG92ZXIge1xuICAgIGJhY2tncm91bmQ6IGluaGVyaXQ7XG4gICAgY3Vyc29yOiBpbmhlcml0O1xufSIsIi5zZiB7XG5cbiAgJl9fbG9nbyB7XG4gICAgb3ZlcmZsb3c6IGhpZGRlbjtcbiAgICB0ZXh0LWluZGVudDogLTk5OTk5cHg7XG4gICAgYmFja2dyb3VuZDogdXJsKFwiLi4vaW1hZ2VzL3NmX2xvZ28uc3ZnXCIpIG5vLXJlcGVhdCBjZW50ZXIgbGVmdDtcbiAgICBiYWNrZ3JvdW5kLXNpemU6IGNvbnRhaW47XG4gICAgbWF4LXdpZHRoOiAyOTdweDtcbiAgICBoZWlnaHQ6IDUzcHg7XG4gIH1cblxuICAmX192ZXJzaW9uIHtcbiAgICBjb2xvcjogJGdyYXk7XG4gICAgZm9udC1zaXplOiAxMnB4O1xuICAgIGxpbmUtaGVpZ2h0OiAxN3B4O1xuICAgIG1hcmdpbjogNXB4IDA7XG4gIH1cblxuICAmX19idXR0b24uYnV0dG9uIHtcbiAgICBjb2xvcjogJHdoaXRlO1xuICAgIHRleHQtdHJhbnNmb3JtOiB1cHBlcmNhc2U7XG4gICAgYm9yZGVyLWNvbG9yOiAkc2Vjb25kYXJ5O1xuICAgIGJhY2tncm91bmQtY29sb3I6ICRzZWNvbmRhcnk7XG4gICAgQGluY2x1ZGUgaG92ZXIge1xuICAgICAgY29sb3I6ICR3aGl0ZTtcbiAgICAgIGJvcmRlci1jb2xvcjogZGFya2VuKCRzZWNvbmRhcnksIDUlKTtcbiAgICAgIGJhY2tncm91bmQtY29sb3I6IGRhcmtlbigkc2Vjb25kYXJ5LCA1JSk7XG4gICAgfVxuICB9XG5cbiAgJl9fYnV0dG9uX19sb2dvdXQuYnV0dG9uIHtcbiAgICBjb2xvcjogJHdoaXRlO1xuICAgIHRleHQtdHJhbnNmb3JtOiB1cHBlcmNhc2U7XG4gICAgYm9yZGVyLWNvbG9yOiAkcHJpbWFyeTtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAkcHJpbWFyeTtcbiAgICBAaW5jbHVkZSBob3ZlciB7XG4gICAgICBjb2xvcjogJHdoaXRlO1xuICAgICAgYm9yZGVyLWNvbG9yOiBkYXJrZW4oJHByaW1hcnksIDUlKTtcbiAgICAgIGJhY2tncm91bmQtY29sb3I6IGRhcmtlbigkcHJpbWFyeSwgNSUpO1xuICAgIH1cbiAgfVxuXG4gICZfX3JlcXVpcmVtZW50cyB7XG4gICAgLnN1Y2Nlc3MsXG4gICAgLmZhaWxlZCB7XG4gICAgICBAaW5jbHVkZSBzbW9vdGhUZXh0O1xuICAgICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgICAgcGFkZGluZy1sZWZ0OiAyNnB4O1xuICAgICAgY29sb3I6ICR0ZXh0Y29sb3I7XG5cbiAgICAgICY6YmVmb3JlIHtcbiAgICAgICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgICAgICB0b3A6IDA7XG4gICAgICAgIGxlZnQ6IDA7XG4gICAgICAgIGZvbnQtZmFtaWx5OiBkYXNoaWNvbnM7XG4gICAgICAgIGNvbnRlbnQ6IFwiXFxmMTQ3XCI7XG4gICAgICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgICAgICB3aWR0aDogMTZweDtcbiAgICAgICAgaGVpZ2h0OiAxNnB4O1xuICAgICAgICBsaW5lLWhlaWdodDogMTZweDtcbiAgICAgICAgZm9udC1zaXplOiAxNHB4O1xuICAgICAgICBib3JkZXItcmFkaXVzOiA1MCU7XG4gICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjtcbiAgICAgICAgY29sb3I6ICR3aGl0ZTtcbiAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogJHNlY29uZGFyeTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAuZmFpbGVkIHtcbiAgICAgIGNvbG9yOiAkdGV4dGNvbG9yO1xuXG4gICAgICAmOmJlZm9yZSB7XG4gICAgICAgIGNvbnRlbnQ6IFwiXFxmMzM1XCI7XG4gICAgICAgIGJhY2tncm91bmQtY29sb3I6ICRncmF5O1xuICAgICAgfVxuICAgIH1cblxuICB9XG5cbiAgJl9faGVybyB7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgIGJvcmRlcjogMDtcbiAgICBib3JkZXItcmFkaXVzOiAycHg7XG4gICAgYmFja2dyb3VuZDogbGluZWFyLWdyYWRpZW50KDE4MGRlZywgIzAzMjc0RCAwJSwgIzUyMzA4MSAxMDAlKTtcbiAgICBiYWNrZ3JvdW5kLXNpemU6IGNvdmVyO1xuICAgIHBhZGRpbmc6IDMwcHg7XG5cbiAgICAmOmJlZm9yZSB7XG4gICAgICBjb250ZW50OiBcIlwiO1xuICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICB6LWluZGV4OiAxO1xuICAgICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgICAgdG9wOiAwO1xuICAgICAgcmlnaHQ6IDMwcHg7XG4gICAgICBib3R0b206IDA7XG4gICAgICBsZWZ0OiAwO1xuICAgICAgYmFja2dyb3VuZDogdXJsKFwiLi4vaW1hZ2VzL3NmX21hcmtldGluZy5zdmdcIikgbm8tcmVwZWF0IGNlbnRlciByaWdodDtcbiAgICAgIHBvaW50ZXItZXZlbnRzOiBub25lO1xuICAgIH1cbiAgICAmLS10aXRsZXtcbiAgICAgIGZvbnQtc2l6ZTogMTZweDtcbiAgICAgIGxpbmUtaGVpZ2h0OiAyMnB4O1xuICAgICAgcGFkZGluZy1yaWdodDogMzBweDtcbiAgICAgIGNvbG9yOiAkd2hpdGU7XG4gICAgICBmb250LXdlaWdodDogYm9sZDtcbiAgICAgIG1hcmdpbi1ib3R0b206IDE1cHg7XG4gICAgfVxuICB9XG5cbiAgJl9fbWFya2V0aW5nIHtcbiAgICBib3JkZXItbGVmdDogMXB4IHNvbGlkICRwcmltYXJ5O1xuICAgIHBhZGRpbmctbGVmdDogMjZweDtcblxuICAgICYtLXRpdGxle1xuICAgICAgY29sb3I6ICRwcmltYXJ5O1xuICAgICAgZm9udC1zaXplOiAxLjVlbTtcbiAgICAgIG1hcmdpbjogMDtcbiAgICAgIG1hcmdpbi10b3A6IDFlbTtcbiAgICB9XG4gICAgJi0tc3VidGl0bGV7XG4gICAgICBjb2xvcjogJGdyYXk7XG4gICAgICBmb250LXNpemU6IDEwcHg7XG4gICAgICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlO1xuICAgICAgbGV0dGVyLXNwYWNpbmc6IDMuMzNweDtcbiAgICAgIG1hcmdpbjogMDtcbiAgICAgIHBhZGRpbmctdG9wOiA1cHg7XG4gICAgfVxuICAgICYtLXRleHR7XG4gICAgICBjb2xvcjogJHRleHRjb2xvcjtcbiAgICAgIGZvbnQtc2l6ZTogMTJweDtcbiAgICAgIGxldHRlci1zcGFjaW5nOiAwO1xuICAgICAgbGluZS1oZWlnaHQ6IDE3cHg7XG4gICAgfVxuXG4gICAgJi0tZG93bmxvYWQge1xuICAgICAgcGFkZGluZzogMWVtIDA7XG5cbiAgICAgIGxpIHtcbiAgICAgICAgbGluZS1oZWlnaHQ6IDEuNWVtO1xuICAgICAgfVxuXG4gICAgICBhIHtcbiAgICAgICAgQGluY2x1ZGUgc21vb3RoVGV4dDtcbiAgICAgICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgICAgICBwYWRkaW5nLWxlZnQ6IDI2cHg7XG4gICAgICAgIGNvbG9yOiAkYmx1ZTtcbiAgICAgICAgZm9udC13ZWlnaHQ6IGJvbGQ7XG5cbiAgICAgICAgJjpiZWZvcmUge1xuICAgICAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAgICAgICB0b3A6IDA7XG4gICAgICAgICAgbGVmdDogMDtcbiAgICAgICAgICBmb250LWZhbWlseTogZGFzaGljb25zO1xuICAgICAgICAgIGNvbnRlbnQ6IFwiXFxmMzQ2XCI7XG4gICAgICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICAgICAgd2lkdGg6IDE2cHg7XG4gICAgICAgICAgaGVpZ2h0OiAxNnB4O1xuICAgICAgICAgIGxpbmUtaGVpZ2h0OiAxNnB4O1xuICAgICAgICAgIGZvbnQtc2l6ZTogMTRweDtcbiAgICAgICAgICBib3JkZXItcmFkaXVzOiA1MCU7XG4gICAgICAgICAgdGV4dC1hbGlnbjogY2VudGVyO1xuICAgICAgICAgIGNvbG9yOiAkd2hpdGU7XG4gICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogJHByaW1hcnk7XG4gICAgICAgIH1cblxuICAgICAgICAmOmhvdmVyIHtcbiAgICAgICAgICBjb2xvcjogJHByaW1hcnk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAmX19wbHVnaW57XG4gICAgLnNmX19ub3RpY2UtLXN0YXJ0IGF7XG4gICAgICBkaXNwbGF5OiBub25lO1xuICAgIH1cbiAgfVxuXG5cbiAgQGluY2x1ZGUgYnJlYWtwb2ludHMobWQpIHtcbiAgICAmX19jb2x1bW5zIHtcbiAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICBmbGV4LXdyYXA6IHdyYXA7XG4gICAgfVxuICAgICZfX2NvbHVtbiB7XG4gICAgICBmbGV4LWJhc2lzOiA2NiU7XG5cbiAgICAgICY6bGFzdC1jaGlsZCB7XG4gICAgICAgIGZsZXgtYmFzaXM6IDMzJTtcbiAgICAgIH1cbiAgICB9XG5cbiAgfVxufVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBR0FBLEFBQUEsY0FBYyxDQUFDO0VBQ1gsTUFBTSxFQUFFLGNBQWM7RUFDdEIsYUFBYSxFQUFFLEdBQUc7RUFDbEIsS0FBSyxFQUFFLElBQUksR0FDZDs7QUFFRCxBQUFBLGNBQWMsQ0FBQyxxQkFBcUI7QUFDcEMsY0FBYyxDQUFDLGlCQUFpQixDQUFDO0VBQzdCLFVBQVUsRUFBRSxVQUFVO0VBQ3RCLE9BQU8sRUFBRSxZQUFZO0VBQ3JCLE1BQU0sRUFBRSxLQUFLO0VBQ2IsVUFBVSxFQUFFLE1BQU07RUFDbEIsT0FBTyxFQUFFLElBQUk7RUFDYixjQUFjLEVBQUUsR0FBRztFQUNuQixLQUFLLEVBQUUsR0FBRyxHQUNiOztBQUVELEFBQUEsY0FBYyxDQUFD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 */
     234/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLmNzcyIsInNvdXJjZXMiOlsiYXBwLnNjc3MiLCJtYWluL19taXhpbnMuc2NzcyIsIm1haW4vX2NvbG9ycy5zY3NzIiwibXVsdGkvX211bHRpLnNjc3MiLCJtYWluL19zZi5zY3NzIl0sInNvdXJjZXNDb250ZW50IjpbIkBpbXBvcnQgXCJtYWluL21peGluc1wiO1xuQGltcG9ydCBcIm1haW4vY29sb3JzXCI7XG5AaW1wb3J0IFwibXVsdGkvbXVsdGlcIjtcbkBpbXBvcnQgXCJtYWluL3NmXCI7IiwiLy8gQm91cmJvbidzIGVzc2VudGlhbHMgbWl4aW5zXG4kZW0tYmFzZTogMTZweCAhZGVmYXVsdDtcbi8vIFN0cmlwIHVuaXRzXG5AZnVuY3Rpb24gc3RyaXAtdW5pdHMoJHZhbHVlKSB7XG5cdEByZXR1cm4gKCR2YWx1ZSAvICgkdmFsdWUgKiAwICsgMSkpO1xufVxuLy8gUFggdG8gRU1cbkBmdW5jdGlvbiBlbSgkcHh2YWwsICRiYXNlOiAkZW0tYmFzZSkge1xuXHRAaWYgbm90IHVuaXRsZXNzKCRweHZhbCkge1xuXHQgICRweHZhbDogc3RyaXAtdW5pdHMoJHB4dmFsKTtcblx0fVxuXHRAaWYgbm90IHVuaXRsZXNzKCRiYXNlKSB7XG5cdCAgJGJhc2U6IHN0cmlwLXVuaXRzKCRiYXNlKTtcblx0fVxuXHRAcmV0dXJuICgkcHh2YWwgLyAkYmFzZSkgKiAxZW07XG59XG4vL0JyZWFrcG9pbnRzXG4kYnJlYWtwb2ludHM6IChcbiAgICB4c206IGVtKDQ4MCksXG4gICAgc206IGVtKDc2OCksXG4gICAgbWQ6IGVtKDEwMjQpLFxuICAgIGxnOiBlbSgxMjYwKSxcbiAgICB4bGc6IGVtKDE0NDApXG4pO1xuXG5AbWl4aW4gYnJlYWtwb2ludHMoJGJyZWFrcG9pbnQpIHtcbiAgICBAbWVkaWEgc2NyZWVuIGFuZCAobWluLXdpZHRoOiBtYXAtZ2V0KCRtYXA6ICRicmVha3BvaW50cywgJGtleTogJGJyZWFrcG9pbnQpKSB7XG4gICAgICAgIEBjb250ZW50O1xuICAgIH1cbn1cbi8vIEhvdmVyIEFjdGl2ZSBGb2N1cyBwc2V1ZG8gc2VsZWN0b3IgbWl4aW5cbkBtaXhpbiBob3ZlciB7XG5cdCY6aG92ZXIsICY6YWN0aXZlLCAmOmZvY3VzIHtcblx0XHRAY29udGVudDtcblx0fVxufVxuLy8gU21vb3RoIHRleHRcbkBtaXhpbiBzbW9vdGhUZXh0IHtcblx0LXdlYmtpdC1mb250LXNtb290aGluZzogYW50aWFsaWFzZWQ7XG4gICAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbn0iLCIkd2hpdGU6ICNmZmY7XG4kYmxhY2s6ICMwMDA7XG4kcHJpbWFyeTogIzYzMkY4RTtcbiRzZWNvbmRhcnk6ICMxQ0I2OUI7XG4kdGV4dGNvbG9yOiAjMjMyODJEO1xuJGdyYXk6ICM5RTlFOUU7XG4kYmx1ZTogIzA0Mjc0RDtcbiRwaW5rOiAjRTkxRTYzO1xuIiwiLm11bHRpLXdyYXBwZXIge1xuICAgIGJvcmRlcjogMXB4IHNvbGlkICNjY2M7XG4gICAgYm9yZGVyLXJhZGl1czogM3B4O1xuICAgIHdpZHRoOiAxMDAlO1xufVxuXG4ubXVsdGktd3JhcHBlciAubm9uLXNlbGVjdGVkLXdyYXBwZXIsXG4ubXVsdGktd3JhcHBlciAuc2VsZWN0ZWQtd3JhcHBlciB7XG4gICAgYm94LXNpemluZzogYm9yZGVyLWJveDtcbiAgICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG4gICAgaGVpZ2h0OiAyMDBweDtcbiAgICBvdmVyZmxvdy15OiBzY3JvbGw7XG4gICAgcGFkZGluZzogMTBweDtcbiAgICB2ZXJ0aWNhbC1hbGlnbjogdG9wO1xuICAgIHdpZHRoOiA1MCU7XG59XG5cbi5tdWx0aS13cmFwcGVyIC5ub24tc2VsZWN0ZWQtd3JhcHBlciB7XG4gICAgYmFja2dyb3VuZDogI2ZhZmFmYTtcbiAgICBib3JkZXItcmlnaHQ6IDFweCBzb2xpZCAjY2NjO1xufVxuXG4ubXVsdGktd3JhcHBlciAuc2VsZWN0ZWQtd3JhcHBlciB7XG4gICAgYmFja2dyb3VuZDogI2ZmZjtcbn1cblxuLm11bHRpLXdyYXBwZXIgLmhlYWRlciB7XG4gICAgY29sb3I6ICM0ZjRmNGY7XG4gICAgY3Vyc29yOiBkZWZhdWx0O1xuICAgIGZvbnQtd2VpZ2h0OiBib2xkO1xuICAgIG1hcmdpbi1ib3R0b206IDVweDtcbiAgICBwYWRkaW5nOiA1cHggMTBweDtcbn1cblxuLm11bHRpLXdyYXBwZXIgLml0ZW0ge1xuICAgIGN1cnNvcjogcG9pbnRlcjtcbiAgICBkaXNwbGF5OiBibG9jaztcbiAgICBwYWRkaW5nOiA1cHggMTBweDtcbn1cblxuLm11bHRpLXdyYXBwZXIgLml0ZW06aG92ZXIge1xuICAgIGJhY2tncm91bmQ6ICNlY2VjZWM7XG4gICAgYm9yZGVyLXJhZGl1czogMnB4O1xufVxuXG4ubXVsdGktd3JhcHBlciAuaXRlbS1ncm91cCB7XG4gICAgcGFkZGluZzogNXB4IDEwcHg7XG59XG5cbi5tdWx0aS13cmFwcGVyIC5pdGVtLWdyb3VwIC5ncm91cC1sYWJlbCB7XG4gICAgZGlzcGxheTogYmxvY2s7XG4gICAgZm9udC1zaXplOiAwLjg3NXJlbTtcbiAgICBvcGFjaXR5OiAwLjU7XG4gICAgcGFkZGluZzogNXB4IDA7XG59XG5cbi5tdWx0aS13cmFwcGVyIC5zZWFyY2gtaW5wdXQge1xuICAgIGJvcmRlcjogMDtcbiAgICBib3JkZXItYm90dG9tOiAxcHggc29saWQgI2NjYztcbiAgICBib3JkZXItcmFkaXVzOiAwO1xuICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgIGZvbnQtc2l6ZTogMWVtO1xuICAgIG1hcmdpbjogMDtcbiAgICBvdXRsaW5lOiAwO1xuICAgIHBhZGRpbmc6IDEwcHggMjBweDtcbiAgICB3aWR0aDogMTAwJTtcbiAgICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xufVxuXG4ubXVsdGktd3JhcHBlciAubm9uLXNlbGVjdGVkLXdyYXBwZXIgLml0ZW0uc2VsZWN0ZWQge1xuICAgIG9wYWNpdHk6IDAuNTtcbn1cblxuLm11bHRpLXdyYXBwZXIgLm5vbi1zZWxlY3RlZC13cmFwcGVyIC5pdGVtLmRpc2FibGVkLFxuLm11bHRpLXdyYXBwZXIgLnNlbGVjdGVkLXdyYXBwZXIgLml0ZW0uZGlzYWJsZWQge1xuICAgIG9wYWNpdHk6IDAuNTtcbiAgICB0ZXh0LWRlY29yYXRpb246IGxpbmUtdGhyb3VnaDtcbn1cblxuLm11bHRpLXdyYXBwZXIgLm5vbi1zZWxlY3RlZC13cmFwcGVyIC5pdGVtLmRpc2FibGVkOmhvdmVyLFxuLm11bHRpLXdyYXBwZXIgLnNlbGVjdGVkLXdyYXBwZXIgLml0ZW0uZGlzYWJsZWQ6aG92ZXIge1xuICAgIGJhY2tncm91bmQ6IGluaGVyaXQ7XG4gICAgY3Vyc29yOiBpbmhlcml0O1xufSIsIi5zZiB7XG5cbiAgJl9faW5saW5le1xuICAgIGRpc3BsYXk6IGlubGluZS1ibG9jaztcbiAgICBtYXJnaW4tcmlnaHQ6IDA7XG4gIH1cblxuICAmX190YWJsZSB7XG4gICAgdHI6Zmlyc3QtY2hpbGQgYnV0dG9ue1xuICAgICAgZGlzcGxheTogbm9uZTtcbiAgICB9XG4gICAgdGQge1xuICAgICAgcGFkZGluZy1sZWZ0OiAwO1xuICAgIH1cbiAgfVxuXG4gICZfX2xvZ28ge1xuICAgIG92ZXJmbG93OiBoaWRkZW47XG4gICAgdGV4dC1pbmRlbnQ6IC05OTk5OXB4O1xuICAgIGJhY2tncm91bmQ6IHVybChcIi4uL2ltYWdlcy9zZl9sb2dvLnN2Z1wiKSBuby1yZXBlYXQgY2VudGVyIGxlZnQ7XG4gICAgYmFja2dyb3VuZC1zaXplOiBjb250YWluO1xuICAgIG1heC13aWR0aDogMjk3cHg7XG4gICAgaGVpZ2h0OiA1M3B4O1xuICB9XG5cbiAgJl9fdmVyc2lvbiB7XG4gICAgY29sb3I6ICRncmF5O1xuICAgIGZvbnQtc2l6ZTogMTJweDtcbiAgICBsaW5lLWhlaWdodDogMTdweDtcbiAgICBtYXJnaW46IDVweCAwO1xuICB9XG5cbiAgJl9fYnV0dG9uLmJ1dHRvbiB7XG4gICAgY29sb3I6ICR3aGl0ZTtcbiAgICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlO1xuICAgIGJvcmRlci1jb2xvcjogJHNlY29uZGFyeTtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAkc2Vjb25kYXJ5O1xuICAgIEBpbmNsdWRlIGhvdmVyIHtcbiAgICAgIGNvbG9yOiAkd2hpdGU7XG4gICAgICBib3JkZXItY29sb3I6IGRhcmtlbigkc2Vjb25kYXJ5LCA1JSk7XG4gICAgICBiYWNrZ3JvdW5kLWNvbG9yOiBkYXJrZW4oJHNlY29uZGFyeSwgNSUpO1xuICAgIH1cbiAgfVxuXG4gICZfX2J1dHRvbl9fc2Vjb25kYXJ5LmJ1dHRvbixcbiAgJl9fYnV0dG9uX19sb2dvdXQuYnV0dG9uIHtcbiAgICBjb2xvcjogJHdoaXRlO1xuICAgIHRleHQtdHJhbnNmb3JtOiB1cHBlcmNhc2U7XG4gICAgYm9yZGVyLWNvbG9yOiAkcHJpbWFyeTtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAkcHJpbWFyeTtcbiAgICBAaW5jbHVkZSBob3ZlciB7XG4gICAgICBjb2xvcjogJHdoaXRlO1xuICAgICAgYm9yZGVyLWNvbG9yOiBkYXJrZW4oJHByaW1hcnksIDUlKTtcbiAgICAgIGJhY2tncm91bmQtY29sb3I6IGRhcmtlbigkcHJpbWFyeSwgNSUpO1xuICAgIH1cbiAgfVxuXG4gICZfX3JlcXVpcmVtZW50cyB7XG4gICAgLnN1Y2Nlc3MsXG4gICAgLmZhaWxlZCB7XG4gICAgICBAaW5jbHVkZSBzbW9vdGhUZXh0O1xuICAgICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgICAgcGFkZGluZy1sZWZ0OiAyNnB4O1xuICAgICAgY29sb3I6ICR0ZXh0Y29sb3I7XG5cbiAgICAgICY6YmVmb3JlIHtcbiAgICAgICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgICAgICB0b3A6IDA7XG4gICAgICAgIGxlZnQ6IDA7XG4gICAgICAgIGZvbnQtZmFtaWx5OiBkYXNoaWNvbnM7XG4gICAgICAgIGNvbnRlbnQ6IFwiXFxmMTQ3XCI7XG4gICAgICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgICAgICB3aWR0aDogMTZweDtcbiAgICAgICAgaGVpZ2h0OiAxNnB4O1xuICAgICAgICBsaW5lLWhlaWdodDogMTZweDtcbiAgICAgICAgZm9udC1zaXplOiAxNHB4O1xuICAgICAgICBib3JkZXItcmFkaXVzOiA1MCU7XG4gICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjtcbiAgICAgICAgY29sb3I6ICR3aGl0ZTtcbiAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogJHNlY29uZGFyeTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAuZmFpbGVkIHtcbiAgICAgIGNvbG9yOiAkdGV4dGNvbG9yO1xuXG4gICAgICAmOmJlZm9yZSB7XG4gICAgICAgIGNvbnRlbnQ6IFwiXFxmMzM1XCI7XG4gICAgICAgIGJhY2tncm91bmQtY29sb3I6ICRncmF5O1xuICAgICAgfVxuICAgIH1cblxuICB9XG5cbiAgJl9faGVybyB7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgIGJvcmRlcjogMDtcbiAgICBib3JkZXItcmFkaXVzOiAycHg7XG4gICAgYmFja2dyb3VuZDogbGluZWFyLWdyYWRpZW50KDE4MGRlZywgIzAzMjc0RCAwJSwgIzUyMzA4MSAxMDAlKTtcbiAgICBiYWNrZ3JvdW5kLXNpemU6IGNvdmVyO1xuICAgIHBhZGRpbmc6IDMwcHg7XG5cbiAgICAmOmJlZm9yZSB7XG4gICAgICBjb250ZW50OiBcIlwiO1xuICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICB6LWluZGV4OiAxO1xuICAgICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgICAgdG9wOiAwO1xuICAgICAgcmlnaHQ6IDMwcHg7XG4gICAgICBib3R0b206IDA7XG4gICAgICBsZWZ0OiAwO1xuICAgICAgYmFja2dyb3VuZDogdXJsKFwiLi4vaW1hZ2VzL3NmX21hcmtldGluZy5zdmdcIikgbm8tcmVwZWF0IGNlbnRlciByaWdodDtcbiAgICAgIHBvaW50ZXItZXZlbnRzOiBub25lO1xuICAgIH1cbiAgICAmLS10aXRsZXtcbiAgICAgIGZvbnQtc2l6ZTogMTZweDtcbiAgICAgIGxpbmUtaGVpZ2h0OiAyMnB4O1xuICAgICAgcGFkZGluZy1yaWdodDogMzBweDtcbiAgICAgIGNvbG9yOiAkd2hpdGU7XG4gICAgICBmb250LXdlaWdodDogYm9sZDtcbiAgICAgIG1hcmdpbi1ib3R0b206IDE1cHg7XG4gICAgfVxuICB9XG5cbiAgJl9fbWFya2V0aW5nIHtcbiAgICBib3JkZXItbGVmdDogMXB4IHNvbGlkICRwcmltYXJ5O1xuICAgIHBhZGRpbmctbGVmdDogMjZweDtcblxuICAgICYtLXRpdGxle1xuICAgICAgY29sb3I6ICRwcmltYXJ5O1xuICAgICAgZm9udC1zaXplOiAxLjVlbTtcbiAgICAgIG1hcmdpbjogMDtcbiAgICAgIG1hcmdpbi10b3A6IDFlbTtcbiAgICB9XG4gICAgJi0tc3VidGl0bGV7XG4gICAgICBjb2xvcjogJGdyYXk7XG4gICAgICBmb250LXNpemU6IDEwcHg7XG4gICAgICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlO1xuICAgICAgbGV0dGVyLXNwYWNpbmc6IDMuMzNweDtcbiAgICAgIG1hcmdpbjogMDtcbiAgICAgIHBhZGRpbmctdG9wOiA1cHg7XG4gICAgfVxuICAgICYtLXRleHR7XG4gICAgICBjb2xvcjogJHRleHRjb2xvcjtcbiAgICAgIGZvbnQtc2l6ZTogMTJweDtcbiAgICAgIGxldHRlci1zcGFjaW5nOiAwO1xuICAgICAgbGluZS1oZWlnaHQ6IDE3cHg7XG4gICAgfVxuXG4gICAgJi0tZG93bmxvYWQge1xuICAgICAgcGFkZGluZzogMWVtIDA7XG5cbiAgICAgIGxpIHtcbiAgICAgICAgbGluZS1oZWlnaHQ6IDEuNWVtO1xuICAgICAgfVxuXG4gICAgICBhIHtcbiAgICAgICAgQGluY2x1ZGUgc21vb3RoVGV4dDtcbiAgICAgICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgICAgICBwYWRkaW5nLWxlZnQ6IDI2cHg7XG4gICAgICAgIGNvbG9yOiAkYmx1ZTtcbiAgICAgICAgZm9udC13ZWlnaHQ6IGJvbGQ7XG5cbiAgICAgICAgJjpiZWZvcmUge1xuICAgICAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAgICAgICB0b3A6IDA7XG4gICAgICAgICAgbGVmdDogMDtcbiAgICAgICAgICBmb250LWZhbWlseTogZGFzaGljb25zO1xuICAgICAgICAgIGNvbnRlbnQ6IFwiXFxmMzQ2XCI7XG4gICAgICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICAgICAgd2lkdGg6IDE2cHg7XG4gICAgICAgICAgaGVpZ2h0OiAxNnB4O1xuICAgICAgICAgIGxpbmUtaGVpZ2h0OiAxNnB4O1xuICAgICAgICAgIGZvbnQtc2l6ZTogMTRweDtcbiAgICAgICAgICBib3JkZXItcmFkaXVzOiA1MCU7XG4gICAgICAgICAgdGV4dC1hbGlnbjogY2VudGVyO1xuICAgICAgICAgIGNvbG9yOiAkd2hpdGU7XG4gICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogJHByaW1hcnk7XG4gICAgICAgIH1cblxuICAgICAgICAmOmhvdmVyIHtcbiAgICAgICAgICBjb2xvcjogJHByaW1hcnk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAmX19wbHVnaW57XG4gICAgLnNmX19ub3RpY2UtLXN0YXJ0IGF7XG4gICAgICBkaXNwbGF5OiBub25lO1xuICAgIH1cbiAgfVxuXG5cbiAgQGluY2x1ZGUgYnJlYWtwb2ludHMobWQpIHtcbiAgICAmX19jb2x1bW5zIHtcbiAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICBmbGV4LXdyYXA6IHdyYXA7XG4gICAgfVxuICAgICZfX2NvbHVtbiB7XG4gICAgICBmbGV4LWJhc2lzOiA2NiU7XG5cbiAgICAgICY6bGFzdC1jaGlsZCB7XG4gICAgICAgIGZsZXgtYmFzaXM6IDMzJTtcbiAgICAgIH1cbiAgICB9XG5cbiAgfVxufVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBR0FBLEFBQUEsY0FBYyxDQUFDO0VBQ1gsTUFBTSxFQUFFLGNBQWM7RUFDdEIsYUFBYSxFQUFFLEdBQUc7RUFDbEIsS0FBSyxFQUFFLElBQUksR0FDZDs7QUFF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 */
  • shopping-feed/tags/6.1.0/assets/scss/main/_sf.scss

    r2339808 r2670662  
    11.sf {
     2
     3  &__inline{
     4    display: inline-block;
     5    margin-right: 0;
     6  }
     7
     8  &__table {
     9    tr:first-child button{
     10      display: none;
     11    }
     12    td {
     13      padding-left: 0;
     14    }
     15  }
    216
    317  &__logo {
     
    2943  }
    3044
     45  &__button__secondary.button,
    3146  &__button__logout.button {
    3247    color: $white;
  • shopping-feed/tags/6.1.0/readme.txt

    r2613147 r2670662  
    22Contributors: ShoppingFeed, BeAPI
    33Tags: shoppingfeed, marketplace, woocommerce, woocommerce shoppingfeed, create woocommerce products shoppingfeed, products feed, generate shoppingfeed, amazon, Jet, Walmart, many marketplace, import orders
    4 Stable tag: 6.0.33
    5 Version: 6.0.33
     4Stable tag: 6.1.0
     5Version: 6.1.0
    66Requires PHP: 5.6
    77Requires at least: 5.2
     
    46466.0.31 Do not send mails to other customers
    47476.0.32 Add link to WC logs
    48 6.0.33 Fix priority issue with other plugins
     486.1.0 Add the possibility to connect multiple ShoppingFeed accounts to one WC shop
    4949
    5050== Description ==
  • shopping-feed/tags/6.1.0/shoppingfeed.php

    r2613147 r2670662  
    88 * Text Domain:     shopping-feed
    99 * Domain Path:     /languages
    10  * Version:         6.0.33
     10 * Version:         6.1.0
    1111 * Requires at least WP: 5.7
    1212 * Requires at least WooCommerce: 5.1.0
     
    2626}
    2727
    28 define( 'SF_VERSION', '6.0.33' );
     28define( 'SF_VERSION', '6.1.0' );
     29define( 'SF_DB_VERSION_SLUG', 'SF_DB_VERSION' );
     30define( 'SF_DB_VERSION', '1.0.0' );
     31define( 'SF_UPGRADE_RUNNING', 'SF_UPGRADE_RUNNING' );
    2932define( 'SF_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    3033define( 'SF_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
     
    5356}
    5457
    55 \add_action( 'plugins_loaded', __NAMESPACE__ . '\\init', 100 );
     58\add_action( 'plugins_loaded', __NAMESPACE__ . '\\init', 99 );
  • shopping-feed/tags/6.1.0/src/Actions/Actions.php

    r2339808 r2670662  
    3737     */
    3838    public static function register_get_orders() {
    39         as_schedule_recurring_action(
    40             time() + 60,
    41             ShoppingFeedHelper::get_sf_orders_import_frequency(),
    42             'sf_get_orders_action',
    43             array(),
    44             self::ORDERS_GROUP
    45         );
     39        $sf_accounts = ShoppingFeedHelper::get_sf_account_options();
     40        if ( empty( $sf_accounts ) ) {
     41            ShoppingFeedHelper::get_logger()->error(
     42                sprintf(
     43                    __( 'No Accounts founds', 'shopping-feed' )
     44                ),
     45                array(
     46                    'source' => 'shopping-feed',
     47                )
     48            );
     49
     50            return;
     51        }
     52        foreach ( $sf_accounts as $key => $sf_account ) {
     53            as_schedule_recurring_action(
     54                time() + 60,
     55                ShoppingFeedHelper::get_sf_orders_import_frequency(),
     56                'sf_get_orders_action_' . $key,
     57                array(
     58                    'sf_account' => $sf_account,
     59                ),
     60                self::ORDERS_GROUP
     61            );
     62        }
    4663    }
    4764
  • shopping-feed/tags/6.1.0/src/Admin/Metabox.php

    r2339808 r2670662  
    6464        }
    6565
    66         $reference    = $order->get_meta( Query::$wc_meta_sf_reference );
    67         $channel_name = $order->get_meta( Query::$wc_meta_sf_channel_name );
     66        $reference    = $order->get_meta( Query::WC_META_SF_REFERENCE );
     67        $channel_name = $order->get_meta( Query::WC_META_SF_CHANNEL_NAME );
    6868
    6969        if ( empty( $reference ) && empty( $channel_name ) ) : ?>
  • shopping-feed/tags/6.1.0/src/Admin/Notices.php

    r2339808 r2670662  
    2020
    2121    public function admin_notices() {
    22         $options = ShoppingFeedHelper::get_sf_account_options();
    23         if (
    24             empty( $options['token'] ) &&
    25             get_current_screen()->parent_base !== Options::SF_SLUG ) {
    26             if ( empty( $options['username'] ) || empty( $options['password'] ) ) {
    27                 $this->display_notice();
    28             }
    29         }
     22        //TODO: CHECK ALL CREDENTIALS
     23        return;
     24        //      $options = ShoppingFeedHelper::get_sf_account_options();
     25        //      if (
     26        //          empty( $options['token'] ) &&
     27        //          get_current_screen()->parent_base !== Options::SF_SLUG ) {
     28        //          if ( empty( $options['username'] ) || empty( $options['password'] ) ) {
     29        //              $this->display_notice();
     30        //          }
     31        //      }
    3032    }
    3133
  • shopping-feed/tags/6.1.0/src/Admin/Options.php

    r2602861 r2670662  
    5858    /** @var array $sf_orders_options */
    5959    private $sf_orders_options;
    60     /** @var bool $connected */
    61     private $connected = true;
     60
    6261
    6362    /**
     
    114113        );
    115114
     115        /**
     116         * Clean and register new cron once account option updated
     117         */
    116118        add_action(
    117             'admin_action_sf_logout',
    118             function () {
    119                 delete_option( Options::SF_ACCOUNT_OPTIONS );
    120                 wp_safe_redirect( ShoppingFeedHelper::get_setting_link(), 302 );
     119            'admin_action_update',
     120            function () {
     121                Actions::clean_get_orders();
     122                Actions::register_get_orders();
    121123            }
    122124        );
     
    225227     */
    226228    public function init_account_setting_page() {
    227             $this->connected = ShoppingFeedHelper::is_authenticated();
    228 
    229229            //check clean action
    230230            $this->check_clean_action();
     
    240240                self::SF_ACCOUNT_SETTINGS_PAGE
    241241            );
    242 
    243             add_settings_field(
    244                 'username',
    245                 __( 'Username', 'shopping-feed' ),
    246                 function () {
    247                     printf(
    248                         '<input class="regular-text" type="text" name="sf_account_options[username]" id="username" value="%s">',
    249                         isset( $this->sf_account_options['username'] ) ? esc_attr( $this->sf_account_options['username'] ) : ''
    250                     );
    251                 },
    252                 self::SF_ACCOUNT_SETTINGS_PAGE,
    253                 'sf_account_settings'
    254             );
    255 
    256             add_settings_field(
    257                 'password',
    258                 __( 'Password', 'shopping-feed' ),
    259                 function () {
    260                     printf(
    261                         '<input class="regular-text" type="password" name="sf_account_options[password]" id="password" value="%s">',
    262                         isset( $this->sf_account_options['password'] ) ? esc_attr( $this->sf_account_options['password'] ) : ''
    263                     );
    264                 },
    265                 self::SF_ACCOUNT_SETTINGS_PAGE,
    266                 'sf_account_settings'
    267             );
    268 
    269         if ( $this->connected ) {
    270             add_settings_field(
    271                 'url',
    272                 __( 'Your source feed', 'shopping-feed' ),
    273                 function () {
    274                     $sf_feed_public_url = ShoppingFeedHelper::get_public_feed_url();
    275                     $sf_process_running = ShoppingFeedHelper::generation_process_running();
    276                     $sf_last_generation_date = get_option( Generator::SF_FEED_LAST_GENERATION_DATE );
    277                     ?>
    278                     <?php if ( ! $sf_process_running ) : ?>
    279                         <a href="<?php echo esc_html( $sf_feed_public_url ); ?>" target="_blank">
    280                             <?php
    281                             echo esc_url( $sf_feed_public_url );
    282                             ;
    283                             ?>
    284                         </a>
    285                     <?php endif; ?>
    286                     <br>
    287                     <p>
    288                         <?php if ( ! $sf_process_running ) : ?>
    289                             <?php esc_html_e( 'Last update', 'shopping-feed' ); ?> :
    290                             <?php
    291                             ! empty( $sf_last_generation_date ) ? esc_html( $sf_last_generation_date ) : esc_html_e( 'Never', 'shopping-feed' );
    292                             ?>
    293                             <a href="<?php echo esc_url( ShoppingFeedHelper::get_public_feed_url_with_generation() ); ?>" target="_blank">
    294                                 <?php esc_html_e( 'Refresh', 'shopping-feed' ); ?>
    295                             </a>
    296                         <?php else : ?>
    297                             <strong>(<?php esc_html_e( 'The feed is update is running', 'shopping-feed' ); ?>) <a href="#" onClick="window.location.reload();"><?php esc_html_e( 'Refresh to check progress', 'shopping-feed' ); ?></a></strong>
    298                         <?php endif; ?>
    299                     </p>
    300                     <?php
    301                 },
    302                 self::SF_ACCOUNT_SETTINGS_PAGE,
    303                 'sf_account_settings'
    304             );
    305         }
    306 
    307             add_settings_field(
    308                 'status',
    309                 __( 'Status', 'shopping-feed' ),
    310                 function () {
    311                     ?>
    312                 <div class="notice-<?php echo esc_html( $this->connected ? 'success' : 'error' ); ?> regular-text notice-alt">
    313                     <?php
    314                     echo esc_html( $this->connected ? _e( 'Connected', 'shopping-feed' ) : _e( 'Not connected', 'shopping-feed' ) );
    315                     ?>
    316                 </div>
    317                     <?php
    318                 },
    319                 self::SF_ACCOUNT_SETTINGS_PAGE,
    320                 'sf_account_settings'
    321             );
    322242        ?>
    323243        <div class="wrap">
    324244            <?php settings_errors(); ?>
    325245
    326 <div class="sf__columns">
    327         <div class="sf__column">
    328              <form method="post" action="options.php">
    329                 <?php
    330                 settings_fields( 'sf_account_page_fields' );
    331                 do_settings_sections( self::SF_ACCOUNT_SETTINGS_PAGE );
    332                 if ( ! $this->connected ) {
    333                     submit_button( __( 'Login', 'shopping-feed' ), 'sf__button' );
    334                 } else {
    335                     echo '<input class="hidden" name="action" value="sf_logout">';
    336                     submit_button( __( 'Logout', 'shopping-feed' ), 'sf__button__logout', 'logout' );
    337                 }
    338                 ?>
    339             </form>
    340         <div class="sf__requirements">
    341         <?php
    342         $requirements = Requirements::get_instance();
    343             echo wp_kses_post( $requirements->curl_requirement() );
    344             echo wp_kses_post( $requirements->php_requirement() );
    345             echo wp_kses_post( $requirements->openssl_requirement() );
    346             echo wp_kses_post( $requirements->account_requirement() );
    347             echo wp_kses_post( $requirements->uploads_directory_access_requirement() );
    348         ?>
     246            <div class="sf__columns">
     247                <div class="sf__column account__wrapper">
     248                    <form method="post" action="options.php">
     249                        <div class="blocks">
     250                            <div class="block_links">
     251                                <table class="form-table sf__table">
     252                                    <thead>
     253                                    <tr>
     254                                        <th> <?php esc_html_e( 'Username', 'shopping-feed' ); ?> </th>
     255                                        <th> <?php esc_html_e( 'Password', 'shopping-feed' ); ?> </th>
     256                                        <th></th>
     257                                    </tr>
     258                                    </thead>
     259                                    <tbody>
     260                                    <tr>
     261                                        <td><input class="regular-text user" type="text"
     262                                                   name="sf_account_options[0][username]"
     263                                                   value="<?php echo isset( $this->sf_account_options[0]['username'] ) ? esc_attr( $this->sf_account_options[0]['username'] ) : ''; ?>">
     264                                        </td>
     265                                        <td><input class="regular-text pass" type="password"
     266                                                   name="sf_account_options[0][password]"
     267                                                   value="<?php echo isset( $this->sf_account_options[0]['password'] ) ? esc_attr( $this->sf_account_options[0]['password'] ) : ''; ?>">
     268                                        </td>
     269                                        <td>
     270                                            <button class="button sf__button__secondary delete_link sf__button--delete">
     271                                                Delete
     272                                            </button>
     273                                        </td>
     274                                        <td class="hidden"><input name="sf_account_options[0][token]"
     275                                                                  value="<?php echo isset( $this->sf_account_options[0]['token'] ) ? esc_attr( $this->sf_account_options[0]['token'] ) : ''; ?>">
     276                                        </td>
     277                                        <td class="hidden"><input name="sf_account_options[0][sf_store_id]"
     278                                                                  value="<?php echo isset( $this->sf_account_options[0]['sf_store_id'] ) ? esc_attr( $this->sf_account_options[0]['sf_store_id'] ) : ''; ?>">
     279                                        </td>
     280                                    </tr>
     281                                    <?php
     282                                    if ( count( $this->sf_account_options ) > 1 ) {
     283                                        foreach ( $this->sf_account_options as $key => $sf_account_option ) {
     284                                            if ( 0 === $key ) {
     285                                                continue;
     286                                            }
     287                                            ?>
     288                                            <tr>
     289                                                <td><input class="regular-text user" type="text"
     290                                                           name="sf_account_options[<?php echo esc_attr( $key ); ?>][username]"
     291                                                           value="<?php echo isset( $this->sf_account_options[ $key ]['username'] ) ? esc_attr( $this->sf_account_options[ $key ]['username'] ) : ''; ?>">
     292                                                </td>
     293                                                <td><input class="regular-text pass" type="password"
     294                                                           name="sf_account_options[<?php echo esc_attr( $key ); ?>][password]"
     295                                                           value="<?php echo isset( $this->sf_account_options[ $key ]['password'] ) ? esc_attr( $this->sf_account_options[ $key ]['password'] ) : ''; ?>">
     296                                                </td>
     297                                                <td class="hidden"><input
     298                                                            name="sf_account_options[<?php echo esc_attr( $key ); ?>][token]"
     299                                                            value="<?php echo isset( $this->sf_account_options[ $key ]['token'] ) ? esc_attr( $this->sf_account_options[ $key ]['token'] ) : ''; ?>">
     300                                                </td>
     301                                                <td class="hidden"><input
     302                                                            name="sf_account_options[<?php echo esc_attr( $key ); ?>][sf_store_id]"
     303                                                            value="<?php echo isset( $this->sf_account_options[ $key ]['sf_store_id'] ) ? esc_attr( $this->sf_account_options[ $key ]['sf_store_id'] ) : ''; ?>">
     304                                                </td>
     305                                                <td>
     306                                                    <button class="button sf__button__secondary delete_link sf__button--delete">
     307                                                        Delete
     308                                                    </button>
     309                                                </td>
     310                                            </tr>
     311                                            <?php
     312                                        }
     313                                    }
     314                                    ?>
     315                                    </tbody>
     316                                </table>
     317                                <div class="sf__inline">
     318                                    <p>
     319                                        <button class="button sf__button__secondary add_link"><?php esc_html_e( 'Add account', 'shopping-feed' ); ?></button>
     320                                    </p>
     321                                </div>
     322                                <div class="sf__inline">
     323                                    <?php
     324                                    settings_fields( 'sf_account_page_fields' );
     325                                    submit_button( __( 'Save', 'shopping-feed' ), 'sf__button' );
     326                                    ?>
     327                                </div>
     328                    </form>
     329                </div>
     330            </div>
     331            <!-- Line template -->
     332            <script type="text/html" id="tpl-line">
     333                <tr>
     334                    <td><input class="regular-text user" type="text" name="sf_account_options[<%= row %>][username]"
     335                               value="<%= user %>"></td>
     336                    <td><input class="regular-text pass" type="password" name="sf_account_options[<%= row %>][password]"
     337                               value="<%= pass %>"></td>
     338                    <td>
     339                        <button class="button sf__button__secondary sf__button--delete delete_link">Delete</button>
     340                    </td>
     341                </tr>
     342            </script>
     343            <div class="sf__requirements">
     344                <?php
     345                $requirements = Requirements::get_instance();
     346                echo wp_kses_post( $requirements->curl_requirement() );
     347                echo wp_kses_post( $requirements->php_requirement() );
     348                echo wp_kses_post( $requirements->openssl_requirement() );
     349                echo wp_kses_post( $requirements->uploads_directory_access_requirement() );
     350                ?>
    349351                <!--        REQUIREMENTS     -->
    350352        </div>
     
    365367    private function check_clean_action() {
    366368        if ( ! empty( $_GET['clean_process'] ) ) {
    367             ShoppingFeedHelper::clean_generation_process_running();
     369            ShoppingFeedHelper::clean_process_running( 'sf_feed_generation_process' );
    368370        }
    369371    }
     
    385387        );
    386388
     389        wp_enqueue_script(
     390            'accounts',
     391            SF_PLUGIN_URL . 'assets/js/accounts.js',
     392            array( 'jquery', 'underscore' ),
     393            true,
     394            true
     395        );
     396
    387397        wp_enqueue_script( 'multi_js_init', SF_PLUGIN_URL . 'assets/js/init.js', array( 'multi_js' ), true );
    388398        wp_localize_script(
     
    390400            'sf_options',
    391401            array(
    392                 'selected_orders'            => __( 'Selected order status', 'shopping-feed' ),
     402                'selected_orders'   => __( 'Selected order status', 'shopping-feed' ),
    393403                'unselected_orders' => __( 'Unselected order status', 'shopping-feed' ),
    394                 'search' => __( 'Search', 'shopping-feed' ),
     404                'search'            => __( 'Search', 'shopping-feed' ),
    395405            )
    396406        );
     
    401411     */
    402412    private function init_feed_setting_page() {
    403         $this->check_connection();
    404413
    405414        //load assets
     
    413422            },
    414423            self::SF_FEED_SETTINGS_PAGE
     424        );
     425
     426        add_settings_field(
     427            'url',
     428            __( 'Your source feed', 'shopping-feed' ),
     429            function () {
     430                $sf_feed_public_url      = ShoppingFeedHelper::get_public_feed_url();
     431                $sf_process_running      = ShoppingFeedHelper::is_process_running( 'sf_feed_generation_process' );
     432                $sf_last_generation_date = get_option( Generator::SF_FEED_LAST_GENERATION_DATE );
     433                ?>
     434                <?php if ( ! $sf_process_running ) : ?>
     435                    <a href="<?php echo esc_html( $sf_feed_public_url ); ?>" target="_blank">
     436                        <?php
     437                        echo esc_url( $sf_feed_public_url );
     438                        ;
     439                        ?>
     440                    </a>
     441                <?php endif; ?>
     442                <br>
     443                <p>
     444                    <?php if ( ! $sf_process_running ) : ?>
     445                        <?php esc_html_e( 'Last update', 'shopping-feed' ); ?> :
     446                        <?php
     447                        echo ! empty( $sf_last_generation_date ) ? esc_html( $sf_last_generation_date ) : esc_html__( 'Never', 'shopping-feed' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
     448                        ?>
     449                        <a href="<?php echo esc_url( ShoppingFeedHelper::get_public_feed_url_with_generation() ); ?>" target="_blank">
     450                            <?php esc_html_e( 'Refresh', 'shopping-feed' ); ?>
     451                        </a>
     452                    <?php else : ?>
     453                        <strong>(<?php esc_html_e( 'The feed is update is running', 'shopping-feed' ); ?>) <a href="#" onClick="window.location.reload();"><?php esc_html_e( 'Refresh to check progress', 'shopping-feed' ); ?></a></strong>
     454                    <?php endif; ?>
     455                </p>
     456                <?php
     457            },
     458            self::SF_FEED_SETTINGS_PAGE,
     459            'sf_feed_settings_categories'
    415460        );
    416461
     
    554599            __( 'Batch size', 'shopping-feed' ),
    555600            function () {
    556                 $running_process = ShoppingFeedHelper::get_running_generation_feed_process();
     601                $running_process = ShoppingFeedHelper::get_running_process( 'sf_feed_generation_process' );
    557602                $running_process = is_array( $running_process ) ? count( $running_process ) : 0;
    558603                ?>
     
    657702
    658703    /**
    659      * Check connection with SF platform with correct credentials
    660      * If not redirect to Account page
    661      */
    662     private function check_connection() {
    663         if ( ! ShoppingFeedHelper::is_authenticated() ) {
    664             wp_safe_redirect( sprintf( '%s&no_connected=%s', ShoppingFeedHelper::get_setting_link(), 'true' ) );
    665         }
    666     }
    667 
    668     /**
    669704     * Define Shipping Page
    670705     */
    671706    private function init_shipping_setting_page() {
    672         $this->check_connection();
    673 
    674707        //load assets
    675708        $this->load_assets();
     
    826859
    827860    private function init_orders_setting_page() {
    828         $this->check_connection();
    829 
    830861        //load assets
    831862        $this->load_assets();
  • shopping-feed/tags/6.1.0/src/Admin/Requirements.php

    r2542211 r2670662  
    8383     * @return string
    8484     */
    85     public function account_requirement() {
    86         return ( $this->valid_account() )
    87             ? '<p class="success">' . __( 'You are logged in your ShoppingFeed account.', 'shopping-feed' ) . '</p>'
    88             : '<p class="failed">' . __( 'You must be logged in your ShoppingFeed account.', 'shopping-feed' ) . '</p>';
    89     }
    90 
    91 
    92     /**
    93      * @return string
    94      */
    9585    public function uploads_directory_access_requirement() {
    9686        return ( $this->uploads_directory_writable() )
     
    137127
    138128    /**
    139      * Check if the user is logged in.
    140      *
    141      * @return bool
    142      */
    143     public function valid_account() {
    144         return ShoppingFeedHelper::is_authenticated();
    145     }
    146 
    147     /**
    148129     * Check if the uploads directory is writable
    149130     *
  • shopping-feed/tags/6.1.0/src/Admin/WoocommerceActions.php

    r2542211 r2670662  
    1010use ShoppingFeed\Sdk\Api\Catalog\PricingUpdate;
    1111use ShoppingFeed\Sdk\Api\Store\StoreResource;
     12use ShoppingFeed\Sdk\Client;
     13use ShoppingFeed\Sdk\Credential;
    1214use ShoppingFeed\ShoppingFeedWC\Feed\AsyncGenerator;
    1315use ShoppingFeed\ShoppingFeedWC\Feed\Generator;
     
    1618use ShoppingFeed\ShoppingFeedWC\Orders\Orders;
    1719use ShoppingFeed\ShoppingFeedWC\Products\Product;
     20use ShoppingFeed\ShoppingFeedWC\Query\Query;
    1821use ShoppingFeed\ShoppingFeedWC\Sdk\Sdk;
    1922use ShoppingFeed\ShoppingFeedWC\ShoppingFeedHelper;
    20 
    2123
    2224/**
     
    6365        add_action(
    6466            'sf_feed_generation_combine_feed_parts',
    65             [
     67            array(
    6668                AsyncGenerator::get_instance(),
    6769                'combine_feed_parts',
    68             ]
     70            )
    6971        );
    7072
     
    8486
    8587        //Get Orders
    86         add_action(
    87             'sf_get_orders_action',
    88             function () {
    89                 Orders::get_instance()->get_orders();
    90             }
    91         );
     88        $sf_accounts = ShoppingFeedHelper::get_sf_account_options();
     89        if ( empty( $sf_accounts ) ) {
     90            ShoppingFeedHelper::get_logger()->error(
     91                sprintf(
     92                    __( 'No Accounts founds', 'shopping-feed' )
     93                ),
     94                array(
     95                    'source' => 'shopping-feed',
     96                )
     97            );
     98        } else {
     99            foreach ( $sf_accounts as $key => $sf_account ) {
     100                add_action(
     101                    'sf_get_orders_action_' . $key,
     102                    function ( $sf_account ) {
     103                        Orders::get_instance()->get_orders( $sf_account );
     104                    }
     105                );
     106            }
     107        }
    92108
    93109        //Acknowledge Remain Order
     
    102118
    103119        /**
     120         * Migrate old accounts to multi account format
     121         */
     122        add_action( 'sf_migrate_single_action', array( $this, 'migrate_old_accounts' ) );
     123
     124        /**
    104125         * Orders Statuses Mapping
    105126         * Each status with action
     
    107128         */
    108129        $statuses_actions = ShoppingFeedHelper::get_sf_statuses_actions();
    109         if ( 0 < $statuses_actions ) {
    110             foreach ( $statuses_actions as $sf_action => $wc_statuses ) {
    111                 if ( ! is_array( $wc_statuses ) || '' === $sf_action ) {
    112                     continue;
    113                 }
    114 
    115                 foreach ( $wc_statuses as $wc_status ) {
    116                     $status = str_replace( 'wc-', '', $wc_status );
    117                     $action = 'woocommerce_order_status_' . $status;
    118                     add_action(
    119                         $action,
    120                         function ( $order_id ) use ( $sf_action ) {
    121                             //if its not a sf order
    122                             if ( ! Order::is_sf_order( $order_id ) ) {
    123                                 return;
    124                             }
    125                             try {
    126                                 $operations = new Operations( $order_id );
    127                                 if ( ! method_exists( $operations, $sf_action ) ) {
    128                                     ShoppingFeedHelper::get_logger()->error(
    129                                         sprintf(
    130                                         /* translators: %s: Action name. */
    131                                             __( 'Cant find any matched method for %s', 'shopping-feed' ),
    132                                             $sf_action
    133                                         ),
    134                                         array(
    135                                             'source' => 'shopping-feed',
    136                                         )
    137                                     );
    138 
    139                                     return;
    140                                 }
    141                                 call_user_func( array( $operations, $sf_action ) );
    142                             } catch ( Exception $exception ) {
    143                                 wc_get_order( $order_id )->add_order_note(
    144                                     sprintf(
    145                                     /* translators: %s: Action */
    146                                         __( 'Failed to %s order', 'shopping-feed' ),
    147                                         ucfirst( $sf_action )
    148                                     )
    149                                 );
     130        if ( empty( $statuses_actions ) ) {
     131            return;
     132        }
     133        foreach ( $statuses_actions as $sf_action => $wc_statuses ) {
     134            if ( ! is_array( $wc_statuses ) || '' === $sf_action ) {
     135                continue;
     136            }
     137
     138            foreach ( $wc_statuses as $wc_status ) {
     139                $status = str_replace( 'wc-', '', $wc_status );
     140                $action = 'woocommerce_order_status_' . $status;
     141                add_action(
     142                    $action,
     143                    function ( $order_id ) use ( $sf_action ) {
     144                        //if its not a sf order
     145                        if ( ! Order::is_sf_order( $order_id ) ) {
     146                            return;
     147                        }
     148                        try {
     149                            $operations = new Operations( $order_id );
     150                            if ( ! method_exists( $operations, $sf_action ) ) {
    150151                                ShoppingFeedHelper::get_logger()->error(
    151152                                    sprintf(
    152                                     /* translators: %1$s: Action. %2$s: Error Message */
    153                                         __( 'Failed to %1$s order => %2$s', 'shopping-feed' ),
    154                                         ucfirst( $sf_action ),
    155                                         $exception->getMessage()
     153                                    /* translators: %s: Action name. */
     154                                        __( 'Cant find any matched method for %s', 'shopping-feed' ),
     155                                        $sf_action
    156156                                    ),
    157157                                    array(
     
    159159                                    )
    160160                                );
     161
     162                                return;
    161163                            }
     164                            call_user_func( array( $operations, $sf_action ) );
     165                        } catch ( Exception $exception ) {
     166                            wc_get_order( $order_id )->add_order_note(
     167                                sprintf(
     168                                /* translators: %s: Action */
     169                                    __( 'Failed to %s order', 'shopping-feed' ),
     170                                    ucfirst( $sf_action )
     171                                )
     172                            );
     173                            ShoppingFeedHelper::get_logger()->error(
     174                                sprintf(
     175                                /* translators: %1$s: Action. %2$s: Error Message */
     176                                    __( 'Failed to %1$s order => %2$s', 'shopping-feed' ),
     177                                    ucfirst( $sf_action ),
     178                                    $exception->getMessage()
     179                                ),
     180                                array(
     181                                    'source' => 'shopping-feed',
     182                                )
     183                            );
    162184                        }
    163                     );
    164                 }
     185                    }
     186                );
    165187            }
    166188        }
     
    182204     * @param $product_id
    183205     * @param bool $only_stock
    184      *
    185      * @return bool
    186206     */
    187207    public function update_product( $product_id, $only_stock = false ) {
    188         $sdk = Sdk::get_instance();
    189         if ( ! $sdk->get_default_shop() instanceof StoreResource ) {
    190             ShoppingFeedHelper::get_logger()->error( __( 'Can\'t find default shop', 'shopping-feed' ) );
    191 
    192             return false;
    193         }
    194 
    195         /** @var StoreResource $shop */
    196         $shop          = $sdk->get_default_shop();
    197         $pricing_api   = $shop->getPricingApi();
    198         $inventory_api = $shop->getInventoryApi();
    199 
    200         $this->pricing_update   = new \ShoppingFeed\Sdk\Api\Catalog\PricingUpdate();
    201         $this->inventory_update = new \ShoppingFeed\Sdk\Api\Catalog\InventoryUpdate();
    202 
    203         $product = new Product( $product_id );
    204         if ( $product->has_variations() ) {
    205             foreach ( $product->get_variations() as $variation ) {
    206                 if ( empty( $variation['sku'] ) ) {
    207                     ShoppingFeedHelper::get_logger()->warning(
    208                         sprintf(
    209                         /* translators: %s: Product ID. */
    210                             __( 'Cant update product without SKU => %s', 'shopping-feed' ),
    211                             $product_id
    212                         ),
    213                         array(
    214                             'source' => 'shopping-feed',
    215                         )
    216                     );
    217 
    218                     continue;
    219                 }
    220 
    221                 $this->may_update_pricing( $variation['sku'], ! empty( $variation['discount'] ) ? $variation['discount'] : $variation['price'] );
    222                 $this->may_update_inventory( $variation['sku'], $variation['quantity'] );
    223             }
    224         } else {
    225             if ( empty( $product->get_sku() ) ) {
    226                 ShoppingFeedHelper::get_logger()->warning(
     208        $sf_accounts = ShoppingFeedHelper::get_sf_account_options();
     209        if ( empty( $sf_accounts ) ) {
     210            ShoppingFeedHelper::get_logger()->error(
     211                sprintf(
     212                    __( 'No Accounts founds', 'shopping-feed' )
     213                ),
     214                array(
     215                    'source' => 'shopping-feed',
     216                )
     217            );
     218
     219            return;
     220        }
     221
     222        foreach ( $sf_accounts as $sf_account ) {
     223            $shop = Sdk::get_sf_shop( $sf_account );
     224            if ( ! $shop instanceof StoreResource ) {
     225                ShoppingFeedHelper::get_logger()->error(
    227226                    sprintf(
    228                     /* translators: %s: Product ID. */
    229                         __( 'Cant update product without SKU => %s', 'shopping-feed' ),
    230                         $product_id
     227                    /* translators: %s: Error message. */
     228                        __( 'Cannot retrieve shop from SDK for account : %s', 'shopping-feed' ),
     229                        $sf_account['username']
    231230                    ),
    232231                    array(
     
    234233                    )
    235234                );
     235                continue;
     236            }
     237
     238            $pricing_api   = $shop->getPricingApi();
     239            $inventory_api = $shop->getInventoryApi();
     240
     241            $this->pricing_update   = new \ShoppingFeed\Sdk\Api\Catalog\PricingUpdate();
     242            $this->inventory_update = new \ShoppingFeed\Sdk\Api\Catalog\InventoryUpdate();
     243
     244            $product = new Product( $product_id );
     245            if ( $product->has_variations() ) {
     246                if ( ! $this->update_variation_product( $product ) ) {
     247                    continue;
     248                }
     249            } else {
     250                if ( ! $this->update_normal_product( $product ) ) {
     251                    continue;
     252                }
     253            }
     254            /**
     255             * Check if we need to update the price or only the stock
     256             */
     257            if ( ! $only_stock ) {
     258                /**
     259                 * Send api request to update the price
     260                 */
     261                $pricing_api->execute( $this->pricing_update );
     262            }
     263
     264            /**
     265             * Send api request to update the inventory
     266             */
     267            $inventory_api->execute( $this->inventory_update );
     268        }
     269    }
     270
     271    /**
     272     * @param Product $product
     273     *
     274     * @psalm-suppress all
     275     */
     276    public function update_normal_product( $product ) {
     277        if ( empty( $product->get_sku() ) ) {
     278            ShoppingFeedHelper::get_logger()->warning(
     279                sprintf(
     280                /* translators: %s: Product ID. */
     281                    __( 'Cant update product without SKU => %s', 'shopping-feed' ),
     282                    $product->get_wc_product()->get_id()
     283                ),
     284                array(
     285                    'source' => 'shopping-feed',
     286                )
     287            );
     288
     289            return false;
     290        }
     291
     292        $this->may_update_pricing( $product->get_sku(), ! empty( $product->get_discount() ) ? $product->get_discount() : $product->get_price() );
     293        $this->may_update_inventory( $product->get_sku(), $product->get_quantity() );
     294
     295        return true;
     296    }
     297
     298    /**
     299     * @param Product $product
     300     *
     301     * @psalm-suppress all
     302     * @return bool
     303     */
     304    public function update_variation_product( $product ) {
     305        foreach ( $product->get_variations() as $variation ) {
     306            if ( empty( $variation['sku'] ) ) {
     307                ShoppingFeedHelper::get_logger()->warning(
     308                    sprintf(
     309                    /* translators: %s: Product ID. */
     310                        __( 'Cant update product without SKU => %s', 'shopping-feed' ),
     311                        $product->get_wc_product()->get_id()
     312                    ),
     313                    array(
     314                        'source' => 'shopping-feed',
     315                    )
     316                );
    236317
    237318                return false;
    238319            }
    239320
    240             $this->may_update_pricing( $product->get_sku(), ! empty( $product->get_discount() ) ? $product->get_discount() : $product->get_price() );
    241             $this->may_update_inventory( $product->get_sku(), $product->get_quantity() );
    242         }
    243         /**
    244          * Check if we need to update the price or only the stock
    245          */
    246         if ( ! $only_stock ) {
    247             /**
    248              * Send api request to update the price
    249              */
    250             $pricing_api->execute( $this->pricing_update );
    251         }
    252 
    253         /**
    254          * Send api request to update the inventory
    255          */
    256         $inventory_api->execute( $this->inventory_update );
     321            $this->may_update_pricing( $variation['sku'], ! empty( $variation['discount'] ) ? $variation['discount'] : $variation['price'] );
     322            $this->may_update_inventory( $variation['sku'], $variation['quantity'] );
     323        }
    257324
    258325        return true;
     
    306373        $this->inventory_update->add( $sku, intval( $inventory ) );
    307374    }
     375
     376    /**
     377     * @return bool
     378     * @throws Exception
     379     * @psalm-suppress all
     380     */
     381    public function migrate_old_accounts() {
     382        //migrate account data and retrieve account ID
     383        $account_options = ShoppingFeedHelper::get_sf_account_options();
     384
     385        if ( empty( $account_options['token'] ) ) {
     386            ShoppingFeedHelper::get_logger()->error(
     387                sprintf(
     388                    __( 'No token founds', 'shopping-feed' )
     389                ),
     390                array(
     391                    'source' => 'shopping-feed',
     392                )
     393            );
     394            ShoppingFeedHelper::end_upgrade();
     395
     396            return false;
     397        }
     398        try {
     399            $main_store = Client\Client::createSession( new Credential\Token( $account_options['token'] ) )->getMainStore();
     400            if ( is_null( $main_store ) ) {
     401                ShoppingFeedHelper::get_logger()->error(
     402                    sprintf(
     403                        __( 'Cant retrieve main store', 'shopping-feed' )
     404                    ),
     405                    array(
     406                        'source' => 'shopping-feed',
     407                    )
     408                );
     409
     410                ShoppingFeedHelper::end_upgrade();
     411
     412                return false;
     413            }
     414            $account_id            = $main_store->getId();
     415            $new_account_options   = array();
     416            $new_account_options[] = array(
     417                'sf_store_id' => $account_id,
     418                'username'    => ! empty( $account_options['username'] ) ? $account_options['username'] : '',
     419                'password'    => ! empty( $account_options['password'] ) ? $account_options['password'] : '',
     420                'token'       => $account_options['token'],
     421            );
     422            ShoppingFeedHelper::set_sf_account_options( $new_account_options );
     423        } catch ( \Exception $exception ) {
     424            ShoppingFeedHelper::get_logger()->error(
     425                sprintf(
     426                /* translators: %s: Error message. */
     427                    __( 'Cant login with actual credentials => %s', 'shopping-feed' ),
     428                    $exception->getMessage()
     429                ),
     430                array(
     431                    'source' => 'shopping-feed',
     432                )
     433            );
     434
     435            ShoppingFeedHelper::end_upgrade();
     436
     437            return false;
     438        }
     439
     440        //Migrate old orders to default account
     441        $args = array(
     442            'limit'        => - 1,
     443            'meta_key'     => Query::WC_META_SF_REFERENCE,
     444            'meta_compare' => 'EXISTS',
     445        );
     446
     447        $query     = new \WC_Order_Query( $args );
     448        $wc_orders = $query->get_orders();
     449        if ( empty( $wc_orders ) ) {
     450            ShoppingFeedHelper::get_logger()->error(
     451                sprintf(
     452                    __( 'No SF orders founds for migration', 'shopping-feed' )
     453                ),
     454                array(
     455                    'source' => 'shopping-feed',
     456                )
     457            );
     458            ShoppingFeedHelper::end_upgrade();
     459
     460            return false;
     461        }
     462
     463        foreach ( $wc_orders as $wc_order ) {
     464            //add store id
     465            /** @var \WC_Order $wc_order */
     466            $wc_order->add_meta_data( Query::WC_META_SF_STORE_ID, $account_id );
     467            $wc_order->save();
     468        }
     469
     470        ShoppingFeedHelper::end_upgrade();
     471
     472        return true;
     473    }
    308474}
  • shopping-feed/tags/6.1.0/src/Feed/Generator.php

    r2542211 r2670662  
    234234
    235235        if ( true === $no_cache || ! is_file( $file_path ) ) {
    236             if ( ShoppingFeedHelper::generation_process_running() ) {
     236            if ( ShoppingFeedHelper::is_process_running( 'sf_feed_generation_process' ) ) {
    237237                wp_die( 'Feed generation already launched' );
    238238            }
  • shopping-feed/tags/6.1.0/src/Orders/Operations.php

    r2542211 r2670662  
    5353     */
    5454    public function __construct( $order_id ) {
    55         $sdk = Sdk::get_instance();
    56         if ( ! $sdk->get_default_shop() instanceof StoreResource ) {
     55        //Check if the order from SF and return it with metas data
     56        $order_sf_metas = Order::get_order_sf_metas( $order_id );
     57        if (
     58            empty( $order_sf_metas ) ||
     59            empty( $order_sf_metas['sf_store_id'] )
     60        ) {
     61            throw new Exception(
     62                sprintf(
     63                /* translators: %s: Error message. */
     64                    __( 'No SF order found in %s', 'shopping-feed' ),
     65                    $order_id
     66                )
     67            );
     68        }
     69
     70        $account_id = $order_sf_metas['sf_store_id'];
     71
     72        $shop = Sdk::get_sf_account_shop( $account_id );
     73
     74        if ( ! $shop instanceof StoreResource ) {
     75            ShoppingFeedHelper::get_logger()->error(
     76                sprintf(
     77                /* translators: %s: Error message. */
     78                    __( 'Cannot retrieve shop from SDK for account : %s', 'shopping-feed' ),
     79                    $account_id
     80                ),
     81                array(
     82                    'source' => 'shopping-feed',
     83                )
     84            );
    5785            throw new Exception(
    5886                __( 'No store found', 'shopping-feed' )
    5987            );
    6088        }
    61         /** @var StoreResource $default_shop */
    62         $default_shop = $sdk->get_default_shop();
    63 
    64         //Check if the order from SF and return it with metas data
    65         $order_sf_metas = Order::get_order_sf_metas( $order_id );
    66         if ( empty( $order_sf_metas ) ) {
    67             throw new Exception(
    68                 __( 'No Order found', 'shopping-feed' )
    69             );
    70         }
    7189
    7290        $this->wc_order           = $order_sf_metas['order'];
    73         $this->order_api          = $default_shop->getOrderApi();
     91        $this->order_api          = $shop->getOrderApi();
    7492        $this->order_operation    = new OrderOperation();
    7593        $this->sf_reference       = (string) $order_sf_metas['sf_reference'];
  • shopping-feed/tags/6.1.0/src/Orders/Order.php

    r2551274 r2670662  
    221221     */
    222222    public static function exists( $sf_order ) {
    223         return ! empty( wc_get_orders( array( Query::$wc_meta_sf_reference => $sf_order->getReference() ) ) );
     223        return ! empty( wc_get_orders( array( Query::WC_META_SF_REFERENCE => $sf_order->getReference() ) ) );
    224224    }
    225225
     
    313313        }
    314314
    315         $sf_reference    = $order->get_meta( Query::$wc_meta_sf_reference );
    316         $sf_channel_name = $order->get_meta( Query::$wc_meta_sf_channel_name );
     315        $sf_store_id     = $order->get_meta( Query::WC_META_SF_STORE_ID );
     316        $sf_reference    = $order->get_meta( Query::WC_META_SF_REFERENCE );
     317        $sf_channel_name = $order->get_meta( Query::WC_META_SF_CHANNEL_NAME );
    317318
    318319        return array(
    319320            'order'           => $order,
     321            'sf_store_id'     => $sf_store_id,
    320322            'sf_reference'    => $sf_reference,
    321323            'sf_channel_name' => $sf_channel_name,
     
    336338        }
    337339
    338         return ! empty( $wc_order->get_meta( Query::$wc_meta_sf_reference ) );
     340        return ! empty( $wc_order->get_meta( Query::WC_META_SF_REFERENCE ) );
    339341    }
    340342
     
    347349     */
    348350    public static function can_update_stock( $wc_order ) {
    349         return empty( $wc_order->get_meta( Query::$wc_meta_sf_reference ) ) || empty( $wc_order->get_meta( Metas::$dont_update_inventory ) );
     351        return empty( $wc_order->get_meta( Query::WC_META_SF_REFERENCE ) ) || empty( $wc_order->get_meta( Metas::$dont_update_inventory ) );
    350352    }
    351353}
  • shopping-feed/tags/6.1.0/src/Orders/Order/Metas.php

    r2339808 r2670662  
    3939        $this->add_order_channel_name();
    4040        $this->add_order_shipping();
     41        $this->add_sf_store_id();
    4142
    4243        do_action_ref_array( 'sf_add_metas', array( $this ) );
     
    4748     */
    4849    private function add_order_referene() {
    49         $this->add_meta( Query::$wc_meta_sf_reference, $this->sf_order->getReference(), true );
     50        $this->add_meta( Query::WC_META_SF_REFERENCE, $this->sf_order->getReference(), true );
    5051    }
    5152
     
    5455     */
    5556    private function add_order_channel_name() {
    56         $this->add_meta( Query::$wc_meta_sf_channel_name, $this->sf_order->getChannel()->getName() );
     57        $this->add_meta( Query::WC_META_SF_CHANNEL_NAME, $this->sf_order->getChannel()->getName() );
    5758    }
    5859
     
    8182        );
    8283    }
     84
     85    /**
     86     * Add store id
     87     */
     88    private function add_sf_store_id() {
     89        $sf_order_array = $this->sf_order->toArray();
     90        $this->add_meta( Query::WC_META_SF_STORE_ID, $sf_order_array['storeId'] );
     91    }
    8392}
  • shopping-feed/tags/6.1.0/src/Orders/Orders.php

    r2417980 r2670662  
    1414 */
    1515class Orders {
    16 
    17     /**
    18      * @var false|StoreResource
    19      */
    20     private $shop;
    2116
    2217    /**
     
    5146
    5247    /**
    53      * Orders constructor.
     48     * Get Orders from SF
    5449     */
    55     private function __construct() {
    56         if ( empty( $this->shop ) ) {
    57             $sdk = Sdk::get_instance();
    58             if ( $sdk->get_default_shop() ) {
    59                 $this->shop = $sdk->get_default_shop();
    60             }
    61         }
    62     }
     50    public function get_orders( $sf_account ) {
     51        $shop = Sdk::get_sf_shop( $sf_account );
    6352
    64     /**
    65      * Get Orders from SF
    66      * @return bool
    67      */
    68     public function get_orders() {
    69         if ( ! $this->shop ) {
     53        if ( ! $shop instanceof StoreResource ) {
     54            ShoppingFeedHelper::get_logger()->error(
     55                sprintf(
     56                /* translators: %s: Error message. */
     57                    __( 'Cannot retrieve shop from SDK for account : %s', 'shopping-feed' ),
     58                    $sf_account['username']
     59                ),
     60                array(
     61                    'source' => 'shopping-feed',
     62                )
     63            );
    7064            return false;
    7165        }
    7266
    73         $order_api                 = $this->shop->getOrderApi();
     67        $order_api                 = $shop->getOrderApi();
    7468        $filters                   = array();
    7569        $filters['acknowledgment'] = 'unacknowledged';
  • shopping-feed/tags/6.1.0/src/Products/Product.php

    r2487968 r2670662  
    4848
    4949        return $this;
     50    }
     51
     52    /**
     53     * Return WC Product
     54     * @return false|\WC_Product|null
     55     */
     56    public function get_wc_product() {
     57        return $this->product;
    5058    }
    5159
  • shopping-feed/tags/6.1.0/src/Query/Query.php

    r2339808 r2670662  
    1010 */
    1111class Query {
     12    /**
     13     * Custom Meta for SF accpunt ID
     14     */
     15    const WC_META_SF_STORE_ID = 'sf_store_id';
    1216
    1317    /**
    1418     * Custom Meta for SF reference
    15      * @var string
    1619     */
    17     public static $wc_meta_sf_reference = 'sf_reference';
     20    const WC_META_SF_REFERENCE = 'sf_reference';
    1821
    1922    /**
    2023     * Custom Meta for SF channel name
    21      * @var string
    2224     */
    23     public static $wc_meta_sf_channel_name = 'sf_marketplace';
     25    const WC_META_SF_CHANNEL_NAME = 'sf_marketplace';
    2426
    2527    public function __construct() {
     
    4446     */
    4547    public function wc_get_by_sf_reference( $query, $query_vars ) {
    46         if ( ! empty( $query_vars[ self::$wc_meta_sf_reference ] ) ) {
     48        if ( ! empty( $query_vars[ self::WC_META_SF_REFERENCE ] ) ) {
    4749            $query['meta_query'][] = array(
    48                 'key'   => self::$wc_meta_sf_reference,
    49                 'value' => esc_attr( $query_vars[ self::$wc_meta_sf_reference ] ),
     50                'key'   => self::WC_META_SF_REFERENCE,
     51                'value' => esc_attr( $query_vars[ self::WC_META_SF_REFERENCE ] ),
    5052            );
    5153        }
  • shopping-feed/tags/6.1.0/src/Sdk/Sdk.php

    r2417980 r2670662  
    66defined( 'ABSPATH' ) || exit;
    77
    8 use ShoppingFeed\Sdk\Api\Session\SessionResource;
    98use ShoppingFeed\Sdk\Client;
    109use ShoppingFeed\Sdk\Credential;
     
    1615class Sdk {
    1716
    18     /** @var SessionResource */
    19     private $session;
     17    /**
     18     * @param int $account_id
     19     */
     20    public static function get_sf_account_shop( $account_id ) {
     21        $sf_account = ShoppingFeedHelper::get_sf_account_credentials( $account_id );
    2022
    21     /** @var string|null */
    22     private $username;
     23        return self::get_sf_shop( $sf_account );
     24    }
    2325
    24     /** @var string|null */
    25     private $password;
    26 
    27     /** @var string|null */
    28     private $token;
    2926
    3027    /**
    31      * @var Sdk
     28     * Return account deault shop
     29     *
     30     * @param array $sf_account
     31     *
     32     * @return false|\ShoppingFeed\Sdk\Api\Store\StoreResource
     33     * @psalm-suppress all
    3234     */
    33     private static $instance;
    34 
    35     /**
    36      * Get the singleton instance.
    37      *
    38      * @return Sdk
    39      */
    40     public static function get_instance() {
    41         if ( is_null( self::$instance ) ) {
    42             self::$instance = new static();
    43         }
    44 
    45         return self::$instance;
    46     }
    47 
    48     /**
    49      * Singleton instance can't be cloned.
    50      */
    51     private function __clone() {
    52     }
    53 
    54     /**
    55      * Singleton instance can't be serialized.
    56      */
    57     private function __wakeup() {
    58     }
    59 
    60     /**
    61      * Sdk constructor.
    62      */
    63     private function __construct() {
    64         if ( empty( $this->session ) ) {
    65             $options = ShoppingFeedHelper::get_sf_account_options();
    66             if ( empty( $options ) ) {
    67                 ShoppingFeedHelper::get_logger()->error(
    68                     __( 'No settings founds', 'shopping-feed' ),
    69                     array(
    70                         'source' => 'shopping-feed',
    71                     )
    72                 );
    73 
    74                 return;
    75             }
    76 
    77             $this->token    = ! empty( $options['token'] ) ? $options['token'] : null;
    78             $this->username = ! empty( $options['username'] ) ? $options['username'] : null;
    79             $this->password = ! empty( $options['password'] ) ? $options['password'] : null;
    80 
    81             $this->authenticate();
    82         }
    83     }
    84 
    85     /**
    86      * @return Credential\Password|Credential\Token|\WP_Error
    87      */
    88     private function get_credential() {
    89 
    90         //Check if we have Token to connect directly
    91         if ( ! empty( $this->token ) ) {
    92             return new Credential\Token( $this->token );
    93         }
    94 
    95         //If no credentials found go back
    96         if ( empty( $this->username ) || empty( $this->password ) ) {
     35    public static function get_sf_shop( $sf_account ) {
     36        if (
     37            empty( $sf_account['token'] ) && (
     38                empty( $sf_account['username'] ) ||
     39                empty( $sf_account['password'] )
     40            )
     41        ) {
     42            //TODO: add more informations about concerned sf account
    9743            ShoppingFeedHelper::get_logger()->error(
    98                 __( 'Need username/password to connect', 'shopping-feed' ),
     44                sprintf(
     45                    __( 'No Credentials found to connect', 'shopping-feed' )
     46                ),
    9947                array(
    10048                    'source' => 'shopping-feed',
    10149                )
    10250            );
    103             return new \WP_Error( 'shopping_feed_auth_required', __( 'Need username/password to connect', 'shopping-feed' ) );
    104         }
    10551
    106         //Return Credentials username/password
    107         return new Credential\Password( $this->username, $this->password );
    108     }
    109 
    110     /**
    111      * Try to connect if we have credentials
    112      * Set Token if the connection is set
    113      * @return bool
    114      */
    115     public function authenticate() {
    116         if ( is_wp_error( $this->get_credential() ) ) {
    11752            return false;
    11853        }
    11954
    120         /** @var Credential\Password|Credential\Token $credentials */
    121         $credentials = $this->get_credential();
     55        $credentials = new Credential\Password( $sf_account['username'], $sf_account['password'] );
     56
     57        if ( ! empty( $sf_account['token'] ) ) {
     58            $credentials = new Credential\Token( $sf_account['token'] );
     59        }
    12260
    12361        try {
    124             $this->session = Client\Client::createSession( $credentials );
    125 
    126             if ( empty( $this->token ) ) {
    127                 ShoppingFeedHelper::set_sf_token( $this->session->getToken() );
    128             }
    129 
    130             return true;
     62            $session = Client\Client::createSession( $credentials );
    13163        } catch ( \Exception $exception ) {
    132 
    13364            ShoppingFeedHelper::get_logger()->error(
    13465                sprintf(
     
    14273            );
    14374
    144             ShoppingFeedHelper::clean_password();
    145             return false;
    146         }
    147     }
    148 
    149     /**
    150      * Return the main store if the connection is set
    151      * @return false|\ShoppingFeed\Sdk\Api\Store\StoreResource
    152      */
    153     public function get_default_shop() {
    154 
    155         if ( ! $this->authenticate() ) {
    15675            return false;
    15776        }
    15877
    159         $main_shop = $this->session->getMainStore();
     78        $main_shop = $session->getMainStore();
    16079
    161         if ( ! $main_shop ) {
     80        if ( empty( $main_shop ) ) {
    16281            ShoppingFeedHelper::get_logger()->error(
     82            //TODO: add more informations about concerned sf account
    16383                __( 'No store found', 'shopping-feed' ),
    16484                array(
     
    16686                )
    16787            );
     88
    16889            return false;
    16990        }
     91
     92        //add storeId to account
     93        $account_options = ShoppingFeedHelper::get_sf_account_options();
     94        $index           = array_search( $sf_account['username'], array_column( $account_options, 'username' ), true );
     95        if ( false === $index || empty( $account_options[ $index ] ) ) {
     96            return false;
     97        }
     98        $account_options[ $index ]['sf_store_id'] = $main_shop->getId();
     99        $account_options[ $index ]['token']       = $session->getToken();
     100        ShoppingFeedHelper::set_sf_account_options( $account_options );
    170101
    171102        return $main_shop;
  • shopping-feed/tags/6.1.0/src/ShoppingFeed.php

    r2542211 r2670662  
    117117        }
    118118
     119        $this->actions = new WoocommerceActions();
     120
     121        //Check Upgrade
     122        if ( ! $this->check_upgrade() ) {
     123            return;
     124        }
     125
    119126        $this->query   = new Query();
    120         $this->actions = new WoocommerceActions();
    121127        $this->filters = new WoocommerceFilters();
    122128        $this->notices = new Notices();
     
    242248        delete_option( Options::SF_CARRIERS );
    243249        delete_option( Generator::SF_FEED_LAST_GENERATION_DATE );
     250        delete_option( SF_DB_VERSION_SLUG );
     251        delete_option( SF_UPGRADE_RUNNING );
    244252        self::remove_sf_directory();
    245253    }
     
    259267        }
    260268    }
     269
     270    public function check_upgrade() {
     271        if ( ! ShoppingFeedHelper::sf_has_upgrade() || ShoppingFeedHelper::sf_new_customer() ) {
     272            return true;
     273        }
     274
     275        //check if we have a running migration
     276        if ( ShoppingFeedHelper::is_upgrade_running() ) {
     277            add_action(
     278                'admin_notices',
     279                function () {
     280                    ?>
     281                    <div id="message" class="notice notice-error">
     282                        <p><?php esc_html_e( 'ShoppingFeed migration is running', 'shopping-feed' ); ?></p>
     283                    </div>
     284                    <?php
     285                }
     286            );
     287
     288            return false;
     289        }
     290
     291        //check if we need to do migration
     292        if (
     293            ! empty( $_GET['sf_action'] ) &&
     294            'sf_migrate_single' === $_GET['sf_action'] &&
     295            isset( $_GET['_wpnonce'] ) &&
     296            wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), 'sf_migrate_single' ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
     297        ) {
     298            $id_action = as_enqueue_async_action( 'sf_migrate_single_action', array(), 'sf_migrate_single' );
     299            update_option( SF_UPGRADE_RUNNING, $id_action );
     300            wp_safe_redirect( admin_url( '/' ), 302 );
     301            exit;
     302        }
     303
     304        add_action(
     305            'admin_notices',
     306            function () {
     307                ?>
     308                <div id="message" class="notice notice-error">
     309                    <p><?php esc_html_e( 'ShoppingFeed need to migrate old data', 'shopping-feed' ); ?></p>
     310                    <a href="
     311                    <?php
     312                    echo esc_url(
     313                        wp_nonce_url(
     314                            add_query_arg(
     315                                array(
     316                                    'sf_action' => 'sf_migrate_single',
     317                                ),
     318                                admin_url()
     319                            ),
     320                            'sf_migrate_single'
     321                        )
     322                    )
     323                    ?>
     324                    ">Migrate</a>
     325                </div>
     326                <?php
     327            }
     328        );
     329
     330        return false;
     331    }
    261332}
  • shopping-feed/tags/6.1.0/src/ShoppingFeedHelper.php

    r2560053 r2670662  
    77
    88use ShoppingFeed\ShoppingFeedWC\Admin\Options;
    9 use ShoppingFeed\ShoppingFeedWC\Sdk\Sdk;
    109use ShoppingFeed\ShoppingFeedWC\Url\Rewrite;
    1110use WC_Logger;
     
    141140
    142141    /**
    143      * Set SF token after connection
    144      *
    145      * @param $token
    146      *
    147      */
    148     public static function set_sf_token( $token ) {
    149         $options          = self::get_sf_account_options();
    150         $options['token'] = $token;
    151 
    152         update_option( Options::SF_ACCOUNT_OPTIONS, $options );
    153     }
    154 
    155     /**
    156142     * Return SF Configuration for Account
    157      * @return mixed|void
     143     * @return array
    158144     */
    159145    public static function get_sf_account_options() {
    160         return get_option( Options::SF_ACCOUNT_OPTIONS );
    161     }
    162 
    163     /**
    164      * Reset password if bad
    165      *
    166      * @param $token
    167      *
    168      */
    169     public static function clean_password() {
    170         $options = self::get_sf_account_options();
    171         if ( isset( $options ) && isset( $options['password'] ) ) {
    172             unset( $options['password'] );
    173         }
    174 
    175         update_option( Options::SF_ACCOUNT_OPTIONS, $options );
     146        return get_option( Options::SF_ACCOUNT_OPTIONS, array() );
     147    }
     148
     149
     150    /**
     151     * Set SF Configuration for Account
     152     * @return bool
     153     */
     154    public static function set_sf_account_options( $option ) {
     155        return update_option( Options::SF_ACCOUNT_OPTIONS, $option );
     156    }
     157
     158    /**
     159     * @param $account_id
     160     * @psalm-suppress all
     161     * @return array
     162     */
     163    public static function get_sf_account_credentials( $account_id ) {
     164        $account_options = self::get_sf_account_options();
     165        $index           = array_search( (int) $account_id, array_column( $account_options, 'sf_store_id' ), true );
     166        if ( false === $index || empty( $account_options[ $index ] ) ) {
     167            return array();
     168        }
     169
     170        return $account_options[ $index ];
    176171    }
    177172
     
    566561
    567562    /**
    568      * @return bool
    569      */
    570     public static function is_authenticated() {
    571         $sdk = Sdk::get_instance();
    572         if ( empty( $sdk ) ) {
    573             return false;
    574         }
    575 
    576         return $sdk->authenticate();
    577     }
    578 
    579     /**
    580563     * Add filter for category taxonomy
    581564     * default: product_cat
     
    662645    /**
    663646     * Check if a running generation process
     647     *
     648     * @param $group
     649     *
    664650     * @return bool
    665651     */
    666     public static function generation_process_running() {
    667         $process = self::get_running_generation_feed_process();
     652    public static function is_process_running( $group ) {
     653        $process = self::get_running_process( $group );
    668654
    669655        return ! empty( $process );
     
    672658    /**
    673659     * Get running process list
     660     *
     661     * @param string $group
     662     *
    674663     * @return array|int
    675664     */
    676     public static function get_running_generation_feed_process() {
     665    public static function get_running_process( $group ) {
    677666        $action_scheduler = \ActionScheduler::store();
    678667
    679668        return $action_scheduler->query_actions(
    680669            array(
    681                 'group'  => 'sf_feed_generation_process',
     670                'group'  => $group,
    682671                'status' => $action_scheduler::STATUS_PENDING,
    683672            )
     
    685674    }
    686675
    687     public static function clean_generation_process_running() {
     676    /**
     677     * @param string $group
     678     */
     679    public static function clean_process_running( $group ) {
    688680        try {
    689             \ActionScheduler::store()->cancel_actions_by_group( 'sf_feed_generation_process' );
     681            \ActionScheduler::store()->cancel_actions_by_group( $group );
    690682        } catch ( \Exception $exception ) {
    691683            self::get_logger()->error(
     
    732724
    733725    /**
     726     * Check if new customer
     727     * @return bool
     728     */
     729    public static function sf_new_customer() {
     730        return empty( get_option( Options::SF_ACCOUNT_OPTIONS ) ) &&
     731               empty( get_option( Options::SF_FEED_OPTIONS ) ) &&
     732               empty( get_option( Options::SF_SHIPPING_OPTIONS ) ) &&
     733               empty( get_option( Options::SF_ORDERS_OPTIONS ) );
     734    }
     735
     736    /**
    734737     * Singleton instance can't be cloned.
    735738     */
     
    742745    private function __wakeup() {
    743746    }
     747
     748    /**
     749     * Check if we have an upgrade
     750     * @return bool
     751     */
     752    public static function sf_has_upgrade() {
     753
     754        $db_version = get_option( SF_DB_VERSION_SLUG );
     755
     756        return empty( $db_version ) || version_compare( $db_version, SF_DB_VERSION, '<' );
     757    }
     758
     759    /**
     760     * Check if we have a running upgrade
     761     * @return bool
     762     */
     763    public static function is_upgrade_running() {
     764        return ! empty( get_option( SF_UPGRADE_RUNNING ) );
     765    }
     766
     767    /*
     768     * End upgrade
     769     */
     770    public static function end_upgrade() {
     771        delete_option( SF_UPGRADE_RUNNING );
     772        update_option( SF_DB_VERSION_SLUG, SF_DB_VERSION );
     773    }
    744774}
  • shopping-feed/tags/6.1.0/vendor/autoload.php

    r2613147 r2670662  
    55require_once __DIR__ . '/composer/autoload_real.php';
    66
    7 return ComposerAutoloaderInit8e1243f94001ab8dc300d6b62efdcaf0::getLoader();
     7return ComposerAutoloaderInit0002ac7dfac052b9a9cc79f1494358a4::getLoader();
  • shopping-feed/tags/6.1.0/vendor/composer/ClassLoader.php

    r2602861 r2670662  
    150150    /**
    151151     * @return string[] Array of classname => path
    152      * @psalm-var array<string, string>
     152     * @psalm-return array<string, string>
    153153     */
    154154    public function getClassMap()
  • shopping-feed/tags/6.1.0/vendor/composer/InstalledVersions.php

    r2602861 r2670662  
    2525class InstalledVersions
    2626{
     27    /**
     28     * @var mixed[]|null
     29     * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
     30     */
    2731    private static $installed;
     32
     33    /**
     34     * @var bool|null
     35     */
    2836    private static $canGetVendors;
     37
     38    /**
     39     * @var array[]
     40     * @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
     41     */
    2942    private static $installedByVendor = array();
    3043
  • shopping-feed/tags/6.1.0/vendor/composer/autoload_real.php

    r2613147 r2670662  
    33// autoload_real.php @generated by Composer
    44
    5 class ComposerAutoloaderInit8e1243f94001ab8dc300d6b62efdcaf0
     5class ComposerAutoloaderInit0002ac7dfac052b9a9cc79f1494358a4
    66{
    77    private static $loader;
     
    2323        }
    2424
    25         spl_autoload_register(array('ComposerAutoloaderInit8e1243f94001ab8dc300d6b62efdcaf0', 'loadClassLoader'), true, true);
     25        spl_autoload_register(array('ComposerAutoloaderInit0002ac7dfac052b9a9cc79f1494358a4', 'loadClassLoader'), true, true);
    2626        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
    27         spl_autoload_unregister(array('ComposerAutoloaderInit8e1243f94001ab8dc300d6b62efdcaf0', 'loadClassLoader'));
     27        spl_autoload_unregister(array('ComposerAutoloaderInit0002ac7dfac052b9a9cc79f1494358a4', 'loadClassLoader'));
    2828
    2929        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
     
    3131            require __DIR__ . '/autoload_static.php';
    3232
    33             call_user_func(\Composer\Autoload\ComposerStaticInit8e1243f94001ab8dc300d6b62efdcaf0::getInitializer($loader));
     33            call_user_func(\Composer\Autoload\ComposerStaticInit0002ac7dfac052b9a9cc79f1494358a4::getInitializer($loader));
    3434        } else {
    3535            $map = require __DIR__ . '/autoload_namespaces.php';
     
    5252
    5353        if ($useStaticLoader) {
    54             $includeFiles = Composer\Autoload\ComposerStaticInit8e1243f94001ab8dc300d6b62efdcaf0::$files;
     54            $includeFiles = Composer\Autoload\ComposerStaticInit0002ac7dfac052b9a9cc79f1494358a4::$files;
    5555        } else {
    5656            $includeFiles = require __DIR__ . '/autoload_files.php';
    5757        }
    5858        foreach ($includeFiles as $fileIdentifier => $file) {
    59             composerRequire8e1243f94001ab8dc300d6b62efdcaf0($fileIdentifier, $file);
     59            composerRequire0002ac7dfac052b9a9cc79f1494358a4($fileIdentifier, $file);
    6060        }
    6161
     
    6464}
    6565
    66 function composerRequire8e1243f94001ab8dc300d6b62efdcaf0($fileIdentifier, $file)
     66/**
     67 * @param string $fileIdentifier
     68 * @param string $file
     69 * @return void
     70 */
     71function composerRequire0002ac7dfac052b9a9cc79f1494358a4($fileIdentifier, $file)
    6772{
    6873    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
     74        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
     75
    6976        require $file;
    70 
    71         $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
    7277    }
    7378}
  • shopping-feed/tags/6.1.0/vendor/composer/autoload_static.php

    r2613147 r2670662  
    55namespace Composer\Autoload;
    66
    7 class ComposerStaticInit8e1243f94001ab8dc300d6b62efdcaf0
     7class ComposerStaticInit0002ac7dfac052b9a9cc79f1494358a4
    88{
    99    public static $files = array (
     
    439439    {
    440440        return \Closure::bind(function () use ($loader) {
    441             $loader->prefixLengthsPsr4 = ComposerStaticInit8e1243f94001ab8dc300d6b62efdcaf0::$prefixLengthsPsr4;
    442             $loader->prefixDirsPsr4 = ComposerStaticInit8e1243f94001ab8dc300d6b62efdcaf0::$prefixDirsPsr4;
    443             $loader->classMap = ComposerStaticInit8e1243f94001ab8dc300d6b62efdcaf0::$classMap;
     441            $loader->prefixLengthsPsr4 = ComposerStaticInit0002ac7dfac052b9a9cc79f1494358a4::$prefixLengthsPsr4;
     442            $loader->prefixDirsPsr4 = ComposerStaticInit0002ac7dfac052b9a9cc79f1494358a4::$prefixDirsPsr4;
     443            $loader->classMap = ComposerStaticInit0002ac7dfac052b9a9cc79f1494358a4::$classMap;
    444444
    445445        }, null, ClassLoader::class);
  • shopping-feed/tags/6.1.0/vendor/composer/installed.php

    r2613147 r2670662  
    11<?php return array(
    22    'root' => array(
    3         'pretty_version' => '6.0.33',
    4         'version' => '6.0.33.0',
     3        'pretty_version' => '6.1.0',
     4        'version' => '6.1.0.0',
    55        'type' => 'wordpress-plugin',
    66        'install_path' => __DIR__ . '/../../',
    77        'aliases' => array(),
    8         'reference' => 'b54ebdf093f62b8a77538414cf5a4eb070cd1bb5',
     8        'reference' => '7457e98e9f4b6982416834d213cea5359612cfd9',
    99        'name' => 'shoppingfeed/shoppingfeed',
    1010        'dev' => false,
     
    102102        ),
    103103        'shoppingfeed/shoppingfeed' => array(
    104             'pretty_version' => '6.0.33',
    105             'version' => '6.0.33.0',
     104            'pretty_version' => '6.1.0',
     105            'version' => '6.1.0.0',
    106106            'type' => 'wordpress-plugin',
    107107            'install_path' => __DIR__ . '/../../',
    108108            'aliases' => array(),
    109             'reference' => 'b54ebdf093f62b8a77538414cf5a4eb070cd1bb5',
     109            'reference' => '7457e98e9f4b6982416834d213cea5359612cfd9',
    110110            'dev_requirement' => false,
    111111        ),
  • shopping-feed/trunk/assets/css/app.css

    r2384546 r2670662  
    7171  cursor: inherit; }
    7272
     73.sf__inline {
     74  display: inline-block;
     75  margin-right: 0; }
     76
     77.sf__table tr:first-child button {
     78  display: none; }
     79
     80.sf__table td {
     81  padding-left: 0; }
     82
    7383.sf__logo {
    7484  overflow: hidden;
     
    95105    background-color: #19a088; }
    96106
    97 .sf__button__logout.button {
     107.sf__button__secondary.button, .sf__button__logout.button {
    98108  color: #fff;
    99109  text-transform: uppercase;
    100110  border-color: #632F8E;
    101111  background-color: #632F8E; }
    102   .sf__button__logout.button:hover, .sf__button__logout.button:active, .sf__button__logout.button:focus {
     112  .sf__button__secondary.button:hover, .sf__button__secondary.button:active, .sf__button__secondary.button:focus, .sf__button__logout.button:hover, .sf__button__logout.button:active, .sf__button__logout.button:focus {
    103113    color: #fff;
    104114    border-color: #56297b;
     
    222232      flex-basis: 33%; } }
    223233
    224 /*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLmNzcyIsInNvdXJjZXMiOlsiYXBwLnNjc3MiLCJtYWluL19taXhpbnMuc2NzcyIsIm1haW4vX2NvbG9ycy5zY3NzIiwibXVsdGkvX211bHRpLnNjc3MiLCJtYWluL19zZi5zY3NzIl0sInNvdXJjZXNDb250ZW50IjpbIkBpbXBvcnQgXCJtYWluL21peGluc1wiO1xuQGltcG9ydCBcIm1haW4vY29sb3JzXCI7XG5AaW1wb3J0IFwibXVsdGkvbXVsdGlcIjtcbkBpbXBvcnQgXCJtYWluL3NmXCI7IiwiLy8gQm91cmJvbidzIGVzc2VudGlhbHMgbWl4aW5zXG4kZW0tYmFzZTogMTZweCAhZGVmYXVsdDtcbi8vIFN0cmlwIHVuaXRzXG5AZnVuY3Rpb24gc3RyaXAtdW5pdHMoJHZhbHVlKSB7XG5cdEByZXR1cm4gKCR2YWx1ZSAvICgkdmFsdWUgKiAwICsgMSkpO1xufVxuLy8gUFggdG8gRU1cbkBmdW5jdGlvbiBlbSgkcHh2YWwsICRiYXNlOiAkZW0tYmFzZSkge1xuXHRAaWYgbm90IHVuaXRsZXNzKCRweHZhbCkge1xuXHQgICRweHZhbDogc3RyaXAtdW5pdHMoJHB4dmFsKTtcblx0fVxuXHRAaWYgbm90IHVuaXRsZXNzKCRiYXNlKSB7XG5cdCAgJGJhc2U6IHN0cmlwLXVuaXRzKCRiYXNlKTtcblx0fVxuXHRAcmV0dXJuICgkcHh2YWwgLyAkYmFzZSkgKiAxZW07XG59XG4vL0JyZWFrcG9pbnRzXG4kYnJlYWtwb2ludHM6IChcbiAgICB4c206IGVtKDQ4MCksXG4gICAgc206IGVtKDc2OCksXG4gICAgbWQ6IGVtKDEwMjQpLFxuICAgIGxnOiBlbSgxMjYwKSxcbiAgICB4bGc6IGVtKDE0NDApXG4pO1xuXG5AbWl4aW4gYnJlYWtwb2ludHMoJGJyZWFrcG9pbnQpIHtcbiAgICBAbWVkaWEgc2NyZWVuIGFuZCAobWluLXdpZHRoOiBtYXAtZ2V0KCRtYXA6ICRicmVha3BvaW50cywgJGtleTogJGJyZWFrcG9pbnQpKSB7XG4gICAgICAgIEBjb250ZW50O1xuICAgIH1cbn1cbi8vIEhvdmVyIEFjdGl2ZSBGb2N1cyBwc2V1ZG8gc2VsZWN0b3IgbWl4aW5cbkBtaXhpbiBob3ZlciB7XG5cdCY6aG92ZXIsICY6YWN0aXZlLCAmOmZvY3VzIHtcblx0XHRAY29udGVudDtcblx0fVxufVxuLy8gU21vb3RoIHRleHRcbkBtaXhpbiBzbW9vdGhUZXh0IHtcblx0LXdlYmtpdC1mb250LXNtb290aGluZzogYW50aWFsaWFzZWQ7XG4gICAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbn0iLCIkd2hpdGU6ICNmZmY7XG4kYmxhY2s6ICMwMDA7XG4kcHJpbWFyeTogIzYzMkY4RTtcbiRzZWNvbmRhcnk6ICMxQ0I2OUI7XG4kdGV4dGNvbG9yOiAjMjMyODJEO1xuJGdyYXk6ICM5RTlFOUU7XG4kYmx1ZTogIzA0Mjc0RDtcbiRwaW5rOiAjRTkxRTYzO1xuIiwiLm11bHRpLXdyYXBwZXIge1xuICAgIGJvcmRlcjogMXB4IHNvbGlkICNjY2M7XG4gICAgYm9yZGVyLXJhZGl1czogM3B4O1xuICAgIHdpZHRoOiAxMDAlO1xufVxuXG4ubXVsdGktd3JhcHBlciAubm9uLXNlbGVjdGVkLXdyYXBwZXIsXG4ubXVsdGktd3JhcHBlciAuc2VsZWN0ZWQtd3JhcHBlciB7XG4gICAgYm94LXNpemluZzogYm9yZGVyLWJveDtcbiAgICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG4gICAgaGVpZ2h0OiAyMDBweDtcbiAgICBvdmVyZmxvdy15OiBzY3JvbGw7XG4gICAgcGFkZGluZzogMTBweDtcbiAgICB2ZXJ0aWNhbC1hbGlnbjogdG9wO1xuICAgIHdpZHRoOiA1MCU7XG59XG5cbi5tdWx0aS13cmFwcGVyIC5ub24tc2VsZWN0ZWQtd3JhcHBlciB7XG4gICAgYmFja2dyb3VuZDogI2ZhZmFmYTtcbiAgICBib3JkZXItcmlnaHQ6IDFweCBzb2xpZCAjY2NjO1xufVxuXG4ubXVsdGktd3JhcHBlciAuc2VsZWN0ZWQtd3JhcHBlciB7XG4gICAgYmFja2dyb3VuZDogI2ZmZjtcbn1cblxuLm11bHRpLXdyYXBwZXIgLmhlYWRlciB7XG4gICAgY29sb3I6ICM0ZjRmNGY7XG4gICAgY3Vyc29yOiBkZWZhdWx0O1xuICAgIGZvbnQtd2VpZ2h0OiBib2xkO1xuICAgIG1hcmdpbi1ib3R0b206IDVweDtcbiAgICBwYWRkaW5nOiA1cHggMTBweDtcbn1cblxuLm11bHRpLXdyYXBwZXIgLml0ZW0ge1xuICAgIGN1cnNvcjogcG9pbnRlcjtcbiAgICBkaXNwbGF5OiBibG9jaztcbiAgICBwYWRkaW5nOiA1cHggMTBweDtcbn1cblxuLm11bHRpLXdyYXBwZXIgLml0ZW06aG92ZXIge1xuICAgIGJhY2tncm91bmQ6ICNlY2VjZWM7XG4gICAgYm9yZGVyLXJhZGl1czogMnB4O1xufVxuXG4ubXVsdGktd3JhcHBlciAuaXRlbS1ncm91cCB7XG4gICAgcGFkZGluZzogNXB4IDEwcHg7XG59XG5cbi5tdWx0aS13cmFwcGVyIC5pdGVtLWdyb3VwIC5ncm91cC1sYWJlbCB7XG4gICAgZGlzcGxheTogYmxvY2s7XG4gICAgZm9udC1zaXplOiAwLjg3NXJlbTtcbiAgICBvcGFjaXR5OiAwLjU7XG4gICAgcGFkZGluZzogNXB4IDA7XG59XG5cbi5tdWx0aS13cmFwcGVyIC5zZWFyY2gtaW5wdXQge1xuICAgIGJvcmRlcjogMDtcbiAgICBib3JkZXItYm90dG9tOiAxcHggc29saWQgI2NjYztcbiAgICBib3JkZXItcmFkaXVzOiAwO1xuICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgIGZvbnQtc2l6ZTogMWVtO1xuICAgIG1hcmdpbjogMDtcbiAgICBvdXRsaW5lOiAwO1xuICAgIHBhZGRpbmc6IDEwcHggMjBweDtcbiAgICB3aWR0aDogMTAwJTtcbiAgICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xufVxuXG4ubXVsdGktd3JhcHBlciAubm9uLXNlbGVjdGVkLXdyYXBwZXIgLml0ZW0uc2VsZWN0ZWQge1xuICAgIG9wYWNpdHk6IDAuNTtcbn1cblxuLm11bHRpLXdyYXBwZXIgLm5vbi1zZWxlY3RlZC13cmFwcGVyIC5pdGVtLmRpc2FibGVkLFxuLm11bHRpLXdyYXBwZXIgLnNlbGVjdGVkLXdyYXBwZXIgLml0ZW0uZGlzYWJsZWQge1xuICAgIG9wYWNpdHk6IDAuNTtcbiAgICB0ZXh0LWRlY29yYXRpb246IGxpbmUtdGhyb3VnaDtcbn1cblxuLm11bHRpLXdyYXBwZXIgLm5vbi1zZWxlY3RlZC13cmFwcGVyIC5pdGVtLmRpc2FibGVkOmhvdmVyLFxuLm11bHRpLXdyYXBwZXIgLnNlbGVjdGVkLXdyYXBwZXIgLml0ZW0uZGlzYWJsZWQ6aG92ZXIge1xuICAgIGJhY2tncm91bmQ6IGluaGVyaXQ7XG4gICAgY3Vyc29yOiBpbmhlcml0O1xufSIsIi5zZiB7XG5cbiAgJl9fbG9nbyB7XG4gICAgb3ZlcmZsb3c6IGhpZGRlbjtcbiAgICB0ZXh0LWluZGVudDogLTk5OTk5cHg7XG4gICAgYmFja2dyb3VuZDogdXJsKFwiLi4vaW1hZ2VzL3NmX2xvZ28uc3ZnXCIpIG5vLXJlcGVhdCBjZW50ZXIgbGVmdDtcbiAgICBiYWNrZ3JvdW5kLXNpemU6IGNvbnRhaW47XG4gICAgbWF4LXdpZHRoOiAyOTdweDtcbiAgICBoZWlnaHQ6IDUzcHg7XG4gIH1cblxuICAmX192ZXJzaW9uIHtcbiAgICBjb2xvcjogJGdyYXk7XG4gICAgZm9udC1zaXplOiAxMnB4O1xuICAgIGxpbmUtaGVpZ2h0OiAxN3B4O1xuICAgIG1hcmdpbjogNXB4IDA7XG4gIH1cblxuICAmX19idXR0b24uYnV0dG9uIHtcbiAgICBjb2xvcjogJHdoaXRlO1xuICAgIHRleHQtdHJhbnNmb3JtOiB1cHBlcmNhc2U7XG4gICAgYm9yZGVyLWNvbG9yOiAkc2Vjb25kYXJ5O1xuICAgIGJhY2tncm91bmQtY29sb3I6ICRzZWNvbmRhcnk7XG4gICAgQGluY2x1ZGUgaG92ZXIge1xuICAgICAgY29sb3I6ICR3aGl0ZTtcbiAgICAgIGJvcmRlci1jb2xvcjogZGFya2VuKCRzZWNvbmRhcnksIDUlKTtcbiAgICAgIGJhY2tncm91bmQtY29sb3I6IGRhcmtlbigkc2Vjb25kYXJ5LCA1JSk7XG4gICAgfVxuICB9XG5cbiAgJl9fYnV0dG9uX19sb2dvdXQuYnV0dG9uIHtcbiAgICBjb2xvcjogJHdoaXRlO1xuICAgIHRleHQtdHJhbnNmb3JtOiB1cHBlcmNhc2U7XG4gICAgYm9yZGVyLWNvbG9yOiAkcHJpbWFyeTtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAkcHJpbWFyeTtcbiAgICBAaW5jbHVkZSBob3ZlciB7XG4gICAgICBjb2xvcjogJHdoaXRlO1xuICAgICAgYm9yZGVyLWNvbG9yOiBkYXJrZW4oJHByaW1hcnksIDUlKTtcbiAgICAgIGJhY2tncm91bmQtY29sb3I6IGRhcmtlbigkcHJpbWFyeSwgNSUpO1xuICAgIH1cbiAgfVxuXG4gICZfX3JlcXVpcmVtZW50cyB7XG4gICAgLnN1Y2Nlc3MsXG4gICAgLmZhaWxlZCB7XG4gICAgICBAaW5jbHVkZSBzbW9vdGhUZXh0O1xuICAgICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgICAgcGFkZGluZy1sZWZ0OiAyNnB4O1xuICAgICAgY29sb3I6ICR0ZXh0Y29sb3I7XG5cbiAgICAgICY6YmVmb3JlIHtcbiAgICAgICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgICAgICB0b3A6IDA7XG4gICAgICAgIGxlZnQ6IDA7XG4gICAgICAgIGZvbnQtZmFtaWx5OiBkYXNoaWNvbnM7XG4gICAgICAgIGNvbnRlbnQ6IFwiXFxmMTQ3XCI7XG4gICAgICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgICAgICB3aWR0aDogMTZweDtcbiAgICAgICAgaGVpZ2h0OiAxNnB4O1xuICAgICAgICBsaW5lLWhlaWdodDogMTZweDtcbiAgICAgICAgZm9udC1zaXplOiAxNHB4O1xuICAgICAgICBib3JkZXItcmFkaXVzOiA1MCU7XG4gICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjtcbiAgICAgICAgY29sb3I6ICR3aGl0ZTtcbiAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogJHNlY29uZGFyeTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAuZmFpbGVkIHtcbiAgICAgIGNvbG9yOiAkdGV4dGNvbG9yO1xuXG4gICAgICAmOmJlZm9yZSB7XG4gICAgICAgIGNvbnRlbnQ6IFwiXFxmMzM1XCI7XG4gICAgICAgIGJhY2tncm91bmQtY29sb3I6ICRncmF5O1xuICAgICAgfVxuICAgIH1cblxuICB9XG5cbiAgJl9faGVybyB7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgIGJvcmRlcjogMDtcbiAgICBib3JkZXItcmFkaXVzOiAycHg7XG4gICAgYmFja2dyb3VuZDogbGluZWFyLWdyYWRpZW50KDE4MGRlZywgIzAzMjc0RCAwJSwgIzUyMzA4MSAxMDAlKTtcbiAgICBiYWNrZ3JvdW5kLXNpemU6IGNvdmVyO1xuICAgIHBhZGRpbmc6IDMwcHg7XG5cbiAgICAmOmJlZm9yZSB7XG4gICAgICBjb250ZW50OiBcIlwiO1xuICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICB6LWluZGV4OiAxO1xuICAgICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgICAgdG9wOiAwO1xuICAgICAgcmlnaHQ6IDMwcHg7XG4gICAgICBib3R0b206IDA7XG4gICAgICBsZWZ0OiAwO1xuICAgICAgYmFja2dyb3VuZDogdXJsKFwiLi4vaW1hZ2VzL3NmX21hcmtldGluZy5zdmdcIikgbm8tcmVwZWF0IGNlbnRlciByaWdodDtcbiAgICAgIHBvaW50ZXItZXZlbnRzOiBub25lO1xuICAgIH1cbiAgICAmLS10aXRsZXtcbiAgICAgIGZvbnQtc2l6ZTogMTZweDtcbiAgICAgIGxpbmUtaGVpZ2h0OiAyMnB4O1xuICAgICAgcGFkZGluZy1yaWdodDogMzBweDtcbiAgICAgIGNvbG9yOiAkd2hpdGU7XG4gICAgICBmb250LXdlaWdodDogYm9sZDtcbiAgICAgIG1hcmdpbi1ib3R0b206IDE1cHg7XG4gICAgfVxuICB9XG5cbiAgJl9fbWFya2V0aW5nIHtcbiAgICBib3JkZXItbGVmdDogMXB4IHNvbGlkICRwcmltYXJ5O1xuICAgIHBhZGRpbmctbGVmdDogMjZweDtcblxuICAgICYtLXRpdGxle1xuICAgICAgY29sb3I6ICRwcmltYXJ5O1xuICAgICAgZm9udC1zaXplOiAxLjVlbTtcbiAgICAgIG1hcmdpbjogMDtcbiAgICAgIG1hcmdpbi10b3A6IDFlbTtcbiAgICB9XG4gICAgJi0tc3VidGl0bGV7XG4gICAgICBjb2xvcjogJGdyYXk7XG4gICAgICBmb250LXNpemU6IDEwcHg7XG4gICAgICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlO1xuICAgICAgbGV0dGVyLXNwYWNpbmc6IDMuMzNweDtcbiAgICAgIG1hcmdpbjogMDtcbiAgICAgIHBhZGRpbmctdG9wOiA1cHg7XG4gICAgfVxuICAgICYtLXRleHR7XG4gICAgICBjb2xvcjogJHRleHRjb2xvcjtcbiAgICAgIGZvbnQtc2l6ZTogMTJweDtcbiAgICAgIGxldHRlci1zcGFjaW5nOiAwO1xuICAgICAgbGluZS1oZWlnaHQ6IDE3cHg7XG4gICAgfVxuXG4gICAgJi0tZG93bmxvYWQge1xuICAgICAgcGFkZGluZzogMWVtIDA7XG5cbiAgICAgIGxpIHtcbiAgICAgICAgbGluZS1oZWlnaHQ6IDEuNWVtO1xuICAgICAgfVxuXG4gICAgICBhIHtcbiAgICAgICAgQGluY2x1ZGUgc21vb3RoVGV4dDtcbiAgICAgICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgICAgICBwYWRkaW5nLWxlZnQ6IDI2cHg7XG4gICAgICAgIGNvbG9yOiAkYmx1ZTtcbiAgICAgICAgZm9udC13ZWlnaHQ6IGJvbGQ7XG5cbiAgICAgICAgJjpiZWZvcmUge1xuICAgICAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAgICAgICB0b3A6IDA7XG4gICAgICAgICAgbGVmdDogMDtcbiAgICAgICAgICBmb250LWZhbWlseTogZGFzaGljb25zO1xuICAgICAgICAgIGNvbnRlbnQ6IFwiXFxmMzQ2XCI7XG4gICAgICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICAgICAgd2lkdGg6IDE2cHg7XG4gICAgICAgICAgaGVpZ2h0OiAxNnB4O1xuICAgICAgICAgIGxpbmUtaGVpZ2h0OiAxNnB4O1xuICAgICAgICAgIGZvbnQtc2l6ZTogMTRweDtcbiAgICAgICAgICBib3JkZXItcmFkaXVzOiA1MCU7XG4gICAgICAgICAgdGV4dC1hbGlnbjogY2VudGVyO1xuICAgICAgICAgIGNvbG9yOiAkd2hpdGU7XG4gICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogJHByaW1hcnk7XG4gICAgICAgIH1cblxuICAgICAgICAmOmhvdmVyIHtcbiAgICAgICAgICBjb2xvcjogJHByaW1hcnk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAmX19wbHVnaW57XG4gICAgLnNmX19ub3RpY2UtLXN0YXJ0IGF7XG4gICAgICBkaXNwbGF5OiBub25lO1xuICAgIH1cbiAgfVxuXG5cbiAgQGluY2x1ZGUgYnJlYWtwb2ludHMobWQpIHtcbiAgICAmX19jb2x1bW5zIHtcbiAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICBmbGV4LXdyYXA6IHdyYXA7XG4gICAgfVxuICAgICZfX2NvbHVtbiB7XG4gICAgICBmbGV4LWJhc2lzOiA2NiU7XG5cbiAgICAgICY6bGFzdC1jaGlsZCB7XG4gICAgICAgIGZsZXgtYmFzaXM6IDMzJTtcbiAgICAgIH1cbiAgICB9XG5cbiAgfVxufVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBR0FBLEFBQUEsY0FBYyxDQUFDO0VBQ1gsTUFBTSxFQUFFLGNBQWM7RUFDdEIsYUFBYSxFQUFFLEdBQUc7RUFDbEIsS0FBSyxFQUFFLElBQUksR0FDZDs7QUFFRCxBQUFBLGNBQWMsQ0FBQyxxQkFBcUI7QUFDcEMsY0FBYyxDQUFDLGlCQUFpQixDQUFDO0VBQzdCLFVBQVUsRUFBRSxVQUFVO0VBQ3RCLE9BQU8sRUFBRSxZQUFZO0VBQ3JCLE1BQU0sRUFBRSxLQUFLO0VBQ2IsVUFBVSxFQUFFLE1BQU07RUFDbEIsT0FBTyxFQUFFLElBQUk7RUFDYixjQUFjLEVBQUUsR0FBRztFQUNuQixLQUFLLEVBQUUsR0FBRyxHQUNiOztBQUVELEFBQUEsY0FBYyxDQUFDLHFCQUFxQixDQUFDO0VBQ2pDLFVBQVUsRUFBRSxPQUFPO0VBQ25CLFlBQVksRUFBRSxjQUFjLEdBQy9COztBQUVELEFBQUEsY0FBYyxDQUFDLGlCQUFpQixDQUFDO0VBQzdCLFVBQVUsRUFBRSxJQUFJLEdBQ25COztBQUVELEFBQUEsY0FBYyxDQUFDLE9BQU8sQ0FBQztFQUNuQixLQUFLLEVBQUUsT0FBTztFQUNkLE1BQU0sRUFBRSxPQUFPO0VBQ2YsV0FBVyxFQUFFLElBQUk7RUFDakIsYUFBYSxFQUFFLEdBQUc7RUFDbEIsT0FBTyxFQUFFLFFBQVEsR0FDcEI7O0FBRUQsQUFBQSxjQUFjLENBQUMsS0FBSyxDQUFDO0VBQ2pCLE1BQU0sRUFBRSxPQUFPO0VBQ2YsT0FBTyxFQUFFLEtBQUs7RUFDZCxPQUFPLEVBQUUsUUFBUSxHQUNwQjs7QUFFRCxBQUFBLGNBQWMsQ0FBQyxLQUFLLEFBQUEsTUFBTSxDQUFDO0VBQ3ZCLFVBQVUsRUFBRSxPQUFPO0VBQ25CLGFBQWEsRUFBRSxHQUFHLEdBQ3JCOztBQUVELEFBQUEsY0FBYyxDQUFDLFdBQVcsQ0FBQztFQUN2QixPQUFPLEVBQUUsUUFBUSxHQUNwQjs7QUFFRCxBQUFBLGNBQWMsQ0FBQyxXQUFXLENBQUMsWUFBWSxDQUFDO0VBQ3BDLE9BQU8sRUFBRSxLQUFLO0VBQ2QsU0FBUyxFQUFFLFFBQVE7RUFDbkIsT0FBTyxFQUFFLEdBQUc7RUFDWixPQUFPLEVBQUUsS0FBSyxHQUNqQjs7QUFFRCxBQUFBLGNBQWMsQ0FBQyxhQUFhLENBQUM7RUFDekIsTUFBTSxFQUFFLENBQUM7RUFDVCxhQUFhLEVBQUUsY0FBYztFQUM3QixhQUFhLEVBQUUsQ0FBQztFQUNoQixPQUFPLEVBQUUsS0FBSztFQUNkLFNBQVMsRUFBRSxHQUFHO0VBQ2QsTUFBTSxFQUFFLENBQUM7RUFDVCxPQUFPLEVBQUUsQ0FBQztFQUNWLE9BQU8sRUFBRSxTQUFTO0VBQ2xCLEtBQUssRUFBRSxJQUFJO0VBQ1gsVUFBVSxFQUFFLFVBQVUsR0FDekI7O0FBRUQsQUFBQSxjQUFjLENBQUMscUJBQXFCLENBQUMsS0FBSyxBQUFBLFNBQVMsQ0FBQztFQUNoRCxPQUFPLEVBQUUsR0FBRyxHQUNmOztBQUVELEFBQUEsY0FBYyxDQUFDLHFCQUFxQixDQUFDLEtBQUssQUFBQSxTQUFTO0FBQ25ELGNBQWMsQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLEFBQUEsU0FBUyxDQUFDO0VBQzVDLE9BQU8sRUFBRSxHQUFHO0VBQ1osZUFBZSxFQUFFLFlBQVksR0FDaEM7O0FBRUQsQUFBQSxjQUFjLENBQUMscUJBQXFCLENBQUMsS0FBSyxBQUFBLFNBQVMsQUFBQSxNQUFNO0FBQ3pELGNBQWMsQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLEFBQUEsU0FBUyxBQUFBLE1BQU0sQ0FBQztFQUNsRCxVQUFVLEVBQUUsT0FBTztFQUNuQixNQUFNLEVBQUUsT0FBTyxHQUNsQjs7QUNqRkUsQUFBRCxTQUFPLENBQUM7RUFDTixRQUFRLEVBQUUsTUFBTTtFQUNoQixXQUFXLEVBQUUsUUFBUTtFQUNyQixVQUFVLEVBQUUsNEJBQTRCLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxJQUFJO0VBQzlELGVBQWUsRUFBRSxPQUFPO0VBQ3hCLFNBQVMsRUFBRSxLQUFLO0VBQ2hCLE1BQU0sRUFBRSxJQUFJLEdBQ2I7O0FBRUEsQUFBRCxZQUFVLENBQUM7RUFDVCxLQUFLLEVGUEYsT0FBTztFRVFWLFNBQVMsRUFBRSxJQUFJO0VBQ2YsV0FBVyxFQUFFLElBQUk7RUFDakIsTUFBTSxFQUFFLEtBQUssR0FDZDs7QUFFQSxBQUFELFdBQVMsQUFBQSxPQUFPLENBQUM7RUFDZixLQUFLLEVGbkJELElBQUk7RUVvQlIsY0FBYyxFQUFFLFNBQVM7RUFDekIsWUFBWSxFRmxCSixPQUFPO0VFbUJmLGdCQUFnQixFRm5CUixPQUFPLEdFeUJoQjtFQVZBLEFIY0YsV0dkVSxBQUFBLE9BQU8sQUhjaEIsTUFBTSxFR2RMLFdBQVEsQUFBQSxPQUFPLEFIY1AsT0FBTyxFR2RmLFdBQVEsQUFBQSxPQUFPLEFIY0csTUFBTSxDQUFDO0lHUnRCLEtBQUssRUZ4QkgsSUFBSTtJRXlCTixZQUFZLEVBQUUsT0FBc0I7SUFDcEMsZ0JBQWdCLEVBQUUsT0FBc0IsR0hRNUM7O0FHSkMsQUFBRCxtQkFBaUIsQUFBQSxPQUFPLENBQUM7RUFDdkIsS0FBSyxFRi9CRCxJQUFJO0VFZ0NSLGNBQWMsRUFBRSxTQUFTO0VBQ3pCLFlBQVksRUYvQk4sT0FBTztFRWdDYixnQkFBZ0IsRUZoQ1YsT0FBTyxHRXNDZDtFQVZBLEFIRUYsbUJHRmtCLEFBQUEsT0FBTyxBSEV4QixNQUFNLEVHRkwsbUJBQWdCLEFBQUEsT0FBTyxBSEVmLE9BQU8sRUdGZixtQkFBZ0IsQUFBQSxPQUFPLEFIRUwsTUFBTSxDQUFDO0lHSXRCLEtBQUssRUZwQ0gsSUFBSTtJRXFDTixZQUFZLEVBQUUsT0FBb0I7SUFDbEMsZ0JBQWdCLEVBQUUsT0FBb0IsR0hKMUM7O0FHUUMsQUFDQyxpQkFEYSxDQUNiLFFBQVE7QUFEVCxpQkFBYyxDQUViLE9BQU8sQ0FBQztFSE5YLHNCQUFzQixFQUFFLFdBQVc7RUFDaEMsdUJBQXVCLEVBQUUsU0FBUztFR09oQyxRQUFRLEVBQUUsUUFBUTtFQUNsQixZQUFZLEVBQUUsSUFBSTtFQUNsQixLQUFLLEVGNUNDLE9BQU8sR0U4RGQ7RUF4QkYsQUFRRyxpQkFSVyxDQUNiLFFBQVEsQUFPTCxPQUFPO0VBUlgsaUJBQWMsQ0FFYixPQUFPLEFBTUosT0FBTyxDQUFDO0lBQ1AsUUFBUSxFQUFFLFFBQVE7SUFDbEIsR0FBRyxFQUFFLENBQUM7SUFDTixJQUFJLEVBQUUsQ0FBQztJQUNQLFdBQVcsRUFBRSxTQUFTO0lBQ3RCLE9BQU8sRUFBRSxPQUFPO0lBQ2hCLE9BQU8sRUFBRSxLQUFLO0lBQ2QsS0FBSyxFQUFFLElBQUk7SUFDWCxNQUFNLEVBQUUsSUFBSTtJQUNaLFdBQVcsRUFBRSxJQUFJO0lBQ2pCLFNBQVMsRUFBRSxJQUFJO0lBQ2YsYUFBYSxFQUFFLEdBQUc7SUFDbEIsVUFBVSxFQUFFLE1BQU07SUFDbEIsS0FBSyxFRi9ETCxJQUFJO0lFZ0VKLGdCQUFnQixFRjdEWixPQUFPLEdFOERaOztBQXZCSixBQTBCQyxpQkExQmEsQ0EwQmIsT0FBTyxDQUFDO0VBQ04sS0FBSyxFRmpFQyxPQUFPLEdFdUVkO0VBakNGLEFBNkJHLGlCQTdCVyxDQTBCYixPQUFPLEFBR0osT0FBTyxDQUFDO0lBQ1AsT0FBTyxFQUFFLE9BQU87SUFDaEIsZ0JBQWdCLEVGcEVqQixPQUFPLEdFcUVQOztBQUtKLEFBQUQsU0FBTyxDQUFDO0VBQ04sUUFBUSxFQUFFLFFBQVE7RUFDbEIsTUFBTSxFQUFFLENBQUM7RUFDVCxhQUFhLEVBQUUsR0FBRztFQUNsQixVQUFVLEVBQUUsaURBQWlEO0VBQzdELGVBQWUsRUFBRSxLQUFLO0VBQ3RCLE9BQU8sRUFBRSxJQUFJLEdBc0JkO0VBNUJBLEFBUUMsU0FSSyxBQVFKLE9BQU8sQ0FBQztJQUNQLE9BQU8sRUFBRSxFQUFFO0lBQ1gsT0FBTyxFQUFFLEtBQUs7SUFDZCxPQUFPLEVBQUUsQ0FBQztJQUNWLFFBQVEsRUFBRSxRQUFRO0lBQ2xCLEdBQUcsRUFBRSxDQUFDO0lBQ04sS0FBSyxFQUFFLElBQUk7SUFDWCxNQUFNLEVBQUUsQ0FBQztJQUNULElBQUksRUFBRSxDQUFDO0lBQ1AsVUFBVSxFQUFFLGlDQUFpQyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsS0FBSztJQUNwRSxjQUFjLEVBQUUsSUFBSSxHQUNyQjtFQUNBLEFBQUQsZ0JBQVEsQ0FBQTtJQUNOLFNBQVMsRUFBRSxJQUFJO0lBQ2YsV0FBVyxFQUFFLElBQUk7SUFDakIsYUFBYSxFQUFFLElBQUk7SUFDbkIsS0FBSyxFRnZHSCxJQUFJO0lFd0dOLFdBQVcsRUFBRSxJQUFJO0lBQ2pCLGFBQWEsRUFBRSxJQUFJLEdBQ3BCOztBQUdGLEFBQUQsY0FBWSxDQUFDO0VBQ1gsV0FBVyxFQUFFLEdBQUcsQ0FBQyxLQUFLLENGNUdoQixPQUFPO0VFNkdiLFlBQVksRUFBRSxJQUFJLEdBMkRuQjtFQXpERSxBQUFELHFCQUFRLENBQUE7SUFDTixLQUFLLEVGaEhELE9BQU87SUVpSFgsU0FBUyxFQUFFLEtBQUs7SUFDaEIsTUFBTSxFQUFFLENBQUM7SUFDVCxVQUFVLEVBQUUsR0FBRyxHQUNoQjtFQUNBLEFBQUQsd0JBQVcsQ0FBQTtJQUNULEtBQUssRUZuSEosT0FBTztJRW9IUixTQUFTLEVBQUUsSUFBSTtJQUNmLGNBQWMsRUFBRSxTQUFTO0lBQ3pCLGNBQWMsRUFBRSxNQUFNO0lBQ3RCLE1BQU0sRUFBRSxDQUFDO0lBQ1QsV0FBVyxFQUFFLEdBQUcsR0FDakI7RUFDQSxBQUFELG9CQUFPLENBQUE7SUFDTCxLQUFLLEVGNUhDLE9BQU87SUU2SGIsU0FBUyxFQUFFLElBQUk7SUFDZixjQUFjLEVBQUUsQ0FBQztJQUNqQixXQUFXLEVBQUUsSUFBSSxHQUNsQjtFQUVBLEFBQUQsd0JBQVcsQ0FBQztJQUNWLE9BQU8sRUFBRSxLQUFLLEdBa0NmO0lBbkNBLEFBR0Msd0JBSFMsQ0FHVCxFQUFFLENBQUM7TUFDRCxXQUFXLEVBQUUsS0FBSyxHQUNuQjtJQUxGLEFBT0Msd0JBUFMsQ0FPVCxDQUFDLENBQUM7TUh2R1Asc0JBQXNCLEVBQUUsV0FBVztNQUNoQyx1QkFBdUIsRUFBRSxTQUFTO01Hd0c5QixRQUFRLEVBQUUsUUFBUTtNQUNsQixZQUFZLEVBQUUsSUFBSTtNQUNsQixLQUFLLEVGM0lOLE9BQU87TUU0SU4sV0FBVyxFQUFFLElBQUksR0FzQmxCO01BbENGLEFBY0csd0JBZE8sQ0FPVCxDQUFDLEFBT0UsT0FBTyxDQUFDO1FBQ1AsUUFBUSxFQUFFLFFBQVE7UUFDbEIsR0FBRyxFQUFFLENBQUM7UUFDTixJQUFJLEVBQUUsQ0FBQztRQUNQLFdBQVcsRUFBRSxTQUFTO1FBQ3RCLE9BQU8sRUFBRSxPQUFPO1FBQ2hCLE9BQU8sRUFBRSxLQUFLO1FBQ2QsS0FBSyxFQUFFLElBQUk7UUFDWCxNQUFNLEVBQUUsSUFBSTtRQUNaLFdBQVcsRUFBRSxJQUFJO1FBQ2pCLFNBQVMsRUFBRSxJQUFJO1FBQ2YsYUFBYSxFQUFFLEdBQUc7UUFDbEIsVUFBVSxFQUFFLE1BQU07UUFDbEIsS0FBSyxFRmpLUCxJQUFJO1FFa0tGLGdCQUFnQixFRmhLaEIsT0FBTyxHRWlLUjtNQTdCSixBQStCRyx3QkEvQk8sQ0FPVCxDQUFDLEFBd0JFLE1BQU0sQ0FBQztRQUNOLEtBQUssRUZwS0wsT0FBTyxHRXFLUjs7QUFLTixBQUNDLFdBRE8sQ0FDUCxrQkFBa0IsQ0FBQyxDQUFDLENBQUE7RUFDbEIsT0FBTyxFQUFFLElBQUksR0FDZDs7QUhySkQsTUFBTSxDQUFDLE1BQU0sTUFBTSxTQUFTLEVBQUUsSUFBSTtFRzBKakMsQUFBRCxZQUFVLENBQUM7SUFDVCxPQUFPLEVBQUUsSUFBSTtJQUNiLFNBQVMsRUFBRSxJQUFJLEdBQ2hCO0VBQ0EsQUFBRCxXQUFTLENBQUM7SUFDUixVQUFVLEVBQUUsR0FBRyxHQUtoQjtJQU5BLEFBR0MsV0FITyxBQUdOLFdBQVcsQ0FBQztNQUNYLFVBQVUsRUFBRSxHQUFHLEdBQ2hCIn0= */
     234/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLmNzcyIsInNvdXJjZXMiOlsiYXBwLnNjc3MiLCJtYWluL19taXhpbnMuc2NzcyIsIm1haW4vX2NvbG9ycy5zY3NzIiwibXVsdGkvX211bHRpLnNjc3MiLCJtYWluL19zZi5zY3NzIl0sInNvdXJjZXNDb250ZW50IjpbIkBpbXBvcnQgXCJtYWluL21peGluc1wiO1xuQGltcG9ydCBcIm1haW4vY29sb3JzXCI7XG5AaW1wb3J0IFwibXVsdGkvbXVsdGlcIjtcbkBpbXBvcnQgXCJtYWluL3NmXCI7IiwiLy8gQm91cmJvbidzIGVzc2VudGlhbHMgbWl4aW5zXG4kZW0tYmFzZTogMTZweCAhZGVmYXVsdDtcbi8vIFN0cmlwIHVuaXRzXG5AZnVuY3Rpb24gc3RyaXAtdW5pdHMoJHZhbHVlKSB7XG5cdEByZXR1cm4gKCR2YWx1ZSAvICgkdmFsdWUgKiAwICsgMSkpO1xufVxuLy8gUFggdG8gRU1cbkBmdW5jdGlvbiBlbSgkcHh2YWwsICRiYXNlOiAkZW0tYmFzZSkge1xuXHRAaWYgbm90IHVuaXRsZXNzKCRweHZhbCkge1xuXHQgICRweHZhbDogc3RyaXAtdW5pdHMoJHB4dmFsKTtcblx0fVxuXHRAaWYgbm90IHVuaXRsZXNzKCRiYXNlKSB7XG5cdCAgJGJhc2U6IHN0cmlwLXVuaXRzKCRiYXNlKTtcblx0fVxuXHRAcmV0dXJuICgkcHh2YWwgLyAkYmFzZSkgKiAxZW07XG59XG4vL0JyZWFrcG9pbnRzXG4kYnJlYWtwb2ludHM6IChcbiAgICB4c206IGVtKDQ4MCksXG4gICAgc206IGVtKDc2OCksXG4gICAgbWQ6IGVtKDEwMjQpLFxuICAgIGxnOiBlbSgxMjYwKSxcbiAgICB4bGc6IGVtKDE0NDApXG4pO1xuXG5AbWl4aW4gYnJlYWtwb2ludHMoJGJyZWFrcG9pbnQpIHtcbiAgICBAbWVkaWEgc2NyZWVuIGFuZCAobWluLXdpZHRoOiBtYXAtZ2V0KCRtYXA6ICRicmVha3BvaW50cywgJGtleTogJGJyZWFrcG9pbnQpKSB7XG4gICAgICAgIEBjb250ZW50O1xuICAgIH1cbn1cbi8vIEhvdmVyIEFjdGl2ZSBGb2N1cyBwc2V1ZG8gc2VsZWN0b3IgbWl4aW5cbkBtaXhpbiBob3ZlciB7XG5cdCY6aG92ZXIsICY6YWN0aXZlLCAmOmZvY3VzIHtcblx0XHRAY29udGVudDtcblx0fVxufVxuLy8gU21vb3RoIHRleHRcbkBtaXhpbiBzbW9vdGhUZXh0IHtcblx0LXdlYmtpdC1mb250LXNtb290aGluZzogYW50aWFsaWFzZWQ7XG4gICAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbn0iLCIkd2hpdGU6ICNmZmY7XG4kYmxhY2s6ICMwMDA7XG4kcHJpbWFyeTogIzYzMkY4RTtcbiRzZWNvbmRhcnk6ICMxQ0I2OUI7XG4kdGV4dGNvbG9yOiAjMjMyODJEO1xuJGdyYXk6ICM5RTlFOUU7XG4kYmx1ZTogIzA0Mjc0RDtcbiRwaW5rOiAjRTkxRTYzO1xuIiwiLm11bHRpLXdyYXBwZXIge1xuICAgIGJvcmRlcjogMXB4IHNvbGlkICNjY2M7XG4gICAgYm9yZGVyLXJhZGl1czogM3B4O1xuICAgIHdpZHRoOiAxMDAlO1xufVxuXG4ubXVsdGktd3JhcHBlciAubm9uLXNlbGVjdGVkLXdyYXBwZXIsXG4ubXVsdGktd3JhcHBlciAuc2VsZWN0ZWQtd3JhcHBlciB7XG4gICAgYm94LXNpemluZzogYm9yZGVyLWJveDtcbiAgICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG4gICAgaGVpZ2h0OiAyMDBweDtcbiAgICBvdmVyZmxvdy15OiBzY3JvbGw7XG4gICAgcGFkZGluZzogMTBweDtcbiAgICB2ZXJ0aWNhbC1hbGlnbjogdG9wO1xuICAgIHdpZHRoOiA1MCU7XG59XG5cbi5tdWx0aS13cmFwcGVyIC5ub24tc2VsZWN0ZWQtd3JhcHBlciB7XG4gICAgYmFja2dyb3VuZDogI2ZhZmFmYTtcbiAgICBib3JkZXItcmlnaHQ6IDFweCBzb2xpZCAjY2NjO1xufVxuXG4ubXVsdGktd3JhcHBlciAuc2VsZWN0ZWQtd3JhcHBlciB7XG4gICAgYmFja2dyb3VuZDogI2ZmZjtcbn1cblxuLm11bHRpLXdyYXBwZXIgLmhlYWRlciB7XG4gICAgY29sb3I6ICM0ZjRmNGY7XG4gICAgY3Vyc29yOiBkZWZhdWx0O1xuICAgIGZvbnQtd2VpZ2h0OiBib2xkO1xuICAgIG1hcmdpbi1ib3R0b206IDVweDtcbiAgICBwYWRkaW5nOiA1cHggMTBweDtcbn1cblxuLm11bHRpLXdyYXBwZXIgLml0ZW0ge1xuICAgIGN1cnNvcjogcG9pbnRlcjtcbiAgICBkaXNwbGF5OiBibG9jaztcbiAgICBwYWRkaW5nOiA1cHggMTBweDtcbn1cblxuLm11bHRpLXdyYXBwZXIgLml0ZW06aG92ZXIge1xuICAgIGJhY2tncm91bmQ6ICNlY2VjZWM7XG4gICAgYm9yZGVyLXJhZGl1czogMnB4O1xufVxuXG4ubXVsdGktd3JhcHBlciAuaXRlbS1ncm91cCB7XG4gICAgcGFkZGluZzogNXB4IDEwcHg7XG59XG5cbi5tdWx0aS13cmFwcGVyIC5pdGVtLWdyb3VwIC5ncm91cC1sYWJlbCB7XG4gICAgZGlzcGxheTogYmxvY2s7XG4gICAgZm9udC1zaXplOiAwLjg3NXJlbTtcbiAgICBvcGFjaXR5OiAwLjU7XG4gICAgcGFkZGluZzogNXB4IDA7XG59XG5cbi5tdWx0aS13cmFwcGVyIC5zZWFyY2gtaW5wdXQge1xuICAgIGJvcmRlcjogMDtcbiAgICBib3JkZXItYm90dG9tOiAxcHggc29saWQgI2NjYztcbiAgICBib3JkZXItcmFkaXVzOiAwO1xuICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgIGZvbnQtc2l6ZTogMWVtO1xuICAgIG1hcmdpbjogMDtcbiAgICBvdXRsaW5lOiAwO1xuICAgIHBhZGRpbmc6IDEwcHggMjBweDtcbiAgICB3aWR0aDogMTAwJTtcbiAgICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xufVxuXG4ubXVsdGktd3JhcHBlciAubm9uLXNlbGVjdGVkLXdyYXBwZXIgLml0ZW0uc2VsZWN0ZWQge1xuICAgIG9wYWNpdHk6IDAuNTtcbn1cblxuLm11bHRpLXdyYXBwZXIgLm5vbi1zZWxlY3RlZC13cmFwcGVyIC5pdGVtLmRpc2FibGVkLFxuLm11bHRpLXdyYXBwZXIgLnNlbGVjdGVkLXdyYXBwZXIgLml0ZW0uZGlzYWJsZWQge1xuICAgIG9wYWNpdHk6IDAuNTtcbiAgICB0ZXh0LWRlY29yYXRpb246IGxpbmUtdGhyb3VnaDtcbn1cblxuLm11bHRpLXdyYXBwZXIgLm5vbi1zZWxlY3RlZC13cmFwcGVyIC5pdGVtLmRpc2FibGVkOmhvdmVyLFxuLm11bHRpLXdyYXBwZXIgLnNlbGVjdGVkLXdyYXBwZXIgLml0ZW0uZGlzYWJsZWQ6aG92ZXIge1xuICAgIGJhY2tncm91bmQ6IGluaGVyaXQ7XG4gICAgY3Vyc29yOiBpbmhlcml0O1xufSIsIi5zZiB7XG5cbiAgJl9faW5saW5le1xuICAgIGRpc3BsYXk6IGlubGluZS1ibG9jaztcbiAgICBtYXJnaW4tcmlnaHQ6IDA7XG4gIH1cblxuICAmX190YWJsZSB7XG4gICAgdHI6Zmlyc3QtY2hpbGQgYnV0dG9ue1xuICAgICAgZGlzcGxheTogbm9uZTtcbiAgICB9XG4gICAgdGQge1xuICAgICAgcGFkZGluZy1sZWZ0OiAwO1xuICAgIH1cbiAgfVxuXG4gICZfX2xvZ28ge1xuICAgIG92ZXJmbG93OiBoaWRkZW47XG4gICAgdGV4dC1pbmRlbnQ6IC05OTk5OXB4O1xuICAgIGJhY2tncm91bmQ6IHVybChcIi4uL2ltYWdlcy9zZl9sb2dvLnN2Z1wiKSBuby1yZXBlYXQgY2VudGVyIGxlZnQ7XG4gICAgYmFja2dyb3VuZC1zaXplOiBjb250YWluO1xuICAgIG1heC13aWR0aDogMjk3cHg7XG4gICAgaGVpZ2h0OiA1M3B4O1xuICB9XG5cbiAgJl9fdmVyc2lvbiB7XG4gICAgY29sb3I6ICRncmF5O1xuICAgIGZvbnQtc2l6ZTogMTJweDtcbiAgICBsaW5lLWhlaWdodDogMTdweDtcbiAgICBtYXJnaW46IDVweCAwO1xuICB9XG5cbiAgJl9fYnV0dG9uLmJ1dHRvbiB7XG4gICAgY29sb3I6ICR3aGl0ZTtcbiAgICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlO1xuICAgIGJvcmRlci1jb2xvcjogJHNlY29uZGFyeTtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAkc2Vjb25kYXJ5O1xuICAgIEBpbmNsdWRlIGhvdmVyIHtcbiAgICAgIGNvbG9yOiAkd2hpdGU7XG4gICAgICBib3JkZXItY29sb3I6IGRhcmtlbigkc2Vjb25kYXJ5LCA1JSk7XG4gICAgICBiYWNrZ3JvdW5kLWNvbG9yOiBkYXJrZW4oJHNlY29uZGFyeSwgNSUpO1xuICAgIH1cbiAgfVxuXG4gICZfX2J1dHRvbl9fc2Vjb25kYXJ5LmJ1dHRvbixcbiAgJl9fYnV0dG9uX19sb2dvdXQuYnV0dG9uIHtcbiAgICBjb2xvcjogJHdoaXRlO1xuICAgIHRleHQtdHJhbnNmb3JtOiB1cHBlcmNhc2U7XG4gICAgYm9yZGVyLWNvbG9yOiAkcHJpbWFyeTtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAkcHJpbWFyeTtcbiAgICBAaW5jbHVkZSBob3ZlciB7XG4gICAgICBjb2xvcjogJHdoaXRlO1xuICAgICAgYm9yZGVyLWNvbG9yOiBkYXJrZW4oJHByaW1hcnksIDUlKTtcbiAgICAgIGJhY2tncm91bmQtY29sb3I6IGRhcmtlbigkcHJpbWFyeSwgNSUpO1xuICAgIH1cbiAgfVxuXG4gICZfX3JlcXVpcmVtZW50cyB7XG4gICAgLnN1Y2Nlc3MsXG4gICAgLmZhaWxlZCB7XG4gICAgICBAaW5jbHVkZSBzbW9vdGhUZXh0O1xuICAgICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgICAgcGFkZGluZy1sZWZ0OiAyNnB4O1xuICAgICAgY29sb3I6ICR0ZXh0Y29sb3I7XG5cbiAgICAgICY6YmVmb3JlIHtcbiAgICAgICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgICAgICB0b3A6IDA7XG4gICAgICAgIGxlZnQ6IDA7XG4gICAgICAgIGZvbnQtZmFtaWx5OiBkYXNoaWNvbnM7XG4gICAgICAgIGNvbnRlbnQ6IFwiXFxmMTQ3XCI7XG4gICAgICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgICAgICB3aWR0aDogMTZweDtcbiAgICAgICAgaGVpZ2h0OiAxNnB4O1xuICAgICAgICBsaW5lLWhlaWdodDogMTZweDtcbiAgICAgICAgZm9udC1zaXplOiAxNHB4O1xuICAgICAgICBib3JkZXItcmFkaXVzOiA1MCU7XG4gICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjtcbiAgICAgICAgY29sb3I6ICR3aGl0ZTtcbiAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogJHNlY29uZGFyeTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAuZmFpbGVkIHtcbiAgICAgIGNvbG9yOiAkdGV4dGNvbG9yO1xuXG4gICAgICAmOmJlZm9yZSB7XG4gICAgICAgIGNvbnRlbnQ6IFwiXFxmMzM1XCI7XG4gICAgICAgIGJhY2tncm91bmQtY29sb3I6ICRncmF5O1xuICAgICAgfVxuICAgIH1cblxuICB9XG5cbiAgJl9faGVybyB7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgIGJvcmRlcjogMDtcbiAgICBib3JkZXItcmFkaXVzOiAycHg7XG4gICAgYmFja2dyb3VuZDogbGluZWFyLWdyYWRpZW50KDE4MGRlZywgIzAzMjc0RCAwJSwgIzUyMzA4MSAxMDAlKTtcbiAgICBiYWNrZ3JvdW5kLXNpemU6IGNvdmVyO1xuICAgIHBhZGRpbmc6IDMwcHg7XG5cbiAgICAmOmJlZm9yZSB7XG4gICAgICBjb250ZW50OiBcIlwiO1xuICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICB6LWluZGV4OiAxO1xuICAgICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgICAgdG9wOiAwO1xuICAgICAgcmlnaHQ6IDMwcHg7XG4gICAgICBib3R0b206IDA7XG4gICAgICBsZWZ0OiAwO1xuICAgICAgYmFja2dyb3VuZDogdXJsKFwiLi4vaW1hZ2VzL3NmX21hcmtldGluZy5zdmdcIikgbm8tcmVwZWF0IGNlbnRlciByaWdodDtcbiAgICAgIHBvaW50ZXItZXZlbnRzOiBub25lO1xuICAgIH1cbiAgICAmLS10aXRsZXtcbiAgICAgIGZvbnQtc2l6ZTogMTZweDtcbiAgICAgIGxpbmUtaGVpZ2h0OiAyMnB4O1xuICAgICAgcGFkZGluZy1yaWdodDogMzBweDtcbiAgICAgIGNvbG9yOiAkd2hpdGU7XG4gICAgICBmb250LXdlaWdodDogYm9sZDtcbiAgICAgIG1hcmdpbi1ib3R0b206IDE1cHg7XG4gICAgfVxuICB9XG5cbiAgJl9fbWFya2V0aW5nIHtcbiAgICBib3JkZXItbGVmdDogMXB4IHNvbGlkICRwcmltYXJ5O1xuICAgIHBhZGRpbmctbGVmdDogMjZweDtcblxuICAgICYtLXRpdGxle1xuICAgICAgY29sb3I6ICRwcmltYXJ5O1xuICAgICAgZm9udC1zaXplOiAxLjVlbTtcbiAgICAgIG1hcmdpbjogMDtcbiAgICAgIG1hcmdpbi10b3A6IDFlbTtcbiAgICB9XG4gICAgJi0tc3VidGl0bGV7XG4gICAgICBjb2xvcjogJGdyYXk7XG4gICAgICBmb250LXNpemU6IDEwcHg7XG4gICAgICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlO1xuICAgICAgbGV0dGVyLXNwYWNpbmc6IDMuMzNweDtcbiAgICAgIG1hcmdpbjogMDtcbiAgICAgIHBhZGRpbmctdG9wOiA1cHg7XG4gICAgfVxuICAgICYtLXRleHR7XG4gICAgICBjb2xvcjogJHRleHRjb2xvcjtcbiAgICAgIGZvbnQtc2l6ZTogMTJweDtcbiAgICAgIGxldHRlci1zcGFjaW5nOiAwO1xuICAgICAgbGluZS1oZWlnaHQ6IDE3cHg7XG4gICAgfVxuXG4gICAgJi0tZG93bmxvYWQge1xuICAgICAgcGFkZGluZzogMWVtIDA7XG5cbiAgICAgIGxpIHtcbiAgICAgICAgbGluZS1oZWlnaHQ6IDEuNWVtO1xuICAgICAgfVxuXG4gICAgICBhIHtcbiAgICAgICAgQGluY2x1ZGUgc21vb3RoVGV4dDtcbiAgICAgICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgICAgICBwYWRkaW5nLWxlZnQ6IDI2cHg7XG4gICAgICAgIGNvbG9yOiAkYmx1ZTtcbiAgICAgICAgZm9udC13ZWlnaHQ6IGJvbGQ7XG5cbiAgICAgICAgJjpiZWZvcmUge1xuICAgICAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAgICAgICB0b3A6IDA7XG4gICAgICAgICAgbGVmdDogMDtcbiAgICAgICAgICBmb250LWZhbWlseTogZGFzaGljb25zO1xuICAgICAgICAgIGNvbnRlbnQ6IFwiXFxmMzQ2XCI7XG4gICAgICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICAgICAgd2lkdGg6IDE2cHg7XG4gICAgICAgICAgaGVpZ2h0OiAxNnB4O1xuICAgICAgICAgIGxpbmUtaGVpZ2h0OiAxNnB4O1xuICAgICAgICAgIGZvbnQtc2l6ZTogMTRweDtcbiAgICAgICAgICBib3JkZXItcmFkaXVzOiA1MCU7XG4gICAgICAgICAgdGV4dC1hbGlnbjogY2VudGVyO1xuICAgICAgICAgIGNvbG9yOiAkd2hpdGU7XG4gICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogJHByaW1hcnk7XG4gICAgICAgIH1cblxuICAgICAgICAmOmhvdmVyIHtcbiAgICAgICAgICBjb2xvcjogJHByaW1hcnk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAmX19wbHVnaW57XG4gICAgLnNmX19ub3RpY2UtLXN0YXJ0IGF7XG4gICAgICBkaXNwbGF5OiBub25lO1xuICAgIH1cbiAgfVxuXG5cbiAgQGluY2x1ZGUgYnJlYWtwb2ludHMobWQpIHtcbiAgICAmX19jb2x1bW5zIHtcbiAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICBmbGV4LXdyYXA6IHdyYXA7XG4gICAgfVxuICAgICZfX2NvbHVtbiB7XG4gICAgICBmbGV4LWJhc2lzOiA2NiU7XG5cbiAgICAgICY6bGFzdC1jaGlsZCB7XG4gICAgICAgIGZsZXgtYmFzaXM6IDMzJTtcbiAgICAgIH1cbiAgICB9XG5cbiAgfVxufVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBR0FBLEFBQUEsY0FBYyxDQUFDO0VBQ1gsTUFBTSxFQUFFLGNBQWM7RUFDdEIsYUFBYSxFQUFFLEdBQUc7RUFDbEIsS0FBSyxFQUFFLElBQUksR0FDZDs7QUFF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 */
  • shopping-feed/trunk/assets/scss/main/_sf.scss

    r2339808 r2670662  
    11.sf {
     2
     3  &__inline{
     4    display: inline-block;
     5    margin-right: 0;
     6  }
     7
     8  &__table {
     9    tr:first-child button{
     10      display: none;
     11    }
     12    td {
     13      padding-left: 0;
     14    }
     15  }
    216
    317  &__logo {
     
    2943  }
    3044
     45  &__button__secondary.button,
    3146  &__button__logout.button {
    3247    color: $white;
  • shopping-feed/trunk/readme.txt

    r2613147 r2670662  
    22Contributors: ShoppingFeed, BeAPI
    33Tags: shoppingfeed, marketplace, woocommerce, woocommerce shoppingfeed, create woocommerce products shoppingfeed, products feed, generate shoppingfeed, amazon, Jet, Walmart, many marketplace, import orders
    4 Stable tag: 6.0.33
    5 Version: 6.0.33
     4Stable tag: 6.1.0
     5Version: 6.1.0
    66Requires PHP: 5.6
    77Requires at least: 5.2
     
    46466.0.31 Do not send mails to other customers
    47476.0.32 Add link to WC logs
    48 6.0.33 Fix priority issue with other plugins
     486.1.0 Add the possibility to connect multiple ShoppingFeed accounts to one WC shop
    4949
    5050== Description ==
  • shopping-feed/trunk/shoppingfeed.php

    r2613147 r2670662  
    88 * Text Domain:     shopping-feed
    99 * Domain Path:     /languages
    10  * Version:         6.0.33
     10 * Version:         6.1.0
    1111 * Requires at least WP: 5.7
    1212 * Requires at least WooCommerce: 5.1.0
     
    2626}
    2727
    28 define( 'SF_VERSION', '6.0.33' );
     28define( 'SF_VERSION', '6.1.0' );
     29define( 'SF_DB_VERSION_SLUG', 'SF_DB_VERSION' );
     30define( 'SF_DB_VERSION', '1.0.0' );
     31define( 'SF_UPGRADE_RUNNING', 'SF_UPGRADE_RUNNING' );
    2932define( 'SF_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    3033define( 'SF_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
     
    5356}
    5457
    55 \add_action( 'plugins_loaded', __NAMESPACE__ . '\\init', 100 );
     58\add_action( 'plugins_loaded', __NAMESPACE__ . '\\init', 99 );
  • shopping-feed/trunk/src/Actions/Actions.php

    r2339808 r2670662  
    3737     */
    3838    public static function register_get_orders() {
    39         as_schedule_recurring_action(
    40             time() + 60,
    41             ShoppingFeedHelper::get_sf_orders_import_frequency(),
    42             'sf_get_orders_action',
    43             array(),
    44             self::ORDERS_GROUP
    45         );
     39        $sf_accounts = ShoppingFeedHelper::get_sf_account_options();
     40        if ( empty( $sf_accounts ) ) {
     41            ShoppingFeedHelper::get_logger()->error(
     42                sprintf(
     43                    __( 'No Accounts founds', 'shopping-feed' )
     44                ),
     45                array(
     46                    'source' => 'shopping-feed',
     47                )
     48            );
     49
     50            return;
     51        }
     52        foreach ( $sf_accounts as $key => $sf_account ) {
     53            as_schedule_recurring_action(
     54                time() + 60,
     55                ShoppingFeedHelper::get_sf_orders_import_frequency(),
     56                'sf_get_orders_action_' . $key,
     57                array(
     58                    'sf_account' => $sf_account,
     59                ),
     60                self::ORDERS_GROUP
     61            );
     62        }
    4663    }
    4764
  • shopping-feed/trunk/src/Admin/Metabox.php

    r2339808 r2670662  
    6464        }
    6565
    66         $reference    = $order->get_meta( Query::$wc_meta_sf_reference );
    67         $channel_name = $order->get_meta( Query::$wc_meta_sf_channel_name );
     66        $reference    = $order->get_meta( Query::WC_META_SF_REFERENCE );
     67        $channel_name = $order->get_meta( Query::WC_META_SF_CHANNEL_NAME );
    6868
    6969        if ( empty( $reference ) && empty( $channel_name ) ) : ?>
  • shopping-feed/trunk/src/Admin/Notices.php

    r2339808 r2670662  
    2020
    2121    public function admin_notices() {
    22         $options = ShoppingFeedHelper::get_sf_account_options();
    23         if (
    24             empty( $options['token'] ) &&
    25             get_current_screen()->parent_base !== Options::SF_SLUG ) {
    26             if ( empty( $options['username'] ) || empty( $options['password'] ) ) {
    27                 $this->display_notice();
    28             }
    29         }
     22        //TODO: CHECK ALL CREDENTIALS
     23        return;
     24        //      $options = ShoppingFeedHelper::get_sf_account_options();
     25        //      if (
     26        //          empty( $options['token'] ) &&
     27        //          get_current_screen()->parent_base !== Options::SF_SLUG ) {
     28        //          if ( empty( $options['username'] ) || empty( $options['password'] ) ) {
     29        //              $this->display_notice();
     30        //          }
     31        //      }
    3032    }
    3133
  • shopping-feed/trunk/src/Admin/Options.php

    r2602861 r2670662  
    5858    /** @var array $sf_orders_options */
    5959    private $sf_orders_options;
    60     /** @var bool $connected */
    61     private $connected = true;
     60
    6261
    6362    /**
     
    114113        );
    115114
     115        /**
     116         * Clean and register new cron once account option updated
     117         */
    116118        add_action(
    117             'admin_action_sf_logout',
    118             function () {
    119                 delete_option( Options::SF_ACCOUNT_OPTIONS );
    120                 wp_safe_redirect( ShoppingFeedHelper::get_setting_link(), 302 );
     119            'admin_action_update',
     120            function () {
     121                Actions::clean_get_orders();
     122                Actions::register_get_orders();
    121123            }
    122124        );
     
    225227     */
    226228    public function init_account_setting_page() {
    227             $this->connected = ShoppingFeedHelper::is_authenticated();
    228 
    229229            //check clean action
    230230            $this->check_clean_action();
     
    240240                self::SF_ACCOUNT_SETTINGS_PAGE
    241241            );
    242 
    243             add_settings_field(
    244                 'username',
    245                 __( 'Username', 'shopping-feed' ),
    246                 function () {
    247                     printf(
    248                         '<input class="regular-text" type="text" name="sf_account_options[username]" id="username" value="%s">',
    249                         isset( $this->sf_account_options['username'] ) ? esc_attr( $this->sf_account_options['username'] ) : ''
    250                     );
    251                 },
    252                 self::SF_ACCOUNT_SETTINGS_PAGE,
    253                 'sf_account_settings'
    254             );
    255 
    256             add_settings_field(
    257                 'password',
    258                 __( 'Password', 'shopping-feed' ),
    259                 function () {
    260                     printf(
    261                         '<input class="regular-text" type="password" name="sf_account_options[password]" id="password" value="%s">',
    262                         isset( $this->sf_account_options['password'] ) ? esc_attr( $this->sf_account_options['password'] ) : ''
    263                     );
    264                 },
    265                 self::SF_ACCOUNT_SETTINGS_PAGE,
    266                 'sf_account_settings'
    267             );
    268 
    269         if ( $this->connected ) {
    270             add_settings_field(
    271                 'url',
    272                 __( 'Your source feed', 'shopping-feed' ),
    273                 function () {
    274                     $sf_feed_public_url = ShoppingFeedHelper::get_public_feed_url();
    275                     $sf_process_running = ShoppingFeedHelper::generation_process_running();
    276                     $sf_last_generation_date = get_option( Generator::SF_FEED_LAST_GENERATION_DATE );
    277                     ?>
    278                     <?php if ( ! $sf_process_running ) : ?>
    279                         <a href="<?php echo esc_html( $sf_feed_public_url ); ?>" target="_blank">
    280                             <?php
    281                             echo esc_url( $sf_feed_public_url );
    282                             ;
    283                             ?>
    284                         </a>
    285                     <?php endif; ?>
    286                     <br>
    287                     <p>
    288                         <?php if ( ! $sf_process_running ) : ?>
    289                             <?php esc_html_e( 'Last update', 'shopping-feed' ); ?> :
    290                             <?php
    291                             ! empty( $sf_last_generation_date ) ? esc_html( $sf_last_generation_date ) : esc_html_e( 'Never', 'shopping-feed' );
    292                             ?>
    293                             <a href="<?php echo esc_url( ShoppingFeedHelper::get_public_feed_url_with_generation() ); ?>" target="_blank">
    294                                 <?php esc_html_e( 'Refresh', 'shopping-feed' ); ?>
    295                             </a>
    296                         <?php else : ?>
    297                             <strong>(<?php esc_html_e( 'The feed is update is running', 'shopping-feed' ); ?>) <a href="#" onClick="window.location.reload();"><?php esc_html_e( 'Refresh to check progress', 'shopping-feed' ); ?></a></strong>
    298                         <?php endif; ?>
    299                     </p>
    300                     <?php
    301                 },
    302                 self::SF_ACCOUNT_SETTINGS_PAGE,
    303                 'sf_account_settings'
    304             );
    305         }
    306 
    307             add_settings_field(
    308                 'status',
    309                 __( 'Status', 'shopping-feed' ),
    310                 function () {
    311                     ?>
    312                 <div class="notice-<?php echo esc_html( $this->connected ? 'success' : 'error' ); ?> regular-text notice-alt">
    313                     <?php
    314                     echo esc_html( $this->connected ? _e( 'Connected', 'shopping-feed' ) : _e( 'Not connected', 'shopping-feed' ) );
    315                     ?>
    316                 </div>
    317                     <?php
    318                 },
    319                 self::SF_ACCOUNT_SETTINGS_PAGE,
    320                 'sf_account_settings'
    321             );
    322242        ?>
    323243        <div class="wrap">
    324244            <?php settings_errors(); ?>
    325245
    326 <div class="sf__columns">
    327         <div class="sf__column">
    328              <form method="post" action="options.php">
    329                 <?php
    330                 settings_fields( 'sf_account_page_fields' );
    331                 do_settings_sections( self::SF_ACCOUNT_SETTINGS_PAGE );
    332                 if ( ! $this->connected ) {
    333                     submit_button( __( 'Login', 'shopping-feed' ), 'sf__button' );
    334                 } else {
    335                     echo '<input class="hidden" name="action" value="sf_logout">';
    336                     submit_button( __( 'Logout', 'shopping-feed' ), 'sf__button__logout', 'logout' );
    337                 }
    338                 ?>
    339             </form>
    340         <div class="sf__requirements">
    341         <?php
    342         $requirements = Requirements::get_instance();
    343             echo wp_kses_post( $requirements->curl_requirement() );
    344             echo wp_kses_post( $requirements->php_requirement() );
    345             echo wp_kses_post( $requirements->openssl_requirement() );
    346             echo wp_kses_post( $requirements->account_requirement() );
    347             echo wp_kses_post( $requirements->uploads_directory_access_requirement() );
    348         ?>
     246            <div class="sf__columns">
     247                <div class="sf__column account__wrapper">
     248                    <form method="post" action="options.php">
     249                        <div class="blocks">
     250                            <div class="block_links">
     251                                <table class="form-table sf__table">
     252                                    <thead>
     253                                    <tr>
     254                                        <th> <?php esc_html_e( 'Username', 'shopping-feed' ); ?> </th>
     255                                        <th> <?php esc_html_e( 'Password', 'shopping-feed' ); ?> </th>
     256                                        <th></th>
     257                                    </tr>
     258                                    </thead>
     259                                    <tbody>
     260                                    <tr>
     261                                        <td><input class="regular-text user" type="text"
     262                                                   name="sf_account_options[0][username]"
     263                                                   value="<?php echo isset( $this->sf_account_options[0]['username'] ) ? esc_attr( $this->sf_account_options[0]['username'] ) : ''; ?>">
     264                                        </td>
     265                                        <td><input class="regular-text pass" type="password"
     266                                                   name="sf_account_options[0][password]"
     267                                                   value="<?php echo isset( $this->sf_account_options[0]['password'] ) ? esc_attr( $this->sf_account_options[0]['password'] ) : ''; ?>">
     268                                        </td>
     269                                        <td>
     270                                            <button class="button sf__button__secondary delete_link sf__button--delete">
     271                                                Delete
     272                                            </button>
     273                                        </td>
     274                                        <td class="hidden"><input name="sf_account_options[0][token]"
     275                                                                  value="<?php echo isset( $this->sf_account_options[0]['token'] ) ? esc_attr( $this->sf_account_options[0]['token'] ) : ''; ?>">
     276                                        </td>
     277                                        <td class="hidden"><input name="sf_account_options[0][sf_store_id]"
     278                                                                  value="<?php echo isset( $this->sf_account_options[0]['sf_store_id'] ) ? esc_attr( $this->sf_account_options[0]['sf_store_id'] ) : ''; ?>">
     279                                        </td>
     280                                    </tr>
     281                                    <?php
     282                                    if ( count( $this->sf_account_options ) > 1 ) {
     283                                        foreach ( $this->sf_account_options as $key => $sf_account_option ) {
     284                                            if ( 0 === $key ) {
     285                                                continue;
     286                                            }
     287                                            ?>
     288                                            <tr>
     289                                                <td><input class="regular-text user" type="text"
     290                                                           name="sf_account_options[<?php echo esc_attr( $key ); ?>][username]"
     291                                                           value="<?php echo isset( $this->sf_account_options[ $key ]['username'] ) ? esc_attr( $this->sf_account_options[ $key ]['username'] ) : ''; ?>">
     292                                                </td>
     293                                                <td><input class="regular-text pass" type="password"
     294                                                           name="sf_account_options[<?php echo esc_attr( $key ); ?>][password]"
     295                                                           value="<?php echo isset( $this->sf_account_options[ $key ]['password'] ) ? esc_attr( $this->sf_account_options[ $key ]['password'] ) : ''; ?>">
     296                                                </td>
     297                                                <td class="hidden"><input
     298                                                            name="sf_account_options[<?php echo esc_attr( $key ); ?>][token]"
     299                                                            value="<?php echo isset( $this->sf_account_options[ $key ]['token'] ) ? esc_attr( $this->sf_account_options[ $key ]['token'] ) : ''; ?>">
     300                                                </td>
     301                                                <td class="hidden"><input
     302                                                            name="sf_account_options[<?php echo esc_attr( $key ); ?>][sf_store_id]"
     303                                                            value="<?php echo isset( $this->sf_account_options[ $key ]['sf_store_id'] ) ? esc_attr( $this->sf_account_options[ $key ]['sf_store_id'] ) : ''; ?>">
     304                                                </td>
     305                                                <td>
     306                                                    <button class="button sf__button__secondary delete_link sf__button--delete">
     307                                                        Delete
     308                                                    </button>
     309                                                </td>
     310                                            </tr>
     311                                            <?php
     312                                        }
     313                                    }
     314                                    ?>
     315                                    </tbody>
     316                                </table>
     317                                <div class="sf__inline">
     318                                    <p>
     319                                        <button class="button sf__button__secondary add_link"><?php esc_html_e( 'Add account', 'shopping-feed' ); ?></button>
     320                                    </p>
     321                                </div>
     322                                <div class="sf__inline">
     323                                    <?php
     324                                    settings_fields( 'sf_account_page_fields' );
     325                                    submit_button( __( 'Save', 'shopping-feed' ), 'sf__button' );
     326                                    ?>
     327                                </div>
     328                    </form>
     329                </div>
     330            </div>
     331            <!-- Line template -->
     332            <script type="text/html" id="tpl-line">
     333                <tr>
     334                    <td><input class="regular-text user" type="text" name="sf_account_options[<%= row %>][username]"
     335                               value="<%= user %>"></td>
     336                    <td><input class="regular-text pass" type="password" name="sf_account_options[<%= row %>][password]"
     337                               value="<%= pass %>"></td>
     338                    <td>
     339                        <button class="button sf__button__secondary sf__button--delete delete_link">Delete</button>
     340                    </td>
     341                </tr>
     342            </script>
     343            <div class="sf__requirements">
     344                <?php
     345                $requirements = Requirements::get_instance();
     346                echo wp_kses_post( $requirements->curl_requirement() );
     347                echo wp_kses_post( $requirements->php_requirement() );
     348                echo wp_kses_post( $requirements->openssl_requirement() );
     349                echo wp_kses_post( $requirements->uploads_directory_access_requirement() );
     350                ?>
    349351                <!--        REQUIREMENTS     -->
    350352        </div>
     
    365367    private function check_clean_action() {
    366368        if ( ! empty( $_GET['clean_process'] ) ) {
    367             ShoppingFeedHelper::clean_generation_process_running();
     369            ShoppingFeedHelper::clean_process_running( 'sf_feed_generation_process' );
    368370        }
    369371    }
     
    385387        );
    386388
     389        wp_enqueue_script(
     390            'accounts',
     391            SF_PLUGIN_URL . 'assets/js/accounts.js',
     392            array( 'jquery', 'underscore' ),
     393            true,
     394            true
     395        );
     396
    387397        wp_enqueue_script( 'multi_js_init', SF_PLUGIN_URL . 'assets/js/init.js', array( 'multi_js' ), true );
    388398        wp_localize_script(
     
    390400            'sf_options',
    391401            array(
    392                 'selected_orders'            => __( 'Selected order status', 'shopping-feed' ),
     402                'selected_orders'   => __( 'Selected order status', 'shopping-feed' ),
    393403                'unselected_orders' => __( 'Unselected order status', 'shopping-feed' ),
    394                 'search' => __( 'Search', 'shopping-feed' ),
     404                'search'            => __( 'Search', 'shopping-feed' ),
    395405            )
    396406        );
     
    401411     */
    402412    private function init_feed_setting_page() {
    403         $this->check_connection();
    404413
    405414        //load assets
     
    413422            },
    414423            self::SF_FEED_SETTINGS_PAGE
     424        );
     425
     426        add_settings_field(
     427            'url',
     428            __( 'Your source feed', 'shopping-feed' ),
     429            function () {
     430                $sf_feed_public_url      = ShoppingFeedHelper::get_public_feed_url();
     431                $sf_process_running      = ShoppingFeedHelper::is_process_running( 'sf_feed_generation_process' );
     432                $sf_last_generation_date = get_option( Generator::SF_FEED_LAST_GENERATION_DATE );
     433                ?>
     434                <?php if ( ! $sf_process_running ) : ?>
     435                    <a href="<?php echo esc_html( $sf_feed_public_url ); ?>" target="_blank">
     436                        <?php
     437                        echo esc_url( $sf_feed_public_url );
     438                        ;
     439                        ?>
     440                    </a>
     441                <?php endif; ?>
     442                <br>
     443                <p>
     444                    <?php if ( ! $sf_process_running ) : ?>
     445                        <?php esc_html_e( 'Last update', 'shopping-feed' ); ?> :
     446                        <?php
     447                        echo ! empty( $sf_last_generation_date ) ? esc_html( $sf_last_generation_date ) : esc_html__( 'Never', 'shopping-feed' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
     448                        ?>
     449                        <a href="<?php echo esc_url( ShoppingFeedHelper::get_public_feed_url_with_generation() ); ?>" target="_blank">
     450                            <?php esc_html_e( 'Refresh', 'shopping-feed' ); ?>
     451                        </a>
     452                    <?php else : ?>
     453                        <strong>(<?php esc_html_e( 'The feed is update is running', 'shopping-feed' ); ?>) <a href="#" onClick="window.location.reload();"><?php esc_html_e( 'Refresh to check progress', 'shopping-feed' ); ?></a></strong>
     454                    <?php endif; ?>
     455                </p>
     456                <?php
     457            },
     458            self::SF_FEED_SETTINGS_PAGE,
     459            'sf_feed_settings_categories'
    415460        );
    416461
     
    554599            __( 'Batch size', 'shopping-feed' ),
    555600            function () {
    556                 $running_process = ShoppingFeedHelper::get_running_generation_feed_process();
     601                $running_process = ShoppingFeedHelper::get_running_process( 'sf_feed_generation_process' );
    557602                $running_process = is_array( $running_process ) ? count( $running_process ) : 0;
    558603                ?>
     
    657702
    658703    /**
    659      * Check connection with SF platform with correct credentials
    660      * If not redirect to Account page
    661      */
    662     private function check_connection() {
    663         if ( ! ShoppingFeedHelper::is_authenticated() ) {
    664             wp_safe_redirect( sprintf( '%s&no_connected=%s', ShoppingFeedHelper::get_setting_link(), 'true' ) );
    665         }
    666     }
    667 
    668     /**
    669704     * Define Shipping Page
    670705     */
    671706    private function init_shipping_setting_page() {
    672         $this->check_connection();
    673 
    674707        //load assets
    675708        $this->load_assets();
     
    826859
    827860    private function init_orders_setting_page() {
    828         $this->check_connection();
    829 
    830861        //load assets
    831862        $this->load_assets();
  • shopping-feed/trunk/src/Admin/Requirements.php

    r2542211 r2670662  
    8383     * @return string
    8484     */
    85     public function account_requirement() {
    86         return ( $this->valid_account() )
    87             ? '<p class="success">' . __( 'You are logged in your ShoppingFeed account.', 'shopping-feed' ) . '</p>'
    88             : '<p class="failed">' . __( 'You must be logged in your ShoppingFeed account.', 'shopping-feed' ) . '</p>';
    89     }
    90 
    91 
    92     /**
    93      * @return string
    94      */
    9585    public function uploads_directory_access_requirement() {
    9686        return ( $this->uploads_directory_writable() )
     
    137127
    138128    /**
    139      * Check if the user is logged in.
    140      *
    141      * @return bool
    142      */
    143     public function valid_account() {
    144         return ShoppingFeedHelper::is_authenticated();
    145     }
    146 
    147     /**
    148129     * Check if the uploads directory is writable
    149130     *
  • shopping-feed/trunk/src/Admin/WoocommerceActions.php

    r2542211 r2670662  
    1010use ShoppingFeed\Sdk\Api\Catalog\PricingUpdate;
    1111use ShoppingFeed\Sdk\Api\Store\StoreResource;
     12use ShoppingFeed\Sdk\Client;
     13use ShoppingFeed\Sdk\Credential;
    1214use ShoppingFeed\ShoppingFeedWC\Feed\AsyncGenerator;
    1315use ShoppingFeed\ShoppingFeedWC\Feed\Generator;
     
    1618use ShoppingFeed\ShoppingFeedWC\Orders\Orders;
    1719use ShoppingFeed\ShoppingFeedWC\Products\Product;
     20use ShoppingFeed\ShoppingFeedWC\Query\Query;
    1821use ShoppingFeed\ShoppingFeedWC\Sdk\Sdk;
    1922use ShoppingFeed\ShoppingFeedWC\ShoppingFeedHelper;
    20 
    2123
    2224/**
     
    6365        add_action(
    6466            'sf_feed_generation_combine_feed_parts',
    65             [
     67            array(
    6668                AsyncGenerator::get_instance(),
    6769                'combine_feed_parts',
    68             ]
     70            )
    6971        );
    7072
     
    8486
    8587        //Get Orders
    86         add_action(
    87             'sf_get_orders_action',
    88             function () {
    89                 Orders::get_instance()->get_orders();
    90             }
    91         );
     88        $sf_accounts = ShoppingFeedHelper::get_sf_account_options();
     89        if ( empty( $sf_accounts ) ) {
     90            ShoppingFeedHelper::get_logger()->error(
     91                sprintf(
     92                    __( 'No Accounts founds', 'shopping-feed' )
     93                ),
     94                array(
     95                    'source' => 'shopping-feed',
     96                )
     97            );
     98        } else {
     99            foreach ( $sf_accounts as $key => $sf_account ) {
     100                add_action(
     101                    'sf_get_orders_action_' . $key,
     102                    function ( $sf_account ) {
     103                        Orders::get_instance()->get_orders( $sf_account );
     104                    }
     105                );
     106            }
     107        }
    92108
    93109        //Acknowledge Remain Order
     
    102118
    103119        /**
     120         * Migrate old accounts to multi account format
     121         */
     122        add_action( 'sf_migrate_single_action', array( $this, 'migrate_old_accounts' ) );
     123
     124        /**
    104125         * Orders Statuses Mapping
    105126         * Each status with action
     
    107128         */
    108129        $statuses_actions = ShoppingFeedHelper::get_sf_statuses_actions();
    109         if ( 0 < $statuses_actions ) {
    110             foreach ( $statuses_actions as $sf_action => $wc_statuses ) {
    111                 if ( ! is_array( $wc_statuses ) || '' === $sf_action ) {
    112                     continue;
    113                 }
    114 
    115                 foreach ( $wc_statuses as $wc_status ) {
    116                     $status = str_replace( 'wc-', '', $wc_status );
    117                     $action = 'woocommerce_order_status_' . $status;
    118                     add_action(
    119                         $action,
    120                         function ( $order_id ) use ( $sf_action ) {
    121                             //if its not a sf order
    122                             if ( ! Order::is_sf_order( $order_id ) ) {
    123                                 return;
    124                             }
    125                             try {
    126                                 $operations = new Operations( $order_id );
    127                                 if ( ! method_exists( $operations, $sf_action ) ) {
    128                                     ShoppingFeedHelper::get_logger()->error(
    129                                         sprintf(
    130                                         /* translators: %s: Action name. */
    131                                             __( 'Cant find any matched method for %s', 'shopping-feed' ),
    132                                             $sf_action
    133                                         ),
    134                                         array(
    135                                             'source' => 'shopping-feed',
    136                                         )
    137                                     );
    138 
    139                                     return;
    140                                 }
    141                                 call_user_func( array( $operations, $sf_action ) );
    142                             } catch ( Exception $exception ) {
    143                                 wc_get_order( $order_id )->add_order_note(
    144                                     sprintf(
    145                                     /* translators: %s: Action */
    146                                         __( 'Failed to %s order', 'shopping-feed' ),
    147                                         ucfirst( $sf_action )
    148                                     )
    149                                 );
     130        if ( empty( $statuses_actions ) ) {
     131            return;
     132        }
     133        foreach ( $statuses_actions as $sf_action => $wc_statuses ) {
     134            if ( ! is_array( $wc_statuses ) || '' === $sf_action ) {
     135                continue;
     136            }
     137
     138            foreach ( $wc_statuses as $wc_status ) {
     139                $status = str_replace( 'wc-', '', $wc_status );
     140                $action = 'woocommerce_order_status_' . $status;
     141                add_action(
     142                    $action,
     143                    function ( $order_id ) use ( $sf_action ) {
     144                        //if its not a sf order
     145                        if ( ! Order::is_sf_order( $order_id ) ) {
     146                            return;
     147                        }
     148                        try {
     149                            $operations = new Operations( $order_id );
     150                            if ( ! method_exists( $operations, $sf_action ) ) {
    150151                                ShoppingFeedHelper::get_logger()->error(
    151152                                    sprintf(
    152                                     /* translators: %1$s: Action. %2$s: Error Message */
    153                                         __( 'Failed to %1$s order => %2$s', 'shopping-feed' ),
    154                                         ucfirst( $sf_action ),
    155                                         $exception->getMessage()
     153                                    /* translators: %s: Action name. */
     154                                        __( 'Cant find any matched method for %s', 'shopping-feed' ),
     155                                        $sf_action
    156156                                    ),
    157157                                    array(
     
    159159                                    )
    160160                                );
     161
     162                                return;
    161163                            }
     164                            call_user_func( array( $operations, $sf_action ) );
     165                        } catch ( Exception $exception ) {
     166                            wc_get_order( $order_id )->add_order_note(
     167                                sprintf(
     168                                /* translators: %s: Action */
     169                                    __( 'Failed to %s order', 'shopping-feed' ),
     170                                    ucfirst( $sf_action )
     171                                )
     172                            );
     173                            ShoppingFeedHelper::get_logger()->error(
     174                                sprintf(
     175                                /* translators: %1$s: Action. %2$s: Error Message */
     176                                    __( 'Failed to %1$s order => %2$s', 'shopping-feed' ),
     177                                    ucfirst( $sf_action ),
     178                                    $exception->getMessage()
     179                                ),
     180                                array(
     181                                    'source' => 'shopping-feed',
     182                                )
     183                            );
    162184                        }
    163                     );
    164                 }
     185                    }
     186                );
    165187            }
    166188        }
     
    182204     * @param $product_id
    183205     * @param bool $only_stock
    184      *
    185      * @return bool
    186206     */
    187207    public function update_product( $product_id, $only_stock = false ) {
    188         $sdk = Sdk::get_instance();
    189         if ( ! $sdk->get_default_shop() instanceof StoreResource ) {
    190             ShoppingFeedHelper::get_logger()->error( __( 'Can\'t find default shop', 'shopping-feed' ) );
    191 
    192             return false;
    193         }
    194 
    195         /** @var StoreResource $shop */
    196         $shop          = $sdk->get_default_shop();
    197         $pricing_api   = $shop->getPricingApi();
    198         $inventory_api = $shop->getInventoryApi();
    199 
    200         $this->pricing_update   = new \ShoppingFeed\Sdk\Api\Catalog\PricingUpdate();
    201         $this->inventory_update = new \ShoppingFeed\Sdk\Api\Catalog\InventoryUpdate();
    202 
    203         $product = new Product( $product_id );
    204         if ( $product->has_variations() ) {
    205             foreach ( $product->get_variations() as $variation ) {
    206                 if ( empty( $variation['sku'] ) ) {
    207                     ShoppingFeedHelper::get_logger()->warning(
    208                         sprintf(
    209                         /* translators: %s: Product ID. */
    210                             __( 'Cant update product without SKU => %s', 'shopping-feed' ),
    211                             $product_id
    212                         ),
    213                         array(
    214                             'source' => 'shopping-feed',
    215                         )
    216                     );
    217 
    218                     continue;
    219                 }
    220 
    221                 $this->may_update_pricing( $variation['sku'], ! empty( $variation['discount'] ) ? $variation['discount'] : $variation['price'] );
    222                 $this->may_update_inventory( $variation['sku'], $variation['quantity'] );
    223             }
    224         } else {
    225             if ( empty( $product->get_sku() ) ) {
    226                 ShoppingFeedHelper::get_logger()->warning(
     208        $sf_accounts = ShoppingFeedHelper::get_sf_account_options();
     209        if ( empty( $sf_accounts ) ) {
     210            ShoppingFeedHelper::get_logger()->error(
     211                sprintf(
     212                    __( 'No Accounts founds', 'shopping-feed' )
     213                ),
     214                array(
     215                    'source' => 'shopping-feed',
     216                )
     217            );
     218
     219            return;
     220        }
     221
     222        foreach ( $sf_accounts as $sf_account ) {
     223            $shop = Sdk::get_sf_shop( $sf_account );
     224            if ( ! $shop instanceof StoreResource ) {
     225                ShoppingFeedHelper::get_logger()->error(
    227226                    sprintf(
    228                     /* translators: %s: Product ID. */
    229                         __( 'Cant update product without SKU => %s', 'shopping-feed' ),
    230                         $product_id
     227                    /* translators: %s: Error message. */
     228                        __( 'Cannot retrieve shop from SDK for account : %s', 'shopping-feed' ),
     229                        $sf_account['username']
    231230                    ),
    232231                    array(
     
    234233                    )
    235234                );
     235                continue;
     236            }
     237
     238            $pricing_api   = $shop->getPricingApi();
     239            $inventory_api = $shop->getInventoryApi();
     240
     241            $this->pricing_update   = new \ShoppingFeed\Sdk\Api\Catalog\PricingUpdate();
     242            $this->inventory_update = new \ShoppingFeed\Sdk\Api\Catalog\InventoryUpdate();
     243
     244            $product = new Product( $product_id );
     245            if ( $product->has_variations() ) {
     246                if ( ! $this->update_variation_product( $product ) ) {
     247                    continue;
     248                }
     249            } else {
     250                if ( ! $this->update_normal_product( $product ) ) {
     251                    continue;
     252                }
     253            }
     254            /**
     255             * Check if we need to update the price or only the stock
     256             */
     257            if ( ! $only_stock ) {
     258                /**
     259                 * Send api request to update the price
     260                 */
     261                $pricing_api->execute( $this->pricing_update );
     262            }
     263
     264            /**
     265             * Send api request to update the inventory
     266             */
     267            $inventory_api->execute( $this->inventory_update );
     268        }
     269    }
     270
     271    /**
     272     * @param Product $product
     273     *
     274     * @psalm-suppress all
     275     */
     276    public function update_normal_product( $product ) {
     277        if ( empty( $product->get_sku() ) ) {
     278            ShoppingFeedHelper::get_logger()->warning(
     279                sprintf(
     280                /* translators: %s: Product ID. */
     281                    __( 'Cant update product without SKU => %s', 'shopping-feed' ),
     282                    $product->get_wc_product()->get_id()
     283                ),
     284                array(
     285                    'source' => 'shopping-feed',
     286                )
     287            );
     288
     289            return false;
     290        }
     291
     292        $this->may_update_pricing( $product->get_sku(), ! empty( $product->get_discount() ) ? $product->get_discount() : $product->get_price() );
     293        $this->may_update_inventory( $product->get_sku(), $product->get_quantity() );
     294
     295        return true;
     296    }
     297
     298    /**
     299     * @param Product $product
     300     *
     301     * @psalm-suppress all
     302     * @return bool
     303     */
     304    public function update_variation_product( $product ) {
     305        foreach ( $product->get_variations() as $variation ) {
     306            if ( empty( $variation['sku'] ) ) {
     307                ShoppingFeedHelper::get_logger()->warning(
     308                    sprintf(
     309                    /* translators: %s: Product ID. */
     310                        __( 'Cant update product without SKU => %s', 'shopping-feed' ),
     311                        $product->get_wc_product()->get_id()
     312                    ),
     313                    array(
     314                        'source' => 'shopping-feed',
     315                    )
     316                );
    236317
    237318                return false;
    238319            }
    239320
    240             $this->may_update_pricing( $product->get_sku(), ! empty( $product->get_discount() ) ? $product->get_discount() : $product->get_price() );
    241             $this->may_update_inventory( $product->get_sku(), $product->get_quantity() );
    242         }
    243         /**
    244          * Check if we need to update the price or only the stock
    245          */
    246         if ( ! $only_stock ) {
    247             /**
    248              * Send api request to update the price
    249              */
    250             $pricing_api->execute( $this->pricing_update );
    251         }
    252 
    253         /**
    254          * Send api request to update the inventory
    255          */
    256         $inventory_api->execute( $this->inventory_update );
     321            $this->may_update_pricing( $variation['sku'], ! empty( $variation['discount'] ) ? $variation['discount'] : $variation['price'] );
     322            $this->may_update_inventory( $variation['sku'], $variation['quantity'] );
     323        }
    257324
    258325        return true;
     
    306373        $this->inventory_update->add( $sku, intval( $inventory ) );
    307374    }
     375
     376    /**
     377     * @return bool
     378     * @throws Exception
     379     * @psalm-suppress all
     380     */
     381    public function migrate_old_accounts() {
     382        //migrate account data and retrieve account ID
     383        $account_options = ShoppingFeedHelper::get_sf_account_options();
     384
     385        if ( empty( $account_options['token'] ) ) {
     386            ShoppingFeedHelper::get_logger()->error(
     387                sprintf(
     388                    __( 'No token founds', 'shopping-feed' )
     389                ),
     390                array(
     391                    'source' => 'shopping-feed',
     392                )
     393            );
     394            ShoppingFeedHelper::end_upgrade();
     395
     396            return false;
     397        }
     398        try {
     399            $main_store = Client\Client::createSession( new Credential\Token( $account_options['token'] ) )->getMainStore();
     400            if ( is_null( $main_store ) ) {
     401                ShoppingFeedHelper::get_logger()->error(
     402                    sprintf(
     403                        __( 'Cant retrieve main store', 'shopping-feed' )
     404                    ),
     405                    array(
     406                        'source' => 'shopping-feed',
     407                    )
     408                );
     409
     410                ShoppingFeedHelper::end_upgrade();
     411
     412                return false;
     413            }
     414            $account_id            = $main_store->getId();
     415            $new_account_options   = array();
     416            $new_account_options[] = array(
     417                'sf_store_id' => $account_id,
     418                'username'    => ! empty( $account_options['username'] ) ? $account_options['username'] : '',
     419                'password'    => ! empty( $account_options['password'] ) ? $account_options['password'] : '',
     420                'token'       => $account_options['token'],
     421            );
     422            ShoppingFeedHelper::set_sf_account_options( $new_account_options );
     423        } catch ( \Exception $exception ) {
     424            ShoppingFeedHelper::get_logger()->error(
     425                sprintf(
     426                /* translators: %s: Error message. */
     427                    __( 'Cant login with actual credentials => %s', 'shopping-feed' ),
     428                    $exception->getMessage()
     429                ),
     430                array(
     431                    'source' => 'shopping-feed',
     432                )
     433            );
     434
     435            ShoppingFeedHelper::end_upgrade();
     436
     437            return false;
     438        }
     439
     440        //Migrate old orders to default account
     441        $args = array(
     442            'limit'        => - 1,
     443            'meta_key'     => Query::WC_META_SF_REFERENCE,
     444            'meta_compare' => 'EXISTS',
     445        );
     446
     447        $query     = new \WC_Order_Query( $args );
     448        $wc_orders = $query->get_orders();
     449        if ( empty( $wc_orders ) ) {
     450            ShoppingFeedHelper::get_logger()->error(
     451                sprintf(
     452                    __( 'No SF orders founds for migration', 'shopping-feed' )
     453                ),
     454                array(
     455                    'source' => 'shopping-feed',
     456                )
     457            );
     458            ShoppingFeedHelper::end_upgrade();
     459
     460            return false;
     461        }
     462
     463        foreach ( $wc_orders as $wc_order ) {
     464            //add store id
     465            /** @var \WC_Order $wc_order */
     466            $wc_order->add_meta_data( Query::WC_META_SF_STORE_ID, $account_id );
     467            $wc_order->save();
     468        }
     469
     470        ShoppingFeedHelper::end_upgrade();
     471
     472        return true;
     473    }
    308474}
  • shopping-feed/trunk/src/Feed/Generator.php

    r2542211 r2670662  
    234234
    235235        if ( true === $no_cache || ! is_file( $file_path ) ) {
    236             if ( ShoppingFeedHelper::generation_process_running() ) {
     236            if ( ShoppingFeedHelper::is_process_running( 'sf_feed_generation_process' ) ) {
    237237                wp_die( 'Feed generation already launched' );
    238238            }
  • shopping-feed/trunk/src/Orders/Operations.php

    r2542211 r2670662  
    5353     */
    5454    public function __construct( $order_id ) {
    55         $sdk = Sdk::get_instance();
    56         if ( ! $sdk->get_default_shop() instanceof StoreResource ) {
     55        //Check if the order from SF and return it with metas data
     56        $order_sf_metas = Order::get_order_sf_metas( $order_id );
     57        if (
     58            empty( $order_sf_metas ) ||
     59            empty( $order_sf_metas['sf_store_id'] )
     60        ) {
     61            throw new Exception(
     62                sprintf(
     63                /* translators: %s: Error message. */
     64                    __( 'No SF order found in %s', 'shopping-feed' ),
     65                    $order_id
     66                )
     67            );
     68        }
     69
     70        $account_id = $order_sf_metas['sf_store_id'];
     71
     72        $shop = Sdk::get_sf_account_shop( $account_id );
     73
     74        if ( ! $shop instanceof StoreResource ) {
     75            ShoppingFeedHelper::get_logger()->error(
     76                sprintf(
     77                /* translators: %s: Error message. */
     78                    __( 'Cannot retrieve shop from SDK for account : %s', 'shopping-feed' ),
     79                    $account_id
     80                ),
     81                array(
     82                    'source' => 'shopping-feed',
     83                )
     84            );
    5785            throw new Exception(
    5886                __( 'No store found', 'shopping-feed' )
    5987            );
    6088        }
    61         /** @var StoreResource $default_shop */
    62         $default_shop = $sdk->get_default_shop();
    63 
    64         //Check if the order from SF and return it with metas data
    65         $order_sf_metas = Order::get_order_sf_metas( $order_id );
    66         if ( empty( $order_sf_metas ) ) {
    67             throw new Exception(
    68                 __( 'No Order found', 'shopping-feed' )
    69             );
    70         }
    7189
    7290        $this->wc_order           = $order_sf_metas['order'];
    73         $this->order_api          = $default_shop->getOrderApi();
     91        $this->order_api          = $shop->getOrderApi();
    7492        $this->order_operation    = new OrderOperation();
    7593        $this->sf_reference       = (string) $order_sf_metas['sf_reference'];
  • shopping-feed/trunk/src/Orders/Order.php

    r2551274 r2670662  
    221221     */
    222222    public static function exists( $sf_order ) {
    223         return ! empty( wc_get_orders( array( Query::$wc_meta_sf_reference => $sf_order->getReference() ) ) );
     223        return ! empty( wc_get_orders( array( Query::WC_META_SF_REFERENCE => $sf_order->getReference() ) ) );
    224224    }
    225225
     
    313313        }
    314314
    315         $sf_reference    = $order->get_meta( Query::$wc_meta_sf_reference );
    316         $sf_channel_name = $order->get_meta( Query::$wc_meta_sf_channel_name );
     315        $sf_store_id     = $order->get_meta( Query::WC_META_SF_STORE_ID );
     316        $sf_reference    = $order->get_meta( Query::WC_META_SF_REFERENCE );
     317        $sf_channel_name = $order->get_meta( Query::WC_META_SF_CHANNEL_NAME );
    317318
    318319        return array(
    319320            'order'           => $order,
     321            'sf_store_id'     => $sf_store_id,
    320322            'sf_reference'    => $sf_reference,
    321323            'sf_channel_name' => $sf_channel_name,
     
    336338        }
    337339
    338         return ! empty( $wc_order->get_meta( Query::$wc_meta_sf_reference ) );
     340        return ! empty( $wc_order->get_meta( Query::WC_META_SF_REFERENCE ) );
    339341    }
    340342
     
    347349     */
    348350    public static function can_update_stock( $wc_order ) {
    349         return empty( $wc_order->get_meta( Query::$wc_meta_sf_reference ) ) || empty( $wc_order->get_meta( Metas::$dont_update_inventory ) );
     351        return empty( $wc_order->get_meta( Query::WC_META_SF_REFERENCE ) ) || empty( $wc_order->get_meta( Metas::$dont_update_inventory ) );
    350352    }
    351353}
  • shopping-feed/trunk/src/Orders/Order/Metas.php

    r2339808 r2670662  
    3939        $this->add_order_channel_name();
    4040        $this->add_order_shipping();
     41        $this->add_sf_store_id();
    4142
    4243        do_action_ref_array( 'sf_add_metas', array( $this ) );
     
    4748     */
    4849    private function add_order_referene() {
    49         $this->add_meta( Query::$wc_meta_sf_reference, $this->sf_order->getReference(), true );
     50        $this->add_meta( Query::WC_META_SF_REFERENCE, $this->sf_order->getReference(), true );
    5051    }
    5152
     
    5455     */
    5556    private function add_order_channel_name() {
    56         $this->add_meta( Query::$wc_meta_sf_channel_name, $this->sf_order->getChannel()->getName() );
     57        $this->add_meta( Query::WC_META_SF_CHANNEL_NAME, $this->sf_order->getChannel()->getName() );
    5758    }
    5859
     
    8182        );
    8283    }
     84
     85    /**
     86     * Add store id
     87     */
     88    private function add_sf_store_id() {
     89        $sf_order_array = $this->sf_order->toArray();
     90        $this->add_meta( Query::WC_META_SF_STORE_ID, $sf_order_array['storeId'] );
     91    }
    8392}
  • shopping-feed/trunk/src/Orders/Orders.php

    r2417980 r2670662  
    1414 */
    1515class Orders {
    16 
    17     /**
    18      * @var false|StoreResource
    19      */
    20     private $shop;
    2116
    2217    /**
     
    5146
    5247    /**
    53      * Orders constructor.
     48     * Get Orders from SF
    5449     */
    55     private function __construct() {
    56         if ( empty( $this->shop ) ) {
    57             $sdk = Sdk::get_instance();
    58             if ( $sdk->get_default_shop() ) {
    59                 $this->shop = $sdk->get_default_shop();
    60             }
    61         }
    62     }
     50    public function get_orders( $sf_account ) {
     51        $shop = Sdk::get_sf_shop( $sf_account );
    6352
    64     /**
    65      * Get Orders from SF
    66      * @return bool
    67      */
    68     public function get_orders() {
    69         if ( ! $this->shop ) {
     53        if ( ! $shop instanceof StoreResource ) {
     54            ShoppingFeedHelper::get_logger()->error(
     55                sprintf(
     56                /* translators: %s: Error message. */
     57                    __( 'Cannot retrieve shop from SDK for account : %s', 'shopping-feed' ),
     58                    $sf_account['username']
     59                ),
     60                array(
     61                    'source' => 'shopping-feed',
     62                )
     63            );
    7064            return false;
    7165        }
    7266
    73         $order_api                 = $this->shop->getOrderApi();
     67        $order_api                 = $shop->getOrderApi();
    7468        $filters                   = array();
    7569        $filters['acknowledgment'] = 'unacknowledged';
  • shopping-feed/trunk/src/Products/Product.php

    r2487968 r2670662  
    4848
    4949        return $this;
     50    }
     51
     52    /**
     53     * Return WC Product
     54     * @return false|\WC_Product|null
     55     */
     56    public function get_wc_product() {
     57        return $this->product;
    5058    }
    5159
  • shopping-feed/trunk/src/Query/Query.php

    r2339808 r2670662  
    1010 */
    1111class Query {
     12    /**
     13     * Custom Meta for SF accpunt ID
     14     */
     15    const WC_META_SF_STORE_ID = 'sf_store_id';
    1216
    1317    /**
    1418     * Custom Meta for SF reference
    15      * @var string
    1619     */
    17     public static $wc_meta_sf_reference = 'sf_reference';
     20    const WC_META_SF_REFERENCE = 'sf_reference';
    1821
    1922    /**
    2023     * Custom Meta for SF channel name
    21      * @var string
    2224     */
    23     public static $wc_meta_sf_channel_name = 'sf_marketplace';
     25    const WC_META_SF_CHANNEL_NAME = 'sf_marketplace';
    2426
    2527    public function __construct() {
     
    4446     */
    4547    public function wc_get_by_sf_reference( $query, $query_vars ) {
    46         if ( ! empty( $query_vars[ self::$wc_meta_sf_reference ] ) ) {
     48        if ( ! empty( $query_vars[ self::WC_META_SF_REFERENCE ] ) ) {
    4749            $query['meta_query'][] = array(
    48                 'key'   => self::$wc_meta_sf_reference,
    49                 'value' => esc_attr( $query_vars[ self::$wc_meta_sf_reference ] ),
     50                'key'   => self::WC_META_SF_REFERENCE,
     51                'value' => esc_attr( $query_vars[ self::WC_META_SF_REFERENCE ] ),
    5052            );
    5153        }
  • shopping-feed/trunk/src/Sdk/Sdk.php

    r2417980 r2670662  
    66defined( 'ABSPATH' ) || exit;
    77
    8 use ShoppingFeed\Sdk\Api\Session\SessionResource;
    98use ShoppingFeed\Sdk\Client;
    109use ShoppingFeed\Sdk\Credential;
     
    1615class Sdk {
    1716
    18     /** @var SessionResource */
    19     private $session;
     17    /**
     18     * @param int $account_id
     19     */
     20    public static function get_sf_account_shop( $account_id ) {
     21        $sf_account = ShoppingFeedHelper::get_sf_account_credentials( $account_id );
    2022
    21     /** @var string|null */
    22     private $username;
     23        return self::get_sf_shop( $sf_account );
     24    }
    2325
    24     /** @var string|null */
    25     private $password;
    26 
    27     /** @var string|null */
    28     private $token;
    2926
    3027    /**
    31      * @var Sdk
     28     * Return account deault shop
     29     *
     30     * @param array $sf_account
     31     *
     32     * @return false|\ShoppingFeed\Sdk\Api\Store\StoreResource
     33     * @psalm-suppress all
    3234     */
    33     private static $instance;
    34 
    35     /**
    36      * Get the singleton instance.
    37      *
    38      * @return Sdk
    39      */
    40     public static function get_instance() {
    41         if ( is_null( self::$instance ) ) {
    42             self::$instance = new static();
    43         }
    44 
    45         return self::$instance;
    46     }
    47 
    48     /**
    49      * Singleton instance can't be cloned.
    50      */
    51     private function __clone() {
    52     }
    53 
    54     /**
    55      * Singleton instance can't be serialized.
    56      */
    57     private function __wakeup() {
    58     }
    59 
    60     /**
    61      * Sdk constructor.
    62      */
    63     private function __construct() {
    64         if ( empty( $this->session ) ) {
    65             $options = ShoppingFeedHelper::get_sf_account_options();
    66             if ( empty( $options ) ) {
    67                 ShoppingFeedHelper::get_logger()->error(
    68                     __( 'No settings founds', 'shopping-feed' ),
    69                     array(
    70                         'source' => 'shopping-feed',
    71                     )
    72                 );
    73 
    74                 return;
    75             }
    76 
    77             $this->token    = ! empty( $options['token'] ) ? $options['token'] : null;
    78             $this->username = ! empty( $options['username'] ) ? $options['username'] : null;
    79             $this->password = ! empty( $options['password'] ) ? $options['password'] : null;
    80 
    81             $this->authenticate();
    82         }
    83     }
    84 
    85     /**
    86      * @return Credential\Password|Credential\Token|\WP_Error
    87      */
    88     private function get_credential() {
    89 
    90         //Check if we have Token to connect directly
    91         if ( ! empty( $this->token ) ) {
    92             return new Credential\Token( $this->token );
    93         }
    94 
    95         //If no credentials found go back
    96         if ( empty( $this->username ) || empty( $this->password ) ) {
     35    public static function get_sf_shop( $sf_account ) {
     36        if (
     37            empty( $sf_account['token'] ) && (
     38                empty( $sf_account['username'] ) ||
     39                empty( $sf_account['password'] )
     40            )
     41        ) {
     42            //TODO: add more informations about concerned sf account
    9743            ShoppingFeedHelper::get_logger()->error(
    98                 __( 'Need username/password to connect', 'shopping-feed' ),
     44                sprintf(
     45                    __( 'No Credentials found to connect', 'shopping-feed' )
     46                ),
    9947                array(
    10048                    'source' => 'shopping-feed',
    10149                )
    10250            );
    103             return new \WP_Error( 'shopping_feed_auth_required', __( 'Need username/password to connect', 'shopping-feed' ) );
    104         }
    10551
    106         //Return Credentials username/password
    107         return new Credential\Password( $this->username, $this->password );
    108     }
    109 
    110     /**
    111      * Try to connect if we have credentials
    112      * Set Token if the connection is set
    113      * @return bool
    114      */
    115     public function authenticate() {
    116         if ( is_wp_error( $this->get_credential() ) ) {
    11752            return false;
    11853        }
    11954
    120         /** @var Credential\Password|Credential\Token $credentials */
    121         $credentials = $this->get_credential();
     55        $credentials = new Credential\Password( $sf_account['username'], $sf_account['password'] );
     56
     57        if ( ! empty( $sf_account['token'] ) ) {
     58            $credentials = new Credential\Token( $sf_account['token'] );
     59        }
    12260
    12361        try {
    124             $this->session = Client\Client::createSession( $credentials );
    125 
    126             if ( empty( $this->token ) ) {
    127                 ShoppingFeedHelper::set_sf_token( $this->session->getToken() );
    128             }
    129 
    130             return true;
     62            $session = Client\Client::createSession( $credentials );
    13163        } catch ( \Exception $exception ) {
    132 
    13364            ShoppingFeedHelper::get_logger()->error(
    13465                sprintf(
     
    14273            );
    14374
    144             ShoppingFeedHelper::clean_password();
    145             return false;
    146         }
    147     }
    148 
    149     /**
    150      * Return the main store if the connection is set
    151      * @return false|\ShoppingFeed\Sdk\Api\Store\StoreResource
    152      */
    153     public function get_default_shop() {
    154 
    155         if ( ! $this->authenticate() ) {
    15675            return false;
    15776        }
    15877
    159         $main_shop = $this->session->getMainStore();
     78        $main_shop = $session->getMainStore();
    16079
    161         if ( ! $main_shop ) {
     80        if ( empty( $main_shop ) ) {
    16281            ShoppingFeedHelper::get_logger()->error(
     82            //TODO: add more informations about concerned sf account
    16383                __( 'No store found', 'shopping-feed' ),
    16484                array(
     
    16686                )
    16787            );
     88
    16889            return false;
    16990        }
     91
     92        //add storeId to account
     93        $account_options = ShoppingFeedHelper::get_sf_account_options();
     94        $index           = array_search( $sf_account['username'], array_column( $account_options, 'username' ), true );
     95        if ( false === $index || empty( $account_options[ $index ] ) ) {
     96            return false;
     97        }
     98        $account_options[ $index ]['sf_store_id'] = $main_shop->getId();
     99        $account_options[ $index ]['token']       = $session->getToken();
     100        ShoppingFeedHelper::set_sf_account_options( $account_options );
    170101
    171102        return $main_shop;
  • shopping-feed/trunk/src/ShoppingFeed.php

    r2542211 r2670662  
    117117        }
    118118
     119        $this->actions = new WoocommerceActions();
     120
     121        //Check Upgrade
     122        if ( ! $this->check_upgrade() ) {
     123            return;
     124        }
     125
    119126        $this->query   = new Query();
    120         $this->actions = new WoocommerceActions();
    121127        $this->filters = new WoocommerceFilters();
    122128        $this->notices = new Notices();
     
    242248        delete_option( Options::SF_CARRIERS );
    243249        delete_option( Generator::SF_FEED_LAST_GENERATION_DATE );
     250        delete_option( SF_DB_VERSION_SLUG );
     251        delete_option( SF_UPGRADE_RUNNING );
    244252        self::remove_sf_directory();
    245253    }
     
    259267        }
    260268    }
     269
     270    public function check_upgrade() {
     271        if ( ! ShoppingFeedHelper::sf_has_upgrade() || ShoppingFeedHelper::sf_new_customer() ) {
     272            return true;
     273        }
     274
     275        //check if we have a running migration
     276        if ( ShoppingFeedHelper::is_upgrade_running() ) {
     277            add_action(
     278                'admin_notices',
     279                function () {
     280                    ?>
     281                    <div id="message" class="notice notice-error">
     282                        <p><?php esc_html_e( 'ShoppingFeed migration is running', 'shopping-feed' ); ?></p>
     283                    </div>
     284                    <?php
     285                }
     286            );
     287
     288            return false;
     289        }
     290
     291        //check if we need to do migration
     292        if (
     293            ! empty( $_GET['sf_action'] ) &&
     294            'sf_migrate_single' === $_GET['sf_action'] &&
     295            isset( $_GET['_wpnonce'] ) &&
     296            wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), 'sf_migrate_single' ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
     297        ) {
     298            $id_action = as_enqueue_async_action( 'sf_migrate_single_action', array(), 'sf_migrate_single' );
     299            update_option( SF_UPGRADE_RUNNING, $id_action );
     300            wp_safe_redirect( admin_url( '/' ), 302 );
     301            exit;
     302        }
     303
     304        add_action(
     305            'admin_notices',
     306            function () {
     307                ?>
     308                <div id="message" class="notice notice-error">
     309                    <p><?php esc_html_e( 'ShoppingFeed need to migrate old data', 'shopping-feed' ); ?></p>
     310                    <a href="
     311                    <?php
     312                    echo esc_url(
     313                        wp_nonce_url(
     314                            add_query_arg(
     315                                array(
     316                                    'sf_action' => 'sf_migrate_single',
     317                                ),
     318                                admin_url()
     319                            ),
     320                            'sf_migrate_single'
     321                        )
     322                    )
     323                    ?>
     324                    ">Migrate</a>
     325                </div>
     326                <?php
     327            }
     328        );
     329
     330        return false;
     331    }
    261332}
  • shopping-feed/trunk/src/ShoppingFeedHelper.php

    r2560053 r2670662  
    77
    88use ShoppingFeed\ShoppingFeedWC\Admin\Options;
    9 use ShoppingFeed\ShoppingFeedWC\Sdk\Sdk;
    109use ShoppingFeed\ShoppingFeedWC\Url\Rewrite;
    1110use WC_Logger;
     
    141140
    142141    /**
    143      * Set SF token after connection
    144      *
    145      * @param $token
    146      *
    147      */
    148     public static function set_sf_token( $token ) {
    149         $options          = self::get_sf_account_options();
    150         $options['token'] = $token;
    151 
    152         update_option( Options::SF_ACCOUNT_OPTIONS, $options );
    153     }
    154 
    155     /**
    156142     * Return SF Configuration for Account
    157      * @return mixed|void
     143     * @return array
    158144     */
    159145    public static function get_sf_account_options() {
    160         return get_option( Options::SF_ACCOUNT_OPTIONS );
    161     }
    162 
    163     /**
    164      * Reset password if bad
    165      *
    166      * @param $token
    167      *
    168      */
    169     public static function clean_password() {
    170         $options = self::get_sf_account_options();
    171         if ( isset( $options ) && isset( $options['password'] ) ) {
    172             unset( $options['password'] );
    173         }
    174 
    175         update_option( Options::SF_ACCOUNT_OPTIONS, $options );
     146        return get_option( Options::SF_ACCOUNT_OPTIONS, array() );
     147    }
     148
     149
     150    /**
     151     * Set SF Configuration for Account
     152     * @return bool
     153     */
     154    public static function set_sf_account_options( $option ) {
     155        return update_option( Options::SF_ACCOUNT_OPTIONS, $option );
     156    }
     157
     158    /**
     159     * @param $account_id
     160     * @psalm-suppress all
     161     * @return array
     162     */
     163    public static function get_sf_account_credentials( $account_id ) {
     164        $account_options = self::get_sf_account_options();
     165        $index           = array_search( (int) $account_id, array_column( $account_options, 'sf_store_id' ), true );
     166        if ( false === $index || empty( $account_options[ $index ] ) ) {
     167            return array();
     168        }
     169
     170        return $account_options[ $index ];
    176171    }
    177172
     
    566561
    567562    /**
    568      * @return bool
    569      */
    570     public static function is_authenticated() {
    571         $sdk = Sdk::get_instance();
    572         if ( empty( $sdk ) ) {
    573             return false;
    574         }
    575 
    576         return $sdk->authenticate();
    577     }
    578 
    579     /**
    580563     * Add filter for category taxonomy
    581564     * default: product_cat
     
    662645    /**
    663646     * Check if a running generation process
     647     *
     648     * @param $group
     649     *
    664650     * @return bool
    665651     */
    666     public static function generation_process_running() {
    667         $process = self::get_running_generation_feed_process();
     652    public static function is_process_running( $group ) {
     653        $process = self::get_running_process( $group );
    668654
    669655        return ! empty( $process );
     
    672658    /**
    673659     * Get running process list
     660     *
     661     * @param string $group
     662     *
    674663     * @return array|int
    675664     */
    676     public static function get_running_generation_feed_process() {
     665    public static function get_running_process( $group ) {
    677666        $action_scheduler = \ActionScheduler::store();
    678667
    679668        return $action_scheduler->query_actions(
    680669            array(
    681                 'group'  => 'sf_feed_generation_process',
     670                'group'  => $group,
    682671                'status' => $action_scheduler::STATUS_PENDING,
    683672            )
     
    685674    }
    686675
    687     public static function clean_generation_process_running() {
     676    /**
     677     * @param string $group
     678     */
     679    public static function clean_process_running( $group ) {
    688680        try {
    689             \ActionScheduler::store()->cancel_actions_by_group( 'sf_feed_generation_process' );
     681            \ActionScheduler::store()->cancel_actions_by_group( $group );
    690682        } catch ( \Exception $exception ) {
    691683            self::get_logger()->error(
     
    732724
    733725    /**
     726     * Check if new customer
     727     * @return bool
     728     */
     729    public static function sf_new_customer() {
     730        return empty( get_option( Options::SF_ACCOUNT_OPTIONS ) ) &&
     731               empty( get_option( Options::SF_FEED_OPTIONS ) ) &&
     732               empty( get_option( Options::SF_SHIPPING_OPTIONS ) ) &&
     733               empty( get_option( Options::SF_ORDERS_OPTIONS ) );
     734    }
     735
     736    /**
    734737     * Singleton instance can't be cloned.
    735738     */
     
    742745    private function __wakeup() {
    743746    }
     747
     748    /**
     749     * Check if we have an upgrade
     750     * @return bool
     751     */
     752    public static function sf_has_upgrade() {
     753
     754        $db_version = get_option( SF_DB_VERSION_SLUG );
     755
     756        return empty( $db_version ) || version_compare( $db_version, SF_DB_VERSION, '<' );
     757    }
     758
     759    /**
     760     * Check if we have a running upgrade
     761     * @return bool
     762     */
     763    public static function is_upgrade_running() {
     764        return ! empty( get_option( SF_UPGRADE_RUNNING ) );
     765    }
     766
     767    /*
     768     * End upgrade
     769     */
     770    public static function end_upgrade() {
     771        delete_option( SF_UPGRADE_RUNNING );
     772        update_option( SF_DB_VERSION_SLUG, SF_DB_VERSION );
     773    }
    744774}
  • shopping-feed/trunk/vendor/autoload.php

    r2613147 r2670662  
    55require_once __DIR__ . '/composer/autoload_real.php';
    66
    7 return ComposerAutoloaderInit8e1243f94001ab8dc300d6b62efdcaf0::getLoader();
     7return ComposerAutoloaderInit0002ac7dfac052b9a9cc79f1494358a4::getLoader();
  • shopping-feed/trunk/vendor/composer/ClassLoader.php

    r2602861 r2670662  
    150150    /**
    151151     * @return string[] Array of classname => path
    152      * @psalm-var array<string, string>
     152     * @psalm-return array<string, string>
    153153     */
    154154    public function getClassMap()
  • shopping-feed/trunk/vendor/composer/InstalledVersions.php

    r2602861 r2670662  
    2525class InstalledVersions
    2626{
     27    /**
     28     * @var mixed[]|null
     29     * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
     30     */
    2731    private static $installed;
     32
     33    /**
     34     * @var bool|null
     35     */
    2836    private static $canGetVendors;
     37
     38    /**
     39     * @var array[]
     40     * @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
     41     */
    2942    private static $installedByVendor = array();
    3043
  • shopping-feed/trunk/vendor/composer/autoload_real.php

    r2613147 r2670662  
    33// autoload_real.php @generated by Composer
    44
    5 class ComposerAutoloaderInit8e1243f94001ab8dc300d6b62efdcaf0
     5class ComposerAutoloaderInit0002ac7dfac052b9a9cc79f1494358a4
    66{
    77    private static $loader;
     
    2323        }
    2424
    25         spl_autoload_register(array('ComposerAutoloaderInit8e1243f94001ab8dc300d6b62efdcaf0', 'loadClassLoader'), true, true);
     25        spl_autoload_register(array('ComposerAutoloaderInit0002ac7dfac052b9a9cc79f1494358a4', 'loadClassLoader'), true, true);
    2626        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
    27         spl_autoload_unregister(array('ComposerAutoloaderInit8e1243f94001ab8dc300d6b62efdcaf0', 'loadClassLoader'));
     27        spl_autoload_unregister(array('ComposerAutoloaderInit0002ac7dfac052b9a9cc79f1494358a4', 'loadClassLoader'));
    2828
    2929        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
     
    3131            require __DIR__ . '/autoload_static.php';
    3232
    33             call_user_func(\Composer\Autoload\ComposerStaticInit8e1243f94001ab8dc300d6b62efdcaf0::getInitializer($loader));
     33            call_user_func(\Composer\Autoload\ComposerStaticInit0002ac7dfac052b9a9cc79f1494358a4::getInitializer($loader));
    3434        } else {
    3535            $map = require __DIR__ . '/autoload_namespaces.php';
     
    5252
    5353        if ($useStaticLoader) {
    54             $includeFiles = Composer\Autoload\ComposerStaticInit8e1243f94001ab8dc300d6b62efdcaf0::$files;
     54            $includeFiles = Composer\Autoload\ComposerStaticInit0002ac7dfac052b9a9cc79f1494358a4::$files;
    5555        } else {
    5656            $includeFiles = require __DIR__ . '/autoload_files.php';
    5757        }
    5858        foreach ($includeFiles as $fileIdentifier => $file) {
    59             composerRequire8e1243f94001ab8dc300d6b62efdcaf0($fileIdentifier, $file);
     59            composerRequire0002ac7dfac052b9a9cc79f1494358a4($fileIdentifier, $file);
    6060        }
    6161
     
    6464}
    6565
    66 function composerRequire8e1243f94001ab8dc300d6b62efdcaf0($fileIdentifier, $file)
     66/**
     67 * @param string $fileIdentifier
     68 * @param string $file
     69 * @return void
     70 */
     71function composerRequire0002ac7dfac052b9a9cc79f1494358a4($fileIdentifier, $file)
    6772{
    6873    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
     74        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
     75
    6976        require $file;
    70 
    71         $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
    7277    }
    7378}
  • shopping-feed/trunk/vendor/composer/autoload_static.php

    r2613147 r2670662  
    55namespace Composer\Autoload;
    66
    7 class ComposerStaticInit8e1243f94001ab8dc300d6b62efdcaf0
     7class ComposerStaticInit0002ac7dfac052b9a9cc79f1494358a4
    88{
    99    public static $files = array (
     
    439439    {
    440440        return \Closure::bind(function () use ($loader) {
    441             $loader->prefixLengthsPsr4 = ComposerStaticInit8e1243f94001ab8dc300d6b62efdcaf0::$prefixLengthsPsr4;
    442             $loader->prefixDirsPsr4 = ComposerStaticInit8e1243f94001ab8dc300d6b62efdcaf0::$prefixDirsPsr4;
    443             $loader->classMap = ComposerStaticInit8e1243f94001ab8dc300d6b62efdcaf0::$classMap;
     441            $loader->prefixLengthsPsr4 = ComposerStaticInit0002ac7dfac052b9a9cc79f1494358a4::$prefixLengthsPsr4;
     442            $loader->prefixDirsPsr4 = ComposerStaticInit0002ac7dfac052b9a9cc79f1494358a4::$prefixDirsPsr4;
     443            $loader->classMap = ComposerStaticInit0002ac7dfac052b9a9cc79f1494358a4::$classMap;
    444444
    445445        }, null, ClassLoader::class);
  • shopping-feed/trunk/vendor/composer/installed.php

    r2613147 r2670662  
    11<?php return array(
    22    'root' => array(
    3         'pretty_version' => '6.0.33',
    4         'version' => '6.0.33.0',
     3        'pretty_version' => '6.1.0',
     4        'version' => '6.1.0.0',
    55        'type' => 'wordpress-plugin',
    66        'install_path' => __DIR__ . '/../../',
    77        'aliases' => array(),
    8         'reference' => 'b54ebdf093f62b8a77538414cf5a4eb070cd1bb5',
     8        'reference' => '7457e98e9f4b6982416834d213cea5359612cfd9',
    99        'name' => 'shoppingfeed/shoppingfeed',
    1010        'dev' => false,
     
    102102        ),
    103103        'shoppingfeed/shoppingfeed' => array(
    104             'pretty_version' => '6.0.33',
    105             'version' => '6.0.33.0',
     104            'pretty_version' => '6.1.0',
     105            'version' => '6.1.0.0',
    106106            'type' => 'wordpress-plugin',
    107107            'install_path' => __DIR__ . '/../../',
    108108            'aliases' => array(),
    109             'reference' => 'b54ebdf093f62b8a77538414cf5a4eb070cd1bb5',
     109            'reference' => '7457e98e9f4b6982416834d213cea5359612cfd9',
    110110            'dev_requirement' => false,
    111111        ),
Note: See TracChangeset for help on using the changeset viewer.