Merge branch 'master' into WebGUI8. Merged up to 7.10.4
This commit is contained in:
commit
5f3014aaee
66 changed files with 3078 additions and 997 deletions
|
|
@ -1,3 +1,32 @@
|
|||
7.10.5
|
||||
|
||||
7.10.4
|
||||
- Added WebGUI::Fork api
|
||||
- Moved html export to Fork
|
||||
- Moved clipboard functions to Fork
|
||||
- Moved trash functions to Fork
|
||||
- Moved version tag rollback to Fork
|
||||
- fixed #11929: In/Out board breaks in Chrome sometimes
|
||||
- fixed #11928: Story Archive breaks if url has extension
|
||||
- fixed #11920: Defaul DataForm emails missing entries.
|
||||
- fixed #11921: DataForm emails contain 1 table per field
|
||||
- fixed #11922: Help tempalte is squatting on a good URL
|
||||
- fixed #11923: Collaboration System Mail Cron Errors
|
||||
- fixed #11925: Some problems in Thingy export (metaData values in CSV export)
|
||||
- fixed #11925: Some problems in Thingy export (export times out on Things with many rows)
|
||||
- refixed #11902: forums bug (works without mobile style being set)
|
||||
- fixed #11924: Deleting version tags leaves pending inbox messages in a permanent state
|
||||
- intalled YUI fix for flash files.
|
||||
- fixed #11932: Bad URL in the newly templated recover password email
|
||||
- fixed #11938: upgrade script always removes specialState
|
||||
- fixed #11937: Duplicating events does not duplicate storage locations
|
||||
- fixed #11935: shortcut uses wrong value for getContentLastModified
|
||||
- fixed #11940: quickCSV chokes on newlines in data
|
||||
- fixed #11933: EMS shows a dollar currency symbol on Badge Listing
|
||||
- fixed #11941: cannot edit default templates in some browsers
|
||||
- fixed #11926: Version Tag: Delete
|
||||
- fixed #11939: WebGUI replacements are not anchored.
|
||||
|
||||
7.10.3
|
||||
- fixed #11903: Unnecessary debug in Thingy
|
||||
- fixed #11908: Inbox messages linger after deleting a user
|
||||
|
|
@ -3978,674 +4007,3 @@
|
|||
- Made the Include macro more secure.
|
||||
- Added Len's patch to fix some caching problems.
|
||||
|
||||
|
||||
7.3.3
|
||||
- fix: Wiki Purge throws fatal
|
||||
- fix: Calendar now reports proper product ID on iCal feed
|
||||
- fix: Calendar now tries to use the feed ID when sending uid of event on iCal
|
||||
feeds (to prevent over-propagation of events shared between calendars).
|
||||
- fix: Bug in AssetLineage->getLineage documentation.
|
||||
- rfe: Event now has a template var to toggle if an event only lasts one day
|
||||
- rfe: WebGUI::DateTime->toMysql now automatically adjusts to UTC. NOTE:
|
||||
toMysqlDate and toMysqlTime do NOT adjust for timezones. If you are
|
||||
using them you must adjust manually.
|
||||
- fix: Bug in WebGUI::DateTime documentation
|
||||
- fix: Calendar default date of "first event" or "last event" now works.
|
||||
- fix: Calendar now handles Events that have ' in titles appropriately.
|
||||
- rfe: Added a "dateSpan" Event template variable that will show a properly
|
||||
formatted date/time span depending on how the event's start and end are.
|
||||
- fix: Disobedient Forum Rich Editor
|
||||
- fix: SQLForm - fixed a bug where regexes would sometimes be ignored (Martin
|
||||
Kamerbeek / Oqapi)
|
||||
- fix: SQLForm - checkList/varchar (Martin Kamerbeek / Oqapi)
|
||||
- fix: testEnvironment.pl
|
||||
|
||||
7.3.2
|
||||
- fix: Calendar and Event now have printable templates and URL parameters.
|
||||
- fix: Miscellaneous Calendar template fixes
|
||||
- fix: Cannot manageAssets with a locked Thread -- NOTE: Kludgy, but any other
|
||||
way would probably have to break API.
|
||||
- t/lib/WebGUI/Test.pm now has a method for returning the path to the
|
||||
test collateral directory. The method is called getTestCollateralPath.
|
||||
Existing tests using that directory have been modified to use the new
|
||||
method instead of finding the path manually.
|
||||
- fix: Avatar in Thread & Posts of CS
|
||||
- fix: CS Phishing Exploit.
|
||||
- fix: Groups admin gui (1) Default should be contains
|
||||
- fix: Groups admin gui (2,3) Make group form sticky
|
||||
- fix: Wiki does not show history correctly
|
||||
- fix: SQLForm - Field Constraint (Martin Kamerbeek / Oqapi)
|
||||
- fix: SQLForm - Default search template uses downloadUrl in stead of
|
||||
templateUrl for displaying thumbnails. (Martin Kamerbeek / Oqapi)
|
||||
- fix: SQLForm - Required file fields could be left open (Martin Kamerbeek /
|
||||
Oqapi)
|
||||
- fix: SQLForm - Using radio buttons would error when re-edited (Martin Kamerbeek /
|
||||
Oqapi)
|
||||
- fix: SQLForm - DBD Error handling (Martin Kamerbeek / Oqapi)
|
||||
- fix: Media folder permission check
|
||||
|
||||
|
||||
7.3.1
|
||||
- Fixed a problem with IE and resizable text areas that caused IE to crash
|
||||
when loading edit screens.
|
||||
- Fixed a problem with the new autocommit code that caused reply posts not to
|
||||
work in the collaboration system.
|
||||
- Storage deletes were throwing fatals when they should throw warnings.
|
||||
- Fixed a bug in WebGUI::ProfileField->getCategory which caused it to always
|
||||
return undef. (Martin Kamerbeek / Procolix)
|
||||
- Fixed a bug in WebGUI::Asset::File where update did not update the
|
||||
internally cached storage object inside of _storageLocation.
|
||||
This is probably only a real problem in persistent code, like Workflow
|
||||
Activities and tests.
|
||||
Added tests for File and Image assets to verify that this happens correctly.
|
||||
- fix - Unable to add EventsCalendar
|
||||
- fix - Some functions in InOutBoard not internationalized
|
||||
- fix: Calendar/Event not handling gateway properly.
|
||||
- fix: Calendar templates crushing other styles.
|
||||
- fix: Using YUI to add the appropriate events when loading the Add/Edit Event
|
||||
page. Should fix the strange IE bugs.
|
||||
- RFE: Add wiki page variables to Wiki_Master.pm
|
||||
- fix: Not translated labels no displaing
|
||||
|
||||
|
||||
7.3.0
|
||||
- NOTICE: The Template Managers group is deprecated. It has not been removed
|
||||
from the system, but you do not have to be in the Template Managers group
|
||||
to edit or add Templates. Those privileges have been transferred to the
|
||||
Turn On Admin group.
|
||||
- TESTS: The help labels were broken out from the i18n/label.t test into their own
|
||||
test. An environment variable, CODE_COP, is used to enable the long
|
||||
i18n/label.t and help/setHelp.t tests.
|
||||
- documented the Deactivate Account Template.
|
||||
- Added the setNamespace and getNamespace methods to WebGUI::International.
|
||||
- Fixed bad caching via codespace in Operation::Help. The original failed all the time.
|
||||
- Implemented codespace caching in WebGUI::International. This replaces the
|
||||
in-memory cache by symbol table lookups into the code itself and saves
|
||||
duplicating the i18n entries.
|
||||
- Added accordion javascript object, which will eventually replace the
|
||||
current adminbar accordion. This one is less of a cludge and uses the YUI
|
||||
API.
|
||||
- WebGUI now has a Wiki!
|
||||
- Upgraded to YUI 0.12.0
|
||||
- Upgraded to YUI-Ext 0.33 RC2
|
||||
- Karma RFE: DataForm file upload patch. Thanks to mistoo for submitting the
|
||||
original patch. Although I couldn't use the code in wG 7, it inspired the RFE.
|
||||
Also added the feature requested in the thread to allow the files to be
|
||||
emailed as attachments.
|
||||
- Fixed behaviour of the Encrypt Login setting, in such way that only the form
|
||||
post containing the login credentials is sent over https. After authentication
|
||||
the user is redirected to http. (Martin Kamerbeek / Procolix)
|
||||
- fix: RSS From Parent assets should always be hidden from navigation
|
||||
- fix: profile field i18ned possibleValues with apostrophes failing
|
||||
- Added a new DateTime subclass, WebGUI::DateTime, with convenience methods to
|
||||
convert to and from MySQL Date/Time strings. Moving forward, this method
|
||||
should be used in place of the existing WebGUI::Session::DateTime, which can
|
||||
create problems when handling time zones.
|
||||
- Form elements Date, DateTime, and TimeField now return MySQL Date/Time
|
||||
strings when given a MySQL Date/Time string as a default value. This is now
|
||||
the recommended method of storing date/time in the database.
|
||||
- WebGUI::Search now accepts more rules, "where" for specifying an additional
|
||||
where clause, "join" for making join clauses, and "columns" for adding more
|
||||
columns to return.
|
||||
- WebGUI::TabForm->addTab now returns the WebGUI::HTMLForm created.
|
||||
- WebGUI::AssetLineage::getLineage can now limit the number of records returned
|
||||
- fix: IP addresses for adminModeSubnets not using X-Forwarded-For properly
|
||||
- add: workflow activity for expiry of email-unvalidated users. This is not
|
||||
enabled by default; add an instance of it to an appropriate workflow if you
|
||||
want it to run.
|
||||
- fix: subscription dates
|
||||
- fix: Default Rich Editor setting not rendering correctly
|
||||
- fix: visitor name disappearing on preview in CS
|
||||
- fix: HTTP proxy not passing form elements through
|
||||
- upgrade script patches some corrupted commerce template settings
|
||||
- fix: bits of other panels showing through in admin bar
|
||||
- fix: Edit Branch on threads makes them not show up in CS
|
||||
- The Events Calendar is now the new Calendar with some fun new features.
|
||||
All your existing Events Calendars will be migrated automatically.
|
||||
- rfe: multiple redirects on a page - which one?
|
||||
- Major change: password recovery is now based on profile fields rather than
|
||||
email account access
|
||||
*** PLEASE READ THE GOTCHAS ***
|
||||
- fix: Updated Snippets not being cleared from cache
|
||||
- fix: IE7 Asset Manager and Admin Console bug defeated!
|
||||
- fix: fixed a 508 compliance issue with login macro.
|
||||
- fix: testEnvironment.pl fails in windows (Rebecca Hunt)
|
||||
- rfe: add simpleReport option to testEnvironment.pl (Rebecca Hunt)
|
||||
- fix: Updated Hover Help on Possible Value and Default Value when creating new Profile Fields. This should clarify things.
|
||||
|
||||
|
||||
7.2.3
|
||||
- fix: minor bug with new template vars in Auth::createAccount
|
||||
- fix: How to get to List Pending Transactions screen?
|
||||
- fix: Users not authorized for any payment gateway get appropriate message
|
||||
|
||||
7.2.2
|
||||
- fix: Show Debugging option not working
|
||||
- fix: Workflow form control edit button won't work. removed.
|
||||
- fix: Bug in HttpProxy.pm
|
||||
- fix: Storage::Image copy does not create thumbnails
|
||||
- fix: Static export - redirect problems
|
||||
- fixed a bug in Session::ErrorHandler::canShowPerformanceIndicators. Moving
|
||||
to CIDR format in debugIp broke it. Added a new convenience method called
|
||||
canShowBasedOnIP, which refactored out the identical code to share
|
||||
between canShowDebug, canShowPerformanceIndicators and any other IP based
|
||||
check for privileges.
|
||||
- fix: RSS From Parent having no icon
|
||||
- fix: HttpProxy now handles styles appropriately.
|
||||
- fix: op=viewPurchaseHistory prices are now formatted correctly
|
||||
- fix: A minor bug in the default viewPurchaseHistory template
|
||||
- fix: Thread determination of "current" Post, and shortcuts to non-Thread Posts
|
||||
- fix: make handling of profile field possible values slightly more robust
|
||||
- RFE: non-required fields shown on user registration
|
||||
|
||||
7.2.1
|
||||
- Made a change to version tag commits to deal with unusually long commit
|
||||
times.
|
||||
- Fixed bugs the SyncProfileToLdap workflow activity where it would ignore the
|
||||
ldapAlias config setting and it crash (Martin Kamerbeek / Procolix)
|
||||
- fix: entry in error log of WebGUI
|
||||
- Fixed part of RSSCapable addition upgrade script in 7.2.0.
|
||||
- fix: MIME types broken from change to the way File assets were streamed
|
||||
- fix: New resizable textareas not obeying width/height parameters
|
||||
- fix: InOutBoard not allowing re-editing of new revisions
|
||||
- Added a fatal error should parsing of JSON config file fail
|
||||
- Fixed a bug with the admin mode subnet feature.
|
||||
- Fixed a problem with rich media ads not processing macros.
|
||||
- Fixed a flaw in the new commerce tax system that caused checkouts to fail.
|
||||
- fix: Bug in "Article with Files"
|
||||
- fix: SQLReport pagination retains op= parameter
|
||||
- fix: Invalid MIME type set for images
|
||||
- Fixed a problem with the adspace upgrade in 7.2.0
|
||||
- Fixed a problem with the survey upgrade in 7.2.0
|
||||
|
||||
|
||||
7.2.0
|
||||
- Added server side spellchecker (Martin Kamerbeek / Procolix)
|
||||
- Added configurable sales tax. (Tiffany Patterson / Elite Marketing)
|
||||
- change: made Text::Aspell optional, nullifying spellchecker if not present
|
||||
- change: made all LWP user agents use env_proxy
|
||||
- Help: If a Help Chapter only has 1 page, then in the TOC view it links
|
||||
right to the page instead of the Chapter.
|
||||
- fix: HTML::Template::Expr templates would not handle template variables
|
||||
with dots in them. Added a fix to the template plugin so that dots are
|
||||
translated to underscores automatically in submitted template variables.
|
||||
Templates will still need to be manually updated.
|
||||
- Help: Added pluggable docs for template plugins, and added a new tab
|
||||
to the Help that lists template parser docs.
|
||||
- Added accessors to Session/Http.pm to make testing easier.
|
||||
- Test: Added t/lib/WebGUI/PseudoRequest, which is a mostly functional
|
||||
Apache::Request object replacement. It doesn't do everything, but it
|
||||
does enough to test Session/Http.pm, except for cookies.
|
||||
- Added an option to the Syndicated Content Wobject that allows use of macros
|
||||
inside the RSS Url property.
|
||||
- semi-fix: WebGUI/Mail/Send.pm no longer has extraneous UTF-8 BOM
|
||||
- new: RSSCapable mixin for assets that can have RSS feeds, and RSSFromParent
|
||||
asset (automatic) that actually generates the feeds from them.
|
||||
- new: workflow activity and hooks for deleting exported files on trash,
|
||||
purge, and changeUrl
|
||||
- fix: editing posts loses changes in preview
|
||||
- change: Asset::getContainer no longer changes the session asset
|
||||
- fix: Survey numeric multiple choice options
|
||||
- fix: Matrix/can't remove picture from listing
|
||||
- fix: inability to create shortcuts to threads
|
||||
- fix: Style templates do not render metadata
|
||||
- fix: Survey duplication not working
|
||||
- fix: "Open link in new window" with WebGUI asset tree link in TinyMCE
|
||||
- fix: Admin Users submenu doesn't fill in uid
|
||||
- Added YUI javascript library to the core, so that we can begin converting
|
||||
to a standard javascript API.
|
||||
- fix: Resizable textarea no longer works in IE
|
||||
- fix: EMS Manage Events broken
|
||||
- fix: "orig_dependant" JavaScript error in PM quick task display
|
||||
- fix: Tasks now start at zero duration in the PM system
|
||||
- fix: RSS for collaboration systems now properly shows in the head rather than the body
|
||||
- fix: Gantt chart bars erroneously being shifted one day to the right
|
||||
- fix: Post titles containing periods result in urls containing periods
|
||||
- fix: Activity list expands outside of edit workflow screen
|
||||
- fix: Thread layout "flat" doesn't stick
|
||||
- fix: Rich Edit omitting rows drops subsequent rows
|
||||
- fix: Phishing Bug... take that spammers!
|
||||
- fix: Default PM Dashboard Template extra form element not implemented yet
|
||||
- refactor: move Dashboard, Folder, and HttpProxy getEditForm overrides into definition clauses
|
||||
- possible fix: Dates messed up on subscriptions
|
||||
- Template variables in the main Survey Template were out of date in the
|
||||
documentation.
|
||||
- fix: SQLReport no longer paginates or runs nested queries when downloading.
|
||||
- Made Stow's warning a debug message, which is what debug messages are for.
|
||||
- fix: WebGUI::Text::splitCsv no longer removes trailing empty fields
|
||||
- fix: Product add-to-group would always try to add a user to a group
|
||||
- Made many minor changes recommended by Perl::Critic.
|
||||
- fix: No Integers or Strings as Placeholder Parameters
|
||||
- Made many minor code efficiency changes.
|
||||
- fix: Two cookies and incorrect Last-Modified date in HTTP header
|
||||
- WebGUI::Text no longer spits out a billion warnings
|
||||
- fix: workaround for IE not handling ' in SyndicatedContent was not catching everything
|
||||
- fix: WebGUI::Operation::ProductManager added a tab with wrong name.
|
||||
- fix: WebGUI::Operation::Commerce www_selectPaymentGateway no longer forces
|
||||
user to choose gateway if they are only authorized to use one
|
||||
- WebGUI::Session::Scratch->delete now returns the value deleted for
|
||||
convenience, like Perl's built-in delete() function.
|
||||
- fix: Auth redirectOnLogin wouldn't work if login called from Operation::execute()
|
||||
- fix: WebGUI::Operation::Commerce->listTransactions now adds trailing 0's to
|
||||
prices/totals.
|
||||
- fix: Uncommitted Collaborations and adding threads
|
||||
- fix: template variable displayLastReply is in none of the CS help files
|
||||
- karma rfe: Faster rendering for editing interface
|
||||
- karma rfe: Limiting access to admin mode to set of ip's
|
||||
|
||||
|
||||
7.1.3
|
||||
- fix: SQLReport now returns error if can't find DatabaseLink
|
||||
- WebGUI::DatabaseLink->new now warns if can't find requested DatabaseLink
|
||||
- fix: Wrong template variable name in default Matrix View template
|
||||
- Tried to clean up some HttpProxy code. Still very ugly. (need rewrite?)
|
||||
- fix: HttpProxy would not put correct values for multiple query params with
|
||||
same name.
|
||||
- Fixed a bug in the template engein that caused CS notifcations not to send
|
||||
in certain circumstances.
|
||||
- fix: metadata (WebGUI Help). Removed mention of the RawHeadTags macro
|
||||
from the Metadata help.
|
||||
- fix: Config
|
||||
- fix: Article Shortcut Loses Style Information
|
||||
- fix: Pagination loses search criteria
|
||||
- WebGUI::Session::Stow now warns if set() is called when cache is disabled
|
||||
- Fixed a bug in the LDAP auth module where LDAP links could not connect to
|
||||
the LDAP server (Martin Kamerbeek / Procolix)
|
||||
- Fixed a bug where the Automatic LDAP Registration setting could not be set.
|
||||
(Martin Kamerbeek / Procolix)
|
||||
- Fixed a bug in the Poll where using graphs could result in errors. See
|
||||
gotcha.txt for details. (Martin Kamerbeek / Procolix)
|
||||
- fix: Group lookups via database link
|
||||
- fix: Error before logging into WebGUI site
|
||||
- fix: Unlock tag
|
||||
- Added some additional indicies for slightly better performance.
|
||||
- fix: PM resource search popup has no scrollbars
|
||||
- fix: Matrix listings create CS assets with wrong permissions
|
||||
- fix: HttpProxy not requiring Apache2::Upload correctly
|
||||
- Fixed a bug that could cause package imports to fail if they included
|
||||
updated revisions of existing assets. This fix may also prevent other
|
||||
revisionDate related errors, though none are known at this time.
|
||||
- fix: Error in Storage.pm
|
||||
- fix: Relative URL in viewRSS function of CS
|
||||
|
||||
7.1.2
|
||||
- Fixed a bug where logging in/out would cause a blank page display.
|
||||
- Fixed a bug that caused workflows to fail if collaboration systems and
|
||||
posts for that CS were in the same version tag at commit time.
|
||||
- fix: minor assetsToHide implementation bug in dashboard
|
||||
- fix: Version tags could not be create()d because no default values set.
|
||||
- fix: Commerce items were required to have a group.
|
||||
|
||||
7.1.1
|
||||
- fix: some issues with asset exports not handling URLs with dots correctly
|
||||
- fix: Search from root
|
||||
- fix: Survey: textarea answers are trunctated
|
||||
- fix: Snippet Security Fails
|
||||
- add: asset exporter making appropriate symlinks for extras, uploads, and root URL
|
||||
- change: asset exporter now uses one session per asset to avoid breaking state in between
|
||||
- fix: Lineage length is not checked (Martin Kamerbeek / Procolix)
|
||||
- fix: Cannot manage user accounts in 7.1.0
|
||||
- fix: New created users don't have password
|
||||
|
||||
7.1.0
|
||||
- fix: mysql and mysqldump were transposed in upgrade.pl --help
|
||||
- fix: adding Matrix listings committing the current version tag
|
||||
- fix: user searches in task resource additions in PM not displaying right without both last name and first name present
|
||||
- fix: task editor in PM not actually receiving start/end date information at first
|
||||
- fix: Error Displaying Multiple TimeTracking Wobjects (ekennedy)
|
||||
- refactoring of PM JavaScript stuff
|
||||
- fix: DHTML calendar bug & fix (maxscience)
|
||||
- fix: Missing translation in calendar (Klaus)
|
||||
- fixed a bug where the calendar would break if a language other than English has
|
||||
been selected (Martin Kamerbeek / Procolix)
|
||||
- fix: Events Calendar: error in "big" template (Martin Kamerbeek / Procolix)
|
||||
- fix: PM task editor not preserving duration
|
||||
- fix: PM project completion percentage updates not working right
|
||||
- fix: useEmptyStyle caused invalid template to be used
|
||||
- Added ability to download an SQLReport in either CSV or as a template.
|
||||
(Special thanks to the Alliance for a Media Literate America for funding
|
||||
this feature.)
|
||||
- Added ability for Products to add a user to a group when purchased.
|
||||
(Special thanks to the Alliance for a Media Literate America for funding
|
||||
this feature.)
|
||||
- Changed the ?op=editProduct form to a TabForm.
|
||||
- fixed a small error in WebGUI::Group documentation.
|
||||
- Added WebGUI::Text with some CSV functions.
|
||||
- Added Karma RFE: Thumbnail size can be enterred in CS
|
||||
- Added diskUsage.pl utility script to show space used by assets in a webgui
|
||||
site, similar to the unix du utility (Special thanks to Volvo for funding
|
||||
this feature).
|
||||
- Added option to WebGUI Auth module to require strong passwords. Admins can
|
||||
now require users to enter a specific combination of characters, etc.
|
||||
(Special thanks to Brunswick Bowling and Billiards for funding this feature.)
|
||||
- Added skeleton code for writing WebGUI utility scripts.
|
||||
- Added auto-registration via LDAP. This allows users to simply login and
|
||||
have a WebGUI account created if their credentials are validated by the
|
||||
directory. (Special thanks to Kemin Industries for funding this feature.)
|
||||
- Added a Sync Profile to LDAP workflow activity that will grab a single user
|
||||
profile from LDAP instead of all of them. (Special thanks to Kemin
|
||||
Industries for funding this feature.)
|
||||
- fix: Article.t copy collateral test false failure.
|
||||
|
||||
7.0.9
|
||||
- Removed the need for DateTime::Cron::Simple, which also added the ability
|
||||
to use ! < and > in schedules.
|
||||
- partial fix: invalid Message-ID headers in outgoing mail
|
||||
- fix: HttpProxy not doing file uploads correctly
|
||||
- fix: leftover discussion template variables in Default Article template
|
||||
- fix: Stock Data asset insufficiently robust handling erroneous data
|
||||
- refactor: move getEditForm data into definition for Collaboration asset
|
||||
- Fixed some bugs in the SQLForm. Also refactored parts of the SQLForm to
|
||||
reduce the number of database queries and lessen the amount of data being
|
||||
uploaded when images are put in the form. (Martin Kamerbeek / Procolix)
|
||||
- change: PM asset task editor now defaults start date to start of project
|
||||
- Rearranged the autotag name creation to be easier to read.
|
||||
- add: progressive (duration-tracked but untimed) tasks now possible in Project Manager
|
||||
- fix: Shortcut causes endless loop
|
||||
- fix: Template variable in Project Management System
|
||||
- fix: behavior of SyncProfilesToLdap workflow activity should be more correct now
|
||||
- add: multiple LDAP recursion filters possible
|
||||
|
||||
7.0.8
|
||||
- Fixed a couple of minor bugs with the default values of the Request
|
||||
Approval for Version Tag workflow activity.
|
||||
- Updated the hoverhelp to denote that you can use ranges in the WebGUI
|
||||
scheduler.
|
||||
- fix: deleting workflows did not delete related instances and crons
|
||||
- Added a "run" link to the scheduler and the running workflows listings to
|
||||
aid in debugging workflow errors.
|
||||
- fix: profile fields not validated by WebGUI::User
|
||||
- fix: Spectre pings not using correct IP address
|
||||
- fix: search functionality throwing fatal errors
|
||||
- fix: DBI connect errors infinitely recurse
|
||||
- add: setting cookieTTL to "session" now creates browser-session cookies
|
||||
- Added a reverse option for the getAssets method in VersionTag.
|
||||
- Fixed a bug that would occur when deploying a package that contained a
|
||||
collaboration system with posts.
|
||||
- structure: normalize signature of Asset::duplicate method
|
||||
- fix: Copying Collaboration System assets fails
|
||||
- fix: Collaboration System packages do not deploy
|
||||
- fix: robots.txt returns wrong MIME type
|
||||
- change: overlong alternate text for Weather Data icons shortened to basename
|
||||
- fix: multiple problems with static export, including wrong asset context and wrong status messages
|
||||
- fix: WebGUI::Asset->new interacting badly with caching
|
||||
- fix: changeUrlConfirm returns to previous URL rather than new URL
|
||||
- fix: performance indicators interfering with CSS
|
||||
- fix: admin bar causes pages to extend forever
|
||||
- fix: File Upload - documented HTTP file upload size limitations in File
|
||||
Pile Assets Hover help as well as the WebGUI settings documentation for Max
|
||||
Upload size.
|
||||
- Eliminated several hundred queries to the database during certain user
|
||||
profile field options.
|
||||
- Fixed the search function that broke in 7.0.7.
|
||||
- fix: typo + obsolete approve section in Collaboration System Default Thread template
|
||||
- fix: attachments section of post form not working correctly on edit
|
||||
- Images now create revisions as you resize them, so you can roll back to a
|
||||
previous size.
|
||||
|
||||
7.0.7
|
||||
- rfe: Image Management (funded by Formation Design Systems)
|
||||
- fix: can't change default size of text fields (midellaq)
|
||||
- fix: sqlform trunctate search results doesn't work (Martin Kamerbeek /
|
||||
Procolix)
|
||||
- fixed some of bugs in the sqlform concerning file uploads, cross table
|
||||
constraints and the join selector on non-key/value pair fields (Martin
|
||||
Kamerbeek / Procolix)
|
||||
- fix: Add event does not work WebGUI 7.0.5 in combination with Proxy Caching
|
||||
turned off (Wouter van Oijen / ProcoliX)
|
||||
- When going to an image by it's webgui url in admin mode, you are now shown the image instead of being taken to the edit screen for the image.
|
||||
- fixed a bug in the Layout Asset where the asset would not inherit the
|
||||
Layout template of its parent on addition (Martin Kamerbeek / Procolix)
|
||||
- fixed some issues with getting original values and template fields in the
|
||||
overrides section of the Shortcut asset (Martin Kamerbeek / Procolix)
|
||||
- fix: extra elements (tags) do not show up in HTML source (Martin Kamerbeek
|
||||
/ Procolix)
|
||||
- fix: Error in StockData Default View Template (Wouter van Oijen / Procolix)
|
||||
- fix: Matrix 'Can instantiate template' and also fixed a bug where the style
|
||||
and printable style were not set for the Collabs attached to the listings
|
||||
in the Matrix (Martin Kamerbeek / Procolix)
|
||||
- fix: Spectre::Admin Error Message (xhunter)
|
||||
- fix: invalid getUrl usage in EventManagementSystem
|
||||
- fix: assets incorrectly setting Last-Modified by revisionDate only
|
||||
- fix: SyndicatedContent caching the wrong thing and not displaying after first time
|
||||
- fix: Database cache trying to freeze non-references with Storable
|
||||
- fix: Apache version string component came before Apache's own version number
|
||||
- RFE: JavaScript confirmation rather than page load for deleteUser
|
||||
- RFE: JavaScript confirmation rather than page load for deleteGroup
|
||||
- RFE: show which user locked an asset in the asset manager
|
||||
- fix: dashlet user preference setting causing nested dashboard to appear
|
||||
- fix: saving edits to dashlet shortcuts kicks you out of your version tag
|
||||
- fix: Discussion tmpl variables in Article asset
|
||||
- fix: dashlet www_saveUserPrefs refusing to execute
|
||||
- API change: ProfileField::new now returns undef for invalid fields
|
||||
- API change: in ProfileField, the get*Fields family of methods are now class methods
|
||||
- API change: 'func' and 'op' are now reserved and not usable as profile fields
|
||||
- fix: project editing in project management systems not reading fields correctly
|
||||
- fix: JavaScript race condition in dashlet prefs form
|
||||
- fix: caching problem with overrides in dashlets
|
||||
- fix: CS pagination does not work for visitors
|
||||
- fixed a problem in the search indexer and made the tabform css compatible
|
||||
with tinymce. (Martin Kamerbeek / Procolix)
|
||||
- fixed WeatherData Wobject, noaa format had changed (ekennedy)
|
||||
- fix: Matrix (updated detailed listing template to include the screenshot)
|
||||
and fixed a bug in sbin/fileUpload.pl wher it didn't handle images with
|
||||
uppercased extensions properly (Martin Kamerbeek / Procolix)
|
||||
- new: In the Project Management asset, tasks can now have multiple resources, which may be users or groups. Original single-resource data is migrated to the new schema by the 7.0.7 upgrade script.
|
||||
- fix: makePrintable operation with other styleId
|
||||
- fix: RandomThread macro not working properly. Only CS's with more than one thread are considered for the random search
|
||||
- new: Tasks in the Project Management asset can now be assigned non-work lag time that is added to the main work duration of the task.
|
||||
- new: Projects in the Project Management asset can now be assigned observer groups; users who are not in the observer group cannot view any aspects of the project.
|
||||
|
||||
7.0.6
|
||||
- fix: Error in DateTime.pm
|
||||
- Added a cookieTTL parameter to the config file which lets you set an optional expiration time of the webgui session cookie
|
||||
- RFE: By default, search results need to match ALL keywords (Len Kranendonk / www.ilance.nl)
|
||||
- fix: page redirect problem
|
||||
- fix: adding in groupdelete macro
|
||||
- fix: semicolons missing
|
||||
- fix: Typo in WebGUI/Form/Date.pm
|
||||
- fix: snytax error in wobject skeleton
|
||||
- fix: potential problem with posts if getThread->parent is not defined
|
||||
- fix: macro_env semicolonmissing again!!!!
|
||||
- Made some changes to make WebGUI compatible with the WRE for Windows.
|
||||
- fix: cacheTimeout not respected as Visitor (Eric Kennedy).
|
||||
- fix: Email address with just one character in the user part not accepted
|
||||
- fix: Image (file) added to page shows before committing changes
|
||||
- fix: Typo in fileImport.pl at line 265 (zxp)
|
||||
- rfe: Workflow activity for assigning users to a group
|
||||
- The prevent proxy cache setting also now sets anti-caching meta tags and
|
||||
HTTP headers.
|
||||
- fix: getMedia asset constructor returning wrong object type
|
||||
|
||||
|
||||
7.0.5
|
||||
- Added a --skipDelete option to upgrade.pl
|
||||
- rfe: Approvers don't need to approve own changes
|
||||
- Added some more tests to the suite.
|
||||
- Fixed the test skeleton
|
||||
- Fixed some bugs regarding Search relevance sorting (Len Kranendonk / www.ilance.nl)
|
||||
- Added an option to override the session cookie name.
|
||||
- Added an option to override the session cookie domain.
|
||||
- fix: Search results not showing synopses
|
||||
- fix: Redirects get displayed inside page layouts as '0'
|
||||
- fix: Mysterious "0" Appearing When Admin Is Off
|
||||
- fix: Deletion of Products
|
||||
- fix: Request Tracker Thread is called Request Tracker Post
|
||||
- fix: asset constructor new, does not return undef as documented
|
||||
- fix: Static export in html not working through the workflow
|
||||
- fix: Fixed project management display
|
||||
|
||||
|
||||
7.0.4
|
||||
- Added a forum.lastPost.user.hasRead variable to the Message Board template.
|
||||
- fix: r_printable macro and op2
|
||||
- fixed a bug where the Include macro could be used to read WebGUI config
|
||||
files.
|
||||
- fix: new by webgui: 31 months in a year
|
||||
- Several new tests.
|
||||
- Many POD fixes.
|
||||
- fix: URI::Escape missing from testEnvironment.pl
|
||||
|
||||
|
||||
7.0.3
|
||||
- Fixed a problem with the 7.0.0-7.0.1 upgrade relating to internationalized
|
||||
department names.
|
||||
- fix: Missing documentation breaks the List of Available Macros (Wouter
|
||||
van Oijen / ProcoliX)
|
||||
- fix: Article thumbnail not working (Len Kranendonk)
|
||||
- Fixed a bug in WebGUI::Asset::Post where userDefined and synopsis form
|
||||
elements were not populated when previewed. (Martin Kamerbeek / Procolix)
|
||||
- fix: Indent Navigation broken (Wouter van Oijen / ProcoliX)
|
||||
- fix: HttpProxy not working (with fix) (Eric Kennedy)
|
||||
- fix: Copyright on Default Template (Wouter van Oijen / ProcoliX)
|
||||
- fix: FileUrl macro doesn't handle snippets (Wouter van Oijen / ProcoliX)
|
||||
- fix: Dataform adding fields without fieldname (Wouter van Oijen / ProcoliX)
|
||||
- fix: Fatal in Affiliate.pm
|
||||
- Fixed several problems to make WebGUI 7 Windows compatible again.
|
||||
- fix: navigation (Wouter van Oijen / ProcoliX)
|
||||
- Fixed typo in template variable project.gantt.rowspan and documentation
|
||||
- fix: Events Calendar Double Date (Wouter van Oijen / ProcoliX)
|
||||
- fix: Data Form Text Area Box Non-Existent (Wouter van Oijen / ProcoliX)
|
||||
- Added an error message to the FileUrl macro to help users figure out why it
|
||||
doesn't work.
|
||||
- Fixed bugs in the GroupAdd and GroupDelete macros.
|
||||
- Fixed a cross-Matrix linking problem when you have two or more Matricies on
|
||||
one site with the same category names.
|
||||
- Deleted a template that was accidentally added to the core.
|
||||
- Made some improvements to the mail subsystems.
|
||||
- fix: Revised WebGUI::HTML::filter "all" so that text does not run together when
|
||||
tags are removed. Added additional tests to HTML.t. (Eric Kennedy)
|
||||
- fix: Shopping Cart Not Working
|
||||
- fix: Editing Products Template wipes out SKU
|
||||
- fix: Email to RFE List Going to Spam
|
||||
- fix: 7.0.0-7.0.1 upgrade -- op called w/o passing session
|
||||
- fix: spectre.pl daemon error
|
||||
- Changed the Spectre tests to be a seperate option on the spectre.pl command
|
||||
line, which fixed a problem with the WRE monitor, and also enabled us to
|
||||
add more complete connectivity testing.
|
||||
- fix: Templates XHTML compliance (Wouter van Oijen / ProcoliX)
|
||||
- Fixed mail bounce processing.
|
||||
- fix: Asset Manager displaying incorrectly
|
||||
- fix: Cannot paste from clipboard
|
||||
- Made the search indexer mor compatible with Chinese and other non-ascii
|
||||
characters. (Thanks to Zhou Xiaopeng)
|
||||
- fix: Splat_random Macro not so random (Wouter van Oijen / ProcoliX) (Thanks
|
||||
to Colin Kuskie for pointing this out and writing some tests)
|
||||
- rfe: phone validation javascript
|
||||
- fix: Head Block in styles
|
||||
- fix: select assetVersionTag
|
||||
- fix: Infinite recursion
|
||||
- fix: assetUiLevel override broken
|
||||
- fix: Indexing files failes (derck)
|
||||
- fix: Unable to approve New listings on Matrix
|
||||
- Added the arrayRef() method to WebGUI::SQL::ResultSet, which is 12% faster
|
||||
than the array() method.
|
||||
- Added more tests to the test suite.
|
||||
- fix: Search Feature Select Box Not Working
|
||||
- Added "Save and Commit" option for environments where the appearance of
|
||||
workflow is unwanted.
|
||||
- fix: WebGUI::International::get can't handle spaces
|
||||
- fix: makePagePrintable macro uses style name instead of styleId
|
||||
- fix: Tell A Friend
|
||||
- Fixed a crash problem with Spectre run once cron jobs.
|
||||
- Fixed a formatting problem and a data collision problem with the Create
|
||||
Cron Job workflow activity.
|
||||
- fix: HTML tags in subject
|
||||
|
||||
|
||||
7.0.2
|
||||
- fix: upgrade from 6.99.4-6.99-5 can fail if site contains groups tied to ldap with no users in it.
|
||||
- GroupText macro returns an error message if it can't find the group by the name the user supplies.
|
||||
- fix: Unable to remove databaselinks (Thanks to misja)
|
||||
- fix: Collaboration System hangs under certain conditions (Martin Kamerbeek
|
||||
/ Procolix)
|
||||
- fix: Insert WebGUI Image inserts image, but does not retain border, spacing
|
||||
or alignment.(Martin Kamerbeek / Procolix)
|
||||
- Added Chinese character support to search engine and indexer thanks to Zhou
|
||||
Xiaopeng.
|
||||
- fix: issue with recursive ldap filter causing it not to work properly
|
||||
- fix: upgrade 7.0.0 to 7.0.1 ldap problem
|
||||
- fix: Typo when trying to display pvt profile
|
||||
- Added an unsubscribe link to the messages generated by collaboration
|
||||
subscriptions per the laws in various countries.
|
||||
- fix: MultiSearch
|
||||
- fix: Unable to duplicate existing Session Id
|
||||
- fix: Admins not in visitors group
|
||||
- fix: Data Form Text Area ignores size settings
|
||||
- Fixed a bug that didn't allow you to search a matrix.
|
||||
- Fixed a bug in the upgrade that caused template problems with the WebGUI 6
|
||||
template if anyone was still using that.
|
||||
- Fixed a bug where the template variables currentPage.hasViewableSiblings
|
||||
and currentPage.hasViewableChildren were always false. Added the
|
||||
page.parent.rank template variable to the Navigation template. (Martin
|
||||
Kamerbeek / Procolix)
|
||||
- Fixed a bug where WebGUI::Asset::File->addRevision did not set correct
|
||||
privs to the storage associated with it. (Martin Kamerbeek / Procolix)
|
||||
- Added a reverse page loop option to the navigation asset (Martin
|
||||
Kamerbeek / Procolix)
|
||||
- fix: cs mail needs archive url
|
||||
- fix: cs mail not sending in-reply-to and references headers
|
||||
- fix: cs mail doesn't like code via email
|
||||
- CS mail now sends out the email address of the poster as from, when it
|
||||
exists.
|
||||
- fix: WebGUI::Image missing methods
|
||||
- Added runOnLogin and runOnLogout config file properties to Authentication to allow
|
||||
for running an external script on successful login or logout.
|
||||
- fix: spectre
|
||||
- fix: Spectre tries to delete the same workflow instance twice
|
||||
- Fixed part of the Spectre memory leak. See gotcha.txt for details.
|
||||
|
||||
|
||||
7.0.1
|
||||
- fix: User profile field "Department" needs i18n
|
||||
- fix: AssetProxied Navigation context menu - items invisible in Style 02
|
||||
- fix: Request Tracker Asset - Reply to a post displays Severity drop down
|
||||
list
|
||||
- fix: Syndicated wobject erro 6.8+
|
||||
- fix: new spectre.pl error (Martin Kamerbeek / Procolix)
|
||||
- fix: Can't create new account
|
||||
- fix: Several new assets aren't added to config during upgrade process
|
||||
- fix: Post Subject HTML
|
||||
- fix: Matrix: can't instantiate template
|
||||
- fix: Session id (Martin Kamerbeek / Procolix)
|
||||
- fix: Style Wizard
|
||||
- fix: content-type
|
||||
- fix: Two cookies and incorrect Last-Modified date in HTTP header
|
||||
- fix: HTTP status code 404 broken
|
||||
- fix: Add missing page on Problem With Request
|
||||
- fix: Avatar/photo upload not working
|
||||
- fix: Shortcut with content lock fails (Thanks to Michelle Lamar)
|
||||
- fix: Security bug in session env
|
||||
- fix: Ldap Registration of new users (Thanks to guiuser)
|
||||
- fix: Missing/Incorrect POD
|
||||
- Made changes to spectre to handle finished workflows better.
|
||||
- Added filter to groups and ldap connections to filter out group members in cases where the ldap group propery and the recursive group poperty are the same
|
||||
|
||||
|
||||
7.0.0
|
||||
- Welcome to a whole new world of WebGUI. After 2.5 years and 20,000 hours of
|
||||
development, WebGUI 7 is finally here.
|
||||
- Fixed a bug in the asset manager where you could be redirected to a wrong
|
||||
page after using the delete, copy, cut, duplicate buttons.
|
||||
- fix: Can't set View Purchase History Template in commerce settings
|
||||
- fix: Template toolbar missing for Transaction Error Template
|
||||
- fix: Page fails and cannot be edited except through the db if custom rich editor deleted.
|
||||
- fix: Search returns not restricted to chosen path or asset type
|
||||
- fix: Product Asset - specification labels not showing
|
||||
- fix: Folders displayed for underprivileged users (wouter / Procolix)
|
||||
- fix: Secure the search function
|
||||
- fix: Export Functionality
|
||||
- fix: Search displays already deleted files
|
||||
- fix: Pagination not working in User Management System
|
||||
- fix: Upgrade 6.8.10 to 6.99.5 (Thanks to Erik Svanberg for the patch)
|
||||
- fix: Adding Survey Choices
|
||||
- fix: User/Group problem
|
||||
- fix: Edit LDAP Connection
|
||||
- fix: SQL Report w/ pagination and nested queries
|
||||
- fix: Unable to add Web Services Client
|
||||
- Fixed a bug in spectre where it wasn't using session cookies.
|
||||
- Fixed a bug in spectre where you couldn't shut it down if you started it on
|
||||
an IP other than 127.0.0.1.
|
||||
- Made the Include macro more secure.
|
||||
- Added Len's patch to fix some caching problems.
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,13 @@ save you many hours of grief.
|
|||
Account Macro template
|
||||
Admin Toggle Macro template
|
||||
|
||||
7.10.4
|
||||
--------------------------------------------------------------------
|
||||
* WebGUI now depends on Monkey::Patch for doing sanely scoped
|
||||
monkeypatches.
|
||||
|
||||
* WebGUI now depends on version 0.20 of Scope::Guard.
|
||||
|
||||
7.10.3
|
||||
--------------------------------------------------------------------
|
||||
* In the Collaboration System, previously the Group to Post group
|
||||
|
|
|
|||
|
|
@ -12,11 +12,24 @@ templates, you will need to apply these changes manually to your copies.
|
|||
toggle.url => toggle_url
|
||||
toggle.text => toggle_text
|
||||
|
||||
7.10.4
|
||||
|
||||
* DataForm email template - default_email
|
||||
Do HTML escaping on field values. Move table tags outside of the loops so only one table
|
||||
is generated.
|
||||
|
||||
* Template Variable template
|
||||
The URL for that template has been changed from "help" to "root/import/adminconsole/help", making
|
||||
the short URL Help available for WebGUI content use.
|
||||
|
||||
* EMS Badge Listing template - root/import/ems/ems-badge-listing-default
|
||||
Changed the javascript to use a custom formatter for the Badge price.
|
||||
|
||||
7.10.3
|
||||
|
||||
* Carousel Default Template - root/import/carousel/carousel-default
|
||||
Add a height parameter to the template.
|
||||
|
||||
|
||||
* survey.css
|
||||
Removed relative positioning and offset from top.
|
||||
|
||||
|
|
|
|||
BIN
docs/upgrades/packages-7.10.4/default_email.wgpkg
Normal file
BIN
docs/upgrades/packages-7.10.4/default_email.wgpkg
Normal file
Binary file not shown.
Binary file not shown.
190
docs/upgrades/upgrade_7.10.3-7.10.4.pl
Normal file
190
docs/upgrades/upgrade_7.10.3-7.10.4.pl
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
#!/usr/bin/env perl
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
#-------------------------------------------------------------------
|
||||
# Please read the legal notices (docs/legal.txt) and the license
|
||||
# (docs/license.txt) that came with this distribution before using
|
||||
# this software.
|
||||
#-------------------------------------------------------------------
|
||||
# http://www.plainblack.com info@plainblack.com
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
our ($webguiRoot);
|
||||
|
||||
BEGIN {
|
||||
$webguiRoot = "../..";
|
||||
unshift (@INC, $webguiRoot."/lib");
|
||||
}
|
||||
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
use WebGUI::Session;
|
||||
use WebGUI::Storage;
|
||||
use WebGUI::Asset;
|
||||
use List::Util qw(first);
|
||||
|
||||
my $toVersion = '7.10.4';
|
||||
my $quiet; # this line required
|
||||
|
||||
|
||||
my $session = start(); # this line required
|
||||
|
||||
# upgrade functions go here
|
||||
changeTemplateHelpUrl($session);
|
||||
addForkTable($session);
|
||||
installForkCleanup($session);
|
||||
|
||||
finish($session); # this line required
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Describe what our function does
|
||||
#sub exampleFunction {
|
||||
# my $session = shift;
|
||||
# print "\tWe're doing some stuff here that you should know about... " unless $quiet;
|
||||
# # and here's our code
|
||||
# print "DONE!\n" unless $quiet;
|
||||
#}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Describe what our function does
|
||||
sub changeTemplateHelpUrl {
|
||||
my $session = shift;
|
||||
print "\tChange the URL for the template that displays help variables... " unless $quiet;
|
||||
# and here's our code
|
||||
my $template = WebGUI::Asset->newByDynamicClass($session, 'PBtmplHelp000000000001');
|
||||
if ($template) {
|
||||
$template->update({url => 'root/import/adminconsole/help'});
|
||||
my $rs = $template->session->db->read("select revisionDate from assetData where assetId=? and revisionDate<>?",[$template->getId, $template->get("revisionDate")]);
|
||||
while (my ($version) = $rs->array) {
|
||||
my $old = WebGUI::Asset->new($session, $template->getId, $template->get("className"), $version);
|
||||
$old->purgeRevision if defined $old;
|
||||
}
|
||||
}
|
||||
else {
|
||||
print "\n\tNO TEMPLATE FOR DISPLAYING TEMPLATE VARIABLES...";
|
||||
}
|
||||
print "DONE!\n" unless $quiet;
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Creates a new table for tracking background processes
|
||||
sub addForkTable {
|
||||
my $session = shift;
|
||||
my $db = $session->db;
|
||||
my $sth = $db->dbh->table_info('', '', 'Fork', 'TABLE');
|
||||
return if ($sth->fetch);
|
||||
print "\tAdding Fork table..." unless $quiet;
|
||||
my $sql = q{
|
||||
CREATE TABLE Fork (
|
||||
id CHAR(22),
|
||||
userId CHAR(22),
|
||||
groupId CHAR(22),
|
||||
status LONGTEXT,
|
||||
error TEXT,
|
||||
startTime BIGINT(20),
|
||||
endTime BIGINT(20),
|
||||
finished BOOLEAN DEFAULT FALSE,
|
||||
latch BOOLEAN DEFAULT FALSE,
|
||||
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
};
|
||||
$db->write($sql);
|
||||
print "DONE!\n" unless $quiet;
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# install a workflow to clean up old background processes
|
||||
sub installForkCleanup {
|
||||
my $session = shift;
|
||||
print "\tInstalling Fork Cleanup workflow..." unless $quiet;
|
||||
my $class = 'WebGUI::Workflow::Activity::RemoveOldForks';
|
||||
$session->config->addToArray('workflowActivities/None', $class);
|
||||
my $wf = WebGUI::Workflow->new($session, 'pbworkflow000000000001');
|
||||
my $a = first { ref $_ eq $class } @{ $wf->getActivities };
|
||||
unless ($a) {
|
||||
$a = $wf->addActivity($class);
|
||||
$a->set(title => 'Remove Old Forks');
|
||||
};
|
||||
print "DONE!\n" unless $quiet;
|
||||
}
|
||||
|
||||
|
||||
# -------------- DO NOT EDIT BELOW THIS LINE --------------------------------
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Add a package to the import node
|
||||
sub addPackage {
|
||||
my $session = shift;
|
||||
my $file = shift;
|
||||
|
||||
print "\tUpgrading package $file\n" unless $quiet;
|
||||
# Make a storage location for the package
|
||||
my $storage = WebGUI::Storage->createTemp( $session );
|
||||
$storage->addFileFromFilesystem( $file );
|
||||
|
||||
# Import the package into the import node
|
||||
my $package = eval {
|
||||
my $node = WebGUI::Asset->getImportNode($session);
|
||||
$node->importPackage( $storage, {
|
||||
overwriteLatest => 1,
|
||||
clearPackageFlag => 1,
|
||||
setDefaultTemplate => 1,
|
||||
} );
|
||||
};
|
||||
|
||||
if ($package eq 'corrupt') {
|
||||
die "Corrupt package found in $file. Stopping upgrade.\n";
|
||||
}
|
||||
if ($@ || !defined $package) {
|
||||
die "Error during package import on $file: $@\nStopping upgrade\n.";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#-------------------------------------------------
|
||||
sub start {
|
||||
my $configFile;
|
||||
$|=1; #disable output buffering
|
||||
GetOptions(
|
||||
'configFile=s'=>\$configFile,
|
||||
'quiet'=>\$quiet
|
||||
);
|
||||
my $session = WebGUI::Session->open($webguiRoot,$configFile);
|
||||
$session->user({userId=>3});
|
||||
my $versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
$versionTag->set({name=>"Upgrade to ".$toVersion});
|
||||
return $session;
|
||||
}
|
||||
|
||||
#-------------------------------------------------
|
||||
sub finish {
|
||||
my $session = shift;
|
||||
updateTemplates($session);
|
||||
my $versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
$versionTag->commit;
|
||||
$session->db->write("insert into webguiVersion values (".$session->db->quote($toVersion).",'upgrade',".time().")");
|
||||
$session->close();
|
||||
}
|
||||
|
||||
#-------------------------------------------------
|
||||
sub updateTemplates {
|
||||
my $session = shift;
|
||||
return undef unless (-d "packages-".$toVersion);
|
||||
print "\tUpdating packages.\n" unless ($quiet);
|
||||
opendir(DIR,"packages-".$toVersion);
|
||||
my @files = readdir(DIR);
|
||||
closedir(DIR);
|
||||
my $newFolder = undef;
|
||||
foreach my $file (@files) {
|
||||
next unless ($file =~ /\.wgpkg$/);
|
||||
# Fix the filename to include a path
|
||||
$file = "packages-" . $toVersion . "/" . $file;
|
||||
addPackage( $session, $file );
|
||||
}
|
||||
}
|
||||
|
||||
#vim:ft=perl
|
||||
123
docs/upgrades/upgrade_7.10.4-7.10.5.pl
Normal file
123
docs/upgrades/upgrade_7.10.4-7.10.5.pl
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
#!/usr/bin/env perl
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
#-------------------------------------------------------------------
|
||||
# Please read the legal notices (docs/legal.txt) and the license
|
||||
# (docs/license.txt) that came with this distribution before using
|
||||
# this software.
|
||||
#-------------------------------------------------------------------
|
||||
# http://www.plainblack.com info@plainblack.com
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
our ($webguiRoot);
|
||||
|
||||
BEGIN {
|
||||
$webguiRoot = "../..";
|
||||
unshift (@INC, $webguiRoot."/lib");
|
||||
}
|
||||
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
use WebGUI::Session;
|
||||
use WebGUI::Storage;
|
||||
use WebGUI::Asset;
|
||||
|
||||
|
||||
my $toVersion = '7.10.5';
|
||||
my $quiet; # this line required
|
||||
|
||||
|
||||
my $session = start(); # this line required
|
||||
|
||||
# upgrade functions go here
|
||||
|
||||
finish($session); # this line required
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Describe what our function does
|
||||
#sub exampleFunction {
|
||||
# my $session = shift;
|
||||
# print "\tWe're doing some stuff here that you should know about... " unless $quiet;
|
||||
# # and here's our code
|
||||
# print "DONE!\n" unless $quiet;
|
||||
#}
|
||||
|
||||
|
||||
# -------------- DO NOT EDIT BELOW THIS LINE --------------------------------
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Add a package to the import node
|
||||
sub addPackage {
|
||||
my $session = shift;
|
||||
my $file = shift;
|
||||
|
||||
print "\tUpgrading package $file\n" unless $quiet;
|
||||
# Make a storage location for the package
|
||||
my $storage = WebGUI::Storage->createTemp( $session );
|
||||
$storage->addFileFromFilesystem( $file );
|
||||
|
||||
# Import the package into the import node
|
||||
my $package = eval {
|
||||
my $node = WebGUI::Asset->getImportNode($session);
|
||||
$node->importPackage( $storage, {
|
||||
overwriteLatest => 1,
|
||||
clearPackageFlag => 1,
|
||||
setDefaultTemplate => 1,
|
||||
} );
|
||||
};
|
||||
|
||||
if ($package eq 'corrupt') {
|
||||
die "Corrupt package found in $file. Stopping upgrade.\n";
|
||||
}
|
||||
if ($@ || !defined $package) {
|
||||
die "Error during package import on $file: $@\nStopping upgrade\n.";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#-------------------------------------------------
|
||||
sub start {
|
||||
my $configFile;
|
||||
$|=1; #disable output buffering
|
||||
GetOptions(
|
||||
'configFile=s'=>\$configFile,
|
||||
'quiet'=>\$quiet
|
||||
);
|
||||
my $session = WebGUI::Session->open($webguiRoot,$configFile);
|
||||
$session->user({userId=>3});
|
||||
my $versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
$versionTag->set({name=>"Upgrade to ".$toVersion});
|
||||
return $session;
|
||||
}
|
||||
|
||||
#-------------------------------------------------
|
||||
sub finish {
|
||||
my $session = shift;
|
||||
updateTemplates($session);
|
||||
my $versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
$versionTag->commit;
|
||||
$session->db->write("insert into webguiVersion values (".$session->db->quote($toVersion).",'upgrade',".time().")");
|
||||
$session->close();
|
||||
}
|
||||
|
||||
#-------------------------------------------------
|
||||
sub updateTemplates {
|
||||
my $session = shift;
|
||||
return undef unless (-d "packages-".$toVersion);
|
||||
print "\tUpdating packages.\n" unless ($quiet);
|
||||
opendir(DIR,"packages-".$toVersion);
|
||||
my @files = readdir(DIR);
|
||||
closedir(DIR);
|
||||
my $newFolder = undef;
|
||||
foreach my $file (@files) {
|
||||
next unless ($file =~ /\.wgpkg$/);
|
||||
# Fix the filename to include a path
|
||||
$file = "packages-" . $toVersion . "/" . $file;
|
||||
addPackage( $session, $file );
|
||||
}
|
||||
}
|
||||
|
||||
#vim:ft=perl
|
||||
|
|
@ -391,6 +391,9 @@ use WebGUI::HTML;
|
|||
use WebGUI::HTMLForm;
|
||||
use WebGUI::Keyword;
|
||||
require WebGUI::ProgressBar;
|
||||
use WebGUI::ProgressTree;
|
||||
use Monkey::Patch;
|
||||
use WebGUI::Fork;
|
||||
use WebGUI::Search::Index;
|
||||
use WebGUI::TabForm;
|
||||
use WebGUI::PassiveAnalytics::Logging;
|
||||
|
|
@ -836,6 +839,59 @@ sub fixUrl {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 forkWithStatusPage ($args)
|
||||
|
||||
Kicks off a WebGUI::Fork running $method with $args (from the args hashref)
|
||||
and redirects to a ProgressTree status page to show the progress. The
|
||||
following arguments are required in $args:
|
||||
|
||||
=head3 method
|
||||
|
||||
The name of the WebGUI::Asset method to call
|
||||
|
||||
=head3 args
|
||||
|
||||
The arguments to pass that method (see WebGUI::Fork)
|
||||
|
||||
=head3 plugin
|
||||
|
||||
The WebGUI::Operation::Fork plugin to render (e.g. ProgressTree)
|
||||
|
||||
=head3 title
|
||||
|
||||
An key in Asset's i18n hash for the title of the rendered console page
|
||||
|
||||
=head3 redirect
|
||||
|
||||
The full url to redirect to after the fork has finished.
|
||||
|
||||
=cut
|
||||
|
||||
sub forkWithStatusPage {
|
||||
my ( $self, $args ) = @_;
|
||||
my $session = $self->session;
|
||||
|
||||
my $process = WebGUI::Fork->start( $session, 'WebGUI::Asset', $args->{method}, $args->{args} );
|
||||
|
||||
if ( my $groupId = $args->{groupId} ) {
|
||||
$process->setGroup($groupId);
|
||||
}
|
||||
|
||||
my $method = $session->form->get('proceed') || 'manageTrash';
|
||||
my $i18n = WebGUI::International->new( $session, 'Asset' );
|
||||
my $pairs = $process->contentPairs(
|
||||
$args->{plugin}, {
|
||||
title => $i18n->get( $args->{title} ),
|
||||
icon => 'assets',
|
||||
proceed => $args->{redirect} || '',
|
||||
}
|
||||
);
|
||||
$session->http->setRedirect( $self->getUrl($pairs) );
|
||||
return 'redirect';
|
||||
} ## end sub forkWithStatusPage
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getClassById ( $session, $assetId )
|
||||
|
||||
Class method that looks up a className for an object in the database, using it's assetId.
|
||||
|
|
@ -2412,6 +2468,35 @@ sub setSize {
|
|||
$self->assetSize($size);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 setState ( $state )
|
||||
|
||||
Updates the asset table with the new state of the asset.
|
||||
|
||||
=cut
|
||||
|
||||
sub setState {
|
||||
my ($self, $state) = @_;
|
||||
my $sql = q{
|
||||
UPDATE asset
|
||||
SET state = ?,
|
||||
stateChangedBy = ?,
|
||||
stateChanged = ?
|
||||
WHERE assetId = ?
|
||||
};
|
||||
my @props = ($state, $self->session->user->userId, time);
|
||||
$self->session->db->write(
|
||||
$sql, [
|
||||
@props,
|
||||
$self->getId,
|
||||
]
|
||||
);
|
||||
$self->state($state);
|
||||
$self->stateChangedBy($props[1]);
|
||||
$self->stateChanged($props[2]);
|
||||
$self->purgeCache;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
@ -2477,23 +2562,7 @@ sub write {
|
|||
|
||||
Returns the asset's url without any site specific prefixes. If you want a browser friendly url see the getUrl() method.
|
||||
|
||||
# set the property
|
||||
if ($propertyDefinition->{serialize}) {
|
||||
# Only serialize references
|
||||
if ( ref $value ) {
|
||||
$setPairs{$property} = JSON->new->canonical->encode($value);
|
||||
}
|
||||
# Passing already serialized JSON string
|
||||
elsif ( $value ) {
|
||||
$setPairs{$property} = $value;
|
||||
$value = JSON->new->decode( $value ); # for setting in _properties, below
|
||||
}
|
||||
}
|
||||
else {
|
||||
$setPairs{$property} = $value;
|
||||
}
|
||||
$self->{_properties}{$property} = $value;
|
||||
}
|
||||
=head3 value
|
||||
|
||||
The new value to set the URL to.
|
||||
|
||||
|
|
|
|||
|
|
@ -400,6 +400,22 @@ sub canEdit {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 duplicate ( )
|
||||
|
||||
Extend the super class to duplicate the storage location.
|
||||
|
||||
=cut
|
||||
|
||||
sub duplicate {
|
||||
my $self = shift;
|
||||
my $newAsset = $self->SUPER::duplicate(@_);
|
||||
my $newStorage = $self->getStorageLocation->copy;
|
||||
$newAsset->update({storageId=>$newStorage->getId});
|
||||
return $newAsset;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 generateRecurrence (date)
|
||||
|
||||
Creates an recurrence event in the parent calendar for the given date
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ sub getContentLastModified {
|
|||
my $shortcut = $self->getShortcut; # XXX "newById must get an assetId"
|
||||
my $shortcuttedRev;
|
||||
if (defined $shortcut) {
|
||||
$shortcuttedRev = $shortcut->get('revisionDate');
|
||||
$shortcuttedRev = $shortcut->getContentLastModified;
|
||||
return $assetRev > $shortcuttedRev ? $assetRev : $shortcuttedRev;
|
||||
} else {
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -762,7 +762,7 @@ ENDHTML
|
|||
. $i18n->get( "warning default template" )
|
||||
. q{</p><p>}
|
||||
. sprintf( q{<a href="} . $duplicateUrl . q{">%s</a>}, $i18n->get( "make duplicate label" ) )
|
||||
. q{</p></div}
|
||||
. q{</p></div>}
|
||||
;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ property mailAccount => (
|
|||
tab => 'mail',
|
||||
label => [ "mail account", 'Asset_Collaboration' ],
|
||||
hoverHelp => [ "mail account help", 'Asset_Collaboration' ],
|
||||
extras => 'autocomplete="off"',
|
||||
);
|
||||
property mailPassword => (
|
||||
fieldType => "password",
|
||||
|
|
|
|||
|
|
@ -107,6 +107,24 @@ sub _fetchDepartments {
|
|||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getStatusList
|
||||
|
||||
Returns the statusList property as an array
|
||||
|
||||
=cut
|
||||
|
||||
sub getStatusList {
|
||||
my $self = shift;
|
||||
my $text = $self->get('statusList');
|
||||
|
||||
return
|
||||
grep { $_ } # no empty lines
|
||||
map { s/^\s+//; s/\s+$//; $_ } # trim
|
||||
split(/\r\n|\r|\n/, $text); # seperated by any kind of newline
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 prepareView ( )
|
||||
|
|
@ -174,16 +192,9 @@ sub view {
|
|||
}
|
||||
|
||||
my $statusUserId = $self->session->scratch->get("userId") || $self->session->user->userId;
|
||||
my $statusListString = $self->statusList;
|
||||
my @statusListArray = split("\n",$statusListString);
|
||||
my $statusListHashRef;
|
||||
tie %$statusListHashRef, 'Tie::IxHash';
|
||||
|
||||
foreach my $status (@statusListArray) {
|
||||
chomp($status);
|
||||
next if $status eq "";
|
||||
$statusListHashRef->{$status} = $status;
|
||||
}
|
||||
tie my %statusOptions, 'Tie::IxHash', (
|
||||
map { $_ => $_ } $self->getStatusList
|
||||
);
|
||||
|
||||
#$self->session->log->warn("VIEW: userId: ".$statusUserId."\n" );
|
||||
my ($status) = $session->db->quickArray(
|
||||
|
|
@ -222,7 +233,7 @@ sub view {
|
|||
$f->radioList(
|
||||
-name=>"status",
|
||||
-value=>$status,
|
||||
-options=>$statusListHashRef,
|
||||
-options=>\%statusOptions,
|
||||
-label=>$i18n->get(5),
|
||||
-hoverHelp=>$i18n->get('5 description'),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -298,9 +298,10 @@ sub getFolder {
|
|||
my ($self, $date) = @_;
|
||||
my $session = $self->session;
|
||||
my $folderName = $session->datetime->epochToHuman($date, DATE_FORMAT);
|
||||
my $folderUrl = join '/', $self->getUrl, $folderName;
|
||||
my $folderUrl = $self->getFolderUrl($folderName);
|
||||
my $folder = eval { WebGUI::Asset->newByUrl($session, $folderUrl); };
|
||||
return $folder if !Exception::Class->caught();
|
||||
|
||||
##The requested folder doesn't exist. Make it and autocommit it.
|
||||
|
||||
##For a fully automatic commit, save the current tag, create a new one
|
||||
|
|
@ -335,6 +336,26 @@ sub getFolder {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getFolderUrl ( name )
|
||||
|
||||
Constructs a url for a subfolder with the given name.
|
||||
|
||||
=cut
|
||||
|
||||
sub getFolderUrl {
|
||||
my ($self, $name) = @_;
|
||||
my $session = $self->session;
|
||||
my $base = $self->getUrl;
|
||||
$base =~ s/(.*)\..*/$1/;
|
||||
my $url = "$base/$name";
|
||||
if (my $ext = $session->setting->get('urlExtension')) {
|
||||
$url .= ".$ext";
|
||||
}
|
||||
return $session->url->urlize($url);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getKeywordFilename ( $keyword )
|
||||
|
||||
Returns the name for the file containing stories that match this keyword. Used
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ sub _defaultThingId_options {
|
|||
return $things;
|
||||
}
|
||||
|
||||
use WebGUI::ProgressBar;
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
@ -2628,6 +2630,11 @@ sub www_export {
|
|||
my $thingProperties = $self->getThing($thingId);
|
||||
return $session->privilege->insufficient() unless $self->hasPrivileges($thingProperties->{groupIdExport});
|
||||
|
||||
my $i18n = WebGUI::International->new($session, 'Asset_Thingy');
|
||||
my $pb = WebGUI::ProgressBar->new($session);
|
||||
$pb->start($i18n->get('export label').' '.$thingProperties->{label}, $session->url->extras('assets/thingy.gif'));
|
||||
$pb->update($i18n->get('Creating column headers'));
|
||||
my $tempStorage = WebGUI::Storage->createTemp($session);
|
||||
$fields = $session->db->read('select * from Thingy_fields where assetId =? and thingId = ? order by sequenceNumber',
|
||||
[$self->getId,$thingId]);
|
||||
while (my $field = $fields->hashRef) {
|
||||
|
|
@ -2649,9 +2656,13 @@ sub www_export {
|
|||
|
||||
### Loop through the returned structure and put it through Text::CSV
|
||||
# Column heads
|
||||
$out = WebGUI::Text::joinCSV(@fieldLabels);
|
||||
my $csv_filename = 'export_'.$thingProperties->{label}.'.csv';
|
||||
$tempStorage->addFileFromScalar($csv_filename, WebGUI::Text::joinCSV(@fieldLabels));
|
||||
open my $CSV, '>', $tempStorage->getPath($csv_filename);
|
||||
|
||||
# Data lines
|
||||
$pb->update($i18n->get('Writing data'));
|
||||
my $rowCounter = 0;
|
||||
while (my $data = $sth->hashRef) {
|
||||
my @fieldValues;
|
||||
foreach my $field (@fields){
|
||||
|
|
@ -2660,19 +2671,20 @@ sub www_export {
|
|||
my $value = $self->getFieldValue($data->{"field_".$fieldId},$field->{properties},"%y-%m-%d","%y-%m-%d %j:%n:%s");
|
||||
push(@fieldValues, $value);
|
||||
}
|
||||
foreach my $metaDataField (@metaDataFields){
|
||||
push(@fieldValues,$data->{$metaDataField});
|
||||
if ($thingProperties->{exportMetaData}) {
|
||||
foreach my $metaDataField (@metaDataFields){
|
||||
push(@fieldValues,$data->{$metaDataField});
|
||||
}
|
||||
}
|
||||
$out .= "\n".WebGUI::Text::joinCSV(
|
||||
@fieldValues
|
||||
);
|
||||
print $CSV "\n".WebGUI::Text::joinCSV( @fieldValues );
|
||||
#if (! ++$rowCounter % 25) {
|
||||
$pb->update($i18n->get('Writing data'));
|
||||
#}
|
||||
}
|
||||
|
||||
$fileName = "export_".$thingProperties->{label}.".csv";
|
||||
$self->session->http->setFilename($fileName,"application/octet-stream");
|
||||
$self->session->http->sendHeader;
|
||||
return $out;
|
||||
close $CSV;
|
||||
|
||||
$pb->update(sprintf q|<a href="%s">%s</a>|, $self->getUrl, sprintf($i18n->get('Return to %s'), $thingProperties->{label}));
|
||||
return $pb->finish($tempStorage->getUrl($csv_filename));
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -46,13 +46,26 @@ Duplicates this asset and the entire subtree below it. Returns the root of the
|
|||
|
||||
If true, then only children, and not descendants, will be duplicated.
|
||||
|
||||
=head3 $state
|
||||
|
||||
Set this to "clipboard" if you want the resulting asset to be on the clipboard
|
||||
(rather than published) when we're done.
|
||||
|
||||
=cut
|
||||
|
||||
sub duplicateBranch {
|
||||
my $self = shift;
|
||||
my $childrenOnly = shift;
|
||||
my ($self, $childrenOnly, $state) = @_;
|
||||
my $session = $self->session;
|
||||
my $log = $session->log;
|
||||
my $clipboard = $state && $state =~ /^clipboard/;
|
||||
|
||||
my $newAsset = $self->duplicate(
|
||||
{ skipAutoCommitWorkflows => 1,
|
||||
skipNotification => 1,
|
||||
state => $state,
|
||||
}
|
||||
);
|
||||
|
||||
my $newAsset = $self->duplicate({skipAutoCommitWorkflows=>1,skipNotification=>1});
|
||||
# Correctly handle positions for Layout assets
|
||||
my $contentPositions = $self->get("contentPositions");
|
||||
my $assetsToHide = $self->get("assetsToHide");
|
||||
|
|
@ -66,7 +79,21 @@ sub duplicateBranch {
|
|||
next;
|
||||
}
|
||||
last unless $child;
|
||||
my $newChild = $childrenOnly ? $child->duplicate({skipAutoCommitWorkflows=>1, skipNotification=>1}) : $child->duplicateBranch;
|
||||
my $newChild;
|
||||
if ($childrenOnly) {
|
||||
$newChild = $child->duplicate(
|
||||
{ skipAutoCommitWorkflows => 1,
|
||||
skipNotification => 1,
|
||||
state => $clipboard && 'clipboard-limbo',
|
||||
}
|
||||
);
|
||||
}
|
||||
elsif($clipboard) {
|
||||
$newChild = $child->duplicateBranch(0, 'clipboard-limbo');
|
||||
}
|
||||
else {
|
||||
$newChild = $child->duplicateBranch;
|
||||
}
|
||||
$newChild->setParent($newAsset);
|
||||
my ($oldChildId, $newChildId) = ($child->getId, $newChild->getId);
|
||||
$contentPositions =~ s/\Q${oldChildId}\E/${newChildId}/g if ($contentPositions);
|
||||
|
|
|
|||
|
|
@ -50,6 +50,58 @@ sub canPaste {
|
|||
return $self->validParent($self->session); ##Lazy call to a class method
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 copyInFork ( $process, $args )
|
||||
|
||||
WebGUI::Fork method called by www_copy
|
||||
|
||||
=cut
|
||||
|
||||
sub copyInFork {
|
||||
my ($process, $args) = @_;
|
||||
my $session = $process->session;
|
||||
my $asset = WebGUI::Asset->new($session, $args->{assetId});
|
||||
my @pedigree = ('self');
|
||||
my $childrenOnly = 0;
|
||||
if ($args->{childrenOnly}) {
|
||||
$childrenOnly = 1;
|
||||
push @pedigree, 'children';
|
||||
}
|
||||
else {
|
||||
push @pedigree, 'descendants';
|
||||
}
|
||||
my $ids = $asset->getLineage(\@pedigree);
|
||||
my $tree = WebGUI::ProgressTree->new($session, $ids);
|
||||
my $patch = Monkey::Patch::patch_class(
|
||||
'WebGUI::Asset', 'duplicate', sub {
|
||||
my $duplicate = shift;
|
||||
my $self = shift;
|
||||
my $id = $self->getId;
|
||||
$tree->focus($id);
|
||||
my $asset = eval { $self->$duplicate(@_) };
|
||||
my $e = $@;
|
||||
if ($e) {
|
||||
$tree->note($id, $e);
|
||||
$tree->failure($id, 'Died');
|
||||
}
|
||||
else {
|
||||
$tree->success($id);
|
||||
}
|
||||
$process->update(sub { $tree->json });
|
||||
die $e if $e;
|
||||
return $asset;
|
||||
}
|
||||
);
|
||||
my $newAsset = $asset->duplicateBranch($childrenOnly, 'clipboard');
|
||||
$newAsset->update({ title => $newAsset->getTitle . ' (copy)'});
|
||||
if ($args->{commit}) {
|
||||
my $tag = WebGUI::VersionTag->getWorking($session);
|
||||
$tag->requestCommit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 cut ( )
|
||||
|
|
@ -98,6 +150,10 @@ A hash reference of options that can modify how this method works.
|
|||
|
||||
Assets that normally autocommit their workflows (like CS Posts, and Wiki Pages) won't if this is true.
|
||||
|
||||
=head4 state
|
||||
|
||||
A state for the duplicated asset (defaults to 'published')
|
||||
|
||||
=cut
|
||||
|
||||
sub duplicate {
|
||||
|
|
@ -134,6 +190,10 @@ sub duplicate {
|
|||
keywords => $keywords,
|
||||
} );
|
||||
|
||||
if (my $state = $options->{state}) {
|
||||
$newAsset->setState($state);
|
||||
}
|
||||
|
||||
return $newAsset;
|
||||
}
|
||||
|
||||
|
|
@ -224,7 +284,11 @@ sub paste {
|
|||
|
||||
# Update lineage in search index.
|
||||
$self->purgeCache;
|
||||
my $assetIter = $pastedAsset->getLineageIterator(['self', 'descendants']);
|
||||
my $assetIter = $pastedAsset->getLineageIterator(
|
||||
['self', 'descendants'], {
|
||||
statesToInclude => ['clipboard','clipboard-limbo']
|
||||
}
|
||||
);
|
||||
while ( 1 ) {
|
||||
my $asset;
|
||||
eval { $asset = $assetIter->() };
|
||||
|
|
@ -235,15 +299,64 @@ sub paste {
|
|||
last unless $asset;
|
||||
|
||||
$outputSub->(sprintf $i18n->get('indexing %s'), $pastedAsset->getTitle) if defined $outputSub;
|
||||
$asset->setState('published');
|
||||
$asset->indexContent();
|
||||
}
|
||||
|
||||
$pastedAsset->updateHistory("pasted to parent ".$self->getId);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 pasteInFork ( )
|
||||
|
||||
WebGUI::Fork method called by www_pasteList
|
||||
|
||||
=cut
|
||||
|
||||
sub pasteInFork {
|
||||
my ( $process, $args ) = @_;
|
||||
my $session = $process->session;
|
||||
my $self = WebGUI::Asset->new( $session, $args->{assetId} );
|
||||
my @roots = grep { $_ && $_->canEdit }
|
||||
map { WebGUI::Asset->newPending( $session, $_ ) } @{ $args->{list} };
|
||||
|
||||
my @ids = map {
|
||||
my $list
|
||||
= $_->getLineage( [ 'self', 'descendants' ], { statesToInclude => [ 'clipboard', 'clipboard-limbo' ] } );
|
||||
@$list;
|
||||
} @roots;
|
||||
|
||||
my $tree = WebGUI::ProgressTree->new( $session, \@ids );
|
||||
my $patch = Monkey::Patch::patch_class(
|
||||
'WebGUI::Asset',
|
||||
'indexContent',
|
||||
sub {
|
||||
my $indexContent = shift;
|
||||
my $self = shift;
|
||||
my $id = $self->getId;
|
||||
$tree->focus($id);
|
||||
my $ret = eval { $self->$indexContent(@_) };
|
||||
my $e = $@;
|
||||
if ($e) {
|
||||
$tree->note( $id, $e );
|
||||
$tree->failure( $id, 'Died' );
|
||||
}
|
||||
else {
|
||||
$tree->success($id);
|
||||
}
|
||||
$process->update( sub { $tree->json } );
|
||||
die $e if $e;
|
||||
return $ret;
|
||||
}
|
||||
);
|
||||
$self->paste( $_->getId ) for @roots;
|
||||
} ## end sub pasteInFork
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_copy ( )
|
||||
|
|
@ -257,89 +370,49 @@ If with children/descendants is selected, a progress bar will be rendered.
|
|||
sub www_copy {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $http = $session->http;
|
||||
my $redir = $self->getParent->getUrl;
|
||||
return $session->privilege->insufficient unless $self->canEdit;
|
||||
|
||||
my $with = $session->form->get('with');
|
||||
my %args;
|
||||
if ($with eq 'children') {
|
||||
$self->_wwwCopyChildren;
|
||||
$args{childrenOnly} = 1;
|
||||
}
|
||||
elsif ($with eq 'descendants') {
|
||||
$self->_wwwCopyDescendants;
|
||||
elsif ($with ne 'descendants') {
|
||||
my $newAsset = $self->duplicate({
|
||||
skipAutoCommitWorkflows => 1,
|
||||
state => 'clipboard'
|
||||
}
|
||||
);
|
||||
$newAsset->update({ title => $newAsset->getTitle . ' (copy)'});
|
||||
my $result = WebGUI::VersionTag->autoCommitWorkingIfEnabled(
|
||||
$session, {
|
||||
allowComments => 1,
|
||||
returnUrl => $redir,
|
||||
}
|
||||
);
|
||||
$http->setRedirect($redir) unless $result eq 'redirect';
|
||||
return 'redirect';
|
||||
}
|
||||
else {
|
||||
$self->_wwwCopySingle;
|
||||
|
||||
my $tag = WebGUI::VersionTag->getWorking($session);
|
||||
if ($tag->canAutoCommit) {
|
||||
$args{commit} = 1;
|
||||
unless ($session->setting->get('skipCommitComments')) {
|
||||
$redir = $tag->autoCommitUrl($redir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub _wwwCopyChildren { shift->_wwwCopyProgress(1) }
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub _wwwCopyDescendants { shift->_wwwCopyProgress(0) }
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub _wwwCopyFinish {
|
||||
my ($self, $newAsset) = @_;
|
||||
my $session = $self->session;
|
||||
my $i18n = WebGUI::International->new($session, 'Asset');
|
||||
my $title = sprintf("%s (%s)", $self->getTitle, $i18n->get('copy'));
|
||||
$newAsset->update({ title => $title });
|
||||
$newAsset->cut;
|
||||
my $result = WebGUI::VersionTag->autoCommitWorkingIfEnabled(
|
||||
$session, {
|
||||
allowComments => 1,
|
||||
returnUrl => $self->getUrl,
|
||||
$args{assetId} = $self->getId;
|
||||
$self->forkWithStatusPage({
|
||||
plugin => 'ProgressTree',
|
||||
title => 'Copy Assets',
|
||||
redirect => $redir,
|
||||
method => 'copyInFork',
|
||||
args => \%args
|
||||
}
|
||||
);
|
||||
my $redirect = $result eq 'redirect';
|
||||
$session->asset($self->getContainer) unless $redirect;
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub _wwwCopyProgress {
|
||||
my ($self, $childrenOnly) = @_;
|
||||
my $session = $self->session;
|
||||
my $i18n = WebGUI::International->new($session, 'Asset');
|
||||
|
||||
# This could potentially time out, so we'll render a progress bar.
|
||||
my $pb = WebGUI::ProgressBar->new($session);
|
||||
my @stack;
|
||||
|
||||
return $pb->run(
|
||||
title => $i18n->get('Copy Assets'),
|
||||
icon => $session->url->extras('adminConsole/assets.gif'),
|
||||
code => sub {
|
||||
my $bar = shift;
|
||||
my $newAsset = $self->duplicateBranch($childrenOnly);
|
||||
$bar->update($i18n->get('cut'));
|
||||
my $redirect = $self->_wwwCopyFinish($newAsset);
|
||||
return $redirect ? $self->getUrl : $self->getContainer->getUrl;
|
||||
},
|
||||
wrap => {
|
||||
'WebGUI::Asset::duplicateBranch' => sub {
|
||||
my ($bar, $original, $asset, @args) = @_;
|
||||
push(@stack, $asset->getTitle);
|
||||
my $ret = $asset->$original(@args);
|
||||
pop(@stack);
|
||||
return $ret;
|
||||
},
|
||||
'WebGUI::Asset::duplicate' => sub {
|
||||
my ($bar, $original, $asset, @args) = @_;
|
||||
my $name = join '/', @stack, $asset->getTitle;
|
||||
$bar->update($name);
|
||||
return $asset->$original(@args);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub _wwwCopySingle {
|
||||
my $self = shift;
|
||||
my $newAsset = $self->duplicate({skipAutoCommitWorkflows => 1});
|
||||
my $redirect = $self->_wwwCopyFinish($newAsset);
|
||||
return $redirect ? undef : $self->getContainer->www_view;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
@ -365,9 +438,8 @@ sub www_copyList {
|
|||
foreach my $assetId ($session->form->param("assetId")) {
|
||||
my $asset = WebGUI::Asset->newById($session,$assetId);
|
||||
if ($asset->canEdit) {
|
||||
my $newAsset = $asset->duplicate({skipAutoCommitWorkflows => 1});
|
||||
my $newAsset = $asset->duplicate({skipAutoCommitWorkflows => 1, state => 'clipboard'});
|
||||
$newAsset->update({ title=>$newAsset->getTitle.' (copy)'});
|
||||
$newAsset->cut;
|
||||
}
|
||||
}
|
||||
if ($self->session->form->process("proceed") ne "") {
|
||||
|
|
@ -505,7 +577,7 @@ sub www_duplicateList {
|
|||
foreach my $assetId ($session->form->param("assetId")) {
|
||||
my $asset = WebGUI::Asset->newById($session,$assetId);
|
||||
if ($asset->canEdit) {
|
||||
my $newAsset = $asset->duplicate({skipAutoCommitWorkflows => 1, });
|
||||
my $newAsset = $asset->duplicate({skipAutoCommitWorkflows => 1});
|
||||
$newAsset->update({ title=>$newAsset->getTitle.' (copy)'});
|
||||
}
|
||||
}
|
||||
|
|
@ -658,26 +730,25 @@ the Asset Manager.
|
|||
sub www_pasteList {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
return $session->privilege->insufficient() unless $self->canEdit && $session->form->validToken;
|
||||
my $form = $session->form;
|
||||
my $pb = WebGUI::ProgressBar->new($session);
|
||||
##Need to store the list of assetIds for the status subroutine
|
||||
my @assetIds = $form->param('assetId');
|
||||
##Need to set the URL that should be displayed when it is done
|
||||
my $i18n = WebGUI::International->new($session, 'Asset');
|
||||
$pb->start($i18n->get('Paste Assets'), $session->url->extras('adminConsole/assets.gif'));
|
||||
ASSET: foreach my $clipId (@assetIds) {
|
||||
next ASSET unless $clipId;
|
||||
my $pasteAsset = WebGUI::Asset->newPending($session, $clipId);
|
||||
if (! $pasteAsset && $pasteAsset->canEdit) {
|
||||
$pb->update(sprintf $i18n->get('skipping %s'), $pasteAsset->getTitle);
|
||||
next ASSET;
|
||||
}
|
||||
$self->paste($clipId, sub {$pb->update(@_);});
|
||||
}
|
||||
return $pb->finish( ($form->param('proceed') eq 'manageAssets') ? $self->getUrl('op=assetManager') : $self->getUrl );
|
||||
}
|
||||
return $session->privilege->insufficient() unless $self->canEdit && $session->form->validToken;
|
||||
|
||||
$self->forkWithStatusPage( {
|
||||
plugin => 'ProgressTree',
|
||||
title => 'Paste Assets',
|
||||
redirect => $self->getUrl(
|
||||
$form->get('proceed') eq 'manageAssets'
|
||||
? 'op=assetManager'
|
||||
: ()
|
||||
),
|
||||
method => 'pasteInFork',
|
||||
args => {
|
||||
assetId => $self->getId,
|
||||
list => [ $form->get('assetId') ],
|
||||
}
|
||||
}
|
||||
);
|
||||
} ## end sub www_pasteList
|
||||
|
||||
1;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ use Scalar::Util qw(looks_like_number);
|
|||
use WebGUI::International;
|
||||
use WebGUI::Exception;
|
||||
use WebGUI::Session;
|
||||
use URI::URL ();
|
||||
use Scope::Guard;
|
||||
use URI::URL;
|
||||
use Scope::Guard qw(guard);
|
||||
use WebGUI::ProgressTree;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
|
|
@ -299,21 +300,53 @@ sub exportAsHtml {
|
|||
}
|
||||
|
||||
sub exportBranch {
|
||||
my $self = shift;
|
||||
my $options = shift;
|
||||
my $reportSession = shift;
|
||||
my ($self, $options, $reportSession) = @_;
|
||||
my $i18n = $reportSession &&
|
||||
WebGUI::International->new($self->session, 'Asset');
|
||||
|
||||
my $depth = $options->{depth};
|
||||
my $indexFileName = $options->{indexFileName};
|
||||
my $extrasUploadAction = $options->{extrasUploadAction};
|
||||
my $rootUrlAction = $options->{rootUrlAction};
|
||||
my $exportedCount = 0;
|
||||
my $report = $options->{report};
|
||||
|
||||
my $i18n;
|
||||
if ( $reportSession ) {
|
||||
$i18n = WebGUI::International->new($self->session, 'Asset');
|
||||
unless ($report) {
|
||||
if ($reportSession) {
|
||||
# We got a report session and no report coderef, so we'll print
|
||||
# messages out. NOTE: this is for backcompat, but I'm not sure we
|
||||
# even need it any more. I think the only thing using it was the
|
||||
# old iframe-based export status report. --frodwith
|
||||
my %reports = (
|
||||
'bad user privileges' => sub {
|
||||
my $asset = shift;
|
||||
my $url = $asset->getUrl;
|
||||
$i18n->get('bad user privileges') . "\n$url"
|
||||
},
|
||||
'not exportable' => sub {
|
||||
my $asset = shift;
|
||||
my $fullPath = $asset->exportGetUrlAsPath;
|
||||
"$fullPath skipped, not exportable<br />";
|
||||
},
|
||||
'exporting page' => sub {
|
||||
my $asset = shift;
|
||||
my $fullPath = $asset->exportGetUrlAsPath;
|
||||
sprintf $i18n->get('exporting page'), $fullPath;
|
||||
},
|
||||
'collateral notes' => sub { pop },
|
||||
'done' => sub { $i18n->get('done') },
|
||||
);
|
||||
$report = sub {
|
||||
my ($asset, $key, @args) = @_;
|
||||
my $code = $reports{$key};
|
||||
my $message = $asset->$code();
|
||||
$reportSession->output->print($message, @args);
|
||||
};
|
||||
}
|
||||
else {
|
||||
$report = sub {};
|
||||
}
|
||||
}
|
||||
|
||||
my $exportedCount = 0;
|
||||
|
||||
my $exportAsset = sub {
|
||||
my ( $assetId ) = @_;
|
||||
|
|
@ -330,26 +363,18 @@ sub exportBranch {
|
|||
|
||||
# skip this asset if we can't view it as this user.
|
||||
unless( $asset->canView ) {
|
||||
if( $reportSession ) {
|
||||
my $message = sprintf( $i18n->get('bad user privileges') . "\n") . $asset->getUrl;
|
||||
$reportSession->output->print($message);
|
||||
}
|
||||
$asset->$report('bad user privileges');
|
||||
next;
|
||||
}
|
||||
|
||||
# skip this asset if it's not exportable.
|
||||
unless ( $asset->exportCheckExportable ) {
|
||||
if ( $reportSession ) {
|
||||
$reportSession->output->print("$fullPath skipped, not exportable<br />");
|
||||
}
|
||||
$asset->$report('not exportable');
|
||||
next;
|
||||
}
|
||||
|
||||
# tell the user which asset we're exporting.
|
||||
if ( $reportSession ) {
|
||||
my $message = sprintf $i18n->get('exporting page'), $fullPath;
|
||||
$reportSession->output->print($message);
|
||||
}
|
||||
$asset->$report('exporting page');
|
||||
|
||||
# try to write the file
|
||||
eval { $asset->exportWriteFile };
|
||||
|
|
@ -359,9 +384,25 @@ sub exportBranch {
|
|||
|
||||
# next, tell the asset that we're exporting, so that it can export any
|
||||
# of its collateral or other extra data.
|
||||
eval { $asset->exportAssetCollateral($asset->exportGetUrlAsPath, $options, $reportSession) };
|
||||
if($@) {
|
||||
WebGUI::Error->throw(error => "failed to export asset collateral for URL " . $asset->getUrl . ": $@");
|
||||
{
|
||||
# For backcompat we want to capture anything that
|
||||
# exportAssetCollateral may have printed and report it to the
|
||||
# coderef. We should get rid of this as soon as we're ready to
|
||||
# break that api.
|
||||
my $cs = $self->session->duplicate();
|
||||
open my $handle, '>', \my $output;
|
||||
$cs->output->setHandle($handle);
|
||||
my $guard = guard {
|
||||
close $handle;
|
||||
$cs->var->end;
|
||||
$cs->close();
|
||||
$asset->$report('collateral notes', $output) if $output;
|
||||
};
|
||||
my $path = $asset->exportGetUrlAsPath;
|
||||
eval { $asset->exportAssetCollateral($path, $options, $cs) };
|
||||
if($@) {
|
||||
WebGUI::Error->throw(error => "failed to export asset collateral for URL " . $asset->getUrl . ": $@");
|
||||
}
|
||||
}
|
||||
|
||||
# we exported this one successfully, so count it
|
||||
|
|
@ -371,19 +412,12 @@ sub exportBranch {
|
|||
$self->session->db->write( "UPDATE asset SET lastExportedAs = ? WHERE assetId = ?",
|
||||
[ $fullPath, $asset->getId ] );
|
||||
|
||||
$self->updateHistory("exported");
|
||||
$asset->updateHistory('exported');
|
||||
|
||||
# tell the user we did this asset correctly
|
||||
if ( $reportSession ) {
|
||||
$reportSession->output->print($i18n->get('done'));
|
||||
}
|
||||
|
||||
#use Devel::Cycle;
|
||||
#warn "CHECKING on " . ref( $asset ) . ' ID: ' . $asset->getId . "\n";
|
||||
#find_cycle( $asset );
|
||||
$asset->$report('done');
|
||||
};
|
||||
|
||||
|
||||
my $assetIds = $self->exportGetDescendants(undef, $depth);
|
||||
foreach my $assetId ( @{$assetIds} ) {
|
||||
$exportAsset->( $assetId );
|
||||
|
|
@ -616,6 +650,44 @@ sub exportGetUrlAsPath {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 exportInFork
|
||||
|
||||
Intended to be called by WebGUI::Fork. Runs exportAsHtml on the
|
||||
specified asset and keeps a json structure as the status.
|
||||
|
||||
=cut
|
||||
|
||||
sub exportInFork {
|
||||
my ( $process, $args ) = @_;
|
||||
my $session = $process->session;
|
||||
my $self = WebGUI::Asset->new( $session, delete $args->{assetId} );
|
||||
$args->{indexFileName} = delete $args->{index};
|
||||
my $assetIds = $self->exportGetDescendants( undef, $args->{depth} );
|
||||
my $tree = WebGUI::ProgressTree->new( $session, $assetIds );
|
||||
my %reports = (
|
||||
'done' => sub { $tree->success(shift) },
|
||||
'exporting page' => sub { $tree->focus(shift) },
|
||||
'collateral notes' => sub { $tree->note(@_) },
|
||||
'bad user privileges' => sub {
|
||||
$tree->failure( shift, 'Bad User Privileges' );
|
||||
},
|
||||
'not exportable' => sub {
|
||||
$tree->failure( shift, 'Not Exportable' );
|
||||
},
|
||||
);
|
||||
$args->{report} = sub {
|
||||
my ( $asset, $key, @args ) = @_;
|
||||
my $code = $reports{$key};
|
||||
$code->( $asset->getId, @args );
|
||||
$process->update( sub { $tree->json } );
|
||||
};
|
||||
$self->exportAsHtml($args);
|
||||
$tree->focus(undef);
|
||||
$process->update( $tree->json );
|
||||
} ## end sub exportInFork
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 exportSymlinkExtrasUploads ( [ session ] )
|
||||
|
||||
Class or object method. Sets up the extras and uploads symlinks.
|
||||
|
|
@ -926,16 +998,25 @@ Displays the export status page
|
|||
=cut
|
||||
|
||||
sub www_exportStatus {
|
||||
my $self = shift;
|
||||
return $self->session->privilege->insufficient() unless ($self->session->user->isInGroup(13));
|
||||
my $i18n = WebGUI::International->new($self->session, "Asset");
|
||||
my $iframeUrl = $self->getUrl('func=exportGenerate');
|
||||
foreach my $formVar (qw/index depth userId extrasUploadsAction rootUrlAction exportUrl/) {
|
||||
$iframeUrl = $self->session->url->append($iframeUrl, $formVar . '=' . $self->session->form->process($formVar));
|
||||
}
|
||||
|
||||
my $output = '<iframe src="' . $iframeUrl . '" title="' . $i18n->get('Page Export Status') . '" width="100%" height="500"></iframe>';
|
||||
$self->getAdminConsole->render($output, $i18n->get('Page Export Status'), "Asset");
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
return $session->privilege->insufficient
|
||||
unless $session->user->isInGroup(13);
|
||||
my $form = $session->form;
|
||||
my @vars = qw(
|
||||
index depth userId extrasUploadsAction rootUrlAction exportUrl
|
||||
);
|
||||
$self->forkWithStatusPage({
|
||||
plugin => 'ProgressTree',
|
||||
title => 'Page Export Status',
|
||||
method => 'exportInFork',
|
||||
groupId => 13,
|
||||
args => {
|
||||
assetId => $self->getId,
|
||||
map { $_ => scalar $form->get($_) } @vars
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -200,6 +200,63 @@ sub purge {
|
|||
return 1;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 purgeInFork
|
||||
|
||||
WebGUI::Fork method called by www_purgeList
|
||||
|
||||
=cut
|
||||
|
||||
sub purgeInFork {
|
||||
my ( $process, $list ) = @_;
|
||||
my $session = $process->session;
|
||||
my @roots = grep { $_ && $_->canEdit }
|
||||
map { WebGUI::Asset->newPending( $session, $_ ) } @$list;
|
||||
|
||||
my @ids = map {
|
||||
my $list = $_->getLineage(
|
||||
[ 'self', 'descendants' ], {
|
||||
statesToInclude => [qw(published clipboard clipboard-limbo trash trash-limbo)],
|
||||
statusToInclude => [qw(approved archived pending)],
|
||||
}
|
||||
);
|
||||
@$list;
|
||||
} @roots;
|
||||
|
||||
my $tree = WebGUI::ProgressTree->new( $session, \@ids );
|
||||
my $patch = Monkey::Patch::patch_class(
|
||||
'WebGUI::Asset',
|
||||
'purge',
|
||||
sub {
|
||||
my ( $purge, $self, $options ) = @_;
|
||||
my $id = $self->getId;
|
||||
my $zero = '';
|
||||
$tree->focus($id);
|
||||
$options ||= {};
|
||||
local $options->{outputSub} = sub { $zero .= $_[0] };
|
||||
my $ret = eval { $self->$purge($options) };
|
||||
my $e = $@;
|
||||
$tree->focus($id);
|
||||
|
||||
if ($e) {
|
||||
$tree->failure( $id, 'Died' );
|
||||
$tree->note( $id, $e );
|
||||
}
|
||||
elsif ( !$ret ) {
|
||||
$tree->failure( $id, 'Failed' );
|
||||
$tree->note( $id, $zero );
|
||||
}
|
||||
else {
|
||||
$tree->success($id);
|
||||
}
|
||||
$process->update( sub { $tree->json } );
|
||||
die $e if $e;
|
||||
return $ret;
|
||||
}
|
||||
);
|
||||
$_->purge for @roots;
|
||||
} ## end sub purgeInFork
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
@ -246,7 +303,13 @@ sub trash {
|
|||
return undef;
|
||||
}
|
||||
|
||||
my $assetIter = $self->getLineageIterator(['self','descendants']);
|
||||
my $assetIter = $self->getLineageIterator(
|
||||
['self','descendants'], {
|
||||
statesToInclude => [qw(published clipboard clipboard-limbo trash trash-limbo)],
|
||||
statusToInclude => [qw(approved archived pending)],
|
||||
}
|
||||
);
|
||||
my $rootId = $self->getId;
|
||||
while ( 1 ) {
|
||||
my $asset;
|
||||
eval { $asset = $assetIter->() };
|
||||
|
|
@ -263,6 +326,18 @@ sub trash {
|
|||
$outputSub->($i18n->get('Clearing cache'));
|
||||
$asset->purgeCache;
|
||||
$asset->updateHistory("trashed");
|
||||
if ($asset->getId eq $rootId) {
|
||||
$asset->setState('trash');
|
||||
# setState will take care of _properties in $asset, but not in
|
||||
# $self (whooops!), so we need to manually update.
|
||||
my @keys = qw(state stateChangedBy stateChanged);
|
||||
$self->state($asset->state);
|
||||
$self->stateChangedBy($asset->stateChangedBy);
|
||||
$self->stateChanged($asset->stateChanged);
|
||||
}
|
||||
else {
|
||||
$asset->setState('trash-limbo');
|
||||
}
|
||||
}
|
||||
|
||||
# Trash any shortcuts to this asset
|
||||
|
|
@ -287,6 +362,50 @@ sub trash {
|
|||
return 1;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 trashInFork
|
||||
|
||||
WebGUI::Fork method called by www_deleteList and www_delete to move assets
|
||||
into the trash.
|
||||
|
||||
=cut
|
||||
|
||||
sub trashInFork {
|
||||
my ( $process, $list ) = @_;
|
||||
my $session = $process->session;
|
||||
my @roots = grep { $_->canEdit && $_->canEditIfLocked }
|
||||
map {
|
||||
eval { WebGUI::Asset->newPending( $session, $_ ) }
|
||||
} @$list;
|
||||
|
||||
my @ids = map {
|
||||
my $list = $_->getLineage(
|
||||
[ 'self', 'descendants' ], {
|
||||
statesToInclude => [qw(published clipboard clipboard-limbo trash trash-limbo)],
|
||||
statusToInclude => [qw(approved archived pending)],
|
||||
}
|
||||
);
|
||||
@$list;
|
||||
} @roots;
|
||||
|
||||
my $tree = WebGUI::ProgressTree->new( $session, \@ids );
|
||||
my $patch = Monkey::Patch::patch_class(
|
||||
'WebGUI::Asset',
|
||||
'setState',
|
||||
sub {
|
||||
my ( $setState, $self, $state ) = @_;
|
||||
my $id = $self->getId;
|
||||
$tree->focus($id);
|
||||
my $ret = $self->$setState($state);
|
||||
$tree->success($id);
|
||||
$process->update(sub { $tree->json });
|
||||
return $ret;
|
||||
}
|
||||
);
|
||||
$_->trash() for @roots;
|
||||
} ## end sub trashInFork
|
||||
|
||||
require WebGUI::Workflow::Activity::DeleteExportedFiles;
|
||||
sub _invokeWorkflowOnExportedFiles {
|
||||
my $self = shift;
|
||||
|
|
@ -316,7 +435,7 @@ sub _invokeWorkflowOnExportedFiles {
|
|||
|
||||
=head2 www_delete
|
||||
|
||||
Moves self to trash, returns www_view() method of Container or Parent if canEdit.
|
||||
Moves self to trash in fork, redirects to Container or Parent if canEdit.
|
||||
Otherwise returns AdminConsole rendered insufficient privilege.
|
||||
|
||||
=cut
|
||||
|
|
@ -331,8 +450,14 @@ sub www_delete {
|
|||
if ($self->getId eq $asset->getId) {
|
||||
$asset = $self->getParent;
|
||||
}
|
||||
$self->session->asset($asset);
|
||||
return $asset->www_view;
|
||||
$self->forkWithStatusPage({
|
||||
plugin => 'ProgressTree',
|
||||
title => 'Delete Assets',
|
||||
redirect => $asset->getUrl,
|
||||
method => 'trashInFork',
|
||||
args => [ $self->getId ],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
@ -348,31 +473,20 @@ by the form variable C<proceeed>.
|
|||
=cut
|
||||
|
||||
sub www_deleteList {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $pb = WebGUI::ProgressBar->new($session);
|
||||
my $i18n = WebGUI::International->new($session, 'Asset');
|
||||
my $form = $session->form;
|
||||
my @assetIds = $form->param('assetId');
|
||||
$pb->start($i18n->get('Delete Assets'), $session->url->extras('adminConsole/assets.gif'));
|
||||
return $self->session->privilege->insufficient() unless $session->form->validToken;
|
||||
ASSETID: foreach my $assetId (@assetIds) {
|
||||
my $asset = eval { WebGUI::Asset->newPending($session,$assetId); };
|
||||
if ($@) {
|
||||
$pb->update(sprintf $i18n->get('Error getting asset with assetId %s'), $assetId);
|
||||
next ASSETID;
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $form = $session->form;
|
||||
return $session->privilege->insufficient() unless $session->form->validToken;
|
||||
my $method = $form->get('proceed') || 'manageTrash';
|
||||
$self->forkWithStatusPage({
|
||||
plugin => 'ProgressTree',
|
||||
title => 'Delete Assets',
|
||||
redirect => $self->getUrl("func=$method"),
|
||||
method => 'trashInFork',
|
||||
args => [ $form->get('assetId') ],
|
||||
}
|
||||
if (! ($asset->canEdit && $asset->canEditIfLocked) ) {
|
||||
$pb->update(sprintf $i18n->get('You cannot edit the asset %s, skipping'), $asset->getTitle);
|
||||
}
|
||||
else {
|
||||
$asset->trash({outputSub => sub { $pb->update(@_); } });
|
||||
}
|
||||
}
|
||||
my $method = ($session->form->process("proceed")) ? $session->form->process('proceed') : 'manageTrash';
|
||||
$pb->finish($self->getUrl('func='.$method));
|
||||
}
|
||||
|
||||
);
|
||||
} ## end sub www_deleteList
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
@ -479,29 +593,18 @@ Returns insufficient privileges unless the submitted form passes the validToken
|
|||
sub www_purgeList {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $form = $session->form;
|
||||
return $session->privilege->insufficient() unless $session->form->validToken;
|
||||
my $pb = WebGUI::ProgressBar->new($session);
|
||||
my $i18n = WebGUI::International->new($session, 'Asset');
|
||||
$pb->start($i18n->get('purge'), $session->url->extras('adminConsole/assets.gif'));
|
||||
|
||||
ASSETID: foreach my $id ($session->form->param("assetId")) {
|
||||
my $asset = eval { WebGUI::Asset->newPending($session,$id); };
|
||||
if ($@) {
|
||||
$pb->update(sprintf $i18n->get('Error getting asset with assetId %s'), $id);
|
||||
next ASSETID;
|
||||
my $method = $form->get('proceed') || 'manageTrash';
|
||||
$method .= ';systemTrash=1' if $form->get('systemTrash');
|
||||
$self->forkWithStatusPage({
|
||||
plugin => 'ProgressTree',
|
||||
title => 'purge',
|
||||
redirect => $self->getUrl("func=$method"),
|
||||
method => 'purgeInFork',
|
||||
args => [ $form->get('assetId') ],
|
||||
}
|
||||
if (! $asset->canEdit) {
|
||||
$pb->update(sprintf $i18n->get('You cannot edit the asset %s, skipping'), $asset->getTitle);
|
||||
}
|
||||
else {
|
||||
$asset->purge({outputSub => sub { $pb->update(@_); } });
|
||||
}
|
||||
}
|
||||
my $method = ($session->form->process("proceed")) ? $session->form->process('proceed') : 'manageTrash';
|
||||
if ($session->form->process('systemTrash') ) {
|
||||
$method .= ';systemTrash=1';
|
||||
}
|
||||
$pb->finish($self->getUrl('func='.$method));
|
||||
);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -400,7 +400,8 @@ sub editUserForm {
|
|||
$f->password(
|
||||
name=>"authWebGUI.identifier",
|
||||
label=>$i18n->get(51),
|
||||
value=>"password"
|
||||
value=>"password",
|
||||
extras=>'autocomplete="off"',
|
||||
);
|
||||
$f->interval(
|
||||
-name=>"authWebGUI.passwordTimeout",
|
||||
|
|
@ -1131,7 +1132,7 @@ sub emailRecoverPasswordFinish {
|
|||
|
||||
my $mail = WebGUI::Mail::Send->create($session, { to=>$email, subject=>$i18n->get('WebGUI password recovery')});
|
||||
my $vars = { };
|
||||
$vars->{recoverPasswordUrl} = $session->url->append($session->url->getSiteURL,'?op=auth;method=emailResetPassword;token='.$recoveryGuid);
|
||||
$vars->{recoverPasswordUrl} = $session->url->append($session->url->getSiteURL,'op=auth;method=emailResetPassword;token='.$recoveryGuid);
|
||||
my $template = WebGUI::Asset->newByDynamicClass($session, $session->setting->get('webguiPasswordRecoveryEmailTemplate'));
|
||||
my $emailText = $template->process($vars);
|
||||
WebGUI::Macro::process($session, \$emailText);
|
||||
|
|
|
|||
668
lib/WebGUI/Fork.pm
Normal file
668
lib/WebGUI/Fork.pm
Normal file
|
|
@ -0,0 +1,668 @@
|
|||
package WebGUI::Fork;
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
use File::Spec;
|
||||
use JSON;
|
||||
use POSIX;
|
||||
use Config;
|
||||
use IO::Pipe;
|
||||
use WebGUI::Session;
|
||||
use WebGUI::Pluggable;
|
||||
use Time::HiRes qw(sleep);
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Fork
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Safely and portably spawn a long running process that you can check the
|
||||
status of.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
package WebGUI::Some::Class;
|
||||
|
||||
sub doWork {
|
||||
my ($process, $data) = @_;
|
||||
$process->update("Starting...");
|
||||
...
|
||||
$process->update("About half way done...");
|
||||
...
|
||||
$process->update("Finished!");
|
||||
}
|
||||
|
||||
sub www_doWork {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $process = WebGUI::Fork->start(
|
||||
$session, 'WebGUI::Some::Class', 'doWork', { some => 'data' }
|
||||
);
|
||||
# See WebGUI::Operation::Fork
|
||||
my $pairs = $process->contentPairs('DoWork');
|
||||
$session->http->setRedirect($self->getUrl($pairs));
|
||||
return 'redirect';
|
||||
}
|
||||
|
||||
package WebGUI::Operation::Fork::DoWork;
|
||||
|
||||
sub handler {
|
||||
my $process = shift;
|
||||
my $session = $process->session;
|
||||
return $session->style->userStyle($process->status);
|
||||
|
||||
# or better yet, an ajaxy page that polls.
|
||||
}
|
||||
|
||||
|
||||
=head1 LEGAL
|
||||
|
||||
-------------------------------------------------------------------
|
||||
WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
-------------------------------------------------------------------
|
||||
Please read the legal notices (docs/legal.txt) and the license
|
||||
(docs/license.txt) that came with this distribution before using
|
||||
this software.
|
||||
-------------------------------------------------------------------
|
||||
http://www.plainblack.com info@plainblack.com
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 canView ($user?)
|
||||
|
||||
Returns whether the current user (or the user passed in, if there is one) has
|
||||
permission to view the status of the fork. By default, only admins can view,
|
||||
but see setGroup.
|
||||
|
||||
=cut
|
||||
|
||||
sub canView {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $user = shift || $session->user;
|
||||
$user = WebGUI::User->new( $session, $user )
|
||||
unless eval { $user->isa('WebGUI::User') };
|
||||
return
|
||||
$user->isAdmin
|
||||
|| $user->userId eq $self->getUserId
|
||||
|| $user->isInGroup( $self->getGroupId );
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 contentPairs ($module, $pid, $extra)
|
||||
|
||||
Returns a bit of query string useful for redirecting to a
|
||||
WebGUI::Operation::Fork plugin. $module should be the bit that comes after
|
||||
WebGUI::Operation::Fork, e.g. $process->contentPairs('Foo') should return
|
||||
something like "op=fork;module=Foo;pid=adlfjafo87ad9f78a7", which will
|
||||
get dispatched to WebGUI::Operation::Fork::Foo::handler($process).
|
||||
|
||||
$extra is an optional hashref that will add further parameters onto the list
|
||||
of pairs, e.g. { foo => 'bar' } becomes ';foo=bar'
|
||||
|
||||
=cut
|
||||
|
||||
sub contentPairs {
|
||||
my ( $self, $module, $extra ) = @_;
|
||||
my $url = $self->session->url;
|
||||
my $pid = $self->getId;
|
||||
my %params = (
|
||||
op => 'fork',
|
||||
module => $module,
|
||||
pid => $self->getId,
|
||||
$extra ? %$extra : ()
|
||||
);
|
||||
return join(
|
||||
';',
|
||||
map {
|
||||
my $k = $_;
|
||||
join( '=', map { $url->escape($_) } ( $k, $params{$k} ) );
|
||||
} keys %params
|
||||
);
|
||||
} ## end sub contentPairs
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 create ( )
|
||||
|
||||
Internal class method. Creates a new Fork object inserts it into the db.
|
||||
|
||||
=cut
|
||||
|
||||
sub create {
|
||||
my ( $class, $session ) = @_;
|
||||
my $id = $session->id->generate;
|
||||
my %data = ( userId => $session->user->userId );
|
||||
$session->db->setRow( $class->tableName, 'id', \%data, $id );
|
||||
bless { session => $session, id => $id }, $class;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 daemonize ( $stdin, $sub )
|
||||
|
||||
Internal lass method. Runs the given $sub in daemon, and prints $stdin to its
|
||||
stdin.
|
||||
|
||||
=cut
|
||||
|
||||
sub daemonize {
|
||||
my ( $class, $stdin, $sub ) = @_;
|
||||
my $pid = fork();
|
||||
die "Cannot fork: $!" unless defined $pid;
|
||||
if ($pid) {
|
||||
|
||||
# The child process will fork again and exit immediately, so we can
|
||||
# wait for it (and thus not have zombie processes).
|
||||
waitpid( $pid, 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
eval {
|
||||
|
||||
# detach from controlling terminal, get us into a new process group
|
||||
die "Cannot become session leader: $!" if POSIX::setsid() < 0;
|
||||
|
||||
# Fork again so we never get a controlling terminal
|
||||
my $worker = IO::Pipe->new;
|
||||
my $pid = fork();
|
||||
die "Child cannot fork: $!" unless defined $pid;
|
||||
|
||||
# We don't want to call any destructors, as it would mess with the
|
||||
# parent's mysql connection, etc.
|
||||
if ($pid) {
|
||||
$worker->writer;
|
||||
$worker->printflush($stdin);
|
||||
POSIX::_exit(0);
|
||||
}
|
||||
|
||||
# We're now in the final target process. STDIN should be whatever the
|
||||
# parent printed to us, and all output should go to /dev/null.
|
||||
$worker->reader();
|
||||
open STDIN, '<&', $worker or die "Cannot dup stdin: $!";
|
||||
open STDOUT, '>', '/dev/null' or die "Cannot write /dev/null: $!";
|
||||
open STDERR, '>&', \*STDOUT or die "Cannot dup stdout: $!";
|
||||
|
||||
# Standard daemon-y things...
|
||||
$SIG{HUP} = 'IGNORE';
|
||||
chdir '/';
|
||||
umask 0;
|
||||
|
||||
# Forcibly close any non-std open file descriptors that remain
|
||||
my $max = POSIX::sysconf(&POSIX::_SC_OPEN_MAX) || 1024;
|
||||
POSIX::close($_) for ( $^F .. $max );
|
||||
|
||||
# Do whatever we're supposed to do
|
||||
&$sub();
|
||||
};
|
||||
|
||||
POSIX::_exit( $@ ? -1 : 0 );
|
||||
} ## end sub daemonize
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 delete ( )
|
||||
|
||||
Clean up the information for this process from the database.
|
||||
|
||||
=cut
|
||||
|
||||
sub delete {
|
||||
my $self = shift;
|
||||
$self->session->db->deleteRow( $self->tableName, 'id', $self->getId );
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 endTime ( )
|
||||
|
||||
Returns the epoch time indicating when the subroutine passed to run() finished
|
||||
executing, or undef if it hasn't finished. Note that even if the sub passed
|
||||
to run dies, an endTime will be recorded.
|
||||
|
||||
=cut
|
||||
|
||||
sub endTime { $_[0]->get('endTime') }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 error ( $msg )
|
||||
|
||||
Call this to record an error status. You probably shouldn't, though -- just
|
||||
dying from your subroutine will cause this to be set.
|
||||
|
||||
=cut
|
||||
|
||||
sub error { $_[0]->set( { error => $_[1] } ) }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 finish ( )
|
||||
|
||||
Mark the process as being finished. This is called for you when your
|
||||
subroutine is finished. If update() wasn't computed on the last call, it will
|
||||
be computed now.
|
||||
|
||||
=cut
|
||||
|
||||
sub finish {
|
||||
my $self = shift;
|
||||
my %props = ( finished => 1 );
|
||||
if ( my $calc = delete $self->{delay} ) {
|
||||
$props{status} = $calc->();
|
||||
$props{latch} = 0;
|
||||
}
|
||||
$props{endTime} = time();
|
||||
$self->set( \%props );
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 forkAndExec ($request)
|
||||
|
||||
Internal method. Forks and execs a new perl process to run $request. This is
|
||||
used as a fallback if the master daemon runner is not working.
|
||||
|
||||
=cut
|
||||
|
||||
sub forkAndExec {
|
||||
my ( $self, $request ) = @_;
|
||||
my $id = $self->getId;
|
||||
my $class = ref $self;
|
||||
my $json = JSON::encode_json($request);
|
||||
my @inc = map {"-I$_"} map { File::Spec->rel2abs($_) } grep { !ref } @INC;
|
||||
my @argv = (@inc, "-M$class", "-e$class->runCmd()" );
|
||||
$class->daemonize(
|
||||
$json,
|
||||
sub {
|
||||
exec ($Config{perlpath}, @argv) or die "Could not exec: $!";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 get ( @keys )
|
||||
|
||||
Get data from the database record for this process (returned as a simple list,
|
||||
not an arrayref). Valid keys are: id, status, error, startTime, endTime,
|
||||
finished, groupId, userId. They all have more specific accessors, but you can
|
||||
use this to get several at once if you're very careful. You should probably
|
||||
use the accessors, though, since some of them have extra logic.
|
||||
|
||||
=cut
|
||||
|
||||
sub get {
|
||||
my ( $self, @keys ) = @_;
|
||||
my $db = $self->session->db;
|
||||
my $dbh = $db->dbh;
|
||||
my $tbl = $dbh->quote_identifier( $self->tableName );
|
||||
my $key
|
||||
= @keys
|
||||
? join( ',', map { $dbh->quote_identifier($_) } @keys )
|
||||
: '*';
|
||||
my $id = $dbh->quote( $self->getId );
|
||||
my @values = $db->quickArray("SELECT $key FROM $tbl WHERE id = $id");
|
||||
return ( @values > 1 ) ? @values : $values[0];
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 getError ( )
|
||||
|
||||
If the process died, this will be set to stringified $@.
|
||||
|
||||
=cut
|
||||
|
||||
sub getError { $_[0]->get('error') }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 getGroupId
|
||||
|
||||
Returns the group ID (not the actual WebGUI::Group) of users who are allowed
|
||||
to view this process.
|
||||
|
||||
=cut
|
||||
|
||||
sub getGroupId {
|
||||
my $id = $_[0]->get('groupId');
|
||||
return $id || 3;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 getId ( )
|
||||
|
||||
The unique id for this fork. Note: this is NOT the pid, but a WebGUI guid.
|
||||
|
||||
=cut
|
||||
|
||||
sub getId { shift->{id} }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 getStatus()
|
||||
|
||||
Signals the fork that it should report its next status, then polls at a
|
||||
configurable, fractional interval (default: .1 seconds) waiting for the fork
|
||||
to claim that its status has been updated. Returns the updated status. See
|
||||
setWait() for a way to change the interval (or disable the waiting procedure
|
||||
entirely). We will only wait for a maximum of 100 intervals.
|
||||
|
||||
=cut
|
||||
|
||||
sub getStatus {
|
||||
my $self = shift;
|
||||
if ( my $interval = $self->{interval} ) {
|
||||
$self->set( { latch => 1 } );
|
||||
my $maxWait;
|
||||
while ($maxWait++ < 100) {
|
||||
sleep $interval;
|
||||
my ( $finished, $latch ) = $self->get( 'finished', 'latch' );
|
||||
last if $finished || !$latch;
|
||||
}
|
||||
}
|
||||
return $self->get('status');
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 getUserId
|
||||
|
||||
Returns the userId of the user who initiated this Fork.
|
||||
|
||||
=cut
|
||||
|
||||
sub getUserId { $_[0]->get('userId') }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 init ( )
|
||||
|
||||
Spawn a master process from which Forks will fork(). The intent
|
||||
is for this to be called once at server startup time, after you've preloaded
|
||||
modules and before you start listening for requests. Returns a filehandle that
|
||||
can be used to print requests to the master process, and which you almost
|
||||
certainly shouldn't use (it's mostly for testing).
|
||||
|
||||
=cut
|
||||
|
||||
my $pipe;
|
||||
|
||||
sub init {
|
||||
my $class = shift;
|
||||
$pipe = IO::Pipe->new;
|
||||
|
||||
my $pid = fork();
|
||||
die "Cannot fork: $!" unless defined $pid;
|
||||
|
||||
if ($pid) {
|
||||
$pipe->writer;
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
$0 = 'webgui-fork-master';
|
||||
$pipe->reader;
|
||||
local $/ = "\x{0}";
|
||||
while ( my $request = $pipe->getline ) {
|
||||
chomp $request;
|
||||
eval {
|
||||
$class->daemonize( $request, sub { $class->runCmd } );
|
||||
};
|
||||
}
|
||||
exit 0;
|
||||
} ## end sub init
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 isFinished ( )
|
||||
|
||||
A simple flag indicating that the fork is no longer running.
|
||||
|
||||
=cut
|
||||
|
||||
sub isFinished { $_[0]->get('finished') }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 new ( $session, $id )
|
||||
|
||||
Returns an object capable of checking on the status of the fork indicated by
|
||||
$id. Returns undef if there is no such process.
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my ( $class, $session, $id ) = @_;
|
||||
my $db = $session->db;
|
||||
my $tbl = $db->dbh->quote_identifier( $class->tableName );
|
||||
my $sql = "SELECT COUNT(*) FROM $tbl WHERE id = ?";
|
||||
my $exists = $db->quickScalar( $sql, [$id] );
|
||||
return $exists
|
||||
? bless( { session => $session, id => $id, interval => .1 }, $class )
|
||||
: undef;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 session ()
|
||||
|
||||
Get the WebGUI::Session this process was created with. Note: this is safe to
|
||||
call in the child process, as it is a duplicated session (same session id) and
|
||||
doesn't share any handles with the parent process.
|
||||
|
||||
=cut
|
||||
|
||||
sub session { $_[0]->{session} }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 set ($properties)
|
||||
|
||||
Updates the database row with the properties given by the $properties hashref.
|
||||
See get() for a list of valid keys.
|
||||
|
||||
=cut
|
||||
|
||||
sub set {
|
||||
my ( $self, $values ) = @_;
|
||||
my %row = ( id => $self->getId, %$values );
|
||||
$self->session->db->setRow( $self->tableName, 'id', \%row );
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 setGroup($groupId)
|
||||
|
||||
Allow the given group (in addition to admins) the ability to check on the
|
||||
status of this process
|
||||
|
||||
=cut
|
||||
|
||||
sub setGroup {
|
||||
my ( $self, $groupId ) = @_;
|
||||
$groupId = eval { $groupId->getId } || $groupId;
|
||||
$self->set( { groupId => $groupId } );
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 request ($module, $subname, $data)
|
||||
|
||||
Internal method. Generates a hashref suitable for passing to runRequest.
|
||||
|
||||
=cut
|
||||
|
||||
sub request {
|
||||
my ( $self, $module, $subname, $data ) = @_;
|
||||
my $session = $self->session;
|
||||
my $config = $session->config;
|
||||
return {
|
||||
webguiRoot => $config->getWebguiRoot,
|
||||
configFile => $config->getFilename,
|
||||
sessionId => $session->getId,
|
||||
module => $module,
|
||||
subname => $subname,
|
||||
id => $self->getId,
|
||||
data => $data,
|
||||
};
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 runCmd ()
|
||||
|
||||
Internal class method. Decodes json off of stdin and passes it to runRequest.
|
||||
|
||||
=cut
|
||||
|
||||
sub runCmd {
|
||||
my $class = shift;
|
||||
my $slurp = do { local $/; <STDIN> };
|
||||
$class->runRequest( JSON::decode_json($slurp) );
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 runRequest ($hashref)
|
||||
|
||||
Internal class method. Expects a hash of arguments describing what to run.
|
||||
|
||||
=cut
|
||||
|
||||
sub runRequest {
|
||||
my ( $class, $args ) = @_;
|
||||
my ( $root, $config, $sid ) = @{$args}{qw(webguiRoot configFile sessionId)};
|
||||
my $session = WebGUI::Session->open( $root, $config, undef, undef, $sid );
|
||||
my $id = $args->{id};
|
||||
my $self = $class->new( $session, $id );
|
||||
$self->set( { startTime => time } );
|
||||
$0 = "webgui-fork-$id";
|
||||
eval {
|
||||
my ( $module, $subname, $data ) = @{$args}{qw(module subname data)};
|
||||
WebGUI::Pluggable::run( $module, $subname, [ $self, $data ] );
|
||||
};
|
||||
$self->error($@) if $@;
|
||||
$self->finish();
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 sendRequestToMaster ($request)
|
||||
|
||||
Internal method. Attempts to send a request to the master daemon runner.
|
||||
Returns 1 on success and 0 on failure.
|
||||
|
||||
=cut
|
||||
|
||||
sub sendRequestToMaster {
|
||||
my ( $self, $request ) = @_;
|
||||
my $json = JSON::encode_json($request);
|
||||
eval {
|
||||
die 'pipe' unless $pipe && $pipe->isa('IO::Handle');
|
||||
local $SIG{PIPE} = sub { die 'pipe' };
|
||||
$pipe->printflush("$json\x{0}") or die 'pipe';
|
||||
};
|
||||
return 1 unless $@;
|
||||
undef $pipe;
|
||||
$self->session->log->error('Problems talking to master daemon process. Please restart the web server.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 setWait ( $interval )
|
||||
|
||||
Use this to control the pace at which getStatus will poll for updated
|
||||
statuses. By default, this is a tenth of a second. If you set it to 0,
|
||||
getStatus will still signal the fork for an update, but will take whatever is
|
||||
currently recorded as the status and return immediately.
|
||||
|
||||
=cut
|
||||
|
||||
sub setWait { $_[0]->{interval} = $_[1] }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 start ( $session, $module, $subname, $data )
|
||||
|
||||
Class method. Executes $module::subname in a forked process with ($process,
|
||||
$data) as its arguments. The only restriction on $data is that it be
|
||||
serializable by JSON.
|
||||
|
||||
=head3 $0
|
||||
|
||||
The process name (as it appears in ps) will be set to webgui-fork-$id,
|
||||
where $id is the value returned by $process->getId. It thus won't look like a
|
||||
modperl process to anyone monitoring the process table (wremonitor.pl, for
|
||||
example).
|
||||
|
||||
=cut
|
||||
|
||||
sub start {
|
||||
my ( $class, $session, $module, $subname, $data ) = @_;
|
||||
my $self = $class->create($session);
|
||||
my $request = $self->request( $module, $subname, $data );
|
||||
$self->sendRequestToMaster($request) or $self->forkAndExec($request);
|
||||
return $self;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 startTime ( )
|
||||
|
||||
Returns the time this process started running in epoch format.
|
||||
|
||||
=cut
|
||||
|
||||
sub startTime { $_[0]->get('startTime') }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 tableName ( )
|
||||
|
||||
Class method: a constant, for convenience. The name of the table that process
|
||||
data is stored in.
|
||||
|
||||
=cut
|
||||
|
||||
sub tableName {'Fork'}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 update ( $msg )
|
||||
|
||||
Set a new status for the fork. This can be anything, and will overwrite the
|
||||
old status. JSON is recommended for complex statuses. Optionally, $msg can
|
||||
be a subroutine that returns the new status -- if your status may take a long
|
||||
time to compute, you should use this, as you may be able to avoid computing
|
||||
some (or all) of your status updates, depending on how often they're being
|
||||
asked for. See the getStatus method for details.
|
||||
|
||||
=cut
|
||||
|
||||
sub update {
|
||||
my ( $self, $msg ) = @_;
|
||||
if ( ref $msg eq 'CODE' ) {
|
||||
if ( $self->get('latch') ) {
|
||||
$msg = $msg->();
|
||||
}
|
||||
else {
|
||||
$self->{delay} = $msg;
|
||||
return;
|
||||
}
|
||||
}
|
||||
delete $self->{delay};
|
||||
$self->set( { latch => 0, status => $msg } );
|
||||
}
|
||||
|
||||
1;
|
||||
140
lib/WebGUI/Fork/ProgressBar.pm
Normal file
140
lib/WebGUI/Fork/ProgressBar.pm
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package WebGUI::Fork::ProgressBar;
|
||||
|
||||
=head1 LEGAL
|
||||
|
||||
-------------------------------------------------------------------
|
||||
WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
-------------------------------------------------------------------
|
||||
Please read the legal notices (docs/legal.txt) and the license
|
||||
(docs/license.txt) that came with this distribution before using
|
||||
this software.
|
||||
-------------------------------------------------------------------
|
||||
http://www.plainblack.com info@plainblack.com
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Fork::ProgressBar
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Renders an admin console page that polls ::Status to draw a simple progress
|
||||
bar along with some kind of message.
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
These subroutines are available from this package:
|
||||
|
||||
=cut
|
||||
|
||||
use Template;
|
||||
use HTML::Entities;
|
||||
use JSON;
|
||||
|
||||
my $template = <<'TEMPLATE';
|
||||
<div id='loading'>[% i18n('WebGUI', 'Loading...') %]</div>
|
||||
<div id='ui' style='display: none'>
|
||||
<p id='message'></p>
|
||||
<div id='meter'></div>
|
||||
<p>
|
||||
[% i18n('Fork_ProgressBar', 'time elapsed') %]:
|
||||
<span id='elapsed'></span> [% i18n('Fork_ProgressBar', 'seconds') %].
|
||||
</p>
|
||||
</div>
|
||||
<script>
|
||||
(function (params) {
|
||||
var bar = new YAHOO.WebGUI.Fork.ProgressBar();
|
||||
YAHOO.util.Event.onDOMReady(function () {
|
||||
bar.render('meter');
|
||||
YAHOO.WebGUI.Fork.poll({
|
||||
url : params.statusUrl,
|
||||
draw : function (data) {
|
||||
var status = YAHOO.lang.JSON.parse(data.status);
|
||||
bar.update(status.finished, status.total);
|
||||
document.getElementById('message').innerHTML = status.message;
|
||||
document.getElementById('elapsed').innerHTML = data.elapsed;
|
||||
},
|
||||
first : function () {
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('ui').style.display = 'block';
|
||||
},
|
||||
finish : function() {
|
||||
YAHOO.WebGUI.Fork.redirect(params.redirect);
|
||||
},
|
||||
error : function (msg) {
|
||||
alert(msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
}([% params %]));
|
||||
</script>
|
||||
TEMPLATE
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 handler ( process )
|
||||
|
||||
See WebGUI::Operation::Fork.
|
||||
|
||||
=cut
|
||||
|
||||
sub handler { renderBar( shift, $template ) }
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 renderBar ( process, template )
|
||||
|
||||
Renders $template, passing a "params" variable to it that is JSON of a
|
||||
statusUrl to poll and a page to redirect to and an i18n function. Includes
|
||||
WebGUI.Fork.redirect, poll, and ProgressBar js and CSS (as well as all their
|
||||
YUI dependancies), and puts the whole template inside an adminConsole rendered
|
||||
based off some form parameters.
|
||||
|
||||
=cut
|
||||
|
||||
sub renderBar {
|
||||
my ( $process, $template ) = @_;
|
||||
my $session = $process->session;
|
||||
my $url = $session->url;
|
||||
my $form = $session->form;
|
||||
my $style = $session->style;
|
||||
my $tt = Template->new;
|
||||
my %vars = (
|
||||
i18n => sub {
|
||||
my ($namespace, $key) = @_;
|
||||
return WebGUI::International->new($session, $namespace)->get($key);
|
||||
},
|
||||
params => JSON::encode_json( {
|
||||
statusUrl => $url->page( $process->contentPairs('Status') ),
|
||||
redirect => scalar $form->get('proceed'),
|
||||
}
|
||||
),
|
||||
);
|
||||
$tt->process( \$template, \%vars, \my $content ) or die $tt->error;
|
||||
|
||||
my $console = WebGUI::AdminConsole->new( $session, $form->get('icon') );
|
||||
$style->setLink( $url->extras("Fork/ProgressBar.css"), { rel => 'stylesheet' } );
|
||||
$style->setScript( $url->extras("$_.js") )
|
||||
for ( (
|
||||
map {"yui/build/$_"}
|
||||
qw(
|
||||
yahoo/yahoo-min
|
||||
dom/dom-min
|
||||
json/json-min
|
||||
event/event-min
|
||||
connection/connection_core-min
|
||||
)
|
||||
),
|
||||
'Fork/ProgressBar',
|
||||
'Fork/poll',
|
||||
'Fork/redirect'
|
||||
);
|
||||
return $console->render( $content, encode_entities( $form->get('title') ) );
|
||||
} ## end sub renderBar
|
||||
|
||||
1;
|
||||
155
lib/WebGUI/Fork/ProgressTree.pm
Normal file
155
lib/WebGUI/Fork/ProgressTree.pm
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
package WebGUI::Fork::ProgressTree;
|
||||
|
||||
=head1 LEGAL
|
||||
|
||||
-------------------------------------------------------------------
|
||||
WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
-------------------------------------------------------------------
|
||||
Please read the legal notices (docs/legal.txt) and the license
|
||||
(docs/license.txt) that came with this distribution before using
|
||||
this software.
|
||||
-------------------------------------------------------------------
|
||||
http://www.plainblack.com info@plainblack.com
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Fork::ProgressTree
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Renders an admin console page that polls ::Status to draw a friendly graphical
|
||||
representation of how progress on a tree of assets is coming along.
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
These subroutines are available from this package:
|
||||
|
||||
=cut
|
||||
|
||||
use Template;
|
||||
use HTML::Entities;
|
||||
use JSON;
|
||||
use WebGUI::Fork::ProgressBar;
|
||||
|
||||
my $template = <<'TEMPLATE';
|
||||
<div id='loading'>[% i18n('WebGUI', 'Loading...') %]</div>
|
||||
<div id='ui' style='display: none'>
|
||||
<div id='meter'></div>
|
||||
[% i18n('Fork_ProgressBar', 'current asset') %]: <span id='focus'></span>
|
||||
(<span id='finished'></span>/<span id='total'></span>).<br />
|
||||
[% i18n('Fork_ProgressBar', 'time elapsed') %]:
|
||||
<span id='elapsed'></span>
|
||||
[% i18n('Fork_ProgressBar', 'seconds') %].
|
||||
<ul id='tree'></ul>
|
||||
</div>
|
||||
<script>
|
||||
(function (params) {
|
||||
var bar = new YAHOO.WebGUI.Fork.ProgressBar();
|
||||
|
||||
function setHtml(id, html) {
|
||||
document.getElementById(id).innerHTML = html;
|
||||
}
|
||||
function draw(data) {
|
||||
var tree, finished = 0, total = 0, focus, pct;
|
||||
function recurse(asset, node) {
|
||||
var li = document.createElement('li'), txt, notes, ul, i;
|
||||
|
||||
total += 1;
|
||||
|
||||
txt = asset.url;
|
||||
if (asset.success) {
|
||||
li.className = 'success';
|
||||
finished += 1;
|
||||
}
|
||||
else if (asset.failure) {
|
||||
li.className = 'failure';
|
||||
txt += ' (' + asset.failure + ')';
|
||||
finished += 1;
|
||||
}
|
||||
if (asset.focus) {
|
||||
li.className += 'focus';
|
||||
focus = asset.url;
|
||||
}
|
||||
li.appendChild(document.createTextNode(txt));
|
||||
if (notes = asset.notes) {
|
||||
_.each(notes, function (note) {
|
||||
var p = document.createElement('p');
|
||||
p.innerHTML = note;
|
||||
li.appendChild(p);
|
||||
});
|
||||
}
|
||||
if (asset.children) {
|
||||
ul = document.createElement('ul');
|
||||
_.each(asset.children, function (child) {
|
||||
recurse(child, ul);
|
||||
});
|
||||
li.appendChild(ul);
|
||||
}
|
||||
node.appendChild(li);
|
||||
}
|
||||
tree = document.getElementById('tree');
|
||||
tree.innerHTML = '';
|
||||
_.each(JSON.parse(data.status), function (root) {
|
||||
recurse(root, tree);
|
||||
});
|
||||
bar.update(finished, total);
|
||||
setHtml('total', total);
|
||||
setHtml('finished', finished);
|
||||
setHtml('focus', focus || 'nothing');
|
||||
setHtml('elapsed', data.elapsed);
|
||||
}
|
||||
YAHOO.util.Event.onDOMReady(function () {
|
||||
bar.render('meter');
|
||||
YAHOO.WebGUI.Fork.poll({
|
||||
url : params.statusUrl,
|
||||
draw : draw,
|
||||
first : function () {
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('ui').style.display = 'block';
|
||||
},
|
||||
finish : function () {
|
||||
YAHOO.WebGUI.Fork.redirect(params.redirect);
|
||||
},
|
||||
error : function (msg) {
|
||||
alert(msg)
|
||||
}
|
||||
});
|
||||
});
|
||||
}([% params %]));
|
||||
</script>
|
||||
TEMPLATE
|
||||
|
||||
my $stylesheet = <<'STYLESHEET';
|
||||
<style>
|
||||
#tree li { color: black }
|
||||
#tree li.focus { color: cyan }
|
||||
#tree li.failure { color: red }
|
||||
#tree li.success { color: green }
|
||||
</style>
|
||||
STYLESHEET
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 handler ( process )
|
||||
|
||||
See WebGUI::Operation::Fork.
|
||||
|
||||
=cut
|
||||
|
||||
sub handler {
|
||||
my $process = shift;
|
||||
my $session = $process->session;
|
||||
my $style = $session->style;
|
||||
my $url = $session->url;
|
||||
$style->setRawHeadTags($stylesheet);
|
||||
$style->setScript($url->extras('underscore/underscore-min.js'));
|
||||
WebGUI::Fork::ProgressBar::renderBar($process, $template);
|
||||
}
|
||||
|
||||
1;
|
||||
84
lib/WebGUI/Fork/Status.pm
Normal file
84
lib/WebGUI/Fork/Status.pm
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package WebGUI::Fork::Status;
|
||||
|
||||
use JSON;
|
||||
|
||||
=head1 LEGAL
|
||||
|
||||
-------------------------------------------------------------------
|
||||
WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
-------------------------------------------------------------------
|
||||
Please read the legal notices (docs/legal.txt) and the license
|
||||
(docs/license.txt) that came with this distribution before using
|
||||
this software.
|
||||
-------------------------------------------------------------------
|
||||
http://www.plainblack.com info@plainblack.com
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Fork::Status
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Returns a json response of the following form:
|
||||
|
||||
{
|
||||
"finished" : true,
|
||||
"elapsed" : 10,
|
||||
"status" : "whatever is in the status field. Could be anything.",
|
||||
"error" : "whatever is in the error field"
|
||||
}
|
||||
|
||||
Note that if your status is JSON, you'll have to decode that seperately, so
|
||||
something like:
|
||||
|
||||
decoded = JSON.parse(r.responseText);
|
||||
status = JSON.parse(decoded.status);
|
||||
|
||||
Finished is obviously true or false. Notably, it will be true in the error
|
||||
case: so to status.finished && !status.error means successful completion.
|
||||
Error will only be present if the process died for some reason.
|
||||
|
||||
Status will always be present, mostly so you can see what the last status was
|
||||
before it died.
|
||||
|
||||
Elapsed will be the number of seconds since the process started (or until the
|
||||
process finished, if it is finished).
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
These subroutines are available from this package:
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 handler ( process )
|
||||
|
||||
See the synopsis for what kind of response this generates.
|
||||
|
||||
=cut
|
||||
|
||||
sub handler {
|
||||
my $process = shift;
|
||||
my $status = $process->getStatus;
|
||||
my ( $finished, $startTime, $endTime, $error ) = $process->get( 'finished', 'startTime', 'endTime', 'error' );
|
||||
|
||||
$endTime = time() unless $finished;
|
||||
|
||||
my %status = (
|
||||
status => $status,
|
||||
elapsed => ( $endTime - $startTime ),
|
||||
finished => ( $finished ? \1 : \0 ),
|
||||
);
|
||||
$status{error} = $error if $finished;
|
||||
$process->session->http->setMimeType('text/plain');
|
||||
JSON::encode_json( \%status );
|
||||
} ## end sub handler
|
||||
|
||||
1;
|
||||
|
|
@ -302,25 +302,25 @@ sub toHtml {
|
|||
value => 'upload',
|
||||
id => $self->get('id')
|
||||
})->toHtml
|
||||
. "<br />";
|
||||
. "\n";
|
||||
}
|
||||
else {
|
||||
$uploadControl .= WebGUI::Form::Hidden->new($self->session, {
|
||||
name => $self->get("name"),
|
||||
value => $self->getOriginalValue,
|
||||
id => $self->get("id")
|
||||
})->toHtml()."<br />";
|
||||
})->toHtml()."\n";
|
||||
$uploadControl .= WebGUI::Form::Hidden->new($self->session, {
|
||||
name => $self->privateName('action'),
|
||||
value => 'keep',
|
||||
id => $self->get("id")
|
||||
})->toHtml()."<br />";
|
||||
})->toHtml()."\n";
|
||||
}
|
||||
if (scalar(@files)) {
|
||||
if ($self->get('maxAttachments') == 1) {
|
||||
$self->set("");
|
||||
}
|
||||
$uploadControl .= $self->getFilePreview($storage);
|
||||
$uploadControl .= "<br />".$self->getFilePreview($storage);
|
||||
}
|
||||
return $uploadControl;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ Renders an HTML area field.
|
|||
sub toHtml {
|
||||
my $self = shift;
|
||||
##Do not display a rich editor on any mobile browser.
|
||||
if ($self->session->style->useMobileStyle) {
|
||||
if ($self->session->style->mobileBrowser) {
|
||||
return $self->SUPER::toHtml;
|
||||
}
|
||||
my $i18n = WebGUI::International->new($self->session);
|
||||
|
|
|
|||
|
|
@ -113,7 +113,6 @@ sub getValue {
|
|||
my ( $self, $value ) = @_;
|
||||
$value ||= $self->SUPER::getValue;
|
||||
|
||||
$self->session->log->info( "JsonTable Got $value from form" );
|
||||
$value = JSON->new->decode( $value );
|
||||
|
||||
for my $row ( @{$value} ) {
|
||||
|
|
|
|||
|
|
@ -409,20 +409,18 @@ sub processReplacements {
|
|||
my $session = shift;
|
||||
my ($content) = @_;
|
||||
my $replacements = $session->stow->get("replacements");
|
||||
if (defined $replacements) {
|
||||
foreach my $searchFor (keys %{$replacements}) {
|
||||
my $replaceWith = $replacements->{$searchFor};
|
||||
$content =~ s/\Q$searchFor/$replaceWith/gs;
|
||||
}
|
||||
} else {
|
||||
if (! defined $replacements) {
|
||||
my $sth = $session->dbSlave->read("select searchFor,replaceWith from replacements");
|
||||
while (my ($searchFor,$replaceWith) = $sth->array) {
|
||||
while (my ($searchFor,$replaceWith) = $sth->array) {
|
||||
$replacements->{$searchFor} = $replaceWith;
|
||||
$content =~ s/\Q$searchFor/$replaceWith/gs;
|
||||
}
|
||||
$sth->finish;
|
||||
$session->stow->set("replacements",$replacements);
|
||||
}
|
||||
$sth->finish;
|
||||
$session->stow->set("replacements",$replacements);
|
||||
}
|
||||
foreach my $searchFor (keys %{$replacements}) {
|
||||
my $replaceWith = $replacements->{$searchFor};
|
||||
$content =~ s/\b\Q$searchFor\E\b/$replaceWith/gs;
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ Returns a hash reference containing operation and package names.
|
|||
|
||||
sub getOperations {
|
||||
return {
|
||||
'fork' => 'Fork',
|
||||
'killSession' => 'ActiveSessions',
|
||||
'viewActiveSessions' => 'ActiveSessions',
|
||||
|
||||
|
|
|
|||
74
lib/WebGUI/Operation/Fork.pm
Normal file
74
lib/WebGUI/Operation/Fork.pm
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package WebGUI::Operation::Fork;
|
||||
|
||||
=head1 LEGAL
|
||||
|
||||
-------------------------------------------------------------------
|
||||
WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
-------------------------------------------------------------------
|
||||
Please read the legal notices (docs/legal.txt) and the license
|
||||
(docs/license.txt) that came with this distribution before using
|
||||
this software.
|
||||
-------------------------------------------------------------------
|
||||
http://www.plainblack.com info@plainblack.com
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use WebGUI::Fork;
|
||||
use WebGUI::Pluggable;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Operation::Fork
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
URL dispatching for WebGUI::Fork monitoring
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
These subroutines are available from this package:
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_fork ( session )
|
||||
|
||||
Dispatches to the proper module based on the module form parameter if op is
|
||||
fork. Returns insufficient privilege page if the user doesn't pass canView on
|
||||
the process before dispatching.
|
||||
|
||||
=cut
|
||||
|
||||
sub www_fork {
|
||||
my $session = shift;
|
||||
my $form = $session->form;
|
||||
my $module = $form->get('module') || 'Status';
|
||||
my $pid = $form->get('pid') || return undef;
|
||||
|
||||
my $process = WebGUI::Fork->new( $session, $pid );
|
||||
|
||||
return $session->privilege->insufficient unless $process->canView;
|
||||
|
||||
my $log = $session->log;
|
||||
|
||||
unless ($process) {
|
||||
$log->error("Tried to get info for nonexistent process $pid");
|
||||
return undef;
|
||||
}
|
||||
|
||||
my $output = eval { WebGUI::Pluggable::run( "WebGUI::Fork::$module", 'handler', [$process] ); };
|
||||
|
||||
if ($@) {
|
||||
$log->error($@);
|
||||
return undef;
|
||||
}
|
||||
|
||||
return $output;
|
||||
} ## end sub www_fork
|
||||
|
||||
1;
|
||||
|
|
@ -645,6 +645,7 @@ sub www_editUser {
|
|||
-name=>"username",
|
||||
-label=>$i18n->get(50),
|
||||
-value=>$username
|
||||
-extras=>'autocomplete="off"',
|
||||
);
|
||||
my %status;
|
||||
tie %status, 'Tie::IxHash';
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ use WebGUI::VersionTag;
|
|||
use WebGUI::HTMLForm;
|
||||
use WebGUI::Paginator;
|
||||
use Tie::IxHash;
|
||||
use WebGUI::Fork;
|
||||
use Monkey::Patch;
|
||||
use JSON;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
|
|
@ -138,6 +141,50 @@ sub getVersionTagOptions {
|
|||
return %tag;
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
=head2 rollbackInFork ($process, $tagId)
|
||||
|
||||
WebGUI::Fork method called by www_rollbackVersionTag
|
||||
|
||||
=cut
|
||||
|
||||
sub rollbackInFork {
|
||||
my ( $process, $tagId ) = @_;
|
||||
my $session = $process->session;
|
||||
my $tag = WebGUI::VersionTag->new( $session, $tagId );
|
||||
my %status = (
|
||||
finished => 0,
|
||||
total => $process->session->db->quickScalar( 'SELECT count(*) FROM assetData WHERE tagId = ?', [$tagId] ),
|
||||
message => '',
|
||||
);
|
||||
my $update = sub {
|
||||
$process->update( sub { JSON::encode_json( \%status ) } );
|
||||
};
|
||||
my $patch = Monkey::Patch::patch_class(
|
||||
'WebGUI::Asset',
|
||||
'purgeRevision',
|
||||
sub {
|
||||
my $purgeRevision = shift;
|
||||
my $self = shift;
|
||||
$self->$purgeRevision(@_);
|
||||
$status{finished}++;
|
||||
$update->();
|
||||
}
|
||||
);
|
||||
$tag->rollback( {
|
||||
outputSub => sub {
|
||||
$status{message} = shift;
|
||||
$update->();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
# need to get at least one of these in for the degenerate case of no
|
||||
# revisions in tag
|
||||
$update->();
|
||||
} ## end sub rollbackInFork
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_approveVersionTag ( session )
|
||||
|
|
@ -656,8 +703,9 @@ sub www_manageRevisionsInTag {
|
|||
|
||||
# Process any actions
|
||||
my $action = lc $session->form->get('action');
|
||||
my $form = $session->form;
|
||||
my $validToken = $session->form->validToken;
|
||||
if ( $action eq "purge" && $validToken) {
|
||||
if ( $form->get('purge') && $validToken) {
|
||||
# Purge these revisions
|
||||
my @assetInfo = $session->form->get('assetInfo');
|
||||
for my $assetInfo ( @assetInfo ) {
|
||||
|
|
@ -672,7 +720,7 @@ sub www_manageRevisionsInTag {
|
|||
return www_manageVersions( $session );
|
||||
}
|
||||
}
|
||||
elsif ( $action eq "move to:" && $validToken) {
|
||||
elsif ( $form->get('moveto') && $validToken) {
|
||||
# Get the new version tag
|
||||
my $moveToTagId = $session->form->get('moveToTagId');
|
||||
my $moveToTag;
|
||||
|
|
@ -700,7 +748,7 @@ sub www_manageRevisionsInTag {
|
|||
return www_manageVersions( $session );
|
||||
}
|
||||
}
|
||||
elsif ( $action eq "update version tag" && $validToken) {
|
||||
elsif ( $form->get('update') && $validToken) {
|
||||
my $startTime = WebGUI::DateTime->new($session,$session->form->process("startTime","dateTime"))->toDatabase;
|
||||
my $endTime = WebGUI::DateTime->new($session,$session->form->process("endTime","dateTime"))->toDatabase;
|
||||
|
||||
|
|
@ -786,19 +834,19 @@ sub www_manageRevisionsInTag {
|
|||
value => WebGUI::DateTime->new($session,$filterEndTime)->epoch,
|
||||
})
|
||||
. '<br />'
|
||||
. '<input type="submit" name="action" value="'. $i18n->get('manageRevisionsInTag update') . '" />'
|
||||
. '<input type="submit" name="update" value="'. $i18n->get('manageRevisionsInTag update') . '" />'
|
||||
. '</td>'
|
||||
. '</tr>'
|
||||
. '<tr><td colspan="5"> </td></tr>'
|
||||
. '<tr>'
|
||||
. '<td colspan="5">'
|
||||
. $i18n->get("manageRevisionsInTag with selected")
|
||||
. '<input type="submit" name="action" value="'. $i18n->get("manageRevisionsInTag move") . '" />'
|
||||
. '<input type="submit" name="moveto" value="'. $i18n->get("manageRevisionsInTag move") . '" />'
|
||||
. WebGUI::Form::SelectBox( $session, {
|
||||
name => 'moveToTagId',
|
||||
options => \%moveToTagOptions,
|
||||
} )
|
||||
. ' <input type="submit" name="action" value="'. $i18n->get('manageRevisionsInTag purge') . '" class="red" />'
|
||||
. ' <input type="submit" name="purge" value="'. $i18n->get('manageRevisionsInTag purge') . '" class="red" />'
|
||||
. '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
|
|
@ -854,16 +902,27 @@ sub www_rollbackVersionTag {
|
|||
return $session->privilege->adminOnly() unless canView($session);
|
||||
my $tagId = $session->form->process("tagId");
|
||||
return $session->privilege->vitalComponent() if ($tagId eq "pbversion0000000000001");
|
||||
my $pb = WebGUI::ProgressBar->new($session);
|
||||
my $i18n = WebGUI::International->new($session, 'VersionTag');
|
||||
$pb->start($i18n->get('rollback version tag'), $session->url->extras('adminConsole/versionTags.gif'));
|
||||
if ($tagId) {
|
||||
my $tag = WebGUI::VersionTag->new($session, $tagId);
|
||||
$tag->rollback({ outputSub => sub { $pb->update(@_) }, }) if defined $tag;
|
||||
}
|
||||
|
||||
my $process = WebGUI::Fork->start(
|
||||
$session, 'WebGUI::Operation::VersionTag', 'rollbackInFork', $tagId
|
||||
);
|
||||
|
||||
my $i18n = WebGUI::International->new($session, 'VersionTag');
|
||||
my $method = $session->form->process("proceed");
|
||||
$method = $method eq "manageCommittedVersions" ? $method : 'manageVersions';
|
||||
$pb->finish(WebGUI::Asset->getDefault($session)->getUrl('op='.$method));
|
||||
my $redir = WebGUI::Asset->getDefault($session)->getUrl("op=$method");
|
||||
$session->http->setRedirect(
|
||||
$session->url->page(
|
||||
$process->contentPairs(
|
||||
'ProgressBar', {
|
||||
icon => 'versions',
|
||||
title => $i18n->get('rollback version tag'),
|
||||
proceed => $redir,
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
return 'redirect';
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
172
lib/WebGUI/ProgressTree.pm
Normal file
172
lib/WebGUI/ProgressTree.pm
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
package WebGUI::ProgressTree;
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::ProgressTree
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Helper functions for maintaining a JSON represtentation of the progress of an
|
||||
operation that modifies a tree of assets. See WebGUI::Fork::ProgressTree for a
|
||||
status page that renders this.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
my $tree = WebGUI::ProgressTree->new($session, \@assetIds);
|
||||
$tree->success($assetId);
|
||||
$tree->failure($assetId, $reason);
|
||||
$tree->note($assetId, 'something about this one...');
|
||||
|
||||
=head1 LEGAL
|
||||
|
||||
-------------------------------------------------------------------
|
||||
WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
-------------------------------------------------------------------
|
||||
Please read the legal notices (docs/legal.txt) and the license
|
||||
(docs/license.txt) that came with this distribution before using
|
||||
this software.
|
||||
-------------------------------------------------------------------
|
||||
http://www.plainblack.com info@plainblack.com
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 new ($session, $assetIds)
|
||||
|
||||
Constructs new tree object for tracking the progress of $assetIds.
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my ( $class, $session, $assetIds ) = @_;
|
||||
my $db = $session->db;
|
||||
my $dbh = $db->dbh;
|
||||
my $set = join( ',', map { $dbh->quote($_) } @$assetIds );
|
||||
my $sql = qq{
|
||||
SELECT a.assetId, a.parentId, d.url
|
||||
FROM asset a INNER JOIN assetData d ON a.assetId = d.assetId
|
||||
WHERE a.assetId IN ($set)
|
||||
ORDER BY a.lineage ASC, d.revisionDate DESC
|
||||
};
|
||||
my $sth = $db->read($sql);
|
||||
my ( %flat, @roots );
|
||||
|
||||
while ( my $asset = $sth->hashRef ) {
|
||||
my ( $id, $parentId ) = delete @{$asset}{ 'assetId', 'parentId' };
|
||||
|
||||
# We'll get back multiple rows for each asset, but the first one is
|
||||
# the latest. Skip the others.
|
||||
next if $flat{$id};
|
||||
$flat{$id} = $asset;
|
||||
if ( my $parent = $flat{$parentId} ) {
|
||||
push( @{ $parent->{children} }, $asset );
|
||||
}
|
||||
else {
|
||||
push( @roots, $asset );
|
||||
}
|
||||
}
|
||||
my $self = {
|
||||
session => $session,
|
||||
tree => \@roots,
|
||||
flat => \%flat,
|
||||
};
|
||||
bless $self, $class;
|
||||
} ## end sub new
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 success ($assetId)
|
||||
|
||||
Whatever we were doing to $assetId succeeded. Woohoo!
|
||||
|
||||
=cut
|
||||
|
||||
sub success {
|
||||
my ( $self, $assetId ) = @_;
|
||||
$self->{flat}->{$assetId}->{success} = 1;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 failure ($assetId, $reason)
|
||||
|
||||
Whatever we were doing to $assetId didn't work for $reason. Aww.
|
||||
|
||||
=cut
|
||||
|
||||
sub failure {
|
||||
my ( $self, $assetId, $reason ) = @_;
|
||||
$self->{flat}->{$assetId}->{failure} = $reason;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 note ($assetId, $note)
|
||||
|
||||
Add some extra text. WebGUI::Fork::ProgressTree displays these as paragraphs
|
||||
under the node for this asset.
|
||||
|
||||
=cut
|
||||
|
||||
sub note {
|
||||
my ( $self, $assetId, $note ) = @_;
|
||||
push( @{ $self->{flat}->{$assetId}->{notes} }, $note );
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 focus ($assetId)
|
||||
|
||||
Make a note that this is the asset that we are currently doing something with.
|
||||
|
||||
=cut
|
||||
|
||||
sub focus {
|
||||
my ( $self, $assetId ) = @_;
|
||||
if ( my $last = delete $self->{last} ) {
|
||||
delete $last->{focus};
|
||||
}
|
||||
if ($assetId) {
|
||||
my $focus = $self->{last} = $self->{flat}->{$assetId};
|
||||
$focus->{focus} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 tree
|
||||
|
||||
A hashy representation of the status of this tree of assets.
|
||||
|
||||
=cut
|
||||
|
||||
sub tree { $_[0]->{tree} }
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 json
|
||||
|
||||
$self->tree encoded as json.
|
||||
|
||||
=cut
|
||||
|
||||
sub json { JSON::encode_json( $_[0]->tree ) }
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 session
|
||||
|
||||
The WebGUI::Session this progress tree is associated with.
|
||||
|
||||
=cut
|
||||
|
||||
sub session { $_[0]->{session} }
|
||||
|
||||
1;
|
||||
|
|
@ -648,7 +648,7 @@ sub quickCSV {
|
|||
my $sql = shift;
|
||||
my $params = shift;
|
||||
|
||||
my $csv = Text::CSV_XS->new({ eol => "\n" });
|
||||
my $csv = Text::CSV_XS->new({ eol => "\n", binary => 1 });
|
||||
|
||||
my $sth = $self->prepare($sql);
|
||||
$sth->execute(@$params);
|
||||
|
|
@ -656,9 +656,12 @@ sub quickCSV {
|
|||
return undef unless $csv->combine($sth->getColumnNames);
|
||||
my $output = $csv->string;
|
||||
|
||||
while (my @data = $sth->fetchrow_array) {
|
||||
return undef unless $csv->combine(@data);
|
||||
$output .= $csv->string;
|
||||
while (my @data = $sth->array) {
|
||||
if ( ! $csv->combine(@data) ) {
|
||||
$self->session->log->error( "Problem creating CSV row: " . $csv->error_diag );
|
||||
return undef;
|
||||
}
|
||||
$output .= $csv->string();
|
||||
}
|
||||
|
||||
$sth->finish;
|
||||
|
|
|
|||
|
|
@ -110,6 +110,25 @@ sub makePrintable {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 mobileBrowser ( )
|
||||
|
||||
Returns true if the user's browser matches any of the mobile browsers set in the config file.
|
||||
|
||||
=cut
|
||||
|
||||
sub mobileBrowser {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $ua = $session->env->get('HTTP_USER_AGENT');
|
||||
for my $mobileUA (@{ $session->config->get('mobileUserAgents') }) {
|
||||
if ($ua =~ m/$mobileUA/) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 useMobileStyle
|
||||
|
||||
Returns a true value if we are on a mobile display.
|
||||
|
|
@ -130,7 +149,6 @@ sub useMobileStyle {
|
|||
if (! $session->setting->get('useMobileStyle')) {
|
||||
return $self->{_useMobileStyle} = 0;
|
||||
}
|
||||
|
||||
if ($session->request->browser->mobile) {
|
||||
return $self->{_useMobileStyle} = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ use List::MoreUtils qw(any);
|
|||
use File::Copy ();
|
||||
use File::Temp ();
|
||||
use Try::Tiny;
|
||||
use Monkey::Patch qw( patch_object );
|
||||
use Scope::Guard;
|
||||
use Try::Tiny;
|
||||
use WebGUI::Paths -inc;
|
||||
use namespace::clean;
|
||||
|
||||
|
|
@ -537,6 +537,26 @@ sub originalConfig {
|
|||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
=head2 overrideSetting (name, val)
|
||||
|
||||
Overrides WebGUI::Test->session->setting->get($name) to return $val until the
|
||||
handle this method returns goes out of scope.
|
||||
|
||||
=cut
|
||||
|
||||
sub overrideSetting {
|
||||
my ($class, $name, $val) = @_;
|
||||
patch_object $class->session->setting => get => sub {
|
||||
my $get = shift;
|
||||
return $val if $_[1] eq $name;
|
||||
goto &$get;
|
||||
};
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
=head2 cleanupAdminInbox ( )
|
||||
|
||||
Push a list of Asset objects onto the stack of assets to be automatically purged
|
||||
|
|
|
|||
|
|
@ -38,6 +38,23 @@ These methods are available from this class:
|
|||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 autoCommitUrl ( $base )
|
||||
|
||||
Returns the url autoCommitWorkingIfEnabled would redirect to if it were going
|
||||
to.
|
||||
|
||||
=cut
|
||||
|
||||
sub autoCommitUrl {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $url = $session->url;
|
||||
my $base = shift || $url->page;
|
||||
my $id = $self->getId;
|
||||
return $url->append($base, "op=commitVersionTag;tagId=$id");
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
@ -76,25 +93,13 @@ sub autoCommitWorkingIfEnabled {
|
|||
return undef
|
||||
unless $versionTag;
|
||||
|
||||
#Auto commit is no longer determined from autoRequestCommit
|
||||
|
||||
# auto commit assets
|
||||
# save and commit button and site wide auto commit work the same
|
||||
# Do not auto commit if tag is system wide tag or tag belongs to someone else
|
||||
if (
|
||||
$options->{override}
|
||||
|| ( $class->getVersionTagMode($session) eq q{autoCommit}
|
||||
&& ! $versionTag->get(q{isSiteWide})
|
||||
&& $versionTag->get(q{createdBy}) eq $session->user()->userId()
|
||||
)
|
||||
) {
|
||||
if ($options->{override} || $versionTag->canAutoCommit) {
|
||||
if ($session->setting->get("skipCommitComments") || !$options->{allowComments}) {
|
||||
$versionTag->requestCommit;
|
||||
return 'commit';
|
||||
}
|
||||
else {
|
||||
my $url = $options->{returnUrl} || $session->url->page;
|
||||
$url = $session->url->append($url, "op=commitVersionTag;tagId=" . $versionTag->getId);
|
||||
my $url = $versionTag->autoCommitUrl($options->{returnUrl});
|
||||
$session->http->setRedirect($url);
|
||||
return 'redirect';
|
||||
}
|
||||
|
|
@ -104,6 +109,24 @@ sub autoCommitWorkingIfEnabled {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 canAutoCommit
|
||||
|
||||
Returns true if we would autocommit this tag without an override.
|
||||
|
||||
=cut
|
||||
|
||||
sub canAutoCommit {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $class = ref $self;
|
||||
my $mode = $class->getVersionTagMode($session);
|
||||
return $mode eq 'autoCommit'
|
||||
&& !$self->get('isSiteWide')
|
||||
&& $self->get('createdBy') eq $session->user->userId;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 clearWorking ( )
|
||||
|
||||
Makes it so this tag is no longer the working tag for any user.
|
||||
|
|
|
|||
|
|
@ -76,6 +76,22 @@ These methods are available from this class:
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 cleanup ( )
|
||||
|
||||
Override this activity to add a cleanup routine to be run if an instance
|
||||
is deleted with this activity currently in a waiting state. This is a stub
|
||||
and will do nothing unless overridden.
|
||||
|
||||
=cut
|
||||
|
||||
sub cleanup {
|
||||
my $self = shift;
|
||||
my $instance = shift;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 create ( session, workflowId [, id, classname ] )
|
||||
|
||||
Creates a new instance of this activity in a workflow.
|
||||
|
|
|
|||
82
lib/WebGUI/Workflow/Activity/RemoveOldForks.pm
Normal file
82
lib/WebGUI/Workflow/Activity/RemoveOldForks.pm
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
package WebGUI::Workflow::Activity::RemoveOldForks;
|
||||
|
||||
=head1 LEGAL
|
||||
|
||||
-------------------------------------------------------------------
|
||||
WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
-------------------------------------------------------------------
|
||||
Please read the legal notices (docs/legal.txt) and the license
|
||||
(docs/license.txt) that came with this distribution before using
|
||||
this software.
|
||||
-------------------------------------------------------------------
|
||||
http://www.plainblack.com info@plainblack.com
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=cut
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
use base 'WebGUI::Workflow::Activity';
|
||||
|
||||
use WebGUI::International;
|
||||
use WebGUI::Fork;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Workflow::Activity::RemoveOldForks
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Remove forks that are older than a configurable threshold.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
These methods are available from this class:
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 definition ( session, definition )
|
||||
|
||||
See WebGUI::Workflow::Activity::definition() for details.
|
||||
|
||||
=cut
|
||||
|
||||
sub definition {
|
||||
my ( $class, $session, $definition ) = @_;
|
||||
my $i18n = WebGUI::International->new( $session, 'Workflow_Activity_RemoveOldForks' );
|
||||
my %def = (
|
||||
name => $i18n->get('activityName'),
|
||||
properties => {
|
||||
interval => {
|
||||
fieldType => 'interval',
|
||||
label => $i18n->get('interval'),
|
||||
defaultValue => 60 * 60 * 24 * 7,
|
||||
hoverHelp => $i18n->get('interval help')
|
||||
}
|
||||
}
|
||||
);
|
||||
push @$definition, \%def;
|
||||
return $class->SUPER::definition( $session, $definition );
|
||||
} ## end sub definition
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 execute ( [ object ] )
|
||||
|
||||
See WebGUI::Workflow::Activity::execute() for details.
|
||||
|
||||
=cut
|
||||
|
||||
sub execute {
|
||||
my $self = shift;
|
||||
my $db = $self->session->db;
|
||||
my $tbl = $db->dbh->quote_identifier( WebGUI::Fork->tableName );
|
||||
my $time = time - $self->get('interval');
|
||||
$db->write( "DELETE FROM $tbl WHERE endTime <= ?", [$time] );
|
||||
return $self->COMPLETE;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
@ -44,6 +44,23 @@ These methods are available from this class:
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 cleanup ( )
|
||||
|
||||
Override this activity to add a cleanup routine to be run if an instance
|
||||
is deleted with this activity currently in a waiting state. This is a stub
|
||||
and will do nothing unless overridden.
|
||||
|
||||
=cut
|
||||
|
||||
sub cleanup {
|
||||
my $self = shift;
|
||||
my $instance = shift;
|
||||
$self->setMessageCompleted($instance);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 definition ( session, definition )
|
||||
|
||||
See WebGUI::Workflow::Activity::definition() for details.
|
||||
|
|
|
|||
|
|
@ -101,11 +101,22 @@ A boolean, that if true will not notify Spectre of the delete.
|
|||
=cut
|
||||
|
||||
sub delete {
|
||||
my $self = shift;
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $skipNotify = shift;
|
||||
$self->session->db->write("delete from WorkflowInstanceScratch where instanceId=?",[$self->getId]);
|
||||
$self->session->db->deleteRow("WorkflowInstance","instanceId",$self->getId);
|
||||
WebGUI::Workflow::Spectre->new($self->session)->notify("workflow/deleteInstance",$self->getId) unless ($skipNotify);
|
||||
|
||||
if ( $self->hasNextActivity ) {
|
||||
#We are deleting in the middle of a workflow - Get the current activity and call the cleanup routine
|
||||
my $activity = $self->getNextActivity;
|
||||
eval { $activity->cleanup($self) };
|
||||
if ($@) {
|
||||
$session->errorHandler->error("Caught exception executing cleanup routine which was not run on workflow activity ".$activity->getId." for instance ".$self->getId.". The following error was reported: ".$@);
|
||||
}
|
||||
}
|
||||
|
||||
$session->db->write("delete from WorkflowInstanceScratch where instanceId=?",[$self->getId]);
|
||||
$session->db->deleteRow("WorkflowInstance","instanceId",$self->getId);
|
||||
WebGUI::Workflow::Spectre->new($session)->notify("workflow/deleteInstance",$self->getId) unless ($skipNotify);
|
||||
|
||||
# We will need to remember that we were deleted if we get realtime-run
|
||||
# during start().
|
||||
|
|
|
|||
|
|
@ -1127,6 +1127,24 @@ search has been done.|,
|
|||
lastUpdated => 1231180362,
|
||||
},
|
||||
|
||||
'Creating column headers' => {
|
||||
message => q|Creating column headers.|,
|
||||
lastUpdated => 1231180362,
|
||||
context => q|Status message in the Export Thingy progress bar.|,
|
||||
},
|
||||
|
||||
'Writing data' => {
|
||||
message => q|Writing data.|,
|
||||
lastUpdated => 1231180362,
|
||||
context => q|Status message in the Export Thingy progress bar.|,
|
||||
},
|
||||
|
||||
'Return to %s' => {
|
||||
message => q|Return to %s.|,
|
||||
lastUpdated => 1231180362,
|
||||
context => q|Status message in the Export Thingy progress bar. %s is the name of the Thing that is being exported.|,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
22
lib/WebGUI/i18n/English/Fork_ProgressBar.pm
Normal file
22
lib/WebGUI/i18n/English/Fork_ProgressBar.pm
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package WebGUI::i18n::English::Fork_ProgressBar;
|
||||
|
||||
use strict;
|
||||
|
||||
our $I18N = {
|
||||
'time elapsed' => {
|
||||
message => 'Time Elapsed',
|
||||
lastUpdated => 1286466369,
|
||||
context => 'Used as a label to indicate how many seconds have gone by since the forked process started running',
|
||||
},
|
||||
'seconds' => {
|
||||
message => 'seconds',
|
||||
lastUpdated => 1286466433,
|
||||
},
|
||||
'current asset' => {
|
||||
message => 'Current Asset',
|
||||
lastUpdated => 1286466701,
|
||||
context => 'Used as a label to indicate which asset is in "focus"',
|
||||
},
|
||||
};
|
||||
|
||||
1;
|
||||
20
lib/WebGUI/i18n/English/Workflow_Activity_RemoveOldForks.pm
Normal file
20
lib/WebGUI/i18n/English/Workflow_Activity_RemoveOldForks.pm
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package WebGUI::i18n::English::Workflow_Activity_RemoveOldForks;
|
||||
|
||||
use strict;
|
||||
|
||||
our $I18N = {
|
||||
'interval help' => {
|
||||
message => 'How long do we wait after process completion before deleting it?',
|
||||
lastUpdated => 1285358250,
|
||||
},
|
||||
'interval' => {
|
||||
message => q|Interval|,
|
||||
lastUpdated => 1285358250,
|
||||
},
|
||||
'activityName' => {
|
||||
message => q|Remove Old Forks|,
|
||||
lastUpdated => 1285358250,
|
||||
},
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
@ -137,7 +137,7 @@ checkModule('JavaScript::Packer', '0.04' );
|
|||
checkModule('CSS::Packer', '0.2' );
|
||||
checkModule('Business::Tax::VAT::Validation', '0.20' );
|
||||
checkModule('Crypt::SSLeay', '0.57' );
|
||||
checkModule('Scope::Guard', '0.03' );
|
||||
checkModule('Scope::Guard', '0.20' );
|
||||
checkModule('Digest::SHA', '5.47' );
|
||||
checkModule("CSS::Minifier::XS", "0.03" );
|
||||
checkModule("JavaScript::Minifier::XS", "0.05" );
|
||||
|
|
@ -165,6 +165,7 @@ checkModule('Email::Valid', );
|
|||
checkModule('Facebook::Graph', '0.0505' );
|
||||
checkModule('HTTP::BrowserDetect', );
|
||||
checkModule('Search::QueryParser', );
|
||||
checkModule('Monkey::Patch', '0.03' );
|
||||
|
||||
failAndExit("Required modules are missing, running no more checks.") if $missingModule;
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -17,6 +17,7 @@ use WebGUI::Test;
|
|||
use WebGUI::Session;
|
||||
use WebGUI::Asset;
|
||||
use WebGUI::VersionTag;
|
||||
use Test::MockObject;
|
||||
|
||||
use Test::More; # increment this value for each test you create
|
||||
plan tests => 29;
|
||||
|
|
@ -147,12 +148,20 @@ sub copied {
|
|||
return undef;
|
||||
}
|
||||
|
||||
my @methods = qw(Single Children Descendants);
|
||||
my $process = Test::MockObject->new->mock(update => sub {});
|
||||
my @methods = (
|
||||
# single duplicate doesn't fork, so we can just test the www method to
|
||||
# make sure it gets it right
|
||||
sub { shift->www_copy },
|
||||
sub { shift->duplicateBranch(1, 'clipboard') },
|
||||
sub { shift->duplicateBranch(0, 'clipboard') },
|
||||
);
|
||||
my @prefixes = qw(single children descendants);
|
||||
for my $i (0..2) {
|
||||
my $meth = "_wwwCopy$methods[$i]";
|
||||
my $meth = $methods[$i];
|
||||
$root->$meth();
|
||||
my $clip = copied();
|
||||
is_tree_of_folders($clip, $i+1, $meth);
|
||||
is_tree_of_folders($clip, $i+1, @prefixes[$i]);
|
||||
$clip->purge;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -142,4 +142,13 @@ is ($event7->get('startDate'), '2000-09-01', 'startDate bumped by 1 day');
|
|||
is ($event7->get('endTime'), '00:00:00', 'endTime set to 00:00:00 if the hour is more than 23');
|
||||
is ($event7->get('endDate'), '2000-09-02', 'endDate bumped by 1 day');
|
||||
|
||||
#######################################
|
||||
#
|
||||
# duplicate
|
||||
#
|
||||
#######################################
|
||||
|
||||
|
||||
my $event6b = $event6->duplicate();
|
||||
isnt($event6b->get('storageId'), $event6->get('storageId'), 'duplicating an asset creates a new storage location');
|
||||
done_testing;
|
||||
|
|
|
|||
66
t/Asset/Shortcut/020-content-last-modified.t
Normal file
66
t/Asset/Shortcut/020-content-last-modified.t
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#-------------------------------------------------------------------
|
||||
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
#-------------------------------------------------------------------
|
||||
# Please read the legal notices (docs/legal.txt) and the license
|
||||
# (docs/license.txt) that came with this distribution before using
|
||||
# this software.
|
||||
#-------------------------------------------------------------------
|
||||
# http://www.plainblack.com info@plainblack.com
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
use FindBin;
|
||||
use strict;
|
||||
use lib "$FindBin::Bin/../../lib";
|
||||
|
||||
## The goal of this test is to test the link between the asset and its shortcut
|
||||
# and that changes to the asset are propagated to the shortcut
|
||||
|
||||
use Scalar::Util qw( blessed );
|
||||
use WebGUI::Test;
|
||||
use WebGUI::Session;
|
||||
use Test::More;
|
||||
use WebGUI::Asset::Shortcut;
|
||||
use WebGUI::Asset::Snippet;
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Init
|
||||
my $session = WebGUI::Test->session;
|
||||
my $node = WebGUI::Asset->getImportNode($session);
|
||||
|
||||
my $versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
$versionTag->set({name=>"Shortcut Test"});
|
||||
WebGUI::Test->addToCleanup($versionTag);
|
||||
# Make a snippet to shortcut
|
||||
my $now = time();
|
||||
my $snippet = $node->addChild({
|
||||
className => "WebGUI::Asset::Snippet",
|
||||
},
|
||||
undef, $now-50);
|
||||
|
||||
my $shortcut = $node->addChild({
|
||||
className => "WebGUI::Asset::Shortcut",
|
||||
shortcutToAssetId => $snippet->getId,
|
||||
},
|
||||
undef, $now-10);
|
||||
$versionTag->commit;
|
||||
$session->db->write(q|update assetData set lastModified=? where assetId=?|,[WebGUI::Test->webguiBirthday, $snippet->getId]);
|
||||
foreach my $asset ($snippet, $shortcut) {
|
||||
$asset = $asset->cloneFromDb;
|
||||
}
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Tests
|
||||
plan tests => 2;
|
||||
|
||||
is( $shortcut->getContentLastModified, $now-10, "getContentLastModified: returns date of shortcut since it has a newer revision date.");
|
||||
|
||||
$snippet->update({snippet => 'updated', }, $now-5);
|
||||
|
||||
diag $snippet->get('lastModified');
|
||||
diag $snippet->getContentLastModified;
|
||||
$shortcut = $shortcut->cloneFromDb; ##Wipe the cached version of the shortcut.
|
||||
|
||||
is( $shortcut->getContentLastModified, $snippet->get('lastModified'), "returns lastModified when shortcutted asset has a more recent date");
|
||||
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ use Test::MockObject::Extends;
|
|||
use WebGUI::Test;
|
||||
use WebGUI::Test::MockAsset;
|
||||
use WebGUI::Session;
|
||||
use Test::More tests => 8; # increment this value for each test you create
|
||||
use Test::More tests => 9; # increment this value for each test you create
|
||||
use Test::Deep;
|
||||
use Data::Dumper;
|
||||
|
||||
|
|
@ -126,6 +126,14 @@ cmp_deeply(
|
|||
$session->request->setup_body({ });
|
||||
$session->scratch->delete('userId');
|
||||
|
||||
################################################################
|
||||
#
|
||||
# getStatusList
|
||||
#
|
||||
################################################################
|
||||
$board->update({statusList => "In\r\nOut\rHome\nLunch"});
|
||||
is_deeply [$board->getStatusList], [qw(In Out Home Lunch)], 'getStatusList';
|
||||
|
||||
################################################################
|
||||
#
|
||||
# view
|
||||
|
|
|
|||
|
|
@ -119,13 +119,37 @@ my $dt = DateTime->from_epoch(epoch => $now, time_zone => $session->datetime->ge
|
|||
my $folderName = $dt->strftime('%B_%d_%Y');
|
||||
$folderName =~ s/^(\w+_)0/$1/;
|
||||
is($todayFolder->getTitle, $folderName, '... folder has the right name');
|
||||
my $folderUrl = join '/', $archive->getUrl, lc $folderName;
|
||||
is($todayFolder->getUrl, $folderUrl, '... folder has the right URL');
|
||||
my $folderUrl = $archive->getFolderUrl($folderName);
|
||||
is($todayFolder->get('url'), $folderUrl, '... folder has the right URL');
|
||||
is($todayFolder->getParent->getId, $archive->getId, '... created folder has the right parent');
|
||||
is($todayFolder->get('state'), 'published', '... created folder is published');
|
||||
is($todayFolder->get('status'), 'approved', '... created folder is approved');
|
||||
is($todayFolder->get('styleTemplateId'), $archive->get('styleTemplateId'), '... created folder has correct styleTemplateId');
|
||||
|
||||
{
|
||||
my $undo = WebGUI::Test->overrideSetting(urlExtension => 'ext');
|
||||
my $arch2 = $home->addChild({
|
||||
className => $class,
|
||||
title => 'Extension Tester',
|
||||
});
|
||||
addToCleanup($arch2);
|
||||
|
||||
is $arch2->get('url'),
|
||||
'home/extension-tester.ext',
|
||||
'ext added';
|
||||
|
||||
is $arch2->getFolderUrl('blah'),
|
||||
'home/extension-tester/blah.ext',
|
||||
'folder url: strip extension from parent and add to child';
|
||||
|
||||
my $folder = $arch2->getFolder($now);
|
||||
ok defined $folder, 'getFolder with url extension';
|
||||
|
||||
is $folder->get('url'),
|
||||
$arch2->getFolderUrl($folder->getMenuTitle),
|
||||
'getFolderUrl and folder getUrl match';
|
||||
}
|
||||
|
||||
my $sameFolder = $archive->getFolder($now);
|
||||
is($sameFolder->getId, $todayFolder->getId, 'call with same time returns the same folder');
|
||||
undef $sameFolder;
|
||||
|
|
|
|||
106
t/Fork.t
Normal file
106
t/Fork.t
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
# vim:syntax=perl
|
||||
#-------------------------------------------------------------------
|
||||
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
#-------------------------------------------------------------------
|
||||
# Please read the legal notices (docs/legal.txt) and the license
|
||||
# (docs/license.txt) that came with this distribution before using
|
||||
# this software.
|
||||
#------------------------------------------------------------------
|
||||
# http://www.plainblack.com info@plainblack.com
|
||||
#------------------------------------------------------------------
|
||||
|
||||
# WebGUI::Fork tests
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/lib";
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
|
||||
use Test::More;
|
||||
use Test::Deep;
|
||||
use Data::Dumper;
|
||||
use JSON;
|
||||
|
||||
use WebGUI::Test;
|
||||
use WebGUI::Session;
|
||||
use WebGUI::Fork;
|
||||
|
||||
my $class = 'WebGUI::Fork';
|
||||
my $testClass = 'WebGUI::Test::Fork';
|
||||
my $pipe = $class->init();
|
||||
my $session = WebGUI::Test->session;
|
||||
|
||||
# test simplest (non-forking) case
|
||||
|
||||
my $process = $class->create($session);
|
||||
my $request = $process->request( $testClass, 'simple', ['data'] );
|
||||
|
||||
cmp_bag(
|
||||
[ keys %$request ],
|
||||
[qw(webguiRoot configFile sessionId id module subname data)],
|
||||
'request hash has the right keys'
|
||||
);
|
||||
|
||||
my $now = time;
|
||||
|
||||
$class->runRequest($request);
|
||||
ok $process->isFinished, 'finished';
|
||||
my $error = $process->getError;
|
||||
ok( !$error, 'no errors' ) or diag " Expected nothing, got: $error\n";
|
||||
$process->setWait(0);
|
||||
is $process->getStatus, 'data', 'proper status';
|
||||
my $started = $process->startTime;
|
||||
ok( ( $started >= $now ), 'sane startTime' );
|
||||
ok( ( $process->endTime >= $started ), 'sane endTime' );
|
||||
|
||||
$process->delete;
|
||||
|
||||
note "Testing error case\n";
|
||||
$process = $class->create($session);
|
||||
$request = $process->request( $testClass, 'error', ['error'] );
|
||||
$class->runRequest($request);
|
||||
ok $process->isFinished, 'finished';
|
||||
is $process->getError, "error\n", 'has error code';
|
||||
$process->setWait(0);
|
||||
my $status = $process->getStatus;
|
||||
ok( !$status, 'no discernable status' ) or diag $status;
|
||||
ok( ( $process->endTime >= $started ), 'sane endTime' );
|
||||
|
||||
my $forkCount = 0;
|
||||
my $forkAndExec = $class->can('forkAndExec');
|
||||
my $replace = sub {
|
||||
my $self = shift;
|
||||
$forkCount++;
|
||||
$self->$forkAndExec(@_);
|
||||
};
|
||||
|
||||
{
|
||||
no strict 'refs';
|
||||
no warnings 'redefine';
|
||||
*{ $class . '::forkAndExec' } = $replace;
|
||||
}
|
||||
|
||||
sub backgroundTest {
|
||||
note "$_[0]\n";
|
||||
$process = $class->start( $session, $testClass, 'complex', ['data'] );
|
||||
my $sleeping;
|
||||
while ( !$process->isFinished && $sleeping++ < 10 ) {
|
||||
sleep 1;
|
||||
}
|
||||
ok $process->isFinished, 'finished';
|
||||
is $process->getStatus, 'baz', 'correct status'
|
||||
or diag $process->getError . "\n";
|
||||
|
||||
$process->delete;
|
||||
}
|
||||
backgroundTest('talk to background');
|
||||
is $forkCount, 0, 'we did not fork';
|
||||
close $pipe;
|
||||
backgroundTest('On-demand fork');
|
||||
is $forkCount, 1, 'we did fork';
|
||||
|
||||
done_testing;
|
||||
|
||||
#vim:ft=perl
|
||||
5
t/HTML.t
5
t/HTML.t
|
|
@ -127,6 +127,7 @@ my @htmlTextSets = (
|
|||
my $numTests = scalar @filterSets
|
||||
+ scalar @macroParamSets
|
||||
+ scalar @htmlTextSets
|
||||
+ 3
|
||||
;
|
||||
|
||||
plan tests => $numTests;
|
||||
|
|
@ -145,3 +146,7 @@ foreach my $testSet (@htmlTextSets) {
|
|||
my $text = WebGUI::HTML::html2text($testSet->{inputText});
|
||||
is($text, $testSet->{output}, $testSet->{comment});
|
||||
}
|
||||
|
||||
is(WebGUI::HTML::processReplacements($session, 'grass'), 'grass', 'processReplacements: grass is not replaced');
|
||||
is(WebGUI::HTML::processReplacements($session, 'shitake'), 'shitake', '... shitake is not replaced');
|
||||
is(WebGUI::HTML::processReplacements($session, 'This is shit.'), 'This is crap.', '... shit is replaced');
|
||||
|
|
|
|||
10
t/SQL.t
10
t/SQL.t
|
|
@ -299,3 +299,13 @@ cmp_deeply(
|
|||
'Check table structure',
|
||||
);
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# REGRESSIONS
|
||||
|
||||
# 11940 : quickCSV chokes on newlines
|
||||
$session->db->write(
|
||||
'INSERT INTO testTable (myIndex,message,myKey) VALUES (?,?,?)',
|
||||
[ 10, "a\ntest", 'B' ],
|
||||
);
|
||||
ok( $session->db->quickCSV( 'SELECT * FROM testTable' ), 'get some output even with newlines in data' );
|
||||
|
||||
|
|
|
|||
20
t/lib/WebGUI/Test/Fork.pm
Normal file
20
t/lib/WebGUI/Test/Fork.pm
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package WebGUI::Test::Fork;
|
||||
|
||||
sub simple {
|
||||
my ( $self, $arr ) = @_;
|
||||
$self->update( $arr->[0] );
|
||||
}
|
||||
|
||||
sub error {
|
||||
my ( $self, $arr ) = @_;
|
||||
die "$arr->[0]\n";
|
||||
}
|
||||
|
||||
sub complex {
|
||||
my $self = shift;
|
||||
$self->update( sub {'foo'} );
|
||||
$self->update( sub {'bar'} );
|
||||
$self->update( sub {'baz'} );
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
@ -2,13 +2,13 @@
|
|||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>PM capturó a tres delincuentes que robaron agencia bancaria en San Martín</title>
|
||||
<link>http://www.vtv.gob.ve/rss-noticias-nacionales</link>
|
||||
<link>http://ejemplo.local/rss-noticias-locales</link>
|
||||
<description>RSS Noticias Nacionales</description>
|
||||
<language>es</language>
|
||||
<item>
|
||||
<title>PM capturó a tres delincuentes que robaron agencia bancaria en San Martín</title>
|
||||
<link>http://www.vtv.gob.ve/noticias-nacionales/25087</link>
|
||||
<description><p>Efectivos de la Policía Metropolitana (PM) de Caracas capturaron, este lunes en horas de la mañana, a tres delincuentes implicados en el robo perpetrado en el Banco Industrial de Venezuela (BIV) ubicado dentro de la oficina del Instituto Postal Telegráfico de Venezuela, Ipostel, en la avenida José Ángel Lamas, San Martín.</p></description>
|
||||
<link>http://ejemplo.local/noticias-locales/99999</link>
|
||||
<description><p>Efectivos de la Policía Metropolitana (PM) capturaron, este lunes en horas de la mañana, a tres delincuentes implicados en un robo.</description>
|
||||
<pubDate>Mon, 19 Oct 2009 15:42:17 -0400</pubDate>
|
||||
</item>
|
||||
</channel>
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>PM capturó a tres delincuentes que robaron agencia bancaria en San Martín</title>
|
||||
<link>http://www.vtv.gob.ve/rss-noticias-nacionales</link>
|
||||
<description>RSS Noticias Nacionales</description>
|
||||
<link>http://ejemplo.local/rss-noticias-locales</link>
|
||||
<description>RSS Noticias Locales</description>
|
||||
<language>es</language>
|
||||
<item>
|
||||
<title>PM capturó a tres delincuentes que robaron agencia bancaria en San Martín</title>
|
||||
<link>http://www.vtv.gob.ve/noticias-nacionales/25087</link>
|
||||
<description><p>Efectivos de la Policía Metropolitana (PM) de Caracas capturaron, este lunes en horas de la mañana, a tres delincuentes implicados en el robo perpetrado en el Banco Industrial de Venezuela (BIV) ubicado dentro de la oficina del Instituto Postal Telegráfico de Venezuela, Ipostel, en la avenida José Ángel Lamas, San Martín.</p></description>
|
||||
<link>http://ejemplo.local/noticias-locales/99999</link>
|
||||
<description><p>Efectivos de la Policía Metropolitana (PM) capturaron, este lunes en horas de la mañana, a tres delincuentes implicados en un robo.</description>
|
||||
<pubDate>Mon, 19 Oct 2009 15:42:17 -0400</pubDate>
|
||||
</item>
|
||||
</channel>
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>PM capturó a tres delincuentes que robaron agencia bancaria en San Martín</title>
|
||||
<link>http://www.vtv.gob.ve/rss-noticias-nacionales</link>
|
||||
<description>RSS Noticias Nacionales</description>
|
||||
<link>http://ejemplo.local/rss-noticias-locales</link>
|
||||
<description>RSS Noticias Locales</description>
|
||||
<language>es</language>
|
||||
<item>
|
||||
<title>PM capturó a tres delincuentes que robaron agencia bancaria en San Martín</title>
|
||||
<link>http://www.vtv.gob.ve/noticias-nacionales/25087</link>
|
||||
<description><p>Efectivos de la Policía Metropolitana (PM) de Caracas capturaron, este lunes en horas de la mañana, a tres delincuentes implicados en el robo perpetrado en el Banco Industrial de Venezuela (BIV) ubicado dentro de la oficina del Instituto Postal Telegráfico de Venezuela, Ipostel, en la avenida José Ángel Lamas, San Martín.</p></description>
|
||||
<link>http://ejemplo.local/noticias-locales/99999</link>
|
||||
<description><p>Efectivos de la Policía Metropolitana (PM) Caracas capturaron, este lunes en horas de la mañana, a tres delincuentes implicados en un robo.</description>
|
||||
<pubDate>Mon, 19 Oct 2009 15:42:17 -0400</pubDate>
|
||||
</item>
|
||||
</channel>
|
||||
|
|
|
|||
20
www/extras/Fork/ProgressBar.css
Normal file
20
www/extras/Fork/ProgressBar.css
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
.webgui-fork-pb {
|
||||
border: thin solid black;
|
||||
position: relative;
|
||||
line-height: 20pt;
|
||||
height: 20pt;
|
||||
}
|
||||
|
||||
.webgui-fork-pb .webgui-fork-pb-bar {
|
||||
background-color: lime;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.webgui-fork-pb .webgui-fork-pb-caption {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 18pt;
|
||||
}
|
||||
30
www/extras/Fork/ProgressBar.js
Normal file
30
www/extras/Fork/ProgressBar.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*global YAHOO, WebGUI, document */
|
||||
/* Dependencies: yahoo, dom */
|
||||
(function () {
|
||||
var dom = YAHOO.util.Dom,
|
||||
ns = YAHOO.namespace('WebGUI.Fork'),
|
||||
cls = ns.ProgressBar = function () {},
|
||||
proto = cls.prototype;
|
||||
|
||||
proto.render = function (node) {
|
||||
var bar, cap;
|
||||
if (!node.tagName) {
|
||||
node = document.getElementById(node);
|
||||
}
|
||||
dom.addClass(node, 'webgui-fork-pb');
|
||||
bar = document.createElement('div');
|
||||
cap = document.createElement('div');
|
||||
dom.addClass(bar, 'webgui-fork-pb-bar');
|
||||
dom.addClass(cap, 'webgui-fork-pb-caption');
|
||||
node.appendChild(bar);
|
||||
node.appendChild(cap);
|
||||
this.domNode = node;
|
||||
this.bar = bar;
|
||||
this.caption = cap;
|
||||
};
|
||||
proto.update = function (done, total) {
|
||||
var pct = (total > 0 ? Math.floor((done/total)*100) : 100) + '%';
|
||||
this.caption.innerHTML = pct;
|
||||
this.bar.style.width = pct;
|
||||
};
|
||||
}());
|
||||
42
www/extras/Fork/poll.js
Normal file
42
www/extras/Fork/poll.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*global YAHOO, setTimeout */
|
||||
/* Dependencies: yahoo, connection_core, json */
|
||||
|
||||
(function () {
|
||||
var ns = YAHOO.namespace('WebGUI.Fork'), JSON = YAHOO.lang.JSON;
|
||||
|
||||
ns.poll = function(args) {
|
||||
function fetch() {
|
||||
var first = true;
|
||||
YAHOO.util.Connect.asyncRequest('GET', args.url, {
|
||||
success: function (o) {
|
||||
var data, e;
|
||||
if (o.status != 200) {
|
||||
args.error("Server returned bad response");
|
||||
return;
|
||||
}
|
||||
data = JSON.parse(o.responseText);
|
||||
e = data.error;
|
||||
if (e) {
|
||||
args.error(e);
|
||||
return;
|
||||
}
|
||||
args.draw(data);
|
||||
if (args.first && first) {
|
||||
first = false;
|
||||
args.first();
|
||||
}
|
||||
if (data.finished) {
|
||||
args.finish();
|
||||
}
|
||||
else {
|
||||
setTimeout(fetch, args.interval || 1000);
|
||||
}
|
||||
},
|
||||
failure: function (o) {
|
||||
args.error("Could not communicate with server");
|
||||
}
|
||||
});
|
||||
}
|
||||
fetch();
|
||||
};
|
||||
}());
|
||||
16
www/extras/Fork/redirect.js
Normal file
16
www/extras/Fork/redirect.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/*global YAHOO, setTimeout, window */
|
||||
/* Dependencies: yahoo */
|
||||
|
||||
(function () {
|
||||
var ns = YAHOO.namespace('WebGUI.Fork');
|
||||
ns.redirect = function (redir, after) {
|
||||
if (!redir) {
|
||||
return;
|
||||
}
|
||||
setTimeout(function() {
|
||||
// The idea here is to only allow local redirects
|
||||
var loc = window.location;
|
||||
loc.href = loc.protocol + '//' + loc.host + redir;
|
||||
}, after || 1000);
|
||||
};
|
||||
}());
|
||||
|
|
@ -314,11 +314,6 @@
|
|||
return;
|
||||
}
|
||||
|
||||
if (!id.match(/^[A-Za-z0-9_-]{22}$/)) {
|
||||
alert('Error: bad response trying to save address.');
|
||||
return;
|
||||
}
|
||||
|
||||
function updateOne(dropdown) {
|
||||
var opt = _.detect(dropdown.options, function (o) {
|
||||
return o.text === label;
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue