User Tools

Site Tools


acq:developers

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
acq:developers [2007/12/09 21:46] artacq:developers [2022/02/10 13:34] (current) – external edit 127.0.0.1
Line 1: Line 1:
 +**This page is obsolete.**  OfBiz is not part of current developments.
  
 +======Acquisitions/Serials - notes for developers======
 +
 +OFBiz/opentaps is huge, but lots of organizations use it and there is a lot of material online. A particularly useful document is [[http://www.opensourcestrategies.com/ofbiz/ofbiz_debugging.txt|Zen and the Art of Debugging OFBiz]], especially the section on log files. The [[http://www.opensourcestrategies.com/ofbiz/hello_world1.php|Hello World]] example gives a good sense of how OFBiz works. David Jones' [[http://docs.ofbiz.org/display/OFBTECH/Framework+Introduction+Videos+and+Diagrams|introductory video]] is also very helpful.
 +
 +We have made some changes to the layout of OFBiz, nothing complex in a technical sense, but it is well worth touching base with us before embarking on a developer's journey. On the other hand, downloading it directly to see how it works is still a valid option. 
 +
 +In general, the main objects of most changes do not require a restart:
 +
 +  * [[http://freemarker.org/|Freemaker]] templates (see this [[http://www.javaworld.com/javaworld/jw-11-2007/jw-11-java-template-engines.html|comparison to Velocity]])
 +  * [[http://www.beanshell.org/|Beanshell]] files
 +  * Screens XML files
 +  * Forms XML files
 +  * controller XML file
 +
 +There is a cache that often needs to be cleared in //Web Tools// when making modifications, but this is still a much faster and more convenient option than waiting for a tomcat restart.
 +
 +====Up close: modifying the Purchase Order screen====
 +
 +OFBiz applications are made up of many small pieces. Although some developers use OFBiz as a general purpose framework, its value is clearly far more as a ready source of financial functions and data models (see [[http://www.djangoproject.com/|Django]] or [[http://www.rubyonrails.org/|Ruby on Rails]] if you are looking for a framework to build something from scratch). 
 +
 +The example that might be the most helpful is both intricate (in terms of layout) and trivial (in terms of actual code), and that is using  [[http://open-ils.org/dokuwiki/doku.php?id=osrf-devel:primer|OpenSRF]] to bring records from the //Evergreen// catalogue into the Purchase Order screen:
 +
 +{{:acq:dev1.jpg|PO Screen}}
 +
 +As you may have guessed, the "Evergreen Bib Lookup" option has been a customized addition to this screen. In order to see how this was done, we go to the key to any interaction, which is the URL, in this case:
 +
 +  https://localhost:8443/ordermgr/control/setOrderCurrencyAgreementShipDates
 +
 +We will ignore the hostname and port, and focus on the rest of the url:
 +
 +  /ordermgr/control/setOrderCurrencyAgreementShipDates
 +
 +The first part of the address is the //mount-point//, and this is specified in the //ofbiz-component.xml// associated with the application. Since the application in this case is related to ordering, the ofbiz-component.xml file we want is in located in the subdirectory //applications/order//.
 +
 +{{:acq:dev2.jpg|ofbiz-component.xml}}
 +
 +And, sure enough, in this file, we find the entry point for //ordermgr//:
 +
 +  <webapp name="order"
 +        title="Order"
 +        server="default-server"
 +        location="webapp/ordermgr"
 +        base-permission="OFBTOOLS,ORDERMGR"
 +        mount-point="/ordermgr"/>
 +
 +This tells us that the building blocks of //ordermgr// are in the subdirectory //webapp/ordermgr//.
 +
 +{{:acq:dev3.jpg|ordermgr dir listing}}
 +
 +====The Role of the Controller====
 +
 +The file that represents the switchboard for URL requests is //controller.xml//, which is always located in the //WEB-INF// subdirectory. The //control// part of the URL identifies the servlet involved and confirms that this interaction is under a controller, and we look for the section that describes what happens when //setOrderCurrencyAgreementShipDates// is requested.
 +
 +      <request-map uri="setOrderCurrencyAgreementShipDates">
 +            <description>
 +                  Handles setting the currency, agreement and shipment dates of an order.
 +            </description>
 +            <security https="true" auth="true"/>
 +            <event type="java" path="org.ofbiz.order.shoppingcart.ShoppingCartEvents"
 +                  invoke="setOrderCurrencyAgreementShipDates"/> 
 +            <response name="success" type="request" value="orderentry"/>
 +            <response name="error" type="request" value="orderagreements"/>
 +      </request-map> 
 +
 +This tells us a bit about what this request is responsible for, and what action should take place. In this case, a java action is invoked (//setOrderCurrencyAgreementShipDates//), and the action can either return a value indicating success or error. Generally speaking, this is how you would normally weave a java interaction into an application, and looking at the source of the //ShoppingCartEvents// class reveals the java contortions that can be mixed in. However, let's assume that the action is successful and skip to //orderentry//. This also maps to a request, and is specified in //controller.xml//.
 +
 +      <request-map uri="orderentry">
 +            <security https="true" auth="true"/>
 +            <event type="java" path="org.ofbiz.order.shoppingcart.ShoppingCartEvents" 
 +                  invoke="routeOrderEntry"/>
 +            <response name="init" type="view" value="checkinits"/>
 +            <response name="agreements" type="view" value="orderagreements"/>
 +            <response name="cart" type="view" value="showcart"/>
 +            <response name="error" type="view" value="checkinits"/>
 +      </request-map>
 +
 +Again, more java plumbing is invoked, but this time there are 4 possible outcomes. It is possible to glean the jumping off point from the code, but the //runtime/logs/console.log// also gives a running update on what's happening.
 +
 +      [        UtilXml.java:246:DEBUG] XML Read 0.061s: jndi:/0.0.0.0/ordermgr/WEB-INF/controller.xml
 +      [ConfigXMLReader.java:561:INFO ] ConfigMap Created: (4) records in 0.0030s
 +      [ConfigXMLReader.java:719:INFO ] HandlerMap Created: (4) view handlers and (6) request/event handlers in 0.0s
 +      [ConfigXMLReader.java:294:INFO ] RequestMap Created: (295) records in 0.016s
 +      [ConfigXMLReader.java:388:INFO ] ViewMap Created: (129) records in 0.0010s
 +      [        UtilXml.java:246:DEBUG] XML Read 0.046s: file:opentaps-1.0-dist/applications/order/widget/ordermgr/OrderViewScreens.xml
 +      [  ScreenFactory.java:121:INFO ] Got 19 screens in 0.084s from: file:opentaps-1.0-dist/applications/order/widget/ordermgr/OrderViewScreens.xml
 +      [        UtilXml.java:246:DEBUG] XML Read 0.03s: file:opentaps-1.0-dist/applications/order/widget/ordermgr/CommonScreens.xml
 +      [  ScreenFactory.java:121:INFO ] Got 4 screens in 0.036s from: file:opentaps-1.0-dist/applications/order/widget/ordermgr/CommonScreens.xml
 +
 +This is where things start getting interesting, and we can start to see how the //screen//, a snippet of HTML for display, is coming together. Each response in //controller.xml// has a //type//, and in this case, the type is //view//. Peeking inside //OrderViewScreens.xml//, for example, shows how screens can be defined.
 +
 +      <screen name="minicart">
 +            <section>
 +                  <actions>
 +                        <set field="hidetoplinks" value="Y"/>
 +                        <set field="hidebottomlinks" value="Y"/>
 +                  </actions>
 +                  <widgets>
 +                        <platform-specific>
 +                              <html>
 +                                    <html-template 
 +                                          location="component://order/webapp/ordermgr/entry/cart/minicart.ftl"/>
 +                              </html>
 +                        </platform-specific>
 +                  </widgets>
 +            </section>
 +      </screen>
 +
 +In this case, //minicart.ftl// is an Freemaker template file, and the //component// designation indicates a directory relative to the //applications// directory, so that the source file can be found in //applications/order/webapp/ordermgr/entry/cart//. Freemaker is similar to other templating engines, and brings together what should normally be a thin layer of business logic with presentation options.
 +
 +      <div class="screenlet">
 +            <div class="screenlet-header">
 +                  <div class='boxhead'><b>${uiLabelMap.EcommerceCartSummary}</b></div>
 +            </div>
 +            <div class="screenlet-body">
 +                  <#if (shoppingCartSize > 0)>
 +                        <#if hidetoplinks?default("N") != "Y">
 +                              <div><a href="<@ofbizUrl>view/showcart</@ofbizUrl>" class="buttontext">
 +                                    ${uiLabelMap.EcommerceViewCart}</a>&nbsp;
 +                                    <a href="<@ofbizUrl>checkoutoptions</@ofbizUrl>" class="buttontext">
 +                                          ${uiLabelMap.EcommerceCheckout}
 +                                    </a>
 +                              </div>
 +                        </#if>
 +                  </#if>
 +            </div>
 +      </div>
 +
 +====Launching from a Button====
 +
 +To add an Evergreen lookup, we modify the //showcart.ftl// file, which contains the layout of the portion of the screen we are targeting. 
 +
 +      <a href="javascript:quicklookupevergreen_popup(document.quickaddform.add_product_id)"
 +            class="buttontext">${uiLabelMap.OrderQuickEvergreenLookup}
 +      </a>
 +
 +Note the use of //uiLabelMap//, it is a common mechanism for keeping labels and other textual content out of the templates. The button initiates a snippet of javascript:
 +
 +      function quicklookupevergreen_popup(element) {
 +            target = element;
 +            var searchTerm = element.value;
 +            var obj_lookupwindow = window.open('/woodchip/control/ils_bib_search?foo=' 
 +                  + searchTerm,'FieldLookup',
 +                  'width=700,height=550,scrollbars=yes,status=no,resizable=yes,top='+my+',
 +                  left='+mx+',dependent=yes,alwaysRaised=yes');
 +            obj_lookupwindow.opener = window;
 +            obj_lookupwindow.focus();
 +      }
 +
 +Once again, the URL invoked is the key construct in making something useful happen, in this case:
 +
 +  /woodchip/control/ils_bib_search
 +
 +Thanks to the wizardry of [[http://esilibrary.com/esi/company.html|Bill Erickson]], this produces a search screen that retrieves records from the catalogue:
 +
 +{{:acq:dev4.jpg|PO Screen}}
 +
 +We would probably want to transfer much more information to //Desiderata//, but let's implement a very simple mechanism to add the //record id// and the //title//, and encapsulate this into the URL:
 +
 +  /catalog/control/EditProduct?record_id=5&title=Harry%20Potter%20and%20the%20deathly%20hallows
 +
 +Once again, the ofbiz-component.xml file reveals the entry point for //catalog//:
 +
 +  <webapp name="catalog" 
 +        title="Catalog" 
 +        server="default-server" 
 +        location="webapp/catalog"
 +        base-permission="OFBTOOLS,CATALOG" 
 +        mount-point="/catalog"/>
 +
 +No surprises here, and the //WEB-INF/controller.xml// tells us that a //view// is produced when this request arrives:
 +
 +  <request-map uri="EditProduct">
 +    <security https="true" auth="true"/>
 +    <response name="success" type="view" value="EditProduct"/>
 +  </request-map>
 +
 +and, in turn, the view is contained inside an XML file called //ProductScreens.xml//:
 +
 +  <view-map name="EditProduct" type="screen" 
 +    page="component://product/widget/catalog/ProductScreens.xml#EditProduct"/>
 +
 +Note the syntax, this indicates that section we want in //ProductScreens.xml// is called //EditProduct//, and we can focus on that part of the screen definition:
 +
 +  <screen name="EditProduct">
 +    <section>
 +      <actions>
 +        <set field="titleProperty" value="PageTitleEditProduct"/>
 +        <set field="tabButtonItem" value="EditProduct"/>
 +        <set field="labelTitleProperty" value="ProductProduct"/>
 +        <set field="productId" from-field="parameters.productId"/>
 +        <script location="component://product/webapp/catalog/WEB-INF/actions/bibadd.bsh"/>
 +        <entity-one entity-name="Product" value-name="product"/>
 +      </actions>
 +      <widgets>
 +      <decorator-screen name="CommonProductDecorator" location="${parameters.mainDecoratorLocation}">
 +        <decorator-section name="body">
 +          <include-form name="EditProduct" 
 +            location="component://product/webapp/catalog/product/ProductForms.xml"/>
 +            <!-- include the duplicate product form template -->
 +            <platform-specific>
 +              <html>
 +                <html-template location="component://product/webapp/catalog/product/EditProductDupForm.ftl"/>
 +              </html>
 +            </platform-specific>
 +          </decorator-section>
 +        </decorator-screen>
 +      </widgets>
 +    </section>
 +  </screen>
 +
 +There is a **lot** going on here, but our addition is quite simple:
 +
 +  <script location="component://product/webapp/catalog/WEB-INF/actions/bibadd.bsh"/>
 +
 +This syntax indicates that we want to run a beanshell script called //bibadd.bsh//, and this is where we specify what values we want for the fields in the resulting form:
 +
 +  import org.ofbiz.base.util.*;
 +  import org.ofbiz.entity.*;
 +  // simple for now, these variables could be passed directly
 +  recordId = request.getParameter("record_id");
 +  title = request.getParameter("title");
 +  if (recordId != null)
 +    context.put("productId", recordId);
 +  if (title != null)
 +    context.put("internalName", title);
 +
 +There are several options for the kind of scripts that can be invoked, but beanshell has the advantage that it can leverage the Java APIs. Not very significant in this example, yet this often very handy for using Java for some quick heavy lifting while retaining a light scripting layer to pull the pieces together. In this case, the resulting form should show the values we transferred from the catalogue.
 +
 +{{:acq:dev5.jpg|Transferred Bib Info Screen}}
 +
 +This is a trivial example, but it hopefully illustrates one general approach to modifying OFBiz/opentaps.
acq/developers.txt · Last modified: 2022/02/10 13:34 by 127.0.0.1

Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 4.0 International
CC Attribution-Share Alike 4.0 International Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki

© 2008-2022 GPLS and others. Evergreen is open source software, freely licensed under GNU GPLv2 or later.
The Evergreen Project is a U.S. 501(c)3 non-profit organization.