In a plugin I need to deactivate the Shopware HTTP-Cache for two categories. The manual says I should emit this event:
Shopware()->Events()->notify(
'Shopware_Plugins_HttpCache_InvalidateCacheId',
array(
'cacheId' => 'a14',
)
);
The a14 stands for the article with the ID 14. According to the manual a c can be used to uncache category pages. So I put this in my plugins bootstrap.php to stop caching of the categorys with the ID 113 and 114:
public function afterInit()
{
Shopware()->Events()->notify(
'Shopware_Plugins_HttpCache_InvalidateCacheId',
array(
'cacheId' => 'c113',
'cacheId' => 'c114',
)
);
}
I have emptied the cache manually on all levels, but nothing happens, neither good or bad, no error thrown and the categories are not removed from cache when the cache has been rebuild after emptying. Does anybody have a clue what I should change?
Here is the complete solution, thanks to Thomas answer, everything is done in the Bootstrap.php:
First subscribe to the PostDispatch_Frontend_Listing Event:
public function install()
{
$this->subscribeEvent('Enlight_Controller_Action_PostDispatch_Frontend_Listing', 'onPostDispatchListing');
return true;
}
Second create a function to send no-cache-header under certain conditions:
public function onPostDispatchListing(Enlight_Event_EventArgs $arguments)
{
$response = $arguments->getResponse();
$categoryId = (int)Shopware()->Front()->Request()->sCategory;
if ($categoryId === 113 || $categoryId === 114) {
$response->setHeader('Cache-Control', 'private, no-cache');
}
}
Third install or reinstall the plugin so the subscription to the event will be persisted in the database.
I think the best way is to add a plugin which adds a Cache-Control: no-cache header to the response for the specified categories. When this header is set the categories are not stored in the HTTP cache and you don't need to invalidate it.
You can listen to the Enlight_Controller_Action_PostDispatch_Frontend_Listing event and check if the category id is the one you need and add the header to the response.
$response->setHeader('Cache-Control', 'private, no-cache');
Related
I'm using WebViewer [1] from PDFTron to fill Form Fields in a PDF on Web [2]. There's a way to make some fields in Read Only Mode, so the user will not be able to add text to textfield, check the checkboxes?
I found this page on the documentation [3] but it seems that I can only set document in read only mode, instead I want only custom fields in read only, users will be able to fill some fields and not others.
I found also this page [4] on doc to set fields to readonly but in my case on WebViewer it doesn't work, in my browser the viewerLoaded events never get called; I tried to put the code in another part of the code but nothing happens.
Are there some hints or some working code that you guys use?
Thanks, Alberto
[1] https://www.pdftron.com/webviewer
[2] https://www.pdftron.com/pdf-sdk/form-filler
[3] https://www.pdftron.com/documentation/web/guides/annotations/annotation-permissions?searchTerm=readon#readonly-mode
[4] https://www.pdftron.com/documentation/web/guides/advanced/forms#set-fields-to-readonly
I managed to make it work with a modified version of this code [1]. The final result is:
$(document).on('documentLoaded', function() {
var docViewer = myWebViewer.getInstance().docViewer;
var annotManager = docViewer.getAnnotationManager();
annotManager.on('annotationChanged', function(e, annotations, action) {
// if the annotation change occurs because of an import then
// these are fields from inside the document
if (action === 'add' && e.imported) {
annotations.forEach(function(annot) {
if(annot.fieldName == 'read_only_field_name'){
annot.fieldFlags.set('ReadOnly', true);
}
});
}
});
});
[1] https://www.pdftron.com/documentation/web/guides/advanced/forms#set-fields-to-readonly
I am trying to use the deleteConfimation function option but I find that the default confirmation box pops up before I even get into the deleteConfimation function - what am I missing?
In the code below I can set break points and watch the data object being set up correctly with its new defaultConfirmMessage, but the basic jtable default delete confirmation box has already appeared and I never see an altered one.
$(container).jtable({
title: tablename,
paging: true,
pageSize: 100,
sorting: true,
defaultSorting: sortvar + ' ASC',
selecting: false,
deleteConfirmation: function(data) {
var defaultMessage = 'This record will be deleted - along with all its assignments!<br>Are you sure?';
if(data.record.Item) { // deleting an item
// Check whether item is in any preset lists
var url = 'CampingTablesData.php?action=CheckPresets&Table=items';
$.when(
ReturnAjax(url, {'ID':data.record.ID}, MyError)
).done(
function(retdata, status) {
if(status=='success') {
if(retdata.PresetList) {
data.deleteConfirmMessage = 'Item is in the following lists: ' + retdata.PresetList + 'Do you still want to delete it?';
}
} else {
data.cancel = true;
data.cancelMessage = retdata.Message;
}
}
);
} else {
data.deleteConfirmMessage = defaultMessage;
}
},
messages: {
addNewRecord: 'Add new',
deleteText: deleteTxt
},
actions: {
listAction: function(postData, jtParams) {
<list action code>
},
createAction: function(postData) {
<create action code>
},
updateAction: 'CampingTablesData.php?action=update&Table=' + tablename,
deleteAction: 'CampingTablesData.php?action=delete&Table=' + tablename
},
fields: tableFields --- preset variable
});
==========
After further testing the problem is only when deleting an item and it goes through the $.when().done() section of code. The Ajax call to the deletion url does not wait for this to complete - how do I overcome this?
i don't think you can get your design to work. What does the A in ajax stand for? Asynchronous! Synchronous Ajax has been deprecated for all sorts of good design and performance reasons.
You need to design you application to function asynchronously. Looking at your code, it feels you are misusing the deleteConfirmation event.
Consider changing the default deleteConfirmation message to inform the user, that the delete might not succeed if certain condition are met. Say
messages: {
deleteConfirmation: "This record will be deleted - along with all its assignments, unless in a preset list. Do you wish to try to delete this record?"
},
Then on the server, check the preset lists, and if not deletable, return an error message for jTable to display.
Depending on how dynamic your preset lists are, another approach might be to let the list function return an additional flag or code indicating which, if any, preset lists the item is already in, then your confirmation function can check this flag / indicator without further access to the server.
Thanks to MisterP for his observation and suggestions. I also considered his last approach but ended up setting deleteConfirmation to false (so as not to generate a system prompt) then writing a delete function that did not actually delete, but returned the information I needed to construct my own deleteConfimation message. Then a simple if confirm(myMessage) go ahead and delete with another Ajax call.
I use if else for custom menus in Wordpress, to load various location menus based on parent page. The agency I work for is adding countless amounts of cities, and it's getting out of hand. One thing I am trying to do, is come up with a more efficient way to check the items, someone suggested switch, and I just wanted to throw this out there and see what you all think. These are not complete codes, and I know the menus are bad UX, and all that, it's not my call. I just want some input on performance differences. thanks.
Here is an example of switch code:
function is_subpage() {
global $post; // load details about this page
if ( is_page() && $post->post_parent ) { // test to see if the page has a parent
return $post->post_parent; // return the ID of the parent post
} else { // there is no parent so ...
return false; // ... the answer to the question is false
}
}
$selectedMenu = "primary";
$my_page_id = is_subpage();
if(!$my_page_id)
$my_page_id = get_the_ID();
switch ($my_page_id) {
case('489'):
$selectedMenu = 'columbus';
break;
case('6583'):
$selectedMenu = 'cumming';
break;
}
wp_nav_menu( array(
'theme_location' => 'main-menu',
'menu' => $selectedMenu,
'menu_class' => 'clearfix'
));
and here is an example of if else code:
if(is_page( '28' ) || '28' == $post->post_parent) { $locationMenu = 'louisville'; }
'menu' => $locationMenu,
Don't second guess or assume anything about the efficiency of an interpreter or compiler. if else might be better at one scenario and switch at another.
The problem with your code is readability and maintainability and not performance. It is hard to be specific without knowing all details about your needs, but it seems like what you need is to have at each post a custom field which indicates the menu associated with that post, and then the admin can configure them and you will have some more coffee time ;)
This is actually a worse solution in terms of performance, but if you really need the site to be fast then you are going to use a caching plugin which will make the whole php related performance discussion just a waste of time.
From a PHP perspective...
In lieu of having the page id to location table in a database, you could include a structure like this on pages you need it:
$idToLocation = array(
"489" => "columbus",
"6583" => "cumming"
// et cetera
);
Then to get the location:
$id = "489"; // for example
if (!array_key_exists($id, $idToLocation)) {
echo "location for id not found";
die();
}
$location = $idToLocation[$id];
With soft delete turned on, I add a single record on the client, push, delete the added record push and then attempt to add a new record (and then push) with the same primary key as the initial record I get an exception. It would appear that EntityDomainManager just attempts to do a new insert without checking to see if the record is to be 'updated' instead of inserted.
However if I turn off soft delete in the domain manager constructor everything works fine.
We are using incremental sync, so soft delete as I understand it is required to make this work, so we don't end up with different pictures of what's right between mobile and server.
When is/are the recommended approach? A Custom EntityDomainManager (or other DomainManager)? If so it would be useful for more clarity on the interactions between the table controller and the domain manager.
I have constructed this custom domain manager which seems to work, but would appreciate any guidance/suggestions.
public class CustomEntityDomainManager<TData> : EntityDomainManager<TData> where TData : class, ITableData
{
public CustomEntityDomainManager(DbContext context, HttpRequestMessage request, ApiServices services)
: base(context, request, services)
{
}
public CustomEntityDomainManager(DbContext context, HttpRequestMessage request, ApiServices services, bool enableSoftDelete) : base(context, request, services, enableSoftDelete)
{
}
public async override Task<TData> InsertAsync(TData data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
// now then, if we have soft delete enabled & data has been provided with an id in it
if (EnableSoftDelete && data.Id != null)
{
// now look to see if the record exists and if it is deleted
// if so we look to remove the record before then attempting the insert
// record old value of deleted, since need to query to see if deleted.
var oldIncludeDeleted = IncludeDeleted;
try
{
IncludeDeleted = true;
var existingData = await this.Lookup(data.Id).Queryable.FirstOrDefaultAsync();
// if record exists, and its soft deleted then truly delete it
if (existingData != null && existingData.Deleted)
{
// now need to remove this record...
this.Context.Set<TData>().Remove(existingData);
}
}
finally
{
IncludeDeleted = oldIncludeDeleted;
}
}
if (data.Id == null)
{
data.Id = Guid.NewGuid().ToString("N");
}
return await base.InsertAsync(data);
}
This behavior is by design--we require that you do an explicit undelete before doing the update.
The solution you've presented is fine. You can also move the code to your table controller, assuming you only need this behavior in one table. If you need it in multiple tables, then the custom domain manager is the best approach.
I have created a custom navigation module specifically for a website, but I really want to be able to list filterable attributes by a specific category. So for instance my main navigation is:
Category 1
Category 2
Category 3 etc.
I then that when a user mouses over a category, they are then presented with an expanded menu with a few filterable options e.g.:
Category 1
View by manufacturer:
Manufacturer 1
Manufacturer 2
Manufacturer 3 etc.
I am able to get all filterable attributes for the store, but I want this list to pull in only the filterable attributes per category, as for instance Category 1 may have different manufacturers to Category 2. I then need to cache these results as this will not change often.
The answer that Joe gave was a good starting point, but the attributes didn't returned any options yet. After a lot of frustrations I solved the problem with the following code. Hope it helps all of you out.
$layer = Mage::getModel("catalog/layer");
foreach($categories as $categoryid) {
$category = Mage::getModel("catalog/category")->load($categoryid);
$layer->setCurrentCategory($category);
$attributes = $layer->getFilterableAttributes();
foreach ($attributes as $attribute) {
if ($attribute->getAttributeCode() == 'price') {
$filterBlockName = 'catalog/layer_filter_price';
} elseif ($attribute->getBackendType() == 'decimal') {
$filterBlockName = 'catalog/layer_filter_decimal';
} else {
$filterBlockName = 'catalog/layer_filter_attribute';
}
$result = $this->getLayout()->createBlock($filterBlockName)->setLayer($layer)->setAttributeModel($attribute)->init();
foreach($result->getItems() as $option) {
echo $option->getLabel().'<br/>';
echo $option->getValue();
}
}
The only thing you'll need to do yourself is create the correct link using the getValue() functions.
This code has been tested in Magento 1.5
Magento uses the model Catalog_Model_Layer to accomplish this, so I'm guessing this may be your best bet. Caveat emptor, I have not tested this code yet:
$layer = Mage::getModel("catalog/layer");
foreach($categories as $categoryid) {
$category = Mage::getModel("catalog/category")->load($categoryid);
$layer->setCurrentCategory($category);
$attributes = $layer->getFilterableAttributes();
// do something with your attributes
}
Each iteration here will give you an object of the class Mage_Catalog_Model_Resource_Eav_Mysql4_Attribute_Collection, which you should be able to iterate over in a foreach loop to get your desired output.
For caching, try enabling block caching on your site and give the block a cache tag like the following. Magento will cache the HTML output and all will be right with the world:
protected function _construct() {
$this->addData(array(
'cache_lifetime' => 3600,
'cache_tags' => array(Mage_Catalog_Model_Product::CACHE_TAG),
'cache_key' => $someUniqueIdentifierYouCreate,
));
}
The cache will only be valid for the key you pass, so make sure that, if the menu is to change (w/o flushing the cache, for instance), that the cache key is different.
Hope that helps!
Thanks,
Joe