Index: openacs-4/packages/ecommerce/tcl/ecds-ex-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ecommerce/tcl/ecds-ex-procs.tcl,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/ecommerce/tcl/ecds-ex-procs.tcl 9 Aug 2008 07:11:51 -0000 1.1 @@ -0,0 +1,259 @@ +ad_library { + Vendor specific customization utilities + This file is a crude example, which requires coding for each vendor + One ecds-N-procs.tcl file per vendor, where N is an abbreviation that identifies the vendor + + procs should return "ERROR" when critical info is unavailable + procs should return empty string for noncritical info + + @creation-date May 2009 + +} + +ad_proc -private ecdsii_ex_product_url_from_vendor_sku { + vendor_sku +} { + returns vendor's url of product or empty string +} { + set url "" + return $url +} + +ad_proc -private ecdsii_ex_no_shipping_avail_p { + page +} { + returns no_shipping_avail_p for item. + defaults to "f". In the future, this should check against an existing shipping status on vendor's page +} { + return "f" +} + +ad_proc -private ecdsii_ex_ec_shipping { + page +} { + returns shipping cost for first item. + For now, returning blank, because we want shipping-cost based on shipping weight. +} { + return "" +} + +ad_proc -private ecdsii_ex_ec_shipping_additional { + page +} { + returns shipping cost for each additional quantity of item. + For now, returning blank, because we want shipping-cost based on shipping weight. +} { + return "" +} + +ad_proc -private ecdsii_ex_product_url { + page +} { + returns product url for vendor's item. + For now, returning blank, but we could point to the vendor's website etc if we wanted +} { + return "" +} + +ad_proc -private ecdsii_ex_product_image_url { + vendor_page +} { + returns url of vendor's product image on vendor's website +} { + #set image_url from image_name + set image_name "" + set image_url "" + return $image_url +} + +ad_proc -private ecdsii_ex_vendor_sku_from_page { + page +} { + returns vendor_sku of vendor's product page +} { + # get the vendor_sku from vendor's page content + set vendor_sku $page + return $vendor_sku +} + +ad_proc -private ecdsii_ex_units { + page +} { + reurns unit of measure from content of a vendor's product page +} { + set unit_of_measure $page + return $unit_of_measure +} + +ad_proc -private ecdsii_ex_unit_price { + page +} { + reurns unit_price from content of a vendor's product page +} { + set unit_price $page + return $unit_price +} + +ad_proc -private ecdsii_ex_brand_name { + page +} { + returns brand_name from content of a vendor's product page +} { + set brand_name $page + return $brand_name +} + +ad_proc -private ecdsii_ex_brand_model_number { + page +} { + returns brand_model_number from content of a vendor's product page +} { + set mfg_model_number $page + return $mfg_model_number +} + +ad_proc -private ecdsii_ex_min_ship_qty { + page +} { + returns minimum shipping quantity from content of a vendor's product page, defaults to 1 +} { + set minimum_shipping_quantity $page + return $minimum_shipping_quantity +} + +ad_proc -private ecdsii_ex_ship_weight { + page +} { + returns shipping weight for one unit from content of a vendor's product page +} { + set ship_weight $page + return $ship_weight +} + +ad_proc -private ecdsii_ex_stock_status { + page +} { + returns stock_status for one unit from content of a vendor's product page +} { + set stock_status $page + return $stock_status +} + +ad_proc -private ecdsii_ex_short_description { + page +} { + returns short_description from content of a vendor's product page +} { + set short_description $page + return $short_description +} + +ad_proc -private ecdsii_ex_long_description { + page +} { + returns long_description from content of a vendor's product page +} { + set long_description $page + return $long_description +} + +ad_proc -private ecdsii_ex_product_name { + page +} { + returns product_name from content of a vendor's product page +} { + set product_name $page + return $product_name +} + +ad_proc -private ecdsii_ex_product_sku { + brand_name + brand_model_number + {sku ""} +} { + returns sku from content of a vendor's product page +} { + set new_sku "{$brand_name}${brand_model_number}" + return $new_sku +} + +ad_proc -private ecdsii_ex_one_line_description { + page +} { + returns one_line_description from content of a vendor's product page +} { + set one_line_description $page + return $one_line_description +} + +ad_proc -private ecdsii_ex_detailed_description { + page +} { + returns detailed_description from content of a vendor's product page +} { + set description $page + return $description +} + +ad_proc -private ecdsii_ex_sales_description { + page +} { + returns sales_description from content of a vendor's product page +} { + set sales_description_html $page + return $sales_description_html +} + +ad_proc -private ecdsii_ex_web_comments { + page +} { + returns comments about product from content of a vendor's product page +} { + set notes_restrictions $page + return $notes_restrictions +} + +ad_proc -private ecdsii_ex_product_options { + page +} { + returns options about product from content of a vendor's product page +} { + return "" +} + +ad_proc -private ecdsii_ex_unspsc_code { + page +} { + returns UNSPSC code about product from content of a vendor's product page +} { + set unspsc_code "" + return $unspsc_code +} + +ad_proc -private ecdsii_ex_category_id_list { + page +} { + returns list of category_ids from content of a vendor's product page +} { + return [list] +} + + +ad_proc -private ecdsii_ex_subcategory_id_list { + page +} { + returns list of subcategory_ids from content of a vendor's product page +} { +upvar category_id_list category_id_list + + set subcategory_id_list [list] + return $subcategory_id_list +} + +ad_proc -private ecdsii_ex_subsubcategory_id_list { + page +} { + returns list of subsubcategory_ids from content of a vendor's product page +} { + return [list] +} Index: openacs-4/packages/ecommerce/tcl/ecds-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ecommerce/tcl/ecds-procs.tcl,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/ecommerce/tcl/ecds-procs.tcl 9 Aug 2008 07:11:51 -0000 1.1 @@ -0,0 +1,1712 @@ +ad_library { + + Customization utilities for maintaining product data in ecommerce module. + + @creation-date Aug 2007 + + #default import procs requires these ec_custom_product_field_values fields defined + unitofmeasure + brandname + brandmodelnumber + minshipqty + shortdescription + longdescription + salesdescription + webcomments + productoptions + unspsccode + vendorsku + +} + +ad_proc -private ecds_is_natural_number { + value +} { + answers question: is value a natural counting number (non-negative integer)? + returns 0 or 1 +} { + set is_natural [regexp {^(0*)(([1-9][0-9]*|0))$} $value match zeros value] + return $is_natural +} + +ad_proc -private ecds_remove_from_list { + value value_list +} { + removes multiple of a specific value from a list + returns list without the passed value +} { + + set value_indexes [lsearch -all -exact $value_list $value] + while { [llength $value_indexes] > 0 } { + set next_index [lindex $value_indexes 0] + set value_list [lreplace $value_list $next_index $next_index] + set value_indexes [lsearch -all -exact $value_list $value] + } + return $value_list +} + + +ad_proc -private ecds_sku_from_brand { + brand_name + brand_model_number + {sku ""} +} { + returns a normalized sku, given brand info, or sku if it exists + @brand_name@ + @brand_model_number@ + @sku@ optional +} { + if { [string length $sku] == 0 } { + set product_sku [string trim [string tolower $brand_model_number]] + + set brandname_width 16 + set brandname_len_limit 14 + regsub -all -- { } $brand_name {} brandname_new + if { [string length $brandname_new] > $brandname_width } { + set brandname_end [expr { [string last " " [string range $brandname_new 0 $brandname_width] ] - 1 } ] + if { $brandname_end < 0 } { + set brandname_end $brandname_len_limit + } + set brandname_new [string range $brandname_new 0 $brandname_end ] + regsub {[^a-zA-Z0-9]+\.\.} $brandname_new {} brandname_new + } + regsub -all -- { } $brandname_new {-} brandname_new + set brandname_new [string trim [string tolower $brandname_new]] + + set sku "${brandname_new}${product_sku}" + } + return $sku +} + + +ad_proc -private ecds_base_shipping_price_from_order_value { + total_price +} { + returns the value based shipping price, based on the value of the total price of items in the cart + this value based shipping price gets added to the base_shipping_price + this is ignored when using a shipping gateway +} { + # example 1, shipping increases with price + # set value_based_shipping [expr pow($total_price,0.5) ] + + #example 2, free shipping for orders over $130, and shipping amount decreases with price + # add a decreasing shipping and handling amount for items that do not have much profit margin (to cover shipping) + # if price is large enough (say over 130), we assume we are able to cover shipping out of profit) + # lets assume we want to have at least 5usd shipping and handling + # if { $total_price <= 130 } { + # set value_based_shipping \[expr { ( -5 * $total_price / 130 ) + 5 } \] + # } else { + # set value_based_shipping 0 + # } + + set value_based_shipping 0 + return $value_based_shipping +} + + +ad_proc -private ecds_get_url { + url + {refresh_period "190 days ago"} +} { + returns page content of url, caches data so as to not clobber other server if page request is newer than refresh_period, + where refresh_period is a tcl based relative time reference +} { + set url_cache_dir [parameter::get -parameter CacheDataDirectory -default ecds-url-cache] + # if the page has been retrieved previously, use the cached version + + db_0or1row check_url_history {select url,cache_filepath ,last_update,flags from ecds_url_cache_map where url = :url} + + # if cache is within refresh period (cache time is > refresh time), get the cached version + if { [info exists last_update] && [clock scan [string range $last_update 0 18]] > [clock scan $refresh_period] } { + # get the page from filepath + # ns_log Notice "ecds_get_url: getting page from filepath" + # set filepathname [file join [acs_root_dir] $url_cache_dir $base_url] + set filepathname $cache_filepath + # file open + if { [catch {open $filepathname r} fileId]} { + ns_log Error "ecds_get_url: file $filepathname not found." + ad_script_abort + } else { + # read file + while { ![eof $fileId] } { + gets $fileId line_of_file + append page $line_of_file + } + close $fileId + } + + } else { + # get file from url + if { [catch {set get_id [ns_http queue GET $url]} err ]} { + set page $err + } else { + ns_log Notice "ecds_get_url: ns_httping $url" + set flags "" + set status [ns_http wait $get_id page] + + if { $page eq "timeout" } { + # no page info returned, just return error + set page "Error: url timed out" + } else { + #ns_log Notice "ecds_get_url: adding page to file cache" + #put page into file cache + set base_url [string range $url 7 end] + set filepathname [file join [acs_root_dir] $url_cache_dir $base_url] + # if ec_assert_directory doesnot work here, try replacing ns_mkdir with 'file mkdir' or + # make the ec_asser_directory recursive + set filepath [file dirname $filepathname] + ec_assert_directory $filepath + if { [catch {open $filepathname w} fileId]} { + ns_log Error "ecds_get_url: unable to write to file $filepathname" + ad_script_abort + } else { + if { ![string match -nocase {*.[jgpb][pinm][egfp]} $url ] } { + #ns_log Notice "ecds_get_url: compressing content of $url" + # strip extra lines and funny characters + regsub -all -- {[\f\e\r\v\n\t]} $page { } oneliner + # strip extra spaces + regsub -all -- {[ ][ ]*} $oneliner { } oneliner2 + # could strip SCRIPT tags here to save space, but that content might contain valuable string fragments + set page $oneliner2 + puts $fileId $page + ns_log Notice "ecds_get_url: writing $filepathname to ecds-cache" + close $fileId + + } else { + # this is an image, prepare to send binary + # following doesn't work for aolserver 4.0x, so we use alternate method + # fconfigure $fileId -translation binary + #puts $fileId $page + + # given $image_url + set file_dir_path [file dirname $filepathname] + ec_assert_directory $file_dir_path + if { [catch {exec /usr/local/bin/wget -q -nc -t 1 -P${file_dir_path} -- $image_url} errmsg ] } { + set testita $errmsg + } else { + set testita $filepathname + ns_log Notice "ecds_get_url: wgetting $image_url" + # wait 20 sec to see if file is gotten + after 20000 + if { [file exists $filepathname] } { + # success! file gotten, now we can process it + } else { + ns_log Warning "file $filepathname does not exist after attempt to fetch from $url" + } + + } + + + ns_log Notice "ecds_get_url: writing $filepathname to ecds-cache" + close $fileId + } + # log cache into map + if { [db_0or1row check_url_in_cache {select url from ecds_url_cache_map where url = :url}] } { + db_dml update_cache {update ecds_url_cache_map set cache_filepath =:filepathname, last_update=now(), flags=:flags where url=:url} + } else { + db_dml insert_to_cache {insert into ecds_url_cache_map + (url,cache_filepath,last_update,flags) + values (:url,:filepathname,now(), :flags) } + } + + } + } + } + } + return $page +} + +ad_proc -private ecds_get_image_from_url { + url + {refresh_period "190 days ago"} +} { + returns filepathname of local copy of image at url + caches data so as to not clobber other server if page request is newer than refresh_period, + where refresh_period is a tcl based relative time reference +} { + set status "OK" + # if the page has been retrieved previously, just get the filepath + set url_cache_dir [parameter::get -parameter CacheDataDirectory -default ecds-url-cache] + + db_0or1row check_url_history {select cache_filepath,last_update,flags from ecds_url_cache_map where url = :url} + + if { [info exists last_update] && [clock scan [string range $last_update 0 18]] > [clock scan $refresh_period] } { + # set the filepath +# set filepathname [file join [acs_root_dir] $url_cache_dir $cache_filepath] + set filepathname $cache_filepath + } else { + + #fetch image, put into cache directory tree + # 1 means use wget because aolserver4.0.10 ns_http does not work for images. + if { 1 } { + # given $url + set base_url [string range $url 7 end] + set filepathname [file join [acs_root_dir] $url_cache_dir $base_url] + set filepath [file dirname $filepathname] + ec_assert_directory $filepath + if { [catch {exec /usr/local/bin/wget -q -nc -T 20 -t 1 -P${filepath} -- $url} errmsg ] } { + ns_log Error "ecds_get_image_from_url: $errmsg" + set status "ERROR" + } else { + # wait 20 sec to see if file is gotten + ns_log Notice "ecds_get_image_from_url: wgetting $url" + after 20000 + if { [file exists $filepathname] } { + # success! file gotten, now we can process it + set flags "" + # log cache into map + if { [db_0or1row check_url_in_cache {select url from ecds_url_cache_map where url = :url}] } { + db_dml update_cache {update ecds_url_cache_map set cache_filepath =:filepathname, last_update=now(), flags=:flags where url=:url} + } else { + db_dml insert_to_cache {insert into ecds_url_cache_map + (url,cache_filepath,last_update,flags) + values (:url,:filepathname,now(), :flags) } + } + + } else { + ns_log Error "ecds_get_image_from_url: file $filepathname does not exist after attempt to fetch from $url" + set status "ERROR" + } + + } + + + } else { + # ns_http does not work for aolserver 4.0.10, using wget instead + # more info at: http://openacs.org/forums/message-view?message_id=1200269 + # if aolserver4.5, should work to use: + + if { [catch {set get_id [ns_http queue GET $url]} err ]} { + set page $err + } else { + ns_log Notice "ecds_get_image_from_url: ns_httping $url" + set flags "" + set status [ns_http wait $get_id page] + + if { $page eq "timeout" || [string length $page] < 20 } { + # no page info returned, just return error + set page "Error: url timed out" + set filepathname "" + set status "ERROR" + } else { + #ns_log Notice "ecds_get_url: adding page to file cache" + #put page into file cache + set base_url [string range $url 7 end] + set filepathname [file join [acs_root_dir] $url_cache_dir $base_url] + set filepath [file dirname $filepathname] + ec_assert_directory $filepath + if { [catch {open $filepathname w} fileId]} { + ns_log Error "ecds_get_image_from_url: unable to write to file $filepathname" + set status "ERROR" + } else { + # this is an image, prepare to save binary + fconfigure $fileId -translation binary + puts $fileId $page + #ns_log Notice "ecds_get_url: writing $filepathname with content: $page" + close $fileId + # log cache into map + if { [db_0or1row check_url_in_cache {select url from ecds_url_cache_map where url = :url}] } { + db_dml update_cache {update ecds_url_cache_map set cache_filepath =:filepathname, last_update=now(), flags=:flags where url=:url} + } else { + db_dml insert_to_cache {insert into ecds_url_cache_map + (url,cache_filepath,last_update,flags) + values (:url,:filepathname,now(), :flags) } + } + + } + } + } + } + } +ns_log Notice "ecds_get_image_from_url: status is $status" + if { $status eq "OK" } { + return $filepathname + } else { + return $status + } +} + +ad_proc -private ecds_import_image_to_ecommerce { + product_id + image_filepathname +} { + imports an image from the system into ecommerce, returns 1 if works, or 0 if errors + # this code requires product_id, image_filepathname +} { + set serious_errors 0 + set convert [ec_convert_path] + # check imagename + if { [string match -nocase {*picsn.jpg} $image_filepathname ] || [empty_string_p $image_filepathname] || [string match -nocase *avail* $image_filepathname ] } { + # image_filepathname is "notavail.jpg" + # do not process + } else { + db_1row get_product_dirname "select dirname from ec_products where product_id = :product_id" + set new_imagetype [string tolower [string range $image_filepathname end-2 end]] + + if { ![string equal $new_imagetype "jpg"] && ![string equal $new_imagetype "gif"] } { + ns_log Error "ecds_import_image_to_ecommerce: cannot handle non jpg/gif files. image_pathname = ${image_filepathname}" + ad_script_abort + } + + set 2prod [ec_product_file_directory $product_id] + set product_path [file join [ec_data_directory_mem] [ec_product_directory_mem] $2prod $dirname] + ec_assert_directory $product_path + set product_base_pathname [file join $product_path "product." ] + set product_image_location "${product_base_pathname}${new_imagetype}" + + # update the product image + if { [file exists "${product_base_pathname}jpg" ] } { + file delete "${product_base_pathname}jpg" + } + if { [file exists "${product_base_pathname}gif" ] } { + file delete "${product_base_pathname}gif" + } + + if { [catch {file copy $image_filepathname $product_image_location} errmsg] } { + ns_log Warning "Ref 50: while creating product image: $errmsg" + set serious_errors 1 + } else { + # create thumbnail + set use_both_param_dimensions [parameter::get -parameter ThumbnailSizeOuterlimits] + set thumbnail_width [parameter::get -parameter ThumbnailWidth] + set thumbnail_height [parameter::get -parameter ThumbnailHeight] + if { $use_both_param_dimensions && !$serious_errors } { + set convert_dimensions "${thumbnail_width}x${thumbnail_height}>" + } elseif { !$serious_errors } { + if { [string length $thumbnail_width] == 0 } { + if { [string length $thumbnail_height] == 0 } { + set convert_dimensions "100x10000" + } else { + set convert_dimensions "10000x${thumbnail_height}" + } + } else { + set convert_dimensions "${thumbnail_width}x10000" + } + } + set system_url [parameter::get -package_id [ad_acs_kernel_id] -parameter SystemURL] + set system_name [parameter::get -package_id [ad_acs_kernel_id] -parameter SystemName] + set image_comment "from $system_url $system_name" + set perm_thumbnail_filename [file join $product_path "product-thumbnail.jpg"] + + if { [catch {exec $convert -geometry $convert_dimensions -comment \"$image_comment\" $product_image_location $perm_thumbnail_filename} errmsg ]} { + ns_log Notice "ecds_import_image_to_ecommerce: while creating thumbnail: $errmsg" + set serious_errors 1 + } + } + } + return $serious_errors +} + +ad_proc -private ecds_get_contents_from_tag { + start_tag + end_tag + page + {start_index 0} +} { + Returns content of an html/xml or other bracketing tag that is uniquely identified within a page fragment or string. + helps pan out the golden nuggets of data from the waste text when given some garbage with input for example +} { + set start_col [string first $start_tag $page $start_index] + set end_col [string first $end_tag $page $start_col] + if { $end_col > $start_col && $start_col > -1 } { + set tag_contents [string trim [string range $page [expr { $start_col + [string length $start_tag] } ] [expr { $end_col -1 } ]]] + } else { + set tag_contents "" + ns_log Warning "no contents for tag $start_tag" + } + return $tag_contents +} + +ad_proc -private ecds_get_contents_from_tags_list { + start_tag + end_tag + page +} { + Returns content (as a list) of all occurances of an html/xml or other bracketing tag that is somewhat uniquely identified within a page fragment or string. + helps pan out the golden nuggets of data from the waste text when given some garbage with input for example +} { + set start_index 0 + set tag_contents_list [list] + set start_tag_len [string length $start_tag] + set start_col [string first $start_tag $page 0] + set end_col [string first $end_tag $page $start_col] + set tag_contents [string range $page [expr { $start_col + $start_tag_len } ] [expr { $end_col - 1 } ]] + while { $start_col != -1 && $end_col != -1 } { + lappend tag_contents_list [string trim $tag_contents] + + set start_index [expr { $end_col + 1 }] + set start_col [string first $start_tag $page $start_index] + set end_col [string first $end_tag $page $start_col] + set tag_contents [string range $page [expr { $start_col + $start_tag_len } ] [expr { $end_col - 1 } ]] + } + return $tag_contents_list +} + +ad_proc -private ecds_remove_tag_contents { + start_tag + end_tag + page +} { + Returns everything but the content between start_tag and end_tag (as a list) + of all occurances on either side of an html/xml or other bracketing tag + that is somewhat uniquely identified within a page fragment or string. + This is handy to remove script tags and < ! - - web comments - - > etc + helps pan out the golden nuggets of data from the waste text when given some garbage with input for example +} { + # start and end refer to the tags and their contents that are to be removed + set start_index 0 + set tag_contents_list [list] + set start_tag_len [string length $start_tag] + set end_tag_len [string length $end_tag] + set start_col [string first $start_tag $page 0] + set end_col [string first $end_tag $page $start_col] + # set tag_contents [string range $page 0 [expr { $start_col - 1 } ] ] + while { $start_col != -1 && $end_col != -1 } { + set tag_contents [string range $page $start_index [expr { $start_col - 1 } ] ] + lappend tag_contents_list [string trim $tag_contents] + + # start index is where we begin the next clip + set start_index [expr { $end_col + $end_tag_len } ] + set start_col [string first $start_tag $page $start_index] + set end_col [string first $end_tag $page $start_col] + # and the new clip ends at the start of the next tag + } + # append any trailing portion + lappend tag_contents_list [string range $page $start_index end] + set remaining_contents [join $tag_contents_list ""] + return $remaining_contents +} + + +ad_proc -private ecds_convert_html_list_to_tcl_list { + html_list +} { + converts a string containing an html list to a tcl list + Assumes there are no embedded sublists, and strips remaining html +} { + set draft_list $html_list + + #we standardize the start and end of the list, so we know where to clip + + if { [regsub -nocase -- {<[ou][l][^\>]*>} $draft_list "