Related
I am using ElasticSearch to index data and wanted to export few fields from index created every day to Google cloud storage, How to get fields from array of objects in elastic search index and send them as csv file to GCS bucket using Logstash
Tried below conf to fetch nested fields from index:
input {
elasticsearch {
hosts => "host:443"
user => "user"
ssl => true
connect_timeout_seconds => 600
request_timeout_seconds => 600
password => "pwd"
ca_file => "ca.crt"
index => "test"
query => '
{
"_source": ["obj1.Name","obj1.addr","obj1.obj2.location", "Hierarchy.categoryUrl"],
"query": {
"match_all": {}
}
}
'
}
}
filter {
mutate {
rename => {
"[obj1][Name]" => "col1"
"[obj1][addr]" => "col2"
"[obj1][obj2][location]" => "col3"
"[Hierarchy][0][categoryUrl]" => "col4"
}
}
}
output {
google_cloud_storage {
codec => csv {
include_headers => true
columns => [ "col1", "col2","col3"]
}
bucket => "bucket"
json_key_file => "creds.json"
temp_directory => "/tmp"
log_file_prefix => "log_gcs"
max_file_size_kbytes => 1024
date_pattern => "%Y-%m-%dT%H:00"
flush_interval_secs => 600
gzip => false
uploader_interval_secs => 600
include_uuid => true
include_hostname => true
}
}
How to get field populated to above csv from array of objects, in below example wanted to fetch categoryUrl from the first object of an array and populate to csv table and send it to GCS Bucket:
have tried below approaches :
"_source": ["obj1.Name","obj1.addr","obj1.obj2.location", "Hierarchy.categoryUrl"]
and
"_source": ["obj1.Name","obj1.addr","obj1.obj2.location", "Hierarchy[0].categoryUrl"]
with
mutate {
rename => {
"[obj1][Name]" => "col1"
"[obj1][addr]" => "col2"
"[obj1][obj2][location]" => "col3"
"[Hierarchy][0][categoryUrl]" => "col4"
}
for input sample :
"Hierarchy" : [
{
"level" : "1",
"category" : "test",
"categoryUrl" : "testurl1"
},
{
"level" : "2",
"category" : "test2",
"categoryUrl" : "testurl2"
}}
Attaching sample document where I am trying to fetch merchandisingHierarchy[0].categoryUrl and pricingInfo[0].basePrice :
{
"_index" : "amulya-test",
"_type" : "_doc",
"_id" : "ldZPJoYBFi8LOEDK_M2f",
"_score" : 1.0,
"_ignored" : [
"itemDetails.description.keyword"
],
"_source" : {
"itemDetails" : {
"compSku" : "202726",
"compName" : "abc.com",
"compWebsite" : "abc.com",
"title" : "Monteray 38.25 in. x 73.375 in. Frameless Hinged Corner Shower Enclosure in Brushed Nickel",
"description" : "Create the modthroom of your dreams with the clean lines of the VIGO Monteray Frameless Shower Enclosure. Solid 3/8 in. tempered glass combined with stainless steel and solid brass construction makes this enclosure strong and long-lasting. The sleek, reversible, outward-opening door features a convenient towel bar. This versatile enclosure can be installed on a tile floor or with a VIGO Shower Base. With a single water deflector along the bottom seal strip, water is redirected back into the shower to keep your bathroom dry, clean, and pristine.",
"modelNumber" : "VG6011BNCL40",
"upc" : "8137756684",
"hasVariations" : false,
"productDetailsBulletPoints" : [ ],
"itemUrls" : {
"productPageUrl" : "https://.abc.com/p/VIGO-Monteray-38-in-x-73-375-in-Frameless-Hinged-Corner-Shower-Enclosure-in-Brushed-Nickel-VG6011BNCL40/202722616",
"primaryImageUrl" : "https://images.thdstatic.com/productImages/d77d9e8b-1ea1-4811-a470-8364c8e47402/svn/vigo-shower-enclosures-vg6011bncl40-64_600.jpg",
"secondaryImageUrls" : [
"https://images.thdstatic.com/productImages/d77d9e8b-1e1-4811-a470-8364c8e47402/svn/vigo-shower-enclosures-vg6011bncl40-64_1000.jpg",
"https://images.thdstatic.com/productImages/db539ff9-6df-48c2-897a-18dd1e1794e3/svn/vigo-shower-enclosures-vg6011bncl40-e1_1000.jpg",
"https://images.thdstatic.com/productImages/47c5090b-49a-46bc-a36d-921ddae5e1ab/svn/vigo-shower-enclosures-vg6011bncl40-40_1000.jpg",
"https://images.thdstatic.com/productImages/add6691c-a02-466d-9a1a-47200b05685e/svn/vigo-shower-enclosures-vg6011bncl40-a0_1000.jpg",
"https://images.thdstatic.com/productImages/d638230e-0d9-40c9-be93-7f7bf24f0732/svn/vigo-shower-enclosures-vg6011bncl40-1d_1000.jpg"
]
}
},
"merchandisingHierarchy" : [
{
"level" : "1",
"category" : "Home",
"categoryUrl" : "host/"
},
{
"level" : "2",
"category" : "Bath",
"categoryUrl" : "host/b/Bath/N-5yc1vZbzb3"
},
{
"level" : "3",
"category" : "Showers",
"categoryUrl" : "host/b/Bath-Showers/N-5yc1vZbzcd"
},
{
"level" : "4",
"category" : "Shower Doors",
"categoryUrl" : "host/b/Bath-Showers-Shower-Doors/N-5yc1vZbzcg"
},
{
"level" : "5",
"category" : "Shower Enclosures",
"categoryUrl" : "host/b/Bath-Showers-Shower-Doors-Shower-Enclosures/N-5yc1vZcbn2"
}
],
"reviewsAndRatings" : {
"pdtReviewCount" : 105
},
"additionalAttributes" : {
"isAddon" : false
},
"productSpecifications" : {
"Warranties" : { },
"Details" : { },
"Dimensions" : { }
},
"promoDetails" : [
{
"promoName" : "Save $150.00 (15%)",
"promoPrice" : 849.9
}
],
"locationDetails" : { },
"storePickupDetails" : {
"deliveryText" : "Get it by Mon, Feb 20",
"toEddDate" : "Mon, Feb 20",
"isBackordered" : false,
"selectedEddZipcode" : "20147",
"shipToStoreEnabled" : true,
"homeDeliveryEnabled" : true,
"scheduledDeliveryEnabled" : false
},
"recommendedProducts" : [ ],
"pricingInfo" : [
{
"type" : "SAS",
"offerPrice" : 849.9,
"sellerName" : "abc.com",
"onClearance" : false,
"inStock" : true,
"isBuyBoxWinner" : true,
"promo" : [
{
"onPromo" : true,
"promoName" : "Save $150.00 (15%)",
"promoPrice" : 849.9
}
],
"basePrice" : 999.9,
"priceVariants" : [
{
"basePrice" : 999.9,
"offerPrice" : 849.9
}
],
"inventoryDetails" : {
"stockInStore" : false,
"stockOnline" : true
}
}
]
}
}
You can do it like this:
input {
elasticsearch {
...
query => '
{
"_source": ["merchandisingHierarchy.categoryUrl"],
"query": {
"match_all": {}
}
}
'
}
}
filter {
mutate {
add_field => {
"col1" => "%{[merchandisingHierarchy][0][categoryUrl]}"
"col2" => "%{[pricingInfo][0][basePrice]}"
}
}
}
output {
stdout {
codec => csv {
include_headers => true
columns => [ "col1"]
}
}
}
I've tested with your sample document and I get the output below, which looks like is working per your expectation:
col1,col2
host/,999.9
Here is my JSON data that i am trying to send from filebeat to ingest pipeline "logpipeline.json" in opensearch.
json data
{
"#timestamp":"2022-11-08T10:07:05+00:00",
"client":"10.x.x.x",
"server_name":"example.stack.com",
"server_port":"80",
"server_protocol":"HTTP/1.1",
"method":"POST",
"request":"/example/api/v1/",
"request_length":"200",
"status":"500",
"bytes_sent":"598",
"body_bytes_sent":"138",
"referer":"",
"user_agent":"Java/1.8.0_191",
"upstream_addr":"10.x.x.x:10376",
"upstream_status":"500",
"gzip_ratio":"",
"content_type":"application/json",
"request_time":"6.826",
"upstream_response_time":"6.826",
"upstream_connect_time":"0.000",
"upstream_header_time":"6.826",
"remote_addr":"10.x.x.x",
"x_forwarded_for":"10.x.x.x",
"upstream_cache_status":"",
"ssl_protocol":"TLSv",
"ssl_cipher":"xxxx",
"ssl_session_reused":"r",
"request_body":"{\"date\":null,\"sourceType\":\"BPM\",\"processId\":\"xxxxx\",\"comment\":\"Process status: xxxxx: \",\"user\":\"xxxx\"}",
"response_body":"{\"statusCode\":500,\"reasonPhrase\":\"Internal Server Error\",\"errorMessage\":\"xxxx\"}",
"limit_req_status":"",
"log_body":"1",
"connection_upgrade":"close",
"http_upgrade":"",
"request_uri":"/example/api/v1/",
"args":""
}
Filebeat to Opensearch log shipping
# ---------------------------- Elasticsearch Output ----------------------------
output.elasticsearch:
# Array of hosts to connect to.
hosts: ["192.168.29.117:9200"]
pipeline: logpipeline
#index: "filebeatelastic-%{[agent.version]}-%{+yyyy.MM.dd}"
index: "nginx_dev-%{+yyyy.MM.dd}"
# Protocol - either `http` (default) or `https`.
protocol: "https"
ssl.enabled: true
ssl.verification_mode: none
# Authentication credentials - either API key or username/password.
#api_key: "id:api_key"
username: "filebeat"
password: "filebeat"
I am carrying out the "data" fields transformation in the ingest pipeline for some of the fields by doing type conversion which works perfectly. But the only problem i am facing is with the "#timestamp".
The "#timestamp" is of "date" type and once the json data goes through the pipeline i am mapping the json data message to root level json object called "data". In that transformed data the "data.#timestamp" is showing as type "string" even though i haven't done any transformation for it.
Opensearch ingestpipeline - logpipeline.json
{
"description" : "Logging Pipeline",
"processors" : [
{
"json" : {
"field" : "message",
"target_field" : "data"
}
},
{
"date" : {
"field" : "data.#timestamp",
"formats" : ["ISO8601"]
}
},
{
"convert" : {
"field" : "data.body_bytes_sent",
"type": "integer",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"convert" : {
"field" : "data.bytes_sent",
"type": "integer",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"convert" : {
"field" : "data.request_length",
"type": "integer",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"convert" : {
"field" : "data.request_time",
"type": "float",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"convert" : {
"field" : "data.upstream_connect_time",
"type": "float",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"convert" : {
"field" : "data.upstream_header_time",
"type": "float",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"convert" : {
"field" : "data.upstream_response_time",
"type": "float",
"ignore_missing": true,
"ignore_failure": true
}
}
]
}
Is there any way i can preserve the "#timestamp" "date" type field even after the transformation carried out in ingest pipeline?
indexed document image:
Edit1: Update ingest pipeline simulate result
{
"docs" : [
{
"doc" : {
"_index" : "_index",
"_id" : "_id",
"_source" : {
"index_date" : "2022.11.08",
"#timestamp" : "2022-11-08T12:07:05.000+02:00",
"message" : """
{ "#timestamp": "2022-11-08T10:07:05+00:00", "client": "10.x.x.x", "server_name": "example.stack.com", "server_port": "80", "server_protocol": "HTTP/1.1", "method": "POST", "request": "/example/api/v1/", "request_length": "200", "status": "500", "bytes_sent": "598", "body_bytes_sent": "138", "referer": "", "user_agent": "Java/1.8.0_191", "upstream_addr": "10.x.x.x:10376", "upstream_status": "500", "gzip_ratio": "", "content_type": "application/json", "request_time": "6.826", "upstream_response_time": "6.826", "upstream_connect_time": "0.000", "upstream_header_time": "6.826", "remote_addr": "10.x.x.x", "x_forwarded_for": "10.x.x.x", "upstream_cache_status": "", "ssl_protocol": "TLSv", "ssl_cipher": "xxxx", "ssl_session_reused": "r", "request_body": "{\"date\":null,\"sourceType\":\"BPM\",\"processId\":\"xxxxx\",\"comment\":\"Process status: xxxxx: \",\"user\":\"xxxx\"}", "response_body": "{\"statusCode\":500,\"reasonPhrase\":\"Internal Server Error\",\"errorMessage\":\"xxxx\"}", "limit_req_status": "", "log_body": "1", "connection_upgrade": "close", "http_upgrade": "", "request_uri": "/example/api/v1/", "args": ""}
""",
"data" : {
"server_name" : "example.stack.com",
"request" : "/example/api/v1/",
"referer" : "",
"log_body" : "1",
"upstream_addr" : "10.x.x.x:10376",
"body_bytes_sent" : 138,
"upstream_header_time" : 6.826,
"ssl_cipher" : "xxxx",
"response_body" : """{"statusCode":500,"reasonPhrase":"Internal Server Error","errorMessage":"xxxx"}""",
"upstream_status" : "500",
"request_time" : 6.826,
"upstream_cache_status" : "",
"content_type" : "application/json",
"client" : "10.x.x.x",
"user_agent" : "Java/1.8.0_191",
"ssl_protocol" : "TLSv",
"limit_req_status" : "",
"remote_addr" : "10.x.x.x",
"method" : "POST",
"gzip_ratio" : "",
"http_upgrade" : "",
"bytes_sent" : 598,
"request_uri" : "/example/api/v1/",
"x_forwarded_for" : "10.x.x.x",
"args" : "",
"#timestamp" : "2022-11-08T10:07:05+00:00",
"upstream_connect_time" : 0.0,
"request_body" : """{"date":null,"sourceType":"BPM","processId":"xxxxx","comment":"Process status: xxxxx: ","user":"xxxx"}""",
"request_length" : 200,
"ssl_session_reused" : "r",
"server_port" : "80",
"upstream_response_time" : 6.826,
"connection_upgrade" : "close",
"server_protocol" : "HTTP/1.1",
"status" : "500"
}
},
"_ingest" : {
"timestamp" : "2023-01-18T08:06:35.335066236Z"
}
}
}
]
}
Finally able to resolve my issue. I updated the filebeat.yml with the following. Previously template name and pattern was different. But this default template name "filebeat" and pattern "filebeat" seems to be doing the job for me.
To
setup.template.name: "filebeat"
setup.template.pattern: "filebeat"
setup.template.settings:
index.number_of_shards: 1
#index.codec: best_compression
#_source.enabled: false
But still need to figure our how templates work though
I am setting the return to true, but when i complete the payment on square, the app does not redirect on its own.
{
"amount_money": {
"amount" : "100",
"currency_code" : "USD"
},
"auto_return":true,
"callback_url" : "https://floating-inlet-19449.herokuapp.com/redirect",
"client_id" : "sq0idp-U8x6mJyLFtHuhCfv9sqL5g",
"version": "1.3",
"notes": "notes for the transaction",
"options" : {
"supported_tender_types" : ["CREDIT_CARD"]
}
auto_return should be nested in the options object, like this:
{
"amount_money": { "amount" : 100, "currency_code" : "USD" },
"callback_url" : "https://floating-inlet-19449.herokuapp.com/redirect",
"client_id" : "sq0idp-U8x6mJyLFtHuhCfv9sqL5g",
"version": "1.3",
"notes": "notes for the transaction",
"options" : {
"supported_tender_types" : ["CREDIT_CARD"],
"auto_return": true
}
I'm working on retrieving all the orders for a given shop, using code that looks like this:
orders = ShopifyAPI::Order.find(:all, :params => {:financial_status => 'paid'})
orders.each do |order|
order_json = order.to_json
post_json_to_server(order_json)
end
But for some reason, when I inspect the JSON created by order.to_json, the discount_codes and client_details attributes look like this:
"client_details":{"":{"accept_language":"en-US,en;q=0.8","browser_ip":"199.185.98.174","session_hash":"6b37d22ebcdf097f5ab4e1c9e596a504c4cdc4c41c4f2b29a3a7aae4ead559c3","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.47 Safari/536.11"}}
"discount_codes":[{"":{"amount":"20.00","code":"CZV57KSE8VMV"}}]
Why is there a leading {"": in both of these lists? Is something misconfigured in my test store?
Here is a complete dump (captured with RequestBin) of the JSON I'm sending:
{ "billing_address" : { "address1" : "asdf",
"address2" : "",
"city" : "asdf",
"company" : "",
"country" : "United States",
"country_code" : "US",
"first_name" : "asdf",
"last_name" : "asdf",
"latitude" : "45.176384",
"longitude" : "-123.045601",
"name" : "asdf asdf",
"phone" : "",
"province" : "Alaska",
"province_code" : "AK",
"zip" : "asdf"
},
"browser_ip" : "199.185.98.174",
"buyer_accepts_marketing" : true,
"cancel_reason" : null,
"cancelled_at" : null,
"cart_token" : "bbf42c99f456f9ccda30554022fec659",
"client_details" : { "" : { "accept_language" : "en-US,en;q=0.8",
"browser_ip" : "199.185.98.174",
"session_hash" : "6b37d22ebcdf097f5ab4e1c9e596a504c4cdc4c41c4f2b29a3a7aae4ead559c3",
"user_agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.47 Safari/536.11"
} },
"closed_at" : null,
"created_at" : "2012-07-10T17:23:53-04:00",
"currency" : "CAD",
"customer" : { "accepts_marketing" : true,
"created_at" : "2012-07-10T17:23:53-04:00",
"email" : "asdf#example.com",
"first_name" : "asdf",
"id" : 94366870,
"last_name" : "asdf",
"last_order_id" : null,
"last_order_name" : null,
"note" : null,
"orders_count" : 0,
"state" : "disabled",
"tags" : "",
"total_spent" : "0.00",
"updated_at" : "2012-07-10T17:35:23-04:00"
},
"discount_codes" : [ { "" : { "amount" : "20.00",
"code" : "CZV57KSE8VMV"
} } ],
"email" : "asdf#example.com",
"financial_status" : "authorized",
"fulfillment_status" : null,
"fulfillments" : [ ],
"gateway" : "bogus",
"id" : 134753494,
"landing_site" : "/",
"landing_site_ref" : null,
"line_items" : [ { "fulfillment_service" : "manual",
"fulfillment_status" : null,
"grams" : 0,
"id" : 219421970,
"name" : "Grass-roots methodical instruction set",
"price" : "19.00",
"product_id" : 95843140,
"quantity" : 2,
"requires_shipping" : true,
"sku" : "",
"title" : "Grass-roots methodical instruction set",
"variant_id" : 224399478,
"variant_inventory_management" : null,
"variant_title" : null,
"vendor" : "Shopify"
} ],
"name" : "#1004",
"note" : "",
"note_attributes" : [ ],
"number" : 4,
"order_number" : 1004,
"payment_details" : { "avs_result_code" : null,
"credit_card_bin" : "1",
"credit_card_company" : "Bogus",
"credit_card_number" : "XXXX-XXXX-XXXX-1",
"cvv_result_code" : null
},
"processing_method" : "direct",
"referring_site" : "",
"shipping_address" : { "address1" : "asdf",
"address2" : "",
"city" : "asdf",
"company" : "",
"country" : "United States",
"country_code" : "US",
"first_name" : "asdf",
"last_name" : "asdf",
"latitude" : "45.176384",
"longitude" : "-123.045601",
"name" : "asdf asdf",
"phone" : "",
"province" : "Alaska",
"province_code" : "AK",
"zip" : "asdf"
},
"shipping_lines" : [ { "code" : "International Shipping",
"price" : "20.00",
"source" : "shopify",
"title" : "International Shipping"
} ],
"subtotal_price" : "18.00",
"tax_lines" : [ ],
"taxes_included" : false,
"token" : "51116b93d2d774b6c537a3bcc8861506",
"total_discounts" : "20.00",
"total_line_items_price" : "38.00",
"total_price" : "38.00",
"total_price_usd" : "37.28",
"total_tax" : "0.00",
"total_weight" : 0,
"updated_at" : "2012-07-10T17:35:20-04:00"
}
In an attempt to get the Order formatted the same way as the JSON sent by a webhook, I ended up doing it myself - here is the code:
ActiveResource::Base.include_root_in_json = true
orders = ShopifyAPI::Order.find(:all, :params => {:financial_status => 'paid'})
orders.each do |order|
order_json = order.as_json
%w(billing_address customer line_items payment_details shipping_address shipping_lines).each do |attribute|
order_json[attribute] = order.send(attribute).as_json
end
if order_json['discount_codes'].length > 0
order_json['discount_codes'] = [order_json['discount_codes'].as_json[0][nil]]
end
order_json['client_details'] = order_json['client_details'].as_json[nil]
post_json_to_server(order_json)
end
By doing it this way, I was able to turn the JSON into something that mapped exactly to what Shopify's order paid webhook sends.
When you use to_json without telling ActiveRecord how to treat the root it can do that.
You can tell ActiveRecord to include the root when rendering JSON.
ActiveRecord::Base.include_root_in_json = true
You would then see
{"order":{...}} and not {"":{...}}
Often you can just use the syntax
order_json = order.to_json(:root => true)
in order to get the key (which is the root) you want. Using JSON is still kinda like walking inside a carnival jumpy ride for kids...
I have been using zend_acl and Zend_Navigation together on my project currently and it seems to work correctly. I am how ever using a lot of AJAX in my site and have made a login with jqModal and jQuery which logs in users without reloading the current page and have the need to reload the navigation as well. I have so far got it working but the navigation being return is all the links with the visible field as true which is incorrect. For example when I am logged in as a user the login link should not be visible.
My code below for Action, JavaScript function and JSON returned.
current role = admins
// CONTROLLER ACTION
public function getnavigationjsonAction()
{
//$view->navigation($container)->setAcl($this->_acl)->setRole(Zend_Registry::get('role'));
// echo $this->navigation()->menu();
$navigation = $this->getView()->navigation()->setAcl($this->_acl)->setRole(Zend_Registry::get('role'));
//die(Zend_Registry::get('role'));
//die($this->_acl);
$this->_helper->json($navigation->toArray());
}
// JAVASCRIPT FUNCTION
function reloadNavigationMenu()
{
//alert('reloading navigation');
$.ajax( {
url : "/default/ajax/getnavigationjson",
type : "POST",
cache : false,
async : false,
data : "format=json",
success : function(data) {
var html='<ul class="navigation">';
console.log(data);
$.each(data, function(i) {
if(data[i].visible === true) {
//console.log(data[i]);
html+='<li>';
html+=''+data[i].label+'';
html+='</li>';
}
});
html+='</ul>';
$('#zend_navigation_container').html(html);
}
});
}
// RETURN JSON data from controller
[ { "action" : "index",
"active" : false,
"class" : null,
"controller" : "admin",
"id" : null,
"label" : "Admin Section",
"module" : "admin",
"order" : null,
"pages" : [ ],
"params" : [ ],
"privilege" : "index",
"rel" : [ ],
"reset_params" : true,
"resource" : "admin:admin",
"rev" : [ ],
"route" : null,
"target" : null,
"title" : null,
"type" : "Zend_Navigation_Page_Mvc",
"visible" : true
},
{ "action" : "index",
"active" : false,
"class" : null,
"controller" : "index",
"id" : null,
"label" : "Home",
"module" : "default",
"order" : null,
"pages" : [ ],
"params" : [ ],
"privilege" : null,
"rel" : [ ],
"reset_params" : true,
"resource" : null,
"rev" : [ ],
"route" : null,
"target" : null,
"title" : null,
"type" : "Zend_Navigation_Page_Mvc",
"visible" : true
},
{ "action" : "index",
"active" : false,
"class" : null,
"controller" : "index",
"id" : null,
"label" : "News",
"module" : "news",
"order" : null,
"pages" : [ ],
"params" : [ ],
"privilege" : "index",
"rel" : [ ],
"reset_params" : true,
"resource" : "news:index",
"rev" : [ ],
"route" : null,
"target" : null,
"title" : null,
"type" : "Zend_Navigation_Page_Mvc",
"visible" : true
},
{ "action" : "index",
"active" : false,
"class" : null,
"controller" : "index",
"id" : null,
"label" : "Tutorials",
"module" : "tutorials",
"order" : null,
"pages" : [ ],
"params" : [ ],
"privilege" : "index",
"rel" : [ ],
"reset_params" : true,
"resource" : "tutorials:index",
"rev" : [ ],
"route" : null,
"target" : null,
"title" : null,
"type" : "Zend_Navigation_Page_Mvc",
"visible" : true
},
{ "action" : "about",
"active" : false,
"class" : null,
"controller" : "index",
"id" : null,
"label" : "About",
"module" : "default",
"order" : null,
"pages" : [ ],
"params" : [ ],
"privilege" : "about",
"rel" : [ ],
"reset_params" : true,
"resource" : "default:index",
"rev" : [ ],
"route" : null,
"target" : null,
"title" : null,
"type" : "Zend_Navigation_Page_Mvc",
"visible" : true
},
{ "action" : "comments",
"active" : false,
"class" : null,
"controller" : "index",
"id" : null,
"label" : "Comments",
"module" : "default",
"order" : null,
"pages" : [ ],
"params" : [ ],
"privilege" : "comments",
"rel" : [ ],
"reset_params" : true,
"resource" : "default:index",
"rev" : [ ],
"route" : null,
"target" : null,
"title" : null,
"type" : "Zend_Navigation_Page_Mvc",
"visible" : true
},
{ "action" : "index",
"active" : false,
"class" : null,
"controller" : "contact",
"id" : null,
"label" : "Contact",
"module" : "default",
"order" : null,
"pages" : [ ],
"params" : [ ],
"privilege" : "index",
"rel" : [ ],
"reset_params" : true,
"resource" : "default:contact",
"rev" : [ ],
"route" : null,
"target" : null,
"title" : null,
"type" : "Zend_Navigation_Page_Mvc",
"visible" : true
},
{ "action" : "login",
"active" : false,
"class" : null,
"controller" : "auth",
"id" : null,
"label" : "Login",
"module" : "default",
"order" : null,
"pages" : [ ],
"params" : [ ],
"privilege" : "login",
"rel" : [ ],
"reset_params" : true,
"resource" : "default:auth",
"rev" : [ ],
"route" : null,
"target" : null,
"title" : null,
"type" : "Zend_Navigation_Page_Mvc",
"visible" : true
},
{ "action" : "logout",
"active" : false,
"class" : null,
"controller" : "auth",
"id" : null,
"label" : "Logout",
"module" : "default",
"order" : null,
"pages" : [ ],
"params" : [ ],
"privilege" : "logout",
"rel" : [ ],
"reset_params" : true,
"resource" : "default:auth",
"rev" : [ ],
"route" : null,
"target" : null,
"title" : null,
"type" : "Zend_Navigation_Page_Mvc",
"visible" : true
}
]
This is when I am currently logged in as admin and the links for login should be hidden but its not. This looks like the ACL is not being properly assigned to the navigation.
I think that Zend_Navigation uses ACL to determine if page should by visible during render time. It doesn't alter your navigation objects when you pass ACL object to it.
You need a different approach if you want to create custom navigation container reduced by ACL - maybe extending Zend Navigation classes and add appropriate methods.
You will need to force Zend_Navigation to re-render the navigation. As stated before, it "filters" the rendering based on the acl. Also, not sure how you are storing that role in Zend_Registry, but you may want to consider having that role returned when you query Zend_Auth to authenticate the user, if that role is not updated it could also cause issue. I would also suggest setting up your acl etc in a action helper instead of an action, so that it can process for "any" action instead of just this action. The fact that the items are present should indicate that the menu for some reason is not being re-rendered, or the updated role is not being passed to the Acl, if it were the menu items would not be present in the menu at all. Zend_Navigation will not render any items that have been filtered by the acl, it goes far beyond just setting the item to not be visible.
Furthermore, what role defines the privilege for showing the login tab? You should tell the acl to deny that to a standard user and up if your roles are inheriting from a common user role, if not, then it may indeed be working just as it should. As an example:
//..//..//
$this->addResource(new Zend_Acl_Resource('login')); //<- Allows the hiding of the login navigation tab
$this->addResource(new Zend_Acl_Resource('logout')); //<- Allows the hiding of the logout navigation tab
$this->allow($guest, array('user'), array('user.login', 'user.register'));
$this->allow('user', array('user', 'useraccount'), array('user.view', 'user.logout', 'user.account-editown', 'user.edit-account', //<-end user privs
));
//..//..//
$this->deny('user', array('user'), array('user.login', 'user.register'));